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/fxge/fx_dib.h"
14
15extern "C" {
16#undef FAR
17#include "third_party/libpng16/png.h"
18}
19
20static void _png_error_data(png_structp png_ptr, png_const_charp error_msg) {
21  if (png_get_error_ptr(png_ptr)) {
22    FXSYS_strncpy((char*)png_get_error_ptr(png_ptr), error_msg,
23                  PNG_ERROR_SIZE - 1);
24  }
25  longjmp(png_jmpbuf(png_ptr), 1);
26}
27static void _png_warning_data(png_structp png_ptr, png_const_charp error_msg) {}
28static void _png_load_bmp_attribute(png_structp png_ptr,
29                                    png_infop info_ptr,
30                                    CFX_DIBAttribute* pAttribute) {
31  if (pAttribute) {
32#if defined(PNG_pHYs_SUPPORTED)
33    pAttribute->m_nXDPI = png_get_x_pixels_per_meter(png_ptr, info_ptr);
34    pAttribute->m_nYDPI = png_get_y_pixels_per_meter(png_ptr, info_ptr);
35    png_uint_32 res_x, res_y;
36    int unit_type;
37    png_get_pHYs(png_ptr, info_ptr, &res_x, &res_y, &unit_type);
38    switch (unit_type) {
39      case PNG_RESOLUTION_METER:
40        pAttribute->m_wDPIUnit = FXCODEC_RESUNIT_METER;
41        break;
42      default:
43        pAttribute->m_wDPIUnit = FXCODEC_RESUNIT_NONE;
44    }
45#endif
46#if defined(PNG_iCCP_SUPPORTED)
47    png_charp icc_name;
48    png_bytep icc_profile;
49    png_uint_32 icc_proflen;
50    int compress_type;
51    png_get_iCCP(png_ptr, info_ptr, &icc_name, &compress_type, &icc_profile,
52                 &icc_proflen);
53#endif
54    int bTime = 0;
55#if defined(PNG_tIME_SUPPORTED)
56    png_timep t = nullptr;
57    png_get_tIME(png_ptr, info_ptr, &t);
58    if (t) {
59      FXSYS_memset(pAttribute->m_strTime, 0, sizeof(pAttribute->m_strTime));
60      FXSYS_snprintf((FX_CHAR*)pAttribute->m_strTime,
61                     sizeof(pAttribute->m_strTime), "%4u:%2u:%2u %2u:%2u:%2u",
62                     t->year, t->month, t->day, t->hour, t->minute, t->second);
63      pAttribute->m_strTime[sizeof(pAttribute->m_strTime) - 1] = 0;
64      bTime = 1;
65    }
66#endif
67#if defined(PNG_TEXT_SUPPORTED)
68    int i;
69    FX_STRSIZE len;
70    const FX_CHAR* buf;
71    int num_text;
72    png_textp text = nullptr;
73    png_get_text(png_ptr, info_ptr, &text, &num_text);
74    for (i = 0; i < num_text; i++) {
75      len = FXSYS_strlen(text[i].key);
76      buf = "Time";
77      if (!FXSYS_memcmp(buf, text[i].key, std::min(len, FXSYS_strlen(buf)))) {
78        if (!bTime) {
79          FXSYS_memset(pAttribute->m_strTime, 0, sizeof(pAttribute->m_strTime));
80          FXSYS_memcpy(
81              pAttribute->m_strTime, text[i].text,
82              std::min(sizeof(pAttribute->m_strTime) - 1, text[i].text_length));
83        }
84      } else {
85        buf = "Author";
86        if (!FXSYS_memcmp(buf, text[i].key, std::min(len, FXSYS_strlen(buf)))) {
87          pAttribute->m_strAuthor =
88              CFX_ByteString(reinterpret_cast<uint8_t*>(text[i].text),
89                             static_cast<FX_STRSIZE>(text[i].text_length));
90        }
91      }
92    }
93#endif
94  }
95}
96struct FXPNG_Context {
97  png_structp png_ptr;
98  png_infop info_ptr;
99  void* parent_ptr;
100
101  void* (*m_AllocFunc)(unsigned int);
102  void (*m_FreeFunc)(void*);
103};
104extern "C" {
105static void* _png_alloc_func(unsigned int size) {
106  return FX_Alloc(char, size);
107}
108static void _png_free_func(void* p) {
109  FX_Free(p);
110}
111};
112static void _png_get_header_func(png_structp png_ptr, png_infop info_ptr) {
113  FXPNG_Context* p = (FXPNG_Context*)png_get_progressive_ptr(png_ptr);
114  if (!p)
115    return;
116
117  CCodec_PngModule* pModule = (CCodec_PngModule*)p->parent_ptr;
118  if (!pModule)
119    return;
120
121  png_uint_32 width = 0, height = 0;
122  int bpc = 0, color_type = 0, color_type1 = 0, pass = 0;
123  double gamma = 1.0;
124  png_get_IHDR(png_ptr, info_ptr, &width, &height, &bpc, &color_type, nullptr,
125               nullptr, nullptr);
126  color_type1 = color_type;
127  if (bpc > 8) {
128    png_set_strip_16(png_ptr);
129  } else if (bpc < 8) {
130    png_set_expand_gray_1_2_4_to_8(png_ptr);
131  }
132  bpc = 8;
133  if (color_type == PNG_COLOR_TYPE_PALETTE) {
134    png_set_palette_to_rgb(png_ptr);
135  }
136  pass = png_set_interlace_handling(png_ptr);
137  if (!pModule->GetDelegate()->PngReadHeader(width, height, bpc, pass,
138                                             &color_type, &gamma)) {
139    png_error(p->png_ptr, "Read Header Callback Error");
140  }
141  int intent;
142  if (png_get_sRGB(png_ptr, info_ptr, &intent)) {
143    png_set_gamma(png_ptr, gamma, 0.45455);
144  } else {
145    double image_gamma;
146    if (png_get_gAMA(png_ptr, info_ptr, &image_gamma)) {
147      png_set_gamma(png_ptr, gamma, image_gamma);
148    } else {
149      png_set_gamma(png_ptr, gamma, 0.45455);
150    }
151  }
152  switch (color_type) {
153    case PNG_COLOR_TYPE_GRAY:
154    case PNG_COLOR_TYPE_GRAY_ALPHA: {
155      if (color_type1 & PNG_COLOR_MASK_COLOR) {
156        png_set_rgb_to_gray(png_ptr, 1, 0.299, 0.587);
157      }
158    } break;
159    case PNG_COLOR_TYPE_PALETTE:
160      if (color_type1 != PNG_COLOR_TYPE_PALETTE) {
161        png_error(p->png_ptr, "Not Support Output Palette Now");
162      }
163    case PNG_COLOR_TYPE_RGB:
164    case PNG_COLOR_TYPE_RGB_ALPHA:
165      if (!(color_type1 & PNG_COLOR_MASK_COLOR)) {
166        png_set_gray_to_rgb(png_ptr);
167      }
168      png_set_bgr(png_ptr);
169      break;
170  }
171  if (!(color_type & PNG_COLOR_MASK_ALPHA)) {
172    png_set_strip_alpha(png_ptr);
173  }
174  if (color_type & PNG_COLOR_MASK_ALPHA &&
175      !(color_type1 & PNG_COLOR_MASK_ALPHA)) {
176    png_set_filler(png_ptr, 0xff, PNG_FILLER_AFTER);
177  }
178  png_read_update_info(png_ptr, info_ptr);
179}
180static void _png_get_end_func(png_structp png_ptr, png_infop info_ptr) {}
181static void _png_get_row_func(png_structp png_ptr,
182                              png_bytep new_row,
183                              png_uint_32 row_num,
184                              int pass) {
185  FXPNG_Context* p = (FXPNG_Context*)png_get_progressive_ptr(png_ptr);
186  if (!p)
187    return;
188
189  CCodec_PngModule* pModule = (CCodec_PngModule*)p->parent_ptr;
190  uint8_t* src_buf = nullptr;
191  if (!pModule->GetDelegate()->PngAskScanlineBuf(row_num, src_buf)) {
192    png_error(png_ptr, "Ask Scanline buffer Callback Error");
193  }
194  if (src_buf) {
195    png_progressive_combine_row(png_ptr, src_buf, new_row);
196  }
197  pModule->GetDelegate()->PngFillScanlineBufCompleted(pass, row_num);
198}
199
200CCodec_PngModule::CCodec_PngModule() {
201  memset(m_szLastError, 0, sizeof(m_szLastError));
202}
203
204CCodec_PngModule::~CCodec_PngModule() {}
205
206FXPNG_Context* CCodec_PngModule::Start() {
207  FXPNG_Context* p = FX_Alloc(FXPNG_Context, 1);
208  if (!p)
209    return nullptr;
210
211  p->m_AllocFunc = _png_alloc_func;
212  p->m_FreeFunc = _png_free_func;
213  p->png_ptr = nullptr;
214  p->info_ptr = nullptr;
215  p->parent_ptr = (void*)this;
216  p->png_ptr =
217      png_create_read_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr);
218  if (!p->png_ptr) {
219    FX_Free(p);
220    return nullptr;
221  }
222  p->info_ptr = png_create_info_struct(p->png_ptr);
223  if (!p->info_ptr) {
224    png_destroy_read_struct(&(p->png_ptr), nullptr, nullptr);
225    FX_Free(p);
226    return nullptr;
227  }
228  if (setjmp(png_jmpbuf(p->png_ptr))) {
229    if (p) {
230      png_destroy_read_struct(&(p->png_ptr), &(p->info_ptr), nullptr);
231      FX_Free(p);
232    }
233    return nullptr;
234  }
235  png_set_progressive_read_fn(p->png_ptr, p, _png_get_header_func,
236                              _png_get_row_func, _png_get_end_func);
237  png_set_error_fn(p->png_ptr, m_szLastError, (png_error_ptr)_png_error_data,
238                   (png_error_ptr)_png_warning_data);
239  return p;
240}
241
242void CCodec_PngModule::Finish(FXPNG_Context* ctx) {
243  if (ctx) {
244    png_destroy_read_struct(&(ctx->png_ptr), &(ctx->info_ptr), nullptr);
245    ctx->m_FreeFunc(ctx);
246  }
247}
248
249bool CCodec_PngModule::Input(FXPNG_Context* ctx,
250                             const uint8_t* src_buf,
251                             uint32_t src_size,
252                             CFX_DIBAttribute* pAttribute) {
253  if (setjmp(png_jmpbuf(ctx->png_ptr))) {
254    if (pAttribute &&
255        0 == FXSYS_strcmp(m_szLastError, "Read Header Callback Error")) {
256      _png_load_bmp_attribute(ctx->png_ptr, ctx->info_ptr, pAttribute);
257    }
258    return false;
259  }
260  png_process_data(ctx->png_ptr, ctx->info_ptr, (uint8_t*)src_buf, src_size);
261  return true;
262}
263