Line data Source code
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 : #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 : #define jpeg_vsiio_dest NITF_jpeg_vsiio_dest12
86 : #else
87 : #define jpeg_vsiio_src NITF_jpeg_vsiio_src
88 : #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 96 : 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 : {
103 96 : GDALDataType eDT = poSrcDS->GetRasterBand(1)->GetRasterDataType();
104 : #if defined(JPEG_DUAL_MODE_8_12) && !defined(NITFWriteJPEGBlock)
105 55 : if (eDT == GDT_UInt16)
106 : {
107 41 : return NITFWriteJPEGBlock_12(poSrcDS, fp, nBlockXOff, nBlockYOff,
108 : nBlockXSize, nBlockYSize, bProgressive,
109 : nQuality, pabyAPP6, nRestartInterval,
110 41 : pfnProgress, pProgressData);
111 : }
112 : #endif
113 :
114 55 : int anBandList[3] = {1, 2, 3};
115 :
116 : /* -------------------------------------------------------------------- */
117 : /* Initialize JPG access to the file. */
118 : /* -------------------------------------------------------------------- */
119 : struct jpeg_compress_struct sCInfo;
120 : struct jpeg_error_mgr sJErr;
121 :
122 55 : memset(&sCInfo, 0, sizeof(sCInfo));
123 55 : sCInfo.err = jpeg_std_error(&sJErr);
124 : #if defined(__GNUC__)
125 : #pragma GCC diagnostic push
126 : #pragma GCC diagnostic ignored "-Wold-style-cast"
127 : #endif
128 55 : jpeg_create_compress(&sCInfo);
129 : #if defined(__GNUC__)
130 : #pragma GCC diagnostic pop
131 : #endif
132 :
133 55 : jpeg_vsiio_dest(&sCInfo, fp);
134 :
135 55 : sCInfo.image_width = nBlockXSize;
136 55 : sCInfo.image_height = nBlockYSize;
137 :
138 55 : const int nBands = poSrcDS->GetRasterCount();
139 55 : sCInfo.input_components = nBands;
140 :
141 55 : if (nBands == 1)
142 : {
143 4 : sCInfo.in_color_space = JCS_GRAYSCALE;
144 : }
145 : else
146 : {
147 51 : sCInfo.in_color_space = JCS_RGB;
148 : }
149 :
150 55 : jpeg_set_defaults(&sCInfo);
151 :
152 : #if defined(JPEG_LIB_MK1_OR_12BIT)
153 41 : if (eDT == GDT_UInt16)
154 : {
155 41 : sCInfo.data_precision = 12;
156 : }
157 : else
158 : {
159 0 : sCInfo.data_precision = 8;
160 : }
161 : #endif
162 :
163 : 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 55 : eWorkDT = eDT;
169 : #endif
170 :
171 55 : sCInfo.write_JFIF_header = FALSE;
172 :
173 : /* Set the restart interval */
174 55 : if (nRestartInterval < 0)
175 : {
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 55 : nRestartInterval = nBlockXSize / 8;
180 : }
181 :
182 55 : if (nRestartInterval > 0)
183 55 : sCInfo.restart_interval = nRestartInterval;
184 :
185 55 : jpeg_set_quality(&sCInfo, nQuality, TRUE);
186 :
187 55 : if (bProgressive)
188 0 : jpeg_simple_progression(&sCInfo);
189 :
190 55 : jpeg_start_compress(&sCInfo, TRUE);
191 :
192 : /* -------------------------------------------------------------------- */
193 : /* Emits APP6 NITF application segment (required by MIL-STD-188-198) */
194 : /* -------------------------------------------------------------------- */
195 55 : if (pabyAPP6)
196 : {
197 : /* 0xe6 = APP6 marker */
198 10 : jpeg_write_marker(&sCInfo, 0xe6,
199 : reinterpret_cast<const JOCTET *>(pabyAPP6), 23);
200 : }
201 :
202 : /* -------------------------------------------------------------------- */
203 : /* Loop over image, copying image data. */
204 : /* -------------------------------------------------------------------- */
205 55 : const int nWorkDTSize = GDALGetDataTypeSizeBytes(eWorkDT);
206 :
207 : GByte *pabyScanline = reinterpret_cast<GByte *>(
208 55 : CPLMalloc(cpl::fits_on<int>(nBands * nBlockXSize * nWorkDTSize)));
209 :
210 55 : const int nXSize = poSrcDS->GetRasterXSize();
211 55 : const int nYSize = poSrcDS->GetRasterYSize();
212 :
213 55 : const double nTotalPixels = static_cast<double>(nXSize) * nYSize;
214 :
215 55 : int nBlockXSizeToRead = nBlockXSize;
216 55 : if (nBlockXSize * nBlockXOff + nBlockXSize > nXSize)
217 : {
218 6 : nBlockXSizeToRead = nXSize - nBlockXSize * nBlockXOff;
219 : }
220 55 : int nBlockYSizeToRead = nBlockYSize;
221 55 : if (nBlockYSize * nBlockYOff + nBlockYSize > nYSize)
222 : {
223 45 : nBlockYSizeToRead = nYSize - nBlockYSize * nBlockYOff;
224 : }
225 :
226 : #if defined(JPEG_LIB_MK1_OR_12BIT)
227 41 : bool bClipWarn = false;
228 : #endif
229 :
230 55 : CPLErr eErr = CE_None;
231 10800 : for (int iLine = 0; iLine < nBlockYSize && eErr == CE_None; iLine++)
232 : {
233 10745 : if (iLine < nBlockYSizeToRead)
234 : {
235 950 : eErr = poSrcDS->RasterIO(
236 : GF_Read, nBlockXSize * nBlockXOff,
237 475 : iLine + nBlockYSize * nBlockYOff, nBlockXSizeToRead, 1,
238 : pabyScanline, nBlockXSizeToRead, 1, eWorkDT, nBands, anBandList,
239 475 : static_cast<GSpacing>(nBands) * nWorkDTSize,
240 475 : static_cast<GSpacing>(nBands) * nWorkDTSize * nBlockXSize,
241 : nWorkDTSize, nullptr);
242 :
243 : /* Repeat the last pixel till the end of the line */
244 : /* to minimize discontinuity */
245 475 : if (nBlockXSizeToRead < nBlockXSize)
246 : {
247 604 : for (int iBand = 0; iBand < nBands; iBand++)
248 : {
249 : #if defined(JPEG_LIB_MK1_OR_12BIT)
250 3 : if (eWorkDT == GDT_UInt16)
251 : {
252 3 : GUInt16 *panScanline =
253 : reinterpret_cast<GUInt16 *>(pabyScanline);
254 3 : const GUInt16 nVal =
255 3 : panScanline[nBands * (nBlockXSizeToRead - 1) +
256 3 : iBand];
257 723 : for (int iX = nBlockXSizeToRead; iX < nBlockXSize; iX++)
258 : {
259 720 : panScanline[nBands * iX + iBand] = nVal;
260 : }
261 : }
262 : else
263 : #endif
264 : {
265 450 : GByte bVal =
266 450 : pabyScanline[nBands * (nBlockXSizeToRead - 1) +
267 450 : iBand];
268 6750 : for (int iX = nBlockXSizeToRead; iX < nBlockXSize; iX++)
269 : {
270 6300 : pabyScanline[nBands * iX + iBand] = bVal;
271 : }
272 : }
273 : }
274 : }
275 : }
276 :
277 : #if defined(JPEG_LIB_MK1_OR_12BIT)
278 : // clamp 16bit values to 12bit.
279 10304 : if (eDT == GDT_UInt16)
280 : {
281 10304 : GUInt16 *panScanline = reinterpret_cast<GUInt16 *>(pabyScanline);
282 :
283 7878720 : for (int iPixel = 0; iPixel < nBlockXSize * nBands; iPixel++)
284 : {
285 7868420 : if (panScanline[iPixel] > 4095)
286 : {
287 20480 : panScanline[iPixel] = 4095;
288 20480 : if (!bClipWarn)
289 : {
290 40 : bClipWarn = true;
291 40 : CPLError(CE_Warning, CPLE_AppDefined,
292 : "One or more pixels clipped to fit 12bit "
293 : "domain for jpeg output.");
294 : }
295 : }
296 : }
297 : }
298 : #endif
299 :
300 10745 : GDAL_JSAMPLE *ppSamples =
301 : reinterpret_cast<GDAL_JSAMPLE *>(pabyScanline);
302 :
303 10745 : if (eErr == CE_None)
304 : {
305 : #if defined(HAVE_JPEGTURBO_DUAL_MODE_8_12) && BITS_IN_JSAMPLE == 12
306 : jpeg12_write_scanlines(&sCInfo, &ppSamples, 1);
307 : #else
308 10745 : jpeg_write_scanlines(&sCInfo, &ppSamples, 1);
309 : #endif
310 : }
311 :
312 10745 : double nCurPixels =
313 10745 : static_cast<double>(nBlockYOff) * nBlockYSize * nXSize +
314 10745 : static_cast<double>(nBlockXOff) * nBlockYSize * nBlockXSize +
315 10745 : (iLine + 1) * nBlockXSizeToRead;
316 21490 : if (eErr == CE_None &&
317 10745 : !pfnProgress(nCurPixels / nTotalPixels, nullptr, pProgressData))
318 : {
319 0 : eErr = CE_Failure;
320 0 : CPLError(CE_Failure, CPLE_UserInterrupt,
321 : "User terminated CreateCopy()");
322 : }
323 : }
324 :
325 : /* -------------------------------------------------------------------- */
326 : /* Cleanup and close. */
327 : /* -------------------------------------------------------------------- */
328 55 : CPLFree(pabyScanline);
329 :
330 55 : if (eErr == CE_None)
331 55 : jpeg_finish_compress(&sCInfo);
332 55 : jpeg_destroy_compress(&sCInfo);
333 :
334 55 : return eErr == CE_None;
335 : }
336 : #endif /* def JPEG_SUPPORTED */
|