287 lines
9.6 KiB
Python
287 lines
9.6 KiB
Python
# ----------------------------------------------------------------------------
|
|
# 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: pil.py 163 2006-11-13 04:15:46Z Alex.Holkner $'
|
|
|
|
import sys
|
|
|
|
from ctypes import *
|
|
|
|
from pyglet.gl import *
|
|
from pyglet.image import *
|
|
from pyglet.image.codecs import *
|
|
|
|
from pyglet.window.carbon import carbon, quicktime, _oscheck
|
|
from pyglet.window.carbon.constants import _name
|
|
from pyglet.window.carbon.types import *
|
|
|
|
Handle = POINTER(POINTER(c_byte))
|
|
|
|
GWorldPtr = c_void_p
|
|
carbon.NewHandle.restype = Handle
|
|
HandleDataHandlerSubType = _name('hndl')
|
|
PointerDataHandlerSubType = _name('ptr ')
|
|
kDataHCanRead = 1
|
|
kDataRefExtensionFileName = _name('fnam')
|
|
kDataRefExtensionMIMEType = _name('mime')
|
|
ComponentInstance = c_void_p
|
|
|
|
k1MonochromePixelFormat = 0x00000001
|
|
k2IndexedPixelFormat = 0x00000002
|
|
k4IndexedPixelFormat = 0x00000004
|
|
k8IndexedPixelFormat = 0x00000008
|
|
k16BE555PixelFormat = 0x00000010
|
|
k24RGBPixelFormat = 0x00000018
|
|
k32ARGBPixelFormat = 0x00000020
|
|
k32BGRAPixelFormat = _name('BGRA')
|
|
k1IndexedGrayPixelFormat = 0x00000021
|
|
k2IndexedGrayPixelFormat = 0x00000022
|
|
k4IndexedGrayPixelFormat = 0x00000024
|
|
k8IndexedGrayPixelFormat = 0x00000028
|
|
kNativeEndianPixMap = 1 << 8
|
|
|
|
newMovieActive = 1
|
|
noErr = 0
|
|
movieTrackMediaType = 1 << 0
|
|
movieTrackCharacteristic = 1 << 1
|
|
movieTrackEnabledOnly = 1 << 2
|
|
VisualMediaCharacteristic = _name('eyes')
|
|
nextTimeMediaSample = 1
|
|
|
|
class PointerDataRefRecord(Structure):
|
|
_fields_ = [
|
|
('data', c_void_p),
|
|
('dataLength', c_long)
|
|
]
|
|
|
|
def Str255(value):
|
|
return create_string_buffer(chr(len(value)) + value)
|
|
|
|
class QuickTimeImageDecoder(ImageDecoder):
|
|
def get_file_extensions(self):
|
|
# Only most common ones shown here
|
|
return ['.bmp', '.cur', '.gif', '.ico', '.jpg', '.jpeg', '.pcx', '.png',
|
|
'.tga', '.tif', '.tiff', '.xbm', '.xpm']
|
|
|
|
def get_animation_file_extensions(self):
|
|
return ['.gif']
|
|
|
|
def _get_data_ref(self, file, filename):
|
|
self._data_hold = data = create_string_buffer(file.read())
|
|
|
|
dataref = carbon.NewHandle(sizeof(PointerDataRefRecord))
|
|
datarec = cast(dataref,
|
|
POINTER(POINTER(PointerDataRefRecord))).contents.contents
|
|
datarec.data = addressof(data)
|
|
datarec.dataLength = len(data)
|
|
|
|
self._data_handler_holder = data_handler = ComponentInstance()
|
|
r = quicktime.OpenADataHandler(dataref, PointerDataHandlerSubType,
|
|
None, 0, None, kDataHCanRead, byref(data_handler))
|
|
_oscheck(r)
|
|
|
|
extension_handle = Handle()
|
|
|
|
self._filename_hold = filename = Str255(filename)
|
|
r = carbon.PtrToHand(filename, byref(extension_handle), len(filename))
|
|
r = quicktime.DataHSetDataRefExtension(data_handler, extension_handle,
|
|
kDataRefExtensionFileName)
|
|
_oscheck(r)
|
|
quicktime.DisposeHandle(extension_handle)
|
|
|
|
quicktime.DisposeHandle(dataref)
|
|
|
|
dataref = c_void_p()
|
|
r = quicktime.DataHGetDataRef(data_handler, byref(dataref))
|
|
_oscheck(r)
|
|
|
|
quicktime.CloseComponent(data_handler)
|
|
|
|
return dataref
|
|
|
|
def _get_formats(self):
|
|
# TODO choose 24 bit where appropriate.
|
|
if sys.byteorder == 'big':
|
|
format = 'ARGB'
|
|
qtformat = k32ARGBPixelFormat
|
|
else:
|
|
format = 'BGRA'
|
|
qtformat = k32BGRAPixelFormat
|
|
return format, qtformat
|
|
|
|
def decode(self, file, filename):
|
|
dataref = self._get_data_ref(file, filename)
|
|
importer = ComponentInstance()
|
|
quicktime.GetGraphicsImporterForDataRef(dataref,
|
|
PointerDataHandlerSubType, byref(importer))
|
|
|
|
if not importer:
|
|
raise ImageDecodeException(filename or file)
|
|
|
|
rect = Rect()
|
|
quicktime.GraphicsImportGetNaturalBounds(importer, byref(rect))
|
|
width = rect.right
|
|
height = rect.bottom
|
|
|
|
format, qtformat = self._get_formats()
|
|
|
|
buffer = (c_byte * (width * height * len(format)))()
|
|
world = GWorldPtr()
|
|
quicktime.QTNewGWorldFromPtr(byref(world), qtformat,
|
|
byref(rect), c_void_p(), c_void_p(), 0, buffer,
|
|
len(format) * width)
|
|
|
|
quicktime.GraphicsImportSetGWorld(importer, world, c_void_p())
|
|
result = quicktime.GraphicsImportDraw(importer)
|
|
quicktime.DisposeGWorld(world)
|
|
|
|
if result != 0:
|
|
raise ImageDecodeException(filename or file)
|
|
|
|
pitch = len(format) * width
|
|
|
|
return ImageData(width, height, format, buffer, -pitch)
|
|
|
|
def decode_animation(self, file, filename):
|
|
# TODO: Stop playing chicken with the GC
|
|
# TODO: Cleanup in errors
|
|
|
|
quicktime.EnterMovies()
|
|
|
|
data_ref = self._get_data_ref(file, filename)
|
|
if not data_ref:
|
|
raise ImageDecodeException(filename or file)
|
|
|
|
movie = c_void_p()
|
|
id = c_short()
|
|
result = quicktime.NewMovieFromDataRef(byref(movie),
|
|
newMovieActive,
|
|
0,
|
|
data_ref,
|
|
PointerDataHandlerSubType)
|
|
|
|
if not movie:
|
|
#_oscheck(result)
|
|
raise ImageDecodeException(filename or file)
|
|
quicktime.GoToBeginningOfMovie(movie)
|
|
|
|
time_scale = float(quicktime.GetMovieTimeScale(movie))
|
|
|
|
format, qtformat = self._get_formats()
|
|
|
|
# Get movie width and height
|
|
rect = Rect()
|
|
quicktime.GetMovieBox(movie, byref(rect))
|
|
width = rect.right
|
|
height = rect.bottom
|
|
pitch = len(format) * width
|
|
|
|
# Set gworld
|
|
buffer = (c_byte * (width * height * len(format)))()
|
|
world = GWorldPtr()
|
|
quicktime.QTNewGWorldFromPtr(byref(world), qtformat,
|
|
byref(rect), c_void_p(), c_void_p(), 0, buffer,
|
|
len(format) * width)
|
|
quicktime.SetGWorld(world, 0)
|
|
quicktime.SetMovieGWorld(movie, world, 0)
|
|
|
|
visual = quicktime.GetMovieIndTrackType(movie, 1,
|
|
VisualMediaCharacteristic,
|
|
movieTrackCharacteristic)
|
|
if not visual:
|
|
raise ImageDecodeException('No video track')
|
|
|
|
time = 0
|
|
|
|
interesting_time = c_int()
|
|
quicktime.GetTrackNextInterestingTime(
|
|
visual,
|
|
nextTimeMediaSample,
|
|
time,
|
|
1,
|
|
byref(interesting_time),
|
|
None)
|
|
duration = interesting_time.value / time_scale
|
|
|
|
frames = []
|
|
|
|
while time >= 0:
|
|
result = quicktime.GetMoviesError()
|
|
if result == noErr:
|
|
# force redraw
|
|
result = quicktime.UpdateMovie(movie)
|
|
if result == noErr:
|
|
# process movie
|
|
quicktime.MoviesTask(movie, 0)
|
|
result = quicktime.GetMoviesError()
|
|
_oscheck(result)
|
|
|
|
buffer_copy = (c_byte * len(buffer))()
|
|
memmove(buffer_copy, buffer, len(buffer))
|
|
image = ImageData(width, height, format, buffer_copy, -pitch)
|
|
frames.append(AnimationFrame(image, duration))
|
|
|
|
interesting_time = c_int()
|
|
duration = c_int()
|
|
quicktime.GetTrackNextInterestingTime(
|
|
visual,
|
|
nextTimeMediaSample,
|
|
time,
|
|
1,
|
|
byref(interesting_time),
|
|
byref(duration))
|
|
|
|
quicktime.SetMovieTimeValue(movie, interesting_time)
|
|
time = interesting_time.value
|
|
duration = duration.value / time_scale
|
|
if duration <= 0.01:
|
|
duration = 0.1
|
|
|
|
quicktime.DisposeMovie(movie)
|
|
carbon.DisposeHandle(data_ref)
|
|
|
|
quicktime.ExitMovies()
|
|
|
|
return Animation(frames)
|
|
|
|
def get_decoders():
|
|
return [QuickTimeImageDecoder()]
|
|
|
|
def get_encoders():
|
|
return []
|
|
|