KHtml

image.cpp
1 /*
2  Large image displaying library.
3 
4  Copyright (C) 2004 Maks Orlovich ([email protected])
5 
6  Permission is hereby granted, free of charge, to any person obtaining a copy
7  of this software and associated documentation files (the "Software"), to deal
8  in the Software without restriction, including without limitation the rights
9  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10  copies of the Software, and to permit persons to whom the Software is
11  furnished to do so, subject to the following conditions:
12 
13  The above copyright notice and this permission notice shall be included in
14  all copies or substantial portions of the Software.
15 
16  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19  AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
20  AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
21  CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22 
23 */
24 
25 #include "image.h"
26 #include "imageloader.h"
27 #include "imagemanager.h"
28 #include "imageowner.h"
29 #include "pixmapplane.h"
30 #include "rawimageplane.h"
31 #include "scaledimageplane.h"
32 
33 #include <QPainter>
34 #include <limits.h>
35 #include "khtml_debug.h"
36 
37 namespace khtmlImLoad
38 {
39 
41 {
42  owner = _owner;
43  loader = nullptr;
44  loaderPlane = nullptr;
45  original = nullptr;
46  loaderScanline = 0;
47 
48  fullyDecoded = false;
49  inError = false;
50 
51  width = height = 0;
52  animationAdvice = KHTMLSettings::KAnimationEnabled;
53 
54  noUpdates();
55 }
56 
58 {
59  ImageManager::updater()->destroyed(this);
60  delete loader;
61  delete original;
62  assert(scaled.isEmpty());
63 }
64 
65 void Image::requestUpdate(int line)
66 {
67  updatesStartLine = qMin(line, updatesStartLine);
68  updatesEndLine = qMax(line, updatesEndLine);
69  if (!updatesPending) {
70  updatesPending = true;
71  ImageManager::updater()->haveUpdates(this);
72  }
73 }
74 
75 void Image::noUpdates()
76 {
77  updatesPending = false;
78  updatesStartLine = INT_MAX;
79  updatesEndLine = 0;
80 }
81 
83 {
84  owner->imageChange(this, QRect(0, updatesStartLine,
85  width, updatesEndLine - updatesStartLine + 1));
86  noUpdates();
87 }
88 
90 {
91  owner->imageChange(this, QRect(0, 0, width, height));
92 }
93 
94 void Image::loadError()
95 {
96  inError = true;
97  delete loader;
98  loader = nullptr;
99 
100  //Make sure to call this last, since we may get deleted here.
101  owner->imageError(this);
102 }
103 
104 bool Image::processData(uchar *data, int length)
105 {
106  if (inError) {
107  return false;
108  }
109 
110  //...if we don't have a loder
111  if (!loader) {
112  if (original) {
113  //We could have already discarded it as we're all done. remind the caller about it
114  return false;
115  } else {
116  //need to to do auto detection... so append all the data into a buffer
117  int oldSize = bufferPreDetect.size();
118  bufferPreDetect.resize(oldSize + length);
119  memcpy(bufferPreDetect.data() + oldSize, data, length);
120 
121  //Attempt to create a loader
122  loader = ImageManager::loaderDatabase()->loaderFor(bufferPreDetect);
123 
124  //if can't, return...
125  if (!loader) {
126  //if there is more than 4K of data,
127  //and we see no use for it, abort.
128  if (bufferPreDetect.size() > 4096) {
129  loadError();
130  return false;
131  }
132  return true;
133  }
134 
135  loader->setImage(this);
136 
137  //All the data is now in the buffer
138  length = 0;
139  }
140  }
141 
142  int pos = 0, stat = 0;
143 
144  //If we got this far, we have the loader.
145  //just feed it any buffered data, and the new data.
146  if (!bufferPreDetect.isEmpty()) {
147  do {
148  stat = loader->processData(reinterpret_cast<uchar *>(bufferPreDetect.data() + pos),
149  bufferPreDetect.size() - pos);
150  if (stat == bufferPreDetect.size() - pos) {
151  break;
152  }
153 
154  pos += stat;
155  } while (stat > 0);
156  bufferPreDetect.resize(0);
157  }
158 
159  if (length) { //if there is something we did not feed from the buffer already..
160  pos = 0;
161  do {
162  stat = loader->processData(data + pos, length - pos);
163 
164  if (stat == length - pos) {
165  break;
166  }
167 
168  pos += stat;
169  } while (stat > 0);
170  }
171 
172  //If we just finished decoding...
173  if (stat == ImageLoader::Done) {
174  fullyDecoded = true;
175  owner->imageDone(this);
176  return false;
177  }
178 
179  if (stat == ImageLoader::Error) {
180  loadError();
181  return false;
182  }
183 
184  return true; //Need more stuff
185 }
186 
188 {
189  if (inError) { //Input error already - nothing to do
190  return;
191  }
192 
193  //If no loader detected, and we're at EOF, it's an error
194  if (!loader) {
195  loadError();
196  return;
197  }
198 
199  //Otherwise, simply tell the loader, and check whether we decoded all right
200  bool decodedOK = loader->processEOF() == ImageLoader::Done;
201 
202  //... and get rid of it
203  delete loader;
204  loader = nullptr;
205 
206  if (!decodedOK) {
207  loadError();
208  } else {
209  if (original && original->animProvider) {
210  original->animProvider->setShowAnimations(animationAdvice);
211  }
212 
213  fullyDecoded = true;
214  owner->imageDone(this);
215  }
216 }
217 
218 void Image::notifyImageInfo(int _width, int _height)
219 {
220  if (!ImageManager::isAcceptableSize(_width, _height)) {
221  qCWarning(KHTML_LOG) << "ImageLoader somehow fed us an illegal size, killing it!";
222  loadError();
223  return;
224  }
225  width = _width;
226  height = _height;
227 
228  owner->imageHasGeometry(this, width, height);
229 }
230 
231 void Image::notifyAppendFrame(int fwidth, int fheight, const ImageFormat &format)
232 {
233  if (!ImageManager::isAcceptableSize(fwidth, fheight)) {
234  qCWarning(KHTML_LOG) << "ImageLoader somehow fed us an illegal size, killing it!";
235  loadError();
236  return;
237  }
238 
239  //Create the new frame.
240  QImage image = format.makeImage(fwidth, fheight);
241  //IMPORTANT: we use image.width(), etc., below for security/paranoia
242  //reasons -- so we e.g. end up with a size 0 image if QImage overflow
243  //checks kick in, etc. This is on top of the policy enforcement
244  //enough, in case someone breaks it or such
245  RawImagePlane *iplane = new RawImagePlane(image.width(), image.height());
246  iplane->image = image;
247  iplane->format = format;
248  PixmapPlane *plane = new PixmapPlane(image.width(), image.height(), iplane);
249 
250  if (loaderPlane) { //Had a previous plane
251  loaderPlane->nextFrame = plane;
252  loaderPlane = plane;
253  } else {
254  //Created the first one
255  loaderPlane = original = plane;
256  }
257 
258  //Go through the list of scaled sizes, and build frames for that.
259 
260  loaderScanline = 0;
261 }
262 
263 static inline unsigned char premulComponent(unsigned original, unsigned alpha)
264 {
265  unsigned product = original * alpha; // this is conceptually 255 * intended value.
266  return (unsigned char)((product + product / 256 + 128) / 256);
267 }
268 
269 void Image::notifyQImage(uchar version, const QImage *image)
270 {
271  RawImagePlane *plane = static_cast<RawImagePlane *>(loaderPlane->parent);
272 
273  plane->image = *image;
274 
275  //Set the versions.
276  for (unsigned int i = 0; i < plane->height; i++) {
277  plane->versions[i] = version;
278  }
279 
280  updatesStartLine = 0;
281  updatesEndLine = plane->height;
282  if (!updatesPending) {
283  updatesPending = true;
284  ImageManager::updater()->haveUpdates(this);
285  }
286 }
287 
288 void Image::notifyScanline(uchar version, uchar *data)
289 {
290  RawImagePlane *plane = static_cast<RawImagePlane *>(loaderPlane->parent);
291  if (loaderScanline >= plane->height) {
292  return;
293  }
294 
295  //Load the data in..
296  if (plane->format.type != ImageFormat::Image_ARGB_32) {
297  //Can just copy
298  std::memcpy(plane->image.scanLine(loaderScanline), data,
299  plane->format.depth() * plane->image.width());
300  } else {
301  //Premultiply. Note that this is assuming that any combination
302  //Will not actually look at the pixel.
303  QRgb *dst = reinterpret_cast<QRgb *>(plane->image.scanLine(loaderScanline));
304  uchar *src = data;
305  int planeImageWidth = plane->image.width();
306  for (int x = 0; x < planeImageWidth; ++x) {
307 #if Q_BYTE_ORDER == Q_BIG_ENDIAN
308  unsigned a = src[0];
309  unsigned r = src[1];
310  unsigned g = src[2];
311  unsigned b = src[3];
312 #else
313  unsigned a = src[3];
314  unsigned r = src[2];
315  unsigned g = src[1];
316  unsigned b = src[0];
317 #endif
318  dst[x] = qRgba(premulComponent(r, a), premulComponent(g, a),
319  premulComponent(b, a), a);
320  src += 4;
321  }
322  }
323 
324  //Set the version.
325  plane->versions[loaderScanline] = version;
326 
327  //calculate update region. Note that we ignore scaling when doing this, and just emit the
328  //scaled version when delivering the event. It's easier this way, and we don't have to worry
329  //about what happens to updates in case of change of scaling.
330  //We only do this for the first frame --- other stuff will only
331  //be full-frame switches from the animation controller
332  if (loaderPlane == original) {
333  requestUpdate(loaderScanline);
334  }
335 
336  loaderScanline++;
337  if (loaderScanline == plane->height) { //Next pass of progressive image
338  loaderScanline = 0;
339  }
340 }
341 
342 void Image::requestScanline(unsigned int lineNum, uchar *lineBuf)
343 {
344  RawImagePlane *plane = static_cast<RawImagePlane *>(loaderPlane->parent);
345  if (lineNum >= plane->height) {
346  return;
347  }
348 
349  std::memcpy(lineBuf, plane->image.scanLine(lineNum),
350  plane->image.width() * plane->format.depth());
351 }
352 
354 {
355  return QSize(width, height);
356 }
357 
358 bool Image::complete() const
359 {
360  //### FIXME: this isn't quite right in case of animation
361  //controller -- e.g. if animation is disabled, we only
362  //care about frame 1.
363  return fullyDecoded;
364 }
365 
366 static QPair<int, int> trSize(QSize size)
367 {
368  return qMakePair(size.width(), size.height());
369 }
370 
371 PixmapPlane *Image::getSize(QSize size)
372 {
373  // If we're empty, we use ourselves as a placeholder,
374  // to avoid trying scaling a 0x0 image
375  if (size == this->size() || this->size().isEmpty()) {
376  return original;
377  }
378 
379  return scaled.value(trSize(size));
380 }
381 
382 void Image::derefSize(QSize size)
383 {
384  assert(original);
385 
386  if (size == this->size() || this->size().isEmpty()) {
387  return;
388  }
389 
390  QPair<int, int> key = trSize(size);
391  PixmapPlane *plane = scaled.value(key);
392  --plane->refCount;
393  if (plane->refCount == 0) {
394  delete plane;
395  scaled.remove(key);
396  }
397 }
398 
399 void Image::refSize(QSize size)
400 {
401  assert(original);
402 
403  if (size == this->size() || this->size().isEmpty()) {
404  return;
405  }
406 
407  QPair<int, int> key = trSize(size);
408  PixmapPlane *plane = scaled.value(key);
409  if (plane) {
410  ++plane->refCount;
411  } else {
412  // Note: ImagePainter enforces our maximum image size, much like the load code does,
413  // so we don't have to worry about extreme size.
414 
415  // Compute scaling ratios. divide-by-zero should not happen
416  // due to check above.
417  double wRatio = size.width() / double(width);
418  double hRatio = size.height() / double(height);
419 
420  //Go through and make scaled planes for each position
421  PixmapPlane *first = nullptr, *prev = nullptr;
422 
423  //### might need unification with ScaledImagePlane's size handling
424  for (PixmapPlane *cur = original; cur; cur = cur->nextFrame) {
425  int newWidth = qRound(cur->width * wRatio);
426  int newHeight = qRound(cur->height * hRatio);
427 
428  // Make 100% sure we do it precisely for the original image
429  if (cur->width == width) {
430  newWidth = size.width();
431  }
432  if (cur->height == height) {
433  newHeight = size.height();
434  }
435 
436  // Ensure non-empty..
437  if (newWidth <= 0) {
438  newWidth = 1;
439  }
440  if (newHeight <= 0) {
441  newHeight = 1;
442  }
443 
444  ScaledImagePlane *splane = new ScaledImagePlane(
445  newWidth, newHeight,
446  static_cast<RawImagePlane *>(cur->parent));
447  PixmapPlane *plane = new PixmapPlane(
448  newWidth, newHeight, splane);
449  if (cur->animProvider) {
450  plane->animProvider = cur->animProvider->clone(plane);
451  }
452 
453  if (prev) {
454  prev->nextFrame = plane;
455  } else {
456  first = plane;
457  }
458 
459  prev = plane;
460  }
461 
462  first->refCount = 1;
463  scaled[key] = first;
464  }
465 }
466 
468 {
469  if (!original || !original->parent) {
470  return nullptr;
471  }
472 
473  return &static_cast<RawImagePlane *>(original->parent)->image;
474 }
475 
476 bool Image::hasAlpha() const
477 {
478  if (!original || !original->parent) {
479  return false;
480  }
481  return original->parent->format.hasAlpha();
482 }
483 
484 void Image::setShowAnimations(KHTMLSettings::KAnimationAdvice newAdvice)
485 {
486  if (animationAdvice != newAdvice) {
487  animationAdvice = newAdvice;
488  if (original && original->animProvider) {
489  original->animProvider->setShowAnimations(newAdvice);
490  }
491  }
492 }
493 
494 }
495 
uchar * scanLine(int i)
bool complete() const
Returns true if the image has been fully loaded.
Definition: image.cpp:358
bool hasAlpha() const
Returns true if the image may have an alpha channel.
Definition: image.cpp:476
void processEOF()
Notifies the image that the data source is exhausted, in case it cares.
Definition: image.cpp:187
int width() const const
A scaled image plane pulls data from a RawImagePlane and resizes it.
virtual void imageError(Image *img)=0
Called to notify the owner that the image is broken.
virtual void imageChange(Image *img, QRect region)=0
Called to notify the owner that a portion has changed.
A pixmap plane is responsible for drawing data of an image plane.
Definition: pixmapplane.h:41
bool isEmpty() const const
QSize size() const
Returns the image&#39;s size.
Definition: image.cpp:353
void resize(int size)
virtual void imageHasGeometry(Image *img, int width, int height)=0
Called to notify the owner when the intrinic size is available.
bool processData(uchar *data, int length)
Provides new data for decoding.
Definition: image.cpp:104
int width() const const
~Image()
Cleans up.
Definition: image.cpp:57
Image(ImageOwner *owner)
Creates an image with a given owner; the owner will be notified about the repaint event...
Definition: image.cpp:40
void notifyPerformUpdate()
Called by the updater when the image should tell its owners about new changes.
Definition: image.cpp:82
virtual int processData(uchar *data, int length)=0
Decodes a portion of the image, and returns the appropriate status, or the number of bytes read...
void notifyFrameChange()
Called when animation frame changes, requesting the owner to repaint.
Definition: image.cpp:89
A raw image plane merely contains a QImage.
Definition: rawimageplane.h:36
void setShowAnimations(KHTMLSettings::KAnimationAdvice)
Enables or disables animations.
Definition: image.cpp:484
int height() const const
char * data()
int height() const const
int size() const const
QImage * qimage() const
Returns the image of basic content.
Definition: image.cpp:467
virtual void imageDone(Image *img)=0
Called to notify the owner that the image is done.
The users of Image&#39;s need to inherit off ImageOwner, in order to receive the information about their ...
Definition: imageowner.h:35
void setShowAnimations(KHTMLSettings::KAnimationAdvice)
Enables or disables animations.
virtual int processEOF()
This method is called to notify the decoder that the input is done, if the decoder has not already in...
Definition: imageloader.h:140
This file is part of the KDE documentation.
Documentation copyright © 1996-2020 The KDE developers.
Generated on Sat Sep 19 2020 22:46:01 by doxygen 1.8.11 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.