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