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