Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: HEIF read-only Driver
4 : * Author: Even Rouault <even.rouault at spatialys.com>
5 : *
6 : ******************************************************************************
7 : * Copyright (c) 2020-2025, Even Rouault <even.rouault at spatialys.com>
8 : *
9 : * SPDX-License-Identifier: MIT
10 : ****************************************************************************/
11 :
12 : #include "heifdataset.h"
13 : #include "gdal_frmts.h"
14 :
15 : #include "cpl_vsi_virtual.h"
16 :
17 : #include <algorithm>
18 : #include <cinttypes>
19 :
20 : /************************************************************************/
21 : /* GDALHEIFRasterBand */
22 : /************************************************************************/
23 :
24 : class GDALHEIFRasterBand final : public GDALPamRasterBand
25 : {
26 : protected:
27 : CPLErr IReadBlock(int, int, void *) override;
28 :
29 : public:
30 : GDALHEIFRasterBand(GDALHEIFDataset *poDSIn, int nBandIn);
31 :
32 6 : GDALColorInterp GetColorInterpretation() override
33 : {
34 6 : return static_cast<GDALColorInterp>(GCI_RedBand + nBand - 1);
35 : }
36 :
37 7 : int GetOverviewCount() override
38 : {
39 7 : GDALHEIFDataset *poGDS = static_cast<GDALHEIFDataset *>(poDS);
40 7 : return static_cast<int>(poGDS->m_apoOvrDS.size());
41 : }
42 :
43 4 : GDALRasterBand *GetOverview(int idx) override
44 : {
45 4 : if (idx < 0 || idx >= GetOverviewCount())
46 2 : return nullptr;
47 2 : GDALHEIFDataset *poGDS = static_cast<GDALHEIFDataset *>(poDS);
48 2 : return poGDS->m_apoOvrDS[idx]->GetRasterBand(nBand);
49 : }
50 : };
51 :
52 : /************************************************************************/
53 : /* GDALHEIFDataset() */
54 : /************************************************************************/
55 :
56 18 : GDALHEIFDataset::GDALHEIFDataset() : m_hCtxt(heif_context_alloc())
57 :
58 : {
59 : #ifdef HAS_CUSTOM_FILE_READER
60 18 : m_oReader.reader_api_version = 1;
61 18 : m_oReader.get_position = GetPositionCbk;
62 18 : m_oReader.read = ReadCbk;
63 18 : m_oReader.seek = SeekCbk;
64 18 : m_oReader.wait_for_file_size = WaitForFileSizeCbk;
65 : #endif
66 18 : }
67 :
68 : /************************************************************************/
69 : /* ~GDALHEIFDataset() */
70 : /************************************************************************/
71 :
72 36 : GDALHEIFDataset::~GDALHEIFDataset()
73 : {
74 18 : GDALHEIFDataset::Close();
75 36 : }
76 :
77 : /************************************************************************/
78 : /* Close() */
79 : /************************************************************************/
80 :
81 28 : CPLErr GDALHEIFDataset::Close()
82 : {
83 28 : CPLErr eErr = CE_None;
84 :
85 28 : if (nOpenFlags != OPEN_FLAGS_CLOSED)
86 : {
87 18 : if (m_hCtxt)
88 18 : heif_context_free(m_hCtxt);
89 18 : m_hCtxt = nullptr;
90 :
91 : #ifdef HAS_CUSTOM_FILE_READER
92 18 : if (m_fpL)
93 11 : VSIFCloseL(m_fpL);
94 18 : m_fpL = nullptr;
95 : #endif
96 :
97 : #ifndef LIBHEIF_SUPPORTS_TILES
98 18 : if (m_hImage)
99 6 : heif_image_release(m_hImage);
100 18 : m_hImage = nullptr;
101 : #endif
102 :
103 18 : if (m_hImageHandle)
104 12 : heif_image_handle_release(m_hImageHandle);
105 18 : m_hImageHandle = nullptr;
106 :
107 18 : eErr = GDALPamDataset::Close();
108 : }
109 28 : return eErr;
110 : }
111 :
112 : #ifdef HAS_CUSTOM_FILE_READER
113 :
114 : /************************************************************************/
115 : /* GetPositionCbk() */
116 : /************************************************************************/
117 :
118 459 : int64_t GDALHEIFDataset::GetPositionCbk(void *userdata)
119 : {
120 459 : GDALHEIFDataset *poThis = static_cast<GDALHEIFDataset *>(userdata);
121 459 : return static_cast<int64_t>(VSIFTellL(poThis->m_fpL));
122 : }
123 :
124 : /************************************************************************/
125 : /* ReadCbk() */
126 : /************************************************************************/
127 :
128 2374 : int GDALHEIFDataset::ReadCbk(void *data, size_t size, void *userdata)
129 : {
130 2374 : GDALHEIFDataset *poThis = static_cast<GDALHEIFDataset *>(userdata);
131 :
132 2374 : const uint64_t nCurPos = poThis->m_fpL->Tell();
133 :
134 : #ifdef DEBUG_VERBOSE
135 : CPLDebugOnly("HEIF",
136 : "ReadCbk["
137 : "%" PRIu64 ","
138 : "%" PRIu64 "[",
139 : nCurPos, nCurPos + size);
140 : #endif
141 :
142 2374 : if (nCurPos >= poThis->m_nAdviseReadStartPos &&
143 2374 : nCurPos + size <=
144 2374 : poThis->m_nAdviseReadStartPos + poThis->m_nAdviseReadSize)
145 : {
146 0 : const size_t nRead = poThis->m_fpL->PRead(data, size, nCurPos);
147 0 : poThis->m_fpL->Seek(nCurPos + nRead, SEEK_SET);
148 0 : return nRead == size ? 0 : -1;
149 : }
150 :
151 2374 : return VSIFReadL(data, 1, size, poThis->m_fpL) == size ? 0 : -1;
152 : }
153 :
154 : /************************************************************************/
155 : /* SeekCbk() */
156 : /************************************************************************/
157 :
158 49 : int GDALHEIFDataset::SeekCbk(int64_t position, void *userdata)
159 : {
160 49 : GDALHEIFDataset *poThis = static_cast<GDALHEIFDataset *>(userdata);
161 49 : return VSIFSeekL(poThis->m_fpL, static_cast<vsi_l_offset>(position),
162 49 : SEEK_SET);
163 : }
164 :
165 : /************************************************************************/
166 : /* WaitForFileSizeCbk() */
167 : /************************************************************************/
168 :
169 : enum heif_reader_grow_status
170 486 : GDALHEIFDataset::WaitForFileSizeCbk(int64_t target_size, void *userdata)
171 : {
172 486 : GDALHEIFDataset *poThis = static_cast<GDALHEIFDataset *>(userdata);
173 486 : if (target_size > static_cast<int64_t>(poThis->m_nSize))
174 11 : return heif_reader_grow_status_size_beyond_eof;
175 475 : return heif_reader_grow_status_size_reached;
176 : }
177 :
178 : /************************************************************************/
179 : /* RequestRangeCbk() */
180 : /************************************************************************/
181 :
182 : #if LIBHEIF_NUMERIC_VERSION >= BUILD_LIBHEIF_VERSION(1, 19, 0)
183 : /* static */
184 : struct heif_reader_range_request_result
185 : GDALHEIFDataset::RequestRangeCbk(uint64_t start_pos, uint64_t end_pos,
186 : void *userdata)
187 : {
188 : GDALHEIFDataset *poThis = static_cast<GDALHEIFDataset *>(userdata);
189 :
190 : heif_reader_range_request_result result;
191 :
192 : #ifdef DEBUG_VERBOSE
193 : CPLDebugOnly("HEIF",
194 : "RequestRangeCbk["
195 : "%" PRIu64 ","
196 : "%" PRIu64 "[",
197 : start_pos, end_pos);
198 : #endif
199 :
200 : // Somewhat arbitrary. Corresponds to the default chunk size of /vsicurl
201 : const size_t MINIMUM_RANGE_SIZE = 16384;
202 :
203 : if (poThis->m_fpL->HasPRead() && end_pos > start_pos + MINIMUM_RANGE_SIZE &&
204 : poThis->m_fpL->GetAdviseReadTotalBytesLimit() > 0)
205 : {
206 : const vsi_l_offset nOffset = start_pos;
207 : const size_t nSize = static_cast<size_t>(
208 : std::min<uint64_t>(poThis->m_fpL->GetAdviseReadTotalBytesLimit(),
209 : end_pos - start_pos));
210 : poThis->m_fpL->AdviseRead(1, &nOffset, &nSize);
211 : poThis->m_nAdviseReadStartPos = nOffset;
212 : poThis->m_nAdviseReadSize = nSize;
213 : }
214 :
215 : if (end_pos >= poThis->m_nSize)
216 : {
217 : result.status = heif_reader_grow_status_size_beyond_eof;
218 : }
219 : else
220 : {
221 : result.status = heif_reader_grow_status_size_reached;
222 : }
223 :
224 : result.range_end = poThis->m_nSize;
225 : result.reader_error_code = 0;
226 : result.reader_error_msg = nullptr;
227 : return result;
228 : }
229 : #endif
230 :
231 : #endif // HAS_CUSTOM_FILE_READER
232 :
233 : /************************************************************************/
234 : /* Init() */
235 : /************************************************************************/
236 :
237 16 : bool GDALHEIFDataset::Init(GDALOpenInfo *poOpenInfo)
238 : {
239 32 : CPLString osFilename(poOpenInfo->pszFilename);
240 : #ifdef HAS_CUSTOM_FILE_READER
241 : VSILFILE *fpL;
242 : #endif
243 16 : int iPart = 0;
244 16 : if (STARTS_WITH_CI(poOpenInfo->pszFilename, "HEIF:"))
245 : {
246 9 : const char *pszPartPos = poOpenInfo->pszFilename + strlen("HEIF:");
247 9 : const char *pszNextColumn = strchr(pszPartPos, ':');
248 9 : if (pszNextColumn == nullptr)
249 2 : return false;
250 7 : iPart = atoi(pszPartPos);
251 7 : if (iPart <= 0)
252 1 : return false;
253 6 : osFilename = pszNextColumn + 1;
254 : #ifdef HAS_CUSTOM_FILE_READER
255 6 : fpL = VSIFOpenL(osFilename, "rb");
256 6 : if (fpL == nullptr)
257 2 : return false;
258 : #endif
259 : }
260 : else
261 : {
262 : #ifdef HAS_CUSTOM_FILE_READER
263 7 : fpL = poOpenInfo->fpL;
264 7 : poOpenInfo->fpL = nullptr;
265 : #endif
266 : }
267 :
268 : #ifdef HAS_CUSTOM_FILE_READER
269 :
270 : #if LIBHEIF_NUMERIC_VERSION >= BUILD_LIBHEIF_VERSION(1, 19, 0)
271 : m_oReader.reader_api_version = 2;
272 : #else
273 11 : m_oReader.reader_api_version = 1;
274 : #endif
275 11 : m_oReader.get_position = GetPositionCbk;
276 11 : m_oReader.read = ReadCbk;
277 11 : m_oReader.seek = SeekCbk;
278 11 : m_oReader.wait_for_file_size = WaitForFileSizeCbk;
279 : #if LIBHEIF_NUMERIC_VERSION >= BUILD_LIBHEIF_VERSION(1, 19, 0)
280 : m_oReader.request_range = RequestRangeCbk;
281 : #endif
282 11 : m_fpL = fpL;
283 :
284 11 : VSIFSeekL(m_fpL, 0, SEEK_END);
285 11 : m_nSize = VSIFTellL(m_fpL);
286 11 : VSIFSeekL(m_fpL, 0, SEEK_SET);
287 :
288 : auto err =
289 11 : heif_context_read_from_reader(m_hCtxt, &m_oReader, this, nullptr);
290 11 : if (err.code != heif_error_Ok)
291 : {
292 0 : CPLError(CE_Failure, CPLE_AppDefined, "%s",
293 0 : err.message ? err.message : "Cannot open file");
294 0 : return false;
295 : }
296 : #else
297 : auto err =
298 : heif_context_read_from_file(m_hCtxt, osFilename.c_str(), nullptr);
299 : if (err.code != heif_error_Ok)
300 : {
301 : CPLError(CE_Failure, CPLE_AppDefined, "%s",
302 : err.message ? err.message : "Cannot open file");
303 : return false;
304 : }
305 : #endif
306 :
307 : const int nSubdatasets =
308 11 : heif_context_get_number_of_top_level_images(m_hCtxt);
309 11 : if (iPart == 0)
310 : {
311 7 : if (nSubdatasets > 1)
312 : {
313 2 : CPLStringList aosSubDS;
314 3 : for (int i = 0; i < nSubdatasets; i++)
315 : {
316 : aosSubDS.SetNameValue(
317 : CPLSPrintf("SUBDATASET_%d_NAME", i + 1),
318 2 : CPLSPrintf("HEIF:%d:%s", i + 1, poOpenInfo->pszFilename));
319 : aosSubDS.SetNameValue(CPLSPrintf("SUBDATASET_%d_DESC", i + 1),
320 2 : CPLSPrintf("Subdataset %d", i + 1));
321 : }
322 1 : GDALDataset::SetMetadata(aosSubDS.List(), "SUBDATASETS");
323 : }
324 : }
325 4 : else if (iPart > nSubdatasets)
326 : {
327 1 : CPLError(CE_Failure, CPLE_AppDefined,
328 : "Invalid image part number. Maximum allowed is %d",
329 : nSubdatasets);
330 1 : return false;
331 : }
332 : else
333 : {
334 3 : iPart--;
335 : }
336 20 : std::vector<heif_item_id> idArray(nSubdatasets);
337 10 : heif_context_get_list_of_top_level_image_IDs(m_hCtxt, &idArray[0],
338 : nSubdatasets);
339 10 : const auto itemId = idArray[iPart];
340 :
341 10 : err = heif_context_get_image_handle(m_hCtxt, itemId, &m_hImageHandle);
342 10 : if (err.code != heif_error_Ok)
343 : {
344 0 : CPLError(CE_Failure, CPLE_AppDefined, "%s",
345 0 : err.message ? err.message : "Cannot open image");
346 0 : return false;
347 : }
348 :
349 : #ifdef LIBHEIF_SUPPORTS_TILES
350 : err = heif_image_handle_get_image_tiling(m_hImageHandle, true, &m_tiling);
351 : if (err.code != heif_error_Ok)
352 : {
353 : CPLError(CE_Failure, CPLE_AppDefined, "%s",
354 : err.message ? err.message : "Cannot get image tiling");
355 : return false;
356 : }
357 : #endif
358 :
359 10 : nRasterXSize = heif_image_handle_get_width(m_hImageHandle);
360 10 : nRasterYSize = heif_image_handle_get_height(m_hImageHandle);
361 : const int l_nBands =
362 10 : 3 + (heif_image_handle_has_alpha_channel(m_hImageHandle) ? 1 : 0);
363 43 : for (int i = 0; i < l_nBands; i++)
364 : {
365 33 : SetBand(i + 1, new GDALHEIFRasterBand(this, i + 1));
366 : }
367 :
368 10 : ReadMetadata();
369 :
370 10 : OpenThumbnails();
371 :
372 10 : if (poOpenInfo->nHeaderBytes > 12 &&
373 7 : memcmp(poOpenInfo->pabyHeader + 4, "ftypavif", 8) == 0)
374 : {
375 0 : poDriver = GetGDALDriverManager()->GetDriverByName("AVIF_HEIF");
376 : }
377 :
378 : // Initialize any PAM information.
379 10 : if (nSubdatasets > 1)
380 : {
381 4 : SetSubdatasetName(CPLSPrintf("%d", iPart + 1));
382 4 : SetPhysicalFilename(osFilename.c_str());
383 : }
384 10 : SetDescription(poOpenInfo->pszFilename);
385 10 : TryLoadXML(poOpenInfo->GetSiblingFiles());
386 :
387 10 : return true;
388 : }
389 :
390 : /************************************************************************/
391 : /* ReadMetadata() */
392 : /************************************************************************/
393 :
394 10 : void GDALHEIFDataset::ReadMetadata()
395 : {
396 : #if LIBHEIF_NUMERIC_VERSION >= BUILD_LIBHEIF_VERSION(1, 19, 0)
397 : processProperties();
398 : ReadUserDescription();
399 : #endif
400 20 : const int nMDBlocks = heif_image_handle_get_number_of_metadata_blocks(
401 10 : m_hImageHandle, nullptr);
402 10 : if (nMDBlocks <= 0)
403 7 : return;
404 :
405 6 : std::vector<heif_item_id> idsMDBlock(nMDBlocks);
406 3 : heif_image_handle_get_list_of_metadata_block_IDs(m_hImageHandle, nullptr,
407 3 : &idsMDBlock[0], nMDBlocks);
408 8 : for (const auto &id : idsMDBlock)
409 : {
410 : const char *pszType =
411 5 : heif_image_handle_get_metadata_type(m_hImageHandle, id);
412 : const size_t nCount =
413 5 : heif_image_handle_get_metadata_size(m_hImageHandle, id);
414 5 : if (pszType && EQUAL(pszType, "Exif") && nCount > 8 &&
415 : nCount < 1024 * 1024)
416 : {
417 2 : std::vector<GByte> data(nCount);
418 2 : heif_image_handle_get_metadata(m_hImageHandle, id, &data[0]);
419 :
420 : // There are 2 variants
421 : // - the one from
422 : // https://github.com/nokiatech/heif_conformance/blob/master/conformance_files/C034.heic
423 : // where the TIFF file immediately starts
424 : // - the one found in iPhone files (among others), where there
425 : // is first a 4-byte big-endian offset (after those initial 4
426 : // bytes) that points to the TIFF file, with a "Exif\0\0" just
427 : // before
428 2 : unsigned nTIFFFileOffset = 0;
429 4 : if (memcmp(&data[0], "II\x2a\x00", 4) == 0 ||
430 2 : memcmp(&data[0], "MM\x00\x2a", 4) == 0)
431 : {
432 : // do nothing
433 : }
434 : else
435 : {
436 : unsigned nOffset;
437 2 : memcpy(&nOffset, &data[0], 4);
438 2 : CPL_MSBPTR32(&nOffset);
439 4 : if (nOffset < nCount - 8 &&
440 2 : (memcmp(&data[nOffset + 4], "II\x2a\x00", 4) == 0 ||
441 1 : memcmp(&data[nOffset + 4], "MM\x00\x2a", 4) == 0))
442 : {
443 2 : nTIFFFileOffset = nOffset + 4;
444 : }
445 : else
446 : {
447 0 : continue;
448 : }
449 : }
450 :
451 : const CPLString osTempFile(
452 4 : VSIMemGenerateHiddenFilename("heif_exif.tif"));
453 : VSILFILE *fpTemp =
454 2 : VSIFileFromMemBuffer(osTempFile, &data[nTIFFFileOffset],
455 2 : nCount - nTIFFFileOffset, FALSE);
456 2 : char **papszMD = nullptr;
457 :
458 3 : const bool bLittleEndianTIFF = data[nTIFFFileOffset] == 'I' &&
459 1 : data[nTIFFFileOffset + 1] == 'I';
460 2 : const bool bLSBPlatform = CPL_IS_LSB != 0;
461 2 : const bool bSwabflag = bLittleEndianTIFF != bLSBPlatform;
462 :
463 : int nTIFFDirOff;
464 2 : memcpy(&nTIFFDirOff, &data[nTIFFFileOffset + 4], 4);
465 2 : if (bSwabflag)
466 : {
467 1 : CPL_SWAP32PTR(&nTIFFDirOff);
468 : }
469 2 : int nExifOffset = 0;
470 2 : int nInterOffset = 0;
471 2 : int nGPSOffset = 0;
472 2 : EXIFExtractMetadata(papszMD, fpTemp, nTIFFDirOff, bSwabflag, 0,
473 : nExifOffset, nInterOffset, nGPSOffset);
474 2 : if (nExifOffset > 0)
475 : {
476 2 : EXIFExtractMetadata(papszMD, fpTemp, nExifOffset, bSwabflag, 0,
477 : nExifOffset, nInterOffset, nGPSOffset);
478 : }
479 2 : if (nGPSOffset > 0)
480 : {
481 1 : EXIFExtractMetadata(papszMD, fpTemp, nGPSOffset, bSwabflag, 0,
482 : nExifOffset, nInterOffset, nGPSOffset);
483 : }
484 2 : if (nInterOffset > 0)
485 : {
486 0 : EXIFExtractMetadata(papszMD, fpTemp, nInterOffset, bSwabflag, 0,
487 : nExifOffset, nInterOffset, nGPSOffset);
488 : }
489 :
490 2 : if (papszMD)
491 : {
492 2 : GDALDataset::SetMetadata(papszMD, "EXIF");
493 2 : CSLDestroy(papszMD);
494 : }
495 :
496 2 : VSIFCloseL(fpTemp);
497 4 : VSIUnlink(osTempFile);
498 : }
499 3 : else if (pszType && EQUAL(pszType, "mime"))
500 : {
501 : #if LIBHEIF_NUMERIC_VERSION >= BUILD_LIBHEIF_VERSION(1, 2, 0)
502 : const char *pszContentType =
503 3 : heif_image_handle_get_metadata_content_type(m_hImageHandle, id);
504 3 : if (pszContentType &&
505 3 : EQUAL(pszContentType, "application/rdf+xml") &&
506 : #else
507 : if (
508 : #endif
509 3 : nCount > 0 && nCount < 1024 * 1024)
510 : {
511 6 : std::string osXMP;
512 3 : osXMP.resize(nCount);
513 3 : heif_image_handle_get_metadata(m_hImageHandle, id, &osXMP[0]);
514 3 : if (osXMP.find("<?xpacket") != std::string::npos)
515 : {
516 3 : char *apszMDList[2] = {&osXMP[0], nullptr};
517 3 : GDALDataset::SetMetadata(apszMDList, "xml:XMP");
518 : }
519 : }
520 : }
521 : }
522 : }
523 :
524 : #if LIBHEIF_NUMERIC_VERSION >= BUILD_LIBHEIF_VERSION(1, 19, 0)
525 : static bool GetPropertyData(heif_context *m_hCtxt, heif_item_id item_id,
526 : heif_property_id prop_id,
527 : std::shared_ptr<std::vector<GByte>> &data)
528 : {
529 : size_t size;
530 : heif_error err =
531 : heif_item_get_property_raw_size(m_hCtxt, item_id, prop_id, &size);
532 : if (err.code != 0)
533 : {
534 : return false;
535 : }
536 : if (size == 0)
537 : {
538 : return false;
539 : }
540 : data = std::make_shared<std::vector<uint8_t>>(size);
541 : err = heif_item_get_property_raw_data(m_hCtxt, item_id, prop_id,
542 : data->data());
543 : if (err.code != 0)
544 : {
545 : return false;
546 : }
547 : return true;
548 : }
549 :
550 : void GDALHEIFDataset::processProperties()
551 : {
552 : #ifdef __clang__
553 : #pragma clang diagnostic push
554 : #pragma clang diagnostic ignored "-Wold-style-cast"
555 : #endif
556 : constexpr heif_item_property_type TIEP_4CC =
557 : (heif_item_property_type)heif_fourcc('t', 'i', 'e', 'p');
558 : constexpr heif_item_property_type MTXF_4CC =
559 : (heif_item_property_type)heif_fourcc('m', 't', 'x', 'f');
560 : constexpr heif_item_property_type MCRS_4CC =
561 : (heif_item_property_type)heif_fourcc('m', 'c', 'r', 's');
562 : #ifdef __clang__
563 : #pragma clang diagnostic pop
564 : #endif
565 : constexpr int MAX_PROPERTIES_REQUIRED = 50;
566 : heif_property_id prop_ids[MAX_PROPERTIES_REQUIRED];
567 : heif_item_id item_id = heif_image_handle_get_item_id(m_hImageHandle);
568 : int num_props = heif_item_get_properties_of_type(
569 : m_hCtxt, item_id, heif_item_property_type_invalid, &prop_ids[0],
570 : MAX_PROPERTIES_REQUIRED);
571 : for (int i = 0; i < num_props; i++)
572 : {
573 : heif_item_property_type prop_type =
574 : heif_item_get_property_type(m_hCtxt, item_id, prop_ids[i]);
575 : if (prop_type == TIEP_4CC)
576 : {
577 : std::shared_ptr<std::vector<uint8_t>> data;
578 : if (!GetPropertyData(m_hCtxt, item_id, prop_ids[i], data))
579 : {
580 : continue;
581 : }
582 : geoHEIF.addGCPs(data->data(), data->size());
583 : }
584 : else if (prop_type == MTXF_4CC)
585 : {
586 : std::shared_ptr<std::vector<uint8_t>> data;
587 : if (!GetPropertyData(m_hCtxt, item_id, prop_ids[i], data))
588 : {
589 : continue;
590 : }
591 : geoHEIF.setModelTransformation(data->data(), data->size());
592 : }
593 : else if (prop_type == MCRS_4CC)
594 : {
595 : std::shared_ptr<std::vector<uint8_t>> data;
596 : if (!GetPropertyData(m_hCtxt, item_id, prop_ids[i], data))
597 : {
598 : continue;
599 : }
600 : geoHEIF.extractSRS(data->data(), data->size());
601 : }
602 : }
603 : }
604 :
605 : /************************************************************************/
606 : /* ReadUserDescription() */
607 : /************************************************************************/
608 : void GDALHEIFDataset::ReadUserDescription()
609 : {
610 : constexpr int MAX_PROPERTIES = 50;
611 : heif_item_id item_id = heif_image_handle_get_item_id(m_hImageHandle);
612 : heif_property_id properties[MAX_PROPERTIES];
613 : int nProps = heif_item_get_properties_of_type(
614 : m_hCtxt, item_id, heif_item_property_type_user_description, properties,
615 : MAX_PROPERTIES);
616 :
617 : heif_property_user_description *user_description = nullptr;
618 : for (int i = 0; i < nProps; i++)
619 : {
620 : heif_error err = heif_item_get_property_user_description(
621 : m_hCtxt, item_id, properties[i], &user_description);
622 : if (err.code == 0)
623 : {
624 : std::string domain = "DESCRIPTION";
625 : if (strlen(user_description->lang) != 0)
626 : {
627 : domain += "_";
628 : domain += user_description->lang;
629 : }
630 : SetMetadataItem("NAME", user_description->name, domain.c_str());
631 : SetMetadataItem("DESCRIPTION", user_description->description,
632 : domain.c_str());
633 : if (strlen(user_description->tags) != 0)
634 : {
635 : SetMetadataItem("TAGS", user_description->tags, domain.c_str());
636 : }
637 : heif_property_user_description_release(user_description);
638 : }
639 : }
640 : }
641 : #endif
642 :
643 : /************************************************************************/
644 : /* OpenThumbnails() */
645 : /************************************************************************/
646 :
647 10 : void GDALHEIFDataset::OpenThumbnails()
648 : {
649 : int nThumbnails =
650 10 : heif_image_handle_get_number_of_thumbnails(m_hImageHandle);
651 10 : if (nThumbnails <= 0)
652 8 : return;
653 :
654 2 : heif_item_id thumbnailId = 0;
655 2 : heif_image_handle_get_list_of_thumbnail_IDs(m_hImageHandle, &thumbnailId,
656 : 1);
657 2 : heif_image_handle *hThumbnailHandle = nullptr;
658 2 : heif_image_handle_get_thumbnail(m_hImageHandle, thumbnailId,
659 2 : &hThumbnailHandle);
660 2 : if (hThumbnailHandle == nullptr)
661 0 : return;
662 :
663 : const int nThumbnailBands =
664 2 : 3 + (heif_image_handle_has_alpha_channel(hThumbnailHandle) ? 1 : 0);
665 2 : if (nThumbnailBands != nBands)
666 : {
667 0 : heif_image_handle_release(hThumbnailHandle);
668 0 : return;
669 : }
670 : #if LIBHEIF_NUMERIC_VERSION >= BUILD_LIBHEIF_VERSION(1, 4, 0)
671 : const int nBits =
672 2 : heif_image_handle_get_luma_bits_per_pixel(hThumbnailHandle);
673 2 : if (nBits != heif_image_handle_get_luma_bits_per_pixel(m_hImageHandle))
674 : {
675 0 : heif_image_handle_release(hThumbnailHandle);
676 0 : return;
677 : }
678 : #endif
679 :
680 4 : auto poOvrDS = std::make_unique<GDALHEIFDataset>();
681 2 : poOvrDS->m_hImageHandle = hThumbnailHandle;
682 2 : poOvrDS->m_bIsThumbnail = true;
683 2 : poOvrDS->nRasterXSize = heif_image_handle_get_width(hThumbnailHandle);
684 2 : poOvrDS->nRasterYSize = heif_image_handle_get_height(hThumbnailHandle);
685 : #ifdef LIBHEIF_SUPPORTS_TILES
686 : auto err = heif_image_handle_get_image_tiling(hThumbnailHandle, true,
687 : &poOvrDS->m_tiling);
688 : if (err.code != heif_error_Ok)
689 : {
690 : CPLError(CE_Failure, CPLE_AppDefined, "%s",
691 : err.message ? err.message : "Cannot get image tiling");
692 : heif_image_handle_release(hThumbnailHandle);
693 : return;
694 : }
695 : #endif
696 9 : for (int i = 0; i < nBands; i++)
697 : {
698 7 : poOvrDS->SetBand(i + 1, new GDALHEIFRasterBand(poOvrDS.get(), i + 1));
699 : }
700 2 : m_apoOvrDS.push_back(std::move(poOvrDS));
701 : }
702 :
703 : /************************************************************************/
704 : /* HEIFDriverIdentify() */
705 : /************************************************************************/
706 :
707 16 : static int HEIFDriverIdentify(GDALOpenInfo *poOpenInfo)
708 :
709 : {
710 16 : if (STARTS_WITH_CI(poOpenInfo->pszFilename, "HEIF:"))
711 9 : return true;
712 :
713 7 : if (poOpenInfo->nHeaderBytes < 12 || poOpenInfo->fpL == nullptr)
714 0 : return false;
715 :
716 : #if LIBHEIF_NUMERIC_VERSION >= BUILD_LIBHEIF_VERSION(1, 4, 0)
717 : const auto res =
718 7 : heif_check_filetype(poOpenInfo->pabyHeader, poOpenInfo->nHeaderBytes);
719 7 : if (res == heif_filetype_yes_supported)
720 7 : return TRUE;
721 0 : if (res == heif_filetype_maybe)
722 0 : return -1;
723 0 : if (res == heif_filetype_yes_unsupported)
724 : {
725 0 : CPLDebug("HEIF", "HEIF file, but not supported by libheif");
726 : }
727 0 : return FALSE;
728 : #else
729 : // Simplistic test...
730 : const unsigned char abySig1[] = "\x00"
731 : "\x00"
732 : "\x00"
733 : "\x20"
734 : "ftypheic";
735 : const unsigned char abySig2[] = "\x00"
736 : "\x00"
737 : "\x00"
738 : "\x18"
739 : "ftypheic";
740 : const unsigned char abySig3[] = "\x00"
741 : "\x00"
742 : "\x00"
743 : "\x18"
744 : "ftypmif1"
745 : "\x00"
746 : "\x00"
747 : "\x00"
748 : "\x00"
749 : "mif1heic";
750 : return (poOpenInfo->nHeaderBytes >= static_cast<int>(sizeof(abySig1)) &&
751 : memcmp(poOpenInfo->pabyHeader, abySig1, sizeof(abySig1)) == 0) ||
752 : (poOpenInfo->nHeaderBytes >= static_cast<int>(sizeof(abySig2)) &&
753 : memcmp(poOpenInfo->pabyHeader, abySig2, sizeof(abySig2)) == 0) ||
754 : (poOpenInfo->nHeaderBytes >= static_cast<int>(sizeof(abySig3)) &&
755 : memcmp(poOpenInfo->pabyHeader, abySig3, sizeof(abySig3)) == 0);
756 : #endif
757 : }
758 :
759 : /************************************************************************/
760 : /* OpenHEIF() */
761 : /************************************************************************/
762 :
763 16 : GDALDataset *GDALHEIFDataset::OpenHEIF(GDALOpenInfo *poOpenInfo)
764 : {
765 16 : if (!HEIFDriverIdentify(poOpenInfo))
766 : {
767 0 : return nullptr;
768 : }
769 16 : if (poOpenInfo->eAccess == GA_Update)
770 : {
771 0 : CPLError(CE_Failure, CPLE_NotSupported,
772 : "Update of existing HEIF file not supported");
773 0 : return nullptr;
774 : }
775 :
776 32 : auto poDS = std::make_unique<GDALHEIFDataset>();
777 16 : if (!poDS->Init(poOpenInfo))
778 6 : return nullptr;
779 :
780 10 : return poDS.release();
781 : }
782 :
783 : #if LIBHEIF_NUMERIC_VERSION >= BUILD_LIBHEIF_VERSION(1, 12, 0)
784 :
785 : /************************************************************************/
786 : /* HEIFIdentifyOnlyAVIF() */
787 : /************************************************************************/
788 :
789 : static int HEIFIdentifyOnlyAVIF(GDALOpenInfo *poOpenInfo)
790 : {
791 : if (poOpenInfo->nHeaderBytes < 12 || poOpenInfo->fpL == nullptr)
792 : return false;
793 : if (memcmp(poOpenInfo->pabyHeader + 4, "ftypavif", 8) == 0)
794 : return true;
795 : return false;
796 : }
797 :
798 : /************************************************************************/
799 : /* OpenAVIF() */
800 : /************************************************************************/
801 :
802 : GDALDataset *GDALHEIFDataset::OpenAVIF(GDALOpenInfo *poOpenInfo)
803 : {
804 : if (!HEIFIdentifyOnlyAVIF(poOpenInfo))
805 : return nullptr;
806 : if (poOpenInfo->eAccess == GA_Update)
807 : {
808 : CPLError(CE_Failure, CPLE_NotSupported,
809 : "Update of existing AVIF file not supported");
810 : return nullptr;
811 : }
812 :
813 : auto poDS = std::make_unique<GDALHEIFDataset>();
814 : if (!poDS->Init(poOpenInfo))
815 : return nullptr;
816 :
817 : return poDS.release();
818 : }
819 : #endif
820 :
821 : /************************************************************************/
822 : /* GDALHEIFRasterBand() */
823 : /************************************************************************/
824 :
825 40 : GDALHEIFRasterBand::GDALHEIFRasterBand(GDALHEIFDataset *poDSIn, int nBandIn)
826 : {
827 40 : poDS = poDSIn;
828 40 : nBand = nBandIn;
829 :
830 40 : eDataType = GDT_Byte;
831 : #if LIBHEIF_NUMERIC_VERSION >= BUILD_LIBHEIF_VERSION(1, 4, 0)
832 : const int nBits =
833 40 : heif_image_handle_get_luma_bits_per_pixel(poDSIn->m_hImageHandle);
834 40 : if (nBits > 8)
835 : {
836 7 : eDataType = GDT_UInt16;
837 : }
838 40 : if (nBits != 8 && nBits != 16)
839 : {
840 7 : GDALRasterBand::SetMetadataItem("NBITS", CPLSPrintf("%d", nBits),
841 : "IMAGE_STRUCTURE");
842 : }
843 : #endif
844 :
845 : #ifdef LIBHEIF_SUPPORTS_TILES
846 : nBlockXSize = poDSIn->m_tiling.tile_width;
847 : nBlockYSize = poDSIn->m_tiling.tile_height;
848 : #else
849 40 : nBlockXSize = poDS->GetRasterXSize();
850 40 : nBlockYSize = 1;
851 : #endif
852 40 : }
853 :
854 : /************************************************************************/
855 : /* IReadBlock() */
856 : /************************************************************************/
857 : #ifdef LIBHEIF_SUPPORTS_TILES
858 : CPLErr GDALHEIFRasterBand::IReadBlock(int nBlockXOff, int nBlockYOff,
859 : void *pImage)
860 : {
861 : GDALHEIFDataset *poGDS = static_cast<GDALHEIFDataset *>(poDS);
862 : if (poGDS->m_bFailureDecoding)
863 : return CE_Failure;
864 : const int nBands = poGDS->GetRasterCount();
865 : heif_image *hImage = nullptr;
866 : struct heif_decoding_options *decode_options =
867 : heif_decoding_options_alloc();
868 :
869 : auto err = heif_image_handle_decode_image_tile(
870 : poGDS->m_hImageHandle, &hImage, heif_colorspace_RGB,
871 : nBands == 3
872 : ? (eDataType == GDT_UInt16 ?
873 : #if CPL_IS_LSB
874 : heif_chroma_interleaved_RRGGBB_LE
875 : #else
876 : heif_chroma_interleaved_RRGGBB_BE
877 : #endif
878 : : heif_chroma_interleaved_RGB)
879 : : (eDataType == GDT_UInt16 ?
880 : #if CPL_IS_LSB
881 : heif_chroma_interleaved_RRGGBBAA_LE
882 : #else
883 : heif_chroma_interleaved_RRGGBBAA_BE
884 : #endif
885 : : heif_chroma_interleaved_RGBA),
886 : decode_options, nBlockXOff, nBlockYOff);
887 :
888 : if (err.code != heif_error_Ok)
889 : {
890 : CPLError(CE_Failure, CPLE_AppDefined, "%s",
891 : err.message ? err.message : "Cannot decode image");
892 : poGDS->m_bFailureDecoding = true;
893 : heif_decoding_options_free(decode_options);
894 : return CE_Failure;
895 : }
896 : heif_decoding_options_free(decode_options);
897 : int nStride = 0;
898 : const uint8_t *pSrcData = heif_image_get_plane_readonly(
899 : hImage, heif_channel_interleaved, &nStride);
900 : if (eDataType == GDT_Byte)
901 : {
902 : for (int y = 0; y < nBlockYSize; y++)
903 : {
904 : for (int x = 0; x < nBlockXSize; x++)
905 : {
906 : const size_t srcIndex = static_cast<size_t>(y) * nStride +
907 : static_cast<size_t>(x) * nBands +
908 : nBand - 1;
909 : const size_t outIndex =
910 : static_cast<size_t>(y) * nBlockXSize + x;
911 : (static_cast<GByte *>(pImage))[outIndex] = pSrcData[srcIndex];
912 : }
913 : }
914 : }
915 : else
916 : {
917 : for (int y = 0; y < nBlockYSize; y++)
918 : {
919 : for (int x = 0; x < nBlockXSize; x++)
920 : {
921 : const size_t srcIndex = static_cast<size_t>(y) * (nStride / 2) +
922 : static_cast<size_t>(x) * nBands +
923 : nBand - 1;
924 : const size_t outIndex =
925 : static_cast<size_t>(y) * nBlockXSize + x;
926 : (static_cast<GUInt16 *>(pImage))[outIndex] =
927 : (reinterpret_cast<const GUInt16 *>(pSrcData))[srcIndex];
928 : }
929 : }
930 : }
931 : heif_image_release(hImage);
932 : return CE_None;
933 : }
934 : #else
935 544 : CPLErr GDALHEIFRasterBand::IReadBlock(int, int nBlockYOff, void *pImage)
936 : {
937 544 : GDALHEIFDataset *poGDS = static_cast<GDALHEIFDataset *>(poDS);
938 544 : if (poGDS->m_bFailureDecoding)
939 0 : return CE_Failure;
940 544 : const int nBands = poGDS->GetRasterCount();
941 544 : if (poGDS->m_hImage == nullptr)
942 : {
943 : auto err = heif_decode_image(
944 6 : poGDS->m_hImageHandle, &(poGDS->m_hImage), heif_colorspace_RGB,
945 : nBands == 3
946 : ? (
947 : #if LIBHEIF_NUMERIC_VERSION >= BUILD_LIBHEIF_VERSION(1, 4, 0)
948 5 : eDataType == GDT_UInt16 ?
949 : #if CPL_IS_LSB
950 : heif_chroma_interleaved_RRGGBB_LE
951 : #else
952 : heif_chroma_interleaved_RRGGBB_BE
953 : #endif
954 : :
955 : #endif
956 : heif_chroma_interleaved_RGB)
957 : : (
958 : #if LIBHEIF_NUMERIC_VERSION >= BUILD_LIBHEIF_VERSION(1, 4, 0)
959 1 : eDataType == GDT_UInt16
960 1 : ?
961 : #if CPL_IS_LSB
962 : heif_chroma_interleaved_RRGGBBAA_LE
963 : #else
964 : heif_chroma_interleaved_RRGGBBAA_BE
965 : #endif
966 : :
967 : #endif
968 : heif_chroma_interleaved_RGBA),
969 12 : nullptr);
970 6 : if (err.code != heif_error_Ok)
971 : {
972 0 : CPLError(CE_Failure, CPLE_AppDefined, "%s",
973 0 : err.message ? err.message : "Cannot decode image");
974 0 : poGDS->m_bFailureDecoding = true;
975 0 : return CE_Failure;
976 : }
977 12 : const int nBitsPerPixel = heif_image_get_bits_per_pixel(
978 6 : poGDS->m_hImage, heif_channel_interleaved);
979 6 : if (nBitsPerPixel != nBands * GDALGetDataTypeSizeBits(eDataType))
980 : {
981 0 : CPLError(CE_Failure, CPLE_AppDefined,
982 : "Unexpected bits_per_pixel = %d value", nBitsPerPixel);
983 0 : poGDS->m_bFailureDecoding = true;
984 0 : return CE_Failure;
985 : }
986 : }
987 :
988 544 : int nStride = 0;
989 1088 : const uint8_t *pSrcData = heif_image_get_plane_readonly(
990 544 : poGDS->m_hImage, heif_channel_interleaved, &nStride);
991 544 : pSrcData += static_cast<size_t>(nBlockYOff) * nStride;
992 544 : if (eDataType == GDT_Byte)
993 : {
994 25176 : for (int i = 0; i < nBlockXSize; i++)
995 24832 : (static_cast<GByte *>(pImage))[i] =
996 24832 : pSrcData[nBand - 1 + static_cast<size_t>(i) * nBands];
997 : }
998 : else
999 : {
1000 80200 : for (int i = 0; i < nBlockXSize; i++)
1001 80000 : (static_cast<GUInt16 *>(pImage))[i] =
1002 : (reinterpret_cast<const GUInt16 *>(
1003 80000 : pSrcData))[nBand - 1 + static_cast<size_t>(i) * nBands];
1004 : }
1005 :
1006 544 : return CE_None;
1007 : }
1008 : #endif
1009 :
1010 : #if LIBHEIF_NUMERIC_VERSION >= BUILD_LIBHEIF_VERSION(1, 19, 0)
1011 : /************************************************************************/
1012 : /* GetGeoTransform() */
1013 : /************************************************************************/
1014 :
1015 : CPLErr GDALHEIFDataset::GetGeoTransform(GDALGeoTransform >) const
1016 : {
1017 : return geoHEIF.GetGeoTransform(gt);
1018 : }
1019 :
1020 : /************************************************************************/
1021 : /* GetSpatialRef() */
1022 : /************************************************************************/
1023 : const OGRSpatialReference *GDALHEIFDataset::GetSpatialRef() const
1024 : {
1025 : return geoHEIF.GetSpatialRef();
1026 : }
1027 :
1028 : int GDALHEIFDataset::GetGCPCount()
1029 : {
1030 : return geoHEIF.GetGCPCount();
1031 : }
1032 :
1033 : const GDAL_GCP *GDALHEIFDataset::GetGCPs()
1034 : {
1035 : return geoHEIF.GetGCPs();
1036 : }
1037 :
1038 : const OGRSpatialReference *GDALHEIFDataset::GetGCPSpatialRef() const
1039 : {
1040 : return this->GetSpatialRef();
1041 : }
1042 : #endif
1043 :
1044 : /************************************************************************/
1045 : /* GDALRegister_HEIF() */
1046 : /************************************************************************/
1047 :
1048 10 : void GDALRegister_HEIF()
1049 :
1050 : {
1051 10 : if (!GDAL_CHECK_VERSION("HEIF driver"))
1052 0 : return;
1053 :
1054 10 : if (GDALGetDriverByName(DRIVER_NAME) != nullptr)
1055 0 : return;
1056 :
1057 10 : auto poDM = GetGDALDriverManager();
1058 : {
1059 10 : GDALDriver *poDriver = new GDALDriver();
1060 10 : HEIFDriverSetCommonMetadata(poDriver);
1061 :
1062 : #if LIBHEIF_NUMERIC_VERSION >= BUILD_LIBHEIF_VERSION(1, 12, 0)
1063 : if (heif_have_decoder_for_format(heif_compression_AVC))
1064 : {
1065 : poDriver->SetMetadataItem("SUPPORTS_AVC", "YES", "HEIF");
1066 : }
1067 : if (heif_have_encoder_for_format(heif_compression_AVC))
1068 : {
1069 : poDriver->SetMetadataItem("SUPPORTS_AVC_WRITE", "YES", "HEIF");
1070 : }
1071 : // If the AVIF dedicated driver is not available, register an AVIF driver,
1072 : // called AVIF_HEIF, based on libheif, if it has AV1 decoding capabilities.
1073 : if (heif_have_decoder_for_format(heif_compression_AV1))
1074 : {
1075 : poDriver->SetMetadataItem("SUPPORTS_AVIF", "YES", "HEIF");
1076 : poDriver->SetMetadataItem("SUPPORTS_AV1", "YES", "HEIF");
1077 : }
1078 : if (heif_have_encoder_for_format(heif_compression_AV1))
1079 : {
1080 : poDriver->SetMetadataItem("SUPPORTS_AV1_WRITE", "YES", "HEIF");
1081 : }
1082 : if (heif_have_decoder_for_format(heif_compression_HEVC))
1083 : {
1084 : poDriver->SetMetadataItem("SUPPORTS_HEVC", "YES", "HEIF");
1085 : }
1086 : if (heif_have_encoder_for_format(heif_compression_HEVC))
1087 : {
1088 : poDriver->SetMetadataItem("SUPPORTS_HEVC_WRITE", "YES", "HEIF");
1089 : }
1090 : if (heif_have_decoder_for_format(heif_compression_JPEG))
1091 : {
1092 : poDriver->SetMetadataItem("SUPPORTS_JPEG", "YES", "HEIF");
1093 : }
1094 : if (heif_have_encoder_for_format(heif_compression_JPEG))
1095 : {
1096 : poDriver->SetMetadataItem("SUPPORTS_JPEG_WRITE", "YES", "HEIF");
1097 : }
1098 : #if LIBHEIF_NUMERIC_VERSION >= BUILD_LIBHEIF_VERSION(1, 15, 0)
1099 : if (heif_have_decoder_for_format(heif_compression_JPEG2000))
1100 : {
1101 : poDriver->SetMetadataItem("SUPPORTS_JPEG2000", "YES", "HEIF");
1102 : }
1103 : if (heif_have_encoder_for_format(heif_compression_JPEG2000))
1104 : {
1105 : poDriver->SetMetadataItem("SUPPORTS_JPEG2000_WRITE", "YES", "HEIF");
1106 : }
1107 : #endif
1108 : #if LIBHEIF_NUMERIC_VERSION >= BUILD_LIBHEIF_VERSION(1, 18, 0)
1109 : if (heif_have_decoder_for_format(heif_compression_HTJ2K))
1110 : {
1111 : poDriver->SetMetadataItem("SUPPORTS_HTJ2K", "YES", "HEIF");
1112 : }
1113 : if (heif_have_encoder_for_format(heif_compression_HTJ2K))
1114 : {
1115 : poDriver->SetMetadataItem("SUPPORTS_HTJ2K_WRITE", "YES", "HEIF");
1116 : }
1117 : #endif
1118 : #if LIBHEIF_NUMERIC_VERSION >= BUILD_LIBHEIF_VERSION(1, 16, 0)
1119 : if (heif_have_decoder_for_format(heif_compression_uncompressed))
1120 : {
1121 : poDriver->SetMetadataItem("SUPPORTS_UNCOMPRESSED", "YES", "HEIF");
1122 : }
1123 : if (heif_have_encoder_for_format(heif_compression_uncompressed))
1124 : {
1125 : poDriver->SetMetadataItem("SUPPORTS_UNCOMPRESSED_WRITE", "YES",
1126 : "HEIF");
1127 : }
1128 : #endif
1129 : #if LIBHEIF_NUMERIC_VERSION >= BUILD_LIBHEIF_VERSION(1, 15, 0)
1130 : if (heif_have_decoder_for_format(heif_compression_VVC))
1131 : {
1132 : poDriver->SetMetadataItem("SUPPORTS_VVC", "YES", "HEIF");
1133 : }
1134 : if (heif_have_encoder_for_format(heif_compression_VVC))
1135 : {
1136 : poDriver->SetMetadataItem("SUPPORTS_VVC_WRITE", "YES", "HEIF");
1137 : }
1138 : #endif
1139 : #else
1140 : // Anything that old probably supports only HEVC
1141 10 : poDriver->SetMetadataItem("SUPPORTS_HEVC", "YES", "HEIF");
1142 : #endif
1143 : #ifdef LIBHEIF_SUPPORTS_TILES
1144 : poDriver->SetMetadataItem("SUPPORTS_TILES", "YES", "HEIF");
1145 : #endif
1146 : #if LIBHEIF_NUMERIC_VERSION >= BUILD_LIBHEIF_VERSION(1, 19, 0)
1147 : poDriver->SetMetadataItem("SUPPORTS_GEOHEIF", "YES", "HEIF");
1148 : #endif
1149 10 : poDriver->pfnOpen = GDALHEIFDataset::OpenHEIF;
1150 :
1151 : #ifdef HAS_CUSTOM_FILE_WRITER
1152 10 : poDriver->pfnCreateCopy = GDALHEIFDataset::CreateCopy;
1153 : #endif
1154 10 : poDM->RegisterDriver(poDriver);
1155 : }
1156 :
1157 : #if LIBHEIF_NUMERIC_VERSION >= BUILD_LIBHEIF_VERSION(1, 12, 0)
1158 : // If the AVIF dedicated driver is not available, register an AVIF driver,
1159 : // called AVIF_HEIF, based on libheif, if it has AV1 decoding capabilities.
1160 : if (heif_have_decoder_for_format(heif_compression_AV1) &&
1161 : !poDM->IsKnownDriver("AVIF") && !poDM->IsKnownDriver("AVIF_HEIF"))
1162 : {
1163 : GDALDriver *poAVIF_HEIFDriver = new GDALDriver();
1164 : poAVIF_HEIFDriver->SetMetadataItem(GDAL_DCAP_RASTER, "YES");
1165 : poAVIF_HEIFDriver->SetDescription("AVIF_HEIF");
1166 : poAVIF_HEIFDriver->SetMetadataItem(
1167 : GDAL_DMD_LONGNAME, "AV1 Image File Format (using libheif)");
1168 : poAVIF_HEIFDriver->SetMetadataItem(GDAL_DMD_MIMETYPE, "image/avif");
1169 : poAVIF_HEIFDriver->SetMetadataItem(GDAL_DMD_HELPTOPIC,
1170 : "drivers/raster/heif.html");
1171 : poAVIF_HEIFDriver->SetMetadataItem(GDAL_DMD_EXTENSION, "avif");
1172 : poAVIF_HEIFDriver->SetMetadataItem(GDAL_DCAP_VIRTUALIO, "YES");
1173 :
1174 : poAVIF_HEIFDriver->pfnOpen = GDALHEIFDataset::OpenAVIF;
1175 : poAVIF_HEIFDriver->pfnIdentify = HEIFIdentifyOnlyAVIF;
1176 :
1177 : poDM->RegisterDriver(poAVIF_HEIFDriver);
1178 : }
1179 : #endif
1180 : }
|