beta-0.89.2
[luatex.git] / source / libs / poppler / poppler-src / splash / SplashBitmap.cc
blob7c14b3161b15614deaa760c86d21abb5e1fcae9d
1 //========================================================================
2 //
3 // SplashBitmap.cc
4 //
5 //========================================================================
7 //========================================================================
8 //
9 // Modified under the Poppler project - http://poppler.freedesktop.org
11 // All changes made under the Poppler project to this file are licensed
12 // under GPL version 2 or later
14 // Copyright (C) 2006, 2009, 2010, 2012, 2015 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 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 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 Adam Reichold <adamreichold@myopera.com>
26 // To see a description of the changes please see the Changelog file that
27 // came with your tarball or type make ChangeLog if you are building from git
29 //========================================================================
31 #include <config.h>
33 #ifdef USE_GCC_PRAGMAS
34 #pragma implementation
35 #endif
37 #include <stdio.h>
38 #include <string.h>
39 #include <stdlib.h>
40 #include <limits.h>
41 #include "goo/gmem.h"
42 #include "SplashErrorCodes.h"
43 #include "SplashBitmap.h"
44 #include "poppler/Error.h"
45 #include "goo/JpegWriter.h"
46 #include "goo/PNGWriter.h"
47 #include "goo/TiffWriter.h"
48 #include "goo/ImgWriter.h"
49 #include "goo/GooList.h"
51 //------------------------------------------------------------------------
52 // SplashBitmap
53 //------------------------------------------------------------------------
55 SplashBitmap::SplashBitmap(int widthA, int heightA, int rowPadA,
56 SplashColorMode modeA, GBool alphaA,
57 GBool topDown, GooList *separationListA) {
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;
69 break;
70 case splashModeMono8:
71 if (width > 0) {
72 rowSize = width;
73 } else {
74 rowSize = -1;
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;
84 break;
85 case splashModeXBGR8:
86 if (width > 0 && width <= INT_MAX / 4) {
87 rowSize = width * 4;
88 } else {
89 rowSize = -1;
91 break;
92 #if SPLASH_CMYK
93 case splashModeCMYK8:
94 if (width > 0 && width <= INT_MAX / 4) {
95 rowSize = width * 4;
96 } else {
97 rowSize = -1;
99 break;
100 case splashModeDeviceN8:
101 if (width > 0 && width <= INT_MAX / 4) {
102 rowSize = width * (SPOT_NCOMPS + 4);
103 } else {
104 rowSize = -1;
106 break;
107 #endif
109 if (rowSize > 0) {
110 rowSize += rowPad - 1;
111 rowSize -= rowSize % rowPad;
113 data = (SplashColorPtr)gmallocn_checkoverflow(rowSize, height);
114 if (data != NULL) {
115 if (!topDown) {
116 data += (height - 1) * rowSize;
117 rowSize = -rowSize;
119 if (alphaA) {
120 alpha = (Guchar *)gmallocn(width, height);
121 } else {
122 alpha = NULL;
124 } else {
125 alpha = NULL;
127 separationList = new GooList();
128 if (separationListA != NULL)
129 for (int i = 0; i < separationListA->getLength(); i++)
130 separationList->append(((GfxSeparationColorSpace *) separationListA->get(i))->copy());
133 SplashBitmap *SplashBitmap::copy(SplashBitmap *src) {
134 SplashBitmap *result = new SplashBitmap(src->getWidth(), src->getHeight(), src->getRowPad(),
135 src->getMode(), src->getAlphaPtr() != NULL, src->getRowSize() >= 0, src->getSeparationList());
136 Guchar *dataSource = src->getDataPtr();
137 Guchar *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();
146 memcpy(dataDest, dataSource, amount);
147 if (src->getAlphaPtr() != NULL) {
148 memcpy(result->getAlphaPtr(), src->getAlphaPtr(), src->getWidth() * src->getHeight());
150 return result;
153 SplashBitmap::~SplashBitmap() {
154 if (data) {
155 if (rowSize < 0) {
156 gfree(data + (height - 1) * rowSize);
157 } else {
158 gfree(data);
161 gfree(alpha);
162 deleteGooList(separationList, GfxSeparationColorSpace);
166 SplashError SplashBitmap::writePNMFile(char *fileName) {
167 FILE *f;
168 SplashError e;
170 if (!(f = fopen(fileName, "wb"))) {
171 return splashErrOpenFile;
174 e = this->writePNMFile(f);
176 fclose(f);
177 return e;
181 SplashError SplashBitmap::writePNMFile(FILE *f) {
182 SplashColorPtr row, p;
183 int x, y;
185 switch (mode) {
187 case splashModeMono1:
188 fprintf(f, "P4\n%d %d\n", width, height);
189 row = data;
190 for (y = 0; y < height; ++y) {
191 p = row;
192 for (x = 0; x < width; x += 8) {
193 fputc(*p ^ 0xff, f);
194 ++p;
196 row += rowSize;
198 break;
200 case splashModeMono8:
201 fprintf(f, "P5\n%d %d\n255\n", width, height);
202 row = data;
203 for (y = 0; y < height; ++y) {
204 fwrite(row, 1, width, f);
205 row += rowSize;
207 break;
209 case splashModeRGB8:
210 fprintf(f, "P6\n%d %d\n255\n", width, height);
211 row = data;
212 for (y = 0; y < height; ++y) {
213 fwrite(row, 1, 3 * width, f);
214 row += rowSize;
216 break;
218 case splashModeXBGR8:
219 fprintf(f, "P6\n%d %d\n255\n", width, height);
220 row = data;
221 for (y = 0; y < height; ++y) {
222 p = row;
223 for (x = 0; x < width; ++x) {
224 fputc(splashBGR8R(p), f);
225 fputc(splashBGR8G(p), f);
226 fputc(splashBGR8B(p), f);
227 p += 4;
229 row += rowSize;
231 break;
234 case splashModeBGR8:
235 fprintf(f, "P6\n%d %d\n255\n", width, height);
236 row = data;
237 for (y = 0; y < height; ++y) {
238 p = row;
239 for (x = 0; x < width; ++x) {
240 fputc(splashBGR8R(p), f);
241 fputc(splashBGR8G(p), f);
242 fputc(splashBGR8B(p), f);
243 p += 3;
245 row += rowSize;
247 break;
249 #if SPLASH_CMYK
250 case splashModeCMYK8:
251 case splashModeDeviceN8:
252 // PNM doesn't support CMYK
253 error(errInternal, -1, "unsupported SplashBitmap mode");
254 return splashErrGeneric;
255 break;
256 #endif
258 return splashOk;
261 SplashError SplashBitmap::writeAlphaPGMFile(char *fileName) {
262 FILE *f;
264 if (!alpha) {
265 return splashErrModeMismatch;
267 if (!(f = fopen(fileName, "wb"))) {
268 return splashErrOpenFile;
270 fprintf(f, "P5\n%d %d\n255\n", width, height);
271 fwrite(alpha, 1, width * height, f);
272 fclose(f);
273 return splashOk;
276 void SplashBitmap::getPixel(int x, int y, SplashColorPtr pixel) {
277 SplashColorPtr p;
279 if (y < 0 || y >= height || x < 0 || x >= width || !data) {
280 return;
282 switch (mode) {
283 case splashModeMono1:
284 p = &data[y * rowSize + (x >> 3)];
285 pixel[0] = (p[0] & (0x80 >> (x & 7))) ? 0xff : 0x00;
286 break;
287 case splashModeMono8:
288 p = &data[y * rowSize + x];
289 pixel[0] = p[0];
290 break;
291 case splashModeRGB8:
292 p = &data[y * rowSize + 3 * x];
293 pixel[0] = p[0];
294 pixel[1] = p[1];
295 pixel[2] = p[2];
296 break;
297 case splashModeXBGR8:
298 p = &data[y * rowSize + 4 * x];
299 pixel[0] = p[2];
300 pixel[1] = p[1];
301 pixel[2] = p[0];
302 pixel[3] = p[3];
303 break;
304 case splashModeBGR8:
305 p = &data[y * rowSize + 3 * x];
306 pixel[0] = p[2];
307 pixel[1] = p[1];
308 pixel[2] = p[0];
309 break;
310 #if SPLASH_CMYK
311 case splashModeCMYK8:
312 p = &data[y * rowSize + 4 * x];
313 pixel[0] = p[0];
314 pixel[1] = p[1];
315 pixel[2] = p[2];
316 pixel[3] = p[3];
317 break;
318 case splashModeDeviceN8:
319 p = &data[y * rowSize + (SPOT_NCOMPS + 4) * x];
320 for (int cp = 0; cp < SPOT_NCOMPS + 4; cp++)
321 pixel[cp] = p[cp];
322 break;
323 #endif
327 Guchar SplashBitmap::getAlpha(int x, int y) {
328 return alpha[y * width + x];
331 SplashColorPtr SplashBitmap::takeData() {
332 SplashColorPtr data2;
334 data2 = data;
335 data = NULL;
336 return data2;
339 SplashError SplashBitmap::writeImgFile(SplashImageFileFormat format, char *fileName, int hDPI, int vDPI, const char *compressionString) {
340 FILE *f;
341 SplashError e;
343 if (!(f = fopen(fileName, "wb"))) {
344 return splashErrOpenFile;
347 e = writeImgFile(format, f, hDPI, vDPI, compressionString);
349 fclose(f);
350 return e;
353 SplashError SplashBitmap::writeImgFile(SplashImageFileFormat format, FILE *f, int hDPI, int vDPI, const char *compressionString) {
354 ImgWriter *writer;
355 SplashError e;
357 switch (format) {
358 #ifdef ENABLE_LIBPNG
359 case splashFormatPng:
360 writer = new PNGWriter();
361 break;
362 #endif
364 #ifdef ENABLE_LIBJPEG
365 #if SPLASH_CMYK
366 case splashFormatJpegCMYK:
367 writer = new JpegWriter(JpegWriter::CMYK);
368 break;
369 #endif
370 case splashFormatJpeg:
371 writer = new JpegWriter();
372 break;
373 #endif
375 #ifdef ENABLE_LIBTIFF
376 case splashFormatTiff:
377 switch (mode) {
378 case splashModeMono1:
379 writer = new TiffWriter(TiffWriter::MONOCHROME);
380 break;
381 case splashModeMono8:
382 writer = new TiffWriter(TiffWriter::GRAY);
383 break;
384 case splashModeRGB8:
385 case splashModeBGR8:
386 writer = new TiffWriter(TiffWriter::RGB);
387 break;
388 #if SPLASH_CMYK
389 case splashModeCMYK8:
390 case splashModeDeviceN8:
391 writer = new TiffWriter(TiffWriter::CMYK);
392 break;
393 #endif
394 default:
395 fprintf(stderr, "TiffWriter: Mode %d not supported\n", mode);
396 writer = new TiffWriter();
398 if (writer) {
399 ((TiffWriter *)writer)->setCompressionString(compressionString);
401 break;
402 #endif
404 default:
405 // Not the greatest error message, but users of this function should
406 // have already checked whether their desired format is compiled in.
407 error(errInternal, -1, "Support for this image type not compiled in");
408 return splashErrGeneric;
411 e = writeImgFile(writer, f, hDPI, vDPI);
412 delete writer;
413 return e;
416 #include "poppler/GfxState_helpers.h"
418 void SplashBitmap::getRGBLine(int yl, SplashColorPtr line) {
419 SplashColor col;
420 double c, m, y, k, c1, m1, y1, k1, r, g, b;
422 for (int x = 0; x < width; x++) {
423 getPixel(x, yl, col);
424 c = byteToDbl(col[0]);
425 m = byteToDbl(col[1]);
426 y = byteToDbl(col[2]);
427 k = byteToDbl(col[3]);
428 #if SPLASH_CMYK
429 if (separationList->getLength() > 0) {
430 for (int i = 0; i < separationList->getLength(); i++) {
431 if (col[i+4] > 0) {
432 GfxCMYK cmyk;
433 GfxColor input;
434 input.c[0] = byteToCol(col[i+4]);
435 GfxSeparationColorSpace *sepCS = (GfxSeparationColorSpace *)separationList->get(i);
436 sepCS->getCMYK(&input, &cmyk);
437 col[0] = colToByte(cmyk.c);
438 col[1] = colToByte(cmyk.m);
439 col[2] = colToByte(cmyk.y);
440 col[3] = colToByte(cmyk.k);
441 c += byteToDbl(col[0]);
442 m += byteToDbl(col[1]);
443 y += byteToDbl(col[2]);
444 k += byteToDbl(col[3]);
447 if (c > 1) c = 1;
448 if (m > 1) m = 1;
449 if (y > 1) y = 1;
450 if (k > 1) k = 1;
452 #endif
453 c1 = 1 - c;
454 m1 = 1 - m;
455 y1 = 1 - y;
456 k1 = 1 - k;
457 cmykToRGBMatrixMultiplication(c, m, y, k, c1, m1, y1, k1, r, g, b);
458 *line++ = dblToByte(clip01(r));
459 *line++ = dblToByte(clip01(g));
460 *line++ = dblToByte(clip01(b));
464 void SplashBitmap::getXBGRLine(int yl, SplashColorPtr line, ConversionMode conversionMode) {
465 SplashColor col;
466 double c, m, y, k, c1, m1, y1, k1, r, g, b;
468 for (int x = 0; x < width; x++) {
469 getPixel(x, yl, col);
470 c = byteToDbl(col[0]);
471 m = byteToDbl(col[1]);
472 y = byteToDbl(col[2]);
473 k = byteToDbl(col[3]);
474 #if SPLASH_CMYK
475 if (separationList->getLength() > 0) {
476 for (int i = 0; i < separationList->getLength(); i++) {
477 if (col[i+4] > 0) {
478 GfxCMYK cmyk;
479 GfxColor input;
480 input.c[0] = byteToCol(col[i+4]);
481 GfxSeparationColorSpace *sepCS = (GfxSeparationColorSpace *)separationList->get(i);
482 sepCS->getCMYK(&input, &cmyk);
483 col[0] = colToByte(cmyk.c);
484 col[1] = colToByte(cmyk.m);
485 col[2] = colToByte(cmyk.y);
486 col[3] = colToByte(cmyk.k);
487 c += byteToDbl(col[0]);
488 m += byteToDbl(col[1]);
489 y += byteToDbl(col[2]);
490 k += byteToDbl(col[3]);
493 if (c > 1) c = 1;
494 if (m > 1) m = 1;
495 if (y > 1) y = 1;
496 if (k > 1) k = 1;
498 #endif
499 c1 = 1 - c;
500 m1 = 1 - m;
501 y1 = 1 - y;
502 k1 = 1 - k;
503 cmykToRGBMatrixMultiplication(c, m, y, k, c1, m1, y1, k1, r, g, b);
505 if (conversionMode == conversionAlphaPremultiplied) {
506 const double a = getAlpha(x, yl) / 255.0;
508 *line++ = dblToByte(clip01(b * a));
509 *line++ = dblToByte(clip01(g * a));
510 *line++ = dblToByte(clip01(r * a));
511 } else {
512 *line++ = dblToByte(clip01(b));
513 *line++ = dblToByte(clip01(g));
514 *line++ = dblToByte(clip01(r));
517 if (conversionMode != conversionOpaque) {
518 *line++ = getAlpha(x, yl);
519 } else {
520 *line++ = 255;
525 static inline Guchar div255(int x) {
526 return (Guchar)((x + (x >> 8) + 0x80) >> 8);
529 GBool SplashBitmap::convertToXBGR(ConversionMode conversionMode) {
530 if (mode == splashModeXBGR8) {
531 if (conversionMode != conversionOpaque) {
532 // Copy the alpha channel into the fourth component so that XBGR becomes ABGR.
533 const SplashColorPtr dbegin = data;
534 const SplashColorPtr dend = data + rowSize * height;
536 Guchar *const abegin = alpha;
537 Guchar *const aend = alpha + width * height;
539 SplashColorPtr d = dbegin;
540 Guchar *a = abegin;
542 if (conversionMode == conversionAlphaPremultiplied) {
543 for (; d < dend && a < aend; d += 4, a += 1) {
544 d[0] = div255(d[0] * *a);
545 d[1] = div255(d[1] * *a);
546 d[2] = div255(d[2] * *a);
547 d[3] = *a;
549 } else {
550 for (d += 3; d < dend && a < aend; d += 4, a += 1) {
551 *d = *a;
556 return gTrue;
559 int newrowSize = width * 4;
560 SplashColorPtr newdata = (SplashColorPtr)gmallocn_checkoverflow(newrowSize, height);
561 if (newdata != NULL) {
562 for (int y = 0; y < height; y++) {
563 unsigned char *row = newdata + y * newrowSize;
564 getXBGRLine(y, row, conversionMode);
566 if (rowSize < 0) {
567 gfree(data + (height - 1) * rowSize);
568 } else {
569 gfree(data);
571 data = newdata;
572 rowSize = newrowSize;
573 mode = splashModeXBGR8;
575 return newdata != NULL;
578 #if SPLASH_CMYK
579 void SplashBitmap::getCMYKLine(int yl, SplashColorPtr line) {
580 SplashColor col;
582 for (int x = 0; x < width; x++) {
583 getPixel(x, yl, col);
584 if (separationList->getLength() > 0) {
585 double c, m, y, k;
586 c = byteToDbl(col[0]);
587 m = byteToDbl(col[1]);
588 y = byteToDbl(col[2]);
589 k = byteToDbl(col[3]);
590 for (int i = 0; i < separationList->getLength(); i++) {
591 if (col[i+4] > 0) {
592 GfxCMYK cmyk;
593 GfxColor input;
594 input.c[0] = byteToCol(col[i+4]);
595 GfxSeparationColorSpace *sepCS = (GfxSeparationColorSpace *)separationList->get(i);
596 sepCS->getCMYK(&input, &cmyk);
597 col[0] = colToByte(cmyk.c);
598 col[1] = colToByte(cmyk.m);
599 col[2] = colToByte(cmyk.y);
600 col[3] = colToByte(cmyk.k);
601 c += byteToDbl(col[0]);
602 m += byteToDbl(col[1]);
603 y += byteToDbl(col[2]);
604 k += byteToDbl(col[3]);
607 col[0] = dblToByte(clip01(c));
608 col[1] = dblToByte(clip01(m));
609 col[2] = dblToByte(clip01(y));
610 col[3] = dblToByte(clip01(k));
612 *line++ = col[0];
613 *line++ = col[1];
614 *line++ = col[2];
615 *line++ = col[3];
618 #endif
620 SplashError SplashBitmap::writeImgFile(ImgWriter *writer, FILE *f, int hDPI, int vDPI) {
621 if (mode != splashModeRGB8 && mode != splashModeMono8 && mode != splashModeMono1 && mode != splashModeXBGR8 && mode != splashModeBGR8
622 #if SPLASH_CMYK
623 && mode != splashModeCMYK8 && mode != splashModeDeviceN8
624 #endif
626 error(errInternal, -1, "unsupported SplashBitmap mode");
627 return splashErrGeneric;
630 if (!writer->init(f, width, height, hDPI, vDPI)) {
631 return splashErrGeneric;
634 switch (mode) {
635 #if SPLASH_CMYK
636 case splashModeCMYK8:
637 if (writer->supportCMYK()) {
638 SplashColorPtr row;
639 unsigned char **row_pointers = new unsigned char*[height];
640 row = data;
642 for (int y = 0; y < height; ++y) {
643 row_pointers[y] = row;
644 row += rowSize;
646 if (!writer->writePointers(row_pointers, height)) {
647 delete[] row_pointers;
648 return splashErrGeneric;
650 delete[] row_pointers;
651 } else {
652 unsigned char *row = new unsigned char[3 * width];
653 for (int y = 0; y < height; y++) {
654 getRGBLine(y, row);
655 if (!writer->writeRow(&row)) {
656 delete[] row;
657 return splashErrGeneric;
660 delete[] row;
662 break;
663 case splashModeDeviceN8:
664 if (writer->supportCMYK()) {
665 unsigned char *row = new unsigned char[4 * width];
666 for (int y = 0; y < height; y++) {
667 getCMYKLine(y, row);
668 if (!writer->writeRow(&row)) {
669 delete[] row;
670 return splashErrGeneric;
673 delete[] row;
674 } else {
675 unsigned char *row = new unsigned char[3 * width];
676 for (int y = 0; y < height; y++) {
677 getRGBLine(y, row);
678 if (!writer->writeRow(&row)) {
679 delete[] row;
680 return splashErrGeneric;
683 delete[] row;
685 break;
686 #endif
687 case splashModeRGB8:
689 SplashColorPtr row;
690 unsigned char **row_pointers = new unsigned char*[height];
691 row = data;
693 for (int y = 0; y < height; ++y) {
694 row_pointers[y] = row;
695 row += rowSize;
697 if (!writer->writePointers(row_pointers, height)) {
698 delete[] row_pointers;
699 return splashErrGeneric;
701 delete[] row_pointers;
703 break;
705 case splashModeBGR8:
707 unsigned char *row = new unsigned char[3 * width];
708 for (int y = 0; y < height; y++) {
709 // Convert into a PNG row
710 for (int x = 0; x < width; x++) {
711 row[3*x] = data[y * rowSize + x * 3 + 2];
712 row[3*x+1] = data[y * rowSize + x * 3 + 1];
713 row[3*x+2] = data[y * rowSize + x * 3];
716 if (!writer->writeRow(&row)) {
717 delete[] row;
718 return splashErrGeneric;
721 delete[] row;
723 break;
725 case splashModeXBGR8:
727 unsigned char *row = new unsigned char[3 * width];
728 for (int y = 0; y < height; y++) {
729 // Convert into a PNG row
730 for (int x = 0; x < width; x++) {
731 row[3*x] = data[y * rowSize + x * 4 + 2];
732 row[3*x+1] = data[y * rowSize + x * 4 + 1];
733 row[3*x+2] = data[y * rowSize + x * 4];
736 if (!writer->writeRow(&row)) {
737 delete[] row;
738 return splashErrGeneric;
741 delete[] row;
743 break;
745 case splashModeMono8:
747 unsigned char *row = new unsigned char[3 * width];
748 for (int y = 0; y < height; y++) {
749 // Convert into a PNG row
750 for (int x = 0; x < width; x++) {
751 row[3*x] = data[y * rowSize + x];
752 row[3*x+1] = data[y * rowSize + x];
753 row[3*x+2] = data[y * rowSize + x];
756 if (!writer->writeRow(&row)) {
757 delete[] row;
758 return splashErrGeneric;
761 delete[] row;
763 break;
765 case splashModeMono1:
767 unsigned char *row = new unsigned char[3 * width];
768 for (int y = 0; y < height; y++) {
769 // Convert into a PNG row
770 for (int x = 0; x < width; x++) {
771 getPixel(x, y, &row[3*x]);
772 row[3*x+1] = row[3*x];
773 row[3*x+2] = row[3*x];
776 if (!writer->writeRow(&row)) {
777 delete[] row;
778 return splashErrGeneric;
781 delete[] row;
783 break;
785 default:
786 // can't happen
787 break;
790 if (!writer->close()) {
791 return splashErrGeneric;
794 return splashOk;