Fixed/implemented support for reading and writing true color ICO images.

git-svn-id: file:///srv/svn/repos/haiku/trunk/current@11295 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Axel Dörfler
2005-02-09 16:52:39 +00:00
parent db3e07c9e8
commit 36129be090
2 changed files with 103 additions and 35 deletions
+97 -33
View File
@@ -24,6 +24,9 @@
using namespace ICO; using namespace ICO;
static const rgba32_color kMagicTransparentColor = *(rgba32_color *)&B_TRANSPARENT_MAGIC_RGBA32;
class TempAllocator { class TempAllocator {
public: public:
TempAllocator() : fMemory(NULL) {} TempAllocator() : fMemory(NULL) {}
@@ -170,8 +173,15 @@ get_alpha_value(color_space space, uint32 value)
} }
static uint16
rgba32_color_to_16_bit_color(rgba32_color &color)
{
return ((color.blue >> 3) << 11) | ((color.green >> 2) << 5) | (color.red >> 3);
}
static int32 static int32
find_ico_color(ico_color *palette, int32 numColors, ico_color &color) find_rgba32_color(rgba32_color *palette, int32 numColors, rgba32_color &color)
{ {
// ToDo: sorting and binary search? // ToDo: sorting and binary search?
for (int32 i = 0; i < numColors; i++) { for (int32 i = 0; i < numColors; i++) {
@@ -183,18 +193,18 @@ find_ico_color(ico_color *palette, int32 numColors, ico_color &color)
} }
static inline ico_color static inline rgba32_color
get_ico_color_from_bits(TranslatorBitmap &bitsHeader, uint8 *data, int32 x, int32 y) get_rgba32_color_from_bits(TranslatorBitmap &bitsHeader, uint8 *data, int32 x, int32 y)
{ {
data += bitsHeader.rowBytes * y; data += bitsHeader.rowBytes * y;
switch (bitsHeader.colors) { switch (bitsHeader.colors) {
case B_RGBA32: case B_RGBA32:
return *(ico_color *)(data + 4 * x); return *(rgba32_color *)(data + 4 * x);
case B_RGB32: case B_RGB32:
default: default:
// stupid applications like ArtPaint use the alpha channel in B_RGB32 images... // stupid applications like ArtPaint use the alpha channel in B_RGB32 images...
ico_color color = *(ico_color *)(data + 4 * x); rgba32_color color = *(rgba32_color *)(data + 4 * x);
if (color.alpha >= 128) if (color.alpha >= 128)
color.alpha = 255; color.alpha = 255;
else else
@@ -206,15 +216,15 @@ get_ico_color_from_bits(TranslatorBitmap &bitsHeader, uint8 *data, int32 x, int3
static int32 static int32
fill_palette(TranslatorBitmap &bitsHeader, uint8 *data, ico_color *palette) fill_palette(TranslatorBitmap &bitsHeader, uint8 *data, rgba32_color *palette)
{ {
int32 numColors = 0; int32 numColors = 0;
for (int32 y = 0; y < bitsHeader.bounds.IntegerHeight() + 1; y++) { for (int32 y = 0; y < bitsHeader.bounds.IntegerHeight() + 1; y++) {
for (int32 x = 0; x < bitsHeader.bounds.IntegerWidth() + 1; x++) { for (int32 x = 0; x < bitsHeader.bounds.IntegerWidth() + 1; x++) {
ico_color color = get_ico_color_from_bits(bitsHeader, data, x, y); rgba32_color color = get_rgba32_color_from_bits(bitsHeader, data, x, y);
int32 index = find_ico_color(palette, numColors, color); int32 index = find_rgba32_color(palette, numColors, color);
if (index == -1) { if (index == -1) {
// add this color if there is space left // add this color if there is space left
if (numColors == 256) if (numColors == 256)
@@ -255,14 +265,16 @@ has_true_alpha_channel(color_space space, uint8 *data,
static status_t static status_t
convert_data_to_bits(ico_dir_entry &entry, ico_bitmap_header &header, convert_data_to_bits(ico_dir_entry &entry, ico_bitmap_header &header,
const uint32 *palette, BPositionIO &source, const rgba32_color *palette, BPositionIO &source,
BPositionIO &target) BPositionIO &target)
{ {
uint16 bitsPerPixel = header.bits_per_pixel; uint16 bitsPerPixel = header.bits_per_pixel;
// round row bytes to next 4 byte boundary // round row bytes to next 4 byte boundary
int32 xorRowBytes = get_bytes_per_row(entry.width, header.bits_per_pixel); int32 xorRowBytes = get_bytes_per_row(entry.width, header.bits_per_pixel);
int32 andRowBytes = get_bytes_per_row(entry.width, 1); int32 andRowBytes = 0;
if (bitsPerPixel != 32)
andRowBytes = get_bytes_per_row(entry.width, 1);
int32 outRowBytes = entry.width * 4; int32 outRowBytes = entry.width * 4;
// allocate buffers // allocate buffers
@@ -274,11 +286,14 @@ convert_data_to_bits(ico_dir_entry &entry, ico_bitmap_header &header,
return B_NO_MEMORY; return B_NO_MEMORY;
int32 andDataSize = andRowBytes * entry.height; int32 andDataSize = andRowBytes * entry.height;
uint8 *andData = (uint8 *)andAllocator.Allocate(andDataSize); uint8 *andData = NULL;
if (andData == NULL) if (bitsPerPixel != 32) {
return B_NO_MEMORY; andData = (uint8 *)andAllocator.Allocate(andDataSize);
if (andData == NULL)
return B_NO_MEMORY;
}
uint32 *outRowData = (uint32 *)rowAllocator.Allocate(outRowBytes); rgba32_color *outRowData = (rgba32_color *)rowAllocator.Allocate(outRowBytes);
if (outRowData == NULL) if (outRowData == NULL)
return B_NO_MEMORY; return B_NO_MEMORY;
@@ -286,9 +301,11 @@ convert_data_to_bits(ico_dir_entry &entry, ico_bitmap_header &header,
if (bytesRead != xorDataSize) if (bytesRead != xorDataSize)
return B_BAD_DATA; return B_BAD_DATA;
bytesRead = source.Read(andData, andDataSize); if (bitsPerPixel != 32) {
if (bytesRead != andDataSize) bytesRead = source.Read(andData, andDataSize);
return B_BAD_DATA; if (bytesRead != andDataSize)
return B_BAD_DATA;
}
for (uint32 row = 0; row < entry.height; row++) { for (uint32 row = 0; row < entry.height; row++) {
for (uint32 x = 0; x < entry.width; x++) { for (uint32 x = 0; x < entry.width; x++) {
@@ -311,16 +328,37 @@ convert_data_to_bits(ico_dir_entry &entry, ico_bitmap_header &header,
} }
outRowData[x] = palette[index]; outRowData[x] = palette[index];
} else } else {
outRowData[x] = ((uint32 *)line)[x]; switch (bitsPerPixel) {
case 16:
{
uint16 color = ((uint16 *)line)[x];
outRowData[x].blue = (color >> 11) << 3;
outRowData[x].green = ((color >> 5) & 0x3f) << 3;
outRowData[x].red = (color & 0x1f) << 3;
break;
}
case 24:
outRowData[x].red = ((rgba32_color *)line)[x].red;
outRowData[x].green = ((rgba32_color *)line)[x].green;
outRowData[x].blue = ((rgba32_color *)line)[x].blue;
break;
case 32:
outRowData[x] = ((rgba32_color *)line)[x];
break;
}
}
if (bitsPerPixel != 32) { if (bitsPerPixel != 32) {
// set alpha channel // set alpha channel
if (get_1_bit_per_pixel(get_data_row(andData, andDataSize, andRowBytes, row), x)) if (get_1_bit_per_pixel(get_data_row(andData, andDataSize, andRowBytes, row), x))
outRowData[x] = B_TRANSPARENT_MAGIC_RGBA32; outRowData[x] = kMagicTransparentColor;
else else
((ico_color *)&outRowData[x])->alpha = 255; outRowData[x].alpha = 255;
} } else if (outRowData[x].alpha == 0)
outRowData[x] = kMagicTransparentColor;
} }
ssize_t bytesWritten = target.Write(outRowData, outRowBytes); ssize_t bytesWritten = target.Write(outRowData, outRowBytes);
@@ -336,7 +374,7 @@ convert_data_to_bits(ico_dir_entry &entry, ico_bitmap_header &header,
static status_t static status_t
convert_bits_to_data(TranslatorBitmap &bitsHeader, uint8 *bitsData, ico_dir_entry &entry, convert_bits_to_data(TranslatorBitmap &bitsHeader, uint8 *bitsData, ico_dir_entry &entry,
ico_bitmap_header &header, ico_color *palette, BPositionIO &target) ico_bitmap_header &header, rgba32_color *palette, BPositionIO &target)
{ {
int32 bitsPerPixel = header.bits_per_pixel; int32 bitsPerPixel = header.bits_per_pixel;
@@ -361,10 +399,10 @@ convert_bits_to_data(TranslatorBitmap &bitsHeader, uint8 *bitsData, ico_dir_entr
for (uint32 row = entry.height; row-- > 0;) { for (uint32 row = entry.height; row-- > 0;) {
for (uint32 x = 0; x < entry.width; x++) { for (uint32 x = 0; x < entry.width; x++) {
ico_color color = get_ico_color_from_bits(bitsHeader, bitsData, x, row); rgba32_color color = get_rgba32_color_from_bits(bitsHeader, bitsData, x, row);
if (palette != NULL) { if (palette != NULL) {
uint8 index = find_ico_color(palette, numColors, color); uint8 index = find_rgba32_color(palette, numColors, color);
switch (bitsPerPixel) { switch (bitsPerPixel) {
case 1: case 1:
@@ -379,7 +417,30 @@ convert_bits_to_data(TranslatorBitmap &bitsHeader, uint8 *bitsData, ico_dir_entr
break; break;
} }
} else { } else {
// ToDo: true color modi switch (bitsPerPixel) {
default:
case 16:
{
uint16 *data = (uint16 *)xorRowData;
data[x] = rgba32_color_to_16_bit_color(color);
break;
}
case 24:
{
xorRowData[x * 3 + 0] = color.blue;
xorRowData[x * 3 + 1] = color.green;
xorRowData[x * 3 + 2] = color.red;
break;
}
case 32:
{
rgba32_color *data = (rgba32_color *)xorRowData;
data[x] = color;
break;
}
}
} }
} }
@@ -399,7 +460,7 @@ convert_bits_to_data(TranslatorBitmap &bitsHeader, uint8 *bitsData, ico_dir_entr
for (uint32 row = entry.height; row-- > 0;) { for (uint32 row = entry.height; row-- > 0;) {
for (uint32 x = 0; x < entry.width; x++) { for (uint32 x = 0; x < entry.width; x++) {
ico_color color = get_ico_color_from_bits(bitsHeader, bitsData, x, row); rgba32_color color = get_rgba32_color_from_bits(bitsHeader, bitsData, x, row);
bool transparent = *(uint32 *)&color == B_TRANSPARENT_MAGIC_RGBA32 || color.alpha == 0; bool transparent = *(uint32 *)&color == B_TRANSPARENT_MAGIC_RGBA32 || color.alpha == 0;
set_1_bit_per_pixel(andRowData, x, transparent ? 1 : 0); set_1_bit_per_pixel(andRowData, x, transparent ? 1 : 0);
@@ -468,6 +529,9 @@ ICO::identify(BPositionIO &stream, int32 &bitsPerPixel)
} }
/** Converts an ICO image of any type into a B_RGBA32 B_TRANSLATOR_BITMAP.
*/
status_t status_t
ICO::convert_ico_to_bits(BPositionIO &source, BPositionIO &target) ICO::convert_ico_to_bits(BPositionIO &source, BPositionIO &target)
{ {
@@ -519,14 +583,14 @@ ICO::convert_ico_to_bits(BPositionIO &source, BPositionIO &target)
if (bitmapHeader.bits_per_pixel <= 8) if (bitmapHeader.bits_per_pixel <= 8)
numColors = 1L << bitmapHeader.bits_per_pixel; numColors = 1L << bitmapHeader.bits_per_pixel;
uint32 palette[256]; rgba32_color palette[256];
if (numColors > 0) { if (numColors > 0) {
if (source.Read(palette, numColors * 4) != numColors * 4) if (source.Read(palette, numColors * 4) != numColors * 4)
return B_BAD_VALUE; return B_BAD_VALUE;
// clear alpha channel // clear alpha channel (it's not used in ICO color information)
for (int32 i = 0; i < numColors; i++) for (int32 i = 0; i < numColors; i++)
palette[i] &= 0xffffff; palette[i].alpha = 0;
} }
// write out Be's Bitmap header // write out Be's Bitmap header
@@ -577,7 +641,7 @@ ICO::convert_bits_to_ico(BPositionIO &source, TranslatorBitmap &bitsHeader, BPos
if (bytesRead < B_OK) if (bytesRead < B_OK)
return bytesRead; return bytesRead;
ico_color palette[256]; rgba32_color palette[256];
if (bitsPerPixel > 8) { if (bitsPerPixel > 8) {
// it's a non-palette mode - but does it have to be? // it's a non-palette mode - but does it have to be?
if (bitsHeader.colors != B_RGBA32 if (bitsHeader.colors != B_RGBA32
@@ -627,7 +691,7 @@ ICO::convert_bits_to_ico(BPositionIO &source, TranslatorBitmap &bitsHeader, BPos
entry.size = sizeof(ico_bitmap_header) + width * (xorRowBytes + andRowBytes); entry.size = sizeof(ico_bitmap_header) + width * (xorRowBytes + andRowBytes);
if (bitsPerPixel <= 8) if (bitsPerPixel <= 8)
entry.size += numColors * sizeof(ico_color); entry.size += numColors * sizeof(rgba32_color);
entry.offset = sizeof(ico_header) + sizeof(ico_dir_entry); entry.offset = sizeof(ico_header) + sizeof(ico_dir_entry);
entry.reserved = 0; entry.reserved = 0;
@@ -658,7 +722,7 @@ ICO::convert_bits_to_ico(BPositionIO &source, TranslatorBitmap &bitsHeader, BPos
bitmapHeader.SwapToHost(); bitmapHeader.SwapToHost();
if (bitsPerPixel <= 8) { if (bitsPerPixel <= 8) {
bytesWritten = target.Write(palette, numColors * sizeof(ico_color)); bytesWritten = target.Write(palette, numColors * sizeof(rgba32_color));
if (bytesWritten < B_OK) if (bytesWritten < B_OK)
return bytesWritten; return bytesWritten;
} }
+6 -2
View File
@@ -59,14 +59,18 @@ struct ico_bitmap_header {
void SwapFromHost(); void SwapFromHost();
} _PACKED; } _PACKED;
struct ico_color {
// More or less accidently, ICO colors are in the same format as
// the color information in B_RGBA32 bitmaps
struct rgba32_color {
uint8 blue; uint8 blue;
uint8 green; uint8 green;
uint8 red; uint8 red;
uint8 alpha; uint8 alpha;
inline bool inline bool
operator==(const ico_color& other) const operator==(const rgba32_color& other) const
{ {
return red == other.red && green == other.green && blue == other.blue; return red == other.red && green == other.green && blue == other.blue;
} }