vfs: check userland buffers before reading them.
[haiku.git] / src / add-ons / translators / bmp / BMPTranslator.cpp
blob2d7ea85734767ce62dd16cb56952159cd7aaf6a6
1 /*
2 * Copyright 2002-2009, Haiku, Inc. All rights reserved.
3 * Distributed under the terms of the MIT License.
5 * Authors:
6 * Michael Wilber <mwilber@users.berlios.de>
7 */
9 #include "BMPTranslator.h"
11 #include <algorithm>
12 #include <new>
13 #include <stdio.h>
14 #include <stdlib.h>
15 #include <string.h>
17 #include <Catalog.h>
19 #include "BMPView.h"
22 using std::nothrow;
23 using std::min;
26 //#define INFO(x) printf(x);
27 #define INFO(x)
28 //#define ERROR(x) printf(x);
29 #define ERROR(x)
31 #undef B_TRANSLATION_CONTEXT
32 #define B_TRANSLATION_CONTEXT "BMPTranslator"
35 // The input formats that this translator supports.
36 static const translation_format sInputFormats[] = {
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)"
46 B_BMP_FORMAT,
47 B_TRANSLATOR_BITMAP,
48 BMP_IN_QUALITY,
49 BMP_IN_CAPABILITY,
50 "image/bmp",
51 "BMP image"
55 // The output formats that this translator supports.
56 static const translation_format sOutputFormats[] = {
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)"
66 B_BMP_FORMAT,
67 B_TRANSLATOR_BITMAP,
68 BMP_OUT_QUALITY,
69 BMP_OUT_CAPABILITY,
70 "image/bmp",
71 "BMP image"
75 // Default settings for the Translator
76 static const TranSetting sDefaultSettings[] = {
77 {B_TRANSLATOR_EXT_HEADER_ONLY, TRAN_SETTING_BOOL, false},
78 {B_TRANSLATOR_EXT_DATA_ONLY, TRAN_SETTING_BOOL, false}
81 const uint32 kNumInputFormats = sizeof(sInputFormats) / sizeof(translation_format);
82 const uint32 kNumOutputFormats = sizeof(sOutputFormats) / sizeof(translation_format);
83 const uint32 kNumDefaultSettings = sizeof(sDefaultSettings) / sizeof(TranSetting);
86 // ---------------------------------------------------------------
87 // make_nth_translator
89 // Creates a BMPTranslator object to be used by BTranslatorRoster
91 // Preconditions:
93 // Parameters: n, The translator to return. Since
94 // BMPTranslator only publishes one
95 // translator, it only returns a
96 // BMPTranslator if n == 0
98 // you, The image_id of the add-on that
99 // contains code (not used).
101 // flags, Has no meaning yet, should be 0.
103 // Postconditions:
105 // Returns: NULL if n is not zero,
106 // a new BMPTranslator if n is zero
107 // ---------------------------------------------------------------
108 BTranslator *
109 make_nth_translator(int32 n, image_id you, uint32 flags, ...)
111 if (!n)
112 return new BMPTranslator();
113 else
114 return NULL;
117 // ---------------------------------------------------------------
118 // Constructor
120 // Sets up the version info and the name of the translator so that
121 // these values can be returned when they are requested.
123 // Preconditions:
125 // Parameters:
127 // Postconditions:
129 // Returns:
130 // ---------------------------------------------------------------
131 BMPTranslator::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)
143 // ---------------------------------------------------------------
144 // Destructor
146 // Does nothing
148 // Preconditions:
150 // Parameters:
152 // Postconditions:
154 // Returns:
155 // ---------------------------------------------------------------
156 BMPTranslator::~BMPTranslator()
160 // ---------------------------------------------------------------
161 // get_padding
163 // Returns number of bytes of padding required at the end of
164 // the row by the BMP format
167 // Preconditions: If bitsperpixel is zero, a division by zero
168 // will occur, which is bad
170 // Parameters: width, width of the row, in pixels
172 // bitsperpixel, bitdepth of the image
174 // Postconditions:
176 // Returns:
177 // ---------------------------------------------------------------
178 int32
179 get_padding(uint32 width, uint16 bitsperpixel)
181 int32 padding = 0;
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;
196 if (padding)
197 padding = 4 - padding;
199 return padding;
202 // ---------------------------------------------------------------
203 // get_rowbytes
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.
209 // Preconditions: If bitsperpixel is zero, a division by zero
210 // will occur, which is bad
212 // Parameters: width, width of the row, in pixels
214 // bitsperpixel, bitdepth of the image
216 // Postconditions:
218 // Returns:
219 // ---------------------------------------------------------------
220 int32
221 get_rowbytes(uint32 width, uint16 bitsperpixel)
223 int32 rowbytes = 0;
224 int32 padding = get_padding(width, bitsperpixel);
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;
235 return rowbytes;
238 // ---------------------------------------------------------------
239 // identify_bmp_header
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.
245 // Preconditions:
247 // Parameters: inSource, The source of the image data
249 // outInfo, Information about the translator
250 // is copied here
252 // amtread, Amount of data read from inSource
253 // before this function was called
255 // read, Pointer to the data that was read
256 // in before this function was called
258 // pfileheader, File header info for the BMP is
259 // copied here after it is read from
260 // the file.
262 // pmsheader, BMP header info read in from the
263 // BMP file
265 // pfrommsformat, Set to true if BMP data is BMP
266 // format, false if BMP data is OS/2
267 // format.
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
273 // Postconditions:
275 // Returns: B_NO_TRANSLATOR, if the data does not look like
276 // BMP format data
278 // B_ERROR, if the header data could not be converted to host
279 // format
281 // B_OK, if the data looks like bits data and no errors were
282 // encountered
283 // ---------------------------------------------------------------
284 status_t
285 identify_bmp_header(BPositionIO *inSource, translator_info *outInfo,
286 BMPFileHeader *pfileheader = NULL, MSInfoHeader *pmsheader = NULL,
287 bool *pfrommsformat = NULL, off_t *pos2skip = NULL)
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;
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;
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;
316 if (fileHeader.reserved != 0)
317 return B_NO_TRANSLATOR;
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;
326 if (headersize == sizeof(MSInfoHeader)) {
327 // MS format
329 if (fileHeader.dataOffset < 54)
330 return B_NO_TRANSLATOR;
332 MSInfoHeader msheader;
333 msheader.size = headersize;
334 if (inSource->Read(
335 reinterpret_cast<uint8 *> (&msheader) + 4, 36) != 36)
336 return B_NO_TRANSLATOR;
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;
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;
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");
385 if (pfileheader) {
386 pfileheader->magic = fileHeader.magic;
387 pfileheader->fileSize = fileHeader.fileSize;
388 pfileheader->reserved = fileHeader.reserved;
389 pfileheader->dataOffset = fileHeader.dataOffset;
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;
404 if (pfrommsformat)
405 (*pfrommsformat) = true;
407 return B_OK;
409 } else if (headersize == sizeof(OS2InfoHeader)) {
410 // OS/2 format
412 if (fileHeader.dataOffset < 26)
413 return B_NO_TRANSLATOR;
415 OS2InfoHeader os2header;
416 os2header.size = headersize;
417 if (inSource->Read(
418 reinterpret_cast<uint8 *> (&os2header) + 4, 8) != 8)
419 return B_NO_TRANSLATOR;
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;
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;
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");
447 if (pfileheader && pmsheader) {
448 pfileheader->magic = 'MB';
449 pfileheader->fileSize = 0;
450 pfileheader->reserved = 0;
451 pfileheader->dataOffset = 0;
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;
465 // determine fileSize / imagesize
466 switch (pmsheader->bitsperpixel) {
467 case 24:
468 if (pos2skip && fileHeader.dataOffset > 26)
469 (*pos2skip) = fileHeader.dataOffset - 26;
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;
477 break;
479 case 8:
480 case 4:
481 case 1:
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));
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;
497 break;
500 default:
501 break;
504 if (pfrommsformat)
505 (*pfrommsformat) = false;
507 return B_OK;
509 } else
510 return B_NO_TRANSLATOR;
513 // ---------------------------------------------------------------
514 // DerivedIdentify
516 // Examines the data from inSource and determines if it is in a
517 // format that this translator knows how to work with.
519 // Preconditions:
521 // Parameters: inSource, where the data to examine is
523 // inFormat, a hint about the data in inSource,
524 // it is ignored since it is only a hint
526 // ioExtension, configuration settings for the
527 // translator
529 // outInfo, information about what data is in
530 // inSource and how well this translator
531 // can handle that data is stored here
533 // outType, The format that the user wants
534 // the data in inSource to be
535 // converted to
537 // Postconditions:
539 // Returns: B_NO_TRANSLATOR, if this translator can't handle
540 // the data in inSource
542 // B_ERROR, if there was an error converting the data to the host
543 // format
545 // B_BAD_VALUE, if the settings in ioExtension are bad
547 // B_OK, if this translator understand the data and there were
548 // no errors found
549 // ---------------------------------------------------------------
550 status_t
551 BMPTranslator::DerivedIdentify(BPositionIO *inSource,
552 const translation_format *inFormat, BMessage *ioExtension,
553 translator_info *outInfo, uint32 outType)
555 return identify_bmp_header(inSource, outInfo);
558 // ---------------------------------------------------------------
559 // translate_from_bits_to_bmp24
561 // Converts various varieties of the Be Bitmap format ('bits') to
562 // the MS BMP 24-bit format.
564 // Preconditions:
566 // Parameters: inSource, contains the bits data to convert
568 // outDestination, where the BMP data will be written
570 // fromspace, the format of the data in inSource
572 // msheader, contains information about the BMP
573 // dimensions and filesize
575 // Postconditions:
577 // Returns: B_ERROR, if memory couldn't be allocated or another
578 // error occured
580 // B_OK, if no errors occurred
581 // ---------------------------------------------------------------
582 status_t
583 translate_from_bits_to_bmp24(BPositionIO *inSource,
584 BPositionIO *outDestination, color_space fromspace, MSInfoHeader &msheader)
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;
601 case B_RGB24:
602 case B_RGB24_BIG:
603 case B_CMY24:
604 bitsBytesPerPixel = 3;
605 break;
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;
616 case B_CMAP8:
617 case B_GRAY8:
618 bitsBytesPerPixel = 1;
619 break;
621 default:
622 return B_ERROR;
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;
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;
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;
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;
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;
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;
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;
714 case B_CMAP8:
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;
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;
733 case B_CMYK32:
735 bitspixel = bitsRowData + (i * bitsBytesPerPixel);
736 bmppixel = bmpRowData + (i * 3);
738 int32 comp = 255 - bitspixel[2] - bitspixel[3];
739 bmppixel[0] = (comp < 0) ? 0 : comp;
741 comp = 255 - bitspixel[1] - bitspixel[3];
742 bmppixel[1] = (comp < 0) ? 0 : comp;
744 comp = 255 - bitspixel[0] - bitspixel[3];
745 bmppixel[2] = (comp < 0) ? 0 : comp;
746 break;
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;
759 default:
760 break;
761 } // switch (fromspace)
762 } // for for (uint32 i = 0; i < msheader.width; i++)
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;
772 if (msheader.height > 0)
773 inSource->Seek(bitsRowBytes * -2, SEEK_CUR);
774 rd = inSource->Read(bitsRowData, bitsRowBytes);
775 } // while (rd == bitsRowBytes)
777 delete[] bmpRowData;
778 delete[] bitsRowData;
780 return B_OK;
783 // ---------------------------------------------------------------
784 // translate_from_bits8_to_bmp8
786 // Converts 8-bit Be Bitmaps ('bits') to the MS 8-bit BMP format
788 // Preconditions:
790 // Parameters: inSource, contains the bits data to convert
792 // outDestination, where the BMP data will be written
794 // bitsRowBytes, number of bytes in one row of
795 // bits data
797 // msheader, contains information about the BMP
798 // dimensions and filesize
800 // Postconditions:
802 // Returns: B_ERROR, if memory couldn't be allocated or another
803 // error occured
805 // B_OK, if no errors occurred
806 // ---------------------------------------------------------------
807 status_t
808 translate_from_bits8_to_bmp8(BPositionIO *inSource,
809 BPositionIO *outDestination, int32 bitsRowBytes, MSInfoHeader &msheader)
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;
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;
837 if (msheader.height > 0)
838 inSource->Seek(bitsRowBytes * -2, SEEK_CUR);
839 rd = inSource->Read(bitsRowData, bitsRowBytes);
840 } // while (rd == bitsRowBytes)
842 delete[] bmpRowData;
843 delete[] bitsRowData;
845 return B_OK;
848 // ---------------------------------------------------------------
849 // translate_from_bits1_to_bmp1
851 // Converts 1-bit Be Bitmaps ('bits') to the MS 1-bit BMP format
853 // Preconditions:
855 // Parameters: inSource, contains the bits data to convert
857 // outDestination, where the BMP data will be written
859 // bitsRowBytes, number of bytes in one row of
860 // bits data
862 // msheader, contains information about the BMP
863 // dimensions and filesize
865 // Postconditions:
867 // Returns: B_ERROR, if memory couldn't be allocated or another
868 // error occured
870 // B_OK, if no errors occurred
871 // ---------------------------------------------------------------
872 status_t
873 translate_from_bits1_to_bmp1(BPositionIO *inSource,
874 BPositionIO *outDestination, int32 bitsRowBytes, MSInfoHeader &msheader)
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;
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++;
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;
923 if (msheader.height > 0)
924 inSource->Seek(bitsRowBytes * -2, SEEK_CUR);
925 rd = inSource->Read(bitsRowData, bitsRowBytes);
926 } // while (rd == bitsRowBytes)
928 delete[] bmpRowData;
929 delete[] bitsRowData;
931 return B_OK;
934 // ---------------------------------------------------------------
935 // write_bmp_headers
937 // Writes the MS BMP headers (fileHeader and msheader)
938 // to outDestination.
940 // Preconditions:
942 // Parameters: outDestination, where the headers are written to
944 // fileHeader, BMP file header data
946 // msheader, BMP info header data
948 // Postconditions:
950 // Returns: B_ERROR, if something went wrong
952 // B_OK, if there were no problems writing out the headers
953 // ---------------------------------------------------------------
954 status_t
955 write_bmp_headers(BPositionIO *outDestination, BMPFileHeader &fileHeader,
956 MSInfoHeader &msheader)
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;
976 return B_OK;
979 // ---------------------------------------------------------------
980 // translate_from_bits
982 // Convert the data in inSource from the Be Bitmap format ('bits')
983 // to the format specified in outType (either bits or BMP).
985 // Preconditions:
987 // Parameters: inSource, the bits data to translate
989 // outType, the type of data to convert to
991 // outDestination, where the output is written to
993 // Postconditions:
995 // Returns: B_NO_TRANSLATOR, if the data is not in a supported
996 // format
998 // B_ERROR, if there was an error allocating memory or some other
999 // error
1001 // B_OK, if successfully translated the data from the bits format
1002 // ---------------------------------------------------------------
1003 status_t
1004 BMPTranslator::translate_from_bits(BPositionIO *inSource, uint32 outType,
1005 BPositionIO *outDestination)
1007 bool bheaderonly, bdataonly;
1008 bheaderonly = bdataonly = false;
1010 TranslatorBitmap bitsHeader;
1011 status_t result;
1012 result = identify_bits_header(inSource, NULL, &bitsHeader);
1013 if (result != B_OK)
1014 return result;
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;
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;
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:
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;
1062 break;
1064 case B_CMAP8:
1065 case B_GRAY8:
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;
1077 break;
1079 case B_GRAY1:
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;
1091 break;
1093 default:
1094 return B_NO_TRANSLATOR;
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;
1103 if (bheaderonly)
1104 // if user only wants the header, bail out
1105 // before the data is written
1106 return result;
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);
1129 case B_CMAP8:
1130 case B_GRAY8:
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;
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;
1158 ssize_t written = outDestination->Write(pal, 1024);
1159 if (written < 0)
1160 return written;
1161 if (written != 1024)
1162 return B_ERROR;
1164 return translate_from_bits8_to_bmp8(inSource, outDestination,
1165 bitsHeader.rowBytes, msheader);
1168 case B_GRAY1:
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;
1178 return translate_from_bits1_to_bmp1(inSource, outDestination,
1179 bitsHeader.rowBytes, msheader);
1182 default:
1183 return B_NO_TRANSLATOR;
1185 } else
1186 return B_NO_TRANSLATOR;
1189 // ---------------------------------------------------------------
1190 // translate_from_bmpnpal_to_bits
1192 // Translates a non-palette BMP from inSource to the B_RGB32
1193 // bits format.
1195 // Preconditions:
1197 // Parameters: inSource, the BMP data to be translated
1199 // outDestination, where the bits data will be written to
1201 // msheader, header information about the BMP to be written
1203 // Postconditions:
1205 // Returns: B_ERROR, if there is an error allocating memory
1207 // B_OK, if all went well
1208 // ---------------------------------------------------------------
1209 status_t
1210 translate_from_bmpnpal_to_bits(BPositionIO *inSource,
1211 BPositionIO *outDestination, MSInfoHeader &msheader)
1213 int32 bitsRowBytes = msheader.width * 4;
1214 int32 bmpBytesPerPixel = msheader.bitsperpixel / 8;
1215 int32 bmpRowBytes =
1216 get_rowbytes(msheader.width, msheader.bitsperpixel);
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;
1230 if (msheader.height > 0)
1231 outDestination->Seek((msheader.height - 1) * bitsRowBytes, SEEK_CUR);
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;
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);
1250 status_t ret = B_OK;
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;
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;
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);
1282 if (written != bitsRowBytes) {
1283 // break on write error
1284 if (written >= 0)
1285 ret = B_ERROR;
1286 else
1287 ret = read;
1288 break;
1292 delete[] bmpRowData;
1293 delete[] bitsRowData;
1295 return ret;
1298 // ---------------------------------------------------------------
1299 // translate_from_bmppal_to_bits
1301 // Translates an uncompressed, palette BMP from inSource to
1302 // the B_RGB32 bits format.
1304 // Preconditions:
1306 // Parameters: inSource, the BMP data to be translated
1308 // outDestination, where the bits data will be written to
1310 // msheader, header information about the BMP to be written
1312 // palette, BMP palette for the BMP image
1314 // frommsformat, true if BMP in inSource is in MS format,
1315 // false if it is in OS/2 format
1317 // Postconditions:
1319 // Returns: B_NO_MEMORY, if there is an error allocating memory
1321 // B_OK, if all went well
1322 // ---------------------------------------------------------------
1323 status_t
1324 translate_from_bmppal_to_bits(BPositionIO *inSource,
1325 BPositionIO *outDestination, MSInfoHeader &msheader,
1326 const uint8 *palette, bool frommsformat)
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;
1336 uint8 mask = 1;
1337 mask = (mask << bitsPerPixel) - 1;
1339 int32 bmpRowBytes =
1340 get_rowbytes(msheader.width, msheader.bitsperpixel);
1341 int32 bmppixrow = 0;
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);
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;
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);
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;
1386 if (msheader.height > 0)
1387 outDestination->Seek(bitsRowBytes * -2, SEEK_CUR);
1388 rd = inSource->Read(bmpRowData, bmpRowBytes);
1391 delete[] bmpRowData;
1392 delete[] bitsRowData;
1394 return B_OK;
1398 // ---------------------------------------------------------------
1399 // pixelcpy
1401 // Copies count 32-bit pixels with a color value of pixel to dest.
1403 // Preconditions:
1405 // Parameters: dest, where the pixel data will be copied to
1407 // pixel, the 32-bit color value to copy to dest
1408 // count times
1410 // count, the number of times pixel is copied to
1411 // dest
1413 // Postconditions:
1415 // Returns:
1416 // ---------------------------------------------------------------
1417 void
1418 pixelcpy(uint8 *dest, uint32 pixel, uint32 count)
1420 for (uint32 i = 0; i < count; i++) {
1421 memcpy(dest, &pixel, 3);
1422 dest += 4;
1426 // ---------------------------------------------------------------
1427 // translate_from_bmppalr_to_bits
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.
1434 // Preconditions:
1436 // Parameters: inSource, the BMP data to be translated
1438 // outDestination, where the bits data will be written to
1440 // datasize, number of bytes of data needed for the bits output
1442 // msheader, header information about the BMP to be written
1444 // palette, BMP palette for data in inSource
1446 // Postconditions:
1448 // Returns: B_ERROR, if there is an error allocating memory
1450 // B_OK, if all went well
1451 // ---------------------------------------------------------------
1452 status_t
1453 translate_from_bmppalr_to_bits(BPositionIO *inSource,
1454 BPositionIO *outDestination, int32 datasize, MSInfoHeader &msheader,
1455 const uint8 *palette)
1457 uint16 pixelsPerByte = 8 / msheader.bitsperpixel;
1458 uint16 bitsPerPixel = msheader.bitsperpixel;
1459 uint8 mask = (1 << bitsPerPixel) - 1;
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;
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;
1500 rd = inSource->Read(&indices, 1);
1501 if (rd != 1) {
1502 rd = -1;
1503 break;
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++;
1511 // special code
1512 } else {
1513 uint8 code;
1514 rd = inSource->Read(&code, 1);
1515 if (rd != 1) {
1516 rd = -1;
1517 break;
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;
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);
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);
1554 rd = 0;
1555 // break out of while loop
1556 break;
1558 // delta, skip several rows and/or columns and
1559 // fill the skipped pixels with the default color
1560 case 2:
1562 uint8 da[2], lastcol, dx, dy;
1563 rd = inSource->Read(da, 2);
1564 if (rd != 2) {
1565 rd = -1;
1566 break;
1568 dx = da[0];
1569 dy = da[1];
1571 // abort if dx or dy is too large
1572 if ((dx + bmppixcol >= msheader.width) ||
1573 (dy + bmppixrow >= rowCount)) {
1574 rd = -1;
1575 break;
1578 lastcol = bmppixcol;
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);
1592 if (bmppixcol < static_cast<int32>(lastcol + dx)) {
1593 pixelcpy(bitsRowData + (bmppixcol * 4), defaultcolor,
1594 dx + lastcol - bmppixcol);
1595 bmppixcol = dx + lastcol;
1598 break;
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;
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;
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;
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++;
1641 break;
1644 if (rd > 0)
1645 rd = inSource->Read(&count, 1);
1648 delete[] bitsRowData;
1650 if (!rd)
1651 return B_OK;
1652 else
1653 return B_NO_TRANSLATOR;
1656 // ---------------------------------------------------------------
1657 // translate_from_bmp
1659 // Convert the data in inSource from the BMP format
1660 // to the format specified in outType (either bits or BMP).
1662 // Preconditions:
1664 // Parameters: inSource, the bits data to translate
1666 // outType, the type of data to convert to
1668 // outDestination, where the output is written to
1670 // Postconditions:
1672 // Returns: B_NO_TRANSLATOR, if the data is not in a supported
1673 // format
1675 // B_ERROR, if there was an error allocating memory or some other
1676 // error
1678 // B_OK, if successfully translated the data from the bits format
1679 // ---------------------------------------------------------------
1680 status_t
1681 BMPTranslator::translate_from_bmp(BPositionIO *inSource, uint32 outType,
1682 BPositionIO *outDestination)
1684 bool bheaderonly, bdataonly;
1685 bheaderonly = bdataonly = false;
1687 BMPFileHeader fileHeader;
1688 MSInfoHeader msheader;
1689 bool frommsformat;
1690 off_t os2skip = 0;
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;
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;
1708 if (bheaderonly)
1709 // if the user only wants the header,
1710 // bail before it is written
1711 return result;
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);
1728 rdtotal = fileHeader.dataOffset;
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);
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);
1743 if (rd == 0)
1744 return B_OK;
1745 else
1746 return B_ERROR;
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;
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) {
1764 uint8 palBytesPerPixel;
1765 if (frommsformat)
1766 palBytesPerPixel = 4;
1767 else
1768 palBytesPerPixel = 3;
1770 if (!msheader.colorsused)
1771 msheader.colorsused = 1 << msheader.bitsperpixel;
1773 if (inSource->Read(bmppalette, msheader.colorsused *
1774 palBytesPerPixel) !=
1775 (off_t) msheader.colorsused * palBytesPerPixel)
1776 return B_NO_TRANSLATOR;
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;
1790 if (nskip > 0 && inSource->Seek(nskip, SEEK_CUR) < 0)
1791 return B_NO_TRANSLATOR;
1793 bitsHeader.rowBytes = msheader.width * 4;
1794 bitsHeader.colors = B_RGB32;
1795 int32 datasize = bitsHeader.rowBytes * abs(msheader.height);
1796 bitsHeader.dataSize = datasize;
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));
1805 if (bheaderonly)
1806 // if the user only wants the header,
1807 // bail before the data is written
1808 return B_OK;
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);
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);
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;
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);
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;
1843 case 1:
1844 return translate_from_bmppal_to_bits(inSource,
1845 outDestination, msheader, bmppalette, frommsformat);
1847 default:
1848 return B_NO_TRANSLATOR;
1851 } else
1852 return B_NO_TRANSLATOR;
1855 // ---------------------------------------------------------------
1856 // DerivedTranslate
1858 // Translates the data in inSource to the type outType and stores
1859 // the translated data in outDestination.
1861 // Preconditions:
1863 // Parameters: inSource, the data to be translated
1865 // inInfo, hint about the data in inSource (not used)
1867 // ioExtension, configuration options for the
1868 // translator
1870 // outType, the type to convert inSource to
1872 // outDestination, where the translated data is
1873 // put
1875 // baseType, indicates whether inSource is in the
1876 // bits format, not in the bits format or
1877 // is unknown
1879 // Postconditions:
1881 // Returns: B_BAD_VALUE, if the options in ioExtension are bad
1883 // B_NO_TRANSLATOR, if this translator doesn't understand the data
1885 // B_ERROR, if there was an error allocating memory or converting
1886 // data
1888 // B_OK, if all went well
1889 // ---------------------------------------------------------------
1890 status_t
1891 BMPTranslator::DerivedTranslate(BPositionIO *inSource,
1892 const translator_info *inInfo, BMessage *ioExtension,
1893 uint32 outType, BPositionIO *outDestination, int32 baseType)
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;
1905 BView *
1906 BMPTranslator::NewConfigView(TranslatorSettings *settings)
1908 return new BMPView(BRect(0, 0, 225, 175),
1909 B_TRANSLATE("BMPTranslator Settings"), B_FOLLOW_ALL, B_WILL_DRAW,
1910 settings);