changeset 420:3a7b36324611

Replace Pyglet’s image loader with our SDL2_image-based one.
author Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
date Tue, 16 Jul 2013 21:07:15 +0200
parents 1c92721f8e49
children b1248bab2d0f
files README pytouhou/lib/sdl.pxd pytouhou/lib/sdl.pyx pytouhou/lib/sdl.pyxbld pytouhou/ui/gamerunner.py pytouhou/ui/sprite.pyx pytouhou/ui/texture.pyx setup.py
diffstat 8 files changed, 116 insertions(+), 35 deletions(-) [+]
line wrap: on
line diff
--- a/README
+++ b/README
@@ -16,6 +16,7 @@ Running:
     * Cython
     * Pyglet
     * SDL2
+    * SDL2_image
 
 
 Building sample data:
--- a/pytouhou/lib/sdl.pxd
+++ b/pytouhou/lib/sdl.pxd
@@ -91,3 +91,35 @@ cdef extern from "SDL_keyboard.h":
 cdef extern from "SDL_timer.h":
     Uint32 SDL_GetTicks()
     void SDL_Delay(Uint32 ms)
+
+
+cdef extern from "SDL_rect.h":
+    ctypedef struct SDL_Rect:
+        int x, y
+        int w, h
+
+
+cdef extern from "SDL_surface.h":
+    ctypedef struct SDL_Surface:
+        int w, h
+        unsigned char *pixels
+
+    void SDL_FreeSurface(SDL_Surface *surface)
+    int SDL_BlitSurface(SDL_Surface *src, const SDL_Rect *srcrect, SDL_Surface *dst, SDL_Rect *dstrect)
+    SDL_Surface *SDL_CreateRGBSurface(Uint32 flags, int width, int height, int depth, Uint32 Rmask, Uint32 Gmask, Uint32 Bmask, Uint32 Amask)
+
+
+cdef extern from "SDL_rwops.h":
+    ctypedef struct SDL_RWops:
+        pass
+
+    SDL_RWops *SDL_RWFromConstMem(const void *mem, int size)
+    int SDL_RWclose(SDL_RWops *context)
+
+
+cdef extern from "SDL_image.h":
+    int IMG_INIT_PNG
+
+    int IMG_Init(int flags)
+    void IMG_Quit()
+    SDL_Surface *IMG_LoadPNG_RW(SDL_RWops *src)
--- a/pytouhou/lib/sdl.pyx
+++ b/pytouhou/lib/sdl.pyx
@@ -13,6 +13,7 @@
 ##
 
 INIT_VIDEO = SDL_INIT_VIDEO
+INIT_PNG = IMG_INIT_PNG
 
 GL_CONTEXT_MAJOR_VERSION = SDL_GL_CONTEXT_MAJOR_VERSION
 GL_CONTEXT_MINOR_VERSION = SDL_GL_CONTEXT_MINOR_VERSION
@@ -63,15 +64,57 @@ cdef class Window:
         SDL_GL_DeleteContext(self.context)
 
 
+cdef class Surface:
+    cdef SDL_Surface *surface
+
+    def __dealloc__(self):
+        if self.surface != NULL:
+            SDL_FreeSurface(self.surface)
+
+    property width:
+        def __get__(self):
+            return self.surface.w
+
+    property height:
+        def __get__(self):
+            return self.surface.h
+
+    property pixels:
+        def __get__(self):
+            return bytes(self.surface.pixels[:self.surface.w * self.surface.h * 4])
+
+    def blit(self, Surface other):
+        if SDL_BlitSurface(other.surface, NULL, self.surface, NULL) < 0:
+            raise SDLError(SDL_GetError())
+
+    def set_alpha(self, Surface alpha_surface):
+        nb_pixels = self.surface.w * self.surface.h
+        image = self.surface.pixels
+        alpha = alpha_surface.surface.pixels
+
+        for i in xrange(nb_pixels):
+            # Only use the red value, assume the others are equal.
+            image[3+4*i] = alpha[3*i]
+
+
 def init(Uint32 flags):
     if SDL_Init(flags) < 0:
         raise SDLError(SDL_GetError())
 
 
