From b09c265cb409f752fc611da4132ab6fbbee9ecba Mon Sep 17 00:00:00 2001 From: John Scipione Date: Mon, 4 Mar 2013 04:45:06 -0500 Subject: [PATCH] Update bitmap downscaling for BeOS icons. Implemented a simple down sampling algorithm in the scale_down() function. For non-integer scaling first scale up using the scale2x, scale3x, or scale4x algorithm doubling, tripling, or quadrupling the icon then use the downscaling algorithm to shrink to the desired size. This produces nicer looking results than bilinear scaling alone. Note that this only applies to bitmap-based BeOS icons and not vector-based HVIF icons. --- src/libs/icon/IconUtils.cpp | 165 ++++++++++++++++++++++++++++++------ 1 file changed, 137 insertions(+), 28 deletions(-) diff --git a/src/libs/icon/IconUtils.cpp b/src/libs/icon/IconUtils.cpp index 0971c02007..6d546e2a33 100644 --- a/src/libs/icon/IconUtils.cpp +++ b/src/libs/icon/IconUtils.cpp @@ -1,11 +1,12 @@ /* - * Copyright 2006-2011, Haiku. All rights reserved. + * Copyright 2006-2013, Haiku. All rights reserved. * Distributed under the terms of the MIT License. * * Authors: - * Stephan Aßmus - * Ingo Weinhold - * John Scipione + * Stephan Aßmus, superstippi@gmx.de + * Axel Dörfler, axeld@pinc-software.de + * John Scipione, jscipione@gmail.com + * Ingo Weinhold, bonefish@cs.tu-berlin.de */ @@ -101,6 +102,70 @@ scale_bilinear(uint8* bits, int32 srcWidth, int32 srcHeight, int32 dstWidth, } +static void +scale_down(const uint8* srcBits, uint8* dstBits, int32 srcWidth, int32 srcHeight, + int32 dstWidth, int32 dstHeight) +{ + int32 l; + int32 c; + float t; + float u; + float tmp; + float d1, d2, d3, d4; + // coefficients + uint32 p1, p2, p3, p4; + // nearby pixels + uint8 red, green, blue, alpha; + // color components + + for (int32 i = 0; i < dstHeight; i++) { + for (int32 j = 0; j < dstWidth; j++) { + tmp = (float)(i) / (float)(dstHeight - 1) * (srcHeight - 1); + l = (int32)floorf(tmp); + if (l < 0) + l = 0; + else if (l >= srcHeight - 1) + l = srcHeight - 2; + u = tmp - l; + + tmp = (float)(j) / (float)(dstWidth - 1) * (srcWidth - 1); + c = (int32)floorf(tmp); + if (c < 0) + c = 0; + else if (c >= srcWidth - 1) + c = srcWidth - 2; + t = tmp - c; + + // coefficients + d1 = (1 - t) * (1 - u); + d2 = t * (1 - u); + d3 = t * u; + d4 = (1 - t) * u; + + // nearby pixels + p1 = *((uint32*)srcBits + (l * srcWidth) + c); + p2 = *((uint32*)srcBits + (l * srcWidth) + c + 1); + p3 = *((uint32*)srcBits + ((l + 1)* srcWidth) + c + 1); + p4 = *((uint32*)srcBits + ((l + 1)* srcWidth) + c); + + // color components + blue = (uint8)p1 * d1 + (uint8)p2 * d2 + (uint8)p3 * d3 + + (uint8)p4 * d4; + green = (uint8)(p1 >> 8) * d1 + (uint8)(p2 >> 8) * d2 + + (uint8)(p3 >> 8) * d3 + (uint8)(p4 >> 8) * d4; + red = (uint8)(p1 >> 16) * d1 + (uint8)(p2 >> 16) * d2 + + (uint8)(p3 >> 16) * d3 + (uint8)(p4 >> 16) * d4; + alpha = (uint8)(p1 >> 24) * d1 + (uint8)(p2 >> 24) * d2 + + (uint8)(p3 >> 24) * d3 + (uint8)(p4 >> 24) * d4; + + // destination RGBA pixel + *((uint32*)dstBits + (i * dstWidth) + j) + = (alpha << 24) | (red << 16) | (green << 8) | (blue); + } + } +} + + static void scale2x(const uint8* srcBits, uint8* dstBits, int32 srcWidth, int32 srcHeight, int32 srcBPR, int32 dstBPR) @@ -584,6 +649,39 @@ BIconUtils::ConvertFromCMAP8(const uint8* src, uint32 width, uint32 height, uint8* dst = (uint8*)result->Bits(); uint32 dstBPR = result->BytesPerRow(); + // check for integer multiple scale + if (dstWidth == 2 * width && dstHeight == 2 * height) { + // scale2x + BBitmap* converted = new BBitmap(BRect(0, 0, width - 1, height - 1), + result->ColorSpace()); + converted->ImportBits(src, height * srcBPR, srcBPR, 0, B_CMAP8); + uint8* convertedBits = (uint8*)converted->Bits(); + int32 convertedBPR = converted->BytesPerRow(); + scale2x(convertedBits, dst, width, height, convertedBPR, dstBPR); + delete converted; + return B_OK; + } else if (dstWidth == 3 * width && dstHeight == 3 * height) { + // scale3x + BBitmap* converted = new BBitmap(BRect(0, 0, width - 1, height - 1), + result->ColorSpace()); + converted->ImportBits(src, height * srcBPR, srcBPR, 0, B_CMAP8); + uint8* convertedBits = (uint8*)converted->Bits(); + int32 convertedBPR = converted->BytesPerRow(); + scale3x(convertedBits, dst, width, height, convertedBPR, dstBPR); + delete converted; + return B_OK; + } else if (dstWidth == 4 * width && dstHeight == 4 * height) { + // scale4x + BBitmap* converted = new BBitmap(BRect(0, 0, width - 1, height - 1), + result->ColorSpace()); + converted->ImportBits(src, height * srcBPR, srcBPR, 0, B_CMAP8); + uint8* convertedBits = (uint8*)converted->Bits(); + int32 convertedBPR = converted->BytesPerRow(); + scale4x(convertedBits, dst, width, height, convertedBPR, dstBPR); + delete converted; + return B_OK; + } + const rgb_color* colorMap = system_colors()->color_list; if (colorMap == NULL) return B_NO_INIT; @@ -591,6 +689,7 @@ BIconUtils::ConvertFromCMAP8(const uint8* src, uint32 width, uint32 height, const uint8* srcStart = src; uint8* dstStart = dst; + // convert from B_CMAP8 to B_RGB(A)32 without scaling for (uint32 y = 0; y < height; y++) { uint32* d = (uint32*)dst; const uint8* s = src; @@ -605,35 +704,45 @@ BIconUtils::ConvertFromCMAP8(const uint8* src, uint32 width, uint32 height, dst += dstBPR; } + if (width == dstWidth && height == dstHeight) + return B_OK; + // reset src and dst back to their original locations src = srcStart; dst = dstStart; - if ((dstWidth == 2 * width && dstHeight == 2 * height) - || (dstWidth == 3 * width && dstHeight == 3 * height) - || (dstWidth == 4 * width && dstHeight == 4 * height)) { - // we can do some special scaling here - - // first convert to B_RGBA32 - BBitmap* converted - = new BBitmap(BRect(0, 0, width - 1, height - 1), - result->ColorSpace()); - converted->ImportBits(src, height * srcBPR, srcBPR, 0, B_CMAP8); - uint8* convertedBits = (uint8*)converted->Bits(); - int32 convertedBPR = converted->BytesPerRow(); - - // scale using the scale2x/scale3x/scale4x algorithm - if (dstWidth == 2 * width && dstHeight == 2 * height) - scale2x(convertedBits, dst, width, height, convertedBPR, dstBPR); - else if (dstWidth == 3 * width && dstHeight == 3 * height) - scale3x(convertedBits, dst, width, height, convertedBPR, dstBPR); - else if (dstWidth == 4 * width && dstHeight == 4 * height) - scale4x(convertedBits, dst, width, height, convertedBPR, dstBPR); - - // cleanup - delete converted; + if (dstWidth > width && dstHeight > height + && dstWidth < 2 * width && dstHeight < 2 * height) { + // scale2x then downscale + BBitmap* temp = new BBitmap(BRect(0, 0, width * 2 - 1, height * 2 - 1), + result->ColorSpace()); + uint8* tempBits = (uint8*)temp->Bits(); + uint32 tempBPR = temp->BytesPerRow(); + scale2x(dst, tempBits, width, height, dstBPR, tempBPR); + scale_down(tempBits, dst, width * 2, height * 2, dstWidth, dstHeight); + delete temp; + } else if (dstWidth > 2 * width && dstHeight > 2 * height + && dstWidth < 3 * width && dstHeight < 3 * height) { + // scale3x then downscale + BBitmap* temp = new BBitmap(BRect(0, 0, width * 3 - 1, height * 3 - 1), + result->ColorSpace()); + uint8* tempBits = (uint8*)temp->Bits(); + uint32 tempBPR = temp->BytesPerRow(); + scale3x(dst, tempBits, width, height, dstBPR, tempBPR); + scale_down(tempBits, dst, width * 3, height * 3, dstWidth, dstHeight); + delete temp; + } else if (dstWidth > 3 * width && dstHeight > 3 * height + && dstWidth < 4 * width && dstHeight < 4 * height) { + // scale4x then downscale + BBitmap* temp = new BBitmap(BRect(0, 0, width * 4 - 1, height * 4 - 1), + result->ColorSpace()); + uint8* tempBits = (uint8*)temp->Bits(); + uint32 tempBPR = temp->BytesPerRow(); + scale4x(dst, tempBits, width, height, dstBPR, tempBPR); + scale_down(tempBits, dst, width * 3, height * 3, dstWidth, dstHeight); + delete temp; } else { - // bilinear scaling + // fall back to bilinear scaling scale_bilinear(dst, width, height, dstWidth, dstHeight, dstBPR); }