xref: /haiku/src/add-ons/translators/ico/ICO.cpp (revision 02354704729d38c3b078c696adc1bbbd33cbcf72)
1 /*
2  * Copyright 2005, Axel Dörfler, axeld@pinc-software.de. All rights reserved.
3  * Distributed under the terms of the MIT License.
4  */
5 
6 // ToDo: This definitely needs to be worked over for endian issues
7 
8 #include "ICO.h"
9 #include "ICOTranslator.h"
10 
11 #include <ByteOrder.h>
12 
13 #include <stdio.h>
14 #include <stdlib.h>
15 #include <string.h>
16 
17 
18 //#define TRACE_ICO
19 #ifdef TRACE_ICO
20 #	define TRACE(x) printf x
21 #else
22 #	define TRACE(x) ;
23 #endif
24 
25 
26 using namespace ICO;
27 
28 
29 static const rgba32_color kMagicTransparentColor = *(rgba32_color *)&B_TRANSPARENT_MAGIC_RGBA32;
30 
31 
32 class TempAllocator {
33 	public:
34 		TempAllocator() : fMemory(NULL) {}
35 		~TempAllocator() { free(fMemory); }
36 
37 		void *Allocate(size_t size) { return fMemory = malloc(size); }
38 
39 	private:
40 		void	*fMemory;
41 };
42 
43 
44 bool
45 ico_header::IsValid() const
46 {
47 	return reserved == 0
48 		&& (type == kTypeIcon || type == kTypeCursor)
49 		&& entry_count < 32;
50 }
51 
52 
53 void
54 ico_header::SwapToHost()
55 {
56 	swap_data(B_UINT16_TYPE, this, sizeof(ico_header), B_SWAP_LENDIAN_TO_HOST);
57 }
58 
59 
60 void
61 ico_header::SwapFromHost()
62 {
63 	swap_data(B_UINT16_TYPE, this, sizeof(ico_header), B_SWAP_HOST_TO_LENDIAN);
64 }
65 
66 
67 //	#pragma mark -
68 
69 
70 void
71 ico_dir_entry::SwapToHost()
72 {
73 	swap_data(B_UINT16_TYPE, &planes, sizeof(uint16) * 2, B_SWAP_LENDIAN_TO_HOST);
74 	swap_data(B_UINT32_TYPE, &size, sizeof(uint32) * 2, B_SWAP_LENDIAN_TO_HOST);
75 }
76 
77 
78 void
79 ico_dir_entry::SwapFromHost()
80 {
81 	swap_data(B_UINT16_TYPE, &planes, sizeof(uint16) * 2, B_SWAP_HOST_TO_LENDIAN);
82 	swap_data(B_UINT32_TYPE, &size, sizeof(uint32) * 2, B_SWAP_HOST_TO_LENDIAN);
83 }
84 
85 
86 //	#pragma mark -
87 
88 
89 bool
90 ico_bitmap_header::IsValid() const
91 {
92 	return size == sizeof(ico_bitmap_header) && compression == 0
93 		&& (bits_per_pixel == 1 || bits_per_pixel == 4 || bits_per_pixel == 8
94 			|| bits_per_pixel == 16 || bits_per_pixel == 24 || bits_per_pixel == 32);
95 }
96 
97 
98 void
99 ico_bitmap_header::SwapToHost()
100 {
101 	swap_data(B_UINT32_TYPE, &size, sizeof(uint32) * 3, B_SWAP_LENDIAN_TO_HOST);
102 	swap_data(B_UINT16_TYPE, &planes, sizeof(uint16) * 2, B_SWAP_LENDIAN_TO_HOST);
103 	swap_data(B_UINT32_TYPE, &compression, sizeof(uint32) * 6, B_SWAP_LENDIAN_TO_HOST);
104 }
105 
106 
107 void
108 ico_bitmap_header::SwapFromHost()
109 {
110 	swap_data(B_UINT32_TYPE, &size, sizeof(uint32) * 3, B_SWAP_HOST_TO_LENDIAN);
111 	swap_data(B_UINT16_TYPE, &planes, sizeof(uint16) * 2, B_SWAP_HOST_TO_LENDIAN);
112 	swap_data(B_UINT32_TYPE, &compression, sizeof(uint32) * 6, B_SWAP_HOST_TO_LENDIAN);
113 }
114 
115 
116 //	#pragma mark -
117 
118 
119 static inline uint8 *
120 get_data_row(uint8 *data, int32 dataSize, int32 rowBytes, int32 row)
121 {
122 	return data + dataSize - (row + 1) * rowBytes;
123 }
124 
125 
126 static inline int32
127 get_bytes_per_row(int32 width, int32 bitsPerPixel)
128 {
129 	return (((bitsPerPixel * width + 7) / 8) + 3) & ~3;
130 }
131 
132 
133 static inline void
134 set_1_bit_per_pixel(uint8 *line, int32 x, int32 value)
135 {
136 	int32 mask = 1 << (7 - (x & 7));
137 	if (value)
138 		line[x / 8] |= mask;
139 	else
140 		line[x / 8] &= ~mask;
141 }
142 
143 
144 static inline void
145 set_4_bits_per_pixel(uint8 *line, int32 x, int32 value)
146 {
147 	int32 shift = x & 1 ? 0 : 4;
148 	int32 mask = ~0L & (0xf0 >> shift);
149 
150 	line[x / 2] &= mask;
151 	line[x / 2] |= value << shift;
152 }
153 
154 
155 static inline int32
156 get_1_bit_per_pixel(uint8 *line, int32 x)
157 {
158 	return (line[x / 8] >> (7 - (x & 7))) & 1;
159 }
160 
161 
162 static inline int32
163 get_4_bits_per_pixel(uint8 *line, int32 x)
164 {
165 	return (line[x / 2] >> (4 * ((x + 1) & 1))) & 0xf;
166 }
167 
168 
169 static uint8
170 get_alpha_value(color_space space, uint32 value)
171 {
172 	// ToDo: support more color spaces
173 	if (space == B_RGBA32)
174 		return value >> 24;
175 
176 	return 0;
177 }
178 
179 
180 static uint16
181 rgba32_color_to_16_bit_color(rgba32_color &color)
182 {
183 	return ((color.blue >> 3) << 11) | ((color.green >> 2) << 5) | (color.red >> 3);
184 }
185 
186 
187 static int32
188 find_rgba32_color(rgba32_color *palette, int32 numColors, rgba32_color &color)
189 {
190 	// ToDo: sorting and binary search?
191 	for (int32 i = 0; i < numColors; i++) {
192 		if (palette[i] == color)
193 			return i;
194 	}
195 
196 	return -1;
197 }
198 
199 
200 static inline rgba32_color
201 get_rgba32_color_from_bits(TranslatorBitmap &bitsHeader, uint8 *data, int32 x, int32 y)
202 {
203 	data += bitsHeader.rowBytes * y;
204 
205 	switch (bitsHeader.colors) {
206 		case B_RGBA32:
207 			return *(rgba32_color *)(data + 4 * x);
208 		case B_RGB32:
209 		default:
210 			// stupid applications like ArtPaint use the alpha channel in B_RGB32 images...
211 			rgba32_color color = *(rgba32_color *)(data + 4 * x);
212 			if (color.alpha >= 128)
213 				color.alpha = 255;
214 			else
215 				color.alpha = 0;
216 			return color;
217 		// ToDo: support some more color spaces...
218 	}
219 }
220 
221 
222 static int32
223 fill_palette(TranslatorBitmap &bitsHeader, uint8 *data, rgba32_color *palette)
224 {
225 	int32 numColors = 0;
226 
227 	for (int32 y = 0; y < bitsHeader.bounds.IntegerHeight() + 1; y++) {
228 		for (int32 x = 0; x < bitsHeader.bounds.IntegerWidth() + 1; x++) {
229 			rgba32_color color = get_rgba32_color_from_bits(bitsHeader, data, x, y);
230 
231 			int32 index = find_rgba32_color(palette, numColors, color);
232 			if (index == -1) {
233 				// add this color if there is space left
234 				if (numColors == 256)
235 					return -1;
236 
237 				color.alpha = 0;
238 					// the alpha channel is actually unused
239 				palette[numColors++] = color;
240 			}
241 		}
242 	}
243 
244 	return numColors;
245 }
246 
247 
248 /**	This function is used to determine, if a true alpha channel has to
249  *	be used in order to preserve all information.
250  */
251 
252 static bool
253 has_true_alpha_channel(color_space space, uint8 *data,
254 	int32 width, int32 height, int32 bytesPerRow)
255 {
256 	for (int32 y = 0; y < height; y++) {
257 		for (int32 x = 0; x < width; x++) {
258 			uint8 value = get_alpha_value(space, ((uint32 *)data)[x]);
259 			if (value != 0 && value != 255)
260 				return true;
261 		}
262 
263 		data += bytesPerRow;
264 	}
265 
266 	return false;
267 }
268 
269 
270 static status_t
271 convert_data_to_bits(ico_dir_entry &entry, ico_bitmap_header &header,
272 	const rgba32_color *palette, BPositionIO &source,
273 	BPositionIO &target)
274 {
275 	uint16 bitsPerPixel = header.bits_per_pixel;
276 
277 	// round row bytes to next 4 byte boundary
278 	int32 xorRowBytes = get_bytes_per_row(entry.width, header.bits_per_pixel);
279 	int32 andRowBytes = 0;
280 	if (bitsPerPixel != 32)
281 		andRowBytes = get_bytes_per_row(entry.width, 1);
282 	int32 outRowBytes = entry.width * 4;
283 
284 	// allocate buffers
285 	TempAllocator xorAllocator, andAllocator, rowAllocator;
286 
287 	int32 xorDataSize = xorRowBytes * entry.height;
288 	uint8 *xorData = (uint8 *)xorAllocator.Allocate(xorDataSize);
289 	if (xorData == NULL)
290 		return B_NO_MEMORY;
291 
292 	int32 andDataSize = andRowBytes * entry.height;
293 	uint8 *andData = NULL;
294 	if (bitsPerPixel != 32) {
295 		andData = (uint8 *)andAllocator.Allocate(andDataSize);
296 		if (andData == NULL)
297 			return B_NO_MEMORY;
298 	}
299 
300 	rgba32_color *outRowData = (rgba32_color *)rowAllocator.Allocate(outRowBytes);
301 	if (outRowData == NULL)
302 		return B_NO_MEMORY;
303 
304 	ssize_t bytesRead = source.Read(xorData, xorDataSize);
305 	if (bytesRead != xorDataSize)
306 		return B_BAD_DATA;
307 
308 	if (bitsPerPixel != 32) {
309 		bytesRead = source.Read(andData, andDataSize);
310 		if (bytesRead != andDataSize) {
311 			// reading the alpha channel failed, so we're ignoring it
312 			// (but we're still able to show the image data)
313 			andData = NULL;
314 		}
315 	}
316 
317 	for (uint32 row = 0; row < entry.height; row++) {
318 		for (uint32 x = 0; x < entry.width; x++) {
319 			uint8 *line = get_data_row(xorData, xorDataSize, xorRowBytes, row);
320 
321 			if (palette != NULL) {
322 				uint8 index;
323 
324 				switch (bitsPerPixel) {
325 					case 1:
326 						index = get_1_bit_per_pixel(line, x);
327 						break;
328 					case 4:
329 						index = get_4_bits_per_pixel(line, x);
330 						break;
331 					case 8:
332 					default:
333 						index = line[x];
334 						break;
335 				}
336 
337 				outRowData[x] = palette[index];
338 			} else {
339 				switch (bitsPerPixel) {
340 					case 16:
341 					{
342 						uint16 color = ((uint16 *)line)[x];
343 						outRowData[x].blue = (color >> 11) << 3;
344 						outRowData[x].green = ((color >> 5) & 0x3f) << 3;
345 						outRowData[x].red = (color & 0x1f) << 3;
346 						break;
347 					}
348 
349 					case 24:
350 						outRowData[x].blue = line[x * 3 + 0];
351 						outRowData[x].green = line[x * 3 + 1];
352 						outRowData[x].red = line[x * 3 + 2];
353 						break;
354 
355 					case 32:
356 						outRowData[x] = ((rgba32_color *)line)[x];
357 						break;
358 				}
359 			}
360 
361 			if (bitsPerPixel != 32) {
362 				// set alpha channel
363 				if (andData != NULL
364 					&& get_1_bit_per_pixel(get_data_row(andData, andDataSize, andRowBytes, row), x))
365 					outRowData[x] = kMagicTransparentColor;
366 				else
367 					outRowData[x].alpha = 255;
368 			} else if (outRowData[x].alpha == 0)
369 				outRowData[x] = kMagicTransparentColor;
370 		}
371 
372 		ssize_t bytesWritten = target.Write(outRowData, outRowBytes);
373 		if (bytesWritten < B_OK)
374 			return bytesWritten;
375 		if (bytesWritten != outRowBytes)
376 			return B_IO_ERROR;
377 	}
378 
379 	return B_OK;
380 }
381 
382 
383 static status_t
384 convert_bits_to_data(TranslatorBitmap &bitsHeader, uint8 *bitsData, ico_dir_entry &entry,
385 	ico_bitmap_header &header, rgba32_color *palette, BPositionIO &target)
386 {
387 	int32 bitsPerPixel = header.bits_per_pixel;
388 
389 	// round row bytes to next 4 byte boundary
390 	int32 xorRowBytes = get_bytes_per_row(entry.width, bitsPerPixel);
391 	int32 andRowBytes = get_bytes_per_row(entry.width, 1);
392 
393 	TempAllocator xorAllocator, andAllocator;
394 
395 	uint8 *xorRowData = (uint8 *)xorAllocator.Allocate(xorRowBytes);
396 	if (xorRowData == NULL)
397 		return B_NO_MEMORY;
398 
399 	uint8 *andRowData = (uint8 *)andAllocator.Allocate(andRowBytes);
400 	if (andRowData == NULL)
401 		return B_NO_MEMORY;
402 
403 	int32 numColors = 1 << bitsPerPixel;
404 
405 	// write XOR data (the actual image data)
406 	// (ICO data is upside down, so we're starting at the last line)
407 
408 	for (uint32 row = entry.height; row-- > 0;) {
409 		for (uint32 x = 0; x < entry.width; x++) {
410 			rgba32_color color = get_rgba32_color_from_bits(bitsHeader, bitsData, x, row);
411 
412 			if (palette != NULL) {
413 				uint8 index = find_rgba32_color(palette, numColors, color);
414 
415 				switch (bitsPerPixel) {
416 					case 1:
417 						set_1_bit_per_pixel(xorRowData, x, index);
418 						break;
419 					case 4:
420 						set_4_bits_per_pixel(xorRowData, x, index);
421 						break;
422 					case 8:
423 					default:
424 						xorRowData[x] = index;
425 						break;
426 				}
427 			} else {
428 				switch (bitsPerPixel) {
429 					default:
430 					case 16:
431 					{
432 						uint16 *data = (uint16 *)xorRowData;
433 						data[x] = rgba32_color_to_16_bit_color(color);
434 						break;
435 					}
436 
437 					case 24:
438 					{
439 						xorRowData[x * 3 + 0] = color.blue;
440 						xorRowData[x * 3 + 1] = color.green;
441 						xorRowData[x * 3 + 2] = color.red;
442 						break;
443 					}
444 
445 					case 32:
446 					{
447 						rgba32_color *data = (rgba32_color *)xorRowData;
448 						data[x] = color;
449 						break;
450 					}
451 				}
452 			}
453 		}
454 
455 		ssize_t bytesWritten = target.Write(xorRowData, xorRowBytes);
456 		if (bytesWritten < B_OK)
457 			return bytesWritten;
458 		if (bytesWritten != xorRowBytes)
459 			return B_IO_ERROR;
460 	}
461 
462 	if (bitsPerPixel == 32) {
463 		// the alpha channel has already been written with the image data
464 		return B_OK;
465 	}
466 
467 	// write AND data (the transparency bit)
468 
469 	for (uint32 row = entry.height; row-- > 0;) {
470 		for (uint32 x = 0; x < entry.width; x++) {
471 			rgba32_color color = get_rgba32_color_from_bits(bitsHeader, bitsData, x, row);
472 			bool transparent = *(uint32 *)&color == B_TRANSPARENT_MAGIC_RGBA32 || color.alpha == 0;
473 
474 			set_1_bit_per_pixel(andRowData, x, transparent ? 1 : 0);
475 		}
476 
477 		ssize_t bytesWritten = target.Write(andRowData, andRowBytes);
478 		if (bytesWritten < B_OK)
479 			return bytesWritten;
480 		if (bytesWritten != andRowBytes)
481 			return B_IO_ERROR;
482 	}
483 
484 	return B_OK;
485 }
486 
487 
488 //	#pragma mark -
489 
490 
491 bool
492 ICO::is_valid_size(int32 size)
493 {
494 	return size == 16 || size == 32 || size == 48;
495 }
496 
497 
498 status_t
499 ICO::identify(BMessage *settings, BPositionIO &stream, uint8 &type, int32 &bitsPerPixel)
500 {
501 	// read in the header
502 
503 	ico_header header;
504 	if (stream.Read(&header, sizeof(ico_header)) != (ssize_t)sizeof(ico_header))
505 		return B_BAD_VALUE;
506 
507 	header.SwapToHost();
508 
509 	// check header
510 
511 	if (!header.IsValid())
512 		return B_BAD_VALUE;
513 
514 	int32 iconIndex = 0;
515 	type = header.type;
516 
517 	if (settings) {
518 		// Add page count to ioExtension
519 		settings->RemoveName(kDocumentCount);
520 		settings->AddInt32(kDocumentCount, header.entry_count);
521 
522 		// Check if a document index has been specified
523 		if (settings->FindInt32(kDocumentIndex, &iconIndex) == B_OK)
524 			iconIndex--;
525 		else
526 			iconIndex = 0;
527 
528 		if (iconIndex < 0 || iconIndex >= header.entry_count)
529 			return B_NO_TRANSLATOR;
530 	}
531 
532 	TRACE(("iconIndex = %ld, count = %ld\n", iconIndex, header.entry_count));
533 
534 	// read in directory entries
535 
536 	for (uint32 i = 0; i < header.entry_count; i++) {
537 		ico_dir_entry entry;
538 		if (stream.Read(&entry, sizeof(ico_dir_entry)) != (ssize_t)sizeof(ico_dir_entry))
539 			return B_BAD_VALUE;
540 
541 		entry.SwapToHost();
542 		TRACE(("width: %d, height: %d, planes: %d, color_count: %d, bits_per_pixel: %d, size: %ld, offset: %ld\n",
543 			entry.width, entry.height, entry.planes, entry.color_count, entry.bits_per_pixel,
544 			entry.size, entry.offset));
545 
546 		ico_bitmap_header bitmapHeader;
547 		if (stream.ReadAt(entry.offset, &bitmapHeader, sizeof(ico_bitmap_header)) != (ssize_t)sizeof(ico_bitmap_header))
548 			return B_BAD_VALUE;
549 
550 		bitmapHeader.SwapToHost();
551 		TRACE(("size: %ld, width: %ld, height: %ld, bits_per_pixel: %d, x/y per meter: %ld:%ld, compression: %ld, image_size: %ld, colors used: %ld, important colors: %ld\n",
552 			bitmapHeader.size, bitmapHeader.width, bitmapHeader.height, bitmapHeader.bits_per_pixel,
553 			bitmapHeader.x_pixels_per_meter, bitmapHeader.y_pixels_per_meter,
554 			bitmapHeader.compression, bitmapHeader.image_size, bitmapHeader.colors_used,
555 			bitmapHeader.important_colors));
556 
557 		if (!bitmapHeader.IsValid())
558 			return B_BAD_VALUE;
559 
560 		if ((uint32)iconIndex == i)
561 			bitsPerPixel = bitmapHeader.bits_per_pixel;
562 	}
563 
564 	return B_OK;
565 }
566 
567 
568 /**	Converts an ICO image of any type into a B_RGBA32 B_TRANSLATOR_BITMAP.
569  */
570 
571 status_t
572 ICO::convert_ico_to_bits(BMessage *settings, BPositionIO &source, BPositionIO &target)
573 {
574 	ico_header header;
575 	if (source.Read(&header, sizeof(ico_header)) != (ssize_t)sizeof(ico_header))
576 		return B_BAD_VALUE;
577 
578 	header.SwapToHost();
579 
580 	// check header
581 
582 	if (!header.IsValid())
583 		return B_BAD_VALUE;
584 
585 	int32 iconIndex = 0;
586 
587 	if (settings) {
588 		// Check if a document index has been specified
589 		if (settings->FindInt32(kDocumentIndex, &iconIndex) == B_OK)
590 			iconIndex--;
591 		else
592 			iconIndex = 0;
593 
594 		if (iconIndex < 0 || iconIndex >= header.entry_count)
595 			return B_BAD_VALUE;
596 	}
597 
598 	// read in selected entry
599 
600 	ico_dir_entry entry;
601 	if (source.ReadAt(sizeof(ico_header) + sizeof(ico_dir_entry) * iconIndex,
602 			&entry, sizeof(ico_dir_entry)) != (ssize_t)sizeof(ico_dir_entry))
603 		return B_BAD_VALUE;
604 
605 	entry.SwapToHost();
606 	source.Seek(entry.offset, SEEK_SET);
607 
608 	ico_bitmap_header bitmapHeader;
609 	if (source.Read(&bitmapHeader, sizeof(ico_bitmap_header)) != (ssize_t)sizeof(ico_bitmap_header))
610 		return B_BAD_VALUE;
611 
612 	bitmapHeader.SwapToHost();
613 
614 	if (!bitmapHeader.IsValid())
615 		return B_BAD_VALUE;
616 
617 	if (bitmapHeader.compression != 0)
618 		return EOPNOTSUPP;
619 
620 	int32 numColors = 0;
621 	if (bitmapHeader.bits_per_pixel <= 8)
622 		numColors = 1L << bitmapHeader.bits_per_pixel;
623 
624 	// This is a work-around for a broken ICO file writer that publishes
625 	// a wrong image height in the ico_dir_entry structure
626 	if (entry.size != 0 && 2 * entry.width == entry.height && numColors != 0
627 		&& sizeof(rgba32_color) * numColors + entry.width * entry.height > entry.size)
628 		entry.height = entry.width;
629 
630 	TranslatorBitmap bitsHeader;
631 	bitsHeader.magic = B_TRANSLATOR_BITMAP;
632 	bitsHeader.bounds.left = 0;
633 	bitsHeader.bounds.top = 0;
634 	bitsHeader.bounds.right = entry.width - 1;
635 	bitsHeader.bounds.bottom = entry.height - 1;
636 	bitsHeader.bounds.Set(0, 0, entry.width - 1, entry.height - 1);
637 	bitsHeader.rowBytes = entry.width * 4;
638 	bitsHeader.colors = B_RGBA32;
639 	bitsHeader.dataSize = bitsHeader.rowBytes * entry.height;
640 
641 	// read in palette
642 
643 	rgba32_color palette[256];
644 	if (numColors > 0) {
645 		if (source.Read(palette, numColors * 4) != numColors * 4)
646 			return B_BAD_VALUE;
647 
648 		// clear alpha channel (it's not used in ICO color information)
649 		for (int32 i = 0; i < numColors; i++)
650 			palette[i].alpha = 0;
651 	}
652 
653 	// write out Be's Bitmap header
654 	swap_data(B_UINT32_TYPE, &bitsHeader, sizeof(TranslatorBitmap), B_SWAP_HOST_TO_BENDIAN);
655 	target.Write(&bitsHeader, sizeof(TranslatorBitmap));
656 
657 	return convert_data_to_bits(entry, bitmapHeader, numColors > 0 ? palette : NULL, source, target);
658 }
659 
660 
661 status_t
662 ICO::convert_bits_to_ico(BMessage *settings, BPositionIO &source,
663 	TranslatorBitmap &bitsHeader, BPositionIO &target)
664 {
665 	int32 width = bitsHeader.bounds.IntegerWidth() + 1;
666 	int32 height = bitsHeader.bounds.IntegerHeight() + 1;
667 	if (!is_valid_size(width) || !is_valid_size(height))
668 		return B_BAD_VALUE;
669 
670 	int32 bitsPerPixel;
671 	switch (bitsHeader.colors) {
672 		case B_RGBA32:
673 			bitsPerPixel = 32;
674 			break;
675 		case B_RGB32:
676 			bitsPerPixel = 24;
677 			break;
678 		case B_RGB16:
679 			bitsPerPixel = 16;
680 			break;
681 		case B_CMAP8:
682 		case B_GRAY8:
683 			bitsPerPixel = 8;
684 			break;
685 		case B_GRAY1:
686 			bitsPerPixel = 1;
687 			break;
688 		default:
689 			fprintf(stderr, "unsupported color space.\n");
690 			return B_BAD_VALUE;
691 	}
692 
693 	TempAllocator dataAllocator;
694 	uint8 *bitsData = (uint8 *)dataAllocator.Allocate(bitsHeader.rowBytes * height);
695 	if (bitsData == NULL)
696 		return B_NO_MEMORY;
697 
698 	ssize_t bytesRead = source.Read(bitsData, bitsHeader.rowBytes * height);
699 	if (bytesRead < B_OK)
700 		return bytesRead;
701 
702 	rgba32_color palette[256];
703 	if (bitsPerPixel > 8) {
704 		// it's a non-palette mode - but does it have to be?
705 		if (bitsHeader.colors != B_RGBA32
706 			|| !has_true_alpha_channel(bitsHeader.colors, bitsData,
707 					width, height, bitsHeader.rowBytes)) {
708 			memset(palette, 0, sizeof(palette));
709 
710 			// count colors
711 			int32 colors = fill_palette(bitsHeader, bitsData, palette);
712 			if (colors != -1) {
713 				// we fit into a palette mode
714 				if (colors > 16)
715 					bitsPerPixel = 8;
716 				else if (colors > 2)
717 					bitsPerPixel = 4;
718 				else
719 					bitsPerPixel = 1;
720 			}
721 		}
722 	}
723 	int32 numColors = 1 << bitsPerPixel;
724 
725 	ico_header header;
726 	header.type = B_HOST_TO_LENDIAN_INT16(1);
727 	header.entry_count = B_HOST_TO_LENDIAN_INT16(1);
728 	header.reserved = 0;
729 
730 	ssize_t bytesWritten = target.Write(&header, sizeof(ico_header));
731 	if (bytesWritten < B_OK)
732 		return bytesWritten;
733 
734 	ico_dir_entry entry;
735 	entry.width = width;
736 	entry.height = height;
737 	entry.planes = 1;
738 	entry.bits_per_pixel = bitsPerPixel;
739 	entry.color_count = 0;
740 	if (bitsPerPixel <= 8)
741 		entry.color_count = numColors;
742 
743 	// When bits_per_pixel == 32, the data already contains the alpha channel
744 
745 	int32 xorRowBytes = get_bytes_per_row(width, bitsPerPixel);
746 	int32 andRowBytes = 0;
747 	if (bitsPerPixel != 32)
748 		andRowBytes = get_bytes_per_row(width, 1);
749 
750 	entry.size = sizeof(ico_bitmap_header) + width * (xorRowBytes + andRowBytes);
751 	if (bitsPerPixel <= 8)
752 		entry.size += numColors * sizeof(rgba32_color);
753 	entry.offset = sizeof(ico_header) + sizeof(ico_dir_entry);
754 	entry.reserved = 0;
755 
756 	ico_bitmap_header bitmapHeader;
757 	memset(&bitmapHeader, 0, sizeof(ico_bitmap_header));
758 	bitmapHeader.size = sizeof(ico_bitmap_header);
759 	bitmapHeader.width = width;
760 	bitmapHeader.height = height + (bitsPerPixel == 32 ? 0 : height);
761 	bitmapHeader.bits_per_pixel = bitsPerPixel;
762 	bitmapHeader.planes = 1;
763 	bitmapHeader.image_size = 0;
764 	if (bitsPerPixel <= 8)
765 		bitmapHeader.colors_used = numColors;
766 
767 	entry.SwapFromHost();
768 	bitmapHeader.SwapFromHost();
769 
770 	bytesWritten = target.Write(&entry, sizeof(ico_dir_entry));
771 	if (bytesWritten < B_OK)
772 		return bytesWritten;
773 
774 	bytesWritten = target.Write(&bitmapHeader, sizeof(ico_bitmap_header));
775 	if (bytesWritten < B_OK)
776 		return bytesWritten;
777 
778 	// we'll need them in convert_bits_to_data()
779 	entry.SwapToHost();
780 	bitmapHeader.SwapToHost();
781 
782 	if (bitsPerPixel <= 8) {
783 		bytesWritten = target.Write(palette, numColors * sizeof(rgba32_color));
784 		if (bytesWritten < B_OK)
785 			return bytesWritten;
786 	}
787 
788 	return convert_bits_to_data(bitsHeader, bitsData, entry, bitmapHeader,
789 		bitsPerPixel <= 8 ? palette : NULL, target);
790 }
791 
792