193 changes: 193 additions & 0 deletions examples/ex_compressed.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,193 @@
#include <stdio.h>
#include <stdlib.h>
#include "allegro5/allegro.h"
#include "allegro5/allegro_image.h"
#include "allegro5/allegro_font.h"

#include "common.c"

typedef struct BITMAP_TYPE {
ALLEGRO_BITMAP* bmp;
ALLEGRO_BITMAP* clone;
ALLEGRO_BITMAP* decomp;
ALLEGRO_BITMAP* lock_clone;
ALLEGRO_PIXEL_FORMAT format;
const char* name;
} BITMAP_TYPE;

int main(int argc, char **argv)
{
const char *filename;
ALLEGRO_DISPLAY *display;
ALLEGRO_TIMER *timer;
ALLEGRO_EVENT_QUEUE *queue;
ALLEGRO_FONT *font;
ALLEGRO_BITMAP *bkg;
bool redraw = true;
int ii;
int cur_bitmap = 0;
bool compare = false;
#define NUM_BITMAPS 4
BITMAP_TYPE bitmaps[NUM_BITMAPS] = {
{NULL, NULL, NULL, NULL, ALLEGRO_PIXEL_FORMAT_ANY, "Uncompressed"},
{NULL, NULL, NULL, NULL, ALLEGRO_PIXEL_FORMAT_RGBA_DXT1, "DXT1"},
{NULL, NULL, NULL, NULL, ALLEGRO_PIXEL_FORMAT_RGBA_DXT3, "DXT3"},
{NULL, NULL, NULL, NULL, ALLEGRO_PIXEL_FORMAT_RGBA_DXT5, "DXT5"},
};

if (argc > 1) {
filename = argv[1];
}
else {
filename = "data/mysha.pcx";
}

if (!al_init()) {
abort_example("Could not init Allegro.\n");
}

open_log();

if (argc > 2) {
al_set_new_display_adapter(atoi(argv[2]));
}

al_init_image_addon();
al_init_font_addon();
al_install_keyboard();

display = al_create_display(640, 480);
if (!display) {
abort_example("Error creating display\n");
}

for (ii = 0; ii < NUM_BITMAPS; ii++) {
double t0, t1;
al_set_new_bitmap_format(bitmaps[ii].format);

/* Load */
t0 = al_get_time();
bitmaps[ii].bmp = al_load_bitmap(filename);
t1 = al_get_time();

if (!bitmaps[ii].bmp) {
abort_example("%s not found or failed to load\n", filename);
}
log_printf("%s load time: %f sec\n", bitmaps[ii].name, t1 - t0);

/* Clone */
t0 = al_get_time();
bitmaps[ii].clone = al_clone_bitmap(bitmaps[ii].bmp);
t1 = al_get_time();

if (!bitmaps[ii].clone) {
abort_example("Couldn't clone %s\n", bitmaps[ii].name);
}
log_printf("%s clone time: %f sec\n", bitmaps[ii].name, t1 - t0);

/* Decompress */
al_set_new_bitmap_format(ALLEGRO_PIXEL_FORMAT_ANY);
t0 = al_get_time();
bitmaps[ii].decomp = al_clone_bitmap(bitmaps[ii].bmp);
t1 = al_get_time();

if (!bitmaps[ii].decomp) {
abort_example("Couldn't decompress %s\n", bitmaps[ii].name);
}
log_printf("%s decompress time: %f sec\n", bitmaps[ii].name, t1 - t0);

/* RW lock */
al_set_new_bitmap_format(bitmaps[ii].format);
bitmaps[ii].lock_clone = al_clone_bitmap(bitmaps[ii].bmp);

if (!bitmaps[ii].lock_clone) {
abort_example("Couldn't clone %s\n", bitmaps[ii].name);
}

if (al_get_bitmap_width(bitmaps[ii].bmp) > 128
&& al_get_bitmap_height(bitmaps[ii].bmp) > 128) {
int bitmap_format = al_get_bitmap_format(bitmaps[ii].bmp);
int block_width = al_get_pixel_block_width(bitmap_format);

/* Lock and unlock it, hopefully causing a no-op operation */
al_lock_bitmap_region_blocked(bitmaps[ii].lock_clone,
16 / block_width, 16 / block_width, 64 / block_width,
64 / block_width, ALLEGRO_LOCK_READWRITE);

al_unlock_bitmap(bitmaps[ii].lock_clone);
}
}

bkg = al_load_bitmap("data/bkg.png");
if (!bkg) {
abort_example("data/bkg.png not found or failed to load\n");
}

al_set_new_bitmap_format(ALLEGRO_PIXEL_FORMAT_ANY);
font = al_create_builtin_font();
timer = al_create_timer(1.0 / 30);
queue = al_create_event_queue();
al_register_event_source(queue, al_get_display_event_source(display));
al_register_event_source(queue, al_get_keyboard_event_source());
al_register_event_source(queue, al_get_timer_event_source(timer));
al_start_timer(timer);

while (1) {
ALLEGRO_EVENT event;
al_wait_for_event(queue, &event);
switch (event.type) {
case ALLEGRO_EVENT_DISPLAY_CLOSE:
goto EXIT;
case ALLEGRO_EVENT_TIMER:
redraw = true;
break;
case ALLEGRO_EVENT_KEY_DOWN:
switch (event.keyboard.keycode) {
case ALLEGRO_KEY_LEFT:
cur_bitmap = (cur_bitmap - 1 + NUM_BITMAPS) % NUM_BITMAPS;
break;
case ALLEGRO_KEY_RIGHT:
cur_bitmap = (cur_bitmap + 1) % NUM_BITMAPS;
break;
case ALLEGRO_KEY_SPACE:
compare = true;
break;
case ALLEGRO_KEY_ESCAPE:
goto EXIT;
}
break;
case ALLEGRO_EVENT_KEY_UP:
if (event.keyboard.keycode == ALLEGRO_KEY_SPACE) {
compare = false;
}
break;
}
if (redraw && al_is_event_queue_empty(queue)) {
int w = al_get_bitmap_width(bitmaps[cur_bitmap].bmp);
int h = al_get_bitmap_height(bitmaps[cur_bitmap].bmp);
int idx = compare ? 0 : cur_bitmap;
redraw = false;
al_clear_to_color(al_map_rgb_f(0, 0, 0));
al_draw_bitmap(bkg, 0, 0, 0);
al_draw_textf(font, al_map_rgb_f(1, 1, 1), 5, 5, ALLEGRO_ALIGN_LEFT,
"SPACE to compare. Arrows to switch. Format: %s", bitmaps[idx].name);
al_draw_bitmap(bitmaps[idx].bmp, 0, 20, 0);
al_draw_bitmap(bitmaps[idx].clone, w, 20, 0);
al_draw_bitmap(bitmaps[idx].decomp, 0, 20 + h, 0);
al_draw_bitmap(bitmaps[idx].lock_clone, w, 20 + h, 0);
al_flip_display();
}
}
EXIT:

al_destroy_bitmap(bkg);
for (ii = 0; ii < NUM_BITMAPS; ii++) {
al_destroy_bitmap(bitmaps[ii].bmp);
}

close_log(false);

return 0;
}

/* vim: set sts=4 sw=4 et: */
4 changes: 4 additions & 0 deletions include/allegro5/bitmap_lock.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,15 @@ struct ALLEGRO_LOCKED_REGION {
int format;
int pitch;
int pixel_size;
int pixel_size_bits;
};


AL_FUNC(ALLEGRO_LOCKED_REGION*, al_lock_bitmap, (ALLEGRO_BITMAP *bitmap, int format, int flags));
AL_FUNC(ALLEGRO_LOCKED_REGION*, al_lock_bitmap_region, (ALLEGRO_BITMAP *bitmap, int x, int y, int width, int height, int format, int flags));
AL_FUNC(ALLEGRO_LOCKED_REGION*, al_lock_bitmap_blocked, (ALLEGRO_BITMAP *bitmap, int flags));
AL_FUNC(ALLEGRO_LOCKED_REGION*, al_lock_bitmap_region_blocked, (ALLEGRO_BITMAP *bitmap, int x_block, int y_block,
int width_block, int height_block, int flags));
AL_FUNC(void, al_unlock_bitmap, (ALLEGRO_BITMAP *bitmap));
AL_FUNC(bool, al_is_bitmap_locked, (ALLEGRO_BITMAP *bitmap));

