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 20 : const int nMDBlocks = heif_image_handle_get_number_of_metadata_blocks(
288 10 : m_hImageHandle, nullptr);
289 10 : if (nMDBlocks <= 0)
290 7 : return;
291 :
292 6 : std::vector<heif_item_id> idsMDBlock(nMDBlocks);
293 3 : heif_image_handle_get_list_of_metadata_block_IDs(m_hImageHandle, nullptr,
294 3 : &idsMDBlock[0], nMDBlocks);
295 8 : for (const auto &id : idsMDBlock)
296 : {
297 : const char *pszType =
298 5 : heif_image_handle_get_metadata_type(m_hImageHandle, id);
299 : const size_t nCount =
300 5 : heif_image_handle_get_metadata_size(m_hImageHandle, id);
301 5 : if (pszType && EQUAL(pszType, "Exif") && nCount > 8 &&
302 : nCount < 1024 * 1024)
303 : {
304 2 : std::vector<GByte> data(nCount);
305 2 : heif_image_handle_get_metadata(m_hImageHandle, id, &data[0]);
306 :
307 : // There are 2 variants
308 : // - the one from
309 : // https://github.com/nokiatech/heif_conformance/blob/master/conformance_files/C034.heic
310 : // where the TIFF file immediately starts
311 : // - the one found in iPhone files (among others), where there
312 : // is first a 4-byte big-endian offset (after those initial 4
313 : // bytes) that points to the TIFF file, with a "Exif\0\0" just
314 : // before
315 2 : unsigned nTIFFFileOffset = 0;
316 4 : if (memcmp(&data[0], "II\x2a\x00", 4) == 0 ||
317 2 : memcmp(&data[0], "MM\x00\x2a", 4) == 0)
318 : {
319 : // do nothing
320 : }
321 : else
322 : {
323 : unsigned nOffset;
324 2 : memcpy(&nOffset, &data[0], 4);
325 2 : CPL_MSBPTR32(&nOffset);
326 4 : if (nOffset < nCount - 8 &&
327 2 : (memcmp(&data[nOffset + 4], "II\x2a\x00", 4) == 0 ||
328 1 : memcmp(&data[nOffset + 4], "MM\x00\x2a", 4) == 0))
329 : {
330 2 : nTIFFFileOffset = nOffset + 4;
331 : }
332 : else
333 : {
334 0 : continue;
335 : }
336 : }
337 :
338 : const CPLString osTempFile(
339 4 : VSIMemGenerateHiddenFilename("heif_exif.tif"));
340 : VSILFILE *fpTemp =
341 2 : VSIFileFromMemBuffer(osTempFile, &data[nTIFFFileOffset],
342 2 : nCount - nTIFFFileOffset, FALSE);
343 2 : char **papszMD = nullptr;
344 :
345 3 : const bool bLittleEndianTIFF = data[nTIFFFileOffset] == 'I' &&
346 1 : data[nTIFFFileOffset + 1] == 'I';
347 2 : const bool bLSBPlatform = CPL_IS_LSB != 0;
348 2 : const bool bSwabflag = bLittleEndianTIFF != bLSBPlatform;
349 :
350 : int nTIFFDirOff;
351 2 : memcpy(&nTIFFDirOff, &data[nTIFFFileOffset + 4], 4);
352 2 : if (bSwabflag)
353 : {
354 1 : CPL_SWAP32PTR(&nTIFFDirOff);
355 : }
356 2 : int nExifOffset = 0;
357 2 : int nInterOffset = 0;
358 2 : int nGPSOffset = 0;
359 2 : EXIFExtractMetadata(papszMD, fpTemp, nTIFFDirOff, bSwabflag, 0,
360 : nExifOffset, nInterOffset, nGPSOffset);
361 2 : if (nExifOffset > 0)
362 : {
363 2 : EXIFExtractMetadata(papszMD, fpTemp, nExifOffset, bSwabflag, 0,
364 : nExifOffset, nInterOffset, nGPSOffset);
365 : }
366 2 : if (nGPSOffset > 0)
367 : {
368 1 : EXIFExtractMetadata(papszMD, fpTemp, nGPSOffset, bSwabflag, 0,
369 : nExifOffset, nInterOffset, nGPSOffset);
370 : }
371 2 : if (nInterOffset > 0)
372 : {
373 0 : EXIFExtractMetadata(papszMD, fpTemp, nInterOffset, bSwabflag, 0,
374 : nExifOffset, nInterOffset, nGPSOffset);
375 : }
376 :
377 2 : if (papszMD)
378 : {
379 2 : GDALDataset::SetMetadata(papszMD, "EXIF");
380 2 : CSLDestroy(papszMD);
381 : }
382 :
383 2 : VSIFCloseL(fpTemp);
384 4 : VSIUnlink(osTempFile);
385 : }
386 3 : else if (pszType && EQUAL(pszType, "mime"))
387 : {
388 : #if LIBHEIF_NUMERIC_VERSION >= BUILD_LIBHEIF_VERSION(1, 2, 0)
389 : const char *pszContentType =
390 3 : heif_image_handle_get_metadata_content_type(m_hImageHandle, id);
391 3 : if (pszContentType &&
392 3 : EQUAL(pszContentType, "application/rdf+xml") &&
393 : #else
394 : if (
395 : #endif
396 3 : nCount > 0 && nCount < 1024 * 1024)
397 : {
398 6 : std::string osXMP;
399 3 : osXMP.resize(nCount);
400 3 : heif_image_handle_get_metadata(m_hImageHandle, id, &osXMP[0]);
401 3 : if (osXMP.find("<?xpacket") != std::string::npos)
402 : {
403 3 : char *apszMDList[2] = {&osXMP[0], nullptr};
404 3 : GDALDataset::SetMetadata(apszMDList, "xml:XMP");
405 : }
406 : }
407 : }
408 : }
409 : }
410 :
411 : /************************************************************************/
412 : /* OpenThumbnails() */
413 : /************************************************************************/
414 :
415 10 : void GDALHEIFDataset::OpenThumbnails()
416 : {
417 : int nThumbnails =
418 10 : heif_image_handle_get_number_of_thumbnails(m_hImageHandle);
419 10 : if (nThumbnails <= 0)
420 8 : return;
421 :
422 2 : heif_item_id thumbnailId = 0;
423 2 : heif_image_handle_get_list_of_thumbnail_IDs(m_hImageHandle, &thumbnailId,
424 : 1);
425 2 : heif_image_handle *hThumbnailHandle = nullptr;
426 2 : heif_image_handle_get_thumbnail(m_hImageHandle, thumbnailId,
427 2 : &hThumbnailHandle);
428 2 : if (hThumbnailHandle == nullptr)
429 0 : return;
430 :
431 : const int nThumbnailBands =
432 2 : 3 + (heif_image_handle_has_alpha_channel(hThumbnailHandle) ? 1 : 0);
433 2 : if (nThumbnailBands != nBands)
434 : {
435 0 : heif_image_handle_release(hThumbnailHandle);
436 0 : return;
437 : }
438 : #if LIBHEIF_NUMERIC_VERSION >= BUILD_LIBHEIF_VERSION(1, 4, 0)
439 : const int nBits =
440 2 : heif_image_handle_get_luma_bits_per_pixel(hThumbnailHandle);
441 2 : if (nBits != heif_image_handle_get_luma_bits_per_pixel(m_hImageHandle))
442 : {
443 0 : heif_image_handle_release(hThumbnailHandle);
444 0 : return;
445 : }
446 : #endif
447 :
448 4 : auto poOvrDS = std::make_unique<GDALHEIFDataset>();
449 2 : poOvrDS->m_hImageHandle = hThumbnailHandle;
450 2 : poOvrDS->m_bIsThumbnail = true;
451 2 : poOvrDS->nRasterXSize = heif_image_handle_get_width(hThumbnailHandle);
452 2 : poOvrDS->nRasterYSize = heif_image_handle_get_height(hThumbnailHandle);
453 : #ifdef LIBHEIF_SUPPORTS_TILES
454 : auto err = heif_image_handle_get_image_tiling(hThumbnailHandle, true,
455 : &poOvrDS->m_tiling);
456 : if (err.code != heif_error_Ok)
457 : {
458 : CPLError(CE_Failure, CPLE_AppDefined, "%s",
459 : err.message ? err.message : "Cannot get image tiling");
460 : heif_image_handle_release(hThumbnailHandle);
461 : return;
462 : }
463 : #endif
464 9 : for (int i = 0; i < nBands; i++)
465 : {
466 7 : poOvrDS->SetBand(i + 1, new GDALHEIFRasterBand(poOvrDS.get(), i + 1));
467 : }
468 2 : m_apoOvrDS.push_back(std::move(poOvrDS));
469 : }
470 :
471 : /************************************************************************/
472 : /* HEIFDriverIdentify() */
473 : /************************************************************************/
474 :
475 16 : static int HEIFDriverIdentify(GDALOpenInfo *poOpenInfo)
476 :
477 : {
478 16 : if (STARTS_WITH_CI(poOpenInfo->pszFilename, "HEIF:"))
479 9 : return true;
480 :
481 7 : if (poOpenInfo->nHeaderBytes < 12 || poOpenInfo->fpL == nullptr)
482 0 : return false;
483 :
484 : #if LIBHEIF_NUMERIC_VERSION >= BUILD_LIBHEIF_VERSION(1, 4, 0)
485 : const auto res =
486 7 : heif_check_filetype(poOpenInfo->pabyHeader, poOpenInfo->nHeaderBytes);
487 7 : if (res == heif_filetype_yes_supported)
488 7 : return TRUE;
489 0 : if (res == heif_filetype_maybe)
490 0 : return -1;
491 0 : if (res == heif_filetype_yes_unsupported)
492 : {
493 0 : CPLDebug("HEIF", "HEIF file, but not supported by libheif");
494 : }
495 0 : return FALSE;
496 : #else
497 : // Simplistic test...
498 : const unsigned char abySig1[] = "\x00"
499 : "\x00"
500 : "\x00"
501 : "\x20"
502 : "ftypheic";
503 : const unsigned char abySig2[] = "\x00"
504 : "\x00"
505 : "\x00"
506 : "\x18"
507 : "ftypheic";
508 : const unsigned char abySig3[] = "\x00"
509 : "\x00"
510 : "\x00"
511 : "\x18"
512 : "ftypmif1"
513 : "\x00"
514 : "\x00"
515 : "\x00"
516 : "\x00"
517 : "mif1heic";
518 : return (poOpenInfo->nHeaderBytes >= static_cast<int>(sizeof(abySig1)) &&
519 : memcmp(poOpenInfo->pabyHeader, abySig1, sizeof(abySig1)) == 0) ||
520 : (poOpenInfo->nHeaderBytes >= static_cast<int>(sizeof(abySig2)) &&
521 : memcmp(poOpenInfo->pabyHeader, abySig2, sizeof(abySig2)) == 0) ||
522 : (poOpenInfo->nHeaderBytes >= static_cast<int>(sizeof(abySig3)) &&
523 : memcmp(poOpenInfo->pabyHeader, abySig3, sizeof(abySig3)) == 0);
524 : #endif
525 : }
526 :
527 : /************************************************************************/
528 : /* OpenHEIF() */
529 : /************************************************************************/
530 :
531 16 : GDALDataset *GDALHEIFDataset::OpenHEIF(GDALOpenInfo *poOpenInfo)
532 : {
533 16 : if (!HEIFDriverIdentify(poOpenInfo))
534 : {
535 0 : return nullptr;
536 : }
537 16 : if (poOpenInfo->eAccess == GA_Update)
538 : {
539 0 : CPLError(CE_Failure, CPLE_NotSupported,
540 : "Update of existing HEIF file not supported");
541 0 : return nullptr;
542 : }
543 :
544 32 : auto poDS = std::make_unique<GDALHEIFDataset>();
545 16 : if (!poDS->Init(poOpenInfo))
546 6 : return nullptr;
547 :
548 10 : return poDS.release();
549 : }
550 :
551 : #if LIBHEIF_NUMERIC_VERSION >= BUILD_LIBHEIF_VERSION(1, 12, 0)
552 :
553 : /************************************************************************/
554 : /* HEIFIdentifyOnlyAVIF() */
555 : /************************************************************************/
556 :
557 : static int HEIFIdentifyOnlyAVIF(GDALOpenInfo *poOpenInfo)
558 : {
559 : if (poOpenInfo->nHeaderBytes < 12 || poOpenInfo->fpL == nullptr)
560 : return false;
561 : if (memcmp(poOpenInfo->pabyHeader + 4, "ftypavif", 8) == 0)
562 : return true;
563 : return false;
564 : }
565 :
566 : /************************************************************************/
567 : /* OpenAVIF() */
568 : /************************************************************************/
569 :
570 : GDALDataset *GDALHEIFDataset::OpenAVIF(GDALOpenInfo *poOpenInfo)
571 : {
572 : if (!HEIFIdentifyOnlyAVIF(poOpenInfo))
573 : return nullptr;
574 : if (poOpenInfo->eAccess == GA_Update)
575 : {
576 : CPLError(CE_Failure, CPLE_NotSupported,
577 : "Update of existing AVIF file not supported");
578 : return nullptr;
579 : }
580 :
581 : auto poDS = std::make_unique<GDALHEIFDataset>();
582 : if (!poDS->Init(poOpenInfo))
583 : return nullptr;
584 :
585 : return poDS.release();
586 : }
587 : #endif
588 :
589 : /************************************************************************/
590 : /* GDALHEIFRasterBand() */
591 : /************************************************************************/
592 :
593 40 : GDALHEIFRasterBand::GDALHEIFRasterBand(GDALHEIFDataset *poDSIn, int nBandIn)
594 : {
595 40 : poDS = poDSIn;
596 40 : nBand = nBandIn;
597 :
598 40 : eDataType = GDT_Byte;
599 : #if LIBHEIF_NUMERIC_VERSION >= BUILD_LIBHEIF_VERSION(1, 4, 0)
600 : const int nBits =
601 40 : heif_image_handle_get_luma_bits_per_pixel(poDSIn->m_hImageHandle);
602 40 : if (nBits > 8)
603 : {
604 7 : eDataType = GDT_UInt16;
605 : }
606 40 : if (nBits != 8 && nBits != 16)
607 : {
608 7 : GDALRasterBand::SetMetadataItem("NBITS", CPLSPrintf("%d", nBits),
609 : "IMAGE_STRUCTURE");
610 : }
611 : #endif
612 :
613 : #ifdef LIBHEIF_SUPPORTS_TILES
614 : nBlockXSize = poDSIn->m_tiling.tile_width;
615 : nBlockYSize = poDSIn->m_tiling.tile_height;
616 : #else
617 40 : nBlockXSize = poDS->GetRasterXSize();
618 40 : nBlockYSize = 1;
619 : #endif
620 40 : }
621 :
622 : /************************************************************************/
623 : /* IReadBlock() */
624 : /************************************************************************/
625 : #ifdef LIBHEIF_SUPPORTS_TILES
626 : CPLErr GDALHEIFRasterBand::IReadBlock(int nBlockXOff, int nBlockYOff,
627 : void *pImage)
628 : {
629 : GDALHEIFDataset *poGDS = static_cast<GDALHEIFDataset *>(poDS);
630 : if (poGDS->m_bFailureDecoding)
631 : return CE_Failure;
632 : const int nBands = poGDS->GetRasterCount();
633 : heif_image *hImage = nullptr;
634 : struct heif_decoding_options *decode_options =
635 : heif_decoding_options_alloc();
636 :
637 : auto err = heif_image_handle_decode_image_tile(
638 : poGDS->m_hImageHandle, &hImage, heif_colorspace_RGB,
639 : nBands == 3
640 : ? (eDataType == GDT_UInt16 ?
641 : #if CPL_IS_LSB
642 : heif_chroma_interleaved_RRGGBB_LE
643 : #else
644 : heif_chroma_interleaved_RRGGBB_BE
645 : #endif
646 : : heif_chroma_interleaved_RGB)
647 : : (eDataType == GDT_UInt16 ?
648 : #if CPL_IS_LSB
649 : heif_chroma_interleaved_RRGGBBAA_LE
650 : #else
651 : heif_chroma_interleaved_RRGGBBAA_BE
652 : #endif
653 : : heif_chroma_interleaved_RGBA),
654 : decode_options, nBlockXOff, nBlockYOff);
655 :
656 : if (err.code != heif_error_Ok)
657 : {
658 : CPLError(CE_Failure, CPLE_AppDefined, "%s",
659 : err.message ? err.message : "Cannot decode image");
660 : poGDS->m_bFailureDecoding = true;
661 : heif_decoding_options_free(decode_options);
662 : return CE_Failure;
663 : }
664 : heif_decoding_options_free(decode_options);
665 : int nStride = 0;
666 : const uint8_t *pSrcData = heif_image_get_plane_readonly(
667 : hImage, heif_channel_interleaved, &nStride);
668 : if (eDataType == GDT_Byte)
669 : {
670 : for (int y = 0; y < nBlockYSize; y++)
671 : {
672 : for (int x = 0; x < nBlockXSize; x++)
673 : {
674 : const size_t srcIndex = static_cast<size_t>(y) * nStride +
675 : static_cast<size_t>(x) * nBands +
676 : nBand - 1;
677 : const size_t outIndex =
678 : static_cast<size_t>(y) * nBlockXSize + x;
679 : (static_cast<GByte *>(pImage))[outIndex] = pSrcData[srcIndex];
680 : }
681 : }
682 : }
683 : else
684 : {
685 : for (int y = 0; y < nBlockYSize; y++)
686 : {
687 : for (int x = 0; x < nBlockXSize; x++)
688 : {
689 : const size_t srcIndex = static_cast<size_t>(y) * (nStride / 2) +
690 : static_cast<size_t>(x) * nBands +
691 : nBand - 1;
692 : const size_t outIndex =
693 : static_cast<size_t>(y) * nBlockXSize + x;
694 : (static_cast<GUInt16 *>(pImage))[outIndex] =
695 : (reinterpret_cast<const GUInt16 *>(pSrcData))[srcIndex];
696 : }
697 : }
698 : }
699 : heif_image_release(hImage);
700 : return CE_None;
701 : }
702 : #else
703 544 : CPLErr GDALHEIFRasterBand::IReadBlock(int, int nBlockYOff, void *pImage)
704 : {
705 544 : GDALHEIFDataset *poGDS = static_cast<GDALHEIFDataset *>(poDS);
706 544 : if (poGDS->m_bFailureDecoding)
707 0 : return CE_Failure;
708 544 : const int nBands = poGDS->GetRasterCount();
709 544 : if (poGDS->m_hImage == nullptr)
710 : {
711 : auto err = heif_decode_image(
712 6 : poGDS->m_hImageHandle, &(poGDS->m_hImage), heif_colorspace_RGB,
713 : nBands == 3
714 : ? (
715 : #if LIBHEIF_NUMERIC_VERSION >= BUILD_LIBHEIF_VERSION(1, 4, 0)
716 5 : eDataType == GDT_UInt16 ?
717 : #if CPL_IS_LSB
718 : heif_chroma_interleaved_RRGGBB_LE
719 : #else
720 : heif_chroma_interleaved_RRGGBB_BE
721 : #endif
722 : :
723 : #endif
724 : heif_chroma_interleaved_RGB)
725 : : (
726 : #if LIBHEIF_NUMERIC_VERSION >= BUILD_LIBHEIF_VERSION(1, 4, 0)
727 1 : eDataType == GDT_UInt16
728 1 : ?
729 : #if CPL_IS_LSB
730 : heif_chroma_interleaved_RRGGBBAA_LE
731 : #else
732 : heif_chroma_interleaved_RRGGBBAA_BE
733 : #endif
734 : :
735 : #endif
736 : heif_chroma_interleaved_RGBA),
737 12 : nullptr);
738 6 : if (err.code != heif_error_Ok)
739 : {
740 0 : CPLError(CE_Failure, CPLE_AppDefined, "%s",
741 0 : err.message ? err.message : "Cannot decode image");
742 0 : poGDS->m_bFailureDecoding = true;
743 0 : return CE_Failure;
744 : }
745 12 : const int nBitsPerPixel = heif_image_get_bits_per_pixel(
746 6 : poGDS->m_hImage, heif_channel_interleaved);
747 6 : if (nBitsPerPixel != nBands * GDALGetDataTypeSize(eDataType))
748 : {
749 0 : CPLError(CE_Failure, CPLE_AppDefined,
750 : "Unexpected bits_per_pixel = %d value", nBitsPerPixel);
751 0 : poGDS->m_bFailureDecoding = true;
752 0 : return CE_Failure;
753 : }
754 : }
755 :
756 544 : int nStride = 0;
757 1088 : const uint8_t *pSrcData = heif_image_get_plane_readonly(
758 544 : poGDS->m_hImage, heif_channel_interleaved, &nStride);
759 544 : pSrcData += static_cast<size_t>(nBlockYOff) * nStride;
760 544 : if (eDataType == GDT_Byte)
761 : {
762 25176 : for (int i = 0; i < nBlockXSize; i++)
763 24832 : (static_cast<GByte *>(pImage))[i] =
764 24832 : pSrcData[nBand - 1 + static_cast<size_t>(i) * nBands];
765 : }
766 : else
767 : {
768 80200 : for (int i = 0; i < nBlockXSize; i++)
769 80000 : (static_cast<GUInt16 *>(pImage))[i] =
770 : (reinterpret_cast<const GUInt16 *>(
771 80000 : pSrcData))[nBand - 1 + static_cast<size_t>(i) * nBands];
772 : }
773 :
774 544 : return CE_None;
775 : }
776 : #endif
777 :
778 : /************************************************************************/
779 : /* GDALRegister_HEIF() */
780 : /************************************************************************/
781 :
782 9 : void GDALRegister_HEIF()
783 :
784 : {
785 9 : if (!GDAL_CHECK_VERSION("HEIF driver"))
786 0 : return;
787 :
788 9 : if (GDALGetDriverByName(DRIVER_NAME) != nullptr)
789 0 : return;
790 :
791 9 : auto poDM = GetGDALDriverManager();
792 : {
793 9 : GDALDriver *poDriver = new GDALDriver();
794 9 : HEIFDriverSetCommonMetadata(poDriver);
795 :
796 : #if LIBHEIF_NUMERIC_VERSION >= BUILD_LIBHEIF_VERSION(1, 12, 0)
797 : if (heif_have_decoder_for_format(heif_compression_AVC))
798 : {
799 : poDriver->SetMetadataItem("SUPPORTS_AVC", "YES", "HEIF");
800 : }
801 : if (heif_have_encoder_for_format(heif_compression_AVC))
802 : {
803 : poDriver->SetMetadataItem("SUPPORTS_AVC_WRITE", "YES", "HEIF");
804 : }
805 : // If the AVIF dedicated driver is not available, register an AVIF driver,
806 : // called AVIF_HEIF, based on libheif, if it has AV1 decoding capabilities.
807 : if (heif_have_decoder_for_format(heif_compression_AV1))
808 : {
809 : poDriver->SetMetadataItem("SUPPORTS_AVIF", "YES", "HEIF");
810 : poDriver->SetMetadataItem("SUPPORTS_AV1", "YES", "HEIF");
811 : }
812 : if (heif_have_encoder_for_format(heif_compression_AV1))
813 : {
814 : poDriver->SetMetadataItem("SUPPORTS_AV1_WRITE", "YES", "HEIF");
815 : }
816 : if (heif_have_decoder_for_format(heif_compression_HEVC))
817 : {
818 : poDriver->SetMetadataItem("SUPPORTS_HEVC", "YES", "HEIF");
819 : }
820 : if (heif_have_encoder_for_format(heif_compression_HEVC))
821 : {
822 : poDriver->SetMetadataItem("SUPPORTS_HEVC_WRITE", "YES", "HEIF");
823 : }
824 : if (heif_have_decoder_for_format(heif_compression_JPEG))
825 : {
826 : poDriver->SetMetadataItem("SUPPORTS_JPEG", "YES", "HEIF");
827 : }
828 : if (heif_have_encoder_for_format(heif_compression_JPEG))
829 : {
830 : poDriver->SetMetadataItem("SUPPORTS_JPEG_WRITE", "YES", "HEIF");
831 : }
832 : #if LIBHEIF_NUMERIC_VERSION >= BUILD_LIBHEIF_VERSION(1, 15, 0)
833 : if (heif_have_decoder_for_format(heif_compression_JPEG2000))
834 : {
835 : poDriver->SetMetadataItem("SUPPORTS_JPEG2000", "YES", "HEIF");
836 : }
837 : if (heif_have_encoder_for_format(heif_compression_JPEG2000))
838 : {
839 : poDriver->SetMetadataItem("SUPPORTS_JPEG2000_WRITE", "YES", "HEIF");
840 : }
841 : #endif
842 : #if LIBHEIF_NUMERIC_VERSION >= BUILD_LIBHEIF_VERSION(1, 18, 0)
843 : if (heif_have_decoder_for_format(heif_compression_HTJ2K))
844 : {
845 : poDriver->SetMetadataItem("SUPPORTS_HTJ2K", "YES", "HEIF");
846 : }
847 : if (heif_have_encoder_for_format(heif_compression_HTJ2K))
848 : {
849 : poDriver->SetMetadataItem("SUPPORTS_HTJ2K_WRITE", "YES", "HEIF");
850 : }
851 : #endif
852 : #if LIBHEIF_NUMERIC_VERSION >= BUILD_LIBHEIF_VERSION(1, 16, 0)
853 : if (heif_have_decoder_for_format(heif_compression_uncompressed))
854 : {
855 : poDriver->SetMetadataItem("SUPPORTS_UNCOMPRESSED", "YES", "HEIF");
856 : }
857 : if (heif_have_encoder_for_format(heif_compression_uncompressed))
858 : {
859 : poDriver->SetMetadataItem("SUPPORTS_UNCOMPRESSED_WRITE", "YES",
860 : "HEIF");
861 : }
862 : #endif
863 : #if LIBHEIF_NUMERIC_VERSION >= BUILD_LIBHEIF_VERSION(1, 15, 0)
864 : if (heif_have_decoder_for_format(heif_compression_VVC))
865 : {
866 : poDriver->SetMetadataItem("SUPPORTS_VVC", "YES", "HEIF");
867 : }
868 : if (heif_have_encoder_for_format(heif_compression_VVC))
869 : {
870 : poDriver->SetMetadataItem("SUPPORTS_VVC_WRITE", "YES", "HEIF");
871 : }
872 : #endif
873 : #else
874 : // Anything that old probably supports only HEVC
875 9 : poDriver->SetMetadataItem("SUPPORTS_HEVC", "YES", "HEIF");
876 : #endif
877 : #ifdef LIBHEIF_SUPPORTS_TILES
878 : poDriver->SetMetadataItem("SUPPORTS_TILES", "YES", "HEIF");
879 : #endif
880 9 : poDriver->pfnOpen = GDALHEIFDataset::OpenHEIF;
881 :
882 : #ifdef HAS_CUSTOM_FILE_WRITER
883 9 : poDriver->pfnCreateCopy = GDALHEIFDataset::CreateCopy;
884 : #endif
885 9 : poDM->RegisterDriver(poDriver);
886 : }
887 :
888 : #if LIBHEIF_NUMERIC_VERSION >= BUILD_LIBHEIF_VERSION(1, 12, 0)
889 : // If the AVIF dedicated driver is not available, register an AVIF driver,
890 : // called AVIF_HEIF, based on libheif, if it has AV1 decoding capabilities.
891 : if (heif_have_decoder_for_format(heif_compression_AV1) &&
892 : !poDM->IsKnownDriver("AVIF") && !poDM->IsKnownDriver("AVIF_HEIF"))
893 : {
894 : GDALDriver *poAVIF_HEIFDriver = new GDALDriver();
895 : poAVIF_HEIFDriver->SetMetadataItem(GDAL_DCAP_RASTER, "YES");
896 : poAVIF_HEIFDriver->SetDescription("AVIF_HEIF");
897 : poAVIF_HEIFDriver->SetMetadataItem(
898 : GDAL_DMD_LONGNAME, "AV1 Image File Format (using libheif)");
899 : poAVIF_HEIFDriver->SetMetadataItem(GDAL_DMD_MIMETYPE, "image/avif");
900 : poAVIF_HEIFDriver->SetMetadataItem(GDAL_DMD_HELPTOPIC,
901 : "drivers/raster/heif.html");
902 : poAVIF_HEIFDriver->SetMetadataItem(GDAL_DMD_EXTENSION, "avif");
903 : poAVIF_HEIFDriver->SetMetadataItem(GDAL_DCAP_VIRTUALIO, "YES");
904 :
905 : poAVIF_HEIFDriver->pfnOpen = GDALHEIFDataset::OpenAVIF;
906 : poAVIF_HEIFDriver->pfnIdentify = HEIFIdentifyOnlyAVIF;
907 :
908 : poDM->RegisterDriver(poAVIF_HEIFDriver);
909 : }
910 : #endif
911 : }
|