* /\ _ \ /\_ \ /\_ \
* \ \ \L\ \\//\ \ \//\ \ __ __ _ __ ___
* \ \ __ \ \ \ \ \ \ \ /'__`\ /'_ `\/\`'__\/ __`\
* \ \ \/\ \ \_\ \_ \_\ \_/\ __//\ \L\ \ \ \//\ \L\ \
* \ \_\ \_\/\____\/\____\ \____\ \____ \ \_\\ \____/
* \/_/\/_/\/____/\/____/\/____/\/___L\ \/_/ \/___/
* /\____/
* \_/__/
*
* Direct3D bitmap driver
*
* By Trent Gamblin.
*
*/
#include <string.h>
#include <stdio.h>
#include <math.h>
#include "allegro5/allegro.h"
#include "allegro5/system.h"
#include "allegro5/internal/aintern.h"
#include "allegro5/internal/aintern_bitmap.h"
#include "allegro5/internal/aintern_display.h"
#include "allegro5/internal/aintern_memblit.h"
#include "allegro5/internal/aintern_pixels.h"
#include "allegro5/internal/aintern_system.h"
#include "allegro5/internal/aintern_tri_soft.h"
#include "allegro5/platform/aintwin.h"
#include "d3d.h"
ALLEGRO_DEBUG_CHANNEL("d3d")
static ALLEGRO_BITMAP_INTERFACE *vt;
#define get_extra(b) ((ALLEGRO_BITMAP_EXTRA_D3D *)\
(b->parent ? b->parent->extra : b->extra))
static bool convert_compressed(LPDIRECT3DTEXTURE9 dest, LPDIRECT3DTEXTURE9 src,
int x, int y, int width, int height) {
#ifdef ALLEGRO_CFG_D3DX9
bool ok = true;
LPDIRECT3DSURFACE9 dest_texture_surface = NULL;
LPDIRECT3DSURFACE9 src_texture_surface = NULL;
if (dest->GetSurfaceLevel(0, &dest_texture_surface) != D3D_OK) {
ALLEGRO_ERROR("convert_compressed: GetSurfaceLevel failed on dest.\n");
ok = false;
}
if (ok && src->GetSurfaceLevel(0, &src_texture_surface) != D3D_OK) {
ALLEGRO_ERROR("convert_compressed: GetSurfaceLevel failed on src.\n");
ok = false;
}
RECT rect;
rect.left = x;
rect.top = y;
rect.right = x + width;
rect.bottom = y + height;
if (ok && _al_imp_D3DXLoadSurfaceFromSurface &&
_al_imp_D3DXLoadSurfaceFromSurface(dest_texture_surface,
NULL,
&rect,
src_texture_surface,
NULL,
&rect,
D3DX_FILTER_NONE,
0) != D3D_OK) {
ALLEGRO_ERROR("convert_compressed: D3DXLoadSurfaceFromSurface failed.\n");
ok = false;
}
int i;
if (src_texture_surface) {
if ((i = src_texture_surface->Release()) != 0) {
ALLEGRO_DEBUG("convert_compressed (src) ref count == %d\n", i);
}
}
if (dest_texture_surface) {
if ((i = dest_texture_surface->Release()) != 0) {
ALLEGRO_DEBUG("convert_compressed (dest) ref count == %d\n", i);
}
}
return ok;
#else
(void)dest;
(void)src;
(void)x;
(void)y;
(void)width;
(void)height;
return false;
#endif
}
*/
bool al_get_d3d_texture_size(ALLEGRO_BITMAP *bitmap, int *width, int *height)
{
int bitmap_flags = al_get_bitmap_flags(bitmap);
ASSERT(bitmap);
ASSERT(width);
ASSERT(height);
if (!(bitmap_flags & _ALLEGRO_INTERNAL_OPENGL) &&
!(bitmap_flags & ALLEGRO_MEMORY_BITMAP)) {
ALLEGRO_BITMAP_EXTRA_D3D *d3d_bmp = get_extra(bitmap);
*width = d3d_bmp->texture_w;
*height = d3d_bmp->texture_h;
return true;
}
else {
*width = 0;
*height = 0;
return false;
}
}
void _al_d3d_bmp_destroy(void)
{
al_free(vt);
vt = NULL;
}
static INLINE void transform_vertex(float* x, float* y, float* z)
{
al_transform_coordinates_3d(al_get_current_transform(), x, y, z);
}
* Draw a textured quad
*/
static void d3d_draw_textured_quad(
ALLEGRO_DISPLAY_D3D *disp, ALLEGRO_BITMAP *bmp, ALLEGRO_COLOR tint,
float sx, float sy, float sw, float sh, int flags)
{
float right;
float bottom;
float tu_start;
float tv_start;
float tu_end;
float tv_end;
int texture_w;
int texture_h;
ALLEGRO_BITMAP_EXTRA_D3D *extra = get_extra(bmp);
const float z = 0.0f;
ALLEGRO_DISPLAY* aldisp = (ALLEGRO_DISPLAY*)disp;
if (aldisp->num_cache_vertices != 0 && (uintptr_t)bmp != aldisp->cache_texture) {
aldisp->vt->flush_vertex_cache(aldisp);
}
aldisp->cache_texture = (uintptr_t)bmp;
right = sw;
bottom = sh;
texture_w = extra->texture_w;
texture_h = extra->texture_h;
tu_start = sx / texture_w;
tv_start = sy / texture_h;
tu_end = sw / texture_w + tu_start;
tv_end = sh / texture_h + tv_start;
if (flags & ALLEGRO_FLIP_HORIZONTAL) {
float temp = tu_start;
tu_start = tu_end;
tu_end = temp;
}
if (flags & ALLEGRO_FLIP_VERTICAL) {
float temp = tv_start;
tv_start = tv_end;
tv_end = temp;
}
#define ALLEGRO_COLOR_TO_D3D(c) D3DCOLOR_COLORVALUE(c.r, c.g, c.b, c.a)
#define SET(f) \
vertices[0].x = 0; \
vertices[0].y = 0; \
vertices[0].z = z; \
vertices[0].color = f(tint); \
vertices[0].u = tu_start; \
vertices[0].v = tv_start; \
\
vertices[1].x = right; \
vertices[1].y = 0; \
vertices[1].z = z; \
vertices[1].color = f(tint); \
vertices[1].u = tu_end; \
vertices[1].v = tv_start; \
\
vertices[2].x = right; \
vertices[2].y = bottom; \
vertices[2].z = z; \
vertices[2].color = f(tint); \
vertices[2].u = tu_end; \
vertices[2].v = tv_end; \
\
vertices[5].x = 0; \
vertices[5].y = bottom; \
vertices[5].z = z; \
vertices[5].color = f(tint); \
vertices[5].u = tu_start; \
vertices[5].v = tv_end; \
\
if (aldisp->cache_enabled) { \
transform_vertex(&vertices[0].x, &vertices[0].y, &vertices[0].z); \
transform_vertex(&vertices[1].x, &vertices[1].y, &vertices[1].z); \
transform_vertex(&vertices[2].x, &vertices[2].y, &vertices[2].z); \
transform_vertex(&vertices[5].x, &vertices[5].y, &vertices[5].z); \
} \
\
vertices[3] = vertices[0]; \
vertices[4] = vertices[2];
bool pp = (aldisp->flags & ALLEGRO_PROGRAMMABLE_PIPELINE) != 0;
if (pp) {
ALLEGRO_VERTEX *vertices = (ALLEGRO_VERTEX *)aldisp->vt->prepare_vertex_cache(aldisp, 6);
SET(ALLEGRO_COLOR)
}
else {
D3D_FIXED_VERTEX *vertices = (D3D_FIXED_VERTEX *)aldisp->vt->prepare_vertex_cache(aldisp, 6);
SET(ALLEGRO_COLOR_TO_D3D)
}
if (!aldisp->cache_enabled)
aldisp->vt->flush_vertex_cache(aldisp);
}
static void d3d_sync_bitmap_memory(ALLEGRO_BITMAP *bitmap)
{
D3DLOCKED_RECT locked_rect;
ALLEGRO_BITMAP_EXTRA_D3D *d3d_bmp = get_extra(bitmap);
LPDIRECT3DTEXTURE9 texture;
int bitmap_format = al_get_bitmap_format(bitmap);
if (_al_d3d_render_to_texture_supported() &&
!_al_pixel_format_is_compressed(bitmap_format))
texture = d3d_bmp->system_texture;
else
texture = d3d_bmp->video_texture;
if (texture->LockRect(0, &locked_rect, NULL, 0) == D3D_OK) {
int block_size = al_get_pixel_block_size(bitmap_format);
int block_width = al_get_pixel_block_width(bitmap_format);
int block_height = al_get_pixel_block_height(bitmap_format);
int mem_pitch = _al_get_least_multiple(bitmap->w, block_width) *
block_size / block_width;
_al_copy_bitmap_data(locked_rect.pBits, locked_rect.Pitch,
bitmap->memory, mem_pitch,
0, 0, 0, 0, _al_get_least_multiple(bitmap->w, block_width),
_al_get_least_multiple(bitmap->h, block_height), bitmap_format);
texture->UnlockRect(0);
}
else {
ALLEGRO_ERROR("d3d_sync_bitmap_memory: Couldn't lock texture.\n");
}
}
static void d3d_sync_bitmap_texture(ALLEGRO_BITMAP *bitmap,
int x, int y, int width, int height)
{
D3DLOCKED_RECT locked_rect;
RECT rect;
ALLEGRO_BITMAP_EXTRA_D3D *d3d_bmp = get_extra(bitmap);
LPDIRECT3DTEXTURE9 texture;
int bitmap_format = al_get_bitmap_format(bitmap);
if (bitmap->parent) return;
rect.left = x;
rect.top = y;
rect.right = x + width;
rect.bottom = y + height;
if (_al_d3d_render_to_texture_supported() &&
!_al_pixel_format_is_compressed(bitmap_format))
texture = d3d_bmp->system_texture;
else
texture = d3d_bmp->video_texture;
if (texture->LockRect(0, &locked_rect, &rect, 0) == D3D_OK) {
int block_size = al_get_pixel_block_size(bitmap_format);
int block_width = al_get_pixel_block_width(bitmap_format);
int mem_pitch = _al_get_least_multiple(bitmap->w, block_width) *
block_size / block_width;
_al_copy_bitmap_data(bitmap->memory, mem_pitch,
locked_rect.pBits, locked_rect.Pitch,
x, y, 0, 0, width, height, bitmap_format);
texture->UnlockRect(0);
}
else {
ALLEGRO_ERROR("d3d_sync_bitmap_texture: Couldn't lock texture to upload.\n");
}
}
static void d3d_do_upload(ALLEGRO_BITMAP *bmp, int x, int y, int width,
int height, bool sync_from_memory)
{
if (sync_from_memory) {
d3d_sync_bitmap_texture(bmp, x, y, width, height);
}
if (_al_d3d_render_to_texture_supported()
&& !_al_pixel_format_is_compressed(al_get_bitmap_format(bmp))) {
ALLEGRO_BITMAP_EXTRA_D3D *d3d_bitmap = get_extra(bmp);
if (d3d_bitmap->display->device->UpdateTexture(
(IDirect3DBaseTexture9 *)d3d_bitmap->system_texture,
(IDirect3DBaseTexture9 *)d3d_bitmap->video_texture) != D3D_OK) {
ALLEGRO_ERROR("d3d_do_upload: Couldn't update texture.\n");
return;
}
}
}
* Release all default pool textures. This must be done before
* resetting the device.
*/
void _al_d3d_release_default_pool_textures(ALLEGRO_DISPLAY *disp)
{
unsigned int i;
for (i = 0; i < disp->bitmaps._size; i++) {
ALLEGRO_BITMAP **bptr = (ALLEGRO_BITMAP **)_al_vector_ref(&disp->bitmaps, i);
ALLEGRO_BITMAP *albmp = *bptr;
if ((al_get_bitmap_flags(albmp) & ALLEGRO_MEMORY_BITMAP) || (albmp->parent))
continue;
ALLEGRO_BITMAP_EXTRA_D3D *d3d_bmp = get_extra(albmp);
if (!d3d_bmp->is_backbuffer && d3d_bmp->render_target) {
d3d_bmp->render_target->Release();
d3d_bmp->render_target = NULL;
}
if (d3d_bmp->video_texture) {
d3d_bmp->video_texture->Release();
d3d_bmp->video_texture = NULL;
}
}
}
static bool d3d_create_textures(ALLEGRO_DISPLAY_D3D *disp, int w, int h,
int flags,
LPDIRECT3DTEXTURE9 *video_texture, LPDIRECT3DTEXTURE9 *system_texture,
int video_format, int system_format)
{
int levels;
int autogenmipmap;
int err;
bool compressed = _al_pixel_format_is_compressed(video_format);
if (flags & ALLEGRO_MIPMAP) {
levels = 0;
autogenmipmap = D3DUSAGE_AUTOGENMIPMAP;
}
else {
levels = 1;
autogenmipmap = 0;
}
if (_al_d3d_render_to_texture_supported()) {
if (video_texture) {
int usage = compressed ? 0 : D3DUSAGE_RENDERTARGET;
* there is no need to manually sync them for device loss.
* It is still necessary to do so for resizing purposes,
* however... not sure there's any real savings to be had here.
*/
D3DPOOL pool = compressed ? D3DPOOL_MANAGED : D3DPOOL_DEFAULT;
err = disp->device->CreateTexture(w, h, levels,
usage | autogenmipmap,
(D3DFORMAT)_al_pixel_format_to_d3d(video_format), pool,
video_texture, NULL);
if (err != D3D_OK && err != D3DOK_NOAUTOGEN) {
ALLEGRO_ERROR("d3d_create_textures: Unable to create video texture.\n");
return false;
}
}
if (system_texture) {
err = disp->device->CreateTexture(w, h, 1,
0, (D3DFORMAT)_al_pixel_format_to_d3d(system_format), D3DPOOL_SYSTEMMEM,
system_texture, NULL);
if (err != D3D_OK) {
ALLEGRO_ERROR("d3d_create_textures: Unable to create system texture.\n");
if (video_texture && (*video_texture)) {
(*video_texture)->Release();
*video_texture = NULL;
}
return false;
}
}
return true;
}
else {
if (video_texture) {
err = disp->device->CreateTexture(w, h, 1,
0, (D3DFORMAT)_al_pixel_format_to_d3d(video_format), D3DPOOL_MANAGED,
video_texture, NULL);
if (err != D3D_OK) {
ALLEGRO_ERROR("d3d_create_textures: Unable to create video texture (no render-to-texture).\n");
return false;
}
}
return true;
}
}
static ALLEGRO_BITMAP
*d3d_create_bitmap_from_surface(LPDIRECT3DSURFACE9 surface,
int format, int flags)
{
ALLEGRO_BITMAP *bitmap;
ALLEGRO_BITMAP_EXTRA_D3D *extra;
D3DSURFACE_DESC desc;
D3DLOCKED_RECT surf_locked_rect;
D3DLOCKED_RECT sys_locked_rect;
unsigned int y;
ASSERT(!_al_pixel_format_is_compressed(format));
if (surface->GetDesc(&desc) != D3D_OK) {
ALLEGRO_ERROR("d3d_create_bitmap_from_surface: GetDesc failed.\n");
return NULL;
}
if (surface->LockRect(&surf_locked_rect, 0, D3DLOCK_READONLY) != D3D_OK) {
ALLEGRO_ERROR("d3d_create_bitmap_from_surface: LockRect failed.\n");
return NULL;
}
bitmap = _al_create_bitmap_params(al_get_current_display(),
desc.Width, desc.Height, format, flags, 0, 0);
if (!bitmap) {
surface->UnlockRect();
return NULL;
}
extra = get_extra(bitmap);
if (extra->system_texture->LockRect(0, &sys_locked_rect, 0, 0) != D3D_OK) {
surface->UnlockRect();
al_destroy_bitmap(bitmap);
ALLEGRO_ERROR("d3d_create_bitmap_from_surface: Lock system texture failed.\n");
return NULL;
}
for (y = 0; y < desc.Height; y++) {
memcpy(
((char*)sys_locked_rect.pBits)+(sys_locked_rect.Pitch*y),
((char*)surf_locked_rect.pBits)+(surf_locked_rect.Pitch*y),
desc.Width*al_get_pixel_size(format)
);
}
surface->UnlockRect();
extra->system_texture->UnlockRect(0);
if (extra->display->device->UpdateTexture(
(IDirect3DBaseTexture9 *)extra->system_texture,
(IDirect3DBaseTexture9 *)extra->video_texture) != D3D_OK) {
ALLEGRO_ERROR("d3d_create_bitmap_from_surface: Couldn't update texture.\n");
}
return bitmap;
}
static bool _al_d3d_sync_bitmap(ALLEGRO_BITMAP *dest)
{
ALLEGRO_BITMAP_EXTRA_D3D *d3d_dest;
LPDIRECT3DSURFACE9 system_texture_surface;
LPDIRECT3DSURFACE9 video_texture_surface;
bool ok;
UINT i;
if (!_al_d3d_render_to_texture_supported()) {
ALLEGRO_ERROR("Render-to-texture not supported.\n");
return false;
}
if (dest->locked) {
ALLEGRO_ERROR("Already locked.\n");
return false;
}
d3d_dest = get_extra(dest);
if (d3d_dest->system_texture == NULL || d3d_dest->video_texture == NULL) {
ALLEGRO_ERROR("A texture is null.\n");
return false;
}
if (dest->parent) {
dest = dest->parent;
}
ok = true;
system_texture_surface = NULL;
video_texture_surface = NULL;
if (d3d_dest->system_texture->GetSurfaceLevel(
0, &system_texture_surface) != D3D_OK) {
ALLEGRO_ERROR("_al_d3d_sync_bitmap: GetSurfaceLevel failed while updating system texture.\n");
ok = false;
}
if (ok && d3d_dest->video_texture->GetSurfaceLevel(
0, &video_texture_surface) != D3D_OK) {
ALLEGRO_ERROR("_al_d3d_sync_bitmap: GetSurfaceLevel failed while updating system texture.\n");
ok = false;
}
if (ok && d3d_dest->display->device->GetRenderTargetData(
video_texture_surface,
system_texture_surface) != D3D_OK) {
ALLEGRO_ERROR("_al_d3d_sync_bitmap: GetRenderTargetData failed.\n");
ok = false;
}
if (system_texture_surface) {
if ((i = system_texture_surface->Release()) != 0) {
ALLEGRO_DEBUG("_al_d3d_sync_bitmap (system) ref count == %d\n", i);
}
}
if (video_texture_surface) {
if ((i = video_texture_surface->Release()) != 0) {
ALLEGRO_DEBUG("_al_d3d_sync_bitmap (video) ref count == %d\n", i);
}
}
if (ok) {
d3d_sync_bitmap_memory(dest);
}
return ok;
}
static void d3d_backup_dirty_bitmap(ALLEGRO_BITMAP *bitmap)
{
ALLEGRO_DISPLAY *display = bitmap->_display;
ALLEGRO_DISPLAY_D3D *d3d_display = (ALLEGRO_DISPLAY_D3D *)display;
if (d3d_display->device_lost)
return;
if (!_al_d3d_render_to_texture_supported())
return;
al_lock_mutex(_al_d3d_lost_device_mutex);
ALLEGRO_BITMAP_EXTRA_D3D *extra = get_extra(bitmap);
int bitmap_flags = al_get_bitmap_flags(bitmap);
if (!(
(bitmap_flags & ALLEGRO_MEMORY_BITMAP) ||
(bitmap_flags & ALLEGRO_NO_PRESERVE_TEXTURE) ||
!bitmap->dirty ||
extra->is_backbuffer ||
bitmap->parent
))
{
if (_al_pixel_format_is_compressed(al_get_bitmap_format(bitmap)))
d3d_sync_bitmap_memory(bitmap);
else
_al_d3d_sync_bitmap(bitmap);
}
bitmap->dirty = false;
al_unlock_mutex(_al_d3d_lost_device_mutex);
}
* Called after the resize is done.
*/
bool _al_d3d_recreate_bitmap_textures(ALLEGRO_DISPLAY_D3D *disp)
{
unsigned int i;
ALLEGRO_DISPLAY *display = (ALLEGRO_DISPLAY *)disp;
for (i = 0; i < display->bitmaps._size; i++) {
ALLEGRO_BITMAP **bptr = (ALLEGRO_BITMAP **)_al_vector_ref(&display->bitmaps, i);
ALLEGRO_BITMAP *bmp = *bptr;
ALLEGRO_BITMAP_EXTRA_D3D *extra = get_extra(bmp);
if ((void *)_al_get_bitmap_display(bmp) == (void *)disp) {
int block_width =
al_get_pixel_block_width(al_get_bitmap_format(bmp));
int block_height =
al_get_pixel_block_height(al_get_bitmap_format(bmp));
if (!d3d_create_textures(disp, extra->texture_w,
extra->texture_h,
al_get_bitmap_flags(bmp),
&extra->video_texture,
&extra->system_texture,
al_get_bitmap_format(bmp),
extra->system_format))
return false;
d3d_do_upload(bmp, 0, 0,
_al_get_least_multiple(bmp->w, block_width),
_al_get_least_multiple(bmp->h, block_height), true);
}
}
return true;
}
* Refresh the texture memory. This must be done after a device is
* lost or after it is reset.
*/
void _al_d3d_refresh_texture_memory(ALLEGRO_DISPLAY *display)
{
unsigned int i;
for (i = 0; i < display->bitmaps._size; i++) {
ALLEGRO_BITMAP **bptr = (ALLEGRO_BITMAP **)_al_vector_ref(&display->bitmaps, i);
ALLEGRO_BITMAP *bmp = *bptr;
ALLEGRO_BITMAP_EXTRA_D3D *extra = get_extra(bmp);
ALLEGRO_DISPLAY_D3D *bmps_display =
(ALLEGRO_DISPLAY_D3D *)_al_get_bitmap_display(bmp);
int bitmap_flags = al_get_bitmap_flags(bmp);
if ((bitmap_flags & ALLEGRO_MEMORY_BITMAP) || bmp->parent) {
continue;
}
d3d_create_textures(bmps_display, extra->texture_w,
extra->texture_h, bitmap_flags,
&extra->video_texture, 0, al_get_bitmap_format(bmp), 0);
if (!(bitmap_flags & ALLEGRO_NO_PRESERVE_TEXTURE)) {
int block_width = al_get_pixel_block_width(al_get_bitmap_format(bmp));
int block_height = al_get_pixel_block_height(al_get_bitmap_format(bmp));
d3d_sync_bitmap_texture(bmp,
0, 0,
_al_get_least_multiple(bmp->w, block_width),
_al_get_least_multiple(bmp->h, block_height));
if (_al_d3d_render_to_texture_supported()
&& !_al_pixel_format_is_compressed(al_get_bitmap_format(bmp))) {
extra->display->device->UpdateTexture(
(IDirect3DBaseTexture9 *)extra->system_texture,
(IDirect3DBaseTexture9 *)extra->video_texture);
}
}
}
}
static bool d3d_upload_bitmap(ALLEGRO_BITMAP *bitmap)
{
ALLEGRO_BITMAP_EXTRA_D3D *d3d_bmp = get_extra(bitmap);
ALLEGRO_SYSTEM *system = al_get_system_driver();
int bitmap_format = al_get_bitmap_format(bitmap);
int system_format = d3d_bmp->system_format;
int block_width = al_get_pixel_block_width(bitmap_format);
int block_height = al_get_pixel_block_height(bitmap_format);
int w = _al_get_least_multiple(bitmap->w, block_width);
int h = _al_get_least_multiple(bitmap->h, block_height);
if (d3d_bmp->display->device_lost)
return false;
if (d3d_bmp->initialized != true) {
bool non_pow2 = al_have_d3d_non_pow2_texture_support();
bool non_square = al_have_d3d_non_square_texture_support();
if (non_pow2 && non_square) {
d3d_bmp->texture_w = w;
d3d_bmp->texture_h = h;
}
else if (non_pow2) {
int max = _ALLEGRO_MAX(w, h);
d3d_bmp->texture_w = max;
d3d_bmp->texture_h = max;
}
else {
d3d_bmp->texture_w = pot(w);
d3d_bmp->texture_h = pot(h);
}
if ((int)d3d_bmp->texture_w < system->min_bitmap_size) d3d_bmp->texture_w = system->min_bitmap_size;
if ((int)d3d_bmp->texture_h < system->min_bitmap_size) d3d_bmp->texture_h = system->min_bitmap_size;
ASSERT(d3d_bmp->texture_w % block_width == 0);
ASSERT(d3d_bmp->texture_h % block_height == 0);
if (d3d_bmp->video_texture == 0)
if (!d3d_create_textures(d3d_bmp->display,
d3d_bmp->texture_w,
d3d_bmp->texture_h,
al_get_bitmap_flags(bitmap),
&d3d_bmp->video_texture,
&d3d_bmp->system_texture,
bitmap_format,
system_format)) {
return false;
}
d3d_bmp->initialized = true;
}
d3d_do_upload(bitmap, 0, 0, w, h, false);
return true;
}
static void d3d_draw_bitmap_region(
ALLEGRO_BITMAP *src,
ALLEGRO_COLOR tint,
float sx, float sy, float sw, float sh, int flags)
{
ALLEGRO_BITMAP *dest = al_get_target_bitmap();
ALLEGRO_BITMAP_EXTRA_D3D *d3d_dest = get_extra(dest);
ALLEGRO_BITMAP_EXTRA_D3D *d3d_src = get_extra(src);
ASSERT(src->parent == NULL);
if (!_al_d3d_render_to_texture_supported()) {
_al_draw_bitmap_region_memory(src, tint,
(int)sx, (int)sy, (int)sw, (int)sh, 0, 0,
(int)flags);
return;
}
if (d3d_dest->display->device_lost)
return;
_al_d3d_set_bitmap_clip(dest);
if (dest->parent) {
dest = dest->parent;
d3d_dest = get_extra(dest);
}
if (d3d_src->is_backbuffer) {
IDirect3DSurface9 *surface;
D3DSURFACE_DESC desc;
if (d3d_src->display->render_target->GetDesc(&desc) != D3D_OK) {
ALLEGRO_ERROR("d3d_draw_bitmap_region: GetDesc failed.\n");
return;
}
if (desc.MultiSampleType == D3DMULTISAMPLE_NONE) {
surface = d3d_src->display->render_target;
}
else {
RECT r;
if (d3d_src->display->device->CreateRenderTarget(
desc.Width,
desc.Height,
desc.Format,
D3DMULTISAMPLE_NONE,
0,
TRUE,
&surface,
NULL
) != D3D_OK) {
ALLEGRO_ERROR(
"d3d_draw_bitmap_region: CreateRenderTarget failed.\n");
return;
}
r.top = 0;
r.left = 0;
r.right = desc.Width;
r.bottom = desc.Height;
if (d3d_src->display->device->StretchRect(
d3d_src->display->render_target,
&r,
surface,
&r,
D3DTEXF_NONE
) != D3D_OK) {
ALLEGRO_ERROR("d3d_draw_bitmap_region: StretchRect failed.\n");
surface->Release();
return;
}
}
ALLEGRO_BITMAP *tmp_bmp = d3d_create_bitmap_from_surface(
surface,
al_get_bitmap_format(src),
al_get_bitmap_flags(src)
);
if (tmp_bmp) {
d3d_draw_bitmap_region(tmp_bmp, tint,
sx, sy, sw, sh, flags);
al_destroy_bitmap(tmp_bmp);
if (desc.MultiSampleType != D3DMULTISAMPLE_NONE) {
surface->Release();
}
}
return;
}
_al_d3d_set_blender(d3d_dest->display);
d3d_draw_textured_quad(
d3d_dest->display, src, tint,
sx, sy, sw, sh, flags);
}
static ALLEGRO_LOCKED_REGION *d3d_lock_region(ALLEGRO_BITMAP *bitmap,
int x, int y, int w, int h, int format,
int flags)
{
ALLEGRO_BITMAP_EXTRA_D3D *d3d_bmp = get_extra(bitmap);
int bitmap_format = al_get_bitmap_format(bitmap);
int system_format = d3d_bmp->system_format;
if (d3d_bmp->display->device_lost)
return NULL;
RECT rect;
DWORD Flags = flags & ALLEGRO_LOCK_READONLY ? D3DLOCK_READONLY : 0;
int f = _al_get_real_pixel_format(al_get_current_display(), format);
if (f < 0) {
return NULL;
}
rect.left = x;
rect.right = x + w;
rect.top = y;
rect.bottom = y + h;
if (d3d_bmp->is_backbuffer) {
ALLEGRO_DISPLAY_D3D *d3d_disp =
(ALLEGRO_DISPLAY_D3D *)_al_get_bitmap_display(bitmap);
if (d3d_disp->render_target->LockRect(&d3d_bmp->locked_rect, &rect, Flags) != D3D_OK) {
ALLEGRO_ERROR("LockRect failed in d3d_lock_region.\n");
return NULL;
}
}
else {
LPDIRECT3DTEXTURE9 texture;
if (_al_pixel_format_is_compressed(bitmap_format)) {
if (!(flags & ALLEGRO_LOCK_WRITEONLY)) {
if(!convert_compressed(
d3d_bmp->system_texture, d3d_bmp->video_texture,
x, y, w, h)) {
ALLEGRO_ERROR("Could not decompress.\n");
return NULL;
}
}
texture = d3d_bmp->system_texture;
}
else if (_al_d3d_render_to_texture_supported()) {
* Sync bitmap->memory with texture
*/
bitmap->locked = false;
if (!(flags & ALLEGRO_LOCK_WRITEONLY) && !_al_d3d_sync_bitmap(bitmap)) {
return NULL;
}
bitmap->locked = true;
texture = d3d_bmp->system_texture;
}
else {
texture = d3d_bmp->video_texture;
}
if (texture->LockRect(0, &d3d_bmp->locked_rect, &rect, Flags) != D3D_OK) {
ALLEGRO_ERROR("LockRect failed in d3d_lock_region.\n");
return NULL;
}
}
if (format == ALLEGRO_PIXEL_FORMAT_ANY || system_format == format || system_format == f) {
bitmap->locked_region.data = d3d_bmp->locked_rect.pBits;
bitmap->locked_region.format = system_format;
bitmap->locked_region.pitch = d3d_bmp->locked_rect.Pitch;
bitmap->locked_region.pixel_size = al_get_pixel_size(system_format);
}
else {
bitmap->locked_region.pitch = al_get_pixel_size(f) * w;
bitmap->locked_region.data = al_malloc(bitmap->locked_region.pitch*h);
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(
d3d_bmp->locked_rect.pBits, system_format, d3d_bmp->locked_rect.Pitch,
bitmap->locked_region.data, f, bitmap->locked_region.pitch,
0, 0, 0, 0, w, h);
}
}
return &bitmap->locked_region;
}
static void d3d_unlock_region(ALLEGRO_BITMAP *bitmap)
{
ALLEGRO_BITMAP_EXTRA_D3D *d3d_bmp = get_extra(bitmap);
int system_format = d3d_bmp->system_format;
if (bitmap->locked_region.format != 0 && bitmap->locked_region.format != system_format) {
if (!(bitmap->lock_flags & ALLEGRO_LOCK_READONLY)) {
_al_convert_bitmap_data(
bitmap->locked_region.data, bitmap->locked_region.format, bitmap->locked_region.pitch,
d3d_bmp->locked_rect.pBits, system_format, d3d_bmp->locked_rect.Pitch,
0, 0, 0, 0, bitmap->lock_w, bitmap->lock_h);
}
al_free(bitmap->locked_region.data);
}
if (d3d_bmp->is_backbuffer) {
ALLEGRO_DISPLAY_D3D *d3d_disp =
(ALLEGRO_DISPLAY_D3D *)_al_get_bitmap_display(bitmap);
d3d_disp->render_target->UnlockRect();
}
else {
LPDIRECT3DTEXTURE9 texture;
int bitmap_format = al_get_bitmap_format(bitmap);
bool compressed = _al_pixel_format_is_compressed(bitmap_format);
if (_al_d3d_render_to_texture_supported() || compressed)
texture = d3d_bmp->system_texture;
else
texture = d3d_bmp->video_texture;
texture->UnlockRect(0);
if (bitmap->lock_flags & ALLEGRO_LOCK_READONLY)
return;
if (compressed) {
int block_width = al_get_pixel_block_width(bitmap_format);
int block_height = al_get_pixel_block_height(bitmap_format);
int xc = (bitmap->lock_x / block_width) * block_width;
int yc = (bitmap->lock_y / block_height) * block_height;
int wc =
_al_get_least_multiple(bitmap->lock_x + bitmap->lock_w, block_width) - xc;
int hc =
_al_get_least_multiple(bitmap->lock_y + bitmap->lock_h, block_height) - yc;
if(!convert_compressed(
d3d_bmp->video_texture, d3d_bmp->system_texture, xc, yc, wc, hc)) {
ALLEGRO_ERROR("Could not compress.\n");
}
}
else {
d3d_do_upload(bitmap, bitmap->lock_x, bitmap->lock_y,
bitmap->lock_w, bitmap->lock_h, false);
}
}
}
static void d3d_update_clipping_rectangle(ALLEGRO_BITMAP *bitmap)
{
_al_d3d_set_bitmap_clip(bitmap);
}
static ALLEGRO_LOCKED_REGION *d3d_lock_compressed_region(
ALLEGRO_BITMAP *bitmap, int x, int y, int w, int h,
int flags)
{
ALLEGRO_BITMAP_EXTRA_D3D *d3d_bmp = get_extra(bitmap);
int bitmap_format = al_get_bitmap_format(bitmap);
if (d3d_bmp->display->device_lost)
return NULL;
ASSERT(_al_pixel_format_is_compressed(bitmap_format));
RECT rect;
DWORD Flags = flags & ALLEGRO_LOCK_READONLY ? D3DLOCK_READONLY : 0;
rect.left = x;
rect.right = x + w;
rect.top = y;
rect.bottom = y + h;
if (d3d_bmp->video_texture->LockRect(0, &d3d_bmp->locked_rect, &rect, Flags) != D3D_OK) {
ALLEGRO_ERROR("LockRect failed in d3d_lock_region.\n");
return NULL;
}
bitmap->locked_region.data = d3d_bmp->locked_rect.pBits;
bitmap->locked_region.format = bitmap_format;
bitmap->locked_region.pitch = d3d_bmp->locked_rect.Pitch;
bitmap->locked_region.pixel_size = al_get_pixel_block_size(bitmap_format);
return &bitmap->locked_region;
}
static void d3d_unlock_compressed_region(ALLEGRO_BITMAP *bitmap)
{
ALLEGRO_BITMAP_EXTRA_D3D *d3d_bmp = get_extra(bitmap);
ASSERT(_al_pixel_format_is_compressed(al_get_bitmap_format(bitmap)));
d3d_bmp->video_texture->UnlockRect(0);
}
ALLEGRO_BITMAP_INTERFACE *_al_bitmap_d3d_driver(void)
{
if (vt)
return vt;
vt = (ALLEGRO_BITMAP_INTERFACE *)al_malloc(sizeof *vt);
memset(vt, 0, sizeof *vt);
vt->draw_bitmap_region = d3d_draw_bitmap_region;
vt->upload_bitmap = d3d_upload_bitmap;
vt->update_clipping_rectangle = NULL;
vt->destroy_bitmap = _al_d3d_destroy_bitmap;
vt->lock_region = d3d_lock_region;
vt->unlock_region = d3d_unlock_region;
vt->lock_compressed_region = d3d_lock_compressed_region;
vt->unlock_compressed_region = d3d_unlock_compressed_region;
vt->update_clipping_rectangle = d3d_update_clipping_rectangle;
vt->backup_dirty_bitmap = d3d_backup_dirty_bitmap;
return vt;
}