1 /*         ______   ___    ___
2  *        /\  _  \ /\_ \  /\_ \
3  *        \ \ \L\ \\//\ \ \//\ \      __     __   _ __   ___
4  *         \ \  __ \ \ \ \  \ \ \   /'__`\ /'_ `\/\`'__\/ __`\
5  *          \ \ \/\ \ \_\ \_ \_\ \_/\  __//\ \L\ \ \ \//\ \L\ \
6  *           \ \_\ \_\/\____\/\____\ \____\ \____ \ \_\\ \____/
7  *            \/_/\/_/\/____/\/____/\/____/\/___L\ \/_/ \/___/
8  *                                           /\____/
9  *                                           \_/__/
10  *
11  *      Convert between memory and video bitmap types.
12  *
13  *      See LICENSE.txt for copyright information.
14  */
15 
16 
17 #include "allegro5/allegro.h"
18 #include "allegro5/internal/aintern.h"
19 #include "allegro5/internal/aintern_bitmap.h"
20 #include "allegro5/internal/aintern_exitfunc.h"
21 #include "allegro5/internal/aintern_shader.h"
22 #include "allegro5/internal/aintern_system.h"
23 #include "allegro5/internal/aintern_vector.h"
24 
25 ALLEGRO_DEBUG_CHANNEL("bitmap")
26 
27 
28 /* Global list of MEMORY bitmaps with ALLEGRO_CONVERT_BITMAP flag. */
29 struct BITMAP_CONVERSION_LIST {
30    ALLEGRO_MUTEX *mutex;
31    _AL_VECTOR bitmaps;
32 };
33 
34 
35 static struct BITMAP_CONVERSION_LIST convert_bitmap_list;
36 
37 
cleanup_convert_bitmap_list(void)38 static void cleanup_convert_bitmap_list(void)
39 {
40    _al_vector_free(&convert_bitmap_list.bitmaps);
41    al_destroy_mutex(convert_bitmap_list.mutex);
42 }
43 
44 
45 /* This is called in al_install_system. Exit functions are called in
46  * al_uninstall_system.
47  */
_al_init_convert_bitmap_list(void)48 void _al_init_convert_bitmap_list(void)
49 {
50    convert_bitmap_list.mutex = al_create_mutex_recursive();
51    _al_vector_init(&convert_bitmap_list.bitmaps, sizeof(ALLEGRO_BITMAP *));
52    _al_add_exit_func(cleanup_convert_bitmap_list,
53       "cleanup_convert_bitmap_list");
54 }
55 
56 
_al_register_convert_bitmap(ALLEGRO_BITMAP * bitmap)57 void _al_register_convert_bitmap(ALLEGRO_BITMAP *bitmap)
58 {
59    int bitmap_flags = al_get_bitmap_flags(bitmap);
60    if (!(bitmap_flags & ALLEGRO_MEMORY_BITMAP))
61       return;
62    if (bitmap_flags & ALLEGRO_CONVERT_BITMAP) {
63       ALLEGRO_BITMAP **back;
64       al_lock_mutex(convert_bitmap_list.mutex);
65       back = _al_vector_alloc_back(&convert_bitmap_list.bitmaps);
66       *back = bitmap;
67       al_unlock_mutex(convert_bitmap_list.mutex);
68    }
69 }
70 
71 
_al_unregister_convert_bitmap(ALLEGRO_BITMAP * bitmap)72 void _al_unregister_convert_bitmap(ALLEGRO_BITMAP *bitmap)
73 {
74    int bitmap_flags = al_get_bitmap_flags(bitmap);
75    if (!(bitmap_flags & ALLEGRO_MEMORY_BITMAP))
76       return;
77    if (bitmap_flags & ALLEGRO_CONVERT_BITMAP) {
78       al_lock_mutex(convert_bitmap_list.mutex);
79       _al_vector_find_and_delete(&convert_bitmap_list.bitmaps, &bitmap);
80       al_unlock_mutex(convert_bitmap_list.mutex);
81    }
82 }
83 
84 
swap_bitmaps(ALLEGRO_BITMAP * bitmap,ALLEGRO_BITMAP * other)85 static void swap_bitmaps(ALLEGRO_BITMAP *bitmap, ALLEGRO_BITMAP *other)
86 {
87    ALLEGRO_BITMAP temp;
88    _AL_LIST_ITEM *bitmap_dtor_item = bitmap->dtor_item;
89    _AL_LIST_ITEM *other_dtor_item = other->dtor_item;
90    ALLEGRO_DISPLAY *bitmap_display, *other_display;
91 
92    _al_unregister_convert_bitmap(bitmap);
93    _al_unregister_convert_bitmap(other);
94 
95    if (other->shader)
96       _al_unregister_shader_bitmap(other->shader, other);
97    if (bitmap->shader)
98       _al_unregister_shader_bitmap(bitmap->shader, bitmap);
99 
100    temp = *bitmap;
101    *bitmap = *other;
102    *other = temp;
103 
104    /* Re-associate the destructors back, as they are tied to the object
105     * pointers.
106     */
107    bitmap->dtor_item = bitmap_dtor_item;
108    other->dtor_item = other_dtor_item;
109 
110    bitmap_display = _al_get_bitmap_display(bitmap);
111    other_display = _al_get_bitmap_display(other);
112 
113    /* We are basically done already. Except we now have to update everything
114     * possibly referencing any of the two bitmaps.
115     */
116 
117    if (bitmap_display && !other_display) {
118       /* This means before the swap, other was the display bitmap, and we
119        * now should replace it with the swapped pointer.
120        */
121       ALLEGRO_BITMAP **back;
122       int pos = _al_vector_find(&bitmap_display->bitmaps, &other);
123       ASSERT(pos >= 0);
124       back = _al_vector_ref(&bitmap_display->bitmaps, pos);
125       *back = bitmap;
126    }
127 
128    if (other_display && !bitmap_display) {
129       ALLEGRO_BITMAP **back;
130       int pos = _al_vector_find(&other_display->bitmaps, &bitmap);
131       ASSERT(pos >= 0);
132       back = _al_vector_ref(&other_display->bitmaps, pos);
133       *back = other;
134    }
135 
136    if (other->shader)
137       _al_register_shader_bitmap(other->shader, other);
138    if (bitmap->shader)
139       _al_register_shader_bitmap(bitmap->shader, bitmap);
140 
141    _al_register_convert_bitmap(bitmap);
142    _al_register_convert_bitmap(other);
143 
144    if (bitmap->vt && bitmap->vt->bitmap_pointer_changed)
145       bitmap->vt->bitmap_pointer_changed(bitmap, other);
146 
147    if (other->vt && other->vt->bitmap_pointer_changed)
148       other->vt->bitmap_pointer_changed(other, bitmap);
149 }
150 
151 
152 /* Function: al_convert_bitmap
153  */
al_convert_bitmap(ALLEGRO_BITMAP * bitmap)154 void al_convert_bitmap(ALLEGRO_BITMAP *bitmap)
155 {
156    ALLEGRO_BITMAP *clone;
157    int bitmap_flags = al_get_bitmap_flags(bitmap);
158    int new_bitmap_flags = al_get_new_bitmap_flags();
159    bool want_memory = (new_bitmap_flags & ALLEGRO_MEMORY_BITMAP) != 0;
160    bool clone_memory;
161    ALLEGRO_BITMAP *target_bitmap;
162 
163    bitmap_flags &= ~_ALLEGRO_INTERNAL_OPENGL;
164 
165    /* If a cloned bitmap would be identical, we can just do nothing. */
166    if (al_get_bitmap_format(bitmap) == al_get_new_bitmap_format() &&
167          bitmap_flags == new_bitmap_flags &&
168          _al_get_bitmap_display(bitmap) == al_get_current_display()) {
169       return;
170    }
171 
172    if (bitmap->parent) {
173       al_convert_bitmap(bitmap->parent);
174       return;
175    }
176    else {
177       clone = al_clone_bitmap(bitmap);
178    }
179 
180    if (!clone) {
181       return;
182    }
183 
184    clone_memory = (al_get_bitmap_flags(clone) & ALLEGRO_MEMORY_BITMAP) != 0;
185 
186    if (clone_memory != want_memory) {
187       /* We couldn't convert. */
188       al_destroy_bitmap(clone);
189       return;
190    }
191 
192    swap_bitmaps(bitmap, clone);
193 
194    /* Preserve bitmap state. */
195    bitmap->cl = clone->cl;
196    bitmap->ct = clone->ct;
197    bitmap->cr_excl = clone->cr_excl;
198    bitmap->cb_excl = clone->cb_excl;
199    bitmap->transform = clone->transform;
200    bitmap->inverse_transform = clone->inverse_transform;
201    bitmap->inverse_transform_dirty = clone->inverse_transform_dirty;
202 
203    /* Memory bitmaps do not support custom projection transforms,
204     * so reset it to the orthographic transform. */
205    if (new_bitmap_flags & ALLEGRO_MEMORY_BITMAP) {
206       al_identity_transform(&bitmap->proj_transform);
207       al_orthographic_transform(&bitmap->proj_transform, 0, 0, -1.0, bitmap->w, bitmap->h, 1.0);
208    } else {
209       bitmap->proj_transform = clone->proj_transform;
210    }
211 
212    /* If we just converted this bitmap, and the backing bitmap is the same
213     * as the target's backing bitmap, then the viewports and transformations
214     * will be messed up. Detect this, and just re-call al_set_target_bitmap
215     * on the current target. */
216    target_bitmap = al_get_target_bitmap();
217    if (target_bitmap) {
218       ALLEGRO_BITMAP *target_parent =
219          target_bitmap->parent ? target_bitmap->parent : target_bitmap;
220       if (bitmap == target_parent || bitmap->parent == target_parent) {
221          al_set_target_bitmap(target_bitmap);
222       }
223    }
224 
225    al_destroy_bitmap(clone);
226 }
227 
228 
229 /* Function: al_convert_memory_bitmaps
230  */
al_convert_memory_bitmaps(void)231 void al_convert_memory_bitmaps(void)
232 {
233    ALLEGRO_STATE backup;
234    ALLEGRO_DISPLAY *display = al_get_current_display();
235    _AL_VECTOR copy;
236    size_t i;
237    if (!display) return;
238 
239    al_store_state(&backup, ALLEGRO_STATE_NEW_BITMAP_PARAMETERS);
240 
241    al_lock_mutex(convert_bitmap_list.mutex);
242 
243    _al_vector_init(&copy, sizeof(ALLEGRO_BITMAP *));
244    for (i = 0; i <  _al_vector_size(&convert_bitmap_list.bitmaps); i++) {
245       ALLEGRO_BITMAP **bptr, **bptr2;
246       bptr = _al_vector_ref(&convert_bitmap_list.bitmaps, i);
247       bptr2 = _al_vector_alloc_back(&copy);
248       *bptr2 = *bptr;
249    }
250    _al_vector_free(&convert_bitmap_list.bitmaps);
251    _al_vector_init(&convert_bitmap_list.bitmaps, sizeof(ALLEGRO_BITMAP *));
252    for (i = 0; i < _al_vector_size(&copy); i++) {
253       ALLEGRO_BITMAP **bptr;
254       int flags;
255       bptr = _al_vector_ref(&copy, i);
256       flags = al_get_bitmap_flags(*bptr);
257       flags &= ~ALLEGRO_MEMORY_BITMAP;
258       al_set_new_bitmap_flags(flags);
259       al_set_new_bitmap_format(al_get_bitmap_format(*bptr));
260 
261       ALLEGRO_DEBUG("converting memory bitmap %p to display bitmap\n", *bptr);
262 
263       al_convert_bitmap(*bptr);
264    }
265 
266    _al_vector_free(&copy);
267 
268    al_unlock_mutex(convert_bitmap_list.mutex);
269 
270    al_restore_state(&backup);
271 }
272 
273 
274 /* Converts a memory bitmap to a display bitmap preserving its contents.
275  * The created bitmap belongs to the current display.
276  *
277  * If this is called for a sub-bitmap, the parent also is converted.
278  */
_al_convert_to_display_bitmap(ALLEGRO_BITMAP * bitmap)279 void _al_convert_to_display_bitmap(ALLEGRO_BITMAP *bitmap)
280 {
281    ALLEGRO_STATE backup;
282    int bitmap_flags = al_get_bitmap_flags(bitmap);
283    /* Do nothing if it is a display bitmap already. */
284    if (!(bitmap_flags & ALLEGRO_MEMORY_BITMAP))
285       return;
286 
287    ALLEGRO_DEBUG("converting memory bitmap %p to display bitmap\n", bitmap);
288 
289    al_store_state(&backup, ALLEGRO_STATE_NEW_BITMAP_PARAMETERS);
290    al_set_new_bitmap_flags(bitmap_flags & ~ALLEGRO_MEMORY_BITMAP);
291    al_set_new_bitmap_format(al_get_bitmap_format(bitmap));
292    al_convert_bitmap(bitmap);
293    al_restore_state(&backup);
294 }
295 
296 
297 /* Converts a display bitmap to a memory bitmap preserving its contents.
298  * If this is called for a sub-bitmap, the parent also is converted.
299  */
_al_convert_to_memory_bitmap(ALLEGRO_BITMAP * bitmap)300 void _al_convert_to_memory_bitmap(ALLEGRO_BITMAP *bitmap)
301 {
302    ALLEGRO_STATE backup;
303    int bitmap_flags = al_get_bitmap_flags(bitmap);
304    /* Do nothing if it is a memory bitmap already. */
305    if (bitmap_flags & ALLEGRO_MEMORY_BITMAP)
306       return;
307 
308    ALLEGRO_DEBUG("converting display bitmap %p to memory bitmap\n", bitmap);
309 
310    al_store_state(&backup, ALLEGRO_STATE_NEW_BITMAP_PARAMETERS);
311    al_set_new_bitmap_flags((bitmap_flags & ~ALLEGRO_VIDEO_BITMAP) | ALLEGRO_MEMORY_BITMAP);
312    al_set_new_bitmap_format(al_get_bitmap_format(bitmap));
313    al_convert_bitmap(bitmap);
314    al_restore_state(&backup);
315 }
316 
317 
318 /* vim: set ts=8 sts=3 sw=3 et: */
319