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