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