Expand Down
6 changes: 5 additions & 1 deletion include/allegro5/color.h
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,9 @@ typedef enum ALLEGRO_PIXEL_FORMAT
ALLEGRO_PIXEL_FORMAT_ABGR_8888_LE = 25,
ALLEGRO_PIXEL_FORMAT_RGBA_4444 = 26,
ALLEGRO_PIXEL_FORMAT_SINGLE_CHANNEL_8 = 27,
ALLEGRO_PIXEL_FORMAT_RGBA_DXT1 = 28,
ALLEGRO_PIXEL_FORMAT_RGBA_DXT3 = 29,
ALLEGRO_PIXEL_FORMAT_RGBA_DXT5 = 30,
ALLEGRO_NUM_PIXEL_FORMATS
} ALLEGRO_PIXEL_FORMAT;

Expand All @@ -69,7 +72,8 @@ AL_FUNC(void, al_unmap_rgba_f, (ALLEGRO_COLOR color, float *r, float *g, float *
/* Pixel formats */
AL_FUNC(int, al_get_pixel_size, (int format));
AL_FUNC(int, al_get_pixel_format_bits, (int format));

AL_FUNC(int, al_get_pixel_block_size, (int format));
AL_FUNC(int, al_get_pixel_block_width, (int format));

#ifdef __cplusplus
}
Expand Down
1 change: 1 addition & 0 deletions include/allegro5/internal/aintern.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
#define _ALLEGRO_MAX(x,y) (((x) > (y)) ? (x) : (y))
#define _ALLEGRO_CLAMP(x,y,z) _ALLEGRO_MAX((x), _ALLEGRO_MIN((y), (z)))

int _al_get_least_multiple(int val, int mul);

/* message stuff */
#define ALLEGRO_MESSAGE_SIZE 4096
Expand Down
18 changes: 16 additions & 2 deletions include/allegro5/internal/aintern_bitmap.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@ struct ALLEGRO_BITMAP
int _format;
int _flags;
ALLEGRO_DISPLAY *_display;
/* What format is used for the backing memory
* (can be different, for e.g. compressed bitmaps) */
int _memory_format;

int w, h;
/*
Expand Down Expand Up @@ -107,11 +110,15 @@ struct ALLEGRO_BITMAP_INTERFACE
void (*destroy_bitmap)(ALLEGRO_BITMAP *bitmap);

ALLEGRO_LOCKED_REGION * (*lock_region)(ALLEGRO_BITMAP *bitmap,
int x, int y, int w, int h, int format,
int flags);
int x, int y, int w, int h, int format, int flags);

void (*unlock_region)(ALLEGRO_BITMAP *bitmap);

ALLEGRO_LOCKED_REGION * (*lock_compressed_region)(ALLEGRO_BITMAP *bitmap,
int x, int y, int w, int h, int flags);

void (*unlock_compressed_region)(ALLEGRO_BITMAP *bitmap);

/* Used to update any dangling pointers the bitmap driver might keep. */
void (*bitmap_pointer_changed)(ALLEGRO_BITMAP *bitmap, ALLEGRO_BITMAP *old);
};
Expand All @@ -132,6 +139,11 @@ void _al_convert_bitmap_data(
int sx, int sy, int dx, int dy,
int width, int height);

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);

/* Bitmap type conversion */
void _al_init_convert_bitmap_list(void);
void _al_register_convert_bitmap(ALLEGRO_BITMAP *bitmap);
Expand All @@ -146,6 +158,8 @@ void _al_put_pixel(ALLEGRO_BITMAP *bitmap, int x, int y, ALLEGRO_COLOR color);
void _al_init_iio_table(void);


int _al_get_bitmap_memory_format(ALLEGRO_BITMAP *bitmap);

#ifdef __cplusplus
}
#endif
Expand Down
12 changes: 6 additions & 6 deletions include/allegro5/internal/aintern_convert.h
Original file line number Diff line number Diff line change
Expand Up @@ -1561,11 +1561,11 @@
#define ALLEGRO_CONVERT_RGBA_4444_TO_SINGLE_CHANNEL_8(x) \
(_al_rgb_scale_4[(((x) >> 12) & 0xf)])
#define ALLEGRO_CONVERT_SINGLE_CHANNEL_8_TO_ARGB_8888(x) \
(0xff000000 | \
(0xff000000L | \
(((x) << 16) & 0xff0000))
#define ALLEGRO_CONVERT_SINGLE_CHANNEL_8_TO_RGBA_8888(x) \
(0xff | \
(((x) << 24) & 0xff000000))
(((x) << 24) & 0xff000000L))
#define ALLEGRO_CONVERT_SINGLE_CHANNEL_8_TO_ARGB_4444(x) \
(0xf000 | \
(((x) << 4) & 0xf00))
Expand All @@ -1582,7 +1582,7 @@
(0x8000 | \
(((x) << 7) & 0x7c00))
#define ALLEGRO_CONVERT_SINGLE_CHANNEL_8_TO_ABGR_8888(x) \
(0xff000000 | \
(0xff000000L | \
((x) & 0xff))
#define ALLEGRO_CONVERT_SINGLE_CHANNEL_8_TO_XBGR_8888(x) \
(((x) & 0xff))
Expand All @@ -1593,18 +1593,18 @@
#define ALLEGRO_CONVERT_SINGLE_CHANNEL_8_TO_BGR_555(x) \
((((x) >> 3) & 0x1f))
#define ALLEGRO_CONVERT_SINGLE_CHANNEL_8_TO_RGBX_8888(x) \
((((x) << 24) & 0xff000000))
((((x) << 24) & 0xff000000L))
#define ALLEGRO_CONVERT_SINGLE_CHANNEL_8_TO_XRGB_8888(x) \
((((x) << 16) & 0xff0000))
#define ALLEGRO_CONVERT_SINGLE_CHANNEL_8_TO_ABGR_F32(x) \
al_map_rgb(x, 0, 0)
#ifdef ALLEGRO_BIG_ENDIAN
#define ALLEGRO_CONVERT_SINGLE_CHANNEL_8_TO_ABGR_8888_LE(x) \
(0xff | \
(((x) << 24) & 0xff000000))
(((x) << 24) & 0xff000000L))
#else
#define ALLEGRO_CONVERT_SINGLE_CHANNEL_8_TO_ABGR_8888_LE(x) \
(0xff000000 | \
(0xff000000L | \
((x) & 0xff))
#endif
#define ALLEGRO_CONVERT_SINGLE_CHANNEL_8_TO_RGBA_4444(x) \
Expand Down
18 changes: 16 additions & 2 deletions include/allegro5/internal/aintern_direct3d.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ typedef struct ALLEGRO_BITMAP_EXTRA_D3D

LPDIRECT3DTEXTURE9 video_texture;
LPDIRECT3DTEXTURE9 system_texture;
int system_format;

bool initialized;
bool is_backbuffer;
Expand Down Expand Up @@ -83,12 +84,25 @@ void _al_d3d_destroy_bitmap(ALLEGRO_BITMAP *bitmap);
void _al_d3d_update_render_state(ALLEGRO_DISPLAY *display);

#ifdef ALLEGRO_CFG_SHADER_HLSL
bool _al_load_d3dx9_module();
void _al_unload_d3dx9_module();
bool _al_hlsl_set_projview_matrix(LPD3DXEFFECT effect,
const ALLEGRO_TRANSFORM *t);
#endif

typedef HRESULT (WINAPI *_ALLEGRO_D3DXLSFLSPROC)(LPDIRECT3DSURFACE9, const PALETTEENTRY*,
const RECT*, LPDIRECT3DSURFACE9, const PALETTEENTRY*, const RECT*,
DWORD, D3DCOLOR);

typedef HRESULT (WINAPI *_ALLEGRO_D3DXCREATEEFFECTPROC)(LPDIRECT3DDEVICE9, LPCVOID, UINT,
CONST D3DXMACRO*, LPD3DXINCLUDE, DWORD, LPD3DXEFFECTPOOL, LPD3DXEFFECT*,
LPD3DXBUFFER*);

#ifdef ALLEGRO_CFG_D3DX9
bool _al_load_d3dx9_module();
void _al_unload_d3dx9_module();

extern _ALLEGRO_D3DXLSFLSPROC _al_imp_D3DXLoadSurfaceFromSurface;
extern _ALLEGRO_D3DXCREATEEFFECTPROC _al_imp_D3DXCreateEffect;
#endif

#ifdef __cplusplus
}
Expand Down
2 changes: 2 additions & 0 deletions include/allegro5/internal/aintern_opengl.h
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,8 @@ void _al_ogl_upload_bitmap_memory(ALLEGRO_BITMAP *bitmap, int format, void *ptr)
void _al_ogl_unlock_region_gles(ALLEGRO_BITMAP *bitmap);
#endif

int _al_ogl_pixel_alignment(int pixel_size, bool compressed);

