Source

PyCessing / drawing_cairo.py

Full commit
"""
    drawing.py
    Copyright 2012 Brendan Howell (brendan@howell-ersatz.com)

    This file is part of PyCessing.

    PyCessing is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    PyCessing is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with PyCessing.  If not, see <http://www.gnu.org/licenses/>.
"""


import pygame
import cairo
import pangocairo
import pango
import numpy
import math
#import Image
import array


class Drawing:
    def __init__(self):
        self.sdl_surface = None
        self.surface = None
        self.ctx = None
        self.stroke = False
        self.stroke_color = (1, 0, 0, 1)
        self.stroke_width = 0
        self.fill = True
        self.fill_color = (0.5, 0, 0.5, 1)
        
    #grab the context at the beginning of the frame
    def setSurface(self, sdl_surface):
        self.sdl_surface = sdl_surface
        width = sdl_surface.get_width()
        height = sdl_surface.get_height()
        
        self.surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, width, height)
        self.ctx = cairo.Context(self.surface)
        self.pangoctx = pangocairo.CairoContext(self.ctx)
        self.font_map = pangocairo.cairo_font_map_get_default()
        self.families = self.font_map.list_families()
        
    def setBackground(self, red, green=None, blue=None):
        if not(green):
            self.ctx.set_source_rgba(red/255.0, red/255.0, red/255.0, 1)
        else:
            self.ctx.set_source_rgba(red/255.0, blue/255.0, green/255.0, 1)
        self.ctx.paint()
        
    def setStroke(self, red, green=None, blue=None, alpha=255, width=1):
        self.setStrokeWidth(width)
        self.setStrokeColor(red, green, blue, alpha)
        
    def setStrokeColor(self, red, green=None, blue=None, alpha=255):
        #assume greyscale if only 1 parameter is specified
        if not(green):
            self.stroke_color = (red/255.0, red/255.0, red/255.0, alpha/255.0)
        else:
            self.stroke_color = (red/255.0, green/255.0, blue/255.0, alpha/255.0)
    
    def setStrokeWidth(self, width):
        self.stroke_width = width
        
    def setFillColor(self, red, green=None, blue=None, alpha=255):
        #assume greyscale if only 1 parameter is specified, assume you want fill
        if not(green):
            self.fill_color = (red/255.0, red/255.0, red/255.0, alpha/255.0)
        else:
            self.fill_color = (red/255.0, green/255.0, blue/255.0, alpha/255.0)
        self.fill = True
        
    def setFillState(self, fill):
        self.fill = fill
        
    def rect(self, x, y, width, height): 
        self.ctx.rectangle(x, y, width, height)
        self._fillAndStroke()
    
    def circle(self, cx, cy, radius):
        self.ctx.arc(cx, cy, radius, 0, 2.0 * math.pi)
        self._fillAndStroke()
    
    def ellipse(self, cx, cy, width, height):
        self.ctx.save()
        self.ctx.translate(cx, cy)
        self.ctx.scale(width / 2.0, height / 2.0)
        self.ctx.arc(0.0, 0.0, 1.0, 0.0, 2.0 * math.pi)
        self.ctx.restore()
        self._fillAndStroke()
    
    def arc(self, cx, cy, radius, startAngle, endAngle):
        self.ctx.arc(cx, cy, radius, math.radians(startAngle), math.radians(endAngle))
        self._fillAndStroke()
        
    def line(self, x1, y1, x2, y2):
        self.ctx.move_to(x1, y1)
        self.ctx.line_to(x2, y2)
        self._fillAndStroke()
    
    def polygon(self, pointlist):
        x, y = pointlist[0]
        self.ctx.move_to(x, y)
        for point in pointlist[1:]:
            x, y = point
            self.ctx.line_to(x, y)
        self.ctx.close_path()
        self._fillAndStroke()
                
    def curve(self, x1, y1, cx1, cy1, cx2, cy2, x2, y2):
        self.ctx.move_to(x1, y1)
        self.ctx.curve_to(cx1, cy1, cx2, cy2, x2, y2)
        self._fillAndStroke()

    def screenGrab(self, fileName):
        pygame.image.save(self.sdl_surface, fileName)
        
    def drawText(self, x, y, txt):
        self.ctx.save()
        self.ctx.translate(x,y)
        self.ctx.set_source_rgb(0, 0, 0)
        layout = self.pangoctx.create_layout()
        #print dir(self.families[0])
        print self.families[0].get_name()
        layout.set_font_description(pango.FontDescription(self.families[0].get_name() + " 25"))
        layout.set_width(300)
        layout.set_text(txt)
        self.pangoctx.update_layout(layout)
        print('ctx.POS: %s %s'%self.ctx.get_current_point())
        self.pangoctx.show_layout(layout)
        print('F => %s %s'%layout.get_pixel_size())
        self.ctx.restore()
        
    def _fillAndStroke(self):
        if(self.fill):
            self.ctx.set_source_rgba(self.fill_color[0], self.fill_color[1], self.fill_color[2], self.fill_color[3])
            self.ctx.fill_preserve()
        if(self.stroke_width > 0):
            self.ctx.set_line_width(self.stroke_width)
            self.ctx.set_source_rgba(self.stroke_color[0], self.stroke_color[1], self.stroke_color[2], self.stroke_color[3])
            self.ctx.stroke()
        else:
            self.ctx.new_path()

    #TODO: finish and test PDF output
    def renderToPDF(self, fileName):
    	self.surface = cairo.PDFSurface(fileName, self.sdl_surface.get_width(), self.sdl_surface.get_height())
    	

    def _blitToScreen(self):  
        dest = pygame.surfarray.pixels2d(self.sdl_surface)
        dest.data[:] = self.surface.get_data()