* /\ _ \ /\_ \ /\_ \
* \ \ \L\ \\//\ \ \//\ \ __ __ _ __ ___
* \ \ __ \ \ \ \ \ \ \ /'__`\ /'_ `\/\`'__\/ __`\
* \ \ \/\ \ \_\ \_ \_\ \_/\ __//\ \L\ \ \ \//\ \L\ \
* \ \_\ \_\/\____\/\____\ \____\ \____ \ \_\\ \____/
* \/_/\/_/\/____/\/____/\/____/\/___L\ \/_/ \/___/
* /\____/
* \_/__/
*
* New bitmap routines.
*
* By Elias Pschernig and Trent Gamblin.
*
* See readme.txt for copyright information.
*/
*/
#include <string.h>
#include "allegro5/allegro.h"
#include "allegro5/internal/aintern.h"
#include "allegro5/internal/aintern_bitmap.h"
#include "allegro5/internal/aintern_display.h"
#include "allegro5/internal/aintern_pixels.h"
#include "allegro5/internal/aintern_shader.h"
#include "allegro5/internal/aintern_system.h"
ALLEGRO_DEBUG_CHANNEL("bitmap")
*/
static ALLEGRO_BITMAP *create_memory_bitmap(ALLEGRO_DISPLAY *current_display,
int w, int h, int format, int flags)
{
ALLEGRO_BITMAP *bitmap;
int pitch;
if (_al_pixel_format_is_video_only(format)) {
return NULL;
}
format = _al_get_real_pixel_format(current_display, format);
bitmap = al_calloc(1, sizeof *bitmap);
pitch = w * al_get_pixel_size(format);
bitmap->vt = NULL;
bitmap->_format = format;
* be converted bitmaps.
*/
bitmap->_flags = flags | ALLEGRO_MEMORY_BITMAP;
bitmap->_flags &= ~ALLEGRO_VIDEO_BITMAP;
bitmap->w = w;
bitmap->h = h;
bitmap->pitch = pitch;
bitmap->_display = NULL;
bitmap->locked = false;
bitmap->cl = bitmap->ct = 0;
bitmap->cr_excl = w;
bitmap->cb_excl = h;
al_identity_transform(&bitmap->transform);
al_identity_transform(&bitmap->inverse_transform);
bitmap->inverse_transform_dirty = false;
al_identity_transform(&bitmap->proj_transform);
al_orthographic_transform(&bitmap->proj_transform, 0, 0, -1.0, w, h, 1.0);
bitmap->parent = NULL;
bitmap->xofs = bitmap->yofs = 0;
bitmap->memory = al_malloc(pitch * h);
bitmap->use_bitmap_blender = false;
bitmap->blender.blend_color = al_map_rgba(0, 0, 0, 0);
al_get_new_bitmap_wrap(&bitmap->_wrap_u, &bitmap->_wrap_v);
_al_register_convert_bitmap(bitmap);
return bitmap;
}
static void destroy_memory_bitmap(ALLEGRO_BITMAP *bmp)
{
_al_unregister_convert_bitmap(bmp);
if (bmp->memory)
al_free(bmp->memory);
al_free(bmp);
}
ALLEGRO_BITMAP *_al_create_bitmap_params(ALLEGRO_DISPLAY *current_display,
int w, int h, int format, int flags, int depth, int samples)
{
ALLEGRO_SYSTEM *system = al_get_system_driver();
ALLEGRO_BITMAP *bitmap;
ALLEGRO_BITMAP **back;
bool result;
* Also reject bitmaps where a calculation pixel_size*w*h would overflow
* int. Supporting such bitmaps would require a lot more work.
* Overflow calc based on https://stackoverflow.com/a/1514309/231929
*/
if (w < 0 || h < 0 || (h > 0 && (int64_t) w > (INT_MAX/4) / (int64_t) h)) {
ALLEGRO_WARN("Rejecting %dx%d bitmap\n", w, h);
return NULL;
}
if ((flags & ALLEGRO_MEMORY_BITMAP) ||
!current_display ||
!current_display->vt ||
current_display->vt->create_bitmap == NULL ||
_al_vector_size(&system->displays) < 1)
{
if (flags & ALLEGRO_VIDEO_BITMAP)
return NULL;
return create_memory_bitmap(current_display, w, h, format, flags);
}
bitmap = current_display->vt->create_bitmap(current_display, w, h,
format, flags);
if (!bitmap) {
ALLEGRO_ERROR("failed to create display bitmap\n");
return NULL;
}
bitmap->_display = current_display;
bitmap->w = w;
bitmap->h = h;
bitmap->locked = false;
bitmap->cl = 0;
bitmap->ct = 0;
bitmap->cr_excl = w;
bitmap->cb_excl = h;
al_identity_transform(&bitmap->transform);
al_identity_transform(&bitmap->inverse_transform);
bitmap->inverse_transform_dirty = false;
al_identity_transform(&bitmap->proj_transform);
al_orthographic_transform(&bitmap->proj_transform, 0, 0, -1.0, w, h, 1.0);
bitmap->parent = NULL;
bitmap->xofs = 0;
bitmap->yofs = 0;
bitmap->_flags |= ALLEGRO_VIDEO_BITMAP;
bitmap->dirty = !(bitmap->_flags & ALLEGRO_NO_PRESERVE_TEXTURE);
bitmap->_depth = depth;
bitmap->_samples = samples;
bitmap->use_bitmap_blender = false;
bitmap->blender.blend_color = al_map_rgba(0, 0, 0, 0);
al_get_new_bitmap_wrap(&bitmap->_wrap_u, &bitmap->_wrap_v);
* appropriate; video bitmaps may leave it NULL.
*/
ASSERT(bitmap->pitch >= w * al_get_pixel_size(bitmap->_format));
result = bitmap->vt->upload_bitmap(bitmap);
if (!result) {
al_destroy_bitmap(bitmap);
if (flags & ALLEGRO_VIDEO_BITMAP)
return NULL;
* video failed.
*/
return create_memory_bitmap(current_display, w, h, format, flags);
}
* convert them to memory bimaps when the display is destroyed. */
back = _al_vector_alloc_back(¤t_display->bitmaps);
*back = bitmap;
return bitmap;
}
*/
ALLEGRO_BITMAP *al_create_bitmap(int w, int h)
{
ALLEGRO_BITMAP *bitmap;
bitmap = _al_create_bitmap_params(al_get_current_display(), w, h,
al_get_new_bitmap_format(), al_get_new_bitmap_flags(),
al_get_new_bitmap_depth(), al_get_new_bitmap_samples());
if (bitmap) {
bitmap->dtor_item = _al_register_destructor(_al_dtor_list, "bitmap", bitmap,
(void (*)(void *))al_destroy_bitmap);
}
return bitmap;
}
*/
void al_destroy_bitmap(ALLEGRO_BITMAP *bitmap)
{
if (!bitmap) {
return;
}
* before it is destroyed, but maintain the current display.
*/
if (bitmap == al_get_target_bitmap()) {
ALLEGRO_DISPLAY *display = al_get_current_display();
if (display)
al_set_target_bitmap(al_get_backbuffer(display));
else
al_set_target_bitmap(NULL);
}
_al_set_bitmap_shader_field(bitmap, NULL);
_al_unregister_destructor(_al_dtor_list, bitmap->dtor_item);
if (!al_is_sub_bitmap(bitmap)) {
ALLEGRO_DISPLAY* disp = _al_get_bitmap_display(bitmap);
if (al_get_bitmap_flags(bitmap) & ALLEGRO_MEMORY_BITMAP) {
destroy_memory_bitmap(bitmap);
return;
}
if (bitmap->locked)
al_unlock_bitmap(bitmap);
if (bitmap->vt)
bitmap->vt->destroy_bitmap(bitmap);
if (disp)
_al_vector_find_and_delete(&disp->bitmaps, &bitmap);
if (bitmap->memory)
al_free(bitmap->memory);
}
al_free(bitmap);
}
*/
void al_convert_mask_to_alpha(ALLEGRO_BITMAP *bitmap, ALLEGRO_COLOR mask_color)
{
ALLEGRO_LOCKED_REGION *lr;
int x, y;
ALLEGRO_COLOR pixel;
ALLEGRO_COLOR alpha_pixel;
ALLEGRO_STATE state;
if (!(lr = al_lock_bitmap(bitmap, ALLEGRO_PIXEL_FORMAT_ANY, 0))) {
ALLEGRO_ERROR("Couldn't lock bitmap.");
return;
}
al_store_state(&state, ALLEGRO_STATE_TARGET_BITMAP);
al_set_target_bitmap(bitmap);
alpha_pixel = al_map_rgba(0, 0, 0, 0);
for (y = 0; y < bitmap->h; y++) {
for (x = 0; x < bitmap->w; x++) {
pixel = al_get_pixel(bitmap, x, y);
if (memcmp(&pixel, &mask_color, sizeof(ALLEGRO_COLOR)) == 0) {
al_put_pixel(x, y, alpha_pixel);
}
}
}
al_unlock_bitmap(bitmap);
al_restore_state(&state);
}
*/
int al_get_bitmap_width(ALLEGRO_BITMAP *bitmap)
{
return bitmap->w;
}
*/
int al_get_bitmap_height(ALLEGRO_BITMAP *bitmap)
{
return bitmap->h;
}
*/
int al_get_bitmap_format(ALLEGRO_BITMAP *bitmap)
{
if (bitmap->parent)
return bitmap->parent->_format;
else
return bitmap->_format;
}
int _al_get_bitmap_memory_format(ALLEGRO_BITMAP *bitmap)
{
if (bitmap->parent)
return bitmap->parent->_memory_format;
else
return bitmap->_memory_format;
}
*/
int al_get_bitmap_flags(ALLEGRO_BITMAP *bitmap)
{
if (bitmap->parent)
return bitmap->parent->_flags;
else
return bitmap->_flags;
}
ALLEGRO_DISPLAY *_al_get_bitmap_display(ALLEGRO_BITMAP *bitmap)
{
if (bitmap->parent)
return bitmap->parent->_display;
else
return bitmap->_display;
}
*/
int al_get_bitmap_depth(ALLEGRO_BITMAP *bitmap)
{
if (bitmap->parent)
return bitmap->parent->_depth;
else
return bitmap->_depth;
}
*/
int al_get_bitmap_samples(ALLEGRO_BITMAP *bitmap)
{
if (bitmap->parent)
return bitmap->parent->_samples;
else
return bitmap->_samples;
}
*/
ALLEGRO_COLOR al_get_bitmap_blend_color(void)
{
ALLEGRO_BITMAP *bitmap = al_get_target_bitmap();
ALLEGRO_BLENDER *b;
ASSERT(bitmap);
if (!bitmap->use_bitmap_blender) {
return al_get_blend_color();
}
b = &bitmap->blender;
return b->blend_color;
}
*/
void al_get_bitmap_blender(int *op, int *src, int *dst)
{
al_get_separate_bitmap_blender(op, src, dst, NULL, NULL, NULL);
}
*/
void al_get_separate_bitmap_blender(int *op, int *src, int *dst, int *alpha_op, int *alpha_src, int *alpha_dst)
{
ALLEGRO_BITMAP *bitmap = al_get_target_bitmap();
ALLEGRO_BLENDER *b;
ASSERT(bitmap);
if (!bitmap->use_bitmap_blender) {
al_get_separate_blender(op, src, dst, alpha_op, alpha_src, alpha_dst);
return;
}
b = &bitmap->blender;
if (op)
*op = b->blend_op;
if (src)
*src = b->blend_source;
if (dst)
*dst = b->blend_dest;
if (alpha_op)
*alpha_op = b->blend_alpha_op;
if (alpha_src)
*alpha_src = b->blend_alpha_source;
if (alpha_dst)
*alpha_dst = b->blend_alpha_dest;
}
*/
void al_set_bitmap_blend_color(ALLEGRO_COLOR col)
{
ALLEGRO_BITMAP *bitmap = al_get_target_bitmap();
ALLEGRO_BLENDER *b;
ASSERT(bitmap);
b = &bitmap->blender;
b->blend_color = col;
}
*/
void al_set_bitmap_blender(int op, int src, int dest)
{
ALLEGRO_BITMAP *bitmap = al_get_target_bitmap();
ASSERT(bitmap);
al_set_separate_bitmap_blender(op, src, dest, op, src, dest);
}
*/
void al_set_separate_bitmap_blender(int op, int src, int dst, int alpha_op, int alpha_src, int alpha_dst)
{
ALLEGRO_BITMAP *bitmap = al_get_target_bitmap();
ALLEGRO_BLENDER *b;
ASSERT(bitmap);
bitmap->use_bitmap_blender = true;
b = &bitmap->blender;
b->blend_op = op;
b->blend_source = src;
b->blend_dest = dst;
b->blend_alpha_op = alpha_op;
b->blend_alpha_source = alpha_src;
b->blend_alpha_dest = alpha_dst;
}
*/
void al_reset_bitmap_blender(void)
{
ALLEGRO_BITMAP *bitmap = al_get_target_bitmap();
ASSERT(bitmap);
bitmap->use_bitmap_blender = false;
bitmap->blender.blend_color = al_map_rgba(0, 0, 0, 0);
}
*/
void al_set_clipping_rectangle(int x, int y, int width, int height)
{
ALLEGRO_BITMAP *bitmap = al_get_target_bitmap();
ASSERT(bitmap);
if (x < 0) {
width += x;
x = 0;
}
if (y < 0) {
height += y;
y = 0;
}
if (x + width > bitmap->w) {
width = bitmap->w - x;
}
if (y + height > bitmap->h) {
height = bitmap->h - y;
}
if (width < 0) {
width = 0;
}
if (height < 0) {
height = 0;
}
bitmap->cl = x;
bitmap->ct = y;
bitmap->cr_excl = x + width;
bitmap->cb_excl = y + height;
if (bitmap->vt && bitmap->vt->update_clipping_rectangle) {
bitmap->vt->update_clipping_rectangle(bitmap);
}
}
*/
void al_reset_clipping_rectangle(void)
{
ALLEGRO_BITMAP *bitmap = al_get_target_bitmap();
if (bitmap) {
int w = al_get_bitmap_width(bitmap);
int h = al_get_bitmap_height(bitmap);
al_set_clipping_rectangle(0, 0, w, h);
}
}
*/
void al_get_clipping_rectangle(int *x, int *y, int *w, int *h)
{
ALLEGRO_BITMAP *bitmap = al_get_target_bitmap();
ASSERT(bitmap);
if (x) *x = bitmap->cl;
if (y) *y = bitmap->ct;
if (w) *w = bitmap->cr_excl - bitmap->cl;
if (h) *h = bitmap->cb_excl - bitmap->ct;
}
*/
ALLEGRO_BITMAP *al_create_sub_bitmap(ALLEGRO_BITMAP *parent,
int x, int y, int w, int h)
{
ALLEGRO_BITMAP *bitmap;
if (parent->parent) {
x += parent->xofs;
y += parent->yofs;
parent = parent->parent;
}
bitmap = al_calloc(1, sizeof *bitmap);
bitmap->vt = parent->vt;
* Leave these unchanged so they can be detected if improperly accessed
* directly. */
bitmap->_format = 0;
bitmap->_flags = 0;
bitmap->_display = (ALLEGRO_DISPLAY*)0x1;
bitmap->_wrap_u = 0;
bitmap->_wrap_v = 0;
bitmap->w = w;
bitmap->h = h;
bitmap->locked = false;
bitmap->cl = bitmap->ct = 0;
bitmap->cr_excl = w;
bitmap->cb_excl = h;
al_identity_transform(&bitmap->transform);
al_identity_transform(&bitmap->inverse_transform);
bitmap->inverse_transform_dirty = false;
al_identity_transform(&bitmap->proj_transform);
al_orthographic_transform(&bitmap->proj_transform, 0, 0, -1.0, w, h, 1.0);
bitmap->shader = NULL;
bitmap->parent = parent;
bitmap->xofs = x;
bitmap->yofs = y;
bitmap->memory = NULL;
bitmap->dtor_item = _al_register_destructor(_al_dtor_list, "sub_bitmap", bitmap,
(void (*)(void *))al_destroy_bitmap);
return bitmap;
}
*/
void al_reparent_bitmap(ALLEGRO_BITMAP *bitmap, ALLEGRO_BITMAP *parent,
int x, int y, int w, int h)
{
ASSERT(bitmap->parent);
* just ignore it. */
if (!bitmap->parent)
return;
if (parent->parent) {
x += parent->xofs;
y += parent->yofs;
parent = parent->parent;
}
bitmap->parent = parent;
bitmap->xofs = x;
bitmap->yofs = y;
bitmap->w = w;
bitmap->h = h;
}
*/
bool al_is_sub_bitmap(ALLEGRO_BITMAP *bitmap)
{
return (bitmap->parent != NULL);
}
*/
ALLEGRO_BITMAP *al_get_parent_bitmap(ALLEGRO_BITMAP *bitmap)
{
ASSERT(bitmap);
return bitmap->parent;
}
*/
int al_get_bitmap_x(ALLEGRO_BITMAP *bitmap)
{
ASSERT(bitmap);
return bitmap->xofs;
}
*/
int al_get_bitmap_y(ALLEGRO_BITMAP *bitmap)
{
ASSERT(bitmap);
return bitmap->yofs;
}
static bool transfer_bitmap_data(ALLEGRO_BITMAP *src, ALLEGRO_BITMAP *dst)
{
ALLEGRO_LOCKED_REGION *dst_region;
ALLEGRO_LOCKED_REGION *src_region;
int src_format = al_get_bitmap_format(src);
int dst_format = al_get_bitmap_format(dst);
bool src_compressed = _al_pixel_format_is_compressed(src_format);
bool dst_compressed = _al_pixel_format_is_compressed(dst_format);
int copy_w = src->w;
int copy_h = src->h;
if (src_compressed && dst_compressed && src_format == dst_format) {
int block_width = al_get_pixel_block_width(src_format);
int block_height = al_get_pixel_block_height(src_format);
if (!(src_region = al_lock_bitmap_blocked(src, ALLEGRO_LOCK_READONLY)))
return false;
if (!(dst_region = al_lock_bitmap_blocked(dst, ALLEGRO_LOCK_WRITEONLY))) {
al_unlock_bitmap(src);
return false;
}
copy_w = _al_get_least_multiple(copy_w, block_width);
copy_h = _al_get_least_multiple(copy_h, block_height);
ALLEGRO_DEBUG("Taking fast clone path.\n");
}
else {
int lock_format = ALLEGRO_PIXEL_FORMAT_ANY;
if (src_compressed && !dst_compressed) {
lock_format = dst_format;
}
else if (!src_compressed && dst_compressed) {
lock_format = src_format;
}
if (!(src_region = al_lock_bitmap(src, lock_format, ALLEGRO_LOCK_READONLY)))
return false;
if (!(dst_region = al_lock_bitmap(dst, lock_format, ALLEGRO_LOCK_WRITEONLY))) {
al_unlock_bitmap(src);
return false;
}
}
_al_convert_bitmap_data(
src_region->data, src_region->format, src_region->pitch,
dst_region->data, dst_region->format, dst_region->pitch,
0, 0, 0, 0, copy_w, copy_h);
al_unlock_bitmap(src);
al_unlock_bitmap(dst);
return true;
}
void _al_copy_bitmap_data(
const void *src, int src_pitch, void *dst, int dst_pitch,
int sx, int sy, int dx, int dy, int width, int height,
int format)
{
int block_width = al_get_pixel_block_width(format);
int block_height = al_get_pixel_block_height(format);
int block_size = al_get_pixel_block_size(format);
const char *src_ptr = src;
char *dst_ptr = dst;
int y;
ASSERT(src);
ASSERT(dst);
ASSERT(_al_pixel_format_is_real(format));
ASSERT(width % block_width == 0);
ASSERT(height % block_height == 0);
ASSERT(sx % block_width == 0);
ASSERT(sy % block_height == 0);
ASSERT(dx % block_width == 0);
ASSERT(dy % block_height == 0);
sx /= block_width;
sy /= block_height;
dx /= block_width;
dy /= block_height;
width /= block_width;
height /= block_height;
if ((src_ptr == dst_ptr) && (src_pitch == dst_pitch)) {
return;
}
src_ptr += sy * src_pitch + sx * block_size;
dst_ptr += dy * dst_pitch + dx * block_size;
for (y = 0; y < height; y++) {
memcpy(dst_ptr, src_ptr, width * block_size);
src_ptr += src_pitch;
dst_ptr += dst_pitch;
}
}
void _al_convert_bitmap_data(
const void *src, int src_format, int src_pitch,
void *dst, int dst_format, int dst_pitch,
int sx, int sy, int dx, int dy, int width, int height)
{
ASSERT(src);
ASSERT(dst);
ASSERT(_al_pixel_format_is_real(dst_format));
if (src_format == dst_format) {
_al_copy_bitmap_data(src, src_pitch, dst, dst_pitch, sx, sy,
dx, dy, width, height, src_format);
return;
}
* been taken care of before reaching this location. */
ASSERT(!_al_pixel_format_is_video_only(src_format));
ASSERT(!_al_pixel_format_is_video_only(dst_format));
(_al_convert_funcs[src_format][dst_format])(src, src_pitch,
dst, dst_pitch, sx, sy, dx, dy, width, height);
}
*/
ALLEGRO_BITMAP *al_clone_bitmap(ALLEGRO_BITMAP *bitmap)
{
ALLEGRO_BITMAP *clone;
ASSERT(bitmap);
clone = al_create_bitmap(bitmap->w, bitmap->h);
if (!clone)
return NULL;
if (!transfer_bitmap_data(bitmap, clone)) {
al_destroy_bitmap(clone);
return NULL;
}
return clone;
}
*/
void al_backup_dirty_bitmap(ALLEGRO_BITMAP *bitmap)
{
if (bitmap->vt && bitmap->vt->backup_dirty_bitmap)
bitmap->vt->backup_dirty_bitmap(bitmap);
}
void _al_get_bitmap_wrap(ALLEGRO_BITMAP *bitmap, ALLEGRO_BITMAP_WRAP *wrap_u, ALLEGRO_BITMAP_WRAP *wrap_v)
{
ASSERT(bitmap);
ASSERT(wrap_u);
ASSERT(wrap_v);
if (bitmap->parent) {
bitmap = bitmap->parent;
}
*wrap_u = bitmap->_wrap_u;
*wrap_v = bitmap->_wrap_v;
}