Coverage Report

Created: 2025-06-09 07:43

/src/gdal/frmts/nitf/nitfwritejpeg.cpp
Line
Count
Source (jump to first uncovered line)
1
/******************************************************************************
2
 *
3
 * Project:  NITF Read/Write Translator
4
 * Purpose:  GDALDataset/GDALRasterBand implementation on top of "nitflib".
5
 * Author:   Frank Warmerdam, warmerdam@pobox.com
6
 *
7
 ******************************************************************************
8
 * Copyright (c) 2002, Frank Warmerdam
9
 * Copyright (c) 2009-2010, Even Rouault <even dot rouault at spatialys.com>
10
 *
11
 * Portions Copyright (c) Her majesty the Queen in right of Canada as
12
 * represented by the Minister of National Defence, 2006.
13
 *
14
 * SPDX-License-Identifier: MIT
15
 ****************************************************************************/
16
17
#ifdef JPEG_SUPPORTED
18
19
#include "cpl_port.h"
20
#include "gdal_pam.h"
21
22
CPL_C_START
23
#ifdef LIBJPEG_12_PATH
24
#include LIBJPEG_12_PATH
25
#else
26
#include "jpeglib.h"
27
#endif
28
CPL_C_END
29
30
#if defined(EXPECTED_JPEG_LIB_VERSION) && !defined(LIBJPEG_12_PATH)
31
#if EXPECTED_JPEG_LIB_VERSION != JPEG_LIB_VERSION
32
#error EXPECTED_JPEG_LIB_VERSION != JPEG_LIB_VERSION
33
#endif
34
#endif
35
36
/*
37
 * Do we want to do special processing suitable for when JSAMPLE is a
38
 * 16bit value?
39
 */
40
41
/* HAVE_JPEGTURBO_DUAL_MODE_8_12 is defined for libjpeg-turbo >= 2.2 which
42
 * adds a dual-mode 8/12 bit API in the same library.
43
 */
44
45
#if defined(HAVE_JPEGTURBO_DUAL_MODE_8_12)
46
/* Start by undefining BITS_IN_JSAMPLE which is always set to 8 in libjpeg-turbo
47
 * >= 2.2 Cf
48
 * https://github.com/libjpeg-turbo/libjpeg-turbo/commit/8b9bc4b9635a2a047fb23ebe70c9acd728d3f99b
49
 */
50
#undef BITS_IN_JSAMPLE
51
/* libjpeg-turbo >= 2.2 adds J12xxxx datatypes for the 12-bit mode. */
52
#if defined(NITFWriteJPEGBlock)
53
#define BITS_IN_JSAMPLE 12
54
#define GDAL_JSAMPLE J12SAMPLE
55
#else
56
#define BITS_IN_JSAMPLE 8
57
#define GDAL_JSAMPLE JSAMPLE
58
#endif
59
#else
60
0
#define GDAL_JSAMPLE JSAMPLE
61
#endif
62
63
#if defined(JPEG_LIB_MK1)
64
#define JPEG_LIB_MK1_OR_12BIT 1
65
#elif BITS_IN_JSAMPLE == 12
66
#define JPEG_LIB_MK1_OR_12BIT 1
67
#endif
68
69
#if defined(JPEG_DUAL_MODE_8_12) && !defined(NITFWriteJPEGBlock)
70
int NITFWriteJPEGBlock_12(GDALDataset *poSrcDS, VSILFILE *fp, int nBlockXOff,
71
                          int nBlockYOff, int nBlockXSize, int nBlockYSize,
72
                          int bProgressive, int nQuality, const GByte *pabyAPP6,
73
                          int nRestartInterval, GDALProgressFunc pfnProgress,
74
                          void *pProgressData);
75
#endif
76
77
int NITFWriteJPEGBlock(GDALDataset *poSrcDS, VSILFILE *fp, int nBlockXOff,
78
                       int nBlockYOff, int nBlockXSize, int nBlockYSize,
79
                       int bProgressive, int nQuality, const GByte *pabyAPP6,
80
                       int nRestartInterval, GDALProgressFunc pfnProgress,
81
                       void *pProgressData);
