Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
path: root/olpcgames/_cairoimage.py
blob: 3cfa22cffa6285ab187433460a47921fc40a7e40 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
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()