File: image.pyx

package info (click to toggle)
soya 0.12-2
  • links: PTS
  • area: main
  • in suites: etch, etch-m68k
  • size: 13,580 kB
  • ctags: 20,171
  • sloc: cpp: 45,252; python: 7,241; ansic: 5,226; perl: 273; makefile: 227; sh: 65
file content (255 lines) | stat: -rw-r--r-- 10,005 bytes parent folder | download | duplicates (2)
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
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
# -*- 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 = <GLubyte*> 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(<char*> 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  = <GLubyte*> malloc(width * height * nb_color * sizeof(GLubyte))
			else:
				self._pixels  = <GLubyte*> 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("<iii", self.nb_color, self.width, self.height), self.pixels
		cdef Chunk* chunk
		chunk = get_chunk()
		chunk_add_int_endian_safe  (chunk, self.nb_color)
		chunk_add_int_endian_safe  (chunk, self.width)
		chunk_add_int_endian_safe  (chunk, self.height)
		chunk_add_chars_endian_safe(chunk, <char*> 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("<iii", cstate[0])
#    self.pixels = cstate[1]
		if isinstance(cstate, tuple):
			self._filename = cstate[1]
			cstate         = cstate[0]
		cdef Chunk* chunk
		chunk = string_to_chunk(cstate)
		chunk_get_int_endian_safe(chunk, &self.nb_color)
		chunk_get_int_endian_safe(chunk, &self.width)
		chunk_get_int_endian_safe(chunk, &self.height)
		self._pixels = <GLubyte*> malloc(self.nb_color * self.width * self.height * sizeof(GLubyte))
		chunk_get_chars_endian_safe(chunk, <char*> 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(<char*> self._pixels, self.nb_color * self.width * self.height)
		def __set__(self, string):
			self._pixels = <GLubyte*> 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 "<Image %s>" % 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 = <GLubyte*> 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 = <GLubyte*> 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] = <GLubyte> ((
						(<float> pixels[(x     + 2 * w[0] *  y)      * nb_color + m]) +
						(<float> pixels[(x + 1 + 2 * w[0] *  y)      * nb_color + m]) +
						(<float> pixels[(x     + 2 * w[0] * (y + 1)) * nb_color + m]) +
						(<float> pixels[(x + 1 + 2 * w[0] * (y + 1)) * nb_color + m])) / 4.0
						)
	return new_pixels