82
83
#ifdef NITFWriteJPEGBlock
84
#define jpeg_vsiio_src NITF_jpeg_vsiio_src12
85
0
#define jpeg_vsiio_dest NITF_jpeg_vsiio_dest12
86
#else
87
#define jpeg_vsiio_src NITF_jpeg_vsiio_src
88
0
#define jpeg_vsiio_dest NITF_jpeg_vsiio_dest
89
#endif
90
#include "../jpeg/vsidataio.h"
91
#include "../jpeg/vsidataio.cpp"
92
93
/************************************************************************/
94
/*                         NITFWriteJPEGBlock()                         */
95
/************************************************************************/
96
97
int NITFWriteJPEGBlock(GDALDataset *poSrcDS, VSILFILE *fp, int nBlockXOff,
98
                       int nBlockYOff, int nBlockXSize, int nBlockYSize,
99
                       int bProgressive, int nQuality, const GByte *pabyAPP6,
100
                       int nRestartInterval, GDALProgressFunc pfnProgress,
101
                       void *pProgressData)
102
0
{
103
0
    GDALDataType eDT = poSrcDS->GetRasterBand(1)->GetRasterDataType();
104
#if defined(JPEG_DUAL_MODE_8_12) && !defined(NITFWriteJPEGBlock)
105
0
    if (eDT == GDT_UInt16)
106
0
    {
107
0
        return NITFWriteJPEGBlock_12(poSrcDS, fp, nBlockXOff, nBlockYOff,
108
0
                                     nBlockXSize, nBlockYSize, bProgressive,
109
0
                                     nQuality, pabyAPP6, nRestartInterval,
110
0
                                     pfnProgress, pProgressData);
111
0
    }
112
0
#endif
113
114
0
    int anBandList[3] = {1, 2, 3};
115
116
    /* -------------------------------------------------------------------- */
117
    /*      Initialize JPG access to the file.                              */
118
    /* -------------------------------------------------------------------- */
119
0
    struct jpeg_compress_struct sCInfo;
120
0
    struct jpeg_error_mgr sJErr;
121
122
0
    memset(&sCInfo, 0, sizeof(sCInfo));
123
0
    sCInfo.err = jpeg_std_error(&sJErr);
124
0
#if defined(__GNUC__)
125
0
#pragma GCC diagnostic push
126
0
#pragma GCC diagnostic ignored "-Wold-style-cast"
127
0
#endif
128
0
    jpeg_create_compress(&sCInfo);
129
0
#if defined(__GNUC__)
130
0
#pragma GCC diagnostic pop
131
0
#endif
132
133
0
    jpeg_vsiio_dest(&sCInfo, fp);
134
135
0
    sCInfo.image_width = nBlockXSize;
136
0
    sCInfo.image_height = nBlockYSize;
137
138
0
    const int nBands = poSrcDS->GetRasterCount();
139
0
    sCInfo.input_components = nBands;
140
141
0
    if (nBands == 1)
142
0
    {
143
0
        sCInfo.in_color_space = JCS_GRAYSCALE;
144
0
    }
145
0
    else
146
0
    {
147
0
        sCInfo.in_color_space = JCS_RGB;
148
0
    }
149
150
0
    jpeg_set_defaults(&sCInfo);
151
152
#if defined(JPEG_LIB_MK1_OR_12BIT)
153
0
    if (eDT == GDT_UInt16)
154
0
    {
155
0
        sCInfo.data_precision = 12;
156
0
    }
157
0
    else
158
0
    {
159
0
        sCInfo.data_precision = 8;
160
0
    }
161
#endif
162
163
0
    GDALDataType eWorkDT;
164
#ifdef JPEG_LIB_MK1
165
    sCInfo.bits_in_jsample = sCInfo.data_precision;
166
    eWorkDT = GDT_UInt16; /* Always force to 16 bit for JPEG_LIB_MK1 */
167
#else
168
0
    eWorkDT = eDT;
169
0
#endif
170
171
0
    sCInfo.write_JFIF_header = FALSE;
172
173
    /* Set the restart interval */
174
0
    if (nRestartInterval < 0)
175
0
    {
176
        /* nRestartInterval < 0 means that we will guess the value */
177
        /* so we set it at the maximum allowed by MIL-STD-188-198 */
178
        /* that is to say the number of MCU per row-block */
179
0
        nRestartInterval = nBlockXSize / 8;
180
0
    }
181
182
0
    if (nRestartInterval > 0)
183
0
        sCInfo.restart_interval = nRestartInterval;
184
185
0
    jpeg_set_quality(&sCInfo, nQuality, TRUE);
186
187
0
    if (bProgressive)
188
0
        jpeg_simple_progression(&sCInfo);
189
190
0
    jpeg_start_compress(&sCInfo, TRUE);
191
192
    /* -------------------------------------------------------------------- */
193
    /*    Emits APP6 NITF application segment (required by MIL-STD-188-198) */
194
    /* -------------------------------------------------------------------- */
195
0
    if (pabyAPP6)
196
0
    {
197
        /* 0xe6 = APP6 marker */
198
0
        jpeg_write_marker(&sCInfo, 0xe6,
199
0
                          reinterpret_cast<const JOCTET *>(pabyAPP6), 23);
200
0
    }
201
202
    /* -------------------------------------------------------------------- */
203
    /*      Loop over image, copying image data.                            */
204
    /* -------------------------------------------------------------------- */
205
0
    const int nWorkDTSize = GDALGetDataTypeSizeBytes(eWorkDT);
206
207
0
    GByte *pabyScanline = reinterpret_cast<GByte *>(
208
0
        CPLMalloc(cpl::fits_on<int>(nBands * nBlockXSize * nWorkDTSize)));
209
210
0
    const int nXSize = poSrcDS->GetRasterXSize();
211
0
    const int nYSize = poSrcDS->GetRasterYSize();
212
213
0
    const double nTotalPixels = static_cast<double>(nXSize) * nYSize;
214
215
0
    int nBlockXSizeToRead = nBlockXSize;
216
0
    if (nBlockXSize * nBlockXOff + nBlockXSize > nXSize)
217
0
    {
218
0
        nBlockXSizeToRead = nXSize - nBlockXSize * nBlockXOff;
219
0
    }
220
0
    int nBlockYSizeToRead = nBlockYSize;
221
0
    if (nBlockYSize * nBlockYOff + nBlockYSize > nYSize)
222
0
    {
223
0
        nBlockYSizeToRead = nYSize - nBlockYSize * nBlockYOff;
224
0
    }
225
226
#if defined(JPEG_LIB_MK1_OR_12BIT)
227
    bool bClipWarn = false;
228
#endif
229
230
0
    CPLErr eErr = CE_None;
231
0
    for (int iLine = 0; iLine < nBlockYSize && eErr == CE_None; iLine++)
232
0
    {
233
0
        if (iLine < nBlockYSizeToRead)
234
0
        {
235
0
            eErr = poSrcDS->RasterIO(
236
0
                GF_Read, nBlockXSize * nBlockXOff,
237
0
                iLine + nBlockYSize * nBlockYOff, nBlockXSizeToRead, 1,
238
0
                pabyScanline, nBlockXSizeToRead, 1, eWorkDT, nBands, anBandList,
239
0
                static_cast<GSpacing>(nBands) * nWorkDTSize,
240
0
                static_cast<GSpacing>(nBands) * nWorkDTSize * nBlockXSize,
241
0
                nWorkDTSize, nullptr);
242
243
            /* Repeat the last pixel till the end of the line */
244
            /* to minimize discontinuity */
245
0
            if (nBlockXSizeToRead < nBlockXSize)
246
0
            {
247
0
                for (int iBand = 0; iBand < nBands; iBand++)
248
0
                {
249
#if defined(JPEG_LIB_MK1_OR_12BIT)
250
0
                    if (eWorkDT == GDT_UInt16)
251
0
                    {
252
0
                        GUInt16 *panScanline =
253
0
                            reinterpret_cast<GUInt16 *>(pabyScanline);
254
0
                        const GUInt16 nVal =
255
0
                            panScanline[nBands * (nBlockXSizeToRead - 1) +
256
0
                                        iBand];
257
0
                        for (int iX = nBlockXSizeToRead; iX < nBlockXSize; iX++)
258
0
                        {
259
0
                            panScanline[nBands * iX + iBand] = nVal;
260
0
                        }
261
0
                    }
262
0
                    else
263
0
#endif
264
0
                    {
265
0
                        GByte bVal =
266
0
                            pabyScanline[nBands * (nBlockXSizeToRead - 1) +
267
0
                                         iBand];
268
0
                        for (int iX = nBlockXSizeToRead; iX < nBlockXSize; iX++)
269
0
                        {
270
0
                            pabyScanline[nBands * iX + iBand] = bVal;
271
0
                        }
272
0
                    }
273
0
                }
274
0
            }
275
0
        }
276
277
#if defined(JPEG_LIB_MK1_OR_12BIT)
278
        // clamp 16bit values to 12bit.
279
0
        if (eDT == GDT_UInt16)
280
0
        {
281
0
            GUInt16 *panScanline = reinterpret_cast<GUInt16 *>(pabyScanline);
282
283
0
            for (int iPixel = 0; iPixel < nBlockXSize * nBands; iPixel++)
284
0
            {
285
0
                if (panScanline[iPixel] > 4095)
286
0
                {
287
0
                    panScanline[iPixel] = 4095;
288
0
                    if (!bClipWarn)
289
0
                    {
290
0
                        bClipWarn = true;
291
0
                        CPLError(CE_Warning, CPLE_AppDefined,
292
0
                                 "One or more pixels clipped to fit 12bit "
293
0
                                 "domain for jpeg output.");
294
0
                    }
295
0
                }
296
0
            }
297
0
        }
298
#endif
299
300
0
        GDAL_JSAMPLE *ppSamples =
301
0
            reinterpret_cast<GDAL_JSAMPLE *>(pabyScanline);
302
303
0
        if (eErr == CE_None)
304
0
        {
305
#if defined(HAVE_JPEGTURBO_DUAL_MODE_8_12) && BITS_IN_JSAMPLE == 12
306
            jpeg12_write_scanlines(&sCInfo, &ppSamples, 1);
307
#else
308
0
            jpeg_write_scanlines(&sCInfo, &ppSamples, 1);
309
0
#endif
310
0
        }
311
312
0
        double nCurPixels =
313
0
            static_cast<double>(nBlockYOff) * nBlockYSize * nXSize +
314
0
            static_cast<double>(nBlockXOff) * nBlockYSize * nBlockXSize +
315
0
            (iLine + 1) * nBlockXSizeToRead;
316
0
        if (eErr == CE_None &&
317
0
            !pfnProgress(nCurPixels / nTotalPixels, nullptr, pProgressData))
318
0
        {
319
0
            eErr = CE_Failure;
320
0
            CPLError(CE_Failure, CPLE_UserInterrupt,
321
0
                     "User terminated CreateCopy()");
322
0
        }
323
0
    }
324
325
    /* -------------------------------------------------------------------- */
326
    /*      Cleanup and close.                                              */
327
    /* -------------------------------------------------------------------- */
328
0
    CPLFree(pabyScanline);
329
330
0
    if (eErr == CE_None)
331
0
        jpeg_finish_compress(&sCInfo);
332
0
    jpeg_destroy_compress(&sCInfo);
333
334
0
    return eErr == CE_None;
335
0
}
Unexecuted instantiation: NITFWriteJPEGBlock(GDALDataset*, VSIVirtualHandle*, int, int, int, int, int, int, unsigned char const*, int, int (*)(double, char const*, void*), void*)
Unexecuted instantiation: NITFWriteJPEGBlock_12(GDALDataset*, VSIVirtualHandle*, int, int, int, int, int, int, unsigned char const*, int, int (*)(double, char const*, void*), void*)
336
#endif /* def JPEG_SUPPORTED */