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