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 jas_matrix_t *cmptmatrix[3]; 00246 jas_seqent_t *buf[3]; 00247 int prec[3]; 00248 00249 for (int k = 0; k < 3; ++k ) { 00250 prec[k] = jas_image_cmptprec(gs.altimage, cmptlut[k]); 00251 if (!(cmptmatrix[k] = jas_matrix_create(1, width))) { 00252 return false; 00253 } 00254 } 00255 00256 qti = QImage( jas_image_width( gs.altimage ), jas_image_height( gs.altimage ), 00257 QImage::Format_RGB32 ); 00258 if (qti.isNull()) { 00259 return false; 00260 } 00261 uint32_t* data = (uint32_t*)qti.bits(); 00262 00263 for( int y = 0; y < height; ++y ) { 00264 for( int k = 0; k < 3; ++k ) { 00265 if (jas_image_readcmpt(gs.altimage, cmptlut[k], 0, y, width, 1, cmptmatrix[k])) { 00266 return false; 00267 } 00268 buf[k] = jas_matrix_getref(cmptmatrix[k], 0, 0); 00269 } 00270 for( int x = 0; x < width; ++x ) { 00271 for( int k = 0; k < 3; ++k ) { 00272 v[k] = *buf[k]; 00273 // if the precision of the component is too small, increase 00274 // it to use the complete value range. 00275 v[k] <<= 8 - prec[k]; 00276 00277 if( v[k] < 0 ) v[k] = 0; 00278 else if( v[k] > 255 ) v[k] = 255; 00279 ++buf[k]; 00280 } // for k 00281 00282 *data++ = qRgb( v[0], v[1], v[2] ); 00283 } // for x 00284 } // for y 00285 00286 for (int k = 0; k < 3; ++k ) { 00287 if (cmptmatrix[k]) { 00288 jas_matrix_destroy(cmptmatrix[k]); 00289 } 00290 } 00291 00292 *outImage = qti; 00293 return true; 00294 } // render_view 00295 00296 00297 static jas_image_t* 00298 create_image( const QImage& qi ) 00299 { 00300 // prepare the component parameters 00301 jas_image_cmptparm_t* cmptparms = new jas_image_cmptparm_t[ 3 ]; 00302 00303 for ( int i = 0; i < 3; ++i ) { 00304 // x and y offset 00305 cmptparms[i].tlx = 0; 00306 cmptparms[i].tly = 0; 00307 00308 // the resulting image will be hstep*width x vstep*height ! 00309 cmptparms[i].hstep = 1; 00310 cmptparms[i].vstep = 1; 00311 cmptparms[i].width = qi.width(); 00312 cmptparms[i].height = qi.height(); 00313 00314 // we write everything as 24bit truecolor ATM 00315 cmptparms[i].prec = 8; 00316 cmptparms[i].sgnd = false; 00317 } 00318 00319 jas_image_t* ji = jas_image_create( 3 /* number components */, cmptparms, JAS_CLRSPC_UNKNOWN ); 00320 delete[] cmptparms; 00321 00322 // returning 0 is ok 00323 return ji; 00324 } // create_image 00325 00326 00327 static bool 00328 write_components( jas_image_t* ji, const QImage& qi ) 00329 { 00330 const unsigned height = qi.height(); 00331 const unsigned width = qi.width(); 00332 00333 jas_matrix_t* m = jas_matrix_create( height, width ); 00334 if( !m ) return false; 00335 00336 jas_image_setclrspc( ji, JAS_CLRSPC_SRGB ); 00337 00338 jas_image_setcmpttype( ji, 0, JAS_IMAGE_CT_RGB_R ); 00339 for( uint y = 0; y < height; ++y ) 00340 for( uint x = 0; x < width; ++x ) 00341 jas_matrix_set( m, y, x, qRed( qi.pixel( x, y ) ) ); 00342 jas_image_writecmpt( ji, 0, 0, 0, width, height, m ); 00343 00344 jas_image_setcmpttype( ji, 1, JAS_IMAGE_CT_RGB_G ); 00345 for( uint y = 0; y < height; ++y ) 00346 for( uint x = 0; x < width; ++x ) 00347 jas_matrix_set( m, y, x, qGreen( qi.pixel( x, y ) ) ); 00348 jas_image_writecmpt( ji, 1, 0, 0, width, height, m ); 00349 00350 jas_image_setcmpttype( ji, 2, JAS_IMAGE_CT_RGB_B ); 00351 for( uint y = 0; y < height; ++y ) 00352 for( uint x = 0; x < width; ++x ) 00353 jas_matrix_set( m, y, x, qBlue( qi.pixel( x, y ) ) ); 00354 jas_image_writecmpt( ji, 2, 0, 0, width, height, m ); 00355 jas_matrix_destroy( m ); 00356 00357 return true; 00358 } // write_components 00359 00360 static bool 00361 write_image( const QImage &image, QIODevice* io, int quality ) 00362 { 00363 jas_stream_t* stream = 0; 00364 stream = jas_stream_qiodevice( io ); 00365 00366 // by here, a jas_stream_t is open 00367 if( !stream ) return false; 00368 00369 jas_image_t* ji = create_image( image ); 00370 if( !ji ) { 00371 jas_stream_close( stream ); 00372 return false; 00373 } // if 00374 00375 if( !write_components( ji, image ) ) { 00376 jas_stream_close( stream ); 00377 jas_image_destroy( ji ); 00378 return false; 00379 } // if 00380 00381 // optstr: 00382 // - rate=#B => the resulting file size is about # bytes 00383 // - rate=0.0 .. 1.0 => the resulting file size is about the factor times 00384 // the uncompressed size 00385 // use sprintf for locale-aware string 00386 char rateBuffer[16]; 00387 sprintf(rateBuffer, "rate=%.2g\n", (quality < 0) ? DEFAULT_RATE : quality / 100.0); 00388 int i = jp2_encode( ji, stream, rateBuffer); 00389 00390 jas_image_destroy( ji ); 00391 jas_stream_close( stream ); 00392 00393 if( i != 0 ) return false; 00394 00395 return true; 00396 } 00397 00398 JP2Handler::JP2Handler() 00399 { 00400 quality = 75; 00401 jas_init(); 00402 } 00403 00404 JP2Handler::~JP2Handler() 00405 { 00406 jas_cleanup(); 00407 } 00408 00409 bool JP2Handler::canRead() const 00410 { 00411 if (canRead(device())) { 00412 setFormat("jp2"); 00413 return true; 00414 } 00415 return false; 00416 } 00417 00418 bool JP2Handler::canRead(QIODevice *device) 00419 { 00420 if (!device) { 00421 return false; 00422 } 00423 return device->peek(6) == QByteArray("\x00\x00\x00\x0C\x6A\x50", 6); 00424 } 00425 00426 bool JP2Handler::read(QImage *image) 00427 { 00428 if (!canRead()) return false; 00429 00430 gs_t gs; 00431 if( !(gs.image = read_image( device() )) ) return false; 00432 00433 if( !convert_colorspace( gs ) ) return false; 00434 00435 render_view( gs, image ); 00436 00437 if( gs.image ) jas_image_destroy( gs.image ); 00438 if( gs.altimage ) jas_image_destroy( gs.altimage ); 00439 return true; 00440 00441 } 00442 00443 bool JP2Handler::write(const QImage &image) 00444 { 00445 return write_image(image, device(),quality); 00446 } 00447 00448 bool JP2Handler::supportsOption(ImageOption option) const 00449 { 00450 return option == Quality; 00451 } 00452 00453 QVariant JP2Handler::option(ImageOption option) const 00454 { 00455 if (option == Quality) 00456 return quality; 00457 return QVariant(); 00458 } 00459 00460 void JP2Handler::setOption(ImageOption option, const QVariant &value) 00461 { 00462 if (option == Quality) 00463 quality = qBound(-1, value.toInt(), 100); 00464 } 00465 00466 QByteArray JP2Handler::name() const 00467 { 00468 return "jp2"; 00469 } 00470 00471 class JP2Plugin : public QImageIOPlugin 00472 { 00473 public: 00474 QStringList keys() const; 00475 Capabilities capabilities(QIODevice *device, const QByteArray &format) const; 00476 QImageIOHandler *create(QIODevice *device, const QByteArray &format = QByteArray()) const; 00477 }; 00478 00479 QStringList JP2Plugin::keys() const 00480 { 00481 return QStringList() << "jp2"; 00482 } 00483 00484 QImageIOPlugin::Capabilities JP2Plugin::capabilities(QIODevice *device, const QByteArray &format) const 00485 { 00486 if (format == "jp2") 00487 return Capabilities(CanRead | CanWrite); 00488 if (!format.isEmpty()) 00489 return 0; 00490 if (!device->isOpen()) 00491 return 0; 00492 00493 Capabilities cap; 00494 if (device->isReadable() && JP2Handler::canRead(device)) 00495 cap |= CanRead; 00496 if (device->isWritable()) 00497 cap |= CanWrite; 00498 return cap; 00499 } 00500 00501 QImageIOHandler *JP2Plugin::create(QIODevice *device, const QByteArray &format) const 00502 { 00503 QImageIOHandler *handler = new JP2Handler; 00504 handler->setDevice(device); 00505 handler->setFormat(format); 00506 return handler; 00507 } 00508 00509 Q_EXPORT_STATIC_PLUGIN(JP2Plugin) 00510 Q_EXPORT_PLUGIN2(jp2, JP2Plugin) 00511 00512
This file is part of the KDE documentation.
Documentation copyright © 1996-2019 The KDE developers.
Generated on Mon Jan 21 2019 12:29:51 by doxygen 1.7.5.1 written by Dimitri van Heesch, © 1997-2006
Documentation copyright © 1996-2019 The KDE developers.
Generated on Mon Jan 21 2019 12:29:51 by doxygen 1.7.5.1 written by Dimitri van Heesch, © 1997-2006
KDE's Doxygen guidelines are available online.