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