xref: /haiku/src/add-ons/translators/bmp/BMPTranslator.cpp (revision bd7645a12ab594d5eb1c6d6a70c3a82a429410dd)
1 /*
2  * Copyright 2002-2009, Haiku, Inc. All rights reserved.
3  * Distributed under the terms of the MIT License.
4  *
5  * Authors:
6  *		Michael Wilber <mwilber@users.berlios.de>
7  */
8 
9 #include "BMPTranslator.h"
10 #include "BMPView.h"
11 
12 #include <algorithm>
13 #include <new>
14 #include <stdio.h>
15 #include <stdlib.h>
16 #include <string.h>
17 
18 
19 using std::nothrow;
20 using std::min;
21 
22 //#define INFO(x) printf(x);
23 #define INFO(x)
24 //#define ERROR(x) printf(x);
25 #define ERROR(x)
26 
27 // The input formats that this translator supports.
28 translation_format gInputFormats[] = {
29 	{
30 		B_TRANSLATOR_BITMAP,
31 		B_TRANSLATOR_BITMAP,
32 		BBT_IN_QUALITY,
33 		BBT_IN_CAPABILITY,
34 		"image/x-be-bitmap",
35 		"Be Bitmap Format (BMPTranslator)"
36 	},
37 	{
38 		B_BMP_FORMAT,
39 		B_TRANSLATOR_BITMAP,
40 		BMP_IN_QUALITY,
41 		BMP_IN_CAPABILITY,
42 		"image/bmp",
43 		"BMP image"
44 	}
45 };
46 
47 // The output formats that this translator supports.
48 translation_format gOutputFormats[] = {
49 	{
50 		B_TRANSLATOR_BITMAP,
51 		B_TRANSLATOR_BITMAP,
52 		BBT_OUT_QUALITY,
53 		BBT_OUT_CAPABILITY,
54 		"image/x-be-bitmap",
55 		"Be Bitmap Format (BMPTranslator)"
56 	},
57 	{
58 		B_BMP_FORMAT,
59 		B_TRANSLATOR_BITMAP,
60 		BMP_OUT_QUALITY,
61 		BMP_OUT_CAPABILITY,
62 		"image/bmp",
63 		"BMP image"
64 	}
65 };
66 
67 // Default settings for the Translator
68 TranSetting gDefaultSettings[] = {
69 	{B_TRANSLATOR_EXT_HEADER_ONLY, TRAN_SETTING_BOOL, false},
70 	{B_TRANSLATOR_EXT_DATA_ONLY, TRAN_SETTING_BOOL, false}
71 };
72 
73 // ---------------------------------------------------------------
74 // make_nth_translator
75 //
76 // Creates a BMPTranslator object to be used by BTranslatorRoster
77 //
78 // Preconditions:
79 //
80 // Parameters: n,		The translator to return. Since
81 //						BMPTranslator only publishes one
82 //						translator, it only returns a
83 //						BMPTranslator if n == 0
84 //
85 //             you, 	The image_id of the add-on that
86 //						contains code (not used).
87 //
88 //             flags,	Has no meaning yet, should be 0.
89 //
90 // Postconditions:
91 //
92 // Returns: NULL if n is not zero,
93 //          a new BMPTranslator if n is zero
94 // ---------------------------------------------------------------
95 BTranslator *
96 make_nth_translator(int32 n, image_id you, uint32 flags, ...)
97 {
98 	if (!n)
99 		return new BMPTranslator();
100 	else
101 		return NULL;
102 }
103 
104 // ---------------------------------------------------------------
105 // Constructor
106 //
107 // Sets up the version info and the name of the translator so that
108 // these values can be returned when they are requested.
109 //
110 // Preconditions:
111 //
112 // Parameters:
113 //
114 // Postconditions:
115 //
116 // Returns:
117 // ---------------------------------------------------------------
118 BMPTranslator::BMPTranslator()
119 	: BaseTranslator("BMP images", "BMP image translator",
120 		BMP_TRANSLATOR_VERSION,
121 		gInputFormats, sizeof(gInputFormats) / sizeof(translation_format),
122 		gOutputFormats, sizeof(gOutputFormats) / sizeof(translation_format),
123 		"BMPTranslator_Settings",
124 		gDefaultSettings, sizeof(gDefaultSettings) / sizeof(TranSetting),
125 		B_TRANSLATOR_BITMAP, B_BMP_FORMAT)
126 {
127 }
128 
129 // ---------------------------------------------------------------
130 // Destructor
131 //
132 // Does nothing
133 //
134 // Preconditions:
135 //
136 // Parameters:
137 //
138 // Postconditions:
139 //
140 // Returns:
141 // ---------------------------------------------------------------
142 BMPTranslator::~BMPTranslator()
143 {
144 }
145 
146 // ---------------------------------------------------------------
147 // get_padding
148 //
149 // Returns number of bytes of padding required at the end of
150 // the row by the BMP format
151 //
152 //
153 // Preconditions: If bitsperpixel is zero, a division by zero
154 //                will occur, which is bad
155 //
156 // Parameters:	width, width of the row, in pixels
157 //
158 //				bitsperpixel, bitdepth of the image
159 //
160 // Postconditions:
161 //
162 // Returns:
163 // ---------------------------------------------------------------
164 int32
165 get_padding(uint32 width, uint16 bitsperpixel)
166 {
167 	int32 padding = 0;
168 
169 	if (bitsperpixel > 8) {
170 		uint8 bytesPerPixel = bitsperpixel / 8;
171 		padding = (width * bytesPerPixel) % 4;
172 	} else {
173 		uint8 pixelsPerByte = 8 / bitsperpixel;
174 		if (!(width % pixelsPerByte))
175 			padding = (width / pixelsPerByte) % 4;
176 		else
177 			padding = ((width + pixelsPerByte -
178 				(width % pixelsPerByte)) /
179 					pixelsPerByte) % 4;
180 	}
181 
182 	if (padding)
183 		padding = 4 - padding;
184 
185 	return padding;
186 }
187 
188 // ---------------------------------------------------------------
189 // get_rowbytes
190 //
191 // Returns number of bytes required to store a row of BMP pixels
192 // with a width of width and a bit depth of bitsperpixel.
193 //
194 //
195 // Preconditions: If bitsperpixel is zero, a division by zero
196 //                will occur, which is bad
197 //
198 // Parameters:	width, width of the row, in pixels
199 //
200 //				bitsperpixel, bitdepth of the image
201 //
202 // Postconditions:
203 //
204 // Returns:
205 // ---------------------------------------------------------------
206 int32
207 get_rowbytes(uint32 width, uint16 bitsperpixel)
208 {
209 	int32 rowbytes = 0;
210 	int32 padding = get_padding(width, bitsperpixel);
211 
212 	if (bitsperpixel > 8) {
213 		uint8 bytesPerPixel = bitsperpixel / 8;
214 		rowbytes = (width * bytesPerPixel) + padding;
215 	} else {
216 		uint8 pixelsPerByte = 8 / bitsperpixel;
217 		rowbytes = (width / pixelsPerByte) +
218 			((width % pixelsPerByte) ? 1 : 0) + padding;
219 	}
220 
221 	return rowbytes;
222 }
223 
224 // ---------------------------------------------------------------
225 // identify_bmp_header
226 //
227 // Determines if the data in inSource is in the MS or OS/2 BMP
228 // format. If it is, it returns info about the data in inSource
229 // to outInfo, pfileheader, pmsheader, pfrommsformat and os2skip.
230 //
231 // Preconditions:
232 //
233 // Parameters:	inSource,	The source of the image data
234 //
235 //				outInfo,	Information about the translator
236 //							is copied here
237 //
238 //				amtread,	Amount of data read from inSource
239 //							before this function was called
240 //
241 //				read,		Pointer to the data that was read
242 // 							in before this function was called
243 //
244 //				pfileheader,	File header info for the BMP is
245 //								copied here after it is read from
246 //								the file.
247 //
248 //				pmsheader,		BMP header info read in from the
249 //								BMP file
250 //
251 //				pfrommsformat,	Set to true if BMP data is BMP
252 //								format, false if BMP data is OS/2
253 //								format.
254 //
255 //				pos2skip,	If data is in OS/2 format, the number
256 //							of bytes to skip between the header
257 //							data and image data is stored here
258 //
259 // Postconditions:
260 //
261 // Returns: B_NO_TRANSLATOR,	if the data does not look like
262 //								BMP format data
263 //
264 // B_ERROR,	if the header data could not be converted to host
265 //			format
266 //
267 // B_OK,	if the data looks like bits data and no errors were
268 //			encountered
269 // ---------------------------------------------------------------
270 status_t
271 identify_bmp_header(BPositionIO *inSource, translator_info *outInfo,
272 	BMPFileHeader *pfileheader = NULL, MSInfoHeader *pmsheader = NULL,
273 	bool *pfrommsformat = NULL, off_t *pos2skip = NULL)
274 {
275 	// read in the fileHeader
276 	uint8 buf[40];
277 	BMPFileHeader fileHeader;
278 	ssize_t size = 14;
279 	if (inSource->Read(buf, size) != size)
280 		return B_NO_TRANSLATOR;
281 
282 	// check BMP magic number
283 	const uint16 kBmpMagic = B_HOST_TO_LENDIAN_INT16('MB');
284 	uint16 sourceMagic;
285 	memcpy(&sourceMagic, buf, sizeof(uint16));
286 	if (sourceMagic != kBmpMagic)
287 		return B_NO_TRANSLATOR;
288 
289 	// convert fileHeader to host byte order
290 	memcpy(&fileHeader.magic, buf, 2);
291 	memcpy(&fileHeader.fileSize, buf + 2, 4);
292 	memcpy(&fileHeader.reserved, buf + 6, 4);
293 	memcpy(&fileHeader.dataOffset, buf + 10, 4);
294 	if (swap_data(B_UINT16_TYPE, &fileHeader.magic, sizeof(uint16),
295 		B_SWAP_LENDIAN_TO_HOST) != B_OK)
296 		return B_ERROR;
297 	if (swap_data(B_UINT32_TYPE,
298 		(reinterpret_cast<uint8 *> (&fileHeader)) + 2, 12,
299 		B_SWAP_LENDIAN_TO_HOST) != B_OK)
300 		return B_ERROR;
301 
302 	if (fileHeader.reserved != 0)
303 		return B_NO_TRANSLATOR;
304 
305 	uint32 headersize = 0;
306 	if (inSource->Read(&headersize, 4) != 4)
307 		return B_NO_TRANSLATOR;
308 	if (swap_data(B_UINT32_TYPE, &headersize, 4,
309 		B_SWAP_LENDIAN_TO_HOST) != B_OK)
310 		return B_ERROR;
311 
312 	if (headersize == sizeof(MSInfoHeader)) {
313 		// MS format
314 
315 		if (fileHeader.dataOffset < 54)
316 			return B_NO_TRANSLATOR;
317 
318 		MSInfoHeader msheader;
319 		msheader.size = headersize;
320 		if (inSource->Read(
321 			reinterpret_cast<uint8 *> (&msheader) + 4, 36) != 36)
322 			return B_NO_TRANSLATOR;
323 
324 		// convert msheader to host byte order
325 		if (swap_data(B_UINT32_TYPE,
326 			reinterpret_cast<uint8 *> (&msheader) + 4, 36,
327 			B_SWAP_LENDIAN_TO_HOST) != B_OK)
328 			return B_ERROR;
329 
330 		// check if msheader is valid
331 		if (msheader.width == 0 || msheader.height == 0)
332 			return B_NO_TRANSLATOR;
333 		if (msheader.planes != 1)
334 			return B_NO_TRANSLATOR;
335 		if ((msheader.bitsperpixel != 1 ||
336 				msheader.compression != BMP_NO_COMPRESS) &&
337 			(msheader.bitsperpixel != 4 ||
338 				msheader.compression != BMP_NO_COMPRESS) &&
339 			(msheader.bitsperpixel != 4 ||
340 				msheader.compression != BMP_RLE4_COMPRESS) &&
341 			(msheader.bitsperpixel != 8 ||
342 				msheader.compression != BMP_NO_COMPRESS) &&
343 			(msheader.bitsperpixel != 8 ||
344 				msheader.compression != BMP_RLE8_COMPRESS) &&
345 			(msheader.bitsperpixel != 24 ||
346 				msheader.compression != BMP_NO_COMPRESS) &&
347 			(msheader.bitsperpixel != 32 ||
348 				msheader.compression != BMP_NO_COMPRESS))
349 			return B_NO_TRANSLATOR;
350 		if (!msheader.imagesize && msheader.compression)
351 			return B_NO_TRANSLATOR;
352 		if (msheader.colorsimportant > msheader.colorsused)
353 			return B_NO_TRANSLATOR;
354 
355 		if (outInfo) {
356 			outInfo->type = B_BMP_FORMAT;
357 			outInfo->group = B_TRANSLATOR_BITMAP;
358 			outInfo->quality = BMP_IN_QUALITY;
359 			outInfo->capability = BMP_IN_CAPABILITY;
360 			sprintf(outInfo->name, "BMP image (MS format, %d bits",
361 				msheader.bitsperpixel);
362 			if (msheader.compression)
363 				strcat(outInfo->name, ", RLE)");
364 			else
365 				strcat(outInfo->name, ")");
366 			strcpy(outInfo->MIME, "image/x-bmp");
367 		}
368 
369 		if (pfileheader) {
370 			pfileheader->magic = fileHeader.magic;
371 			pfileheader->fileSize = fileHeader.fileSize;
372 			pfileheader->reserved = fileHeader.reserved;
373 			pfileheader->dataOffset = fileHeader.dataOffset;
374 		}
375 		if (pmsheader) {
376 			pmsheader->size = msheader.size;
377 			pmsheader->width = msheader.width;
378 			pmsheader->height = abs((int32)msheader.height);
379 				// TODO: negative height means the BMP is up side down
380 				// -> support this...
381 			pmsheader->planes = msheader.planes;
382 			pmsheader->bitsperpixel = msheader.bitsperpixel;
383 			pmsheader->compression = msheader.compression;
384 			pmsheader->imagesize = msheader.imagesize;
385 			pmsheader->xpixperm = msheader.xpixperm;
386 			pmsheader->ypixperm = msheader.ypixperm;
387 			pmsheader->colorsused = msheader.colorsused;
388 			pmsheader->colorsimportant = msheader.colorsimportant;
389 		}
390 		if (pfrommsformat)
391 			(*pfrommsformat) = true;
392 
393 		return B_OK;
394 
395 	} else if (headersize == sizeof(OS2InfoHeader)) {
396 		// OS/2 format
397 
398 		if (fileHeader.dataOffset < 26)
399 			return B_NO_TRANSLATOR;
400 
401 		OS2InfoHeader os2header;
402 		os2header.size = headersize;
403 		if (inSource->Read(
404 			reinterpret_cast<uint8 *> (&os2header) + 4, 8) != 8)
405 			return B_NO_TRANSLATOR;
406 
407 		// convert msheader to host byte order
408 		if (swap_data(B_UINT32_TYPE,
409 			reinterpret_cast<uint8 *> (&os2header) + 4, 8,
410 			B_SWAP_LENDIAN_TO_HOST) != B_OK)
411 			return B_ERROR;
412 
413 		// check if msheader is valid
414 		if (os2header.width == 0 || os2header.height == 0)
415 			return B_NO_TRANSLATOR;
416 		if (os2header.planes != 1)
417 			return B_NO_TRANSLATOR;
418 		if (os2header.bitsperpixel != 1 &&
419 			os2header.bitsperpixel != 4 &&
420 			os2header.bitsperpixel != 8 &&
421 			os2header.bitsperpixel != 24)
422 			return B_NO_TRANSLATOR;
423 
424 		if (outInfo) {
425 			outInfo->type = B_BMP_FORMAT;
426 			outInfo->group = B_TRANSLATOR_BITMAP;
427 			outInfo->quality = BMP_IN_QUALITY;
428 			outInfo->capability = BMP_IN_CAPABILITY;
429 			sprintf(outInfo->name, "BMP image (OS/2 format, %d bits)",
430 				os2header.bitsperpixel);
431 			strcpy(outInfo->MIME, "image/x-bmp");
432 		}
433 		if (pfileheader && pmsheader) {
434 			pfileheader->magic = 'MB';
435 			pfileheader->fileSize = 0;
436 			pfileheader->reserved = 0;
437 			pfileheader->dataOffset = 0;
438 
439 			pmsheader->size = 40;
440 			pmsheader->width = os2header.width;
441 			pmsheader->height = os2header.height;
442 			pmsheader->planes = 1;
443 			pmsheader->bitsperpixel = os2header.bitsperpixel;
444 			pmsheader->compression = BMP_NO_COMPRESS;
445 			pmsheader->imagesize = 0;
446 			pmsheader->xpixperm = 2835; // 72 dpi horizontal
447 			pmsheader->ypixperm = 2835; // 72 dpi vertical
448 			pmsheader->colorsused = 0;
449 			pmsheader->colorsimportant = 0;
450 
451 			// determine fileSize / imagesize
452 			switch (pmsheader->bitsperpixel) {
453 				case 24:
454 					if (pos2skip && fileHeader.dataOffset > 26)
455 						(*pos2skip) = fileHeader.dataOffset - 26;
456 
457 					pfileheader->dataOffset = 54;
458 					pmsheader->imagesize = get_rowbytes(pmsheader->width,
459 						pmsheader->bitsperpixel) * pmsheader->height;
460 					pfileheader->fileSize = pfileheader->dataOffset +
461 						pmsheader->imagesize;
462 
463 					break;
464 
465 				case 8:
466 				case 4:
467 				case 1:
468 				{
469 					uint16 ncolors = 1 << pmsheader->bitsperpixel;
470 					pmsheader->colorsused = ncolors;
471 					pmsheader->colorsimportant = ncolors;
472 					if (pos2skip && fileHeader.dataOffset >
473 						static_cast<uint32> (26 + (ncolors * 3)))
474 							(*pos2skip) = fileHeader.dataOffset -
475 								(26 + (ncolors * 3));
476 
477 					pfileheader->dataOffset = 54 + (ncolors * 4);
478 					pmsheader->imagesize = get_rowbytes(pmsheader->width,
479 						pmsheader->bitsperpixel) * pmsheader->height;
480 					pfileheader->fileSize = pfileheader->dataOffset +
481 						pmsheader->imagesize;
482 
483 					break;
484 				}
485 
486 				default:
487 					break;
488 			}
489 		}
490 		if (pfrommsformat)
491 			(*pfrommsformat) = false;
492 
493 		return B_OK;
494 
495 	} else
496 		return B_NO_TRANSLATOR;
497 }
498 
499 // ---------------------------------------------------------------
500 // DerivedIdentify
501 //
502 // Examines the data from inSource and determines if it is in a
503 // format that this translator knows how to work with.
504 //
505 // Preconditions:
506 //
507 // Parameters:	inSource,	where the data to examine is
508 //
509 //				inFormat,	a hint about the data in inSource,
510 //							it is ignored since it is only a hint
511 //
512 //				ioExtension,	configuration settings for the
513 //								translator
514 //
515 //				outInfo,	information about what data is in
516 //							inSource and how well this translator
517 //							can handle that data is stored here
518 //
519 //				outType,	The format that the user wants
520 //							the data in inSource to be
521 //							converted to
522 //
523 // Postconditions:
524 //
525 // Returns: B_NO_TRANSLATOR,	if this translator can't handle
526 //								the data in inSource
527 //
528 // B_ERROR,	if there was an error converting the data to the host
529 //			format
530 //
531 // B_BAD_VALUE, if the settings in ioExtension are bad
532 //
533 // B_OK,	if this translator understand the data and there were
534 //			no errors found
535 // ---------------------------------------------------------------
536 status_t
537 BMPTranslator::DerivedIdentify(BPositionIO *inSource,
538 	const translation_format *inFormat, BMessage *ioExtension,
539 	translator_info *outInfo, uint32 outType)
540 {
541 	return identify_bmp_header(inSource, outInfo);
542 }
543 
544 // ---------------------------------------------------------------
545 // translate_from_bits_to_bmp24
546 //
547 // Converts various varieties of the Be Bitmap format ('bits') to
548 // the MS BMP 24-bit format.
549 //
550 // Preconditions:
551 //
552 // Parameters:	inSource,	contains the bits data to convert
553 //
554 //				outDestination,	where the BMP data will be written
555 //
556 //				fromspace,	the format of the data in inSource
557 //
558 //				msheader,	contains information about the BMP
559 //							dimensions and filesize
560 //
561 // Postconditions:
562 //
563 // Returns: B_ERROR,	if memory couldn't be allocated or another
564 //						error occured
565 //
566 // B_OK,	if no errors occurred
567 // ---------------------------------------------------------------
568 status_t
569 translate_from_bits_to_bmp24(BPositionIO *inSource,
570 BPositionIO *outDestination, color_space fromspace, MSInfoHeader &msheader)
571 {
572 	// TODO: WHOHA! big switch statement for the innermost loop!
573 	// make a loop per colorspace and put the switch outside!!!
574 	// remove memcpy() to copy 3 bytes
575 	int32 bitsBytesPerPixel = 0;
576 	switch (fromspace) {
577 		case B_RGB32:
578 		case B_RGB32_BIG:
579 		case B_RGBA32:
580 		case B_RGBA32_BIG:
581 		case B_CMY32:
582 		case B_CMYA32:
583 		case B_CMYK32:
584 			bitsBytesPerPixel = 4;
585 			break;
586 
587 		case B_RGB24:
588 		case B_RGB24_BIG:
589 		case B_CMY24:
590 			bitsBytesPerPixel = 3;
591 			break;
592 
593 		case B_RGB16:
594 		case B_RGB16_BIG:
595 		case B_RGBA15:
596 		case B_RGBA15_BIG:
597 		case B_RGB15:
598 		case B_RGB15_BIG:
599 			bitsBytesPerPixel = 2;
600 			break;
601 
602 		case B_CMAP8:
603 		case B_GRAY8:
604 			bitsBytesPerPixel = 1;
605 			break;
606 
607 		default:
608 			return B_ERROR;
609 	}
610 	int32 bitsRowBytes = msheader.width * bitsBytesPerPixel;
611 	int32 padding = get_padding(msheader.width, msheader.bitsperpixel);
612 	int32 bmpRowBytes =
613 		get_rowbytes(msheader.width, msheader.bitsperpixel);
614 	uint32 bmppixrow = 0;
615 	off_t bitsoffset = ((msheader.height - 1) * bitsRowBytes);
616 	inSource->Seek(bitsoffset, SEEK_CUR);
617 	uint8 *bmpRowData = new (nothrow) uint8[bmpRowBytes];
618 	if (!bmpRowData)
619 		return B_NO_MEMORY;
620 	uint8 *bitsRowData = new (nothrow) uint8[bitsRowBytes];
621 	if (!bitsRowData) {
622 		delete[] bmpRowData;
623 		return B_NO_MEMORY;
624 	}
625 	memset(bmpRowData + (bmpRowBytes - padding), 0, padding);
626 	ssize_t rd = inSource->Read(bitsRowData, bitsRowBytes);
627 	const color_map *pmap = NULL;
628 	if (fromspace == B_CMAP8) {
629 		pmap = system_colors();
630 		if (!pmap) {
631 			delete [] bmpRowData;
632 			delete [] bitsRowData;
633 			return B_ERROR;
634 		}
635 	}
636 	while (rd == static_cast<ssize_t>(bitsRowBytes)) {
637 
638 		for (uint32 i = 0; i < msheader.width; i++) {
639 			uint8 *bitspixel, *bmppixel;
640 			uint16 val;
641 			switch (fromspace) {
642 				case B_RGB32:
643 				case B_RGBA32:
644 				case B_RGB24:
645 					memcpy(bmpRowData + (i * 3),
646 						bitsRowData + (i * bitsBytesPerPixel), 3);
647 					break;
648 
649 				case B_RGB16:
650 				case B_RGB16_BIG:
651 					bitspixel = bitsRowData + (i * bitsBytesPerPixel);
652 					bmppixel = bmpRowData + (i * 3);
653 					if (fromspace == B_RGB16)
654 						val = bitspixel[0] + (bitspixel[1] << 8);
655 					else
656 						val = bitspixel[1] + (bitspixel[0] << 8);
657 					bmppixel[0] =
658 						((val & 0x1f) << 3) | ((val & 0x1f) >> 2);
659 					bmppixel[1] =
660 						((val & 0x7e0) >> 3) | ((val & 0x7e0) >> 9);
661 					bmppixel[2] =
662 						((val & 0xf800) >> 8) | ((val & 0xf800) >> 13);
663 					break;
664 
665 				case B_RGB15:
666 				case B_RGB15_BIG:
667 				case B_RGBA15:
668 				case B_RGBA15_BIG:
669 					// NOTE: the alpha data for B_RGBA15* is not used
670 					bitspixel = bitsRowData + (i * bitsBytesPerPixel);
671 					bmppixel = bmpRowData + (i * 3);
672 					if (fromspace == B_RGB15 || fromspace == B_RGBA15)
673 						val = bitspixel[0] + (bitspixel[1] << 8);
674 					else
675 						val = bitspixel[1] + (bitspixel[0] << 8);
676 					bmppixel[0] =
677 						((val & 0x1f) << 3) | ((val & 0x1f) >> 2);
678 					bmppixel[1] =
679 						((val & 0x3e0) >> 2) | ((val & 0x3e0) >> 7);
680 					bmppixel[2] =
681 						((val & 0x7c00) >> 7) | ((val & 0x7c00) >> 12);
682 					break;
683 
684 				case B_RGB32_BIG:
685 				case B_RGBA32_BIG:
686 					bitspixel = bitsRowData + (i * bitsBytesPerPixel);
687 					bmppixel = bmpRowData + (i * 3);
688 					bmppixel[0] = bitspixel[3];
689 					bmppixel[1] = bitspixel[2];
690 					bmppixel[2] = bitspixel[1];
691 					break;
692 
693 				case B_RGB24_BIG:
694 					bitspixel = bitsRowData + (i * bitsBytesPerPixel);
695 					bmppixel = bmpRowData + (i * 3);
696 					bmppixel[0] = bitspixel[2];
697 					bmppixel[1] = bitspixel[1];
698 					bmppixel[2] = bitspixel[0];
699 					break;
700 
701 				case B_CMAP8:
702 				{
703 					bitspixel = bitsRowData + (i * bitsBytesPerPixel);
704 					bmppixel = bmpRowData + (i * 3);
705 					rgb_color c = pmap->color_list[bitspixel[0]];
706 					bmppixel[0] = c.blue;
707 					bmppixel[1] = c.green;
708 					bmppixel[2] = c.red;
709 					break;
710 				}
711 
712 				case B_GRAY8:
713 					bitspixel = bitsRowData + (i * bitsBytesPerPixel);
714 					bmppixel = bmpRowData + (i * 3);
715 					bmppixel[0] = bitspixel[0];
716 					bmppixel[1] = bitspixel[0];
717 					bmppixel[2] = bitspixel[0];
718 					break;
719 
720 				case B_CMYK32:
721 				{
722 					bitspixel = bitsRowData + (i * bitsBytesPerPixel);
723 					bmppixel = bmpRowData + (i * 3);
724 
725 					int32 comp = 255 - bitspixel[2] - bitspixel[3];
726 					bmppixel[0] = (comp < 0) ? 0 : comp;
727 
728 					comp = 255 - bitspixel[1] - bitspixel[3];
729 					bmppixel[1] = (comp < 0) ? 0 : comp;
730 
731 					comp = 255 - bitspixel[0] - bitspixel[3];
732 					bmppixel[2] = (comp < 0) ? 0 : comp;
733 					break;
734 				}
735 
736 				case B_CMY32:
737 				case B_CMYA32:
738 				case B_CMY24:
739 					bitspixel = bitsRowData + (i * bitsBytesPerPixel);
740 					bmppixel = bmpRowData + (i * 3);
741 					bmppixel[0] = 255 - bitspixel[2];
742 					bmppixel[1] = 255 - bitspixel[1];
743 					bmppixel[2] = 255 - bitspixel[0];
744 					break;
745 
746 				default:
747 					break;
748 			} // switch (fromspace)
749 		} // for for (uint32 i = 0; i < msheader.width; i++)
750 
751 		outDestination->Write(bmpRowData, bmpRowBytes);
752 		bmppixrow++;
753 		// if I've read all of the pixel data, break
754 		// out of the loop so I don't try to read
755 		// non-pixel data
756 		if (bmppixrow == msheader.height)
757 			break;
758 
759 		inSource->Seek(bitsRowBytes * -2, SEEK_CUR);
760 		rd = inSource->Read(bitsRowData, bitsRowBytes);
761 	} // while (rd == bitsRowBytes)
762 
763 	delete[] bmpRowData;
764 	delete[] bitsRowData;
765 
766 	return B_OK;
767 }
768 
769 // ---------------------------------------------------------------
770 // translate_from_bits8_to_bmp8
771 //
772 // Converts 8-bit Be Bitmaps ('bits') to the MS 8-bit BMP format
773 //
774 // Preconditions:
775 //
776 // Parameters:	inSource,	contains the bits data to convert
777 //
778 //				outDestination,	where the BMP data will be written
779 //
780 //				bitsRowBytes,	number of bytes in one row of
781 //								bits data
782 //
783 //				msheader,	contains information about the BMP
784 //							dimensions and filesize
785 //
786 // Postconditions:
787 //
788 // Returns: B_ERROR,	if memory couldn't be allocated or another
789 //						error occured
790 //
791 // B_OK,	if no errors occurred
792 // ---------------------------------------------------------------
793 status_t
794 translate_from_bits8_to_bmp8(BPositionIO *inSource,
795 	BPositionIO *outDestination, int32 bitsRowBytes, MSInfoHeader &msheader)
796 {
797 	int32 padding = get_padding(msheader.width, msheader.bitsperpixel);
798 	int32 bmpRowBytes =
799 		get_rowbytes(msheader.width, msheader.bitsperpixel);
800 	uint32 bmppixrow = 0;
801 	off_t bitsoffset = ((msheader.height - 1) * bitsRowBytes);
802 	inSource->Seek(bitsoffset, SEEK_CUR);
803 	uint8 *bmpRowData = new (nothrow) uint8[bmpRowBytes];
804 	if (!bmpRowData)
805 		return B_NO_MEMORY;
806 	uint8 *bitsRowData = new (nothrow) uint8[bitsRowBytes];
807 	if (!bitsRowData) {
808 		delete[] bmpRowData;
809 		return B_NO_MEMORY;
810 	}
811 	memset(bmpRowData + (bmpRowBytes - padding), 0, padding);
812 	ssize_t rd = inSource->Read(bitsRowData, bitsRowBytes);
813 	while (rd == bitsRowBytes) {
814 		memcpy(bmpRowData, bitsRowData, msheader.width);
815 		outDestination->Write(bmpRowData, bmpRowBytes);
816 		bmppixrow++;
817 		// if I've read all of the pixel data, break
818 		// out of the loop so I don't try to read
819 		// non-pixel data
820 		if (bmppixrow == msheader.height)
821 			break;
822 
823 		inSource->Seek(bitsRowBytes * -2, SEEK_CUR);
824 		rd = inSource->Read(bitsRowData, bitsRowBytes);
825 	} // while (rd == bitsRowBytes)
826 
827 	delete[] bmpRowData;
828 	delete[] bitsRowData;
829 
830 	return B_OK;
831 }
832 
833 // ---------------------------------------------------------------
834 // translate_from_bits1_to_bmp1
835 //
836 // Converts 1-bit Be Bitmaps ('bits') to the MS 1-bit BMP format
837 //
838 // Preconditions:
839 //
840 // Parameters:	inSource,	contains the bits data to convert
841 //
842 //				outDestination,	where the BMP data will be written
843 //
844 //				bitsRowBytes,	number of bytes in one row of
845 //								bits data
846 //
847 //				msheader,	contains information about the BMP
848 //							dimensions and filesize
849 //
850 // Postconditions:
851 //
852 // Returns: B_ERROR,	if memory couldn't be allocated or another
853 //						error occured
854 //
855 // B_OK,	if no errors occurred
856 // ---------------------------------------------------------------
857 status_t
858 translate_from_bits1_to_bmp1(BPositionIO *inSource,
859 	BPositionIO *outDestination, int32 bitsRowBytes, MSInfoHeader &msheader)
860 {
861 	uint8 pixelsPerByte = 8 / msheader.bitsperpixel;
862 	int32 bmpRowBytes =
863 		get_rowbytes(msheader.width, msheader.bitsperpixel);
864 	uint32 bmppixrow = 0;
865 	off_t bitsoffset = ((msheader.height - 1) * bitsRowBytes);
866 	inSource->Seek(bitsoffset, SEEK_CUR);
867 	uint8 *bmpRowData = new (nothrow) uint8[bmpRowBytes];
868 	if (!bmpRowData)
869 		return B_NO_MEMORY;
870 	uint8 *bitsRowData = new (nothrow) uint8[bitsRowBytes];
871 	if (!bitsRowData) {
872 		delete[] bmpRowData;
873 		return B_NO_MEMORY;
874 	}
875 	ssize_t rd = inSource->Read(bitsRowData, bitsRowBytes);
876 	while (rd == bitsRowBytes) {
877 		uint32 bmppixcol = 0;
878 		memset(bmpRowData, 0, bmpRowBytes);
879 		for (int32 i = 0; (bmppixcol < msheader.width) &&
880 			(i < bitsRowBytes); i++) {
881 			// process each byte in the row
882 			uint8 pixels = bitsRowData[i];
883 			for (uint8 compbit = 128; (bmppixcol < msheader.width) &&
884 				compbit; compbit >>= 1) {
885 				// for each bit in the current byte, convert to a BMP palette
886 				// index and store that in the bmpRowData
887 				uint8 index;
888 				if (pixels & compbit)
889 					// 1 == black
890 					index = 1;
891 				else
892 					// 0 == white
893 					index = 0;
894 				bmpRowData[bmppixcol / pixelsPerByte] |=
895 					index << (7 - (bmppixcol % pixelsPerByte));
896 				bmppixcol++;
897 			}
898 		}
899 
900 		outDestination->Write(bmpRowData, bmpRowBytes);
901 		bmppixrow++;
902 		// if I've read all of the pixel data, break
903 		// out of the loop so I don't try to read
904 		// non-pixel data
905 		if (bmppixrow == msheader.height)
906 			break;
907 
908 		inSource->Seek(bitsRowBytes * -2, SEEK_CUR);
909 		rd = inSource->Read(bitsRowData, bitsRowBytes);
910 	} // while (rd == bitsRowBytes)
911 
912 	delete[] bmpRowData;
913 	delete[] bitsRowData;
914 
915 	return B_OK;
916 }
917 
918 // ---------------------------------------------------------------
919 // write_bmp_headers
920 //
921 // Writes the MS BMP headers (fileHeader and msheader)
922 // to outDestination.
923 //
924 // Preconditions:
925 //
926 // Parameters:	outDestination,	where the headers are written to
927 //
928 // 				fileHeader, BMP file header data
929 //
930 //				msheader, BMP info header data
931 //
932 // Postconditions:
933 //
934 // Returns: B_ERROR, if something went wrong
935 //
936 // B_OK, if there were no problems writing out the headers
937 // ---------------------------------------------------------------
938 status_t
939 write_bmp_headers(BPositionIO *outDestination, BMPFileHeader &fileHeader,
940 	MSInfoHeader &msheader)
941 {
942 	uint8 bmpheaders[54];
943 	memcpy(bmpheaders, &fileHeader.magic, sizeof(uint16));
944 	memcpy(bmpheaders + 2, &fileHeader.fileSize, sizeof(uint32));
945 	memcpy(bmpheaders + 6, &fileHeader.reserved, sizeof(uint32));
946 	memcpy(bmpheaders + 10, &fileHeader.dataOffset, sizeof(uint32));
947 	memcpy(bmpheaders + 14, &msheader, sizeof(msheader));
948 	if (swap_data(B_UINT16_TYPE, bmpheaders, 2,
949 		B_SWAP_HOST_TO_LENDIAN) != B_OK)
950 		return B_ERROR;
951 	if (swap_data(B_UINT32_TYPE, bmpheaders + 2, 12,
952 		B_SWAP_HOST_TO_LENDIAN) != B_OK)
953 		return B_ERROR;
954 	if (swap_data(B_UINT32_TYPE, bmpheaders + 14,
955 		sizeof(MSInfoHeader), B_SWAP_HOST_TO_LENDIAN) != B_OK)
956 		return B_ERROR;
957 	if (outDestination->Write(bmpheaders, 54) != 54)
958 		return B_ERROR;
959 
960 	return B_OK;
961 }
962 
963 // ---------------------------------------------------------------
964 // translate_from_bits
965 //
966 // Convert the data in inSource from the Be Bitmap format ('bits')
967 // to the format specified in outType (either bits or BMP).
968 //
969 // Preconditions:
970 //
971 // Parameters:	inSource,	the bits data to translate
972 //
973 //				outType,	the type of data to convert to
974 //
975 //				outDestination,	where the output is written to
976 //
977 // Postconditions:
978 //
979 // Returns: B_NO_TRANSLATOR,	if the data is not in a supported
980 //								format
981 //
982 // B_ERROR, if there was an error allocating memory or some other
983 //			error
984 //
985 // B_OK, if successfully translated the data from the bits format
986 // ---------------------------------------------------------------
987 status_t
988 BMPTranslator::translate_from_bits(BPositionIO *inSource, uint32 outType,
989 	BPositionIO *outDestination)
990 {
991 	bool bheaderonly, bdataonly;
992 	bheaderonly = bdataonly = false;
993 
994 	TranslatorBitmap bitsHeader;
995 	status_t result;
996 	result = identify_bits_header(inSource, NULL, &bitsHeader);
997 	if (result != B_OK)
998 		return result;
999 
1000 	// Translate B_TRANSLATOR_BITMAP to B_BMP_FORMAT
1001 	if (outType == B_BMP_FORMAT) {
1002 		// Set up BMP header
1003 		BMPFileHeader fileHeader;
1004 		fileHeader.magic = 'MB';
1005 		fileHeader.reserved = 0;
1006 
1007 		MSInfoHeader msheader;
1008 		msheader.size = 40;
1009 		msheader.width =
1010 			static_cast<uint32> (bitsHeader.bounds.Width() + 1);
1011 		msheader.height =
1012 			static_cast<uint32> (bitsHeader.bounds.Height() + 1);
1013 		msheader.planes = 1;
1014 		msheader.xpixperm = 2835; // 72 dpi horizontal
1015 		msheader.ypixperm = 2835; // 72 dpi vertical
1016 		msheader.colorsused = 0;
1017 		msheader.colorsimportant = 0;
1018 
1019 		// determine fileSize / imagesize
1020 		switch (bitsHeader.colors) {
1021 			case B_RGB32:
1022 			case B_RGB32_BIG:
1023 			case B_RGBA32:
1024 			case B_RGBA32_BIG:
1025 			case B_RGB24:
1026 			case B_RGB24_BIG:
1027 			case B_RGB16:
1028 			case B_RGB16_BIG:
1029 			case B_RGB15:
1030 			case B_RGB15_BIG:
1031 			case B_RGBA15:
1032 			case B_RGBA15_BIG:
1033 			case B_CMYK32:
1034 			case B_CMY32:
1035 			case B_CMYA32:
1036 			case B_CMY24:
1037 
1038 				fileHeader.dataOffset = 54;
1039 				msheader.bitsperpixel = 24;
1040 				msheader.compression = BMP_NO_COMPRESS;
1041 				msheader.imagesize = get_rowbytes(msheader.width, 24) *
1042 					msheader.height;
1043 				fileHeader.fileSize = fileHeader.dataOffset +
1044 					msheader.imagesize;
1045 
1046 				break;
1047 
1048 			case B_CMAP8:
1049 			case B_GRAY8:
1050 
1051 				msheader.colorsused = 256;
1052 				msheader.colorsimportant = 256;
1053 				fileHeader.dataOffset = 54 + (4 * 256);
1054 				msheader.bitsperpixel = 8;
1055 				msheader.compression = BMP_NO_COMPRESS;
1056 				msheader.imagesize = get_rowbytes(msheader.width,
1057 					msheader.bitsperpixel) * msheader.height;
1058 				fileHeader.fileSize = fileHeader.dataOffset +
1059 					msheader.imagesize;
1060 
1061 				break;
1062 
1063 			case B_GRAY1:
1064 
1065 				msheader.colorsused = 2;
1066 				msheader.colorsimportant = 2;
1067 				fileHeader.dataOffset = 62;
1068 				msheader.bitsperpixel = 1;
1069 				msheader.compression = BMP_NO_COMPRESS;
1070 				msheader.imagesize = get_rowbytes(msheader.width,
1071 					msheader.bitsperpixel) * msheader.height;
1072 				fileHeader.fileSize = fileHeader.dataOffset +
1073 					msheader.imagesize;
1074 
1075 				break;
1076 
1077 			default:
1078 				return B_NO_TRANSLATOR;
1079 		}
1080 
1081 		// write out the BMP headers
1082 		if (bheaderonly || (!bheaderonly && !bdataonly)) {
1083 			result = write_bmp_headers(outDestination, fileHeader, msheader);
1084 			if (result != B_OK)
1085 				return result;
1086 		}
1087 		if (bheaderonly)
1088 			// if user only wants the header, bail out
1089 			// before the data is written
1090 			return result;
1091 
1092 		// write out the BMP pixel data
1093 		switch (bitsHeader.colors) {
1094 			case B_RGB32:
1095 			case B_RGB32_BIG:
1096 			case B_RGBA32:
1097 			case B_RGBA32_BIG:
1098 			case B_RGB24:
1099 			case B_RGB24_BIG:
1100 			case B_RGB16:
1101 			case B_RGB16_BIG:
1102 			case B_RGB15:
1103 			case B_RGB15_BIG:
1104 			case B_RGBA15:
1105 			case B_RGBA15_BIG:
1106 			case B_CMYK32:
1107 			case B_CMY32:
1108 			case B_CMYA32:
1109 			case B_CMY24:
1110 				return translate_from_bits_to_bmp24(inSource, outDestination,
1111 					bitsHeader.colors, msheader);
1112 
1113 			case B_CMAP8:
1114 			case B_GRAY8:
1115 			{
1116 				// write palette to BMP file
1117 				uint8 pal[1024];
1118 				uint8* palHandle = pal;
1119 				if (bitsHeader.colors == B_CMAP8) {
1120 					// write system palette
1121 					const color_map *pmap = system_colors();
1122 					if (!pmap)
1123 						return B_ERROR;
1124 					for (int32 i = 0; i < 256; i++) {
1125 						rgb_color c = pmap->color_list[i];
1126 						palHandle[0] = c.blue;
1127 						palHandle[1] = c.green;
1128 						palHandle[2] = c.red;
1129 						palHandle[3] = c.alpha;
1130 						palHandle += 4;
1131 					}
1132 				} else {
1133 					// write gray palette
1134 					for (int32 i = 0; i < 256; i++) {
1135 						palHandle[0] = i;
1136 						palHandle[1] = i;
1137 						palHandle[2] = i;
1138 						palHandle[3] = 255;
1139 						palHandle += 4;
1140 					}
1141 				}
1142 				ssize_t written = outDestination->Write(pal, 1024);
1143 				if (written < 0)
1144 					return written;
1145 				if (written != 1024)
1146 					return B_ERROR;
1147 
1148 				return translate_from_bits8_to_bmp8(inSource, outDestination,
1149 					bitsHeader.rowBytes, msheader);
1150 			}
1151 
1152 			case B_GRAY1:
1153 			{
1154 				// write monochrome palette to the BMP file
1155 				const uint32 monopal[] = { 0x00ffffff, 0x00000000 };
1156 				ssize_t written = outDestination->Write(monopal, 8);
1157 				if (written < 0)
1158 					return written;
1159 				if (written != 8)
1160 					return B_ERROR;
1161 
1162 				return translate_from_bits1_to_bmp1(inSource, outDestination,
1163 					bitsHeader.rowBytes, msheader);
1164 			}
1165 
1166 			default:
1167 				return B_NO_TRANSLATOR;
1168 		}
1169 	} else
1170 		return B_NO_TRANSLATOR;
1171 }
1172 
1173 // ---------------------------------------------------------------
1174 // translate_from_bmpnpal_to_bits
1175 //
1176 // Translates a non-palette BMP from inSource to the B_RGB32
1177 // bits format.
1178 //
1179 // Preconditions:
1180 //
1181 // Parameters: inSource,	the BMP data to be translated
1182 //
1183 // outDestination,	where the bits data will be written to
1184 //
1185 // msheader,	header information about the BMP to be written
1186 //
1187 // Postconditions:
1188 //
1189 // Returns: B_ERROR, if there is an error allocating memory
1190 //
1191 // B_OK, if all went well
1192 // ---------------------------------------------------------------
1193 status_t
1194 translate_from_bmpnpal_to_bits(BPositionIO *inSource,
1195 	BPositionIO *outDestination, MSInfoHeader &msheader)
1196 {
1197 	int32 bitsRowBytes = msheader.width * 4;
1198 	int32 bmpBytesPerPixel = msheader.bitsperpixel / 8;
1199 	int32 bmpRowBytes =
1200 		get_rowbytes(msheader.width, msheader.bitsperpixel);
1201 
1202 	// Setup outDestination so that it can be written to
1203 	// from the end of the file to the beginning instead of
1204 	// the other way around
1205 	off_t bitsFileSize = (bitsRowBytes * msheader.height) +
1206 		sizeof(TranslatorBitmap);
1207 	if (outDestination->SetSize(bitsFileSize) != B_OK) {
1208 		// This call should work for BFile and BMallocIO objects,
1209 		// but may not work for other BPositionIO based types
1210 		ERROR("BMPTranslator::translate_from_bmpnpal_to_bits() - failed to SetSize()\n");
1211 		return B_ERROR;
1212 	}
1213 	off_t bitsoffset = (msheader.height - 1) * bitsRowBytes;
1214 	outDestination->Seek(bitsoffset, SEEK_CUR);
1215 
1216 	// allocate row buffers
1217 	uint8 *bmpRowData = new (nothrow) uint8[bmpRowBytes];
1218 	if (!bmpRowData)
1219 		return B_NO_MEMORY;
1220 	uint8 *bitsRowData = new (nothrow) uint8[bitsRowBytes];
1221 	if (!bitsRowData) {
1222 		delete[] bmpRowData;
1223 		return B_NO_MEMORY;
1224 	}
1225 
1226 	// perform the actual translation
1227 	if (bmpBytesPerPixel != 4) {
1228 		// clean out buffer so that we don't have to write
1229 		// alpha for each row
1230 		memset(bitsRowData, 0xff, bitsRowBytes);
1231 	}
1232 
1233 	status_t ret = B_OK;
1234 
1235 	for (uint32 y = 0; y < msheader.height; y++) {
1236 		ssize_t read = inSource->Read(bmpRowData, bmpRowBytes);
1237 		if (read != bmpRowBytes) {
1238 			// break on read error
1239 			if (read >= 0)
1240 				ret = B_ERROR;
1241 			else
1242 				ret = read;
1243 			break;
1244 		}
1245 
1246 		if (bmpBytesPerPixel == 4) {
1247 			memcpy(bitsRowData, bmpRowData, bmpRowBytes);
1248 		} else {
1249 			uint8 *pBitsPixel = bitsRowData;
1250 			uint8 *pBmpPixel = bmpRowData;
1251 			for (uint32 i = 0; i < msheader.width; i++) {
1252 				pBitsPixel[0] = pBmpPixel[0];
1253 				pBitsPixel[1] = pBmpPixel[1];
1254 				pBitsPixel[2] = pBmpPixel[2];
1255 				pBitsPixel += 4;
1256 				pBmpPixel += bmpBytesPerPixel;
1257 			}
1258 		}
1259 		// write row and seek backward by two rows
1260 		ssize_t written = outDestination->Write(bitsRowData, bitsRowBytes);
1261 		outDestination->Seek(bitsRowBytes * -2, SEEK_CUR);
1262 
1263 		if (written != bitsRowBytes) {
1264 			// break on write error
1265 			if (written >= 0)
1266 				ret = B_ERROR;
1267 			else
1268 				ret = read;
1269 			break;
1270 		}
1271 	}
1272 
1273 	delete[] bmpRowData;
1274 	delete[] bitsRowData;
1275 
1276 	return ret;
1277 }
1278 
1279 // ---------------------------------------------------------------
1280 // translate_from_bmppal_to_bits
1281 //
1282 // Translates an uncompressed, palette BMP from inSource to
1283 // the B_RGB32 bits format.
1284 //
1285 // Preconditions:
1286 //
1287 // Parameters: inSource,	the BMP data to be translated
1288 //
1289 // outDestination,	where the bits data will be written to
1290 //
1291 // msheader,	header information about the BMP to be written
1292 //
1293 // palette,	BMP palette for the BMP image
1294 //
1295 // frommsformat, true if BMP in inSource is in MS format,
1296 //				 false if it is in OS/2 format
1297 //
1298 // Postconditions:
1299 //
1300 // Returns: B_NO_MEMORY, if there is an error allocating memory
1301 //
1302 // B_OK, if all went well
1303 // ---------------------------------------------------------------
1304 status_t
1305 translate_from_bmppal_to_bits(BPositionIO *inSource,
1306 	BPositionIO *outDestination, MSInfoHeader &msheader,
1307 	const uint8 *palette, bool frommsformat)
1308 {
1309 	uint16 pixelsPerByte = 8 / msheader.bitsperpixel;
1310 	uint16 bitsPerPixel = msheader.bitsperpixel;
1311 	uint8 palBytesPerPixel;
1312 	if (frommsformat)
1313 		palBytesPerPixel = 4;
1314 	else
1315 		palBytesPerPixel = 3;
1316 
1317 	uint8 mask = 1;
1318 	mask = (mask << bitsPerPixel) - 1;
1319 
1320 	int32 bmpRowBytes =
1321 		get_rowbytes(msheader.width, msheader.bitsperpixel);
1322 	uint32 bmppixrow = 0;
1323 
1324 	// Setup outDestination so that it can be written to
1325 	// from the end of the file to the beginning instead of
1326 	// the other way around
1327 	int32 bitsRowBytes = msheader.width * 4;
1328 	off_t bitsFileSize = (bitsRowBytes * msheader.height) +
1329 		sizeof(TranslatorBitmap);
1330 	if (outDestination->SetSize(bitsFileSize) != B_OK)
1331 		// This call should work for BFile and BMallocIO objects,
1332 		// but may not work for other BPositionIO based types
1333 		return B_ERROR;
1334 	off_t bitsoffset = ((msheader.height - 1) * bitsRowBytes);
1335 	outDestination->Seek(bitsoffset, SEEK_CUR);
1336 
1337 	// allocate row buffers
1338 	uint8 *bmpRowData = new (nothrow) uint8[bmpRowBytes];
1339 	if (!bmpRowData)
1340 		return B_NO_MEMORY;
1341 	uint8 *bitsRowData = new (nothrow) uint8[bitsRowBytes];
1342 	if (!bitsRowData) {
1343 		delete[] bmpRowData;
1344 		return B_NO_MEMORY;
1345 	}
1346 	memset(bitsRowData, 0xff, bitsRowBytes);
1347 	ssize_t rd = inSource->Read(bmpRowData, bmpRowBytes);
1348 	while (rd == static_cast<ssize_t>(bmpRowBytes)) {
1349 		for (uint32 i = 0; i < msheader.width; i++) {
1350 			uint8 indices = (bmpRowData + (i / pixelsPerByte))[0];
1351 			uint8 index;
1352 			index = (indices >>
1353 				(bitsPerPixel * ((pixelsPerByte - 1) -
1354 					(i % pixelsPerByte)))) & mask;
1355 			memcpy(bitsRowData + (i * 4),
1356 				palette + (index * palBytesPerPixel), 3);
1357 		}
1358 
1359 		outDestination->Write(bitsRowData, bitsRowBytes);
1360 		bmppixrow++;
1361 		// if I've read all of the pixel data, break
1362 		// out of the loop so I don't try to read
1363 		// non-pixel data
1364 		if (bmppixrow == msheader.height)
1365 			break;
1366 
1367 		outDestination->Seek(bitsRowBytes * -2, SEEK_CUR);
1368 		rd = inSource->Read(bmpRowData, bmpRowBytes);
1369 	}
1370 
1371 	delete[] bmpRowData;
1372 	delete[] bitsRowData;
1373 
1374 	return B_OK;
1375 }
1376 
1377 
1378 // ---------------------------------------------------------------
1379 // pixelcpy
1380 //
1381 // Copies count 32-bit pixels with a color value of pixel to dest.
1382 //
1383 // Preconditions:
1384 //
1385 // Parameters:	dest,	where the pixel data will be copied to
1386 //
1387 //				pixel,	the 32-bit color value to copy to dest
1388 //						count times
1389 //
1390 //				count,	the number of times pixel is copied to
1391 //						dest
1392 //
1393 // Postconditions:
1394 //
1395 // Returns:
1396 // ---------------------------------------------------------------
1397 void
1398 pixelcpy(uint8 *dest, uint32 pixel, uint32 count)
1399 {
1400 	for (uint32 i = 0; i < count; i++) {
1401 		memcpy(dest, &pixel, 3);
1402 		dest += 4;
1403 	}
1404 }
1405 
1406 // ---------------------------------------------------------------
1407 // translate_from_bmppalr_to_bits
1408 //
1409 // Translates an RLE compressed, palette BMP from inSource to
1410 // the B_RGB32 bits format. Currently, this code is not as
1411 // memory effcient as it could be. It assumes that the BMP
1412 // from inSource is relatively small.
1413 //
1414 // Preconditions:
1415 //
1416 // Parameters: inSource,	the BMP data to be translated
1417 //
1418 // outDestination,	where the bits data will be written to
1419 //
1420 // datasize, number of bytes of data needed for the bits output
1421 //
1422 // msheader,	header information about the BMP to be written
1423 //
1424 // palette,	BMP palette for data in inSource
1425 //
1426 // Postconditions:
1427 //
1428 // Returns: B_ERROR, if there is an error allocating memory
1429 //
1430 // B_OK, if all went well
1431 // ---------------------------------------------------------------
1432 status_t
1433 translate_from_bmppalr_to_bits(BPositionIO *inSource,
1434 	BPositionIO *outDestination, int32 datasize, MSInfoHeader &msheader,
1435 	const uint8 *palette)
1436 {
1437 	uint16 pixelsPerByte = 8 / msheader.bitsperpixel;
1438 	uint16 bitsPerPixel = msheader.bitsperpixel;
1439 	uint8 mask = (1 << bitsPerPixel) - 1;
1440 
1441 	uint8 count, indices, index;
1442 	// Setup outDestination so that it can be written to
1443 	// from the end of the file to the beginning instead of
1444 	// the other way around
1445 	int32 bitsRowBytes = msheader.width * 4;
1446 	off_t bitsFileSize = (bitsRowBytes * msheader.height) +
1447 		sizeof(TranslatorBitmap);
1448 	if (outDestination->SetSize(bitsFileSize) != B_OK)
1449 		// This call should work for BFile and BMallocIO objects,
1450 		// but may not work for other BPositionIO based types
1451 		return B_ERROR;
1452 	uint8 *bitsRowData = new (nothrow) uint8[bitsRowBytes];
1453 	if (!bitsRowData)
1454 		return B_NO_MEMORY;
1455 	memset(bitsRowData, 0xff, bitsRowBytes);
1456 	uint32 bmppixcol = 0, bmppixrow = 0;
1457 	uint32 defaultcolor = *(uint32*)palette;
1458 	// set bits output to last row in the image
1459 	off_t bitsoffset = ((msheader.height - (bmppixrow + 1)) * bitsRowBytes) +
1460 		(bmppixcol * 4);
1461 	outDestination->Seek(bitsoffset, SEEK_CUR);
1462 	ssize_t rd = inSource->Read(&count, 1);
1463 	while (rd > 0) {
1464 		// repeated color
1465 		if (count) {
1466 			// abort if all of the pixels in the row
1467 			// have already been drawn to
1468 			if (bmppixcol == msheader.width) {
1469 				rd = -1;
1470 				break;
1471 			}
1472 			// if count is greater than the number of
1473 			// pixels remaining in the current row,
1474 			// only process the correct number of pixels
1475 			// remaining in the row
1476 			if (count + bmppixcol > msheader.width)
1477 				count = msheader.width - bmppixcol;
1478 
1479 			rd = inSource->Read(&indices, 1);
1480 			if (rd != 1) {
1481 				rd = -1;
1482 				break;
1483 			}
1484 			for (uint8 i = 0; i < count; i++) {
1485 				index = (indices >> (bitsPerPixel * ((pixelsPerByte - 1) -
1486 					(i % pixelsPerByte)))) & mask;
1487 				memcpy(bitsRowData + (bmppixcol*4), palette + (index*4), 3);
1488 				bmppixcol++;
1489 			}
1490 		// special code
1491 		} else {
1492 			uint8 code;
1493 			rd = inSource->Read(&code, 1);
1494 			if (rd != 1) {
1495 				rd = -1;
1496 				break;
1497 			}
1498 			switch (code) {
1499 				// end of line
1500 				case 0:
1501 					// if there are columns remaing on this
1502 					// line, set them to the color at index zero
1503 					if (bmppixcol < msheader.width)
1504 						pixelcpy(bitsRowData + (bmppixcol * 4),
1505 							defaultcolor, msheader.width - bmppixcol);
1506 					outDestination->Write(bitsRowData, bitsRowBytes);
1507 					bmppixcol = 0;
1508 					bmppixrow++;
1509 					if (bmppixrow < msheader.height)
1510 						outDestination->Seek(bitsRowBytes * -2, SEEK_CUR);
1511 					break;
1512 
1513 				// end of bitmap
1514 				case 1:
1515 					// if at the end of a row
1516 					if (bmppixcol == msheader.width) {
1517 						outDestination->Write(bitsRowData, bitsRowBytes);
1518 						bmppixcol = 0;
1519 						bmppixrow++;
1520 						if (bmppixrow < msheader.height)
1521 							outDestination->Seek(bitsRowBytes * -2, SEEK_CUR);
1522 					}
1523 
1524 					while (bmppixrow < msheader.height) {
1525 						pixelcpy(bitsRowData + (bmppixcol * 4), defaultcolor,
1526 							msheader.width - bmppixcol);
1527 						outDestination->Write(bitsRowData, bitsRowBytes);
1528 						bmppixcol = 0;
1529 						bmppixrow++;
1530 						if (bmppixrow < msheader.height)
1531 							outDestination->Seek(bitsRowBytes * -2, SEEK_CUR);
1532 					}
1533 					rd = 0;
1534 						// break out of while loop
1535 					break;
1536 
1537 				// delta, skip several rows and/or columns and
1538 				// fill the skipped pixels with the default color
1539 				case 2:
1540 				{
1541 					uint8 da[2], lastcol, dx, dy;
1542 					rd = inSource->Read(da, 2);
1543 					if (rd != 2) {
1544 						rd = -1;
1545 						break;
1546 					}
1547 					dx = da[0];
1548 					dy = da[1];
1549 
1550 					// abort if dx or dy is too large
1551 					if ((dx + bmppixcol >= msheader.width) ||
1552 						(dy + bmppixrow >= msheader.height)) {
1553 						rd = -1;
1554 						break;
1555 					}
1556 
1557 					lastcol = bmppixcol;
1558 
1559 					// set all pixels to the first entry in
1560 					// the palette, for the number of rows skipped
1561 					while (dy > 0) {
1562 						pixelcpy(bitsRowData + (bmppixcol * 4), defaultcolor,
1563 							msheader.width - bmppixcol);
1564 						outDestination->Write(bitsRowData, bitsRowBytes);
1565 						bmppixcol = 0;
1566 						bmppixrow++;
1567 						dy--;
1568 						outDestination->Seek(bitsRowBytes * -2, SEEK_CUR);
1569 					}
1570 
1571 					if (bmppixcol < static_cast<uint32>(lastcol + dx)) {
1572 						pixelcpy(bitsRowData + (bmppixcol * 4), defaultcolor,
1573 							dx + lastcol - bmppixcol);
1574 						bmppixcol = dx + lastcol;
1575 					}
1576 
1577 					break;
1578 				}
1579 
1580 				// code >= 3
1581 				// read code uncompressed indices
1582 				default:
1583 					// abort if all of the pixels in the row
1584 					// have already been drawn to
1585 					if (bmppixcol == msheader.width) {
1586 						rd = -1;
1587 						break;
1588 					}
1589 					// if code is greater than the number of
1590 					// pixels remaining in the current row,
1591 					// only process the correct number of pixels
1592 					// remaining in the row
1593 					if (code + bmppixcol > msheader.width)
1594 						code = msheader.width - bmppixcol;
1595 
1596 					uint8 uncomp[256];
1597 					int32 padding;
1598 					if (!(code % pixelsPerByte))
1599 						padding = (code / pixelsPerByte) % 2;
1600 					else
1601 						padding = ((code + pixelsPerByte -
1602 							(code % pixelsPerByte)) / pixelsPerByte) % 2;
1603 					int32 uncompBytes = (code / pixelsPerByte) +
1604 						((code % pixelsPerByte) ? 1 : 0) + padding;
1605 					rd = inSource->Read(uncomp, uncompBytes);
1606 					if (rd != uncompBytes) {
1607 						rd = -1;
1608 						break;
1609 					}
1610 					for (uint8 i = 0; i < code; i++) {
1611 						indices = (uncomp + (i / pixelsPerByte))[0];
1612 						index = (indices >>
1613 							(bitsPerPixel * ((pixelsPerByte - 1) -
1614 								(i % pixelsPerByte)))) & mask;
1615 						memcpy(bitsRowData + (bmppixcol * 4),
1616 							palette + (index * 4), 3);
1617 						bmppixcol++;
1618 					}
1619 
1620 					break;
1621 			}
1622 		}
1623 		if (rd > 0)
1624 			rd = inSource->Read(&count, 1);
1625 	}
1626 
1627 	delete[] bitsRowData;
1628 
1629 	if (!rd)
1630 		return B_OK;
1631 	else
1632 		return B_NO_TRANSLATOR;
1633 }
1634 
1635 // ---------------------------------------------------------------
1636 // translate_from_bmp
1637 //
1638 // Convert the data in inSource from the BMP format
1639 // to the format specified in outType (either bits or BMP).
1640 //
1641 // Preconditions:
1642 //
1643 // Parameters:	inSource,	the bits data to translate
1644 //
1645 //				outType,	the type of data to convert to
1646 //
1647 //				outDestination,	where the output is written to
1648 //
1649 // Postconditions:
1650 //
1651 // Returns: B_NO_TRANSLATOR,	if the data is not in a supported
1652 //								format
1653 //
1654 // B_ERROR, if there was an error allocating memory or some other
1655 //			error
1656 //
1657 // B_OK, if successfully translated the data from the bits format
1658 // ---------------------------------------------------------------
1659 status_t
1660 BMPTranslator::translate_from_bmp(BPositionIO *inSource, uint32 outType,
1661 	BPositionIO *outDestination)
1662 {
1663 	bool bheaderonly, bdataonly;
1664 	bheaderonly = bdataonly = false;
1665 
1666 	BMPFileHeader fileHeader;
1667 	MSInfoHeader msheader;
1668 	bool frommsformat;
1669 	off_t os2skip = 0;
1670 
1671 	status_t result;
1672 	result = identify_bmp_header(inSource, NULL, &fileHeader, &msheader,
1673 		&frommsformat, &os2skip);
1674 	if (result != B_OK) {
1675 		INFO("BMPTranslator::translate_from_bmp() - identify_bmp_header failed\n");
1676 		return result;
1677 	}
1678 
1679 	// if the user wants to translate a BMP to a BMP, easy enough :)
1680 	if (outType == B_BMP_FORMAT) {
1681 		// write out the BMP headers
1682 		if (bheaderonly || (!bheaderonly && !bdataonly)) {
1683 			result = write_bmp_headers(outDestination, fileHeader, msheader);
1684 			if (result != B_OK)
1685 				return result;
1686 		}
1687 		if (bheaderonly)
1688 			// if the user only wants the header,
1689 			// bail before it is written
1690 			return result;
1691 
1692 		uint8 buf[1024];
1693 		ssize_t rd;
1694 		uint32 rdtotal = 54;
1695 		if (!frommsformat && (msheader.bitsperpixel == 1 ||
1696 			msheader.bitsperpixel == 4 || msheader.bitsperpixel == 8)) {
1697 			// if OS/2 paletted format, convert palette to MS format
1698 			uint16 ncolors = 1 << msheader.bitsperpixel;
1699 			rd = inSource->Read(buf, ncolors * 3);
1700 			if (rd != ncolors * 3)
1701 				return B_NO_TRANSLATOR;
1702 			uint8 mspalent[4] = {0, 0, 0, 0};
1703 			for (uint16 i = 0; i < ncolors; i++) {
1704 				memcpy(mspalent, buf + (i * 3), 3);
1705 				outDestination->Write(mspalent, 4);
1706 			}
1707 			rdtotal = fileHeader.dataOffset;
1708 		}
1709 		// if there is junk between the OS/2 headers and
1710 		// the actual data, skip it
1711 		if (!frommsformat && os2skip)
1712 			inSource->Seek(os2skip, SEEK_CUR);
1713 
1714 		rd = min((size_t)1024, fileHeader.fileSize - rdtotal);
1715 		rd = inSource->Read(buf, rd);
1716 		while (rd > 0) {
1717 			outDestination->Write(buf, rd);
1718 			rdtotal += rd;
1719 			rd = min((size_t)1024, fileHeader.fileSize - rdtotal);
1720 			rd = inSource->Read(buf, rd);
1721 		}
1722 		if (rd == 0)
1723 			return B_OK;
1724 		else
1725 			return B_ERROR;
1726 
1727 	// if translating a BMP to a Be Bitmap
1728 	} else if (outType == B_TRANSLATOR_BITMAP) {
1729 		TranslatorBitmap bitsHeader;
1730 		bitsHeader.magic = B_TRANSLATOR_BITMAP;
1731 		bitsHeader.bounds.left = 0;
1732 		bitsHeader.bounds.top = 0;
1733 		bitsHeader.bounds.right = msheader.width - 1;
1734 		bitsHeader.bounds.bottom = msheader.height - 1;
1735 
1736 		// read in palette and/or skip non-BMP data
1737 		uint8 bmppalette[1024];
1738 		off_t nskip = 0;
1739 		if (msheader.bitsperpixel == 1 ||
1740 			msheader.bitsperpixel == 4 ||
1741 			msheader.bitsperpixel == 8) {
1742 
1743 			uint8 palBytesPerPixel;
1744 			if (frommsformat)
1745 				palBytesPerPixel = 4;
1746 			else
1747 				palBytesPerPixel = 3;
1748 
1749 			if (!msheader.colorsused)
1750 				msheader.colorsused = 1 << msheader.bitsperpixel;
1751 
1752 			if (inSource->Read(bmppalette, msheader.colorsused *
1753 				palBytesPerPixel) !=
1754 					(off_t) msheader.colorsused * palBytesPerPixel)
1755 				return B_NO_TRANSLATOR;
1756 
1757 			// skip over non-BMP data
1758 			if (frommsformat) {
1759 				if (fileHeader.dataOffset > (msheader.colorsused *
1760 					palBytesPerPixel) + 54)
1761 					nskip = fileHeader.dataOffset -
1762 						((msheader.colorsused * palBytesPerPixel) + 54);
1763 			} else
1764 				nskip = os2skip;
1765 		} else if (fileHeader.dataOffset > 54)
1766 			// skip over non-BMP data
1767 			nskip = fileHeader.dataOffset - 54;
1768 
1769 		if (nskip > 0 && inSource->Seek(nskip, SEEK_CUR) < 0)
1770 			return B_NO_TRANSLATOR;
1771 
1772 		bitsHeader.rowBytes = msheader.width * 4;
1773 		bitsHeader.colors = B_RGB32;
1774 		int32 datasize = bitsHeader.rowBytes * msheader.height;
1775 		bitsHeader.dataSize = datasize;
1776 
1777 		// write out Be's Bitmap header
1778 		if (bheaderonly || (!bheaderonly && !bdataonly)) {
1779 			if (swap_data(B_UINT32_TYPE, &bitsHeader,
1780 				sizeof(TranslatorBitmap), B_SWAP_HOST_TO_BENDIAN) != B_OK)
1781 				return B_ERROR;
1782 			outDestination->Write(&bitsHeader, sizeof(TranslatorBitmap));
1783 		}
1784 		if (bheaderonly)
1785 			// if the user only wants the header,
1786 			// bail before the data is written
1787 			return B_OK;
1788 
1789 		// write out the actual image data
1790 		switch (msheader.bitsperpixel) {
1791 			case 32:
1792 			case 24:
1793 				return translate_from_bmpnpal_to_bits(inSource,
1794 					outDestination, msheader);
1795 
1796 			case 8:
1797 				// 8 bit BMP with NO compression
1798 				if (msheader.compression == BMP_NO_COMPRESS)
1799 					return translate_from_bmppal_to_bits(inSource,
1800 						outDestination, msheader, bmppalette, frommsformat);
1801 
1802 				// 8 bit RLE compressed BMP
1803 				else if (msheader.compression == BMP_RLE8_COMPRESS)
1804 					return translate_from_bmppalr_to_bits(inSource,
1805 						outDestination, datasize, msheader, bmppalette);
1806 				else
1807 					return B_NO_TRANSLATOR;
1808 
1809 			case 4:
1810 				// 4 bit BMP with NO compression
1811 				if (!msheader.compression)
1812 					return translate_from_bmppal_to_bits(inSource,
1813 						outDestination, msheader, bmppalette, frommsformat);
1814 
1815 				// 4 bit RLE compressed BMP
1816 				else if (msheader.compression == BMP_RLE4_COMPRESS)
1817 					return translate_from_bmppalr_to_bits(inSource,
1818 						outDestination, datasize, msheader, bmppalette);
1819 				else
1820 					return B_NO_TRANSLATOR;
1821 
1822 			case 1:
1823 				return translate_from_bmppal_to_bits(inSource,
1824 					outDestination, msheader, bmppalette, frommsformat);
1825 
1826 			default:
1827 				return B_NO_TRANSLATOR;
1828 		}
1829 
1830 	} else
1831 		return B_NO_TRANSLATOR;
1832 }
1833 
1834 // ---------------------------------------------------------------
1835 // DerivedTranslate
1836 //
1837 // Translates the data in inSource to the type outType and stores
1838 // the translated data in outDestination.
1839 //
1840 // Preconditions:
1841 //
1842 // Parameters:	inSource,	the data to be translated
1843 //
1844 //				inInfo,	hint about the data in inSource (not used)
1845 //
1846 //				ioExtension,	configuration options for the
1847 //								translator
1848 //
1849 //				outType,	the type to convert inSource to
1850 //
1851 //				outDestination,	where the translated data is
1852 //								put
1853 //
1854 //				baseType, indicates whether inSource is in the
1855 //				          bits format, not in the bits format or
1856 //				          is unknown
1857 //
1858 // Postconditions:
1859 //
1860 // Returns: B_BAD_VALUE, if the options in ioExtension are bad
1861 //
1862 // B_NO_TRANSLATOR, if this translator doesn't understand the data
1863 //
1864 // B_ERROR, if there was an error allocating memory or converting
1865 //          data
1866 //
1867 // B_OK, if all went well
1868 // ---------------------------------------------------------------
1869 status_t
1870 BMPTranslator::DerivedTranslate(BPositionIO *inSource,
1871 		const translator_info *inInfo, BMessage *ioExtension,
1872 		uint32 outType, BPositionIO *outDestination, int32 baseType)
1873 {
1874 	if (baseType == 1)
1875 		// if inSource is in bits format
1876 		return translate_from_bits(inSource, outType, outDestination);
1877 	else if (baseType == 0)
1878 		// if inSource is NOT in bits format
1879 		return translate_from_bmp(inSource, outType, outDestination);
1880 	else
1881 		return B_NO_TRANSLATOR;
1882 }
1883 
1884 BView *
1885 BMPTranslator::NewConfigView(TranslatorSettings *settings)
1886 {
1887 	return new BMPView(BRect(0, 0, 225, 175), "BMPTranslator Settings",
1888 		B_FOLLOW_ALL, B_WILL_DRAW, settings);
1889 }
1890