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 : * Permission is hereby granted, free of charge, to any person obtaining a
10 : * copy of this software and associated documentation files (the "Software"),
11 : * to deal in the Software without restriction, including without limitation
12 : * the rights to use, copy, modify, merge, publish, distribute, sublicense,
13 : * and/or sell copies of the Software, and to permit persons to whom the
14 : * Software is furnished to do so, subject to the following conditions:
15 : *
16 : * The above copyright notice and this permission notice shall be included
17 : * in all copies or substantial portions of the Software.
18 : *
19 : * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
20 : * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21 : * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
22 : * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23 : * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
24 : * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
25 : * DEALINGS IN THE SOFTWARE.
26 : ****************************************************************************/
27 :
28 : #include "gdal_pam.h"
29 : #include "ogr_spatialref.h"
30 :
31 : #include "include_libheif.h"
32 :
33 : #include "heifdrivercore.h"
34 :
35 : #include <vector>
36 :
37 : extern "C" void CPL_DLL GDALRegister_HEIF();
38 :
39 : // g++ -fPIC -std=c++11 frmts/heif/heifdataset.cpp -Iport -Igcore -Iogr
40 : // -Iogr/ogrsf_frmts -I$HOME/heif/install-ubuntu-18.04/include
41 : // -L$HOME/heif/install-ubuntu-18.04/lib -lheif -shared -o gdal_HEIF.so -L.
42 : // -lgdal
43 :
44 : /************************************************************************/
45 : /* GDALHEIFDataset */
46 : /************************************************************************/
47 :
48 : class GDALHEIFDataset final : public GDALPamDataset
49 : {
50 : friend class GDALHEIFRasterBand;
51 :
52 : heif_context *m_hCtxt = nullptr;
53 : heif_image_handle *m_hImageHandle = nullptr;
54 : heif_image *m_hImage = nullptr;
55 : bool m_bFailureDecoding = false;
56 : std::vector<std::unique_ptr<GDALHEIFDataset>> m_apoOvrDS{};
57 : bool m_bIsThumbnail = false;
58 :
59 : #ifdef HAS_CUSTOM_FILE_READER
60 : heif_reader m_oReader{};
61 : VSILFILE *m_fpL = nullptr;
62 : vsi_l_offset m_nSize = 0;
63 :
64 : static int64_t GetPositionCbk(void *userdata);
65 : static int ReadCbk(void *data, size_t size, void *userdata);
66 : static int SeekCbk(int64_t position, void *userdata);
67 : static enum heif_reader_grow_status WaitForFileSizeCbk(int64_t target_size,
68 : void *userdata);
69 : #endif
70 :
71 : bool Init(GDALOpenInfo *poOpenInfo);
72 : void ReadMetadata();
73 : void OpenThumbnails();
74 :
75 : public:
76 : GDALHEIFDataset();
77 : ~GDALHEIFDataset();
78 :
79 : static GDALDataset *Open(GDALOpenInfo *poOpenInfo);
80 : };
81 :
82 : /************************************************************************/
83 : /* GDALHEIFRasterBand */
84 : /************************************************************************/
85 :
86 : class GDALHEIFRasterBand final : public GDALPamRasterBand
87 : {
88 : protected:
89 : CPLErr IReadBlock(int, int, void *) override;
90 :
91 : public:
92 : GDALHEIFRasterBand(GDALHEIFDataset *poDSIn, int nBandIn);
93 :
94 6 : GDALColorInterp GetColorInterpretation() override
95 : {
96 6 : return static_cast<GDALColorInterp>(GCI_RedBand + nBand - 1);
97 : }
98 :
99 7 : int GetOverviewCount() override
100 : {
101 7 : GDALHEIFDataset *poGDS = static_cast<GDALHEIFDataset *>(poDS);
102 7 : return static_cast<int>(poGDS->m_apoOvrDS.size());
103 : }
104 :
105 4 : GDALRasterBand *GetOverview(int idx) override
106 : {
107 4 : if (idx < 0 || idx >= GetOverviewCount())
108 2 : return nullptr;
109 2 : GDALHEIFDataset *poGDS = static_cast<GDALHEIFDataset *>(poDS);
110 2 : return poGDS->m_apoOvrDS[idx]->GetRasterBand(nBand);
111 : }
112 : };
113 :
114 : /************************************************************************/
115 : /* GDALHEIFDataset() */
116 : /************************************************************************/
117 :
118 17 : GDALHEIFDataset::GDALHEIFDataset() : m_hCtxt(heif_context_alloc())
119 :
120 : {
121 : #ifdef HAS_CUSTOM_FILE_READER
122 17 : m_oReader.reader_api_version = 1;
123 17 : m_oReader.get_position = GetPositionCbk;
124 17 : m_oReader.read = ReadCbk;
125 17 : m_oReader.seek = SeekCbk;
126 17 : m_oReader.wait_for_file_size = WaitForFileSizeCbk;
127 : #endif
128 17 : }
129 :
130 : /************************************************************************/
131 : /* ~GDALHEIFDataset() */
132 : /************************************************************************/
133 :
134 34 : GDALHEIFDataset::~GDALHEIFDataset()
135 : {
136 17 : if (m_hCtxt)
137 17 : heif_context_free(m_hCtxt);
138 : #ifdef HAS_CUSTOM_FILE_READER
139 17 : if (m_fpL)
140 10 : VSIFCloseL(m_fpL);
141 : #endif
142 17 : if (m_hImage)
143 5 : heif_image_release(m_hImage);
144 17 : if (m_hImageHandle)
145 11 : heif_image_handle_release(m_hImageHandle);
146 34 : }
147 :
148 : #ifdef HAS_CUSTOM_FILE_READER
149 :
150 : /************************************************************************/
151 : /* GetPositionCbk() */
152 : /************************************************************************/
153 :
154 414 : int64_t GDALHEIFDataset::GetPositionCbk(void *userdata)
155 : {
156 414 : GDALHEIFDataset *poThis = static_cast<GDALHEIFDataset *>(userdata);
157 414 : return static_cast<int64_t>(VSIFTellL(poThis->m_fpL));
158 : }
159 :
160 : /************************************************************************/
161 : /* ReadCbk() */
162 : /************************************************************************/
163 :
164 2128 : int GDALHEIFDataset::ReadCbk(void *data, size_t size, void *userdata)
165 : {
166 2128 : GDALHEIFDataset *poThis = static_cast<GDALHEIFDataset *>(userdata);
167 2128 : return VSIFReadL(data, size, 1, poThis->m_fpL) == 1 ? 0 : -1;
168 : }
169 :
170 : /************************************************************************/
171 : /* SeekCbk() */
172 : /************************************************************************/
173 :
174 44 : int GDALHEIFDataset::SeekCbk(int64_t position, void *userdata)
175 : {
176 44 : GDALHEIFDataset *poThis = static_cast<GDALHEIFDataset *>(userdata);
177 44 : return VSIFSeekL(poThis->m_fpL, static_cast<vsi_l_offset>(position),
178 44 : SEEK_SET);
179 : }
180 :
181 : /************************************************************************/
182 : /* WaitForFileSizeCbk() */
183 : /************************************************************************/
184 :
185 : enum heif_reader_grow_status
186 438 : GDALHEIFDataset::WaitForFileSizeCbk(int64_t target_size, void *userdata)
187 : {
188 438 : GDALHEIFDataset *poThis = static_cast<GDALHEIFDataset *>(userdata);
189 438 : if (target_size > static_cast<int64_t>(poThis->m_nSize))
190 10 : return heif_reader_grow_status_size_beyond_eof;
191 428 : return heif_reader_grow_status_size_reached;
192 : }
193 :
194 : #endif
195 :
196 : /************************************************************************/
197 : /* Init() */
198 : /************************************************************************/
199 :
200 15 : bool GDALHEIFDataset::Init(GDALOpenInfo *poOpenInfo)
201 : {
202 30 : CPLString osFilename(poOpenInfo->pszFilename);
203 : #ifdef HAS_CUSTOM_FILE_READER
204 : VSILFILE *fpL;
205 : #endif
206 15 : int iPart = 0;
207 15 : if (STARTS_WITH_CI(poOpenInfo->pszFilename, "HEIF:"))
208 : {
209 8 : const char *pszPartPos = poOpenInfo->pszFilename + strlen("HEIF:");
210 8 : const char *pszNextColumn = strchr(pszPartPos, ':');
211 8 : if (pszNextColumn == nullptr)
212 2 : return false;
213 6 : iPart = atoi(pszPartPos);
214 6 : if (iPart <= 0)
215 1 : return false;
216 5 : osFilename = pszNextColumn + 1;
217 : #ifdef HAS_CUSTOM_FILE_READER
218 5 : fpL = VSIFOpenL(osFilename, "rb");
219 5 : if (fpL == nullptr)
220 2 : return false;
221 : #endif
222 : }
223 : else
224 : {
225 : #ifdef HAS_CUSTOM_FILE_READER
226 7 : fpL = poOpenInfo->fpL;
227 7 : poOpenInfo->fpL = nullptr;
228 : #endif
229 : }
230 :
231 : #ifdef HAS_CUSTOM_FILE_READER
232 10 : m_oReader.reader_api_version = 1;
233 10 : m_oReader.get_position = GetPositionCbk;
234 10 : m_oReader.read = ReadCbk;
235 10 : m_oReader.seek = SeekCbk;
236 10 : m_oReader.wait_for_file_size = WaitForFileSizeCbk;
237 10 : m_fpL = fpL;
238 :
239 10 : VSIFSeekL(m_fpL, 0, SEEK_END);
240 10 : m_nSize = VSIFTellL(m_fpL);
241 10 : VSIFSeekL(m_fpL, 0, SEEK_SET);
242 :
243 : auto err =
244 10 : heif_context_read_from_reader(m_hCtxt, &m_oReader, this, nullptr);
245 10 : if (err.code != heif_error_Ok)
246 : {
247 0 : CPLError(CE_Failure, CPLE_AppDefined, "%s",
248 0 : err.message ? err.message : "Cannot open file");
249 0 : return false;
250 : }
251 : #else
252 : auto err =
253 : heif_context_read_from_file(m_hCtxt, osFilename.c_str(), nullptr);
254 : if (err.code != heif_error_Ok)
255 : {
256 : CPLError(CE_Failure, CPLE_AppDefined, "%s",
257 : err.message ? err.message : "Cannot open file");
258 : return false;
259 : }
260 : #endif
261 :
262 : const int nSubdatasets =
263 10 : heif_context_get_number_of_top_level_images(m_hCtxt);
264 10 : if (iPart == 0)
265 : {
266 7 : if (nSubdatasets > 1)
267 : {
268 2 : CPLStringList aosSubDS;
269 3 : for (int i = 0; i < nSubdatasets; i++)
270 : {
271 : aosSubDS.SetNameValue(
272 : CPLSPrintf("SUBDATASET_%d_NAME", i + 1),
273 2 : CPLSPrintf("HEIF:%d:%s", i + 1, poOpenInfo->pszFilename));
274 : aosSubDS.SetNameValue(CPLSPrintf("SUBDATASET_%d_DESC", i + 1),
275 2 : CPLSPrintf("Subdataset %d", i + 1));
276 : }
277 1 : GDALDataset::SetMetadata(aosSubDS.List(), "SUBDATASETS");
278 : }
279 : }
280 3 : else if (iPart > nSubdatasets)
281 : {
282 1 : CPLError(CE_Failure, CPLE_AppDefined,
283 : "Invalid image part number. Maximum allowed is %d",
284 : nSubdatasets);
285 1 : return false;
286 : }
287 : else
288 : {
289 2 : iPart--;
290 : }
291 18 : std::vector<heif_item_id> idArray(nSubdatasets);
292 9 : heif_context_get_list_of_top_level_image_IDs(m_hCtxt, &idArray[0],
293 : nSubdatasets);
294 9 : const auto itemId = idArray[iPart];
295 :
296 9 : err = heif_context_get_image_handle(m_hCtxt, itemId, &m_hImageHandle);
297 9 : if (err.code != heif_error_Ok)
298 : {
299 0 : CPLError(CE_Failure, CPLE_AppDefined, "%s",
300 0 : err.message ? err.message : "Cannot open image");
301 0 : return false;
302 : }
303 :
304 9 : nRasterXSize = heif_image_handle_get_width(m_hImageHandle);
305 9 : nRasterYSize = heif_image_handle_get_height(m_hImageHandle);
306 : const int l_nBands =
307 9 : 3 + (heif_image_handle_has_alpha_channel(m_hImageHandle) ? 1 : 0);
308 39 : for (int i = 0; i < l_nBands; i++)
309 : {
310 30 : SetBand(i + 1, new GDALHEIFRasterBand(this, i + 1));
311 : }
312 :
313 9 : ReadMetadata();
314 :
315 9 : OpenThumbnails();
316 :
317 : // Initialize any PAM information.
318 9 : SetDescription(poOpenInfo->pszFilename);
319 9 : TryLoadXML(poOpenInfo->GetSiblingFiles());
320 :
321 9 : return true;
322 : }
323 :
324 : /************************************************************************/
325 : /* ReadMetadata() */
326 : /************************************************************************/
327 :
328 9 : void GDALHEIFDataset::ReadMetadata()
329 : {
330 18 : const int nMDBlocks = heif_image_handle_get_number_of_metadata_blocks(
331 9 : m_hImageHandle, nullptr);
332 9 : if (nMDBlocks <= 0)
333 6 : return;
334 :
335 6 : std::vector<heif_item_id> idsMDBlock(nMDBlocks);
336 3 : heif_image_handle_get_list_of_metadata_block_IDs(m_hImageHandle, nullptr,
337 3 : &idsMDBlock[0], nMDBlocks);
338 8 : for (const auto &id : idsMDBlock)
339 : {
340 : const char *pszType =
341 5 : heif_image_handle_get_metadata_type(m_hImageHandle, id);
342 : const size_t nCount =
343 5 : heif_image_handle_get_metadata_size(m_hImageHandle, id);
344 5 : if (pszType && EQUAL(pszType, "Exif") && nCount > 8 &&
345 : nCount < 1024 * 1024)
346 : {
347 2 : std::vector<GByte> data(nCount);
348 2 : heif_image_handle_get_metadata(m_hImageHandle, id, &data[0]);
349 :
350 : // There are 2 variants
351 : // - the one from
352 : // https://github.com/nokiatech/heif_conformance/blob/master/conformance_files/C034.heic
353 : // where the TIFF file immediately starts
354 : // - the one found in iPhone files (among others), where there
355 : // is first a 4-byte big-endian offset (after those initial 4
356 : // bytes) that points to the TIFF file, with a "Exif\0\0" just
357 : // before
358 2 : unsigned nTIFFFileOffset = 0;
359 4 : if (memcmp(&data[0], "II\x2a\x00", 4) == 0 ||
360 2 : memcmp(&data[0], "MM\x00\x2a", 4) == 0)
361 : {
362 : // do nothing
363 : }
364 : else
365 : {
366 : unsigned nOffset;
367 2 : memcpy(&nOffset, &data[0], 4);
368 2 : CPL_MSBPTR32(&nOffset);
369 4 : if (nOffset < nCount - 8 &&
370 2 : (memcmp(&data[nOffset + 4], "II\x2a\x00", 4) == 0 ||
371 1 : memcmp(&data[nOffset + 4], "MM\x00\x2a", 4) == 0))
372 : {
373 2 : nTIFFFileOffset = nOffset + 4;
374 : }
375 : else
376 : {
377 0 : continue;
378 : }
379 : }
380 :
381 4 : CPLString osTempFile;
382 2 : osTempFile.Printf("/vsimem/heif_exif_%p.tif", this);
383 : VSILFILE *fpTemp =
384 2 : VSIFileFromMemBuffer(osTempFile, &data[nTIFFFileOffset],
385 2 : nCount - nTIFFFileOffset, FALSE);
386 2 : char **papszMD = nullptr;
387 :
388 3 : const bool bLittleEndianTIFF = data[nTIFFFileOffset] == 'I' &&
389 1 : data[nTIFFFileOffset + 1] == 'I';
390 2 : const bool bLSBPlatform = CPL_IS_LSB != 0;
391 2 : const bool bSwabflag = bLittleEndianTIFF != bLSBPlatform;
392 :
393 : int nTIFFDirOff;
394 2 : memcpy(&nTIFFDirOff, &data[nTIFFFileOffset + 4], 4);
395 2 : if (bSwabflag)
396 : {
397 1 : CPL_SWAP32PTR(&nTIFFDirOff);
398 : }
399 2 : int nExifOffset = 0;
400 2 : int nInterOffset = 0;
401 2 : int nGPSOffset = 0;
402 2 : EXIFExtractMetadata(papszMD, fpTemp, nTIFFDirOff, bSwabflag, 0,
403 : nExifOffset, nInterOffset, nGPSOffset);
404 2 : if (nExifOffset > 0)
405 : {
406 2 : EXIFExtractMetadata(papszMD, fpTemp, nExifOffset, bSwabflag, 0,
407 : nExifOffset, nInterOffset, nGPSOffset);
408 : }
409 2 : if (nGPSOffset > 0)
410 : {
411 1 : EXIFExtractMetadata(papszMD, fpTemp, nGPSOffset, bSwabflag, 0,
412 : nExifOffset, nInterOffset, nGPSOffset);
413 : }
414 2 : if (nInterOffset > 0)
415 : {
416 0 : EXIFExtractMetadata(papszMD, fpTemp, nInterOffset, bSwabflag, 0,
417 : nExifOffset, nInterOffset, nGPSOffset);
418 : }
419 :
420 2 : if (papszMD)
421 : {
422 2 : GDALDataset::SetMetadata(papszMD, "EXIF");
423 2 : CSLDestroy(papszMD);
424 : }
425 :
426 2 : VSIFCloseL(fpTemp);
427 4 : VSIUnlink(osTempFile);
428 : }
429 3 : else if (pszType && EQUAL(pszType, "mime"))
430 : {
431 : #if LIBHEIF_NUMERIC_VERSION >= BUILD_LIBHEIF_VERSION(1, 2, 0)
432 : const char *pszContentType =
433 3 : heif_image_handle_get_metadata_content_type(m_hImageHandle, id);
434 3 : if (pszContentType &&
435 3 : EQUAL(pszContentType, "application/rdf+xml") &&
436 : #else
437 : if (
438 : #endif
439 3 : nCount > 0 && nCount < 1024 * 1024)
440 : {
441 6 : std::string osXMP;
442 3 : osXMP.resize(nCount);
443 3 : heif_image_handle_get_metadata(m_hImageHandle, id, &osXMP[0]);
444 3 : if (osXMP.find("<?xpacket") != std::string::npos)
445 : {
446 3 : char *apszMDList[2] = {&osXMP[0], nullptr};
447 3 : GDALDataset::SetMetadata(apszMDList, "xml:XMP");
448 : }
449 : }
450 : }
451 : }
452 : }
453 :
454 : /************************************************************************/
455 : /* OpenThumbnails() */
456 : /************************************************************************/
457 :
458 9 : void GDALHEIFDataset::OpenThumbnails()
459 : {
460 : int nThumbnails =
461 9 : heif_image_handle_get_number_of_thumbnails(m_hImageHandle);
462 9 : if (nThumbnails <= 0)
463 7 : return;
464 :
465 2 : heif_item_id thumbnailId = 0;
466 2 : heif_image_handle_get_list_of_thumbnail_IDs(m_hImageHandle, &thumbnailId,
467 : 1);
468 2 : heif_image_handle *hThumbnailHandle = nullptr;
469 2 : heif_image_handle_get_thumbnail(m_hImageHandle, thumbnailId,
470 2 : &hThumbnailHandle);
471 2 : if (hThumbnailHandle == nullptr)
472 0 : return;
473 :
474 : const int nThumbnailBands =
475 2 : 3 + (heif_image_handle_has_alpha_channel(hThumbnailHandle) ? 1 : 0);
476 2 : if (nThumbnailBands != nBands)
477 : {
478 0 : heif_image_handle_release(hThumbnailHandle);
479 0 : return;
480 : }
481 : #if LIBHEIF_NUMERIC_VERSION >= BUILD_LIBHEIF_VERSION(1, 4, 0)
482 : const int nBits =
483 2 : heif_image_handle_get_luma_bits_per_pixel(hThumbnailHandle);
484 2 : if (nBits != heif_image_handle_get_luma_bits_per_pixel(m_hImageHandle))
485 : {
486 0 : heif_image_handle_release(hThumbnailHandle);
487 0 : return;
488 : }
489 : #endif
490 :
491 4 : auto poOvrDS = std::make_unique<GDALHEIFDataset>();
492 2 : poOvrDS->m_hImageHandle = hThumbnailHandle;
493 2 : poOvrDS->m_bIsThumbnail = true;
494 2 : poOvrDS->nRasterXSize = heif_image_handle_get_width(hThumbnailHandle);
495 2 : poOvrDS->nRasterYSize = heif_image_handle_get_height(hThumbnailHandle);
496 9 : for (int i = 0; i < nBands; i++)
497 : {
498 7 : poOvrDS->SetBand(i + 1, new GDALHEIFRasterBand(poOvrDS.get(), i + 1));
499 : }
500 2 : m_apoOvrDS.push_back(std::move(poOvrDS));
501 : }
502 :
503 : /************************************************************************/
504 : /* HEIFDriverIdentify() */
505 : /************************************************************************/
506 :
507 15 : static int HEIFDriverIdentify(GDALOpenInfo *poOpenInfo)
508 :
509 : {
510 15 : if (STARTS_WITH_CI(poOpenInfo->pszFilename, "HEIF:"))
511 8 : return true;
512 :
513 7 : if (poOpenInfo->nHeaderBytes < 12 || poOpenInfo->fpL == nullptr)
514 0 : return false;
515 : #if LIBHEIF_NUMERIC_VERSION >= BUILD_LIBHEIF_VERSION(1, 4, 0)
516 : const auto res =
517 7 : heif_check_filetype(poOpenInfo->pabyHeader, poOpenInfo->nHeaderBytes);
518 7 : if (res == heif_filetype_yes_supported)
519 7 : return TRUE;
520 0 : if (res == heif_filetype_maybe)
521 0 : return -1;
522 0 : if (res == heif_filetype_yes_unsupported)
523 : {
524 0 : CPLDebug("HEIF", "HEIF file, but not supported by libheif");
525 : }
526 0 : return FALSE;
527 : #else
528 : // Simplistic test...
529 : const unsigned char abySig1[] = "\x00"
530 : "\x00"
531 : "\x00"
532 : "\x20"
533 : "ftypheic";
534 : const unsigned char abySig2[] = "\x00"
535 : "\x00"
536 : "\x00"
537 : "\x18"
538 : "ftypheic";
539 : const unsigned char abySig3[] = "\x00"
540 : "\x00"
541 : "\x00"
542 : "\x18"
543 : "ftypmif1"
544 : "\x00"
545 : "\x00"
546 : "\x00"
547 : "\x00"
548 : "mif1heic";
549 : return (poOpenInfo->nHeaderBytes >= static_cast<int>(sizeof(abySig1)) &&
550 : memcmp(poOpenInfo->pabyHeader, abySig1, sizeof(abySig1)) == 0) ||
551 : (poOpenInfo->nHeaderBytes >= static_cast<int>(sizeof(abySig2)) &&
552 : memcmp(poOpenInfo->pabyHeader, abySig2, sizeof(abySig2)) == 0) ||
553 : (poOpenInfo->nHeaderBytes >= static_cast<int>(sizeof(abySig3)) &&
554 : memcmp(poOpenInfo->pabyHeader, abySig3, sizeof(abySig3)) == 0);
555 : #endif
556 : }
557 :
558 : /************************************************************************/
559 : /* Open() */
560 : /************************************************************************/
561 :
562 15 : GDALDataset *GDALHEIFDataset::Open(GDALOpenInfo *poOpenInfo)
563 : {
564 15 : if (!HEIFDriverIdentify(poOpenInfo))
565 0 : return nullptr;
566 15 : if (poOpenInfo->eAccess == GA_Update)
567 : {
568 0 : CPLError(CE_Failure, CPLE_NotSupported,
569 : "Update of existing HEIF file not supported");
570 0 : return nullptr;
571 : }
572 :
573 30 : auto poDS = std::make_unique<GDALHEIFDataset>();
574 15 : if (!poDS->Init(poOpenInfo))
575 6 : return nullptr;
576 :
577 9 : return poDS.release();
578 : }
579 :
580 : /************************************************************************/
581 : /* GDALHEIFRasterBand() */
582 : /************************************************************************/
583 :
584 37 : GDALHEIFRasterBand::GDALHEIFRasterBand(GDALHEIFDataset *poDSIn, int nBandIn)
585 : {
586 37 : poDS = poDSIn;
587 37 : nBand = nBandIn;
588 :
589 37 : eDataType = GDT_Byte;
590 : #if LIBHEIF_NUMERIC_VERSION >= BUILD_LIBHEIF_VERSION(1, 4, 0)
591 : const int nBits =
592 37 : heif_image_handle_get_luma_bits_per_pixel(poDSIn->m_hImageHandle);
593 37 : if (nBits > 8)
594 : {
595 7 : eDataType = GDT_UInt16;
596 : }
597 37 : if (nBits != 8 && nBits != 16)
598 : {
599 7 : GDALRasterBand::SetMetadataItem("NBITS", CPLSPrintf("%d", nBits),
600 : "IMAGE_STRUCTURE");
601 : }
602 : #endif
603 37 : nBlockXSize = poDS->GetRasterXSize();
604 37 : nBlockYSize = 1;
605 37 : }
606 :
607 : /************************************************************************/
608 : /* IReadBlock() */
609 : /************************************************************************/
610 :
611 480 : CPLErr GDALHEIFRasterBand::IReadBlock(int, int nBlockYOff, void *pImage)
612 : {
613 480 : GDALHEIFDataset *poGDS = static_cast<GDALHEIFDataset *>(poDS);
614 480 : if (poGDS->m_bFailureDecoding)
615 0 : return CE_Failure;
616 480 : const int nBands = poGDS->GetRasterCount();
617 480 : if (poGDS->m_hImage == nullptr)
618 : {
619 : auto err = heif_decode_image(
620 5 : poGDS->m_hImageHandle, &(poGDS->m_hImage), heif_colorspace_RGB,
621 : nBands == 3
622 : ? (
623 : #if LIBHEIF_NUMERIC_VERSION >= BUILD_LIBHEIF_VERSION(1, 4, 0)
624 4 : eDataType == GDT_UInt16 ?
625 : #if CPL_IS_LSB
626 : heif_chroma_interleaved_RRGGBB_LE
627 : #else
628 : heif_chroma_interleaved_RRGGBB_BE
629 : #endif
630 : :
631 : #endif
632 : heif_chroma_interleaved_RGB)
633 : : (
634 : #if LIBHEIF_NUMERIC_VERSION >= BUILD_LIBHEIF_VERSION(1, 4, 0)
635 1 : eDataType == GDT_UInt16
636 1 : ?
637 : #if CPL_IS_LSB
638 : heif_chroma_interleaved_RRGGBBAA_LE
639 : #else
640 : heif_chroma_interleaved_RRGGBBAA_BE
641 : #endif
642 : :
643 : #endif
644 : heif_chroma_interleaved_RGBA),
645 10 : nullptr);
646 5 : if (err.code != heif_error_Ok)
647 : {
648 0 : CPLError(CE_Failure, CPLE_AppDefined, "%s",
649 0 : err.message ? err.message : "Cannot decode image");
650 0 : poGDS->m_bFailureDecoding = true;
651 0 : return CE_Failure;
652 : }
653 10 : const int nBitsPerPixel = heif_image_get_bits_per_pixel(
654 5 : poGDS->m_hImage, heif_channel_interleaved);
655 5 : if (nBitsPerPixel != nBands * GDALGetDataTypeSize(eDataType))
656 : {
657 0 : CPLError(CE_Failure, CPLE_AppDefined,
658 : "Unexpected bits_per_pixel = %d value", nBitsPerPixel);
659 0 : poGDS->m_bFailureDecoding = true;
660 0 : return CE_Failure;
661 : }
662 : }
663 :
664 480 : int nStride = 0;
665 960 : const uint8_t *pSrcData = heif_image_get_plane_readonly(
666 480 : poGDS->m_hImage, heif_channel_interleaved, &nStride);
667 480 : pSrcData += nBlockYOff * nStride;
668 480 : if (eDataType == GDT_Byte)
669 : {
670 21016 : for (int i = 0; i < nBlockXSize; i++)
671 20736 : (static_cast<GByte *>(pImage))[i] =
672 20736 : pSrcData[nBand - 1 + i * nBands];
673 : }
674 : else
675 : {
676 80200 : for (int i = 0; i < nBlockXSize; i++)
677 80000 : (static_cast<GUInt16 *>(pImage))[i] =
678 : (reinterpret_cast<const GUInt16 *>(
679 80000 : pSrcData))[nBand - 1 + i * nBands];
680 : }
681 :
682 480 : return CE_None;
683 : }
684 :
685 : /************************************************************************/
686 : /* GDALRegister_HEIF() */
687 : /************************************************************************/
688 :
689 6 : void GDALRegister_HEIF()
690 :
691 : {
692 6 : if (!GDAL_CHECK_VERSION("HEIF driver"))
693 0 : return;
694 :
695 6 : if (GDALGetDriverByName(DRIVER_NAME) != nullptr)
696 0 : return;
697 :
698 6 : GDALDriver *poDriver = new GDALDriver();
699 6 : HEIFDriverSetCommonMetadata(poDriver);
700 :
701 6 : poDriver->pfnOpen = GDALHEIFDataset::Open;
702 :
703 6 : GetGDALDriverManager()->RegisterDriver(poDriver);
704 : }
|