11#! /usr/bin/env bash
22# GenerateTestEncodes.sh
3- # Generates test DDS and KTX texture files using cuttlefish, then decodes
4- # reference PNGs using tacentview.
3+ # Generates test DDS, KTX, and KTX2 texture files using cuttlefish, then decodes
4+ # reference PNGs. KTX2 files are converted from KTX using ktx2ktx2. Reference
5+ # PNGs for KTX/KTX2 are extracted with ktx extract (KTX2 only); DDS reference
6+ # PNGs are decoded with tacentview.
57# Replaces GenerateTestDds.ps1 and GenerateTestKtx.ps1.
68#
79# Notes:
810# - Only DX10-header DDS files are generated; cuttlefish does not support DX9/legacy headers.
911# - ETC/EAC/ASTC/PVRTC formats are KTX-only (not valid in DDS containers).
10- # - tacentview is used for reference decoding; if not found the decode step is skipped.
1112
1213set -euo pipefail
1314
1415usage () {
1516 cat << 'EOF '
16- GenerateTestEncodes.sh - Generate test DDS and KTX files for BCnEncoder.NET
17+ GenerateTestEncodes.sh - Generate test DDS, KTX, and KTX2 files for BCnEncoder.NET
1718
1819USAGE:
1920 ./GenerateTestEncodes.sh -i <input> [OPTIONS]
@@ -23,28 +24,37 @@ OPTIONS:
2324 -i, --input <path> Path to input image (required unless --no-encode)
2425 -d, --dds-dir <path> DDS directory (default: ./testdds)
2526 -k, --ktx-dir <path> KTX directory (default: ./testktx)
27+ -K, --ktx2-dir <path> KTX2 directory (default: ./testktx2)
2628 --no-encode Skip encoding; only decode reference PNGs from
27- existing files in the DDS and KTX directories
28- -h, --help Show this help
29+ existing files in the DDS, KTX, and KTX2 directories
30+ --tacentview-ktx Use tacentview for KTX reference decoding instead
31+ of the default ktx extract method
2932
3033TOOLS:
3134 cuttlefish Required for encoding (not needed with --no-encode)
32- tacentview Required for reference PNG decoding
35+ ktx2ktx2 Required for KTX -> KTX2 conversion
36+ ktx Required for KTX2 reference PNG extraction
37+ tacentview Required for DDS reference PNG decoding; also used for KTX
38+ decoding if --tacentview-ktx is set
3339EOF
3440}
3541
3642INPUT=" "
3743DDS_DIR=" ./testdds"
3844KTX_DIR=" ./testktx"
45+ KTX2_DIR=" ./testktx2"
3946NO_ENCODE=0
47+ TACENTVIEW_KTX=0
4048
4149while [[ $# -gt 0 ]]; do
4250 case " $1 " in
43- -i|--input) INPUT=" $2 " ; shift 2 ;;
44- -d|--dds-dir) DDS_DIR=" $2 " ; shift 2 ;;
45- -k|--ktx-dir) KTX_DIR=" $2 " ; shift 2 ;;
46- --no-encode) NO_ENCODE=1; shift ;;
47- -h|--help) usage; exit 0 ;;
51+ -i|--input) INPUT=" $2 " ; shift 2 ;;
52+ -d|--dds-dir) DDS_DIR=" $2 " ; shift 2 ;;
53+ -k|--ktx-dir) KTX_DIR=" $2 " ; shift 2 ;;
54+ -K|--ktx2-dir) KTX2_DIR=" $2 " ; shift 2 ;;
55+ --no-encode) NO_ENCODE=1; shift ;;
56+ --tacentview-ktx) TACENTVIEW_KTX=1; shift ;;
57+ -h|--help) usage; exit 0 ;;
4858 * ) echo " Unknown option: $1 " >&2 ; usage >&2 ; exit 1 ;;
4959 esac
5060done
5767
5868HAS_TACENTVIEW=0
5969command -v tacentview & > /dev/null && HAS_TACENTVIEW=1 \
60- || echo " Warning: tacentview not found — reference PNG decode step will be skipped"
70+ || echo " Warning: tacentview not found — DDS reference PNG decoding will be skipped"
71+
72+ HAS_KTX_TOOLS=0
73+ command -v ktx2ktx2 & > /dev/null && command -v ktx & > /dev/null && HAS_KTX_TOOLS=1 \
74+ || echo " Warning: ktx2ktx2/ktx not found — KTX2 conversion and reference PNG extraction will be skipped"
6175
6276if [[ " $NO_ENCODE " == " 0" ]]; then
6377 BASE_NAME=" $( basename " ${INPUT% .* } " ) "
64- mkdir -p " $DDS_DIR " " $KTX_DIR "
78+ mkdir -p " $DDS_DIR " " $KTX_DIR " " $KTX2_DIR "
6579fi
6680
6781run_cf () {
@@ -105,10 +119,10 @@ COMMON_FORMATS=(
105119 " bc3|BC3|unorm"
106120 # BC4
107121 " bc4|BC4|unorm"
108- " bc4-snorm |BC4|snorm"
122+ " bc4s |BC4|snorm"
109123 # BC5
110124 " bc5|BC5|unorm"
111- " bc5-snorm |BC5|snorm"
125+ " bc5s |BC5|snorm"
112126 # BC6H
113127 " bc6h|BC6H|ufloat"
114128 " bc6h-signed|BC6H|float"
@@ -127,9 +141,9 @@ COMMON_FORMATS=(
127141 " a1r5g5b5|A1R5G5B5|unorm"
128142 # Uncompressed 8-bit
129143 " r8|R8|unorm"
130- " r8-snorm |R8|snorm"
144+ " r8s |R8|snorm"
131145 " r8g8|R8G8|unorm"
132- " r8g8-snorm |R8G8|snorm"
146+ " r8g8s |R8G8|snorm"
133147 " r8g8b8|R8G8B8|unorm"
134148 " b8g8r8|B8G8R8|unorm"
135149 " r8g8b8a8|R8G8B8A8|unorm"
@@ -169,9 +183,9 @@ KTX_EXTRA_FORMATS=(
169183 " etc2-rgb-a1|ETC2_R8G8B8A1|unorm"
170184 # EAC (single- and dual-channel, signed and unsigned)
171185 " eac-r11|EAC_R11|unorm"
172- " eac-r11-snorm |EAC_R11|snorm"
186+ " eac-r11s |EAC_R11|snorm"
173187 " eac-rg11|EAC_R11G11|unorm"
174- " eac-rg11-snorm |EAC_R11G11|snorm"
188+ " eac-rg11s |EAC_R11G11|snorm"
175189 # ASTC
176190 " astc-4x4|ASTC_4x4|"
177191 " astc-5x4|ASTC_5x4|"
@@ -231,25 +245,58 @@ done
231245echo " KTX generation complete. Files written to: $KTX_DIR "
232246fi # NO_ENCODE
233247
234- # ─── Reference PNG decode ──────────────────────────────────────────────────────
235- # Decodes each generated DDS/KTX back to PNG for visual inspection.
236- # tacentview outputs the PNG alongside the source file, which is then moved
237- # into a reference/ subdirectory.
248+ # ─── Convert KTX -> KTX2 ─────────────────────────────────────────────────────
249+ # ktx extract (used for reference PNGs) only supports KTX2, so convert each
250+ # KTX file. The pixel data is identical; KTX2 is just a different container.
251+
252+ if [[ " $HAS_KTX_TOOLS " == " 1" ]]; then
253+ mkdir -p " $KTX2_DIR "
254+ ktx_files=(" $KTX_DIR " /* .ktx)
255+ if [[ -f " ${ktx_files[0]} " ]]; then
256+ echo " "
257+ echo " Converting KTX -> KTX2..."
258+ total=" ${# ktx_files[@]} " count=0
259+ for ktx in " ${ktx_files[@]} " ; do
260+ count=$(( count + 1 ))
261+ name=" $( basename " ${ktx% .* } " ) "
262+ ktx2=" $KTX2_DIR /${name} .ktx2"
263+ printf ' [%d/%d] %s\n' " $count " " $total " " $name "
264+ if [[ -f " $ktx2 " ]]; then
265+ echo " Skipped (already exists): $ktx2 "
266+ elif ktx2ktx2 -o " $ktx2 " " $ktx " ; then
267+ echo " Created $ktx2 "
268+ else
269+ echo " Warning: ktx2ktx2 failed for $ktx " >&2
270+ fi
271+ done
272+ echo " KTX2 conversion complete. Files written to: $KTX2_DIR "
273+ else
274+ echo " No KTX files found in $KTX_DIR — skipping KTX2 conversion"
275+ fi
276+ else
277+ echo " "
278+ echo " Skipping KTX2 conversion (ktx2ktx2 not available)."
279+ fi
280+
281+ # ─── Reference PNG decode ─────────────────────────────────────────────────────
282+ # DDS: decoded with tacentview
283+ # KTX/KTX2: decoded with ktx extract (KTX2 only); PNGs are then copied from
284+ # the KTX2 reference folder into the KTX reference folder since the
285+ # pixel data is identical. Falls back to tacentview if --tacentview-ktx.
238286#
239- # HDR formats (BC6H, float, ufloat) get tone=1.0 (neutral exposure) so the
240- # result is representable as an 8-bit PNG. corr=auto respects any sRGB tagging
241- # embedded in the file by cuttlefish.
287+ # HDR formats (BC6H, float, ufloat) get tone=1.0 for tacentview so the preview
288+ # is representable as an 8-bit PNG.
242289
243- decode_references () {
244- local dir=" $1 " ext= " $2 " in_flag= " $3 "
290+ decode_dds_references () {
291+ local dir=" $1 "
245292 local ref_dir=" $dir /reference"
246293 mkdir -p " $ref_dir "
247294
248- local files=(" $dir " /* ." $ext " )
249- [[ -f " ${files[0]} " ]] || { echo " No .$ext files found in $dir " ; return ; }
295+ local files=(" $dir " /* .dds )
296+ [[ -f " ${files[0]} " ]] || { echo " No .dds files found in $dir " ; return ; }
250297
251298 local total=" ${# files[@]} " count=0
252- echo " Decoding $ext reference PNGs..."
299+ echo " Decoding DDS reference PNGs with tacentview ..."
253300
254301 for src in " ${files[@]} " ; do
255302 count=$(( count + 1 ))
@@ -259,20 +306,116 @@ decode_references() {
259306
260307 printf ' [%d/%d] %s\n' " $count " " $total " " $name "
261308
262- # Apply neutral tone-map for HDR/float formats so the preview is
263- # representable as an 8-bit PNG
264- local params=" corr=auto"
265- if [[ " $name " == * bc6h* ]] || [[ " $name " == * float* ]] || [[ " $name " == * ufloat* ]]; then
266- params=" corr=auto,tone=1.0"
309+ if [[ -f " $ref_png " ]]; then
310+ echo " Skipped (already exists): $ref_png "
311+ continue
312+ fi
313+
314+ local params=" corr=none"
315+
316+ if tacentview -c -w --inDDS " $params " -o png " $src " ; then
317+ if [[ -f " $out_png " ]]; then
318+ mv " $out_png " " $ref_png "
319+ echo " Created $ref_png "
320+ else
321+ echo " Warning: expected $out_png not found" >&2
322+ fi
323+ else
324+ echo " Warning: tacentview failed for $src " >&2
325+ fi
326+ done
327+
328+ echo " DDS reference PNGs written to: $ref_dir "
329+ }
330+
331+ decode_ktx2_references () {
332+ local ktx2_dir=" $1 " ktx_dir=" $2 "
333+ local ktx2_ref_dir=" $ktx2_dir /reference"
334+ local ktx_ref_dir=" $ktx_dir /reference"
335+ mkdir -p " $ktx2_ref_dir " " $ktx_ref_dir "
336+
337+ local files=(" $ktx2_dir " /* .ktx2)
338+ [[ -f " ${files[0]} " ]] || { echo " No .ktx2 files found in $ktx2_dir " ; return ; }
339+
340+ local total=" ${# files[@]} " count=0
341+ echo " Extracting KTX2 reference PNGs with ktx extract (falling back to tacentview for block-compressed)..."
342+
343+ for src in " ${files[@]} " ; do
344+ count=$(( count + 1 ))
345+ local name; name=" $( basename " ${src% .* } " ) "
346+ local ktx_src=" $ktx_dir /${name} .ktx"
347+ local ktx2_ref_png=" $ktx2_ref_dir /${name} .png"
348+ local ktx_ref_png=" $ktx_ref_dir /${name} .png"
349+
350+ printf ' [%d/%d] %s\n' " $count " " $total " " $name "
351+
352+ if [[ ! -f " $ktx2_ref_png " ]]; then
353+ # ktx extract only decodes uncompressed KTX2 to PNG; block-compressed
354+ # KTX2 files (BCn, ETC, ASTC) are not "transcodable" without BasisLZ
355+ # supercompression, so extract won't produce a PNG for those. We detect
356+ # this by checking whether the output file was actually created.
357+ ktx extract " $src " " $ktx2_ref_png " 2> /dev/null || true
358+ if [[ -f " $ktx2_ref_png " ]]; then
359+ echo " Created $ktx2_ref_png "
360+ elif [[ " $HAS_TACENTVIEW " == " 1" && -f " $ktx_src " ]]; then
361+ echo " ktx extract produced no PNG (block-compressed); falling back to tacentview"
362+ local out_png=" ${ktx_src% .* } .png"
363+ local params=" corr=none"
364+ if tacentview -c -w --inKTX " $params " -o png " $ktx_src " && [[ -f " $out_png " ]]; then
365+ mv " $out_png " " $ktx2_ref_png "
366+ echo " Created $ktx2_ref_png "
367+ else
368+ echo " Warning: tacentview fallback also failed for $name " >&2
369+ continue
370+ fi
371+ else
372+ echo " Warning: no PNG produced for $name (block-compressed; tacentview unavailable)" >&2
373+ continue
374+ fi
375+ else
376+ echo " Skipped (already exists): $ktx2_ref_png "
377+ fi
378+
379+ # Copy to KTX reference folder — pixel data is identical
380+ if [[ -f " $ktx_ref_png " ]]; then
381+ echo " Skipped (already exists): $ktx_ref_png "
382+ else
383+ cp " $ktx2_ref_png " " $ktx_ref_png "
384+ echo " Copied to $ktx_ref_png "
267385 fi
386+ done
387+
388+ echo " KTX2 reference PNGs written to: $ktx2_ref_dir "
389+ echo " KTX reference PNGs written to: $ktx_ref_dir "
390+ }
391+
392+ decode_ktx_references_tacentview () {
393+ local dir=" $1 "
394+ local ref_dir=" $dir /reference"
395+ mkdir -p " $ref_dir "
396+
397+ local files=(" $dir " /* .ktx)
398+ [[ -f " ${files[0]} " ]] || { echo " No .ktx files found in $dir " ; return ; }
399+
400+ local total=" ${# files[@]} " count=0
401+ echo " Decoding KTX reference PNGs with tacentview..."
402+
403+ for src in " ${files[@]} " ; do
404+ count=$(( count + 1 ))
405+ local name; name=" $( basename " ${src% .* } " ) "
406+ local out_png=" ${src% .* } .png"
407+ local ref_png=" $ref_dir /${name} .png"
408+
409+ printf ' [%d/%d] %s\n' " $count " " $total " " $name "
268410
269411 if [[ -f " $ref_png " ]]; then
270412 echo " Skipped (already exists): $ref_png "
271413 continue
272414 fi
273415
274- # tacentview writes output alongside the input file; we then move it
275- if tacentview -c -w " $in_flag " " $params " -o png " $src " ; then
416+ local params=" corr=none"
417+
418+ if tacentview -c -w --inKTX " $params " -o png " $src " ; then
276419 if [[ -f " $out_png " ]]; then
277420 mv " $out_png " " $ref_png "
278421 echo " Created $ref_png "
@@ -284,19 +427,33 @@ decode_references() {
284427 fi
285428 done
286429
287- echo " $ext reference PNGs written to: $ref_dir "
430+ echo " KTX reference PNGs written to: $ref_dir "
288431}
289432
433+ echo " "
290434if [[ " $HAS_TACENTVIEW " == " 1" ]]; then
291- echo " "
292- [[ -d " $DDS_DIR " ]] && decode_references " $DDS_DIR " " dds" " --inDDS" \
435+ [[ -d " $DDS_DIR " ]] && decode_dds_references " $DDS_DIR " \
293436 || echo " Skipping DDS decode: $DDS_DIR does not exist"
294- echo " "
295- [[ -d " $KTX_DIR " ]] && decode_references " $KTX_DIR " " ktx" " --inKTX" \
296- || echo " Skipping KTX decode: $KTX_DIR does not exist"
297437else
298- echo " "
299- echo " Skipping reference PNG decode (tacentview not available)."
438+ echo " Skipping DDS reference PNG decode (tacentview not available)."
439+ fi
440+
441+ echo " "
442+ if [[ " $TACENTVIEW_KTX " == " 1" ]]; then
443+ if [[ " $HAS_TACENTVIEW " == " 1" ]]; then
444+ [[ -d " $KTX_DIR " ]] && decode_ktx_references_tacentview " $KTX_DIR " \
445+ || echo " Skipping KTX decode: $KTX_DIR does not exist"
446+ else
447+ echo " Skipping KTX reference PNG decode (tacentview not available)."
448+ fi
449+ elif [[ " $HAS_KTX_TOOLS " == " 1" ]]; then
450+ if [[ -d " $KTX2_DIR " ]]; then
451+ decode_ktx2_references " $KTX2_DIR " " $KTX_DIR "
452+ else
453+ echo " Skipping KTX/KTX2 decode: $KTX2_DIR does not exist"
454+ fi
455+ else
456+ echo " Skipping KTX reference PNG decode (ktx2ktx2/ktx not available; use --tacentview-ktx to use tacentview instead)."
300457fi
301458
302459echo " "
0 commit comments