File indexing completed on 2024-05-12 15:38:28

0001 /*
0002     Large image load library -- PNG decoder
0003 
0004     Copyright (C) 2004 Maksim 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 "pngloader.h"
0026 #include "imageloader.h"
0027 #include "imagemanager.h"
0028 
0029 #include <png.h>
0030 
0031 namespace khtmlImLoad
0032 {
0033 
0034 class PNGLoader;
0035 /**
0036 Paranoia: since g++ may very well store "this" in a register, we
0037 use and external volatile to refer to our instance in case libPNG longjumps
0038 on us. We also use this variable to find where to map libPNG's callbacks.
0039 */
0040 static volatile PNGLoader *curLoader;
0041 
0042 class PNGLoader: public ImageLoader
0043 {
0044 private:
0045     png_structp pngReadStruct;
0046     png_infop   pngInfoStruct;
0047 
0048     bool        libPngError;
0049     bool        done;
0050 
0051     bool        interlaced;
0052 
0053     png_uint_32  width;
0054     png_uint_32  height;
0055     unsigned int depth;
0056 
0057     unsigned char *scanlineBuf; //Used only for interlaced images
0058 
0059     /**
0060      Dispatch hooks for libPNG callbacks -- call instance methods,
0061      and make parameters slightly more our-style
0062     */
0063     static void dispHaveInfo(png_structp, png_infop)
0064     {
0065         const_cast<PNGLoader *>(curLoader)->haveInfo();
0066     }
0067 
0068     static void dispHaveRow(png_structp, png_bytep row, png_uint_32 rowNum, int pass)
0069     {
0070         const_cast<PNGLoader *>(curLoader)->haveRow(rowNum, pass, row);
0071     }
0072 
0073     static void dispHaveEnd(png_structp, png_infop)
0074     {
0075         const_cast<PNGLoader *>(curLoader)->haveEnd();
0076     }
0077 
0078     /**
0079      Our implementation of libPNG callbacks
0080     */
0081     void haveInfo()
0082     {
0083         int bitDepth, colorType, interlaceType;
0084 
0085         png_get_IHDR(pngReadStruct, pngInfoStruct, &width, &height, &bitDepth,
0086                      &colorType, &interlaceType, nullptr, nullptr);
0087 
0088         if (!ImageManager::isAcceptableSize(width, height)) {
0089             libPngError = true;
0090             return;
0091         }
0092 
0093         //Ask libPNG to change bit depths we don't support
0094         if (bitDepth < 8)
0095 #if PNG_LIBPNG_VER < 10400
0096             png_set_gray_1_2_4_to_8(pngReadStruct);
0097 #else
0098             png_set_expand_gray_1_2_4_to_8(pngReadStruct);
0099 #endif
0100 
0101         if (bitDepth > 8) {
0102             png_set_strip_16(pngReadStruct);
0103         }
0104 
0105         //Some images (basically, only paletted ones) may have alpha
0106         //included as part of a tRNS chunk. We want to convert that to regular alpha
0107         //channel..
0108         bool haveTRNS = false;
0109         if (png_get_valid(pngReadStruct, pngInfoStruct, PNG_INFO_tRNS)) {
0110             png_set_tRNS_to_alpha(pngReadStruct);
0111             haveTRNS = true;
0112 
0113             if (colorType == PNG_COLOR_TYPE_RGB) {
0114                 colorType =  PNG_COLOR_TYPE_RGB_ALPHA;    //Paranoia..
0115             } else if (colorType == PNG_COLOR_TYPE_GRAY) {
0116                 colorType = PNG_COLOR_TYPE_GRAY_ALPHA;
0117             }
0118         }
0119 
0120         ImageFormat imFrm;
0121 
0122         //Prepare for mapping from colorType to our format descriptors.
0123         switch (colorType) {
0124         case PNG_COLOR_TYPE_GRAY:
0125             imFrm.greyscaleSetup();
0126             break;
0127         case PNG_COLOR_TYPE_GRAY_ALPHA:
0128             //We don't natively support 8-bit plus alpha, so ask libPNG to expand it out to RGB
0129             png_set_gray_to_rgb(pngReadStruct);
0130             imFrm.type = ImageFormat::Image_ARGB_32;
0131             break;
0132         case PNG_COLOR_TYPE_PALETTE:
0133             //For now, we handle paletted images as RGB or ARGB
0134             //### TODO: handle non-alpha paletted images with a sufficiently small palette as
0135             //paletted
0136             imFrm.type = haveTRNS ? ImageFormat::Image_ARGB_32 : ImageFormat::Image_RGB_32;
0137             png_set_palette_to_rgb(pngReadStruct);
0138             break;
0139         case PNG_COLOR_TYPE_RGB:
0140             imFrm.type = ImageFormat::Image_RGB_32;
0141             break;
0142         case PNG_COLOR_TYPE_RGB_ALPHA:
0143             imFrm.type = ImageFormat::Image_ARGB_32;
0144             break;
0145         default:
0146             //Huh?
0147             libPngError = true;
0148             return;
0149         }
0150 
0151         //Configure padding/byte swapping if need be (32-bit images)
0152         //We want a 32-bit value with ARGB.
0153         //This means that for little-endian, in memory we should have BGRA,
0154         //and for big-endian, well, ARGB
0155         if (imFrm.type == ImageFormat::Image_RGB_32) {
0156             //Need fillers, plus perhaps BGR swapping for non-alpha
0157 #if Q_BYTE_ORDER == Q_BIG_ENDIAN
0158             png_set_filler(pngReadStruct, 0xff, PNG_FILLER_BEFORE);
0159 #else
0160             png_set_filler(pngReadStruct, 0xff, PNG_FILLER_AFTER);
0161             png_set_bgr(pngReadStruct);
0162 #endif
0163         } else if (imFrm.type == ImageFormat::Image_ARGB_32) {
0164 #if Q_BYTE_ORDER == Q_BIG_ENDIAN
0165             png_set_swap_alpha(pngReadStruct); //ARGB, not RGBA
0166 #else
0167             png_set_bgr(pngReadStruct);        //BGRA
0168 #endif
0169         }
0170 
0171         //Remember depth, for our own use
0172         depth = imFrm.depth();
0173 
0174         //handle interlacing
0175         if (interlaceType != PNG_INTERLACE_NONE) {
0176             interlaced  = true;
0177             scanlineBuf = new unsigned char[depth * width];
0178             png_set_interlace_handling(pngReadStruct);
0179 
0180             // Give up on premultiply in this case..
0181             if (imFrm.type == ImageFormat::Image_ARGB_32) {
0182                 imFrm.type = ImageFormat::Image_ARGB_32_DontPremult;
0183             }
0184         }
0185 
0186         notifySingleFrameImage(width, height, imFrm);
0187 
0188         //OK, time to start input
0189         png_read_update_info(pngReadStruct, pngInfoStruct);
0190     }
0191 
0192     void haveRow(unsigned int rowNum, int pass, unsigned char *data)
0193     {
0194         if (interlaced) {
0195             Q_ASSERT(png_get_bit_depth(pngReadStruct, pngInfoStruct) <= depth * 8);
0196             requestScanline(rowNum, scanlineBuf);
0197             png_progressive_combine_row(pngReadStruct, scanlineBuf, data);
0198             notifyScanline(pass + 1, scanlineBuf);
0199         } else {
0200             notifyScanline(pass + 1, data);
0201         }
0202     }
0203 
0204     void haveEnd()
0205     {
0206         done = true;
0207     }
0208 
0209 public:
0210     PNGLoader()
0211     {
0212         done        = false;
0213         libPngError = false;
0214 
0215         interlaced  = false;
0216         scanlineBuf = nullptr;
0217 
0218         //Setup the basic structures.
0219         //### I think I want to pass the handlers to shut it up?
0220         pngReadStruct = png_create_read_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr);
0221 
0222         pngInfoStruct = png_create_info_struct(pngReadStruct);
0223 
0224         //Prepare for progressive loading
0225         png_set_progressive_read_fn(pngReadStruct, nullptr, dispHaveInfo, dispHaveRow, dispHaveEnd);
0226     }
0227 
0228     ~PNGLoader()
0229     {
0230         delete[] scanlineBuf;
0231 
0232         png_destroy_read_struct(&pngReadStruct, &pngInfoStruct, nullptr);
0233         //### CHECKME!
0234     }
0235 
0236     int processData(uchar *data, int length) override
0237     {
0238         if (done) {
0239             return Done;
0240         }
0241 
0242         //This bit takes care of dealing with the libPNG error handling
0243         if (libPngError) {
0244             return Error;
0245         }
0246 
0247         curLoader = this;
0248         if (setjmp(png_jmpbuf(pngReadStruct))) {
0249             curLoader->libPngError = true;
0250             return Error;
0251         }
0252 
0253         //OK, now we can actually do work.... Push data to libPNG,
0254         //it will use callbacks
0255         //### time limiting?
0256         png_process_data(pngReadStruct, pngInfoStruct, data, length);
0257 
0258         return length;
0259     }
0260 };
0261 
0262 ImageLoaderProvider::Type PNGLoaderProvider::type()
0263 {
0264     return Efficient;
0265 }
0266 
0267 ImageLoader *PNGLoaderProvider::loaderFor(const QByteArray &prefix)
0268 {
0269     uchar *data = (uchar *)prefix.data();
0270     if (prefix.size() < 8) {
0271         return nullptr;
0272     }
0273 
0274     if (data[0] == 0x89 &&
0275             data[1] == 'P'  &&
0276             data[2] == 'N'  &&
0277             data[3] == 'G'  &&
0278             data[4] == 0x0D &&
0279             data[5] == 0x0A &&
0280             data[6] == 0x1A &&
0281             data[7] == 0x0A) {
0282         return new PNGLoader;
0283     }
0284 
0285     return nullptr;
0286 }
0287 
0288 }
0289