2 * QImageIO Routines to read/write JPEG2000 images.
3 * copyright (c) 2002 Michael Ritzert <michael@ritzert.de>
5 * This library is distributed under the conditions of the GNU LGPL.
12 #ifdef HAVE_SYS_TYPES_H
13 #include <sys/types.h>
22 #include <QTextStream>
24 // dirty, but avoids a warning because jasper.h includes jas_config.h.
27 #include <jasper/jasper.h>
29 // code taken in parts from JasPer's jiv.c
31 #define DEFAULT_RATE 0.10
35 /************************* JasPer QIODevice stream ***********************/
37 //unfortunately this is declared as static in JasPer libraries
38 static jas_stream_t
*jas_stream_create()
42 if (!(stream
= (jas_stream_t
*)jas_malloc(sizeof(jas_stream_t
)))) {
45 stream
->openmode_
= 0;
49 stream
->bufstart_
= 0;
56 stream
->rwlimit_
= -1;
61 //unfortunately this is declared as static in JasPer libraries
62 static void jas_stream_initbuf(jas_stream_t
*stream
, int bufmode
, char *buf
,
65 /* If this function is being called, the buffer should not have been
67 assert(!stream
->bufbase_
);
69 if (bufmode
!= JAS_STREAM_UNBUF
) {
70 /* The full- or line-buffered mode is being employed. */
72 /* The caller has not specified a buffer to employ, so allocate
74 if ((stream
->bufbase_
= (unsigned char*)jas_malloc(JAS_STREAM_BUFSIZE
+
75 JAS_STREAM_MAXPUTBACK
))) {
76 stream
->bufmode_
|= JAS_STREAM_FREEBUF
;
77 stream
->bufsize_
= JAS_STREAM_BUFSIZE
;
79 /* The buffer allocation has failed. Resort to unbuffered
81 stream
->bufbase_
= stream
->tinybuf_
;
85 /* The caller has specified a buffer to employ. */
86 /* The buffer must be large enough to accommodate maximum
88 assert(bufsize
> JAS_STREAM_MAXPUTBACK
);
89 stream
->bufbase_
= JAS_CAST(uchar
*, buf
);
90 stream
->bufsize_
= bufsize
- JAS_STREAM_MAXPUTBACK
;
93 /* The unbuffered mode is being employed. */
94 /* A buffer should not have been supplied by the caller. */
96 /* Use a trivial one-character buffer. */
97 stream
->bufbase_
= stream
->tinybuf_
;
100 stream
->bufstart_
= &stream
->bufbase_
[JAS_STREAM_MAXPUTBACK
];
101 stream
->ptr_
= stream
->bufstart_
;
103 stream
->bufmode_
|= bufmode
& JAS_STREAM_BUFMODEMASK
;
106 static int qiodevice_read(jas_stream_obj_t
*obj
, char *buf
, int cnt
)
108 QIODevice
*io
= (QIODevice
*) obj
;
109 return io
->read(buf
, cnt
);
112 static int qiodevice_write(jas_stream_obj_t
*obj
, char *buf
, int cnt
)
114 QIODevice
*io
= (QIODevice
*) obj
;
115 return io
->write(buf
, cnt
);
118 static long qiodevice_seek(jas_stream_obj_t
*obj
, long offset
, int origin
)
120 QIODevice
*io
= (QIODevice
*) obj
;
128 newpos
= io
->size() - offset
;
131 newpos
= io
->size() + offset
;
139 if ( io
->seek(newpos
) )
145 static int qiodevice_close(jas_stream_obj_t
*)
150 static jas_stream_ops_t jas_stream_qiodeviceops
= {
157 static jas_stream_t
*jas_stream_qiodevice(QIODevice
*iodevice
)
159 jas_stream_t
*stream
;
161 if ( !iodevice
) return 0;
162 if (!(stream
= jas_stream_create())) {
166 /* A stream associated with a memory buffer is always opened
167 for both reading and writing in binary mode. */
168 stream
->openmode_
= JAS_STREAM_READ
| JAS_STREAM_WRITE
| JAS_STREAM_BINARY
;
170 jas_stream_initbuf(stream
, JAS_STREAM_FULLBUF
, 0, 0);
172 /* Select the operations for a memory stream. */
173 stream
->obj_
= (void *)iodevice
;
174 stream
->ops_
= &jas_stream_qiodeviceops
;
179 /************************ End of JasPer QIODevice stream ****************/
184 int cmptlut
[MAXCMPTS
];
186 jas_image_t
* altimage
;
191 read_image( QIODevice
* io
)
193 jas_stream_t
* in
= 0;
195 in
= jas_stream_qiodevice( io
);
199 jas_image_t
* image
= jas_image_decode( in
, -1, 0 );
200 jas_stream_close( in
);
202 // image may be 0, but that's Ok
207 convert_colorspace( gs_t
& gs
)
209 jas_cmprof_t
*outprof
= jas_cmprof_createfromclrspc( JAS_CLRSPC_SRGB
);
210 if( !outprof
) return false;
212 gs
.altimage
= jas_image_chclrspc( gs
.image
, outprof
,
213 JAS_CMXFORM_INTENT_PER
);
214 if( !gs
.altimage
) return false;
217 } // convert_colorspace
220 render_view( gs_t
& gs
, QImage
* outImage
)
222 if ( !gs
.altimage
) return false;
224 if((gs
.cmptlut
[0] = jas_image_getcmptbytype(gs
.altimage
,
225 JAS_IMAGE_CT_COLOR(JAS_CLRSPC_CHANIND_RGB_R
))) < 0 ||
226 (gs
.cmptlut
[1] = jas_image_getcmptbytype(gs
.altimage
,
227 JAS_IMAGE_CT_COLOR(JAS_CLRSPC_CHANIND_RGB_G
))) < 0 ||
228 (gs
.cmptlut
[2] = jas_image_getcmptbytype(gs
.altimage
,
229 JAS_IMAGE_CT_COLOR(JAS_CLRSPC_CHANIND_RGB_B
))) < 0) {
233 const int* cmptlut
= gs
.cmptlut
;
236 // check that all components have the same size.
237 const int width
= jas_image_cmptwidth( gs
.altimage
, cmptlut
[0] );
238 const int height
= jas_image_cmptheight( gs
.altimage
, cmptlut
[0] );
239 for( int i
= 1; i
< 3; ++i
) {
240 if (jas_image_cmptwidth( gs
.altimage
, cmptlut
[i
] ) != width
||
241 jas_image_cmptheight( gs
.altimage
, cmptlut
[i
] ) != height
)
245 qti
= QImage( jas_image_width( gs
.altimage
), jas_image_height( gs
.altimage
),
246 QImage::Format_RGB32
);
248 uint32_t* data
= (uint32_t*)qti
.bits();
250 for( int y
= 0; y
< height
; ++y
) {
251 for( int x
= 0; x
< width
; ++x
) {
252 for( int k
= 0; k
< 3; ++k
) {
253 v
[k
] = jas_image_readcmptsample( gs
.altimage
, cmptlut
[k
], x
, y
);
254 // if the precision of the component is too small, increase
255 // it to use the complete value range.
256 v
[k
] <<= 8 - jas_image_cmptprec( gs
.altimage
, cmptlut
[k
] );
258 if( v
[k
] < 0 ) v
[k
] = 0;
259 else if( v
[k
] > 255 ) v
[k
] = 255;
262 *data
++ = qRgb( v
[0], v
[1], v
[2] );
271 create_image( const QImage
& qi
)
273 // prepare the component parameters
274 jas_image_cmptparm_t
* cmptparms
= new jas_image_cmptparm_t
[ 3 ];
276 for ( int i
= 0; i
< 3; ++i
) {
278 cmptparms
[i
].tlx
= 0;
279 cmptparms
[i
].tly
= 0;
281 // the resulting image will be hstep*width x vstep*height !
282 cmptparms
[i
].hstep
= 1;
283 cmptparms
[i
].vstep
= 1;
284 cmptparms
[i
].width
= qi
.width();
285 cmptparms
[i
].height
= qi
.height();
287 // we write everything as 24bit truecolor ATM
288 cmptparms
[i
].prec
= 8;
289 cmptparms
[i
].sgnd
= false;
292 jas_image_t
* ji
= jas_image_create( 3 /* number components */, cmptparms
, JAS_CLRSPC_UNKNOWN
);
301 write_components( jas_image_t
* ji
, const QImage
& qi
)
303 const unsigned height
= qi
.height();
304 const unsigned width
= qi
.width();
306 jas_matrix_t
* m
= jas_matrix_create( height
, width
);
307 if( !m
) return false;
309 jas_image_setclrspc( ji
, JAS_CLRSPC_SRGB
);
311 jas_image_setcmpttype( ji
, 0, JAS_IMAGE_CT_RGB_R
);
312 for( uint y
= 0; y
< height
; ++y
)
313 for( uint x
= 0; x
< width
; ++x
)
314 jas_matrix_set( m
, y
, x
, qRed( qi
.pixel( x
, y
) ) );
315 jas_image_writecmpt( ji
, 0, 0, 0, width
, height
, m
);
317 jas_image_setcmpttype( ji
, 1, JAS_IMAGE_CT_RGB_G
);
318 for( uint y
= 0; y
< height
; ++y
)
319 for( uint x
= 0; x
< width
; ++x
)
320 jas_matrix_set( m
, y
, x
, qGreen( qi
.pixel( x
, y
) ) );
321 jas_image_writecmpt( ji
, 1, 0, 0, width
, height
, m
);
323 jas_image_setcmpttype( ji
, 2, JAS_IMAGE_CT_RGB_B
);
324 for( uint y
= 0; y
< height
; ++y
)
325 for( uint x
= 0; x
< width
; ++x
)
326 jas_matrix_set( m
, y
, x
, qBlue( qi
.pixel( x
, y
) ) );
327 jas_image_writecmpt( ji
, 2, 0, 0, width
, height
, m
);
328 jas_matrix_destroy( m
);
331 } // write_components
334 write_image( const QImage
&image
, QIODevice
* io
, int quality
)
336 jas_stream_t
* stream
= 0;
337 stream
= jas_stream_qiodevice( io
);
339 // by here, a jas_stream_t is open
340 if( !stream
) return false;
342 jas_image_t
* ji
= create_image( image
);
344 jas_stream_close( stream
);
348 if( !write_components( ji
, image
) ) {
349 jas_stream_close( stream
);
350 jas_image_destroy( ji
);
355 // - rate=#B => the resulting file size is about # bytes
356 // - rate=0.0 .. 1.0 => the resulting file size is about the factor times
357 // the uncompressed size
359 QTextStream
ts( &rate
, QIODevice::WriteOnly
);
361 << ( (quality
< 0) ? DEFAULT_RATE
: quality
/ 100.0F
);
362 int i
= jp2_encode( ji
, stream
, rate
.toUtf8().data() );
364 jas_image_destroy( ji
);
365 jas_stream_close( stream
);
367 if( i
!= 0 ) return false;
372 JP2Handler::JP2Handler()
378 JP2Handler::~JP2Handler()
383 bool JP2Handler::canRead() const
385 if (canRead(device())) {
392 bool JP2Handler::canRead(QIODevice
*device
)
397 return device
->peek(6) == QByteArray("\x00\x00\x00\x0C\x6A\x50", 6);
400 bool JP2Handler::read(QImage
*image
)
402 if (!canRead()) return false;
405 if( !(gs
.image
= read_image( device() )) ) return false;
407 if( !convert_colorspace( gs
) ) return false;
409 render_view( gs
, image
);
411 if( gs
.image
) jas_image_destroy( gs
.image
);
412 if( gs
.altimage
) jas_image_destroy( gs
.altimage
);
417 bool JP2Handler::write(const QImage
&image
)
419 return write_image(image
, device(),quality
);
422 bool JP2Handler::supportsOption(ImageOption option
) const
424 return option
== Quality
;
427 QVariant
JP2Handler::option(ImageOption option
) const
429 if (option
== Quality
)
434 QByteArray
JP2Handler::name() const
439 class JP2Plugin
: public QImageIOPlugin
442 QStringList
keys() const;
443 Capabilities
capabilities(QIODevice
*device
, const QByteArray
&format
) const;
444 QImageIOHandler
*create(QIODevice
*device
, const QByteArray
&format
= QByteArray()) const;
447 QStringList
JP2Plugin::keys() const
449 return QStringList() << "jp2";
452 QImageIOPlugin::Capabilities
JP2Plugin::capabilities(QIODevice
*device
, const QByteArray
&format
) const
455 return Capabilities(CanRead
| CanWrite
);
456 if (!format
.isEmpty())
458 if (!device
->isOpen())
462 if (device
->isReadable() && JP2Handler::canRead(device
))
464 if (device
->isWritable())
469 QImageIOHandler
*JP2Plugin::create(QIODevice
*device
, const QByteArray
&format
) const
471 QImageIOHandler
*handler
= new JP2Handler
;
472 handler
->setDevice(device
);
473 handler
->setFormat(format
);
477 Q_EXPORT_STATIC_PLUGIN(JP2Plugin
)
478 Q_EXPORT_PLUGIN2(jp2
, JP2Plugin
)