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 55 : jpeg_create_compress(&sCInfo);
125 :
126 55 : jpeg_vsiio_dest(&sCInfo, fp);
127 :
128 55 : sCInfo.image_width = nBlockXSize;
129 55 : sCInfo.image_height = nBlockYSize;
130 :
131 55 : const int nBands = poSrcDS->GetRasterCount();
132 55 : sCInfo.input_components = nBands;
133 :
134 55 : if (nBands == 1)
135 : {
136 4 : sCInfo.in_color_space = JCS_GRAYSCALE;
137 : }
138 : else
139 : {
140 51 : sCInfo.in_color_space = JCS_RGB;
141 : }
142 :
143 55 : jpeg_set_defaults(&sCInfo);
144 :
145 : #if defined(JPEG_LIB_MK1_OR_12BIT)
146 41 : if (eDT == GDT_UInt16)
147 : {
148 41 : sCInfo.data_precision = 12;
149 : }
150 : else
151 : {
152 0 : sCInfo.data_precision = 8;
153 : }
154 : #endif
155 :
156 : GDALDataType eWorkDT;
157 : #ifdef JPEG_LIB_MK1
158 : sCInfo.bits_in_jsample = sCInfo.data_precision;
159 : eWorkDT = GDT_UInt16; /* Always force to 16 bit for JPEG_LIB_MK1 */
160 : #else
161 55 : eWorkDT = eDT;
162 : #endif
163 :
164 55 : sCInfo.write_JFIF_header = FALSE;
165 :
166 : /* Set the restart interval */
167 55 : if (nRestartInterval < 0)
168 : {
169 : /* nRestartInterval < 0 means that we will guess the value */
170 : /* so we set it at the maximum allowed by MIL-STD-188-198 */
171 : /* that is to say the number of MCU per row-block */
172 55 : nRestartInterval = nBlockXSize / 8;
173 : }
174 :
175 55 : if (nRestartInterval > 0)
176 55 : sCInfo.restart_interval = nRestartInterval;
177 :
178 55 : jpeg_set_quality(&sCInfo, nQuality, TRUE);
179 :
180 55 : if (bProgressive)
181 0 : jpeg_simple_progression(&sCInfo);
182 :
183 55 : jpeg_start_compress(&sCInfo, TRUE);
184 :
185 : /* -------------------------------------------------------------------- */
186 : /* Emits APP6 NITF application segment (required by MIL-STD-188-198) */
187 : /* -------------------------------------------------------------------- */
188 55 : if (pabyAPP6)
189 : {
190 : /* 0xe6 = APP6 marker */
191 10 : jpeg_write_marker(&sCInfo, 0xe6, (const JOCTET *)pabyAPP6, 23);
192 : }
193 :
194 : /* -------------------------------------------------------------------- */
195 : /* Loop over image, copying image data. */
196 : /* -------------------------------------------------------------------- */
197 55 : const int nWorkDTSize = GDALGetDataTypeSizeBytes(eWorkDT);
198 :
199 : GByte *pabyScanline = reinterpret_cast<GByte *>(
200 55 : CPLMalloc(cpl::fits_on<int>(nBands * nBlockXSize * nWorkDTSize)));
201 :
202 55 : const int nXSize = poSrcDS->GetRasterXSize();
203 55 : const int nYSize = poSrcDS->GetRasterYSize();
204 :
205 55 : const double nTotalPixels = static_cast<double>(nXSize) * nYSize;
206 :
207 55 : int nBlockXSizeToRead = nBlockXSize;
208 55 : if (nBlockXSize * nBlockXOff + nBlockXSize > nXSize)
209 : {
210 6 : nBlockXSizeToRead = nXSize - nBlockXSize * nBlockXOff;
211 : }
212 55 : int nBlockYSizeToRead = nBlockYSize;
213 55 : if (nBlockYSize * nBlockYOff + nBlockYSize > nYSize)
214 : {
215 45 : nBlockYSizeToRead = nYSize - nBlockYSize * nBlockYOff;
216 : }
217 :
218 : #if defined(JPEG_LIB_MK1_OR_12BIT)
219 41 : bool bClipWarn = false;
220 : #endif
221 :
222 55 : CPLErr eErr = CE_None;
223 10800 : for (int iLine = 0; iLine < nBlockYSize && eErr == CE_None; iLine++)
224 : {
225 10745 : if (iLine < nBlockYSizeToRead)
226 : {
227 950 : eErr = poSrcDS->RasterIO(
228 : GF_Read, nBlockXSize * nBlockXOff,
229 475 : iLine + nBlockYSize * nBlockYOff, nBlockXSizeToRead, 1,
230 : pabyScanline, nBlockXSizeToRead, 1, eWorkDT, nBands, anBandList,
231 475 : static_cast<GSpacing>(nBands) * nWorkDTSize,
232 475 : static_cast<GSpacing>(nBands) * nWorkDTSize * nBlockXSize,
233 : nWorkDTSize, nullptr);
234 :
235 : /* Repeat the last pixel till the end of the line */
236 : /* to minimize discontinuity */
237 475 : if (nBlockXSizeToRead < nBlockXSize)
238 : {
239 604 : for (int iBand = 0; iBand < nBands; iBand++)
240 : {
241 : #if defined(JPEG_LIB_MK1_OR_12BIT)
242 3 : if (eWorkDT == GDT_UInt16)
243 : {
244 3 : GUInt16 *panScanline =
245 : reinterpret_cast<GUInt16 *>(pabyScanline);
246 3 : const GUInt16 nVal =
247 3 : panScanline[nBands * (nBlockXSizeToRead - 1) +
248 3 : iBand];
249 723 : for (int iX = nBlockXSizeToRead; iX < nBlockXSize; iX++)
250 : {
251 720 : panScanline[nBands * iX + iBand] = nVal;
252 : }
253 : }
254 : else
255 : #endif
256 : {
257 450 : GByte bVal =
258 450 : pabyScanline[nBands * (nBlockXSizeToRead - 1) +
259 450 : iBand];
260 6750 : for (int iX = nBlockXSizeToRead; iX < nBlockXSize; iX++)
261 : {
262 6300 : pabyScanline[nBands * iX + iBand] = bVal;
263 : }
264 : }
265 : }
266 : }
267 : }
268 :
269 : #if defined(JPEG_LIB_MK1_OR_12BIT)
270 : // clamp 16bit values to 12bit.
271 10304 : if (eDT == GDT_UInt16)
272 : {
273 10304 : GUInt16 *panScanline = reinterpret_cast<GUInt16 *>(pabyScanline);
274 :
275 7878720 : for (int iPixel = 0; iPixel < nBlockXSize * nBands; iPixel++)
276 : {
277 7868420 : if (panScanline[iPixel] > 4095)
278 : {
279 20480 : panScanline[iPixel] = 4095;
280 20480 : if (!bClipWarn)
281 : {
282 40 : bClipWarn = true;
283 40 : CPLError(CE_Warning, CPLE_AppDefined,
284 : "One or more pixels clipped to fit 12bit "
285 : "domain for jpeg output.");
286 : }
287 : }
288 : }
289 : }
290 : #endif
291 :
292 10745 : GDAL_JSAMPLE *ppSamples =
293 : reinterpret_cast<GDAL_JSAMPLE *>(pabyScanline);
294 :
295 10745 : if (eErr == CE_None)
296 : {
297 : #if defined(HAVE_JPEGTURBO_DUAL_MODE_8_12) && BITS_IN_JSAMPLE == 12
298 : jpeg12_write_scanlines(&sCInfo, &ppSamples, 1);
299 : #else
300 10745 : jpeg_write_scanlines(&sCInfo, &ppSamples, 1);
301 : #endif
302 : }
303 :
304 10745 : double nCurPixels =
305 10745 : static_cast<double>(nBlockYOff) * nBlockYSize * nXSize +
306 10745 : static_cast<double>(nBlockXOff) * nBlockYSize * nBlockXSize +
307 10745 : (iLine + 1) * nBlockXSizeToRead;
308 21490 : if (eErr == CE_None &&
309 10745 : !pfnProgress(nCurPixels / nTotalPixels, nullptr, pProgressData))
310 : {
311 0 : eErr = CE_Failure;
312 0 : CPLError(CE_Failure, CPLE_UserInterrupt,
313 : "User terminated CreateCopy()");
314 : }
315 : }
316 :
317 : /* -------------------------------------------------------------------- */
318 : /* Cleanup and close. */
319 : /* -------------------------------------------------------------------- */
320 55 : CPLFree(pabyScanline);
321 :
322 55 : if (eErr == CE_None)
323 55 : jpeg_finish_compress(&sCInfo);
324 55 : jpeg_destroy_compress(&sCInfo);
325 :
326 55 : return eErr == CE_None;
327 : }
328 : #endif /* def JPEG_SUPPORTED */
|