mirror of
https://github.com/RetroDECK/ES-DE.git
synced 2025-04-10 19:15:13 +00:00
855 lines
25 KiB
Plaintext
855 lines
25 KiB
Plaintext
//========================================================================
|
|
//
|
|
// SplashBitmap.cc
|
|
//
|
|
//========================================================================
|
|
|
|
//========================================================================
|
|
//
|
|
// Modified under the Poppler project - http://poppler.freedesktop.org
|
|
//
|
|
// All changes made under the Poppler project to this file are licensed
|
|
// under GPL version 2 or later
|
|
//
|
|
// Copyright (C) 2006, 2009, 2010, 2012, 2015, 2018, 2019, 2021, 2022 Albert Astals Cid <aacid@kde.org>
|
|
// Copyright (C) 2007 Ilmari Heikkinen <ilmari.heikkinen@gmail.com>
|
|
// Copyright (C) 2009 Shen Liang <shenzhuxi@gmail.com>
|
|
// Copyright (C) 2009 Stefan Thomas <thomas@eload24.com>
|
|
// Copyright (C) 2010, 2012, 2017 Adrian Johnson <ajohnson@redneon.com>
|
|
// Copyright (C) 2010 Harry Roberts <harry.roberts@midnight-labs.org>
|
|
// Copyright (C) 2010 Christian Feuersänger <cfeuersaenger@googlemail.com>
|
|
// Copyright (C) 2010, 2015, 2019 William Bader <williambader@hotmail.com>
|
|
// Copyright (C) 2011-2013 Thomas Freitag <Thomas.Freitag@alfa.de>
|
|
// Copyright (C) 2012 Anthony Wesley <awesley@smartnetworks.com.au>
|
|
// Copyright (C) 2015, 2018 Adam Reichold <adamreichold@myopera.com>
|
|
// Copyright (C) 2016 Kenji Uno <ku@digitaldolphins.jp>
|
|
// Copyright (C) 2018 Martin Packman <gzlist@googlemail.com>
|
|
// Copyright (C) 2019 Christian Persch <chpe@src.gnome.org>
|
|
// Copyright (C) 2019 Oliver Sander <oliver.sander@tu-dresden.de>
|
|
//
|
|
// To see a description of the changes please see the Changelog file that
|
|
// came with your tarball or type make ChangeLog if you are building from git
|
|
//
|
|
//========================================================================
|
|
|
|
#include <config.h>
|
|
|
|
#include <cstdio>
|
|
#include <cstring>
|
|
#include <cstdlib>
|
|
#include <climits>
|
|
#include "goo/gfile.h"
|
|
#include "goo/gmem.h"
|
|
#include "SplashErrorCodes.h"
|
|
#include "SplashBitmap.h"
|
|
#include "poppler/Error.h"
|
|
#include "poppler/GfxState.h"
|
|
#include "goo/JpegWriter.h"
|
|
#include "goo/PNGWriter.h"
|
|
#include "goo/TiffWriter.h"
|
|
#include "goo/ImgWriter.h"
|
|
|
|
//------------------------------------------------------------------------
|
|
// SplashBitmap
|
|
//------------------------------------------------------------------------
|
|
|
|
SplashBitmap::SplashBitmap(int widthA, int heightA, int rowPadA, SplashColorMode modeA, bool alphaA, bool topDown, const std::vector<GfxSeparationColorSpace *> *separationListA)
|
|
{
|
|
width = widthA;
|
|
height = heightA;
|
|
mode = modeA;
|
|
rowPad = rowPadA;
|
|
switch (mode) {
|
|
case splashModeMono1:
|
|
if (width > 0) {
|
|
rowSize = (width + 7) >> 3;
|
|
} else {
|
|
rowSize = -1;
|
|
}
|
|
break;
|
|
case splashModeMono8:
|
|
if (width > 0) {
|
|
rowSize = width;
|
|
} else {
|
|
rowSize = -1;
|
|
}
|
|
break;
|
|
case splashModeRGB8:
|
|
case splashModeBGR8:
|
|
if (width > 0 && width <= INT_MAX / 3) {
|
|
rowSize = width * 3;
|
|
} else {
|
|
rowSize = -1;
|
|
}
|
|
break;
|
|
case splashModeXBGR8:
|
|
if (width > 0 && width <= INT_MAX / 4) {
|
|
rowSize = width * 4;
|
|
} else {
|
|
rowSize = -1;
|
|
}
|
|
break;
|
|
case splashModeCMYK8:
|
|
if (width > 0 && width <= INT_MAX / 4) {
|
|
rowSize = width * 4;
|
|
} else {
|
|
rowSize = -1;
|
|
}
|
|
break;
|
|
case splashModeDeviceN8:
|
|
if (width > 0 && width <= static_cast<int>(INT_MAX / splashMaxColorComps)) {
|
|
rowSize = width * splashMaxColorComps;
|
|
} else {
|
|
rowSize = -1;
|
|
}
|
|
break;
|
|
}
|
|
if (rowSize > 0) {
|
|
rowSize += rowPad - 1;
|
|
rowSize -= rowSize % rowPad;
|
|
}
|
|
data = (SplashColorPtr)gmallocn_checkoverflow(rowSize, height);
|
|
if (data != nullptr) {
|
|
if (!topDown) {
|
|
data += (height - 1) * rowSize;
|
|
rowSize = -rowSize;
|
|
}
|
|
if (alphaA) {
|
|
alpha = (unsigned char *)gmallocn_checkoverflow(width, height);
|
|
} else {
|
|
alpha = nullptr;
|
|
}
|
|
} else {
|
|
alpha = nullptr;
|
|
}
|
|
separationList = new std::vector<GfxSeparationColorSpace *>();
|
|
if (separationListA != nullptr) {
|
|
for (const GfxSeparationColorSpace *separation : *separationListA) {
|
|
separationList->push_back((GfxSeparationColorSpace *)separation->copy());
|
|
}
|
|
}
|
|
}
|
|
|
|
SplashBitmap *SplashBitmap::copy(const SplashBitmap *src)
|
|
{
|
|
SplashBitmap *result = new SplashBitmap(src->getWidth(), src->getHeight(), src->getRowPad(), src->getMode(), src->getAlphaPtr() != nullptr, src->getRowSize() >= 0, src->getSeparationList());
|
|
SplashColorConstPtr dataSource = src->getDataPtr();
|
|
unsigned char *dataDest = result->getDataPtr();
|
|
int amount = src->getRowSize();
|
|
if (amount < 0) {
|
|
dataSource = dataSource + (src->getHeight() - 1) * amount;
|
|
dataDest = dataDest + (src->getHeight() - 1) * amount;
|
|
amount *= -src->getHeight();
|
|
} else {
|
|
amount *= src->getHeight();
|
|
}
|
|
memcpy(dataDest, dataSource, amount);
|
|
if (src->getAlphaPtr() != nullptr) {
|
|
memcpy(result->getAlphaPtr(), src->getAlphaPtr(), src->getWidth() * src->getHeight());
|
|
}
|
|
return result;
|
|
}
|
|
|
|
SplashBitmap::~SplashBitmap()
|
|
{
|
|
if (data) {
|
|
if (rowSize < 0) {
|
|
gfree(data + (height - 1) * rowSize);
|
|
} else {
|
|
gfree(data);
|
|
}
|
|
}
|
|
gfree(alpha);
|
|
for (auto entry : *separationList) {
|
|
delete entry;
|
|
}
|
|
delete separationList;
|
|
}
|
|
|
|
SplashError SplashBitmap::writePNMFile(char *fileName)
|
|
{
|
|
FILE *f;
|
|
SplashError e;
|
|
|
|
if (!(f = openFile(fileName, "wb"))) {
|
|
return splashErrOpenFile;
|
|
}
|
|
|
|
e = this->writePNMFile(f);
|
|
|
|
fclose(f);
|
|
return e;
|
|
}
|
|
|
|
SplashError SplashBitmap::writePNMFile(FILE *f)
|
|
{
|
|
SplashColorPtr row, p;
|
|
int x, y;
|
|
|
|
switch (mode) {
|
|
|
|
case splashModeMono1:
|
|
fprintf(f, "P4\n%d %d\n", width, height);
|
|
row = data;
|
|
for (y = 0; y < height; ++y) {
|
|
p = row;
|
|
for (x = 0; x < width; x += 8) {
|
|
fputc(*p ^ 0xff, f);
|
|
++p;
|
|
}
|
|
row += rowSize;
|
|
}
|
|
break;
|
|
|
|
case splashModeMono8:
|
|
fprintf(f, "P5\n%d %d\n255\n", width, height);
|
|
row = data;
|
|
for (y = 0; y < height; ++y) {
|
|
fwrite(row, 1, width, f);
|
|
row += rowSize;
|
|
}
|
|
break;
|
|
|
|
case splashModeRGB8:
|
|
fprintf(f, "P6\n%d %d\n255\n", width, height);
|
|
row = data;
|
|
for (y = 0; y < height; ++y) {
|
|
fwrite(row, 1, 3 * width, f);
|
|
row += rowSize;
|
|
}
|
|
break;
|
|
|
|
case splashModeXBGR8:
|
|
fprintf(f, "P6\n%d %d\n255\n", width, height);
|
|
row = data;
|
|
for (y = 0; y < height; ++y) {
|
|
p = row;
|
|
for (x = 0; x < width; ++x) {
|
|
fputc(splashBGR8R(p), f);
|
|
fputc(splashBGR8G(p), f);
|
|
fputc(splashBGR8B(p), f);
|
|
p += 4;
|
|
}
|
|
row += rowSize;
|
|
}
|
|
break;
|
|
|
|
case splashModeBGR8:
|
|
fprintf(f, "P6\n%d %d\n255\n", width, height);
|
|
row = data;
|
|
for (y = 0; y < height; ++y) {
|
|
p = row;
|
|
for (x = 0; x < width; ++x) {
|
|
fputc(splashBGR8R(p), f);
|
|
fputc(splashBGR8G(p), f);
|
|
fputc(splashBGR8B(p), f);
|
|
p += 3;
|
|
}
|
|
row += rowSize;
|
|
}
|
|
break;
|
|
|
|
case splashModeCMYK8:
|
|
case splashModeDeviceN8:
|
|
// PNM doesn't support CMYK
|
|
error(errInternal, -1, "unsupported SplashBitmap mode");
|
|
return splashErrGeneric;
|
|
break;
|
|
}
|
|
return splashOk;
|
|
}
|
|
|
|
SplashError SplashBitmap::writeAlphaPGMFile(char *fileName)
|
|
{
|
|
FILE *f;
|
|
|
|
if (!alpha) {
|
|
return splashErrModeMismatch;
|
|
}
|
|
if (!(f = openFile(fileName, "wb"))) {
|
|
return splashErrOpenFile;
|
|
}
|
|
fprintf(f, "P5\n%d %d\n255\n", width, height);
|
|
fwrite(alpha, 1, width * height, f);
|
|
fclose(f);
|
|
return splashOk;
|
|
}
|
|
|
|
void SplashBitmap::getPixel(int x, int y, SplashColorPtr pixel)
|
|
{
|
|
SplashColorPtr p;
|
|
|
|
if (y < 0 || y >= height || x < 0 || x >= width || !data) {
|
|
return;
|
|
}
|
|
switch (mode) {
|
|
case splashModeMono1:
|
|
p = &data[y * rowSize + (x >> 3)];
|
|
pixel[0] = (p[0] & (0x80 >> (x & 7))) ? 0xff : 0x00;
|
|
break;
|
|
case splashModeMono8:
|
|
p = &data[y * rowSize + x];
|
|
pixel[0] = p[0];
|
|
break;
|
|
case splashModeRGB8:
|
|
p = &data[y * rowSize + 3 * x];
|
|
pixel[0] = p[0];
|
|
pixel[1] = p[1];
|
|
pixel[2] = p[2];
|
|
break;
|
|
case splashModeXBGR8:
|
|
p = &data[y * rowSize + 4 * x];
|
|
pixel[0] = p[2];
|
|
pixel[1] = p[1];
|
|
pixel[2] = p[0];
|
|
pixel[3] = p[3];
|
|
break;
|
|
case splashModeBGR8:
|
|
p = &data[y * rowSize + 3 * x];
|
|
pixel[0] = p[2];
|
|
pixel[1] = p[1];
|
|
pixel[2] = p[0];
|
|
break;
|
|
case splashModeCMYK8:
|
|
p = &data[y * rowSize + 4 * x];
|
|
pixel[0] = p[0];
|
|
pixel[1] = p[1];
|
|
pixel[2] = p[2];
|
|
pixel[3] = p[3];
|
|
break;
|
|
case splashModeDeviceN8:
|
|
p = &data[y * rowSize + (SPOT_NCOMPS + 4) * x];
|
|
for (int cp = 0; cp < SPOT_NCOMPS + 4; cp++) {
|
|
pixel[cp] = p[cp];
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
unsigned char SplashBitmap::getAlpha(int x, int y)
|
|
{
|
|
return alpha[y * width + x];
|
|
}
|
|
|
|
SplashColorPtr SplashBitmap::takeData()
|
|
{
|
|
SplashColorPtr data2;
|
|
|
|
data2 = data;
|
|
data = nullptr;
|
|
return data2;
|
|
}
|
|
|
|
SplashError SplashBitmap::writeImgFile(SplashImageFileFormat format, const char *fileName, double hDPI, double vDPI, WriteImgParams *params)
|
|
{
|
|
FILE *f;
|
|
SplashError e;
|
|
|
|
if (!(f = openFile(fileName, "wb"))) {
|
|
return splashErrOpenFile;
|
|
}
|
|
|
|
e = writeImgFile(format, f, hDPI, vDPI, params);
|
|
|
|
fclose(f);
|
|
return e;
|
|
}
|
|
|
|
void SplashBitmap::setJpegParams(ImgWriter *writer, WriteImgParams *params)
|
|
{
|
|
#ifdef ENABLE_LIBJPEG
|
|
if (params) {
|
|
static_cast<JpegWriter *>(writer)->setProgressive(params->jpegProgressive);
|
|
static_cast<JpegWriter *>(writer)->setOptimize(params->jpegOptimize);
|
|
if (params->jpegQuality >= 0) {
|
|
static_cast<JpegWriter *>(writer)->setQuality(params->jpegQuality);
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
SplashError SplashBitmap::writeImgFile(SplashImageFileFormat format, FILE *f, double hDPI, double vDPI, WriteImgParams *params)
|
|
{
|
|
ImgWriter *writer;
|
|
SplashError e;
|
|
|
|
SplashColorMode imageWriterFormat = splashModeRGB8;
|
|
|
|
switch (format) {
|
|
#ifdef ENABLE_LIBPNG
|
|
case splashFormatPng:
|
|
writer = new PNGWriter();
|
|
break;
|
|
#endif
|
|
|
|
#ifdef ENABLE_LIBJPEG
|
|
case splashFormatJpegCMYK:
|
|
writer = new JpegWriter(JpegWriter::CMYK);
|
|
setJpegParams(writer, params);
|
|
break;
|
|
case splashFormatJpeg:
|
|
writer = new JpegWriter();
|
|
setJpegParams(writer, params);
|
|
break;
|
|
#endif
|
|
|
|
#ifdef ENABLE_LIBTIFF
|
|
case splashFormatTiff:
|
|
switch (mode) {
|
|
case splashModeMono1:
|
|
writer = new TiffWriter(TiffWriter::MONOCHROME);
|
|
imageWriterFormat = splashModeMono1;
|
|
break;
|
|
case splashModeMono8:
|
|
writer = new TiffWriter(TiffWriter::GRAY);
|
|
imageWriterFormat = splashModeMono8;
|
|
break;
|
|
case splashModeRGB8:
|
|
case splashModeBGR8:
|
|
writer = new TiffWriter(TiffWriter::RGB);
|
|
break;
|
|
case splashModeCMYK8:
|
|
case splashModeDeviceN8:
|
|
writer = new TiffWriter(TiffWriter::CMYK);
|
|
break;
|
|
default:
|
|
fprintf(stderr, "TiffWriter: Mode %d not supported\n", mode);
|
|
writer = new TiffWriter();
|
|
}
|
|
if (writer && params) {
|
|
((TiffWriter *)writer)->setCompressionString(params->tiffCompression.c_str());
|
|
}
|
|
break;
|
|
#endif
|
|
|
|
default:
|
|
// Not the greatest error message, but users of this function should
|
|
// have already checked whether their desired format is compiled in.
|
|
error(errInternal, -1, "Support for this image type not compiled in");
|
|
return splashErrGeneric;
|
|
}
|
|
|
|
e = writeImgFile(writer, f, hDPI, vDPI, imageWriterFormat);
|
|
delete writer;
|
|
return e;
|
|
}
|
|
|
|
#include "poppler/GfxState_helpers.h"
|
|
|
|
void SplashBitmap::getRGBLine(int yl, SplashColorPtr line)
|
|
{
|
|
SplashColor col;
|
|
double c, m, y, k, c1, m1, y1, k1, r, g, b;
|
|
|
|
for (int x = 0; x < width; x++) {
|
|
getPixel(x, yl, col);
|
|
c = byteToDbl(col[0]);
|
|
m = byteToDbl(col[1]);
|
|
y = byteToDbl(col[2]);
|
|
k = byteToDbl(col[3]);
|
|
if (separationList->size() > 0) {
|
|
for (std::size_t i = 0; i < separationList->size(); i++) {
|
|
if (col[i + 4] > 0) {
|
|
GfxCMYK cmyk;
|
|
GfxColor input;
|
|
input.c[0] = byteToCol(col[i + 4]);
|
|
GfxSeparationColorSpace *sepCS = (GfxSeparationColorSpace *)((*separationList)[i]);
|
|
sepCS->getCMYK(&input, &cmyk);
|
|
col[0] = colToByte(cmyk.c);
|
|
col[1] = colToByte(cmyk.m);
|
|
col[2] = colToByte(cmyk.y);
|
|
col[3] = colToByte(cmyk.k);
|
|
c += byteToDbl(col[0]);
|
|
m += byteToDbl(col[1]);
|
|
y += byteToDbl(col[2]);
|
|
k += byteToDbl(col[3]);
|
|
}
|
|
}
|
|
if (c > 1) {
|
|
c = 1;
|
|
}
|
|
if (m > 1) {
|
|
m = 1;
|
|
}
|
|
if (y > 1) {
|
|
y = 1;
|
|
}
|
|
if (k > 1) {
|
|
k = 1;
|
|
}
|
|
}
|
|
c1 = 1 - c;
|
|
m1 = 1 - m;
|
|
y1 = 1 - y;
|
|
k1 = 1 - k;
|
|
cmykToRGBMatrixMultiplication(c, m, y, k, c1, m1, y1, k1, r, g, b);
|
|
*line++ = dblToByte(clip01(r));
|
|
*line++ = dblToByte(clip01(g));
|
|
*line++ = dblToByte(clip01(b));
|
|
}
|
|
}
|
|
|
|
void SplashBitmap::getXBGRLine(int yl, SplashColorPtr line, ConversionMode conversionMode)
|
|
{
|
|
SplashColor col;
|
|
double c, m, y, k, c1, m1, y1, k1, r, g, b;
|
|
|
|
for (int x = 0; x < width; x++) {
|
|
getPixel(x, yl, col);
|
|
c = byteToDbl(col[0]);
|
|
m = byteToDbl(col[1]);
|
|
y = byteToDbl(col[2]);
|
|
k = byteToDbl(col[3]);
|
|
if (separationList->size() > 0) {
|
|
for (std::size_t i = 0; i < separationList->size(); i++) {
|
|
if (col[i + 4] > 0) {
|
|
GfxCMYK cmyk;
|
|
GfxColor input;
|
|
input.c[0] = byteToCol(col[i + 4]);
|
|
GfxSeparationColorSpace *sepCS = (GfxSeparationColorSpace *)((*separationList)[i]);
|
|
sepCS->getCMYK(&input, &cmyk);
|
|
col[0] = colToByte(cmyk.c);
|
|
col[1] = colToByte(cmyk.m);
|
|
col[2] = colToByte(cmyk.y);
|
|
col[3] = colToByte(cmyk.k);
|
|
c += byteToDbl(col[0]);
|
|
m += byteToDbl(col[1]);
|
|
y += byteToDbl(col[2]);
|
|
k += byteToDbl(col[3]);
|
|
}
|
|
}
|
|
if (c > 1) {
|
|
c = 1;
|
|
}
|
|
if (m > 1) {
|
|
m = 1;
|
|
}
|
|
if (y > 1) {
|
|
y = 1;
|
|
}
|
|
if (k > 1) {
|
|
k = 1;
|
|
}
|
|
}
|
|
c1 = 1 - c;
|
|
m1 = 1 - m;
|
|
y1 = 1 - y;
|
|
k1 = 1 - k;
|
|
cmykToRGBMatrixMultiplication(c, m, y, k, c1, m1, y1, k1, r, g, b);
|
|
|
|
if (conversionMode == conversionAlphaPremultiplied) {
|
|
const double a = getAlpha(x, yl) / 255.0;
|
|
|
|
*line++ = dblToByte(clip01(b * a));
|
|
*line++ = dblToByte(clip01(g * a));
|
|
*line++ = dblToByte(clip01(r * a));
|
|
} else {
|
|
*line++ = dblToByte(clip01(b));
|
|
*line++ = dblToByte(clip01(g));
|
|
*line++ = dblToByte(clip01(r));
|
|
}
|
|
|
|
if (conversionMode != conversionOpaque) {
|
|
*line++ = getAlpha(x, yl);
|
|
} else {
|
|
*line++ = 255;
|
|
}
|
|
}
|
|
}
|
|
|
|
static inline unsigned char div255(int x)
|
|
{
|
|
return (unsigned char)((x + (x >> 8) + 0x80) >> 8);
|
|
}
|
|
|
|
bool SplashBitmap::convertToXBGR(ConversionMode conversionMode)
|
|
{
|
|
if (mode == splashModeXBGR8) {
|
|
if (conversionMode != conversionOpaque) {
|
|
// Copy the alpha channel into the fourth component so that XBGR becomes ABGR.
|
|
const SplashColorPtr dbegin = data;
|
|
const SplashColorPtr dend = data + rowSize * height;
|
|
|
|
unsigned char *const abegin = alpha;
|
|
unsigned char *const aend = alpha + width * height;
|
|
|
|
SplashColorPtr d = dbegin;
|
|
unsigned char *a = abegin;
|
|
|
|
if (conversionMode == conversionAlphaPremultiplied) {
|
|
for (; d < dend && a < aend; d += 4, a += 1) {
|
|
d[0] = div255(d[0] * *a);
|
|
d[1] = div255(d[1] * *a);
|
|
d[2] = div255(d[2] * *a);
|
|
d[3] = *a;
|
|
}
|
|
} else {
|
|
for (d += 3; d < dend && a < aend; d += 4, a += 1) {
|
|
*d = *a;
|
|
}
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
int newrowSize = width * 4;
|
|
SplashColorPtr newdata = (SplashColorPtr)gmallocn_checkoverflow(newrowSize, height);
|
|
if (newdata != nullptr) {
|
|
for (int y = 0; y < height; y++) {
|
|
unsigned char *row = newdata + y * newrowSize;
|
|
getXBGRLine(y, row, conversionMode);
|
|
}
|
|
if (rowSize < 0) {
|
|
gfree(data + (height - 1) * rowSize);
|
|
} else {
|
|
gfree(data);
|
|
}
|
|
data = newdata;
|
|
rowSize = newrowSize;
|
|
mode = splashModeXBGR8;
|
|
}
|
|
return newdata != nullptr;
|
|
}
|
|
|
|
void SplashBitmap::getCMYKLine(int yl, SplashColorPtr line)
|
|
{
|
|
SplashColor col;
|
|
|
|
for (int x = 0; x < width; x++) {
|
|
getPixel(x, yl, col);
|
|
if (separationList->size() > 0) {
|
|
double c, m, y, k;
|
|
c = byteToDbl(col[0]);
|
|
m = byteToDbl(col[1]);
|
|
y = byteToDbl(col[2]);
|
|
k = byteToDbl(col[3]);
|
|
for (std::size_t i = 0; i < separationList->size(); i++) {
|
|
if (col[i + 4] > 0) {
|
|
GfxCMYK cmyk;
|
|
GfxColor input;
|
|
input.c[0] = byteToCol(col[i + 4]);
|
|
GfxSeparationColorSpace *sepCS = (GfxSeparationColorSpace *)((*separationList)[i]);
|
|
sepCS->getCMYK(&input, &cmyk);
|
|
col[0] = colToByte(cmyk.c);
|
|
col[1] = colToByte(cmyk.m);
|
|
col[2] = colToByte(cmyk.y);
|
|
col[3] = colToByte(cmyk.k);
|
|
c += byteToDbl(col[0]);
|
|
m += byteToDbl(col[1]);
|
|
y += byteToDbl(col[2]);
|
|
k += byteToDbl(col[3]);
|
|
}
|
|
}
|
|
col[0] = dblToByte(clip01(c));
|
|
col[1] = dblToByte(clip01(m));
|
|
col[2] = dblToByte(clip01(y));
|
|
col[3] = dblToByte(clip01(k));
|
|
}
|
|
*line++ = col[0];
|
|
*line++ = col[1];
|
|
*line++ = col[2];
|
|
*line++ = col[3];
|
|
}
|
|
}
|
|
|
|
SplashError SplashBitmap::writeImgFile(ImgWriter *writer, FILE *f, double hDPI, double vDPI, SplashColorMode imageWriterFormat)
|
|
{
|
|
if (mode != splashModeRGB8 && mode != splashModeMono8 && mode != splashModeMono1 && mode != splashModeXBGR8 && mode != splashModeBGR8 && mode != splashModeCMYK8 && mode != splashModeDeviceN8) {
|
|
error(errInternal, -1, "unsupported SplashBitmap mode");
|
|
return splashErrGeneric;
|
|
}
|
|
|
|
if (!writer->init(f, width, height, hDPI, vDPI)) {
|
|
return splashErrGeneric;
|
|
}
|
|
|
|
switch (mode) {
|
|
case splashModeCMYK8:
|
|
if (writer->supportCMYK()) {
|
|
SplashColorPtr row;
|
|
unsigned char **row_pointers = new unsigned char *[height];
|
|
row = data;
|
|
|
|
for (int y = 0; y < height; ++y) {
|
|
row_pointers[y] = row;
|
|
row += rowSize;
|
|
}
|
|
if (!writer->writePointers(row_pointers, height)) {
|
|
delete[] row_pointers;
|
|
return splashErrGeneric;
|
|
}
|
|
delete[] row_pointers;
|
|
} else {
|
|
unsigned char *row = new unsigned char[3 * width];
|
|
for (int y = 0; y < height; y++) {
|
|
getRGBLine(y, row);
|
|
if (!writer->writeRow(&row)) {
|
|
delete[] row;
|
|
return splashErrGeneric;
|
|
}
|
|
}
|
|
delete[] row;
|
|
}
|
|
break;
|
|
case splashModeDeviceN8:
|
|
if (writer->supportCMYK()) {
|
|
unsigned char *row = new unsigned char[4 * width];
|
|
for (int y = 0; y < height; y++) {
|
|
getCMYKLine(y, row);
|
|
if (!writer->writeRow(&row)) {
|
|
delete[] row;
|
|
return splashErrGeneric;
|
|
}
|
|
}
|
|
delete[] row;
|
|
} else {
|
|
unsigned char *row = new unsigned char[3 * width];
|
|
for (int y = 0; y < height; y++) {
|
|
getRGBLine(y, row);
|
|
if (!writer->writeRow(&row)) {
|
|
delete[] row;
|
|
return splashErrGeneric;
|
|
}
|
|
}
|
|
delete[] row;
|
|
}
|
|
break;
|
|
case splashModeRGB8: {
|
|
SplashColorPtr row;
|
|
unsigned char **row_pointers = new unsigned char *[height];
|
|
row = data;
|
|
|
|
for (int y = 0; y < height; ++y) {
|
|
row_pointers[y] = row;
|
|
row += rowSize;
|
|
}
|
|
if (!writer->writePointers(row_pointers, height)) {
|
|
delete[] row_pointers;
|
|
return splashErrGeneric;
|
|
}
|
|
delete[] row_pointers;
|
|
} break;
|
|
|
|
case splashModeBGR8: {
|
|
unsigned char *row = new unsigned char[3 * width];
|
|
for (int y = 0; y < height; y++) {
|
|
// Convert into a PNG row
|
|
for (int x = 0; x < width; x++) {
|
|
row[3 * x] = data[y * rowSize + x * 3 + 2];
|
|
row[3 * x + 1] = data[y * rowSize + x * 3 + 1];
|
|
row[3 * x + 2] = data[y * rowSize + x * 3];
|
|
}
|
|
|
|
if (!writer->writeRow(&row)) {
|
|
delete[] row;
|
|
return splashErrGeneric;
|
|
}
|
|
}
|
|
delete[] row;
|
|
} break;
|
|
|
|
case splashModeXBGR8: {
|
|
unsigned char *row = new unsigned char[3 * width];
|
|
for (int y = 0; y < height; y++) {
|
|
// Convert into a PNG row
|
|
for (int x = 0; x < width; x++) {
|
|
row[3 * x] = data[y * rowSize + x * 4 + 2];
|
|
row[3 * x + 1] = data[y * rowSize + x * 4 + 1];
|
|
row[3 * x + 2] = data[y * rowSize + x * 4];
|
|
}
|
|
|
|
if (!writer->writeRow(&row)) {
|
|
delete[] row;
|
|
return splashErrGeneric;
|
|
}
|
|
}
|
|
delete[] row;
|
|
} break;
|
|
|
|
case splashModeMono8: {
|
|
if (imageWriterFormat == splashModeMono8) {
|
|
SplashColorPtr row;
|
|
unsigned char **row_pointers = new unsigned char *[height];
|
|
row = data;
|
|
|
|
for (int y = 0; y < height; ++y) {
|
|
row_pointers[y] = row;
|
|
row += rowSize;
|
|
}
|
|
if (!writer->writePointers(row_pointers, height)) {
|
|
delete[] row_pointers;
|
|
return splashErrGeneric;
|
|
}
|
|
delete[] row_pointers;
|
|
} else if (imageWriterFormat == splashModeRGB8) {
|
|
unsigned char *row = new unsigned char[3 * width];
|
|
for (int y = 0; y < height; y++) {
|
|
// Convert into a PNG row
|
|
for (int x = 0; x < width; x++) {
|
|
row[3 * x] = data[y * rowSize + x];
|
|
row[3 * x + 1] = data[y * rowSize + x];
|
|
row[3 * x + 2] = data[y * rowSize + x];
|
|
}
|
|
|
|
if (!writer->writeRow(&row)) {
|
|
delete[] row;
|
|
return splashErrGeneric;
|
|
}
|
|
}
|
|
delete[] row;
|
|
} else {
|
|
// only splashModeMono8 or splashModeRGB8
|
|
return splashErrGeneric;
|
|
}
|
|
} break;
|
|
|
|
case splashModeMono1: {
|
|
if (imageWriterFormat == splashModeMono1) {
|
|
SplashColorPtr row;
|
|
unsigned char **row_pointers = new unsigned char *[height];
|
|
row = data;
|
|
|
|
for (int y = 0; y < height; ++y) {
|
|
row_pointers[y] = row;
|
|
row += rowSize;
|
|
}
|
|
if (!writer->writePointers(row_pointers, height)) {
|
|
delete[] row_pointers;
|
|
return splashErrGeneric;
|
|
}
|
|
delete[] row_pointers;
|
|
} else if (imageWriterFormat == splashModeRGB8) {
|
|
unsigned char *row = new unsigned char[3 * width];
|
|
for (int y = 0; y < height; y++) {
|
|
// Convert into a PNG row
|
|
for (int x = 0; x < width; x++) {
|
|
getPixel(x, y, &row[3 * x]);
|
|
row[3 * x + 1] = row[3 * x];
|
|
row[3 * x + 2] = row[3 * x];
|
|
}
|
|
|
|
if (!writer->writeRow(&row)) {
|
|
delete[] row;
|
|
return splashErrGeneric;
|
|
}
|
|
}
|
|
delete[] row;
|
|
} else {
|
|
// only splashModeMono1 or splashModeRGB8
|
|
return splashErrGeneric;
|
|
}
|
|
} break;
|
|
|
|
default:
|
|
// can't happen
|
|
break;
|
|
}
|
|
|
|
if (!writer->close()) {
|
|
return splashErrGeneric;
|
|
}
|
|
|
|
return splashOk;
|
|
}
|