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
|