/*****************************************************************************/ // SGITranslator // Written by Stephan Aßmus // based on TIFFTranslator written mostly by // Michael Wilber // // SGITranslator.cpp // // This BTranslator based object is for opening and writing // SGI images. // // // Copyright (c) 2003-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. /*****************************************************************************/ // // How this works: // // libtiff has a special version of SGIOpen() that gets passed custom // functions for reading writing etc. and a handle. This handle in our case // is a BPositionIO object, which libtiff passes on to the functions for reading // writing etc. So when operations are performed on the SGI* handle that is // returned by SGIOpen(), libtiff uses the special reading writing etc // functions so that all stream io happens on the BPositionIO object. #include #include #include #include #include "SGIImage.h" #include "SGITranslator.h" #include "SGIView.h" using std::nothrow; // The input formats that this translator supports. translation_format gInputFormats[] = { { B_TRANSLATOR_BITMAP, B_TRANSLATOR_BITMAP, BBT_IN_QUALITY, BBT_IN_CAPABILITY, "image/x-be-bitmap", "Be Bitmap Format (SGITranslator)" }, { SGI_FORMAT, B_TRANSLATOR_BITMAP, SGI_IN_QUALITY, SGI_IN_CAPABILITY, "image/sgi", "SGI image" } }; // The output formats that this translator supports. translation_format gOutputFormats[] = { { B_TRANSLATOR_BITMAP, B_TRANSLATOR_BITMAP, BBT_OUT_QUALITY, BBT_OUT_CAPABILITY, "image/x-be-bitmap", "Be Bitmap Format (SGITranslator)" }, { SGI_FORMAT, B_TRANSLATOR_BITMAP, SGI_OUT_QUALITY, SGI_OUT_CAPABILITY, "image/sgi", "SGI image" } }; // Default settings for the Translator TranSetting gDefaultSettings[] = { {B_TRANSLATOR_EXT_HEADER_ONLY, TRAN_SETTING_BOOL, false}, {B_TRANSLATOR_EXT_DATA_ONLY, TRAN_SETTING_BOOL, false}, {SGI_SETTING_COMPRESSION, TRAN_SETTING_INT32, SGI_COMP_RLE} // compression is set to RLE by default }; // --------------------------------------------------------------- // make_nth_translator // // Creates a SGITranslator object to be used by BTranslatorRoster // // Preconditions: // // Parameters: n, The translator to return. Since // SGITranslator only publishes one // translator, it only returns a // SGITranslator 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 SGITranslator if n is zero // --------------------------------------------------------------- BTranslator * make_nth_translator(int32 n, image_id you, uint32 flags, ...) { if (!n) return new SGITranslator(); else return NULL; } // --------------------------------------------------------------- // 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: // --------------------------------------------------------------- SGITranslator::SGITranslator() : BaseTranslator("SGI images", "SGI image translator", SGI_TRANSLATOR_VERSION, gInputFormats, sizeof(gInputFormats) / sizeof(translation_format), gOutputFormats, sizeof(gOutputFormats) / sizeof(translation_format), "SGITranslator_Settings", gDefaultSettings, sizeof(gDefaultSettings) / sizeof(TranSetting), B_TRANSLATOR_BITMAP, SGI_FORMAT) { } // --------------------------------------------------------------- // Destructor // // Does nothing // // Preconditions: // // Parameters: // // Postconditions: // // Returns: // --------------------------------------------------------------- SGITranslator::~SGITranslator() { } status_t identify_sgi_header(BPositionIO *inSource, translator_info *outInfo, uint32 outType, SGIImage **poutSGIImage = NULL) { status_t status = B_NO_MEMORY; // construct new SGIImage object and set it to the provided BPositionIO SGIImage* sgiImage = new(nothrow) SGIImage(); if (sgiImage) status = sgiImage->SetTo(inSource); if (status >= B_OK) { if (outInfo) { outInfo->type = SGI_FORMAT; outInfo->group = B_TRANSLATOR_BITMAP; outInfo->quality = SGI_IN_QUALITY; outInfo->capability = SGI_IN_CAPABILITY; strcpy(outInfo->MIME, "image/sgi"); strcpy(outInfo->name, "SGI image"); } } else { delete sgiImage; sgiImage = NULL; } if (!poutSGIImage) // close SGIImage if caller is not interested in SGIImage handle delete sgiImage; else // leave SGIImage open (if it is) and return handle if caller needs it *poutSGIImage = sgiImage; return status; } // --------------------------------------------------------------- // DerivedIdentify // // Examines the data from inSource and determines if it is in a // format that this translator knows how to work with. // // Preconditions: // // Parameters: inSource, where the data to examine is // // inFormat, a hint about the data in inSource, // it is ignored since it is only a hint // // ioExtension, configuration settings for the // translator (not used) // // outInfo, information about what data is in // inSource and how well this translator // can handle that data is stored here // // outType, The format that the user wants // the data in inSource to be // converted to // // Postconditions: // // Returns: B_NO_TRANSLATOR, if this translator can't handle // the data in inSource // // B_ERROR, if there was an error converting the data to the host // format // // B_BAD_VALUE, if the settings in ioExtension are bad // // B_OK, if this translator understood the data and there were // no errors found // // Other errors if BPositionIO::Read() returned an error value // --------------------------------------------------------------- status_t SGITranslator::DerivedIdentify(BPositionIO *inSource, const translation_format *inFormat, BMessage *ioExtension, translator_info *outInfo, uint32 outType) { return identify_sgi_header(inSource, outInfo, outType); } // translate_from_bits status_t SGITranslator::translate_from_bits(BPositionIO *inSource, uint32 outType, BPositionIO *outDestination) { TranslatorBitmap bitsHeader; uint32 compression = fSettings->SetGetInt32(SGI_SETTING_COMPRESSION); status_t ret = identify_bits_header(inSource, NULL, &bitsHeader); if (ret < B_OK) return ret; // Translate B_TRANSLATOR_BITMAP to SGI_FORMAT if (outType == SGI_FORMAT) { // common fields which are independent of the bitmap format uint32 width = bitsHeader.bounds.IntegerWidth() + 1; uint32 height = bitsHeader.bounds.IntegerHeight() + 1; uint32 bytesPerRow = bitsHeader.rowBytes; uint32 bytesPerChannel = 1; color_space format = bitsHeader.colors; uint32 channelCount; switch (format) { case B_GRAY8: channelCount = 1; break; case B_RGB32: case B_RGB32_BIG: case B_RGB24: case B_RGB24_BIG: channelCount = 3; break; case B_RGBA32: case B_RGBA32_BIG: channelCount = 4; break; default: return B_NO_TRANSLATOR; } // Set up SGI header SGIImage* sgiImage = new SGIImage(); status_t ret = sgiImage->SetTo(outDestination, width, height, channelCount, bytesPerChannel, compression); if (ret >= B_OK) { // read one row at a time, // convert to the correct format // and write out the results // SGI Images store each channel separately // a buffer is allocated big enough to hold all channels // then the pointers are assigned with offsets into that buffer uint8** rows = new(nothrow) uint8*[channelCount]; if (rows) rows[0] = new(nothrow) uint8[width * channelCount * bytesPerChannel]; // rowBuffer is going to hold the converted data uint8* rowBuffer = new(nothrow) uint8[bytesPerRow]; if (rows && rows[0] && rowBuffer) { // assign the other pointers (channel offsets in row buffer) for (uint32 i = 1; i < channelCount; i++) rows[i] = rows[0] + i * width; // loop through all lines of the image for (int32 y = height - 1; y >= 0 && ret >= B_OK; y--) { ret = inSource->Read(rowBuffer, bytesPerRow); // see if an error happened while reading if (ret < B_OK) break; // convert to native format (big endian) switch (format) { case B_GRAY8: { uint8* src = rowBuffer; for (uint32 x = 0; x < width; x++) { rows[0][x] = src[0]; src += 1; } break; } case B_RGB24: { uint8* src = rowBuffer; for (uint32 x = 0; x < width; x++) { rows[0][x] = src[2]; rows[1][x] = src[1]; rows[2][x] = src[0]; src += 3; } break; } case B_RGB24_BIG: { uint8* src = rowBuffer; for (uint32 x = 0; x < width; x++) { rows[0][x] = src[0]; rows[1][x] = src[1]; rows[2][x] = src[2]; src += 3; } break; } case B_RGB32: { uint8* src = rowBuffer; for (uint32 x = 0; x < width; x++) { rows[0][x] = src[2]; rows[1][x] = src[1]; rows[2][x] = src[0]; // ignore src[3] src += 4; } break; } case B_RGB32_BIG: { uint8* src = rowBuffer; for (uint32 x = 0; x < width; x++) { rows[0][x] = src[1]; rows[1][x] = src[2]; rows[2][x] = src[3]; // ignore src[0] src += 4; } break; } case B_RGBA32: { uint8* src = rowBuffer; for (uint32 x = 0; x < width; x++) { rows[0][x] = src[2]; rows[1][x] = src[1]; rows[2][x] = src[0]; rows[3][x] = src[3]; src += 4; } break; } case B_RGBA32_BIG: { uint8* src = rowBuffer; for (uint32 x = 0; x < width; x++) { rows[0][x] = src[1]; rows[1][x] = src[2]; rows[2][x] = src[3]; rows[3][x] = src[0]; src += 4; } break; } default: // cannot be here break; } // switch (format) // for each channel, write a row buffer for (uint32 z = 0; z < channelCount; z++) { ret = sgiImage->WriteRow(rows[z], y, z); if (ret < B_OK) { printf("WriteRow() returned %s!\n", strerror(ret)); break; } } } // for (uint32 y = 0; y < height && ret >= B_OK; y++) if (ret >= B_OK) ret = B_OK; } else // if (rows && rows[0] && rowBuffer) ret = B_NO_MEMORY; delete[] rows[0]; delete[] rows; delete[] rowBuffer; } // done with the SGIImage object delete sgiImage; return ret; } return B_NO_TRANSLATOR; } // translate_from_sgi status_t SGITranslator::translate_from_sgi(BPositionIO *inSource, uint32 outType, BPositionIO *outDestination) { status_t ret = B_NO_TRANSLATOR; // if copying SGI_FORMAT to SGI_FORMAT if (outType == SGI_FORMAT) { translate_direct_copy(inSource, outDestination); return B_OK; } // variables needing cleanup SGIImage* sgiImage = NULL; ret = identify_sgi_header(inSource, NULL, outType, &sgiImage); if (ret >= B_OK) { bool bheaderonly = false, bdataonly = false; uint32 width = sgiImage->Width(); uint32 height = sgiImage->Height(); uint32 channelCount = sgiImage->CountChannels(); color_space format = B_RGBA32; uint32 bytesPerRow = 0; uint32 bytesPerChannel = sgiImage->BytesPerChannel(); if (channelCount == 1) { // format = B_GRAY8; // this format is not supported by most applications // bytesPerRow = width; format = B_RGB32; bytesPerRow = width * 4; } else if (channelCount == 2) { // means gray (luminance) + alpha, we convert that to B_RGBA32 format = B_RGBA32; bytesPerRow = width * 4; } else if (channelCount == 3) { format = B_RGB32; // should be B_RGB24, but let's not push it too hard... bytesPerRow = width * 4; } else if (channelCount == 4) { format = B_RGBA32; bytesPerRow = width * 4; } else ret = B_NO_TRANSLATOR; // we cannot handle this image if (ret >= B_OK && !bdataonly) { // Construct and write Be bitmap header TranslatorBitmap bitsHeader; bitsHeader.magic = B_TRANSLATOR_BITMAP; bitsHeader.bounds.left = 0; bitsHeader.bounds.top = 0; bitsHeader.bounds.right = width - 1; bitsHeader.bounds.bottom = height - 1; bitsHeader.rowBytes = bytesPerRow; bitsHeader.colors = format; bitsHeader.dataSize = bitsHeader.rowBytes * height; if ((ret = swap_data(B_UINT32_TYPE, &bitsHeader, sizeof(TranslatorBitmap), B_SWAP_HOST_TO_BENDIAN)) < B_OK) { return ret; } else ret = outDestination->Write(&bitsHeader, sizeof(TranslatorBitmap)); } if (ret < B_OK) printf("error writing bits header: %s\n", strerror(ret)); if (ret >= B_OK && !bheaderonly) { // read one row at a time, // convert to the correct format // and write out the results // SGI Images store each channel separately // a buffer is allocated big enough to hold all channels // then the pointers are assigned with offsets into that buffer uint8** rows = new(nothrow) uint8*[channelCount]; if (rows) rows[0] = new(nothrow) uint8[width * channelCount * bytesPerChannel]; // rowBuffer is going to hold the converted data uint8* rowBuffer = new(nothrow) uint8[bytesPerRow]; if (rows && rows[0] && rowBuffer) { // assign the other pointers (channel offsets in row buffer) for (uint32 i = 1; i < channelCount; i++) rows[i] = rows[0] + i * width * bytesPerChannel; // loop through all lines of the image for (int32 y = height - 1; y >= 0 && ret >= B_OK; y--) { // fill the row buffer with each channel for (uint32 z = 0; z < channelCount; z++) { ret = sgiImage->ReadRow(rows[z], y, z); if (ret < B_OK) break; } // see if an error happened while reading if (ret < B_OK) break; // convert to native format (big endian) if (bytesPerChannel == 1) { switch (format) { case B_GRAY8: { uint8* dst = rowBuffer; for (uint32 x = 0; x < width; x++) { dst[0] = rows[0][x]; dst += 1; } break; } case B_RGB24: { uint8* dst = rowBuffer; for (uint32 x = 0; x < width; x++) { dst[0] = rows[2][x]; dst[1] = rows[1][x]; dst[2] = rows[0][x]; dst += 3; } break; } case B_RGB32: { uint8* dst = rowBuffer; if (channelCount == 1) { for (uint32 x = 0; x < width; x++) { dst[0] = rows[0][x]; dst[1] = rows[0][x]; dst[2] = rows[0][x]; dst[3] = 255; dst += 4; } } else { for (uint32 x = 0; x < width; x++) { dst[0] = rows[2][x]; dst[1] = rows[1][x]; dst[2] = rows[0][x]; dst[3] = 255; dst += 4; } } break; } case B_RGBA32: { uint8* dst = rowBuffer; if (channelCount == 2) { for (uint32 x = 0; x < width; x++) { dst[0] = rows[0][x]; dst[1] = rows[0][x]; dst[2] = rows[0][x]; dst[3] = rows[1][x]; dst += 4; } } else { for (uint32 x = 0; x < width; x++) { dst[0] = rows[2][x]; dst[1] = rows[1][x]; dst[2] = rows[0][x]; dst[3] = rows[3][x]; dst += 4; } } break; } default: // cannot be here break; } // switch (format) ret = outDestination->Write(rowBuffer, bytesPerRow); } else { // support for 16 bits per channel images uint16** rows16 = (uint16**)rows; switch (format) { case B_GRAY8: { uint8* dst = rowBuffer; for (uint32 x = 0; x < width; x++) { dst[0] = rows16[0][x] >> 8; dst += 1; } break; } case B_RGB24: { uint8* dst = rowBuffer; for (uint32 x = 0; x < width; x++) { dst[0] = rows16[2][x] >> 8; dst[1] = rows16[1][x] >> 8; dst[2] = rows16[0][x] >> 8; dst += 3; } break; } case B_RGB32: { uint8* dst = rowBuffer; if (channelCount == 1) { for (uint32 x = 0; x < width; x++) { dst[0] = rows16[0][x] >> 8; dst[1] = rows16[0][x] >> 8; dst[2] = rows16[0][x] >> 8; dst[3] = 255; dst += 4; } } else { for (uint32 x = 0; x < width; x++) { dst[0] = rows16[2][x] >> 8; dst[1] = rows16[1][x] >> 8; dst[2] = rows16[0][x] >> 8; dst[3] = 255; dst += 4; } } break; } case B_RGBA32: { uint8* dst = rowBuffer; if (channelCount == 2) { for (uint32 x = 0; x < width; x++) { dst[0] = rows16[0][x] >> 8; dst[1] = rows16[0][x] >> 8; dst[2] = rows16[0][x] >> 8; dst[3] = rows16[1][x] >> 8; dst += 4; } } else { for (uint32 x = 0; x < width; x++) { dst[0] = rows16[2][x] >> 8; dst[1] = rows16[1][x] >> 8; dst[2] = rows16[0][x] >> 8; dst[3] = rows16[3][x] >> 8; dst += 4; } } break; } default: // cannot be here break; } // switch (format) ret = outDestination->Write(rowBuffer, bytesPerRow); } // 16 bit version } // for (uint32 y = 0; y < height && ret >= B_OK; y++) if (ret >= B_OK) ret = B_OK; } else // if (rows && rows[0] && rowBuffer) ret = B_NO_MEMORY; delete[] rows[0]; delete[] rows; delete[] rowBuffer; } // if (ret >= B_OK && !bheaderonly) } // if (ret >= B_OK) delete sgiImage; return ret; } // --------------------------------------------------------------- // DerivedTranslate // // Translates the data in inSource to the type outType and stores // the translated data in outDestination. // // Preconditions: // // Parameters: inSource, the data to be translated // // inInfo, hint about the data in inSource (not used) // // ioExtension, configuration options for the // translator // // outType, the type to convert inSource to // // outDestination, where the translated data is // put // // baseType, indicates whether inSource is in the // bits format, not in the bits format or // is unknown // // Postconditions: // // Returns: B_BAD_VALUE, if the options in ioExtension are bad // // B_NO_TRANSLATOR, if this translator doesn't understand the data // // B_ERROR, if there was an error allocating memory or converting // data // // B_OK, if all went well // --------------------------------------------------------------- status_t SGITranslator::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_sgi(inSource, outType, outDestination); else // if BaseTranslator did not properly identify the data as // bits or not bits return B_NO_TRANSLATOR; } BView * SGITranslator::NewConfigView(TranslatorSettings *settings) { return new SGIView("SGITranslator Settings", B_WILL_DRAW, settings); }