1 | /* |
2 | * Copyright (C) 2010-2011, Pino Toscano <pino@kde.org> |
3 | * Copyright (C) 2013 Adrian Johnson <ajohnson@redneon.com> |
4 | * Copyright (C) 2017-2019, 2021, Albert Astals Cid <aacid@kde.org> |
5 | * Copyright (C) 2017, Jeroen Ooms <jeroenooms@gmail.com> |
6 | * Copyright (C) 2018, Zsombor Hollay-Horvath <hollay.horvath@gmail.com> |
7 | * Copyright (C) 2018, Adam Reichold <adam.reichold@t-online.de> |
8 | * |
9 | * This program is free software; you can redistribute it and/or modify |
10 | * it under the terms of the GNU General Public License as published by |
11 | * the Free Software Foundation; either version 2, or (at your option) |
12 | * any later version. |
13 | * |
14 | * This program is distributed in the hope that it will be useful, |
15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
17 | * GNU General Public License for more details. |
18 | * |
19 | * You should have received a copy of the GNU General Public License |
20 | * along with this program; if not, write to the Free Software |
21 | * Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA. |
22 | */ |
23 | |
24 | /** |
25 | \file poppler-image.h |
26 | */ |
27 | #include "poppler-image.h" |
28 | |
29 | #include "poppler-image-private.h" |
30 | |
31 | #include <config.h> |
32 | #include "ImgWriter.h" |
33 | #if defined(ENABLE_LIBPNG) |
34 | # include "PNGWriter.h" |
35 | #endif |
36 | #if defined(ENABLE_LIBJPEG) |
37 | # include "JpegWriter.h" |
38 | #endif |
39 | #if defined(ENABLE_LIBTIFF) |
40 | # include "TiffWriter.h" |
41 | #endif |
42 | #include "NetPBMWriter.h" |
43 | |
44 | #include <cstdlib> |
45 | #include <cstring> |
46 | #include <algorithm> |
47 | #include <memory> |
48 | #include <vector> |
49 | |
50 | namespace { |
51 | |
52 | struct FileCloser |
53 | { |
54 | inline explicit FileCloser(FILE *ff) : f(ff) { } |
55 | inline ~FileCloser() { (void)close(); } |
56 | FileCloser(const FileCloser &) = delete; |
57 | FileCloser &operator=(const FileCloser &) = delete; |
58 | inline bool close() |
59 | { |
60 | if (f) { |
61 | const int c = fclose(stream: f); |
62 | f = nullptr; |
63 | return c == 0; |
64 | } |
65 | return true; |
66 | } |
67 | |
68 | FILE *f; |
69 | }; |
70 | |
71 | int calc_bytes_per_row(int width, poppler::image::format_enum format) |
72 | { |
73 | switch (format) { |
74 | case poppler::image::format_invalid: |
75 | return 0; |
76 | case poppler::image::format_mono: |
77 | return (width + 7) >> 3; |
78 | case poppler::image::format_gray8: |
79 | return (width + 3) >> 2 << 2; |
80 | case poppler::image::format_rgb24: |
81 | case poppler::image::format_bgr24: |
82 | return (width * 3 + 3) >> 2 << 2; |
83 | case poppler::image::format_argb32: |
84 | return width * 4; |
85 | } |
86 | return 0; |
87 | } |
88 | |
89 | NetPBMWriter::Format pnm_format(poppler::image::format_enum format) |
90 | { |
91 | switch (format) { |
92 | case poppler::image::format_invalid: // unused, anyway |
93 | case poppler::image::format_mono: |
94 | return NetPBMWriter::MONOCHROME; |
95 | case poppler::image::format_gray8: |
96 | case poppler::image::format_rgb24: |
97 | case poppler::image::format_bgr24: |
98 | case poppler::image::format_argb32: |
99 | return NetPBMWriter::RGB; |
100 | } |
101 | return NetPBMWriter::RGB; |
102 | } |
103 | |
104 | } |
105 | |
106 | using namespace poppler; |
107 | |
108 | image_private::image_private(int iwidth, int iheight, image::format_enum iformat) : ref(1), data(nullptr), width(iwidth), height(iheight), bytes_per_row(0), bytes_num(0), format(iformat), own_data(true) { } |
109 | |
110 | image_private::~image_private() |
111 | { |
112 | if (own_data) { |
113 | std::free(ptr: data); |
114 | } |
115 | } |
116 | |
117 | image_private *image_private::create_data(int width, int height, image::format_enum format) |
118 | { |
119 | if (width <= 0 || height <= 0) { |
120 | return nullptr; |
121 | } |
122 | |
123 | int bpr = calc_bytes_per_row(width, format); |
124 | if (bpr <= 0) { |
125 | return nullptr; |
126 | } |
127 | |
128 | auto d = std::make_unique<image_private>(args&: width, args&: height, args&: format); |
129 | d->bytes_num = bpr * height; |
130 | d->data = reinterpret_cast<char *>(std::malloc(size: d->bytes_num)); |
131 | if (!d->data) { |
132 | return nullptr; |
133 | } |
134 | d->own_data = true; |
135 | d->bytes_per_row = bpr; |
136 | |
137 | return d.release(); |
138 | } |
139 | |
140 | image_private *image_private::create_data(char *data, int width, int height, image::format_enum format) |
141 | { |
142 | if (width <= 0 || height <= 0 || !data) { |
143 | return nullptr; |
144 | } |
145 | |
146 | int bpr = calc_bytes_per_row(width, format); |
147 | if (bpr <= 0) { |
148 | return nullptr; |
149 | } |
150 | |
151 | image_private *d = new image_private(width, height, format); |
152 | d->bytes_num = bpr * height; |
153 | d->data = data; |
154 | d->own_data = false; |
155 | d->bytes_per_row = bpr; |
156 | |
157 | return d; |
158 | } |
159 | |
160 | /** |
161 | \class poppler::image poppler-image.h "poppler/cpp/poppler-image.h" |
162 | |
163 | A simple representation of image, with direct access to the data. |
164 | |
165 | This class uses implicit sharing for the internal data, so it can be used as |
166 | value class. This also means any non-const operation will make sure that the |
167 | data used by current instance is not shared with other instances (ie |
168 | \em detaching), copying the shared data. |
169 | |
170 | \since 0.16 |
171 | */ |
172 | |
173 | /** |
174 | \enum poppler::image::format_enum |
175 | |
176 | The possible formats for an image. |
177 | |
178 | format_gray8 and format_bgr24 were introduced in poppler 0.65. |
179 | */ |
180 | |
181 | /** |
182 | Construct an invalid image. |
183 | */ |
184 | image::image() : d(nullptr) { } |
185 | |
186 | /** |
187 | Construct a new image. |
188 | |
189 | It allocates the storage needed for the image data; if the allocation fails, |
190 | the image is an invalid one. |
191 | |
192 | \param iwidth the width for the image |
193 | \param iheight the height for the image |
194 | \param iformat the format for the bits of the image |
195 | */ |
196 | image::image(int iwidth, int iheight, image::format_enum iformat) : d(image_private::create_data(width: iwidth, height: iheight, format: iformat)) { } |
197 | |
198 | /** |
199 | Construct a new image. |
200 | |
201 | It uses the provide data buffer for the image, so you \b must ensure it |
202 | remains valid for the whole lifetime of the image. |
203 | |
204 | \param idata the buffer to use for the image |
205 | \param iwidth the width for the image |
206 | \param iheight the height for the image |
207 | \param iformat the format for the bits of the image |
208 | */ |
209 | image::image(char *idata, int iwidth, int iheight, image::format_enum iformat) : d(image_private::create_data(data: idata, width: iwidth, height: iheight, format: iformat)) { } |
210 | |
211 | /** |
212 | Copy constructor. |
213 | */ |
214 | image::image(const image &img) : d(img.d) |
215 | { |
216 | if (d) { |
217 | ++d->ref; |
218 | } |
219 | } |
220 | |
221 | /** |
222 | Destructor. |
223 | */ |
224 | image::~image() |
225 | { |
226 | if (d && !--d->ref) { |
227 | delete d; |
228 | } |
229 | } |
230 | |
231 | /** |
232 | Image validity check. |
233 | |
234 | \returns whether the image is valid. |
235 | */ |
236 | bool image::is_valid() const |
237 | { |
238 | return d && d->format != format_invalid; |
239 | } |
240 | |
241 | /** |
242 | \returns the format of the image |
243 | */ |
244 | image::format_enum image::format() const |
245 | { |
246 | return d ? d->format : format_invalid; |
247 | } |
248 | |
249 | /** |
250 | \returns whether the width of the image |
251 | */ |
252 | int image::width() const |
253 | { |
254 | return d ? d->width : 0; |
255 | } |
256 | |
257 | /** |
258 | \returns whether the height of the image |
259 | */ |
260 | int image::height() const |
261 | { |
262 | return d ? d->height : 0; |
263 | } |
264 | |
265 | /** |
266 | \returns the number of bytes in each row of the image |
267 | */ |
268 | int image::bytes_per_row() const |
269 | { |
270 | return d ? d->bytes_per_row : 0; |
271 | } |
272 | |
273 | /** |
274 | Access to the image bits. |
275 | |
276 | This function will detach and copy the shared data. |
277 | |
278 | \returns the pointer to the first pixel |
279 | */ |
280 | char *image::data() |
281 | { |
282 | if (!d) { |
283 | return nullptr; |
284 | } |
285 | |
286 | detach(); |
287 | return d->data; |
288 | } |
289 | |
290 | /** |
291 | Access to the image bits. |
292 | |
293 | This function provides const access to the data. |
294 | |
295 | \returns the pointer to the first pixel |
296 | */ |
297 | const char *image::const_data() const |
298 | { |
299 | return d ? d->data : nullptr; |
300 | } |
301 | |
302 | /** |
303 | Copy of a slice of the image. |
304 | |
305 | \param r the sub-area of this image to copy; if empty, the whole image is |
306 | copied |
307 | |
308 | \returns a new image representing the specified part of the current image |
309 | */ |
310 | image image::copy(const rect &r) const |
311 | { |
312 | if (r.is_empty()) { |
313 | image img(*this); |
314 | img.detach(); |
315 | return img; |
316 | } |
317 | |
318 | // ### FIXME |
319 | return *this; |
320 | } |
321 | |
322 | /** |
323 | Saves the current image to file. |
324 | |
325 | Using this function it is possible to save the image to the specified |
326 | \p file_name, in the specified \p out_format and with a resolution of the |
327 | specified \p dpi. |
328 | |
329 | Image formats commonly supported are: |
330 | \li PNG: \c png |
331 | \li JPEG: \c jpeg, \c jpg |
332 | \li TIFF: \c tiff |
333 | \li PNM: \c pnm (with Poppler >= 0.18) |
334 | |
335 | If an image format is not supported (check the result of |
336 | supported_image_formats()), the saving fails. |
337 | |
338 | \returns whether the saving succeeded |
339 | */ |
340 | bool image::save(const std::string &file_name, const std::string &out_format, int dpi) const |
341 | { |
342 | if (!is_valid() || file_name.empty() || out_format.empty()) { |
343 | return false; |
344 | } |
345 | |
346 | std::string fmt = out_format; |
347 | std::transform(first: fmt.begin(), last: fmt.end(), result: fmt.begin(), unary_op: tolower); |
348 | |
349 | std::unique_ptr<ImgWriter> w; |
350 | const int actual_dpi = dpi == -1 ? 75 : dpi; |
351 | if (false) { |
352 | } |
353 | #if defined(ENABLE_LIBPNG) |
354 | else if (fmt == "png" ) { |
355 | w = std::make_unique<PNGWriter>(); |
356 | } |
357 | #endif |
358 | #if defined(ENABLE_LIBJPEG) |
359 | else if (fmt == "jpeg" || fmt == "jpg" ) { |
360 | w = std::make_unique<JpegWriter>(); |
361 | } |
362 | #endif |
363 | #if defined(ENABLE_LIBTIFF) |
364 | else if (fmt == "tiff" ) { |
365 | w = std::make_unique<TiffWriter>(); |
366 | } |
367 | #endif |
368 | else if (fmt == "pnm" ) { |
369 | w = std::make_unique<NetPBMWriter>(args: pnm_format(format: d->format)); |
370 | } |
371 | if (!w.get()) { |
372 | return false; |
373 | } |
374 | FILE *f = fopen(filename: file_name.c_str(), modes: "wb" ); |
375 | if (!f) { |
376 | return false; |
377 | } |
378 | const FileCloser fc(f); |
379 | if (!w->init(f, width: d->width, height: d->height, hDPI: actual_dpi, vDPI: actual_dpi)) { |
380 | return false; |
381 | } |
382 | switch (d->format) { |
383 | case format_invalid: |
384 | return false; |
385 | case format_mono: |
386 | return false; |
387 | case format_gray8: { |
388 | std::vector<unsigned char> row(3 * d->width); |
389 | char *hptr = d->data; |
390 | for (int y = 0; y < d->height; ++y) { |
391 | unsigned char *rowptr = &row[0]; |
392 | for (int x = 0; x < d->width; ++x, rowptr += 3) { |
393 | rowptr[0] = *reinterpret_cast<unsigned char *>(hptr + x); |
394 | rowptr[1] = *reinterpret_cast<unsigned char *>(hptr + x); |
395 | rowptr[2] = *reinterpret_cast<unsigned char *>(hptr + x); |
396 | } |
397 | rowptr = &row[0]; |
398 | if (!w->writeRow(row: &rowptr)) { |
399 | return false; |
400 | } |
401 | hptr += d->bytes_per_row; |
402 | } |
403 | break; |
404 | } |
405 | case format_bgr24: { |
406 | std::vector<unsigned char> row(3 * d->width); |
407 | char *hptr = d->data; |
408 | for (int y = 0; y < d->height; ++y) { |
409 | unsigned char *rowptr = &row[0]; |
410 | for (int x = 0; x < d->width; ++x, rowptr += 3) { |
411 | rowptr[0] = *reinterpret_cast<unsigned char *>(hptr + x * 3 + 2); |
412 | rowptr[1] = *reinterpret_cast<unsigned char *>(hptr + x * 3 + 1); |
413 | rowptr[2] = *reinterpret_cast<unsigned char *>(hptr + x * 3); |
414 | } |
415 | rowptr = &row[0]; |
416 | if (!w->writeRow(row: &rowptr)) { |
417 | return false; |
418 | } |
419 | hptr += d->bytes_per_row; |
420 | } |
421 | break; |
422 | } |
423 | case format_rgb24: { |
424 | char *hptr = d->data; |
425 | for (int y = 0; y < d->height; ++y) { |
426 | if (!w->writeRow(row: reinterpret_cast<unsigned char **>(&hptr))) { |
427 | return false; |
428 | } |
429 | hptr += d->bytes_per_row; |
430 | } |
431 | break; |
432 | } |
433 | case format_argb32: { |
434 | std::vector<unsigned char> row(3 * d->width); |
435 | char *hptr = d->data; |
436 | for (int y = 0; y < d->height; ++y) { |
437 | unsigned char *rowptr = &row[0]; |
438 | for (int x = 0; x < d->width; ++x, rowptr += 3) { |
439 | const unsigned int pixel = *reinterpret_cast<unsigned int *>(hptr + x * 4); |
440 | rowptr[0] = (pixel >> 16) & 0xff; |
441 | rowptr[1] = (pixel >> 8) & 0xff; |
442 | rowptr[2] = pixel & 0xff; |
443 | } |
444 | rowptr = &row[0]; |
445 | if (!w->writeRow(row: &rowptr)) { |
446 | return false; |
447 | } |
448 | hptr += d->bytes_per_row; |
449 | } |
450 | break; |
451 | } |
452 | } |
453 | |
454 | if (!w->close()) { |
455 | return false; |
456 | } |
457 | |
458 | return true; |
459 | } |
460 | |
461 | /** |
462 | \returns a list of the supported image formats |
463 | */ |
464 | std::vector<std::string> image::supported_image_formats() |
465 | { |
466 | std::vector<std::string> formats; |
467 | #if defined(ENABLE_LIBPNG) |
468 | formats.emplace_back(args: "png" ); |
469 | #endif |
470 | #if defined(ENABLE_LIBJPEG) |
471 | formats.emplace_back(args: "jpeg" ); |
472 | formats.emplace_back(args: "jpg" ); |
473 | #endif |
474 | #if defined(ENABLE_LIBTIFF) |
475 | formats.emplace_back(args: "tiff" ); |
476 | #endif |
477 | formats.emplace_back(args: "pnm" ); |
478 | return formats; |
479 | } |
480 | |
481 | /** |
482 | Assignment operator. |
483 | */ |
484 | image &image::operator=(const image &img) |
485 | { |
486 | if (this == &img) { |
487 | return *this; |
488 | } |
489 | |
490 | if (img.d) { |
491 | ++img.d->ref; |
492 | } |
493 | image_private *old_d = d; |
494 | d = img.d; |
495 | if (old_d && !--old_d->ref) { |
496 | delete old_d; |
497 | } |
498 | return *this; |
499 | } |
500 | |
501 | void image::detach() |
502 | { |
503 | if (d->ref == 1) { |
504 | return; |
505 | } |
506 | |
507 | image_private *old_d = d; |
508 | d = image_private::create_data(width: old_d->width, height: old_d->height, format: old_d->format); |
509 | if (d) { |
510 | std::memcpy(dest: d->data, src: old_d->data, n: old_d->bytes_num); |
511 | --old_d->ref; |
512 | } else { |
513 | d = old_d; |
514 | } |
515 | } |
516 | |