/* * Copyright 2003-2009, Haiku, Inc. All rights reserved. * Distributed under the terms of the MIT License. * * Authors: * Michael Wilber * Stephan Aßmus (write support) */ #include "TIFFTranslator.h" #include "TIFFView.h" #define TIFF_DISABLE_DEPRECATED #include "tiffio.h" #if __GNUC__ == 2 #define TIFF_UINT32_TYPE uint32 #else #define TIFF_UINT32_TYPE uint32_t #endif #include #include #include #undef B_TRANSLATION_CONTEXT #define B_TRANSLATION_CONTEXT "TIFFTranslator" /*! How this works: libtiff has a special version of TIFFOpen() 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 TIFF* handle that is returned by TIFFOpen(), libtiff uses the special reading writing etc functions so that all stream io happens on the BPositionIO object. */ // 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 (TIFFTranslator)" }, { B_TIFF_FORMAT, B_TRANSLATOR_BITMAP, TIFF_IN_QUALITY, TIFF_IN_CAPABILITY, "image/tiff", "TIFF 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 (TIFFTranslator)" }, { B_TIFF_FORMAT, B_TRANSLATOR_BITMAP, TIFF_OUT_QUALITY, TIFF_OUT_CAPABILITY, "image/tiff", "TIFF 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}, {TIFF_SETTING_COMPRESSION, TRAN_SETTING_INT32, COMPRESSION_LZW} // Compression is LZW 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 TIFFTranslator object to be used by BTranslatorRoster // // Preconditions: // // Parameters: n, The translator to return. Since // TIFFTranslator only publishes one // translator, it only returns a // TIFFTranslator 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 TIFFTranslator if n is zero // --------------------------------------------------------------- BTranslator * make_nth_translator(int32 n, image_id you, uint32 flags, ...) { if (!n) return new TIFFTranslator(); else return NULL; } //// libtiff Callback functions! BPositionIO * tiff_get_pio(thandle_t stream) { BPositionIO *pio = NULL; pio = static_cast(stream); if (!pio) debugger("pio is NULL"); return pio; } tsize_t tiff_read_proc(thandle_t stream, tdata_t buf, tsize_t size) { return tiff_get_pio(stream)->Read(buf, size); } tsize_t tiff_write_proc(thandle_t stream, tdata_t buf, tsize_t size) { return tiff_get_pio(stream)->Write(buf, size); } toff_t tiff_seek_proc(thandle_t stream, toff_t off, int whence) { return tiff_get_pio(stream)->Seek(off, whence); } int tiff_close_proc(thandle_t stream) { tiff_get_pio(stream)->Seek(0, SEEK_SET); return 0; } toff_t tiff_size_proc(thandle_t stream) { BPositionIO *pio = tiff_get_pio(stream); off_t cur, end; cur = pio->Position(); end = pio->Seek(0, SEEK_END); pio->Seek(cur, SEEK_SET); return end; } int tiff_map_file_proc(thandle_t stream, tdata_t *pbase, toff_t *psize) { // BeOS doesn't support mmap() so just return 0 return 0; } void tiff_unmap_file_proc(thandle_t stream, tdata_t base, toff_t size) { return; } status_t identify_tiff_header(BPositionIO *inSource, BMessage *ioExtension, translator_info *outInfo, uint32 outType, TIFF **poutTIFF = NULL) { // get TIFF handle TIFF* tif = TIFFClientOpen("TIFFTranslator", "r", inSource, tiff_read_proc, tiff_write_proc, tiff_seek_proc, tiff_close_proc, tiff_size_proc, tiff_map_file_proc, tiff_unmap_file_proc); if (!tif) return B_NO_TRANSLATOR; // count number of documents int32 documentCount = 0, documentIndex = 1; do { documentCount++; } while (TIFFReadDirectory(tif)); if (ioExtension) { // Check if a document index has been specified if (ioExtension->FindInt32(DOCUMENT_INDEX, &documentIndex) != B_OK) documentIndex = 1; if (documentIndex < 1 || documentIndex > documentCount) { // document index is invalid fputs(B_TRANSLATE("identify_tiff_header: invalid " "document index\n"), stderr); return B_NO_TRANSLATOR; } } // identify the document the user specified or the first document // if the user did not specify which document they wanted to identify if (!TIFFSetDirectory(tif, documentIndex - 1)) { fputs(B_TRANSLATE("identify_tiff_header: couldn't set " "directory\n"), stderr); return B_NO_TRANSLATOR; } if (ioExtension) { // add page count to ioExtension ioExtension->RemoveName(DOCUMENT_COUNT); ioExtension->AddInt32(DOCUMENT_COUNT, documentCount); } if (outInfo) { outInfo->type = B_TIFF_FORMAT; outInfo->group = B_TRANSLATOR_BITMAP; outInfo->quality = TIFF_IN_QUALITY; outInfo->capability = TIFF_IN_CAPABILITY; strcpy(outInfo->MIME, "image/tiff"); strlcpy(outInfo->name, B_TRANSLATE("TIFF image"), sizeof(outInfo->name)); } if (!poutTIFF) { // close TIFF if caller is not interested in TIFF handle TIFFClose(tif); } else { // leave TIFF open and return handle if caller needs it *poutTIFF = tif; } return B_OK; } // How this works: // Following are a couple of functions, // // convert_buffer_* to convert a buffer in place to the TIFF native format // // convert_buffers_* to convert from one buffer to another to the TIFF // native format, additionally compensating for padding bytes // I don't know if libTIFF can be set up to respect padding bytes, // otherwise this whole thing could be simplified a bit. // // Additionally, there are two functions convert_buffer() and convert_buffers() that take // a color_space as one of the arguments and pick the correct worker functions from there. // This way I don't write any code more than once, for easier debugging and maintainance. // convert_buffer_bgra_rgba inline void convert_buffer_bgra_rgba(uint8* buffer, uint32 rows, uint32 width, uint32 bytesPerRow) { for (uint32 y = 0; y < rows; y++) { uint8* handle = buffer; for (uint32 x = 0; x < width; x++) { uint8 temp = handle[0]; handle[0] = handle[2]; handle[2] = temp; handle += 4; } buffer += bytesPerRow; } } // convert_buffer_argb_rgba inline void convert_buffer_argb_rgba(uint8* buffer, uint32 rows, uint32 width, uint32 bytesPerRow) { for (uint32 y = 0; y < rows; y++) { uint8* handle = buffer; for (uint32 x = 0; x < width; x++) { uint8 temp = handle[0]; handle[0] = handle[1]; handle[1] = handle[2]; handle[2] = handle[3]; handle[3] = temp; handle += 4; } buffer += bytesPerRow; } } // convert_buffers_bgra_rgba inline void convert_buffers_bgra_rgba(uint8* inBuffer, uint8* outBuffer, uint32 rows, uint32 width, uint32 bytesPerRow) { for (uint32 y = 0; y < rows; y++) { uint8* inHandle = inBuffer; uint8* outHandle = outBuffer; for (uint32 x = 0; x < width; x++) { outHandle[0] = inHandle[2]; outHandle[1] = inHandle[1]; outHandle[2] = inHandle[0]; outHandle[3] = inHandle[3]; inHandle += 4; outHandle += 4; } inBuffer += bytesPerRow; outBuffer += width * 4; } } // convert_buffers_argb_rgba inline void convert_buffers_argb_rgba(uint8* inBuffer, uint8* outBuffer, uint32 rows, uint32 width, uint32 bytesPerRow) { for (uint32 y = 0; y < rows; y++) { uint8* inHandle = inBuffer; uint8* outHandle = outBuffer; for (uint32 x = 0; x < width; x++) { outHandle[0] = inHandle[1]; outHandle[1] = inHandle[2]; outHandle[2] = inHandle[3]; outHandle[3] = inHandle[0]; inHandle += 4; outHandle += 4; } inBuffer += bytesPerRow; outBuffer += width * 4; } } // convert_buffers_bgrX_rgb inline void convert_buffers_bgrX_rgb(uint8* inBuffer, uint8* outBuffer, uint32 rows, uint32 width, uint32 bytesPerRow, uint32 samplesPerPixel) { for (uint32 y = 0; y < rows; y++) { uint8* inHandle = inBuffer; uint8* outHandle = outBuffer; for (uint32 x = 0; x < width; x++) { // the usage of temp is just in case inBuffer == outBuffer // (see convert_buffer() for B_RGB24) uint8 temp = inHandle[0]; outHandle[0] = inHandle[2]; outHandle[1] = inHandle[1]; outHandle[2] = temp; inHandle += samplesPerPixel; outHandle += 3; } inBuffer += bytesPerRow; outBuffer += width * 3; } } // convert_buffers_rgbX_rgb inline void convert_buffers_rgbX_rgb(uint8* inBuffer, uint8* outBuffer, uint32 rows, uint32 width, uint32 bytesPerRow, uint32 samplesPerPixel) { for (uint32 y = 0; y < rows; y++) { uint8* inHandle = inBuffer; uint8* outHandle = outBuffer; for (uint32 x = 0; x < width; x++) { outHandle[0] = inHandle[0]; outHandle[1] = inHandle[1]; outHandle[2] = inHandle[2]; inHandle += samplesPerPixel; outHandle += 3; } inBuffer += bytesPerRow; outBuffer += width * 3; } } // convert_buffers_cmap inline void convert_buffers_cmap(uint8* inBuffer, uint8* outBuffer, uint32 rows, uint32 width, uint32 bytesPerRow) { // compensate for bytesPerRow != width (padding bytes) // this function will not be called if bytesPerRow == width, btw for (uint32 y = 0; y < rows; y++) { _TIFFmemcpy(outBuffer, inBuffer, width); inBuffer += bytesPerRow; outBuffer += width; } } // convert_buffer inline void convert_buffer(color_space format, uint8* buffer, uint32 rows, uint32 width, uint32 bytesPerRow) { switch (format) { case B_RGBA32: convert_buffer_bgra_rgba(buffer, rows, width, bytesPerRow); break; case B_RGBA32_BIG: convert_buffer_argb_rgba(buffer, rows, width, bytesPerRow); break; // case B_RGB32: // case B_RGB32_BIG: // these two cannot be encountered, since inBufferSize != bytesPerStrip // (we're stripping the unused "alpha" channel 32->24 bits) case B_RGB24: convert_buffers_bgrX_rgb(buffer, buffer, rows, width, bytesPerRow, 3); break; // case B_RGB24_BIG: // buffer already has the correct format break; // case B_CMAP8: // case B_GRAY8: // buffer already has the correct format break; default: break; } } // convert_buffers inline void convert_buffers(color_space format, uint8* inBuffer, uint8* outBuffer, uint32 rows, uint32 width, uint32 bytesPerRow) { switch (format) { case B_RGBA32: convert_buffers_bgra_rgba(inBuffer, outBuffer, rows, width, bytesPerRow); break; case B_RGBA32_BIG: convert_buffers_argb_rgba(inBuffer, outBuffer, rows, width, bytesPerRow); break; case B_RGB32: convert_buffers_bgrX_rgb(inBuffer, outBuffer, rows, width, bytesPerRow, 4); break; case B_RGB32_BIG: convert_buffers_rgbX_rgb(inBuffer, outBuffer, rows, width, bytesPerRow, 4); break; case B_RGB24: convert_buffers_bgrX_rgb(inBuffer, outBuffer, rows, width, bytesPerRow, 3); break; case B_RGB24_BIG: convert_buffers_rgbX_rgb(inBuffer, outBuffer, rows, width, bytesPerRow, 3); break; case B_CMAP8: case B_GRAY8: convert_buffers_cmap(inBuffer, outBuffer, rows, width, bytesPerRow); break; default: break; } } // Sets up any additional TIFF fields for the color spaces it supports, // determines if it needs one or two buffers to carry out any conversions, // uses the various convert routines above to do the actual conversion, // writes complete strips of data plus one strip of remaining data. // // write_tif_stream status_t write_tif_stream(TIFF* tif, BPositionIO* inSource, color_space format, uint32 width, uint32 height, uint32 bytesPerRow, uint32 rowsPerStrip, uint32 dataSize) { uint32 bytesPerStrip = 0; // set up the TIFF fields about what channels we write switch (format) { case B_RGBA32: case B_RGBA32_BIG: uint16 extraSamples[1]; extraSamples[0] = EXTRASAMPLE_UNASSALPHA; TIFFSetField(tif, TIFFTAG_EXTRASAMPLES, 1, extraSamples); TIFFSetField(tif, TIFFTAG_SAMPLESPERPIXEL, 4); // going to write rgb + alpha channels bytesPerStrip = width * 4 * rowsPerStrip; break; case B_RGB32: case B_RGB32_BIG: case B_RGB24: case B_RGB24_BIG: TIFFSetField(tif, TIFFTAG_SAMPLESPERPIXEL, 3); // going to write just the rgb channels bytesPerStrip = width * 3 * rowsPerStrip; break; case B_CMAP8: case B_GRAY8: bytesPerStrip = width * rowsPerStrip; break; default: return B_BAD_VALUE; } uint32 remaining = dataSize; status_t ret = B_OK; // Write the information to the stream uint32 inBufferSize = bytesPerRow * rowsPerStrip; // allocate intermediate input buffer uint8* inBuffer = (uint8*)_TIFFmalloc(inBufferSize); ssize_t read, written = B_ERROR; // bytesPerStrip is the size of the buffer that libtiff expects to write per strip // it might be different to the size of the input buffer, // if that one contains padding bytes at the end of each row if (inBufferSize != bytesPerStrip) { // allocate a second buffer // (two buffers are needed since padding bytes have to be compensated for) uint8* outBuffer = (uint8*)_TIFFmalloc(bytesPerStrip); if (inBuffer && outBuffer) { //printf("using two buffers\n"); read = inSource->Read(inBuffer, inBufferSize); uint32 stripIndex = 0; while (read == (ssize_t)inBufferSize) { //printf("writing bytes: %ld (strip: %ld)\n", read, stripIndex); // convert the buffers (channel order) and compensate // for bytesPerRow != samplesPerRow (padding bytes) convert_buffers(format, inBuffer, outBuffer, rowsPerStrip, width, bytesPerRow); // let libtiff write the encoded strip to the BPositionIO written = TIFFWriteEncodedStrip(tif, stripIndex, outBuffer, bytesPerStrip); stripIndex++; if (written < B_OK) break; remaining -= inBufferSize; read = inSource->Read(inBuffer, min_c(inBufferSize, remaining)); } // write the rest of the remaining rows if (read < (ssize_t)inBufferSize && read > 0) { //printf("writing remaining bytes: %ld\n", read); // convert the buffers (channel order) and compensate // for bytesPerRow != samplesPerRow (padding bytes) convert_buffers(format, inBuffer, outBuffer, read / bytesPerRow, width, bytesPerRow); // let libtiff write the encoded strip to the BPositionIO written = TIFFWriteEncodedStrip(tif, stripIndex, outBuffer, read); remaining -= read; } } else ret = B_NO_MEMORY; // clean up output buffer if (outBuffer) _TIFFfree(outBuffer); } else { //printf("using one buffer\n"); // the input buffer is all we need, we convert it in place if (inBuffer) { read = inSource->Read(inBuffer, inBufferSize); uint32 stripIndex = 0; while (read == (ssize_t)inBufferSize) { //printf("writing bytes: %ld (strip: %ld)\n", read, stripIndex); // convert the buffer (channel order) convert_buffer(format, inBuffer, rowsPerStrip, width, bytesPerRow); // let libtiff write the encoded strip to the BPositionIO written = TIFFWriteEncodedStrip(tif, stripIndex, inBuffer, bytesPerStrip); stripIndex++; if (written < 0) break; remaining -= inBufferSize; read = inSource->Read(inBuffer, min_c(inBufferSize, remaining)); } // write the rest of the remaining rows if (read < (ssize_t)inBufferSize && read > 0) { //printf("writing remaining bytes: %ld (strip: %ld)\n", read, stripIndex); // convert the buffers (channel order) and compensate // for bytesPerRow != samplesPerRow (padding bytes) convert_buffer(format, inBuffer, read / bytesPerRow, width, bytesPerRow); // let libtiff write the encoded strip to the BPositionIO written = TIFFWriteEncodedStrip(tif, stripIndex, inBuffer, read); remaining -= read; } } else ret = B_NO_MEMORY; } // clean up input buffer if (inBuffer) _TIFFfree(inBuffer); // see if there was an error reading or writing the streams if (remaining > 0) // "written" may contain a more specific error ret = written < 0 ? written : B_ERROR; else ret = B_OK; return ret; } // #pragma mark - TIFFTranslator::TIFFTranslator() : BaseTranslator(B_TRANSLATE("TIFF images"), B_TRANSLATE("TIFF image translator"), TIFF_TRANSLATOR_VERSION, sInputFormats, kNumInputFormats, sOutputFormats, kNumOutputFormats, "TIFFTranslator_Settings", sDefaultSettings, kNumDefaultSettings, B_TRANSLATOR_BITMAP, B_TIFF_FORMAT) { // TODO: for now! TIFFSetErrorHandler(NULL); } TIFFTranslator::~TIFFTranslator() { } status_t TIFFTranslator::DerivedIdentify(BPositionIO *inSource, const translation_format *inFormat, BMessage *ioExtension, translator_info *outInfo, uint32 outType) { return identify_tiff_header(inSource, ioExtension, outInfo, outType); } status_t TIFFTranslator::translate_from_bits(BPositionIO *inSource, uint32 outType, BPositionIO *outDestination) { TranslatorBitmap bitsHeader; uint32 compression = fSettings->SetGetInt32(TIFF_SETTING_COMPRESSION); status_t result; result = identify_bits_header(inSource, NULL, &bitsHeader); if (result != B_OK) return result; // Translate B_TRANSLATOR_BITMAP to B_TIFF_FORMAT if (outType == B_TIFF_FORMAT) { // Set up TIFF header // get TIFF handle TIFF* tif = TIFFClientOpen("TIFFTranslator", "w", outDestination, tiff_read_proc, tiff_write_proc, tiff_seek_proc, tiff_close_proc, tiff_size_proc, tiff_map_file_proc, tiff_unmap_file_proc); if (!tif) return B_NO_TRANSLATOR; // common fields which are independent of the bitmap format uint32 width = bitsHeader.bounds.IntegerWidth() + 1; uint32 height = bitsHeader.bounds.IntegerHeight() + 1; uint32 dataSize = bitsHeader.dataSize; uint32 bytesPerRow = bitsHeader.rowBytes; TIFFSetField(tif, TIFFTAG_IMAGEWIDTH, width); TIFFSetField(tif, TIFFTAG_IMAGELENGTH, height); TIFFSetField(tif, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG); /*const char* compressionString = NULL; switch (compression) { case COMPRESSION_NONE: compressionString = "None"; break; case COMPRESSION_PACKBITS: compressionString = "RLE"; break; case COMPRESSION_DEFLATE: compressionString = "Deflate"; break; case COMPRESSION_LZW: compressionString = "LZW"; break; case COMPRESSION_JPEG: compressionString = "JPEG"; break; case COMPRESSION_JP2000: compressionString = "JPEG2000"; break; } if (compressionString) printf("using compression: %s\n", compressionString); else printf("using unkown compression (%ld).\n", compression); */ TIFFSetField(tif, TIFFTAG_COMPRESSION, compression); // TODO: some extra fields that should also get some special attention TIFFSetField(tif, TIFFTAG_XRESOLUTION, 150.0); TIFFSetField(tif, TIFFTAG_YRESOLUTION, 150.0); TIFFSetField(tif, TIFFTAG_RESOLUTIONUNIT, RESUNIT_INCH); // we are going to write XX row(s) of pixels (lines) per strip uint32 rowsPerStrip = TIFFDefaultStripSize(tif, 0); //printf("recommended rows per strip: %ld\n", TIFFDefaultStripSize(tif, 0)); TIFFSetField(tif, TIFFTAG_ROWSPERSTRIP, rowsPerStrip); status_t ret = B_OK; // set the rest of the fields according to the bitmap format switch (bitsHeader.colors) { // Output to 32-bit True Color TIFF (8 bits alpha) case B_RGBA32: case B_RGB32: case B_RGB24: case B_RGBA32_BIG: case B_RGB32_BIG: case B_RGB24_BIG: // set the fields specific to this color space TIFFSetField(tif, TIFFTAG_BITSPERSAMPLE, 8); TIFFSetField(tif, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_RGB); // TIFFSetField(tif, TIFFTAG_SAMPLEFORMAT, SAMPLEFORMAT_UINT); // write the tiff stream ret = write_tif_stream(tif, inSource, bitsHeader.colors, width, height, bytesPerRow, rowsPerStrip, dataSize); break; /* case B_CMYA32: break; // Output to 15-bit True Color TIFF case B_RGB15: case B_RGB15_BIG: TIFFSetField(tif, TIFFTAG_BITSPERSAMPLE, 5); TIFFSetField(tif, TIFFTAG_SAMPLESPERPIXEL, 3); TIFFSetField(tif, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_RGB); bytesPerStrip = width * 2 * rowsPerStrip; break; */ // Output to 8-bit Color Mapped TIFF 32 bits per color map entry case B_CMAP8: { TIFFSetField(tif, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_PALETTE); TIFFSetField(tif, TIFFTAG_SAMPLESPERPIXEL, 1); TIFFSetField(tif, TIFFTAG_BITSPERSAMPLE, 8); // convert the system palette to 16 bit values for libtiff const color_map *map = system_colors(); if (map) { uint16 red[256]; uint16 green[256]; uint16 blue[256]; for (uint32 i = 0; i < 256; i++) { // scale 8 bits to 16 bits red[i] = map->color_list[i].red * 256 + map->color_list[i].red; green[i] = map->color_list[i].green * 256 + map->color_list[i].green; blue[i] = map->color_list[i].blue * 256 + map->color_list[i].blue; } TIFFSetField(tif, TIFFTAG_COLORMAP, &red, &green, &blue); // write the tiff stream ret = write_tif_stream(tif, inSource, bitsHeader.colors, width, height, bytesPerRow, rowsPerStrip, dataSize); } else ret = B_ERROR; break; } // Output to 8-bit Black and White TIFF case B_GRAY8: TIFFSetField(tif, TIFFTAG_BITSPERSAMPLE, 8); TIFFSetField(tif, TIFFTAG_SAMPLESPERPIXEL, 1); TIFFSetField(tif, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_MINISBLACK); ret = write_tif_stream(tif, inSource, bitsHeader.colors, width, height, bytesPerRow, rowsPerStrip, dataSize); break; /* // Output to 1-bit Black and White TIFF case B_GRAY1: TIFFSetField(tif, TIFFTAG_BITSPERSAMPLE, 1); TIFFSetField(tif, TIFFTAG_SAMPLESPERPIXEL, 8); TIFFSetField(tif, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_MINISBLACK); bytesPerStrip = ((width + 7) / 8) * rowsPerStrip; break; */ default: ret = B_NO_TRANSLATOR; } // Close the handle TIFFClose(tif); return ret; } else return B_NO_TRANSLATOR; } status_t TIFFTranslator::translate_from_tiff(BPositionIO *inSource, BMessage *ioExtension, uint32 outType, BPositionIO *outDestination) { status_t result = B_NO_TRANSLATOR; bool bheaderonly = false, bdataonly = false; // Always write out the entire image. Some programs // fail when given "headerOnly", even though they requested it. // These settings are not applicable when outputting TIFFs // variables needing cleanup TIFF *ptif = NULL; uint32 *praster = NULL; status_t ret; ret = identify_tiff_header(inSource, ioExtension, NULL, outType, &ptif); if (outType == B_TIFF_FORMAT && ret == B_OK && ptif) { // if translating from TIFF to TIFF, // just write out the entire TIFF TIFFClose(ptif); translate_direct_copy(inSource, outDestination); return B_OK; } while (ret == B_OK && ptif) { // use while / break not for looping, but for // cleaner goto like capability ret = B_ERROR; // make certain there is no looping uint32 width = 0, height = 0; if (!TIFFGetField(ptif, TIFFTAG_IMAGEWIDTH, &width)) { result = B_NO_TRANSLATOR; break; } if (!TIFFGetField(ptif, TIFFTAG_IMAGELENGTH, &height)) { result = B_NO_TRANSLATOR; break; } size_t npixels = 0; npixels = width * height; praster = static_cast(_TIFFmalloc(npixels * 4)); if (praster && TIFFReadRGBAImage(ptif, width, height, (TIFF_UINT32_TYPE*)praster, 0)) { if (!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 = 4 * width; bitsHeader.colors = B_RGBA32; bitsHeader.dataSize = bitsHeader.rowBytes * height; if (swap_data(B_UINT32_TYPE, &bitsHeader, sizeof(TranslatorBitmap), B_SWAP_HOST_TO_BENDIAN) != B_OK) { result = B_ERROR; break; } outDestination->Write(&bitsHeader, sizeof(TranslatorBitmap)); } if (!bheaderonly) { // Convert raw RGBA data to B_RGBA32 colorspace // and write out the results uint8 *pbitsrow = new uint8[width * 4]; if (!pbitsrow) { result = B_NO_MEMORY; break; } uint8 *pras8 = reinterpret_cast(praster); for (uint32 i = 0; i < height; i++) { uint8 *pbits, *prgba; pbits = pbitsrow; prgba = pras8 + ((height - (i + 1)) * width * 4); for (uint32 k = 0; k < width; k++) { pbits[0] = prgba[2]; pbits[1] = prgba[1]; pbits[2] = prgba[0]; pbits[3] = prgba[3]; pbits += 4; prgba += 4; } outDestination->Write(pbitsrow, width * 4); } delete[] pbitsrow; pbitsrow = NULL; } result = B_OK; break; } // if (praster && TIFFReadRGBAImage(ptif, width, height, praster, 0)) } // while (ret == B_OK && ptif) if (praster) { _TIFFfree(praster); praster = NULL; } if (ptif) { TIFFClose(ptif); ptif = NULL; } return result; } // --------------------------------------------------------------- // 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 TIFFTranslator::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_tiff(inSource, ioExtension, outType, outDestination); else // if BaseTranslator did not properly identify the data as // bits or not bits return B_NO_TRANSLATOR; } BView * TIFFTranslator::NewConfigView(TranslatorSettings *settings) { return new TIFFView(B_TRANSLATE("TIFFTranslator Settings"), B_WILL_DRAW, settings); }