1 | //======================================================================== |
2 | // |
3 | // SplashBitmap.cc |
4 | // |
5 | //======================================================================== |
6 | |
7 | //======================================================================== |
8 | // |
9 | // Modified under the Poppler project - http://poppler.freedesktop.org |
10 | // |
11 | // All changes made under the Poppler project to this file are licensed |
12 | // under GPL version 2 or later |
13 | // |
14 | // Copyright (C) 2006, 2009, 2010, 2012, 2015, 2018, 2019, 2021, 2022 Albert Astals Cid <aacid@kde.org> |
15 | // Copyright (C) 2007 Ilmari Heikkinen <ilmari.heikkinen@gmail.com> |
16 | // Copyright (C) 2009 Shen Liang <shenzhuxi@gmail.com> |
17 | // Copyright (C) 2009 Stefan Thomas <thomas@eload24.com> |
18 | // Copyright (C) 2010, 2012, 2017 Adrian Johnson <ajohnson@redneon.com> |
19 | // Copyright (C) 2010 Harry Roberts <harry.roberts@midnight-labs.org> |
20 | // Copyright (C) 2010 Christian Feuersänger <cfeuersaenger@googlemail.com> |
21 | // Copyright (C) 2010, 2015, 2019 William Bader <williambader@hotmail.com> |
22 | // Copyright (C) 2011-2013 Thomas Freitag <Thomas.Freitag@alfa.de> |
23 | // Copyright (C) 2012 Anthony Wesley <awesley@smartnetworks.com.au> |
24 | // Copyright (C) 2015, 2018 Adam Reichold <adamreichold@myopera.com> |
25 | // Copyright (C) 2016 Kenji Uno <ku@digitaldolphins.jp> |
26 | // Copyright (C) 2018 Martin Packman <gzlist@googlemail.com> |
27 | // Copyright (C) 2019 Christian Persch <chpe@src.gnome.org> |
28 | // Copyright (C) 2019 Oliver Sander <oliver.sander@tu-dresden.de> |
29 | // |
30 | // To see a description of the changes please see the Changelog file that |
31 | // came with your tarball or type make ChangeLog if you are building from git |
32 | // |
33 | //======================================================================== |
34 | |
35 | #include <config.h> |
36 | |
37 | #include <cstdio> |
38 | #include <cstring> |
39 | #include <cstdlib> |
40 | #include <climits> |
41 | #include "goo/gfile.h" |
42 | #include "goo/gmem.h" |
43 | #include "SplashErrorCodes.h" |
44 | #include "SplashBitmap.h" |
45 | #include "poppler/Error.h" |
46 | #include "poppler/GfxState.h" |
47 | #include "goo/JpegWriter.h" |
48 | #include "goo/PNGWriter.h" |
49 | #include "goo/TiffWriter.h" |
50 | #include "goo/ImgWriter.h" |
51 | |
52 | //------------------------------------------------------------------------ |
53 | // SplashBitmap |
54 | //------------------------------------------------------------------------ |
55 | |
56 | SplashBitmap::SplashBitmap(int widthA, int heightA, int rowPadA, SplashColorMode modeA, bool alphaA, bool topDown, const std::vector<GfxSeparationColorSpace *> *separationListA) |
57 | { |
58 | width = widthA; |
59 | height = heightA; |
60 | mode = modeA; |
61 | rowPad = rowPadA; |
62 | switch (mode) { |
63 | case splashModeMono1: |
64 | if (width > 0) { |
65 | rowSize = (width + 7) >> 3; |
66 | } else { |
67 | rowSize = -1; |
68 | } |
69 | break; |
70 | case splashModeMono8: |
71 | if (width > 0) { |
72 | rowSize = width; |
73 | } else { |
74 | rowSize = -1; |
75 | } |
76 | break; |
77 | case splashModeRGB8: |
78 | case splashModeBGR8: |
79 | if (width > 0 && width <= INT_MAX / 3) { |
80 | rowSize = width * 3; |
81 | } else { |
82 | rowSize = -1; |
83 | } |
84 | break; |
85 | case splashModeXBGR8: |
86 | if (width > 0 && width <= INT_MAX / 4) { |
87 | rowSize = width * 4; |
88 | } else { |
89 | rowSize = -1; |
90 | } |
91 | break; |
92 | case splashModeCMYK8: |
93 | if (width > 0 && width <= INT_MAX / 4) { |
94 | rowSize = width * 4; |
95 | } else { |
96 | rowSize = -1; |
97 | } |
98 | break; |
99 | case splashModeDeviceN8: |
100 | if (width > 0 && width <= static_cast<int>(INT_MAX / splashMaxColorComps)) { |
101 | rowSize = width * splashMaxColorComps; |
102 | } else { |
103 | rowSize = -1; |
104 | } |
105 | break; |
106 | } |
107 | if (rowSize > 0) { |
108 | rowSize += rowPad - 1; |
109 | rowSize -= rowSize % rowPad; |
110 | } |
111 | data = (SplashColorPtr)gmallocn_checkoverflow(count: rowSize, size: height); |
112 | if (data != nullptr) { |
113 | if (!topDown) { |
114 | data += (height - 1) * rowSize; |
115 | rowSize = -rowSize; |
116 | } |
117 | if (alphaA) { |
118 | alpha = (unsigned char *)gmallocn_checkoverflow(count: width, size: height); |
119 | } else { |
120 | alpha = nullptr; |
121 | } |
122 | } else { |
123 | alpha = nullptr; |
124 | } |
125 | separationList = new std::vector<GfxSeparationColorSpace *>(); |
126 | if (separationListA != nullptr) { |
127 | for (const GfxSeparationColorSpace *separation : *separationListA) { |
128 | separationList->push_back(x: (GfxSeparationColorSpace *)separation->copy()); |
129 | } |
130 | } |
131 | } |
132 | |
133 | SplashBitmap *SplashBitmap::copy(const SplashBitmap *src) |
134 | { |
135 | SplashBitmap *result = new SplashBitmap(src->getWidth(), src->getHeight(), src->getRowPad(), src->getMode(), src->getAlphaPtr() != nullptr, src->getRowSize() >= 0, src->getSeparationList()); |
136 | SplashColorConstPtr dataSource = src->getDataPtr(); |
137 | unsigned char *dataDest = result->getDataPtr(); |
138 | int amount = src->getRowSize(); |
139 | if (amount < 0) { |
140 | dataSource = dataSource + (src->getHeight() - 1) * amount; |
141 | dataDest = dataDest + (src->getHeight() - 1) * amount; |
142 | amount *= -src->getHeight(); |
143 | } else { |
144 | amount *= src->getHeight(); |
145 | } |
146 | memcpy(dest: dataDest, src: dataSource, n: amount); |
147 | if (src->getAlphaPtr() != nullptr) { |
148 | memcpy(dest: result->getAlphaPtr(), src: src->getAlphaPtr(), n: src->getWidth() * src->getHeight()); |
149 | } |
150 | return result; |
151 | } |
152 | |
153 | SplashBitmap::~SplashBitmap() |
154 | { |
155 | if (data) { |
156 | if (rowSize < 0) { |
157 | gfree(p: data + (height - 1) * rowSize); |
158 | } else { |
159 | gfree(p: data); |
160 | } |
161 | } |
162 | gfree(p: alpha); |
163 | for (auto entry : *separationList) { |
164 | delete entry; |
165 | } |
166 | delete separationList; |
167 | } |
168 | |
169 | SplashError SplashBitmap::writePNMFile(char *fileName) |
170 | { |
171 | FILE *f; |
172 | SplashError e; |
173 | |
174 | if (!(f = openFile(path: fileName, mode: "wb" ))) { |
175 | return splashErrOpenFile; |
176 | } |
177 | |
178 | e = this->writePNMFile(f); |
179 | |
180 | fclose(stream: f); |
181 | return e; |
182 | } |
183 | |
184 | SplashError SplashBitmap::writePNMFile(FILE *f) |
185 | { |
186 | SplashColorPtr row, p; |
187 | int x, y; |
188 | |
189 | switch (mode) { |
190 | |
191 | case splashModeMono1: |
192 | fprintf(stream: f, format: "P4\n%d %d\n" , width, height); |
193 | row = data; |
194 | for (y = 0; y < height; ++y) { |
195 | p = row; |
196 | for (x = 0; x < width; x += 8) { |
197 | fputc(c: *p ^ 0xff, stream: f); |
198 | ++p; |
199 | } |
200 | row += rowSize; |
201 | } |
202 | break; |
203 | |
204 | case splashModeMono8: |
205 | fprintf(stream: f, format: "P5\n%d %d\n255\n" , width, height); |
206 | row = data; |
207 | for (y = 0; y < height; ++y) { |
208 | fwrite(ptr: row, size: 1, n: width, s: f); |
209 | row += rowSize; |
210 | } |
211 | break; |
212 | |
213 | case splashModeRGB8: |
214 | fprintf(stream: f, format: "P6\n%d %d\n255\n" , width, height); |
215 | row = data; |
216 | for (y = 0; y < height; ++y) { |
217 | fwrite(ptr: row, size: 1, n: 3 * width, s: f); |
218 | row += rowSize; |
219 | } |
220 | break; |
221 | |
222 | case splashModeXBGR8: |
223 | fprintf(stream: f, format: "P6\n%d %d\n255\n" , width, height); |
224 | row = data; |
225 | for (y = 0; y < height; ++y) { |
226 | p = row; |
227 | for (x = 0; x < width; ++x) { |
228 | fputc(c: splashBGR8R(bgr8: p), stream: f); |
229 | fputc(c: splashBGR8G(bgr8: p), stream: f); |
230 | fputc(c: splashBGR8B(bgr8: p), stream: f); |
231 | p += 4; |
232 | } |
233 | row += rowSize; |
234 | } |
235 | break; |
236 | |
237 | case splashModeBGR8: |
238 | fprintf(stream: f, format: "P6\n%d %d\n255\n" , width, height); |
239 | row = data; |
240 | for (y = 0; y < height; ++y) { |
241 | p = row; |
242 | for (x = 0; x < width; ++x) { |
243 | fputc(c: splashBGR8R(bgr8: p), stream: f); |
244 | fputc(c: splashBGR8G(bgr8: p), stream: f); |
245 | fputc(c: splashBGR8B(bgr8: p), stream: f); |
246 | p += 3; |
247 | } |
248 | row += rowSize; |
249 | } |
250 | break; |
251 | |
252 | case splashModeCMYK8: |
253 | case splashModeDeviceN8: |
254 | // PNM doesn't support CMYK |
255 | error(category: errInternal, pos: -1, msg: "unsupported SplashBitmap mode" ); |
256 | return splashErrGeneric; |
257 | break; |
258 | } |
259 | return splashOk; |
260 | } |
261 | |
262 | SplashError SplashBitmap::writeAlphaPGMFile(char *fileName) |
263 | { |
264 | FILE *f; |
265 | |
266 | if (!alpha) { |
267 | return splashErrModeMismatch; |
268 | } |
269 | if (!(f = openFile(path: fileName, mode: "wb" ))) { |
270 | return splashErrOpenFile; |
271 | } |
272 | fprintf(stream: f, format: "P5\n%d %d\n255\n" , width, height); |
273 | fwrite(ptr: alpha, size: 1, n: width * height, s: f); |
274 | fclose(stream: f); |
275 | return splashOk; |
276 | } |
277 | |
278 | void SplashBitmap::getPixel(int x, int y, SplashColorPtr pixel) |
279 | { |
280 | SplashColorPtr p; |
281 | |
282 | if (y < 0 || y >= height || x < 0 || x >= width || !data) { |
283 | return; |
284 | } |
285 | switch (mode) { |
286 | case splashModeMono1: |
287 | p = &data[y * rowSize + (x >> 3)]; |
288 | pixel[0] = (p[0] & (0x80 >> (x & 7))) ? 0xff : 0x00; |
289 | break; |
290 | case splashModeMono8: |
291 | p = &data[y * rowSize + x]; |
292 | pixel[0] = p[0]; |
293 | break; |
294 | case splashModeRGB8: |
295 | p = &data[y * rowSize + 3 * x]; |
296 | pixel[0] = p[0]; |
297 | pixel[1] = p[1]; |
298 | pixel[2] = p[2]; |
299 | break; |
300 | case splashModeXBGR8: |
301 | p = &data[y * rowSize + 4 * x]; |
302 | pixel[0] = p[2]; |
303 | pixel[1] = p[1]; |
304 | pixel[2] = p[0]; |
305 | pixel[3] = p[3]; |
306 | break; |
307 | case splashModeBGR8: |
308 | p = &data[y * rowSize + 3 * x]; |
309 | pixel[0] = p[2]; |
310 | pixel[1] = p[1]; |
311 | pixel[2] = p[0]; |
312 | break; |
313 | case splashModeCMYK8: |
314 | p = &data[y * rowSize + 4 * x]; |
315 | pixel[0] = p[0]; |
316 | pixel[1] = p[1]; |
317 | pixel[2] = p[2]; |
318 | pixel[3] = p[3]; |
319 | break; |
320 | case splashModeDeviceN8: |
321 | p = &data[y * rowSize + (SPOT_NCOMPS + 4) * x]; |
322 | for (int cp = 0; cp < SPOT_NCOMPS + 4; cp++) { |
323 | pixel[cp] = p[cp]; |
324 | } |
325 | break; |
326 | } |
327 | } |
328 | |
329 | unsigned char SplashBitmap::getAlpha(int x, int y) |
330 | { |
331 | return alpha[y * width + x]; |
332 | } |
333 | |
334 | SplashColorPtr SplashBitmap::takeData() |
335 | { |
336 | SplashColorPtr data2; |
337 | |
338 | data2 = data; |
339 | data = nullptr; |
340 | return data2; |
341 | } |
342 | |
343 | SplashError SplashBitmap::writeImgFile(SplashImageFileFormat format, const char *fileName, double hDPI, double vDPI, WriteImgParams *params) |
344 | { |
345 | FILE *f; |
346 | SplashError e; |
347 | |
348 | if (!(f = openFile(path: fileName, mode: "wb" ))) { |
349 | return splashErrOpenFile; |
350 | } |
351 | |
352 | e = writeImgFile(format, f, hDPI, vDPI, params); |
353 | |
354 | fclose(stream: f); |
355 | return e; |
356 | } |
357 | |
358 | void SplashBitmap::setJpegParams(ImgWriter *writer, WriteImgParams *params) |
359 | { |
360 | #ifdef ENABLE_LIBJPEG |
361 | if (params) { |
362 | static_cast<JpegWriter *>(writer)->setProgressive(params->jpegProgressive); |
363 | static_cast<JpegWriter *>(writer)->setOptimize(params->jpegOptimize); |
364 | if (params->jpegQuality >= 0) { |
365 | static_cast<JpegWriter *>(writer)->setQuality(params->jpegQuality); |
366 | } |
367 | } |
368 | #endif |
369 | } |
370 | |
371 | SplashError SplashBitmap::writeImgFile(SplashImageFileFormat format, FILE *f, double hDPI, double vDPI, WriteImgParams *params) |
372 | { |
373 | ImgWriter *writer; |
374 | SplashError e; |
375 | |
376 | SplashColorMode imageWriterFormat = splashModeRGB8; |
377 | |
378 | switch (format) { |
379 | #ifdef ENABLE_LIBPNG |
380 | case splashFormatPng: |
381 | writer = new PNGWriter(); |
382 | break; |
383 | #endif |
384 | |
385 | #ifdef ENABLE_LIBJPEG |
386 | case splashFormatJpegCMYK: |
387 | writer = new JpegWriter(JpegWriter::CMYK); |
388 | setJpegParams(writer, params); |
389 | break; |
390 | case splashFormatJpeg: |
391 | writer = new JpegWriter(); |
392 | setJpegParams(writer, params); |
393 | break; |
394 | #endif |
395 | |
396 | #ifdef ENABLE_LIBTIFF |
397 | case splashFormatTiff: |
398 | switch (mode) { |
399 | case splashModeMono1: |
400 | writer = new TiffWriter(TiffWriter::MONOCHROME); |
401 | imageWriterFormat = splashModeMono1; |
402 | break; |
403 | case splashModeMono8: |
404 | writer = new TiffWriter(TiffWriter::GRAY); |
405 | imageWriterFormat = splashModeMono8; |
406 | break; |
407 | case splashModeRGB8: |
408 | case splashModeBGR8: |
409 | writer = new TiffWriter(TiffWriter::RGB); |
410 | break; |
411 | case splashModeCMYK8: |
412 | case splashModeDeviceN8: |
413 | writer = new TiffWriter(TiffWriter::CMYK); |
414 | break; |
415 | default: |
416 | fprintf(stderr, format: "TiffWriter: Mode %d not supported\n" , mode); |
417 | writer = new TiffWriter(); |
418 | } |
419 | if (writer && params) { |
420 | ((TiffWriter *)writer)->setCompressionString(params->tiffCompression.c_str()); |
421 | } |
422 | break; |
423 | #endif |
424 | |
425 | default: |
426 | // Not the greatest error message, but users of this function should |
427 | // have already checked whether their desired format is compiled in. |
428 | error(category: errInternal, pos: -1, msg: "Support for this image type not compiled in" ); |
429 | return splashErrGeneric; |
430 | } |
431 | |
432 | e = writeImgFile(writer, f, hDPI, vDPI, imageWriterFormat); |
433 | delete writer; |
434 | return e; |
435 | } |
436 | |
437 | #include "poppler/GfxState_helpers.h" |
438 | |
439 | void SplashBitmap::getRGBLine(int yl, SplashColorPtr line) |
440 | { |
441 | SplashColor col; |
442 | double c, m, y, k, c1, m1, y1, k1, r, g, b; |
443 | |
444 | for (int x = 0; x < width; x++) { |
445 | getPixel(x, y: yl, pixel: col); |
446 | c = byteToDbl(x: col[0]); |
447 | m = byteToDbl(x: col[1]); |
448 | y = byteToDbl(x: col[2]); |
449 | k = byteToDbl(x: col[3]); |
450 | if (separationList->size() > 0) { |
451 | for (std::size_t i = 0; i < separationList->size(); i++) { |
452 | if (col[i + 4] > 0) { |
453 | GfxCMYK cmyk; |
454 | GfxColor input; |
455 | input.c[0] = byteToCol(x: col[i + 4]); |
456 | GfxSeparationColorSpace *sepCS = (GfxSeparationColorSpace *)((*separationList)[i]); |
457 | sepCS->getCMYK(color: &input, cmyk: &cmyk); |
458 | col[0] = colToByte(x: cmyk.c); |
459 | col[1] = colToByte(x: cmyk.m); |
460 | col[2] = colToByte(x: cmyk.y); |
461 | col[3] = colToByte(x: cmyk.k); |
462 | c += byteToDbl(x: col[0]); |
463 | m += byteToDbl(x: col[1]); |
464 | y += byteToDbl(x: col[2]); |
465 | k += byteToDbl(x: col[3]); |
466 | } |
467 | } |
468 | if (c > 1) { |
469 | c = 1; |
470 | } |
471 | if (m > 1) { |
472 | m = 1; |
473 | } |
474 | if (y > 1) { |
475 | y = 1; |
476 | } |
477 | if (k > 1) { |
478 | k = 1; |
479 | } |
480 | } |
481 | c1 = 1 - c; |
482 | m1 = 1 - m; |
483 | y1 = 1 - y; |
484 | k1 = 1 - k; |
485 | cmykToRGBMatrixMultiplication(c, m, y, k, c1, m1, y1, k1, r, g, b); |
486 | *line++ = dblToByte(x: clip01(x: r)); |
487 | *line++ = dblToByte(x: clip01(x: g)); |
488 | *line++ = dblToByte(x: clip01(x: b)); |
489 | } |
490 | } |
491 | |
492 | void SplashBitmap::getXBGRLine(int yl, SplashColorPtr line, ConversionMode conversionMode) |
493 | { |
494 | SplashColor col; |
495 | double c, m, y, k, c1, m1, y1, k1, r, g, b; |
496 | |
497 | for (int x = 0; x < width; x++) { |
498 | getPixel(x, y: yl, pixel: col); |
499 | c = byteToDbl(x: col[0]); |
500 | m = byteToDbl(x: col[1]); |
501 | y = byteToDbl(x: col[2]); |
502 | k = byteToDbl(x: col[3]); |
503 | if (separationList->size() > 0) { |
504 | for (std::size_t i = 0; i < separationList->size(); i++) { |
505 | if (col[i + 4] > 0) { |
506 | GfxCMYK cmyk; |
507 | GfxColor input; |
508 | input.c[0] = byteToCol(x: col[i + 4]); |
509 | GfxSeparationColorSpace *sepCS = (GfxSeparationColorSpace *)((*separationList)[i]); |
510 | sepCS->getCMYK(color: &input, cmyk: &cmyk); |
511 | col[0] = colToByte(x: cmyk.c); |
512 | col[1] = colToByte(x: cmyk.m); |
513 | col[2] = colToByte(x: cmyk.y); |
514 | col[3] = colToByte(x: cmyk.k); |
515 | c += byteToDbl(x: col[0]); |
516 | m += byteToDbl(x: col[1]); |
517 | y += byteToDbl(x: col[2]); |
518 | k += byteToDbl(x: col[3]); |
519 | } |
520 | } |
521 | if (c > 1) { |
522 | c = 1; |
523 | } |
524 | if (m > 1) { |
525 | m = 1; |
526 | } |
527 | if (y > 1) { |
528 | y = 1; |
529 | } |
530 | if (k > 1) { |
531 | k = 1; |
532 | } |
533 | } |
534 | c1 = 1 - c; |
535 | m1 = 1 - m; |
536 | y1 = 1 - y; |
537 | k1 = 1 - k; |
538 | cmykToRGBMatrixMultiplication(c, m, y, k, c1, m1, y1, k1, r, g, b); |
539 | |
540 | if (conversionMode == conversionAlphaPremultiplied) { |
541 | const double a = getAlpha(x, y: yl) / 255.0; |
542 | |
543 | *line++ = dblToByte(x: clip01(x: b * a)); |
544 | *line++ = dblToByte(x: clip01(x: g * a)); |
545 | *line++ = dblToByte(x: clip01(x: r * a)); |
546 | } else { |
547 | *line++ = dblToByte(x: clip01(x: b)); |
548 | *line++ = dblToByte(x: clip01(x: g)); |
549 | *line++ = dblToByte(x: clip01(x: r)); |
550 | } |
551 | |
552 | if (conversionMode != conversionOpaque) { |
553 | *line++ = getAlpha(x, y: yl); |
554 | } else { |
555 | *line++ = 255; |
556 | } |
557 | } |
558 | } |
559 | |
560 | static inline unsigned char div255(int x) |
561 | { |
562 | return (unsigned char)((x + (x >> 8) + 0x80) >> 8); |
563 | } |
564 | |
565 | bool SplashBitmap::convertToXBGR(ConversionMode conversionMode) |
566 | { |
567 | if (mode == splashModeXBGR8) { |
568 | if (conversionMode != conversionOpaque) { |
569 | // Copy the alpha channel into the fourth component so that XBGR becomes ABGR. |
570 | const SplashColorPtr dbegin = data; |
571 | const SplashColorPtr dend = data + rowSize * height; |
572 | |
573 | unsigned char *const abegin = alpha; |
574 | unsigned char *const aend = alpha + width * height; |
575 | |
576 | SplashColorPtr d = dbegin; |
577 | unsigned char *a = abegin; |
578 | |
579 | if (conversionMode == conversionAlphaPremultiplied) { |
580 | for (; d < dend && a < aend; d += 4, a += 1) { |
581 | d[0] = div255(x: d[0] * *a); |
582 | d[1] = div255(x: d[1] * *a); |
583 | d[2] = div255(x: d[2] * *a); |
584 | d[3] = *a; |
585 | } |
586 | } else { |
587 | for (d += 3; d < dend && a < aend; d += 4, a += 1) { |
588 | *d = *a; |
589 | } |
590 | } |
591 | } |
592 | |
593 | return true; |
594 | } |
595 | |
596 | int newrowSize = width * 4; |
597 | SplashColorPtr newdata = (SplashColorPtr)gmallocn_checkoverflow(count: newrowSize, size: height); |
598 | if (newdata != nullptr) { |
599 | for (int y = 0; y < height; y++) { |
600 | unsigned char *row = newdata + y * newrowSize; |
601 | getXBGRLine(yl: y, line: row, conversionMode); |
602 | } |
603 | if (rowSize < 0) { |
604 | gfree(p: data + (height - 1) * rowSize); |
605 | } else { |
606 | gfree(p: data); |
607 | } |
608 | data = newdata; |
609 | rowSize = newrowSize; |
610 | mode = splashModeXBGR8; |
611 | } |
612 | return newdata != nullptr; |
613 | } |
614 | |
615 | void SplashBitmap::getCMYKLine(int yl, SplashColorPtr line) |
616 | { |
617 | SplashColor col; |
618 | |
619 | for (int x = 0; x < width; x++) { |
620 | getPixel(x, y: yl, pixel: col); |
621 | if (separationList->size() > 0) { |
622 | double c, m, y, k; |
623 | c = byteToDbl(x: col[0]); |
624 | m = byteToDbl(x: col[1]); |
625 | y = byteToDbl(x: col[2]); |
626 | k = byteToDbl(x: col[3]); |
627 | for (std::size_t i = 0; i < separationList->size(); i++) { |
628 | if (col[i + 4] > 0) { |
629 | GfxCMYK cmyk; |
630 | GfxColor input; |
631 | input.c[0] = byteToCol(x: col[i + 4]); |
632 | GfxSeparationColorSpace *sepCS = (GfxSeparationColorSpace *)((*separationList)[i]); |
633 | sepCS->getCMYK(color: &input, cmyk: &cmyk); |
634 | col[0] = colToByte(x: cmyk.c); |
635 | col[1] = colToByte(x: cmyk.m); |
636 | col[2] = colToByte(x: cmyk.y); |
637 | col[3] = colToByte(x: cmyk.k); |
638 | c += byteToDbl(x: col[0]); |
639 | m += byteToDbl(x: col[1]); |
640 | y += byteToDbl(x: col[2]); |
641 | k += byteToDbl(x: col[3]); |
642 | } |
643 | } |
644 | col[0] = dblToByte(x: clip01(x: c)); |
645 | col[1] = dblToByte(x: clip01(x: m)); |
646 | col[2] = dblToByte(x: clip01(x: y)); |
647 | col[3] = dblToByte(x: clip01(x: k)); |
648 | } |
649 | *line++ = col[0]; |
650 | *line++ = col[1]; |
651 | *line++ = col[2]; |
652 | *line++ = col[3]; |
653 | } |
654 | } |
655 | |
656 | SplashError SplashBitmap::writeImgFile(ImgWriter *writer, FILE *f, double hDPI, double vDPI, SplashColorMode imageWriterFormat) |
657 | { |
658 | if (mode != splashModeRGB8 && mode != splashModeMono8 && mode != splashModeMono1 && mode != splashModeXBGR8 && mode != splashModeBGR8 && mode != splashModeCMYK8 && mode != splashModeDeviceN8) { |
659 | error(category: errInternal, pos: -1, msg: "unsupported SplashBitmap mode" ); |
660 | return splashErrGeneric; |
661 | } |
662 | |
663 | if (!writer->init(f, width, height, hDPI, vDPI)) { |
664 | return splashErrGeneric; |
665 | } |
666 | |
667 | switch (mode) { |
668 | case splashModeCMYK8: |
669 | if (writer->supportCMYK()) { |
670 | SplashColorPtr row; |
671 | unsigned char **row_pointers = new unsigned char *[height]; |
672 | row = data; |
673 | |
674 | for (int y = 0; y < height; ++y) { |
675 | row_pointers[y] = row; |
676 | row += rowSize; |
677 | } |
678 | if (!writer->writePointers(rowPointers: row_pointers, rowCount: height)) { |
679 | delete[] row_pointers; |
680 | return splashErrGeneric; |
681 | } |
682 | delete[] row_pointers; |
683 | } else { |
684 | unsigned char *row = new unsigned char[3 * width]; |
685 | for (int y = 0; y < height; y++) { |
686 | getRGBLine(yl: y, line: row); |
687 | if (!writer->writeRow(row: &row)) { |
688 | delete[] row; |
689 | return splashErrGeneric; |
690 | } |
691 | } |
692 | delete[] row; |
693 | } |
694 | break; |
695 | case splashModeDeviceN8: |
696 | if (writer->supportCMYK()) { |
697 | unsigned char *row = new unsigned char[4 * width]; |
698 | for (int y = 0; y < height; y++) { |
699 | getCMYKLine(yl: y, line: row); |
700 | if (!writer->writeRow(row: &row)) { |
701 | delete[] row; |
702 | return splashErrGeneric; |
703 | } |
704 | } |
705 | delete[] row; |
706 | } else { |
707 | unsigned char *row = new unsigned char[3 * width]; |
708 | for (int y = 0; y < height; y++) { |
709 | getRGBLine(yl: y, line: row); |
710 | if (!writer->writeRow(row: &row)) { |
711 | delete[] row; |
712 | return splashErrGeneric; |
713 | } |
714 | } |
715 | delete[] row; |
716 | } |
717 | break; |
718 | case splashModeRGB8: { |
719 | SplashColorPtr row; |
720 | unsigned char **row_pointers = new unsigned char *[height]; |
721 | row = data; |
722 | |
723 | for (int y = 0; y < height; ++y) { |
724 | row_pointers[y] = row; |
725 | row += rowSize; |
726 | } |
727 | if (!writer->writePointers(rowPointers: row_pointers, rowCount: height)) { |
728 | delete[] row_pointers; |
729 | return splashErrGeneric; |
730 | } |
731 | delete[] row_pointers; |
732 | } break; |
733 | |
734 | case splashModeBGR8: { |
735 | unsigned char *row = new unsigned char[3 * width]; |
736 | for (int y = 0; y < height; y++) { |
737 | // Convert into a PNG row |
738 | for (int x = 0; x < width; x++) { |
739 | row[3 * x] = data[y * rowSize + x * 3 + 2]; |
740 | row[3 * x + 1] = data[y * rowSize + x * 3 + 1]; |
741 | row[3 * x + 2] = data[y * rowSize + x * 3]; |
742 | } |
743 | |
744 | if (!writer->writeRow(row: &row)) { |
745 | delete[] row; |
746 | return splashErrGeneric; |
747 | } |
748 | } |
749 | delete[] row; |
750 | } break; |
751 | |
752 | case splashModeXBGR8: { |
753 | unsigned char *row = new unsigned char[3 * width]; |
754 | for (int y = 0; y < height; y++) { |
755 | // Convert into a PNG row |
756 | for (int x = 0; x < width; x++) { |
757 | row[3 * x] = data[y * rowSize + x * 4 + 2]; |
758 | row[3 * x + 1] = data[y * rowSize + x * 4 + 1]; |
759 | row[3 * x + 2] = data[y * rowSize + x * 4]; |
760 | } |
761 | |
762 | if (!writer->writeRow(row: &row)) { |
763 | delete[] row; |
764 | return splashErrGeneric; |
765 | } |
766 | } |
767 | delete[] row; |
768 | } break; |
769 | |
770 | case splashModeMono8: { |
771 | if (imageWriterFormat == splashModeMono8) { |
772 | SplashColorPtr row; |
773 | unsigned char **row_pointers = new unsigned char *[height]; |
774 | row = data; |
775 | |
776 | for (int y = 0; y < height; ++y) { |
777 | row_pointers[y] = row; |
778 | row += rowSize; |
779 | } |
780 | if (!writer->writePointers(rowPointers: row_pointers, rowCount: height)) { |
781 | delete[] row_pointers; |
782 | return splashErrGeneric; |
783 | } |
784 | delete[] row_pointers; |
785 | } else if (imageWriterFormat == splashModeRGB8) { |
786 | unsigned char *row = new unsigned char[3 * width]; |
787 | for (int y = 0; y < height; y++) { |
788 | // Convert into a PNG row |
789 | for (int x = 0; x < width; x++) { |
790 | row[3 * x] = data[y * rowSize + x]; |
791 | row[3 * x + 1] = data[y * rowSize + x]; |
792 | row[3 * x + 2] = data[y * rowSize + x]; |
793 | } |
794 | |
795 | if (!writer->writeRow(row: &row)) { |
796 | delete[] row; |
797 | return splashErrGeneric; |
798 | } |
799 | } |
800 | delete[] row; |
801 | } else { |
802 | // only splashModeMono8 or splashModeRGB8 |
803 | return splashErrGeneric; |
804 | } |
805 | } break; |
806 | |
807 | case splashModeMono1: { |
808 | if (imageWriterFormat == splashModeMono1) { |
809 | SplashColorPtr row; |
810 | unsigned char **row_pointers = new unsigned char *[height]; |
811 | row = data; |
812 | |
813 | for (int y = 0; y < height; ++y) { |
814 | row_pointers[y] = row; |
815 | row += rowSize; |
816 | } |
817 | if (!writer->writePointers(rowPointers: row_pointers, rowCount: height)) { |
818 | delete[] row_pointers; |
819 | return splashErrGeneric; |
820 | } |
821 | delete[] row_pointers; |
822 | } else if (imageWriterFormat == splashModeRGB8) { |
823 | unsigned char *row = new unsigned char[3 * width]; |
824 | for (int y = 0; y < height; y++) { |
825 | // Convert into a PNG row |
826 | for (int x = 0; x < width; x++) { |
827 | getPixel(x, y, pixel: &row[3 * x]); |
828 | row[3 * x + 1] = row[3 * x]; |
829 | row[3 * x + 2] = row[3 * x]; |
830 | } |
831 | |
832 | if (!writer->writeRow(row: &row)) { |
833 | delete[] row; |
834 | return splashErrGeneric; |
835 | } |
836 | } |
837 | delete[] row; |
838 | } else { |
839 | // only splashModeMono1 or splashModeRGB8 |
840 | return splashErrGeneric; |
841 | } |
842 | } break; |
843 | |
844 | default: |
845 | // can't happen |
846 | break; |
847 | } |
848 | |
849 | if (!writer->close()) { |
850 | return splashErrGeneric; |
851 | } |
852 | |
853 | return splashOk; |
854 | } |
855 | |