Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: OpenGIS Simple Features Reference Implementation
4 : * Purpose: Implements Basis Universal / BASISU driver.
5 : * Author: Even Rouault, <even dot rouault at spatialys.com>
6 : *
7 : ******************************************************************************
8 : * Copyright (c) 2022, Even Rouault <even dot rouault at spatialys.com>
9 : *
10 : * SPDX-License-Identifier: MIT
11 : ****************************************************************************/
12 :
13 : #include "gdal_frmts.h"
14 : #include "gdal_pam.h"
15 : #include "common.h"
16 : #include "include_basisu_sdk.h"
17 : #include "basisudrivercore.h"
18 :
19 : #include <algorithm>
20 : #include <cstdlib>
21 : #include <limits>
22 :
23 : /************************************************************************/
24 : /* BASISUDataset */
25 : /************************************************************************/
26 :
27 : class BASISUDataset final : public GDALPamDataset
28 : {
29 : friend class BASISURasterBand;
30 :
31 : basist::basisu_transcoder m_transcoder{};
32 : basist::basisu_transcoder &m_transcoderRef;
33 : bool m_bHasDecodeRun = false;
34 : void *m_pEncodedData = nullptr;
35 : uint32_t m_nEncodedDataSize = 0;
36 : void *m_pDecodedData = nullptr;
37 : uint32_t m_nLineStride = 0;
38 : BASISUDataset *m_poParent = nullptr;
39 : uint32_t m_iImageIdx = 0;
40 : uint32_t m_iLevel = 0;
41 : std::vector<std::unique_ptr<BASISUDataset>> m_apoOverviewsDS{};
42 :
43 : void *GetDecodedData(uint32_t &nLineStride);
44 :
45 : CPL_DISALLOW_COPY_ASSIGN(BASISUDataset)
46 :
47 : public:
48 : ~BASISUDataset() override;
49 : BASISUDataset(uint32_t iImageIdx, void *pEncodedData,
50 : uint32_t nEncodedDataSize);
51 : BASISUDataset(BASISUDataset *poParent, uint32_t iLevel);
52 :
53 : static GDALDataset *Open(GDALOpenInfo *poOpenInfo);
54 : static GDALDataset *CreateCopy(const char *pszFilename,
55 : GDALDataset *poSrcDS, int bStrict,
56 : char **papszOptions,
57 : GDALProgressFunc pfnProgress,
58 : void *pProgressData);
59 : };
60 :
61 : /************************************************************************/
62 : /* BASISURasterBand */
63 : /************************************************************************/
64 :
65 : class BASISURasterBand final : public GDALPamRasterBand
66 : {
67 : protected:
68 : CPLErr IReadBlock(int nBlockXOff, int nBlockYOff, void *pImage) override;
69 :
70 : public:
71 : BASISURasterBand(BASISUDataset *poDSIn, int nBandIn);
72 :
73 : int GetOverviewCount() override;
74 : GDALRasterBand *GetOverview(int nIdx) override;
75 : };
76 :
77 : /************************************************************************/
78 : /* BASISUDataset() */
79 : /************************************************************************/
80 :
81 44 : BASISUDataset::BASISUDataset(uint32_t iImageIdx, void *pEncodedData,
82 44 : uint32_t nEncodedDataSize)
83 44 : : m_transcoderRef(m_transcoder), m_pEncodedData(pEncodedData),
84 44 : m_nEncodedDataSize(nEncodedDataSize), m_iImageIdx(iImageIdx)
85 : {
86 44 : }
87 :
88 : /************************************************************************/
89 : /* BASISUDataset() */
90 : /************************************************************************/
91 :
92 7 : BASISUDataset::BASISUDataset(BASISUDataset *poParent, uint32_t iLevel)
93 7 : : m_transcoderRef(poParent->m_transcoderRef), m_poParent(poParent),
94 7 : m_iImageIdx(poParent->m_iImageIdx), m_iLevel(iLevel)
95 : {
96 : basist::basisu_image_level_info level_info;
97 7 : CPL_IGNORE_RET_VAL(m_transcoderRef.get_image_level_info(
98 7 : m_poParent->m_pEncodedData, m_poParent->m_nEncodedDataSize, level_info,
99 : m_iImageIdx, m_iLevel));
100 7 : nRasterXSize = static_cast<int>(level_info.m_orig_width);
101 7 : nRasterYSize = static_cast<int>(level_info.m_orig_height);
102 7 : }
103 :
104 : /************************************************************************/
105 : /* ~BASISUDataset() */
106 : /************************************************************************/
107 :
108 102 : BASISUDataset::~BASISUDataset()
109 : {
110 51 : VSIFree(m_pEncodedData);
111 51 : VSIFree(m_pDecodedData);
112 102 : }
113 :
114 : /************************************************************************/
115 : /* GetDecodedData() */
116 : /************************************************************************/
117 :
118 2220 : void *BASISUDataset::GetDecodedData(uint32_t &nLineStride)
119 : {
120 2220 : if (m_bHasDecodeRun)
121 : {
122 2214 : nLineStride = m_nLineStride;
123 2214 : return m_pDecodedData;
124 : }
125 6 : m_bHasDecodeRun = true;
126 :
127 6 : GDALInitBasisUTranscoder();
128 :
129 : basist::basisu_image_level_info level_info;
130 6 : const auto poRefDS = m_poParent ? m_poParent : this;
131 6 : CPL_IGNORE_RET_VAL(m_transcoderRef.get_image_level_info(
132 6 : poRefDS->m_pEncodedData, poRefDS->m_nEncodedDataSize, level_info,
133 : m_iImageIdx, m_iLevel));
134 :
135 6 : if (!m_transcoderRef.start_transcoding(poRefDS->m_pEncodedData,
136 : poRefDS->m_nEncodedDataSize))
137 : {
138 0 : CPLError(CE_Failure, CPLE_AppDefined,
139 : "basisu_transcoder::start_transcoding() failed!");
140 0 : return nullptr;
141 : }
142 :
143 6 : m_pDecodedData = VSI_MALLOC3_VERBOSE(level_info.m_orig_width,
144 : level_info.m_orig_height, 4);
145 6 : if (m_pDecodedData == nullptr)
146 0 : return nullptr;
147 :
148 6 : constexpr basist::transcoder_texture_format transcoder_tex_fmt =
149 : basist::transcoder_texture_format::cTFRGBA32;
150 6 : if (!m_transcoderRef.transcode_image_level(
151 6 : poRefDS->m_pEncodedData, poRefDS->m_nEncodedDataSize, m_iImageIdx,
152 : m_iLevel, m_pDecodedData,
153 6 : level_info.m_orig_width * level_info.m_orig_height * 4,
154 : transcoder_tex_fmt))
155 : {
156 0 : CPLError(CE_Failure, CPLE_AppDefined,
157 : "basisu_transcoder::transcode_image_level() failed!");
158 0 : VSIFree(m_pDecodedData);
159 0 : m_pDecodedData = nullptr;
160 0 : return nullptr;
161 : }
162 :
163 6 : m_nLineStride = level_info.m_orig_width * 4;
164 6 : nLineStride = m_nLineStride;
165 6 : return m_pDecodedData;
166 : }
167 :
168 : /************************************************************************/
169 : /* BASISURasterBand() */
170 : /************************************************************************/
171 :
172 188 : BASISURasterBand::BASISURasterBand(BASISUDataset *poDSIn, int nBandIn)
173 : {
174 188 : poDS = poDSIn;
175 188 : nBand = nBandIn;
176 188 : nRasterXSize = poDSIn->GetRasterXSize();
177 188 : nRasterYSize = poDSIn->GetRasterYSize();
178 188 : nBlockXSize = nRasterXSize;
179 188 : nBlockYSize = 1;
180 188 : eDataType = GDT_Byte;
181 188 : SetColorInterpretation(
182 188 : static_cast<GDALColorInterp>(GCI_RedBand + nBandIn - 1));
183 188 : }
184 :
185 : /************************************************************************/
186 : /* IReadBlock() */
187 : /************************************************************************/
188 :
189 2220 : CPLErr BASISURasterBand::IReadBlock(int /*nBlockXOff*/, int nBlockYOff,
190 : void *pImage)
191 : {
192 2220 : auto poGDS = cpl::down_cast<BASISUDataset *>(poDS);
193 2220 : uint32_t nLineStride = 0;
194 2220 : void *decoded_data = poGDS->GetDecodedData(nLineStride);
195 2220 : if (decoded_data == nullptr)
196 0 : return CE_Failure;
197 :
198 2220 : GDALCopyWords(static_cast<GByte *>(decoded_data) +
199 2220 : nBlockYOff * nLineStride + nBand - 1,
200 : GDT_Byte, 4, pImage, GDT_Byte, 1, nBlockXSize);
201 2220 : return CE_None;
202 : }
203 :
204 : /************************************************************************/
205 : /* GetOverviewCount() */
206 : /************************************************************************/
207 :
208 6 : int BASISURasterBand::GetOverviewCount()
209 : {
210 6 : auto poGDS = cpl::down_cast<BASISUDataset *>(poDS);
211 6 : return static_cast<int>(poGDS->m_apoOverviewsDS.size());
212 : }
213 :
214 : /************************************************************************/
215 : /* GetOverview() */
216 : /************************************************************************/
217 :
218 3 : GDALRasterBand *BASISURasterBand::GetOverview(int nIdx)
219 : {
220 3 : if (nIdx < 0 || nIdx >= GetOverviewCount())
221 2 : return nullptr;
222 1 : auto poGDS = cpl::down_cast<BASISUDataset *>(poDS);
223 1 : return poGDS->m_apoOverviewsDS[nIdx]->GetRasterBand(nBand);
224 : }
225 :
226 : /************************************************************************/
227 : /* Open() */
228 : /************************************************************************/
229 :
230 47 : GDALDataset *BASISUDataset::Open(GDALOpenInfo *poOpenInfo)
231 : {
232 47 : if (!BASISUDriverIdentify(poOpenInfo) || poOpenInfo->eAccess == GA_Update)
233 0 : return nullptr;
234 :
235 47 : VSILFILE *fpL = nullptr;
236 47 : uint32_t nImageIdx = static_cast<uint32_t>(-1);
237 47 : if (STARTS_WITH(poOpenInfo->pszFilename, "BASISU:"))
238 : {
239 : const CPLStringList aosTokens(CSLTokenizeString2(
240 6 : poOpenInfo->pszFilename, ":", CSLT_HONOURSTRINGS));
241 6 : if (aosTokens.size() != 3)
242 2 : return nullptr;
243 4 : fpL = VSIFOpenL(aosTokens[1], "rb");
244 4 : if (fpL == nullptr)
245 : {
246 1 : CPLError(CE_Failure, CPLE_FileIO, "Cannot open %s", aosTokens[1]);
247 1 : return nullptr;
248 : }
249 3 : nImageIdx = static_cast<uint32_t>(atoi(aosTokens[2]));
250 : }
251 44 : GIntBig nMaxSize = std::strtoull(
252 44 : CPLGetConfigOption("BASISU_MAX_FILE_SIZE", "0"), nullptr, 10);
253 44 : constexpr GIntBig BASISU_LIMIT = std::numeric_limits<uint32_t>::max();
254 44 : if (nMaxSize == 0 || nMaxSize > BASISU_LIMIT)
255 44 : nMaxSize = BASISU_LIMIT;
256 44 : GByte *pabyRet = nullptr;
257 44 : vsi_l_offset nSizeLarge = 0;
258 44 : int nRet = VSIIngestFile(fpL ? fpL : poOpenInfo->fpL, nullptr, &pabyRet,
259 : &nSizeLarge, nMaxSize);
260 44 : if (fpL != nullptr)
261 3 : VSIFCloseL(fpL);
262 44 : if (!nRet)
263 : {
264 0 : return nullptr;
265 : }
266 44 : const uint32_t nSize = static_cast<uint32_t>(nSizeLarge);
267 :
268 : auto poDS = std::make_unique<BASISUDataset>(
269 88 : nImageIdx != static_cast<uint32_t>(-1) ? nImageIdx : 0, pabyRet, nSize);
270 44 : auto &transcoder = poDS->m_transcoder;
271 88 : basist::basisu_file_info file_info;
272 44 : if (!transcoder.get_file_info(pabyRet, nSize, file_info))
273 : {
274 0 : CPLError(CE_Failure, CPLE_AppDefined,
275 : "basisu_transcoder::get_file_info() failed! "
276 : "File either uses an unsupported feature or is invalid");
277 0 : return nullptr;
278 : }
279 44 : if (nImageIdx == static_cast<uint32_t>(-1) && file_info.m_total_images > 1)
280 : {
281 2 : CPLStringList aosSubdatasets;
282 3 : for (uint32_t iImageIdx = 0; iImageIdx < file_info.m_total_images;
283 : ++iImageIdx)
284 : {
285 : aosSubdatasets.SetNameValue(
286 : CPLSPrintf("SUBDATASET_%d_NAME", iImageIdx + 1),
287 : CPLSPrintf("BASISU:\"%s\":%u", poOpenInfo->pszFilename,
288 2 : iImageIdx));
289 : aosSubdatasets.SetNameValue(
290 : CPLSPrintf("SUBDATASET_%d_DESC", iImageIdx + 1),
291 : CPLSPrintf("Image %u of %s", iImageIdx,
292 2 : poOpenInfo->pszFilename));
293 : }
294 1 : poDS->nRasterXSize = 0;
295 1 : poDS->nRasterYSize = 0;
296 1 : poDS->SetMetadata(aosSubdatasets.List(), "SUBDATASETS");
297 :
298 1 : poDS->SetPamFlags(poDS->GetPamFlags() & ~GPF_DIRTY);
299 :
300 1 : return poDS.release();
301 : }
302 :
303 : basist::basisu_image_info image_info;
304 43 : if (!transcoder.get_image_info(pabyRet, nSize, image_info,
305 43 : poDS->m_iImageIdx))
306 : {
307 1 : CPLError(CE_Failure, CPLE_AppDefined,
308 : "basisu_transcoder::get_image_info() failed");
309 1 : return nullptr;
310 : }
311 42 : poDS->nRasterXSize = static_cast<int>(image_info.m_orig_width);
312 42 : poDS->nRasterYSize = static_cast<int>(image_info.m_orig_height);
313 :
314 42 : switch (file_info.m_tex_format)
315 : {
316 31 : case basist::basis_tex_format::cETC1S:
317 31 : poDS->SetMetadataItem("COMPRESSION", "ETC1S", "IMAGE_STRUCTURE");
318 31 : break;
319 11 : case basist::basis_tex_format::cUASTC4x4:
320 11 : poDS->SetMetadataItem("COMPRESSION", "UASTC", "IMAGE_STRUCTURE");
321 11 : break;
322 : }
323 :
324 42 : const int l_nBands = 3 + (image_info.m_alpha_flag ? 1 : 0);
325 202 : for (int i = 1; i <= l_nBands; ++i)
326 : {
327 160 : poDS->SetBand(i, new BASISURasterBand(poDS.get(), i));
328 : }
329 :
330 42 : const uint32_t nLevels = file_info.m_image_mipmap_levels[poDS->m_iImageIdx];
331 49 : for (uint32_t level_index = 1; level_index < nLevels; ++level_index)
332 : {
333 : basist::basisu_image_level_info level_info;
334 7 : if (transcoder.get_image_level_info(pabyRet, nSize, level_info,
335 7 : poDS->m_iImageIdx, level_index))
336 : {
337 : auto poOverviewDS =
338 14 : std::make_unique<BASISUDataset>(poDS.get(), level_index);
339 35 : for (int i = 1; i <= l_nBands; ++i)
340 : {
341 56 : poOverviewDS->SetBand(
342 28 : i, new BASISURasterBand(poOverviewDS.get(), i));
343 : }
344 7 : poDS->m_apoOverviewsDS.emplace_back(std::move(poOverviewDS));
345 : }
346 : }
347 :
348 42 : poDS->SetPamFlags(poDS->GetPamFlags() & ~GPF_DIRTY);
349 :
350 : // Initialize any PAM information.
351 42 : poDS->SetDescription(poOpenInfo->pszFilename);
352 42 : poDS->TryLoadXML(poOpenInfo->GetSiblingFiles());
353 :
354 42 : return poDS.release();
355 : }
356 :
357 : /************************************************************************/
358 : /* CreateCopy() */
359 : /************************************************************************/
360 :
361 54 : GDALDataset *BASISUDataset::CreateCopy(const char *pszFilename,
362 : GDALDataset *poSrcDS, int /*bStrict*/,
363 : char **papszOptions,
364 : GDALProgressFunc pfnProgress,
365 : void *pProgressData)
366 : {
367 54 : if (!GDAL_KTX2_BASISU_CreateCopy(pszFilename, poSrcDS,
368 : false, // bIsKTX2
369 : papszOptions, pfnProgress, pProgressData))
370 : {
371 31 : return nullptr;
372 : }
373 46 : GDALOpenInfo oOpenInfo(pszFilename, GA_ReadOnly);
374 23 : return Open(&oOpenInfo);
375 : }
376 :
377 : /************************************************************************/
378 : /* GDALRegister_BASISU() */
379 : /************************************************************************/
380 :
381 11 : void GDALRegister_BASISU()
382 : {
383 11 : if (GDALGetDriverByName(BASISU_DRIVER_NAME) != nullptr)
384 0 : return;
385 :
386 11 : GDALDriver *poDriver = new GDALDriver();
387 11 : BASISUDriverSetCommonMetadata(poDriver);
388 :
389 11 : poDriver->pfnOpen = BASISUDataset::Open;
390 11 : poDriver->pfnCreateCopy = BASISUDataset::CreateCopy;
391 :
392 11 : GetGDALDriverManager()->RegisterDriver(poDriver);
393 : }
|