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