355 lines
12 KiB
Python
355 lines
12 KiB
Python
"""Software decoder for S3TC compressed texture (i.e., DDS).
|
|
|
|
http://oss.sgi.com/projects/ogl-sample/registry/EXT/texture_compression_s3tc.txt
|
|
"""
|
|
|
|
import re
|
|
import ctypes
|
|
|
|
from pyglet.gl import *
|
|
from pyglet.gl import gl_info
|
|
from pyglet.image import AbstractImage, Texture
|
|
|
|
split_8byte = re.compile('.' * 8, flags=re.DOTALL)
|
|
split_16byte = re.compile('.' * 16, flags=re.DOTALL)
|
|
|
|
|
|
class PackedImageData(AbstractImage):
|
|
_current_texture = None
|
|
|
|
def __init__(self, width, height, fmt, packed_format, data):
|
|
super().__init__(width, height)
|
|
self.format = fmt
|
|
self.packed_format = packed_format
|
|
self.data = data
|
|
|
|
def unpack(self):
|
|
if self.packed_format == GL_UNSIGNED_SHORT_5_6_5:
|
|
# Unpack to GL_RGB. Assume self.data is already 16-bit
|
|
i = 0
|
|
out = (ctypes.c_ubyte * (self.width * self.height * 3))()
|
|
for c in self.data:
|
|
out[i + 2] = (c & 0x1f) << 3
|
|
out[i + 1] = (c & 0x7e0) >> 3
|
|
out[i] = (c & 0xf800) >> 8
|
|
i += 3
|
|
self.data = out
|
|
self.packed_format = GL_UNSIGNED_BYTE
|
|
|
|
def _get_texture(self):
|
|
if self._current_texture:
|
|
return self._current_texture
|
|
|
|
texture = Texture.create(self.width, self.height, GL_TEXTURE_2D, None)
|
|
glBindTexture(texture.target, texture.id)
|
|
glTexParameteri(texture.target, GL_TEXTURE_MIN_FILTER, GL_LINEAR)
|
|
|
|
if not gl_info.have_version(1, 2) or True:
|
|
self.unpack()
|
|
|
|
glTexImage2D(texture.target, texture.level,
|
|
self.format, self.width, self.height, 0,
|
|
self.format, self.packed_format, self.data)
|
|
|
|
self._current_texture = texture
|
|
return texture
|
|
|
|
texture = property(_get_texture)
|
|
|
|
def get_texture(self, rectangle=False, force_rectangle=False):
|
|
"""The parameters 'rectangle' and 'force_rectangle' are ignored.
|
|
See the documentation of the method 'AbstractImage.get_texture' for
|
|
a more detailed documentation of the method. """
|
|
return self._get_texture()
|
|
|
|
|
|
def decode_dxt1_rgb(data, width, height):
|
|
# Decode to 16-bit RGB UNSIGNED_SHORT_5_6_5
|
|
out = (ctypes.c_uint16 * (width * height))()
|
|
|
|
# Read 8 bytes at a time
|
|
image_offset = 0
|
|
for c0_lo, c0_hi, c1_lo, c1_hi, b0, b1, b2, b3 in split_8byte.findall(data):
|
|
color0 = ord(c0_lo) | ord(c0_hi) << 8
|
|
color1 = ord(c1_lo) | ord(c1_hi) << 8
|
|
bits = ord(b0) | ord(b1) << 8 | ord(b2) << 16 | ord(b3) << 24
|
|
|
|
r0 = color0 & 0x1f
|
|
g0 = (color0 & 0x7e0) >> 5
|
|
b0 = (color0 & 0xf800) >> 11
|
|
r1 = color1 & 0x1f
|
|
g1 = (color1 & 0x7e0) >> 5
|
|
b1 = (color1 & 0xf800) >> 11
|
|
|
|
# i is the dest ptr for this block
|
|
i = image_offset
|
|
for y in range(4):
|
|
for x in range(4):
|
|
code = bits & 0x3
|
|
|
|
if code == 0:
|
|
out[i] = color0
|
|
elif code == 1:
|
|
out[i] = color1
|
|
elif code == 3 and color0 <= color1:
|
|
out[i] = 0
|
|
else:
|
|
if code == 2 and color0 > color1:
|
|
r = (2 * r0 + r1) // 3
|
|
g = (2 * g0 + g1) // 3
|
|
b = (2 * b0 + b1) // 3
|
|
elif code == 3 and color0 > color1:
|
|
r = (r0 + 2 * r1) // 3
|
|
g = (g0 + 2 * g1) // 3
|
|
b = (b0 + 2 * b1) // 3
|
|
else:
|
|
assert code == 2 and color0 <= color1
|
|
r = (r0 + r1) // 2
|
|
g = (g0 + g1) // 2
|
|
b = (b0 + b1) // 2
|
|
out[i] = r | g << 5 | b << 11
|
|
|
|
bits >>= 2
|
|
i += 1
|
|
i += width - 4
|
|
|
|
# Move dest ptr to next 4x4 block
|
|
advance_row = (image_offset + 4) % width == 0
|
|
image_offset += width * 3 * advance_row + 4
|
|
|
|
return PackedImageData(width, height, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, out)
|
|
|
|
|
|
def decode_dxt1_rgba(data, width, height):
|
|
# Decode to GL_RGBA
|
|
out = (ctypes.c_ubyte * (width * height * 4))()
|
|
pitch = width << 2
|
|
|
|
# Read 8 bytes at a time
|
|
image_offset = 0
|
|
for c0_lo, c0_hi, c1_lo, c1_hi, b0, b1, b2, b3 in split_8byte.findall(data):
|
|
color0 = ord(c0_lo) | ord(c0_hi) << 8
|
|
color1 = ord(c1_lo) | ord(c1_hi) << 8
|
|
bits = ord(b0) | ord(b1) << 8 | ord(b2) << 16 | ord(b3) << 24
|
|
|
|
r0 = color0 & 0x1f
|
|
g0 = (color0 & 0x7e0) >> 5
|
|
b0 = (color0 & 0xf800) >> 11
|
|
r1 = color1 & 0x1f
|
|
g1 = (color1 & 0x7e0) >> 5
|
|
b1 = (color1 & 0xf800) >> 11
|
|
|
|
# i is the dest ptr for this block
|
|
i = image_offset
|
|
for y in range(4):
|
|
for x in range(4):
|
|
code = bits & 0x3
|
|
a = 255
|
|
|
|
if code == 0:
|
|
r, g, b = r0, g0, b0
|
|
elif code == 1:
|
|
r, g, b = r1, g1, b1
|
|
elif code == 3 and color0 <= color1:
|
|
r = g = b = a = 0
|
|
else:
|
|
if code == 2 and color0 > color1:
|
|
r = (2 * r0 + r1) // 3
|
|
g = (2 * g0 + g1) // 3
|
|
b = (2 * b0 + b1) // 3
|
|
elif code == 3 and color0 > color1:
|
|
r = (r0 + 2 * r1) // 3
|
|
g = (g0 + 2 * g1) // 3
|
|
b = (b0 + 2 * b1) // 3
|
|
else:
|
|
assert code == 2 and color0 <= color1
|
|
r = (r0 + r1) // 2
|
|
g = (g0 + g1) // 2
|
|
b = (b0 + b1) // 2
|
|
|
|
out[i] = b << 3
|
|
out[i + 1] = g << 2
|
|
out[i + 2] = r << 3
|
|
out[i + 3] = a << 4
|
|
|
|
bits >>= 2
|
|
i += 4
|
|
i += pitch - 16
|
|
|
|
# Move dest ptr to next 4x4 block
|
|
advance_row = (image_offset + 16) % pitch == 0
|
|
image_offset += pitch * 3 * advance_row + 16
|
|
|
|
return PackedImageData(width, height, GL_RGBA, GL_UNSIGNED_BYTE, out)
|
|
|
|
|
|
def decode_dxt3(data, width, height):
|
|
# Decode to GL_RGBA
|
|
out = (ctypes.c_ubyte * (width * height * 4))()
|
|
pitch = width << 2
|
|
|
|
# Read 16 bytes at a time
|
|
image_offset = 0
|
|
for (a0, a1, a2, a3, a4, a5, a6, a7,
|
|
c0_lo, c0_hi, c1_lo, c1_hi,
|
|
b0, b1, b2, b3) in split_16byte.findall(data):
|
|
color0 = ord(c0_lo) | ord(c0_hi) << 8
|
|
color1 = ord(c1_lo) | ord(c1_hi) << 8
|
|
bits = ord(b0) | ord(b1) << 8 | ord(b2) << 16 | ord(b3) << 24
|
|
alpha = ord(a0) | ord(a1) << 8 | ord(a2) << 16 | ord(a3) << 24 | \
|
|
ord(a4) << 32 | ord(a5) << 40 | ord(a6) << 48 | ord(a7) << 56
|
|
|
|
r0 = color0 & 0x1f
|
|
g0 = (color0 & 0x7e0) >> 5
|
|
b0 = (color0 & 0xf800) >> 11
|
|
r1 = color1 & 0x1f
|
|
g1 = (color1 & 0x7e0) >> 5
|
|
b1 = (color1 & 0xf800) >> 11
|
|
|
|
# i is the dest ptr for this block
|
|
i = image_offset
|
|
for y in range(4):
|
|
for x in range(4):
|
|
code = bits & 0x3
|
|
a = alpha & 0xf
|
|
|
|
if code == 0:
|
|
r, g, b = r0, g0, b0
|
|
elif code == 1:
|
|
r, g, b = r1, g1, b1
|
|
elif code == 3 and color0 <= color1:
|
|
r = g = b = 0
|
|
else:
|
|
if code == 2 and color0 > color1:
|
|
r = (2 * r0 + r1) // 3
|
|
g = (2 * g0 + g1) // 3
|
|
b = (2 * b0 + b1) // 3
|
|
elif code == 3 and color0 > color1:
|
|
r = (r0 + 2 * r1) // 3
|
|
g = (g0 + 2 * g1) // 3
|
|
b = (b0 + 2 * b1) // 3
|
|
else:
|
|
assert code == 2 and color0 <= color1
|
|
r = (r0 + r1) // 2
|
|
g = (g0 + g1) // 2
|
|
b = (b0 + b1) // 2
|
|
|
|
out[i] = b << 3
|
|
out[i + 1] = g << 2
|
|
out[i + 2] = r << 3
|
|
out[i + 3] = a << 4
|
|
|
|
bits >>= 2
|
|
alpha >>= 4
|
|
i += 4
|
|
i += pitch - 16
|
|
|
|
# Move dest ptr to next 4x4 block
|
|
advance_row = (image_offset + 16) % pitch == 0
|
|
image_offset += pitch * 3 * advance_row + 16
|
|
|
|
return PackedImageData(width, height, GL_RGBA, GL_UNSIGNED_BYTE, out)
|
|
|
|
|
|
def decode_dxt5(data, width, height):
|
|
# Decode to GL_RGBA
|
|
out = (ctypes.c_ubyte * (width * height * 4))()
|
|
pitch = width << 2
|
|
|
|
# Read 16 bytes at a time
|
|
image_offset = 0
|
|
for (alpha0, alpha1, ab0, ab1, ab2, ab3, ab4, ab5,
|
|
c0_lo, c0_hi, c1_lo, c1_hi,
|
|
b0, b1, b2, b3) in split_16byte.findall(data):
|
|
color0 = ord(c0_lo) | ord(c0_hi) << 8
|
|
color1 = ord(c1_lo) | ord(c1_hi) << 8
|
|
alpha0 = ord(alpha0)
|
|
alpha1 = ord(alpha1)
|
|
bits = ord(b0) | ord(b1) << 8 | ord(b2) << 16 | ord(b3) << 24
|
|
abits = ord(ab0) | ord(ab1) << 8 | ord(ab2) << 16 | ord(ab3) << 24 | \
|
|
ord(ab4) << 32 | ord(ab5) << 40
|
|
|
|
r0 = color0 & 0x1f
|
|
g0 = (color0 & 0x7e0) >> 5
|
|
b0 = (color0 & 0xf800) >> 11
|
|
r1 = color1 & 0x1f
|
|
g1 = (color1 & 0x7e0) >> 5
|
|
b1 = (color1 & 0xf800) >> 11
|
|
|
|
# i is the dest ptr for this block
|
|
i = image_offset
|
|
for y in range(4):
|
|
for x in range(4):
|
|
code = bits & 0x3
|
|
acode = abits & 0x7
|
|
|
|
if code == 0:
|
|
r, g, b = r0, g0, b0
|
|
elif code == 1:
|
|
r, g, b = r1, g1, b1
|
|
elif code == 3 and color0 <= color1:
|
|
r = g = b = 0
|
|
else:
|
|
if code == 2 and color0 > color1:
|
|
r = (2 * r0 + r1) // 3
|
|
g = (2 * g0 + g1) // 3
|
|
b = (2 * b0 + b1) // 3
|
|
elif code == 3 and color0 > color1:
|
|
r = (r0 + 2 * r1) // 3
|
|
g = (g0 + 2 * g1) // 3
|
|
b = (b0 + 2 * b1) // 3
|
|
else:
|
|
assert code == 2 and color0 <= color1
|
|
r = (r0 + r1) / 2
|
|
g = (g0 + g1) / 2
|
|
b = (b0 + b1) / 2
|
|
|
|
if acode == 0:
|
|
a = alpha0
|
|
elif acode == 1:
|
|
a = alpha1
|
|
elif alpha0 > alpha1:
|
|
if acode == 2:
|
|
a = (6 * alpha0 + 1 * alpha1) // 7
|
|
elif acode == 3:
|
|
a = (5 * alpha0 + 2 * alpha1) // 7
|
|
elif acode == 4:
|
|
a = (4 * alpha0 + 3 * alpha1) // 7
|
|
elif acode == 5:
|
|
a = (3 * alpha0 + 4 * alpha1) // 7
|
|
elif acode == 6:
|
|
a = (2 * alpha0 + 5 * alpha1) // 7
|
|
else:
|
|
assert acode == 7
|
|
a = (1 * alpha0 + 6 * alpha1) // 7
|
|
else:
|
|
if acode == 2:
|
|
a = (4 * alpha0 + 1 * alpha1) // 5
|
|
elif acode == 3:
|
|
a = (3 * alpha0 + 2 * alpha1) // 5
|
|
elif acode == 4:
|
|
a = (2 * alpha0 + 3 * alpha1) // 5
|
|
elif acode == 5:
|
|
a = (1 * alpha0 + 4 * alpha1) // 5
|
|
elif acode == 6:
|
|
a = 0
|
|
else:
|
|
assert acode == 7
|
|
a = 255
|
|
|
|
out[i] = b << 3
|
|
out[i + 1] = g << 2
|
|
out[i + 2] = r << 3
|
|
out[i + 3] = a
|
|
|
|
bits >>= 2
|
|
abits >>= 3
|
|
i += 4
|
|
i += pitch - 16
|
|
|
|
# Move dest ptr to next 4x4 block
|
|
advance_row = (image_offset + 16) % pitch == 0
|
|
image_offset += pitch * 3 * advance_row + 16
|
|
|
|
return PackedImageData(width, height, GL_RGBA, GL_UNSIGNED_BYTE, out)
|