/* ______ ___ ___ * /\ _ \ /\_ \ /\_ \ * \ \ \L\ \\//\ \ \//\ \ __ __ _ __ ___ * \ \ __ \ \ \ \ \ \ \ /'__`\ /'_ `\/\`'__\/ __`\ * \ \ \/\ \ \_\ \_ \_\ \_/\ __//\ \L\ \ \ \//\ \L\ \ * \ \_\ \_\/\____\/\____\ \____\ \____ \ \_\\ \____/ * \/_/\/_/\/____/\/____/\/____/\/___L\ \/_/ \/___/ * /\____/ * \_/__/ * * Convert between memory and video bitmap types. * * See LICENSE.txt for copyright information. */ #include "allegro5/allegro.h" #include "allegro5/internal/aintern.h" #include "allegro5/internal/aintern_bitmap.h" #include "allegro5/internal/aintern_exitfunc.h" #include "allegro5/internal/aintern_shader.h" #include "allegro5/internal/aintern_system.h" #include "allegro5/internal/aintern_vector.h" ALLEGRO_DEBUG_CHANNEL("bitmap") /* Global list of MEMORY bitmaps with ALLEGRO_CONVERT_BITMAP flag. */ struct BITMAP_CONVERSION_LIST { ALLEGRO_MUTEX *mutex; _AL_VECTOR bitmaps; }; static struct BITMAP_CONVERSION_LIST convert_bitmap_list; static void cleanup_convert_bitmap_list(void) { _al_vector_free(&convert_bitmap_list.bitmaps); al_destroy_mutex(convert_bitmap_list.mutex); } /* This is called in al_install_system. Exit functions are called in * al_uninstall_system. */ void _al_init_convert_bitmap_list(void) { convert_bitmap_list.mutex = al_create_mutex_recursive(); _al_vector_init(&convert_bitmap_list.bitmaps, sizeof(ALLEGRO_BITMAP *)); _al_add_exit_func(cleanup_convert_bitmap_list, "cleanup_convert_bitmap_list"); } void _al_register_convert_bitmap(ALLEGRO_BITMAP *bitmap) { int bitmap_flags = al_get_bitmap_flags(bitmap); if (!(bitmap_flags & ALLEGRO_MEMORY_BITMAP)) return; if (bitmap_flags & ALLEGRO_CONVERT_BITMAP) { ALLEGRO_BITMAP **back; al_lock_mutex(convert_bitmap_list.mutex); back = _al_vector_alloc_back(&convert_bitmap_list.bitmaps); *back = bitmap; al_unlock_mutex(convert_bitmap_list.mutex); } } void _al_unregister_convert_bitmap(ALLEGRO_BITMAP *bitmap) { int bitmap_flags = al_get_bitmap_flags(bitmap); if (!(bitmap_flags & ALLEGRO_MEMORY_BITMAP)) return; if (bitmap_flags & ALLEGRO_CONVERT_BITMAP) { al_lock_mutex(convert_bitmap_list.mutex); _al_vector_find_and_delete(&convert_bitmap_list.bitmaps, &bitmap); al_unlock_mutex(convert_bitmap_list.mutex); } } static void swap_bitmaps(ALLEGRO_BITMAP *bitmap, ALLEGRO_BITMAP *other) { ALLEGRO_BITMAP temp; _AL_LIST_ITEM *bitmap_dtor_item = bitmap->dtor_item; _AL_LIST_ITEM *other_dtor_item = other->dtor_item; ALLEGRO_DISPLAY *bitmap_display, *other_display; _al_unregister_convert_bitmap(bitmap); _al_unregister_convert_bitmap(other); if (other->shader) _al_unregister_shader_bitmap(other->shader, other); if (bitmap->shader) _al_unregister_shader_bitmap(bitmap->shader, bitmap); temp = *bitmap; *bitmap = *other; *other = temp; /* Re-associate the destructors back, as they are tied to the object * pointers. */ bitmap->dtor_item = bitmap_dtor_item; other->dtor_item = other_dtor_item; bitmap_display = _al_get_bitmap_display(bitmap); other_display = _al_get_bitmap_display(other); /* We are basically done already. Except we now have to update everything * possibly referencing any of the two bitmaps. */ if (bitmap_display && !other_display) { /* This means before the swap, other was the display bitmap, and we * now should replace it with the swapped pointer. */ ALLEGRO_BITMAP **back; int pos = _al_vector_find(&bitmap_display->bitmaps, &other); ASSERT(pos >= 0); back = _al_vector_ref(&bitmap_display->bitmaps, pos); *back = bitmap; } if (other_display && !bitmap_display) { ALLEGRO_BITMAP **back; int pos = _al_vector_find(&other_display->bitmaps, &bitmap); ASSERT(pos >= 0); back = _al_vector_ref(&other_display->bitmaps, pos); *back = other; } if (other->shader) _al_register_shader_bitmap(other->shader, other); if (bitmap->shader) _al_register_shader_bitmap(bitmap->shader, bitmap); _al_register_convert_bitmap(bitmap); _al_register_convert_bitmap(other); if (bitmap->vt && bitmap->vt->bitmap_pointer_changed) bitmap->vt->bitmap_pointer_changed(bitmap, other); if (other->vt && other->vt->bitmap_pointer_changed) other->vt->bitmap_pointer_changed(other, bitmap); } /* Function: al_convert_bitmap */ void al_convert_bitmap(ALLEGRO_BITMAP *bitmap) { ALLEGRO_BITMAP *clone; int bitmap_flags = al_get_bitmap_flags(bitmap); int new_bitmap_flags = al_get_new_bitmap_flags(); bool want_memory = (new_bitmap_flags & ALLEGRO_MEMORY_BITMAP) != 0; bool clone_memory; ALLEGRO_BITMAP *target_bitmap; bitmap_flags &= ~_ALLEGRO_INTERNAL_OPENGL; /* If a cloned bitmap would be identical, we can just do nothing. */ if (al_get_bitmap_format(bitmap) == al_get_new_bitmap_format() && bitmap_flags == new_bitmap_flags && _al_get_bitmap_display(bitmap) == al_get_current_display()) { return; } if (bitmap->parent) { al_convert_bitmap(bitmap->parent); return; } else { clone = al_clone_bitmap(bitmap); } if (!clone) { return; } clone_memory = (al_get_bitmap_flags(clone) & ALLEGRO_MEMORY_BITMAP) != 0; if (clone_memory != want_memory) { /* We couldn't convert. */ al_destroy_bitmap(clone); return; } swap_bitmaps(bitmap, clone); /* Preserve bitmap state. */ bitmap->cl = clone->cl; bitmap->ct = clone->ct; bitmap->cr_excl = clone->cr_excl; bitmap->cb_excl = clone->cb_excl; bitmap->transform = clone->transform; bitmap->inverse_transform = clone->inverse_transform; bitmap->inverse_transform_dirty = clone->inverse_transform_dirty; /* Memory bitmaps do not support custom projection transforms, * so reset it to the orthographic transform. */ if (new_bitmap_flags & ALLEGRO_MEMORY_BITMAP) { al_identity_transform(&bitmap->proj_transform); al_orthographic_transform(&bitmap->proj_transform, 0, 0, -1.0, bitmap->w, bitmap->h, 1.0); } else { bitmap->proj_transform = clone->proj_transform; } /* If we just converted this bitmap, and the backing bitmap is the same * as the target's backing bitmap, then the viewports and transformations * will be messed up. Detect this, and just re-call al_set_target_bitmap * on the current target. */ target_bitmap = al_get_target_bitmap(); if (target_bitmap) { ALLEGRO_BITMAP *target_parent = target_bitmap->parent ? target_bitmap->parent : target_bitmap; if (bitmap == target_parent || bitmap->parent == target_parent) { al_set_target_bitmap(target_bitmap); } } al_destroy_bitmap(clone); } /* Function: al_convert_memory_bitmaps */ void al_convert_memory_bitmaps(void) { ALLEGRO_STATE backup; ALLEGRO_DISPLAY *display = al_get_current_display(); _AL_VECTOR copy; size_t i; if (!display) return; al_store_state(&backup, ALLEGRO_STATE_NEW_BITMAP_PARAMETERS); al_lock_mutex(convert_bitmap_list.mutex); _al_vector_init(©, sizeof(ALLEGRO_BITMAP *)); for (i = 0; i < _al_vector_size(&convert_bitmap_list.bitmaps); i++) { ALLEGRO_BITMAP **bptr, **bptr2; bptr = _al_vector_ref(&convert_bitmap_list.bitmaps, i); bptr2 = _al_vector_alloc_back(©); *bptr2 = *bptr; } _al_vector_free(&convert_bitmap_list.bitmaps); _al_vector_init(&convert_bitmap_list.bitmaps, sizeof(ALLEGRO_BITMAP *)); for (i = 0; i < _al_vector_size(©); i++) { ALLEGRO_BITMAP **bptr; int flags; bptr = _al_vector_ref(©, i); flags = al_get_bitmap_flags(*bptr); flags &= ~ALLEGRO_MEMORY_BITMAP; al_set_new_bitmap_flags(flags); al_set_new_bitmap_format(al_get_bitmap_format(*bptr)); ALLEGRO_DEBUG("converting memory bitmap %p to display bitmap\n", *bptr); al_convert_bitmap(*bptr); } _al_vector_free(©); al_unlock_mutex(convert_bitmap_list.mutex); al_restore_state(&backup); } /* Converts a memory bitmap to a display bitmap preserving its contents. * The created bitmap belongs to the current display. * * If this is called for a sub-bitmap, the parent also is converted. */ void _al_convert_to_display_bitmap(ALLEGRO_BITMAP *bitmap) { ALLEGRO_STATE backup; int bitmap_flags = al_get_bitmap_flags(bitmap); /* Do nothing if it is a display bitmap already. */ if (!(bitmap_flags & ALLEGRO_MEMORY_BITMAP)) return; ALLEGRO_DEBUG("converting memory bitmap %p to display bitmap\n", bitmap); al_store_state(&backup, ALLEGRO_STATE_NEW_BITMAP_PARAMETERS); al_set_new_bitmap_flags(bitmap_flags & ~ALLEGRO_MEMORY_BITMAP); al_set_new_bitmap_format(al_get_bitmap_format(bitmap)); al_convert_bitmap(bitmap); al_restore_state(&backup); } /* Converts a display bitmap to a memory bitmap preserving its contents. * If this is called for a sub-bitmap, the parent also is converted. */ void _al_convert_to_memory_bitmap(ALLEGRO_BITMAP *bitmap) { ALLEGRO_STATE backup; int bitmap_flags = al_get_bitmap_flags(bitmap); /* Do nothing if it is a memory bitmap already. */ if (bitmap_flags & ALLEGRO_MEMORY_BITMAP) return; ALLEGRO_DEBUG("converting display bitmap %p to memory bitmap\n", bitmap); al_store_state(&backup, ALLEGRO_STATE_NEW_BITMAP_PARAMETERS); al_set_new_bitmap_flags((bitmap_flags & ~ALLEGRO_VIDEO_BITMAP) | ALLEGRO_MEMORY_BITMAP); al_set_new_bitmap_format(al_get_bitmap_format(bitmap)); al_convert_bitmap(bitmap); al_restore_state(&backup); } /* vim: set ts=8 sts=3 sw=3 et: */