KImgIO
jp2.cpp
Go to the documentation of this file.
00001 00008 #include "jp2.h" 00009 00010 #include <config.h> 00011 00012 #ifdef HAVE_SYS_TYPES_H 00013 #include <sys/types.h> 00014 #endif 00015 00016 #ifdef HAVE_STDINT_H 00017 #include <stdint.h> 00018 #endif 00019 00020 #include <QImage> 00021 #include <QVariant> 00022 #include <QTextStream> 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 /************************* JasPer QIODevice stream ***********************/ 00036 00037 //unfortunately this is declared as static in JasPer libraries 00038 static jas_stream_t *jas_stream_create() 00039 { 00040 jas_stream_t *stream; 00041 00042 if (!(stream = (jas_stream_t*)jas_malloc(sizeof(jas_stream_t)))) { 00043 return 0; 00044 } 00045 stream->openmode_ = 0; 00046 stream->bufmode_ = 0; 00047 stream->flags_ = 0; 00048 stream->bufbase_ = 0; 00049 stream->bufstart_ = 0; 00050 stream->bufsize_ = 0; 00051 stream->ptr_ = 0; 00052 stream->cnt_ = 0; 00053 stream->ops_ = 0; 00054 stream->obj_ = 0; 00055 stream->rwcnt_ = 0; 00056 stream->rwlimit_ = -1; 00057 00058 return stream; 00059 } 00060 00061 //unfortunately this is declared as static in JasPer libraries 00062 static void jas_stream_initbuf(jas_stream_t *stream, int bufmode, char *buf, 00063 int bufsize) 00064 { 00065 /* If this function is being called, the buffer should not have been 00066 initialized yet. */ 00067 assert(!stream->bufbase_); 00068 00069 if (bufmode != JAS_STREAM_UNBUF) { 00070 /* The full- or line-buffered mode is being employed. */ 00071 if (!buf) { 00072 /* The caller has not specified a buffer to employ, so allocate 00073 one. */ 00074 if ((stream->bufbase_ = (unsigned char*)jas_malloc(JAS_STREAM_BUFSIZE + 00075 JAS_STREAM_MAXPUTBACK))) { 00076 stream->bufmode_ |= JAS_STREAM_FREEBUF; 00077 stream->bufsize_ = JAS_STREAM_BUFSIZE; 00078 } else { 00079 /* The buffer allocation has failed. Resort to unbuffered 00080 operation. */ 00081 stream->bufbase_ = stream->tinybuf_; 00082 stream->bufsize_ = 1; 00083 } 00084 } else { 00085 /* The caller has specified a buffer to employ. */ 00086 /* The buffer must be large enough to accommodate maximum 00087 putback. */ 00088 assert(bufsize > JAS_STREAM_MAXPUTBACK); 00089 stream->bufbase_ = JAS_CAST(uchar *, buf); 00090 stream->bufsize_ = bufsize - JAS_STREAM_MAXPUTBACK; 00091 } 00092 } else { 00093 /* The unbuffered mode is being employed. */ 00094 /* A buffer should not have been supplied by the caller. */ 00095 assert(!buf); 00096 /* Use a trivial one-character buffer. */ 00097 stream->bufbase_ = stream->tinybuf_; 00098 stream->bufsize_ = 1; 00099 } 00100 stream->bufstart_ = &stream->bufbase_[JAS_STREAM_MAXPUTBACK]; 00101 stream->ptr_ = stream->bufstart_; 00102 stream->cnt_ = 0; 00103 stream->bufmode_ |= bufmode & JAS_STREAM_BUFMODEMASK; 00104 } 00105 00106 static int qiodevice_read(jas_stream_obj_t *obj, char *buf, int cnt) 00107 { 00108 QIODevice *io = (QIODevice*) obj; 00109 return io->read(buf, cnt); 00110 } 00111 00112 static int qiodevice_write(jas_stream_obj_t *obj, char *buf, int cnt) 00113 { 00114 QIODevice *io = (QIODevice*) obj; 00115 return io->write(buf, cnt); 00116 } 00117 00118 static long qiodevice_seek(jas_stream_obj_t *obj, long offset, int origin) 00119 { 00120 QIODevice *io = (QIODevice*) obj; 00121 long newpos; 00122 00123 switch (origin) { 00124 case SEEK_SET: 00125 newpos = offset; 00126 break; 00127 case SEEK_END: 00128 newpos = io->size() - offset; 00129 break; 00130 case SEEK_CUR: 00131 newpos = io->size() + offset; 00132 break; 00133 default: 00134 return -1; 00135 } 00136 if (newpos < 0) { 00137 return -1; 00138 } 00139 if ( io->seek(newpos) ) 00140 return newpos; 00141 else 00142 return -1; 00143 } 00144 00145 static int qiodevice_close(jas_stream_obj_t *) 00146 { 00147 return 0; 00148 } 00149 00150 static jas_stream_ops_t jas_stream_qiodeviceops = { 00151 qiodevice_read, 00152 qiodevice_write, 00153 qiodevice_seek, 00154 qiodevice_close 00155 }; 00156 00157 static jas_stream_t *jas_stream_qiodevice(QIODevice *iodevice) 00158 { 00159 jas_stream_t *stream; 00160 00161 if ( !iodevice ) return 0; 00162 if (!(stream = jas_stream_create())) { 00163 return 0; 00164 } 00165 00166 /* A stream associated with a memory buffer is always opened 00167 for both reading and writing in binary mode. */ 00168 stream->openmode_ = JAS_STREAM_READ | JAS_STREAM_WRITE | JAS_STREAM_BINARY; 00169 00170 jas_stream_initbuf(stream, JAS_STREAM_FULLBUF, 0, 0); 00171 00172 /* Select the operations for a memory stream. */ 00173 stream->obj_ = (void *)iodevice; 00174 stream->ops_ = &jas_stream_qiodeviceops; 00175 00176 return stream; 00177 } 00178 00179 /************************ End of JasPer QIODevice stream ****************/ 00180 00181 typedef struct { 00182 jas_image_t* image; 00183 00184 int cmptlut[MAXCMPTS]; 00185 00186 jas_image_t* altimage; 00187 } gs_t; 00188 00189 00190 static jas_image_t* 00191 read_image( QIODevice* io ) 00192 { 00193 jas_stream_t* in = 0; 00194 00195 in = jas_stream_qiodevice( io ); 00196 00197 if( !in ) return 0; 00198 00199 jas_image_t* image = jas_image_decode( in, -1, 0 ); 00200 jas_stream_close( in ); 00201 00202 // image may be 0, but that's Ok 00203 return image; 00204 } // read_image 00205 00206 static bool 00207 convert_colorspace( gs_t& gs ) 00208 { 00209 jas_cmprof_t *outprof = jas_cmprof_createfromclrspc( JAS_CLRSPC_SRGB ); 00210 if( !outprof ) return false; 00211 00212 gs.altimage = jas_image_chclrspc( gs.image, outprof, 00213 JAS_CMXFORM_INTENT_PER ); 00214 if( !gs.altimage ) return false; 00215 00216 return true; 00217 } // convert_colorspace 00218 00219 static bool 00220 render_view( gs_t& gs, QImage* outImage ) 00221 { 00222 if ( !gs.altimage ) return false; 00223 QImage qti; 00224 if((gs.cmptlut[0] = jas_image_getcmptbytype(gs.altimage, 00225 JAS_IMAGE_CT_COLOR(JAS_CLRSPC_CHANIND_RGB_R))) < 0 || 00226 (gs.cmptlut[1] = jas_image_getcmptbytype(gs.altimage, 00227 JAS_IMAGE_CT_COLOR(JAS_CLRSPC_CHANIND_RGB_G))) < 0 || 00228 (gs.cmptlut[2] = jas_image_getcmptbytype(gs.altimage, 00229 JAS_IMAGE_CT_COLOR(JAS_CLRSPC_CHANIND_RGB_B))) < 0) { 00230 return false; 00231 } // if 00232 00233 const int* cmptlut = gs.cmptlut; 00234 int v[3]; 00235 00236 // check that all components have the same size. 00237 const int width = jas_image_cmptwidth( gs.altimage, cmptlut[0] ); 00238 const int height = jas_image_cmptheight( gs.altimage, cmptlut[0] ); 00239 for( int i = 1; i < 3; ++i ) { 00240 if (jas_image_cmptwidth( gs.altimage, cmptlut[i] ) != width || 00241 jas_image_cmptheight( gs.altimage, cmptlut[i] ) != height) 00242 return false; 00243 } // for 00244 00245 qti = QImage( jas_image_width( gs.altimage ), jas_image_height( gs.altimage ), 00246 QImage::Format_RGB32 ); 00247 00248 uint32_t* data = (uint32_t*)qti.bits(); 00249 00250 for( int y = 0; y < height; ++y ) { 00251 for( int x = 0; x < width; ++x ) { 00252 for( int k = 0; k < 3; ++k ) { 00253 v[k] = jas_image_readcmptsample( gs.altimage, cmptlut[k], x, y ); 00254 // if the precision of the component is too small, increase 00255 // it to use the complete value range. 00256 v[k] <<= 8 - jas_image_cmptprec( gs.altimage, cmptlut[k] ); 00257 00258 if( v[k] < 0 ) v[k] = 0; 00259 else if( v[k] > 255 ) v[k] = 255; 00260 } // for k 00261 00262 *data++ = qRgb( v[0], v[1], v[2] ); 00263 } // for x 00264 } // for y 00265 *outImage = qti; 00266 return true; 00267 } // render_view 00268 00269 00270 static jas_image_t* 00271 create_image( const QImage& qi ) 00272 { 00273 // prepare the component parameters 00274 jas_image_cmptparm_t* cmptparms = new jas_image_cmptparm_t[ 3 ]; 00275 00276 for ( int i = 0; i < 3; ++i ) { 00277 // x and y offset 00278 cmptparms[i].tlx = 0; 00279 cmptparms[i].tly = 0; 00280 00281 // the resulting image will be hstep*width x vstep*height ! 00282 cmptparms[i].hstep = 1; 00283 cmptparms[i].vstep = 1; 00284 cmptparms[i].width = qi.width(); 00285 cmptparms[i].height = qi.height(); 00286 00287 // we write everything as 24bit truecolor ATM 00288 cmptparms[i].prec = 8; 00289 cmptparms[i].sgnd = false; 00290 } 00291 00292 jas_image_t* ji = jas_image_create( 3 /* number components */, cmptparms, JAS_CLRSPC_UNKNOWN ); 00293 delete[] cmptparms; 00294 00295 // returning 0 is ok 00296 return ji; 00297 } // create_image 00298 00299 00300 static bool 00301 write_components( jas_image_t* ji, const QImage& qi ) 00302 { 00303 const unsigned height = qi.height(); 00304 const unsigned width = qi.width(); 00305 00306 jas_matrix_t* m = jas_matrix_create( height, width ); 00307 if( !m ) return false; 00308 00309 jas_image_setclrspc( ji, JAS_CLRSPC_SRGB ); 00310 00311 jas_image_setcmpttype( ji, 0, JAS_IMAGE_CT_RGB_R ); 00312 for( uint y = 0; y < height; ++y ) 00313 for( uint x = 0; x < width; ++x ) 00314 jas_matrix_set( m, y, x, qRed( qi.pixel( x, y ) ) ); 00315 jas_image_writecmpt( ji, 0, 0, 0, width, height, m ); 00316 00317 jas_image_setcmpttype( ji, 1, JAS_IMAGE_CT_RGB_G ); 00318 for( uint y = 0; y < height; ++y ) 00319 for( uint x = 0; x < width; ++x ) 00320 jas_matrix_set( m, y, x, qGreen( qi.pixel( x, y ) ) ); 00321 jas_image_writecmpt( ji, 1, 0, 0, width, height, m ); 00322 00323 jas_image_setcmpttype( ji, 2, JAS_IMAGE_CT_RGB_B ); 00324 for( uint y = 0; y < height; ++y ) 00325 for( uint x = 0; x < width; ++x ) 00326 jas_matrix_set( m, y, x, qBlue( qi.pixel( x, y ) ) ); 00327 jas_image_writecmpt( ji, 2, 0, 0, width, height, m ); 00328 jas_matrix_destroy( m ); 00329 00330 return true; 00331 } // write_components 00332 00333 static bool 00334 write_image( const QImage &image, QIODevice* io, int quality ) 00335 { 00336 jas_stream_t* stream = 0; 00337 stream = jas_stream_qiodevice( io ); 00338 00339 // by here, a jas_stream_t is open 00340 if( !stream ) return false; 00341 00342 jas_image_t* ji = create_image( image ); 00343 if( !ji ) { 00344 jas_stream_close( stream ); 00345 return false; 00346 } // if 00347 00348 if( !write_components( ji, image ) ) { 00349 jas_stream_close( stream ); 00350 jas_image_destroy( ji ); 00351 return false; 00352 } // if 00353 00354 // optstr: 00355 // - rate=#B => the resulting file size is about # bytes 00356 // - rate=0.0 .. 1.0 => the resulting file size is about the factor times 00357 // the uncompressed size 00358 QString rate; 00359 QTextStream ts( &rate, QIODevice::WriteOnly ); 00360 ts << "rate=" 00361 << ( (quality < 0) ? DEFAULT_RATE : quality / 100.0F ); 00362 int i = jp2_encode( ji, stream, rate.toUtf8().data() ); 00363 00364 jas_image_destroy( ji ); 00365 jas_stream_close( stream ); 00366 00367 if( i != 0 ) return false; 00368 00369 return true; 00370 } 00371 00372 JP2Handler::JP2Handler() 00373 { 00374 quality = 75; 00375 jas_init(); 00376 } 00377 00378 JP2Handler::~JP2Handler() 00379 { 00380 jas_cleanup(); 00381 } 00382 00383 bool JP2Handler::canRead() const 00384 { 00385 if (canRead(device())) { 00386 setFormat("jp2"); 00387 return true; 00388 } 00389 return false; 00390 } 00391 00392 bool JP2Handler::canRead(QIODevice *device) 00393 { 00394 if (!device) { 00395 return false; 00396 } 00397 return device->peek(6) == QByteArray("\x00\x00\x00\x0C\x6A\x50", 6); 00398 } 00399 00400 bool JP2Handler::read(QImage *image) 00401 { 00402 if (!canRead()) return false; 00403 00404 gs_t gs; 00405 if( !(gs.image = read_image( device() )) ) return false; 00406 00407 if( !convert_colorspace( gs ) ) return false; 00408 00409 render_view( gs, image ); 00410 00411 if( gs.image ) jas_image_destroy( gs.image ); 00412 if( gs.altimage ) jas_image_destroy( gs.altimage ); 00413 return true; 00414 00415 } 00416 00417 bool JP2Handler::write(const QImage &image) 00418 { 00419 return write_image(image, device(),quality); 00420 } 00421 00422 bool JP2Handler::supportsOption(ImageOption option) const 00423 { 00424 return option == Quality; 00425 } 00426 00427 QVariant JP2Handler::option(ImageOption option) const 00428 { 00429 if (option == Quality) 00430 return quality; 00431 return QVariant(); 00432 } 00433 00434 QByteArray JP2Handler::name() const 00435 { 00436 return "jp2"; 00437 } 00438 00439 class JP2Plugin : public QImageIOPlugin 00440 { 00441 public: 00442 QStringList keys() const; 00443 Capabilities capabilities(QIODevice *device, const QByteArray &format) const; 00444 QImageIOHandler *create(QIODevice *device, const QByteArray &format = QByteArray()) const; 00445 }; 00446 00447 QStringList JP2Plugin::keys() const 00448 { 00449 return QStringList() << "jp2"; 00450 } 00451 00452 QImageIOPlugin::Capabilities JP2Plugin::capabilities(QIODevice *device, const QByteArray &format) const 00453 { 00454 if (format == "jp2") 00455 return Capabilities(CanRead | CanWrite); 00456 if (!format.isEmpty()) 00457 return 0; 00458 if (!device->isOpen()) 00459 return 0; 00460 00461 Capabilities cap; 00462 if (device->isReadable() && JP2Handler::canRead(device)) 00463 cap |= CanRead; 00464 if (device->isWritable()) 00465 cap |= CanWrite; 00466 return cap; 00467 } 00468 00469 QImageIOHandler *JP2Plugin::create(QIODevice *device, const QByteArray &format) const 00470 { 00471 QImageIOHandler *handler = new JP2Handler; 00472 handler->setDevice(device); 00473 handler->setFormat(format); 00474 return handler; 00475 } 00476 00477 Q_EXPORT_STATIC_PLUGIN(JP2Plugin) 00478 Q_EXPORT_PLUGIN2(jp2, JP2Plugin) 00479 00480
KDE 4.6 API Reference