Source

PyCessing / images.py

Full commit
"""
    images.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 os
import io
import cairo
import math

SCREEN = None
PROJECT_DIR = None
CTX = None

#TODO: write a generic transform stack to manage arbitrary sequences of 
#      transformations:
#       ctx.save()
#       

class Image2:
    """All-purpose class for cairo-based pixel images in pycessing"""
    def __init__(self, file_name):
        self.file_name = file_name
        self.loaded = False
        self.rotation = 0
        self.scalex = None
        self.scaley = None
        
        
    def _loadSurface_(self):
        """load image from file or file_obj"""
        try:
            path = os.path.join(PROJECT_DIR, self.file_name)
            surface = pygame.image.load(path).convert_alpha()
        except:
            path = self.file_name
            buf = io.BytesIO(path.read())
            surface = pygame.image.load(buf).convert_alpha()
        
        self.width = surface.get_width()
        self.height = surface.get_height()    
        ar = pygame.surfarray.array2d(surface)
        stride = surface.get_width() * 4
        self.surface = cairo.ImageSurface.create_for_data(ar,
                           cairo.FORMAT_ARGB32, surface.get_width(), 
                           surface.get_height(), stride)
        self.loaded = True

    def draw(self, x, y, sx=None, sy=None):
        #draws to the cairo context
        if(not(self.loaded)):
            self._loadSurface_()
        if(sx and sy):
            if(width and height):
                area = (sx, sy, width, height)
            else:
                area = (sx, sy, self.surface.get_width(), 
						self.surface.get_height())
        else:
            area = None
            
        CTX.save()

        CTX.translate(x, y)
        CTX.rotate(self.rotation)
        CTX.scale(self.scalex, self.scaley)

        CTX.set_source_surface(self.surface, 0, 0)
        CTX.paint()
        CTX.restore()
        
    def rotate(self, angle):
        self.rotation = math.pi * angle / 180.0
        
    def scale(self, pixelsx, pixelsy):
        if (pixelsx < 1) or (pixelsy < 1):
            print "image must be at least 1px wide and 1px high!"
            return
        if(not(self.loaded)):
            self._loadSurface_()
        self.scalex = float(pixelsx) / self.width
        self.scaley = float(pixelsy) / self.height
        
    def get_width(self):
        if(not(self.loaded)):
            self._loadSurface_()
        if self.scalex:
            return self.scalex
        else:
            return self.width
    
    def get_height(self):
        if(not(self.loaded)):
            self._loadSurface_()
        dx, dy = self.matrix.transform_distance(0, self.height)
        if self.scaley:
            return self.scaley
        else:
            return self.height

class Image:
    """All-purpose class for pixel images in pycessing"""
    def __init__(self, file_name):
        self.file_name = file_name
        self.loaded = False
        self.transformed = False

    def _loadSurface_(self):
        """load image from file or file_obj"""
        try:
            path = os.path.join(PROJECT_DIR, self.file_name)
            self.surface = pygame.image.load(path).convert_alpha()
        except:
            path = self.file_name
            buf = io.BytesIO(path.read())
            self.surface = pygame.image.load(buf).convert_alpha()
        self.original_surface = self.surface.copy()
        self.loaded = True
    
    def draw(self, x, y, sx=None, sy=None, width=None, height=None):
        if(not(self.loaded)):
            self._loadSurface_()
        if(sx and sy):
            if(width and height):
                area = (sx, sy, width, height)
            else:
                area = (sx, sy, self.surface.get_width(), 
						self.surface.get_height())
        else:
            area = None
        SCREEN.unlock()
        SCREEN.blit(self.surface, (x, y), area)
        self.transformed = False

    def scale(self,width,height):
        if not(self.loaded):
            self._loadSurface_()
        if not(self.transformed):
            img = self.original_surface
            self.transformed = True
        else:
            img = self.surface
        self.surface = pygame.transform.smoothscale(img, (width, height))

    def flip(self,flipx=True,flipy=True):
        if not(self.loaded):
           self._loadSurface_()
        self.surface = pygame.transform.flip(self.surface, flipx, flipy)

    def rotate(self, angle):
        if not(self.loaded):
            self._loadSurface_()
        if not(self.transformed):
            img = self.original_surface
            self.transformed = True
        else:
            img = self.surface
        self.surface = pygame.transform.rotate(img, angle)

    def getWidth(self):
        if(not(self.loaded)):
            self._loadSurface_()
        return self.surface.get_width()

    def getHeight(self):
        if(not(self.loaded)):
            self._loadSurface_()
        return self.surface.get_height()

    def getPixel(self, x, y):
        if(not(self.loaded)):
            self._loadSurface_()
        return self.surface.get_at((x,y))

    def setPixel(self, x, y, r, g, b, a=None):
        if(not(self.loaded)):
            self._loadSurface_()
        if (a):
            self.surface.set_at((x, y), (r, g, b, a))
        else:
            self.surface.set_at((x, y), (r, g, b))