/* framebuffer objects */
GLint _al_ogl_bind_framebuffer(GLint fbo);
void _al_ogl_reset_fbo_info(ALLEGRO_FBO_INFO *info);
Expand Down
16 changes: 16 additions & 0 deletions include/allegro5/internal/aintern_pixels.h
Original file line number Diff line number Diff line change
Expand Up @@ -252,6 +252,13 @@
abort(); \
break; \
\
case ALLEGRO_PIXEL_FORMAT_RGBA_DXT1: \
case ALLEGRO_PIXEL_FORMAT_RGBA_DXT3: \
case ALLEGRO_PIXEL_FORMAT_RGBA_DXT5: \
ALLEGRO_ERROR("INLINE_GET got compressed format: %d\n", format); \
abort(); \
break; \
\
case ALLEGRO_NUM_PIXEL_FORMATS: \
default: \
ALLEGRO_ERROR("INLINE_GET got non pixel format: %d\n", format); \
Expand Down Expand Up @@ -460,6 +467,13 @@
abort(); \
break; \
\
case ALLEGRO_PIXEL_FORMAT_RGBA_DXT1: \
case ALLEGRO_PIXEL_FORMAT_RGBA_DXT3: \
case ALLEGRO_PIXEL_FORMAT_RGBA_DXT5: \
ALLEGRO_ERROR("INLINE_PUT got compressed format: %d\n", format); \
abort(); \
break; \
\
case ALLEGRO_NUM_PIXEL_FORMATS: \
ALLEGRO_ERROR("INLINE_PUT got non _pp_pixel format: %d\n", format); \
abort(); \
Expand All @@ -476,6 +490,8 @@ AL_ARRAY(float, _al_u8_to_float);
void _al_init_pixels(void);
bool _al_pixel_format_has_alpha(int format);
bool _al_pixel_format_is_real(int format);
bool _al_pixel_format_is_video_only(int format);
bool _al_pixel_format_is_compressed(int format);
int _al_get_real_pixel_format(ALLEGRO_DISPLAY *display, int format);
char const *_al_pixel_format_name(ALLEGRO_PIXEL_FORMAT format);

Expand Down
1 change: 1 addition & 0 deletions include/allegro5/platform/alplatf.h.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@

