Coverage Report

Created: 2025-12-31 07:23

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