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/png/pngmodule.h"
8 
9 #include <algorithm>
10 
11 #include "core/fxcodec/cfx_codec_memory.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/compiler_specific.h"
16 #include "third_party/base/ptr_util.h"
17 
18 #ifdef USE_SYSTEM_LIBPNG
19 #include <png.h>
20 #else
21 #include "third_party/libpng16/png.h"
22 #endif
23 
24 #define PNG_ERROR_SIZE 256
25 
26 class CPngContext final : public ModuleIface::Context {
27  public:
28   explicit CPngContext(PngModule::Delegate* pDelegate);
29   ~CPngContext() override;
30 
31   png_structp m_pPng = nullptr;
32   png_infop m_pInfo = nullptr;
33   UnownedPtr<PngModule::Delegate> const m_pDelegate;
34   char m_szLastError[PNG_ERROR_SIZE];
35 };
36 
37 extern "C" {
38 
_png_error_data(png_structp png_ptr,png_const_charp error_msg)39 void _png_error_data(png_structp png_ptr, png_const_charp error_msg) {
40   if (png_get_error_ptr(png_ptr)) {
41     strncpy(static_cast<char*>(png_get_error_ptr(png_ptr)), error_msg,
42             PNG_ERROR_SIZE - 1);
43   }
44 
45   longjmp(png_jmpbuf(png_ptr), 1);
46 }
47 
_png_warning_data(png_structp png_ptr,png_const_charp error_msg)48 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 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 num_text;
78     png_textp text = nullptr;
79     png_get_text(png_ptr, info_ptr, &text, &num_text);
80 #endif
81   }
82 }
83 
_png_get_header_func(png_structp png_ptr,png_infop info_ptr)84 void _png_get_header_func(png_structp png_ptr, png_infop info_ptr) {
85   auto* pContext =
86       reinterpret_cast<CPngContext*>(png_get_progressive_ptr(png_ptr));
87   if (!pContext)
88     return;
89 
90   png_uint_32 width = 0;
91   png_uint_32 height = 0;
92   int bpc = 0;
93   int color_type = 0;
94   png_get_IHDR(png_ptr, info_ptr, &width, &height, &bpc, &color_type, nullptr,
95                nullptr, nullptr);
96   int color_type1 = color_type;
97   if (bpc > 8)
98     png_set_strip_16(png_ptr);
99   else if (bpc < 8)
100     png_set_expand_gray_1_2_4_to_8(png_ptr);
101 
102   bpc = 8;
103   if (color_type == PNG_COLOR_TYPE_PALETTE)
104     png_set_palette_to_rgb(png_ptr);
105 
106   int pass = png_set_interlace_handling(png_ptr);
107   double gamma = 1.0;
108   if (!pContext->m_pDelegate->PngReadHeader(width, height, bpc, pass,
109                                             &color_type, &gamma)) {
110     png_error(pContext->m_pPng, "Read Header Callback Error");
111   }
112   int intent;
113   if (png_get_sRGB(png_ptr, info_ptr, &intent)) {
114     png_set_gamma(png_ptr, gamma, 0.45455);
115   } else {
116     double image_gamma;
117     if (png_get_gAMA(png_ptr, info_ptr, &image_gamma))
118       png_set_gamma(png_ptr, gamma, image_gamma);
119     else
120       png_set_gamma(png_ptr, gamma, 0.45455);
121   }
122   switch (color_type) {
123     case PNG_COLOR_TYPE_GRAY:
124     case PNG_COLOR_TYPE_GRAY_ALPHA: {
125       if (color_type1 & PNG_COLOR_MASK_COLOR) {
126         png_set_rgb_to_gray(png_ptr, 1, 0.299, 0.587);
127       }
128     } break;
129     case PNG_COLOR_TYPE_PALETTE:
130       if (color_type1 != PNG_COLOR_TYPE_PALETTE) {
131         png_error(pContext->m_pPng, "Not Support Output Palette Now");
132       }
133       FALLTHROUGH;
134     case PNG_COLOR_TYPE_RGB:
135     case PNG_COLOR_TYPE_RGB_ALPHA:
136       if (!(color_type1 & PNG_COLOR_MASK_COLOR)) {
137         png_set_gray_to_rgb(png_ptr);
138       }
139       png_set_bgr(png_ptr);
140       break;
141   }
142   if (!(color_type & PNG_COLOR_MASK_ALPHA))
143     png_set_strip_alpha(png_ptr);
144 
145   if (color_type & PNG_COLOR_MASK_ALPHA &&
146       !(color_type1 & PNG_COLOR_MASK_ALPHA)) {
147     png_set_filler(png_ptr, 0xff, PNG_FILLER_AFTER);
148   }
149   png_read_update_info(png_ptr, info_ptr);
150 }
151 
_png_get_end_func(png_structp png_ptr,png_infop info_ptr)152 void _png_get_end_func(png_structp png_ptr, png_infop info_ptr) {}
153 
_png_get_row_func(png_structp png_ptr,png_bytep new_row,png_uint_32 row_num,int pass)154 void _png_get_row_func(png_structp png_ptr,
155                        png_bytep new_row,
156                        png_uint_32 row_num,
157                        int pass) {
158   auto* pContext =
159       reinterpret_cast<CPngContext*>(png_get_progressive_ptr(png_ptr));
160   if (!pContext)
161     return;
162 
163   uint8_t* src_buf;
164   if (!pContext->m_pDelegate->PngAskScanlineBuf(row_num, &src_buf))
165     png_error(png_ptr, "Ask Scanline buffer Callback Error");
166 
167   if (src_buf)
168     png_progressive_combine_row(png_ptr, src_buf, new_row);
169 
170   pContext->m_pDelegate->PngFillScanlineBufCompleted(pass, row_num);
171 }
172 
173 }  // extern "C"
174 
CPngContext(PngModule::Delegate * pDelegate)175 CPngContext::CPngContext(PngModule::Delegate* pDelegate)
176     : m_pDelegate(pDelegate) {
177   memset(m_szLastError, 0, sizeof(m_szLastError));
178 }
179 
~CPngContext()180 CPngContext::~CPngContext() {
181   png_destroy_read_struct(m_pPng ? &m_pPng : nullptr,
182                           m_pInfo ? &m_pInfo : nullptr, nullptr);
183 }
184 
185 namespace fxcodec {
186 
187 PngModule::PngModule() = default;
188 
189 PngModule::~PngModule() = default;
190 
Start(Delegate * pDelegate)191 std::unique_ptr<ModuleIface::Context> PngModule::Start(Delegate* pDelegate) {
192   auto p = pdfium::MakeUnique<CPngContext>(pDelegate);
193   p->m_pPng =
194       png_create_read_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr);
195   if (!p->m_pPng)
196     return nullptr;
197 
198   p->m_pInfo = png_create_info_struct(p->m_pPng);
199   if (!p->m_pInfo)
200     return nullptr;
201 
202   if (setjmp(png_jmpbuf(p->m_pPng)))
203     return nullptr;
204 
205   png_set_progressive_read_fn(p->m_pPng, p.get(), _png_get_header_func,
206                               _png_get_row_func, _png_get_end_func);
207   png_set_error_fn(p->m_pPng, p->m_szLastError, _png_error_data,
208                    _png_warning_data);
209   return p;
210 }
211 
GetAvailInput(Context * pContext) const212 FX_FILESIZE PngModule::GetAvailInput(Context* pContext) const {
213   NOTREACHED();
214   return 0;
215 }
216 
Input(Context * pContext,RetainPtr<CFX_CodecMemory> codec_memory,CFX_DIBAttribute * pAttribute)217 bool PngModule::Input(Context* pContext,
218                       RetainPtr<CFX_CodecMemory> codec_memory,
219                       CFX_DIBAttribute* pAttribute) {
220   auto* ctx = static_cast<CPngContext*>(pContext);
221   if (setjmp(png_jmpbuf(ctx->m_pPng))) {
222     if (pAttribute &&
223         strcmp(ctx->m_szLastError, "Read Header Callback Error") == 0) {
224       _png_load_bmp_attribute(ctx->m_pPng, ctx->m_pInfo, pAttribute);
225     }
226     return false;
227   }
228   pdfium::span<uint8_t> src_buf = codec_memory->GetSpan();
229   png_process_data(ctx->m_pPng, ctx->m_pInfo, src_buf.data(), src_buf.size());
230   return true;
231 }
232 
233 }  // namespace fxcodec
234