jp2.cpp
00001 // This library is distributed under the conditions of the GNU LGPL. 00002 #include "config.h" 00003 00004 #ifdef HAVE_JASPER 00005 00006 #include "jp2.h" 00007 00008 #if !defined(__STDC_LIMIT_MACROS) 00009 #define __STDC_LIMIT_MACROS 00010 #endif 00011 00012 #ifdef HAVE_SYS_TYPES_H 00013 #include <sys/types.h> 00014 #endif 00015 #ifdef HAVE_STDINT_H 00016 #include <stdint.h> 00017 #endif 00018 #include <tdetempfile.h> 00019 #include <tqcolor.h> 00020 #include <tqcstring.h> 00021 #include <tqfile.h> 00022 #include <tqimage.h> 00023 00024 // dirty, but avoids a warning because jasper.h includes jas_config.h. 00025 #undef PACKAGE 00026 #undef VERSION 00027 #include <jasper/jasper.h> 00028 00029 // code taken in parts from JasPer's jiv.c 00030 00031 #define DEFAULT_RATE 0.10 00032 #define MAXCMPTS 256 00033 00034 00035 typedef struct { 00036 jas_image_t* image; 00037 00038 int cmptlut[MAXCMPTS]; 00039 00040 jas_image_t* altimage; 00041 } gs_t; 00042 00043 00044 jas_image_t* 00045 read_image( const TQImageIO* io ) 00046 { 00047 jas_stream_t* in = 0; 00048 // for QIODevice's other than TQFile, a temp. file is used. 00049 KTempFile* tempf = 0; 00050 00051 TQFile* qf = 0; 00052 if( ( qf = dynamic_cast<TQFile*>( io->ioDevice() ) ) ) { 00053 // great, it's a TQFile. Let's just take the filename. 00054 in = jas_stream_fopen( TQFile::encodeName( qf->name() ), "rb" ); 00055 } else { 00056 // not a TQFile. Copy the whole data to a temp. file. 00057 tempf = new KTempFile(); 00058 if( tempf->status() != 0 ) { 00059 delete tempf; 00060 return 0; 00061 } // if 00062 tempf->setAutoDelete( true ); 00063 TQFile* out = tempf->file(); 00064 // 4096 (=4k) is a common page size. 00065 TQByteArray b( 4096 ); 00066 TQ_LONG size; 00067 // 0 or -1 is EOF / error 00068 while( ( size = io->ioDevice()->readBlock( b.data(), 4096 ) ) > 0 ) { 00069 // in case of a write error, still give the decoder a try 00070 if( ( out->writeBlock( b.data(), size ) ) == -1 ) break; 00071 } // while 00072 // flush everything out to disk 00073 out->flush(); 00074 00075 in = jas_stream_fopen( TQFile::encodeName( tempf->name() ), "rb" ); 00076 } // else 00077 if( !in ) { 00078 delete tempf; 00079 return 0; 00080 } // if 00081 00082 jas_image_t* image = jas_image_decode( in, -1, 0 ); 00083 jas_stream_close( in ); 00084 delete tempf; 00085 00086 // image may be 0, but that's Ok 00087 return image; 00088 } // read_image 00089 00090 static bool 00091 convert_colorspace( gs_t& gs ) 00092 { 00093 jas_cmprof_t *outprof = jas_cmprof_createfromclrspc( JAS_CLRSPC_SRGB ); 00094 if( !outprof ) return false; 00095 00096 gs.altimage = jas_image_chclrspc( gs.image, outprof, 00097 JAS_CMXFORM_INTENT_PER ); 00098 if( !gs.altimage ) return false; 00099 00100 return true; 00101 } // convert_colorspace 00102 00103 static bool 00104 render_view( gs_t& gs, TQImage& qti ) 00105 { 00106 if((gs.cmptlut[0] = jas_image_getcmptbytype(gs.altimage, 00107 JAS_IMAGE_CT_COLOR(JAS_CLRSPC_CHANIND_RGB_R))) < 0 || 00108 (gs.cmptlut[1] = jas_image_getcmptbytype(gs.altimage, 00109 JAS_IMAGE_CT_COLOR(JAS_CLRSPC_CHANIND_RGB_G))) < 0 || 00110 (gs.cmptlut[2] = jas_image_getcmptbytype(gs.altimage, 00111 JAS_IMAGE_CT_COLOR(JAS_CLRSPC_CHANIND_RGB_B))) < 0) { 00112 return false; 00113 } // if 00114 00115 const int* cmptlut = gs.cmptlut; 00116 int v[3]; 00117 00118 // check that all components have the same size. 00119 const int width = jas_image_cmptwidth( gs.altimage, cmptlut[0] ); 00120 const int height = jas_image_cmptheight( gs.altimage, cmptlut[0] ); 00121 for( int i = 1; i < 3; ++i ) { 00122 if (jas_image_cmptwidth( gs.altimage, cmptlut[i] ) != width || 00123 jas_image_cmptheight( gs.altimage, cmptlut[i] ) != height) 00124 return false; 00125 } // for 00126 00127 if( !qti.create( jas_image_width( gs.altimage ), 00128 jas_image_height( gs.altimage ), 32 ) ) 00129 return false; 00130 00131 uint32_t* data = (uint32_t*)qti.bits(); 00132 00133 for( int y = 0; y < height; ++y ) { 00134 for( int x = 0; x < width; ++x ) { 00135 for( int k = 0; k < 3; ++k ) { 00136 v[k] = jas_image_readcmptsample( gs.altimage, cmptlut[k], x, y ); 00137 // if the precision of the component is too small, increase 00138 // it to use the complete value range. 00139 v[k] <<= 8 - jas_image_cmptprec( gs.altimage, cmptlut[k] ); 00140 00141 if( v[k] < 0 ) v[k] = 0; 00142 else if( v[k] > 255 ) v[k] = 255; 00143 } // for k 00144 00145 *data++ = tqRgb( v[0], v[1], v[2] ); 00146 } // for x 00147 } // for y 00148 return true; 00149 } // render_view 00150 00151 00152 KDE_EXPORT void 00153 kimgio_jp2_read( TQImageIO* io ) 00154 { 00155 if( jas_init() ) return; 00156 00157 gs_t gs; 00158 if( !(gs.image = read_image( io )) ) return; 00159 00160 if( !convert_colorspace( gs ) ) return; 00161 00162 TQImage image; 00163 render_view( gs, image ); 00164 00165 if( gs.image ) jas_image_destroy( gs.image ); 00166 if( gs.altimage ) jas_image_destroy( gs.altimage ); 00167 00168 io->setImage( image ); 00169 io->setStatus( 0 ); 00170 } // kimgio_jp2_read 00171 00172 00173 static jas_image_t* 00174 create_image( const TQImage& qi ) 00175 { 00176 // prepare the component parameters 00177 jas_image_cmptparm_t* cmptparms = new jas_image_cmptparm_t[ 3 ]; 00178 00179 for ( int i = 0; i < 3; ++i ) { 00180 // x and y offset 00181 cmptparms[i].tlx = 0; 00182 cmptparms[i].tly = 0; 00183 00184 // the resulting image will be hstep*width x vstep*height ! 00185 cmptparms[i].hstep = 1; 00186 cmptparms[i].vstep = 1; 00187 cmptparms[i].width = qi.width(); 00188 cmptparms[i].height = qi.height(); 00189 00190 // we write everything as 24bit truecolor ATM 00191 cmptparms[i].prec = 8; 00192 cmptparms[i].sgnd = false; 00193 } 00194 00195 jas_image_t* ji = jas_image_create( 3 /* number components */, cmptparms, JAS_CLRSPC_UNKNOWN ); 00196 delete[] cmptparms; 00197 00198 // returning 0 is ok 00199 return ji; 00200 } // create_image 00201 00202 00203 static bool 00204 write_components( jas_image_t* ji, const TQImage& qi ) 00205 { 00206 const unsigned height = qi.height(); 00207 const unsigned width = qi.width(); 00208 00209 jas_matrix_t* m = jas_matrix_create( height, width ); 00210 if( !m ) return false; 00211 00212 jas_image_setclrspc( ji, JAS_CLRSPC_SRGB ); 00213 00214 jas_image_setcmpttype( ji, 0, JAS_IMAGE_CT_RGB_R ); 00215 for( uint y = 0; y < height; ++y ) 00216 for( uint x = 0; x < width; ++x ) 00217 jas_matrix_set( m, y, x, tqRed( qi.pixel( x, y ) ) ); 00218 jas_image_writecmpt( ji, 0, 0, 0, width, height, m ); 00219 00220 jas_image_setcmpttype( ji, 1, JAS_IMAGE_CT_RGB_G ); 00221 for( uint y = 0; y < height; ++y ) 00222 for( uint x = 0; x < width; ++x ) 00223 jas_matrix_set( m, y, x, tqGreen( qi.pixel( x, y ) ) ); 00224 jas_image_writecmpt( ji, 1, 0, 0, width, height, m ); 00225 00226 jas_image_setcmpttype( ji, 2, JAS_IMAGE_CT_RGB_B ); 00227 for( uint y = 0; y < height; ++y ) 00228 for( uint x = 0; x < width; ++x ) 00229 jas_matrix_set( m, y, x, tqBlue( qi.pixel( x, y ) ) ); 00230 jas_image_writecmpt( ji, 2, 0, 0, width, height, m ); 00231 jas_matrix_destroy( m ); 00232 00233 return true; 00234 } // write_components 00235 00236 KDE_EXPORT void 00237 kimgio_jp2_write( TQImageIO* io ) 00238 { 00239 if( jas_init() ) return; 00240 00241 // open the stream. we write directly to the file if possible, to a 00242 // temporary file otherwise. 00243 jas_stream_t* stream = 0; 00244 00245 TQFile* qf = 0; 00246 KTempFile* ktempf = 0; 00247 if( ( qf = dynamic_cast<TQFile*>( io->ioDevice() ) ) ) { 00248 // jas_stream_fdopen works here, but not when reading... 00249 stream = jas_stream_fdopen( dup( qf->handle() ), "w" ); 00250 } else { 00251 ktempf = new KTempFile; 00252 ktempf->setAutoDelete( true ); 00253 stream = jas_stream_fdopen( dup( ktempf->handle()), "w" ); 00254 } // else 00255 00256 00257 // by here, a jas_stream_t is open 00258 if( !stream ) return; 00259 00260 jas_image_t* ji = create_image( io->image() ); 00261 if( !ji ) { 00262 delete ktempf; 00263 jas_stream_close( stream ); 00264 return; 00265 } // if 00266 00267 if( !write_components( ji, io->image() ) ) { 00268 delete ktempf; 00269 jas_stream_close( stream ); 00270 jas_image_destroy( ji ); 00271 return; 00272 } // if 00273 00274 // optstr: 00275 // - rate=#B => the resulting file size is about # bytes 00276 // - rate=0.0 .. 1.0 => the resulting file size is about the factor times 00277 // the uncompressed size 00278 TQString rate; 00279 TQTextStream ts( &rate, IO_WriteOnly ); 00280 ts << "rate=" 00281 << ( (io->quality() < 0) ? DEFAULT_RATE : io->quality() / 100.0F ); 00282 int i = jp2_encode( ji, stream, rate.utf8().data() ); 00283 00284 jas_image_destroy( ji ); 00285 jas_stream_close( stream ); 00286 00287 if( i != 0 ) { delete ktempf; return; } 00288 00289 if( ktempf ) { 00290 // We've written to a tempfile. Copy the data to the final destination. 00291 TQFile* in = ktempf->file(); 00292 00293 TQByteArray b( 4096 ); 00294 TQ_LONG size; 00295 00296 // seek to the beginning of the file. 00297 if( !in->at( 0 ) ) { delete ktempf; return; } 00298 00299 // 0 or -1 is EOF / error 00300 while( ( size = in->readBlock( b.data(), 4096 ) ) > 0 ) { 00301 if( ( io->ioDevice()->writeBlock( b.data(), size ) ) == -1 ) { 00302 delete ktempf; 00303 return; 00304 } // if 00305 } // while 00306 io->ioDevice()->flush(); 00307 delete ktempf; 00308 00309 // see if we've left the while loop due to an error. 00310 if( size == -1 ) return; 00311 } // if 00312 00313 00314 // everything went fine 00315 io->setStatus( IO_Ok ); 00316 } // kimgio_jp2_write 00317 00318 #endif // HAVE_JASPER 00319