1 /* ResidualVM - A 3D game interpreter
2  *
3  * ResidualVM is the legal property of its developers, whose names
4  * are too numerous to list here. Please refer to the AUTHORS
5  * file distributed with this source distribution.
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * as published by the Free Software Foundation; either version 2
10  * of the License, or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20  *
21  */
22 
23 /*
24  * This file is based on, or a modified version of code from TinyGL (C) 1997-1998 Fabrice Bellard,
25  * which is licensed under the zlib-license (see LICENSE).
26  * It also has modifications by the ResidualVM-team, which are covered under the GPLv2 (or later).
27  */
28 
29 // Texture Manager
30 
31 #include "common/endian.h"
32 
33 #include "graphics/tinygl/zgl.h"
34 
35 struct tglColorAssociation {
36 	Graphics::PixelFormat pf;
37 	TGLuint format;
38 	TGLuint type;
39 };
40 
41 static const struct tglColorAssociation colorAssociationList[] = {
42 /*
43  * TGL_UNSIGNED_BYTE before other variants to provide OpenGLES-friendly formats
44  * when this table is used to look these up.
45  * Note: this does not matter at all for TinyGL, but this is to be consistent
46  * with future OpenGL equivalent for this code.
47  */
48 // TODO: remove pixel endianness conversions from tinygl callers and enable
49 //#if defined(SCUMM_LITTLE_ENDIAN)
50 #if 1
51 	{Graphics::PixelFormat(4, 8, 8, 8, 8, 0, 8, 16, 24), TGL_RGBA, TGL_UNSIGNED_BYTE},
52 	{Graphics::PixelFormat(4, 8, 8, 8, 8, 16, 8, 0, 24), TGL_BGRA, TGL_UNSIGNED_BYTE},
53 	{Graphics::PixelFormat(3, 8, 8, 8, 0, 0, 8, 16, 0),  TGL_RGB,  TGL_UNSIGNED_BYTE},
54 	{Graphics::PixelFormat(3, 8, 8, 8, 0, 16, 8, 0, 0),  TGL_BGR,  TGL_UNSIGNED_BYTE},
55 #else
56 	{Graphics::PixelFormat(4, 8, 8, 8, 8, 24, 16, 8, 0), TGL_RGBA, TGL_UNSIGNED_BYTE},
57 	{Graphics::PixelFormat(4, 8, 8, 8, 8, 8, 16, 24, 0), TGL_BGRA, TGL_UNSIGNED_BYTE},
58 	{Graphics::PixelFormat(3, 8, 8, 8, 0, 16, 8, 0, 0),  TGL_RGB,  TGL_UNSIGNED_BYTE},
59 	{Graphics::PixelFormat(3, 8, 8, 8, 0, 0, 8, 16, 0),  TGL_BGR,  TGL_UNSIGNED_BYTE},
60 #endif
61 	{Graphics::PixelFormat(4, 8, 8, 8, 8, 0, 8, 16, 24), TGL_RGBA, TGL_UNSIGNED_INT_8_8_8_8_REV},
62 	{Graphics::PixelFormat(4, 8, 8, 8, 8, 24, 16, 8, 0), TGL_RGBA, TGL_UNSIGNED_INT_8_8_8_8},
63 	{Graphics::PixelFormat(4, 8, 8, 8, 8, 16, 8, 0, 24), TGL_BGRA, TGL_UNSIGNED_INT_8_8_8_8_REV},
64 	{Graphics::PixelFormat(4, 8, 8, 8, 8, 8, 16, 24, 0), TGL_BGRA, TGL_UNSIGNED_INT_8_8_8_8},
65 	{Graphics::PixelFormat(2, 5, 5, 5, 1, 0, 5, 10, 15), TGL_RGBA, TGL_UNSIGNED_SHORT_1_5_5_5_REV},
66 	{Graphics::PixelFormat(2, 5, 5, 5, 1, 11, 6, 1, 0),  TGL_RGBA, TGL_UNSIGNED_SHORT_5_5_5_1},
67 	{Graphics::PixelFormat(2, 5, 5, 5, 1, 10, 5, 0, 15), TGL_BGRA, TGL_UNSIGNED_SHORT_1_5_5_5_REV},
68 	{Graphics::PixelFormat(2, 5, 5, 5, 1, 1, 6, 11, 0),  TGL_BGRA, TGL_UNSIGNED_SHORT_5_5_5_1},
69 	{Graphics::PixelFormat(2, 5, 6, 5, 0, 0, 5, 11, 0),  TGL_RGB,  TGL_UNSIGNED_SHORT_5_6_5_REV},
70 	{Graphics::PixelFormat(2, 5, 6, 5, 0, 11, 5, 0, 0),  TGL_BGR,  TGL_UNSIGNED_SHORT_5_6_5},
71 	{Graphics::PixelFormat(2, 5, 6, 5, 0, 11, 5, 0, 0),  TGL_BGR,  TGL_UNSIGNED_SHORT_5_6_5_REV},
72 	{Graphics::PixelFormat(2, 5, 6, 5, 0, 0, 5, 11, 0),  TGL_RGB,  TGL_UNSIGNED_SHORT_5_6_5}
73 };
74 #define COLOR_ASSOCIATION_LIST_LENGTH (sizeof(colorAssociationList) / sizeof(*colorAssociationList))
75 
76 namespace TinyGL {
77 
find_texture(GLContext * c,unsigned int h)78 static GLTexture *find_texture(GLContext *c, unsigned int h) {
79 	GLTexture *t;
80 
81 	t = c->shared_state.texture_hash_table[h % TEXTURE_HASH_TABLE_SIZE];
82 	while (t) {
83 		if (t->handle == h)
84 			return t;
85 		t = t->next;
86 	}
87 	return NULL;
88 }
89 
free_texture(GLContext * c,int h)90 void free_texture(GLContext *c, int h) {
91 	free_texture(c, find_texture(c, h));
92 }
93 
free_texture(GLContext * c,GLTexture * t)94 void free_texture(GLContext *c, GLTexture *t) {
95 	GLTexture **ht;
96 	GLImage *im;
97 
98 	if (!t->prev) {
99 		ht = &c->shared_state.texture_hash_table[t->handle % TEXTURE_HASH_TABLE_SIZE];
100 		*ht = t->next;
101 	} else {
102 		t->prev->next = t->next;
103 	}
104 	if (t->next)
105 		t->next->prev = t->prev;
106 
107 	for (int i = 0; i < MAX_TEXTURE_LEVELS; i++) {
108 		im = &t->images[i];
109 		if (im->pixmap) {
110 			delete im->pixmap;
111 			im->pixmap = nullptr;
112 		}
113 	}
114 
115 	gl_free(t);
116 }
117 
alloc_texture(GLContext * c,int h)118 GLTexture *alloc_texture(GLContext *c, int h) {
119 	GLTexture *t, **ht;
120 
121 	t = (GLTexture *)gl_zalloc(sizeof(GLTexture));
122 
123 	ht = &c->shared_state.texture_hash_table[h % TEXTURE_HASH_TABLE_SIZE];
124 
125 	t->next = *ht;
126 	t->prev = NULL;
127 	if (t->next)
128 		t->next->prev = t;
129 	*ht = t;
130 
131 	t->handle = h;
132 	t->disposed = false;
133 	t->versionNumber = 0;
134 
135 	return t;
136 }
137 
glInitTextures(GLContext * c)138 void glInitTextures(GLContext *c) {
139 	// textures
140 	c->texture_2d_enabled = 0;
141 	c->current_texture = find_texture(c, 0);
142 	c->texture_mag_filter = TGL_LINEAR;
143 	c->texture_min_filter = TGL_NEAREST_MIPMAP_LINEAR;
144 }
145 
glopBindTexture(GLContext * c,GLParam * p)146 void glopBindTexture(GLContext *c, GLParam *p) {
147 	int target = p[1].i;
148 	int texture = p[2].i;
149 	GLTexture *t;
150 
151 	assert(target == TGL_TEXTURE_2D && texture >= 0);
152 
153 	t = find_texture(c, texture);
154 	if (!t) {
155 		t = alloc_texture(c, texture);
156 	}
157 	c->current_texture = t;
158 }
159 
formatType2PixelFormat(TGLuint format,TGLuint type)160 static inline const Graphics::PixelFormat formatType2PixelFormat(TGLuint format,  TGLuint type) {
161 	for (unsigned int i = 0; i < COLOR_ASSOCIATION_LIST_LENGTH; i++) {
162 		if (colorAssociationList[i].format == format &&
163 		    colorAssociationList[i].type == type)
164 			return colorAssociationList[i].pf;
165 	}
166 	error("TinyGL texture: format 0x%04x and type 0x%04x combination not supported", format, type);
167 }
168 
glopTexImage2D(GLContext * c,GLParam * p)169 void glopTexImage2D(GLContext *c, GLParam *p) {
170 	int target = p[1].i;
171 	int level = p[2].i;
172 // "components" is guessed from "format".
173 //	int components = p[3].i;
174 	int width = p[4].i;
175 	int height = p[5].i;
176 	int border = p[6].i;
177 	int format = p[7].i;
178 	int type = p[8].i;
179 	byte *pixels = (byte *)p[9].p;
180 	GLImage *im;
181 
182 	if (target != TGL_TEXTURE_2D)
183 		error("tglTexImage2D: target not handled");
184 	if (level < 0 || level >= MAX_TEXTURE_LEVELS)
185 		error("tglTexImage2D: invalid level");
186 	if (border != 0)
187 		error("tglTexImage2D: invalid border");
188 
189 	c->current_texture->versionNumber++;
190 	im = &c->current_texture->images[level];
191 	im->xsize = c->_textureSize;
192 	im->ysize = c->_textureSize;
193 	if (im->pixmap) {
194 		delete im->pixmap;
195 		im->pixmap = nullptr;
196 	}
197 	if (pixels != NULL) {
198 		unsigned int filter;
199 		Graphics::PixelBuffer src(formatType2PixelFormat(format, type), pixels);
200 		if (width > c->_textureSize || height > c->_textureSize)
201 			filter = c->texture_mag_filter;
202 		else
203 			filter = c->texture_min_filter;
204 		switch (filter) {
205 		case TGL_LINEAR_MIPMAP_NEAREST:
206 		case TGL_LINEAR_MIPMAP_LINEAR:
207 		case TGL_LINEAR:
208 			im->pixmap = new Graphics::BilinearTexelBuffer(
209 				src,
210 				width, height,
211 				c->_textureSize
212 			);
213 			break;
214 		default:
215 			im->pixmap = new Graphics::NearestTexelBuffer(
216 				src,
217 				width, height,
218 				c->_textureSize
219 			);
220 			break;
221 		}
222 	}
223 }
224 
225 // TODO: not all tests are done
glopTexEnv(GLContext *,GLParam * p)226 void glopTexEnv(GLContext *, GLParam *p) {
227 	int target = p[1].i;
228 	int pname = p[2].i;
229 	int param = p[3].i;
230 
231 	if (target != TGL_TEXTURE_ENV) {
232 error:
233 		error("tglTexParameter: unsupported option");
234 	}
235 
236 	if (pname != TGL_TEXTURE_ENV_MODE)
237 		goto error;
238 
239 	if (param != TGL_DECAL)
240 		goto error;
241 }
242 
243 // TODO: not all tests are done
glopTexParameter(GLContext * c,GLParam * p)244 void glopTexParameter(GLContext *c, GLParam *p) {
245 	int target = p[1].i;
246 	int pname = p[2].i;
247 	int param = p[3].i;
248 
249 	if (target != TGL_TEXTURE_2D) {
250 error:
251 		error("tglTexParameter: unsupported option");
252 	}
253 
254 	switch (pname) {
255 	case TGL_TEXTURE_WRAP_S:
256 		c->texture_wrap_s = param;
257 		break;
258 	case TGL_TEXTURE_WRAP_T:
259 		c->texture_wrap_t = param;
260 		break;
261 	case TGL_TEXTURE_MAG_FILTER:
262 		switch (param) {
263 		case TGL_NEAREST:
264 		case TGL_LINEAR:
265 			c->texture_mag_filter = param;
266 			break;
267 		default:
268 			goto error;
269 		}
270 		break;
271 	case TGL_TEXTURE_MIN_FILTER:
272 		switch (param) {
273 		case TGL_LINEAR_MIPMAP_NEAREST:
274 		case TGL_LINEAR_MIPMAP_LINEAR:
275 		case TGL_NEAREST_MIPMAP_NEAREST:
276 		case TGL_NEAREST_MIPMAP_LINEAR:
277 		case TGL_NEAREST:
278 		case TGL_LINEAR:
279 			c->texture_min_filter = param;
280 			break;
281 		default:
282 			goto error;
283 		}
284 		break;
285 	default:
286 		;
287 	}
288 }
289 
glopPixelStore(GLContext *,GLParam * p)290 void glopPixelStore(GLContext *, GLParam *p) {
291 	int pname = p[1].i;
292 	int param = p[2].i;
293 
294 	if (pname != TGL_UNPACK_ALIGNMENT || param != 1) {
295 		error("tglPixelStore: unsupported option");
296 	}
297 }
298 
299 } // end of namespace TinyGL
300 
tglGenTextures(int n,unsigned int * textures)301 void tglGenTextures(int n, unsigned int *textures) {
302 	TinyGL::GLContext *c = TinyGL::gl_get_context();
303 	unsigned int max;
304 	TinyGL::GLTexture *t;
305 
306 	max = 0;
307 	for (int i = 0; i < TEXTURE_HASH_TABLE_SIZE; i++) {
308 		t = c->shared_state.texture_hash_table[i];
309 		while (t) {
310 			if (t->handle > max)
311 				max = t->handle;
312 			t = t->next;
313 		}
314 	}
315 	for (int i = 0; i < n; i++) {
316 		textures[i] = max + i + 1;
317 	}
318 }
319 
tglDeleteTextures(int n,const unsigned int * textures)320 void tglDeleteTextures(int n, const unsigned int *textures) {
321 	TinyGL::GLContext *c = TinyGL::gl_get_context();
322 	TinyGL::GLTexture *t;
323 
324 	for (int i = 0; i < n; i++) {
325 		t = TinyGL::find_texture(c, textures[i]);
326 		if (t) {
327 			if (t == c->current_texture) {
328 				tglBindTexture(TGL_TEXTURE_2D, 0);
329 			}
330 			t->disposed = true;
331 		}
332 	}
333 }
334