1/* -*- tab-width: 4; -*- */
2/* vi: set sw=2 ts=4 expandtab: */
3
4/*
5 * Copyright 2019-2020 The Khronos Group Inc.
6 * SPDX-License-Identifier: Apache-2.0
7 */
8
9/**
10 * @internal
11 * @file basis_transcode.cpp
12 * @~English
13 *
14 * @brief Functions for transcoding Basis Universal BasisLZ/ETC1S and UASTC textures.
15 *
16 * Two worlds collide here too. More uglyness!
17 *
18 * @author Mark Callow, www.edgewise-consulting.com
19 */
20
21#include <inttypes.h>
22#include <stdio.h>
23#include <KHR/khr_df.h>
24
25#include "dfdutils/dfd.h"
26#include "ktx.h"
27#include "ktxint.h"
28#include "texture2.h"
29#include "vkformat_enum.h"
30#include "vk_format.h"
31#include "basis_sgd.h"
32#include "transcoder/basisu_file_headers.h"
33#include "transcoder/basisu_transcoder.h"
34#include "transcoder/basisu_transcoder_internal.h"
35
36#undef DECLARE_PRIVATE
37#undef DECLARE_PROTECTED
38#define DECLARE_PRIVATE(n,t2) ktxTexture2_private& n = *(t2->_private)
39#define DECLARE_PROTECTED(n,t2) ktxTexture_protected& n = *(t2->_protected)
40
41using namespace basisu;
42using namespace basist;
43
44inline bool isPow2(uint32_t x) { return x && ((x & (x - 1U)) == 0U); }
45
46inline bool isPow2(uint64_t x) { return x && ((x & (x - 1U)) == 0U); }
47
48KTX_error_code
49ktxTexture2_transcodeLzEtc1s(ktxTexture2* This,
50 alpha_content_e alphaContent,
51 ktxTexture2* prototype,
52 ktx_transcode_fmt_e outputFormat,
53 ktx_transcode_flags transcodeFlags);
54KTX_error_code
55ktxTexture2_transcodeUastc(ktxTexture2* This,
56 alpha_content_e alphaContent,
57 ktxTexture2* prototype,
58 ktx_transcode_fmt_e outputFormat,
59 ktx_transcode_flags transcodeFlags);
60
61/**
62 * @memberof ktxTexture2
63 * @ingroup reader
64 * @~English
65 * @brief Transcode a KTX2 texture with BasisLZ/ETC1S or UASTC images.
66 *
67 * If the texture contains BasisLZ supercompressed images, Inflates them from
68 * back to ETC1S then transcodes them to the specified block-compressed
69 * format. If the texture contains UASTC images, inflates them, if they have been
70 * supercompressed with zstd, then transcodes then to the specified format, The
71 * transcoded images replace the original images and the texture's fields including
72 * the DFD are modified to reflect the new format.
73 *
74 * These types of textures must be transcoded to a desired target
75 * block-compressed format before they can be uploaded to a GPU via a
76 * graphics API.
77 *
78 * The following block compressed transcode targets are available: @c KTX_TTF_ETC1_RGB,
79 * @c KTX_TTF_ETC2_RGBA, @c KTX_TTF_BC1_RGB, @c KTX_TTF_BC3_RGBA,
80 * @c KTX_TTF_BC4_R, @c KTX_TTF_BC5_RG, @c KTX_TTF_BC7_RGBA,
81 * @c @c KTX_TTF_PVRTC1_4_RGB, @c KTX_TTF_PVRTC1_4_RGBA,
82 * @c KTX_TTF_PVRTC2_4_RGB, @c KTX_TTF_PVRTC2_4_RGBA, @c KTX_TTF_ASTC_4x4_RGBA,
83 * @c KTX_TTF_ETC2_EAC_R11, @c KTX_TTF_ETC2_EAC_RG11, @c KTX_TTF_ETC and
84 * @c KTX_TTF_BC1_OR_3.
85 *
86 * @c KTX_TTF_ETC automatically selects between @c KTX_TTF_ETC1_RGB and
87 * @c KTX_TTF_ETC2_RGBA according to whether an alpha channel is available. @c KTX_TTF_BC1_OR_3
88 * does likewise between @c KTX_TTF_BC1_RGB and @c KTX_TTF_BC3_RGBA. Note that if
89 * @c KTX_TTF_PVRTC1_4_RGBA or @c KTX_TTF_PVRTC2_4_RGBA is specified and there is no alpha
90 * channel @c KTX_TTF_PVRTC1_4_RGB or @c KTX_TTF_PVRTC2_4_RGB respectively will be selected.
91 *
92 * Transcoding to ATC & FXT1 formats is not supported by libktx as there
93 * are no equivalent Vulkan formats.
94 *
95 * The following uncompressed transcode targets are also available: @c KTX_TTF_RGBA32,
96 * @c KTX_TTF_RGB565, KTX_TTF_BGR565 and KTX_TTF_RGBA4444.
97 *
98 * The following @p transcodeFlags are available.
99 *
100 * @sa ktxtexture2_CompressBasis().
101 *
102 * @param[in] This pointer to the ktxTexture2 object of interest.
103 * @param[in] outputFormat a value from the ktx_texture_transcode_fmt_e enum
104 * specifying the target format.
105 * @param[in] transcodeFlags bitfield of flags modifying the transcode
106 * operation. @sa ktx_texture_decode_flags_e.
107 *
108 * @return KTX_SUCCESS on success, other KTX_* enum values on error.
109 *
110 * @exception KTX_FILE_DATA_ERROR
111 * Supercompression global data is corrupted.
112 * @exception KTX_INVALID_OPERATION
113 * The texture's format is not transcodable (not
114 * ETC1S/BasisLZ or UASTC).
115 * @exception KTX_INVALID_OPERATION
116 * Supercompression global data is missing, i.e.,
117 * the texture object is invalid.
118 * @exception KTX_INVALID_OPERATION
119 * Image data is missing, i.e., the texture object
120 * is invalid.
121 * @exception KTX_INVALID_OPERATION
122 * @p outputFormat is PVRTC1 but the texture does
123 * does not have power-of-two dimensions.
124 * @exception KTX_INVALID_VALUE @p outputFormat is invalid.
125 * @exception KTX_TRANSCODE_FAILED
126 * Something went wrong during transcoding.
127 * @exception KTX_UNSUPPORTED_FEATURE
128 * KTX_TF_PVRTC_DECODE_TO_NEXT_POW2 was requested
129 * or the specified transcode target has not been
130 * included in the library being used.
131 * @exception KTX_OUT_OF_MEMORY Not enough memory to carry out transcoding.
132 */
133 KTX_error_code
134 ktxTexture2_TranscodeBasis(ktxTexture2* This,
135 ktx_transcode_fmt_e outputFormat,
136 ktx_transcode_flags transcodeFlags)
137{
138 uint32_t* BDB = This->pDfd + 1;
139 khr_df_model_e colorModel = (khr_df_model_e)KHR_DFDVAL(BDB, MODEL);
140 if (colorModel != KHR_DF_MODEL_UASTC
141 // Constructor has checked color model matches BASIS_LZ.
142 && This->supercompressionScheme != KTX_SS_BASIS_LZ)
143 {
144 return KTX_INVALID_OPERATION; // Not in a transcodable format.
145 }
146
147 DECLARE_PRIVATE(priv, This);
148 if (This->supercompressionScheme == KTX_SS_BASIS_LZ) {
149 if (!priv._supercompressionGlobalData || priv._sgdByteLength == 0)
150 return KTX_INVALID_OPERATION;
151 }
152
153 if (transcodeFlags & KTX_TF_PVRTC_DECODE_TO_NEXT_POW2) {
154 debug_printf("ktxTexture_TranscodeBasis: KTX_TF_PVRTC_DECODE_TO_NEXT_POW2 currently unsupported\n");
155 return KTX_UNSUPPORTED_FEATURE;
156 }
157
158 if (outputFormat == KTX_TTF_PVRTC1_4_RGB
159 || outputFormat == KTX_TTF_PVRTC1_4_RGBA) {
160 if ((!isPow2(This->baseWidth)) || (!isPow2(This->baseHeight))) {
161 debug_printf("ktxTexture_TranscodeBasis: PVRTC1 only supports power of 2 dimensions\n");
162 return KTX_INVALID_OPERATION;
163 }
164 }
165
166 const bool srgb = (KHR_DFDVAL(BDB, TRANSFER) == KHR_DF_TRANSFER_SRGB);
167 alpha_content_e alphaContent = eNone;
168 if (colorModel == KHR_DF_MODEL_ETC1S) {
169 if (KHR_DFDSAMPLECOUNT(BDB) == 2) {
170 uint32_t channelId = KHR_DFDSVAL(BDB, 1, CHANNELID);
171 if (channelId == KHR_DF_CHANNEL_ETC1S_AAA) {
172 alphaContent = eAlpha;
173 } else if (channelId == KHR_DF_CHANNEL_ETC1S_GGG){
174 alphaContent = eGreen;
175 } else {
176 return KTX_FILE_DATA_ERROR;
177 }
178 }
179 } else {
180 uint32_t channelId = KHR_DFDSVAL(BDB, 0, CHANNELID);
181 if (channelId == KHR_DF_CHANNEL_UASTC_RGBA)
182 alphaContent = eAlpha;
183 else if (channelId == KHR_DF_CHANNEL_UASTC_RRRG)
184 alphaContent = eGreen;
185 }
186
187 VkFormat vkFormat;
188
189 // Do some format mapping.
190 switch (outputFormat) {
191 case KTX_TTF_BC1_OR_3:
192 outputFormat = alphaContent != eNone ? KTX_TTF_BC3_RGBA
193 : KTX_TTF_BC1_RGB;
194 break;
195 case KTX_TTF_ETC:
196 outputFormat = alphaContent != eNone ? KTX_TTF_ETC2_RGBA
197 : KTX_TTF_ETC1_RGB;
198 break;
199 case KTX_TTF_PVRTC1_4_RGBA:
200 // This transcoder does not write opaque alpha blocks.
201 outputFormat = alphaContent != eNone ? KTX_TTF_PVRTC1_4_RGBA
202 : KTX_TTF_PVRTC1_4_RGB;
203 break;
204 case KTX_TTF_PVRTC2_4_RGBA:
205 // This transcoder does not write opaque alpha blocks.
206 outputFormat = alphaContent != eNone ? KTX_TTF_PVRTC2_4_RGBA
207 : KTX_TTF_PVRTC2_4_RGB;
208 break;
209 default:
210 /*NOP*/;
211 }
212
213 switch (outputFormat) {
214 case KTX_TTF_ETC1_RGB:
215 vkFormat = srgb ? VK_FORMAT_ETC2_R8G8B8_SRGB_BLOCK
216 : VK_FORMAT_ETC2_R8G8B8_UNORM_BLOCK;
217 break;
218 case KTX_TTF_ETC2_RGBA:
219 vkFormat = srgb ? VK_FORMAT_ETC2_R8G8B8A8_SRGB_BLOCK
220 : VK_FORMAT_ETC2_R8G8B8A8_UNORM_BLOCK;
221 break;
222 case KTX_TTF_ETC2_EAC_R11:
223 vkFormat = VK_FORMAT_EAC_R11_UNORM_BLOCK;
224 break;
225 case KTX_TTF_ETC2_EAC_RG11:
226 vkFormat = VK_FORMAT_EAC_R11G11_UNORM_BLOCK;
227 break;
228 case KTX_TTF_BC1_RGB:
229 // Transcoding doesn't support BC1 alpha.
230 vkFormat = srgb ? VK_FORMAT_BC1_RGB_SRGB_BLOCK
231 : VK_FORMAT_BC1_RGB_UNORM_BLOCK;
232 break;
233 case KTX_TTF_BC3_RGBA:
234 vkFormat = srgb ? VK_FORMAT_BC3_SRGB_BLOCK
235 : VK_FORMAT_BC3_UNORM_BLOCK;
236 break;
237 case KTX_TTF_BC4_R:
238 vkFormat = VK_FORMAT_BC4_UNORM_BLOCK;
239 break;
240 case KTX_TTF_BC5_RG:
241 vkFormat = VK_FORMAT_BC5_UNORM_BLOCK;
242 break;
243 case KTX_TTF_PVRTC1_4_RGB:
244 case KTX_TTF_PVRTC1_4_RGBA:
245 vkFormat = srgb ? VK_FORMAT_PVRTC1_4BPP_SRGB_BLOCK_IMG
246 : VK_FORMAT_PVRTC1_4BPP_UNORM_BLOCK_IMG;
247 break;
248 case KTX_TTF_PVRTC2_4_RGB:
249 case KTX_TTF_PVRTC2_4_RGBA:
250 vkFormat = srgb ? VK_FORMAT_PVRTC2_4BPP_SRGB_BLOCK_IMG
251 : VK_FORMAT_PVRTC2_4BPP_UNORM_BLOCK_IMG;
252 break;
253 case KTX_TTF_BC7_RGBA:
254 vkFormat = srgb ? VK_FORMAT_BC7_SRGB_BLOCK
255 : VK_FORMAT_BC7_UNORM_BLOCK;
256 break;
257 case KTX_TTF_ASTC_4x4_RGBA:
258 vkFormat = srgb ? VK_FORMAT_ASTC_4x4_SRGB_BLOCK
259 : VK_FORMAT_ASTC_4x4_UNORM_BLOCK;
260 break;
261 case KTX_TTF_RGB565:
262 vkFormat = VK_FORMAT_R5G6B5_UNORM_PACK16;
263 break;
264 case KTX_TTF_BGR565:
265 vkFormat = VK_FORMAT_B5G6R5_UNORM_PACK16;
266 break;
267 case KTX_TTF_RGBA4444:
268 vkFormat = VK_FORMAT_R4G4B4A4_UNORM_PACK16;
269 break;
270 case KTX_TTF_RGBA32:
271 vkFormat = srgb ? VK_FORMAT_R8G8B8A8_SRGB
272 : VK_FORMAT_R8G8B8A8_UNORM;
273 break;
274 default:
275 return KTX_INVALID_VALUE;
276 }
277
278 basis_tex_format textureFormat;
279 if (colorModel == KHR_DF_MODEL_UASTC)
280 textureFormat = basis_tex_format::cUASTC4x4;
281 else
282 textureFormat = basis_tex_format::cETC1S;
283
284 if (!basis_is_format_supported((transcoder_texture_format)outputFormat,
285 textureFormat)) {
286 return KTX_UNSUPPORTED_FEATURE;
287 }
288
289
290 // Create a prototype texture to use for calculating sizes in the target
291 // format and, as useful side effects, provide us with a properly sized
292 // data allocation and the DFD for the target format.
293 ktxTextureCreateInfo createInfo;
294 createInfo.glInternalformat = 0;
295 createInfo.vkFormat = vkFormat;
296 createInfo.baseWidth = This->baseWidth;
297 createInfo.baseHeight = This->baseHeight;
298 createInfo.baseDepth = This->baseDepth;
299 createInfo.generateMipmaps = This->generateMipmaps;
300 createInfo.isArray = This->isArray;
301 createInfo.numDimensions = This->numDimensions;
302 createInfo.numFaces = This->numFaces;
303 createInfo.numLayers = This->numLayers;
304 createInfo.numLevels = This->numLevels;
305 createInfo.pDfd = nullptr;
306
307 KTX_error_code result;
308 ktxTexture2* prototype;
309 result = ktxTexture2_Create(&createInfo, KTX_TEXTURE_CREATE_ALLOC_STORAGE,
310 &prototype);
311
312 if (result != KTX_SUCCESS) {
313 assert(result == KTX_OUT_OF_MEMORY); // The only run time error
314 return result;
315 }
316
317 if (!This->pData) {
318 if (ktxTexture_isActiveStream((ktxTexture*)This)) {
319 // Load pending. Complete it.
320 result = ktxTexture2_LoadImageData(This, NULL, 0);
321 if (result != KTX_SUCCESS)
322 {
323 ktxTexture2_Destroy(prototype);
324 return result;
325 }
326 } else {
327 // No data to transcode.
328 ktxTexture2_Destroy(prototype);
329 return KTX_INVALID_OPERATION;
330 }
331 }
332
333 // Transcoder global initialization. Requires ~9 milliseconds when compiled
334 // and executed natively on a Core i7 2.2 GHz. If this is too slow, the
335 // tables it computes can easily be moved to be compiled in.
336 static bool transcoderInitialized;
337 if (!transcoderInitialized) {
338 basisu_transcoder_init();
339 transcoderInitialized = true;
340 }
341
342 if (textureFormat == basis_tex_format::cETC1S) {
343 result = ktxTexture2_transcodeLzEtc1s(This, alphaContent,
344 prototype, outputFormat,
345 transcodeFlags);
346 } else {
347 result = ktxTexture2_transcodeUastc(This, alphaContent,
348 prototype, outputFormat,
349 transcodeFlags);
350 }
351
352 if (result == KTX_SUCCESS) {
353 // Fix up the current texture
354 DECLARE_PROTECTED(thisPrtctd, This);
355 DECLARE_PRIVATE(protoPriv, prototype);
356 DECLARE_PROTECTED(protoPrtctd, prototype);
357 memcpy(&thisPrtctd._formatSize, &protoPrtctd._formatSize,
358 sizeof(ktxFormatSize));
359 This->vkFormat = vkFormat;
360 This->isCompressed = prototype->isCompressed;
361 This->supercompressionScheme = KTX_SS_NONE;
362 priv._requiredLevelAlignment = protoPriv._requiredLevelAlignment;
363 // Copy the levelIndex from the prototype to This.
364 memcpy(priv._levelIndex, protoPriv._levelIndex,
365 This->numLevels * sizeof(ktxLevelIndexEntry));
366 // Move the DFD and data from the prototype to This.
367 free(This->pDfd);
368 This->pDfd = prototype->pDfd;
369 prototype->pDfd = 0;
370 free(This->pData);
371 This->pData = prototype->pData;
372 This->dataSize = prototype->dataSize;
373 prototype->pData = 0;
374 prototype->dataSize = 0;
375 }
376 ktxTexture2_Destroy(prototype);
377 return result;
378 }
379
380/**
381 * @memberof ktxTexture2 @private
382 * @ingroup reader
383 * @~English
384 * @brief Transcode a KTX2 texture with BasisLZ supercompressed ETC1S images.
385 *
386 * Inflates the images from BasisLZ supercompression back to ETC1S
387 * then transcodes them to the specified block-compressed format. The
388 * transcoded images replace the original images and the texture's fields
389 * including the DFD are modified to reflect the new format.
390 *
391 * BasisLZ supercompressed textures must be transcoded to a desired target
392 * block-compressed format before they can be uploaded to a GPU via a graphics
393 * API.
394 *
395 * The following block compressed transcode targets are available: @c KTX_TTF_ETC1_RGB,
396 * @c KTX_TTF_ETC2_RGBA, @c KTX_TTF_BC1_RGB, @c KTX_TTF_BC3_RGBA,
397 * @c KTX_TTF_BC4_R, @c KTX_TTF_BC5_RG, @c KTX_TTF_BC7_RGBA,
398 * @c @c KTX_TTF_PVRTC1_4_RGB, @c KTX_TTF_PVRTC1_4_RGBA,
399 * @c KTX_TTF_PVRTC2_4_RGB, @c KTX_TTF_PVRTC2_4_RGBA, @c KTX_TTF_ASTC_4x4_RGBA,
400 * @c KTX_TTF_ETC2_EAC_R11, @c KTX_TTF_ETC2_EAC_RG11, @c KTX_TTF_ETC and
401 * @c KTX_TTF_BC1_OR_3.
402 *
403 * @c KTX_TTF_ETC automatically selects between @c KTX_TTF_ETC1_RGB and
404 * @c KTX_TTF_ETC2_RGBA according to whether an alpha channel is available. @c KTX_TTF_BC1_OR_3
405 * does likewise between @c KTX_TTF_BC1_RGB and @c KTX_TTF_BC3_RGBA. Note that if
406 * @c KTX_TTF_PVRTC1_4_RGBA or @c KTX_TTF_PVRTC2_4_RGBA is specified and there is no alpha
407 * channel @c KTX_TTF_PVRTC1_4_RGB or @c KTX_TTF_PVRTC2_4_RGB respectively will be selected.
408 *
409 * ATC & FXT1 formats are not supported by KTX2 & libktx as there are no equivalent Vulkan formats.
410 *
411 * The following uncompressed transcode targets are also available: @c KTX_TTF_RGBA32,
412 * @c KTX_TTF_RGB565, KTX_TTF_BGR565 and KTX_TTF_RGBA4444.
413 *
414 * The following @p transcodeFlags are available.
415 *
416 * @sa ktxtexture2_CompressBasis().
417 *
418 * @param[in] This pointer to the ktxTexture2 object of interest.
419 * @param[in] outputFormat a value from the ktx_texture_transcode_fmt_e enum
420 * specifying the target format.
421 * @param[in] transcodeFlags bitfield of flags modifying the transcode
422 * operation. @sa ktx_texture_decode_flags_e.
423 *
424 * @return KTX_SUCCESS on success, other KTX_* enum values on error.
425 *
426 * @exception KTX_FILE_DATA_ERROR
427 * Supercompression global data is corrupted.
428 * @exception KTX_INVALID_OPERATION
429 * The texture's format is not transcodable (not
430 * ETC1S/BasisLZ or UASTC).
431 * @exception KTX_INVALID_OPERATION
432 * Supercompression global data is missing, i.e.,
433 * the texture object is invalid.
434 * @exception KTX_INVALID_OPERATION
435 * Image data is missing, i.e., the texture object
436 * is invalid.
437 * @exception KTX_INVALID_OPERATION
438 * @p outputFormat is PVRTC1 but the texture does
439 * does not have power-of-two dimensions.
440 * @exception KTX_INVALID_VALUE @p outputFormat is invalid.
441 * @exception KTX_TRANSCODE_FAILED
442 * Something went wrong during transcoding. The
443 * texture object will be corrupted.
444 * @exception KTX_UNSUPPORTED_FEATURE
445 * KTX_TF_PVRTC_DECODE_TO_NEXT_POW2 was requested
446 * or the specified transcode target has not been
447 * included in the library being used.
448 * @exception KTX_OUT_OF_MEMORY Not enough memory to carry out transcoding.
449 */
450KTX_error_code
451ktxTexture2_transcodeLzEtc1s(ktxTexture2* This,
452 alpha_content_e alphaContent,
453 ktxTexture2* prototype,
454 ktx_transcode_fmt_e outputFormat,
455 ktx_transcode_flags transcodeFlags)
456{
457 DECLARE_PRIVATE(priv, This);
458 DECLARE_PRIVATE(protoPriv, prototype);
459 KTX_error_code result = KTX_SUCCESS;
460
461 assert(This->supercompressionScheme == KTX_SS_BASIS_LZ);
462
463 uint8_t* bgd = priv._supercompressionGlobalData;
464 ktxBasisLzGlobalHeader& bgdh = *reinterpret_cast<ktxBasisLzGlobalHeader*>(bgd);
465 if (!(bgdh.endpointsByteLength && bgdh.selectorsByteLength && bgdh.tablesByteLength)) {
466 debug_printf("ktxTexture_TranscodeBasis: missing endpoints, selectors or tables");
467 return KTX_FILE_DATA_ERROR;
468 }
469
470 // Compute some helpful numbers.
471 //
472 // firstImages contains the indices of the first images for each level to
473 // ease finding the correct slice description when iterating from smallest
474 // level to largest or when randomly accessing them (t.b.c). The last array
475 // entry contains the total number of images, for calculating the offsets
476 // of the endpoints, etc.
477 uint32_t* firstImages = new uint32_t[This->numLevels+1];
478
479 // Temporary invariant value
480 uint32_t layersFaces = This->numLayers * This->numFaces;
481 firstImages[0] = 0;
482 for (uint32_t level = 1; level <= This->numLevels; level++) {
483 // NOTA BENE: numFaces * depth is only reasonable because they can't
484 // both be > 1. I.e there are no 3d cubemaps.
485 firstImages[level] = firstImages[level - 1]
486 + layersFaces * MAX(This->baseDepth >> (level - 1), 1);
487 }
488 uint32_t& imageCount = firstImages[This->numLevels];
489
490 if (BGD_TABLES_ADDR(0, bgdh, imageCount) + bgdh.tablesByteLength > priv._sgdByteLength) {
491 return KTX_FILE_DATA_ERROR;
492 }
493 // FIXME: Do more validation.
494
495 // Prepare low-level transcoder for transcoding slices.
496 basist::basisu_lowlevel_etc1s_transcoder bit;
497
498 // basisu_transcoder_state is used to find the previous frame when
499 // decoding a video P-Frame. It tracks the previous frame for each mip
500 // level. For cube map array textures we need to find the previous frame
501 // for each face so we a state per face. Although providing this is only
502 // needed for video, it is easier to always pass our own.
503 std::vector<basisu_transcoder_state> xcoderStates;
504 xcoderStates.resize(This->isVideo ? This->numFaces : 1);
505
506 bit.decode_palettes(bgdh.endpointCount, BGD_ENDPOINTS_ADDR(bgd, imageCount),
507 bgdh.endpointsByteLength,
508 bgdh.selectorCount, BGD_SELECTORS_ADDR(bgd, bgdh, imageCount),
509 bgdh.selectorsByteLength);
510
511 bit.decode_tables(BGD_TABLES_ADDR(bgd, bgdh, imageCount),
512 bgdh.tablesByteLength);
513
514 // Find matching VkFormat and calculate output sizes.
515
516 const bool isVideo = This->isVideo;
517
518 ktx_uint8_t* pXcodedData = prototype->pData;
519 // Inconveniently, the output buffer size parameter of transcode_image
520 // has to be in pixels for uncompressed output and in blocks for
521 // compressed output. The only reason for humouring the API is so
522 // its buffer size tests provide a real check. An alternative is to
523 // always provide the size in bytes which will always pass.
524 ktx_uint32_t outputBlockByteLength
525 = prototype->_protected->_formatSize.blockSizeInBits / 8;
526 ktx_size_t xcodedDataLength
527 = prototype->dataSize / outputBlockByteLength;
528 ktxLevelIndexEntry* protoLevelIndex;
529 uint64_t levelOffsetWrite;
530 const ktxBasisLzEtc1sImageDesc* imageDescs = BGD_ETC1S_IMAGE_DESCS(bgd);
531
532 // Finally we're ready to transcode the slices.
533
534 // FIXME: Iframe flag needs to be queryable by the application. In Basis
535 // the app can query file_info and image_info from the transcoder which
536 // returns a structure with lots of info about the image.
537
538 protoLevelIndex = protoPriv._levelIndex;
539 levelOffsetWrite = 0;
540 for (int32_t level = This->numLevels - 1; level >= 0; level--) {
541 uint64_t levelOffset = ktxTexture2_levelDataOffset(This, level);
542 uint64_t writeOffset = levelOffsetWrite;
543 uint64_t writeOffsetBlocks = levelOffsetWrite / outputBlockByteLength;
544 uint32_t levelWidth = MAX(1, This->baseWidth >> level);
545 uint32_t levelHeight = MAX(1, This->baseHeight >> level);
546 // ETC1S texel block dimensions
547 const uint32_t bw = 4, bh = 4;
548 uint32_t levelBlocksX = (levelWidth + (bw - 1)) / bw;
549 uint32_t levelBlocksY = (levelHeight + (bh - 1)) / bh;
550 uint32_t depth = MAX(1, This->baseDepth >> level);
551 //uint32_t faceSlices = This->numFaces == 1 ? depth : This->numFaces;
552 uint32_t faceSlices = This->numFaces * depth;
553 uint32_t numImages = This->numLayers * faceSlices;
554 uint32_t image = firstImages[level];
555 uint32_t endImage = image + numImages;
556 ktx_size_t levelImageSizeOut, levelSizeOut;
557 uint32_t stateIndex = 0;
558
559 levelSizeOut = 0;
560 // FIXME: Figure out a way to get the size out of the transcoder.
561 levelImageSizeOut = ktxTexture2_GetImageSize(prototype, level);
562 for (; image < endImage; image++) {
563 const ktxBasisLzEtc1sImageDesc& imageDesc = imageDescs[image];
564
565 basisu_transcoder_state& xcoderState = xcoderStates[stateIndex];
566 // We have face0 [face1 ...] within each layer. Use `stateIndex`
567 // rather than a double loop of layers and faceSlices as this
568 // works for 3d texture and non-array cube maps as well as
569 // cube map arrays without special casing.
570 if (++stateIndex == xcoderStates.size())
571 stateIndex = 0;
572
573 if (alphaContent != eNone)
574 {
575 // The slice descriptions should have alpha information.
576 if (imageDesc.alphaSliceByteOffset == 0
577 || imageDesc.alphaSliceByteLength == 0)
578 return KTX_FILE_DATA_ERROR;
579 }
580
581 bool status;
582 status = bit.transcode_image(
583 (transcoder_texture_format)outputFormat,
584 pXcodedData + writeOffset,
585 (uint32_t)(xcodedDataLength - writeOffsetBlocks),
586 This->pData,
587 (uint32_t)This->dataSize,
588 levelBlocksX,
589 levelBlocksY,
590 levelWidth,
591 levelHeight,
592 level,
593 (uint32_t)(levelOffset + imageDesc.rgbSliceByteOffset),
594 imageDesc.rgbSliceByteLength,
595 (uint32_t)(levelOffset + imageDesc.alphaSliceByteOffset),
596 imageDesc.alphaSliceByteLength,
597 transcodeFlags,
598 alphaContent != eNone,
599 isVideo,
600 // Our P-Frame flag is in the same bit as
601 // cSliceDescFlagsFrameIsIFrame. We have to
602 // invert it to make it an I-Frame flag.
603 //
604 // API currently doesn't have any way to pass
605 // the I-Frame flag.
606 //imageDesc.imageFlags ^ cSliceDescFlagsFrameIsIFrame,
607 0, // output_row_pitch_in_blocks_or_pixels
608 &xcoderState,
609 0 // output_rows_in_pixels
610 );
611 if (!status) {
612 result = KTX_TRANSCODE_FAILED;
613 goto cleanup;
614 }
615
616 writeOffset += levelImageSizeOut;
617 levelSizeOut += levelImageSizeOut;
618 } // end images loop
619 protoLevelIndex[level].byteOffset = levelOffsetWrite;
620 protoLevelIndex[level].byteLength = levelSizeOut;
621 protoLevelIndex[level].uncompressedByteLength = levelSizeOut;
622 levelOffsetWrite += levelSizeOut;
623 assert(levelOffsetWrite == writeOffset);
624 // In case of transcoding to uncompressed.
625 levelOffsetWrite = _KTX_PADN(protoPriv._requiredLevelAlignment,
626 levelOffsetWrite);
627 } // level loop
628
629 result = KTX_SUCCESS;
630
631cleanup:
632 delete[] firstImages;
633 return result;
634}
635
636
637KTX_error_code
638ktxTexture2_transcodeUastc(ktxTexture2* This,
639 alpha_content_e alphaContent,
640 ktxTexture2* prototype,
641 ktx_transcode_fmt_e outputFormat,
642 ktx_transcode_flags transcodeFlags)
643{
644 assert(This->supercompressionScheme != KTX_SS_BASIS_LZ);
645
646 ktx_uint8_t* pXcodedData = prototype->pData;
647 ktx_uint32_t outputBlockByteLength
648 = prototype->_protected->_formatSize.blockSizeInBits / 8;
649 ktx_size_t xcodedDataLength
650 = prototype->dataSize / outputBlockByteLength;
651 DECLARE_PRIVATE(protoPriv, prototype);
652 ktxLevelIndexEntry* protoLevelIndex = protoPriv._levelIndex;
653 ktx_size_t levelOffsetWrite = 0;
654
655 basisu_lowlevel_uastc_transcoder uit;
656 // See comment on same declaration in transcodeEtc1s.
657 std::vector<basisu_transcoder_state> xcoderStates;
658 xcoderStates.resize(This->isVideo ? This->numFaces : 1);
659
660 for (ktx_int32_t level = This->numLevels - 1; level >= 0; level--)
661 {
662 ktx_uint32_t depth;
663 uint64_t writeOffset = levelOffsetWrite;
664 uint64_t writeOffsetBlocks = levelOffsetWrite / outputBlockByteLength;
665 ktx_size_t levelImageSizeIn, levelImageOffsetIn;
666 ktx_size_t levelImageSizeOut, levelSizeOut;
667 ktx_uint32_t levelImageCount;
668 uint32_t levelWidth = MAX(1, This->baseWidth >> level);
669 uint32_t levelHeight = MAX(1, This->baseHeight >> level);
670 // UASTC texel block dimensions
671 const uint32_t bw = 4, bh = 4;
672 uint32_t levelBlocksX = (levelWidth + (bw - 1)) / bw;
673 uint32_t levelBlocksY = (levelHeight + (bh - 1)) / bh;
674 uint32_t stateIndex = 0;
675
676 depth = MAX(1, This->baseDepth >> level);
677
678 levelImageCount = This->numLayers * This->numFaces * depth;
679 levelImageSizeIn = ktxTexture_calcImageSize(ktxTexture(This), level,
680 KTX_FORMAT_VERSION_TWO);
681 levelImageSizeOut = ktxTexture_calcImageSize(ktxTexture(prototype),
682 level,
683 KTX_FORMAT_VERSION_TWO);
684
685 levelImageOffsetIn = ktxTexture2_levelDataOffset(This, level);
686 levelSizeOut = 0;
687 bool status;
688 for (uint32_t image = 0; image < levelImageCount; image++) {
689 basisu_transcoder_state& xcoderState = xcoderStates[stateIndex];
690 // See comment before same lines in transcodeEtc1s.
691 if (++stateIndex == xcoderStates.size())
692 stateIndex = 0;
693
694 status = uit.transcode_image(
695 (transcoder_texture_format)outputFormat,
696 pXcodedData + writeOffset,
697 (uint32_t)(xcodedDataLength - writeOffsetBlocks),
698 This->pData,
699 (uint32_t)This->dataSize,
700 levelBlocksX,
701 levelBlocksY,
702 levelWidth,
703 levelHeight,
704 level,
705 (uint32_t)levelImageOffsetIn,
706 (uint32_t)levelImageSizeIn,
707 transcodeFlags,
708 alphaContent != eNone,
709 This->isVideo, // is_video
710 //imageDesc.imageFlags ^ cSliceDescFlagsFrameIsIFrame,
711 0, // output_row_pitch_in_blocks_or_pixels
712 &xcoderState, // pState
713 0, // output_rows_in_pixels,
714 -1, // channel0
715 -1 // channel1
716 );
717 if (!status)
718 return KTX_TRANSCODE_FAILED;
719 writeOffset += levelImageSizeOut;
720 levelSizeOut += levelImageSizeOut;
721 levelImageOffsetIn += levelImageSizeIn;
722 }
723 protoLevelIndex[level].byteOffset = levelOffsetWrite;
724 // writeOffset will be equal to total size of the images in the level.
725 protoLevelIndex[level].byteLength = levelSizeOut;
726 protoLevelIndex[level].uncompressedByteLength = levelSizeOut;
727 levelOffsetWrite += levelSizeOut;
728 }
729 // In case of transcoding to uncompressed.
730 levelOffsetWrite = _KTX_PADN(protoPriv._requiredLevelAlignment,
731 levelOffsetWrite);
732 return KTX_SUCCESS;
733}
734