/*****************************************************************************/ // TGATranslator // Written by Michael Wilber, Haiku Translation Kit Team // // TGATranslator.cpp // // This BTranslator based object is for opening and writing TGA files. // // // Copyright (c) 2002-2009, Haiku, Inc. All rights reserved. // // 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 #include #include #include "TGATranslator.h" #include "TGAView.h" #include "StreamBuffer.h" #undef B_TRANSLATION_CONTEXT #define B_TRANSLATION_CONTEXT "TGATranslator" // The input formats that this translator supports. static const translation_format sInputFormats[] = { { B_TRANSLATOR_BITMAP, B_TRANSLATOR_BITMAP, BBT_IN_QUALITY, BBT_IN_CAPABILITY, "image/x-be-bitmap", "Be Bitmap Format (TGATranslator)" }, { B_TGA_FORMAT, B_TRANSLATOR_BITMAP, TGA_IN_QUALITY, TGA_IN_CAPABILITY, "image/x-targa", "Targa image" } }; // The output formats that this translator supports. static const translation_format sOutputFormats[] = { { B_TRANSLATOR_BITMAP, B_TRANSLATOR_BITMAP, BBT_OUT_QUALITY, BBT_OUT_CAPABILITY, "image/x-be-bitmap", "Be Bitmap Format (TGATranslator)" }, { B_TGA_FORMAT, B_TRANSLATOR_BITMAP, TGA_OUT_QUALITY, TGA_OUT_CAPABILITY, "image/x-targa", "Targa image" } }; // Default settings for the Translator static const TranSetting sDefaultSettings[] = { {B_TRANSLATOR_EXT_HEADER_ONLY, TRAN_SETTING_BOOL, false}, {B_TRANSLATOR_EXT_DATA_ONLY, TRAN_SETTING_BOOL, false}, {TGA_SETTING_RLE, TRAN_SETTING_BOOL, false}, // RLE compression is off by default {TGA_SETTING_IGNORE_ALPHA, TRAN_SETTING_BOOL, false} // Don't ignore the alpha channel by default }; const uint32 kNumInputFormats = sizeof(sInputFormats) / sizeof(translation_format); const uint32 kNumOutputFormats = sizeof(sOutputFormats) / sizeof(translation_format); const uint32 kNumDefaultSettings = sizeof(sDefaultSettings) / sizeof(TranSetting); // --------------------------------------------------------------- // make_nth_translator // // Creates a TGATranslator object to be used by BTranslatorRoster // // Preconditions: // // Parameters: n, The translator to return. Since // TGATranslator only publishes one // translator, it only returns a // TGATranslator if n == 0 // // you, The image_id of the add-on that // contains code (not used). // // flags, Has no meaning yet, should be 0. // // Postconditions: // // Returns: NULL if n is not zero, // a new TGATranslator if n is zero // --------------------------------------------------------------- BTranslator * make_nth_translator(int32 n, image_id you, uint32 flags, ...) { BTranslator *ptranslator = NULL; if (!n) ptranslator = new(std::nothrow) TGATranslator(); return ptranslator; } // --------------------------------------------------------------- // Constructor // // Sets up the version info and the name of the translator so that // these values can be returned when they are requested. // // Preconditions: // // Parameters: // // Postconditions: // // Returns: // --------------------------------------------------------------- TGATranslator::TGATranslator() : BaseTranslator(B_TRANSLATE("TGA images"), B_TRANSLATE("TGA image translator"), TGA_TRANSLATOR_VERSION, sInputFormats, kNumInputFormats, sOutputFormats, kNumOutputFormats, "TGATranslator_Settings", sDefaultSettings, kNumDefaultSettings, B_TRANSLATOR_BITMAP, B_TGA_FORMAT) { } // --------------------------------------------------------------- // Destructor // // Does nothing // // Preconditions: // // Parameters: // // Postconditions: // // Returns: // --------------------------------------------------------------- // // NOTE: It may be the case, that under Be's libtranslation.so, // that this destructor will never be called TGATranslator::~TGATranslator() { } uint8 TGATranslator::tga_alphabits(TGAFileHeader &filehead, TGAColorMapSpec &mapspec, TGAImageSpec &imagespec) { if (fSettings->SetGetBool(TGA_SETTING_IGNORE_ALPHA)) return 0; else { uint8 nalpha; if (filehead.imagetype == TGA_NOCOMP_COLORMAP || filehead.imagetype == TGA_RLE_COLORMAP) { // color mapped images if (mapspec.entrysize == 32) nalpha = 8; else if (mapspec.entrysize == 16) nalpha = 1; else nalpha = 0; } else { // non-color mapped images if (imagespec.depth == 32) // Some programs that generate 32-bit TGA files // have an alpha channel, but have an incorrect // descriptor which says there are no alpha bits. // This logic is so that the alpha data can be // obtained from TGA files that lie. nalpha = 8; else nalpha = imagespec.descriptor & TGA_DESC_ALPHABITS; } return nalpha; } } // --------------------------------------------------------------- // identify_tga_header // // Determines if the data in inSource is in the TGA format. // If it is, it returns info about the data in inSource // to outInfo, pfileheader, pmapspec and pimagespec. // // Preconditions: // // Parameters: inSource, The source of the image data // // outInfo, Information about the translator // is copied here // // pfileheader, File header info for the TGA is // copied here after it is read from // the file. // // pmapspec, color map info for the TGA is copied // here after it is read from the file // // pimagespec, Info about the image width/height etc. // is copied here after it is read from // the file // // // Postconditions: // // Returns: B_NO_TRANSLATOR, if the data does not look like // TGA format data // // B_ERROR, if the header data could not be converted to host // format // // B_OK, if the data looks like bits data and no errors were // encountered // --------------------------------------------------------------- status_t identify_tga_header(BPositionIO *inSource, translator_info *outInfo, TGAFileHeader *pfileheader = NULL, TGAColorMapSpec *pmapspec = NULL, TGAImageSpec *pimagespec = NULL) { uint8 buf[TGA_HEADERS_SIZE]; // read in the rest of the TGA headers ssize_t size = TGA_HEADERS_SIZE; if (size > 0 && inSource->Read(buf, size) != size) return B_NO_TRANSLATOR; // Read in TGA file header TGAFileHeader fileheader; fileheader.idlength = buf[0]; fileheader.colormaptype = buf[1]; if (fileheader.colormaptype > 1) return B_NO_TRANSLATOR; fileheader.imagetype = buf[2]; if ((fileheader.imagetype > 3 && fileheader.imagetype < 9) || fileheader.imagetype > 11) return B_NO_TRANSLATOR; if ((fileheader.colormaptype == TGA_NO_COLORMAP && fileheader.imagetype == TGA_NOCOMP_COLORMAP) || (fileheader.colormaptype == TGA_COLORMAP && fileheader.imagetype != TGA_NOCOMP_COLORMAP && fileheader.imagetype != TGA_RLE_COLORMAP)) return B_NO_TRANSLATOR; // Read in TGA color map spec TGAColorMapSpec mapspec; memcpy(&mapspec.firstentry, buf + 3, 2); mapspec.firstentry = B_LENDIAN_TO_HOST_INT16(mapspec.firstentry); if (fileheader.colormaptype == 0 && mapspec.firstentry != 0) return B_NO_TRANSLATOR; memcpy(&mapspec.length, buf + 5, 2); mapspec.length = B_LENDIAN_TO_HOST_INT16(mapspec.length); if (fileheader.colormaptype == TGA_NO_COLORMAP && mapspec.length != 0) return B_NO_TRANSLATOR; if (fileheader.colormaptype == TGA_COLORMAP && mapspec.length == 0) return B_NO_TRANSLATOR; mapspec.entrysize = buf[7]; if (fileheader.colormaptype == TGA_NO_COLORMAP && mapspec.entrysize != 0) return B_NO_TRANSLATOR; if (fileheader.colormaptype == TGA_COLORMAP && mapspec.entrysize != 15 && mapspec.entrysize != 16 && mapspec.entrysize != 24 && mapspec.entrysize != 32) return B_NO_TRANSLATOR; // Read in TGA image spec TGAImageSpec imagespec; memcpy(&imagespec.xorigin, buf + 8, 2); imagespec.xorigin = B_LENDIAN_TO_HOST_INT16(imagespec.xorigin); memcpy(&imagespec.yorigin, buf + 10, 2); imagespec.yorigin = B_LENDIAN_TO_HOST_INT16(imagespec.yorigin); memcpy(&imagespec.width, buf + 12, 2); imagespec.width = B_LENDIAN_TO_HOST_INT16(imagespec.width); if (imagespec.width == 0) return B_NO_TRANSLATOR; memcpy(&imagespec.height, buf + 14, 2); imagespec.height = B_LENDIAN_TO_HOST_INT16(imagespec.height); if (imagespec.height == 0) return B_NO_TRANSLATOR; imagespec.depth = buf[16]; if (imagespec.depth < 1 || imagespec.depth > 32) return B_NO_TRANSLATOR; if ((fileheader.imagetype == TGA_NOCOMP_TRUECOLOR || fileheader.imagetype == TGA_RLE_TRUECOLOR) && imagespec.depth != 15 && imagespec.depth != 16 && imagespec.depth != 24 && imagespec.depth != 32) return B_NO_TRANSLATOR; if ((fileheader.imagetype == TGA_NOCOMP_BW || fileheader.imagetype == TGA_RLE_BW) && imagespec.depth != 8) return B_NO_TRANSLATOR; if (fileheader.colormaptype == TGA_COLORMAP && imagespec.depth != 8) return B_NO_TRANSLATOR; imagespec.descriptor = buf[17]; // images ordered from Right to Left (rather than Left to Right) // are not supported if ((imagespec.descriptor & TGA_ORIGIN_HORZ_BIT) != TGA_ORIGIN_LEFT) return B_NO_TRANSLATOR; // unused descriptor bits, these bits must be zero if (imagespec.descriptor & TGA_DESC_BITS76) return B_NO_TRANSLATOR; if ((fileheader.imagetype == TGA_NOCOMP_TRUECOLOR || fileheader.imagetype == TGA_RLE_TRUECOLOR) && imagespec.depth == 32 && (imagespec.descriptor & TGA_DESC_ALPHABITS) != 8 && (imagespec.descriptor & TGA_DESC_ALPHABITS) != 0) return B_NO_TRANSLATOR; if ((fileheader.imagetype == TGA_NOCOMP_TRUECOLOR || fileheader.imagetype == TGA_RLE_TRUECOLOR) && imagespec.depth == 24 && (imagespec.descriptor & TGA_DESC_ALPHABITS) != 0) return B_NO_TRANSLATOR; if ((fileheader.imagetype == TGA_NOCOMP_TRUECOLOR || fileheader.imagetype == TGA_RLE_TRUECOLOR) && imagespec.depth == 16 && (imagespec.descriptor & TGA_DESC_ALPHABITS) != 1 && (imagespec.descriptor & TGA_DESC_ALPHABITS) != 0) if ((fileheader.imagetype == TGA_NOCOMP_TRUECOLOR || fileheader.imagetype == TGA_RLE_TRUECOLOR) && imagespec.depth == 15 && (imagespec.descriptor & TGA_DESC_ALPHABITS) != 0) return B_NO_TRANSLATOR; // Fill in headers passed to this function if (pfileheader) { pfileheader->idlength = fileheader.idlength; pfileheader->colormaptype = fileheader.colormaptype; pfileheader->imagetype = fileheader.imagetype; } if (pmapspec) { pmapspec->firstentry = mapspec.firstentry; pmapspec->length = mapspec.length; pmapspec->entrysize = mapspec.entrysize; } if (pimagespec) { pimagespec->xorigin = imagespec.xorigin; pimagespec->yorigin = imagespec.yorigin; pimagespec->width = imagespec.width; pimagespec->height = imagespec.height; pimagespec->depth = imagespec.depth; pimagespec->descriptor = imagespec.descriptor; } if (outInfo) { outInfo->type = B_TGA_FORMAT; outInfo->group = B_TRANSLATOR_BITMAP; outInfo->quality = TGA_IN_QUALITY; outInfo->capability = TGA_IN_CAPABILITY; switch (fileheader.imagetype) { case TGA_NOCOMP_COLORMAP: snprintf(outInfo->name, sizeof(outInfo->name), B_TRANSLATE("Targa image (%d bits colormap)"), imagespec.depth); break; case TGA_NOCOMP_TRUECOLOR: snprintf(outInfo->name, sizeof(outInfo->name), B_TRANSLATE("Targa image (%d bits truecolor)"), imagespec.depth); break; case TGA_RLE_COLORMAP: snprintf(outInfo->name, sizeof(outInfo->name), B_TRANSLATE("Targa image (%d bits RLE colormap)"), imagespec.depth); break; case TGA_RLE_TRUECOLOR: snprintf(outInfo->name, sizeof(outInfo->name), B_TRANSLATE("Targa image (%d bits RLE truecolor)"), imagespec.depth); break; case TGA_RLE_BW: snprintf(outInfo->name, sizeof(outInfo->name), B_TRANSLATE("Targa image (%d bits RLE gray)"), imagespec.depth); break; case TGA_NOCOMP_BW: default: snprintf(outInfo->name, sizeof(outInfo->name), B_TRANSLATE("Targa image (%d bits gray)"), imagespec.depth); break; } strcpy(outInfo->MIME, "image/x-targa"); } return B_OK; } status_t TGATranslator::DerivedIdentify(BPositionIO *inSource, const translation_format *inFormat, BMessage *ioExtension, translator_info *outInfo, uint32 outType) { return identify_tga_header(inSource, outInfo); } // Convert width pixels from pbits to TGA format, storing the // result in ptga status_t pix_bits_to_tga(uint8 *pbits, uint8 *ptga, color_space fromspace, uint16 width, const color_map *pmap, int32 bitsBytesPerPixel) { status_t bytescopied = 0; switch (fromspace) { case B_RGBA32: bytescopied = width * 4; memcpy(ptga, pbits, bytescopied); break; case B_RGBA32_BIG: bytescopied = width * 4; while (width--) { ptga[0] = pbits[3]; ptga[1] = pbits[2]; ptga[2] = pbits[1]; ptga[3] = pbits[0]; ptga += 4; pbits += 4; } break; case B_CMYA32: bytescopied = width * 4; while (width--) { ptga[0] = 255 - pbits[2]; ptga[1] = 255 - pbits[1]; ptga[2] = 255 - pbits[0]; ptga[3] = pbits[3]; ptga += 4; pbits += 4; } break; case B_RGB32: case B_RGB24: bytescopied = width * 3; while (width--) { memcpy(ptga, pbits, 3); ptga += 3; pbits += bitsBytesPerPixel; } break; case B_CMYK32: { int32 comp; bytescopied = width * 3; while (width--) { comp = 255 - pbits[2] - pbits[3]; ptga[0] = (comp < 0) ? 0 : comp; comp = 255 - pbits[1] - pbits[3]; ptga[1] = (comp < 0) ? 0 : comp; comp = 255 - pbits[0] - pbits[3]; ptga[2] = (comp < 0) ? 0 : comp; ptga += 3; pbits += 4; } break; } case B_CMY32: case B_CMY24: bytescopied = width * 3; while (width--) { ptga[0] = 255 - pbits[2]; ptga[1] = 255 - pbits[1]; ptga[2] = 255 - pbits[0]; ptga += 3; pbits += bitsBytesPerPixel; } break; case B_RGB16: case B_RGB16_BIG: { // Expand to 24 bit because the TGA format handles // 16 bit images differently than the Be Image Format // which would cause a loss in quality uint16 val; bytescopied = width * 3; while (width--) { if (fromspace == B_RGB16) val = pbits[0] + (pbits[1] << 8); else val = pbits[1] + (pbits[0] << 8); ptga[0] = ((val & 0x1f) << 3) | ((val & 0x1f) >> 2); ptga[1] = ((val & 0x7e0) >> 3) | ((val & 0x7e0) >> 9); ptga[2] = ((val & 0xf800) >> 8) | ((val & 0xf800) >> 13); ptga += 3; pbits += 2; } break; } case B_RGBA15: bytescopied = width * 2; memcpy(ptga, pbits, bytescopied); break; case B_RGBA15_BIG: bytescopied = width * 2; while (width--) { ptga[0] = pbits[1]; ptga[1] = pbits[0]; ptga += 2; pbits += 2; } break; case B_RGB15: bytescopied = width * 2; while (width--) { ptga[0] = pbits[0]; ptga[1] = pbits[1] | 0x80; // alpha bit is always 1 ptga += 2; pbits += 2; } break; case B_RGB15_BIG: bytescopied = width * 2; while (width--) { ptga[0] = pbits[1]; ptga[1] = pbits[0] | 0x80; // alpha bit is always 1 ptga += 2; pbits += 2; } break; case B_RGB32_BIG: bytescopied = width * 3; while (width--) { ptga[0] = pbits[3]; ptga[1] = pbits[2]; ptga[2] = pbits[1]; ptga += 3; pbits += 4; } break; case B_RGB24_BIG: bytescopied = width * 3; while (width--) { ptga[0] = pbits[2]; ptga[1] = pbits[1]; ptga[2] = pbits[0]; ptga += 3; pbits += 3; } break; case B_CMAP8: { rgb_color c; bytescopied = width * 3; while (width--) { c = pmap->color_list[pbits[0]]; ptga[0] = c.blue; ptga[1] = c.green; ptga[2] = c.red; ptga += 3; pbits++; } break; } case B_GRAY8: // NOTE: this code assumes that the // destination TGA color space is either // 8 bit indexed color or 8 bit grayscale bytescopied = width; memcpy(ptga, pbits, bytescopied); break; default: bytescopied = B_ERROR; break; } // switch (fromspace) return bytescopied; } // create a TGA RLE packet for pixel and copy the // packet header and pixel data to ptga status_t copy_rle_packet(uint8 *ptga, uint32 pixel, uint8 count, color_space fromspace, const color_map *pmap, int32 bitsBytesPerPixel) { // copy packet header // (made of type and count) uint8 packethead = (count - 1) | 0x80; ptga[0] = packethead; ptga++; return pix_bits_to_tga(reinterpret_cast (&pixel), ptga, fromspace, 1, pmap, bitsBytesPerPixel) + 1; } // create a TGA raw packet for pixel and copy the // packet header and pixel data to ptga status_t copy_raw_packet(uint8 *ptga, uint8 *praw, uint8 count, color_space fromspace, const color_map *pmap, int32 bitsBytesPerPixel) { // copy packet header // (made of type and count) uint8 packethead = count - 1; ptga[0] = packethead; ptga++; return pix_bits_to_tga(praw, ptga, fromspace, count, pmap, bitsBytesPerPixel) + 1; } // convert a row of pixel data from pbits to a // row of pixel data in the TGA format using // Run Length Encoding status_t pix_bits_to_tgarle(uint8 *pbits, uint8 *ptga, color_space fromspace, uint16 width, const color_map *pmap, int32 bitsBytesPerPixel) { if (width == 0) return B_ERROR; uint32 current = 0, next = 0, aftnext = 0; uint16 nread = 0; status_t result, bytescopied = 0; uint8 *prawbuf, *praw; prawbuf = new(std::nothrow) uint8[bitsBytesPerPixel * 128]; praw = prawbuf; if (!prawbuf) return B_ERROR; uint8 rlecount = 1, rawcount = 0; bool bJustWroteRLE = false; memcpy(¤t, pbits, bitsBytesPerPixel); pbits += bitsBytesPerPixel; if (width == 1) { result = copy_raw_packet(ptga, reinterpret_cast (¤t), 1, fromspace, pmap, bitsBytesPerPixel); ptga += result; bytescopied += result; nread++; // don't enter the while loop } else { memcpy(&next, pbits, bitsBytesPerPixel); pbits += bitsBytesPerPixel; nread++; } while (nread < width) { if (nread < width - 1) { memcpy(&aftnext, pbits, bitsBytesPerPixel); pbits += bitsBytesPerPixel; } nread++; // RLE Packet Creation if (current == next && !bJustWroteRLE) { rlecount++; if (next != aftnext || nread == width || rlecount == 128) { result = copy_rle_packet(ptga, current, rlecount, fromspace, pmap, bitsBytesPerPixel); ptga += result; bytescopied += result; rlecount = 1; bJustWroteRLE = true; } // RAW Packet Creation } else { if (!bJustWroteRLE) { // output the current pixel only if // it was not just written out in an RLE packet rawcount++; memcpy(praw, ¤t, bitsBytesPerPixel); praw += bitsBytesPerPixel; } if (nread == width) { // if in the last iteration of the loop, // "next" will be the last pixel in the row, // and will need to be written out for this // special case if (rawcount == 128) { result = copy_raw_packet(ptga, prawbuf, rawcount, fromspace, pmap, bitsBytesPerPixel); ptga += result; bytescopied += result; praw = prawbuf; rawcount = 0; } rawcount++; memcpy(praw, &next, bitsBytesPerPixel); praw += bitsBytesPerPixel; } if ((!bJustWroteRLE && next == aftnext) || nread == width || rawcount == 128) { result = copy_raw_packet(ptga, prawbuf, rawcount, fromspace, pmap, bitsBytesPerPixel); ptga += result; bytescopied += result; praw = prawbuf; rawcount = 0; } bJustWroteRLE = false; } current = next; next = aftnext; } delete[] prawbuf; prawbuf = NULL; return bytescopied; } // --------------------------------------------------------------- // translate_from_bits_to_tgatc // // Converts various varieties of the Be Bitmap format ('bits') to // the TGA True Color format (RLE or uncompressed) // // Preconditions: // // Parameters: inSource, contains the bits data to convert // // outDestination, where the TGA data will be written // // fromspace, the format of the data in inSource // // imagespec, info about width / height / etc. of // the image // // brle, output using RLE if true, uncompressed // if false // // // Postconditions: // // Returns: B_ERROR, if memory couldn't be allocated or another // error occured // // B_OK, if no errors occurred // --------------------------------------------------------------- status_t translate_from_bits_to_tgatc(BPositionIO *inSource, BPositionIO *outDestination, color_space fromspace, TGAImageSpec &imagespec, bool brle) { 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 = imagespec.width * bitsBytesPerPixel; uint8 tgaBytesPerPixel = (imagespec.depth / 8) + ((imagespec.depth % 8) ? 1 : 0); int32 tgaRowBytes = (imagespec.width * tgaBytesPerPixel) + (imagespec.width / 2); uint32 tgapixrow = 0; uint8 *tgaRowData = new(std::nothrow) uint8[tgaRowBytes]; if (!tgaRowData) return B_ERROR; uint8 *bitsRowData = new(std::nothrow) uint8[bitsRowBytes]; if (!bitsRowData) { delete[] tgaRowData; tgaRowData = NULL; return B_ERROR; } // conversion function pointer, points to either // RLE or normal TGA conversion function status_t (*convert_to_tga)(uint8 *pbits, uint8 *ptga, color_space fromspace, uint16 width, const color_map *pmap, int32 bitsBytesPerPixel); if (brle) convert_to_tga = pix_bits_to_tgarle; else convert_to_tga = pix_bits_to_tga; ssize_t rd = inSource->Read(bitsRowData, bitsRowBytes); const color_map *pmap = NULL; if (fromspace == B_CMAP8) { pmap = system_colors(); if (!pmap) { delete[] tgaRowData; delete[] bitsRowData; return B_ERROR; } } while (rd == bitsRowBytes) { status_t bytescopied; bytescopied = convert_to_tga(bitsRowData, tgaRowData, fromspace, imagespec.width, pmap, bitsBytesPerPixel); outDestination->Write(tgaRowData, bytescopied); tgapixrow++; // 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 (tgapixrow == imagespec.height) break; rd = inSource->Read(bitsRowData, bitsRowBytes); } // while (rd == bitsRowBytes) delete[] bitsRowData; bitsRowData = NULL; delete[] tgaRowData; tgaRowData = NULL; return B_OK; } // --------------------------------------------------------------- // translate_from_bits1_to_tgabw // // Converts 1-bit Be Bitmaps ('bits') to the // black and white (8-bit grayscale) TGA format // // Preconditions: // // Parameters: inSource, contains the bits data to convert // // outDestination, where the TGA data will be written // // bitsRowBytes, number of bytes in one row of // bits data // // imagespec, info about width / height / etc. of // the image // // brle, output using RLE if true, uncompressed // if false // // // Postconditions: // // Returns: B_ERROR, if memory couldn't be allocated or another // error occured // // B_OK, if no errors occurred // --------------------------------------------------------------- status_t translate_from_bits1_to_tgabw(BPositionIO *inSource, BPositionIO *outDestination, int32 bitsRowBytes, TGAImageSpec &imagespec, bool brle) { uint8 tgaBytesPerPixel = 1; int32 tgaRowBytes = (imagespec.width * tgaBytesPerPixel) + (imagespec.width / 2); uint32 tgapixrow = 0; uint8 *tgaRowData = new(std::nothrow) uint8[tgaRowBytes]; if (!tgaRowData) return B_ERROR; uint8 *medRowData = new(std::nothrow) uint8[imagespec.width]; if (!medRowData) { delete[] tgaRowData; tgaRowData = NULL; return B_ERROR; } uint8 *bitsRowData = new(std::nothrow) uint8[bitsRowBytes]; if (!bitsRowData) { delete[] medRowData; medRowData = NULL; delete[] tgaRowData; tgaRowData = NULL; return B_ERROR; } // conversion function pointer, points to either // RLE or normal TGA conversion function status_t (*convert_to_tga)(uint8 *pbits, uint8 *ptga, color_space fromspace, uint16 width, const color_map *pmap, int32 bitsBytesPerPixel); if (brle) convert_to_tga = pix_bits_to_tgarle; else convert_to_tga = pix_bits_to_tga; ssize_t rd = inSource->Read(bitsRowData, bitsRowBytes); while (rd == bitsRowBytes) { uint32 tgapixcol = 0; for (int32 i = 0; (tgapixcol < imagespec.width) && (i < bitsRowBytes); i++) { // process each byte in the row uint8 pixels = bitsRowData[i]; for (uint8 compbit = 128; (tgapixcol < imagespec.width) && compbit; compbit >>= 1) { // for each bit in the current byte, convert to a TGA palette // index and store that in the tgaRowData if (pixels & compbit) // black medRowData[tgapixcol] = 0; else // white medRowData[tgapixcol] = 255; tgapixcol++; } } status_t bytescopied; bytescopied = convert_to_tga(medRowData, tgaRowData, B_GRAY8, imagespec.width, NULL, 1); outDestination->Write(tgaRowData, bytescopied); tgapixrow++; // 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 (tgapixrow == imagespec.height) break; rd = inSource->Read(bitsRowData, bitsRowBytes); } // while (rd == bitsRowBytes) delete[] bitsRowData; bitsRowData = NULL; delete[] medRowData; medRowData = NULL; delete[] tgaRowData; tgaRowData = NULL; return B_OK; } // --------------------------------------------------------------- // write_tga_headers // // Writes the TGA headers to outDestination. // // Preconditions: // // Parameters: outDestination, where the headers are written to // // fileheader, TGA file header // // mapspec, color map information // // imagespec, width / height / etc. info // // // Postconditions: // // Returns: B_ERROR, if something went wrong // // B_OK, if there were no problems writing out the headers // --------------------------------------------------------------- status_t write_tga_headers(BPositionIO *outDestination, TGAFileHeader &fileheader, TGAColorMapSpec &mapspec, TGAImageSpec &imagespec) { uint8 tgaheaders[TGA_HEADERS_SIZE]; // Convert host format headers to Little Endian (Intel) byte order TGAFileHeader outFileheader; outFileheader.idlength = fileheader.idlength; outFileheader.colormaptype = fileheader.colormaptype; outFileheader.imagetype = fileheader.imagetype; TGAColorMapSpec outMapspec; outMapspec.firstentry = B_HOST_TO_LENDIAN_INT16(mapspec.firstentry); outMapspec.length = B_HOST_TO_LENDIAN_INT16(mapspec.length); outMapspec.entrysize = mapspec.entrysize; TGAImageSpec outImagespec; outImagespec.xorigin = B_HOST_TO_LENDIAN_INT16(imagespec.xorigin); outImagespec.yorigin = B_HOST_TO_LENDIAN_INT16(imagespec.yorigin); outImagespec.width = B_HOST_TO_LENDIAN_INT16(imagespec.width); outImagespec.height = B_HOST_TO_LENDIAN_INT16(imagespec.height); outImagespec.depth = imagespec.depth; outImagespec.descriptor = imagespec.descriptor; // Copy TGA headers to buffer to be written out // all at once tgaheaders[0] = outFileheader.idlength; tgaheaders[1] = outFileheader.colormaptype; tgaheaders[2] = outFileheader.imagetype; memcpy(tgaheaders + 3, &outMapspec.firstentry, 2); memcpy(tgaheaders + 5, &outMapspec.length, 2); tgaheaders[7] = outMapspec.entrysize; memcpy(tgaheaders + 8, &outImagespec.xorigin, 2); memcpy(tgaheaders + 10, &outImagespec.yorigin, 2); memcpy(tgaheaders + 12, &outImagespec.width, 2); memcpy(tgaheaders + 14, &outImagespec.height, 2); tgaheaders[16] = outImagespec.depth; tgaheaders[17] = outImagespec.descriptor; ssize_t written; written = outDestination->Write(tgaheaders, TGA_HEADERS_SIZE); if (written == TGA_HEADERS_SIZE) return B_OK; else return B_ERROR; } // --------------------------------------------------------------- // write_tga_footer // // Writes the TGA footer. This information is contant in this // code because this translator does not output the developer // information section of the TGA format. // // Preconditions: // // Parameters: outDestination, where the headers are written to // // // Postconditions: // // Returns: B_ERROR, if something went wrong // // B_OK, if there were no problems writing out the headers // --------------------------------------------------------------- status_t write_tga_footer(BPositionIO *outDestination) { const int32 kfootersize = 26; uint8 footer[kfootersize]; memset(footer, 0, 8); // set the Extension Area Offset and Developer // Area Offset to zero (as they are not present) memcpy(footer + 8, "TRUEVISION-XFILE.", 18); // copy the string including the '.' and the '\0' ssize_t written; written = outDestination->Write(footer, kfootersize); if (written == kfootersize) return B_OK; else return B_ERROR; } // --------------------------------------------------------------- // translate_from_bits // // Convert the data in inSource from the Be Bitmap format ('bits') // to the format specified in outType (either bits or TGA). // // Preconditions: // // Parameters: inSource, the bits data to translate // // amtread, the amount of data already read from // inSource // // read, pointer to the data already read from // inSource // // // outType, the type of data to convert to // // outDestination, where the output is written to // // Postconditions: // // Returns: B_NO_TRANSLATOR, if the data is not in a supported // format // // B_ERROR, if there was an error allocating memory or some other // error // // B_OK, if successfully translated the data from the bits format // --------------------------------------------------------------- status_t TGATranslator::translate_from_bits(BPositionIO *inSource, uint32 outType, BPositionIO *outDestination) { TranslatorBitmap bitsHeader; bool bheaderonly = false, bdataonly = false, brle; brle = fSettings->SetGetBool(TGA_SETTING_RLE); status_t result; result = identify_bits_header(inSource, NULL, &bitsHeader); if (result != B_OK) return result; // Translate B_TRANSLATOR_BITMAP to B_TGA_FORMAT if (outType == B_TGA_FORMAT) { // Set up TGA header TGAFileHeader fileheader; fileheader.idlength = 0; fileheader.colormaptype = TGA_NO_COLORMAP; fileheader.imagetype = 0; TGAColorMapSpec mapspec; mapspec.firstentry = 0; mapspec.length = 0; mapspec.entrysize = 0; TGAImageSpec imagespec; imagespec.xorigin = 0; imagespec.yorigin = 0; imagespec.width = static_cast (bitsHeader.bounds.Width() + 1); imagespec.height = static_cast (bitsHeader.bounds.Height() + 1); imagespec.depth = 0; imagespec.descriptor = TGA_ORIGIN_VERT_BIT; // determine fileSize / imagesize switch (bitsHeader.colors) { // Output to 32-bit True Color TGA (8 bits alpha) case B_RGBA32: case B_RGBA32_BIG: case B_CMYA32: if (brle) fileheader.imagetype = TGA_RLE_TRUECOLOR; else fileheader.imagetype = TGA_NOCOMP_TRUECOLOR; imagespec.depth = 32; imagespec.descriptor |= 8; // 8 bits of alpha break; // Output to 24-bit True Color TGA (no alpha) case B_RGB32: case B_RGB32_BIG: case B_RGB24: case B_RGB24_BIG: case B_CMYK32: case B_CMY32: case B_CMY24: if (brle) fileheader.imagetype = TGA_RLE_TRUECOLOR; else fileheader.imagetype = TGA_NOCOMP_TRUECOLOR; imagespec.depth = 24; break; // Output to 16-bit True Color TGA (no alpha) // (TGA doesn't see 16 bit images as Be does // so converting 16 bit Be Image to 16-bit TGA // image would result in loss of quality) case B_RGB16: case B_RGB16_BIG: if (brle) fileheader.imagetype = TGA_RLE_TRUECOLOR; else fileheader.imagetype = TGA_NOCOMP_TRUECOLOR; imagespec.depth = 24; break; // Output to 15-bit True Color TGA (1 bit alpha) case B_RGB15: case B_RGB15_BIG: if (brle) fileheader.imagetype = TGA_RLE_TRUECOLOR; else fileheader.imagetype = TGA_NOCOMP_TRUECOLOR; imagespec.depth = 16; imagespec.descriptor |= 1; // 1 bit of alpha (always opaque) break; // Output to 16-bit True Color TGA (1 bit alpha) case B_RGBA15: case B_RGBA15_BIG: if (brle) fileheader.imagetype = TGA_RLE_TRUECOLOR; else fileheader.imagetype = TGA_NOCOMP_TRUECOLOR; imagespec.depth = 16; imagespec.descriptor |= 1; // 1 bit of alpha break; // Output to 8-bit Color Mapped TGA 32 bits per color map entry case B_CMAP8: fileheader.colormaptype = TGA_COLORMAP; if (brle) fileheader.imagetype = TGA_RLE_COLORMAP; else fileheader.imagetype = TGA_NOCOMP_COLORMAP; mapspec.firstentry = 0; mapspec.length = 256; mapspec.entrysize = 32; imagespec.depth = 8; imagespec.descriptor |= 8; // the pixel values contain 8 bits of attribute data break; // Output to 8-bit Black and White TGA case B_GRAY8: case B_GRAY1: if (brle) fileheader.imagetype = TGA_RLE_BW; else fileheader.imagetype = TGA_NOCOMP_BW; imagespec.depth = 8; break; default: return B_NO_TRANSLATOR; } // write out the TGA headers if (bheaderonly || (!bheaderonly && !bdataonly)) { result = write_tga_headers(outDestination, fileheader, mapspec, imagespec); if (result != B_OK) return result; } if (bheaderonly) // if user only wants the header, bail out // before the data is written return result; // write out the TGA 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: result = translate_from_bits_to_tgatc(inSource, outDestination, bitsHeader.colors, imagespec, brle); break; case B_CMAP8: { // write Be's system palette to the TGA file uint8 pal[1024]; const color_map *pmap = system_colors(); if (!pmap) return B_ERROR; 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; palent[3] = c.alpha; } if (outDestination->Write(pal, 1024) != 1024) return B_ERROR; result = translate_from_bits_to_tgatc(inSource, outDestination, B_GRAY8, imagespec, brle); break; } case B_GRAY8: result = translate_from_bits_to_tgatc(inSource, outDestination, B_GRAY8, imagespec, brle); break; case B_GRAY1: result = translate_from_bits1_to_tgabw(inSource, outDestination, bitsHeader.rowBytes, imagespec, brle); break; default: result = B_NO_TRANSLATOR; break; } if (result == B_OK) result = write_tga_footer(outDestination); return result; } else return B_NO_TRANSLATOR; } // convert a row of uncompressed, non-color mapped // TGA pixels from ptga to pbits status_t pix_tganm_to_bits(uint8 *pbits, uint8 *ptga, uint16 width, uint8 depth, uint8 tgaBytesPerPixel, uint8 nalpha) { status_t result = B_OK; switch (depth) { case 32: if (nalpha == 8 && tgaBytesPerPixel == 4) memcpy(pbits, ptga, 4 * width); else if (nalpha == 8) { // copy the same 32-bit pixel over and over while (width--) { memcpy(pbits, ptga, 4); pbits += 4; } } else { while (width--) { memcpy(pbits, ptga, 3); pbits += 4; ptga += tgaBytesPerPixel; } } break; case 24: while (width--) { memcpy(pbits, ptga, 3); pbits += 4; ptga += tgaBytesPerPixel; } break; case 16: { uint16 val; if (nalpha == 1) { while (width--) { val = ptga[0] + (ptga[1] << 8); pbits[0] = ((val & 0x1f) << 3) | ((val & 0x1f) >> 2); pbits[1] = ((val & 0x3e0) >> 2) | ((val & 0x3e0) >> 7); pbits[2] = ((val & 0x7c00) >> 7) | ((val & 0x7c00) >> 12); pbits[3] = (val & 0x8000) ? 255 : 0; pbits += 4; ptga += tgaBytesPerPixel; } } else { while (width--) { val = ptga[0] + (ptga[1] << 8); pbits[0] = ((val & 0x1f) << 3) | ((val & 0x1f) >> 2); pbits[1] = ((val & 0x3e0) >> 2) | ((val & 0x3e0) >> 7); pbits[2] = ((val & 0x7c00) >> 7) | ((val & 0x7c00) >> 12); pbits += 4; ptga += tgaBytesPerPixel; } } break; } case 15: { uint16 val; while (width--) { val = ptga[0] + (ptga[1] << 8); pbits[0] = ((val & 0x1f) << 3) | ((val & 0x1f) >> 2); pbits[1] = ((val & 0x3e0) >> 2) | ((val & 0x3e0) >> 7); pbits[2] = ((val & 0x7c00) >> 7) | ((val & 0x7c00) >> 12); pbits += 4; ptga += tgaBytesPerPixel; } break; } case 8: while (width--) { memset(pbits, ptga[0], 3); pbits += 4; ptga += tgaBytesPerPixel; } break; default: result = B_ERROR; break; } return result; } // --------------------------------------------------------------- // translate_from_tganm_to_bits // // Translates a uncompressed, non-palette TGA from inSource // to the B_RGB32 or B_RGBA32 bits format. // // Preconditions: // // Parameters: inSource, the TGA data to be translated // // outDestination, where the bits data will be written to // // filehead, image type info // // mapspec, color map info // // imagespec, width / height info // // // // Postconditions: // // Returns: B_ERROR, if there is an error allocating memory // // B_OK, if all went well // --------------------------------------------------------------- status_t TGATranslator::translate_from_tganm_to_bits(BPositionIO *inSource, BPositionIO *outDestination, TGAFileHeader &filehead, TGAColorMapSpec &mapspec, TGAImageSpec &imagespec) { bool bvflip; if (imagespec.descriptor & TGA_ORIGIN_VERT_BIT) bvflip = false; else bvflip = true; uint8 nalpha = tga_alphabits(filehead, mapspec, imagespec); int32 bitsRowBytes = imagespec.width * 4; uint8 tgaBytesPerPixel = (imagespec.depth / 8) + ((imagespec.depth % 8) ? 1 : 0); int32 tgaRowBytes = (imagespec.width * tgaBytesPerPixel); uint32 tgapixrow = 0; // Setup outDestination so that it can be written to // from the end of the file to the beginning instead of // the other way around off_t bitsFileSize = (bitsRowBytes * imagespec.height) + sizeof(TranslatorBitmap); if (outDestination->SetSize(bitsFileSize) != B_OK) // This call should work for BFile and BMallocIO objects, // but may not work for other BPositionIO based types return B_ERROR; off_t bitsoffset = (imagespec.height - 1) * bitsRowBytes; if (bvflip) outDestination->Seek(bitsoffset, SEEK_CUR); // allocate row buffers uint8 *tgaRowData = new(std::nothrow) uint8[tgaRowBytes]; if (!tgaRowData) return B_ERROR; uint8 *bitsRowData = new(std::nothrow) uint8[bitsRowBytes]; if (!bitsRowData) { delete[] tgaRowData; tgaRowData = NULL; return B_ERROR; } // perform the actual translation memset(bitsRowData, 0xff, bitsRowBytes); ssize_t rd = inSource->Read(tgaRowData, tgaRowBytes); while (rd == tgaRowBytes) { pix_tganm_to_bits(bitsRowData, tgaRowData, imagespec.width, imagespec.depth, tgaBytesPerPixel, nalpha); outDestination->Write(bitsRowData, bitsRowBytes); tgapixrow++; // 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 (tgapixrow == imagespec.height) break; if (bvflip) outDestination->Seek(-(bitsRowBytes * 2), SEEK_CUR); rd = inSource->Read(tgaRowData, tgaRowBytes); } delete[] tgaRowData; tgaRowData = NULL; delete[] bitsRowData; bitsRowData = NULL; return B_OK; } // --------------------------------------------------------------- // translate_from_tganmrle_to_bits // // Convert non color map, RLE TGA to Be bitmap format // and write results to outDestination // // Preconditions: // // Parameters: inSource, the TGA data to be translated // // outDestination, where the bits data will be written to // // filehead, image type info // // mapspec, color map info // // imagespec, width / height info // // // // Postconditions: // // Returns: B_ERROR, if there is an error allocating memory // // B_OK, if all went well // --------------------------------------------------------------- status_t TGATranslator::translate_from_tganmrle_to_bits(BPositionIO *inSource, BPositionIO *outDestination, TGAFileHeader &filehead, TGAColorMapSpec &mapspec, TGAImageSpec &imagespec) { status_t result = B_OK; bool bvflip; if (imagespec.descriptor & TGA_ORIGIN_VERT_BIT) bvflip = false; else bvflip = true; uint8 nalpha = tga_alphabits(filehead, mapspec, imagespec); int32 bitsRowBytes = imagespec.width * 4; uint8 tgaBytesPerPixel = (imagespec.depth / 8) + ((imagespec.depth % 8) ? 1 : 0); uint16 tgapixrow = 0, tgapixcol = 0; // Setup outDestination so that it can be written to // from the end of the file to the beginning instead of // the other way around off_t bitsFileSize = (bitsRowBytes * imagespec.height) + sizeof(TranslatorBitmap); if (outDestination->SetSize(bitsFileSize) != B_OK) // This call should work for BFile and BMallocIO objects, // but may not work for other BPositionIO based types return B_ERROR; off_t bitsoffset = (imagespec.height - 1) * bitsRowBytes; if (bvflip) outDestination->Seek(bitsoffset, SEEK_CUR); // allocate row buffers uint8 *bitsRowData = new(std::nothrow) uint8[bitsRowBytes]; if (!bitsRowData) return B_ERROR; // perform the actual translation memset(bitsRowData, 0xff, bitsRowBytes); uint8 *pbitspixel = bitsRowData; uint8 packethead; StreamBuffer sbuf(inSource, TGA_STREAM_BUFFER_SIZE); ssize_t rd = 0; if (sbuf.InitCheck() == B_OK) rd = sbuf.Read(&packethead, 1); while (rd == 1) { // Run Length Packet if (packethead & TGA_RLE_PACKET_TYPE_BIT) { uint8 tgapixel[4], rlecount; rlecount = (packethead & ~TGA_RLE_PACKET_TYPE_BIT) + 1; if (tgapixcol + rlecount > imagespec.width) { result = B_NO_TRANSLATOR; break; } rd = sbuf.Read(tgapixel, tgaBytesPerPixel); if (rd == tgaBytesPerPixel) { pix_tganm_to_bits(pbitspixel, tgapixel, rlecount, imagespec.depth, 0, nalpha); pbitspixel += 4 * rlecount; tgapixcol += rlecount; } else { result = B_NO_TRANSLATOR; break; // error } // Raw Packet } else { uint8 tgaPixelBuf[512], rawcount; uint16 rawbytes; rawcount = (packethead & ~TGA_RLE_PACKET_TYPE_BIT) + 1; if (tgapixcol + rawcount > imagespec.width) { result = B_NO_TRANSLATOR; break; } rawbytes = tgaBytesPerPixel * rawcount; rd = sbuf.Read(tgaPixelBuf, rawbytes); if (rd == rawbytes) { pix_tganm_to_bits(pbitspixel, tgaPixelBuf, rawcount, imagespec.depth, tgaBytesPerPixel, nalpha); pbitspixel += 4 * rawcount; tgapixcol += rawcount; } else { result = B_NO_TRANSLATOR; break; } } if (tgapixcol == imagespec.width) { outDestination->Write(bitsRowData, bitsRowBytes); tgapixcol = 0; tgapixrow++; if (tgapixrow == imagespec.height) break; if (bvflip) outDestination->Seek(-(bitsRowBytes * 2), SEEK_CUR); pbitspixel = bitsRowData; } rd = sbuf.Read(&packethead, 1); } delete[] bitsRowData; bitsRowData = NULL; return result; } // convert a row of color mapped pixels to pbits status_t pix_tgam_to_bits(uint8 *pbits, uint8 *ptgaindices, uint16 width, uint8 depth, uint8 *pmap) { status_t result = B_OK; uint8 *ptgapixel = NULL; switch (depth) { case 32: for (uint16 i = 0; i < width; i++) { ptgapixel = pmap + (ptgaindices[i] * 4); memcpy(pbits, ptgapixel, 4); pbits += 4; } break; case 24: for (uint16 i = 0; i < width; i++) { ptgapixel = pmap + (ptgaindices[i] * 3); memcpy(pbits, ptgapixel, 3); pbits += 4; } break; case 16: for (uint16 i = 0; i < width; i++) { uint16 val; ptgapixel = pmap + (ptgaindices[i] * 2); val = ptgapixel[0] + (ptgapixel[1] << 8); pbits[0] = ((val & 0x1f) << 3) | ((val & 0x1f) >> 2); pbits[1] = ((val & 0x3e0) >> 2) | ((val & 0x3e0) >> 7); pbits[2] = ((val & 0x7c00) >> 7) | ((val & 0x7c00) >> 12); pbits[3] = (val & 0x8000) ? 255 : 0; pbits += 4; } break; case 15: for (uint16 i = 0; i < width; i++) { uint16 val; ptgapixel = pmap + (ptgaindices[i] * 2); val = ptgapixel[0] + (ptgapixel[1] << 8); pbits[0] = ((val & 0x1f) << 3) | ((val & 0x1f) >> 2); pbits[1] = ((val & 0x3e0) >> 2) | ((val & 0x3e0) >> 7); pbits[2] = ((val & 0x7c00) >> 7) | ((val & 0x7c00) >> 12); pbits += 4; } break; default: result = B_ERROR; break; } return result; } // --------------------------------------------------------------- // translate_from_tgam_to_bits // // Translates a paletted TGA from inSource to the bits format. // // Preconditions: // // Parameters: inSource, the TGA data to be translated // // outDestination, where the bits data will be written to // // mapspec, info about the color map (palette) // // imagespec, width / height info // // pmap, color palette // // // Postconditions: // // Returns: B_ERROR, if there is an error allocating memory // // B_OK, if all went well // --------------------------------------------------------------- status_t translate_from_tgam_to_bits(BPositionIO *inSource, BPositionIO *outDestination, TGAColorMapSpec &mapspec, TGAImageSpec &imagespec, uint8 *pmap) { bool bvflip; if (imagespec.descriptor & TGA_ORIGIN_VERT_BIT) bvflip = false; else bvflip = true; int32 bitsRowBytes = imagespec.width * 4; uint8 tgaBytesPerPixel = (imagespec.depth / 8) + ((imagespec.depth % 8) ? 1 : 0); int32 tgaRowBytes = (imagespec.width * tgaBytesPerPixel); uint32 tgapixrow = 0; // Setup outDestination so that it can be written to // from the end of the file to the beginning instead of // the other way around off_t bitsFileSize = (bitsRowBytes * imagespec.height) + sizeof(TranslatorBitmap); if (outDestination->SetSize(bitsFileSize) != B_OK) // This call should work for BFile and BMallocIO objects, // but may not work for other BPositionIO based types return B_ERROR; off_t bitsoffset = (imagespec.height - 1) * bitsRowBytes; if (bvflip) outDestination->Seek(bitsoffset, SEEK_CUR); // allocate row buffers uint8 *tgaRowData = new(std::nothrow) uint8[tgaRowBytes]; if (!tgaRowData) return B_ERROR; uint8 *bitsRowData = new(std::nothrow) uint8[bitsRowBytes]; if (!bitsRowData) { delete[] tgaRowData; tgaRowData = NULL; return B_ERROR; } // perform the actual translation memset(bitsRowData, 0xff, bitsRowBytes); ssize_t rd = inSource->Read(tgaRowData, tgaRowBytes); while (rd == tgaRowBytes) { pix_tgam_to_bits(bitsRowData, tgaRowData, imagespec.width, mapspec.entrysize, pmap); outDestination->Write(bitsRowData, bitsRowBytes); tgapixrow++; // 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 (tgapixrow == imagespec.height) break; if (bvflip) outDestination->Seek(-(bitsRowBytes * 2), SEEK_CUR); rd = inSource->Read(tgaRowData, tgaRowBytes); } delete[] tgaRowData; tgaRowData = NULL; delete[] bitsRowData; bitsRowData = NULL; return B_OK; } // --------------------------------------------------------------- // translate_from_tgamrle_to_bits // // Translates a color mapped or non color mapped RLE TGA from // inSource to the bits format. // // Preconditions: // // Parameters: inSource, the TGA data to be translated // // outDestination, where the bits data will be written to // // filehead, image type info // // mapspec, info about the color map (palette) // // imagespec, width / height info // // pmap, color palette // // // Postconditions: // // Returns: B_ERROR, if there is an error allocating memory // // B_OK, if all went well // --------------------------------------------------------------- status_t TGATranslator::translate_from_tgamrle_to_bits(BPositionIO *inSource, BPositionIO *outDestination, TGAFileHeader &filehead, TGAColorMapSpec &mapspec, TGAImageSpec &imagespec, uint8 *pmap) { status_t result = B_OK; bool bvflip; if (imagespec.descriptor & TGA_ORIGIN_VERT_BIT) bvflip = false; else bvflip = true; uint8 nalpha = tga_alphabits(filehead, mapspec, imagespec); int32 bitsRowBytes = imagespec.width * 4; uint8 tgaPalBytesPerPixel = (mapspec.entrysize / 8) + ((mapspec.entrysize % 8) ? 1 : 0); uint8 tgaBytesPerPixel = (imagespec.depth / 8) + ((imagespec.depth % 8) ? 1 : 0); uint16 tgapixrow = 0, tgapixcol = 0; // Setup outDestination so that it can be written to // from the end of the file to the beginning instead of // the other way around off_t bitsFileSize = (bitsRowBytes * imagespec.height) + sizeof(TranslatorBitmap); if (outDestination->SetSize(bitsFileSize) != B_OK) // This call should work for BFile and BMallocIO objects, // but may not work for other BPositionIO based types return B_ERROR; off_t bitsoffset = (imagespec.height - 1) * bitsRowBytes; if (bvflip) outDestination->Seek(bitsoffset, SEEK_CUR); // allocate row buffers uint8 *bitsRowData = new(std::nothrow) uint8[bitsRowBytes]; if (!bitsRowData) return B_ERROR; // perform the actual translation memset(bitsRowData, 0xff, bitsRowBytes); uint8 *pbitspixel = bitsRowData; uint8 packethead; StreamBuffer sbuf(inSource, TGA_STREAM_BUFFER_SIZE); ssize_t rd = 0; if (sbuf.InitCheck() == B_OK) rd = sbuf.Read(&packethead, 1); while (rd == 1) { // Run Length Packet if (packethead & TGA_RLE_PACKET_TYPE_BIT) { uint8 tgaindex, rlecount; rlecount = (packethead & ~TGA_RLE_PACKET_TYPE_BIT) + 1; if (tgapixcol + rlecount > imagespec.width) { result = B_NO_TRANSLATOR; break; } rd = sbuf.Read(&tgaindex, 1); if (rd == tgaBytesPerPixel) { uint8 *ptgapixel; ptgapixel = pmap + (tgaindex * tgaPalBytesPerPixel); pix_tganm_to_bits(pbitspixel, ptgapixel, rlecount, mapspec.entrysize, 0, nalpha); pbitspixel += 4 * rlecount; tgapixcol += rlecount; } else { result = B_NO_TRANSLATOR; break; // error } // Raw Packet } else { uint8 tgaIndexBuf[128], rawcount; rawcount = (packethead & ~TGA_RLE_PACKET_TYPE_BIT) + 1; if (tgapixcol + rawcount > imagespec.width) { result = B_NO_TRANSLATOR; break; } rd = sbuf.Read(tgaIndexBuf, rawcount); if (rd == rawcount) { pix_tgam_to_bits(pbitspixel, tgaIndexBuf, rawcount, mapspec.entrysize, pmap); pbitspixel += 4 * rawcount; tgapixcol += rawcount; } else { result = B_NO_TRANSLATOR; break; } } if (tgapixcol == imagespec.width) { outDestination->Write(bitsRowData, bitsRowBytes); tgapixcol = 0; tgapixrow++; if (tgapixrow == imagespec.height) break; if (bvflip) outDestination->Seek(-(bitsRowBytes * 2), SEEK_CUR); pbitspixel = bitsRowData; } rd = sbuf.Read(&packethead, 1); } delete[] bitsRowData; bitsRowData = NULL; return result; } // --------------------------------------------------------------- // translate_from_tga // // Convert the data in inSource from the TGA format // to the format specified in outType (either bits or TGA). // // Preconditions: // // Parameters: inSource, the bits data to translate // // amtread, the amount of data already read from // inSource // // read, pointer to the data already read from // inSource // // outType, the type of data to convert to // // outDestination, where the output is written to // // Postconditions: // // Returns: B_NO_TRANSLATOR, if the data is not in a supported // format // // B_ERROR, if there was an error allocating memory or some other // error // // B_OK, if successfully translated the data from the bits format // --------------------------------------------------------------- status_t TGATranslator::translate_from_tga(BPositionIO *inSource, uint32 outType, BPositionIO *outDestination) { TGAFileHeader fileheader; TGAColorMapSpec mapspec; TGAImageSpec imagespec; bool bheaderonly = false, bdataonly = false; status_t result; result = identify_tga_header(inSource, NULL, &fileheader, &mapspec, &imagespec); if (result != B_OK) return result; // if the user wants to translate a TGA to a TGA, easy enough :) if (outType == B_TGA_FORMAT) { // write out the TGA headers if (bheaderonly || (!bheaderonly && !bdataonly)) { result = write_tga_headers(outDestination, fileheader, mapspec, imagespec); if (result != B_OK) return result; } if (bheaderonly) // if the user only wants the header, // bail before it is written return result; const int32 kbuflen = 1024; uint8 buf[kbuflen]; ssize_t rd = inSource->Read(buf, kbuflen); while (rd > 0) { outDestination->Write(buf, rd); rd = inSource->Read(buf, kbuflen); } if (rd == 0) return B_OK; else return B_ERROR; // if translating a TGA 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 = imagespec.width - 1; bitsHeader.bounds.bottom = imagespec.height - 1; // skip over Image ID data (if present) if (fileheader.idlength > 0) inSource->Seek(fileheader.idlength, SEEK_CUR); // read in palette and/or skip non-TGA data uint8 *ptgapalette = NULL; if (fileheader.colormaptype == TGA_COLORMAP) { uint32 nentrybytes; nentrybytes = mapspec.entrysize / 8; if (mapspec.entrysize % 8) nentrybytes++; ptgapalette = new(std::nothrow) uint8[nentrybytes * mapspec.length]; inSource->Read(ptgapalette, nentrybytes * mapspec.length); } bitsHeader.rowBytes = imagespec.width * 4; if (fileheader.imagetype != TGA_NOCOMP_BW && fileheader.imagetype != TGA_RLE_BW && tga_alphabits(fileheader, mapspec, imagespec)) bitsHeader.colors = B_RGBA32; else bitsHeader.colors = B_RGB32; int32 datasize = bitsHeader.rowBytes * imagespec.height; bitsHeader.dataSize = datasize; // write out Be's Bitmap header if (bheaderonly || (!bheaderonly && !bdataonly)) { if (swap_data(B_UINT32_TYPE, &bitsHeader, sizeof(TranslatorBitmap), B_SWAP_HOST_TO_BENDIAN) != B_OK) return B_ERROR; outDestination->Write(&bitsHeader, sizeof(TranslatorBitmap)); } if (bheaderonly) // if the user only wants the header, // bail before the data is written return B_OK; // write out the actual image data switch (fileheader.imagetype) { case TGA_NOCOMP_TRUECOLOR: case TGA_NOCOMP_BW: result = translate_from_tganm_to_bits(inSource, outDestination, fileheader, mapspec, imagespec); break; case TGA_NOCOMP_COLORMAP: result = translate_from_tgam_to_bits(inSource, outDestination, mapspec, imagespec, ptgapalette); break; case TGA_RLE_TRUECOLOR: case TGA_RLE_BW: result = translate_from_tganmrle_to_bits(inSource, outDestination, fileheader, mapspec, imagespec); break; case TGA_RLE_COLORMAP: result = translate_from_tgamrle_to_bits(inSource, outDestination, fileheader, mapspec, imagespec, ptgapalette); break; default: result = B_NO_TRANSLATOR; break; } delete[] ptgapalette; ptgapalette = NULL; return result; } else return B_NO_TRANSLATOR; } status_t TGATranslator::DerivedTranslate(BPositionIO *inSource, const translator_info *inInfo, BMessage *ioExtension, uint32 outType, BPositionIO *outDestination, int32 baseType) { if (baseType == 1) // if inSource is in bits format return translate_from_bits(inSource, outType, outDestination); else if (baseType == 0) // if inSource is NOT in bits format return translate_from_tga(inSource, outType, outDestination); else // if BaseTranslator did not properly identify the data as // bits or not bits return B_NO_TRANSLATOR; } BView * TGATranslator::NewConfigView(TranslatorSettings *settings) { return new(std::nothrow) TGAView(B_TRANSLATE("TGATranslator Settings"), B_WILL_DRAW, settings); }