File indexing completed on 2024-12-08 09:38:54

0001 /*
0002     Large image displaying library.
0003 
0004     Copyright (C) 2004 Maks Orlovich (maksim@kde.org)
0005 
0006     Permission is hereby granted, free of charge, to any person obtaining a copy
0007     of this software and associated documentation files (the "Software"), to deal
0008     in the Software without restriction, including without limitation the rights
0009     to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
0010     copies of the Software, and to permit persons to whom the Software is
0011     furnished to do so, subject to the following conditions:
0012 
0013     The above copyright notice and this permission notice shall be included in
0014     all copies or substantial portions of the Software.
0015 
0016     THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
0017     IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
0018     FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
0019     AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
0020     AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
0021     CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
0022 
0023 */
0024 
0025 #include "image.h"
0026 #include "imageloader.h"
0027 #include "imagemanager.h"
0028 #include "imageowner.h"
0029 #include "pixmapplane.h"
0030 #include "rawimageplane.h"
0031 #include "scaledimageplane.h"
0032 
0033 #include <QPainter>
0034 #include <limits.h>
0035 #include "khtml_debug.h"
0036 
0037 namespace khtmlImLoad
0038 {
0039 
0040 Image::Image(ImageOwner *_owner)
0041 {
0042     owner       = _owner;
0043     loader      = nullptr;
0044     loaderPlane = nullptr;
0045     original    = nullptr;
0046     loaderScanline = 0;
0047 
0048     fullyDecoded = false;
0049     inError      = false;
0050 
0051     width = height = 0;
0052     animationAdvice = KHTMLSettings::KAnimationEnabled;
0053 
0054     noUpdates();
0055 }
0056 
0057 Image::~Image()
0058 {
0059     ImageManager::updater()->destroyed(this);
0060     delete   loader;
0061     delete   original;
0062     assert(scaled.isEmpty());
0063 }
0064 
0065 void Image::requestUpdate(int line)
0066 {
0067     updatesStartLine = qMin(line, updatesStartLine);
0068     updatesEndLine   = qMax(line, updatesEndLine);
0069     if (!updatesPending) {
0070         updatesPending = true;
0071         ImageManager::updater()->haveUpdates(this);
0072     }
0073 }
0074 
0075 void Image::noUpdates()
0076 {
0077     updatesPending   = false;
0078     updatesStartLine = INT_MAX;
0079     updatesEndLine   = 0;
0080 }
0081 
0082 void Image::notifyPerformUpdate()
0083 {
0084     owner->imageChange(this, QRect(0, updatesStartLine,
0085                                    width, updatesEndLine - updatesStartLine + 1));
0086     noUpdates();
0087 }
0088 
0089 void Image::notifyFrameChange()
0090 {
0091     owner->imageChange(this, QRect(0, 0, width, height));
0092 }
0093 
0094 void Image::loadError()
0095 {
0096     inError = true;
0097     delete loader;
0098     loader = nullptr;
0099 
0100     //Make sure to call this last, since we may get deleted here.
0101     owner->imageError(this);
0102 }
0103 
0104 bool Image::processData(uchar *data, int length)
0105 {
0106     if (inError) {
0107         return false;
0108     }
0109 
0110     //...if we don't have a loder
0111     if (!loader) {
0112         if (original) {
0113             //We could have already discarded it as we're all done. remind the caller about it
0114             return false;
0115         } else {
0116             //need to to do auto detection... so append all the data into a buffer
0117             int oldSize = bufferPreDetect.size();
0118             bufferPreDetect.resize(oldSize + length);
0119             memcpy(bufferPreDetect.data() + oldSize, data, length);
0120 
0121             //Attempt to create a loader
0122             loader = ImageManager::loaderDatabase()->loaderFor(bufferPreDetect);
0123 
0124             //if can't, return...
0125             if (!loader) {
0126                 //if there is more than 4K of data,
0127                 //and we see no use for it, abort.
0128                 if (bufferPreDetect.size() > 4096) {
0129                     loadError();
0130                     return false;
0131                 }
0132                 return true;
0133             }
0134 
0135             loader->setImage(this);
0136 
0137             //All the data is now in the buffer
0138             length = 0;
0139         }
0140     }
0141 
0142     int pos = 0, stat = 0;
0143 
0144     //If we got this far, we have the loader.
0145     //just feed it any buffered data, and the new data.
0146     if (!bufferPreDetect.isEmpty()) {
0147         do {
0148             stat = loader->processData(reinterpret_cast<uchar *>(bufferPreDetect.data() + pos),
0149                                        bufferPreDetect.size() - pos);
0150             if (stat == bufferPreDetect.size() - pos) {
0151                 break;
0152             }
0153 
0154             pos += stat;
0155         } while (stat > 0);
0156         bufferPreDetect.resize(0);
0157     }
0158 
0159     if (length) { //if there is something we did not feed from the buffer already..
0160         pos = 0;
0161         do {
0162             stat = loader->processData(data + pos, length - pos);
0163 
0164             if (stat == length - pos) {
0165                 break;
0166             }
0167 
0168             pos  += stat;
0169         } while (stat > 0);
0170     }
0171 
0172     //If we just finished decoding...
0173     if (stat == ImageLoader::Done) {
0174         fullyDecoded = true;
0175         owner->imageDone(this);
0176         return false;
0177     }
0178 
0179     if (stat == ImageLoader::Error) {
0180         loadError();
0181         return false;
0182     }
0183 
0184     return true; //Need more stuff
0185 }
0186 
0187 void Image::processEOF()
0188 {
0189     if (inError) { //Input error already - nothing to do
0190         return;
0191     }
0192 
0193     //If no loader detected, and we're at EOF, it's an error
0194     if (!loader) {
0195         loadError();
0196         return;
0197     }
0198 
0199     //Otherwise, simply tell the loader, and check whether we decoded all right
0200     bool decodedOK = loader->processEOF() == ImageLoader::Done;
0201 
0202     //... and get rid of it
0203     delete loader;
0204     loader = nullptr;
0205 
0206     if (!decodedOK) {
0207         loadError();
0208     } else {
0209         if (original && original->animProvider) {
0210             original->animProvider->setShowAnimations(animationAdvice);
0211         }
0212 
0213         fullyDecoded = true;
0214         owner->imageDone(this);
0215     }
0216 }
0217 
0218 void Image::notifyImageInfo(int _width, int _height)
0219 {
0220     if (!ImageManager::isAcceptableSize(_width, _height)) {
0221         qCWarning(KHTML_LOG) << "ImageLoader somehow fed us an illegal size, killing it!";
0222         loadError();
0223         return;
0224     }
0225     width  = _width;
0226     height = _height;
0227 
0228     owner->imageHasGeometry(this, width, height);
0229 }
0230 
0231 void Image::notifyAppendFrame(int fwidth, int fheight, const ImageFormat &format)
0232 {
0233     if (!ImageManager::isAcceptableSize(fwidth, fheight)) {
0234         qCWarning(KHTML_LOG) << "ImageLoader somehow fed us an illegal size, killing it!";
0235         loadError();
0236         return;
0237     }
0238 
0239     //Create the new frame.
0240     QImage image = format.makeImage(fwidth, fheight);
0241     //IMPORTANT: we use image.width(), etc., below for security/paranoia
0242     //reasons -- so we e.g. end up with a size 0 image if QImage overflow
0243     //checks kick in, etc. This is on top of the policy enforcement
0244     //enough, in case someone breaks it or such
0245     RawImagePlane *iplane = new RawImagePlane(image.width(), image.height());
0246     iplane->image         = image;
0247     iplane->format        = format;
0248     PixmapPlane   *plane  = new PixmapPlane(image.width(), image.height(), iplane);
0249 
0250     if (loaderPlane) { //Had a previous plane
0251         loaderPlane->nextFrame = plane;
0252         loaderPlane            = plane;
0253     } else {
0254         //Created the first one
0255         loaderPlane = original = plane;
0256     }
0257 
0258     //Go through the list of scaled sizes, and build frames for that.
0259 
0260     loaderScanline = 0;
0261 }
0262 
0263 static inline unsigned char premulComponent(unsigned original, unsigned alpha)
0264 {
0265     unsigned product = original * alpha; // this is conceptually 255 * intended value.
0266     return (unsigned char)((product + product / 256 + 128) / 256);
0267 }
0268 
0269 void Image::notifyQImage(uchar version, const QImage *image)
0270 {
0271     RawImagePlane *plane = static_cast<RawImagePlane *>(loaderPlane->parent);
0272 
0273     plane->image = *image;
0274 
0275     //Set the versions.
0276     for (unsigned int i = 0; i < plane->height; i++) {
0277         plane->versions[i] = version;
0278     }
0279 
0280     updatesStartLine = 0;
0281     updatesEndLine   = plane->height;
0282     if (!updatesPending) {
0283         updatesPending = true;
0284         ImageManager::updater()->haveUpdates(this);
0285     }
0286 }
0287 
0288 void Image::notifyScanline(uchar version, uchar *data)
0289 {
0290     RawImagePlane *plane = static_cast<RawImagePlane *>(loaderPlane->parent);
0291     if (loaderScanline >= plane->height) {
0292         return;
0293     }
0294 
0295     //Load the data in..
0296     if (plane->format.type != ImageFormat::Image_ARGB_32) {
0297         //Can just copy
0298         std::memcpy(plane->image.scanLine(loaderScanline), data,
0299                     plane->format.depth() * plane->image.width());
0300     } else {
0301         //Premultiply. Note that this is assuming that any combination
0302         //Will not actually look at the pixel.
0303         QRgb *dst = reinterpret_cast<QRgb *>(plane->image.scanLine(loaderScanline));
0304         uchar *src = data;
0305         int planeImageWidth = plane->image.width();
0306         for (int x = 0; x < planeImageWidth; ++x) {
0307 #if Q_BYTE_ORDER == Q_BIG_ENDIAN
0308             unsigned a = src[0];
0309             unsigned r = src[1];
0310             unsigned g = src[2];
0311             unsigned b = src[3];
0312 #else
0313             unsigned a = src[3];
0314             unsigned r = src[2];
0315             unsigned g = src[1];
0316             unsigned b = src[0];
0317 #endif
0318             dst[x]  = qRgba(premulComponent(r, a), premulComponent(g, a),
0319                             premulComponent(b, a), a);
0320             src += 4;
0321         }
0322     }
0323 
0324     //Set the version.
0325     plane->versions[loaderScanline] = version;
0326 
0327     //calculate update region. Note that we ignore scaling when doing this, and just emit the
0328     //scaled version when delivering the event. It's easier this way, and we don't have to worry
0329     //about what happens to updates in case of change of scaling.
0330     //We only do this for the first frame --- other stuff will only
0331     //be full-frame switches from the animation controller
0332     if (loaderPlane == original) {
0333         requestUpdate(loaderScanline);
0334     }
0335 
0336     loaderScanline++;
0337     if (loaderScanline == plane->height) { //Next pass of progressive image
0338         loaderScanline = 0;
0339     }
0340 }
0341 
0342 void Image::requestScanline(unsigned int lineNum, uchar *lineBuf)
0343 {
0344     RawImagePlane *plane = static_cast<RawImagePlane *>(loaderPlane->parent);
0345     if (lineNum >= plane->height) {
0346         return;
0347     }
0348 
0349     std::memcpy(lineBuf, plane->image.scanLine(lineNum),
0350                 plane->image.width() * plane->format.depth());
0351 }
0352 
0353 QSize Image::size() const
0354 {
0355     return QSize(width, height);
0356 }
0357 
0358 bool Image::complete() const
0359 {
0360     //### FIXME: this isn't quite right in case of animation
0361     //controller -- e.g. if animation is disabled, we only
0362     //care about frame 1.
0363     return fullyDecoded;
0364 }
0365 
0366 static QPair<int, int> trSize(QSize size)
0367 {
0368     return qMakePair(size.width(), size.height());
0369 }
0370 
0371 PixmapPlane *Image::getSize(QSize size)
0372 {
0373     // If we're empty, we use ourselves as a placeholder,
0374     // to avoid trying scaling a 0x0 image
0375     if (size == this->size() || this->size().isEmpty()) {
0376         return original;
0377     }
0378 
0379     return scaled.value(trSize(size));
0380 }
0381 
0382 void Image::derefSize(QSize size)
0383 {
0384     assert(original);
0385 
0386     if (size == this->size() || this->size().isEmpty()) {
0387         return;
0388     }
0389 
0390     QPair<int, int> key = trSize(size);
0391     PixmapPlane *plane = scaled.value(key);
0392     --plane->refCount;
0393     if (plane->refCount == 0) {
0394         delete plane;
0395         scaled.remove(key);
0396     }
0397 }
0398 
0399 void Image::refSize(QSize size)
0400 {
0401     assert(original);
0402 
0403     if (size == this->size() || this->size().isEmpty()) {
0404         return;
0405     }
0406 
0407     QPair<int, int> key = trSize(size);
0408     PixmapPlane *plane = scaled.value(key);
0409     if (plane) {
0410         ++plane->refCount;
0411     } else {
0412         // Note: ImagePainter enforces our maximum image size, much like the load code does,
0413         // so we don't have to worry about extreme size.
0414 
0415         // Compute scaling ratios. divide-by-zero should not happen
0416         // due to check above.
0417         double wRatio = size.width()  / double(width);
0418         double hRatio = size.height() / double(height);
0419 
0420         //Go through and make scaled planes for each position
0421         PixmapPlane *first = nullptr, *prev = nullptr;
0422 
0423         //### might need unification with ScaledImagePlane's size handling
0424         for (PixmapPlane *cur = original; cur; cur = cur->nextFrame) {
0425             int newWidth  = qRound(cur->width  * wRatio);
0426             int newHeight = qRound(cur->height * hRatio);
0427 
0428             // Make 100% sure we do it precisely for the original image
0429             if (cur->width == width) {
0430                 newWidth = size.width();
0431             }
0432             if (cur->height == height) {
0433                 newHeight = size.height();
0434             }
0435 
0436             // Ensure non-empty..
0437             if (newWidth  <= 0) {
0438                 newWidth  = 1;
0439             }
0440             if (newHeight <= 0) {
0441                 newHeight = 1;
0442             }
0443 
0444             ScaledImagePlane *splane = new ScaledImagePlane(
0445                 newWidth, newHeight,
0446                 static_cast<RawImagePlane *>(cur->parent));
0447             PixmapPlane      *plane  = new PixmapPlane(
0448                 newWidth, newHeight, splane);
0449             if (cur->animProvider) {
0450                 plane->animProvider = cur->animProvider->clone(plane);
0451             }
0452 
0453             if (prev) {
0454                 prev->nextFrame = plane;
0455             } else {
0456                 first           = plane;
0457             }
0458 
0459             prev            = plane;
0460         }
0461 
0462         first->refCount = 1;
0463         scaled[key]     = first;
0464     }
0465 }
0466 
0467 QImage *Image::qimage() const
0468 {
0469     if (!original || !original->parent) {
0470         return nullptr;
0471     }
0472 
0473     return &static_cast<RawImagePlane *>(original->parent)->image;
0474 }
0475 
0476 bool Image::hasAlpha() const
0477 {
0478     if (!original || !original->parent) {
0479         return false;
0480     }
0481     return original->parent->format.hasAlpha();
0482 }
0483 
0484 void Image::setShowAnimations(KHTMLSettings::KAnimationAdvice newAdvice)
0485 {
0486     if (animationAdvice != newAdvice) {
0487         animationAdvice = newAdvice;
0488         if (original && original->animProvider) {
0489             original->animProvider->setShowAnimations(newAdvice);
0490         }
0491     }
0492 }
0493 
0494 }
0495