1 // Copyright 2014 PDFium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 // Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com 6 7 #include "core/fxcodec/codec/ccodec_pngmodule.h" 8 9 #include <algorithm> 10 11 #include "core/fxcodec/codec/codec_int.h" 12 #include "core/fxcodec/fx_codec.h" 13 #include "core/fxcrt/unowned_ptr.h" 14 #include "core/fxge/fx_dib.h" 15 #include "third_party/base/ptr_util.h" 16 17 #ifdef USE_SYSTEM_LIBPNG 18 #include <png.h> 19 #else 20 #include "third_party/libpng16/png.h" 21 #endif 22 23 #define PNG_ERROR_SIZE 256 24 25 class CPngContext : public CCodec_PngModule::Context { 26 public: 27 CPngContext(CCodec_PngModule* pModule, CCodec_PngModule::Delegate* pDelegate); 28 ~CPngContext() override; 29 30 png_structp m_pPng; 31 png_infop m_pInfo; 32 UnownedPtr<CCodec_PngModule> m_pModule; 33 UnownedPtr<CCodec_PngModule::Delegate> m_pDelegate; 34 void* (*m_AllocFunc)(unsigned int); 35 void (*m_FreeFunc)(void*); 36 char m_szLastError[PNG_ERROR_SIZE]; 37 }; 38 39 extern "C" { 40 41 static void _png_error_data(png_structp png_ptr, png_const_charp error_msg) { 42 if (png_get_error_ptr(png_ptr)) 43 strncpy((char*)png_get_error_ptr(png_ptr), error_msg, PNG_ERROR_SIZE - 1); 44 45 longjmp(png_jmpbuf(png_ptr), 1); 46 } 47 48 static void _png_warning_data(png_structp png_ptr, png_const_charp error_msg) {} 49 50 static void _png_load_bmp_attribute(png_structp png_ptr, 51 png_infop info_ptr, 52 CFX_DIBAttribute* pAttribute) { 53 if (pAttribute) { 54 #if defined(PNG_pHYs_SUPPORTED) 55 pAttribute->m_nXDPI = png_get_x_pixels_per_meter(png_ptr, info_ptr); 56 pAttribute->m_nYDPI = png_get_y_pixels_per_meter(png_ptr, info_ptr); 57 png_uint_32 res_x, res_y; 58 int unit_type; 59 png_get_pHYs(png_ptr, info_ptr, &res_x, &res_y, &unit_type); 60 switch (unit_type) { 61 case PNG_RESOLUTION_METER: 62 pAttribute->m_wDPIUnit = FXCODEC_RESUNIT_METER; 63 break; 64 default: 65 pAttribute->m_wDPIUnit = FXCODEC_RESUNIT_NONE; 66 } 67 #endif 68 #if defined(PNG_iCCP_SUPPORTED) 69 png_charp icc_name; 70 png_bytep icc_profile; 71 png_uint_32 icc_proflen; 72 int compress_type; 73 png_get_iCCP(png_ptr, info_ptr, &icc_name, &compress_type, &icc_profile, 74 &icc_proflen); 75 #endif 76 #if defined(PNG_TEXT_SUPPORTED) 77 int i; 78 size_t len; 79 const char* buf; 80 int num_text; 81 png_textp text = nullptr; 82 png_get_text(png_ptr, info_ptr, &text, &num_text); 83 for (i = 0; i < num_text; i++) { 84 len = strlen(text[i].key); 85 buf = "Time"; 86 if (memcmp(buf, text[i].key, std::min(len, strlen(buf)))) { 87 buf = "Author"; 88 if (!memcmp(buf, text[i].key, std::min(len, strlen(buf)))) { 89 pAttribute->m_strAuthor = 90 text[i].text_length > 0 91 ? ByteString(reinterpret_cast<uint8_t*>(text[i].text), 92 static_cast<size_t>(text[i].text_length)) 93 : ByteString(); 94 } 95 } 96 } 97 #endif 98 } 99 } 100 101 static void* _png_alloc_func(unsigned int size) { 102 return FX_Alloc(char, size); 103 } 104 105 static void _png_free_func(void* p) { 106 FX_Free(p); 107 } 108 109 static void _png_get_header_func(png_structp png_ptr, png_infop info_ptr) { 110 auto* pContext = 111 reinterpret_cast<CPngContext*>(png_get_progressive_ptr(png_ptr)); 112 if (!pContext) 113 return; 114 115 png_uint_32 width = 0; 116 png_uint_32 height = 0; 117 int bpc = 0; 118 int color_type = 0; 119 png_get_IHDR(png_ptr, info_ptr, &width, &height, &bpc, &color_type, nullptr, 120 nullptr, nullptr); 121 int color_type1 = color_type; 122 if (bpc > 8) 123 png_set_strip_16(png_ptr); 124 else if (bpc < 8) 125 png_set_expand_gray_1_2_4_to_8(png_ptr); 126 127 bpc = 8; 128 if (color_type == PNG_COLOR_TYPE_PALETTE) 129 png_set_palette_to_rgb(png_ptr); 130 131 int pass = png_set_interlace_handling(png_ptr); 132 double gamma = 1.0; 133 if (!pContext->m_pDelegate->PngReadHeader(width, height, bpc, pass, 134 &color_type, &gamma)) { 135 png_error(pContext->m_pPng, "Read Header Callback Error"); 136 } 137 int intent; 138 if (png_get_sRGB(png_ptr, info_ptr, &intent)) { 139 png_set_gamma(png_ptr, gamma, 0.45455); 140 } else { 141 double image_gamma; 142 if (png_get_gAMA(png_ptr, info_ptr, &image_gamma)) 143 png_set_gamma(png_ptr, gamma, image_gamma); 144 else 145 png_set_gamma(png_ptr, gamma, 0.45455); 146 } 147 switch (color_type) { 148 case PNG_COLOR_TYPE_GRAY: 149 case PNG_COLOR_TYPE_GRAY_ALPHA: { 150 if (color_type1 & PNG_COLOR_MASK_COLOR) { 151 png_set_rgb_to_gray(png_ptr, 1, 0.299, 0.587); 152 } 153 } break; 154 case PNG_COLOR_TYPE_PALETTE: 155 if (color_type1 != PNG_COLOR_TYPE_PALETTE) { 156 png_error(pContext->m_pPng, "Not Support Output Palette Now"); 157 } 158 case PNG_COLOR_TYPE_RGB: 159 case PNG_COLOR_TYPE_RGB_ALPHA: 160 if (!(color_type1 & PNG_COLOR_MASK_COLOR)) { 161 png_set_gray_to_rgb(png_ptr); 162 } 163 png_set_bgr(png_ptr); 164 break; 165 } 166 if (!(color_type & PNG_COLOR_MASK_ALPHA)) 167 png_set_strip_alpha(png_ptr); 168 169 if (color_type & PNG_COLOR_MASK_ALPHA && 170 !(color_type1 & PNG_COLOR_MASK_ALPHA)) { 171 png_set_filler(png_ptr, 0xff, PNG_FILLER_AFTER); 172 } 173 png_read_update_info(png_ptr, info_ptr); 174 } 175 176 static void _png_get_end_func(png_structp png_ptr, png_infop info_ptr) {} 177 178 static void _png_get_row_func(png_structp png_ptr, 179 png_bytep new_row, 180 png_uint_32 row_num, 181 int pass) { 182 auto* pContext = 183 reinterpret_cast<CPngContext*>(png_get_progressive_ptr(png_ptr)); 184 if (!pContext) 185 return; 186 187 uint8_t* src_buf; 188 if (!pContext->m_pDelegate->PngAskScanlineBuf(row_num, &src_buf)) 189 png_error(png_ptr, "Ask Scanline buffer Callback Error"); 190 191 if (src_buf) 192 png_progressive_combine_row(png_ptr, src_buf, new_row); 193 194 pContext->m_pDelegate->PngFillScanlineBufCompleted(pass, row_num); 195 } 196 197 } // extern "C" 198 199 CPngContext::CPngContext(CCodec_PngModule* pModule, 200 CCodec_PngModule::Delegate* pDelegate) 201 : m_pPng(nullptr), 202 m_pInfo(nullptr), 203 m_pModule(pModule), 204 m_pDelegate(pDelegate), 205 m_AllocFunc(_png_alloc_func), 206 m_FreeFunc(_png_free_func) { 207 memset(m_szLastError, 0, sizeof(m_szLastError)); 208 } 209 210 CPngContext::~CPngContext() { 211 png_destroy_read_struct(m_pPng ? &m_pPng : nullptr, 212 m_pInfo ? &m_pInfo : nullptr, nullptr); 213 } 214 215 std::unique_ptr<CCodec_PngModule::Context> CCodec_PngModule::Start( 216 Delegate* pDelegate) { 217 auto p = pdfium::MakeUnique<CPngContext>(this, pDelegate); 218 p->m_pPng = 219 png_create_read_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr); 220 if (!p->m_pPng) 221 return nullptr; 222 223 p->m_pInfo = png_create_info_struct(p->m_pPng); 224 if (!p->m_pInfo) 225 return nullptr; 226 227 if (setjmp(png_jmpbuf(p->m_pPng))) 228 return nullptr; 229 230 png_set_progressive_read_fn(p->m_pPng, p.get(), _png_get_header_func, 231 _png_get_row_func, _png_get_end_func); 232 png_set_error_fn(p->m_pPng, p->m_szLastError, _png_error_data, 233 _png_warning_data); 234 return p; 235 } 236 237 bool CCodec_PngModule::Input(Context* pContext, 238 const uint8_t* src_buf, 239 uint32_t src_size, 240 CFX_DIBAttribute* pAttribute) { 241 auto* ctx = static_cast<CPngContext*>(pContext); 242 if (setjmp(png_jmpbuf(ctx->m_pPng))) { 243 if (pAttribute && 244 strcmp(ctx->m_szLastError, "Read Header Callback Error") == 0) { 245 _png_load_bmp_attribute(ctx->m_pPng, ctx->m_pInfo, pAttribute); 246 } 247 return false; 248 } 249 png_process_data(ctx->m_pPng, ctx->m_pInfo, (uint8_t*)src_buf, src_size); 250 return true; 251 } 252