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

Generated by: LCOV version 1.14