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