#cmakedefine ALLEGRO_CFG_D3D
#cmakedefine ALLEGRO_CFG_D3D9EX
#cmakedefine ALLEGRO_CFG_D3DX9
#cmakedefine ALLEGRO_CFG_XINPUT
#cmakedefine ALLEGRO_CFG_OPENGL
#cmakedefine ALLEGRO_CFG_OPENGLES
Expand Down
1 change: 1 addition & 0 deletions misc/make_converters.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ def parse_format(format):
Parse the format name into an info structure.
"""
if format.startswith("ANY"): return None
if "DXT" in format: return None

separator = format.find("_")
class Info: pass
Expand Down
9 changes: 9 additions & 0 deletions src/allegro.c
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@

#include "allegro5/allegro.h"
#include "allegro5/platform/alplatf.h"
#include "allegro5/internal/aintern.h"



Expand All @@ -41,5 +42,13 @@ int al_run_main(int argc, char **argv, int (*user_main)(int, char **))
#endif
}

int _al_get_least_multiple(int val, int mul)
{
int rem = val % mul;
if (rem == 0)
return val;
else
return val + mul - rem;
}

/* vim: set sts=3 sw=3 et: */
3 changes: 2 additions & 1 deletion src/android/android_display.c
Original file line number Diff line number Diff line change
Expand Up @@ -856,7 +856,8 @@ static void android_acknowledge_drawing_resume(ALLEGRO_DISPLAY *dpy)
!(bitmap_flags & ALLEGRO_MEMORY_BITMAP) &&
!(bitmap_flags & ALLEGRO_NO_PRESERVE_TEXTURE))
{
_al_ogl_upload_bitmap_memory(bmp, al_get_bitmap_format(bmp), bmp->memory);
int format = _al_pixel_format_is_compressed(format) ? ALLEGRO_PIXEL_FORMAT_ABGR_8888_LE : format;
_al_ogl_upload_bitmap_memory(bmp, bmp->memory_format, bmp->memory);
bmp->dirty = false;
}
}
Expand Down
123 changes: 104 additions & 19 deletions src/bitmap.c
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,11 @@ static ALLEGRO_BITMAP *create_memory_bitmap(ALLEGRO_DISPLAY *current_display,
ALLEGRO_BITMAP *bitmap;
int pitch;

if (_al_pixel_format_is_video_only(format)) {
/* Can't have a video-only memory bitmap... */
return NULL;
}

format = _al_get_real_pixel_format(current_display, format);

bitmap = al_calloc(1, sizeof *bitmap);
Expand Down Expand Up @@ -298,6 +303,15 @@ int al_get_bitmap_format(ALLEGRO_BITMAP *bitmap)
}


int _al_get_bitmap_memory_format(ALLEGRO_BITMAP *bitmap)
{
if (bitmap->parent)
return bitmap->parent->_memory_format;
else
return bitmap->_memory_format;
}



/* Function: al_get_bitmap_flags
*/
Expand Down Expand Up @@ -446,26 +460,97 @@ ALLEGRO_BITMAP *al_get_parent_bitmap(ALLEGRO_BITMAP *bitmap)
}


static void transfer_bitmap_data(ALLEGRO_BITMAP *src, ALLEGRO_BITMAP *dst)
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);
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_width);
ALLEGRO_DEBUG("Taking fast clone path");
}
else {
int lock_format = ALLEGRO_PIXEL_FORMAT_ANY;
/* Go through a non-compressed intermediate */
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 (!(src_region = al_lock_bitmap(src, ALLEGRO_PIXEL_FORMAT_ANY, ALLEGRO_LOCK_READONLY)))
return;

if (!(dst_region = al_lock_bitmap(dst, ALLEGRO_PIXEL_FORMAT_ANY, ALLEGRO_LOCK_WRITEONLY))) {
al_unlock_bitmap(src);
return;
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, src->w, src->h);
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_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(height % block_width == 0);
ASSERT(width % block_width == 0);
ASSERT(sx % block_width == 0);
ASSERT(sy % block_width == 0);
ASSERT(dy % block_width == 0);
ASSERT(dx % block_width == 0);

sx /= block_width;
sy /= block_width;
dx /= block_width;
dy /= block_width;
width /= block_width;
height /= block_width;

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;
}
}


Expand All @@ -480,19 +565,16 @@ void _al_convert_bitmap_data(

/* Use memcpy if no conversion is needed. */
if (src_format == dst_format) {
int y;
int size = al_get_pixel_size(src_format);
const char *src_ptr = ((const char *)src) + sy * src_pitch + sx * size;
char *dst_ptr = ((char *)dst) + dy * dst_pitch + dx * size;
width *= size;
for (y = 0; y < height; y++) {
memcpy(dst_ptr, src_ptr, width);
src_ptr += src_pitch;
dst_ptr += dst_pitch;
}
_al_copy_bitmap_data(src, src_pitch, dst, dst_pitch, sx, sy,
dx, dy, width, height, src_format);
return;
}

/* Video-only formats don't have conversion functions, so they should have
* 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);
}
Expand All @@ -508,7 +590,10 @@ ALLEGRO_BITMAP *al_clone_bitmap(ALLEGRO_BITMAP *bitmap)
clone = al_create_bitmap(bitmap->w, bitmap->h);
if (!clone)
return NULL;
transfer_bitmap_data(bitmap, clone);
if (!transfer_bitmap_data(bitmap, clone)) {
al_destroy_bitmap(clone);
return NULL;
}
return clone;
}

Expand Down
4 changes: 3 additions & 1 deletion src/bitmap_draw.c
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
#include "allegro5/internal/aintern_bitmap.h"
#include "allegro5/internal/aintern_display.h"
#include "allegro5/internal/aintern_memblit.h"
#include "allegro5/internal/aintern_pixels.h"


static ALLEGRO_COLOR solid_white = {1, 1, 1, 1};
Expand All @@ -33,7 +34,8 @@ static void _bitmap_drawer(ALLEGRO_BITMAP *bitmap, ALLEGRO_COLOR tint,
ASSERT(bitmap != dest && bitmap != dest->parent);

/* If destination is memory, do a memory blit */
if (al_get_bitmap_flags(dest) & ALLEGRO_MEMORY_BITMAP) {
if (al_get_bitmap_flags(dest) & ALLEGRO_MEMORY_BITMAP
|| _al_pixel_format_is_compressed(al_get_bitmap_format(dest))) {
_al_draw_bitmap_region_memory(bitmap, tint, sx, sy, sw, sh, 0, 0, flags);
}
else {
Expand Down
117 changes: 107 additions & 10 deletions src/bitmap_lock.c
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,13 @@ ALLEGRO_LOCKED_REGION *al_lock_bitmap_region(ALLEGRO_BITMAP *bitmap,
ALLEGRO_LOCKED_REGION *lr;
int bitmap_format = al_get_bitmap_format(bitmap);
int bitmap_flags = al_get_bitmap_flags(bitmap);
int block_width = al_get_pixel_block_width(bitmap_format);
int xc, yc, wc, hc;
ASSERT(x >= 0);
ASSERT(y >= 0);
ASSERT(width >= 0);
ASSERT(height >= 0);
ASSERT(!_al_pixel_format_is_video_only(format));

/* For sub-bitmaps */
if (bitmap->parent) {
Expand All @@ -50,12 +53,25 @@ ALLEGRO_LOCKED_REGION *al_lock_bitmap_region(ALLEGRO_BITMAP *bitmap,
ASSERT(x+width <= bitmap->w);
ASSERT(y+height <= bitmap->h);

bitmap->lock_x = x;
bitmap->lock_y = y;
bitmap->lock_w = width;
bitmap->lock_h = height;
xc = (x / block_width) * block_width;
yc = (y / block_width) * block_width;
wc = _al_get_least_multiple(x + width, block_width) - xc;
hc = _al_get_least_multiple(y + height, block_width) - yc;

bitmap->lock_x = xc;
bitmap->lock_y = yc;
bitmap->lock_w = wc;
bitmap->lock_h = hc;
bitmap->lock_flags = flags;

if (flags == ALLEGRO_LOCK_WRITEONLY
&& (xc != x || yc != y || wc != width || hc != height)) {
/* Unaligned write-only access requires that we fill in the padding
* from the texture.
* XXX: In principle, this could be done more efficiently. */
flags = ALLEGRO_LOCK_READWRITE;
}

if (bitmap_flags & ALLEGRO_MEMORY_BITMAP) {
int f = _al_get_real_pixel_format(al_get_current_display(), format);
if (f < 0) {
Expand All @@ -64,32 +80,35 @@ ALLEGRO_LOCKED_REGION *al_lock_bitmap_region(ALLEGRO_BITMAP *bitmap,
ASSERT(bitmap->memory);
if (format == ALLEGRO_PIXEL_FORMAT_ANY || bitmap_format == format || bitmap_format == f) {
bitmap->locked_region.data = bitmap->memory
+ bitmap->pitch * y + x * al_get_pixel_size(bitmap_format);
+ bitmap->pitch * yc + xc * al_get_pixel_size(bitmap_format);
bitmap->locked_region.format = bitmap_format;
bitmap->locked_region.pitch = bitmap->pitch;
bitmap->locked_region.pixel_size = al_get_pixel_size(bitmap_format);
}
else {
bitmap->locked_region.pitch = al_get_pixel_size(f) * width;
bitmap->locked_region.data = al_malloc(bitmap->locked_region.pitch*height);
bitmap->locked_region.pitch = al_get_pixel_size(f) * wc;
bitmap->locked_region.data = al_malloc(bitmap->locked_region.pitch*hc);
bitmap->locked_region.format = f;
bitmap->locked_region.pixel_size = al_get_pixel_size(f);
if (!(bitmap->lock_flags & ALLEGRO_LOCK_WRITEONLY)) {
_al_convert_bitmap_data(
bitmap->memory, bitmap_format, bitmap->pitch,
bitmap->locked_region.data, f, bitmap->locked_region.pitch,
x, y, 0, 0, width, height);
xc, yc, 0, 0, wc, hc);
}
}
lr = &bitmap->locked_region;
}
else {
lr = bitmap->vt->lock_region(bitmap, x, y, width, height, format, flags);
lr = bitmap->vt->lock_region(bitmap, xc, yc, wc, hc, format, flags);
if (!lr) {
return NULL;
}
}

/* Fixup the data pointer for unaligned access */
lr->data = (char*)lr->data + (xc - x) * lr->pixel_size + (yc - y) * lr->pitch;

bitmap->locked = true;

return lr;
Expand All @@ -116,7 +135,10 @@ void al_unlock_bitmap(ALLEGRO_BITMAP *bitmap)
}

if (!(al_get_bitmap_flags(bitmap) & ALLEGRO_MEMORY_BITMAP)) {
bitmap->vt->unlock_region(bitmap);
if (_al_pixel_format_is_compressed(bitmap->locked_region.format))
bitmap->vt->unlock_compressed_region(bitmap);
else
bitmap->vt->unlock_region(bitmap);
}
else {
if (bitmap->locked_region.format != 0 && bitmap->locked_region.format != bitmap_format) {
Expand All @@ -141,5 +163,80 @@ bool al_is_bitmap_locked(ALLEGRO_BITMAP *bitmap)
return bitmap->locked;
}

/* Function: al_lock_bitmap_region_blocked
*/
ALLEGRO_LOCKED_REGION *al_lock_bitmap_blocked(ALLEGRO_BITMAP *bitmap,
int flags)
{
int bitmap_format = al_get_bitmap_format(bitmap);
int block_width = al_get_pixel_block_width(bitmap_format);

return al_lock_bitmap_region_blocked(bitmap, 0, 0,
_al_get_least_multiple(bitmap->w, block_width) / block_width,
_al_get_least_multiple(bitmap->h, block_width) / block_width,
flags);
}

/* Function: al_lock_bitmap_region_blocked
*/
ALLEGRO_LOCKED_REGION *al_lock_bitmap_region_blocked(ALLEGRO_BITMAP *bitmap,
int x_block, int y_block, int width_block, int height_block, int flags)
{
ALLEGRO_LOCKED_REGION *lr;
int bitmap_format = al_get_bitmap_format(bitmap);
int bitmap_flags = al_get_bitmap_flags(bitmap);
int block_width = al_get_pixel_block_width(bitmap_format);
ASSERT(x_block >= 0);
ASSERT(y_block >= 0);
ASSERT(width_block >= 0);
ASSERT(height_block >= 0);

if (block_width == 1 && !_al_pixel_format_is_video_only(bitmap_format)) {
return al_lock_bitmap_region(bitmap, x_block, y_block, width_block,
height_block, bitmap_format, flags);
}

/* Currently, this is the only format that gets to this point */
ASSERT(_al_pixel_format_is_compressed(bitmap_format));
ASSERT(!(bitmap_flags & ALLEGRO_MEMORY_BITMAP));

/* For sub-bitmaps */
if (bitmap->parent) {
if (bitmap->xofs % block_width != 0
|| bitmap->yofs % block_width != 0) {
return NULL;
}
x_block += bitmap->xofs / block_width;
y_block += bitmap->yofs / block_width;
bitmap = bitmap->parent;
}

if (bitmap->locked)
return NULL;

if (!(flags & ALLEGRO_LOCK_READONLY))
bitmap->dirty = true;

ASSERT(x_block + width_block
<= _al_get_least_multiple(bitmap->w, block_width) / block_width);
ASSERT(y_block + height_block
<= _al_get_least_multiple(bitmap->h, block_width) / block_width);

bitmap->lock_x = x_block * block_width;
bitmap->lock_y = y_block * block_width;
bitmap->lock_w = width_block * block_width;
bitmap->lock_h = height_block * block_width;
bitmap->lock_flags = flags;

lr = bitmap->vt->lock_compressed_region(bitmap, bitmap->lock_x,
bitmap->lock_y, bitmap->lock_w, bitmap->lock_h, flags);
if (!lr) {
return NULL;
}

bitmap->locked = true;

return lr;
}

/* vim: set ts=8 sts=3 sw=3 et: */
31 changes: 16 additions & 15 deletions src/bitmap_pixel.c
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,7 @@ ALLEGRO_COLOR al_get_pixel(ALLEGRO_BITMAP *bitmap, int x, int y)
{
ALLEGRO_LOCKED_REGION *lr;
char *data;
ALLEGRO_COLOR color;
int bitmap_format = al_get_bitmap_format(bitmap);
ALLEGRO_COLOR color = al_map_rgba_f(0, 0, 0, 0);

if (bitmap->parent) {
x += bitmap->xofs;
Expand All @@ -38,11 +37,14 @@ ALLEGRO_COLOR al_get_pixel(ALLEGRO_BITMAP *bitmap, int x, int y)
}

if (bitmap->locked) {
if (_al_pixel_format_is_video_only(bitmap->locked_region.format)) {
ALLEGRO_ERROR("Invalid lock format.");
return color;
}
x -= bitmap->lock_x;
y -= bitmap->lock_y;
if (x < 0 || y < 0 || x >= bitmap->lock_w || y >= bitmap->lock_h) {
ALLEGRO_ERROR("Out of bounds.");
memset(&color, 0, sizeof(ALLEGRO_COLOR));
return color;
}

Expand All @@ -55,21 +57,18 @@ ALLEGRO_COLOR al_get_pixel(ALLEGRO_BITMAP *bitmap, int x, int y)
else {
/* FIXME: must use clip not full bitmap */
if (x < 0 || y < 0 || x >= bitmap->w || y >= bitmap->h) {
memset(&color, 0, sizeof(ALLEGRO_COLOR));
return color;
}

if (!(lr = al_lock_bitmap_region(bitmap, x, y, 1, 1, bitmap_format,
ALLEGRO_LOCK_READONLY)))
{
memset(&color, 0, sizeof(ALLEGRO_COLOR));
if (!(lr = al_lock_bitmap_region(bitmap, x, y, 1, 1,
ALLEGRO_PIXEL_FORMAT_ANY, ALLEGRO_LOCK_READONLY))) {
return color;
}

/* FIXME: check for valid pixel format */

data = lr->data;
_AL_INLINE_GET_PIXEL(bitmap_format, data, color, false);
_AL_INLINE_GET_PIXEL(lr->format, data, color, false);

al_unlock_bitmap(bitmap);
}
Expand All @@ -82,7 +81,6 @@ void _al_put_pixel(ALLEGRO_BITMAP *bitmap, int x, int y, ALLEGRO_COLOR color)
{
ALLEGRO_LOCKED_REGION *lr;
char *data;
int bitmap_format = al_get_bitmap_format(bitmap);

if (bitmap->parent) {
x += bitmap->xofs;
Expand All @@ -91,12 +89,15 @@ void _al_put_pixel(ALLEGRO_BITMAP *bitmap, int x, int y, ALLEGRO_COLOR color)
}

if (x < bitmap->cl || y < bitmap->ct ||
x >= bitmap->cr_excl || y >= bitmap->cb_excl)
{
x >= bitmap->cr_excl || y >= bitmap->cb_excl) {
return;
}

if (bitmap->locked) {
if (_al_pixel_format_is_video_only(bitmap->locked_region.format)) {
ALLEGRO_ERROR("Invalid lock format.");
return;
}
x -= bitmap->lock_x;
y -= bitmap->lock_y;
if (x < 0 || y < 0 || x >= bitmap->lock_w || y >= bitmap->lock_h) {
Expand All @@ -110,15 +111,15 @@ void _al_put_pixel(ALLEGRO_BITMAP *bitmap, int x, int y, ALLEGRO_COLOR color)
_AL_INLINE_PUT_PIXEL(bitmap->locked_region.format, data, color, false);
}
else {
lr = al_lock_bitmap_region(bitmap, x, y, 1, 1, bitmap_format,
ALLEGRO_LOCK_WRITEONLY);
lr = al_lock_bitmap_region(bitmap, x, y, 1, 1,
ALLEGRO_PIXEL_FORMAT_ANY, ALLEGRO_LOCK_WRITEONLY);
if (!lr)
return;

/* FIXME: check for valid pixel format */

data = lr->data;
_AL_INLINE_PUT_PIXEL(bitmap_format, data, color, false);
_AL_INLINE_PUT_PIXEL(lr->format, data, color, false);

al_unlock_bitmap(bitmap);
}
Expand Down
23 changes: 22 additions & 1 deletion src/convert.c
Original file line number Diff line number Diff line change
Expand Up @@ -8062,6 +8062,7 @@ void (*_al_convert_funcs[ALLEGRO_NUM_PIXEL_FORMATS]
argb_8888_to_abgr_8888_le,
argb_8888_to_rgba_4444,
argb_8888_to_single_channel_8,
NULL, NULL, NULL,
},
{
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
Expand All @@ -8084,6 +8085,7 @@ void (*_al_convert_funcs[ALLEGRO_NUM_PIXEL_FORMATS]
rgba_8888_to_abgr_8888_le,
rgba_8888_to_rgba_4444,
rgba_8888_to_single_channel_8,
NULL, NULL, NULL,
},
{
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
Expand All @@ -8106,6 +8108,7 @@ void (*_al_convert_funcs[ALLEGRO_NUM_PIXEL_FORMATS]
argb_4444_to_abgr_8888_le,
argb_4444_to_rgba_4444,
argb_4444_to_single_channel_8,
NULL, NULL, NULL,
},
{
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
Expand All @@ -8128,6 +8131,7 @@ void (*_al_convert_funcs[ALLEGRO_NUM_PIXEL_FORMATS]
rgb_888_to_abgr_8888_le,
rgb_888_to_rgba_4444,
rgb_888_to_single_channel_8,
NULL, NULL, NULL,
},
{
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
Expand All @@ -8150,6 +8154,7 @@ void (*_al_convert_funcs[ALLEGRO_NUM_PIXEL_FORMATS]
rgb_565_to_abgr_8888_le,
rgb_565_to_rgba_4444,
rgb_565_to_single_channel_8,
NULL, NULL, NULL,
},
{
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
Expand All @@ -8172,6 +8177,7 @@ void (*_al_convert_funcs[ALLEGRO_NUM_PIXEL_FORMATS]
rgb_555_to_abgr_8888_le,
rgb_555_to_rgba_4444,
rgb_555_to_single_channel_8,
NULL, NULL, NULL,
},
{
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
Expand All @@ -8194,6 +8200,7 @@ void (*_al_convert_funcs[ALLEGRO_NUM_PIXEL_FORMATS]
rgba_5551_to_abgr_8888_le,
rgba_5551_to_rgba_4444,
rgba_5551_to_single_channel_8,
NULL, NULL, NULL,
},
{
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
Expand All @@ -8216,6 +8223,7 @@ void (*_al_convert_funcs[ALLEGRO_NUM_PIXEL_FORMATS]
argb_1555_to_abgr_8888_le,
argb_1555_to_rgba_4444,
argb_1555_to_single_channel_8,
NULL, NULL, NULL,
},
{
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
Expand All @@ -8238,6 +8246,7 @@ void (*_al_convert_funcs[ALLEGRO_NUM_PIXEL_FORMATS]
abgr_8888_to_abgr_8888_le,
abgr_8888_to_rgba_4444,
abgr_8888_to_single_channel_8,
NULL, NULL, NULL,
},
{
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
Expand All @@ -8260,6 +8269,7 @@ void (*_al_convert_funcs[ALLEGRO_NUM_PIXEL_FORMATS]
xbgr_8888_to_abgr_8888_le,
xbgr_8888_to_rgba_4444,
xbgr_8888_to_single_channel_8,
NULL, NULL, NULL,
},
{
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
Expand All @@ -8282,6 +8292,7 @@ void (*_al_convert_funcs[ALLEGRO_NUM_PIXEL_FORMATS]
bgr_888_to_abgr_8888_le,
bgr_888_to_rgba_4444,
bgr_888_to_single_channel_8,
NULL, NULL, NULL,
},
{
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
Expand All @@ -8304,6 +8315,7 @@ void (*_al_convert_funcs[ALLEGRO_NUM_PIXEL_FORMATS]
bgr_565_to_abgr_8888_le,
bgr_565_to_rgba_4444,
bgr_565_to_single_channel_8,
NULL, NULL, NULL,
},
{
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
Expand All @@ -8326,6 +8338,7 @@ void (*_al_convert_funcs[ALLEGRO_NUM_PIXEL_FORMATS]
bgr_555_to_abgr_8888_le,
bgr_555_to_rgba_4444,
bgr_555_to_single_channel_8,
NULL, NULL, NULL,
},
{
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
Expand All @@ -8348,6 +8361,7 @@ void (*_al_convert_funcs[ALLEGRO_NUM_PIXEL_FORMATS]
rgbx_8888_to_abgr_8888_le,
rgbx_8888_to_rgba_4444,
rgbx_8888_to_single_channel_8,
NULL, NULL, NULL,
},
{
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
Expand All @@ -8370,6 +8384,7 @@ void (*_al_convert_funcs[ALLEGRO_NUM_PIXEL_FORMATS]
xrgb_8888_to_abgr_8888_le,
xrgb_8888_to_rgba_4444,
xrgb_8888_to_single_channel_8,
NULL, NULL, NULL,
},
{
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
Expand All @@ -8392,6 +8407,7 @@ void (*_al_convert_funcs[ALLEGRO_NUM_PIXEL_FORMATS]
abgr_f32_to_abgr_8888_le,
abgr_f32_to_rgba_4444,
abgr_f32_to_single_channel_8,
NULL, NULL, NULL,
},
{
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
Expand All @@ -8414,6 +8430,7 @@ void (*_al_convert_funcs[ALLEGRO_NUM_PIXEL_FORMATS]
NULL,
abgr_8888_le_to_rgba_4444,
abgr_8888_le_to_single_channel_8,
NULL, NULL, NULL,
},
{
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
Expand All @@ -8436,6 +8453,7 @@ void (*_al_convert_funcs[ALLEGRO_NUM_PIXEL_FORMATS]
rgba_4444_to_abgr_8888_le,
NULL,
rgba_4444_to_single_channel_8,
NULL, NULL, NULL,
},
{
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
Expand All @@ -8457,8 +8475,11 @@ void (*_al_convert_funcs[ALLEGRO_NUM_PIXEL_FORMATS]
single_channel_8_to_abgr_f32,
single_channel_8_to_abgr_8888_le,
single_channel_8_to_rgba_4444,
NULL,
NULL, NULL, NULL, NULL,
},
{NULL},
{NULL},
{NULL},
};

// Warning: This file was created by make_converters.py - do not edit.
3 changes: 2 additions & 1 deletion src/drawing.c
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,8 @@ void al_draw_pixel(float x, float y, ALLEGRO_COLOR color)

ASSERT(target);

if (al_get_bitmap_flags(target) & ALLEGRO_MEMORY_BITMAP) {
if (al_get_bitmap_flags(target) & ALLEGRO_MEMORY_BITMAP
|| _al_pixel_format_is_compressed(al_get_bitmap_format(target))) {
_al_draw_pixel_memory(target, x, y, &color);
}
else {
Expand Down
8 changes: 4 additions & 4 deletions src/memblit.c
Original file line number Diff line number Diff line change
Expand Up @@ -281,20 +281,20 @@ static void _al_draw_bitmap_region_memory_fast(ALLEGRO_BITMAP *bitmap,
CLIPPER(bitmap, sx, sy, sw, sh, dest, dx, dy, dw, dh, 1, 1, flags)

if (!(src_region = al_lock_bitmap_region(bitmap, sx, sy, sw, sh,
al_get_bitmap_format(bitmap), ALLEGRO_LOCK_READONLY))) {
ALLEGRO_PIXEL_FORMAT_ANY, ALLEGRO_LOCK_READONLY))) {
return;
}

if (!(dst_region = al_lock_bitmap_region(dest, dx, dy, sw, sh,
al_get_bitmap_format(dest), ALLEGRO_LOCK_WRITEONLY))) {
ALLEGRO_PIXEL_FORMAT_ANY, ALLEGRO_LOCK_WRITEONLY))) {
al_unlock_bitmap(bitmap);
return;
}

/* will detect if no conversion is needed */
_al_convert_bitmap_data(
src_region->data, al_get_bitmap_format(bitmap), src_region->pitch,
dst_region->data, al_get_bitmap_format(dest), dst_region->pitch,
src_region->data, src_region->format, src_region->pitch,
dst_region->data, dst_region->format, dst_region->pitch,
0, 0, 0, 0, sw, sh);

al_unlock_bitmap(bitmap);
Expand Down
336 changes: 322 additions & 14 deletions src/opengl/ogl_bitmap.c

Large diffs are not rendered by default.

11 changes: 10 additions & 1 deletion src/opengl/ogl_lock.c
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,16 @@ ALLEGRO_LOCKED_REGION *_al_ogl_lock_region_new(ALLEGRO_BITMAP *bitmap,
bool ok;

if (format == ALLEGRO_PIXEL_FORMAT_ANY) {
format = al_get_bitmap_format(bitmap);
/* Never pick compressed formats with ANY, as it interacts weirdly with
* existing code (e.g. al_get_pixel_size() etc) */
int bitmap_format = al_get_bitmap_format(bitmap);
if (_al_pixel_format_is_compressed(bitmap_format)) {
// XXX Get a good format from the driver?
format = ALLEGRO_PIXEL_FORMAT_ABGR_8888_LE;
}
else {
format = bitmap_format;
}
}

disp = al_get_current_display();
Expand Down
11 changes: 10 additions & 1 deletion src/opengl/ogl_lock_es.c
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,16 @@ ALLEGRO_LOCKED_REGION *_al_ogl_lock_region_gles(ALLEGRO_BITMAP *bitmap,
int real_format;

if (format == ALLEGRO_PIXEL_FORMAT_ANY) {
format = al_get_bitmap_format(bitmap);
/* Never pick compressed formats with ANY, as it interacts weirdly with
* existing code (e.g. al_get_pixel_size() etc) */
int bitmap_format = al_get_bitmap_format(bitmap);
if (_al_pixel_format_is_compressed(bitmap_format)) {
// XXX Get a good format from the driver?
format = ALLEGRO_PIXEL_FORMAT_ABGR_8888_LE;
}
else {
format = bitmap_format;
}
}

disp = al_get_current_display();
Expand Down
185 changes: 185 additions & 0 deletions src/pixels.c
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,9 @@ static int pixel_sizes[] = {
4, /* ALLEGRO_PIXEL_FORMAT_ABGR_LE */
2, /* ALLEGRO_PIXEL_FORMAT_RGBA_4444 */
1, /* ALLEGRO_PIXEL_FORMAT_SINGLE_CHANNEL_8 */
0,
0,
0,
};

static int pixel_bits[] = {
Expand Down Expand Up @@ -88,6 +91,77 @@ static int pixel_bits[] = {
32, /* ALLEGRO_PIXEL_FORMAT_ABGR_LE */
16, /* ALLEGRO_PIXEL_FORMAT_RGBA_4444 */
8, /* ALLEGRO_PIXEL_FORMAT_SINGLE_CHANNEL_8 */
0,
0,
0,
};

static int pixel_block_widths[] = {
0, /* ALLEGRO_PIXEL_FORMAT_ANY */
0,
0,
1,
1,
1,
1,
1,
1,
1, /* ALLEGRO_PIXEL_FORMAT_ARGB_8888 */
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1, /* ALLEGRO_PIXEL_FORMAT_ABGR_F32 */
1, /* ALLEGRO_PIXEL_FORMAT_ABGR_LE */
1, /* ALLEGRO_PIXEL_FORMAT_RGBA_4444 */
1, /* ALLEGRO_PIXEL_FORMAT_SINGLE_CHANNEL_8 */
4,
4,
4,
};

static int pixel_block_sizes[] = {
0, /* ALLEGRO_PIXEL_FORMAT_ANY */
0,
0,
2,
2,
2,
3,
4,
4,
4, /* ALLEGRO_PIXEL_FORMAT_ARGB_8888 */
4,
2,
3,
2,
2,
2,
2,
4,
4,
3,
2,
2,
4,
4,
16, /* ALLEGRO_PIXEL_FORMAT_ABGR_F32 */
4, /* ALLEGRO_PIXEL_FORMAT_ABGR_LE */
2, /* ALLEGRO_PIXEL_FORMAT_RGBA_4444 */
1, /* ALLEGRO_PIXEL_FORMAT_SINGLE_CHANNEL_8 */
8,
16,
16,
};

static bool format_alpha_table[ALLEGRO_NUM_PIXEL_FORMATS] = {
Expand Down Expand Up @@ -119,6 +193,9 @@ static bool format_alpha_table[ALLEGRO_NUM_PIXEL_FORMATS] = {
true, /* ALLEGRO_PIXEL_FORMAT_ABGR_LE */
true, /* ALLEGRO_PIXEL_FORMAT_RGBA_4444 */
false, /* ALLEGRO_PIXEL_FORMAT_SINGLE_CHANNEL_8 */
true,
true,
true,
};

static char const *pixel_format_names[ALLEGRO_NUM_PIXEL_FORMATS + 1] = {
Expand Down Expand Up @@ -150,6 +227,9 @@ static char const *pixel_format_names[ALLEGRO_NUM_PIXEL_FORMATS + 1] = {
"ABGR_8888_LE",
"RGBA_4444",
"SINGLE_CHANNEL_8",
"RGBA_DXT1",
"RGBA_DXT3",
"RGBA_DXT5",
"INVALID"
};

Expand Down Expand Up @@ -183,6 +263,79 @@ static bool format_is_real[ALLEGRO_NUM_PIXEL_FORMATS] =
true, /* ALLEGRO_PIXEL_FORMAT_ABGR_LE */
true, /* ALLEGRO_PIXEL_FORMAT_RGBA_4444 */
true, /* ALLEGRO_PIXEL_FORMAT_SINGLE_CHANNEL_8 */
true,
true,
true,
};

static bool format_is_video_only[ALLEGRO_NUM_PIXEL_FORMATS] =
{
false, /* ALLEGRO_PIXEL_FORMAT_ANY */
false,
false,
false,
false,
false,
false,
false,
false,
false, /* ALLEGRO_PIXEL_FORMAT_ARGB_8888 */
false,
false,
false,
false,
false,
false,
false,
false,
false,
false,
false,
false,
false,
false,
false, /* ALLEGRO_PIXEL_FORMAT_ABGR_F32 */
false, /* ALLEGRO_PIXEL_FORMAT_ABGR_LE */
false, /* ALLEGRO_PIXEL_FORMAT_RGBA_4444 */
false, /* ALLEGRO_PIXEL_FORMAT_SINGLE_CHANNEL_8 */
true,
true,
true,
};

static bool format_is_compressed[ALLEGRO_NUM_PIXEL_FORMATS] =
{
false, /* ALLEGRO_PIXEL_FORMAT_ANY */
false,
false,
false,
false,
false,
false,
false,
false,
false, /* ALLEGRO_PIXEL_FORMAT_ARGB_8888 */
false,
false,
false,
false,
false,
false,
false,
false,
false,
false,
false,
false,
false,
false,
false, /* ALLEGRO_PIXEL_FORMAT_ABGR_F32 */
false, /* ALLEGRO_PIXEL_FORMAT_ABGR_LE */
false, /* ALLEGRO_PIXEL_FORMAT_RGBA_4444 */
false, /* ALLEGRO_PIXEL_FORMAT_SINGLE_CHANNEL_8 */
true,
true,
true,
};


Expand All @@ -206,6 +359,22 @@ void _al_init_pixels(void)
}


/* Function: al_get_pixel_block_size
*/
int al_get_pixel_block_size(int format)
{
return pixel_block_sizes[format];
}


/* Function: al_get_pixel_block_width
*/
int al_get_pixel_block_width(int format)
{
return pixel_block_widths[format];
}


/* Function: al_get_pixel_size
*/
int al_get_pixel_size(int format)
Expand Down Expand Up @@ -236,6 +405,22 @@ bool _al_pixel_format_is_real(int format)
return format_is_real[format];
}

bool _al_pixel_format_is_video_only(int format)
{
ASSERT(format >= 0);
ASSERT(format < ALLEGRO_NUM_PIXEL_FORMATS);

return format_is_video_only[format];
}

bool _al_pixel_format_is_compressed(int format)
{
ASSERT(format >= 0);
ASSERT(format < ALLEGRO_NUM_PIXEL_FORMATS);

return format_is_compressed[format];
}


/* We use al_get_display_format() as a hint for the preferred RGB ordering when
* nothing else is specified.
Expand Down
3 changes: 2 additions & 1 deletion src/tri_soft.c
Original file line number Diff line number Diff line change
Expand Up @@ -829,7 +829,8 @@ void _al_draw_soft_triangle(
min_y = clip_min_y;

if (al_is_bitmap_locked(target)) {
if (!bitmap_region_is_locked(target, min_x, min_y, max_x - min_x, max_y - min_y))
if (!bitmap_region_is_locked(target, min_x, min_y, max_x - min_x, max_y - min_y)
|| _al_pixel_format_is_video_only(target->locked_region.format))
return;
} else {
if (!(lr = al_lock_bitmap_region(target, min_x, min_y, max_x - min_x, max_y - min_y, ALLEGRO_PIXEL_FORMAT_ANY, 0)))
Expand Down
375 changes: 255 additions & 120 deletions src/win/d3d_bmp.cpp

Large diffs are not rendered by default.

129 changes: 129 additions & 0 deletions src/win/d3d_d3dx9.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
/* ______ ___ ___
* /\ _ \ /\_ \ /\_ \
* \ \ \L\ \\//\ \ \//\ \ __ __ _ __ ___
* \ \ __ \ \ \ \ \ \ \ /'__`\ /'_ `\/\`'__\/ __`\
* \ \ \/\ \ \_\ \_ \_\ \_/\ __//\ \L\ \ \ \//\ \L\ \
* \ \_\ \_\/\____\/\____\ \____\ \____ \ \_\\ \____/
* \/_/\/_/\/____/\/____/\/____/\/___L\ \/_/ \/___/
* /\____/
* \_/__/
*
* D3DX9 DLL dynamic loading.
*
* See LICENSE.txt for copyright information.
*/

#include "allegro5/allegro.h"
#include "allegro5/allegro_direct3d.h"
#include "allegro5/internal/aintern.h"
#include "allegro5/internal/aintern_bitmap.h"
#include "allegro5/internal/aintern_direct3d.h"

#ifdef ALLEGRO_CFG_D3DX9

#include <d3dx9.h>
#include <stdio.h>

#include "d3d.h"

ALLEGRO_DEBUG_CHANNEL("d3dx9")

// DXSDK redistributable install d3dx9_xx.dll from version
// 24 to 43. It's unlikely that any new version will come,
// since HLSL compiler was moved to D3DCompiler_xx.dll and
// most recent versions of this utility library are bound
// to DirectX 11.
//
// However, if any new version appears anyway, this range
// should be changed.
#define D3DX9_MIN_VERSION 24
#define D3DX9_MAX_VERSION 43

static HMODULE _imp_d3dx9_module = 0;
_ALLEGRO_D3DXCREATEEFFECTPROC _al_imp_D3DXCreateEffect = NULL;
_ALLEGRO_D3DXLSFLSPROC _al_imp_D3DXLoadSurfaceFromSurface = NULL;

extern "C"
void _al_unload_d3dx9_module()
{
FreeLibrary(_imp_d3dx9_module);
_imp_d3dx9_module = NULL;
}

static bool _imp_load_d3dx9_module_version(int version)
{
char module_name[16];

// Sanity check
// Comented out, to not reject choice of the user if any new version
// appears. See force_d3dx9_version entry in config file.
/*
if (version < 24 || version > 43) {
ALLEGRO_ERROR("Error: Requested version (%d) of D3DX9 library is invalid.\n", version);
return false;
}
*/

sprintf(module_name, "d3dx9_%d.dll", version);

_imp_d3dx9_module = _al_win_safe_load_library(module_name);
if (NULL == _imp_d3dx9_module)
return false;

_al_imp_D3DXCreateEffect = (_ALLEGRO_D3DXCREATEEFFECTPROC)GetProcAddress(_imp_d3dx9_module, "D3DXCreateEffect");
if (NULL == _al_imp_D3DXCreateEffect) {
FreeLibrary(_imp_d3dx9_module);
_imp_d3dx9_module = NULL;
return false;
}

_al_imp_D3DXLoadSurfaceFromSurface =
(_ALLEGRO_D3DXLSFLSPROC)GetProcAddress(_imp_d3dx9_module, "D3DXLoadSurfaceFromSurface");
if (NULL == _al_imp_D3DXLoadSurfaceFromSurface) {
FreeLibrary(_imp_d3dx9_module);
_imp_d3dx9_module = NULL;
return false;
}

ALLEGRO_INFO("Module \"%s\" loaded.\n", module_name);

return true;
}

extern "C"
bool _al_load_d3dx9_module()
{
ALLEGRO_CONFIG *cfg;
long version;

if (_imp_d3dx9_module) {
return true;
}

cfg = al_get_system_config();
if (cfg) {
char const *value = al_get_config_value(cfg,
"shader", "force_d3dx9_version");
if (value) {
errno = 0;
version = strtol(value, NULL, 10);
if (errno) {
ALLEGRO_ERROR("Failed to override D3DX9 version. \"%s\" is not valid integer number.", value);
return false;
}
else
return _imp_load_d3dx9_module_version((int)version);
}
}

// Iterate over all valid versions.
for (version = D3DX9_MAX_VERSION; version >= D3DX9_MIN_VERSION; version--)
if (_imp_load_d3dx9_module_version((int)version))
return true;

ALLEGRO_ERROR("Failed to load D3DX9 library. Library is not installed.");

return false;
}

#endif
48 changes: 44 additions & 4 deletions src/win/d3d_disp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,8 @@ static bool already_fullscreen = false; /* real fullscreen */
static ALLEGRO_MUTEX *present_mutex;
ALLEGRO_MUTEX *_al_d3d_lost_device_mutex;

#define FOURCC(c0, c1, c2, c3) ((int)(c0) | ((int)(c1) << 8) | ((int)(c2) << 16) | ((int)(c3) << 24))

/*
* These parameters cannot be gotten by the display thread because
* they're thread local. We get them in the calling thread first.
Expand Down Expand Up @@ -111,6 +113,9 @@ static int allegro_formats[] = {
//ALLEGRO_PIXEL_FORMAT_ARGB_1555, this format seems not to be allowed
ALLEGRO_PIXEL_FORMAT_ABGR_F32,
ALLEGRO_PIXEL_FORMAT_SINGLE_CHANNEL_8,
ALLEGRO_PIXEL_FORMAT_RGBA_DXT1,
ALLEGRO_PIXEL_FORMAT_RGBA_DXT3,
ALLEGRO_PIXEL_FORMAT_RGBA_DXT5,
-1
};

Expand All @@ -132,6 +137,9 @@ static int d3d_formats[] = {
//D3DFMT_A1R5G5B5,
D3DFMT_A32B32G32R32F,
D3DFMT_L8,
FOURCC('D', 'X', 'T', '1'),
FOURCC('D', 'X', 'T', '3'),
FOURCC('D', 'X', 'T', '5'),
-1
};

Expand Down Expand Up @@ -1203,6 +1211,14 @@ static BOOL IsTextureFormatOk(D3DFORMAT TextureFormat, D3DFORMAT AdapterFormat)
return SUCCEEDED(hr);
}

/* Same as above, but using Allegro's formats */
static bool is_texture_format_ok(ALLEGRO_DISPLAY *display, int texture_format)
{
ALLEGRO_DISPLAY_D3D *d3d_display = (ALLEGRO_DISPLAY_D3D*)display;
return IsTextureFormatOk((D3DFORMAT)_al_pixel_format_to_d3d(texture_format),
(D3DFORMAT)_al_pixel_format_to_d3d(d3d_display->format));
}

static int real_choose_bitmap_format(ALLEGRO_DISPLAY_D3D *d3d_display,
int bits, bool alpha)
{
Expand Down Expand Up @@ -1765,6 +1781,7 @@ static ALLEGRO_DISPLAY_D3D *d3d_create_display_internals(
d3d_display->backbuffer_bmp_extra.is_backbuffer = true;
d3d_display->backbuffer_bmp._display = al_display;
d3d_display->backbuffer_bmp._format = _al_deduce_color_format(&al_display->extra_settings);
d3d_display->backbuffer_bmp._memory_format = d3d_display->backbuffer_bmp._format;
d3d_display->backbuffer_bmp._flags = ALLEGRO_VIDEO_BITMAP;
d3d_display->backbuffer_bmp.w = al_display->w;
d3d_display->backbuffer_bmp.h = al_display->h;
Expand Down Expand Up @@ -2374,24 +2391,46 @@ static ALLEGRO_BITMAP *d3d_create_bitmap(ALLEGRO_DISPLAY *d,
}

if (_al_pixel_format_to_d3d(format) < 0) {
ALLEGRO_ERROR("Requested bitmap format not supported (%d).\n", format);
ALLEGRO_ERROR("Requested bitmap format not supported (%s).\n",
_al_pixel_format_name((ALLEGRO_PIXEL_FORMAT)format));
return NULL;
}

if (!is_texture_format_ok(d, format)) {
ALLEGRO_ERROR("Requested bitmap format not supported (%s).\n",
_al_pixel_format_name((ALLEGRO_PIXEL_FORMAT)format));
return NULL;
}

bool compressed = _al_pixel_format_is_compressed(format);
if (compressed) {
if (!_al_d3d_render_to_texture_supported()) {
/* Not implemented. XXX: Why not? */
return NULL;
}
if (!_al_imp_D3DXLoadSurfaceFromSurface) {
/* Need this for locking compressed bitmaps */
return NULL;
}
}
int block_width = al_get_pixel_block_width(format);
int block_size = al_get_pixel_block_size(format);

ALLEGRO_INFO("Chose bitmap format %d\n", format);

bitmap = (ALLEGRO_BITMAP *)al_malloc(sizeof *bitmap);
ASSERT(bitmap);
memset(bitmap, 0, sizeof(*bitmap));

bitmap->vt = _al_bitmap_d3d_driver();
bitmap->memory = NULL;
bitmap->_format = format;
bitmap->_flags = flags;
bitmap->pitch = w * al_get_pixel_size(format);
al_identity_transform(&bitmap->transform);

bitmap->memory = (unsigned char *)al_malloc(bitmap->pitch * h);
bitmap->pitch =
_al_get_least_multiple(w, block_width) / block_width * block_size;
bitmap->memory = (unsigned char *)al_malloc(
bitmap->pitch * _al_get_least_multiple(h, block_width) / block_width);

extra = (ALLEGRO_BITMAP_EXTRA_D3D *)al_calloc(1, sizeof *extra);
bitmap->extra = extra;
Expand All @@ -2400,6 +2439,7 @@ static ALLEGRO_BITMAP *d3d_create_bitmap(ALLEGRO_DISPLAY *d,
extra->initialized = false;
extra->is_backbuffer = false;
extra->render_target = NULL;
extra->system_format = compressed ? ALLEGRO_PIXEL_FORMAT_ARGB_8888 : format;

extra->display = (ALLEGRO_DISPLAY_D3D *)d;

Expand Down
99 changes: 2 additions & 97 deletions src/win/d3d_shader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -40,26 +40,6 @@ struct ALLEGRO_SHADER_HLSL_S
LPD3DXEFFECT hlsl_shader;
};


// DXSDK redistributable install d3dx9_xx.dll from version
// 24 to 43. It's unlikely that any new version will come,
// since HLSL compiler was moved to D3DCompiler_xx.dll and
// most recent versions of this utility library are bound
// to DirectX 11.
//
// However, if any new version appears anyway, this range
// should be changed.
#define D3DX9_MIN_VERSION 24
#define D3DX9_MAX_VERSION 43

typedef HRESULT (WINAPI *D3DXCREATEEFFECTPROC)(LPDIRECT3DDEVICE9, LPCVOID, UINT,
CONST D3DXMACRO*, LPD3DXINCLUDE, DWORD, LPD3DXEFFECTPOOL, LPD3DXEFFECT*,
LPD3DXBUFFER*);

static HMODULE _imp_d3dx9_module = 0;
static D3DXCREATEEFFECTPROC _imp_D3DXCreateEffect = NULL;


static const char *null_source = "";

static const char *technique_source_vertex =
Expand Down Expand Up @@ -92,81 +72,6 @@ static const char *technique_source_both =
" }\n"
"}\n";

extern "C"
void _al_unload_d3dx9_module()
{
FreeLibrary(_imp_d3dx9_module);
_imp_d3dx9_module = NULL;
}

static bool _imp_load_d3dx9_module_version(int version)
{
char module_name[16];

// Sanity check
// Comented out, to not reject choice of the user if any new version
// appears. See force_d3dx9_version entry in config file.
/*
if (version < 24 || version > 43) {
ALLEGRO_ERROR("Error: Requested version (%d) of D3DX9 library is invalid.\n", version);
return false;
}
*/

sprintf(module_name, "d3dx9_%d.dll", version);

_imp_d3dx9_module = _al_win_safe_load_library(module_name);
if (NULL == _imp_d3dx9_module)
return false;

_imp_D3DXCreateEffect = (D3DXCREATEEFFECTPROC)GetProcAddress(_imp_d3dx9_module, "D3DXCreateEffect");
if (NULL == _imp_D3DXCreateEffect) {
FreeLibrary(_imp_d3dx9_module);
_imp_d3dx9_module = NULL;
return false;
}

ALLEGRO_INFO("Module \"%s\" loaded.\n", module_name);

return true;
}

extern "C"
bool _al_load_d3dx9_module()
{
ALLEGRO_CONFIG *cfg;
long version;

if (_imp_d3dx9_module) {
return true;
}

cfg = al_get_system_config();
if (cfg) {
char const *value = al_get_config_value(cfg,
"shader", "force_d3dx9_version");
if (value) {
errno = 0;
version = strtol(value, NULL, 10);
if (errno) {
ALLEGRO_ERROR("Failed to override D3DX9 version. \"%s\" is not valid integer number.", value);
return false;
}
else
return _imp_load_d3dx9_module_version((int)version);
}
}

// Iterate over all valid versions.
for (version = D3DX9_MAX_VERSION; version >= D3DX9_MIN_VERSION; version--)
if (_imp_load_d3dx9_module_version((int)version))
return true;

ALLEGRO_ERROR("Failed to load D3DX9 library. Library is not installed.");

return false;
}


static bool hlsl_attach_shader_source(ALLEGRO_SHADER *shader,
ALLEGRO_SHADER_TYPE type, const char *source);
Expand Down Expand Up @@ -236,7 +141,7 @@ ALLEGRO_SHADER *_al_create_shader_hlsl(ALLEGRO_SHADER_PLATFORM platform)
{
ALLEGRO_SHADER_HLSL_S *shader;

if (NULL == _imp_D3DXCreateEffect) {
if (NULL == _al_imp_D3DXCreateEffect) {
ALLEGRO_ERROR("D3DXCreateEffect unavailable\n");
return NULL;
}
Expand Down Expand Up @@ -334,7 +239,7 @@ static bool hlsl_attach_shader_source(ALLEGRO_SHADER *shader,
full_source = al_ustr_newf("%s\n#line 1\n%s\n%s\n",
vertex_source, pixel_source, technique_source);

DWORD ok = _imp_D3DXCreateEffect(
DWORD ok = _al_imp_D3DXCreateEffect(
al_get_d3d_device(display),
al_cstr(full_source),
al_ustr_size(full_source),
Expand Down