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
26using namespace ICO;
27
28
29static const rgba32_color kMagicTransparentColor = *(rgba32_color *)&B_TRANSPARENT_MAGIC_RGBA32;
30
31
32class 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
44bool
45ico_header::IsValid() const
46{
47	return reserved == 0
48		&& (type == kTypeIcon || type == kTypeCursor)
49		&& entry_count < 32;
50}
51
52
53void
54ico_header::SwapToHost()
55{
56	swap_data(B_UINT16_TYPE, this, sizeof(ico_header), B_SWAP_LENDIAN_TO_HOST);
57}
58
59
60void
61ico_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
70void
71ico_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
78void
79ico_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
89bool
90ico_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
98void
99ico_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
107void
108ico_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
119static inline uint8 *
120get_data_row(uint8 *data, int32 dataSize, int32 rowBytes, int32 row)
121{
122	return data + dataSize - (row + 1) * rowBytes;
123}
124
125
126static inline int32
127get_bytes_per_row(int32 width, int32 bitsPerPixel)
128{
129	return (((bitsPerPixel * width + 7) / 8) + 3) & ~3;
130}
131
132
133static inline void
134set_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
144static inline void
145set_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
155static inline int32
156get_1_bit_per_pixel(uint8 *line, int32 x)
157{
158	return (line[x / 8] >> (7 - (x & 7))) & 1;
159}
160
161
162static inline int32
163get_4_bits_per_pixel(uint8 *line, int32 x)
164{
165	return (line[x / 2] >> (4 * ((x + 1) & 1))) & 0xf;
166}
167
168
169static uint8
170get_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
180static uint16
181rgba32_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
187static int32
188find_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
200static inline rgba32_color
201get_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
222static int32
223fill_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
252static bool
253has_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
270static status_t
271convert_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
383static status_t
384convert_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
491bool
492ICO::is_valid_size(int32 size)
493{
494	return size == 16 || size == 32 || size == 48;
495}
496
497
498status_t
499ICO::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
571status_t
572ICO::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
661status_t
662ICO::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