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