# -*- indent-tabs-mode: t -*- # Soya 3D # Copyright (C) 2003-2004 Jean-Baptiste LAMY -- jiba@tuxfamily.org # # This program 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 2 of the License, or # (at your option) any later version. # # This program 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 this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA def open_image(filename): """open_image(filename) -> Image Creates and returns a Soya image from file FILENAME. The image is loaded with PIL; the supported image formats are the same than PIL.""" cdef _Image image import PIL.Image image = image_from_pil(PIL.Image.open(filename)) image._filename = filename return image def image_from_pil(pil_image): """image_from_pil(pil_image) -> Image Creates and returns a Soya image from PIL image PIL_IMAGE.""" cdef _Image image image = Image() image.width, image.height = pil_image.size if pil_image.mode == "RGBA": image.nb_color = 4 elif pil_image.mode == "RGB" : image.nb_color = 3 elif pil_image.mode == "L" : image.nb_color = 1 elif pil_image.mode == "P" : # XXX Palette may not be monochrome image.nb_color = 1 image.palette = pil_image.palette.palette else: raise ValueError("unknown image mode (%s)" % pil_image.mode) image.pixels = pil_image.tostring() return image def screenshot(filename = None, int x = 0, int y = 0, int width = 0, int height = 0): """screenshot(filename = None, x = 0, y = 0, width = soya.get_screen_width(), height = soya.get_screen_height()) -> PIL.Image.Image Take a screenshot of the soya rendering screen, and return it as a PIL image object. If given, the image is saved under FILENAME. You must have rendered something before (e.g. by calling soya.render, or by running an MainLoop).""" import PIL.Image cdef GLubyte* pixels cdef int size width = width or renderer.screen_width height = height or renderer.screen_height size = 3 * width * height pixels = malloc(size * sizeof(GLubyte)) glReadBuffer(GL_FRONT) glReadPixels(x, y, width, height, GL_RGB, GL_UNSIGNED_BYTE, pixels) image = PIL.Image.fromstring("RGB", (width, height), PyString_FromStringAndSize( pixels, size)) image = image.transpose(PIL.Image.FLIP_TOP_BOTTOM) free(pixels) if not filename is None: image.save(filename) return image # XXX indexed images are not supported yet (not sure) cdef class _Image(_CObj): """Image object, used in particular for texture.""" #cdef readonly int nb_color, width, height #cdef GLubyte* _pixels #cdef public _filename def __init__(self, pixels = None, int width = 0, int height = 0, int nb_color = 0): """Image(pixels = None, int width = 0, int height = 0, int nb_color = 0) -> Image Creates a Soya image of dimension WIDTH and HEIGHT, with NB_COLOR color channels (4 for RGBA), and with data PIXELS (a raw image in a string). See Image.get(filename), open_image(filename) or image_from_pil(pil_image) for higher level image loading functions.""" if nb_color == 0: self._pixels = NULL else: if pixels is None: self._pixels = malloc(width * height * nb_color * sizeof(GLubyte)) else: self._pixels = dup(PyString_AS_STRING(pixels), width * height * nb_color) self.width = width self.height = height self.nb_color = nb_color def __dealloc__(self): if self._pixels != NULL: free(self._pixels) cdef __getcstate__(self): #return struct.pack(" self._pixels, self.nb_color * self.width * self.height) return drop_chunk_to_string(chunk), self._filename cdef void __setcstate__(self, cstate): # self.nb_color, self.width, self.height = struct.unpack(" malloc(self.nb_color * self.width * self.height * sizeof(GLubyte)) chunk_get_chars_endian_safe(chunk, self._pixels, self.nb_color * self.width * self.height) drop_chunk(chunk) def __deepcopy__(self, memo): """Images are immutable.""" return self property pixels: def __get__(self): return PyString_FromStringAndSize( self._pixels, self.nb_color * self.width * self.height) def __set__(self, string): self._pixels = dup(PyString_AS_STRING(string), self.nb_color * self.width * self.height) def has_border(self): return self.check_for_gl() == 2 cdef int check_for_gl(self): """_Image.check_for_gl() -> int Returns 1 if the image is suitable for OpenGL (=if both dimension are power of 2). Returns 2 if the image is suitable for OpenGL, with a border (=if both dimension are power of 2 + 2 (for the border)). Else returns 0.""" if ( self.width == power_of_2(self.width )) and ( self.height == power_of_2(self.height )): return 1 if ((self.width - 2) == power_of_2(self.width - 2)) and ((self.height - 2) == power_of_2(self.height - 2)): return 2 return 0 # cdef int i, j, n # i = 1 # j = 1 # n = 1 # while((n <= self.width) or (n <= self.height)): # if (i == 1) and (self.width == n): i = 0 # if (j == 1) and (self.height == n): j = 0 # n = n << 1 # if (i == 0) and (j == 0): return 1 cdef GLuint typ(self): """_Image.typ() -> GLuint Returns the image type, depending on the number of color channels, i.e. either GL_LUMINANCE, GL_RGB or GL_RGBA.""" if self.nb_color == 1: return GL_LUMINANCE elif self.nb_color == 3: return GL_RGB elif self.nb_color == 4: return GL_RGBA raise ValueError("unknown image number of color (%s)" % self.nb_color) cdef GLuint internal_format(self): """_Image.internal_format() -> GLuint Returns the image internal format, depending on the number of color channels and the rendering quality.""" if quality == QUALITY_LOW: if self.nb_color == 1: return GL_LUMINANCE elif self.nb_color == 3: return GL_RGB elif self.nb_color == 4: return GL_RGBA elif quality == QUALITY_MEDIUM: if self.nb_color == 1: return GL_LUMINANCE8 elif self.nb_color == 3: return GL_RGB8 elif self.nb_color == 4: return GL_RGBA8 elif quality == QUALITY_HIGH: if self.nb_color == 1: return GL_LUMINANCE16 elif self.nb_color == 3: return GL_RGB16 elif self.nb_color == 4: return GL_RGBA16 raise ValueError("unknown image number of color (%s)" % self.nb_color) def __repr__(self): return "" % self._filename def to_pil(self): import PIL.Image if self.nb_color == 4: return PIL.Image.fromstring("RGBA", (self.width, self.height), self.pixels) elif self.nb_color == 3: return PIL.Image.fromstring("RGB" , (self.width, self.height), self.pixels) elif self.nb_color == 1: if self.palette == None: return PIL.Image.fromstring("L", (self.width, self.height), self.pixels) else : pimage = PIL.Image.fromstring("P", (self.width, self.height), self.pixels) pimage.putpalette(self.palette) return pimage cdef int power_of_2(int i): if i <= 1 : return 1 if i <= 2 : return 2 if i <= 4 : return 4 if i <= 8 : return 8 if i <= 16 : return 16 if i <= 32 : return 32 if i <= 64 : return 64 if i <= 128 : return 128 if i <= 256 : return 256 if i <= 512 : return 512 if i <= 1024: return 1024 if i <= 2048: return 2048 return 4096 cdef GLubyte* pixels_scale_down_2(int nb_color, GLuint* w, GLuint* h, int border, GLubyte* pixels): """pixels_scale_down_2(int nb_color, GLuint* w, GLuint* h, int border, GLubyte* pixels) -> GLubyte* Returns a reduced version of an image. Each dimension is divided by 2. The image is given by PIXELS, NB_COLOR. W and H are pointers on the dimensions (i.e. the old image dimension at the beginning and then half of the image dimensions). The returned image is malloc'ed, and must be free'd by the caller. """ cdef int i, j, m, x, y cdef GLubyte* new_pixels if (w[0] == 1) or (h[0] == 1): if w[0] != 1: w[0] = w[0] >> 1 if h[0] != 1: h[0] = h[0] >> 1 new_pixels = malloc((h[0] + 2 * border) * (w[0] + 2 * border) * nb_color * sizeof(GLubyte)) for j from 0 <= j < (h[0] + 2 * border): for i from 0 <= i < (w[0] + 2 * border): for m from 0 <= m < nb_color: new_pixels[(i + w[0] * j) * nb_color + m] = pixels[(i + w[0] * j) * nb_color * 2 + m] else: w[0] = w[0] >> 1 h[0] = h[0] >> 1 new_pixels = malloc((h[0] + 2 * border) * (w[0] + 2 * border) * nb_color * sizeof(GLubyte)) #for i from 0 <= i < h[0] * w[0] * nb_color * sizeof(GLubyte): # new_pixels[i] = 0 for j from 0 <= j < (h[0] + 2 * border): y = 2 * j for i from 0 <= i < (w[0] + 2 * border): x = 2 * i for m from 0 <= m < nb_color: new_pixels[(i + w[0] * j) * nb_color + m] = (( ( pixels[(x + 2 * w[0] * y) * nb_color + m]) + ( pixels[(x + 1 + 2 * w[0] * y) * nb_color + m]) + ( pixels[(x + 2 * w[0] * (y + 1)) * nb_color + m]) + ( pixels[(x + 1 + 2 * w[0] * (y + 1)) * nb_color + m])) / 4.0 ) return new_pixels