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  * Permission is hereby granted, free of charge, to any person obtaining a
15  * copy of this software and associated documentation files (the "Software"),
16  * to deal in the Software without restriction, including without limitation
17  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
18  * and/or sell copies of the Software, and to permit persons to whom the
19  * Software is furnished to do so, subject to the following conditions:
20  *
21  * The above copyright notice and this permission notice shall be included
22  * in all copies or substantial portions of the Software.
23  *
24  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
25  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
26  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
27  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
28  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
29  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
30  * DEALINGS IN THE SOFTWARE.
31  ****************************************************************************/
32 
33 #ifdef JPEG_SUPPORTED
34 
35 #include "cpl_port.h"
36 #include "gdal_pam.h"
37 
38 CPL_CVSID("$Id: nitfwritejpeg.cpp b1c9c12ad373e40b955162b45d704070d4ebf7b0 2019-06-19 16:50:15 +0200 Even Rouault $")
39 
40 CPL_C_START
41 #ifdef LIBJPEG_12_PATH
42 #  include LIBJPEG_12_PATH
43 #else
44 #  include "jpeglib.h"
45 #endif
46 CPL_C_END
47 
48 /*
49 * Do we want to do special processing suitable for when JSAMPLE is a
50 * 16bit value?
51 */
52 #if defined(JPEG_LIB_MK1)
53 #  define JPEG_LIB_MK1_OR_12BIT 1
54 #elif BITS_IN_JSAMPLE == 12
55 #  define JPEG_LIB_MK1_OR_12BIT 1
56 #endif
57 
58 #if defined(JPEG_DUAL_MODE_8_12) && !defined(NITFWriteJPEGBlock)
59 int
60 NITFWriteJPEGBlock_12( GDALDataset *poSrcDS, VSILFILE *fp,
61                      int nBlockXOff, int nBlockYOff,
62                      int nBlockXSize, int nBlockYSize,
63                      int bProgressive, int nQuality,
64                      const GByte* pabyAPP6, int nRestartInterval,
65                      GDALProgressFunc pfnProgress, void * pProgressData );
66 #endif
67 
68 int
69 NITFWriteJPEGBlock( GDALDataset *poSrcDS, VSILFILE *fp,
70                     int nBlockXOff, int nBlockYOff,
71                     int nBlockXSize, int nBlockYSize,
72                     int bProgressive, int nQuality,
73                     const GByte* pabyAPP6, int nRestartInterval,
74                     GDALProgressFunc pfnProgress, void * pProgressData );
75 
76 void jpeg_vsiio_dest (j_compress_ptr cinfo, VSILFILE * outfile);
77 
78 /************************************************************************/
79 /*                         NITFWriteJPEGBlock()                         */
80 /************************************************************************/
81 
82 int
83 NITFWriteJPEGBlock( GDALDataset *poSrcDS, VSILFILE *fp,
84                     int nBlockXOff, int nBlockYOff,
85                     int nBlockXSize, int nBlockYSize,
86                     int bProgressive, int nQuality,
87                     const GByte* pabyAPP6, int nRestartInterval,
88                     GDALProgressFunc pfnProgress, void * pProgressData )
89 {
90     GDALDataType eDT = poSrcDS->GetRasterBand(1)->GetRasterDataType();
91 #if defined(JPEG_DUAL_MODE_8_12) && !defined(NITFWriteJPEGBlock)
92     if( eDT == GDT_UInt16 )
93     {
94         return NITFWriteJPEGBlock_12(poSrcDS, fp,
95                                      nBlockXOff, nBlockYOff,
96                                      nBlockXSize, nBlockYSize,
97                                      bProgressive, nQuality,
98                                      pabyAPP6, nRestartInterval,
99                                      pfnProgress, pProgressData );
100     }
101 #endif
102 
103     int  anBandList[3] = {1,2,3};
104 
105 /* -------------------------------------------------------------------- */
106 /*      Initialize JPG access to the file.                              */
107 /* -------------------------------------------------------------------- */
108     struct jpeg_compress_struct sCInfo;
109     struct jpeg_error_mgr sJErr;
110 
111     memset(&sCInfo, 0, sizeof(sCInfo));
112     sCInfo.err = jpeg_std_error( &sJErr );
113     jpeg_create_compress( &sCInfo );
114 
115     jpeg_vsiio_dest( &sCInfo, fp );
116 
117     sCInfo.image_width = nBlockXSize;
118     sCInfo.image_height = nBlockYSize;
119 
120     const int nBands = poSrcDS->GetRasterCount();
121     sCInfo.input_components = nBands;
122 
123     if( nBands == 1 )
124     {
125         sCInfo.in_color_space = JCS_GRAYSCALE;
126     }
127     else
128     {
129         sCInfo.in_color_space = JCS_RGB;
130     }
131 
132     jpeg_set_defaults( &sCInfo );
133 
134 #if defined(JPEG_LIB_MK1_OR_12BIT)
135     if( eDT == GDT_UInt16 )
136     {
137         sCInfo.data_precision = 12;
138     }
139     else
140     {
141         sCInfo.data_precision = 8;
142     }
143 #endif
144 
145     GDALDataType eWorkDT;
146 #ifdef JPEG_LIB_MK1
147     sCInfo.bits_in_jsample = sCInfo.data_precision;
148     eWorkDT = GDT_UInt16; /* Always force to 16 bit for JPEG_LIB_MK1 */
149 #else
150     eWorkDT = eDT;
151 #endif
152 
153     sCInfo.write_JFIF_header = FALSE;
154 
155     /* Set the restart interval */
156     if (nRestartInterval < 0)
157     {
158         /* nRestartInterval < 0 means that we will guess the value */
159         /* so we set it at the maximum allowed by MIL-STD-188-198 */
160         /* that is to say the number of MCU per row-block */
161         nRestartInterval = nBlockXSize / 8;
162     }
163 
164     if (nRestartInterval > 0)
165         sCInfo.restart_interval = nRestartInterval;
166 
167     jpeg_set_quality( &sCInfo, nQuality, TRUE );
168 
169     if( bProgressive )
170         jpeg_simple_progression( &sCInfo );
171 
172     jpeg_start_compress( &sCInfo, TRUE );
173 
174 /* -------------------------------------------------------------------- */
175 /*    Emits APP6 NITF application segment (required by MIL-STD-188-198) */
176 /* -------------------------------------------------------------------- */
177     if (pabyAPP6)
178     {
179         /* 0xe6 = APP6 marker */
180         jpeg_write_marker( &sCInfo, 0xe6, (const JOCTET*) pabyAPP6, 23);
181     }
182 
183 /* -------------------------------------------------------------------- */
184 /*      Loop over image, copying image data.                            */
185 /* -------------------------------------------------------------------- */
186     const int nWorkDTSize = GDALGetDataTypeSizeBytes(eWorkDT);
187 
188     GByte *pabyScanline = reinterpret_cast<GByte *>(
189         CPLMalloc( nBands * nBlockXSize * nWorkDTSize ) );
190 
191     const int nXSize = poSrcDS->GetRasterXSize();
192     const int nYSize = poSrcDS->GetRasterYSize();
193 
194     const double nTotalPixels = static_cast<double>( nXSize * nYSize );
195 
196     int nBlockXSizeToRead = nBlockXSize;
197     if (nBlockXSize * nBlockXOff + nBlockXSize > nXSize)
198     {
199         nBlockXSizeToRead = nXSize - nBlockXSize * nBlockXOff;
200     }
201     int nBlockYSizeToRead = nBlockYSize;
202     if (nBlockYSize * nBlockYOff + nBlockYSize > nYSize)
203     {
204         nBlockYSizeToRead = nYSize - nBlockYSize * nBlockYOff;
205     }
206 
207 #if defined(JPEG_LIB_MK1_OR_12BIT)
208     bool bClipWarn = false;
209 #endif
210 
211     CPLErr eErr = CE_None;
212     for( int iLine = 0; iLine < nBlockYSize && eErr == CE_None; iLine++ )
213     {
214         if (iLine < nBlockYSizeToRead)
215         {
216             eErr = poSrcDS->RasterIO( GF_Read, nBlockXSize * nBlockXOff, iLine + nBlockYSize * nBlockYOff, nBlockXSizeToRead, 1,
217                                     pabyScanline, nBlockXSizeToRead, 1, eWorkDT,
218                                     nBands, anBandList,
219                                     nBands*nWorkDTSize, nBands * nBlockXSize * nWorkDTSize, nWorkDTSize, nullptr );
220 
221 #if !defined(JPEG_LIB_MK1_OR_12BIT)
222             /* Repeat the last pixel till the end of the line */
223             /* to minimize discontinuity */
224             if (nBlockXSizeToRead < nBlockXSize)
225             {
226                 for (int iBand = 0; iBand < nBands; iBand++)
227                 {
228                     GByte bVal = pabyScanline[nBands * (nBlockXSizeToRead - 1) + iBand];
229                     for(int iX = nBlockXSizeToRead; iX < nBlockXSize; iX ++)
230                     {
231                         pabyScanline[nBands * iX + iBand ] = bVal;
232                     }
233                 }
234             }
235 #endif
236         }
237 
238 #if defined(JPEG_LIB_MK1_OR_12BIT)
239         // clamp 16bit values to 12bit.
240         if( eDT == GDT_UInt16 )
241         {
242             GUInt16 *panScanline = reinterpret_cast<GUInt16 *>( pabyScanline );
243 
244             for( int iPixel = 0; iPixel < nXSize*nBands; iPixel++ )
245             {
246                 if( panScanline[iPixel] > 4095 )
247                 {
248                     panScanline[iPixel] = 4095;
249                     if( !bClipWarn )
250                     {
251                         bClipWarn = true;
252                         CPLError( CE_Warning, CPLE_AppDefined,
253                                   "One or more pixels clipped to fit 12bit domain for jpeg output." );
254                     }
255                 }
256             }
257         }
258 #endif
259 
260         JSAMPLE *ppSamples = reinterpret_cast<JSAMPLE *>( pabyScanline );
261 
262         if( eErr == CE_None )
263             jpeg_write_scanlines( &sCInfo, &ppSamples, 1 );
264 
265         double nCurPixels =
266             static_cast<double>( nBlockYOff ) * nBlockYSize * nXSize +
267             static_cast<double>( nBlockXOff ) * nBlockYSize * nBlockXSize +
268             (iLine + 1) * nBlockXSizeToRead;
269         if( eErr == CE_None
270             && !pfnProgress( nCurPixels / nTotalPixels, nullptr, pProgressData ) )
271         {
272             eErr = CE_Failure;
273             CPLError( CE_Failure, CPLE_UserInterrupt,
274                       "User terminated CreateCopy()" );
275         }
276     }
277 
278 /* -------------------------------------------------------------------- */
279 /*      Cleanup and close.                                              */
280 /* -------------------------------------------------------------------- */
281     CPLFree( pabyScanline );
282 
283     if( eErr == CE_None )
284         jpeg_finish_compress( &sCInfo );
285     jpeg_destroy_compress( &sCInfo );
286 
287     return eErr == CE_None;
288 }
289 #endif /* def JPEG_SUPPORTED */
290