+def img_init(Uint32 flags):
+    if IMG_Init(flags) != flags:
+        raise SDLError(SDL_GetError())
+
+
 def quit():
     SDL_Quit()
 
 
+def img_quit():
+    IMG_Quit()
+
+
 def gl_set_attribute(SDL_GLattr attr, int value):
     if SDL_GL_SetAttribute(attr, value) < 0:
         raise SDLError(SDL_GetError())
@@ -96,6 +139,25 @@ def get_keyboard_state():
     return tuple([k is not False for k in state[:numkeys]])
 
 
+def load_png(file_):
+    data = file_.read()
+    rwops = SDL_RWFromConstMem(<char*>data, len(data))
+    surface = Surface()
+    surface.surface = IMG_LoadPNG_RW(rwops)
+    SDL_RWclose(rwops)
+    if surface.surface == NULL:
+        raise SDLError(SDL_GetError())
+    return surface
+
+
+def create_rgb_surface(int width, int height, int depth, Uint32 rmask=0, Uint32 gmask=0, Uint32 bmask=0, Uint32 amask=0):
+    surface = Surface()
+    surface.surface = SDL_CreateRGBSurface(0, width, height, depth, rmask, gmask, bmask, amask)
+    if surface.surface == NULL:
+        raise SDLError(SDL_GetError())
+    return surface
+
+
 def get_ticks():
     return SDL_GetTicks()
 
--- a/pytouhou/lib/sdl.pyxbld
+++ b/pytouhou/lib/sdl.pyxbld
@@ -18,7 +18,7 @@ from distutils.extension import Extensio
 from subprocess import check_output
 
 COMMAND = 'pkg-config'
-LIBRARIES = ['sdl2']
+LIBRARIES = ['sdl2', 'SDL2_image']
 
 def make_ext(modname, pyxfilename):
     """ Compile and link with the corrects options. """
--- a/pytouhou/ui/gamerunner.py
+++ b/pytouhou/ui/gamerunner.py
@@ -88,6 +88,7 @@ class GameRunner(GameRenderer):
         GameRenderer.__init__(self, resource_loader, game, background)
 
         sdl.init(sdl.INIT_VIDEO)
+        sdl.img_init(sdl.INIT_PNG)
         sdl.gl_set_attribute(sdl.GL_CONTEXT_MAJOR_VERSION, 2)
         sdl.gl_set_attribute(sdl.GL_CONTEXT_MINOR_VERSION, 1)
         sdl.gl_set_attribute(sdl.GL_DOUBLEBUFFER, int(double_buffer))
@@ -188,6 +189,7 @@ class GameRunner(GameRenderer):
 
         self.win.gl_delete_context()
         self.win.destroy_window()
+        sdl.img_quit()
         sdl.quit()
 
 
--- a/pytouhou/ui/sprite.pyx
+++ b/pytouhou/ui/sprite.pyx
@@ -61,8 +61,8 @@ cpdef object get_sprite_rendering_data(o
     tox, toy = sprite.texoffsets
     uvs = (tx * x_1 + tox,
            (tx + tw) * x_1 + tox,
-           1. - (ty * y_1 + toy),
-           1. - ((ty + th) * y_1 + toy))
+           ty * y_1 + toy,
+           (ty + th) * y_1 + toy)
 
     (x1, x2 , x3, x4), (y1, y2, y3, y4), (z1, z2, z3, z4), _ = vertmat.data
 
--- a/pytouhou/ui/texture.pyx
+++ b/pytouhou/ui/texture.pyx
@@ -12,9 +12,6 @@
 ## GNU General Public License for more details.
 ##
 
