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