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.
This commit is contained in:
John Scipione 2013-03-04 04:45:06 -05:00
parent f3ac8bc089
commit b09c265cb4

View File

@ -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 <superstippi@gmx.de>
* Ingo Weinhold <bonefish@cs.tu-berlin.de>
* John Scipione <jscipione@gmail.com>
* 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);
}