Files
haiku-beta6/src/add-ons/translators/bmptranslator/BMPTranslator.cpp
T
2002-07-27 01:47:54 +00:00

1427 lines
40 KiB
C++

/*****************************************************************************/
// [Application Name]
//
// Version: [0.0.0] [Development Stage]
//
// [Description]
//
//
// This application and all source files used in its construction, except
// where noted, are licensed under the MIT License, and have been written
// and are:
//
// Copyright (c) 2002 OpenBeOS Project
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
/*****************************************************************************/
#include <string.h>
#include <stdio.h>
#include "BMPTranslator.h"
#include "BMPView.h"
#define min(x,y) ((x < y) ? x : y)
translation_format gInputFormats[] = {
{
B_TRANSLATOR_BITMAP,
B_TRANSLATOR_BITMAP,
BBT_QUALITY,
BBT_CAPABILITY,
"image/x-be-bitmap",
"Be Bitmap Format (BMPTranslator)"
},
{
B_BMP_FORMAT,
B_TRANSLATOR_BITMAP,
BMP_QUALITY,
BMP_CAPABILITY,
"image/x-bmp",
"BMP image"
}
};
translation_format gOutputFormats[] = {
{
B_TRANSLATOR_BITMAP,
B_TRANSLATOR_BITMAP,
BBT_QUALITY,
BBT_CAPABILITY,
"image/x-be-bitmap",
"Be Bitmap Format (BMPTranslator)"
},
{
B_BMP_FORMAT,
B_TRANSLATOR_BITMAP,
BMP_QUALITY,
BMP_CAPABILITY,
"image/x-bmp",
"BMP image (MS format)"
}
};
BTranslator *make_nth_translator(int32 n, image_id you, uint32 flags, ...)
{
if (!n)
return new BMPTranslator();
else
return NULL;
}
BMPTranslator::BMPTranslator() : BTranslator()
{
strcpy(fName, "BMP Images");
sprintf(fInfo, "BMP image translator v%d.%d.%d %s\n",
BMP_TRANSLATOR_VERSION / 100, (BMP_TRANSLATOR_VERSION / 10) % 10,
BMP_TRANSLATOR_VERSION % 10, __DATE__);
}
BMPTranslator::~BMPTranslator()
{
}
const char *BMPTranslator::TranslatorName() const
{
return fName;
}
const char *BMPTranslator::TranslatorInfo() const
{
return fInfo;
}
int32 BMPTranslator::TranslatorVersion() const
{
return BMP_TRANSLATOR_VERSION;
}
const translation_format *BMPTranslator::InputFormats(int32 *out_count) const
{
if (out_count)
*out_count = 2;
return gInputFormats;
}
const translation_format *BMPTranslator::OutputFormats(int32 *out_count) const
{
if (out_count)
*out_count = 2;
return gOutputFormats;
}
status_t identify_bits_header(BPositionIO *inSource, translator_info *outInfo,
ssize_t amtread, uint8 *read, TranslatorBitmap *pheader = NULL)
{
TranslatorBitmap header;
memcpy(&header, read, amtread);
// copy portion of header already read in
// read in the rest of the header
ssize_t size = sizeof(TranslatorBitmap) - amtread;
if (inSource->Read(((uint8 *) &header) + amtread, size) != size)
return B_NO_TRANSLATOR;
// convert to host byte order
if (swap_data(B_UINT32_TYPE, &header, sizeof(TranslatorBitmap),
B_SWAP_BENDIAN_TO_HOST) != B_OK)
return B_ERROR;
// check if header values are reasonable
if (header.colors != B_RGB32 &&
header.colors != B_RGB32_BIG &&
header.colors != B_RGBA32 &&
header.colors != B_RGBA32_BIG &&
header.colors != B_RGB24 &&
header.colors != B_RGB24_BIG &&
header.colors != B_RGB16 &&
header.colors != B_RGB16_BIG &&
header.colors != B_RGB15 &&
header.colors != B_RGB15_BIG &&
header.colors != B_RGBA15 &&
header.colors != B_RGBA15_BIG &&
header.colors != B_CMAP8 &&
header.colors != B_GRAY8 &&
header.colors != B_GRAY1 &&
header.colors != B_CMYK32 &&
header.colors != B_CMY32 &&
header.colors != B_CMYA32 &&
header.colors != B_CMY24)
return B_NO_TRANSLATOR;
if (header.rowBytes * (header.bounds.Height() + 1) > header.dataSize)
return B_NO_TRANSLATOR;
if (outInfo) {
outInfo->type = B_TRANSLATOR_BITMAP;
outInfo->group = B_TRANSLATOR_BITMAP;
outInfo->quality = BBT_QUALITY;
outInfo->capability = BBT_CAPABILITY;
strcpy(outInfo->name, "Be Bitmap Format (BMPTranslator)");
strcpy(outInfo->MIME, "image/x-be-bitmap");
}
if (pheader) {
pheader->magic = header.magic;
pheader->bounds = header.bounds;
pheader->rowBytes = header.rowBytes;
pheader->colors = header.colors;
pheader->dataSize = header.dataSize;
}
return B_OK;
}
status_t identify_bmp_header(BPositionIO *inSource, translator_info *outInfo,
ssize_t amtread, uint8 *read, BMPFileHeader *pfileheader = NULL, MSInfoHeader *pmsheader = NULL, bool *pfrommsformat = NULL)
{
uint8 buf[40];
BMPFileHeader fileHeader;
memcpy(buf, read, amtread);
// copy portion of fileHeader already read in
// read in the rest of the fileHeader
ssize_t size = 14 - amtread;
if (inSource->Read(buf + amtread, size) != size)
return B_NO_TRANSLATOR;
// convert fileHeader to host byte order
memcpy(&fileHeader.magic, buf, 2);
memcpy(&fileHeader.fileSize, buf + 2, 4);
memcpy(&fileHeader.reserved, buf + 6, 4);
memcpy(&fileHeader.dataOffset, buf + 10, 4);
if (swap_data(B_UINT16_TYPE, &fileHeader.magic, sizeof(uint16),
B_SWAP_LENDIAN_TO_HOST) != B_OK)
return B_ERROR;
if (swap_data(B_UINT32_TYPE, ((uint8 *) &fileHeader) + 2, 12,
B_SWAP_LENDIAN_TO_HOST) != B_OK)
return B_ERROR;
uint32 headersize = 0;
if (inSource->Read(&headersize, 4) != 4)
return B_NO_TRANSLATOR;
if (swap_data(B_UINT32_TYPE, &headersize, 4, B_SWAP_LENDIAN_TO_HOST) != B_OK)
return B_ERROR;
if (headersize == sizeof(MSInfoHeader)) {
// MS format
MSInfoHeader msheader;
msheader.size = headersize;
if (inSource->Read((uint8 *) (&msheader) + 4, 36) != 36)
return B_NO_TRANSLATOR;
// convert msheader to host byte order
if (swap_data(B_UINT32_TYPE, (uint8 *) (&msheader) + 4, 36,
B_SWAP_LENDIAN_TO_HOST) != B_OK)
return B_ERROR;
// check if msheader is valid
if (msheader.planes != 1)
return B_NO_TRANSLATOR;
if ((msheader.bitsperpixel != 1 || msheader.compression != BMP_NO_COMPRESS) &&
(msheader.bitsperpixel != 4 || msheader.compression != BMP_NO_COMPRESS) &&
(msheader.bitsperpixel != 4 || msheader.compression != BMP_RLE4_COMPRESS) &&
(msheader.bitsperpixel != 8 || msheader.compression != BMP_NO_COMPRESS) &&
(msheader.bitsperpixel != 8 || msheader.compression != BMP_RLE8_COMPRESS) &&
(msheader.bitsperpixel != 24 || msheader.compression != BMP_NO_COMPRESS) &&
(msheader.bitsperpixel != 32 || msheader.compression != BMP_NO_COMPRESS))
return B_NO_TRANSLATOR;
if (msheader.colorsimportant > msheader.colorsused)
return B_NO_TRANSLATOR;
if (outInfo) {
outInfo->type = B_BMP_FORMAT;
outInfo->group = B_TRANSLATOR_BITMAP;
outInfo->quality = BMP_QUALITY;
outInfo->capability = BMP_CAPABILITY;
sprintf(outInfo->name, "BMP image (MS format, %d bits", msheader.bitsperpixel);
if (msheader.compression)
strcat(outInfo->name, ", RLE)");
else
strcat(outInfo->name, ")");
strcpy(outInfo->MIME, "image/x-bmp");
}
if (pfileheader)
(*pfileheader) = fileHeader;
if (pmsheader)
(*pmsheader) = msheader;
if (pfrommsformat)
(*pfrommsformat) = true;
return B_OK;
} else if (headersize == sizeof(OS2InfoHeader)) {
// OS/2 format
OS2InfoHeader os2header;
os2header.size = headersize;
if (inSource->Read((uint8 *) (&os2header) + 4, 8) != 8)
return B_NO_TRANSLATOR;
// convert msheader to host byte order
if (swap_data(B_UINT32_TYPE, (uint8 *) (&os2header) + 4, 8,
B_SWAP_LENDIAN_TO_HOST) != B_OK)
return B_ERROR;
// check if msheader is valid
if (os2header.planes != 1)
return B_NO_TRANSLATOR;
if (os2header.bitsperpixel != 1 &&
os2header.bitsperpixel != 4 &&
os2header.bitsperpixel != 8 &&
os2header.bitsperpixel != 24)
return B_NO_TRANSLATOR;
if (outInfo) {
outInfo->type = B_BMP_FORMAT;
outInfo->group = B_TRANSLATOR_BITMAP;
outInfo->quality = BMP_QUALITY;
outInfo->capability = BMP_CAPABILITY;
sprintf(outInfo->name, "BMP image (OS/2 format, %d bits)", os2header.bitsperpixel);
strcpy(outInfo->MIME, "image/x-bmp");
}
if (pfileheader && pmsheader) {
pfileheader->magic = 'MB';
pfileheader->fileSize = 0;
pfileheader->reserved = 0;
pfileheader->dataOffset = 0;
pmsheader->size = 40;
pmsheader->width = os2header.width;
pmsheader->height = os2header.height;
pmsheader->planes = 1;
pmsheader->bitsperpixel = os2header.bitsperpixel;
pmsheader->compression = BMP_NO_COMPRESS;
//pmsheader->imagesize = 0;
pmsheader->xpixperm = 2835; // 72 dpi horizontal
pmsheader->ypixperm = 2835; // 72 dpi vertical
pmsheader->colorsused = 0;
pmsheader->colorsimportant = 0;
int32 padding = 0;
// determine fileSize / imagesize
switch (pmsheader->bitsperpixel) {
case 32:
case 24:
{
uint8 bytesPerPixel = pmsheader->bitsperpixel / 8;
pfileheader->dataOffset = 54;
padding = (pmsheader->width * bytesPerPixel) % 4;
if (padding)
padding = 4 - padding;
pmsheader->imagesize = ((pmsheader->width * bytesPerPixel) + padding) *
pmsheader->height;
pfileheader->fileSize = pfileheader->dataOffset +
pmsheader->imagesize;
break;
}
case 8:
case 4:
case 1:
{
uint16 ncolors = 1 << pmsheader->bitsperpixel;
uint8 pixelsPerByte = 8 / pmsheader->bitsperpixel;
pfileheader->dataOffset = 54 + (ncolors * 4);
if (!(pmsheader->width % pixelsPerByte))
padding = (pmsheader->width / pixelsPerByte) % 4;
else
padding = ((pmsheader->width + pixelsPerByte -
(pmsheader->width % pixelsPerByte)) / pixelsPerByte) % 4;
if (padding)
padding = 4 - padding;
pmsheader->imagesize = ((pmsheader->width / pixelsPerByte) + ((pmsheader->width % pixelsPerByte) ? 1 : 0) + padding) *
pmsheader->height;
pfileheader->fileSize = pfileheader->dataOffset +
pmsheader->imagesize;
break;
}
default:
break;
}
}
if (pfrommsformat)
(*pfrommsformat) = false;
return B_OK;
} else
return B_NO_TRANSLATOR;
}
status_t BMPTranslator::Identify(BPositionIO *inSource,
const translation_format *inFormat, BMessage *ioExtension,
translator_info *outInfo, uint32 outType)
{
if (!outType)
outType = B_TRANSLATOR_BITMAP;
if (outType != B_TRANSLATOR_BITMAP && outType != B_BMP_FORMAT)
return B_NO_TRANSLATOR;
uint8 ch[4];
uint32 nbits = B_TRANSLATOR_BITMAP;
uint16 nbm = 'MB';
// Convert the magic numbers to the various byte orders so that
// I won't have to convert the data read in to see whether or not
// it is a supported type
if (swap_data(B_UINT32_TYPE, &nbits, 4, B_SWAP_HOST_TO_BENDIAN) != B_OK)
return B_ERROR;
if (swap_data(B_UINT16_TYPE, &nbm, 2, B_SWAP_HOST_TO_LENDIAN) != B_OK)
return B_ERROR;
// Read in the magic number and determine if it
// is a supported type
if (inSource->Read(ch, 4) != 4)
return B_NO_TRANSLATOR;
uint32 n32ch;
memcpy(&n32ch, ch, sizeof(uint32));
uint16 n16ch;
memcpy(&n16ch, ch, sizeof(uint16));
// if B_TRANSLATOR_BITMAP type
if (n32ch == nbits)
return identify_bits_header(inSource, outInfo, 4, ch);
// if BMP type in Little Endian byte order
else if (n16ch == nbm)
return identify_bmp_header(inSource, outInfo, 4, ch);
else
return B_NO_TRANSLATOR;
}
// for converting uncompressed BMP images with no palette to the Be Bitmap format
status_t translate_from_bits_to_bmp24(BPositionIO *inSource, BPositionIO *outDestination,
color_space fromspace, MSInfoHeader &msheader)
{
int32 bitsBytesPerPixel = 0;
switch (fromspace) {
case B_RGB32:
case B_RGB32_BIG:
case B_RGBA32:
case B_RGBA32_BIG:
case B_CMY32:
case B_CMYA32:
case B_CMYK32:
bitsBytesPerPixel = 4;
break;
case B_RGB24:
case B_RGB24_BIG:
case B_CMY24:
bitsBytesPerPixel = 3;
break;
case B_RGB16:
case B_RGB16_BIG:
case B_RGBA15:
case B_RGBA15_BIG:
case B_RGB15:
case B_RGB15_BIG:
bitsBytesPerPixel = 2;
break;
case B_CMAP8:
case B_GRAY8:
bitsBytesPerPixel = 1;
break;
default:
return B_ERROR;
}
int32 bitsRowBytes = msheader.width * bitsBytesPerPixel;
int32 bmpBytesPerPixel = msheader.bitsperpixel / 8;
int32 padding = (msheader.width * bmpBytesPerPixel) % 4;
if (padding)
padding = 4 - padding;
int32 bmpRowBytes = (msheader.width * bmpBytesPerPixel) + padding;
uint32 bmppixrow = 0;
off_t bitsoffset = ((msheader.height - 1) * bitsRowBytes);
inSource->Seek(bitsoffset, SEEK_CUR);
uint8 *bmpRowData = new uint8[bmpRowBytes];
if (!bmpRowData)
return B_ERROR;
uint8 *bitsRowData = new uint8[bitsRowBytes];
if (!bitsRowData) {
delete[] bmpRowData;
return B_ERROR;
}
memset(bmpRowData, 0, bmpRowBytes);
ssize_t rd = inSource->Read(bitsRowData, bitsRowBytes);
const color_map *pmap = system_colors();
while (rd == bitsRowBytes) {
for (uint32 i = 0; i < msheader.width; i++) {
uint8 *bitspixel, *bmppixel;
uint16 val;
switch (fromspace) {
case B_RGB32:
case B_RGBA32:
case B_RGB24:
memcpy(bmpRowData + (i * 3),
bitsRowData + (i * bitsBytesPerPixel), 3);
break;
case B_RGB16:
case B_RGB16_BIG:
bitspixel = bitsRowData + (i * bitsBytesPerPixel);
bmppixel = bmpRowData + (i * 3);
if (fromspace == B_RGB16)
val = bitspixel[0] + (bitspixel[1] << 8);
else
val = bitspixel[1] + (bitspixel[1] << 8);
bmppixel[0] = ((val & 0x1f) << 3) | ((val & 0x1f) >> 2);
bmppixel[1] = ((val & 0x7e0) >> 3) | ((val & 0x7e0) >> 9);
bmppixel[2] = ((val & 0xf800) >> 8) | ((val & 0xf800) >> 13);
break;
case B_RGB15:
case B_RGB15_BIG:
case B_RGBA15:
case B_RGBA15_BIG:
// NOTE: the alpha data for B_RGBA15* is ignored and discarded
bitspixel = bitsRowData + (i * bitsBytesPerPixel);
bmppixel = bmpRowData + (i * 3);
if (fromspace == B_RGB15 || fromspace == B_RGBA15)
val = bitspixel[0] + (bitspixel[1] << 8);
else
val = bitspixel[1] + (bitspixel[0] << 8);
bmppixel[0] = ((val & 0x1f) << 3) | ((val & 0x1f) >> 2);
bmppixel[1] = ((val & 0x3e0) >> 2) | ((val & 0x3e0) >> 7);
bmppixel[2] = ((val & 0x7c00) >> 7) | ((val & 0x7c00) >> 12);
break;
case B_RGB32_BIG:
case B_RGBA32_BIG:
bitspixel = bitsRowData + (i * bitsBytesPerPixel);
bmppixel = bmpRowData + (i * 3);
bmppixel[0] = bitspixel[3];
bmppixel[1] = bitspixel[2];
bmppixel[2] = bitspixel[1];
break;
case B_RGB24_BIG:
bitspixel = bitsRowData + (i * bitsBytesPerPixel);
bmppixel = bmpRowData + (i * 3);
bmppixel[0] = bitspixel[2];
bmppixel[1] = bitspixel[1];
bmppixel[2] = bitspixel[0];
break;
case B_CMAP8:
bitspixel = bitsRowData + (i * bitsBytesPerPixel);
bmppixel = bmpRowData + (i * 3);
rgb_color c = pmap->color_list[bitspixel[0]];
bmppixel[0] = c.blue;
bmppixel[1] = c.green;
bmppixel[2] = c.red;
break;
case B_GRAY8:
bitspixel = bitsRowData + (i * bitsBytesPerPixel);
bmppixel = bmpRowData + (i * 3);
bmppixel[0] = bitspixel[0];
bmppixel[1] = bitspixel[0];
bmppixel[2] = bitspixel[0];
break;
case B_CMYK32:
{
bitspixel = bitsRowData + (i * bitsBytesPerPixel);
bmppixel = bmpRowData + (i * 3);
int32 comp = 255 - bitspixel[2] - bitspixel[3];
bmppixel[0] = (comp < 0) ? 0 : comp;
comp = 255 - bitspixel[1] - bitspixel[3];
bmppixel[1] = (comp < 0) ? 0 : comp;
comp = 255 - bitspixel[0] - bitspixel[3];
bmppixel[2] = (comp < 0) ? 0 : comp;
break;
}
case B_CMY32:
case B_CMYA32:
case B_CMY24:
bitspixel = bitsRowData + (i * bitsBytesPerPixel);
bmppixel = bmpRowData + (i * 3);
bmppixel[0] = 255 - bitspixel[2];
bmppixel[1] = 255 - bitspixel[1];
bmppixel[2] = 255 - bitspixel[0];
break;
default:
break;
} // switch (fromspace)
} // for for (uint32 i = 0; i < msheader.width; i++)
outDestination->Write(bmpRowData, bmpRowBytes);
bmppixrow++;
// if I've read all of the pixel data, break
// out of the loop so I don't try to read
// non-pixel data
if (bmppixrow == msheader.height)
break;
inSource->Seek(-(bitsRowBytes * 2), SEEK_CUR);
rd = inSource->Read(bitsRowData, bitsRowBytes);
} // while (rd == bitsRowBytes)
delete[] bmpRowData;
delete[] bitsRowData;
return B_OK;
}
// for converting uncompressed BMP images with no palette to the Be Bitmap format
status_t translate_from_bits8_to_bmp8(BPositionIO *inSource, BPositionIO *outDestination,
int32 bitsRowBytes, MSInfoHeader &msheader)
{
int32 padding;
padding = msheader.width % 4;
if (padding)
padding = 4 - padding;
int32 bmpRowBytes = msheader.width + padding;
uint32 bmppixrow = 0;
off_t bitsoffset = ((msheader.height - 1) * bitsRowBytes);
inSource->Seek(bitsoffset, SEEK_CUR);
uint8 *bmpRowData = new uint8[bmpRowBytes];
if (!bmpRowData)
return B_ERROR;
uint8 *bitsRowData = new uint8[bitsRowBytes];
if (!bitsRowData) {
delete[] bmpRowData;
return B_ERROR;
}
ssize_t rd = inSource->Read(bitsRowData, bitsRowBytes);
while (rd == bitsRowBytes) {
memcpy(bmpRowData, bitsRowData, msheader.width);
if (padding) {
uint8 zeros[] = {0, 0, 0, 0};
memcpy(bmpRowData + msheader.width, zeros, padding);
}
outDestination->Write(bmpRowData, bmpRowBytes);
bmppixrow++;
// if I've read all of the pixel data, break
// out of the loop so I don't try to read
// non-pixel data
if (bmppixrow == msheader.height)
break;
inSource->Seek(-(bitsRowBytes * 2), SEEK_CUR);
rd = inSource->Read(bitsRowData, bitsRowBytes);
} // while (rd == bitsRowBytes)
delete[] bmpRowData;
delete[] bitsRowData;
return B_OK;
}
// for converting uncompressed BMP images with no palette to the Be Bitmap format
status_t translate_from_bits1_to_bmp1(BPositionIO *inSource, BPositionIO *outDestination,
int32 bitsRowBytes, MSInfoHeader &msheader)
{
uint16 pixelsPerByte = 8 / msheader.bitsperpixel;
int32 padding;
if (!(msheader.width % pixelsPerByte))
padding = (msheader.width / pixelsPerByte) % 4;
else
padding = ((msheader.width + pixelsPerByte -
(msheader.width % pixelsPerByte)) / pixelsPerByte) % 4;
if (padding)
padding = 4 - padding;
int32 bmpRowBytes = (msheader.width / pixelsPerByte) +
((msheader.width % pixelsPerByte) ? 1 : 0) + padding;
uint32 bmppixrow = 0;
off_t bitsoffset = ((msheader.height - 1) * bitsRowBytes);
inSource->Seek(bitsoffset, SEEK_CUR);
uint8 *bmpRowData = new uint8[bmpRowBytes];
if (!bmpRowData)
return B_ERROR;
uint8 *bitsRowData = new uint8[bitsRowBytes];
if (!bitsRowData) {
delete[] bmpRowData;
return B_ERROR;
}
ssize_t rd = inSource->Read(bitsRowData, bitsRowBytes);
while (rd == bitsRowBytes) {
uint32 bmppixcol = 0;
memset(bmpRowData, 0, bmpRowBytes);
for (int32 i = 0; (bmppixcol < msheader.width) && (i < bitsRowBytes); i++) {
// process each byte in the row
uint8 pixels = bitsRowData[i];
for (uint8 compbit = 128; (bmppixcol < msheader.width) && compbit; compbit >>= 1) {
// for each bit in the current byte, convert to a BMP palette index and
// store that in the bmpRowData
uint8 index;
if (pixels & compbit)
// 1 == black
index = 1;
else
// 0 == white
index = 0;
bmpRowData[bmppixcol / pixelsPerByte] |= index << (7 - (bmppixcol % pixelsPerByte));
bmppixcol++;
}
}
outDestination->Write(bmpRowData, bmpRowBytes);
bmppixrow++;
// if I've read all of the pixel data, break
// out of the loop so I don't try to read
// non-pixel data
if (bmppixrow == msheader.height)
break;
inSource->Seek(-(bitsRowBytes * 2), SEEK_CUR);
rd = inSource->Read(bitsRowData, bitsRowBytes);
} // while (rd == bitsRowBytes)
delete[] bmpRowData;
delete[] bitsRowData;
return B_OK;
}
status_t translate_from_bits(BPositionIO *inSource, ssize_t amtread,
uint8 *read, uint32 outType, BPositionIO *outDestination)
{
TranslatorBitmap bitsHeader;
status_t result;
result = identify_bits_header(inSource, NULL, amtread, read, &bitsHeader);
if (result != B_OK)
return result;
// Translate B_TRANSLATOR_BITMAP to B_TRANSLATOR_BITMAP, easy enough :)
if (outType == B_TRANSLATOR_BITMAP) {
// write out bitsHeader
if (swap_data(B_UINT32_TYPE, &bitsHeader, sizeof(TranslatorBitmap),
B_SWAP_HOST_TO_BENDIAN) != B_OK)
return B_ERROR;
if (outDestination->Write(&bitsHeader,
sizeof(TranslatorBitmap)) != sizeof(TranslatorBitmap))
return B_ERROR;
uint8 buf[1024];
ssize_t rd = inSource->Read(buf, 1024);
while (rd > 0) {
outDestination->Write(buf, rd);
rd = inSource->Read(buf, 1024);
}
if (rd == 0)
return B_OK;
else
return B_ERROR;
// Translate B_TRANSLATOR_BITMAP to B_BMP_FORMAT
} else if (outType == B_BMP_FORMAT) {
// Set up BMP header
BMPFileHeader fileHeader;
fileHeader.magic = 'MB';
fileHeader.reserved = 0;
MSInfoHeader msheader;
msheader.size = 40;
msheader.width = (uint32) bitsHeader.bounds.Width() + 1;
msheader.height = (uint32) bitsHeader.bounds.Height() + 1;
msheader.planes = 1;
msheader.xpixperm = 2835; // 72 dpi horizontal
msheader.ypixperm = 2835; // 72 dpi vertical
msheader.colorsused = 0;
msheader.colorsimportant = 0;
int32 padding = 0;
// determine fileSize / imagesize
switch (bitsHeader.colors) {
case B_RGB32:
case B_RGB32_BIG:
case B_RGBA32:
case B_RGBA32_BIG:
case B_RGB24:
case B_RGB24_BIG:
case B_RGB16:
case B_RGB16_BIG:
case B_RGB15:
case B_RGB15_BIG:
case B_RGBA15:
case B_RGBA15_BIG:
case B_CMYK32:
case B_CMY32:
case B_CMYA32:
case B_CMY24:
fileHeader.dataOffset = 54;
msheader.bitsperpixel = 24;
msheader.compression = BMP_NO_COMPRESS;
padding = (msheader.width * 3) % 4;
if (padding)
padding = 4 - padding;
msheader.imagesize = ((msheader.width * 3) + padding) *
msheader.height;
fileHeader.fileSize = fileHeader.dataOffset +
msheader.imagesize;
break;
case B_CMAP8:
case B_GRAY8:
msheader.colorsused = 256;
msheader.colorsimportant = 256;
fileHeader.dataOffset = 54 + (4 * 256);
msheader.bitsperpixel = 8;
msheader.compression = BMP_NO_COMPRESS;
padding = msheader.width % 4;
if (padding)
padding = 4 - padding;
msheader.imagesize = (msheader.width + padding) * msheader.height;
fileHeader.fileSize = fileHeader.dataOffset +
msheader.imagesize;
break;
case B_GRAY1:
msheader.colorsused = 2;
msheader.colorsimportant = 2;
fileHeader.dataOffset = 62;
msheader.bitsperpixel = 1;
msheader.compression = BMP_NO_COMPRESS;
if (!(msheader.width % 8))
padding = (msheader.width / 8) % 4;
else
padding = ((msheader.width + 8 -
(msheader.width % 8)) / 8) % 4;
if (padding)
padding = 4 - padding;
msheader.imagesize = ((msheader.width / 8) + ((msheader.width % 8) ? 1 : 0) + padding) *
msheader.height;
fileHeader.fileSize = fileHeader.dataOffset +
msheader.imagesize;
break;
default:
return B_NO_TRANSLATOR;
}
// write out the BMP headers
uint8 bmpheaders[54];
memcpy(bmpheaders, &fileHeader.magic, sizeof(uint16));
memcpy(bmpheaders + 2, &fileHeader.fileSize, sizeof(uint32));
memcpy(bmpheaders + 6, &fileHeader.reserved, sizeof(uint32));
memcpy(bmpheaders + 10, &fileHeader.dataOffset, sizeof(uint32));
memcpy(bmpheaders + 14, &msheader, sizeof(msheader));
if (swap_data(B_UINT16_TYPE, bmpheaders, 2,
B_SWAP_HOST_TO_LENDIAN) != B_OK)
return B_ERROR;
if (swap_data(B_UINT32_TYPE, bmpheaders + 2, 12,
B_SWAP_HOST_TO_LENDIAN) != B_OK)
return B_ERROR;
if (swap_data(B_UINT32_TYPE, bmpheaders + 14,
sizeof(MSInfoHeader), B_SWAP_HOST_TO_LENDIAN) != B_OK)
return B_ERROR;
if (outDestination->Write(bmpheaders, 54) != 54)
return B_ERROR;
// write out the BMP pixel data
switch (bitsHeader.colors) {
case B_RGB32:
case B_RGB32_BIG:
case B_RGBA32:
case B_RGBA32_BIG:
case B_RGB24:
case B_RGB24_BIG:
case B_RGB16:
case B_RGB16_BIG:
case B_RGB15:
case B_RGB15_BIG:
case B_RGBA15:
case B_RGBA15_BIG:
case B_CMYK32:
case B_CMY32:
case B_CMYA32:
case B_CMY24:
return translate_from_bits_to_bmp24(inSource, outDestination,
bitsHeader.colors, msheader);
case B_CMAP8:
{
uint8 pal[1024] = { 0 };
const color_map *pmap = system_colors();
for (int32 i = 0; i < 256; i++) {
uint8 *palent = pal + (i * 4);
rgb_color c = pmap->color_list[i];
palent[0] = c.blue;
palent[1] = c.green;
palent[2] = c.red;
}
if (outDestination->Write(pal, 1024) != 1024)
return B_ERROR;
return translate_from_bits8_to_bmp8(inSource, outDestination,
bitsHeader.rowBytes, msheader);
}
case B_GRAY8:
{
uint8 pal[1024] = { 0 };
for (int32 i = 0; i < 256; i++) {
uint8 *palent = pal + (i * 4);
palent[0] = i;
palent[1] = i;
palent[2] = i;
}
if (outDestination->Write(pal, 1024) != 1024)
return B_ERROR;
return translate_from_bits8_to_bmp8(inSource, outDestination,
bitsHeader.rowBytes, msheader);
}
case B_GRAY1:
{
const uint32 monopal[] = { 0x00ffffff, 0x00000000 };
if (outDestination->Write(monopal, 8) != 8)
return B_ERROR;
return translate_from_bits1_to_bmp1(inSource, outDestination,
bitsHeader.rowBytes, msheader);
}
default:
break;
}
return B_NO_TRANSLATOR;
} else
return B_NO_TRANSLATOR;
}
// for converting uncompressed BMP images with no palette to the Be Bitmap format
status_t translate_from_bmpnpal_to_bits(BPositionIO *inSource, BPositionIO *outDestination,
int32 datasize, MSInfoHeader &msheader)
{
int32 bitsRowBytes = msheader.width * 4;
int32 bmpBytesPerPixel = msheader.bitsperpixel / 8;
int32 padding = (msheader.width * bmpBytesPerPixel) % 4;
if (padding)
padding = 4 - padding;
int32 bmpRowBytes = (msheader.width * bmpBytesPerPixel) + padding;
uint32 bmppixrow = 0;
off_t bmpoffset = ((msheader.height - 1) * bmpRowBytes);
inSource->Seek(bmpoffset, SEEK_CUR);
uint8 *bmpRowData = new uint8[bmpRowBytes];
if (!bmpRowData)
return B_ERROR;
uint8 *bitsRowData = new uint8[bitsRowBytes];
if (!bitsRowData) {
delete[] bmpRowData;
return B_ERROR;
}
memset(bitsRowData, 0xffffffff, bitsRowBytes);
ssize_t rd = inSource->Read(bmpRowData, bmpRowBytes);
while (rd == bmpRowBytes) {
for (uint32 i = 0; i < msheader.width; i++)
memcpy(bitsRowData + (i * 4),
bmpRowData + (i * bmpBytesPerPixel), 3);
outDestination->Write(bitsRowData, bitsRowBytes);
bmppixrow++;
// if I've read all of the pixel data, break
// out of the loop so I don't try to read
// non-pixel data
if (bmppixrow == msheader.height)
break;
inSource->Seek(-(bmpRowBytes * 2), SEEK_CUR);
rd = inSource->Read(bmpRowData, bmpRowBytes);
}
delete[] bmpRowData;
delete[] bitsRowData;
return B_OK;
}
// for converting palette-based uncompressed BMP images to the Be Bitmap format
status_t translate_from_bmppal_to_bits(BPositionIO *inSource, BPositionIO *outDestination,
int32 datasize, MSInfoHeader &msheader, const uint8 *palette, bool frommsformat)
{
uint16 pixelsPerByte = 8 / msheader.bitsperpixel;
uint16 bitsPerPixel = msheader.bitsperpixel;
uint8 palBytesPerPixel;
if (frommsformat)
palBytesPerPixel = 4;
else
palBytesPerPixel = 3;
uint8 mask = 1;
mask = (mask << bitsPerPixel) - 1;
int32 padding;
if (!(msheader.width % pixelsPerByte))
padding = (msheader.width / pixelsPerByte) % 4;
else
padding = ((msheader.width + pixelsPerByte -
(msheader.width % pixelsPerByte)) / pixelsPerByte) % 4;
if (padding)
padding = 4 - padding;
int32 bmpRowBytes = (msheader.width / pixelsPerByte) +
((msheader.width % pixelsPerByte) ? 1 : 0) + padding;
uint32 bmppixrow = 0;
off_t bmpoffset = ((msheader.height - 1) * bmpRowBytes);
inSource->Seek(bmpoffset, SEEK_CUR);
uint8 *bmpRowData = new uint8[bmpRowBytes];
if (!bmpRowData)
return B_ERROR;
int32 bitsRowBytes = msheader.width * 4;
uint8 *bitsRowData = new uint8[bitsRowBytes];
if (!bitsRowData) {
delete[] bmpRowData;
return B_ERROR;
}
memset(bitsRowData, 0xffffffff, bitsRowBytes);
ssize_t rd = inSource->Read(bmpRowData, bmpRowBytes);
while (rd == bmpRowBytes) {
for (uint32 i = 0; i < msheader.width; i++) {
uint8 indices = (bmpRowData + (i / pixelsPerByte))[0];
uint8 index;
index = (indices >>
(bitsPerPixel * ((pixelsPerByte - 1) - (i % pixelsPerByte)))) & mask;
memcpy(bitsRowData + (i * 4),
palette + (index * palBytesPerPixel), 3);
}
outDestination->Write(bitsRowData, bitsRowBytes);
bmppixrow++;
// if I've read all of the pixel data, break
// out of the loop so I don't try to read
// non-pixel data
if (bmppixrow == msheader.height)
break;
inSource->Seek(-(bmpRowBytes * 2), SEEK_CUR);
rd = inSource->Read(bmpRowData, bmpRowBytes);
}
delete[] bmpRowData;
delete[] bitsRowData;
return B_OK;
}
// for converting palette-based RLE BMP images to the Be Bitmap format
status_t translate_from_bmppalr_to_bits(BPositionIO *inSource, BPositionIO *outDestination,
int32 datasize, MSInfoHeader &msheader, const uint8 *palette)
{
uint16 pixelsPerByte = 8 / msheader.bitsperpixel;
uint16 bitsPerPixel = msheader.bitsperpixel;
uint8 mask = (1 << bitsPerPixel) - 1;
uint8 count, indices, index;
uint8 *bitspixels = new uint8[datasize];
if (!bitspixels)
return B_ERROR;
memset(bitspixels, 0xffffffff, datasize);
int32 bitsRowBytes = msheader.width * 4;
uint32 bmppixcol = 0, bmppixrow = 0;
int32 bitsoffset = 0;
ssize_t rd = inSource->Read(&count, 1);
while (rd > 0) {
// repeated color
if (count) {
rd = inSource->Read(&indices, 1);
if (rd != 1)
break;
for (uint8 i = 0; i < count; i++) {
bitsoffset = ((msheader.height -
(bmppixrow + 1)) * bitsRowBytes) +
(bmppixcol * 4);
index = (indices >> (bitsPerPixel * ((pixelsPerByte - 1) - (i % pixelsPerByte)))) & mask;
memcpy(bitspixels + bitsoffset, palette + (index * 4), 3);
bmppixcol++;
}
// special code
} else {
uint8 code;
rd = inSource->Read(&code, 1);
if (rd != 1)
break;
switch (code) {
case 0:
// end of line
// if there are columns remaing on this
// line, set them to the color at index zero
while (bmppixcol != msheader.width) {
bitsoffset = ((msheader.height -
(bmppixrow + 1)) *
bitsRowBytes) +
(bmppixcol * 4);
memcpy(bitspixels + bitsoffset, palette, 3);
bmppixcol++;
}
bmppixcol = 0;
bmppixrow++;
break;
case 1:
// end of bitmap
if (bmppixcol == msheader.width) {
bmppixcol = 0;
bmppixrow++;
}
while (bmppixrow < msheader.height) {
bitsoffset = ((msheader.height -
(bmppixrow + 1)) *
bitsRowBytes) +
(bmppixcol * 4);
memcpy(bitspixels + bitsoffset, palette, 3);
bmppixcol++;
if (bmppixcol == msheader.width) {
bmppixcol = 0;
bmppixrow++;
}
}
rd = 0;
// break out of while loop
break;
case 2:
{
// delta, wierd feature
uint8 x, dx, dy;
inSource->Read(&dx, 1);
inSource->Read(&dy, 1);
x = bmppixcol;
// set all pixels to the first entry in
// the palette, for the number of rows skipped
while (dy > 0) {
bitsoffset = ((msheader.height -
(bmppixrow + 1)) *
bitsRowBytes) +
(bmppixcol * 4);
memcpy(bitspixels + bitsoffset, palette, 3);
bmppixcol++;
if (bmppixcol == msheader.width) {
bmppixcol = 0;
bmppixrow++;
dy--;
}
}
// get to the same column as where we started
while (x != bmppixcol) {
bitsoffset = ((msheader.height -
(bmppixrow + 1)) *
bitsRowBytes) +
(bmppixcol * 4);
memcpy(bitspixels + bitsoffset, palette, 3);
bmppixcol++;
}
// move over dx pixels
for (uint8 i = 0; i < dx; i++) {
bitsoffset = ((msheader.height -
(bmppixrow + 1)) *
bitsRowBytes) +
(bmppixcol * 4);
memcpy(bitspixels + bitsoffset, palette, 3);
bmppixcol++;
}
break;
}
// code >= 3
default:
// read code uncompressed indices
uint8 uncomp[256];
int32 padding;
if (!(code % pixelsPerByte))
padding = (code / pixelsPerByte) % 2;
else
padding = ((code + pixelsPerByte -
(code % pixelsPerByte)) / pixelsPerByte) % 2;
int32 uncompBytes = (code / pixelsPerByte) +
((code % pixelsPerByte) ? 1 : 0) + padding;
rd = inSource->Read(uncomp, uncompBytes);
if (rd != uncompBytes) {
rd = 0;
break;
}
for (uint8 i = 0; i < code; i++) {
bitsoffset = ((msheader.height -
(bmppixrow + 1)) *
bitsRowBytes) +
(bmppixcol * 4);
indices = (uncomp + (i / pixelsPerByte))[0];
index = (indices >>
(bitsPerPixel * ((pixelsPerByte - 1) - (i % pixelsPerByte)))) & mask;
memcpy(bitspixels + bitsoffset,
palette + (index * 4), 3);
bmppixcol++;
}
break;
}
}
if (rd > 0)
rd = inSource->Read(&count, 1);
}
outDestination->Write(bitspixels, datasize);
delete[] bitspixels;
return B_OK;
}
status_t translate_from_bmp(BPositionIO *inSource, ssize_t amtread,
uint8 *read, uint32 outType, BPositionIO *outDestination)
{
BMPFileHeader fileHeader;
MSInfoHeader msheader;
bool frommsformat;
status_t result;
result = identify_bmp_header(inSource, NULL, amtread, read, &fileHeader, &msheader, &frommsformat);
if (result != B_OK)
return result;
// if the user wants to translate a BMP to a BMP, easy enough :)
if (outType == B_BMP_FORMAT) {
// write out the BMP headers
uint8 bmpheaders[54];
memcpy(bmpheaders, &fileHeader.magic, sizeof(uint16));
memcpy(bmpheaders + 2, &fileHeader.fileSize, sizeof(uint32));
memcpy(bmpheaders + 6, &fileHeader.reserved, sizeof(uint32));
memcpy(bmpheaders + 10, &fileHeader.dataOffset, sizeof(uint32));
memcpy(bmpheaders + 14, &msheader, sizeof(msheader));
if (swap_data(B_UINT16_TYPE, bmpheaders, 2,
B_SWAP_HOST_TO_LENDIAN) != B_OK)
return B_ERROR;
if (swap_data(B_UINT32_TYPE, bmpheaders + 2, 12,
B_SWAP_HOST_TO_LENDIAN) != B_OK)
return B_ERROR;
if (swap_data(B_UINT32_TYPE, bmpheaders + 14,
sizeof(MSInfoHeader), B_SWAP_HOST_TO_LENDIAN) != B_OK)
return B_ERROR;
if (outDestination->Write(bmpheaders, 54) != 54)
return B_ERROR;
uint8 buf[1024];
ssize_t rd;
if (!frommsformat && (msheader.bitsperpixel == 1 || msheader.bitsperpixel == 4 || msheader.bitsperpixel == 8)) {
// if OS/2 paletted format, convert palette to MS format
uint16 ncolors = 1 << msheader.bitsperpixel;
rd = inSource->Read(buf, ncolors * 3);
if (rd != ncolors * 3)
return B_NO_TRANSLATOR;
uint8 mspalent[4] = {0, 0, 0, 0};
for (uint16 i = 0; i < ncolors; i++) {
memcpy(mspalent, buf + (i * 3), 3);
outDestination->Write(mspalent, 4);
}
}
uint32 rdtotal = fileHeader.dataOffset;
rd = min(1024, fileHeader.fileSize - rdtotal);
rd = inSource->Read(buf, rd);
while (rd > 0) {
outDestination->Write(buf, rd);
rdtotal += rd;
rd = min(1024, fileHeader.fileSize - rdtotal);
rd = inSource->Read(buf, rd);
}
if (rd == 0)
return B_OK;
else
return B_ERROR;
// if translating a BMP to a Be Bitmap
} else if (outType == B_TRANSLATOR_BITMAP) {
TranslatorBitmap bitsHeader;
bitsHeader.magic = B_TRANSLATOR_BITMAP;
bitsHeader.bounds.left = 0;
bitsHeader.bounds.top = 0;
bitsHeader.bounds.right = msheader.width - 1;
bitsHeader.bounds.bottom = msheader.height - 1;
// read in palette and/or skip non-BMP data
uint8 bmppalette[1024];
off_t nskip = 0;
if (msheader.bitsperpixel == 1 ||
msheader.bitsperpixel == 4 ||
msheader.bitsperpixel == 8) {
uint8 palBytesPerPixel;
if (frommsformat)
palBytesPerPixel = 4;
else
palBytesPerPixel = 3;
if (!msheader.colorsused) {
msheader.colorsused = 1;
msheader.colorsused <<= msheader.bitsperpixel;
}
if (inSource->Read(bmppalette, msheader.colorsused * palBytesPerPixel) !=
msheader.colorsused * palBytesPerPixel)
return B_NO_TRANSLATOR;
// skip over non-BMP data
if (fileHeader.dataOffset > (msheader.colorsused * palBytesPerPixel) + 54)
nskip = fileHeader.dataOffset - ((msheader.colorsused * palBytesPerPixel) + 54);
} else if (fileHeader.dataOffset > 54)
// skip over non-BMP data
nskip = fileHeader.dataOffset - 54;
if (nskip > 0 && inSource->Seek(nskip, SEEK_CUR) < 0)
return B_NO_TRANSLATOR;
bitsHeader.rowBytes = msheader.width * 4;
bitsHeader.colors = B_RGB32;
int32 datasize = bitsHeader.rowBytes * msheader.height;
bitsHeader.dataSize = datasize;
// write out Be's Bitmap header
if (swap_data(B_UINT32_TYPE, &bitsHeader,
sizeof(TranslatorBitmap), B_SWAP_HOST_TO_BENDIAN) != B_OK)
return B_ERROR;
outDestination->Write(&bitsHeader, sizeof(TranslatorBitmap));
switch (msheader.bitsperpixel) {
case 32:
case 24:
return translate_from_bmpnpal_to_bits(inSource, outDestination,
datasize, msheader);
case 8:
// 8 bit BMP with NO compression
if (msheader.compression == BMP_NO_COMPRESS)
return translate_from_bmppal_to_bits(inSource,
outDestination, datasize, msheader, bmppalette, frommsformat);
// 8 bit RLE compressed BMP
else if (msheader.compression == BMP_RLE8_COMPRESS)
return translate_from_bmppalr_to_bits(inSource,
outDestination, datasize, msheader, bmppalette);
else
return B_NO_TRANSLATOR;
case 4:
// 4 bit BMP with NO compression
if (!msheader.compression)
return translate_from_bmppal_to_bits(inSource,
outDestination, datasize, msheader, bmppalette, frommsformat);
// 4 bit RLE compressed BMP
else if (msheader.compression == BMP_RLE4_COMPRESS)
return translate_from_bmppalr_to_bits(inSource,
outDestination, datasize, msheader, bmppalette);
else
return B_NO_TRANSLATOR;
case 1:
return translate_from_bmppal_to_bits(inSource,
outDestination, datasize, msheader, bmppalette, frommsformat);
default:
return B_NO_TRANSLATOR;
}
} else
return B_NO_TRANSLATOR;
}
status_t BMPTranslator::Translate(BPositionIO *inSource,
const translator_info *inInfo, BMessage *ioExtension,
uint32 outType, BPositionIO *outDestination)
{
if (!outType)
outType = B_TRANSLATOR_BITMAP;
if (outType != B_TRANSLATOR_BITMAP && outType != B_BMP_FORMAT)
return B_NO_TRANSLATOR;
inSource->Seek(0, SEEK_SET);
uint8 ch[4];
uint32 nbits = B_TRANSLATOR_BITMAP;
uint16 nbm = 'MB';
// Convert the magic numbers to the various byte orders so that
// I won't have to convert the data read in to see whether or not
// it is a supported type
if (swap_data(B_UINT32_TYPE, &nbits, sizeof(uint32),
B_SWAP_HOST_TO_BENDIAN) != B_OK)
return B_ERROR;
if (swap_data(B_UINT16_TYPE, &nbm, sizeof(uint16),
B_SWAP_HOST_TO_LENDIAN) != B_OK)
return B_ERROR;
// Read in the magic number and determine if it
// is a supported type
if (inSource->Read(ch, 4) != 4)
return B_NO_TRANSLATOR;
uint32 n32ch;
memcpy(&n32ch, ch, sizeof(uint32));
uint16 n16ch;
memcpy(&n16ch, ch, sizeof(uint16));
// if B_TRANSLATOR_BITMAP type
if (n32ch == nbits)
return translate_from_bits(inSource, 4, ch, outType, outDestination);
// if BMP type in Little Endian byte order
else if (n16ch == nbm)
return translate_from_bmp(inSource, 4, ch, outType, outDestination);
else
return B_NO_TRANSLATOR;
}
status_t BMPTranslator::MakeConfigurationView(BMessage *ioExtension,
BView **outView, BRect *outExtent)
{
if (!outView || !outExtent)
return B_BAD_VALUE;
BMPView *view = new BMPView(BRect(0,0,225,175),
"BMPTranslator Settings", B_FOLLOW_ALL, B_WILL_DRAW);
*outView = view;
*outExtent = view->Bounds();
return B_OK;
}