# ----------------------------------------------------------------------------
# pyglet
# Copyright (c) 2006-2008 Alex Holkner
# All rights reserved.
# 
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions 
# are met:
#
#  * Redistributions of source code must retain the above copyright
#    notice, this list of conditions and the following disclaimer.
#  * Redistributions in binary form must reproduce the above copyright 
#    notice, this list of conditions and the following disclaimer in
#    the documentation and/or other materials provided with the
#    distribution.
#  * Neither the name of pyglet nor the names of its
#    contributors may be used to endorse or promote products
#    derived from this software without specific prior written
#    permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
# ----------------------------------------------------------------------------

'''
'''

__docformat__ = 'restructuredtext'
__version__ = '$Id$'

from ctypes import *

from pyglet.gl import *
from pyglet.image import *
from pyglet.image.codecs import *
from pyglet.image.codecs import gif

import pyglet.lib
import pyglet.window

gdk = pyglet.lib.load_library('gdk-x11-2.0')
gdkpixbuf = pyglet.lib.load_library('gdk_pixbuf-2.0')

GdkPixbufLoader = c_void_p
GdkPixbuf = c_void_p
guchar = c_char
gdkpixbuf.gdk_pixbuf_loader_new.restype = POINTER(GdkPixbufLoader)
gdkpixbuf.gdk_pixbuf_loader_get_pixbuf.restype = POINTER(GdkPixbuf)
gdkpixbuf.gdk_pixbuf_get_pixels.restype = POINTER(guchar)
gdkpixbuf.gdk_pixbuf_loader_get_animation.restype = POINTER(c_void_p)
gdkpixbuf.gdk_pixbuf_animation_get_iter.restype = POINTER(c_void_p)
gdkpixbuf.gdk_pixbuf_animation_iter_get_pixbuf.restype = POINTER(GdkPixbuf)

class GTimeVal(Structure):
    _fields_ = [
        ('tv_sec', c_long),
        ('tv_usec', c_long)
    ]

class GdkPixbuf2ImageDecoder(ImageDecoder):
    def get_file_extensions(self):
        return ['.png', '.xpm', '.jpg', '.jpeg', '.tif', '.tiff', '.pnm',
                '.ras', '.bmp', '.gif']

    def get_animation_file_extensions(self):
        return ['.gif', '.ani']

    def _load(self, file, filename, load_func):
        data = file.read()
        loader = gdkpixbuf.gdk_pixbuf_loader_new()
        gdkpixbuf.gdk_pixbuf_loader_write(loader, data, len(data), None)
        if not gdkpixbuf.gdk_pixbuf_loader_close(loader, None):
            raise ImageDecodeException(filename)
        result = load_func(loader)
        if not result:
            raise ImageDecodeException('Unable to load: %s' % filename)
        return result

    def _pixbuf_to_image(self, pixbuf):
        # Get format and dimensions
        width = gdkpixbuf.gdk_pixbuf_get_width(pixbuf)
        height = gdkpixbuf.gdk_pixbuf_get_height(pixbuf)
        channels = gdkpixbuf.gdk_pixbuf_get_n_channels(pixbuf)
        rowstride = gdkpixbuf.gdk_pixbuf_get_rowstride(pixbuf)
        #has_alpha = gdkpixbuf.gdk_pixbuf_get_has_alpha(pixbuf)
        pixels = gdkpixbuf.gdk_pixbuf_get_pixels(pixbuf)

        # Copy pixel data.
        buffer = (c_ubyte * (rowstride * height))()
        memmove(buffer, pixels, rowstride * (height - 1) + width * channels)

        # Release pixbuf
        gdk.g_object_unref(pixbuf)

        # Determine appropriate GL type
        if channels == 3:
            format = 'RGB'
        else:
            format = 'RGBA'

        return ImageData(width, height, format, buffer, -rowstride)

    def decode(self, file, filename):
        pixbuf = self._load(file, filename, 
                            gdkpixbuf.gdk_pixbuf_loader_get_pixbuf)
       
        return self._pixbuf_to_image(pixbuf)

    def decode_animation(self, file, filename):
        # Extract GIF control data.  If it's not a GIF, this method will
        # raise.
        gif_stream = gif.read(file)
        delays = [image.delay for image in gif_stream.images]

        # Get GDK animation iterator
        file.seek(0)
        anim = self._load(file, filename, 
                          gdkpixbuf.gdk_pixbuf_loader_get_animation)
        time = GTimeVal(0, 0)
        iter = gdkpixbuf.gdk_pixbuf_animation_get_iter(anim, byref(time))

        frames = []

        # Extract each image   
        for control_delay in delays:
            pixbuf = gdkpixbuf.gdk_pixbuf_animation_iter_get_pixbuf(iter)
            # When attempting to load animated gifs with an alpha channel on
            #  linux gdkpixbuf will normally return a null pixbuf for the final
            #  frame resulting in a segfault:
            #  http://code.google.com/p/pyglet/issues/detail?id=411
            # Since it is unclear why exactly this happens, the workaround
            #  below is to start again and extract that frame on its own.
            if pixbuf == None:
                file.seek(0)
                anim = self._load(file, filename,
                                gdkpixbuf.gdk_pixbuf_loader_get_animation)
                temptime = GTimeVal(0, 0)
                iter = gdkpixbuf.gdk_pixbuf_animation_get_iter(anim, byref(temptime))
                gdkpixbuf.gdk_pixbuf_animation_iter_advance(iter, byref(time))
                pixbuf = gdkpixbuf.gdk_pixbuf_animation_iter_get_pixbuf(iter)
            image = self._pixbuf_to_image(pixbuf)
            frames.append(AnimationFrame(image, control_delay))

            gdk_delay = gdkpixbuf.gdk_pixbuf_animation_iter_get_delay_time(iter)

            if gdk_delay == -1:
                break

            gdk_delay = gdkpixbuf.gdk_pixbuf_animation_iter_get_delay_time(iter)
            gdk_delay *= 1000 # milliseconds to microseconds
            # Compare gdk_delay to control_delay for interest only.
            #print control_delay, gdk_delay / 1000000.

            us = time.tv_usec + gdk_delay
            time.tv_sec += us // 1000000
            time.tv_usec = us % 1000000
            gdkpixbuf.gdk_pixbuf_animation_iter_advance(iter, byref(time))

        return Animation(frames)

def get_decoders():
    return [GdkPixbuf2ImageDecoder()]

def get_encoders():
    return []

def init():
    gdk.g_type_init()

init()

