Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: OpenGIS Simple Features Reference Implementation
4 : * Purpose: Implements Basis Universal / KTX2 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 "ktx2drivercore.h"
17 :
18 : #include <algorithm>
19 : #include <cstdlib>
20 : #include <limits>
21 :
22 : /************************************************************************/
23 : /* KTX2Dataset */
24 : /************************************************************************/
25 :
26 : class KTX2Dataset final : public GDALPamDataset
27 : {
28 : friend class KTX2RasterBand;
29 :
30 : basist::ktx2_transcoder m_transcoder{};
31 : basist::ktx2_transcoder &m_transcoderRef;
32 : bool m_bHasDecodeRun = false;
33 : void *m_pEncodedData = nullptr;
34 : void *m_pDecodedData = nullptr;
35 : uint32_t m_nLineStride = 0;
36 : uint32_t m_iLayer = 0;
37 : uint32_t m_iFace = 0;
38 : uint32_t m_iLevel = 0;
39 : std::vector<std::unique_ptr<KTX2Dataset>> m_apoOverviewsDS{};
40 :
41 : void *GetDecodedData(uint32_t &nLineStride);
42 :
43 : CPL_DISALLOW_COPY_ASSIGN(KTX2Dataset)
44 :
45 : public:
46 : ~KTX2Dataset() override;
47 : KTX2Dataset(uint32_t iLayer, uint32_t iFace, void *pEncodedData);
48 : KTX2Dataset(KTX2Dataset *poParent, uint32_t iLevel);
49 :
50 : static GDALDataset *Open(GDALOpenInfo *poOpenInfo);
51 : static GDALDataset *CreateCopy(const char *pszFilename,
52 : GDALDataset *poSrcDS, int bStrict,
53 : char **papszOptions,
54 : GDALProgressFunc pfnProgress,
55 : void *pProgressData);
56 : };
57 :
58 : /************************************************************************/
59 : /* KTX2RasterBand */
60 : /************************************************************************/
61 :
62 : class KTX2RasterBand final : public GDALPamRasterBand
63 : {
64 : protected:
65 : CPLErr IReadBlock(int nBlockXOff, int nBlockYOff, void *pImage) override;
66 :
67 : public:
68 : KTX2RasterBand(KTX2Dataset *poDSIn, int nBandIn);
69 :
70 : int GetOverviewCount() override;
71 : GDALRasterBand *GetOverview(int nIdx) override;
72 : };
73 :
74 : /************************************************************************/
75 : /* KTX2Dataset() */
76 : /************************************************************************/
77 :
78 49 : KTX2Dataset::KTX2Dataset(uint32_t iLayer, uint32_t iFace, void *pEncodedData)
79 49 : : m_transcoderRef(m_transcoder), m_pEncodedData(pEncodedData),
80 49 : m_iLayer(iLayer), m_iFace(iFace)
81 : {
82 49 : }
83 :
84 : /************************************************************************/
85 : /* KTX2Dataset() */
86 : /************************************************************************/
87 :
88 7 : KTX2Dataset::KTX2Dataset(KTX2Dataset *poParent, uint32_t iLevel)
89 7 : : m_transcoderRef(poParent->m_transcoderRef), m_iLayer(poParent->m_iLayer),
90 7 : m_iFace(poParent->m_iFace), m_iLevel(iLevel)
91 : {
92 : basist::ktx2_image_level_info level_info;
93 7 : CPL_IGNORE_RET_VAL(m_transcoderRef.get_image_level_info(
94 : level_info, m_iLevel, m_iLayer, m_iFace));
95 7 : nRasterXSize = static_cast<int>(level_info.m_orig_width);
96 7 : nRasterYSize = static_cast<int>(level_info.m_orig_height);
97 7 : }
98 :
99 : /************************************************************************/
100 : /* ~KTX2Dataset() */
101 : /************************************************************************/
102 :
103 112 : KTX2Dataset::~KTX2Dataset()
104 : {
105 56 : VSIFree(m_pEncodedData);
106 56 : VSIFree(m_pDecodedData);
107 112 : }
108 :
109 : /************************************************************************/
110 : /* GetDecodedData() */
111 : /************************************************************************/
112 :
113 2220 : void *KTX2Dataset::GetDecodedData(uint32_t &nLineStride)
114 : {
115 2220 : if (m_bHasDecodeRun)
116 : {
117 2214 : nLineStride = m_nLineStride;
118 2214 : return m_pDecodedData;
119 : }
120 6 : m_bHasDecodeRun = true;
121 :
122 6 : GDALInitBasisUTranscoder();
123 :
124 : basist::ktx2_image_level_info level_info;
125 6 : if (!m_transcoderRef.get_image_level_info(level_info, m_iLevel, m_iLayer,
126 : m_iFace))
127 : {
128 0 : CPLError(CE_Failure, CPLE_AppDefined,
129 : "ktx2_transcoder::get_image_level_info() failed!");
130 0 : return nullptr;
131 : }
132 :
133 6 : if (!m_transcoderRef.start_transcoding())
134 : {
135 0 : CPLError(CE_Failure, CPLE_AppDefined,
136 : "ktx2_transcoder::start_transcoding() failed!");
137 0 : return nullptr;
138 : }
139 :
140 6 : m_pDecodedData = VSI_MALLOC3_VERBOSE(level_info.m_orig_width,
141 : level_info.m_orig_height, 4);
142 6 : if (m_pDecodedData == nullptr)
143 0 : return nullptr;
144 :
145 6 : constexpr basist::transcoder_texture_format transcoder_tex_fmt =
146 : basist::transcoder_texture_format::cTFRGBA32;
147 6 : if (!m_transcoderRef.transcode_image_level(
148 : m_iLevel, m_iLayer, m_iFace, m_pDecodedData,
149 6 : level_info.m_orig_width * level_info.m_orig_height * 4,
150 : transcoder_tex_fmt))
151 : {
152 0 : CPLError(CE_Failure, CPLE_AppDefined,
153 : "ktx2_transcoder::transcode_image_level() failed!");
154 0 : VSIFree(m_pDecodedData);
155 0 : m_pDecodedData = nullptr;
156 0 : return nullptr;
157 : }
158 :
159 6 : m_nLineStride = level_info.m_orig_width * 4;
160 6 : nLineStride = m_nLineStride;
161 6 : return m_pDecodedData;
162 : }
163 :
164 : /************************************************************************/
165 : /* KTX2RasterBand() */
166 : /************************************************************************/
167 :
168 204 : KTX2RasterBand::KTX2RasterBand(KTX2Dataset *poDSIn, int nBandIn)
169 : {
170 204 : poDS = poDSIn;
171 204 : nBand = nBandIn;
172 204 : nRasterXSize = poDSIn->GetRasterXSize();
173 204 : nRasterYSize = poDSIn->GetRasterYSize();
174 204 : nBlockXSize = nRasterXSize;
175 204 : nBlockYSize = 1;
176 204 : eDataType = GDT_Byte;
177 204 : SetColorInterpretation(
178 204 : static_cast<GDALColorInterp>(GCI_RedBand + nBandIn - 1));
179 204 : }
180 :
181 : /************************************************************************/
182 : /* IReadBlock() */
183 : /************************************************************************/
184 :
185 2220 : CPLErr KTX2RasterBand::IReadBlock(int /*nBlockXOff*/, int nBlockYOff,
186 : void *pImage)
187 : {
188 2220 : auto poGDS = cpl::down_cast<KTX2Dataset *>(poDS);
189 2220 : uint32_t nLineStride = 0;
190 2220 : void *decoded_data = poGDS->GetDecodedData(nLineStride);
191 2220 : if (decoded_data == nullptr)
192 0 : return CE_Failure;
193 :
194 2220 : GDALCopyWords(static_cast<GByte *>(decoded_data) +
195 2220 : nBlockYOff * nLineStride + nBand - 1,
196 : GDT_Byte, 4, pImage, GDT_Byte, 1, nBlockXSize);
197 2220 : return CE_None;
198 : }
199 :
200 : /************************************************************************/
201 : /* GetOverviewCount() */
202 : /************************************************************************/
203 :
204 6 : int KTX2RasterBand::GetOverviewCount()
205 : {
206 6 : auto poGDS = cpl::down_cast<KTX2Dataset *>(poDS);
207 6 : return static_cast<int>(poGDS->m_apoOverviewsDS.size());
208 : }
209 :
210 : /************************************************************************/
211 : /* GetOverview() */
212 : /************************************************************************/
213 :
214 3 : GDALRasterBand *KTX2RasterBand::GetOverview(int nIdx)
215 : {
216 3 : if (nIdx < 0 || nIdx >= GetOverviewCount())
217 2 : return nullptr;
218 1 : auto poGDS = cpl::down_cast<KTX2Dataset *>(poDS);
219 1 : return poGDS->m_apoOverviewsDS[nIdx]->GetRasterBand(nBand);
220 : }
221 :
222 : /************************************************************************/
223 : /* Open() */
224 : /************************************************************************/
225 :
226 53 : GDALDataset *KTX2Dataset::Open(GDALOpenInfo *poOpenInfo)
227 : {
228 53 : if (!KTX2DriverIdentify(poOpenInfo) || poOpenInfo->eAccess == GA_Update)
229 0 : return nullptr;
230 :
231 53 : VSILFILE *fpL = nullptr;
232 53 : uint32_t nLayer = static_cast<uint32_t>(-1);
233 53 : uint32_t nFace = static_cast<uint32_t>(-1);
234 53 : if (STARTS_WITH(poOpenInfo->pszFilename, "KTX2:"))
235 : {
236 : const CPLStringList aosTokens(CSLTokenizeString2(
237 8 : poOpenInfo->pszFilename, ":", CSLT_HONOURSTRINGS));
238 8 : if (aosTokens.size() != 4)
239 3 : return nullptr;
240 5 : fpL = VSIFOpenL(aosTokens[1], "rb");
241 5 : if (fpL == nullptr)
242 : {
243 1 : CPLError(CE_Failure, CPLE_FileIO, "Cannot open %s", aosTokens[1]);
244 1 : return nullptr;
245 : }
246 4 : nLayer = static_cast<uint32_t>(atoi(aosTokens[2]));
247 4 : nFace = static_cast<uint32_t>(atoi(aosTokens[3]));
248 : }
249 49 : GIntBig nMaxSize = std::strtoull(
250 49 : CPLGetConfigOption("KTX2_MAX_FILE_SIZE", "0"), nullptr, 10);
251 49 : constexpr GIntBig KTX2_LIMIT = std::numeric_limits<uint32_t>::max();
252 49 : if (nMaxSize == 0 || nMaxSize > KTX2_LIMIT)
253 49 : nMaxSize = KTX2_LIMIT;
254 49 : GByte *pabyRet = nullptr;
255 49 : vsi_l_offset nSizeLarge = 0;
256 49 : int nRet = VSIIngestFile(fpL ? fpL : poOpenInfo->fpL, nullptr, &pabyRet,
257 : &nSizeLarge, nMaxSize);
258 49 : if (fpL != nullptr)
259 4 : VSIFCloseL(fpL);
260 49 : if (!nRet)
261 : {
262 0 : return nullptr;
263 : }
264 49 : const uint32_t nSize = static_cast<uint32_t>(nSizeLarge);
265 :
266 : auto poDS = std::make_unique<KTX2Dataset>(
267 49 : nLayer != static_cast<uint32_t>(-1) ? nLayer : 0,
268 147 : nFace != static_cast<uint32_t>(-1) ? nFace : 0, pabyRet);
269 49 : auto &transcoder = poDS->m_transcoder;
270 49 : const bool bInit = transcoder.init(pabyRet, nSize);
271 49 : if (!bInit)
272 : {
273 0 : if (nSize >= sizeof(basist::ktx2_header))
274 : {
275 : #define DEBUG_u32(x) \
276 : CPLDebug("KTX2", #x " = %u", \
277 : static_cast<uint32_t>(transcoder.get_header().m_##x))
278 0 : DEBUG_u32(vk_format);
279 0 : DEBUG_u32(type_size);
280 0 : DEBUG_u32(pixel_width);
281 0 : DEBUG_u32(pixel_height);
282 0 : DEBUG_u32(pixel_depth);
283 0 : DEBUG_u32(layer_count);
284 0 : DEBUG_u32(face_count);
285 0 : DEBUG_u32(level_count);
286 0 : DEBUG_u32(supercompression_scheme);
287 0 : DEBUG_u32(dfd_byte_offset);
288 0 : DEBUG_u32(dfd_byte_length);
289 : }
290 0 : CPLError(CE_Failure, CPLE_AppDefined,
291 : "ktx2_transcoder::init() failed! "
292 : "File either uses an unsupported feature or is invalid");
293 0 : return nullptr;
294 : }
295 :
296 : const uint32_t nLayers =
297 49 : std::max(1U, transcoder.get_layers()); // get_layers() may return 0
298 49 : const uint32_t nFaces = transcoder.get_faces();
299 49 : CPLDebug("KTX2", "levels = %u, faces = %u, layers = %u",
300 : transcoder.get_levels(), nFaces, nLayers);
301 :
302 49 : switch (transcoder.get_format())
303 : {
304 34 : case basist::basis_tex_format::cETC1S:
305 34 : poDS->SetMetadataItem("COMPRESSION", "ETC1S", "IMAGE_STRUCTURE");
306 34 : break;
307 15 : case basist::basis_tex_format::cUASTC4x4:
308 15 : poDS->SetMetadataItem("COMPRESSION", "UASTC", "IMAGE_STRUCTURE");
309 15 : break;
310 : }
311 :
312 49 : if (nLayer == static_cast<uint32_t>(-1) && (nFaces >= 2 || nLayers >= 2))
313 : {
314 2 : CPLStringList aosSubdatasets;
315 1 : int nSubDS = 1;
316 3 : for (uint32_t iLayer = 0; iLayer < nLayers; ++iLayer)
317 : {
318 4 : for (uint32_t iFace = 0; iFace < nFaces; ++iFace)
319 : {
320 : aosSubdatasets.SetNameValue(
321 : CPLSPrintf("SUBDATASET_%d_NAME", nSubDS),
322 : CPLSPrintf("KTX2:\"%s\":%u:%u", poOpenInfo->pszFilename,
323 2 : iLayer, iFace));
324 : aosSubdatasets.SetNameValue(
325 : CPLSPrintf("SUBDATASET_%d_DESC", nSubDS),
326 : CPLSPrintf("Layer %u, face %u of %s", iLayer, iFace,
327 2 : poOpenInfo->pszFilename));
328 2 : nSubDS++;
329 : }
330 : }
331 1 : poDS->nRasterXSize = 0;
332 1 : poDS->nRasterYSize = 0;
333 1 : poDS->SetMetadata(aosSubdatasets.List(), "SUBDATASETS");
334 :
335 1 : poDS->SetPamFlags(poDS->GetPamFlags() & ~GPF_DIRTY);
336 :
337 1 : return poDS.release();
338 : }
339 48 : else if (nLayer != static_cast<uint32_t>(-1) && nLayer >= nLayers)
340 : {
341 1 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid layer number: %u",
342 : nLayer);
343 1 : return nullptr;
344 : }
345 47 : else if (nFace != static_cast<uint32_t>(-1) && nFace >= nFaces)
346 : {
347 1 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid face number: %u", nFace);
348 1 : return nullptr;
349 : }
350 :
351 46 : poDS->nRasterXSize = transcoder.get_width();
352 46 : poDS->nRasterYSize = transcoder.get_height();
353 :
354 46 : const int l_nBands = 3 + (transcoder.get_has_alpha() ? 1 : 0);
355 222 : for (int i = 1; i <= l_nBands; ++i)
356 : {
357 176 : poDS->SetBand(i, new KTX2RasterBand(poDS.get(), i));
358 : }
359 :
360 99 : for (uint32_t level_index = 0; level_index < transcoder.get_levels();
361 : ++level_index)
362 : {
363 : basist::ktx2_image_level_info level_info;
364 53 : uint32_t layer_index = 0;
365 53 : uint32_t face_index = 0;
366 53 : if (transcoder.get_image_level_info(level_info, level_index,
367 : layer_index, face_index))
368 : {
369 53 : CPLDebug(
370 : "KTX2",
371 : "level %u: width=%u, orig_width=%u, height=%u, orig_height=%u",
372 : level_index, level_info.m_width, level_info.m_orig_width,
373 : level_info.m_height, level_info.m_orig_height);
374 :
375 53 : if (level_index > 0)
376 : {
377 : auto poOverviewDS =
378 14 : std::make_unique<KTX2Dataset>(poDS.get(), level_index);
379 35 : for (int i = 1; i <= l_nBands; ++i)
380 : {
381 56 : poOverviewDS->SetBand(
382 28 : i, new KTX2RasterBand(poOverviewDS.get(), i));
383 : }
384 7 : poDS->m_apoOverviewsDS.emplace_back(std::move(poOverviewDS));
385 : }
386 : }
387 : }
388 :
389 46 : poDS->SetPamFlags(poDS->GetPamFlags() & ~GPF_DIRTY);
390 :
391 : // Initialize any PAM information.
392 46 : poDS->SetDescription(poOpenInfo->pszFilename);
393 46 : poDS->TryLoadXML(poOpenInfo->GetSiblingFiles());
394 :
395 46 : return poDS.release();
396 : }
397 :
398 : /************************************************************************/
399 : /* CreateCopy() */
400 : /************************************************************************/
401 :
402 56 : GDALDataset *KTX2Dataset::CreateCopy(const char *pszFilename,
403 : GDALDataset *poSrcDS, int /*bStrict*/,
404 : char **papszOptions,
405 : GDALProgressFunc pfnProgress,
406 : void *pProgressData)
407 : {
408 56 : if (!GDAL_KTX2_BASISU_CreateCopy(pszFilename, poSrcDS,
409 : true, // bIsKTX2
410 : papszOptions, pfnProgress, pProgressData))
411 : {
412 31 : return nullptr;
413 : }
414 50 : GDALOpenInfo oOpenInfo(pszFilename, GA_ReadOnly);
415 25 : return Open(&oOpenInfo);
416 : }
417 :
418 : /************************************************************************/
419 : /* GDALRegister_KTX2() */
420 : /************************************************************************/
421 :
422 10 : void GDALRegister_KTX2()
423 : {
424 10 : if (GDALGetDriverByName(KTX2_DRIVER_NAME) != nullptr)
425 0 : return;
426 :
427 10 : GDALDriver *poDriver = new GDALDriver();
428 10 : KTX2DriverSetCommonMetadata(poDriver);
429 :
430 10 : poDriver->pfnOpen = KTX2Dataset::Open;
431 10 : poDriver->pfnCreateCopy = KTX2Dataset::CreateCopy;
432 :
433 10 : GetGDALDriverManager()->RegisterDriver(poDriver);
434 : }
|