Skip to content

Commit

Permalink
convert: Use libpng for image output.
Browse files Browse the repository at this point in the history
  • Loading branch information
heinezen committed Mar 8, 2020
1 parent 99cc0bc commit 0ad3191
Show file tree
Hide file tree
Showing 12 changed files with 217 additions and 10 deletions.
29 changes: 29 additions & 0 deletions buildsystem/modules/FindLibpng.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# Copyright 2020-2020 the openage authors. See copying.md for legal info.

# - Find libpng library
# Find the native libpng headers and library.
# This module defines
# LIBPNG_INCLUDE_DIRS - where to find ogg/ogg.h etc.
# LIBPNG_LIBRARIES - List of libraries when using libogg
# LIBPNG_FOUND - True if ogg is found.

find_path(LIBPNG_INCLUDE_DIR
NAMES libpng/png.h
DOC "libpng include directory"
)

find_library(LIBPNG_LIBRARY
NAMES png
DOC "Path to libpng library"
)

# handle the QUIETLY and REQUIRED arguments and set LIBPNG_FOUND to TRUE if
# all listed variables are TRUE
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(Libpng DEFAULT_MSG LIBPNG_INCLUDE_DIR LIBPNG_LIBRARY)

mark_as_advanced(LIBPNG_INCLUDE_DIR LIBPNG_LIBRARY)

# export the variables
set(LIBPNG_INCLUDE_DIRS "${LIBPNG_INCLUDE_DIR}")
set(LIBPNG_LIBRARIES "${LIBPNG_LIBRARY}")
1 change: 1 addition & 0 deletions openage/convert/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -35,4 +35,5 @@ add_subdirectory(hardcoded)
add_subdirectory(interface)
add_subdirectory(nyan)
add_subdirectory(opus)
add_subdirectory(png)
add_subdirectory(processor)
2 changes: 0 additions & 2 deletions openage/convert/opus/ogg.pxd
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,6 @@ cdef extern from "ogg/ogg.h":
ogg_int64_t granulepos
ogg_int64_t packetno



int ogg_stream_packetin(ogg_stream_state *os, ogg_packet *op)
int ogg_stream_pageout(ogg_stream_state *os, ogg_page *og)
int ogg_stream_flush(ogg_stream_state *os, ogg_page *og)
Expand Down
27 changes: 27 additions & 0 deletions openage/convert/png/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
find_package(Libpng REQUIRED)

# Currently there is no way to link cython modules to extra libraries.
# Since PYEXT_LINK_LIBRARY normally only includes libopenage (what
# opusenc doesn't need), we hijack this variable. This is ok, because
# there are no subdirectories, that will see the changed variable.
set(PYEXT_LINK_LIBRARY
${LIBPNG_LIBRARIES}
)

set(PYEXT_INCLUDE_DIRS
${PYEXT_INCLUDE_DIRS}
${LIBPNG_INCLUDE_DIRS}
)

add_cython_modules(
png_create.pyx
)

add_pxds(
__init__.pxd
libpng.pxd
)

add_py_modules(
__init__.py
)
Empty file.
5 changes: 5 additions & 0 deletions openage/convert/png/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# Copyright 2020-2020 the openage authors. See copying.md for legal info.

"""
Cython module to create png files using libpng.
"""
69 changes: 69 additions & 0 deletions openage/convert/png/libpng.pxd
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
# Copyright 2020-2020 the openage authors. See copying.md for legal info.

from libc.stdio cimport FILE

cdef extern from "libpng/png.h":
const char PNG_LIBPNG_VER_STRING[]
const int PNG_COLOR_TYPE_RGBA = 6
const int PNG_INTERLACE_NONE = 0
const int PNG_COMPRESSION_TYPE_DEFAULT = 0
const int PNG_FILTER_TYPE_DEFAULT = 0
const int PNG_TRANSFORM_IDENTITY = 0

ctypedef unsigned char png_byte
ctypedef const png_byte *png_const_bytep
ctypedef png_byte **png_bytepp
ctypedef unsigned long int png_uint_32

ctypedef struct png_struct
ctypedef png_struct *png_structp
ctypedef png_struct *png_structrp
ctypedef png_struct **png_structpp
ctypedef const png_struct *png_const_structrp

ctypedef struct png_info
ctypedef png_info *png_infop
ctypedef png_info *png_inforp
ctypedef png_info **png_infopp
ctypedef const png_info *png_const_inforp

ctypedef const char *png_const_charp
ctypedef void *png_voidp
ctypedef (png_structp, png_const_charp) *png_error_ptr
ctypedef FILE *png_FILE_p

png_structp png_create_write_struct(png_const_charp user_png_ver,
png_voidp error_ptr,
png_error_ptr error_fn,
png_error_ptr warn_fn)

png_infop png_create_info_struct(png_const_structrp png_ptr)
void png_set_IHDR(png_const_structrp png_ptr,
png_inforp info_ptr,
png_uint_32 width,
png_uint_32 height,
int bit_depth,
int color_type,
int interlace_method,
int compression_method,
int filter_method)
void png_init_io(png_structrp png_ptr,
png_FILE_p fp)
void png_set_rows(png_const_structrp png_ptr,
png_inforp info_ptr,
png_bytepp row_pointers)
void png_write_png(png_structrp png_ptr,
png_inforp info_ptr,
int transforms,
png_voidp params)
void png_destroy_write_struct(png_structpp png_ptr_ptr,
png_infopp info_ptr_ptr)

