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