1//========================================================================
2//
3// JpegWriter.cc
4//
5// This file is licensed under the GPLv2 or later
6//
7// Copyright (C) 2009 Stefan Thomas <thomas@eload24.com>
8// Copyright (C) 2010, 2012, 2017 Adrian Johnson <ajohnson@redneon.com>
9// Copyright (C) 2010 Harry Roberts <harry.roberts@midnight-labs.org>
10// Copyright (C) 2011 Thomas Freitag <Thomas.Freitag@alfa.de>
11// Copyright (C) 2013 Peter Breitenlohner <peb@mppmu.mpg.de>
12// Copyright (C) 2017, 2018, 2022 Albert Astals Cid <aacid@kde.org>
13// Copyright (C) 2018 Martin Packman <gzlist@googlemail.com>
14// Copyright (C) 2018 Ed Porras <ed@motologic.com>
15// Copyright (C) 2021 Peter Williams <peter@newton.cx>
16// Copyright (C) 2023 Jordan Abrahams-Whitehead <ajordanr@google.com>
17//
18//========================================================================
19
20#include "JpegWriter.h"
21
22#include <limits>
23
24#ifdef ENABLE_LIBJPEG
25
26# include "poppler/Error.h"
27# include <cstdio>
28extern "C" {
29# include <jpeglib.h>
30}
31
32struct JpegWriterPrivate
33{
34 bool progressive;
35 bool optimize;
36 int quality;
37 JpegWriter::Format format;
38 struct jpeg_compress_struct cinfo;
39 struct jpeg_error_mgr jerr;
40};
41
42static void outputMessage(j_common_ptr cinfo)
43{
44 char buffer[JMSG_LENGTH_MAX];
45
46 // Create the message
47 (*cinfo->err->format_message)(cinfo, buffer);
48
49 // Send it to poppler's error handler
50 error(category: errInternal, pos: -1, msg: "{0:s}", buffer);
51}
52
53JpegWriter::JpegWriter(int q, bool p, Format formatA)
54{
55 priv = new JpegWriterPrivate;
56 priv->progressive = p;
57 priv->optimize = false;
58 priv->quality = q;
59 priv->format = formatA;
60}
61
62JpegWriter::JpegWriter(Format formatA) : JpegWriter(-1, false, formatA) { }
63
64JpegWriter::~JpegWriter()
65{
66 // cleanup
67 jpeg_destroy_compress(cinfo: &priv->cinfo);
68 delete priv;
69}
70
71void JpegWriter::setQuality(int quality)
72{
73 priv->quality = quality;
74}
75
76void JpegWriter::setProgressive(bool progressive)
77{
78 priv->progressive = progressive;
79}
80
81void JpegWriter::setOptimize(bool optimize)
82{
83 priv->optimize = optimize;
84}
85
86bool JpegWriter::init(FILE *f, int width, int height, double hDPI, double vDPI)
87{
88 if (hDPI < 0 || vDPI < 0 || hDPI > std::numeric_limits<UINT16>::max() || vDPI > std::numeric_limits<UINT16>::max()) {
89 error(category: errInternal, pos: -1, msg: "JpegWriter::init: hDPI or vDPI values are invalid {0:f} {1:f}", hDPI, vDPI);
90 return false;
91 }
92
93 // Setup error handler
94 priv->cinfo.err = jpeg_std_error(err: &priv->jerr);
95 priv->jerr.output_message = &outputMessage;
96
97 // Initialize libjpeg
98 jpeg_create_compress(&priv->cinfo);
99
100 // First set colorspace and call jpeg_set_defaults() since
101 // jpeg_set_defaults() sets default values for all fields in
102 // cinfo based on the colorspace.
103 switch (priv->format) {
104 case RGB:
105 priv->cinfo.in_color_space = JCS_RGB;
106 break;
107 case GRAY:
108 priv->cinfo.in_color_space = JCS_GRAYSCALE;
109 break;
110 case CMYK:
111 priv->cinfo.in_color_space = JCS_CMYK;
112 break;
113 default:
114 return false;
115 }
116 jpeg_set_defaults(cinfo: &priv->cinfo);
117
118 // Set destination file
119 jpeg_stdio_dest(cinfo: &priv->cinfo, outfile: f);
120
121 // Set libjpeg configuration
122 priv->cinfo.image_width = width;
123 priv->cinfo.image_height = height;
124 priv->cinfo.density_unit = 1; // dots per inch
125 priv->cinfo.X_density = static_cast<UINT16>(hDPI);
126 priv->cinfo.Y_density = static_cast<UINT16>(vDPI);
127 switch (priv->format) {
128 case GRAY:
129 priv->cinfo.input_components = 1;
130 break;
131 case RGB:
132 priv->cinfo.input_components = 3;
133 break;
134 case CMYK:
135 priv->cinfo.input_components = 4;
136 jpeg_set_colorspace(cinfo: &priv->cinfo, colorspace: JCS_YCCK);
137 priv->cinfo.write_JFIF_header = TRUE;
138 break;
139 default:
140 return false;
141 }
142
143 // Set quality
144 if (priv->quality >= 0 && priv->quality <= 100) {
145 jpeg_set_quality(cinfo: &priv->cinfo, quality: priv->quality, TRUE);
146 }
147
148 // Use progressive mode
149 if (priv->progressive) {
150 jpeg_simple_progression(cinfo: &priv->cinfo);
151 }
152
153 // Set whether to compute optimal Huffman coding tables
154 priv->cinfo.optimize_coding = static_cast<boolean>(priv->optimize);
155
156 // Get ready for data
157 jpeg_start_compress(cinfo: &priv->cinfo, TRUE);
158
159 return true;
160}
161
162bool JpegWriter::writePointers(unsigned char **rowPointers, int rowCount)
163{
164 if (priv->format == CMYK) {
165 for (int y = 0; y < rowCount; y++) {
166 unsigned char *row = rowPointers[y];
167 for (unsigned int x = 0; x < priv->cinfo.image_width; x++) {
168 for (int n = 0; n < 4; n++) {
169 *row = 0xff - *row;
170 row++;
171 }
172 }
173 }
174 }
175 // Write all rows to the file
176 jpeg_write_scanlines(cinfo: &priv->cinfo, scanlines: rowPointers, num_lines: rowCount);
177
178 return true;
179}
180
181bool JpegWriter::writeRow(unsigned char **rowPointer)
182{
183 if (priv->format == CMYK) {
184 unsigned char *row = rowPointer[0];
185 for (unsigned int x = 0; x < priv->cinfo.image_width; x++) {
186 for (int n = 0; n < 4; n++) {
187 *row = 0xff - *row;
188 row++;
189 }
190 }
191 }
192 // Write the row to the file
193 jpeg_write_scanlines(cinfo: &priv->cinfo, scanlines: rowPointer, num_lines: 1);
194
195 return true;
196}
197
198bool JpegWriter::close()
199{
200 jpeg_finish_compress(cinfo: &priv->cinfo);
201
202 return true;
203}
204
205bool JpegWriter::supportCMYK()
206{
207 return priv->format == CMYK;
208}
209
210#endif
211

source code of poppler/goo/JpegWriter.cc