LCOV - code coverage report
Current view: top level - frmts/nitf - nitfwritejpeg.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 86 90 95.6 %
Date: 2025-06-19 12:30:01 Functions: 2 2 100.0 %

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

Generated by: LCOV version 1.14