Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
path: root/olpcgames/_cairoimage.py
diff options
context:
space:
mode:
Diffstat (limited to 'olpcgames/_cairoimage.py')
-rwxr-xr-xolpcgames/_cairoimage.py135
1 files changed, 135 insertions, 0 deletions
diff --git a/olpcgames/_cairoimage.py b/olpcgames/_cairoimage.py
new file mode 100755
index 0000000..3cfa22c
--- /dev/null
+++ b/olpcgames/_cairoimage.py
@@ -0,0 +1,135 @@
+"""Utility functions for cairo-specific operations
+
+USE_BASE_ARRAY -- if False (default), uses numpy arrays,
+ currently this is the only version that works on 32-bit
+ machines.
+"""
+import pygame, struct, logging
+big_endian = struct.pack( '=i', 1 ) == struct.pack( '>i', 1 )
+
+log = logging.getLogger( 'olpcgames._cairoimage' )
+##log.setLevel( logging.DEBUG )
+
+USE_BASE_ARRAY = False
+
+def newContext( width, height ):
+ """Create a new render-to-image context
+
+ width, height -- pixel dimensions to be rendered
+
+ Produces an ARGB format Cairo ImageSurface for
+ rendering your data into using rsvg, Cairo or Pango.
+
+ returns (ImageSurface, CairoContext) for rendering
+ """
+ import cairo
+ csrf = cairo.ImageSurface(cairo.FORMAT_ARGB32, width, height)
+ context = cairo.Context (csrf)
+ #log.info( 'Format (expect: %s): %s', cairo.FORMAT_ARGB32, csrf.get_format())
+ return csrf, context
+
+def mangle_color(color):
+ """Mange a colour depending on endian-ness, and swap-necessity
+
+ Converts a 3 or 4 int (or float) value in the range 0-255 into a
+ 4-float value in the range 0.0-1.0
+ """
+ r,g,b = color[:3]
+ if len(color) > 3:
+ a = color[3]
+ else:
+ a = 255.0
+ return map(_fixColorBase, (r,g,b,a) )
+
+def _fixColorBase( v ):
+ """Return a properly clamped colour in floating-point space"""
+ return max((0,min((v,255.0))))/255.0
+
+def asImage( csrf ):
+ """Get the pixels in csrf as a Pygame image
+
+ Note that Pygame 1.7.1 on (Gentoo Linux) AMD64 is incorrectly
+ calculating the required size ARGB images, so this code will *not* work
+ on that platform with that version of the library. Pygame-ctypes
+ does work correctly there.
+
+ Note also that Pygame 1.7.1 is showing a strange colour rotation
+ bug on 32-bit platforms, such that ARGB mode cannot be used for
+ images there. Instead we have to do an expensive bit-shift operation
+ to produce an RGBA image from the ARGB native Cairo format.
+
+ Will raise a ValueError if passed a Null image (i.e. dimension of 0)
+
+ returns Pygame.Surface (image) with convert_alpha() called for it.
+ """
+ # Create and return a new Pygame Image derived from the Cairo Surface
+ format = 'ARGB'
+ if hasattr(csrf,'get_data'):
+ # more recent API, native-format, but have to (potentially) convert the format...
+ log.debug( 'Native-mode api (get_data)' )
+ data = csrf.get_data()
+ if not big_endian:
+ # we use array here because it's considerably lighter-weight
+ # to import than the numpy module
+ log.debug( 'Not big-endian, byte-swapping array' )
+ if USE_BASE_ARRAY:
+ import array
+ a = array.array( 'I' )
+ a.fromstring( data )
+ a.byteswap()
+ data = a.tostring()
+ else:
+ import numpy
+ n = numpy.fromstring( data, dtype='I' )
+ n = ((n & 0xff000000) >> 24 ) | ((n & 0x00ffffff) << 8 )
+ n = n.byteswap()
+ data = n.tostring()
+ format = 'RGBA'
+ else:
+ log.debug( 'Big-endian, array unchanged' )
+ data = str(data) # there's one copy
+ else:
+ # older api, not native, but we know what it is...
+ log.debug( 'Non-native mode api, explicitly RGBA' )
+ data = csrf.get_data_as_rgba()
+ data = str(data) # there's one copy
+ format = 'RGBA'
+ width, height = csrf.get_width(),csrf.get_height()
+
+ try:
+ log.info( 'Format = %s', format )
+ return pygame.image.fromstring(
+ data,
+ (width,height),
+ format
+ ) # there's the next
+ except ValueError, err:
+ err.args += (len(data), (width,height), width*height*4,format )
+ raise
+
+if __name__ == "__main__":
+ import unittest
+ logging.basicConfig()
+ class Tests( unittest.TestCase ):
+ def test_colours( self ):
+ """Test that colours are correctly translated
+
+ If we draw a given colour in cairo, we want the same
+ colour to show up in Pygame, let's test that...
+ """
+ for sourceColour in [
+ (255,0,0, 255),
+ (0,255,0, 255),
+ (0,0,255, 255),
+ (255,255,0, 255),
+ (0,255,255,255),
+ (255,0,255,255),
+ ]:
+ csrf,cctx = newContext( 1,1 )
+ background = mangle_color( sourceColour )
+ cctx.set_source_rgba(*background)
+ cctx.paint()
+ img = asImage( csrf )
+ colour = img.get_at( (0,0))
+ assert colour == sourceColour, (sourceColour,mangle_color(sourceColour),colour)
+ unittest.main()