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->pos() + 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 // use sprintf for locale-aware string 00359 char rateBuffer[16]; 00360 sprintf(rateBuffer, "rate=%.2g\n", (quality < 0) ? DEFAULT_RATE : quality / 100.0); 00361 int i = jp2_encode( ji, stream, rateBuffer); 00362 00363 jas_image_destroy( ji ); 00364 jas_stream_close( stream ); 00365 00366 if( i != 0 ) return false; 00367 00368 return true; 00369 } 00370 00371 JP2Handler::JP2Handler() 00372 { 00373 quality = 75; 00374 jas_init(); 00375 } 00376 00377 JP2Handler::~JP2Handler() 00378 { 00379 jas_cleanup(); 00380 } 00381 00382 bool JP2Handler::canRead() const 00383 { 00384 if (canRead(device())) { 00385 setFormat("jp2"); 00386 return true; 00387 } 00388 return false; 00389 } 00390 00391 bool JP2Handler::canRead(QIODevice *device) 00392 { 00393 if (!device) { 00394 return false; 00395 } 00396 return device->peek(6) == QByteArray("\x00\x00\x00\x0C\x6A\x50", 6); 00397 } 00398 00399 bool JP2Handler::read(QImage *image) 00400 { 00401 if (!canRead()) return false; 00402 00403 gs_t gs; 00404 if( !(gs.image = read_image( device() )) ) return false; 00405 00406 if( !convert_colorspace( gs ) ) return false; 00407 00408 render_view( gs, image ); 00409 00410 if( gs.image ) jas_image_destroy( gs.image ); 00411 if( gs.altimage ) jas_image_destroy( gs.altimage ); 00412 return true; 00413 00414 } 00415 00416 bool JP2Handler::write(const QImage &image) 00417 { 00418 return write_image(image, device(),quality); 00419 } 00420 00421 bool JP2Handler::supportsOption(ImageOption option) const 00422 { 00423 return option == Quality; 00424 } 00425 00426 QVariant JP2Handler::option(ImageOption option) const 00427 { 00428 if (option == Quality) 00429 return quality; 00430 return QVariant(); 00431 } 00432 00433 void JP2Handler::setOption(ImageOption option, const QVariant &value) 00434 { 00435 if (option == Quality) 00436 quality = qBound(-1, value.toInt(), 100); 00437 } 00438 00439 QByteArray JP2Handler::name() const 00440 { 00441 return "jp2"; 00442 } 00443 00444 class JP2Plugin : public QImageIOPlugin 00445 { 00446 public: 00447 QStringList keys() const; 00448 Capabilities capabilities(QIODevice *device, const QByteArray &format) const; 00449 QImageIOHandler *create(QIODevice *device, const QByteArray &format = QByteArray()) const; 00450 }; 00451 00452 QStringList JP2Plugin::keys() const 00453 { 00454 return QStringList() << "jp2"; 00455 } 00456 00457 QImageIOPlugin::Capabilities JP2Plugin::capabilities(QIODevice *device, const QByteArray &format) const 00458 { 00459 if (format == "jp2") 00460 return Capabilities(CanRead | CanWrite); 00461 if (!format.isEmpty()) 00462 return 0; 00463 if (!device->isOpen()) 00464 return 0; 00465 00466 Capabilities cap; 00467 if (device->isReadable() && JP2Handler::canRead(device)) 00468 cap |= CanRead; 00469 if (device->isWritable()) 00470 cap |= CanWrite; 00471 return cap; 00472 } 00473 00474 QImageIOHandler *JP2Plugin::create(QIODevice *device, const QByteArray &format) const 00475 { 00476 QImageIOHandler *handler = new JP2Handler; 00477 handler->setDevice(device); 00478 handler->setFormat(format); 00479 return handler; 00480 } 00481 00482 Q_EXPORT_STATIC_PLUGIN(JP2Plugin) 00483 Q_EXPORT_PLUGIN2(jp2, JP2Plugin) 00484 00485
KDE 4.7 API Reference