• Skip to content
  • Skip to link menu
  • KDE API Reference
  • kdelibs-4.8.5 API Reference
  • KDE Home
  • Contact Us
 

KImgIO

  • kimgio
jp2.cpp
Go to the documentation of this file.
1 
8 #include "jp2.h"
9 
10 #include <config.h>
11 
12 #ifdef HAVE_SYS_TYPES_H
13 #include <sys/types.h>
14 #endif
15 
16 #ifdef HAVE_STDINT_H
17 #include <stdint.h>
18 #endif
19 
20 #include <QImage>
21 #include <QVariant>
22 #include <QTextStream>
23 
24 // dirty, but avoids a warning because jasper.h includes jas_config.h.
25 #undef PACKAGE
26 #undef VERSION
27 #include <jasper/jasper.h>
28 
29 // code taken in parts from JasPer's jiv.c
30 
31 #define DEFAULT_RATE 0.10
32 #define MAXCMPTS 256
33 
34 
35 /************************* JasPer QIODevice stream ***********************/
36 
37 //unfortunately this is declared as static in JasPer libraries
38 static jas_stream_t *jas_stream_create()
39 {
40  jas_stream_t *stream;
41 
42  if (!(stream = (jas_stream_t*)jas_malloc(sizeof(jas_stream_t)))) {
43  return 0;
44  }
45  stream->openmode_ = 0;
46  stream->bufmode_ = 0;
47  stream->flags_ = 0;
48  stream->bufbase_ = 0;
49  stream->bufstart_ = 0;
50  stream->bufsize_ = 0;
51  stream->ptr_ = 0;
52  stream->cnt_ = 0;
53  stream->ops_ = 0;
54  stream->obj_ = 0;
55  stream->rwcnt_ = 0;
56  stream->rwlimit_ = -1;
57 
58  return stream;
59 }
60 
61 //unfortunately this is declared as static in JasPer libraries
62 static void jas_stream_initbuf(jas_stream_t *stream, int bufmode, char *buf,
63  int bufsize)
64 {
65  /* If this function is being called, the buffer should not have been
66  initialized yet. */
67  assert(!stream->bufbase_);
68 
69  if (bufmode != JAS_STREAM_UNBUF) {
70  /* The full- or line-buffered mode is being employed. */
71  if (!buf) {
72  /* The caller has not specified a buffer to employ, so allocate
73  one. */
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;
78  } else {
79  /* The buffer allocation has failed. Resort to unbuffered
80  operation. */
81  stream->bufbase_ = stream->tinybuf_;
82  stream->bufsize_ = 1;
83  }
84  } else {
85  /* The caller has specified a buffer to employ. */
86  /* The buffer must be large enough to accommodate maximum
87  putback. */
88  assert(bufsize > JAS_STREAM_MAXPUTBACK);
89  stream->bufbase_ = JAS_CAST(uchar *, buf);
90  stream->bufsize_ = bufsize - JAS_STREAM_MAXPUTBACK;
91  }
92  } else {
93  /* The unbuffered mode is being employed. */
94  /* A buffer should not have been supplied by the caller. */
95  assert(!buf);
96  /* Use a trivial one-character buffer. */
97  stream->bufbase_ = stream->tinybuf_;
98  stream->bufsize_ = 1;
99  }
100  stream->bufstart_ = &stream->bufbase_[JAS_STREAM_MAXPUTBACK];
101  stream->ptr_ = stream->bufstart_;
102  stream->cnt_ = 0;
103  stream->bufmode_ |= bufmode & JAS_STREAM_BUFMODEMASK;
104 }
105 
106 static int qiodevice_read(jas_stream_obj_t *obj, char *buf, int cnt)
107 {
108  QIODevice *io = (QIODevice*) obj;
109  return io->read(buf, cnt);
110 }
111 
112 static int qiodevice_write(jas_stream_obj_t *obj, char *buf, int cnt)
113 {
114  QIODevice *io = (QIODevice*) obj;
115  return io->write(buf, cnt);
116 }
117 
118 static long qiodevice_seek(jas_stream_obj_t *obj, long offset, int origin)
119 {
120  QIODevice *io = (QIODevice*) obj;
121  long newpos;
122 
123  switch (origin) {
124  case SEEK_SET:
125  newpos = offset;
126  break;
127  case SEEK_END:
128  newpos = io->size() - offset;
129  break;
130  case SEEK_CUR:
131  newpos = io->pos() + offset;
132  break;
133  default:
134  return -1;
135  }
136  if (newpos < 0) {
137  return -1;
138  }
139  if ( io->seek(newpos) )
140  return newpos;
141  else
142  return -1;
143 }
144 
145 static int qiodevice_close(jas_stream_obj_t *)
146 {
147  return 0;
148 }
149 
150 static jas_stream_ops_t jas_stream_qiodeviceops = {
151  qiodevice_read,
152  qiodevice_write,
153  qiodevice_seek,
154  qiodevice_close
155 };
156 
157 static jas_stream_t *jas_stream_qiodevice(QIODevice *iodevice)
158 {
159  jas_stream_t *stream;
160 
161  if ( !iodevice ) return 0;
162  if (!(stream = jas_stream_create())) {
163  return 0;
164  }
165 
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;
169 
170  jas_stream_initbuf(stream, JAS_STREAM_FULLBUF, 0, 0);
171 
172  /* Select the operations for a memory stream. */
173  stream->obj_ = (void *)iodevice;
174  stream->ops_ = &jas_stream_qiodeviceops;
175 
176  return stream;
177 }
178 
179 /************************ End of JasPer QIODevice stream ****************/
180 
181 typedef struct {
182  jas_image_t* image;
183 
184  int cmptlut[MAXCMPTS];
185 
186  jas_image_t* altimage;
187 } gs_t;
188 
189 
190 static jas_image_t*
191 read_image( QIODevice* io )
192 {
193  jas_stream_t* in = 0;
194 
195  in = jas_stream_qiodevice( io );
196 
197  if( !in ) return 0;
198 
199  jas_image_t* image = jas_image_decode( in, -1, 0 );
200  jas_stream_close( in );
201 
202  // image may be 0, but that's Ok
203  return image;
204 } // read_image
205 
206 static bool
207 convert_colorspace( gs_t& gs )
208 {
209  jas_cmprof_t *outprof = jas_cmprof_createfromclrspc( JAS_CLRSPC_SRGB );
210  if( !outprof ) return false;
211 
212  gs.altimage = jas_image_chclrspc( gs.image, outprof,
213  JAS_CMXFORM_INTENT_PER );
214  if( !gs.altimage ) return false;
215 
216  return true;
217 } // convert_colorspace
218 
219 static bool
220 render_view( gs_t& gs, QImage* outImage )
221 {
222  if ( !gs.altimage ) return false;
223  QImage qti;
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) {
230  return false;
231  } // if
232 
233  const int* cmptlut = gs.cmptlut;
234  int v[3];
235 
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)
242  return false;
243  } // for
244 
245  qti = QImage( jas_image_width( gs.altimage ), jas_image_height( gs.altimage ),
246  QImage::Format_RGB32 );
247 
248  uint32_t* data = (uint32_t*)qti.bits();
249 
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] );
257 
258  if( v[k] < 0 ) v[k] = 0;
259  else if( v[k] > 255 ) v[k] = 255;
260  } // for k
261 
262  *data++ = qRgb( v[0], v[1], v[2] );
263  } // for x
264  } // for y
265  *outImage = qti;
266  return true;
267 } // render_view
268 
269 
270 static jas_image_t*
271 create_image( const QImage& qi )
272 {
273  // prepare the component parameters
274  jas_image_cmptparm_t* cmptparms = new jas_image_cmptparm_t[ 3 ];
275 
276  for ( int i = 0; i < 3; ++i ) {
277  // x and y offset
278  cmptparms[i].tlx = 0;
279  cmptparms[i].tly = 0;
280 
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();
286 
287  // we write everything as 24bit truecolor ATM
288  cmptparms[i].prec = 8;
289  cmptparms[i].sgnd = false;
290  }
291 
292  jas_image_t* ji = jas_image_create( 3 /* number components */, cmptparms, JAS_CLRSPC_UNKNOWN );
293  delete[] cmptparms;
294 
295  // returning 0 is ok
296  return ji;
297 } // create_image
298 
299 
300 static bool
301 write_components( jas_image_t* ji, const QImage& qi )
302 {
303  const unsigned height = qi.height();
304  const unsigned width = qi.width();
305 
306  jas_matrix_t* m = jas_matrix_create( height, width );
307  if( !m ) return false;
308 
309  jas_image_setclrspc( ji, JAS_CLRSPC_SRGB );
310 
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 );
316 
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 );
322 
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 );
329 
330  return true;
331 } // write_components
332 
333 static bool
334 write_image( const QImage &image, QIODevice* io, int quality )
335 {
336  jas_stream_t* stream = 0;
337  stream = jas_stream_qiodevice( io );
338 
339  // by here, a jas_stream_t is open
340  if( !stream ) return false;
341 
342  jas_image_t* ji = create_image( image );
343  if( !ji ) {
344  jas_stream_close( stream );
345  return false;
346  } // if
347 
348  if( !write_components( ji, image ) ) {
349  jas_stream_close( stream );
350  jas_image_destroy( ji );
351  return false;
352  } // if
353 
354  // optstr:
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
358  // use sprintf for locale-aware string
359  char rateBuffer[16];
360  sprintf(rateBuffer, "rate=%.2g\n", (quality < 0) ? DEFAULT_RATE : quality / 100.0);
361  int i = jp2_encode( ji, stream, rateBuffer);
362 
363  jas_image_destroy( ji );
364  jas_stream_close( stream );
365 
366  if( i != 0 ) return false;
367 
368  return true;
369 }
370 
371 JP2Handler::JP2Handler()
372 {
373  quality = 75;
374  jas_init();
375 }
376 
377 JP2Handler::~JP2Handler()
378 {
379  jas_cleanup();
380 }
381 
382 bool JP2Handler::canRead() const
383 {
384  if (canRead(device())) {
385  setFormat("jp2");
386  return true;
387  }
388  return false;
389 }
390 
391 bool JP2Handler::canRead(QIODevice *device)
392 {
393  if (!device) {
394  return false;
395  }
396  return device->peek(6) == QByteArray("\x00\x00\x00\x0C\x6A\x50", 6);
397 }
398 
399 bool JP2Handler::read(QImage *image)
400 {
401  if (!canRead()) return false;
402 
403  gs_t gs;
404  if( !(gs.image = read_image( device() )) ) return false;
405 
406  if( !convert_colorspace( gs ) ) return false;
407 
408  render_view( gs, image );
409 
410  if( gs.image ) jas_image_destroy( gs.image );
411  if( gs.altimage ) jas_image_destroy( gs.altimage );
412  return true;
413 
414 }
415 
416 bool JP2Handler::write(const QImage &image)
417 {
418  return write_image(image, device(),quality);
419 }
420 
421 bool JP2Handler::supportsOption(ImageOption option) const
422 {
423  return option == Quality;
424 }
425 
426 QVariant JP2Handler::option(ImageOption option) const
427 {
428  if (option == Quality)
429  return quality;
430  return QVariant();
431 }
432 
433 void JP2Handler::setOption(ImageOption option, const QVariant &value)
434 {
435  if (option == Quality)
436  quality = qBound(-1, value.toInt(), 100);
437 }
438 
439 QByteArray JP2Handler::name() const
440 {
441  return "jp2";
442 }
443 
444 class JP2Plugin : public QImageIOPlugin
445 {
446 public:
447  QStringList keys() const;
448  Capabilities capabilities(QIODevice *device, const QByteArray &format) const;
449  QImageIOHandler *create(QIODevice *device, const QByteArray &format = QByteArray()) const;
450 };
451 
452 QStringList JP2Plugin::keys() const
453 {
454  return QStringList() << "jp2";
455 }
456 
457 QImageIOPlugin::Capabilities JP2Plugin::capabilities(QIODevice *device, const QByteArray &format) const
458 {
459  if (format == "jp2")
460  return Capabilities(CanRead | CanWrite);
461  if (!format.isEmpty())
462  return 0;
463  if (!device->isOpen())
464  return 0;
465 
466  Capabilities cap;
467  if (device->isReadable() && JP2Handler::canRead(device))
468  cap |= CanRead;
469  if (device->isWritable())
470  cap |= CanWrite;
471  return cap;
472 }
473 
474 QImageIOHandler *JP2Plugin::create(QIODevice *device, const QByteArray &format) const
475 {
476  QImageIOHandler *handler = new JP2Handler;
477  handler->setDevice(device);
478  handler->setFormat(format);
479  return handler;
480 }
481 
482 Q_EXPORT_STATIC_PLUGIN(JP2Plugin)
483 Q_EXPORT_PLUGIN2(jp2, JP2Plugin)
484 
485 
This file is part of the KDE documentation.
Documentation copyright © 1996-2013 The KDE developers.
Generated on Thu Feb 21 2013 11:03:18 by doxygen 1.8.1 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.

KImgIO

Skip menu "KImgIO"
  • Main Page
  • Alphabetical List
  • Class List
  • Class Hierarchy
  • Class Members
  • File List
  • File Members
  • Related Pages

kdelibs-4.8.5 API Reference

Skip menu "kdelibs-4.8.5 API Reference"
  • DNSSD
  • Interfaces
  •   KHexEdit
  •   KMediaPlayer
  •   KSpeech
  •   KTextEditor
  • kconf_update
  • KDE3Support
  •   KUnitTest
  • KDECore
  • KDED
  • KDEsu
  • KDEUI
  • KDEWebKit
  • KDocTools
  • KFile
  • KHTML
  • KImgIO
  • KInit
  • kio
  • KIOSlave
  • KJS
  •   KJS-API
  •   WTF
  • kjsembed
  • KNewStuff
  • KParts
  • KPty
  • Kross
  • KUnitConversion
  • KUtils
  • Nepomuk
  • Plasma
  • Solid
  • Sonnet
  • ThreadWeaver
Report problems with this website to our bug tracking system.
Contact the specific authors with questions and comments about the page contents.

KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal