1 | //======================================================================== |
2 | // |
3 | // PNGWriter.cc |
4 | // |
5 | // This file is licensed under the GPLv2 or later |
6 | // |
7 | // Copyright (C) 2009 Warren Toomey <wkt@tuhs.org> |
8 | // Copyright (C) 2009 Shen Liang <shenzhuxi@gmail.com> |
9 | // Copyright (C) 2009, 2011-2023 Albert Astals Cid <aacid@kde.org> |
10 | // Copyright (C) 2009 Stefan Thomas <thomas@eload24.com> |
11 | // Copyright (C) 2010, 2011, 2013, 2017 Adrian Johnson <ajohnson@redneon.com> |
12 | // Copyright (C) 2011 Thomas Klausner <wiz@danbala.tuwien.ac.at> |
13 | // Copyright (C) 2012 Pino Toscano <pino@kde.org> |
14 | // |
15 | //======================================================================== |
16 | |
17 | #include "PNGWriter.h" |
18 | |
19 | #ifdef ENABLE_LIBPNG |
20 | |
21 | # include <zlib.h> |
22 | # include <cstdlib> |
23 | # include <cstring> |
24 | |
25 | # include "poppler/Error.h" |
26 | # include "goo/gmem.h" |
27 | |
28 | # include <png.h> |
29 | |
30 | struct PNGWriterPrivate |
31 | { |
32 | explicit PNGWriterPrivate(PNGWriter::Format f) : format(f) { } |
33 | |
34 | PNGWriter::Format format; |
35 | png_structp png_ptr = nullptr; |
36 | png_infop info_ptr = nullptr; |
37 | unsigned char *icc_data = nullptr; |
38 | int icc_data_size = 0; |
39 | char *icc_name = nullptr; |
40 | bool sRGB_profile = false; |
41 | |
42 | PNGWriterPrivate(const PNGWriterPrivate &) = delete; |
43 | PNGWriterPrivate &operator=(const PNGWriterPrivate &) = delete; |
44 | }; |
45 | |
46 | PNGWriter::PNGWriter(Format formatA) |
47 | { |
48 | priv = new PNGWriterPrivate(formatA); |
49 | } |
50 | |
51 | PNGWriter::~PNGWriter() |
52 | { |
53 | /* cleanup heap allocation */ |
54 | png_destroy_write_struct(png_ptr_ptr: &priv->png_ptr, info_ptr_ptr: &priv->info_ptr); |
55 | if (priv->icc_data) { |
56 | gfree(p: priv->icc_data); |
57 | free(ptr: priv->icc_name); |
58 | } |
59 | |
60 | delete priv; |
61 | } |
62 | |
63 | void PNGWriter::setICCProfile(const char *name, unsigned char *data, int size) |
64 | { |
65 | priv->icc_data = (unsigned char *)gmalloc(size); |
66 | memcpy(dest: priv->icc_data, src: data, n: size); |
67 | priv->icc_data_size = size; |
68 | priv->icc_name = strdup(s: name); |
69 | } |
70 | |
71 | void PNGWriter::setSRGBProfile() |
72 | { |
73 | priv->sRGB_profile = true; |
74 | } |
75 | |
76 | bool PNGWriter::init(FILE *f, int width, int height, double hDPI, double vDPI) |
77 | { |
78 | /* libpng changed the png_set_iCCP() prototype in 1.5.0 */ |
79 | # if PNG_LIBPNG_VER < 10500 |
80 | png_charp icc_data_ptr = (png_charp)priv->icc_data; |
81 | # else |
82 | png_const_bytep icc_data_ptr = (png_const_bytep)priv->icc_data; |
83 | # endif |
84 | |
85 | if (hDPI < 0 || vDPI < 0) { |
86 | error(category: errInternal, pos: -1, msg: "PNGWriter::init: hDPI or vDPI values are invalid {0:f} {1:f}" , hDPI, vDPI); |
87 | return false; |
88 | } |
89 | |
90 | const double png_res_x = hDPI / 0.0254; |
91 | const double png_res_y = vDPI / 0.0254; |
92 | if (png_res_x > std::numeric_limits<png_uint_32>::max() || png_res_y > std::numeric_limits<png_uint_32>::max()) { |
93 | error(category: errInternal, pos: -1, msg: "PNGWriter::init: hDPI or vDPI values are invalid {0:f} {1:f}" , hDPI, vDPI); |
94 | return false; |
95 | } |
96 | |
97 | /* initialize stuff */ |
98 | priv->png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, error_ptr: nullptr, error_fn: nullptr, warn_fn: nullptr); |
99 | if (!priv->png_ptr) { |
100 | error(category: errInternal, pos: -1, msg: "png_create_write_struct failed" ); |
101 | return false; |
102 | } |
103 | |
104 | priv->info_ptr = png_create_info_struct(png_ptr: priv->png_ptr); |
105 | if (!priv->info_ptr) { |
106 | error(category: errInternal, pos: -1, msg: "png_create_info_struct failed" ); |
107 | return false; |
108 | } |
109 | |
110 | if (setjmp(png_jmpbuf(priv->png_ptr))) { |
111 | error(category: errInternal, pos: -1, msg: "png_jmpbuf failed" ); |
112 | return false; |
113 | } |
114 | |
115 | /* write header */ |
116 | png_init_io(png_ptr: priv->png_ptr, fp: f); |
117 | if (setjmp(png_jmpbuf(priv->png_ptr))) { |
118 | error(category: errInternal, pos: -1, msg: "Error during writing header" ); |
119 | return false; |
120 | } |
121 | |
122 | // Set up the type of PNG image and the compression level |
123 | png_set_compression_level(png_ptr: priv->png_ptr, Z_BEST_COMPRESSION); |
124 | |
125 | // Silence silly gcc |
126 | png_byte bit_depth = -1; |
127 | png_byte color_type = -1; |
128 | switch (priv->format) { |
129 | case RGB: |
130 | bit_depth = 8; |
131 | color_type = PNG_COLOR_TYPE_RGB; |
132 | break; |
133 | case RGB48: |
134 | bit_depth = 16; |
135 | color_type = PNG_COLOR_TYPE_RGB; |
136 | break; |
137 | case RGBA: |
138 | bit_depth = 8; |
139 | color_type = PNG_COLOR_TYPE_RGB_ALPHA; |
140 | break; |
141 | case GRAY: |
142 | bit_depth = 8; |
143 | color_type = PNG_COLOR_TYPE_GRAY; |
144 | break; |
145 | case MONOCHROME: |
146 | bit_depth = 1; |
147 | color_type = PNG_COLOR_TYPE_GRAY; |
148 | break; |
149 | } |
150 | png_byte interlace_type = PNG_INTERLACE_NONE; |
151 | |
152 | png_set_IHDR(png_ptr: priv->png_ptr, info_ptr: priv->info_ptr, width, height, bit_depth, color_type, interlace_method: interlace_type, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT); |
153 | |
154 | png_set_pHYs(png_ptr: priv->png_ptr, info_ptr: priv->info_ptr, res_x: static_cast<png_uint_32>(png_res_x), res_y: static_cast<png_uint_32>(png_res_y), PNG_RESOLUTION_METER); |
155 | |
156 | if (priv->icc_data) { |
157 | png_set_iCCP(png_ptr: priv->png_ptr, info_ptr: priv->info_ptr, name: priv->icc_name, PNG_COMPRESSION_TYPE_BASE, profile: icc_data_ptr, proflen: priv->icc_data_size); |
158 | } else if (priv->sRGB_profile) { |
159 | png_set_sRGB(png_ptr: priv->png_ptr, info_ptr: priv->info_ptr, PNG_sRGB_INTENT_RELATIVE); |
160 | } |
161 | |
162 | png_write_info(png_ptr: priv->png_ptr, info_ptr: priv->info_ptr); |
163 | if (setjmp(png_jmpbuf(priv->png_ptr))) { |
164 | error(category: errInternal, pos: -1, msg: "error during writing png info bytes" ); |
165 | return false; |
166 | } |
167 | |
168 | return true; |
169 | } |
170 | |
171 | bool PNGWriter::writePointers(unsigned char **rowPointers, int rowCount) |
172 | { |
173 | png_write_image(png_ptr: priv->png_ptr, image: rowPointers); |
174 | /* write bytes */ |
175 | if (setjmp(png_jmpbuf(priv->png_ptr))) { |
176 | error(category: errInternal, pos: -1, msg: "Error during writing bytes" ); |
177 | return false; |
178 | } |
179 | |
180 | return true; |
181 | } |
182 | |
183 | bool PNGWriter::writeRow(unsigned char **row) |
184 | { |
185 | // Write the row to the file |
186 | png_write_rows(png_ptr: priv->png_ptr, row, num_rows: 1); |
187 | if (setjmp(png_jmpbuf(priv->png_ptr))) { |
188 | error(category: errInternal, pos: -1, msg: "error during png row write" ); |
189 | return false; |
190 | } |
191 | |
192 | return true; |
193 | } |
194 | |
195 | bool PNGWriter::close() |
196 | { |
197 | /* end write */ |
198 | png_write_end(png_ptr: priv->png_ptr, info_ptr: priv->info_ptr); |
199 | if (setjmp(png_jmpbuf(priv->png_ptr))) { |
200 | error(category: errInternal, pos: -1, msg: "Error during end of write" ); |
201 | return false; |
202 | } |
203 | |
204 | return true; |
205 | } |
206 | |
207 | #endif |
208 | |