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