KHtml

jpegloader.cpp
1 /*
2  This file is part of the KDE libraries
3 
4  Copyright (C) 2004 Maks Orlovich ([email protected])
5  Copyright (C) 2000 Dirk Mueller ([email protected])
6 
7  Permission is hereby granted, free of charge, to any person obtaining a copy
8  of this software and associated documentation files (the "Software"), to deal
9  in the Software without restriction, including without limitation the rights
10  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11  copies of the Software, and to permit persons to whom the Software is
12  furnished to do so, subject to the following conditions:
13 
14  The above copyright notice and this permission notice shall be included in
15  all copies or substantial portions of the Software.
16 
17  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20  AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
21  AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
22  CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23 
24 */
25 #include "jpegloader.h"
26 
27 //### FIXME: I removed all the fancy configury stuff. it needs to be put back
28 
29 #include <stdio.h>
30 #include <setjmp.h>
31 #include <QDate>
32 #include <QImage>
33 #include <QElapsedTimer>
34 
35 #include "imageloader.h"
36 #include "imagemanager.h"
37 
38 extern "C" {
39 #define XMD_H
40 #include <jpeglib.h>
41 #undef const
42 }
43 
44 #undef BUFFER_DEBUG
45 //#define BUFFER_DEBUG
46 
47 #undef JPEG_DEBUG
48 //#define JPEG_DEBUG
49 
50 namespace khtmlImLoad
51 {
52 
53 class JPEGLoader: public ImageLoader
54 {
55  struct Private;
56  friend struct Private;
57  Private *d;
58 public:
59  JPEGLoader();
60  ~JPEGLoader();
61  int processData(uchar *data, int length) override;
62 };
63 
64 ImageLoaderProvider::Type JPEGLoaderProvider::type()
65 {
66  return Efficient;
67 }
68 
69 ImageLoader *JPEGLoaderProvider::loaderFor(const QByteArray &prefix)
70 {
71  uchar *data = (uchar *)prefix.data();
72  if (prefix.size() < 3) {
73  return nullptr;
74  }
75 
76  if (data[0] == 0377 &&
77  data[1] == 0330 &&
78  data[2] == 0377) {
79  return new JPEGLoader;
80  }
81 
82  return nullptr;
83 }
84 
85 // -----------------------------------------------------------------------------
86 
87 struct khtml_error_mgr : public jpeg_error_mgr {
88  jmp_buf setjmp_buffer;
89 };
90 
91 extern "C" {
92 
93  static
94  void khtml_error_exit(j_common_ptr cinfo)
95  {
96  khtml_error_mgr *myerr = (khtml_error_mgr *) cinfo->err;
97  char buffer[JMSG_LENGTH_MAX];
98  (*cinfo->err->format_message)(cinfo, buffer);
99  qWarning("%s", buffer);
100  longjmp(myerr->setjmp_buffer, 1);
101  }
102 }
103 
104 static const int max_buf = 8192;
105 static const int max_consumingtime = 500;
106 
107 struct khtml_jpeg_source_mgr : public jpeg_source_mgr {
108  JOCTET buffer[max_buf];
109 
110  int valid_buffer_len;
111  size_t skip_input_bytes;
112  int ateof;
113  QElapsedTimer decoder_timestamp;
114  bool final_pass;
115  bool decoding_done;
116  bool do_progressive;
117 public:
118  khtml_jpeg_source_mgr();
119 };
120 
121 extern "C" {
122 
123  static
124  void khtml_j_decompress_dummy(j_decompress_ptr)
125  {
126  }
127 
128  static
129  boolean khtml_fill_input_buffer(j_decompress_ptr cinfo)
130  {
131 #ifdef BUFFER_DEBUG
132  qDebug("khtml_fill_input_buffer called!");
133 #endif
134 
135  khtml_jpeg_source_mgr *src = (khtml_jpeg_source_mgr *)cinfo->src;
136 
137  if (src->ateof) {
138  /* Insert a fake EOI marker - as per jpeglib recommendation */
139  src->buffer[0] = (JOCTET) 0xFF;
140  src->buffer[1] = (JOCTET) JPEG_EOI;
141  src->bytes_in_buffer = 2;
142  src->next_input_byte = (JOCTET *) src->buffer;
143 #ifdef BUFFER_DEBUG
144  qDebug("...returning true!");
145 #endif
146  return TRUE;
147  } else {
148  return FALSE; /* I/O suspension mode */
149  }
150  }
151 
152  static
153  void khtml_skip_input_data(j_decompress_ptr cinfo, long num_bytes)
154  {
155  if (num_bytes <= 0) {
156  return; /* required noop */
157  }
158 
159 #ifdef BUFFER_DEBUG
160  qDebug("khtml_skip_input_data (%d) called!", num_bytes);
161 #endif
162 
163  khtml_jpeg_source_mgr *src = (khtml_jpeg_source_mgr *)cinfo->src;
164  src->skip_input_bytes += num_bytes;
165 
166  unsigned int skipbytes = qMin(src->bytes_in_buffer, src->skip_input_bytes);
167 
168 #ifdef BUFFER_DEBUG
169  qDebug("skip_input_bytes is now %d", src->skip_input_bytes);
170  qDebug("skipbytes is now %d", skipbytes);
171  qDebug("valid_buffer_len is before %d", src->valid_buffer_len);
172  qDebug("bytes_in_buffer is %d", src->bytes_in_buffer);
173 #endif
174 
175  if (skipbytes < src->bytes_in_buffer) {
176  memmove(src->buffer, src->next_input_byte + skipbytes, src->bytes_in_buffer - skipbytes);
177  }
178 
179  src->bytes_in_buffer -= skipbytes;
180  src->valid_buffer_len = src->bytes_in_buffer;
181  src->skip_input_bytes -= skipbytes;
182 
183  /* adjust data for jpeglib */
184  cinfo->src->next_input_byte = (JOCTET *) src->buffer;
185  cinfo->src->bytes_in_buffer = (size_t) src->valid_buffer_len;
186 #ifdef BUFFER_DEBUG
187  qDebug("valid_buffer_len is afterwards %d", src->valid_buffer_len);
188  qDebug("skip_input_bytes is now %d", src->skip_input_bytes);
189 #endif
190  }
191 }
192 
193 khtml_jpeg_source_mgr::khtml_jpeg_source_mgr()
194 {
195  jpeg_source_mgr::init_source = khtml_j_decompress_dummy;
196  jpeg_source_mgr::fill_input_buffer = khtml_fill_input_buffer;
197  jpeg_source_mgr::skip_input_data = khtml_skip_input_data;
198  jpeg_source_mgr::resync_to_restart = jpeg_resync_to_restart;
199  jpeg_source_mgr::term_source = khtml_j_decompress_dummy;
200  bytes_in_buffer = 0;
201  valid_buffer_len = 0;
202  skip_input_bytes = 0;
203  ateof = 0;
204  next_input_byte = buffer;
205  final_pass = false;
206  decoding_done = false;
207 }
208 
209 struct JPEGLoader::Private {
210  int processData(uchar *data, int length);
211  Private();
212  ~Private();
213 
214  JPEGLoader *owner;
215 private:
216  int passNum;
217  uchar *scanline;
218 
219  enum {
220  Init,
221  readHeader,
222  startDecompress,
223  decompressStarted,
224  consumeInput,
225  prepareOutputScan,
226  doOutputScan,
227  readDone,
228  invalid
229  } state;
230 
231  // structs for the jpeglib
232  struct jpeg_decompress_struct cinfo;
233  struct khtml_error_mgr jerr;
234  struct khtml_jpeg_source_mgr jsrc;
235 };
236 
237 JPEGLoader::Private::Private()
238 {
239  scanline = nullptr;
240  passNum = 0;
241 
242  memset(&cinfo, 0, sizeof(cinfo));
243  cinfo.err = jpeg_std_error(&jerr);
244  jpeg_create_decompress(&cinfo);
245  cinfo.err = jpeg_std_error(&jerr);
246  jerr.error_exit = khtml_error_exit;
247  cinfo.src = &jsrc;
248  state = Init;
249 }
250 
251 JPEGLoader::Private::~Private()
252 {
253  delete[] scanline;
254  (void) jpeg_destroy_decompress(&cinfo);
255 }
256 
257 int JPEGLoader::Private::processData(uchar *buffer, int length)
258 {
259  if (jsrc.ateof) {
260 #ifdef JPEG_DEBUG
261  qDebug("ateof, eating");
262 #endif
263  return ImageLoader::Done;
264  }
265 
266  if (setjmp(jerr.setjmp_buffer)) {
267 #ifdef JPEG_DEBUG
268  qDebug("jump into state invalid");
269 #endif
270 
271  // this is fatal
272  return ImageLoader::Error;
273  }
274 
275  int consumed = qMin(length, max_buf - jsrc.valid_buffer_len);
276 
277 #ifdef BUFFER_DEBUG
278  qDebug("consuming %d bytes", consumed);
279 #endif
280 
281  // filling buffer with the new data
282  memcpy(jsrc.buffer + jsrc.valid_buffer_len, buffer, consumed);
283  jsrc.valid_buffer_len += consumed;
284 
285  if (jsrc.skip_input_bytes) {
286 #ifdef BUFFER_DEBUG
287  qDebug("doing skipping");
288  qDebug("valid_buffer_len %d", jsrc.valid_buffer_len);
289  qDebug("skip_input_bytes %d", jsrc.skip_input_bytes);
290 #endif
291  int skipbytes = qMin((size_t) jsrc.valid_buffer_len, jsrc.skip_input_bytes);
292 
293  if (skipbytes < jsrc.valid_buffer_len) {
294  memmove(jsrc.buffer, jsrc.buffer + skipbytes, jsrc.valid_buffer_len - skipbytes);
295  }
296 
297  jsrc.valid_buffer_len -= skipbytes;
298  jsrc.skip_input_bytes -= skipbytes;
299 
300  // still more bytes to skip
301  if (jsrc.skip_input_bytes) {
302  if (consumed <= 0) {
303  qDebug("ERROR!!!");
304  }
305  return consumed;
306  }
307  }
308 
309  cinfo.src->next_input_byte = (JOCTET *) jsrc.buffer;
310  cinfo.src->bytes_in_buffer = (size_t) jsrc.valid_buffer_len;
311 
312 #ifdef BUFFER_DEBUG
313  qDebug("buffer contains %d bytes", jsrc.valid_buffer_len);
314 #endif
315 
316  if (state == Init) {
317  if (jpeg_read_header(&cinfo, TRUE) != JPEG_SUSPENDED) {
318  state = startDecompress;
319 
320  // libJPEG can scale down 2x, 4x, and 8x,
321  // so do this for oversize images.
322  int scaleDown = 1;
323  while (scaleDown <= 8 && !ImageManager::isAcceptableSize(
324  cinfo.image_width / scaleDown, cinfo.image_height / scaleDown)) {
325  scaleDown *= 2;
326  }
327 
328  cinfo.scale_denom *= scaleDown;
329 
330  if (scaleDown > 8) {
331  // Still didn't fit... Abort.
332  return ImageLoader::Error;
333  }
334  }
335  }
336 
337  if (state == startDecompress) {
338  jsrc.do_progressive = jpeg_has_multiple_scans(&cinfo);
339  if (jsrc.do_progressive) {
340  cinfo.buffered_image = TRUE;
341  } else {
342  cinfo.buffered_image = FALSE;
343  }
344  // setup image sizes
345  jpeg_calc_output_dimensions(&cinfo);
346 
347  if (cinfo.jpeg_color_space == JCS_YCbCr) {
348  cinfo.out_color_space = JCS_RGB;
349  }
350 
351  if (cinfo.jpeg_color_space == JCS_YCCK) {
352  cinfo.out_color_space = JCS_CMYK;
353  }
354 
355  cinfo.do_fancy_upsampling = TRUE;
356  cinfo.do_block_smoothing = FALSE;
357  cinfo.quantize_colors = FALSE;
358 
359  // false: IO suspension
360  if (jpeg_start_decompress(&cinfo)) {
361  ImageFormat f;
362  if (cinfo.output_components == 3 || cinfo.output_components == 4) {
363  f.type = ImageFormat::Image_RGB_32;
364  scanline = new uchar[cinfo.output_width * 4];
365 
366  } else if (cinfo.output_components == 1) {
367  f.greyscaleSetup();
368  scanline = new uchar[cinfo.output_width];
369  }
370  // ### else return Error?
371 
372  owner->notifySingleFrameImage(cinfo.output_width, cinfo.output_height, f);
373 
374 #ifdef JPEG_DEBUG
375  qDebug("will create a picture %d/%d in size", cinfo.output_width, cinfo.output_height);
376 #endif
377 
378 #ifdef JPEG_DEBUG
379  qDebug("ok, going to decompressStarted");
380 #endif
381 
382  jsrc.decoder_timestamp.start();
383  state = jsrc.do_progressive ? decompressStarted : doOutputScan;
384  }
385  }
386 
387  if (state == decompressStarted) {
388  state = (!jsrc.final_pass && jsrc.decoder_timestamp.elapsed() < max_consumingtime)
389  ? consumeInput : prepareOutputScan;
390  }
391 
392  if (state == consumeInput) {
393  int retval;
394 
395  do {
396  retval = jpeg_consume_input(&cinfo);
397  } while (retval != JPEG_SUSPENDED && retval != JPEG_REACHED_EOI);
398 
399  if (jsrc.decoder_timestamp.elapsed() > max_consumingtime || jsrc.final_pass ||
400  retval == JPEG_REACHED_EOI || retval == JPEG_REACHED_SOS) {
401  state = prepareOutputScan;
402  }
403  }
404 
405  if (state == prepareOutputScan) {
406  jsrc.decoder_timestamp.restart();
407  if (jpeg_start_output(&cinfo, cinfo.input_scan_number)) {
408  state = doOutputScan;
409  }
410  }
411 
412  if (state == doOutputScan) {
413  if (!scanline || jsrc.decoding_done) {
414 #ifdef JPEG_DEBUG
415  qDebug("complete in doOutputscan, eating..");
416 #endif
417  return consumed;
418  }
419  uchar *lines[1] = {scanline};
420  //int oldoutput_scanline = cinfo.output_scanline;
421 
422  //Decode and feed line-by-line
423  while (cinfo.output_scanline < cinfo.output_height) {
424  if (!jpeg_read_scanlines(&cinfo, lines, 1)) {
425  break;
426  }
427 
428  if (cinfo.output_components == 3) {
429  // Expand 24->32 bpp.
430  uchar *in = scanline + cinfo.output_width * 3;
431  QRgb *out = (QRgb *)scanline;
432 
433  for (uint i = cinfo.output_width; i--;) {
434  in -= 3;
435  out[i] = qRgb(in[0], in[1], in[2]);
436  }
437  } else if (cinfo.out_color_space == JCS_CMYK) {
438  uchar *in = scanline + cinfo.output_width * 4;
439  QRgb *out = (QRgb *) scanline;
440 
441  for (uint i = cinfo.output_width; i--;) {
442  in -= 4;
443  int k = in[3];
444  out[i] = qRgb(k * in[0] / 255, k * in[1] / 255, k * in[2] / 255);
445  }
446  }
447 
448  owner->notifyScanline(passNum + 1, scanline);
449  } //per-line scan
450 
451  if (cinfo.output_scanline >= cinfo.output_height) {
452  passNum++;
453 
454  if (jsrc.do_progressive) {
455  jpeg_finish_output(&cinfo);
456  jsrc.final_pass = jpeg_input_complete(&cinfo);
457  jsrc.decoding_done = jsrc.final_pass && cinfo.input_scan_number == cinfo.output_scan_number;
458  } else {
459  jsrc.decoding_done = true;
460  }
461 
462  if (passNum > ImageLoader::FinalVersionID) {
463  qWarning("JPEG Decoder: Too many interlacing passes needed");
464  jsrc.decoding_done = true; //Force exit
465  }
466 
467 #ifdef JPEG_DEBUG
468  qDebug("one pass is completed, final_pass = %d, dec_done: %d, complete: %d",
469  jsrc.final_pass, jsrc.decoding_done, jpeg_input_complete(&cinfo));
470 #endif
471  if (!jsrc.decoding_done) {
472 #ifdef JPEG_DEBUG
473  qDebug("starting another one, input_scan_number is %d/%d", cinfo.input_scan_number,
474  cinfo.output_scan_number);
475 #endif
476  jsrc.decoder_timestamp.restart();
477  state = decompressStarted;
478  }
479  }
480  }
481 
482  if (state == doOutputScan && jsrc.decoding_done) {
483 #ifdef JPEG_DEBUG
484  qDebug("input is complete, cleaning up, returning..");
485 #endif
486 
487  jsrc.ateof = true;
488 
489  (void) jpeg_finish_decompress(&cinfo);
490  (void) jpeg_destroy_decompress(&cinfo);
491 
492  state = readDone;
493 
494  return Done;
495  }
496 
497 #ifdef BUFFER_DEBUG
498  qDebug("valid_buffer_len is now %d", jsrc.valid_buffer_len);
499  qDebug("bytes_in_buffer is now %d", jsrc.bytes_in_buffer);
500  qDebug("consumed %d bytes", consumed);
501 #endif
502  if (jsrc.bytes_in_buffer && jsrc.buffer != jsrc.next_input_byte) {
503  memmove(jsrc.buffer, jsrc.next_input_byte, jsrc.bytes_in_buffer);
504  }
505  jsrc.valid_buffer_len = jsrc.bytes_in_buffer;
506  return consumed;
507 }
508 
509 JPEGLoader::JPEGLoader()
510 {
511  d = new Private;
512  d->owner = this;
513 }
514 
515 JPEGLoader::~JPEGLoader()
516 {
517  delete d;
518 }
519 
520 int JPEGLoader::processData(uchar *data, int length)
521 {
522  return d->processData(data, length);
523 }
524 
525 }
526 
if(recurs()&&!first)
char * data()
int size() const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2021 The KDE developers.
Generated on Mon Oct 25 2021 22:48:16 by doxygen 1.8.11 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.