Source

PyCessing / pycessing / drawing_cairo.py

"""
    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 math
import sys


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)
        if sys.platform == "darwin":
            self.machack = True  # check if we are on a mac
        else:
            self.machack = False

    #set up cairo and pango contexts
    def setSurface(self, sdl_surface):
        self.sdl_surface = sdl_surface
        width = sdl_surface.get_width()
        height = sdl_surface.get_height()

        #TODO: try this on the mac with surface.convert instead
        # of reversing the array - make sure to check platform here
        #r,g,b,a = subsurf.get_shifts()
        #print r,g,b,a
        #rm,gm,bm,am = subsurf.get_masks()
        #print rm,gm,bm,am
        #subsurf.set_shifts((a, r, g, b))
        #subsurf.set_masks((am, rm, gm, bm))

        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 green is None:
            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 green is None:
            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()

    #TODO: this is broken under cairo
    def screenGrab(self, fileName):
        pygame.image.save(self.sdl_surface, fileName)

    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)
        if self.machack:
            dest.data[:] = self.surface.get_data()[::-1]
        else:
            dest.data[:] = self.surface.get_data()