-from libc.stdlib cimport malloc, free
-
-import pyglet
 from pyglet.gl import (glTexParameteri, GL_TEXTURE_MIN_FILTER,
                        GL_TEXTURE_MAG_FILTER, GL_LINEAR, GL_BGRA, GL_RGBA,
                        GL_RGB, GL_LUMINANCE, GL_UNSIGNED_BYTE,
@@ -22,6 +19,7 @@ from pyglet.gl import (glTexParameteri, 
                        glGenTextures, glBindTexture, glTexImage2D,
                        GL_TEXTURE_2D)
 from ctypes import c_uint, byref
+from pytouhou.lib.sdl import load_png, create_rgb_surface
 import os
 
 from pytouhou.formats.thtx import Texture #TODO: perhaps define that elsewhere?
@@ -45,35 +43,25 @@ cdef class TextureManager:
 
 
     def load_png_texture(self, first_name, secondary_name):
-        cdef char *image, *alpha, *new_data
-        cdef unsigned int i, width, height, pixels
+        image_file = load_png(self.loader.get_file(os.path.basename(first_name)))
+        width, height = image_file.width, image_file.height
 
-        image_file = pyglet.image.load(first_name, file=self.loader.get_file(os.path.basename(first_name)))
-        width, height = image_file.width, image_file.height
-        image_data = image_file.get_data('RGB', width * 3)
+        # Support only 32 bits RGBA. Paletted surfaces are awful to work with.
+        #TODO: verify it doesn’t blow up on big-endian systems.
+        new_image = create_rgb_surface(width, height, 32, 0x000000ff, 0x0000ff00, 0x00ff0000, 0xff000000)
+        new_image.blit(image_file)
 
         if secondary_name:
-            alpha_file = pyglet.image.load(secondary_name, file=self.loader.get_file(os.path.basename(secondary_name)))
-            assert (image_file.width, image_file.height) == (alpha_file.width, image_file.height)
-
-            pixels = width * height
-
-            alpha_data = alpha_file.get_data('RGB', width * 3)
-            image = <char *>image_data
-            alpha = <char *>alpha_data
+            alpha_file = load_png(self.loader.get_file(os.path.basename(secondary_name)))
+            assert (width == alpha_file.width and
+                    height == alpha_file.height)
 
-            # TODO: further optimizations
-            new_data = <char *>malloc(pixels * 4)
-            for i in range(pixels):
-                new_data[i*4] = image[i*3]
-                new_data[i*4+1] = image[i*3+1]
-                new_data[i*4+2] = image[i*3+2]
-                new_data[i*4+3] = alpha[i*3]
-            data = new_data[:(pixels * 4)]
-            free(new_data)
-            return Texture(width, height, -4, data)
+            new_alpha_file = create_rgb_surface(width, height, 24)
+            new_alpha_file.blit(alpha_file)
 
-        return Texture(width, height, -3, image_data)
+            new_image.set_alpha(new_alpha_file)
+
+        return Texture(width, height, -4, new_image.pixels)
 
 
     def load_texture(self, key):
@@ -97,10 +85,6 @@ cdef class TextureManager:
             format_ = GL_LUMINANCE
             type_ = GL_UNSIGNED_BYTE
             composants = GL_LUMINANCE
-        elif key.fmt == -3: #XXX: non-standard, remove it!
-            format_ = GL_RGB
-            type_ = GL_UNSIGNED_BYTE
-            composants = GL_RGB
         elif key.fmt == -4: #XXX: non-standard
             format_ = GL_RGBA
             type_ = GL_UNSIGNED_BYTE
--- a/setup.py
+++ b/setup.py
@@ -18,7 +18,7 @@ except ImportError:
 
 
 COMMAND = 'pkg-config'
-LIBRARIES = ['sdl2']
+LIBRARIES = ['sdl2', 'SDL2_image']
 
 packages = []
 extension_names = []