LCOV - code coverage report
Current view: top level - frmts/heif - heifdatasetcreatecopy.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 72 122 59.0 %
Date: 2026-01-16 04:37:55 Functions: 4 5 80.0 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  HEIF Driver
       4             :  * Author:   Brad Hards <bradh@frogmouth.net>
       5             :  *
       6             :  ******************************************************************************
       7             :  * Copyright (c) 2024, Brad Hards <bradh@frogmouth.net>
       8             :  *
       9             :  * SPDX-License-Identifier: MIT
      10             :  ****************************************************************************/
      11             : 
      12             : #include "heifdataset.h"
      13             : #include <algorithm>
      14             : #include <cstddef>
      15             : #include <cstdint>
      16             : #include <cstring>
      17             : #include <iostream>
      18             : #include <memory>
      19             : #include <vector>
      20             : 
      21             : #include "include_libheif.h"
      22             : 
      23             : #include "cpl_error.h"
      24             : 
      25             : #ifdef HAS_CUSTOM_FILE_WRITER
      26             : 
      27             : // Same default as libheif encoder example
      28             : constexpr int DEFAULT_QUALITY = 50;
      29             : 
      30           5 : static CPLErr mapColorInterpretation(GDALColorInterp colourInterpretation,
      31             :                                      heif_channel *channel)
      32             : {
      33           5 :     switch (colourInterpretation)
      34             :     {
      35           2 :         case GCI_GrayIndex:
      36           2 :             *channel = heif_channel_Y;
      37           2 :             return CE_None;
      38           0 :         case GCI_RedBand:
      39           0 :             *channel = heif_channel_R;
      40           0 :             return CE_None;
      41           0 :         case GCI_GreenBand:
      42           0 :             *channel = heif_channel_G;
      43           0 :             return CE_None;
      44           0 :         case GCI_BlueBand:
      45           0 :             *channel = heif_channel_B;
      46           0 :             return CE_None;
      47           0 :         case GCI_AlphaBand:
      48           0 :             *channel = heif_channel_Alpha;
      49           0 :             return CE_None;
      50           3 :         default:
      51           3 :             return CE_Failure;
      52             :     }
      53             : }
      54             : 
      55          15 : static heif_compression_format getCompressionType(CSLConstList papszOptions)
      56             : {
      57          15 :     const char *pszValue = CSLFetchNameValue(papszOptions, "CODEC");
      58          15 :     if (pszValue == nullptr)
      59             :     {
      60          15 :         return heif_compression_HEVC;
      61             :     }
      62           0 :     if (strcmp(pszValue, "HEVC") == 0)
      63             :     {
      64           0 :         return heif_compression_HEVC;
      65             :     }
      66             : #if LIBHEIF_HAVE_VERSION(1, 7, 0)
      67             :     if (strcmp(pszValue, "AV1") == 0)
      68             :     {
      69             :         return heif_compression_AV1;
      70             :     }
      71             : #endif
      72             : #if LIBHEIF_HAVE_VERSION(1, 17, 0)
      73             :     if (strcmp(pszValue, "JPEG") == 0)
      74             :     {
      75             :         return heif_compression_JPEG;
      76             :     }
      77             : #endif
      78             : #if LIBHEIF_HAVE_VERSION(1, 17, 0)
      79             :     if (strcmp(pszValue, "JPEG2000") == 0)
      80             :     {
      81             :         return heif_compression_JPEG2000;
      82             :     }
      83             : #endif
      84             : #if LIBHEIF_HAVE_VERSION(1, 16, 0)
      85             :     if (strcmp(pszValue, "UNCOMPRESSED") == 0)
      86             :     {
      87             :         return heif_compression_uncompressed;
      88             :     }
      89             : #endif
      90             : #if LIBHEIF_HAVE_VERSION(1, 18, 0)
      91             :     if (strcmp(pszValue, "VVC") == 0)
      92             :     {
      93             :         return heif_compression_VVC;
      94             :     }
      95             : #endif
      96           0 :     CPLError(CE_Warning, CPLE_IllegalArg,
      97             :              "CODEC=%s value not recognised, ignoring.", pszValue);
      98           0 :     return heif_compression_HEVC;
      99             : }
     100             : 
     101          15 : static void setEncoderParameters(heif_encoder *encoder,
     102             :                                  CSLConstList papszOptions)
     103             : {
     104          15 :     const char *pszValue = CSLFetchNameValue(papszOptions, "QUALITY");
     105          15 :     int nQuality = DEFAULT_QUALITY;
     106          15 :     if (pszValue != nullptr)
     107             :     {
     108           0 :         nQuality = atoi(pszValue);
     109           0 :         if ((nQuality < 0) || (nQuality > 100))
     110             :         {
     111           0 :             CPLError(CE_Warning, CPLE_IllegalArg,
     112             :                      "QUALITY=%s value not recognised, ignoring.", pszValue);
     113           0 :             nQuality = DEFAULT_QUALITY;
     114             :         }
     115             :     }
     116          15 :     heif_encoder_set_lossy_quality(encoder, nQuality);
     117          15 : }
     118             : 
     119           0 : heif_error GDALHEIFDataset::VFS_WriterCallback(struct heif_context *,
     120             :                                                const void *data, size_t size,
     121             :                                                void *userdata)
     122             : {
     123           0 :     VSILFILE *fp = static_cast<VSILFILE *>(userdata);
     124           0 :     size_t bytesWritten = VSIFWriteL(data, 1, size, fp);
     125             :     heif_error result;
     126           0 :     if (bytesWritten == size)
     127             :     {
     128           0 :         result.code = heif_error_Ok;
     129           0 :         result.subcode = heif_suberror_Unspecified;
     130           0 :         result.message = "Success";
     131             :     }
     132             :     else
     133             :     {
     134           0 :         result.code = heif_error_Encoding_error;
     135           0 :         result.subcode = heif_suberror_Cannot_write_output_data;
     136           0 :         result.message = "Not all data written";
     137             :     }
     138           0 :     return result;
     139             : }
     140             : 
     141             : /************************************************************************/
     142             : /*                             CreateCopy()                             */
     143             : /************************************************************************/
     144             : GDALDataset *
     145          18 : GDALHEIFDataset::CreateCopy(const char *pszFilename, GDALDataset *poSrcDS, int,
     146             :                             CPL_UNUSED char **papszOptions,
     147             :                             CPL_UNUSED GDALProgressFunc pfnProgress,
     148             :                             CPL_UNUSED void *pProgressData)
     149             : {
     150          18 :     if (pfnProgress == nullptr)
     151           0 :         pfnProgress = GDALDummyProgress;
     152             : 
     153          18 :     int nBands = poSrcDS->GetRasterCount();
     154          18 :     if ((nBands != 1) && (nBands != 3) && (nBands != 4))
     155             :     {
     156           3 :         CPLError(CE_Failure, CPLE_NotSupported,
     157             :                  "Driver only supports source dataset with 1, 3 or 4 bands.");
     158           3 :         return nullptr;
     159             :     }
     160             :     // TODO: more sanity checks
     161             : 
     162          15 :     heif_context *ctx = heif_context_alloc();
     163             :     heif_encoder *encoder;
     164          15 :     heif_compression_format codec = getCompressionType(papszOptions);
     165             :     struct heif_error err;
     166          15 :     err = heif_context_get_encoder_for_format(ctx, codec, &encoder);
     167          15 :     if (err.code)
     168             :     {
     169           0 :         heif_context_free(ctx);
     170             :         // TODO: get the error message and printf
     171           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     172             :                  "Failed to create libheif encoder.");
     173           0 :         return nullptr;
     174             :     }
     175             : 
     176          15 :     setEncoderParameters(encoder, papszOptions);
     177             : 
     178             :     heif_image *image;
     179          15 :     heif_colorspace colorspace = heif_colorspace_RGB;
     180          15 :     heif_chroma chroma = heif_chroma_444;
     181          15 :     const int width = poSrcDS->GetRasterXSize();
     182          15 :     const int height = poSrcDS->GetRasterYSize();
     183             : 
     184          15 :     if (nBands == 1)
     185             :     {
     186          13 :         colorspace = heif_colorspace_monochrome;
     187          13 :         chroma = heif_chroma_monochrome;
     188             :     }
     189          15 :     err = heif_image_create(width, height, colorspace, chroma, &image);
     190          15 :     if (err.code)
     191             :     {
     192           0 :         heif_encoder_release(encoder);
     193           0 :         heif_context_free(ctx);
     194           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     195             :                  "Failed to create libheif input image.\n");
     196           0 :         return nullptr;
     197             :     }
     198             : 
     199          16 :     for (auto &&poBand : poSrcDS->GetBands())
     200             :     {
     201          15 :         if (poBand->GetRasterDataType() != GDT_UInt8)
     202             :         {
     203          10 :             heif_image_release(image);
     204          10 :             heif_encoder_release(encoder);
     205          10 :             heif_context_free(ctx);
     206          10 :             CPLError(CE_Failure, CPLE_AppDefined, "Unsupported data type.");
     207          14 :             return nullptr;
     208             :         }
     209             :         heif_channel channel;
     210           5 :         GDALColorInterp ci = poBand->GetColorInterpretation();
     211           5 :         auto mapError = mapColorInterpretation(ci, &channel);
     212           5 :         if (mapError != CE_None)
     213             :         {
     214           3 :             heif_image_release(image);
     215           3 :             heif_encoder_release(encoder);
     216           3 :             heif_context_free(ctx);
     217           3 :             CPLError(CE_Failure, CPLE_NotSupported,
     218             :                      "Unsupported Color Interpretation %d.", ci);
     219           3 :             return nullptr;
     220             :         }
     221           2 :         err = heif_image_add_plane(image, channel, width, height, 8);
     222           2 :         if (err.code)
     223             :         {
     224           0 :             heif_image_release(image);
     225           0 :             heif_encoder_release(encoder);
     226           0 :             heif_context_free(ctx);
     227           0 :             CPLError(CE_Failure, CPLE_AppDefined,
     228             :                      "Failed to add image plane to libheif input image.");
     229           0 :             return nullptr;
     230             :         }
     231             :         int stride;
     232           2 :         uint8_t *p = heif_image_get_plane(image, channel, &stride);
     233           2 :         auto eErr = poBand->RasterIO(GF_Read, 0, 0, width, height, p, width,
     234             :                                      height, GDT_UInt8, 0, stride, nullptr);
     235             : 
     236           2 :         if (eErr != CE_None)
     237             :         {
     238           1 :             heif_image_release(image);
     239           1 :             heif_encoder_release(encoder);
     240           1 :             heif_context_free(ctx);
     241           1 :             return nullptr;
     242             :         }
     243             :     }
     244             : 
     245             :     // TODO: set options based on creation options
     246           1 :     heif_encoding_options *encoding_options = nullptr;
     247             : 
     248             :     heif_image_handle *out_image_handle;
     249             : 
     250             :     heif_context_encode_image(ctx, image, encoder, encoding_options,
     251           1 :                               &out_image_handle);
     252             : 
     253           1 :     heif_image_release(image);
     254             : 
     255             :     // TODO: set properties on output image
     256           1 :     heif_image_handle_release(out_image_handle);
     257           1 :     heif_encoding_options_free(encoding_options);
     258           1 :     heif_encoder_release(encoder);
     259             : 
     260           1 :     VSILFILE *fp = VSIFOpenL(pszFilename, "wb");
     261           1 :     if (fp == nullptr)
     262             :     {
     263           1 :         ReportError(pszFilename, CE_Failure, CPLE_OpenFailed,
     264             :                     "Unable to create file.");
     265           1 :         heif_context_free(ctx);
     266           1 :         return nullptr;
     267             :     }
     268             :     heif_writer writer;
     269           0 :     writer.writer_api_version = 1;
     270           0 :     writer.write = VFS_WriterCallback;
     271           0 :     heif_context_write(ctx, &writer, fp);
     272           0 :     VSIFCloseL(fp);
     273             : 
     274           0 :     heif_context_free(ctx);
     275             : 
     276           0 :     return GDALDataset::Open(pszFilename);
     277             : }
     278             : #endif

Generated by: LCOV version 1.14