lib/mnit: fix and improve magnification for a pixelized (to smooth) look
[nit.git] / lib / mnit / opengles1.nit
1 # This file is part of NIT ( http://www.nitlanguage.org ).
2 #
3 # Copyright 2011-2013 Alexis Laferrière <alexis.laf@xymus.net>
4 #
5 # Licensed under the Apache License, Version 2.0 (the "License");
6 # you may not use this file except in compliance with the License.
7 # You may obtain a copy of the License at
8 #
9 # http://www.apache.org/licenses/LICENSE-2.0
10 #
11 # Unless required by applicable law or agreed to in writing, software
12 # distributed under the License is distributed on an "AS IS" BASIS,
13 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 # See the License for the specific language governing permissions and
15 # limitations under the License.
16
17 # OpenGL ES1 general support (most of it)
18 module opengles1 is pkgconfig("glesv1_cm", "egl")
19
20 import mnit_display
21
22 in "C header" `{
23 #include <EGL/egl.h>
24 #include <GLES/gl.h>
25
26 EGLDisplay mnit_display;
27 EGLSurface mnit_surface;
28 EGLContext mnit_context;
29 EGLConfig mnit_config;
30 int32_t mnit_width;
31 int32_t mnit_height;
32
33 struct mnit_opengles_Texture {
34 GLuint texture;
35
36 /* offsets on source texture */
37 float src_xo, src_yo, src_xi, src_yi;
38
39 /* destination width and height */
40 int width, height;
41
42 /* may vary depending on scaling */
43 int center_x, center_y;
44
45 float scale;
46 int blended;
47 };
48
49 struct mnit_opengles_DrawableTexture {
50 struct mnit_opengles_Texture super;
51 GLuint fbo;
52 GLuint depth;
53 GLuint color;
54 };
55
56 GLenum mnit_opengles_error_code;
57
58 struct mnit_opengles_Texture *mnit_opengles_load_image(
59 const uint_least32_t *pixels, int width, int height,
60 int width_pow2, int height_pow2, int has_alpha);
61 `}
62
63 in "C" `{
64 extern NativeWindowType mnit_window;
65 extern EGLNativeDisplayType mnit_native_display;
66
67 GLfloat mnit_opengles_vertices[6][3] =
68 {
69 {0.0f, 1.0f, 0.0f},
70 {1.0f, 1.0f, 0.0f},
71 {0.0f, 0.0f, 0.0f},
72 {1.0f, 0.0f, 0.0f},
73 };
74 GLfloat mnit_opengles_texture[6][2] =
75 {
76 {0.0f, 0.0f},
77 {0.0f, 1.0f},
78 {1.0f, 1.0f},
79 {0.0f, 0.0f},
80 {1.0f, 1.0f},
81 {1.0f, 0.0f}
82 };
83
84 struct mnit_opengles_Texture *mnit_opengles_load_image(
85 const uint_least32_t *pixels, int width, int height,
86 int width_pow2, int height_pow2, int has_alpha)
87 {
88 struct mnit_opengles_Texture *image = malloc(sizeof(struct mnit_opengles_Texture));
89 int format = has_alpha? GL_RGBA: GL_RGB;
90
91 image->width = width;
92 image->height = height;
93 image->center_x = width/2;
94 image->center_y = height/2;
95 image->scale = 1.0f;
96 image->blended = has_alpha;
97
98 image->src_xo = 0;
99 image->src_yo = 0;
100 image->src_xi = ((float)width)/width_pow2;
101 image->src_yi = ((float)height)/height_pow2;
102
103 if ((mnit_opengles_error_code = glGetError()) != GL_NO_ERROR) {
104 PRINT_ERROR("error loading image after malloc: %i", mnit_opengles_error_code);
105 }
106
107 glGenTextures(1, &image->texture);
108
109 if ((mnit_opengles_error_code = glGetError()) != GL_NO_ERROR) {
110 PRINT_ERROR("error loading image after glGenTextures: %i", mnit_opengles_error_code);
111 }
112
113 glBindTexture(GL_TEXTURE_2D, image->texture);
114
115 if ((mnit_opengles_error_code = glGetError()) != GL_NO_ERROR) {
116 PRINT_ERROR("error loading image glBindTexture: %i", mnit_opengles_error_code);
117 }
118
119 glTexImage2D( GL_TEXTURE_2D, 0, format, width_pow2, height_pow2,
120 0, format, GL_UNSIGNED_BYTE, (GLvoid*)pixels);
121
122 if ((mnit_opengles_error_code = glGetError()) != GL_NO_ERROR) {
123 PRINT_ERROR("error loading image after glTexImage2D: %i", mnit_opengles_error_code);
124 }
125
126 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
127 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
128
129 if ((mnit_opengles_error_code = glGetError()) != GL_NO_ERROR) {
130 PRINT_ERROR("error loading image after gtTexParameter: %i", mnit_opengles_error_code);
131 }
132
133 return image;
134 }
135 `}
136
137 # OpenGL ES1 display
138 # Uses 3d hardware optimization
139 class Opengles1Display
140 super Display
141
142 redef type I: Opengles1Image
143
144 init do extern_init
145 fun midway_init( format: Int ) do end
146 fun extern_init: Bool is extern import midway_init `{
147 /* initialize OpenGL ES and EGL */
148 const EGLint attribs[] = {
149 EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
150 EGL_BLUE_SIZE, 8,
151 EGL_GREEN_SIZE, 8,
152 EGL_RED_SIZE, 8,
153 EGL_NONE
154 };
155 EGLint w, h, dummy, format;
156 EGLint numConfigs;
157 EGLConfig config;
158 EGLSurface surface;
159 EGLContext context;
160
161 EGLDisplay display = eglGetDisplay(mnit_native_display);
162 if ( display == EGL_NO_DISPLAY) {
163 PRINT_ERROR("Unable to eglGetDisplay");
164 return -1;
165 }
166
167 if ( eglInitialize(display, 0, 0) == EGL_FALSE) {
168 PRINT_ERROR("Unable to eglInitialize");
169 return -1;
170 }
171
172 if ( eglChooseConfig(display, attribs, &config, 1, &numConfigs) == EGL_FALSE) {
173 PRINT_ERROR("Unable to eglChooseConfig");
174 return -1;
175 }
176
177 if ( numConfigs == 0 ) {
178 PRINT_ERROR("No configs available for egl");
179 return -1;
180 }
181
182 if ( eglGetConfigAttrib(display, config, EGL_NATIVE_VISUAL_ID, &format) == EGL_FALSE) {
183 PRINT_ERROR("Unable to eglGetConfigAttrib");
184 return -1;
185 }
186
187 /* Used by Android to set buffer geometry */
188 Opengles1Display_midway_init(recv, format);
189
190 surface = eglCreateWindowSurface(display, config, mnit_window, NULL);
191 context = eglCreateContext(display, config, NULL, NULL);
192
193 if (eglMakeCurrent(display, surface, surface, context) == EGL_FALSE) {
194 PRINT_ERROR("Unable to eglMakeCurrent");
195 return -1;
196 }
197
198 eglQuerySurface(display, surface, EGL_WIDTH, &w);
199 eglQuerySurface(display, surface, EGL_HEIGHT, &h);
200
201 mnit_display = display;
202 mnit_context = context;
203 mnit_surface = surface;
204 mnit_config = config;
205 mnit_width = w;
206 mnit_height = h;
207
208 glViewport(0, 0, mnit_width, mnit_height);
209 glMatrixMode(GL_PROJECTION);
210 glLoadIdentity();
211 glOrthof(0.0f, w, h, 0.0f, 0.0f, 1.0f);
212 glMatrixMode(GL_MODELVIEW);
213
214 glFrontFace( GL_CW );
215
216 return 0;
217 `}
218
219 fun close is extern `{
220 if ( mnit_display != EGL_NO_DISPLAY) {
221 eglMakeCurrent( mnit_display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
222 if ( mnit_context != EGL_NO_CONTEXT) {
223 eglDestroyContext( mnit_display, mnit_context );
224 }
225 if ( mnit_surface != EGL_NO_SURFACE) {
226 eglDestroySurface( mnit_display, mnit_surface );
227 }
228 eglTerminate( mnit_display);
229 }
230 mnit_display = EGL_NO_DISPLAY;
231 mnit_context = EGL_NO_CONTEXT;
232 mnit_surface = EGL_NO_SURFACE;
233 `}
234
235 redef fun begin is extern `{
236 glClear(GL_COLOR_BUFFER_BIT);
237 glLoadIdentity();
238 `}
239
240 redef fun width: Int is extern `{
241 return mnit_width;
242 `}
243 redef fun height: Int is extern `{
244 return mnit_height;
245 `}
246
247 redef fun finish is extern `{
248 eglSwapBuffers( mnit_display, mnit_surface );
249 `}
250
251 redef fun set_viewport( x, y, w, h ) is extern `{
252 glLoadIdentity();
253 glViewport(0,0, mnit_width, mnit_height );
254 glMatrixMode(GL_PROJECTION);
255 glLoadIdentity();
256 glOrthof(x, x+w, y+h, y, 0.0f, 1.0f);
257 glMatrixMode(GL_MODELVIEW);
258 glFrontFace( GL_CW );
259 `}
260
261 redef fun blit(image, x, y) do native_blit(image, x.to_f, y.to_f)
262
263 private fun native_blit(image: Opengles1Image, x, y: Float) `{
264 GLfloat texture_coord[4][2] =
265 {
266 {image->src_xo, image->src_yi},
267 {image->src_xi, image->src_yi},
268 {image->src_xo, image->src_yo},
269 {image->src_xi, image->src_yo}
270 };
271
272 glLoadIdentity();
273
274 glBindTexture(GL_TEXTURE_2D, image->texture);
275
276 glEnableClientState(GL_VERTEX_ARRAY);
277 glEnableClientState(GL_TEXTURE_COORD_ARRAY);
278 glTranslatef( x, y, 0.0f );
279 glScalef( image->width*image->scale, image->height*image->scale, 1.0f );
280
281 if ( image->blended ) {
282 glEnable(GL_BLEND);
283 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
284 }
285
286 glEnable(GL_TEXTURE_2D);
287 glDisable(GL_DEPTH_TEST);
288
289 glVertexPointer(3, GL_FLOAT, 0, mnit_opengles_vertices);
290 glTexCoordPointer(2, GL_FLOAT, 0, texture_coord );
291
292 glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
293
294 glDisableClientState(GL_VERTEX_ARRAY);
295 glDisableClientState(GL_TEXTURE_COORD_ARRAY);
296 if ( image->blended ) glDisable(GL_BLEND);
297 glDisable(GL_TEXTURE_2D);
298
299 if ((mnit_opengles_error_code = glGetError()) != GL_NO_ERROR) {
300 PRINT_ERROR("error drawing: %i", mnit_opengles_error_code);
301 }
302 `}
303
304 redef fun blit_centered(img, x, y)
305 do
306 x = x.sub(img.center_x)
307 y = y.sub(img.center_y)
308 blit(img, x, y)
309 end
310
311 redef fun blit_rotated(image, x, y, angle) do native_blit_rotated(image, x.to_f, y.to_f, angle)
312
313 private fun native_blit_rotated(image: Opengles1Image, x, y, angle: Float) `{
314 GLfloat texture_coord[4][2] =
315 {
316 {image->src_xo, image->src_yi},
317 {image->src_xi, image->src_yi},
318 {image->src_xo, image->src_yo},
319 {image->src_xi, image->src_yo}
320 };
321
322 glLoadIdentity();
323
324 glBindTexture(GL_TEXTURE_2D, image->texture);
325
326 glEnableClientState(GL_VERTEX_ARRAY);
327 glEnableClientState(GL_TEXTURE_COORD_ARRAY);
328 glTranslatef( x, y, 0.0f );
329 glRotatef( angle*180.0f/3.14156f, 0, 0, 1.0f );
330 glTranslatef( image->width*image->scale/-2, image->height*image->scale/-2, 0.0f );
331 glScalef( image->width*image->scale, image->height*image->scale, 1.0f );
332 if ( image->blended ) {
333 glEnable(GL_BLEND);
334 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
335 }
336 glEnable(GL_TEXTURE_2D);
337 glDisable(GL_DEPTH_TEST);
338
339 glVertexPointer(3, GL_FLOAT, 0, mnit_opengles_vertices);
340 glTexCoordPointer(2, GL_FLOAT, 0, texture_coord );
341
342 glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
343
344 glDisableClientState(GL_VERTEX_ARRAY);
345 glDisableClientState(GL_TEXTURE_COORD_ARRAY);
346 if ( image->blended ) glDisable(GL_BLEND);
347 glDisable(GL_TEXTURE_2D);
348
349 if ((mnit_opengles_error_code = glGetError()) != GL_NO_ERROR) {
350 PRINT_ERROR("error drawing: %i", mnit_opengles_error_code);
351 }
352 `}
353
354 # a = top left, b = bottom left, c = bottom right, d = top right
355 redef fun blit_stretched(image, ax, ay, bx, by, cx, cy, dx, dy)
356 do
357 native_blit_stretched(image,
358 ax.to_f, ay.to_f, bx.to_f, by.to_f,
359 cx.to_f, cy.to_f, dx.to_f, dy.to_f)
360 end
361
362 private fun native_blit_stretched(image: I, ax, ay, bx, by, cx, cy, dx, dy: Float) `{
363 GLfloat texture_coord[4][2] =
364 {
365 {image->src_xo, image->src_yi},
366 {image->src_xi, image->src_yi},
367 {image->src_xo, image->src_yo},
368 {image->src_xi, image->src_yo}
369 };
370
371 GLfloat mnit_opengles_vertices_stretched[6][3] =
372 {
373 {bx, by, 0.0f},
374 {cx, cy, 0.0f},
375 {ax, ay, 0.0f},
376 {dx, dy, 0.0f},
377 };
378
379 glLoadIdentity();
380
381 glBindTexture(GL_TEXTURE_2D, image->texture);
382
383 glEnableClientState(GL_VERTEX_ARRAY);
384 glEnableClientState(GL_TEXTURE_COORD_ARRAY);
385
386 if ( image->blended ) {
387 glEnable(GL_BLEND);
388 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
389 }
390
391 glEnable(GL_TEXTURE_2D);
392 glDisable(GL_DEPTH_TEST);
393
394 glVertexPointer(3, GL_FLOAT, 0, mnit_opengles_vertices_stretched);
395 glTexCoordPointer(2, GL_FLOAT, 0, texture_coord );
396
397 glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
398
399 glDisableClientState(GL_VERTEX_ARRAY);
400 glDisableClientState(GL_TEXTURE_COORD_ARRAY);
401 if ( image->blended ) glDisable(GL_BLEND);
402 glDisable(GL_TEXTURE_2D);
403
404 if ((mnit_opengles_error_code = glGetError()) != GL_NO_ERROR) {
405 PRINT_ERROR("error drawing: %i", mnit_opengles_error_code);
406 }
407 `}
408
409 redef fun clear( r, g, b: Float ) is extern `{
410 glClearColor( r, g, b, 1.0 );
411 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
412 `}
413
414 fun clear_alpha( r, g, b, a: Float ) is extern `{
415 glClearColor( r, g, b, a );
416 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
417 `}
418
419 # Set the current color applied to all drawing
420 #
421 # require: r, g, b, a in [0.0 .. 1.0]
422 fun color(r, g, b, a: Float) `{ glColor4f(r, g, b, a); `}
423
424 # Reset the current color to opaque white
425 fun reset_color `{ glColor4f(1.0f, 1.0f, 1.0f, 1.0f); `}
426 end
427
428 extern class Opengles1Image in "C" `{struct mnit_opengles_Texture *`}
429 super Image
430
431 redef fun destroy is extern `{ free( recv ); `}
432
433 redef fun width: Int is extern `{ return recv->width; `}
434 redef fun height: Int is extern `{ return recv->height; `}
435
436 fun center_x: Int `{ return recv->center_x; `}
437 fun center_y: Int `{ return recv->center_y; `}
438
439 redef fun scale=( v: Float ) is extern `{
440 recv->scale = v;
441 recv->center_x = v*recv->width/2;
442 recv->center_y = v*recv->height/2;
443 `}
444 redef fun scale: Float is extern `{ return recv->scale; `}
445
446 redef fun blended=( v: Bool ) is extern `{ recv->blended = v; `}
447 redef fun blended: Bool is extern `{ return recv->blended; `}
448
449 # inherits scale and blend from source
450 redef fun subimage( x, y, w, h: Int ): Image is extern import Opengles1Image.as( Image ) `{
451 struct mnit_opengles_Texture* image =
452 malloc( sizeof( struct mnit_opengles_Texture ) );
453
454 image->texture = recv->texture;
455 image->width = w;
456 image->height = h;
457 image->center_x = recv->scale*w/2;
458 image->center_y = recv->scale*h/2;
459 image->scale = recv->scale;
460 image->blended = recv->blended;
461
462 float r_dx = recv->src_xi - recv->src_xo;
463 float r_dy = recv->src_yi - recv->src_yo;
464 image->src_xo = recv->src_xo + ((float)x)/recv->width*r_dx;
465 image->src_yo = recv->src_yo + ((float)y)/recv->height*r_dy;
466 image->src_xi = recv->src_xo + ((float)x+w)/recv->width*r_dx;
467 image->src_yi = recv->src_yo + ((float)y+h)/recv->height*r_dy;
468
469 return Opengles1Image_as_Image( image );
470 `}
471 end