1 /**
2 * Copyright (c) 2006-2012 LOVE Development Team
3 *
4 * This software is provided 'as-is', without any express or implied
5 * warranty.  In no event will the authors be held liable for any damages
6 * arising from the use of this software.
7 *
8 * Permission is granted to anyone to use this software for any purpose,
9 * including commercial applications, and to alter it and redistribute it
10 * freely, subject to the following restrictions:
11 *
12 * 1. The origin of this software must not be misrepresented; you must not
13 *    claim that you wrote the original software. If you use this software
14 *    in a product, an acknowledgment in the product documentation would be
15 *    appreciated but is not required.
16 * 2. Altered source versions must be plainly marked as such, and must not be
17 *    misrepresented as being the original software.
18 * 3. This notice may not be removed or altered from any source distribution.
19 **/
20 
21 #include "Image.h"
22 
23 // STD
24 #include <cstring> // For memcpy
25 
26 #include <iostream>
27 using namespace std;
28 
29 namespace love
30 {
31 namespace graphics
32 {
33 namespace opengl
34 {
Image(love::image::ImageData * data)35 	Image::Image(love::image::ImageData * data)
36 		: width((float)(data->getWidth())), height((float)(data->getHeight())), texture(0)
37 	{
38 		data->retain();
39 		this->data = data;
40 
41 		memset(vertices, 255, sizeof(vertex)*4);
42 
43 		vertices[0].x = 0; vertices[0].y = 0;
44 		vertices[1].x = 0; vertices[1].y = height;
45 		vertices[2].x = width; vertices[2].y = height;
46 		vertices[3].x = width; vertices[3].y = 0;
47 
48 		vertices[0].s = 0; vertices[0].t = 0;
49 		vertices[1].s = 0; vertices[1].t = 1;
50 		vertices[2].s = 1; vertices[2].t = 1;
51 		vertices[3].s = 1; vertices[3].t = 0;
52 	}
53 
~Image()54 	Image::~Image()
55 	{
56 		if (data != 0)
57 			data->release();
58 		unload();
59 	}
60 
getWidth() const61 	float Image::getWidth() const
62 	{
63 		return width;
64 	}
65 
getHeight() const66 	float Image::getHeight() const
67 	{
68 		return height;
69 	}
70 
getVertices() const71 	const vertex * Image::getVertices() const
72 	{
73 		return vertices;
74 	}
75 
getData() const76 	love::image::ImageData * Image::getData() const
77 	{
78 		return data;
79 	}
80 
getRectangleVertices(int x,int y,int w,int h,vertex * vertices) const81 	void Image::getRectangleVertices(int x, int y, int w, int h, vertex * vertices) const
82 	{
83 		// Check upper.
84 		x = (x+w > (int)width) ? (int)width-w : x;
85 		y = (y+h > (int)height) ? (int)height-h : y;
86 
87 		// Check lower.
88 		x = (x < 0) ? 0 : x;
89 		y = (y < 0) ? 0 : y;
90 
91 		vertices[0].x = 0; vertices[0].y = 0;
92 		vertices[1].x = 0; vertices[1].y = (float)h;
93 		vertices[2].x = (float)w; vertices[2].y = (float)h;
94 		vertices[3].x = (float)w; vertices[3].y = 0;
95 
96 		float tx = (float)x/width;
97 		float ty = (float)y/height;
98 		float tw = (float)w/width;
99 		float th = (float)h/height;
100 
101 		vertices[0].s = tx; vertices[0].t = ty;
102 		vertices[1].s = tx; vertices[1].t = ty+th;
103 		vertices[2].s = tx+tw; vertices[2].t = ty+th;
104 		vertices[3].s = tx+tw; vertices[3].t = ty;
105 	}
106 
draw(float x,float y,float angle,float sx,float sy,float ox,float oy,float kx,float ky) const107 	void Image::draw(float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky) const
108 	{
109 		static Matrix t;
110 
111 		t.setTransformation(x, y, angle, sx, sy, ox, oy, kx, ky);
112 		drawv(t, vertices);
113 	}
114 
drawq(love::graphics::Quad * quad,float x,float y,float angle,float sx,float sy,float ox,float oy,float kx,float ky) const115 	void Image::drawq(love::graphics::Quad * quad, float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky) const
116 	{
117 		static Matrix t;
118 		const vertex * v = quad->getVertices();
119 
120 		t.setTransformation(x, y, angle, sx, sy, ox, oy, kx, ky);
121 		drawv(t, v);
122 	}
123 
setFilter(const Image::Filter & f)124 	void Image::setFilter(const Image::Filter& f)
125 	{
126 		GLint gmin, gmag;
127 		gmin = gmag = 0; // so that they're not used uninitialized
128 
129 		switch(f.min)
130 		{
131 		case FILTER_LINEAR:
132 			gmin = GL_LINEAR;
133 			break;
134 		case FILTER_NEAREST:
135 			gmin = GL_NEAREST;
136 			break;
137 		default:
138 			break;
139 		}
140 
141 		switch(f.mag)
142 		{
143 		case FILTER_LINEAR:
144 			gmag = GL_LINEAR;
145 			break;
146 		case FILTER_NEAREST:
147 			gmag = GL_NEAREST;
148 			break;
149 		default:
150 			break;
151 		}
152 
153 		bind();
154 
155 		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, gmin);
156 		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, gmag);
157 	}
158 
getFilter() const159 	Image::Filter Image::getFilter() const
160 	{
161 		bind();
162 
163 		GLint gmin, gmag;
164 
165 		glGetTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, &gmin);
166 		glGetTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, &gmag);
167 
168 		Image::Filter f;
169 
170 		switch(gmin)
171 		{
172 		case GL_NEAREST:
173 			f.min = FILTER_NEAREST;
174 			break;
175 		case GL_LINEAR:
176 		default:
177 			f.min = FILTER_LINEAR;
178 			break;
179 		}
180 
181 		switch(gmin)
182 		{
183 		case GL_NEAREST:
184 			f.mag = FILTER_NEAREST;
185 			break;
186 		case GL_LINEAR:
187 		default:
188 			f.mag = FILTER_LINEAR;
189 			break;
190 		}
191 
192 		return f;
193 	}
194 
setWrap(Image::Wrap w)195 	void Image::setWrap(Image::Wrap w)
196 	{
197 		GLint gs, gt;
198 
199 		switch(w.s)
200 		{
201 		case WRAP_CLAMP:
202 			gs = GL_CLAMP_TO_EDGE;
203 			break;
204 		case WRAP_REPEAT:
205 		default:
206 			gs = GL_REPEAT;
207 			break;
208 		}
209 
210 		switch(w.t)
211 		{
212 		case WRAP_CLAMP:
213 			gt = GL_CLAMP_TO_EDGE;
214 			break;
215 		case WRAP_REPEAT:
216 		default:
217 			gt = GL_REPEAT;
218 			break;
219 		}
220 
221 		bind();
222 
223 		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, gs);
224 		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, gt);
225 	}
226 
getWrap() const227 	Image::Wrap Image::getWrap() const
228 	{
229 		bind();
230 
231 		GLint gs, gt;
232 
233 		glGetTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, &gs);
234 		glGetTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, &gt);
235 
236 		Wrap w;
237 
238 		switch(gs)
239 		{
240 		case GL_CLAMP_TO_EDGE:
241 			w.s = WRAP_CLAMP;
242 			break;
243 		case GL_REPEAT:
244 		default:
245 			w.s = WRAP_REPEAT;
246 			break;
247 		}
248 
249 		switch(gt)
250 		{
251 		case GL_CLAMP_TO_EDGE:
252 			w.t = WRAP_CLAMP;
253 			break;
254 		case GL_REPEAT:
255 		default:
256 			w.t = WRAP_REPEAT;
257 			break;
258 		}
259 
260 		return w;
261 	}
262 
bind() const263 	void Image::bind() const
264 	{
265 		if (texture == 0)
266 			return;
267 
268 		bindTexture(texture);
269 	}
270 
load()271 	bool Image::load()
272 	{
273 		return loadVolatile();
274 	}
275 
unload()276 	void Image::unload()
277 	{
278 		return unloadVolatile();
279 	}
280 
loadVolatile()281 	bool Image::loadVolatile()
282 	{
283 		if (hasNpot())
284 			return loadVolatileNPOT();
285 		else
286 			return loadVolatilePOT();
287 	}
288 
loadVolatilePOT()289 	bool Image::loadVolatilePOT()
290 	{
291 		glGenTextures(1,(GLuint*)&texture);
292 		bindTexture(texture);
293 		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
294 		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
295 
296 		glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
297 		glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
298 
299 		float p2width = next_p2(width);
300 		float p2height = next_p2(height);
301 		float s = width/p2width;
302 		float t = height/p2height;
303 
304 		vertices[1].t = t;
305 		vertices[2].t = t;
306 		vertices[2].s = s;
307 		vertices[3].s = s;
308 
309 		glTexImage2D(GL_TEXTURE_2D,
310 			0,
311 			GL_RGBA8,
312 			(GLsizei)p2width,
313 			(GLsizei)p2height,
314 			0,
315 			GL_RGBA,
316 			GL_UNSIGNED_BYTE,
317 			0);
318 
319 		glTexSubImage2D(GL_TEXTURE_2D,
320 			0,
321 			0,
322 			0,
323 			(GLsizei)width,
324 			(GLsizei)height,
325 			GL_RGBA,
326 			GL_UNSIGNED_BYTE,
327 			data->getData());
328 
329 		setFilter(settings.filter);
330 		setWrap(settings.wrap);
331 
332 		return true;
333 	}
334 
loadVolatileNPOT()335 	bool Image::loadVolatileNPOT()
336 	{
337 		glGenTextures(1,(GLuint*)&texture);
338 		bindTexture(texture);
339 		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
340 		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
341 
342 		glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
343 		glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
344 
345 		glTexImage2D(GL_TEXTURE_2D,
346 			0,
347 			GL_RGBA8,
348 			(GLsizei)width,
349 			(GLsizei)height,
350 			0,
351 			GL_RGBA,
352 			GL_UNSIGNED_BYTE,
353 			data->getData());
354 
355 		setFilter(settings.filter);
356 		setWrap(settings.wrap);
357 
358 		return true;
359 	}
360 
unloadVolatile()361 	void Image::unloadVolatile()
362 	{
363 		settings.filter = getFilter();
364 		settings.wrap = getWrap();
365 		// Delete the hardware texture.
366 		if (texture != 0)
367 		{
368 			deleteTexture(texture);
369 			texture = 0;
370 		}
371 	}
372 
drawv(const Matrix & t,const vertex * v) const373 	void Image::drawv(const Matrix & t, const vertex * v) const
374 	{
375 		bind();
376 
377 		glPushMatrix();
378 
379 		glMultMatrixf((const GLfloat*)t.getElements());
380 
381 		glEnableClientState(GL_VERTEX_ARRAY);
382 		glEnableClientState(GL_TEXTURE_COORD_ARRAY);
383 		glVertexPointer(2, GL_FLOAT, sizeof(vertex), (GLvoid*)&v[0].x);
384 		glTexCoordPointer(2, GL_FLOAT, sizeof(vertex), (GLvoid*)&v[0].s);
385 		glDrawArrays(GL_QUADS, 0, 4);
386 		glDisableClientState(GL_TEXTURE_COORD_ARRAY);
387 		glDisableClientState(GL_VERTEX_ARRAY);
388 
389 		glPopMatrix();
390 	}
391 
hasNpot()392 	bool Image::hasNpot()
393 	{
394 		return GLEE_ARB_texture_non_power_of_two != 0;
395 	}
396 
397 } // opengl
398 } // graphics
399 } // love
400