1 //========================================================================
5 //========================================================================
7 //========================================================================
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 //========================================================================
33 #ifdef USE_GCC_PRAGMAS
34 #pragma implementation
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 //------------------------------------------------------------------------
53 //------------------------------------------------------------------------
55 SplashBitmap::SplashBitmap(int widthA
, int heightA
, int rowPadA
,
56 SplashColorMode modeA
, GBool alphaA
,
57 GBool topDown
, GooList
*separationListA
) {
65 rowSize
= (width
+ 7) >> 3;
79 if (width
> 0 && width
<= INT_MAX
/ 3) {
86 if (width
> 0 && width
<= INT_MAX
/ 4) {
94 if (width
> 0 && width
<= INT_MAX
/ 4) {
100 case splashModeDeviceN8
:
101 if (width
> 0 && width
<= INT_MAX
/ 4) {
102 rowSize
= width
* (SPOT_NCOMPS
+ 4);
110 rowSize
+= rowPad
- 1;
111 rowSize
-= rowSize
% rowPad
;
113 data
= (SplashColorPtr
)gmallocn_checkoverflow(rowSize
, height
);
116 data
+= (height
- 1) * rowSize
;
120 alpha
= (Guchar
*)gmallocn(width
, height
);
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();
140 dataSource
= dataSource
+ (src
->getHeight() - 1) * amount
;
141 dataDest
= dataDest
+ (src
->getHeight() - 1) * amount
;
142 amount
*= -src
->getHeight();
144 amount
*= src
->getHeight();
146 memcpy(dataDest
, dataSource
, amount
);
147 if (src
->getAlphaPtr() != NULL
) {
148 memcpy(result
->getAlphaPtr(), src
->getAlphaPtr(), src
->getWidth() * src
->getHeight());
153 SplashBitmap::~SplashBitmap() {
156 gfree(data
+ (height
- 1) * rowSize
);
162 deleteGooList(separationList
, GfxSeparationColorSpace
);
166 SplashError
SplashBitmap::writePNMFile(char *fileName
) {
170 if (!(f
= fopen(fileName
, "wb"))) {
171 return splashErrOpenFile
;
174 e
= this->writePNMFile(f
);
181 SplashError
SplashBitmap::writePNMFile(FILE *f
) {
182 SplashColorPtr row
, p
;
187 case splashModeMono1
:
188 fprintf(f
, "P4\n%d %d\n", width
, height
);
190 for (y
= 0; y
< height
; ++y
) {
192 for (x
= 0; x
< width
; x
+= 8) {
200 case splashModeMono8
:
201 fprintf(f
, "P5\n%d %d\n255\n", width
, height
);
203 for (y
= 0; y
< height
; ++y
) {
204 fwrite(row
, 1, width
, f
);
210 fprintf(f
, "P6\n%d %d\n255\n", width
, height
);
212 for (y
= 0; y
< height
; ++y
) {
213 fwrite(row
, 1, 3 * width
, f
);
218 case splashModeXBGR8
:
219 fprintf(f
, "P6\n%d %d\n255\n", width
, height
);
221 for (y
= 0; y
< height
; ++y
) {
223 for (x
= 0; x
< width
; ++x
) {
224 fputc(splashBGR8R(p
), f
);
225 fputc(splashBGR8G(p
), f
);
226 fputc(splashBGR8B(p
), f
);
235 fprintf(f
, "P6\n%d %d\n255\n", width
, height
);
237 for (y
= 0; y
< height
; ++y
) {
239 for (x
= 0; x
< width
; ++x
) {
240 fputc(splashBGR8R(p
), f
);
241 fputc(splashBGR8G(p
), f
);
242 fputc(splashBGR8B(p
), f
);
250 case splashModeCMYK8
:
251 case splashModeDeviceN8
:
252 // PNM doesn't support CMYK
253 error(errInternal
, -1, "unsupported SplashBitmap mode");
254 return splashErrGeneric
;
261 SplashError
SplashBitmap::writeAlphaPGMFile(char *fileName
) {
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
);
276 void SplashBitmap::getPixel(int x
, int y
, SplashColorPtr pixel
) {
279 if (y
< 0 || y
>= height
|| x
< 0 || x
>= width
|| !data
) {
283 case splashModeMono1
:
284 p
= &data
[y
* rowSize
+ (x
>> 3)];
285 pixel
[0] = (p
[0] & (0x80 >> (x
& 7))) ? 0xff : 0x00;
287 case splashModeMono8
:
288 p
= &data
[y
* rowSize
+ x
];
292 p
= &data
[y
* rowSize
+ 3 * x
];
297 case splashModeXBGR8
:
298 p
= &data
[y
* rowSize
+ 4 * x
];
305 p
= &data
[y
* rowSize
+ 3 * x
];
311 case splashModeCMYK8
:
312 p
= &data
[y
* rowSize
+ 4 * x
];
318 case splashModeDeviceN8
:
319 p
= &data
[y
* rowSize
+ (SPOT_NCOMPS
+ 4) * x
];
320 for (int cp
= 0; cp
< SPOT_NCOMPS
+ 4; cp
++)
327 Guchar
SplashBitmap::getAlpha(int x
, int y
) {
328 return alpha
[y
* width
+ x
];
331 SplashColorPtr
SplashBitmap::takeData() {
332 SplashColorPtr data2
;
339 SplashError
SplashBitmap::writeImgFile(SplashImageFileFormat format
, char *fileName
, int hDPI
, int vDPI
, const char *compressionString
) {
343 if (!(f
= fopen(fileName
, "wb"))) {
344 return splashErrOpenFile
;
347 e
= writeImgFile(format
, f
, hDPI
, vDPI
, compressionString
);
353 SplashError
SplashBitmap::writeImgFile(SplashImageFileFormat format
, FILE *f
, int hDPI
, int vDPI
, const char *compressionString
) {
359 case splashFormatPng
:
360 writer
= new PNGWriter();
364 #ifdef ENABLE_LIBJPEG
366 case splashFormatJpegCMYK
:
367 writer
= new JpegWriter(JpegWriter::CMYK
);
370 case splashFormatJpeg
:
371 writer
= new JpegWriter();
375 #ifdef ENABLE_LIBTIFF
376 case splashFormatTiff
:
378 case splashModeMono1
:
379 writer
= new TiffWriter(TiffWriter::MONOCHROME
);
381 case splashModeMono8
:
382 writer
= new TiffWriter(TiffWriter::GRAY
);
386 writer
= new TiffWriter(TiffWriter::RGB
);
389 case splashModeCMYK8
:
390 case splashModeDeviceN8
:
391 writer
= new TiffWriter(TiffWriter::CMYK
);
395 fprintf(stderr
, "TiffWriter: Mode %d not supported\n", mode
);
396 writer
= new TiffWriter();
399 ((TiffWriter
*)writer
)->setCompressionString(compressionString
);
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
);
416 #include "poppler/GfxState_helpers.h"
418 void SplashBitmap::getRGBLine(int yl
, SplashColorPtr line
) {
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]);
429 if (separationList
->getLength() > 0) {
430 for (int i
= 0; i
< separationList
->getLength(); i
++) {
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]);
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
) {
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]);
475 if (separationList
->getLength() > 0) {
476 for (int i
= 0; i
< separationList
->getLength(); i
++) {
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]);
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
));
512 *line
++ = dblToByte(clip01(b
));
513 *line
++ = dblToByte(clip01(g
));
514 *line
++ = dblToByte(clip01(r
));
517 if (conversionMode
!= conversionOpaque
) {
518 *line
++ = getAlpha(x
, yl
);
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
;
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
);
550 for (d
+= 3; d
< dend
&& a
< aend
; d
+= 4, a
+= 1) {
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
);
567 gfree(data
+ (height
- 1) * rowSize
);
572 rowSize
= newrowSize
;
573 mode
= splashModeXBGR8
;
575 return newdata
!= NULL
;
579 void SplashBitmap::getCMYKLine(int yl
, SplashColorPtr line
) {
582 for (int x
= 0; x
< width
; x
++) {
583 getPixel(x
, yl
, col
);
584 if (separationList
->getLength() > 0) {
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
++) {
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
));
620 SplashError
SplashBitmap::writeImgFile(ImgWriter
*writer
, FILE *f
, int hDPI
, int vDPI
) {
621 if (mode
!= splashModeRGB8
&& mode
!= splashModeMono8
&& mode
!= splashModeMono1
&& mode
!= splashModeXBGR8
&& mode
!= splashModeBGR8
623 && mode
!= splashModeCMYK8
&& mode
!= splashModeDeviceN8
626 error(errInternal
, -1, "unsupported SplashBitmap mode");
627 return splashErrGeneric
;
630 if (!writer
->init(f
, width
, height
, hDPI
, vDPI
)) {
631 return splashErrGeneric
;
636 case splashModeCMYK8
:
637 if (writer
->supportCMYK()) {
639 unsigned char **row_pointers
= new unsigned char*[height
];
642 for (int y
= 0; y
< height
; ++y
) {
643 row_pointers
[y
] = row
;
646 if (!writer
->writePointers(row_pointers
, height
)) {
647 delete[] row_pointers
;
648 return splashErrGeneric
;
650 delete[] row_pointers
;
652 unsigned char *row
= new unsigned char[3 * width
];
653 for (int y
= 0; y
< height
; y
++) {
655 if (!writer
->writeRow(&row
)) {
657 return splashErrGeneric
;
663 case splashModeDeviceN8
:
664 if (writer
->supportCMYK()) {
665 unsigned char *row
= new unsigned char[4 * width
];
666 for (int y
= 0; y
< height
; y
++) {
668 if (!writer
->writeRow(&row
)) {
670 return splashErrGeneric
;
675 unsigned char *row
= new unsigned char[3 * width
];
676 for (int y
= 0; y
< height
; y
++) {
678 if (!writer
->writeRow(&row
)) {
680 return splashErrGeneric
;
690 unsigned char **row_pointers
= new unsigned char*[height
];
693 for (int y
= 0; y
< height
; ++y
) {
694 row_pointers
[y
] = row
;
697 if (!writer
->writePointers(row_pointers
, height
)) {
698 delete[] row_pointers
;
699 return splashErrGeneric
;
701 delete[] row_pointers
;
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
)) {
718 return splashErrGeneric
;
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
)) {
738 return splashErrGeneric
;
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
)) {
758 return splashErrGeneric
;
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
)) {
778 return splashErrGeneric
;
790 if (!writer
->close()) {
791 return splashErrGeneric
;