xref: /haiku/src/add-ons/translators/ico/ICO.cpp (revision 93aeb8c3bc3f13cb1f282e3e749258a23790d947)
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 bool
170 is_valid_size(int32 size)
171 {
172 	return size == 16 || size == 32 || size == 48;
173 }
174 
175 
176 static uint8
177 get_alpha_value(color_space space, uint32 value)
178 {
179 	// ToDo: support more color spaces
180 	if (space == B_RGBA32)
181 		return value >> 24;
182 
183 	return 0;
184 }
185 
186 
187 static uint16
188 rgba32_color_to_16_bit_color(rgba32_color &color)
189 {
190 	return ((color.blue >> 3) << 11) | ((color.green >> 2) << 5) | (color.red >> 3);
191 }
192 
193 
194 static int32
195 find_rgba32_color(rgba32_color *palette, int32 numColors, rgba32_color &color)
196 {
197 	// ToDo: sorting and binary search?
198 	for (int32 i = 0; i < numColors; i++) {
199 		if (palette[i] == color)
200 			return i;
201 	}
202 
203 	return -1;
204 }
205 
206 
207 static inline rgba32_color
208 get_rgba32_color_from_bits(TranslatorBitmap &bitsHeader, uint8 *data, int32 x, int32 y)
209 {
210 	data += bitsHeader.rowBytes * y;
211 
212 	switch (bitsHeader.colors) {
213 		case B_RGBA32:
214 			return *(rgba32_color *)(data + 4 * x);
215 		case B_RGB32:
216 		default:
217 			// stupid applications like ArtPaint use the alpha channel in B_RGB32 images...
218 			rgba32_color color = *(rgba32_color *)(data + 4 * x);
219 			if (color.alpha >= 128)
220 				color.alpha = 255;
221 			else
222 				color.alpha = 0;
223 			return color;
224 		// ToDo: support some more color spaces...
225 	}
226 }
227 
228 
229 static int32
230 fill_palette(TranslatorBitmap &bitsHeader, uint8 *data, rgba32_color *palette)
231 {
232 	int32 numColors = 0;
233 
234 	for (int32 y = 0; y < bitsHeader.bounds.IntegerHeight() + 1; y++) {
235 		for (int32 x = 0; x < bitsHeader.bounds.IntegerWidth() + 1; x++) {
236 			rgba32_color color = get_rgba32_color_from_bits(bitsHeader, data, x, y);
237 
238 			int32 index = find_rgba32_color(palette, numColors, color);
239 			if (index == -1) {
240 				// add this color if there is space left
241 				if (numColors == 256)
242 					return -1;
243 
244 				color.alpha = 0;
245 					// the alpha channel is actually unused
246 				palette[numColors++] = color;
247 			}
248 		}
249 	}
250 
251 	return numColors;
252 }
253 
254 
255 /**	This function is used to determine, if a true alpha channel has to
256  *	be used in order to preserve all information.
257  */
258 
259 static bool
260 has_true_alpha_channel(color_space space, uint8 *data,
261 	int32 width, int32 height, int32 bytesPerRow)
262 {
263 	for (int32 y = 0; y < height; y++) {
264 		for (int32 x = 0; x < width; x++) {
265 			uint8 value = get_alpha_value(space, ((uint32 *)data)[x]);
266 			if (value != 0 && value != 255)
267 				return true;
268 		}
269 
270 		data += bytesPerRow;
271 	}
272 
273 	return false;
274 }
275 
276 
277 static status_t
278 convert_data_to_bits(ico_dir_entry &entry, ico_bitmap_header &header,
279 	const rgba32_color *palette, BPositionIO &source,
280 	BPositionIO &target)
281 {
282 	uint16 bitsPerPixel = header.bits_per_pixel;
283 
284 	// round row bytes to next 4 byte boundary
285 	int32 xorRowBytes = get_bytes_per_row(entry.width, header.bits_per_pixel);
286 	int32 andRowBytes = 0;
287 	if (bitsPerPixel != 32)
288 		andRowBytes = get_bytes_per_row(entry.width, 1);
289 	int32 outRowBytes = entry.width * 4;
290 
291 	// allocate buffers
292 	TempAllocator xorAllocator, andAllocator, rowAllocator;
293 
294 	int32 xorDataSize = xorRowBytes * entry.height;
295 	uint8 *xorData = (uint8 *)xorAllocator.Allocate(xorDataSize);
296 	if (xorData == NULL)
297 		return B_NO_MEMORY;
298 
299 	int32 andDataSize = andRowBytes * entry.height;
300 	uint8 *andData = NULL;
301 	if (bitsPerPixel != 32) {
302 		andData = (uint8 *)andAllocator.Allocate(andDataSize);
303 		if (andData == NULL)
304 			return B_NO_MEMORY;
305 	}
306 
307 	rgba32_color *outRowData = (rgba32_color *)rowAllocator.Allocate(outRowBytes);
308 	if (outRowData == NULL)
309 		return B_NO_MEMORY;
310 
311 	ssize_t bytesRead = source.Read(xorData, xorDataSize);
312 	if (bytesRead != xorDataSize)
313 		return B_BAD_DATA;
314 
315 	if (bitsPerPixel != 32) {
316 		bytesRead = source.Read(andData, andDataSize);
317 		if (bytesRead != andDataSize) {
318 			// reading the alpha channel failed, so we're ignoring it
319 			// (but we're still able to show the image data)
320 			andData = NULL;
321 		}
322 	}
323 
324 	for (uint32 row = 0; row < entry.height; row++) {
325 		for (uint32 x = 0; x < entry.width; x++) {
326 			uint8 *line = get_data_row(xorData, xorDataSize, xorRowBytes, row);
327 
328 			if (palette != NULL) {
329 				uint8 index;
330 
331 				switch (bitsPerPixel) {
332 					case 1:
333 						index = get_1_bit_per_pixel(line, x);
334 						break;
335 					case 4:
336 						index = get_4_bits_per_pixel(line, x);
337 						break;
338 					case 8:
339 					default:
340 						index = line[x];
341 						break;
342 				}
343 
344 				outRowData[x] = palette[index];
345 			} else {
346 				switch (bitsPerPixel) {
347 					case 16:
348 					{
349 						uint16 color = ((uint16 *)line)[x];
350 						outRowData[x].blue = (color >> 11) << 3;
351 						outRowData[x].green = ((color >> 5) & 0x3f) << 3;
352 						outRowData[x].red = (color & 0x1f) << 3;
353 						break;
354 					}
355 
356 					case 24:
357 						outRowData[x].blue = line[x * 3 + 0];
358 						outRowData[x].green = line[x * 3 + 1];
359 						outRowData[x].red = line[x * 3 + 2];
360 						break;
361 
362 					case 32:
363 						outRowData[x] = ((rgba32_color *)line)[x];
364 						break;
365 				}
366 			}
367 
368 			if (bitsPerPixel != 32) {
369 				// set alpha channel
370 				if (andData != NULL
371 					&& get_1_bit_per_pixel(get_data_row(andData, andDataSize, andRowBytes, row), x))
372 					outRowData[x] = kMagicTransparentColor;
373 				else
374 					outRowData[x].alpha = 255;
375 			} else if (outRowData[x].alpha == 0)
376 				outRowData[x] = kMagicTransparentColor;
377 		}
378 
379 		ssize_t bytesWritten = target.Write(outRowData, outRowBytes);
380 		if (bytesWritten < B_OK)
381 			return bytesWritten;
382 		if (bytesWritten != outRowBytes)
383 			return B_IO_ERROR;
384 	}
385 
386 	return B_OK;
387 }
388 
389 
390 static status_t
391 convert_bits_to_data(TranslatorBitmap &bitsHeader, uint8 *bitsData, ico_dir_entry &entry,
392 	ico_bitmap_header &header, rgba32_color *palette, BPositionIO &target)
393 {
394 	int32 bitsPerPixel = header.bits_per_pixel;
395 
396 	// round row bytes to next 4 byte boundary
397 	int32 xorRowBytes = get_bytes_per_row(entry.width, bitsPerPixel);
398 	int32 andRowBytes = get_bytes_per_row(entry.width, 1);
399 
400 	TempAllocator xorAllocator, andAllocator;
401 
402 	uint8 *xorRowData = (uint8 *)xorAllocator.Allocate(xorRowBytes);
403 	if (xorRowData == NULL)
404 		return B_NO_MEMORY;
405 
406 	uint8 *andRowData = (uint8 *)andAllocator.Allocate(andRowBytes);
407 	if (andRowData == NULL)
408 		return B_NO_MEMORY;
409 
410 	int32 numColors = 1 << bitsPerPixel;
411 
412 	// write XOR data (the actual image data)
413 	// (ICO data is upside down, so we're starting at the last line)
414 
415 	for (uint32 row = entry.height; row-- > 0;) {
416 		for (uint32 x = 0; x < entry.width; x++) {
417 			rgba32_color color = get_rgba32_color_from_bits(bitsHeader, bitsData, x, row);
418 
419 			if (palette != NULL) {
420 				uint8 index = find_rgba32_color(palette, numColors, color);
421 
422 				switch (bitsPerPixel) {
423 					case 1:
424 						set_1_bit_per_pixel(xorRowData, x, index);
425 						break;
426 					case 4:
427 						set_4_bits_per_pixel(xorRowData, x, index);
428 						break;
429 					case 8:
430 					default:
431 						xorRowData[x] = index;
432 						break;
433 				}
434 			} else {
435 				switch (bitsPerPixel) {
436 					default:
437 					case 16:
438 					{
439 						uint16 *data = (uint16 *)xorRowData;
440 						data[x] = rgba32_color_to_16_bit_color(color);
441 						break;
442 					}
443 
444 					case 24:
445 					{
446 						xorRowData[x * 3 + 0] = color.blue;
447 						xorRowData[x * 3 + 1] = color.green;
448 						xorRowData[x * 3 + 2] = color.red;
449 						break;
450 					}
451 
452 					case 32:
453 					{
454 						rgba32_color *data = (rgba32_color *)xorRowData;
455 						data[x] = color;
456 						break;
457 					}
458 				}
459 			}
460 		}
461 
462 		ssize_t bytesWritten = target.Write(xorRowData, xorRowBytes);
463 		if (bytesWritten < B_OK)
464 			return bytesWritten;
465 		if (bytesWritten != xorRowBytes)
466 			return B_IO_ERROR;
467 	}
468 
469 	if (bitsPerPixel == 32) {
470 		// the alpha channel has already been written with the image data
471 		return B_OK;
472 	}
473 
474 	// write AND data (the transparency bit)
475 
476 	for (uint32 row = entry.height; row-- > 0;) {
477 		for (uint32 x = 0; x < entry.width; x++) {
478 			rgba32_color color = get_rgba32_color_from_bits(bitsHeader, bitsData, x, row);
479 			bool transparent = *(uint32 *)&color == B_TRANSPARENT_MAGIC_RGBA32 || color.alpha == 0;
480 
481 			set_1_bit_per_pixel(andRowData, x, transparent ? 1 : 0);
482 		}
483 
484 		ssize_t bytesWritten = target.Write(andRowData, andRowBytes);
485 		if (bytesWritten < B_OK)
486 			return bytesWritten;
487 		if (bytesWritten != andRowBytes)
488 			return B_IO_ERROR;
489 	}
490 
491 	return B_OK;
492 }
493 
494 
495 //	#pragma mark -
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