/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 */ |