Skip to content

Commit

Permalink
adjustments
Browse files Browse the repository at this point in the history
  • Loading branch information
jere8184 committed Dec 30, 2024
1 parent 18f86a1 commit 5a7efa2
Show file tree
Hide file tree
Showing 2 changed files with 211 additions and 173 deletions.
162 changes: 85 additions & 77 deletions openage/convert/value_object/read/media/smp.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -258,6 +258,9 @@ cdef class SMPMainLayer(SMPLayer):
def __init__(self, layer_header, data):
super().__init__(layer_header, data)

for i in range(self.row_count):
self.pcolor.push_back(create_color_row(self, i))

def get_damage_mask(self):
"""
Convert the 4th pixel byte to a mask used for damaged units.
Expand All @@ -268,18 +271,78 @@ cdef class SMPShadowLayer(SMPLayer):
def __init__(self, layer_header, data):
super().__init__(layer_header, data)

for i in range(self.row_count):
self.pcolor.push_back(create_color_row(self, i))

cdef class SMPOutlineLayer(SMPLayer):
def __init__(self, layer_header, data):
super().__init__(layer_header, data)

for i in range(self.row_count):
self.pcolor.push_back(create_color_row(self, i))


ctypedef fused SMPLayerVariant:
SMPLayer
SMPMainLayer
SMPShadowLayer
SMPOutlineLayer


@cython.boundscheck(False)
cdef vector[pixel] create_color_row(SMPLayerVariant variant,
Py_ssize_t rowid):
"""
extract colors (pixels) for the given rowid.
"""
cdef vector[pixel] row_data
cdef Py_ssize_t i

cdef Py_ssize_t first_cmd_offset = variant.cmd_offsets[rowid] # redudent
cdef boundary_def bounds = variant.boundaries[rowid]
cdef size_t pixel_count = variant.info.size[0]

# preallocate memory
row_data.reserve(pixel_count)

# row is completely transparent
if bounds.full_row:
for _ in range(pixel_count):
row_data.push_back(pixel(color_transparent, 0, 0, 0, 0))

return row_data

# start drawing the left transparent space
for i in range(bounds.left):
row_data.push_back(pixel(color_transparent, 0, 0, 0, 0))

# process the drawing commands for this row.
process_drawing_cmds(variant, variant.data_raw,
row_data, rowid,
first_cmd_offset,
pixel_count - bounds.right)

# finish by filling up the right transparent space
for i in range(bounds.right):
row_data.push_back(pixel(color_transparent, 0, 0, 0, 0))

# verify size of generated row
if row_data.size() != pixel_count:
got = row_data.size()
summary = (
f"{got:d}/{pixel_count:d} -> row {rowid:d}, "
f"layer type {variant.info.layer_type:x}, "
f"offset {first_cmd_offset:d} / {first_cmd_offset:#x}"
)
message = (
f"got {'LESS' if got < pixel_count else 'MORE'} pixels than expected: {summary}, "
f"missing: {abs(pixel_count - got):d}"
)

raise Exception(message)

return row_data


@cython.boundscheck(False)
cdef void process_drawing_cmds(SMPLayerVariant variant,
const uint8_t[::1] & data_raw,
Expand Down Expand Up @@ -312,22 +375,21 @@ cdef void process_drawing_cmds(SMPLayerVariant variant,
if row_data.size() > expected_size:
raise Exception(
f"Only {expected_size:d} pixels should be drawn in row {rowid:d} " +
f"with layer type {variant.info.layer_type:#x}, but we have {row_data.size():d}"
f"with layer type {variant.info.layer_type:#x}, but we have {row_data.size():d} already!"
)

# fetch drawing instruction
cmd = data_raw[dpos]

# Last 2 bits store command type
lower_crumb = 0b00000011 & cmd

# opcode: cmd, rowid: rowid

if lower_crumb == 0b00000011:
# eol (end of line) command, this row is finished now.
eor = True

if variant is SMPShadowLayer and row_data.size() < expected_size:
if SMPLayerVariant is SMPShadowLayer and row_data.size() < expected_size:
# copy the last drawn pixel
# (still stored in nextbyte)
#
Expand Down Expand Up @@ -356,17 +418,17 @@ cdef void process_drawing_cmds(SMPLayerVariant variant,
pixel_count = (cmd >> 2) + 1

for _ in range(pixel_count):
if variant is SMPShadowLayer:
if SMPLayerVariant is SMPShadowLayer:
dpos += 1
nextbyte = data_raw[dpos]
row_data.push_back(pixel(color_shadow,
nextbyte, 0, 0, 0))
elif variant is SMPOutlineLayer:
elif SMPLayerVariant is SMPOutlineLayer:
# we don't know the color the game wants
# so we just draw index 0
row_data.push_back(pixel(color_outline,
0, 0, 0, 0))
elif variant is SMPMainLayer:
elif SMPLayerVariant is SMPMainLayer:
for _ in range(4):
dpos += 1
pixel_data.push_back(data_raw[dpos])
Expand All @@ -378,7 +440,7 @@ cdef void process_drawing_cmds(SMPLayerVariant variant,
pixel_data[3] & 0x1F)) # remove "usage" bit here
pixel_data.clear()

elif lower_crumb == 0b00000010 and variant in (SMPMainLayer, SMPShadowLayer):
elif lower_crumb == 0b00000010 and (SMPLayerVariant is SMPMainLayer or SMPLayerVariant is SMPShadowLayer):
# player_color command
# draw the following 'count' pixels
# pixels are stored as 4 byte palette and meta infos
Expand All @@ -387,7 +449,7 @@ cdef void process_drawing_cmds(SMPLayerVariant variant,
pixel_count = (cmd >> 2) + 1

for _ in range(pixel_count):
if variant is SMPShadowLayer:
if SMPLayerVariant is SMPShadowLayer:
dpos += 1
nextbyte = data_raw[dpos]
row_data.push_back(pixel(color_shadow,
Expand Down Expand Up @@ -446,27 +508,32 @@ cdef class SMPLayer:
# pixel matrix representing the final image
cdef vector[vector[pixel]] pcolor

# memory pointer
cdef const uint8_t[::1] data_raw

# rows of image
cdef size_t row_count

def __init__(self, layer_header, data):
self.info = layer_header

if not (isinstance(data, bytes) or isinstance(data, bytearray)):
raise ValueError("Layer data must be some bytes object")

# memory pointer
# convert the bytes obj to char*
cdef const uint8_t[::1] data_raw = data
self.data_raw = data

cdef unsigned short left
cdef unsigned short right

cdef size_t i
cdef int cmd_offset

cdef size_t row_count = self.info.size[1]
self.pcolor.reserve(row_count)
self.row_count = self.info.size[1]
self.pcolor.reserve(self.row_count)

# process bondary table
for i in range(row_count):
for i in range(self.row_count):
outline_entry_position = (self.info.outline_table_offset +
i * SMPLayer.smp_frame_row_edge.size)

Expand All @@ -481,73 +548,14 @@ cdef class SMPLayer:
self.boundaries.push_back(boundary_def(left, right, False))

# process cmd table
for i in range(row_count):
for i in range(self.row_count):
cmd_table_position = (self.info.qdl_table_offset +
i * SMPLayer.smp_command_offset.size)

cmd_offset = SMPLayer.smp_command_offset.unpack_from(
data, cmd_table_position)[0] + self.info.frame_offset
self.cmd_offsets.push_back(cmd_offset)

for i in range(row_count):
self.pcolor.push_back(self.create_color_row(data_raw, i))

cdef vector[pixel] create_color_row(self,
const uint8_t[::1] & data_raw,
Py_ssize_t rowid):
"""
extract colors (pixels) for the given rowid.
"""

cdef vector[pixel] row_data
cdef Py_ssize_t i

first_cmd_offset = self.cmd_offsets[rowid]
cdef boundary_def bounds = self.boundaries[rowid]
cdef size_t pixel_count = self.info.size[0]

# preallocate memory
row_data.reserve(pixel_count)

# row is completely transparent
if bounds.full_row:
for _ in range(pixel_count):
row_data.push_back(pixel(color_transparent, 0, 0, 0, 0))

return row_data

# start drawing the left transparent space
for i in range(bounds.left):
row_data.push_back(pixel(color_transparent, 0, 0, 0, 0))

# process the drawing commands for this row.
process_drawing_cmds(self,
data_raw,
row_data, rowid,
first_cmd_offset,
pixel_count - bounds.right)

# finish by filling up the right transparent space
for i in range(bounds.right):
row_data.push_back(pixel(color_transparent, 0, 0, 0, 0))

# verify size of generated row
if row_data.size() != pixel_count:
got = row_data.size()
summary = (
f"{got:d}/{pixel_count:d} -> row {rowid:d}, "
f"layer type {self.info.layer_type:x}, "
f"offset {first_cmd_offset:d} / {first_cmd_offset:#x}"
)
message = (
f"got {'LESS' if got < pixel_count else 'MORE'} pixels than expected: {summary}, "
f"missing: {abs(pixel_count - got):d}"
)

raise Exception(message)

return row_data

def get_picture_data(self, palette):
"""
Convert the palette index matrix to a colored image.
Expand Down Expand Up @@ -576,15 +584,15 @@ cdef class SMPLayer:
@cython.boundscheck(False)
@cython.wraparound(False)
cdef numpy.ndarray determine_rgba_matrix(vector[vector[pixel]] & image_matrix,
numpy.ndarray[numpy.uint8_t, ndim = 2, mode = "c"] palette):
numpy.ndarray[numpy.uint8_t, ndim= 2, mode = "c"] palette):
"""
converts a palette index image matrix to an rgba matrix.
"""

cdef size_t height = image_matrix.size()
cdef size_t width = image_matrix[0].size()

cdef numpy.ndarray[numpy.uint8_t, ndim = 3, mode = "c"] array_data = \
cdef numpy.ndarray[numpy.uint8_t, ndim= 3, mode = "c"] array_data = \
numpy.zeros((height, width, 4), dtype=numpy.uint8)

cdef uint8_t[:, ::1] m_lookup = palette
Expand Down Expand Up @@ -692,7 +700,7 @@ cdef numpy.ndarray determine_damage_matrix(vector[vector[pixel]] & image_matrix)
cdef size_t height = image_matrix.size()
cdef size_t width = image_matrix[0].size()

cdef numpy.ndarray[numpy.uint8_t, ndim = 3, mode = "c"] array_data = \
cdef numpy.ndarray[numpy.uint8_t, ndim= 3, mode = "c"] array_data = \
numpy.zeros((height, width, 4), dtype=numpy.uint8)

cdef uint8_t r
Expand Down
Loading

0 comments on commit 5a7efa2

Please sign in to comment.