// This library is distributed under the conditions of the GNU LGPL. #include "config.h" #ifdef HAVE_OPENJPEG #include #include "jp2.h" #if !defined(__STDC_LIMIT_MACROS) #define __STDC_LIMIT_MACROS #endif #ifdef HAVE_SYS_TYPES_H #include #endif #ifdef HAVE_STDINT_H #include #endif #include #include #include #include #include #include #include #include #include /* * JPEG-2000 Plugin for KImageIO. * * Current limitations: * - Doesn't support writing images. * - Doesn't support OPJ_CODEC_J2K. * - Doesn't support subsampling. * - Doesn't read ICC profiles. * - Doesn't support esycc or cymk colorspaces. * * The API documentation is rather poor, so good references on how to use OpenJPEG * are the tools provided by OpenJPEG, such as 'opj_decompress': * https://github.com/uclouvain/openjpeg/blob/master/src/bin/jp2/opj_decompress.c * https://github.com/uclouvain/openjpeg/blob/master/src/bin/jp2/opj_compress.c */ // kdDebug category constexpr int kCategory = 399; struct KIMGJP2Wrapper { public: opj_codec_t *codec { nullptr }; opj_image_t *image { nullptr }; opj_stream_t *stream { nullptr }; KTempFile tempFile; ~KIMGJP2Wrapper() { if (stream) { opj_stream_destroy(stream); } if (image) { opj_image_destroy(image); } if (codec) { opj_destroy_codec(codec); } } }; /* * The following sycc* functions come from OpenJPEG * https://github.com/uclouvain/openjpeg/blob/master/src/bin/common/color.c * * It has beens slightly adjusted to better fit the code style of this file. * * The copyright in this software is being made available under the 2-clauses * BSD License, included below. This software may be subject to other third * party and contributor rights, including patent rights, and no such rights * are granted under this license. * * Copyright (c) 2002-2014, Universite catholique de Louvain (UCL), Belgium * Copyright (c) 2002-2014, Professor Benoit Macq * Copyright (c) 2001-2003, David Janssens * Copyright (c) 2002-2003, Yannick Verschueren * Copyright (c) 2003-2007, Francois-Olivier Devaux * Copyright (c) 2003-2014, Antonin Descampe * Copyright (c) 2005, Herve Drolon, FreeImage Team * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS `AS IS' * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ static void sycc_to_rgb(int offset, int upb, int y, int cb, int cr, int *out_r, int *out_g, int *out_b) { int r, g, b; cb -= offset; cr -= offset; r = y + (int)(1.402 * (float)cr); if (r < 0) { r = 0; } else if (r > upb) { r = upb; } *out_r = r; g = y - (int)(0.344 * (float)cb + 0.714 * (float)cr); if (g < 0) { g = 0; } else if (g > upb) { g = upb; } *out_g = g; b = y + (int)(1.772 * (float)cb); if (b < 0) { b = 0; } else if (b > upb) { b = upb; } *out_b = b; } static bool sycc444_to_rgb(opj_image_t *img) { int *d0, *d1, *d2, *r, *g, *b; const int *y, *cb, *cr; size_t maxw, maxh, max, i; int offset, upb; upb = (int)img->comps[0].prec; offset = 1 << (upb - 1); upb = (1 << upb) - 1; maxw = (size_t)img->comps[0].w; maxh = (size_t)img->comps[0].h; max = maxw * maxh; y = img->comps[0].data; cb = img->comps[1].data; cr = img->comps[2].data; d0 = r = (int*)opj_image_data_alloc(sizeof(int) * max); d1 = g = (int*)opj_image_data_alloc(sizeof(int) * max); d2 = b = (int*)opj_image_data_alloc(sizeof(int) * max); if (r == nullptr || g == nullptr || b == nullptr) { goto fails; } for (i = 0U; i < max; ++i) { sycc_to_rgb(offset, upb, *y, *cb, *cr, r, g, b); ++y; ++cb; ++cr; ++r; ++g; ++b; } opj_image_data_free(img->comps[0].data); img->comps[0].data = d0; opj_image_data_free(img->comps[1].data); img->comps[1].data = d1; opj_image_data_free(img->comps[2].data); img->comps[2].data = d2; img->color_space = OPJ_CLRSPC_SRGB; return true; fails: opj_image_data_free(r); opj_image_data_free(g); opj_image_data_free(b); return false; } static bool sycc422_to_rgb(opj_image_t *img) { int *d0, *d1, *d2, *r, *g, *b; const int *y, *cb, *cr; size_t maxw, maxh, max, offx, loopmaxw; int offset, upb; size_t i; upb = (int)img->comps[0].prec; offset = 1 << (upb - 1); upb = (1 << upb) - 1; maxw = (size_t)img->comps[0].w; maxh = (size_t)img->comps[0].h; max = maxw * maxh; y = img->comps[0].data; cb = img->comps[1].data; cr = img->comps[2].data; d0 = r = (int*)opj_image_data_alloc(sizeof(int) * max); d1 = g = (int*)opj_image_data_alloc(sizeof(int) * max); d2 = b = (int*)opj_image_data_alloc(sizeof(int) * max); if (r == nullptr || g == nullptr || b == nullptr) { goto fails; } /* if img->x0 is odd, then first column shall use Cb/Cr = 0 */ offx = img->x0 & 1U; loopmaxw = maxw - offx; for (i = 0U; i < maxh; ++i) { size_t j; if (offx > 0U) { sycc_to_rgb(offset, upb, *y, 0, 0, r, g, b); ++y; ++r; ++g; ++b; } for (j = 0U; j < (loopmaxw & ~(size_t)1U); j += 2U) { sycc_to_rgb(offset, upb, *y, *cb, *cr, r, g, b); ++y; ++r; ++g; ++b; sycc_to_rgb(offset, upb, *y, *cb, *cr, r, g, b); ++y; ++r; ++g; ++b; ++cb; ++cr; } if (j < loopmaxw) { sycc_to_rgb(offset, upb, *y, *cb, *cr, r, g, b); ++y; ++r; ++g; ++b; ++cb; ++cr; } } opj_image_data_free(img->comps[0].data); img->comps[0].data = d0; opj_image_data_free(img->comps[1].data); img->comps[1].data = d1; opj_image_data_free(img->comps[2].data); img->comps[2].data = d2; img->comps[1].w = img->comps[2].w = img->comps[0].w; img->comps[1].h = img->comps[2].h = img->comps[0].h; img->comps[1].dx = img->comps[2].dx = img->comps[0].dx; img->comps[1].dy = img->comps[2].dy = img->comps[0].dy; img->color_space = OPJ_CLRSPC_SRGB; return true; fails: opj_image_data_free(r); opj_image_data_free(g); opj_image_data_free(b); return false; } static bool sycc420_to_rgb(opj_image_t *img) { int *d0, *d1, *d2, *r, *g, *b, *nr, *ng, *nb; const int *y, *cb, *cr, *ny; size_t maxw, maxh, max, offx, loopmaxw, offy, loopmaxh; int offset, upb; size_t i; upb = (int)img->comps[0].prec; offset = 1 << (upb - 1); upb = (1 << upb) - 1; maxw = (size_t)img->comps[0].w; maxh = (size_t)img->comps[0].h; max = maxw * maxh; y = img->comps[0].data; cb = img->comps[1].data; cr = img->comps[2].data; d0 = r = (int*)opj_image_data_alloc(sizeof(int) * max); d1 = g = (int*)opj_image_data_alloc(sizeof(int) * max); d2 = b = (int*)opj_image_data_alloc(sizeof(int) * max); if (r == nullptr || g == nullptr || b == nullptr) { goto fails; } /* if img->x0 is odd, then first column shall use Cb/Cr = 0 */ offx = img->x0 & 1U; loopmaxw = maxw - offx; /* if img->y0 is odd, then first line shall use Cb/Cr = 0 */ offy = img->y0 & 1U; loopmaxh = maxh - offy; if (offy > 0U) { size_t j; for (j = 0; j < maxw; ++j) { sycc_to_rgb(offset, upb, *y, 0, 0, r, g, b); ++y; ++r; ++g; ++b; } } for (i = 0U; i < (loopmaxh & ~(size_t)1U); i += 2U) { size_t j; ny = y + maxw; nr = r + maxw; ng = g + maxw; nb = b + maxw; if (offx > 0U) { sycc_to_rgb(offset, upb, *y, 0, 0, r, g, b); ++y; ++r; ++g; ++b; sycc_to_rgb(offset, upb, *ny, *cb, *cr, nr, ng, nb); ++ny; ++nr; ++ng; ++nb; } for (j = 0; j < (loopmaxw & ~(size_t)1U); j += 2U) { sycc_to_rgb(offset, upb, *y, *cb, *cr, r, g, b); ++y; ++r; ++g; ++b; sycc_to_rgb(offset, upb, *y, *cb, *cr, r, g, b); ++y; ++r; ++g; ++b; sycc_to_rgb(offset, upb, *ny, *cb, *cr, nr, ng, nb); ++ny; ++nr; ++ng; ++nb; sycc_to_rgb(offset, upb, *ny, *cb, *cr, nr, ng, nb); ++ny; ++nr; ++ng; ++nb; ++cb; ++cr; } if (j < loopmaxw) { sycc_to_rgb(offset, upb, *y, *cb, *cr, r, g, b); ++y; ++r; ++g; ++b; sycc_to_rgb(offset, upb, *ny, *cb, *cr, nr, ng, nb); ++ny; ++nr; ++ng; ++nb; ++cb; ++cr; } y += maxw; r += maxw; g += maxw; b += maxw; } if (i < loopmaxh) { size_t j; if (offx > 0U) { sycc_to_rgb(offset, upb, *y, 0, 0, r, g, b); ++y; ++r; ++g; ++b; } for (j = 0U; j < (loopmaxw & ~(size_t)1U); j += 2U) { sycc_to_rgb(offset, upb, *y, *cb, *cr, r, g, b); ++y; ++r; ++g; ++b; sycc_to_rgb(offset, upb, *y, *cb, *cr, r, g, b); ++y; ++r; ++g; ++b; ++cb; ++cr; } if (j < loopmaxw) { sycc_to_rgb(offset, upb, *y, *cb, *cr, r, g, b); } } opj_image_data_free(img->comps[0].data); img->comps[0].data = d0; opj_image_data_free(img->comps[1].data); img->comps[1].data = d1; opj_image_data_free(img->comps[2].data); img->comps[2].data = d2; img->comps[1].w = img->comps[2].w = img->comps[0].w; img->comps[1].h = img->comps[2].h = img->comps[0].h; img->comps[1].dx = img->comps[2].dx = img->comps[0].dx; img->comps[1].dy = img->comps[2].dy = img->comps[0].dy; img->color_space = OPJ_CLRSPC_SRGB; return true; fails: opj_image_data_free(r); opj_image_data_free(g); opj_image_data_free(b); return false; } static bool color_sycc_to_rgb(opj_image_t *img) { if (img->numcomps < 3) { img->color_space = OPJ_CLRSPC_GRAY; return true; } if ((img->comps[0].dx == 1) && (img->comps[1].dx == 2) && (img->comps[2].dx == 2) && (img->comps[0].dy == 1) && (img->comps[1].dy == 2) && (img->comps[2].dy == 2)) { /* horizontal and vertical sub-sample */ return sycc420_to_rgb(img); } else if ((img->comps[0].dx == 1) && (img->comps[1].dx == 2) && (img->comps[2].dx == 2) && (img->comps[0].dy == 1) && (img->comps[1].dy == 1) && (img->comps[2].dy == 1)) { /* horizontal sub-sample only */ return sycc422_to_rgb(img); } else if ((img->comps[0].dx == 1) && (img->comps[1].dx == 1) && (img->comps[2].dx == 1) && (img->comps[0].dy == 1) && (img->comps[1].dy == 1) && (img->comps[2].dy == 1)) { /* no sub-sample */ return sycc444_to_rgb(img); } else { kdWarning(kCategory) << "Can not convert in color_sycc_to_rgb" << endl; return false; } } static void kimgio_jp2_err_handler(const char *message, void *data) { kdError(kCategory) << "Error decoding JP2 image: " << message; } static void kimgio_jp2_info_handler(const char *message, void *data) { // Reports status, e.g.: Main header has been correctly decoded. // kdDebug(kCategory) << "JP2 decoding message: " << message; } static void kimgio_jp2_warn_handler(const char *message, void *data) { kdWarning(kCategory) << "Warning decoding JP2 image: " << message; } static void kimgio_jp2_read_image(TQImage &image, const KIMGJP2Wrapper &jp2) { const int height = image.height(); const int width = image.width(); unsigned char alphaMask = 0x0; for (int row = 0; row < height; row++) { for (int col = 0; col < width; col++) { const int offset = row * width + col; uint8_t rgba[4] = { 0x00, 0x00, 0x00, 0xFF }; for (int comp = 0; comp < jp2.image->numcomps; comp++) { OPJ_INT32 value = jp2.image->comps[comp].data[offset]; value += (jp2.image->comps[comp].sgnd ? (1 << jp2.image->comps[comp].prec - 1) : 0); value = kClamp(value, 0, 255); switch (comp) { case 0: { // Red or Grayscale rgba[0] = value; rgba[1] = value; rgba[2] = value; break; } case 1: { if ((jp2.image->color_space == OPJ_CLRSPC_GRAY) && (jp2.image->comps[comp].alpha != 0)) { // Grayscale with Alpha rgba[3] = value; } else { // Green rgba[1] = value; } break; } case 2: { // Blue rgba[2] = value; break; } case 3: { // Alpha? if (jp2.image->comps[comp].alpha != 0) { rgba[3] = value; } break; } default: { break; } } } image.setPixel(col, row, tqRgba(rgba[0], rgba[1], rgba[2], rgba[3])); alphaMask |= (255 - rgba[3]); } } if (alphaMask != 0x0) { image.setAlphaBuffer(true); } } static const char *colorspaceToString(COLOR_SPACE clr) { switch (clr) { case OPJ_CLRSPC_SRGB: { return "sRGB"; } case OPJ_CLRSPC_GRAY: { return "sRGB Grayscale"; } case OPJ_CLRSPC_SYCC: { return "YUV"; } case OPJ_CLRSPC_EYCC: { return "YCbCr"; } case OPJ_CLRSPC_CMYK: { return "CMYK"; } case OPJ_CLRSPC_UNSPECIFIED: { return "Unspecified"; } default: { return "Unknown"; } } } TDE_EXPORT void kimgio_jp2_read(TQImageIO* io) { KIMGJP2Wrapper jp2; opj_dparameters_t parameters; if (auto tqfile = dynamic_cast(io->ioDevice())) { jp2.stream = opj_stream_create_default_file_stream(tqfile->name().local8Bit().data(), OPJ_TRUE); } else { // 4096 (=4k) is a common page size. constexpr int pageSize = 4096; // Use a temporary file, since TQSocket::size() reports bytes // available to read *now*, not the file size. if (jp2.tempFile.status() != 0) { kdError(kCategory) << "Failed to create temporary file for non-TQFile IO" << endl; return; } jp2.tempFile.setAutoDelete(true); TQFile *tempFile = jp2.tempFile.file(); TQByteArray b(pageSize); TQ_LONG bytesRead; // 0 or -1 is EOF / error while ((bytesRead = io->ioDevice()->readBlock(b.data(), pageSize)) > 0) { if ((tempFile->writeBlock(b.data(), bytesRead)) == -1) { break; } } // flush everything out to disk tempFile->flush(); jp2.stream = opj_stream_create_default_file_stream(tempFile->name().local8Bit().data(), OPJ_TRUE); } if (nullptr == jp2.stream) { kdError(kCategory) << "Failed to create input stream for JP2" << endl; io->setStatus(IO_ResourceError); return; } jp2.codec = opj_create_decompress(OPJ_CODEC_JP2); if (nullptr == jp2.codec) { kdError(kCategory) << "Unable to create decompressor for JP2" << endl; io->setStatus(IO_ResourceError); return; } opj_set_error_handler(jp2.codec, kimgio_jp2_err_handler, nullptr); opj_set_info_handler(jp2.codec, kimgio_jp2_info_handler, nullptr); opj_set_warning_handler(jp2.codec, kimgio_jp2_warn_handler, nullptr); opj_set_default_decoder_parameters(¶meters); if (OPJ_FALSE == opj_setup_decoder(jp2.codec, ¶meters)) { kdError(kCategory) << "Failed to setup decoder for JP2" << endl; io->setStatus(IO_ResourceError); return; } if (OPJ_FALSE == opj_read_header(jp2.stream, jp2.codec, &jp2.image)) { kdError(kCategory) << "Failed to read JP2 header" << endl; io->setStatus(IO_ReadError); return; } if (OPJ_FALSE == opj_decode(jp2.codec, jp2.stream, jp2.image)) { kdError(kCategory) << "Failed to decode JP2 image" << endl; io->setStatus(IO_ReadError); return; } if (OPJ_FALSE == opj_end_decompress(jp2.codec, jp2.stream)) { kdError(kCategory) << "Failed to decode JP2 image ending" << endl; io->setStatus(IO_ReadError); return; } OPJ_UINT32 width = jp2.image->x1 - jp2.image->x0; OPJ_UINT32 height = jp2.image->y1 - jp2.image->y0; TQImage image(width, height, 32); switch (jp2.image->color_space) { case OPJ_CLRSPC_SRGB: case OPJ_CLRSPC_GRAY: { kimgio_jp2_read_image(image, jp2); break; } case OPJ_CLRSPC_SYCC: { if (false == color_sycc_to_rgb(jp2.image)) { kdError(kCategory) << "Could not convert YCbCr JP2 encoded image to sRGB." << endl; io->setStatus(IO_UnspecifiedError); return; } kimgio_jp2_read_image(image, jp2); break; } default: { kdError(kCategory) << "Unsupported colorspace detected: " << colorspaceToString(jp2.image->color_space) << endl; io->setStatus(IO_ReadError); return; } } io->setImage(image); io->setStatus(IO_Ok); } static void kimgio_jp2_write_handler(void *buffer, OPJ_SIZE_T buffer_size, void *user_data) { // TODO(mio): } TDE_EXPORT void kimgio_jp2_write(TQImageIO *io) { kdDebug(kCategory) << "Writing JP2 with OpenJPEG is not supported yet." << endl; } /* #define DEFAULT_RATE 0.10 #define MAXCMPTS 256 typedef struct { jas_image_t* image; int cmptlut[MAXCMPTS]; jas_image_t* altimage; } gs_t; jas_image_t* read_image( const TQImageIO* io ) { jas_stream_t* in = 0; // for TQIODevice's other than TQFile, a temp. file is used. KTempFile* tempf = 0; TQFile* qf = 0; if( ( qf = dynamic_cast( io->ioDevice() ) ) ) { // great, it's a TQFile. Let's just take the filename. in = jas_stream_fopen( TQFile::encodeName( qf->name() ), "rb" ); } else { // not a TQFile. Copy the whole data to a temp. file. tempf = new KTempFile(); if( tempf->status() != 0 ) { delete tempf; return 0; } // if tempf->setAutoDelete( true ); TQFile* out = tempf->file(); // 4096 (=4k) is a common page size. TQByteArray b( 4096 ); TQ_LONG size; // 0 or -1 is EOF / error while( ( size = io->ioDevice()->readBlock( b.data(), 4096 ) ) > 0 ) { // in case of a write error, still give the decoder a try if( ( out->writeBlock( b.data(), size ) ) == -1 ) break; } // while // flush everything out to disk out->flush(); in = jas_stream_fopen( TQFile::encodeName( tempf->name() ), "rb" ); } // else if( !in ) { delete tempf; return 0; } // if jas_image_t* image = jas_image_decode( in, -1, 0 ); jas_stream_close( in ); delete tempf; // image may be 0, but that's Ok return image; } // read_image static bool convert_colorspace( gs_t& gs ) { jas_cmprof_t *outprof = jas_cmprof_createfromclrspc( JAS_CLRSPC_SRGB ); if( !outprof ) return false; gs.altimage = jas_image_chclrspc( gs.image, outprof, JAS_CMXFORM_INTENT_PER ); if( !gs.altimage ) return false; return true; } // convert_colorspace static bool render_view( gs_t& gs, TQImage& qti ) { if((gs.cmptlut[0] = jas_image_getcmptbytype(gs.altimage, JAS_IMAGE_CT_COLOR(JAS_CLRSPC_CHANIND_RGB_R))) < 0 || (gs.cmptlut[1] = jas_image_getcmptbytype(gs.altimage, JAS_IMAGE_CT_COLOR(JAS_CLRSPC_CHANIND_RGB_G))) < 0 || (gs.cmptlut[2] = jas_image_getcmptbytype(gs.altimage, JAS_IMAGE_CT_COLOR(JAS_CLRSPC_CHANIND_RGB_B))) < 0) { return false; } // if const int* cmptlut = gs.cmptlut; int v[3]; // check that all components have the same size. const int width = jas_image_cmptwidth( gs.altimage, cmptlut[0] ); const int height = jas_image_cmptheight( gs.altimage, cmptlut[0] ); for( int i = 1; i < 3; ++i ) { if (jas_image_cmptwidth( gs.altimage, cmptlut[i] ) != width || jas_image_cmptheight( gs.altimage, cmptlut[i] ) != height) return false; } // for if( !qti.create( jas_image_width( gs.altimage ), jas_image_height( gs.altimage ), 32 ) ) return false; uint32_t* data = (uint32_t*)qti.bits(); for( int y = 0; y < height; ++y ) { for( int x = 0; x < width; ++x ) { for( int k = 0; k < 3; ++k ) { v[k] = jas_image_readcmptsample( gs.altimage, cmptlut[k], x, y ); // if the precision of the component is too small, increase // it to use the complete value range. v[k] <<= 8 - jas_image_cmptprec( gs.altimage, cmptlut[k] ); if( v[k] < 0 ) v[k] = 0; else if( v[k] > 255 ) v[k] = 255; } // for k *data++ = tqRgb( v[0], v[1], v[2] ); } // for x } // for y return true; } // render_view TDE_EXPORT void kimgio_jp2_read( TQImageIO* io ) { if( jas_init() ) return; gs_t gs; if( !(gs.image = read_image( io )) ) return; if( !convert_colorspace( gs ) ) return; TQImage image; render_view( gs, image ); if( gs.image ) jas_image_destroy( gs.image ); if( gs.altimage ) jas_image_destroy( gs.altimage ); io->setImage( image ); io->setStatus( 0 ); } // kimgio_jp2_read static jas_image_t* create_image( const TQImage& qi ) { // prepare the component parameters jas_image_cmptparm_t* cmptparms = new jas_image_cmptparm_t[ 3 ]; for ( int i = 0; i < 3; ++i ) { // x and y offset cmptparms[i].tlx = 0; cmptparms[i].tly = 0; // the resulting image will be hstep*width x vstep*height ! cmptparms[i].hstep = 1; cmptparms[i].vstep = 1; cmptparms[i].width = qi.width(); cmptparms[i].height = qi.height(); // we write everything as 24bit truecolor ATM cmptparms[i].prec = 8; cmptparms[i].sgnd = false; } jas_image_t* ji = jas_image_create( 3 /* number components *//*, cmptparms, JAS_CLRSPC_UNKNOWN ); delete[] cmptparms; // returning 0 is ok return ji; } // create_image static bool write_components( jas_image_t* ji, const TQImage& qi ) { const unsigned height = qi.height(); const unsigned width = qi.width(); jas_matrix_t* m = jas_matrix_create( height, width ); if( !m ) return false; jas_image_setclrspc( ji, JAS_CLRSPC_SRGB ); jas_image_setcmpttype( ji, 0, JAS_IMAGE_CT_RGB_R ); for( uint y = 0; y < height; ++y ) for( uint x = 0; x < width; ++x ) jas_matrix_set( m, y, x, tqRed( qi.pixel( x, y ) ) ); jas_image_writecmpt( ji, 0, 0, 0, width, height, m ); jas_image_setcmpttype( ji, 1, JAS_IMAGE_CT_RGB_G ); for( uint y = 0; y < height; ++y ) for( uint x = 0; x < width; ++x ) jas_matrix_set( m, y, x, tqGreen( qi.pixel( x, y ) ) ); jas_image_writecmpt( ji, 1, 0, 0, width, height, m ); jas_image_setcmpttype( ji, 2, JAS_IMAGE_CT_RGB_B ); for( uint y = 0; y < height; ++y ) for( uint x = 0; x < width; ++x ) jas_matrix_set( m, y, x, tqBlue( qi.pixel( x, y ) ) ); jas_image_writecmpt( ji, 2, 0, 0, width, height, m ); jas_matrix_destroy( m ); return true; } // write_components TDE_EXPORT void kimgio_jp2_write( TQImageIO* io ) { if( jas_init() ) return; // open the stream. we write directly to the file if possible, to a // temporary file otherwise. jas_stream_t* stream = 0; TQFile* qf = 0; KTempFile* ktempf = 0; if( ( qf = dynamic_cast( io->ioDevice() ) ) ) { // jas_stream_fdopen works here, but not when reading... stream = jas_stream_fdopen( dup( qf->handle() ), "w" ); } else { ktempf = new KTempFile; ktempf->setAutoDelete( true ); stream = jas_stream_fdopen( dup( ktempf->handle()), "w" ); } // else // by here, a jas_stream_t is open if( !stream ) return; jas_image_t* ji = create_image( io->image() ); if( !ji ) { delete ktempf; jas_stream_close( stream ); return; } // if if( !write_components( ji, io->image() ) ) { delete ktempf; jas_stream_close( stream ); jas_image_destroy( ji ); return; } // if // optstr: // - rate=#B => the resulting file size is about # bytes // - rate=0.0 .. 1.0 => the resulting file size is about the factor times // the uncompressed size TQString rate; TQTextStream ts( &rate, IO_WriteOnly ); ts << "rate=" << ( (io->quality() < 0) ? DEFAULT_RATE : io->quality() / 100.0F ); # if defined(JAS_VERSION_MAJOR) && (JAS_VERSION_MAJOR >= 3) const jas_image_fmtinfo_t *jp2_fmtinfo = jas_image_lookupfmtbyname("jp2"); int i = -1; if (jp2_fmtinfo) { i = jas_image_encode(ji, stream, jp2_fmtinfo->id, rate.utf8().data()); } # else int i = jp2_encode( ji, stream, rate.utf8().data() ); # endif jas_image_destroy( ji ); jas_stream_close( stream ); if( i != 0 ) { delete ktempf; return; } if( ktempf ) { // We've written to a tempfile. Copy the data to the final destination. TQFile* in = ktempf->file(); TQByteArray b( 4096 ); TQ_LONG size; // seek to the beginning of the file. if( !in->at( 0 ) ) { delete ktempf; return; } // 0 or -1 is EOF / error while( ( size = in->readBlock( b.data(), 4096 ) ) > 0 ) { if( ( io->ioDevice()->writeBlock( b.data(), size ) ) == -1 ) { delete ktempf; return; } // if } // while io->ioDevice()->flush(); delete ktempf; // see if we've left the while loop due to an error. if( size == -1 ) return; } // if // everything went fine io->setStatus( IO_Ok ); } // kimgio_jp2_write */ #endif // HAVE_OPENJPEG