1 /*         ______   ___    ___
2  *        /\  _  \ /\_ \  /\_ \
3  *        \ \ \L\ \\//\ \ \//\ \      __     __   _ __   ___
4  *         \ \  __ \ \ \ \  \ \ \   /'__`\ /'_ `\/\`'__\/ __`\
5  *          \ \ \/\ \ \_\ \_ \_\ \_/\  __//\ \L\ \ \ \//\ \L\ \
6  *           \ \_\ \_\/\____\/\____\ \____\ \____ \ \_\\ \____/
7  *            \/_/\/_/\/____/\/____/\/____/\/___L\ \/_/ \/___/
8  *                                           /\____/
9  *                                           \_/__/
10  *
11  *      OpenGL bitmap locking.
12  *
13  *      See LICENSE.txt for copyright information.
14  */
15 
16 #include "allegro5/allegro.h"
17 #include "allegro5/allegro_opengl.h"
18 #include "allegro5/internal/aintern.h"
19 #include "allegro5/internal/aintern_opengl.h"
20 #include "allegro5/internal/aintern_pixels.h"
21 
22 /*
23  * This is an attempt to refactor ogl_lock_region and ogl_unlock_region.
24  * To begin with it only supports desktop OpenGL.  Support for mobile platforms
25  * should be migrated here gradually, but PLEASE try not to do it by inserting
26  * #ifdefs everywhere.  Combined with huge functions, that made the previous
27  * version very hard to follow and prone to break.
28  */
29 #if !defined(ALLEGRO_CFG_OPENGLES)
30 
31 ALLEGRO_DEBUG_CHANNEL("opengl")
32 
33 #define get_glformat(f, c) _al_ogl_get_glformat((f), (c))
34 
35 
36 /*
37  * Helpers - duplicates code in ogl_bitmap.c for now
38  */
39 
ogl_pixel_alignment(int pixel_size)40 static int ogl_pixel_alignment(int pixel_size)
41 {
42    /* Valid alignments are: 1, 2, 4, 8 bytes. */
43    switch (pixel_size) {
44       case 1:
45       case 2:
46       case 4:
47       case 8:
48          return pixel_size;
49       case 3:
50          return 1;
51       case 16: /* float32 */
52          return 4;
53       default:
54          ASSERT(false);
55          return 4;
56    }
57 }
58 
ogl_pitch(int w,int pixel_size)59 static int ogl_pitch(int w, int pixel_size)
60 {
61    int pitch = w * pixel_size;
62    return pitch;
63 }
64 
exactly_15bpp(int pixel_format)65 static bool exactly_15bpp(int pixel_format)
66 {
67    return pixel_format == ALLEGRO_PIXEL_FORMAT_RGB_555
68       || pixel_format == ALLEGRO_PIXEL_FORMAT_BGR_555;
69 }
70 
71 
72 
73 /*
74  * Locking
75  */
76 
77 static bool ogl_lock_region_backbuffer(
78    ALLEGRO_BITMAP *bitmap, ALLEGRO_BITMAP_EXTRA_OPENGL *ogl_bitmap,
79    int x, int gl_y, int w, int h, int format, int flags);
80 static bool ogl_lock_region_nonbb_writeonly(
81    ALLEGRO_BITMAP *bitmap, ALLEGRO_BITMAP_EXTRA_OPENGL *ogl_bitmap,
82    int x, int gl_y, int w, int h, int format);
83 static bool ogl_lock_region_nonbb_readwrite(
84    ALLEGRO_BITMAP *bitmap, ALLEGRO_BITMAP_EXTRA_OPENGL *ogl_bitmap,
85    int x, int gl_y, int w, int h, int format, bool* restore_fbo);
86 static bool ogl_lock_region_nonbb_readwrite_fbo(
87    ALLEGRO_BITMAP *bitmap, ALLEGRO_BITMAP_EXTRA_OPENGL *ogl_bitmap,
88    int x, int gl_y, int w, int h, int format);
89 static bool ogl_lock_region_nonbb_readwrite_nonfbo(
90    ALLEGRO_BITMAP *bitmap, ALLEGRO_BITMAP_EXTRA_OPENGL *ogl_bitmap,
91    int x, int gl_y, int w, int h, int format);
92 
93 
_al_ogl_lock_region_new(ALLEGRO_BITMAP * bitmap,int x,int y,int w,int h,int format,int flags)94 ALLEGRO_LOCKED_REGION *_al_ogl_lock_region_new(ALLEGRO_BITMAP *bitmap,
95    int x, int y, int w, int h, int format, int flags)
96 {
97    ALLEGRO_BITMAP_EXTRA_OPENGL * const ogl_bitmap = bitmap->extra;
98    const GLint gl_y = bitmap->h - y - h;
99    ALLEGRO_DISPLAY *disp;
100    ALLEGRO_DISPLAY *old_disp = NULL;
101    ALLEGRO_BITMAP *old_target = al_get_target_bitmap();
102    GLenum e;
103    bool ok;
104    bool restore_fbo = false;
105    bool reset_alignment = false;
106 
107    if (format == ALLEGRO_PIXEL_FORMAT_ANY) {
108       /* Never pick compressed formats with ANY, as it interacts weirdly with
109        * existing code (e.g. al_get_pixel_size() etc) */
110       int bitmap_format = al_get_bitmap_format(bitmap);
111       if (_al_pixel_format_is_compressed(bitmap_format)) {
112          // XXX Get a good format from the driver?
113          format = ALLEGRO_PIXEL_FORMAT_ABGR_8888_LE;
114       }
115       else {
116          format = bitmap_format;
117       }
118    }
119 
120    disp = al_get_current_display();
121    format = _al_get_real_pixel_format(disp, format);
122 
123    /* Change OpenGL context if necessary. */
124    if (!disp ||
125       (_al_get_bitmap_display(bitmap)->ogl_extras->is_shared == false &&
126        _al_get_bitmap_display(bitmap) != disp))
127    {
128       old_disp = disp;
129       _al_set_current_display_only(_al_get_bitmap_display(bitmap));
130    }
131 
132    ok = true;
133 
134    /* Set up the pixel store state.  We will need to match it when unlocking.
135     * There may be other pixel store state we should be setting.
136     * See also pitfalls 7 & 8 from:
137     * http://www.opengl.org/resources/features/KilgardTechniques/oglpitfall/
138     */
139    int previous_alignment;
140    glGetIntegerv(GL_PACK_ALIGNMENT, &previous_alignment);
141    {
142       const int pixel_size = al_get_pixel_size(format);
143       const int pixel_alignment = ogl_pixel_alignment(pixel_size);
144       if (previous_alignment != pixel_alignment) {
145          reset_alignment = true;
146          glPixelStorei(GL_PACK_ALIGNMENT, pixel_alignment);
147          e = glGetError();
148          if (e) {
149             ALLEGRO_ERROR("glPixelStorei(GL_PACK_ALIGNMENT, %d) failed (%s).\n",
150                pixel_alignment, _al_gl_error_string(e));
151             ok = false;
152          }
153       }
154    }
155 
156    if (ok) {
157       if (ogl_bitmap->is_backbuffer) {
158          ALLEGRO_DEBUG("Locking backbuffer\n");
159          ok = ogl_lock_region_backbuffer(bitmap, ogl_bitmap,
160             x, gl_y, w, h, format, flags);
161       }
162       else if (flags & ALLEGRO_LOCK_WRITEONLY) {
163          ALLEGRO_DEBUG("Locking non-backbuffer WRITEONLY\n");
164          ok = ogl_lock_region_nonbb_writeonly(bitmap, ogl_bitmap,
165             x, gl_y, w, h, format);
166       }
167       else {
168          ALLEGRO_DEBUG("Locking non-backbuffer READWRITE\n");
169          ok = ogl_lock_region_nonbb_readwrite(bitmap, ogl_bitmap,
170             x, gl_y, w, h, format, &restore_fbo);
171       }
172    }
173 
174    if (reset_alignment) {
175       glPixelStorei(GL_PACK_ALIGNMENT, previous_alignment);
176    }
177 
178    /* Restore state after switching FBO. */
179    if (restore_fbo) {
180       if (!old_target) {
181          /* Old target was NULL; release the context. */
182          _al_set_current_display_only(NULL);
183       }
184       else if (!_al_get_bitmap_display(old_target)) {
185          /* Old target was memory bitmap; leave the current display alone. */
186       }
187       else if (old_target != bitmap) {
188          /* Old target was another OpenGL bitmap. */
189          _al_ogl_setup_fbo(_al_get_bitmap_display(old_target), old_target);
190       }
191    }
192 
193    ASSERT(al_get_target_bitmap() == old_target);
194 
195    if (old_disp != NULL) {
196       _al_set_current_display_only(old_disp);
197    }
198 
199    if (ok) {
200       return &bitmap->locked_region;
201    }
202 
203    ALLEGRO_ERROR("Failed to lock region\n");
204    ASSERT(ogl_bitmap->lock_buffer == NULL);
205    return NULL;
206 }
207 
208 
ogl_lock_region_backbuffer(ALLEGRO_BITMAP * bitmap,ALLEGRO_BITMAP_EXTRA_OPENGL * ogl_bitmap,int x,int gl_y,int w,int h,int format,int flags)209 static bool ogl_lock_region_backbuffer(
210    ALLEGRO_BITMAP *bitmap, ALLEGRO_BITMAP_EXTRA_OPENGL *ogl_bitmap,
211    int x, int gl_y, int w, int h, int format, int flags)
212 {
213    const int pixel_size = al_get_pixel_size(format);
214    const int pitch = ogl_pitch(w, pixel_size);
215    GLenum e;
216 
217    ogl_bitmap->lock_buffer = al_malloc(pitch * h);
218    if (ogl_bitmap->lock_buffer == NULL) {
219       return false;
220    }
221 
222    if (!(flags & ALLEGRO_LOCK_WRITEONLY)) {
223       glReadPixels(x, gl_y, w, h,
224          get_glformat(format, 2),
225          get_glformat(format, 1),
226          ogl_bitmap->lock_buffer);
227       e = glGetError();
228       if (e) {
229          ALLEGRO_ERROR("glReadPixels for format %s failed (%s).\n",
230             _al_pixel_format_name(format), _al_gl_error_string(e));
231          al_free(ogl_bitmap->lock_buffer);
232          ogl_bitmap->lock_buffer = NULL;
233          return false;
234       }
235    }
236 
237    bitmap->locked_region.data = ogl_bitmap->lock_buffer + pitch * (h - 1);
238    bitmap->locked_region.format = format;
239    bitmap->locked_region.pitch = -pitch;
240    bitmap->locked_region.pixel_size = pixel_size;
241    return true;
242 }
243 
244 
ogl_lock_region_nonbb_writeonly(ALLEGRO_BITMAP * bitmap,ALLEGRO_BITMAP_EXTRA_OPENGL * ogl_bitmap,int x,int gl_y,int w,int h,int format)245 static bool ogl_lock_region_nonbb_writeonly(
246    ALLEGRO_BITMAP *bitmap, ALLEGRO_BITMAP_EXTRA_OPENGL *ogl_bitmap,
247    int x, int gl_y, int w, int h, int format)
248 {
249    const int pixel_size = al_get_pixel_size(format);
250    const int pitch = ogl_pitch(w, pixel_size);
251    (void) x;
252    (void) gl_y;
253 
254    ogl_bitmap->lock_buffer = al_malloc(pitch * h);
255    if (ogl_bitmap->lock_buffer == NULL) {
256       return false;
257    }
258 
259    bitmap->locked_region.data = ogl_bitmap->lock_buffer + pitch * (h - 1);
260    bitmap->locked_region.format = format;
261    bitmap->locked_region.pitch = -pitch;
262    bitmap->locked_region.pixel_size = pixel_size;
263    return true;
264 }
265 
266 
ogl_lock_region_nonbb_readwrite(ALLEGRO_BITMAP * bitmap,ALLEGRO_BITMAP_EXTRA_OPENGL * ogl_bitmap,int x,int gl_y,int w,int h,int format,bool * restore_fbo)267 static bool ogl_lock_region_nonbb_readwrite(
268    ALLEGRO_BITMAP *bitmap, ALLEGRO_BITMAP_EXTRA_OPENGL *ogl_bitmap,
269    int x, int gl_y, int w, int h, int format, bool* restore_fbo)
270 {
271    bool ok;
272 
273    ASSERT(bitmap->parent == NULL);
274    ASSERT(bitmap->locked == false);
275    ASSERT(_al_get_bitmap_display(bitmap) == al_get_current_display());
276 
277    /* Try to create an FBO if there isn't one. */
278    *restore_fbo =
279       _al_ogl_setup_fbo_non_backbuffer(_al_get_bitmap_display(bitmap), bitmap);
280 
281    if (ogl_bitmap->fbo_info) {
282       ALLEGRO_DEBUG("Locking non-backbuffer READWRITE with fbo\n");
283       ok = ogl_lock_region_nonbb_readwrite_fbo(bitmap, ogl_bitmap,
284          x, gl_y, w, h, format);
285    }
286    else {
287       ALLEGRO_DEBUG("Locking non-backbuffer READWRITE no fbo\n");
288       ok = ogl_lock_region_nonbb_readwrite_nonfbo(bitmap, ogl_bitmap,
289          x, gl_y, w, h, format);
290    }
291 
292    return ok;
293 }
294 
295 
ogl_lock_region_nonbb_readwrite_fbo(ALLEGRO_BITMAP * bitmap,ALLEGRO_BITMAP_EXTRA_OPENGL * ogl_bitmap,int x,int gl_y,int w,int h,int format)296 static bool ogl_lock_region_nonbb_readwrite_fbo(
297    ALLEGRO_BITMAP *bitmap, ALLEGRO_BITMAP_EXTRA_OPENGL *ogl_bitmap,
298    int x, int gl_y, int w, int h, int format)
299 {
300    const int pixel_size = al_get_pixel_size(format);
301    const int pitch = ogl_pitch(w, pixel_size);
302    GLint old_fbo;
303    GLenum e;
304    bool ok;
305 
306    glGetIntegerv(GL_FRAMEBUFFER_BINDING_EXT, &old_fbo);
307    e = glGetError();
308    if (e) {
309       ALLEGRO_ERROR("glGetIntegerv(GL_FRAMEBUFFER_BINDING_EXT) failed (%s).\n",
310          _al_gl_error_string(e));
311       return false;
312    }
313 
314    ok = true;
315 
316    glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, ogl_bitmap->fbo_info->fbo);
317    e = glGetError();
318    if (e) {
319       ALLEGRO_ERROR("glBindFramebufferEXT failed (%s).\n",
320          _al_gl_error_string(e));
321       ok = false;
322    }
323 
324    if (ok) {
325       ogl_bitmap->lock_buffer = al_malloc(pitch * h);
326       if (ogl_bitmap->lock_buffer == NULL) {
327          ok = false;
328       }
329    }
330 
331    if (ok) {
332       glReadPixels(x, gl_y, w, h,
333          get_glformat(format, 2),
334          get_glformat(format, 1),
335          ogl_bitmap->lock_buffer);
336       e = glGetError();
337       if (e) {
338          ALLEGRO_ERROR("glReadPixels for format %s failed (%s).\n",
339             _al_pixel_format_name(format), _al_gl_error_string(e));
340       }
341    }
342 
343    glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, old_fbo);
344 
345    if (ok) {
346       bitmap->locked_region.data = ogl_bitmap->lock_buffer + pitch * (h - 1);
347       bitmap->locked_region.format = format;
348       bitmap->locked_region.pitch = -pitch;
349       bitmap->locked_region.pixel_size = pixel_size;
350       return true;
351    }
352 
353    al_free(ogl_bitmap->lock_buffer);
354    ogl_bitmap->lock_buffer = NULL;
355    return ok;
356 }
357 
358 
ogl_lock_region_nonbb_readwrite_nonfbo(ALLEGRO_BITMAP * bitmap,ALLEGRO_BITMAP_EXTRA_OPENGL * ogl_bitmap,int x,int gl_y,int w,int h,int format)359 static bool ogl_lock_region_nonbb_readwrite_nonfbo(
360    ALLEGRO_BITMAP *bitmap, ALLEGRO_BITMAP_EXTRA_OPENGL *ogl_bitmap,
361    int x, int gl_y, int w, int h, int format)
362 {
363    /* No FBO - fallback to reading the entire texture */
364    const int pixel_size = al_get_pixel_size(format);
365    const int pitch = ogl_pitch(ogl_bitmap->true_w, pixel_size);
366    GLenum e;
367    bool ok;
368    (void) w;
369 
370    ogl_bitmap->lock_buffer = al_malloc(pitch * ogl_bitmap->true_h);
371    if (ogl_bitmap->lock_buffer == NULL) {
372       return false;
373    }
374 
375    ok = true;
376 
377    glBindTexture(GL_TEXTURE_2D, ogl_bitmap->texture);
378    glGetTexImage(GL_TEXTURE_2D, 0,
379       get_glformat(format, 2),
380       get_glformat(format, 1),
381       ogl_bitmap->lock_buffer);
382 
383    e = glGetError();
384    if (e) {
385       ALLEGRO_ERROR("glGetTexImage for format %s failed (%s).\n",
386          _al_pixel_format_name(format), _al_gl_error_string(e));
387       al_free(ogl_bitmap->lock_buffer);
388       ogl_bitmap->lock_buffer = NULL;
389       ok = false;
390    }
391 
392    if (ok) {
393       bitmap->locked_region.data = ogl_bitmap->lock_buffer +
394          pitch * (gl_y + h - 1) + pixel_size * x;
395       bitmap->locked_region.format = format;
396       bitmap->locked_region.pitch = -pitch;
397       bitmap->locked_region.pixel_size = pixel_size;
398    }
399 
400    return ok;
401 }
402 
403 
404 
405 /*
406  * Unlocking
407  */
408 
409 static void ogl_unlock_region_non_readonly(ALLEGRO_BITMAP *bitmap,
410    ALLEGRO_BITMAP_EXTRA_OPENGL *ogl_bitmap);
411 static void ogl_unlock_region_backbuffer(ALLEGRO_BITMAP *bitmap,
412    ALLEGRO_BITMAP_EXTRA_OPENGL *ogl_bitmap, int gl_y);
413 static void ogl_unlock_region_nonbb_fbo(ALLEGRO_BITMAP *bitmap,
414    ALLEGRO_BITMAP_EXTRA_OPENGL *ogl_bitmap, int gl_y, int orig_format);
415 static void ogl_unlock_region_nonbb_fbo_writeonly(ALLEGRO_BITMAP *bitmap,
416    ALLEGRO_BITMAP_EXTRA_OPENGL *ogl_bitmap, int gl_y, int orig_format);
417 static void ogl_unlock_region_nonbb_fbo_readwrite(ALLEGRO_BITMAP *bitmap,
418    ALLEGRO_BITMAP_EXTRA_OPENGL *ogl_bitmap, int gl_y);
419 static void ogl_unlock_region_nonbb_nonfbo(ALLEGRO_BITMAP *bitmap,
420    ALLEGRO_BITMAP_EXTRA_OPENGL *ogl_bitmap, int gl_y);
421 
422 
_al_ogl_unlock_region_new(ALLEGRO_BITMAP * bitmap)423 void _al_ogl_unlock_region_new(ALLEGRO_BITMAP *bitmap)
424 {
425    ALLEGRO_BITMAP_EXTRA_OPENGL *ogl_bitmap = bitmap->extra;
426 
427    if (bitmap->lock_flags & ALLEGRO_LOCK_READONLY) {
428       ALLEGRO_DEBUG("Unlocking non-backbuffer READONLY\n");
429    }
430    else {
431       ogl_unlock_region_non_readonly(bitmap, ogl_bitmap);
432    }
433 
434    al_free(ogl_bitmap->lock_buffer);
435    ogl_bitmap->lock_buffer = NULL;
436 }
437 
438 
ogl_unlock_region_non_readonly(ALLEGRO_BITMAP * bitmap,ALLEGRO_BITMAP_EXTRA_OPENGL * ogl_bitmap)439 static void ogl_unlock_region_non_readonly(ALLEGRO_BITMAP *bitmap,
440    ALLEGRO_BITMAP_EXTRA_OPENGL *ogl_bitmap)
441 {
442    const int lock_format = bitmap->locked_region.format;
443    const int gl_y = bitmap->h - bitmap->lock_y - bitmap->lock_h;
444    ALLEGRO_DISPLAY *old_disp = NULL;
445    ALLEGRO_DISPLAY *disp;
446    int orig_format;
447    bool biased_alpha = false;
448    bool reset_alignment = false;
449    GLenum e;
450 
451    disp = al_get_current_display();
452    orig_format = _al_get_real_pixel_format(disp, _al_get_bitmap_memory_format(bitmap));
453 
454    /* Change OpenGL context if necessary. */
455    if (!disp ||
456       (_al_get_bitmap_display(bitmap)->ogl_extras->is_shared == false &&
457        _al_get_bitmap_display(bitmap) != disp))
458    {
459       old_disp = disp;
460       _al_set_current_display_only(_al_get_bitmap_display(bitmap));
461    }
462 
463    /* Keep this in sync with ogl_lock_region. */
464    int previous_alignment;
465    glGetIntegerv(GL_UNPACK_ALIGNMENT, &previous_alignment);
466    {
467       const int lock_pixel_size = al_get_pixel_size(lock_format);
468       const int pixel_alignment = ogl_pixel_alignment(lock_pixel_size);
469       if (pixel_alignment != previous_alignment) {
470          reset_alignment = true;
471          glPixelStorei(GL_UNPACK_ALIGNMENT, pixel_alignment);
472          e = glGetError();
473          if (e) {
474             ALLEGRO_ERROR("glPixelStorei(GL_UNPACK_ALIGNMENT, %d) failed (%s).\n",
475                pixel_alignment, _al_gl_error_string(e));
476          }
477       }
478    }
479    if (exactly_15bpp(lock_format)) {
480       /* OpenGL does not support 15-bpp internal format without an alpha,
481        * so when storing such data we must ensure the alpha bit is set.
482        */
483       glPixelTransferi(GL_ALPHA_BIAS, 1);
484       biased_alpha = true;
485    }
486 
487    if (ogl_bitmap->is_backbuffer) {
488       ALLEGRO_DEBUG("Unlocking backbuffer\n");
489       ogl_unlock_region_backbuffer(bitmap, ogl_bitmap, gl_y);
490    }
491    else {
492       glBindTexture(GL_TEXTURE_2D, ogl_bitmap->texture);
493       if (ogl_bitmap->fbo_info) {
494          ALLEGRO_DEBUG("Unlocking non-backbuffer (FBO)\n");
495          ogl_unlock_region_nonbb_fbo(bitmap, ogl_bitmap, gl_y, orig_format);
496       }
497       else {
498          ALLEGRO_DEBUG("Unlocking non-backbuffer (non-FBO)\n");
499          ogl_unlock_region_nonbb_nonfbo(bitmap, ogl_bitmap, gl_y);
500       }
501 
502       /* If using FBOs, we need to regenerate mipmaps explicitly now. */
503       /* XXX why don't we check ogl_bitmap->fbo_info? */
504       if ((al_get_bitmap_flags(bitmap) & ALLEGRO_MIPMAP) &&
505          al_get_opengl_extension_list()->ALLEGRO_GL_EXT_framebuffer_object)
506       {
507          glGenerateMipmapEXT(GL_TEXTURE_2D);
508          e = glGetError();
509          if (e) {
510             ALLEGRO_ERROR("glGenerateMipmapEXT for texture %d failed (%s).\n",
511                ogl_bitmap->texture, _al_gl_error_string(e));
512          }
513       }
514    }
515 
516    if (biased_alpha) {
517       glPixelTransferi(GL_ALPHA_BIAS, 0);
518    }
519    if (reset_alignment) {
520       glPixelStorei(GL_UNPACK_ALIGNMENT, previous_alignment);
521    }
522 
523    if (old_disp) {
524       _al_set_current_display_only(old_disp);
525    }
526 }
527 
528 
ogl_unlock_region_backbuffer(ALLEGRO_BITMAP * bitmap,ALLEGRO_BITMAP_EXTRA_OPENGL * ogl_bitmap,int gl_y)529 static void ogl_unlock_region_backbuffer(ALLEGRO_BITMAP *bitmap,
530       ALLEGRO_BITMAP_EXTRA_OPENGL *ogl_bitmap, int gl_y)
531 {
532    const int lock_format = bitmap->locked_region.format;
533    bool popmatrix = false;
534    GLenum e;
535    GLint program = 0;
536    ALLEGRO_DISPLAY *display = al_get_current_display();
537 
538    if (display->flags & ALLEGRO_PROGRAMMABLE_PIPELINE) {
539       // FIXME: This is a hack where we temporarily disable the active shader.
540       // It will only work on Desktop OpenGL in non-strict mode where we even
541       // can switch back to the fixed pipeline. The correct way would be to not
542       // use any OpenGL 2 functions (like glDrawPixels). Probably we will want
543       // separate OpenGL <= 2 (including OpenGL ES 1) and OpenGL >= 3 (including
544       // OpenGL ES >= 2) drivers at some point.
545       glGetIntegerv(GL_CURRENT_PROGRAM, &program);
546       glUseProgram(0);
547    }
548 
549    /* glWindowPos2i may not be available. */
550    if (al_get_opengl_version() >= _ALLEGRO_OPENGL_VERSION_1_4) {
551       glWindowPos2i(bitmap->lock_x, gl_y);
552    }
553    else {
554       /* glRasterPos is affected by the current modelview and projection
555        * matrices (so maybe we actually need to reset both of them?).
556        * The coordinate is also clipped; the small offset was required to
557        * prevent it being culled on one of my machines. --pw
558        *
559        * Consider using glWindowPos2fMESAemulate from:
560        * http://www.opengl.org/resources/features/KilgardTechniques/oglpitfall/
561        */
562       glPushMatrix();
563       glLoadIdentity();
564       glRasterPos2f(bitmap->lock_x, bitmap->lock_y + bitmap->lock_h - 1e-4f);
565       popmatrix = true;
566    }
567 
568    glDisable(GL_TEXTURE_2D);
569    glDisable(GL_BLEND);
570    glDrawPixels(bitmap->lock_w, bitmap->lock_h,
571       get_glformat(lock_format, 2),
572       get_glformat(lock_format, 1),
573       ogl_bitmap->lock_buffer);
574    e = glGetError();
575    if (e) {
576       ALLEGRO_ERROR("glDrawPixels for format %s failed (%s).\n",
577          _al_pixel_format_name(lock_format), _al_gl_error_string(e));
578    }
579 
580    if (popmatrix) {
581       glPopMatrix();
582    }
583 
584    if (program != 0) {
585       glUseProgram(program);
586    }
587 }
588 
589 
ogl_unlock_region_nonbb_fbo(ALLEGRO_BITMAP * bitmap,ALLEGRO_BITMAP_EXTRA_OPENGL * ogl_bitmap,int gl_y,int orig_format)590 static void ogl_unlock_region_nonbb_fbo(ALLEGRO_BITMAP *bitmap,
591    ALLEGRO_BITMAP_EXTRA_OPENGL *ogl_bitmap, int gl_y, int orig_format)
592 {
593    if (bitmap->lock_flags & ALLEGRO_LOCK_WRITEONLY) {
594       ALLEGRO_DEBUG("Unlocking non-backbuffer FBO WRITEONLY\n");
595       ogl_unlock_region_nonbb_fbo_writeonly(bitmap, ogl_bitmap, gl_y,
596          orig_format);
597    }
598    else {
599       ALLEGRO_DEBUG("Unlocking non-backbuffer FBO READWRITE\n");
600       ogl_unlock_region_nonbb_fbo_readwrite(bitmap, ogl_bitmap, gl_y);
601    }
602 }
603 
604 
ogl_unlock_region_nonbb_fbo_writeonly(ALLEGRO_BITMAP * bitmap,ALLEGRO_BITMAP_EXTRA_OPENGL * ogl_bitmap,int gl_y,int orig_format)605 static void ogl_unlock_region_nonbb_fbo_writeonly(ALLEGRO_BITMAP *bitmap,
606    ALLEGRO_BITMAP_EXTRA_OPENGL *ogl_bitmap, int gl_y, int orig_format)
607 {
608    const int lock_format = bitmap->locked_region.format;
609    const int orig_pixel_size = al_get_pixel_size(orig_format);
610    const int dst_pitch = bitmap->lock_w * orig_pixel_size;
611    unsigned char * const tmpbuf = al_malloc(dst_pitch * bitmap->lock_h);
612    GLenum e;
613 
614    _al_convert_bitmap_data(
615       ogl_bitmap->lock_buffer,
616       bitmap->locked_region.format,
617       -bitmap->locked_region.pitch,
618       tmpbuf,
619       orig_format,
620       dst_pitch,
621       0, 0, 0, 0,
622       bitmap->lock_w, bitmap->lock_h);
623 
624    glTexSubImage2D(GL_TEXTURE_2D, 0,
625       bitmap->lock_x, gl_y,
626       bitmap->lock_w, bitmap->lock_h,
627       get_glformat(orig_format, 2),
628       get_glformat(orig_format, 1),
629       tmpbuf);
630    e = glGetError();
631    if (e) {
632       ALLEGRO_ERROR("glTexSubImage2D for format %d failed (%s).\n",
633          lock_format, _al_gl_error_string(e));
634    }
635 
636    al_free(tmpbuf);
637 }
638 
639 
ogl_unlock_region_nonbb_fbo_readwrite(ALLEGRO_BITMAP * bitmap,ALLEGRO_BITMAP_EXTRA_OPENGL * ogl_bitmap,int gl_y)640 static void ogl_unlock_region_nonbb_fbo_readwrite(ALLEGRO_BITMAP *bitmap,
641    ALLEGRO_BITMAP_EXTRA_OPENGL *ogl_bitmap, int gl_y)
642 {
643    const int lock_format = bitmap->locked_region.format;
644    GLenum e;
645    GLint tex_internalformat;
646 
647    glTexSubImage2D(GL_TEXTURE_2D, 0, bitmap->lock_x, gl_y,
648       bitmap->lock_w, bitmap->lock_h,
649       get_glformat(lock_format, 2),
650       get_glformat(lock_format, 1),
651       ogl_bitmap->lock_buffer);
652 
653    e = glGetError();
654    if (e) {
655       ALLEGRO_ERROR("glTexSubImage2D for format %s failed (%s).\n",
656          _al_pixel_format_name(lock_format), _al_gl_error_string(e));
657       glGetTexLevelParameteriv(GL_TEXTURE_2D, 0,
658          GL_TEXTURE_INTERNAL_FORMAT, &tex_internalformat);
659       ALLEGRO_DEBUG("x/y/w/h: %d/%d/%d/%d, internal format: %d\n",
660          bitmap->lock_x, gl_y, bitmap->lock_w, bitmap->lock_h,
661          tex_internalformat);
662    }
663 }
664 
665 
ogl_unlock_region_nonbb_nonfbo(ALLEGRO_BITMAP * bitmap,ALLEGRO_BITMAP_EXTRA_OPENGL * ogl_bitmap,int gl_y)666 static void ogl_unlock_region_nonbb_nonfbo(ALLEGRO_BITMAP *bitmap,
667    ALLEGRO_BITMAP_EXTRA_OPENGL *ogl_bitmap, int gl_y)
668 {
669    const int lock_format = bitmap->locked_region.format;
670    unsigned char *start_ptr;
671    GLenum e;
672 
673    if (bitmap->lock_flags & ALLEGRO_LOCK_WRITEONLY) {
674       ALLEGRO_DEBUG("Unlocking non-backbuffer non-FBO WRITEONLY\n");
675       start_ptr = ogl_bitmap->lock_buffer;
676    }
677    else {
678       ALLEGRO_DEBUG("Unlocking non-backbuffer non-FBO READWRITE\n");
679       glPixelStorei(GL_UNPACK_ROW_LENGTH, ogl_bitmap->true_w);
680       start_ptr = (unsigned char *)bitmap->lock_data
681             + (bitmap->lock_h - 1) * bitmap->locked_region.pitch;
682    }
683 
684    glTexSubImage2D(GL_TEXTURE_2D, 0,
685       bitmap->lock_x, gl_y,
686       bitmap->lock_w, bitmap->lock_h,
687       get_glformat(lock_format, 2),
688       get_glformat(lock_format, 1),
689       start_ptr);
690 
691    e = glGetError();
692    if (e) {
693       ALLEGRO_ERROR("glTexSubImage2D for format %s failed (%s).\n",
694          _al_pixel_format_name(lock_format), _al_gl_error_string(e));
695    }
696 }
697 
698 
699 #endif
700 
701 /* vim: set sts=3 sw=3 et: */
702