Coverage Report

Created: 2026-06-30 07:44

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