mirror of
https://github.com/BinomialLLC/basis_universal.git
synced 2026-08-05 12:59:03 +00:00
adding new files
This commit is contained in:
1
python/tests/__init__.py
Normal file
1
python/tests/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
# python/tests/__init__.py
|
||||
70
python/tests/test_backend_loading.py
Normal file
70
python/tests/test_backend_loading.py
Normal file
@@ -0,0 +1,70 @@
|
||||
#!/usr/bin/env python3
|
||||
import numpy as np
|
||||
from PIL import Image
|
||||
|
||||
from basisu_py.codec import Encoder, EncoderBackend
|
||||
from basisu_py.constants import BasisTexFormat
|
||||
|
||||
print("========== BACKEND LOADING TEST ==========\n")
|
||||
|
||||
# --------------------------------------------------------------
|
||||
# 1. Test native backend (if available)
|
||||
# --------------------------------------------------------------
|
||||
print("Testing native backend...")
|
||||
|
||||
try:
|
||||
enc_native = Encoder(backend=EncoderBackend.NATIVE)
|
||||
print(" [OK] Native backend loaded")
|
||||
except Exception as e:
|
||||
print(" [FAIL] Native backend failed to load:", e)
|
||||
enc_native = None
|
||||
|
||||
# If native loaded, test very basic functionality
|
||||
if enc_native:
|
||||
try:
|
||||
version = enc_native._native.get_version()
|
||||
print(f" Native get_version() ? {version}")
|
||||
|
||||
ptr = enc_native._native.alloc(16)
|
||||
print(f" Native alloc() returned ptr = {ptr}")
|
||||
|
||||
enc_native._native.free(ptr)
|
||||
print(f" Native free() OK")
|
||||
|
||||
print(" [OK] Native basic operations working.\n")
|
||||
except Exception as e:
|
||||
print(" [FAIL] Native operations error:", e)
|
||||
else:
|
||||
print(" Skipping native basic operations.\n")
|
||||
|
||||
# --------------------------------------------------------------
|
||||
# 2. Test WASM backend
|
||||
# --------------------------------------------------------------
|
||||
print("\nTesting WASM backend...")
|
||||
|
||||
try:
|
||||
enc_wasm = Encoder(backend=EncoderBackend.WASM)
|
||||
print(" [OK] WASM backend loaded")
|
||||
except Exception as e:
|
||||
print(" [FAIL] WASM backend failed to load:", e)
|
||||
enc_wasm = None
|
||||
|
||||
# If WASM loaded, test basic methods
|
||||
if enc_wasm and enc_wasm._wasm is not None:
|
||||
try:
|
||||
version = enc_wasm._wasm.get_version()
|
||||
print(f" WASM get_version() ? {version}")
|
||||
|
||||
ptr = enc_wasm._wasm.alloc(16)
|
||||
print(f" WASM alloc() returned ptr = {ptr}")
|
||||
|
||||
enc_wasm._wasm.free(ptr)
|
||||
print(f" WASM free() OK")
|
||||
|
||||
print(" [OK] WASM basic operations working.\n")
|
||||
except Exception as e:
|
||||
print(" [FAIL] WASM operations error:", e)
|
||||
else:
|
||||
print(" Skipping WASM basic operations.\n")
|
||||
|
||||
print("\n========== DONE ==========\n")
|
||||
7
python/tests/test_basic_backend_selection.py
Normal file
7
python/tests/test_basic_backend_selection.py
Normal file
@@ -0,0 +1,7 @@
|
||||
from basisu_py import Encoder
|
||||
|
||||
enc = Encoder() # AUTO mode
|
||||
print("Encoder backend:", enc.backend)
|
||||
print("Native loaded:", enc._native is not None)
|
||||
print("WASM loaded:", enc._wasm is not None)
|
||||
print("Version:", enc._native.get_version() if enc._native else enc._wasm.get_version())
|
||||
19
python/tests/test_basic_decode.py
Normal file
19
python/tests/test_basic_decode.py
Normal file
@@ -0,0 +1,19 @@
|
||||
from basisu_py import Transcoder
|
||||
from PIL import Image
|
||||
import numpy as np
|
||||
|
||||
# Load input file
|
||||
with open("test.ktx2", "rb") as f:
|
||||
data = f.read()
|
||||
|
||||
# Decode (AUTO backend)
|
||||
t = Transcoder()
|
||||
rgba = t.decode_rgba(data) # returns HxWx4 uint8 NumPy array
|
||||
|
||||
print("Decoded:", rgba.shape, rgba.dtype)
|
||||
|
||||
# Convert to Pillow Image and save
|
||||
img = Image.fromarray(rgba, mode="RGBA")
|
||||
img.save("decoded.png")
|
||||
|
||||
print("Wrote decoded.png")
|
||||
10
python/tests/test_basic_transcode.py
Normal file
10
python/tests/test_basic_transcode.py
Normal file
@@ -0,0 +1,10 @@
|
||||
from basisu_py import Transcoder
|
||||
|
||||
with open("test.ktx2", "rb") as f:
|
||||
data = f.read()
|
||||
|
||||
t = Transcoder() # AUTO backend
|
||||
img = t.decode_rgba(data)
|
||||
|
||||
print("Decoded shape:", img.shape)
|
||||
print("dtype:", img.dtype)
|
||||
6
python/tests/test_basic_wasm_selection.py
Normal file
6
python/tests/test_basic_wasm_selection.py
Normal file
@@ -0,0 +1,6 @@
|
||||
from basisu_py import Transcoder
|
||||
from basisu_py.transcoder import TranscoderBackend
|
||||
|
||||
t = Transcoder(backend=TranscoderBackend.WASM)
|
||||
print("Backend:", t.backend_name)
|
||||
t.decode_rgba(open("test.ktx2","rb").read())
|
||||
82
python/tests/test_compress_swirl.py
Normal file
82
python/tests/test_compress_swirl.py
Normal file
@@ -0,0 +1,82 @@
|
||||
#!/usr/bin/env python3
|
||||
import numpy as np
|
||||
from PIL import Image
|
||||
from math import sin, cos, atan2, hypot
|
||||
|
||||
from basisu_py.codec import Encoder, EncoderBackend
|
||||
from basisu_py.constants import BasisTexFormat, BasisQuality, BasisEffort, BasisFlags
|
||||
|
||||
|
||||
# --------------------------------------------------------------
|
||||
# Procedural swirl pattern (RGBA8)
|
||||
# --------------------------------------------------------------
|
||||
def make_swirl_image(w=256, h=256):
|
||||
arr = np.zeros((h, w, 4), dtype=np.uint8)
|
||||
|
||||
cx = w / 2.0
|
||||
cy = h / 2.0
|
||||
|
||||
for y in range(h):
|
||||
for x in range(w):
|
||||
dx = x - cx
|
||||
dy = y - cy
|
||||
|
||||
dist = hypot(dx, dy)
|
||||
angle = atan2(dy, dx)
|
||||
|
||||
r = int((sin(dist * 0.15) * 0.5 + 0.5) * 255)
|
||||
g = int((sin(angle * 3.0) * 0.5 + 0.5) * 255)
|
||||
b = int((cos(dist * 0.10 + angle * 2.0) * 0.5 + 0.5) * 255)
|
||||
|
||||
arr[y, x] = (r, g, b, 255)
|
||||
|
||||
return arr
|
||||
|
||||
|
||||
# --------------------------------------------------------------
|
||||
# Test encode using a given backend
|
||||
# --------------------------------------------------------------
|
||||
def compress_swirl(backend, outfile):
|
||||
print(f"\n========== Testing {backend} backend ==========")
|
||||
|
||||
# Build procedural image
|
||||
swirl = make_swirl_image(256, 256)
|
||||
print("Generated swirl image:", swirl.shape)
|
||||
|
||||
# Create encoder
|
||||
enc = Encoder(backend=backend)
|
||||
|
||||
# Compress
|
||||
blob = enc.compress(
|
||||
swirl,
|
||||
format=BasisTexFormat.cUASTC_LDR_4x4,
|
||||
quality=BasisQuality.MAX,
|
||||
effort=BasisEffort.DEFAULT,
|
||||
flags=BasisFlags.KTX2_OUTPUT | BasisFlags.SRGB
|
||||
)
|
||||
|
||||
print(f"Compressed blob size: {len(blob)} bytes")
|
||||
|
||||
# Save output
|
||||
with open(outfile, "wb") as f:
|
||||
f.write(blob)
|
||||
|
||||
print(f"Wrote: {outfile}")
|
||||
print("==============================================")
|
||||
|
||||
|
||||
# --------------------------------------------------------------
|
||||
# Main
|
||||
# --------------------------------------------------------------
|
||||
if __name__ == "__main__":
|
||||
# Test native backend
|
||||
try:
|
||||
compress_swirl(EncoderBackend.NATIVE, "swirl_native.ktx2")
|
||||
except Exception as e:
|
||||
print("Native backend ERROR:", e)
|
||||
|
||||
# Test WASM backend
|
||||
try:
|
||||
compress_swirl(EncoderBackend.WASM, "swirl_wasm.ktx2")
|
||||
except Exception as e:
|
||||
print("WASM backend ERROR:", e)
|
||||
75
python/tests/test_compress_swirl_hdr.py
Normal file
75
python/tests/test_compress_swirl_hdr.py
Normal file
@@ -0,0 +1,75 @@
|
||||
#!/usr/bin/env python3
|
||||
import numpy as np
|
||||
from math import sin, cos, atan2, hypot
|
||||
from basisu_py.codec import Encoder, EncoderBackend
|
||||
from basisu_py.constants import BasisTexFormat, BasisQuality, BasisEffort, BasisFlags
|
||||
|
||||
|
||||
# --------------------------------------------------------------
|
||||
# Procedural HDR swirl pattern (float32 RGBA)
|
||||
# --------------------------------------------------------------
|
||||
def make_hdr_swirl_image(w=256, h=256):
|
||||
arr = np.zeros((h, w, 4), dtype=np.float32)
|
||||
|
||||
cx = w / 2.0
|
||||
cy = h / 2.0
|
||||
|
||||
for y in range(h):
|
||||
for x in range(w):
|
||||
dx = x - cx
|
||||
dy = y - cy
|
||||
dist = hypot(dx, dy)
|
||||
angle = atan2(dy, dx)
|
||||
|
||||
r = (sin(dist * 0.15) * 0.5 + 0.5)
|
||||
g = (sin(angle * 3.0) * 0.5 + 0.5)
|
||||
b = (cos(dist * 0.10 + angle * 2.0) * 0.5 + 0.5)
|
||||
|
||||
arr[y, x] = (r, g, b, 1.0) # full alpha
|
||||
|
||||
return arr
|
||||
|
||||
|
||||
# --------------------------------------------------------------
|
||||
# Test encode using a given backend
|
||||
# --------------------------------------------------------------
|
||||
def compress_hdr_swirl(backend, outfile):
|
||||
print(f"\n========== Testing HDR {backend} backend ==========")
|
||||
|
||||
hdr = make_hdr_swirl_image(256, 256)
|
||||
print("Generated HDR swirl image:", hdr.shape, hdr.dtype)
|
||||
|
||||
enc = Encoder(backend=backend)
|
||||
|
||||
blob = enc.compress(
|
||||
hdr,
|
||||
format=-1, # auto-select HDR (UASTC_HDR_4x4)
|
||||
quality=BasisQuality.MAX,
|
||||
effort=BasisEffort.DEFAULT,
|
||||
flags=BasisFlags.KTX2_OUTPUT | BasisFlags.SRGB
|
||||
)
|
||||
|
||||
print(f"Compressed blob size: {len(blob)} bytes")
|
||||
|
||||
with open(outfile, "wb") as f:
|
||||
f.write(blob)
|
||||
|
||||
print(f"Wrote: {outfile}")
|
||||
print("==============================================")
|
||||
|
||||
|
||||
# --------------------------------------------------------------
|
||||
# Main
|
||||
# --------------------------------------------------------------
|
||||
if __name__ == "__main__":
|
||||
# Native backend
|
||||
try:
|
||||
compress_hdr_swirl(EncoderBackend.NATIVE, "hdr_swirl_native.ktx2")
|
||||
except Exception as e:
|
||||
print("Native HDR backend ERROR:", e)
|
||||
|
||||
# WASM backend
|
||||
try:
|
||||
compress_hdr_swirl(EncoderBackend.WASM, "hdr_swirl_wasm.ktx2")
|
||||
except Exception as e:
|
||||
print("WASM HDR backend ERROR:", e)
|
||||
18
python/tests/test_transcoder_astc.py
Normal file
18
python/tests/test_transcoder_astc.py
Normal file
@@ -0,0 +1,18 @@
|
||||
from basisu_py import Transcoder
|
||||
from astc_writer import write_astc_file
|
||||
|
||||
# Load a .ktx2
|
||||
data = open("input.ktx2", "rb").read()
|
||||
t = Transcoder()
|
||||
|
||||
# Transcode to ASTC
|
||||
h = t.open(data)
|
||||
bw = t.get_block_width(h) # or basis_get_block_width(astc_tfmt)
|
||||
bh = t.get_block_height(h)
|
||||
tfmt = t.basis_get_transcoder_texture_format_from_basis_tex_format(
|
||||
t.get_basis_tex_format(h)
|
||||
)
|
||||
|
||||
blocks = t.transcode_tfmt(data, tfmt)
|
||||
write_astc_file("output.astc", blocks, bw, bh, t.get_width(h), t.get_height(h))
|
||||
t.close(h)
|
||||
72
python/tests/test_transcoder_backend_loading.py
Normal file
72
python/tests/test_transcoder_backend_loading.py
Normal file
@@ -0,0 +1,72 @@
|
||||
#!/usr/bin/env python3
|
||||
import sys
|
||||
from basisu_py.transcoder import Transcoder, TranscoderBackend
|
||||
from basisu_py.constants import BasisTexFormat
|
||||
|
||||
print("========== TESTING TRANSCODER BACKENDS ==========\n")
|
||||
|
||||
# Load some test data (ensure test.ktx2 exists)
|
||||
try:
|
||||
test_data = open("test.ktx2", "rb").read()
|
||||
print("[INFO] Loaded test.ktx2")
|
||||
except FileNotFoundError:
|
||||
print("[ERROR] test.ktx2 not found. Create one first via encoder tests.")
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
# -------------------------------------------------------------------
|
||||
# 1. Test NATIVE backend
|
||||
# -------------------------------------------------------------------
|
||||
print("\n--- Testing NATIVE transcoder backend ---")
|
||||
|
||||
try:
|
||||
t_native = Transcoder(TranscoderBackend.NATIVE)
|
||||
print(" [OK] Native backend loaded")
|
||||
|
||||
version = t_native.get_version()
|
||||
print(f" Native get_version() = {version}")
|
||||
|
||||
# Open KTX2
|
||||
raw = t_native.open(test_data)
|
||||
print(" [OK] Opened KTX2 (native)")
|
||||
|
||||
# Query some basic properties
|
||||
print(" Width :", t_native.get_width(raw))
|
||||
print(" Height:", t_native.get_height(raw))
|
||||
print(" Levels:", t_native.get_levels(raw))
|
||||
|
||||
# Cleanup
|
||||
t_native.close(raw)
|
||||
print(" [OK] Native transcoder basic operations working.")
|
||||
|
||||
except Exception as e:
|
||||
print(" [FAIL] Native transcoder error:", e)
|
||||
|
||||
|
||||
# -------------------------------------------------------------------
|
||||
# 2. Test WASM backend
|
||||
# -------------------------------------------------------------------
|
||||
print("\n--- Testing WASM transcoder backend ---")
|
||||
|
||||
try:
|
||||
t_wasm = Transcoder(TranscoderBackend.WASM)
|
||||
print(" [OK] WASM backend loaded")
|
||||
|
||||
version = t_wasm.get_version()
|
||||
print(f" WASM get_version() = {version}")
|
||||
|
||||
raw = t_wasm.open(test_data)
|
||||
print(" [OK] Opened KTX2 (wasm)")
|
||||
|
||||
print(" Width :", t_wasm.get_width(raw))
|
||||
print(" Height:", t_wasm.get_height(raw))
|
||||
print(" Levels:", t_wasm.get_levels(raw))
|
||||
|
||||
t_wasm.close(raw)
|
||||
print(" [OK] WASM transcoder basic operations working.")
|
||||
|
||||
except Exception as e:
|
||||
print(" [FAIL] WASM transcoder error:", e)
|
||||
|
||||
|
||||
print("\n========== DONE ==========")
|
||||
154
python/tests/test_transcoder_end_to_end.py
Normal file
154
python/tests/test_transcoder_end_to_end.py
Normal file
@@ -0,0 +1,154 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Full end-to-end transcoder test with automatic fallback.
|
||||
|
||||
- Generates a swirl image
|
||||
- Compresses it using native OR WASM (AUTO mode)
|
||||
- Writes test.ktx2
|
||||
- Decodes it using whichever backends are available:
|
||||
* AUTO (native if present, otherwise WASM)
|
||||
* Native (if available)
|
||||
* WASM (if available)
|
||||
- Produces PNG outputs for all successful backends
|
||||
"""
|
||||
|
||||
import numpy as np
|
||||
from math import sin, cos, atan2, hypot
|
||||
from PIL import Image
|
||||
import sys
|
||||
|
||||
from basisu_py.codec import Encoder, EncoderBackend
|
||||
from basisu_py.transcoder import Transcoder, TranscoderBackend
|
||||
from basisu_py.constants import (
|
||||
BasisTexFormat,
|
||||
BasisQuality,
|
||||
BasisEffort,
|
||||
BasisFlags,
|
||||
)
|
||||
|
||||
|
||||
# -------------------------------------------------------------------
|
||||
# Create an RGBA swirl test image
|
||||
# -------------------------------------------------------------------
|
||||
def make_swirl(w=256, h=256):
|
||||
arr = np.zeros((h, w, 4), dtype=np.uint8)
|
||||
|
||||
cx, cy = w / 2.0, h / 2.0
|
||||
|
||||
for y in range(h):
|
||||
for x in range(w):
|
||||
dx, dy = x - cx, y - cy
|
||||
dist = hypot(dx, dy)
|
||||
angle = atan2(dy, dx)
|
||||
|
||||
r = int((sin(dist * 0.15) * 0.5 + 0.5) * 255)
|
||||
g = int((sin(angle * 3.0) * 0.5 + 0.5) * 255)
|
||||
b = int((cos(dist * 0.10 + angle * 2.0) * 0.5 + 0.5) * 255)
|
||||
|
||||
arr[y, x] = (r, g, b, 255)
|
||||
|
||||
return arr
|
||||
|
||||
|
||||
# -------------------------------------------------------------------
|
||||
# Try loading transcoder with a backend, return (success, transcoder)
|
||||
# -------------------------------------------------------------------
|
||||
def try_transcoder(backend):
|
||||
try:
|
||||
t = Transcoder(backend)
|
||||
print(f"[OK] Loaded transcoder backend '{backend}' ({t.backend_name})")
|
||||
return True, t
|
||||
except Exception as e:
|
||||
print(f"[SKIP] Backend '{backend}' unavailable:", e)
|
||||
return False, None
|
||||
|
||||
|
||||
# -------------------------------------------------------------------
|
||||
# Try loading encoder with a backend, return blob or None
|
||||
# -------------------------------------------------------------------
|
||||
def try_encoder(backend, img):
|
||||
try:
|
||||
enc = Encoder(backend)
|
||||
print(f"[OK] Loaded encoder backend '{backend}' ({enc.backend_name})")
|
||||
except Exception as e:
|
||||
print(f"[SKIP] Encoder backend '{backend}' unavailable:", e)
|
||||
return None
|
||||
|
||||
try:
|
||||
print(f"[Test] Compressing swirl -> KTX2 using {enc.backend_name}...")
|
||||
blob = enc.compress(
|
||||
img,
|
||||
format=-1,
|
||||
quality=BasisQuality.MAX,
|
||||
effort=BasisEffort.DEFAULT,
|
||||
flags=BasisFlags.KTX2_OUTPUT | BasisFlags.SRGB
|
||||
)
|
||||
return blob
|
||||
except Exception as e:
|
||||
print(f"[FAIL] Compression failed on backend '{backend}':", e)
|
||||
return None
|
||||
|
||||
|
||||
# -------------------------------------------------------------------
|
||||
# Decode blob with a given transcoder
|
||||
# -------------------------------------------------------------------
|
||||
def decode_with_backend(name, t, blob):
|
||||
try:
|
||||
rgba = t.decode_rgba(blob)
|
||||
outname = f"decoded_{name}.png"
|
||||
Image.fromarray(rgba, mode="RGBA").save(outname)
|
||||
print(f" --> {name}: decoded successfully, wrote {outname}")
|
||||
except Exception as e:
|
||||
print(f" [FAIL] decode_rgba on backend '{name}':", e)
|
||||
|
||||
|
||||
# -------------------------------------------------------------------
|
||||
# Main test
|
||||
# -------------------------------------------------------------------
|
||||
if __name__ == "__main__":
|
||||
print("========== BasisU End-to-End Compression & Transcoding Test ==========")
|
||||
|
||||
# -------------------------------------------------------
|
||||
# Generate swirl test
|
||||
# -------------------------------------------------------
|
||||
img = make_swirl(256, 256)
|
||||
print("[Test] Generated swirl:", img.shape)
|
||||
|
||||
# -------------------------------------------------------
|
||||
# Try AUTO encoder (native if available, else WASM)
|
||||
# -------------------------------------------------------
|
||||
blob = try_encoder(EncoderBackend.AUTO, img)
|
||||
if blob is None:
|
||||
print("[FAIL] Could not encode using AUTO backend; aborting.")
|
||||
sys.exit(1)
|
||||
|
||||
# Save test.ktx2
|
||||
with open("test.ktx2", "wb") as f:
|
||||
f.write(blob)
|
||||
print("[Test] Wrote: test.ktx2")
|
||||
|
||||
# -------------------------------------------------------
|
||||
# Test transcoding using AUTO
|
||||
# -------------------------------------------------------
|
||||
print("\n[Test] Decoding via AUTO backend...")
|
||||
ok_auto, t_auto = try_transcoder(TranscoderBackend.AUTO)
|
||||
if ok_auto:
|
||||
decode_with_backend("auto", t_auto, blob)
|
||||
|
||||
# -------------------------------------------------------
|
||||
# Test NATIVE explicitly (if available)
|
||||
# -------------------------------------------------------
|
||||
print("\n[Test] Decoding via NATIVE backend...")
|
||||
ok_native, t_native = try_transcoder(TranscoderBackend.NATIVE)
|
||||
if ok_native:
|
||||
decode_with_backend("native", t_native, blob)
|
||||
|
||||
# -------------------------------------------------------
|
||||
# Test WASM explicitly (if available)
|
||||
# -------------------------------------------------------
|
||||
print("\n[Test] Decoding via WASM backend...")
|
||||
ok_wasm, t_wasm = try_transcoder(TranscoderBackend.WASM)
|
||||
if ok_wasm:
|
||||
decode_with_backend("wasm", t_wasm, blob)
|
||||
|
||||
print("\n========== DONE ==========")
|
||||
175
python/tests/test_transcoder_end_to_end_hdr.py
Normal file
175
python/tests/test_transcoder_end_to_end_hdr.py
Normal file
@@ -0,0 +1,175 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
HDR End-to-End Compression & Transcoding Test
|
||||
Works on all platforms:
|
||||
- native if available
|
||||
- WASM fallback otherwise
|
||||
"""
|
||||
|
||||
import numpy as np
|
||||
from math import sin, cos, atan2, hypot
|
||||
from PIL import Image
|
||||
import subprocess
|
||||
import tempfile
|
||||
import os
|
||||
import imageio.v3 as iio
|
||||
|
||||
from basisu_py.codec import Encoder, EncoderBackend
|
||||
from basisu_py.transcoder import Transcoder, TranscoderBackend
|
||||
from basisu_py.constants import (
|
||||
BasisTexFormat,
|
||||
BasisQuality,
|
||||
BasisEffort,
|
||||
BasisFlags
|
||||
)
|
||||
|
||||
|
||||
# -------------------------------------------------------------------
|
||||
# Save EXR using TIFF temp + oiiotool (as required)
|
||||
# -------------------------------------------------------------------
|
||||
def save_exr(path, rgba32f):
|
||||
"""
|
||||
Save float32 RGBA as EXR if possible.
|
||||
If oiiotool is not available, save TIFF instead (Windows-safe).
|
||||
"""
|
||||
import numpy as np
|
||||
import imageio.v3 as iio
|
||||
import subprocess, tempfile, os
|
||||
|
||||
# Write temp TIFF
|
||||
with tempfile.NamedTemporaryFile(suffix=".tiff", delete=False) as tmp:
|
||||
temp_path = tmp.name
|
||||
|
||||
iio.imwrite(temp_path, rgba32f.astype(np.float32))
|
||||
|
||||
# Try EXR via oiiotool
|
||||
try:
|
||||
subprocess.run(["oiiotool", temp_path, "-o", path], check=True)
|
||||
os.remove(temp_path)
|
||||
print(" Wrote EXR:", path)
|
||||
return
|
||||
|
||||
except Exception:
|
||||
# --- FALLBACK: save TIFF ---
|
||||
fallback_path = path + ".tiff"
|
||||
|
||||
# Windows cannot overwrite files via rename(), so remove first
|
||||
if os.path.exists(fallback_path):
|
||||
os.remove(fallback_path)
|
||||
|
||||
# os.replace() always overwrites
|
||||
os.replace(temp_path, fallback_path)
|
||||
|
||||
print(" [Fallback] Wrote TIFF instead:", fallback_path)
|
||||
|
||||
# -------------------------------------------------------------------
|
||||
# Generate HDR swirl image (float32)
|
||||
# -------------------------------------------------------------------
|
||||
def make_swirl_hdr(w=256, h=256):
|
||||
arr = np.zeros((h, w, 4), dtype=np.float32)
|
||||
cx, cy = w / 2.0, h / 2.0
|
||||
|
||||
for y in range(h):
|
||||
for x in range(w):
|
||||
dx, dy = x - cx, y - cy
|
||||
dist = hypot(dx, dy)
|
||||
angle = atan2(dy, dx)
|
||||
|
||||
# HDR values range up to about 4.0
|
||||
r = (sin(dist * 0.08) * 0.5 + 0.5) * 4.0
|
||||
g = (sin(angle * 2.0) * 0.5 + 0.5) * 4.0
|
||||
b = (cos(dist * 0.06 + angle * 1.5) * 0.5 + 0.5) * 4.0
|
||||
|
||||
arr[y, x] = (r, g, b, 1.0)
|
||||
|
||||
return arr
|
||||
|
||||
|
||||
# -------------------------------------------------------------------
|
||||
# Try loading a transcoder backend
|
||||
# -------------------------------------------------------------------
|
||||
def try_transcoder(name, backend):
|
||||
try:
|
||||
t = Transcoder(backend)
|
||||
print(f"[OK] Loaded transcoder backend '{name}' ({t.backend_name})")
|
||||
return t
|
||||
except Exception as e:
|
||||
print(f"[SKIP] Backend '{name}' unavailable:", e)
|
||||
return None
|
||||
|
||||
|
||||
# -------------------------------------------------------------------
|
||||
# MAIN
|
||||
# -------------------------------------------------------------------
|
||||
if __name__ == "__main__":
|
||||
print("========== HDR End-to-End Compression & Transcoding Test ==========")
|
||||
|
||||
# -------------------------------------------------------
|
||||
# Create HDR test image
|
||||
# -------------------------------------------------------
|
||||
img_hdr = make_swirl_hdr(256, 256)
|
||||
print("[HDR] swirl:", img_hdr.shape, img_hdr.dtype)
|
||||
|
||||
# -------------------------------------------------------
|
||||
# ENCODE using AUTO backend (native ? or WASM)
|
||||
# -------------------------------------------------------
|
||||
try:
|
||||
enc = Encoder(EncoderBackend.AUTO)
|
||||
print(f"[HDR] Encoder backend = {enc.backend_name}")
|
||||
except Exception as e:
|
||||
print("[FATAL] Could not create encoder:", e)
|
||||
exit(1)
|
||||
|
||||
try:
|
||||
print("[HDR] Compressing HDR swirl -> test_hdr.ktx2...")
|
||||
ktx2_blob = enc.compress(
|
||||
img_hdr,
|
||||
format=-1, # auto-select HDR format
|
||||
quality=BasisQuality.MAX,
|
||||
effort=BasisEffort.DEFAULT,
|
||||
flags=BasisFlags.KTX2_OUTPUT
|
||||
)
|
||||
print(" KTX2 size:", len(ktx2_blob))
|
||||
open("test_hdr.ktx2", "wb").write(ktx2_blob)
|
||||
print(" Wrote test_hdr.ktx2")
|
||||
except Exception as e:
|
||||
print("[FATAL] Encoding failed:", e)
|
||||
exit(1)
|
||||
|
||||
# -------------------------------------------------------
|
||||
# DECODE using AUTO (native ? or WASM)
|
||||
# -------------------------------------------------------
|
||||
t_auto = try_transcoder("AUTO", TranscoderBackend.AUTO)
|
||||
if t_auto:
|
||||
try:
|
||||
hdr = t_auto.decode_rgba_hdr(ktx2_blob)
|
||||
print(" AUTO decoded:", hdr.shape, hdr.dtype)
|
||||
save_exr("decoded_auto_hdr.exr", hdr)
|
||||
except Exception as e:
|
||||
print(" [FAIL] AUTO decode failed:", e)
|
||||
|
||||
# -------------------------------------------------------
|
||||
# DECODE using NATIVE if available
|
||||
# -------------------------------------------------------
|
||||
t_native = try_transcoder("NATIVE", TranscoderBackend.NATIVE)
|
||||
if t_native:
|
||||
try:
|
||||
hdr_n = t_native.decode_rgba_hdr(ktx2_blob)
|
||||
print(" Native decoded:", hdr_n.shape, hdr_n.dtype)
|
||||
save_exr("decoded_native_hdr.exr", hdr_n)
|
||||
except Exception as e:
|
||||
print(" [FAIL] Native decode failed:", e)
|
||||
|
||||
# -------------------------------------------------------
|
||||
# DECODE using WASM if available
|
||||
# -------------------------------------------------------
|
||||
t_wasm = try_transcoder("WASM", TranscoderBackend.WASM)
|
||||
if t_wasm:
|
||||
try:
|
||||
hdr_w = t_wasm.decode_rgba_hdr(ktx2_blob)
|
||||
print(" WASM decoded:", hdr_w.shape, hdr_w.dtype)
|
||||
save_exr("decoded_wasm_hdr.exr", hdr_w)
|
||||
except Exception as e:
|
||||
print(" [FAIL] WASM decode failed:", e)
|
||||
|
||||
print("\n========== DONE ==========")
|
||||
190
python/tests/test_transcoder_helpers.py
Normal file
190
python/tests/test_transcoder_helpers.py
Normal file
@@ -0,0 +1,190 @@
|
||||
#!/usr/bin/env python3
|
||||
import sys
|
||||
import numpy as np
|
||||
|
||||
from basisu_py.transcoder import Transcoder, TranscoderBackend
|
||||
from basisu_py.constants import BasisTexFormat, TranscoderTextureFormat
|
||||
|
||||
print("========== TESTING TRANSCODER HELPERS & METADATA ==========\n")
|
||||
|
||||
# ----------------------------------------------------------------------------
|
||||
# Load test KTX2 file
|
||||
# ----------------------------------------------------------------------------
|
||||
try:
|
||||
ktx2_bytes = open("test.ktx2", "rb").read()
|
||||
print("[INFO] Loaded test.ktx2")
|
||||
except FileNotFoundError:
|
||||
print("[ERROR] test.ktx2 not found. Run encoder tests first.")
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
# ----------------------------------------------------------------------------
|
||||
# Utility: run helper tests on a given backend
|
||||
# ----------------------------------------------------------------------------
|
||||
def test_backend(name, backend):
|
||||
print(f"\n=== Testing {name} backend ===")
|
||||
|
||||
try:
|
||||
t = Transcoder(backend)
|
||||
except Exception as e:
|
||||
print(f"[FAIL] Could not initialize {name} backend:", e)
|
||||
return
|
||||
|
||||
print(f"[OK] {name} backend loaded")
|
||||
|
||||
# Version
|
||||
try:
|
||||
ver = t.get_version()
|
||||
print(f" version = {ver}")
|
||||
except Exception as e:
|
||||
print(" [FAIL] get_version() error:", e)
|
||||
return
|
||||
|
||||
# enable_debug_printf
|
||||
try:
|
||||
t.enable_debug_printf(True)
|
||||
except Exception as e:
|
||||
print(" [FAIL] enable_debug_printf() failed")
|
||||
return
|
||||
|
||||
# Open KTX2
|
||||
try:
|
||||
raw = t.open(ktx2_bytes)
|
||||
print(" [OK] open() success")
|
||||
except Exception as e:
|
||||
print(" [FAIL] open() failed:", e)
|
||||
return
|
||||
|
||||
# ----------------------------------------------------------------------
|
||||
# KTX2 top-level metadata
|
||||
# ----------------------------------------------------------------------
|
||||
try:
|
||||
w = t.get_width(raw)
|
||||
h = t.get_height(raw)
|
||||
lv = t.get_levels(raw)
|
||||
fc = t.get_faces(raw)
|
||||
la = t.get_layers(raw)
|
||||
fmt = t.get_basis_tex_format(raw)
|
||||
|
||||
print(f" Width = {w}")
|
||||
print(f" Height = {h}")
|
||||
print(f" Levels = {lv}")
|
||||
print(f" Faces = {fc}")
|
||||
print(f" Layers = {la}")
|
||||
print(f" basis_tex_format = {fmt}")
|
||||
print(f" has_alpha = {t.has_alpha(raw)}")
|
||||
print(f" is_hdr = {t.is_hdr(raw)}")
|
||||
print(f" is_ldr = {t.is_ldr(raw)}")
|
||||
print(f" is_srgb = {t.is_srgb(raw)}")
|
||||
print(f" is_etc1s = {t.is_etc1s(raw)}")
|
||||
print(f" is_uastc_ldr_4x4 = {t.is_uastc_ldr_4x4(raw)}")
|
||||
print(f" is_xuastc_ldr = {t.is_xuastc_ldr(raw)}")
|
||||
print(f" is_astc_ldr = {t.is_astc_ldr(raw)}")
|
||||
print(f" block dims = {t.get_block_width(raw)} x {t.get_block_height(raw)}")
|
||||
|
||||
except Exception as e:
|
||||
print(" [FAIL] get_* metadata error:", e)
|
||||
t.close(raw)
|
||||
return
|
||||
|
||||
# ----------------------------------------------------------------------
|
||||
# Per-level metadata for each mipmap
|
||||
# ----------------------------------------------------------------------
|
||||
print("\n -- Level Metadata --")
|
||||
for level in range(lv):
|
||||
try:
|
||||
ow = t.get_level_orig_width(raw, level)
|
||||
oh = t.get_level_orig_height(raw, level)
|
||||
nbx = t.get_level_num_blocks_x(raw, level)
|
||||
nby = t.get_level_num_blocks_y(raw, level)
|
||||
tb = t.get_level_total_blocks(raw, level)
|
||||
af = t.get_level_alpha_flag(raw, level)
|
||||
ff = t.get_level_iframe_flag(raw, level)
|
||||
|
||||
print(f" Level {level}: orig={ow}x{oh}, blocks={nbx}x{nby}, total={tb}, alpha={af}, iframe={ff}")
|
||||
except Exception as e:
|
||||
print(f" [FAIL] Level {level} metadata error:", e)
|
||||
|
||||
# ----------------------------------------------------------------------
|
||||
# Test ALL basis_tex_format helpers on the file's format
|
||||
# ----------------------------------------------------------------------
|
||||
print("\n -- basis_tex_format helpers --")
|
||||
|
||||
try:
|
||||
print(f" is_xuastc_ldr = {t.basis_tex_format_is_xuastc_ldr(fmt)}")
|
||||
print(f" is_astc_ldr = {t.basis_tex_format_is_astc_ldr(fmt)}")
|
||||
print(f" block W/H = {t.basis_tex_format_get_block_width(fmt)} x "
|
||||
f"{t.basis_tex_format_get_block_height(fmt)}")
|
||||
print(f" is_hdr = {t.basis_tex_format_is_hdr(fmt)}")
|
||||
print(f" is_ldr = {t.basis_tex_format_is_ldr(fmt)}")
|
||||
except Exception as e:
|
||||
print(" [FAIL] basis_tex_format_* error:", e)
|
||||
|
||||
# ----------------------------------------------------------------------
|
||||
# Test transcoder_texture_format helpers using a few common formats
|
||||
# ----------------------------------------------------------------------
|
||||
print("\n -- transcoder_texture_format helpers --")
|
||||
|
||||
test_formats = [
|
||||
TranscoderTextureFormat.TF_RGBA32,
|
||||
TranscoderTextureFormat.TF_RGBA_HALF,
|
||||
TranscoderTextureFormat.TF_BC7_RGBA,
|
||||
TranscoderTextureFormat.TF_ETC1_RGB,
|
||||
]
|
||||
|
||||
for tfmt in test_formats:
|
||||
try:
|
||||
print(f" Format {tfmt}: hdr={t.basis_transcoder_format_is_hdr(tfmt)}, "
|
||||
f"ldr={t.basis_transcoder_format_is_ldr(tfmt)}, "
|
||||
f"has_alpha={t.basis_transcoder_format_has_alpha(tfmt)}, "
|
||||
f"uncompressed={t.basis_transcoder_format_is_uncompressed(tfmt)}, "
|
||||
f"bytes/pixel or block={t.basis_get_bytes_per_block_or_pixel(tfmt)}")
|
||||
except Exception as e:
|
||||
print(" [FAIL] transcoder_texture_format_* error:", e)
|
||||
|
||||
# ----------------------------------------------------------------------
|
||||
# Compute transcode buffer sizes
|
||||
# ----------------------------------------------------------------------
|
||||
print("\n -- compute_transcoded_image_size_in_bytes --")
|
||||
try:
|
||||
for tfmt in test_formats:
|
||||
sz = t.basis_compute_transcoded_image_size_in_bytes(tfmt, w, h)
|
||||
print(f" Format {tfmt}: size = {sz}")
|
||||
except Exception as e:
|
||||
print(" [FAIL] size computation error:", e)
|
||||
|
||||
# ----------------------------------------------------------------------
|
||||
# Decode RGBA (LDR)
|
||||
# ----------------------------------------------------------------------
|
||||
print("\n -- decode_rgba --")
|
||||
try:
|
||||
img_rgba = t.decode_rgba(ktx2_bytes)
|
||||
print(f" decode_rgba: shape={img_rgba.shape}, dtype={img_rgba.dtype}")
|
||||
except Exception as e:
|
||||
print(" [FAIL] decode_rgba error:", e)
|
||||
|
||||
# ----------------------------------------------------------------------
|
||||
# Decode HDR if applicable
|
||||
# ----------------------------------------------------------------------
|
||||
if t.is_hdr(raw):
|
||||
print("\n -- decode_rgba_hdr --")
|
||||
try:
|
||||
img_hdr = t.decode_rgba_hdr(ktx2_bytes)
|
||||
print(f" decode_rgba_hdr: shape={img_hdr.shape}, dtype={img_hdr.dtype}")
|
||||
except Exception as e:
|
||||
print(" [FAIL] decode_rgba_hdr error:", e)
|
||||
else:
|
||||
print(" Texture is LDR; skipping decode_rgba_hdr().")
|
||||
|
||||
# Cleanup
|
||||
t.close(raw)
|
||||
print(f"\n=== {name} backend OK ===\n")
|
||||
|
||||
|
||||
# ----------------------------------------------------------------------------
|
||||
# Run tests for both backends
|
||||
# ----------------------------------------------------------------------------
|
||||
test_backend("NATIVE", TranscoderBackend.NATIVE)
|
||||
test_backend("WASM", TranscoderBackend.WASM)
|
||||
|
||||
print("\n========== DONE ==========\n")
|
||||
Reference in New Issue
Block a user