source: vendor/trolltech/current/src/kernel/qjpegio.cpp

Last change on this file was 2, checked in by dmik, 20 years ago

Imported xplatform parts of the official release 3.3.1 from Trolltech

  • Property svn:keywords set to Id
File size: 16.1 KB
Line 
1/****************************************************************************
2** $Id: qjpegio.cpp 2 2005-11-16 15:49:26Z dmik $
3**
4** Implementation of JPEG QImage IOHandler
5**
6** Created : 990521
7**
8** Copyright (C) 1992-2003 Trolltech AS. All rights reserved.
9**
10** This file is part of the kernel module of the Qt GUI Toolkit.
11**
12** This file may be distributed under the terms of the Q Public License
13** as defined by Trolltech AS of Norway and appearing in the file
14** LICENSE.QPL included in the packaging of this file.
15**
16** This file may be distributed and/or modified under the terms of the
17** GNU General Public License version 2 as published by the Free Software
18** Foundation and appearing in the file LICENSE.GPL included in the
19** packaging of this file.
20**
21** Licensees holding valid Qt Enterprise Edition or Qt Professional Edition
22** licenses may use this file in accordance with the Qt Commercial License
23** Agreement provided with the Software.
24**
25** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
26** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
27**
28** See http://www.trolltech.com/pricing.html or email sales@trolltech.com for
29** information about Qt Commercial License Agreements.
30** See http://www.trolltech.com/qpl/ for QPL licensing information.
31** See http://www.trolltech.com/gpl/ for GPL licensing information.
32**
33** Contact info@trolltech.com if any conditions of this licensing are
34** not clear to you.
35**
36**********************************************************************/
37
38#ifndef QT_CLEAN_NAMESPACE
39#define QT_CLEAN_NAMESPACE
40#endif
41
42#include "qimage.h"
43
44#ifndef QT_NO_IMAGEIO_JPEG
45
46#include "qiodevice.h"
47#include "qjpegio.h"
48
49#include <stdio.h> // jpeglib needs this to be pre-included
50#include <setjmp.h>
51
52
53// including jpeglib.h seems to be a little messy
54extern "C" {
55#define XMD_H // shut JPEGlib up
56#if defined(Q_OS_UNIXWARE)
57# define HAVE_BOOLEAN // libjpeg under Unixware seems to need this
58#endif
59#include <jpeglib.h>
60#ifdef const
61# undef const // remove crazy C hackery in jconfig.h
62#endif
63}
64
65
66struct my_error_mgr : public jpeg_error_mgr {
67 jmp_buf setjmp_buffer;
68};
69
70#if defined(Q_C_CALLBACKS)
71extern "C" {
72#endif
73
74static
75void my_error_exit (j_common_ptr cinfo)
76{
77 my_error_mgr* myerr = (my_error_mgr*) cinfo->err;
78 char buffer[JMSG_LENGTH_MAX];
79 (*cinfo->err->format_message)(cinfo, buffer);
80 qWarning(buffer);
81 longjmp(myerr->setjmp_buffer, 1);
82}
83
84#if defined(Q_C_CALLBACKS)
85}
86#endif
87
88
89static const int max_buf = 4096;
90
91struct my_jpeg_source_mgr : public jpeg_source_mgr {
92 // Nothing dynamic - cannot rely on destruction over longjump
93 QImageIO* iio;
94 JOCTET buffer[max_buf];
95
96public:
97 my_jpeg_source_mgr(QImageIO* iio);
98};
99
100#if defined(Q_C_CALLBACKS)
101extern "C" {
102#endif
103
104static
105void qt_init_source(j_decompress_ptr)
106{
107}
108
109static
110boolean qt_fill_input_buffer(j_decompress_ptr cinfo)
111{
112 int num_read;
113 my_jpeg_source_mgr* src = (my_jpeg_source_mgr*)cinfo->src;
114 QIODevice* dev = src->iio->ioDevice();
115 src->next_input_byte = src->buffer;
116 num_read = dev->readBlock((char*)src->buffer, max_buf);
117 if ( num_read <= 0 ) {
118 // Insert a fake EOI marker - as per jpeglib recommendation
119 src->buffer[0] = (JOCTET) 0xFF;
120 src->buffer[1] = (JOCTET) JPEG_EOI;
121 src->bytes_in_buffer = 2;
122 } else {
123 src->bytes_in_buffer = num_read;
124 }
125#if defined(Q_OS_UNIXWARE)
126 return B_TRUE;
127#else
128 return TRUE;
129#endif
130}
131
132static
133void qt_skip_input_data(j_decompress_ptr cinfo, long num_bytes)
134{
135 my_jpeg_source_mgr* src = (my_jpeg_source_mgr*)cinfo->src;
136
137 // `dumb' implementation from jpeglib
138
139 /* Just a dumb implementation for now. Could use fseek() except
140 * it doesn't work on pipes. Not clear that being smart is worth
141 * any trouble anyway --- large skips are infrequent.
142 */
143 if (num_bytes > 0) {
144 while (num_bytes > (long) src->bytes_in_buffer) {
145 num_bytes -= (long) src->bytes_in_buffer;
146 (void) qt_fill_input_buffer(cinfo);
147 /* note we assume that qt_fill_input_buffer will never return FALSE,
148 * so suspension need not be handled.
149 */
150 }
151 src->next_input_byte += (size_t) num_bytes;
152 src->bytes_in_buffer -= (size_t) num_bytes;
153 }
154}
155
156static
157void qt_term_source(j_decompress_ptr)
158{
159}
160
161#if defined(Q_C_CALLBACKS)
162}
163#endif
164
165
166inline my_jpeg_source_mgr::my_jpeg_source_mgr(QImageIO* iioptr)
167{
168 jpeg_source_mgr::init_source = qt_init_source;
169 jpeg_source_mgr::fill_input_buffer = qt_fill_input_buffer;
170 jpeg_source_mgr::skip_input_data = qt_skip_input_data;
171 jpeg_source_mgr::resync_to_restart = jpeg_resync_to_restart;
172 jpeg_source_mgr::term_source = qt_term_source;
173 iio = iioptr;
174 bytes_in_buffer = 0;
175 next_input_byte = buffer;
176}
177
178
179static
180void scaleSize( int &reqW, int &reqH, int imgW, int imgH, QImage::ScaleMode mode )
181{
182 if ( mode == QImage::ScaleFree )
183 return;
184 int t1 = imgW * reqH;
185 int t2 = reqW * imgH;
186 if (( mode == QImage::ScaleMin && (t1 > t2) ) || ( mode == QImage::ScaleMax && (t1 < t2) ))
187 reqH = t2 / imgW;
188 else
189 reqW = t1 / imgH;
190}
191
192
193static
194void read_jpeg_image(QImageIO* iio)
195{
196 QImage image;
197
198 struct jpeg_decompress_struct cinfo;
199
200 struct my_jpeg_source_mgr *iod_src = new my_jpeg_source_mgr(iio);
201 struct my_error_mgr jerr;
202
203 jpeg_create_decompress(&cinfo);
204
205 cinfo.src = iod_src;
206
207 cinfo.err = jpeg_std_error(&jerr);
208 jerr.error_exit = my_error_exit;
209
210 if (!setjmp(jerr.setjmp_buffer)) {
211#if defined(Q_OS_UNIXWARE)
212 (void) jpeg_read_header(&cinfo, B_TRUE);
213#else
214 (void) jpeg_read_header(&cinfo, TRUE);
215#endif
216
217 (void) jpeg_start_decompress(&cinfo);
218
219 QString params = iio->parameters();
220 params.simplifyWhiteSpace();
221 int sWidth = 0, sHeight = 0;
222 char sModeStr[1024] = "";
223 QImage::ScaleMode sMode;
224
225 if ( params.contains( "GetHeaderInformation" ) ) {
226
227 // Create QImage's without allocating the data
228 if ( cinfo.output_components == 3 || cinfo.output_components == 4) {
229 image = QImage( NULL, cinfo.output_width, cinfo.output_height, 32, NULL, 0, QImage::IgnoreEndian );
230 } else if ( cinfo.output_components == 1 ) {
231 image = QImage( NULL, cinfo.output_width, cinfo.output_height, 8, NULL, 0, QImage::IgnoreEndian );
232 } else {
233 // Unsupported format
234 }
235
236
237 } else if ( params.contains( "Scale" ) ) {
238 sscanf( params.latin1(), "Scale( %i, %i, %1023s )",
239 &sWidth, &sHeight, sModeStr );
240
241 QString sModeQStr( sModeStr );
242 if ( sModeQStr == "ScaleFree" ) {
243 sMode = QImage::ScaleFree;
244 } else if ( sModeQStr == "ScaleMin" ) {
245 sMode = QImage::ScaleMin;
246 } else if ( sModeQStr == "ScaleMax" ) {
247 sMode = QImage::ScaleMax;
248 } else {
249 qDebug("read_jpeg_image: invalid scale mode \"%s\", see QImage::ScaleMode documentation", sModeStr);
250 sMode = QImage::ScaleFree;
251 }
252
253// qDebug( "Parameters ask to scale the image to %i x %i ScaleMode: %s", sWidth, sHeight, sModeStr );
254 scaleSize( sWidth, sHeight, cinfo.output_width, cinfo.output_height, sMode );
255// qDebug( "Scaling the jpeg to %i x %i", sWidth, sHeight, sModeStr );
256
257 if ( cinfo.output_components == 3 || cinfo.output_components == 4) {
258 image.create( sWidth, sHeight, 32 );
259 } else if ( cinfo.output_components == 1 ) {
260 image.create( sWidth, sHeight, 8, 256 );
261 for (int i=0; i<256; i++)
262 image.setColor(i, qRgb(i,i,i));
263 } else {
264 // Unsupported format
265 }
266
267 if (!image.isNull()) {
268 QImage tmpImage( cinfo.output_width, 1, 32 );
269 uchar** inLines = tmpImage.jumpTable();
270 uchar** outLines = image.jumpTable();
271 while (cinfo.output_scanline < cinfo.output_height) {
272 int outputLine = sHeight * cinfo.output_scanline / cinfo.output_height;
273 (void) jpeg_read_scanlines(&cinfo, inLines, 1);
274 if ( cinfo.output_components == 3 ) {
275 uchar *in = inLines[0];
276 QRgb *out = (QRgb*)outLines[outputLine];
277 for (uint i=0; i<cinfo.output_width; i++ ) {
278// ### Only scaling down an image works, I don't think scaling up will work at the moment
279// ### An idea I have to make this a smooth scale is to progressively add the pixel values up
280// When scaling down, multiple values are being over drawn in to the output buffer.
281// Instead, a weighting based on the distance the line or pixel is from the output pixel determines
282// the weight of it when added to the output buffer. At present it is a non-smooth scale which is
283// inefficently implemented, it still uncompresses all the jpeg, an optimization for progressive
284// jpegs could be made if scaling by say 50% or some other special cases
285 out[sWidth * i / cinfo.output_width] = qRgb( in[0], in[1], in[2] );
286 in += 3;
287 }
288 } else {
289// ### Need to test the case where the jpeg is grayscale, need some black and white jpegs to test
290// this code. (also only scales down and probably won't scale to a larger size)
291 uchar *in = inLines[0];
292 uchar *out = outLines[outputLine];
293 for (uint i=0; i<cinfo.output_width; i++ ) {
294 out[sWidth * i / cinfo.output_width] = in[i];
295 }
296 }
297 }
298 (void) jpeg_finish_decompress(&cinfo);
299 }
300
301 } else {
302
303 if ( cinfo.output_components == 3 || cinfo.output_components == 4) {
304 image.create( cinfo.output_width, cinfo.output_height, 32 );
305 } else if ( cinfo.output_components == 1 ) {
306 image.create( cinfo.output_width, cinfo.output_height, 8, 256 );
307 for (int i=0; i<256; i++)
308 image.setColor(i, qRgb(i,i,i));
309 } else {
310 // Unsupported format
311 }
312
313 if (!image.isNull()) {
314 uchar** lines = image.jumpTable();
315 while (cinfo.output_scanline < cinfo.output_height)
316 (void) jpeg_read_scanlines(&cinfo,
317 lines + cinfo.output_scanline,
318 cinfo.output_height);
319 (void) jpeg_finish_decompress(&cinfo);
320 }
321
322 if ( cinfo.output_components == 3 ) {
323 // Expand 24->32 bpp.
324 for (uint j=0; j<cinfo.output_height; j++) {
325 uchar *in = image.scanLine(j) + cinfo.output_width * 3;
326 QRgb *out = (QRgb*)image.scanLine(j);
327
328 for (uint i=cinfo.output_width; i--; ) {
329 in-=3;
330 out[i] = qRgb(in[0], in[1], in[2]);
331 }
332 }
333 }
334 }
335
336 if ( cinfo.density_unit == 1 ) {
337 image.setDotsPerMeterX( int(100. * cinfo.X_density / 2.54) );
338 image.setDotsPerMeterY( int(100. * cinfo.Y_density / 2.54) );
339 } else if ( cinfo.density_unit == 2 ) {
340 image.setDotsPerMeterX( int(100. * cinfo.X_density) );
341 image.setDotsPerMeterY( int(100. * cinfo.Y_density) );
342 }
343
344 iio->setImage(image);
345 iio->setStatus(0);
346 }
347
348 jpeg_destroy_decompress(&cinfo);
349 delete iod_src;
350}
351
352
353struct my_jpeg_destination_mgr : public jpeg_destination_mgr {
354 // Nothing dynamic - cannot rely on destruction over longjump
355 QImageIO* iio;
356 JOCTET buffer[max_buf];
357
358public:
359 my_jpeg_destination_mgr(QImageIO*);
360};
361
362
363#if defined(Q_C_CALLBACKS)
364extern "C" {
365#endif
366
367static
368void qt_init_destination(j_compress_ptr)
369{
370}
371
372static
373void qt_exit_on_error(j_compress_ptr cinfo, QIODevice* dev)
374{
375 if (dev->status() == IO_Ok) {
376 return;
377 } else {
378 // cinfo->err->msg_code = JERR_FILE_WRITE;
379 (*cinfo->err->error_exit)((j_common_ptr)cinfo);
380 }
381}
382
383static
384boolean qt_empty_output_buffer(j_compress_ptr cinfo)
385{
386 my_jpeg_destination_mgr* dest = (my_jpeg_destination_mgr*)cinfo->dest;
387 QIODevice* dev = dest->iio->ioDevice();
388
389 if ( dev->writeBlock( (char*)dest->buffer, max_buf ) != max_buf )
390 qt_exit_on_error(cinfo, dev);
391
392 dest->next_output_byte = dest->buffer;
393 dest->free_in_buffer = max_buf;
394
395#if defined(Q_OS_UNIXWARE)
396 return B_TRUE;
397#else
398 return TRUE;
399#endif
400}
401
402static
403void qt_term_destination(j_compress_ptr cinfo)
404{
405 my_jpeg_destination_mgr* dest = (my_jpeg_destination_mgr*)cinfo->dest;
406 QIODevice* dev = dest->iio->ioDevice();
407 Q_LONG n = max_buf - dest->free_in_buffer;
408
409 if ( dev->writeBlock( (char*)dest->buffer, n ) != n )
410 qt_exit_on_error(cinfo, dev);
411
412 dev->flush();
413
414 qt_exit_on_error(cinfo, dev);
415}
416
417#if defined(Q_C_CALLBACKS)
418}
419#endif
420
421
422inline
423my_jpeg_destination_mgr::my_jpeg_destination_mgr(QImageIO* iioptr)
424{
425 jpeg_destination_mgr::init_destination = qt_init_destination;
426 jpeg_destination_mgr::empty_output_buffer = qt_empty_output_buffer;
427 jpeg_destination_mgr::term_destination = qt_term_destination;
428 iio = iioptr;
429 next_output_byte = buffer;
430 free_in_buffer = max_buf;
431}
432
433
434static
435void write_jpeg_image(QImageIO* iio)
436{
437 QImage image = iio->image();
438
439 struct jpeg_compress_struct cinfo;
440 JSAMPROW row_pointer[1];
441 row_pointer[0] = 0;
442
443 struct my_jpeg_destination_mgr *iod_dest = new my_jpeg_destination_mgr(iio);
444 struct my_error_mgr jerr;
445
446 cinfo.err = jpeg_std_error(&jerr);
447
448 jerr.error_exit = my_error_exit;
449
450 if (!setjmp(jerr.setjmp_buffer)) {
451 jpeg_create_compress(&cinfo);
452
453 cinfo.dest = iod_dest;
454
455 cinfo.image_width = image.width();
456 cinfo.image_height = image.height();
457
458 QRgb* cmap=0;
459 bool gray=FALSE;
460 switch ( image.depth() ) {
461 case 1:
462 case 8:
463 cmap = image.colorTable();
464 gray = TRUE;
465 int i;
466 for (i=image.numColors(); gray && i--; ) {
467 gray = gray & ( qRed(cmap[i]) == qGreen(cmap[i]) &&
468 qRed(cmap[i]) == qBlue(cmap[i]) );
469 }
470 cinfo.input_components = gray ? 1 : 3;
471 cinfo.in_color_space = gray ? JCS_GRAYSCALE : JCS_RGB;
472 break;
473 case 32:
474 cinfo.input_components = 3;
475 cinfo.in_color_space = JCS_RGB;
476 }
477
478 jpeg_set_defaults(&cinfo);
479 int quality = iio->quality() >= 0 ? QMIN(iio->quality(),100) : 75;
480#if defined(Q_OS_UNIXWARE)
481 jpeg_set_quality(&cinfo, quality, B_TRUE /* limit to baseline-JPEG values */);
482 jpeg_start_compress(&cinfo, B_TRUE);
483#else
484 jpeg_set_quality(&cinfo, quality, TRUE /* limit to baseline-JPEG values */);
485 jpeg_start_compress(&cinfo, TRUE);
486#endif
487
488 row_pointer[0] = new uchar[cinfo.image_width*cinfo.input_components];
489 int w = cinfo.image_width;
490 while (cinfo.next_scanline < cinfo.image_height) {
491 uchar *row = row_pointer[0];
492 switch ( image.depth() ) {
493 case 1:
494 if (gray) {
495 uchar* data = image.scanLine(cinfo.next_scanline);
496 if ( image.bitOrder() == QImage::LittleEndian ) {
497 for (int i=0; i<w; i++) {
498 bool bit = !!(*(data + (i >> 3)) & (1 << (i & 7)));
499 row[i] = qRed(cmap[bit]);
500 }
501 } else {
502 for (int i=0; i<w; i++) {
503 bool bit = !!(*(data + (i >> 3)) & (1 << (7 -(i & 7))));
504 row[i] = qRed(cmap[bit]);
505 }
506 }
507 } else {
508 uchar* data = image.scanLine(cinfo.next_scanline);
509 if ( image.bitOrder() == QImage::LittleEndian ) {
510 for (int i=0; i<w; i++) {
511 bool bit = !!(*(data + (i >> 3)) & (1 << (i & 7)));
512 *row++ = qRed(cmap[bit]);
513 *row++ = qGreen(cmap[bit]);
514 *row++ = qBlue(cmap[bit]);
515 }
516 } else {
517 for (int i=0; i<w; i++) {
518 bool bit = !!(*(data + (i >> 3)) & (1 << (7 -(i & 7))));
519 *row++ = qRed(cmap[bit]);
520 *row++ = qGreen(cmap[bit]);
521 *row++ = qBlue(cmap[bit]);
522 }
523 }
524 }
525 break;
526 case 8:
527 if (gray) {
528 uchar* pix = image.scanLine(cinfo.next_scanline);
529 for (int i=0; i<w; i++) {
530 *row = qRed(cmap[*pix]);
531 ++row; ++pix;
532 }
533 } else {
534 uchar* pix = image.scanLine(cinfo.next_scanline);
535 for (int i=0; i<w; i++) {
536 *row++ = qRed(cmap[*pix]);
537 *row++ = qGreen(cmap[*pix]);
538 *row++ = qBlue(cmap[*pix]);
539 ++pix;
540 }
541 }
542 break;
543 case 32: {
544 QRgb* rgb = (QRgb*)image.scanLine(cinfo.next_scanline);
545 for (int i=0; i<w; i++) {
546 *row++ = qRed(*rgb);
547 *row++ = qGreen(*rgb);
548 *row++ = qBlue(*rgb);
549 ++rgb;
550 }
551 }
552 }
553 jpeg_write_scanlines(&cinfo, row_pointer, 1);
554 }
555
556 jpeg_finish_compress(&cinfo);
557 jpeg_destroy_compress(&cinfo);
558
559 iio->setStatus(0);
560 }
561
562 delete iod_dest;
563 delete [] row_pointer[0];
564}
565
566void qInitJpegIO()
567{
568 // Not much to go on - just 3 bytes: 0xFF, M_SOI, 0xFF
569 // Even the third is not strictly specified as required.
570 QImageIO::defineIOHandler("JPEG", "^\377\330\377", 0, read_jpeg_image, write_jpeg_image);
571}
572
573#endif
Note: See TracBrowser for help on using the repository browser.