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 
_png_error_data(png_structp png_ptr,png_const_charp error_msg)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 
_png_warning_data(png_structp png_ptr,png_const_charp error_msg)48 static void _png_warning_data(png_structp png_ptr, png_const_charp error_msg) {}
49 
_png_load_bmp_attribute(png_structp png_ptr,png_infop info_ptr,CFX_DIBAttribute * pAttribute)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 
_png_alloc_func(unsigned int size)101 static void* _png_alloc_func(unsigned int size) {
102   return FX_Alloc(char, size);
103 }
104 
_png_free_func(void * p)105 static void _png_free_func(void* p) {
106   FX_Free(p);
107 }
108 
_png_get_header_func(png_structp png_ptr,png_infop info_ptr)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 
_png_get_end_func(png_structp png_ptr,png_infop info_ptr)176 static void _png_get_end_func(png_structp png_ptr, png_infop info_ptr) {}
177 
_png_get_row_func(png_structp png_ptr,png_bytep new_row,png_uint_32 row_num,int pass)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 
CPngContext(CCodec_PngModule * pModule,CCodec_PngModule::Delegate * pDelegate)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 
~CPngContext()210 CPngContext::~CPngContext() {
211   png_destroy_read_struct(m_pPng ? &m_pPng : nullptr,
212                           m_pInfo ? &m_pInfo : nullptr, nullptr);
213 }
214 
Start(Delegate * pDelegate)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 
Input(Context * pContext,const uint8_t * src_buf,uint32_t src_size,CFX_DIBAttribute * pAttribute)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