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