xref: /haiku/src/add-ons/translators/bmp/BMPTranslator.cpp (revision adb0d19d561947362090081e81d90dde59142026)
1 /*
2  * Copyright 2002-2006, Haiku.
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 			return B_ERROR;
632 	}
633 	while (rd == static_cast<ssize_t>(bitsRowBytes)) {
634 
635 		for (uint32 i = 0; i < msheader.width; i++) {
636 			uint8 *bitspixel, *bmppixel;
637 			uint16 val;
638 			switch (fromspace) {
639 				case B_RGB32:
640 				case B_RGBA32:
641 				case B_RGB24:
642 					memcpy(bmpRowData + (i * 3),
643 						bitsRowData + (i * bitsBytesPerPixel), 3);
644 					break;
645 
646 				case B_RGB16:
647 				case B_RGB16_BIG:
648 					bitspixel = bitsRowData + (i * bitsBytesPerPixel);
649 					bmppixel = bmpRowData + (i * 3);
650 					if (fromspace == B_RGB16)
651 						val = bitspixel[0] + (bitspixel[1] << 8);
652 					else
653 						val = bitspixel[1] + (bitspixel[0] << 8);
654 					bmppixel[0] =
655 						((val & 0x1f) << 3) | ((val & 0x1f) >> 2);
656 					bmppixel[1] =
657 						((val & 0x7e0) >> 3) | ((val & 0x7e0) >> 9);
658 					bmppixel[2] =
659 						((val & 0xf800) >> 8) | ((val & 0xf800) >> 13);
660 					break;
661 
662 				case B_RGB15:
663 				case B_RGB15_BIG:
664 				case B_RGBA15:
665 				case B_RGBA15_BIG:
666 					// NOTE: the alpha data for B_RGBA15* is not used
667 					bitspixel = bitsRowData + (i * bitsBytesPerPixel);
668 					bmppixel = bmpRowData + (i * 3);
669 					if (fromspace == B_RGB15 || fromspace == B_RGBA15)
670 						val = bitspixel[0] + (bitspixel[1] << 8);
671 					else
672 						val = bitspixel[1] + (bitspixel[0] << 8);
673 					bmppixel[0] =
674 						((val & 0x1f) << 3) | ((val & 0x1f) >> 2);
675 					bmppixel[1] =
676 						((val & 0x3e0) >> 2) | ((val & 0x3e0) >> 7);
677 					bmppixel[2] =
678 						((val & 0x7c00) >> 7) | ((val & 0x7c00) >> 12);
679 					break;
680 
681 				case B_RGB32_BIG:
682 				case B_RGBA32_BIG:
683 					bitspixel = bitsRowData + (i * bitsBytesPerPixel);
684 					bmppixel = bmpRowData + (i * 3);
685 					bmppixel[0] = bitspixel[3];
686 					bmppixel[1] = bitspixel[2];
687 					bmppixel[2] = bitspixel[1];
688 					break;
689 
690 				case B_RGB24_BIG:
691 					bitspixel = bitsRowData + (i * bitsBytesPerPixel);
692 					bmppixel = bmpRowData + (i * 3);
693 					bmppixel[0] = bitspixel[2];
694 					bmppixel[1] = bitspixel[1];
695 					bmppixel[2] = bitspixel[0];
696 					break;
697 
698 				case B_CMAP8:
699 				{
700 					bitspixel = bitsRowData + (i * bitsBytesPerPixel);
701 					bmppixel = bmpRowData + (i * 3);
702 					rgb_color c = pmap->color_list[bitspixel[0]];
703 					bmppixel[0] = c.blue;
704 					bmppixel[1] = c.green;
705 					bmppixel[2] = c.red;
706 					break;
707 				}
708 
709 				case B_GRAY8:
710 					bitspixel = bitsRowData + (i * bitsBytesPerPixel);
711 					bmppixel = bmpRowData + (i * 3);
712 					bmppixel[0] = bitspixel[0];
713 					bmppixel[1] = bitspixel[0];
714 					bmppixel[2] = bitspixel[0];
715 					break;
716 
717 				case B_CMYK32:
718 				{
719 					bitspixel = bitsRowData + (i * bitsBytesPerPixel);
720 					bmppixel = bmpRowData + (i * 3);
721 
722 					int32 comp = 255 - bitspixel[2] - bitspixel[3];
723 					bmppixel[0] = (comp < 0) ? 0 : comp;
724 
725 					comp = 255 - bitspixel[1] - bitspixel[3];
726 					bmppixel[1] = (comp < 0) ? 0 : comp;
727 
728 					comp = 255 - bitspixel[0] - bitspixel[3];
729 					bmppixel[2] = (comp < 0) ? 0 : comp;
730 					break;
731 				}
732 
733 				case B_CMY32:
734 				case B_CMYA32:
735 				case B_CMY24:
736 					bitspixel = bitsRowData + (i * bitsBytesPerPixel);
737 					bmppixel = bmpRowData + (i * 3);
738 					bmppixel[0] = 255 - bitspixel[2];
739 					bmppixel[1] = 255 - bitspixel[1];
740 					bmppixel[2] = 255 - bitspixel[0];
741 					break;
742 
743 				default:
744 					break;
745 			} // switch (fromspace)
746 		} // for for (uint32 i = 0; i < msheader.width; i++)
747 
748 		outDestination->Write(bmpRowData, bmpRowBytes);
749 		bmppixrow++;
750 		// if I've read all of the pixel data, break
751 		// out of the loop so I don't try to read
752 		// non-pixel data
753 		if (bmppixrow == msheader.height)
754 			break;
755 
756 		inSource->Seek(bitsRowBytes * -2, SEEK_CUR);
757 		rd = inSource->Read(bitsRowData, bitsRowBytes);
758 	} // while (rd == bitsRowBytes)
759 
760 	delete[] bmpRowData;
761 	delete[] bitsRowData;
762 
763 	return B_OK;
764 }
765 
766 // ---------------------------------------------------------------
767 // translate_from_bits8_to_bmp8
768 //
769 // Converts 8-bit Be Bitmaps ('bits') to the MS 8-bit BMP format
770 //
771 // Preconditions:
772 //
773 // Parameters:	inSource,	contains the bits data to convert
774 //
775 //				outDestination,	where the BMP data will be written
776 //
777 //				bitsRowBytes,	number of bytes in one row of
778 //								bits data
779 //
780 //				msheader,	contains information about the BMP
781 //							dimensions and filesize
782 //
783 // Postconditions:
784 //
785 // Returns: B_ERROR,	if memory couldn't be allocated or another
786 //						error occured
787 //
788 // B_OK,	if no errors occurred
789 // ---------------------------------------------------------------
790 status_t
791 translate_from_bits8_to_bmp8(BPositionIO *inSource,
792 	BPositionIO *outDestination, int32 bitsRowBytes, MSInfoHeader &msheader)
793 {
794 	int32 padding = get_padding(msheader.width, msheader.bitsperpixel);
795 	int32 bmpRowBytes =
796 		get_rowbytes(msheader.width, msheader.bitsperpixel);
797 	uint32 bmppixrow = 0;
798 	off_t bitsoffset = ((msheader.height - 1) * bitsRowBytes);
799 	inSource->Seek(bitsoffset, SEEK_CUR);
800 	uint8 *bmpRowData = new (nothrow) uint8[bmpRowBytes];
801 	if (!bmpRowData)
802 		return B_NO_MEMORY;
803 	uint8 *bitsRowData = new (nothrow) uint8[bitsRowBytes];
804 	if (!bitsRowData) {
805 		delete[] bmpRowData;
806 		return B_NO_MEMORY;
807 	}
808 	memset(bmpRowData + (bmpRowBytes - padding), 0, padding);
809 	ssize_t rd = inSource->Read(bitsRowData, bitsRowBytes);
810 	while (rd == bitsRowBytes) {
811 		memcpy(bmpRowData, bitsRowData, msheader.width);
812 		outDestination->Write(bmpRowData, bmpRowBytes);
813 		bmppixrow++;
814 		// if I've read all of the pixel data, break
815 		// out of the loop so I don't try to read
816 		// non-pixel data
817 		if (bmppixrow == msheader.height)
818 			break;
819 
820 		inSource->Seek(bitsRowBytes * -2, SEEK_CUR);
821 		rd = inSource->Read(bitsRowData, bitsRowBytes);
822 	} // while (rd == bitsRowBytes)
823 
824 	delete[] bmpRowData;
825 	delete[] bitsRowData;
826 
827 	return B_OK;
828 }
829 
830 // ---------------------------------------------------------------
831 // translate_from_bits1_to_bmp1
832 //
833 // Converts 1-bit Be Bitmaps ('bits') to the MS 1-bit BMP format
834 //
835 // Preconditions:
836 //
837 // Parameters:	inSource,	contains the bits data to convert
838 //
839 //				outDestination,	where the BMP data will be written
840 //
841 //				bitsRowBytes,	number of bytes in one row of
842 //								bits data
843 //
844 //				msheader,	contains information about the BMP
845 //							dimensions and filesize
846 //
847 // Postconditions:
848 //
849 // Returns: B_ERROR,	if memory couldn't be allocated or another
850 //						error occured
851 //
852 // B_OK,	if no errors occurred
853 // ---------------------------------------------------------------
854 status_t
855 translate_from_bits1_to_bmp1(BPositionIO *inSource,
856 	BPositionIO *outDestination, int32 bitsRowBytes, MSInfoHeader &msheader)
857 {
858 	uint8 pixelsPerByte = 8 / msheader.bitsperpixel;
859 	int32 bmpRowBytes =
860 		get_rowbytes(msheader.width, msheader.bitsperpixel);
861 	uint32 bmppixrow = 0;
862 	off_t bitsoffset = ((msheader.height - 1) * bitsRowBytes);
863 	inSource->Seek(bitsoffset, SEEK_CUR);
864 	uint8 *bmpRowData = new (nothrow) uint8[bmpRowBytes];
865 	if (!bmpRowData)
866 		return B_NO_MEMORY;
867 	uint8 *bitsRowData = new (nothrow) uint8[bitsRowBytes];
868 	if (!bitsRowData) {
869 		delete[] bmpRowData;
870 		return B_NO_MEMORY;
871 	}
872 	ssize_t rd = inSource->Read(bitsRowData, bitsRowBytes);
873 	while (rd == bitsRowBytes) {
874 		uint32 bmppixcol = 0;
875 		memset(bmpRowData, 0, bmpRowBytes);
876 		for (int32 i = 0; (bmppixcol < msheader.width) &&
877 			(i < bitsRowBytes); i++) {
878 			// process each byte in the row
879 			uint8 pixels = bitsRowData[i];
880 			for (uint8 compbit = 128; (bmppixcol < msheader.width) &&
881 				compbit; compbit >>= 1) {
882 				// for each bit in the current byte, convert to a BMP palette
883 				// index and store that in the bmpRowData
884 				uint8 index;
885 				if (pixels & compbit)
886 					// 1 == black
887 					index = 1;
888 				else
889 					// 0 == white
890 					index = 0;
891 				bmpRowData[bmppixcol / pixelsPerByte] |=
892 					index << (7 - (bmppixcol % pixelsPerByte));
893 				bmppixcol++;
894 			}
895 		}
896 
897 		outDestination->Write(bmpRowData, bmpRowBytes);
898 		bmppixrow++;
899 		// if I've read all of the pixel data, break
900 		// out of the loop so I don't try to read
901 		// non-pixel data
902 		if (bmppixrow == msheader.height)
903 			break;
904 
905 		inSource->Seek(bitsRowBytes * -2, SEEK_CUR);
906 		rd = inSource->Read(bitsRowData, bitsRowBytes);
907 	} // while (rd == bitsRowBytes)
908 
909 	delete[] bmpRowData;
910 	delete[] bitsRowData;
911 
912 	return B_OK;
913 }
914 
915 // ---------------------------------------------------------------
916 // write_bmp_headers
917 //
918 // Writes the MS BMP headers (fileHeader and msheader)
919 // to outDestination.
920 //
921 // Preconditions:
922 //
923 // Parameters:	outDestination,	where the headers are written to
924 //
925 // 				fileHeader, BMP file header data
926 //
927 //				msheader, BMP info header data
928 //
929 // Postconditions:
930 //
931 // Returns: B_ERROR, if something went wrong
932 //
933 // B_OK, if there were no problems writing out the headers
934 // ---------------------------------------------------------------
935 status_t
936 write_bmp_headers(BPositionIO *outDestination, BMPFileHeader &fileHeader,
937 	MSInfoHeader &msheader)
938 {
939 	uint8 bmpheaders[54];
940 	memcpy(bmpheaders, &fileHeader.magic, sizeof(uint16));
941 	memcpy(bmpheaders + 2, &fileHeader.fileSize, sizeof(uint32));
942 	memcpy(bmpheaders + 6, &fileHeader.reserved, sizeof(uint32));
943 	memcpy(bmpheaders + 10, &fileHeader.dataOffset, sizeof(uint32));
944 	memcpy(bmpheaders + 14, &msheader, sizeof(msheader));
945 	if (swap_data(B_UINT16_TYPE, bmpheaders, 2,
946 		B_SWAP_HOST_TO_LENDIAN) != B_OK)
947 		return B_ERROR;
948 	if (swap_data(B_UINT32_TYPE, bmpheaders + 2, 12,
949 		B_SWAP_HOST_TO_LENDIAN) != B_OK)
950 		return B_ERROR;
951 	if (swap_data(B_UINT32_TYPE, bmpheaders + 14,
952 		sizeof(MSInfoHeader), B_SWAP_HOST_TO_LENDIAN) != B_OK)
953 		return B_ERROR;
954 	if (outDestination->Write(bmpheaders, 54) != 54)
955 		return B_ERROR;
956 
957 	return B_OK;
958 }
959 
960 // ---------------------------------------------------------------
961 // translate_from_bits
962 //
963 // Convert the data in inSource from the Be Bitmap format ('bits')
964 // to the format specified in outType (either bits or BMP).
965 //
966 // Preconditions:
967 //
968 // Parameters:	inSource,	the bits data to translate
969 //
970 //				outType,	the type of data to convert to
971 //
972 //				outDestination,	where the output is written to
973 //
974 // Postconditions:
975 //
976 // Returns: B_NO_TRANSLATOR,	if the data is not in a supported
977 //								format
978 //
979 // B_ERROR, if there was an error allocating memory or some other
980 //			error
981 //
982 // B_OK, if successfully translated the data from the bits format
983 // ---------------------------------------------------------------
984 status_t
985 BMPTranslator::translate_from_bits(BPositionIO *inSource, uint32 outType,
986 	BPositionIO *outDestination)
987 {
988 	bool bheaderonly, bdataonly;
989 	bheaderonly = bdataonly = false;
990 
991 	TranslatorBitmap bitsHeader;
992 	status_t result;
993 	result = identify_bits_header(inSource, NULL, &bitsHeader);
994 	if (result != B_OK)
995 		return result;
996 
997 	// Translate B_TRANSLATOR_BITMAP to B_BMP_FORMAT
998 	if (outType == B_BMP_FORMAT) {
999 		// Set up BMP header
1000 		BMPFileHeader fileHeader;
1001 		fileHeader.magic = 'MB';
1002 		fileHeader.reserved = 0;
1003 
1004 		MSInfoHeader msheader;
1005 		msheader.size = 40;
1006 		msheader.width =
1007 			static_cast<uint32> (bitsHeader.bounds.Width() + 1);
1008 		msheader.height =
1009 			static_cast<uint32> (bitsHeader.bounds.Height() + 1);
1010 		msheader.planes = 1;
1011 		msheader.xpixperm = 2835; // 72 dpi horizontal
1012 		msheader.ypixperm = 2835; // 72 dpi vertical
1013 		msheader.colorsused = 0;
1014 		msheader.colorsimportant = 0;
1015 
1016 		// determine fileSize / imagesize
1017 		switch (bitsHeader.colors) {
1018 			case B_RGB32:
1019 			case B_RGB32_BIG:
1020 			case B_RGBA32:
1021 			case B_RGBA32_BIG:
1022 			case B_RGB24:
1023 			case B_RGB24_BIG:
1024 			case B_RGB16:
1025 			case B_RGB16_BIG:
1026 			case B_RGB15:
1027 			case B_RGB15_BIG:
1028 			case B_RGBA15:
1029 			case B_RGBA15_BIG:
1030 			case B_CMYK32:
1031 			case B_CMY32:
1032 			case B_CMYA32:
1033 			case B_CMY24:
1034 
1035 				fileHeader.dataOffset = 54;
1036 				msheader.bitsperpixel = 24;
1037 				msheader.compression = BMP_NO_COMPRESS;
1038 				msheader.imagesize = get_rowbytes(msheader.width, 24) *
1039 					msheader.height;
1040 				fileHeader.fileSize = fileHeader.dataOffset +
1041 					msheader.imagesize;
1042 
1043 				break;
1044 
1045 			case B_CMAP8:
1046 			case B_GRAY8:
1047 
1048 				msheader.colorsused = 256;
1049 				msheader.colorsimportant = 256;
1050 				fileHeader.dataOffset = 54 + (4 * 256);
1051 				msheader.bitsperpixel = 8;
1052 				msheader.compression = BMP_NO_COMPRESS;
1053 				msheader.imagesize = get_rowbytes(msheader.width,
1054 					msheader.bitsperpixel) * msheader.height;
1055 				fileHeader.fileSize = fileHeader.dataOffset +
1056 					msheader.imagesize;
1057 
1058 				break;
1059 
1060 			case B_GRAY1:
1061 
1062 				msheader.colorsused = 2;
1063 				msheader.colorsimportant = 2;
1064 				fileHeader.dataOffset = 62;
1065 				msheader.bitsperpixel = 1;
1066 				msheader.compression = BMP_NO_COMPRESS;
1067 				msheader.imagesize = get_rowbytes(msheader.width,
1068 					msheader.bitsperpixel) * msheader.height;
1069 				fileHeader.fileSize = fileHeader.dataOffset +
1070 					msheader.imagesize;
1071 
1072 				break;
1073 
1074 			default:
1075 				return B_NO_TRANSLATOR;
1076 		}
1077 
1078 		// write out the BMP headers
1079 		if (bheaderonly || (!bheaderonly && !bdataonly)) {
1080 			result = write_bmp_headers(outDestination, fileHeader, msheader);
1081 			if (result != B_OK)
1082 				return result;
1083 		}
1084 		if (bheaderonly)
1085 			// if user only wants the header, bail out
1086 			// before the data is written
1087 			return result;
1088 
1089 		// write out the BMP pixel data
1090 		switch (bitsHeader.colors) {
1091 			case B_RGB32:
1092 			case B_RGB32_BIG:
1093 			case B_RGBA32:
1094 			case B_RGBA32_BIG:
1095 			case B_RGB24:
1096 			case B_RGB24_BIG:
1097 			case B_RGB16:
1098 			case B_RGB16_BIG:
1099 			case B_RGB15:
1100 			case B_RGB15_BIG:
1101 			case B_RGBA15:
1102 			case B_RGBA15_BIG:
1103 			case B_CMYK32:
1104 			case B_CMY32:
1105 			case B_CMYA32:
1106 			case B_CMY24:
1107 				return translate_from_bits_to_bmp24(inSource, outDestination,
1108 					bitsHeader.colors, msheader);
1109 
1110 			case B_CMAP8:
1111 			case B_GRAY8:
1112 			{
1113 				// write palette to BMP file
1114 				uint8 pal[1024];
1115 				uint8* palHandle = pal;
1116 				if (bitsHeader.colors == B_CMAP8) {
1117 					// write system palette
1118 					const color_map *pmap = system_colors();
1119 					if (!pmap)
1120 						return B_ERROR;
1121 					for (int32 i = 0; i < 256; i++) {
1122 						rgb_color c = pmap->color_list[i];
1123 						palHandle[0] = c.blue;
1124 						palHandle[1] = c.green;
1125 						palHandle[2] = c.red;
1126 						palHandle[3] = c.alpha;
1127 						palHandle += 4;
1128 					}
1129 				} else {
1130 					// write gray palette
1131 					for (int32 i = 0; i < 256; i++) {
1132 						palHandle[0] = i;
1133 						palHandle[1] = i;
1134 						palHandle[2] = i;
1135 						palHandle[3] = 255;
1136 						palHandle += 4;
1137 					}
1138 				}
1139 				ssize_t written = outDestination->Write(pal, 1024);
1140 				if (written < 0)
1141 					return written;
1142 				if (written != 1024)
1143 					return B_ERROR;
1144 
1145 				return translate_from_bits8_to_bmp8(inSource, outDestination,
1146 					bitsHeader.rowBytes, msheader);
1147 			}
1148 
1149 			case B_GRAY1:
1150 			{
1151 				// write monochrome palette to the BMP file
1152 				const uint32 monopal[] = { 0x00ffffff, 0x00000000 };
1153 				ssize_t written = outDestination->Write(monopal, 8);
1154 				if (written < 0)
1155 					return written;
1156 				if (written != 8)
1157 					return B_ERROR;
1158 
1159 				return translate_from_bits1_to_bmp1(inSource, outDestination,
1160 					bitsHeader.rowBytes, msheader);
1161 			}
1162 
1163 			default:
1164 				return B_NO_TRANSLATOR;
1165 		}
1166 	} else
1167 		return B_NO_TRANSLATOR;
1168 }
1169 
1170 // ---------------------------------------------------------------
1171 // translate_from_bmpnpal_to_bits
1172 //
1173 // Translates a non-palette BMP from inSource to the B_RGB32
1174 // bits format.
1175 //
1176 // Preconditions:
1177 //
1178 // Parameters: inSource,	the BMP data to be translated
1179 //
1180 // outDestination,	where the bits data will be written to
1181 //
1182 // msheader,	header information about the BMP to be written
1183 //
1184 // Postconditions:
1185 //
1186 // Returns: B_ERROR, if there is an error allocating memory
1187 //
1188 // B_OK, if all went well
1189 // ---------------------------------------------------------------
1190 status_t
1191 translate_from_bmpnpal_to_bits(BPositionIO *inSource,
1192 	BPositionIO *outDestination, MSInfoHeader &msheader)
1193 {
1194 	int32 bitsRowBytes = msheader.width * 4;
1195 	int32 bmpBytesPerPixel = msheader.bitsperpixel / 8;
1196 	int32 bmpRowBytes =
1197 		get_rowbytes(msheader.width, msheader.bitsperpixel);
1198 
1199 	// Setup outDestination so that it can be written to
1200 	// from the end of the file to the beginning instead of
1201 	// the other way around
1202 	off_t bitsFileSize = (bitsRowBytes * msheader.height) +
1203 		sizeof(TranslatorBitmap);
1204 	if (outDestination->SetSize(bitsFileSize) != B_OK) {
1205 		// This call should work for BFile and BMallocIO objects,
1206 		// but may not work for other BPositionIO based types
1207 		ERROR("BMPTranslator::translate_from_bmpnpal_to_bits() - failed to SetSize()\n");
1208 		return B_ERROR;
1209 	}
1210 	off_t bitsoffset = (msheader.height - 1) * bitsRowBytes;
1211 	outDestination->Seek(bitsoffset, SEEK_CUR);
1212 
1213 	// allocate row buffers
1214 	uint8 *bmpRowData = new (nothrow) uint8[bmpRowBytes];
1215 	if (!bmpRowData)
1216 		return B_NO_MEMORY;
1217 	uint8 *bitsRowData = new (nothrow) uint8[bitsRowBytes];
1218 	if (!bitsRowData) {
1219 		delete[] bmpRowData;
1220 		return B_NO_MEMORY;
1221 	}
1222 
1223 	// perform the actual translation
1224 	if (bmpBytesPerPixel != 4) {
1225 		// clean out buffer so that we don't have to write
1226 		// alpha for each row
1227 		memset(bitsRowData, 0xff, bitsRowBytes);
1228 	}
1229 
1230 	status_t ret = B_OK;
1231 
1232 	for (uint32 y = 0; y < msheader.height; y++) {
1233 		ssize_t read = inSource->Read(bmpRowData, bmpRowBytes);
1234 		if (read != bmpRowBytes) {
1235 			// break on read error
1236 			if (read >= 0)
1237 				ret = B_ERROR;
1238 			else
1239 				ret = read;
1240 			break;
1241 		}
1242 
1243 		if (bmpBytesPerPixel == 4) {
1244 			memcpy(bitsRowData, bmpRowData, bmpRowBytes);
1245 		} else {
1246 			uint8 *pBitsPixel = bitsRowData;
1247 			uint8 *pBmpPixel = bmpRowData;
1248 			for (uint32 i = 0; i < msheader.width; i++) {
1249 				pBitsPixel[0] = pBmpPixel[0];
1250 				pBitsPixel[1] = pBmpPixel[1];
1251 				pBitsPixel[2] = pBmpPixel[2];
1252 				pBitsPixel += 4;
1253 				pBmpPixel += bmpBytesPerPixel;
1254 			}
1255 		}
1256 		// write row and seek backward by two rows
1257 		ssize_t written = outDestination->Write(bitsRowData, bitsRowBytes);
1258 		outDestination->Seek(bitsRowBytes * -2, SEEK_CUR);
1259 
1260 		if (written != bitsRowBytes) {
1261 			// break on write error
1262 			if (written >= 0)
1263 				ret = B_ERROR;
1264 			else
1265 				ret = read;
1266 			break;
1267 		}
1268 	}
1269 
1270 	delete[] bmpRowData;
1271 	delete[] bitsRowData;
1272 
1273 	return ret;
1274 }
1275 
1276 // ---------------------------------------------------------------
1277 // translate_from_bmppal_to_bits
1278 //
1279 // Translates an uncompressed, palette BMP from inSource to
1280 // the B_RGB32 bits format.
1281 //
1282 // Preconditions:
1283 //
1284 // Parameters: inSource,	the BMP data to be translated
1285 //
1286 // outDestination,	where the bits data will be written to
1287 //
1288 // msheader,	header information about the BMP to be written
1289 //
1290 // palette,	BMP palette for the BMP image
1291 //
1292 // frommsformat, true if BMP in inSource is in MS format,
1293 //				 false if it is in OS/2 format
1294 //
1295 // Postconditions:
1296 //
1297 // Returns: B_NO_MEMORY, if there is an error allocating memory
1298 //
1299 // B_OK, if all went well
1300 // ---------------------------------------------------------------
1301 status_t
1302 translate_from_bmppal_to_bits(BPositionIO *inSource,
1303 	BPositionIO *outDestination, MSInfoHeader &msheader,
1304 	const uint8 *palette, bool frommsformat)
1305 {
1306 	uint16 pixelsPerByte = 8 / msheader.bitsperpixel;
1307 	uint16 bitsPerPixel = msheader.bitsperpixel;
1308 	uint8 palBytesPerPixel;
1309 	if (frommsformat)
1310 		palBytesPerPixel = 4;
1311 	else
1312 		palBytesPerPixel = 3;
1313 
1314 	uint8 mask = 1;
1315 	mask = (mask << bitsPerPixel) - 1;
1316 
1317 	int32 bmpRowBytes =
1318 		get_rowbytes(msheader.width, msheader.bitsperpixel);
1319 	uint32 bmppixrow = 0;
1320 
1321 	// Setup outDestination so that it can be written to
1322 	// from the end of the file to the beginning instead of
1323 	// the other way around
1324 	int32 bitsRowBytes = msheader.width * 4;
1325 	off_t bitsFileSize = (bitsRowBytes * msheader.height) +
1326 		sizeof(TranslatorBitmap);
1327 	if (outDestination->SetSize(bitsFileSize) != B_OK)
1328 		// This call should work for BFile and BMallocIO objects,
1329 		// but may not work for other BPositionIO based types
1330 		return B_ERROR;
1331 	off_t bitsoffset = ((msheader.height - 1) * bitsRowBytes);
1332 	outDestination->Seek(bitsoffset, SEEK_CUR);
1333 
1334 	// allocate row buffers
1335 	uint8 *bmpRowData = new (nothrow) uint8[bmpRowBytes];
1336 	if (!bmpRowData)
1337 		return B_NO_MEMORY;
1338 	uint8 *bitsRowData = new (nothrow) uint8[bitsRowBytes];
1339 	if (!bitsRowData) {
1340 		delete[] bmpRowData;
1341 		return B_NO_MEMORY;
1342 	}
1343 	memset(bitsRowData, 0xff, bitsRowBytes);
1344 	ssize_t rd = inSource->Read(bmpRowData, bmpRowBytes);
1345 	while (rd == static_cast<ssize_t>(bmpRowBytes)) {
1346 		for (uint32 i = 0; i < msheader.width; i++) {
1347 			uint8 indices = (bmpRowData + (i / pixelsPerByte))[0];
1348 			uint8 index;
1349 			index = (indices >>
1350 				(bitsPerPixel * ((pixelsPerByte - 1) -
1351 					(i % pixelsPerByte)))) & mask;
1352 			memcpy(bitsRowData + (i * 4),
1353 				palette + (index * palBytesPerPixel), 3);
1354 		}
1355 
1356 		outDestination->Write(bitsRowData, bitsRowBytes);
1357 		bmppixrow++;
1358 		// if I've read all of the pixel data, break
1359 		// out of the loop so I don't try to read
1360 		// non-pixel data
1361 		if (bmppixrow == msheader.height)
1362 			break;
1363 
1364 		outDestination->Seek(bitsRowBytes * -2, SEEK_CUR);
1365 		rd = inSource->Read(bmpRowData, bmpRowBytes);
1366 	}
1367 
1368 	delete[] bmpRowData;
1369 	delete[] bitsRowData;
1370 
1371 	return B_OK;
1372 }
1373 
1374 
1375 // ---------------------------------------------------------------
1376 // pixelcpy
1377 //
1378 // Copies count 32-bit pixels with a color value of pixel to dest.
1379 //
1380 // Preconditions:
1381 //
1382 // Parameters:	dest,	where the pixel data will be copied to
1383 //
1384 //				pixel,	the 32-bit color value to copy to dest
1385 //						count times
1386 //
1387 //				count,	the number of times pixel is copied to
1388 //						dest
1389 //
1390 // Postconditions:
1391 //
1392 // Returns:
1393 // ---------------------------------------------------------------
1394 void
1395 pixelcpy(uint8 *dest, uint32 pixel, uint32 count)
1396 {
1397 	for (uint32 i = 0; i < count; i++) {
1398 		memcpy(dest, &pixel, 3);
1399 		dest += 4;
1400 	}
1401 }
1402 
1403 // ---------------------------------------------------------------
1404 // translate_from_bmppalr_to_bits
1405 //
1406 // Translates an RLE compressed, palette BMP from inSource to
1407 // the B_RGB32 bits format. Currently, this code is not as
1408 // memory effcient as it could be. It assumes that the BMP
1409 // from inSource is relatively small.
1410 //
1411 // Preconditions:
1412 //
1413 // Parameters: inSource,	the BMP data to be translated
1414 //
1415 // outDestination,	where the bits data will be written to
1416 //
1417 // datasize, number of bytes of data needed for the bits output
1418 //
1419 // msheader,	header information about the BMP to be written
1420 //
1421 // palette,	BMP palette for data in inSource
1422 //
1423 // Postconditions:
1424 //
1425 // Returns: B_ERROR, if there is an error allocating memory
1426 //
1427 // B_OK, if all went well
1428 // ---------------------------------------------------------------
1429 status_t
1430 translate_from_bmppalr_to_bits(BPositionIO *inSource,
1431 	BPositionIO *outDestination, int32 datasize, MSInfoHeader &msheader,
1432 	const uint8 *palette)
1433 {
1434 	uint16 pixelsPerByte = 8 / msheader.bitsperpixel;
1435 	uint16 bitsPerPixel = msheader.bitsperpixel;
1436 	uint8 mask = (1 << bitsPerPixel) - 1;
1437 
1438 	uint8 count, indices, index;
1439 	// Setup outDestination so that it can be written to
1440 	// from the end of the file to the beginning instead of
1441 	// the other way around
1442 	int32 bitsRowBytes = msheader.width * 4;
1443 	off_t bitsFileSize = (bitsRowBytes * msheader.height) +
1444 		sizeof(TranslatorBitmap);
1445 	if (outDestination->SetSize(bitsFileSize) != B_OK)
1446 		// This call should work for BFile and BMallocIO objects,
1447 		// but may not work for other BPositionIO based types
1448 		return B_ERROR;
1449 	uint8 *bitsRowData = new (nothrow) uint8[bitsRowBytes];
1450 	if (!bitsRowData)
1451 		return B_NO_MEMORY;
1452 	memset(bitsRowData, 0xff, bitsRowBytes);
1453 	uint32 bmppixcol = 0, bmppixrow = 0;
1454 	uint32 defaultcolor = *(uint32*)palette;
1455 	// set bits output to last row in the image
1456 	off_t bitsoffset = ((msheader.height - (bmppixrow + 1)) * bitsRowBytes) +
1457 		(bmppixcol * 4);
1458 	outDestination->Seek(bitsoffset, SEEK_CUR);
1459 	ssize_t rd = inSource->Read(&count, 1);
1460 	while (rd > 0) {
1461 		// repeated color
1462 		if (count) {
1463 			// abort if all of the pixels in the row
1464 			// have already been drawn to
1465 			if (bmppixcol == msheader.width) {
1466 				rd = -1;
1467 				break;
1468 			}
1469 			// if count is greater than the number of
1470 			// pixels remaining in the current row,
1471 			// only process the correct number of pixels
1472 			// remaining in the row
1473 			if (count + bmppixcol > msheader.width)
1474 				count = msheader.width - bmppixcol;
1475 
1476 			rd = inSource->Read(&indices, 1);
1477 			if (rd != 1) {
1478 				rd = -1;
1479 				break;
1480 			}
1481 			for (uint8 i = 0; i < count; i++) {
1482 				index = (indices >> (bitsPerPixel * ((pixelsPerByte - 1) -
1483 					(i % pixelsPerByte)))) & mask;
1484 				memcpy(bitsRowData + (bmppixcol*4), palette + (index*4), 3);
1485 				bmppixcol++;
1486 			}
1487 		// special code
1488 		} else {
1489 			uint8 code;
1490 			rd = inSource->Read(&code, 1);
1491 			if (rd != 1) {
1492 				rd = -1;
1493 				break;
1494 			}
1495 			switch (code) {
1496 				// end of line
1497 				case 0:
1498 					// if there are columns remaing on this
1499 					// line, set them to the color at index zero
1500 					if (bmppixcol < msheader.width)
1501 						pixelcpy(bitsRowData + (bmppixcol * 4),
1502 							defaultcolor, msheader.width - bmppixcol);
1503 					outDestination->Write(bitsRowData, bitsRowBytes);
1504 					bmppixcol = 0;
1505 					bmppixrow++;
1506 					if (bmppixrow < msheader.height)
1507 						outDestination->Seek(bitsRowBytes * -2, SEEK_CUR);
1508 					break;
1509 
1510 				// end of bitmap
1511 				case 1:
1512 					// if at the end of a row
1513 					if (bmppixcol == msheader.width) {
1514 						outDestination->Write(bitsRowData, bitsRowBytes);
1515 						bmppixcol = 0;
1516 						bmppixrow++;
1517 						if (bmppixrow < msheader.height)
1518 							outDestination->Seek(bitsRowBytes * -2, SEEK_CUR);
1519 					}
1520 
1521 					while (bmppixrow < msheader.height) {
1522 						pixelcpy(bitsRowData + (bmppixcol * 4), defaultcolor,
1523 							msheader.width - bmppixcol);
1524 						outDestination->Write(bitsRowData, bitsRowBytes);
1525 						bmppixcol = 0;
1526 						bmppixrow++;
1527 						if (bmppixrow < msheader.height)
1528 							outDestination->Seek(bitsRowBytes * -2, SEEK_CUR);
1529 					}
1530 					rd = 0;
1531 						// break out of while loop
1532 					break;
1533 
1534 				// delta, skip several rows and/or columns and
1535 				// fill the skipped pixels with the default color
1536 				case 2:
1537 				{
1538 					uint8 da[2], lastcol, dx, dy;
1539 					rd = inSource->Read(da, 2);
1540 					if (rd != 2) {
1541 						rd = -1;
1542 						break;
1543 					}
1544 					dx = da[0];
1545 					dy = da[1];
1546 
1547 					// abort if dx or dy is too large
1548 					if ((dx + bmppixcol >= msheader.width) ||
1549 						(dy + bmppixrow >= msheader.height)) {
1550 						rd = -1;
1551 						break;
1552 					}
1553 
1554 					lastcol = bmppixcol;
1555 
1556 					// set all pixels to the first entry in
1557 					// the palette, for the number of rows skipped
1558 					while (dy > 0) {
1559 						pixelcpy(bitsRowData + (bmppixcol * 4), defaultcolor,
1560 							msheader.width - bmppixcol);
1561 						outDestination->Write(bitsRowData, bitsRowBytes);
1562 						bmppixcol = 0;
1563 						bmppixrow++;
1564 						dy--;
1565 						outDestination->Seek(bitsRowBytes * -2, SEEK_CUR);
1566 					}
1567 
1568 					if (bmppixcol < static_cast<uint32>(lastcol + dx)) {
1569 						pixelcpy(bitsRowData + (bmppixcol * 4), defaultcolor,
1570 							dx + lastcol - bmppixcol);
1571 						bmppixcol = dx + lastcol;
1572 					}
1573 
1574 					break;
1575 				}
1576 
1577 				// code >= 3
1578 				// read code uncompressed indices
1579 				default:
1580 					// abort if all of the pixels in the row
1581 					// have already been drawn to
1582 					if (bmppixcol == msheader.width) {
1583 						rd = -1;
1584 						break;
1585 					}
1586 					// if code is greater than the number of
1587 					// pixels remaining in the current row,
1588 					// only process the correct number of pixels
1589 					// remaining in the row
1590 					if (code + bmppixcol > msheader.width)
1591 						code = msheader.width - bmppixcol;
1592 
1593 					uint8 uncomp[256];
1594 					int32 padding;
1595 					if (!(code % pixelsPerByte))
1596 						padding = (code / pixelsPerByte) % 2;
1597 					else
1598 						padding = ((code + pixelsPerByte -
1599 							(code % pixelsPerByte)) / pixelsPerByte) % 2;
1600 					int32 uncompBytes = (code / pixelsPerByte) +
1601 						((code % pixelsPerByte) ? 1 : 0) + padding;
1602 					rd = inSource->Read(uncomp, uncompBytes);
1603 					if (rd != uncompBytes) {
1604 						rd = -1;
1605 						break;
1606 					}
1607 					for (uint8 i = 0; i < code; i++) {
1608 						indices = (uncomp + (i / pixelsPerByte))[0];
1609 						index = (indices >>
1610 							(bitsPerPixel * ((pixelsPerByte - 1) -
1611 								(i % pixelsPerByte)))) & mask;
1612 						memcpy(bitsRowData + (bmppixcol * 4),
1613 							palette + (index * 4), 3);
1614 						bmppixcol++;
1615 					}
1616 
1617 					break;
1618 			}
1619 		}
1620 		if (rd > 0)
1621 			rd = inSource->Read(&count, 1);
1622 	}
1623 
1624 	delete[] bitsRowData;
1625 
1626 	if (!rd)
1627 		return B_OK;
1628 	else
1629 		return B_NO_TRANSLATOR;
1630 }
1631 
1632 // ---------------------------------------------------------------
1633 // translate_from_bmp
1634 //
1635 // Convert the data in inSource from the BMP format
1636 // to the format specified in outType (either bits or BMP).
1637 //
1638 // Preconditions:
1639 //
1640 // Parameters:	inSource,	the bits data to translate
1641 //
1642 //				outType,	the type of data to convert to
1643 //
1644 //				outDestination,	where the output is written to
1645 //
1646 // Postconditions:
1647 //
1648 // Returns: B_NO_TRANSLATOR,	if the data is not in a supported
1649 //								format
1650 //
1651 // B_ERROR, if there was an error allocating memory or some other
1652 //			error
1653 //
1654 // B_OK, if successfully translated the data from the bits format
1655 // ---------------------------------------------------------------
1656 status_t
1657 BMPTranslator::translate_from_bmp(BPositionIO *inSource, uint32 outType,
1658 	BPositionIO *outDestination)
1659 {
1660 	bool bheaderonly, bdataonly;
1661 	bheaderonly = bdataonly = false;
1662 
1663 	BMPFileHeader fileHeader;
1664 	MSInfoHeader msheader;
1665 	bool frommsformat;
1666 	off_t os2skip = 0;
1667 
1668 	status_t result;
1669 	result = identify_bmp_header(inSource, NULL, &fileHeader, &msheader,
1670 		&frommsformat, &os2skip);
1671 	if (result != B_OK) {
1672 		INFO("BMPTranslator::translate_from_bmp() - identify_bmp_header failed\n");
1673 		return result;
1674 	}
1675 
1676 	// if the user wants to translate a BMP to a BMP, easy enough :)
1677 	if (outType == B_BMP_FORMAT) {
1678 		// write out the BMP headers
1679 		if (bheaderonly || (!bheaderonly && !bdataonly)) {
1680 			result = write_bmp_headers(outDestination, fileHeader, msheader);
1681 			if (result != B_OK)
1682 				return result;
1683 		}
1684 		if (bheaderonly)
1685 			// if the user only wants the header,
1686 			// bail before it is written
1687 			return result;
1688 
1689 		uint8 buf[1024];
1690 		ssize_t rd;
1691 		uint32 rdtotal = 54;
1692 		if (!frommsformat && (msheader.bitsperpixel == 1 ||
1693 			msheader.bitsperpixel == 4 || msheader.bitsperpixel == 8)) {
1694 			// if OS/2 paletted format, convert palette to MS format
1695 			uint16 ncolors = 1 << msheader.bitsperpixel;
1696 			rd = inSource->Read(buf, ncolors * 3);
1697 			if (rd != ncolors * 3)
1698 				return B_NO_TRANSLATOR;
1699 			uint8 mspalent[4] = {0, 0, 0, 0};
1700 			for (uint16 i = 0; i < ncolors; i++) {
1701 				memcpy(mspalent, buf + (i * 3), 3);
1702 				outDestination->Write(mspalent, 4);
1703 			}
1704 			rdtotal = fileHeader.dataOffset;
1705 		}
1706 		// if there is junk between the OS/2 headers and
1707 		// the actual data, skip it
1708 		if (!frommsformat && os2skip)
1709 			inSource->Seek(os2skip, SEEK_CUR);
1710 
1711 		rd = min((size_t)1024, fileHeader.fileSize - rdtotal);
1712 		rd = inSource->Read(buf, rd);
1713 		while (rd > 0) {
1714 			outDestination->Write(buf, rd);
1715 			rdtotal += rd;
1716 			rd = min((size_t)1024, fileHeader.fileSize - rdtotal);
1717 			rd = inSource->Read(buf, rd);
1718 		}
1719 		if (rd == 0)
1720 			return B_OK;
1721 		else
1722 			return B_ERROR;
1723 
1724 	// if translating a BMP to a Be Bitmap
1725 	} else if (outType == B_TRANSLATOR_BITMAP) {
1726 		TranslatorBitmap bitsHeader;
1727 		bitsHeader.magic = B_TRANSLATOR_BITMAP;
1728 		bitsHeader.bounds.left = 0;
1729 		bitsHeader.bounds.top = 0;
1730 		bitsHeader.bounds.right = msheader.width - 1;
1731 		bitsHeader.bounds.bottom = msheader.height - 1;
1732 
1733 		// read in palette and/or skip non-BMP data
1734 		uint8 bmppalette[1024];
1735 		off_t nskip = 0;
1736 		if (msheader.bitsperpixel == 1 ||
1737 			msheader.bitsperpixel == 4 ||
1738 			msheader.bitsperpixel == 8) {
1739 
1740 			uint8 palBytesPerPixel;
1741 			if (frommsformat)
1742 				palBytesPerPixel = 4;
1743 			else
1744 				palBytesPerPixel = 3;
1745 
1746 			if (!msheader.colorsused)
1747 				msheader.colorsused = 1 << msheader.bitsperpixel;
1748 
1749 			if (inSource->Read(bmppalette, msheader.colorsused *
1750 				palBytesPerPixel) !=
1751 					(off_t) msheader.colorsused * palBytesPerPixel)
1752 				return B_NO_TRANSLATOR;
1753 
1754 			// skip over non-BMP data
1755 			if (frommsformat) {
1756 				if (fileHeader.dataOffset > (msheader.colorsused *
1757 					palBytesPerPixel) + 54)
1758 					nskip = fileHeader.dataOffset -
1759 						((msheader.colorsused * palBytesPerPixel) + 54);
1760 			} else
1761 				nskip = os2skip;
1762 		} else if (fileHeader.dataOffset > 54)
1763 			// skip over non-BMP data
1764 			nskip = fileHeader.dataOffset - 54;
1765 
1766 		if (nskip > 0 && inSource->Seek(nskip, SEEK_CUR) < 0)
1767 			return B_NO_TRANSLATOR;
1768 
1769 		bitsHeader.rowBytes = msheader.width * 4;
1770 		bitsHeader.colors = B_RGB32;
1771 		int32 datasize = bitsHeader.rowBytes * msheader.height;
1772 		bitsHeader.dataSize = datasize;
1773 
1774 		// write out Be's Bitmap header
1775 		if (bheaderonly || (!bheaderonly && !bdataonly)) {
1776 			if (swap_data(B_UINT32_TYPE, &bitsHeader,
1777 				sizeof(TranslatorBitmap), B_SWAP_HOST_TO_BENDIAN) != B_OK)
1778 				return B_ERROR;
1779 			outDestination->Write(&bitsHeader, sizeof(TranslatorBitmap));
1780 		}
1781 		if (bheaderonly)
1782 			// if the user only wants the header,
1783 			// bail before the data is written
1784 			return B_OK;
1785 
1786 		// write out the actual image data
1787 		switch (msheader.bitsperpixel) {
1788 			case 32:
1789 			case 24:
1790 				return translate_from_bmpnpal_to_bits(inSource,
1791 					outDestination, msheader);
1792 
1793 			case 8:
1794 				// 8 bit BMP with NO compression
1795 				if (msheader.compression == BMP_NO_COMPRESS)
1796 					return translate_from_bmppal_to_bits(inSource,
1797 						outDestination, msheader, bmppalette, frommsformat);
1798 
1799 				// 8 bit RLE compressed BMP
1800 				else if (msheader.compression == BMP_RLE8_COMPRESS)
1801 					return translate_from_bmppalr_to_bits(inSource,
1802 						outDestination, datasize, msheader, bmppalette);
1803 				else
1804 					return B_NO_TRANSLATOR;
1805 
1806 			case 4:
1807 				// 4 bit BMP with NO compression
1808 				if (!msheader.compression)
1809 					return translate_from_bmppal_to_bits(inSource,
1810 						outDestination, msheader, bmppalette, frommsformat);
1811 
1812 				// 4 bit RLE compressed BMP
1813 				else if (msheader.compression == BMP_RLE4_COMPRESS)
1814 					return translate_from_bmppalr_to_bits(inSource,
1815 						outDestination, datasize, msheader, bmppalette);
1816 				else
1817 					return B_NO_TRANSLATOR;
1818 
1819 			case 1:
1820 				return translate_from_bmppal_to_bits(inSource,
1821 					outDestination, msheader, bmppalette, frommsformat);
1822 
1823 			default:
1824 				return B_NO_TRANSLATOR;
1825 		}
1826 
1827 	} else
1828 		return B_NO_TRANSLATOR;
1829 }
1830 
1831 // ---------------------------------------------------------------
1832 // DerivedTranslate
1833 //
1834 // Translates the data in inSource to the type outType and stores
1835 // the translated data in outDestination.
1836 //
1837 // Preconditions:
1838 //
1839 // Parameters:	inSource,	the data to be translated
1840 //
1841 //				inInfo,	hint about the data in inSource (not used)
1842 //
1843 //				ioExtension,	configuration options for the
1844 //								translator
1845 //
1846 //				outType,	the type to convert inSource to
1847 //
1848 //				outDestination,	where the translated data is
1849 //								put
1850 //
1851 //				baseType, indicates whether inSource is in the
1852 //				          bits format, not in the bits format or
1853 //				          is unknown
1854 //
1855 // Postconditions:
1856 //
1857 // Returns: B_BAD_VALUE, if the options in ioExtension are bad
1858 //
1859 // B_NO_TRANSLATOR, if this translator doesn't understand the data
1860 //
1861 // B_ERROR, if there was an error allocating memory or converting
1862 //          data
1863 //
1864 // B_OK, if all went well
1865 // ---------------------------------------------------------------
1866 status_t
1867 BMPTranslator::DerivedTranslate(BPositionIO *inSource,
1868 		const translator_info *inInfo, BMessage *ioExtension,
1869 		uint32 outType, BPositionIO *outDestination, int32 baseType)
1870 {
1871 	if (baseType == 1)
1872 		// if inSource is in bits format
1873 		return translate_from_bits(inSource, outType, outDestination);
1874 	else if (baseType == 0)
1875 		// if inSource is NOT in bits format
1876 		return translate_from_bmp(inSource, outType, outDestination);
1877 	else
1878 		return B_NO_TRANSLATOR;
1879 }
1880 
1881 BView *
1882 BMPTranslator::NewConfigView(TranslatorSettings *settings)
1883 {
1884 	return new BMPView(BRect(0, 0, 225, 175), "BMPTranslator Settings",
1885 		B_FOLLOW_ALL, B_WILL_DRAW, settings);
1886 }
1887