# Should not be necessary if png_write_png() works
void png_write_info(png_structrp png_ptr,
png_const_inforp info_ptr)
void png_write_row(png_structrp png_ptr,
png_const_bytep row)
void png_write_end(png_structrp png_ptr,
png_inforp info_ptr)

67 changes: 67 additions & 0 deletions openage/convert/png/png_create.pyx
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
# Copyright 2020-2020 the openage authors. See copying.md for legal info.
#
# cython: profile=False

from libc.stdio cimport fopen, fclose
from libc.stdint cimport uint8_t
from libc.stdlib cimport realloc
from cpython.mem cimport PyMem_Malloc

from . cimport libpng

cimport cython
import numpy
cimport numpy


@cython.boundscheck(False)
@cython.wraparound(False)
def save(filename, numpy.ndarray[numpy.uint8_t, ndim=3, mode="c"] imagedata not None):
"""
Save an image as a PNG file.
"""
cdef unsigned int width = imagedata.shape[1]
cdef unsigned int height = imagedata.shape[0]
cdef numpy.uint8_t[:,:,::1] mview = imagedata
png_create(filename.encode('UTF-8'), mview, width, height)


@cython.boundscheck(False)
@cython.wraparound(False)
cdef void png_create(char* filename, numpy.uint8_t[:,:,::1] imagedata,
int width, int height):
"""
Create a PNG file with libpng and write it to file.
"""
cdef libpng.png_FILE_p fp = fopen(filename, "wb")

cdef libpng.png_structp png
cdef libpng.png_infop info

png = libpng.png_create_write_struct(libpng.PNG_LIBPNG_VER_STRING, NULL, NULL, NULL)
info = libpng.png_create_info_struct(png)

libpng.png_init_io(png, fp)
libpng.png_set_IHDR(png,
info,
width,
height,
8,
libpng.PNG_COLOR_TYPE_RGBA,
libpng.PNG_INTERLACE_NONE,
libpng.PNG_COMPRESSION_TYPE_DEFAULT,
libpng.PNG_FILTER_TYPE_DEFAULT)
libpng.png_write_info(png, info)

for row_idx in range(height):
libpng.png_write_row(png, &imagedata[row_idx,0,0])

libpng.png_write_end(png, info)

# TODO: This doesn't work, but would be faster
# libpng.png_set_rows(png, info, imagedata)
# libpng.png_write_png(png, info, libpng.PNG_TRANSFORM_IDENTITY, NULL)

fclose(fp)

libpng.png_destroy_write_struct(&png, &info)
2 changes: 1 addition & 1 deletion openage/convert/slp.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -968,7 +968,7 @@ cdef numpy.ndarray determine_rgba_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] array_data = \
cdef numpy.ndarray[numpy.uint8_t, ndim=3, mode="c"] array_data = \
numpy.zeros((height, width, 4), dtype=numpy.uint8)

# micro optimization to avoid call to ColorTable.__getitem__()
Expand Down
4 changes: 2 additions & 2 deletions openage/convert/smp.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -689,7 +689,7 @@ cdef numpy.ndarray determine_rgba_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] array_data = \
cdef numpy.ndarray[numpy.uint8_t, ndim=3, mode="c"] array_data = \
numpy.zeros((height, width, 4), dtype=numpy.uint8)

# micro optimization to avoid call to ColorTable.__getitem__()
Expand Down Expand Up @@ -790,7 +790,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] 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
4 changes: 2 additions & 2 deletions openage/convert/smx.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -1009,7 +1009,7 @@ cdef numpy.ndarray determine_rgba_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] array_data = \
cdef numpy.ndarray[numpy.uint8_t, ndim=3, mode="c"] array_data = \
numpy.zeros((height, width, 4), dtype=numpy.uint8)

# micro optimization to avoid call to ColorTable.__getitem__()
Expand Down Expand Up @@ -1104,7 +1104,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] 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
17 changes: 14 additions & 3 deletions openage/convert/texture.py
Original file line number Diff line number Diff line change
Expand Up @@ -210,9 +210,20 @@ def save(self, targetdir, filename, meta_formats=None):
# without the dot
ext = ext[1:]

# generate PNG file
with targetdir[filename].open("wb") as imagefile:
self.image_data.get_pil_image().save(imagefile, ext)
# ASDF: Remove the shoddy PNG file location mess
# TODO: The png conversion should return a bytearray and
# let Python handle writing it. To do that libpng
# must write to a buffer that is returned.
# ------------------------------------------------------------------------------
assetdir = targetdir.fsobj.obj.fsobj.obj.fsobj.path.decode("utf-8") + "/"
convertdir = assetdir + targetdir.fsobj.obj.fsobj.obj.parts[0].decode("utf-8") + "/"
for part in targetdir.parts:
convertdir = convertdir + part.decode("utf-8") + "/"
targetstr = convertdir + filename

from .png import png_create
png_create.save(targetstr, self.image_data.data)
# ------------------------------------------------------------------------------

if meta_formats:
# generate formatted texture metadata
Expand Down

0 comments on commit 0ad3191

Please sign in to comment.