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