/* * Copyright 2006-2023 Haiku, Inc. All rights reserved. * Distributed under the terms of the MIT License. * * Authors: * 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 */ #include "IconUtils.h" #include #include #include #include #include #include #include #include #include #include #include #include #include "AutoDeleter.h" #include "Icon.h" #include "IconRenderer.h" #include "FlatIconImporter.h" #include "MessageImporter.h" #define B_MINI_ICON_TYPE 'MICN' #define B_LARGE_ICON_TYPE 'ICON' _USING_ICON_NAMESPACE; // #pragma mark - Scaling functions static void scale_bilinear(uint8* bits, int32 srcWidth, int32 srcHeight, int32 dstWidth, int32 dstHeight, uint32 bpr) { // first pass: scale bottom to top uint8* dst = bits + (dstHeight - 1) * bpr; // offset to bottom left pixel in target size for (int32 x = 0; x < srcWidth; x++) { uint8* d = dst; for (int32 y = dstHeight - 1; y >= 0; y--) { int32 lineF = (y << 8) * (srcHeight - 1) / (dstHeight - 1); int32 lineI = lineF >> 8; uint8 weight = (uint8)(lineF & 0xff); uint8* s1 = bits + lineI * bpr + 4 * x; if (weight == 0) { d[0] = s1[0]; d[1] = s1[1]; d[2] = s1[2]; d[3] = s1[3]; } else { uint8* s2 = s1 + bpr; d[0] = (((s2[0] - s1[0]) * weight) + (s1[0] << 8)) >> 8; d[1] = (((s2[1] - s1[1]) * weight) + (s1[1] << 8)) >> 8; d[2] = (((s2[2] - s1[2]) * weight) + (s1[2] << 8)) >> 8; d[3] = (((s2[3] - s1[3]) * weight) + (s1[3] << 8)) >> 8; } d -= bpr; } dst += 4; } // second pass: scale right to left dst = bits + (dstWidth - 1) * 4; // offset to top left pixel in target size for (int32 y = 0; y < dstWidth; y++) { uint8* d = dst; for (int32 x = dstWidth - 1; x >= 0; x--) { int32 columnF = (x << 8) * (srcWidth - 1) / (dstWidth - 1); int32 columnI = columnF >> 8; uint8 weight = (uint8)(columnF & 0xff); uint8* s1 = bits + y * bpr + 4 * columnI; if (weight == 0) { d[0] = s1[0]; d[1] = s1[1]; d[2] = s1[2]; d[3] = s1[3]; } else { uint8* s2 = s1 + 4; d[0] = (((s2[0] - s1[0]) * weight) + (s1[0] << 8)) >> 8; d[1] = (((s2[1] - s1[1]) * weight) + (s1[1] << 8)) >> 8; d[2] = (((s2[2] - s1[2]) * weight) + (s1[2] << 8)) >> 8; d[3] = (((s2[3] - s1[3]) * weight) + (s1[3] << 8)) >> 8; } d -= 4; } dst += bpr; } } 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 rgb_color p1, p2, p3, p4; // nearby pixels rgb_color out; // 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 = *((rgb_color*)srcBits + (l * srcWidth) + c); p2 = *((rgb_color*)srcBits + (l * srcWidth) + c + 1); p3 = *((rgb_color*)srcBits + ((l + 1) * srcWidth) + c + 1); p4 = *((rgb_color*)srcBits + ((l + 1) * srcWidth) + c); // color components out.blue = (uint8)(p1.blue * d1 + p2.blue * d2 + p3.blue * d3 + p4.blue * d4); out.green = (uint8)(p1.green * d1 + p2.green * d2 + p3.green * d3 + p4.green * d4); out.red = (uint8)(p1.red * d1 + p2.red * d2 + p3.red * d3 + p4.red * d4); out.alpha = (uint8)(p1.alpha * d1 + p2.alpha * d2 + p3.alpha * d3 + p4.alpha * d4); // destination RGBA pixel *((rgb_color*)dstBits + (i * dstWidth) + j) = out; } } } static void scale2x(const uint8* srcBits, uint8* dstBits, int32 srcWidth, int32 srcHeight, int32 srcBPR, int32 dstBPR) { /* * This implements the AdvanceMAME Scale2x algorithm found on: * http://scale2x.sourceforge.net/ * * It is an incredibly simple and powerful image doubling routine that does * an astonishing job of doubling game graphic data while interpolating out * the jaggies. * * Derived from the (public domain) SDL version of the library by Pete * Shinners. */ // Assume that both src and dst are 4 BPP (B_RGBA32) for (int32 y = 0; y < srcHeight; ++y) { for (int32 x = 0; x < srcWidth; ++x) { uint32 b = *(uint32*)(srcBits + (MAX(0, y - 1) * srcBPR) + (4 * x)); uint32 d = *(uint32*)(srcBits + (y * srcBPR) + (4 * MAX(0, x - 1))); uint32 e = *(uint32*)(srcBits + (y * srcBPR) + (4 * x)); uint32 f = *(uint32*)(srcBits + (y * srcBPR) + (4 * MIN(srcWidth - 1, x + 1))); uint32 h = *(uint32*)(srcBits + (MIN(srcHeight - 1, y + 1) * srcBPR) + (4 * x)); uint32 e0 = d == b && b != f && d != h ? d : e; uint32 e1 = b == f && b != d && f != h ? f : e; uint32 e2 = d == h && d != b && h != f ? d : e; uint32 e3 = h == f && d != h && b != f ? f : e; *(uint32*)(dstBits + y * 2 * dstBPR + x * 2 * 4) = e0; *(uint32*)(dstBits + y * 2 * dstBPR + (x * 2 + 1) * 4) = e1; *(uint32*)(dstBits + (y * 2 + 1) * dstBPR + x * 2 * 4) = e2; *(uint32*)(dstBits + (y * 2 + 1) * dstBPR + (x * 2 + 1) * 4) = e3; } } } static void scale3x(const uint8* srcBits, uint8* dstBits, int32 srcWidth, int32 srcHeight, int32 srcBPR, int32 dstBPR) { /* * This implements the AdvanceMAME Scale3x algorithm found on: * http://scale2x.sourceforge.net/ * * It is an incredibly simple and powerful image tripling routine that does * an astonishing job of tripling game graphic data while interpolating out * the jaggies. * * Derived from the (public domain) SDL version of the library by Pete * Shinners. */ // Assume that both src and dst are 4 BPP (B_RGBA32) for (int32 y = 0; y < srcHeight; ++y) { for (int32 x = 0; x < srcWidth; ++x) { uint32 a = *(uint32*)(srcBits + (MAX(0, y - 1) * srcBPR) + (4 * MAX(0, x - 1))); uint32 b = *(uint32*)(srcBits + (MAX(0, y - 1) * srcBPR) + (4 * x)); uint32 c = *(uint32*)(srcBits + (MAX(0, y - 1) * srcBPR) + (4 * MIN(srcWidth - 1, x + 1))); uint32 d = *(uint32*)(srcBits + (y * srcBPR) + (4 * MAX(0, x - 1))); uint32 e = *(uint32*)(srcBits + (y * srcBPR) + (4 * x)); uint32 f = *(uint32*)(srcBits + (y * srcBPR) + (4 * MIN(srcWidth - 1,x + 1))); uint32 g = *(uint32*)(srcBits + (MIN(srcHeight - 1, y + 1) * srcBPR) + (4 * MAX(0, x - 1))); uint32 h = *(uint32*)(srcBits + (MIN(srcHeight - 1, y + 1) * srcBPR) + (4 * x)); uint32 i = *(uint32*)(srcBits + (MIN(srcHeight - 1, y + 1) * srcBPR) + (4 * MIN(srcWidth - 1, x + 1))); uint32 e0 = d == b && b != f && d != h ? d : e; uint32 e1 = (d == b && b != f && d != h && e != c) || (b == f && b != d && f != h && e != a) ? b : e; uint32 e2 = b == f && b != d && f != h ? f : e; uint32 e3 = (d == b && b != f && d != h && e != g) || (d == b && b != f && d != h && e != a) ? d : e; uint32 e4 = e; uint32 e5 = (b == f && b != d && f != h && e != i) || (h == f && d != h && b != f && e != c) ? f : e; uint32 e6 = d == h && d != b && h != f ? d : e; uint32 e7 = (d == h && d != b && h != f && e != i) || (h == f && d != h && b != f && e != g) ? h : e; uint32 e8 = h == f && d != h && b != f ? f : e; *(uint32*)(dstBits + y * 3 * dstBPR + x * 3 * 4) = e0; *(uint32*)(dstBits + y * 3 * dstBPR + (x * 3 + 1) * 4) = e1; *(uint32*)(dstBits + y * 3 * dstBPR + (x * 3 + 2) * 4) = e2; *(uint32*)(dstBits + (y * 3 + 1) * dstBPR + x * 3 * 4) = e3; *(uint32*)(dstBits + (y * 3 + 1) * dstBPR + (x * 3 + 1) * 4) = e4; *(uint32*)(dstBits + (y * 3 + 1) * dstBPR + (x * 3 + 2) * 4) = e5; *(uint32*)(dstBits + (y * 3 + 2) * dstBPR + x * 3 * 4) = e6; *(uint32*)(dstBits + (y * 3 + 2) * dstBPR + (x * 3 + 1) * 4) = e7; *(uint32*)(dstBits + (y * 3 + 2) * dstBPR + (x * 3 + 2) * 4) = e8; } } } static void scale4x(const uint8* srcBits, uint8* dstBits, int32 srcWidth, int32 srcHeight, int32 srcBPR, int32 dstBPR) { // scale4x is just scale2x twice BRect rect = BRect(0, 0, srcWidth * 2 - 1, srcHeight * 2 - 1); BBitmap* tmp = new BBitmap(rect, B_BITMAP_NO_SERVER_LINK, B_RGBA32); uint8* tmpBits = (uint8*)tmp->Bits(); int32 tmpBPR = tmp->BytesPerRow(); scale2x(srcBits, tmpBits, srcWidth, srcHeight, srcBPR, tmpBPR); scale2x(tmpBits, dstBits, srcWidth * 2, srcHeight * 2, tmpBPR, dstBPR); delete tmp; } // #pragma mark - GetIcon() status_t BIconUtils::GetIcon(BNode* node, const char* vectorIconAttrName, const char* smallIconAttrName, const char* largeIconAttrName, icon_size which, BBitmap* icon) { if (node == NULL || icon == NULL) return B_BAD_VALUE; status_t result = node->InitCheck(); if (result != B_OK) return result; result = icon->InitCheck(); if (result != B_OK) return result; switch (icon->ColorSpace()) { case B_RGBA32: case B_RGB32: // prefer vector icon result = GetVectorIcon(node, vectorIconAttrName, icon); if (result != B_OK) { // try to fallback to B_CMAP8 icons // (converting to B_RGBA32 is handled) // override size if (icon->Bounds().IntegerWidth() + 1 >= B_LARGE_ICON) which = B_LARGE_ICON; else which = B_MINI_ICON; result = GetCMAP8Icon(node, smallIconAttrName, largeIconAttrName, which, icon); } break; case B_CMAP8: // prefer old B_CMAP8 icons result = GetCMAP8Icon(node, smallIconAttrName, largeIconAttrName, which, icon); if (result != B_OK) { // try to fallback to vector icon BBitmap temp(icon->Bounds(), B_BITMAP_NO_SERVER_LINK, B_RGBA32); result = temp.InitCheck(); if (result != B_OK) break; result = GetVectorIcon(node, vectorIconAttrName, &temp); if (result != B_OK) break; uint32 width = temp.Bounds().IntegerWidth() + 1; uint32 height = temp.Bounds().IntegerHeight() + 1; uint32 bytesPerRow = temp.BytesPerRow(); result = ConvertToCMAP8((uint8*)temp.Bits(), width, height, bytesPerRow, icon); } break; default: printf("BIconUtils::GetIcon() - unsupported colorspace\n"); result = B_ERROR; break; } return result; } // #pragma mark - GetVectorIcon() status_t BIconUtils::GetVectorIcon(BNode* node, const char* attrName, BBitmap* icon) { if (node == NULL || attrName == NULL || *attrName == '\0' || icon == NULL) return B_BAD_VALUE; status_t result = node->InitCheck(); if (result != B_OK) return result; result = icon->InitCheck(); if (result != B_OK) return result; #if TIME_VECTOR_ICONS bigtime_t startTime = system_time(); #endif // get the attribute info and check type and size of the attr contents attr_info attrInfo; result = node->GetAttrInfo(attrName, &attrInfo); if (result != B_OK) return result; type_code attrType = B_VECTOR_ICON_TYPE; if (attrInfo.type != attrType) return B_BAD_TYPE; // chicken out on unrealisticly large attributes if (attrInfo.size > 512 * 1024) return B_BAD_VALUE; uint8* buffer = new(std::nothrow) uint8[attrInfo.size]; if (buffer == NULL) return B_NO_MEMORY; ArrayDeleter deleter(buffer); ssize_t bytesRead = node->ReadAttr(attrName, attrType, 0, buffer, attrInfo.size); if (bytesRead != attrInfo.size) return B_ERROR; #if TIME_VECTOR_ICONS bigtime_t importTime = system_time(); #endif result = GetVectorIcon(buffer, attrInfo.size, icon); if (result != B_OK) return result; #if TIME_VECTOR_ICONS bigtime_t finishTime = system_time(); printf("read: %lld, import: %lld\n", importTime - startTime, finishTime - importTime); #endif return B_OK; } status_t BIconUtils::GetVectorIcon(const uint8* buffer, size_t size, BBitmap* icon) { if (buffer == NULL || size <= 0 || icon == NULL) return B_BAD_VALUE; status_t result = icon->InitCheck(); if (result != B_OK) return result; BBitmap* temp = icon; ObjectDeleter deleter; if (icon->ColorSpace() != B_RGBA32 && icon->ColorSpace() != B_RGB32) { temp = new(std::nothrow) BBitmap(icon->Bounds(), B_BITMAP_NO_SERVER_LINK, B_RGBA32); deleter.SetTo(temp); if (temp == NULL || temp->InitCheck() != B_OK) return B_NO_MEMORY; } Icon vector; result = vector.InitCheck(); if (result != B_OK) return result; FlatIconImporter importer; result = importer.Import(&vector, const_cast(buffer), size); if (result != B_OK) { // try the message based format used by Icon-O-Matic MessageImporter messageImporter; BMemoryIO memoryIO(const_cast(buffer), size); result = messageImporter.Import(&vector, &memoryIO); if (result != B_OK) return result; } IconRenderer renderer(temp); renderer.SetIcon(&vector); renderer.SetScale((temp->Bounds().Width() + 1.0) / 64.0); renderer.Render(); if (temp != icon) { uint8* src = (uint8*)temp->Bits(); uint32 width = temp->Bounds().IntegerWidth() + 1; uint32 height = temp->Bounds().IntegerHeight() + 1; uint32 srcBPR = temp->BytesPerRow(); result = ConvertToCMAP8(src, width, height, srcBPR, icon); } // TODO: would be nice to get rid of this // (B_RGBA32_PREMULTIPLIED or better yet, new blending_mode) // NOTE: probably not necessary only because // transparent colors are "black" in all existing icons // lighter transparent colors should be too dark if // app_server uses correct blending //renderer.Demultiply(); return result; } // #pragma mark - GetCMAP8Icon() status_t BIconUtils::GetCMAP8Icon(BNode* node, const char* smallIconAttrName, const char* largeIconAttrName, icon_size which, BBitmap* icon) { // NOTE: this might be changed if other icon // sizes are supported in B_CMAP8 attributes, // but this is currently not the case, so we // relax the requirement to pass an icon // of just the right size if (which < B_LARGE_ICON) which = B_MINI_ICON; else which = B_LARGE_ICON; // check parameters and initialization if (node == NULL || icon == NULL || (which == B_MINI_ICON && (smallIconAttrName == NULL || *smallIconAttrName == '\0')) || (which == B_LARGE_ICON && (largeIconAttrName == NULL || *largeIconAttrName == '\0'))) { return B_BAD_VALUE; } status_t result; result = node->InitCheck(); if (result != B_OK) return result; result = icon->InitCheck(); if (result != B_OK) return result; // set some icon size related variables const char* attribute = NULL; BRect bounds; uint32 attrType = 0; off_t attrSize = 0; switch (which) { case B_MINI_ICON: attribute = smallIconAttrName; bounds.Set(0, 0, B_MINI_ICON - 1, B_MINI_ICON - 1); attrType = B_MINI_ICON_TYPE; attrSize = B_MINI_ICON * B_MINI_ICON; break; case B_LARGE_ICON: attribute = largeIconAttrName; bounds.Set(0, 0, B_LARGE_ICON - 1, B_LARGE_ICON - 1); attrType = B_LARGE_ICON_TYPE; attrSize = B_LARGE_ICON * B_LARGE_ICON; break; default: // can not happen, see above result = B_BAD_VALUE; break; } // get the attribute info and check type and size of the attr contents attr_info attrInfo; if (result == B_OK) result = node->GetAttrInfo(attribute, &attrInfo); if (result == B_OK && attrInfo.type != attrType) result = B_BAD_TYPE; if (result == B_OK && attrInfo.size != attrSize) result = B_BAD_DATA; // check parameters // currently, scaling B_CMAP8 icons is not supported if (icon->ColorSpace() == B_CMAP8 && icon->Bounds() != bounds) return B_BAD_VALUE; // read the attribute if (result == B_OK) { bool useBuffer = (icon->ColorSpace() != B_CMAP8 || icon->Bounds() != bounds); uint8* buffer = NULL; ssize_t bytesRead; if (useBuffer) { // other color space or bitmap size than stored in attribute buffer = new(std::nothrow) uint8[attrSize]; if (buffer == NULL) bytesRead = result = B_NO_MEMORY; else { bytesRead = node->ReadAttr(attribute, attrType, 0, buffer, attrSize); } } else { bytesRead = node->ReadAttr(attribute, attrType, 0, icon->Bits(), attrSize); } if (result == B_OK) { if (bytesRead < 0) result = (status_t)bytesRead; else if (bytesRead != (ssize_t)attrSize) result = B_ERROR; } if (useBuffer) { // other color space than stored in attribute if (result == B_OK) { result = ConvertFromCMAP8(buffer, (uint32)which, (uint32)which, (uint32)which, icon); } delete[] buffer; } } return result; } status_t BIconUtils::GetSystemIcon(const char* iconName, BBitmap* icon) { static BResources resources; static bool resourcesAreLoaded = false; if (!resourcesAreLoaded) { BPath path; status_t status = find_directory(B_SYSTEM_LIB_DIRECTORY, &path); if (status != B_OK) { return status; } path.Append("libbe.so"); BFile file; status = file.SetTo(path.Path(), B_READ_ONLY); if (status != B_OK) { return status; } status = resources.SetTo(&file); if (status != B_OK) { return status; } resourcesAreLoaded = true; } // Check the icon bitmap if (icon == NULL || icon->InitCheck() < B_OK) { return B_BAD_DATA; } // Load the raw icon data size_t size = 0; const uint8* rawIcon; // Try to load vector icon rawIcon = (const uint8*)resources.LoadResource(B_VECTOR_ICON_TYPE, iconName, &size); if (rawIcon != NULL && BIconUtils::GetVectorIcon(rawIcon, size, icon) == B_OK) { return B_OK; } // Fall back to bitmap icon rawIcon = (const uint8*)resources.LoadResource(B_LARGE_ICON_TYPE, iconName, &size); if (rawIcon == NULL) { delete icon; return B_ENTRY_NOT_FOUND; } // Handle color space conversion if (icon->ColorSpace() != B_CMAP8) { BIconUtils::ConvertFromCMAP8(rawIcon, B_LARGE_ICON, B_LARGE_ICON, B_LARGE_ICON, icon); } return B_OK; } // #pragma mark - ConvertFromCMAP8() and ConvertToCMAP8() status_t BIconUtils::ConvertFromCMAP8(BBitmap* source, BBitmap* destination) { if (source == NULL || source->ColorSpace() != B_CMAP8) return B_BAD_VALUE; status_t result = source->InitCheck(); if (result != B_OK) return result; result = destination->InitCheck(); if (result != B_OK) return result; uint8* src = (uint8*)source->Bits(); uint32 srcBPR = source->BytesPerRow(); uint32 width = source->Bounds().IntegerWidth() + 1; uint32 height = source->Bounds().IntegerHeight() + 1; return ConvertFromCMAP8(src, width, height, srcBPR, destination); } status_t BIconUtils::ConvertToCMAP8(BBitmap* source, BBitmap* destination) { if (source == NULL || source->ColorSpace() != B_RGBA32 || destination->ColorSpace() != B_CMAP8) { return B_BAD_VALUE; } status_t result = source->InitCheck(); if (result != B_OK) return result; result = destination->InitCheck(); if (result != B_OK) return result; uint8* src = (uint8*)source->Bits(); uint32 srcBPR = source->BytesPerRow(); uint32 width = source->Bounds().IntegerWidth() + 1; uint32 height = source->Bounds().IntegerHeight() + 1; return ConvertToCMAP8(src, width, height, srcBPR, destination); } status_t BIconUtils::ConvertFromCMAP8(const uint8* src, uint32 width, uint32 height, uint32 srcBPR, BBitmap* icon) { if (src == NULL || icon == NULL || srcBPR == 0) return B_BAD_VALUE; status_t result = icon->InitCheck(); if (result != B_OK) return result; if (icon->ColorSpace() != B_RGBA32 && icon->ColorSpace() != B_RGB32) { // TODO: support other color spaces return B_BAD_VALUE; } uint32 dstWidth = icon->Bounds().IntegerWidth() + 1; uint32 dstHeight = icon->Bounds().IntegerHeight() + 1; uint8* dst = (uint8*)icon->Bits(); uint32 dstBPR = icon->BytesPerRow(); // check for downscaling or integer multiple scaling if (dstWidth < width || dstHeight < height || (dstWidth == 2 * width && dstHeight == 2 * height) || (dstWidth == 3 * width && dstHeight == 3 * height) || (dstWidth == 4 * width && dstHeight == 4 * height)) { BRect rect = BRect(0, 0, width - 1, height - 1); BBitmap* converted = new(std::nothrow) BBitmap(rect, B_BITMAP_NO_SERVER_LINK, icon->ColorSpace()); if (converted == NULL) return B_NO_MEMORY; converted->ImportBits(src, height * srcBPR, srcBPR, 0, B_CMAP8); uint8* convertedBits = (uint8*)converted->Bits(); int32 convertedBPR = converted->BytesPerRow(); if (dstWidth < width || dstHeight < height) scale_down(convertedBits, dst, width, height, dstWidth, dstHeight); else 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); delete converted; return B_OK; } const rgb_color* colorMap = system_colors()->color_list; if (colorMap == NULL) return B_NO_INIT; 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; for (uint32 x = 0; x < width; x++, s++, d++) { const rgb_color c = colorMap[*s]; uint8 alpha = 0xff; if (*s == B_TRANSPARENT_MAGIC_CMAP8) alpha = 0; *d = (alpha << 24) | (c.red << 16) | (c.green << 8) | (c.blue); } src += srcBPR; 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 > width && dstHeight > height && dstWidth < 2 * width && dstHeight < 2 * height) { // scale2x then downscale BRect rect = BRect(0, 0, width * 2 - 1, height * 2 - 1); BBitmap* temp = new(std::nothrow) BBitmap(rect, B_BITMAP_NO_SERVER_LINK, icon->ColorSpace()); if (temp == NULL) return B_NO_MEMORY; 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 BRect rect = BRect(0, 0, width * 3 - 1, height * 3 - 1); BBitmap* temp = new BBitmap(rect, B_BITMAP_NO_SERVER_LINK, icon->ColorSpace()); if (temp == NULL) return B_NO_MEMORY; 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 BRect rect = BRect(0, 0, width * 4 - 1, height * 4 - 1); BBitmap* temp = new BBitmap(rect, B_BITMAP_NO_SERVER_LINK, icon->ColorSpace()); if (temp == NULL) return B_NO_MEMORY; 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 if (dstWidth > 4 * width && dstHeight > 4 * height) { // scale4x then bilinear BRect rect = BRect(0, 0, width * 4 - 1, height * 4 - 1); BBitmap* temp = new BBitmap(rect, B_BITMAP_NO_SERVER_LINK, icon->ColorSpace()); if (temp == NULL) return B_NO_MEMORY; uint8* tempBits = (uint8*)temp->Bits(); uint32 tempBPR = temp->BytesPerRow(); scale4x(dst, tempBits, width, height, dstBPR, tempBPR); icon->ImportBits(tempBits, height * tempBPR, tempBPR, 0, temp->ColorSpace()); scale_bilinear(dst, width, height, dstWidth, dstHeight, dstBPR); delete temp; } else { // fall back to bilinear scaling scale_bilinear(dst, width, height, dstWidth, dstHeight, dstBPR); } return B_OK; } status_t BIconUtils::ConvertToCMAP8(const uint8* src, uint32 width, uint32 height, uint32 srcBPR, BBitmap* icon) { if (src == NULL || icon == NULL || srcBPR == 0) return B_BAD_VALUE; status_t result = icon->InitCheck(); if (result != B_OK) return result; if (icon->ColorSpace() != B_CMAP8) return B_BAD_VALUE; uint32 dstWidth = icon->Bounds().IntegerWidth() + 1; uint32 dstHeight = icon->Bounds().IntegerHeight() + 1; if (dstWidth < width || dstHeight < height) { // TODO: down scaling return B_ERROR; } else if (dstWidth > width || dstHeight > height) { // TODO: up scaling // (currently copies bitmap into icon at left-top) memset(icon->Bits(), 255, icon->BitsLength()); } //#if __HAIKU__ // return icon->ImportBits(src, height * srcBPR, srcBPR, 0, B_RGBA32); //#else uint8* dst = (uint8*)icon->Bits(); uint32 dstBPR = icon->BytesPerRow(); const color_map* colorMap = system_colors(); if (colorMap == NULL) return B_NO_INIT; uint16 index; for (uint32 y = 0; y < height; y++) { uint8* d = dst; const uint8* s = src; for (uint32 x = 0; x < width; x++) { if (s[3] < 128) { *d = B_TRANSPARENT_MAGIC_CMAP8; } else { index = ((s[2] & 0xf8) << 7) | ((s[1] & 0xf8) << 2) | (s[0] >> 3); *d = colorMap->index_map[index]; } s += 4; d += 1; } src += srcBPR; dst += dstBPR; } return B_OK; //#endif // __HAIKU__ } // #pragma mark - Forbidden BIconUtils::BIconUtils() {} BIconUtils::~BIconUtils() {} BIconUtils::BIconUtils(const BIconUtils&) {} BIconUtils& BIconUtils::operator=(const BIconUtils&) { return *this; }