efl/legacy/evas/src/modules/engines/direct3d/evas_direct3d_image_cache.cpp

437 lines
10 KiB
C++

#include "evas_direct3d_image_cache.h"
#include "evas_direct3d_device.h"
#include <assert.h>
Ref<D3DImageCache> D3DImageCache::_this;
D3DImageCache::D3DImageCache()
{
_max_width = 512;
_max_height = 512;
_margin = 0;
}
D3DImageCache::~D3DImageCache()
{
Uninitialize();
}
D3DImageCache *D3DImageCache::Current()
{
if (_this.IsNull())
_this = new D3DImageCache();
return _this;
}
void D3DImageCache::SetCurrent(D3DImageCache *obj)
{
_this = obj;
}
void D3DImageCache::Uninitialize()
{
for (int i = 0; i < _cache.Length(); i++)
{
// In normal case they all will be NULL
if (_cache[i].texture != NULL)
_cache[i].texture->Release();
}
_cache.Resize();
}
bool D3DImageCache::SelectImageToDevice(D3DDevice *d3d, int id)
{
if (id < 0 || id >= _cache.Length())
return false;
assert(_cache[id].texture != NULL);
return SUCCEEDED(d3d->SetTexture(_cache[id].stage, _cache[id].texture));
}
void D3DImageCache::RemoveImageUser(int id)
{
if (id < 0 || id >= _cache.Length())
return;
assert(_cache[id].texture != NULL);
_cache[id].users--;
if (_cache[id].users == 0)
{
_cache[id].texture->Release();
ZeroMemory(&_cache[id], sizeof(_cache[id]));
}
}
void D3DImageCache::AddImageUser(int id)
{
if (id < 0 || id >= _cache.Length())
return;
assert(_cache[id].texture != NULL);
_cache[id].users++;
}
bool D3DImageCache::InsertImage(D3DDevice *d3d, DWORD *data, int w, int h, CacheEntryInfo &info)
{
CacheEntry *ce = NULL;
int id = -1;
for (int i = 0; i < _cache.Length(); i++)
{
if (!_cache[i].locked && RequestInsert(_cache[i], w, h))
{
ce = &_cache[i];
id = i;
break;
}
}
if (ce == NULL)
{
CacheEntry new_entry;
if (!CreateEntry(d3d, new_entry, w, h))
return false;
for (id = 0; id < _cache.Length(); id++)
{
if (_cache[id].texture == NULL)
break;
}
if (id < _cache.Length())
{
_cache[id] = new_entry;
ce = &_cache[id];
}
else
{
_cache.Add(new_entry);
ce = _cache.Last();
id = _cache.Length() - 1;
}
}
assert(ce != NULL && ce->texture != NULL);
if (!InsertData(*ce, data, w, h))
return false;
info.id = id;
info.u = FLOAT(ce->cur_x) / FLOAT(ce->width);
info.v = FLOAT(ce->cur_y) / FLOAT(ce->height);
info.du = FLOAT(w) / FLOAT(ce->width);
info.dv = FLOAT(h) / FLOAT(ce->height);
info.width = w;
info.height = h;
UpdateInsert(*ce, w, h);
return true;
}
bool D3DImageCache::InsertImage(D3DDevice *d3d, int id, DWORD *data, int w, int h, CacheEntryInfo &info)
{
if (id < 0 || id >= _cache.Length())
return false;
assert(_cache[id].texture != NULL);
CacheEntry *ce = &_cache[id];
if (!RequestInsert(*ce, w, h))
return false;
if (!InsertData(*ce, data, w, h))
return false;
info.id = id;
info.u = FLOAT(ce->cur_x) / FLOAT(ce->width);
info.v = FLOAT(ce->cur_y) / FLOAT(ce->height);
info.du = FLOAT(w) / FLOAT(ce->width);
info.dv = FLOAT(h) / FLOAT(ce->height);
info.width = w;
info.height = h;
UpdateInsert(*ce, w, h);
return true;
}
bool D3DImageCache::CreateImage(D3DDevice *d3d, int w, int h, bool locked, CacheEntryInfo &info)
{
int id;
CacheEntry new_entry;
CacheEntry *ce = NULL;
if (!CreateEntry(d3d, new_entry, w, h, true))
return false;
for (id = 0; id < _cache.Length(); id++)
{
if (_cache[id].texture == NULL)
break;
}
if (id < _cache.Length())
{
_cache[id] = new_entry;
ce = &_cache[id];
}
else
{
_cache.Add(new_entry);
ce = _cache.Last();
id = _cache.Length() - 1;
}
assert(ce != NULL && ce->texture != NULL);
// Fill with zero
if (!InsertData(*ce, NULL, w, h))
return false;
info.id = id;
info.u = 0;
info.v = 0;
info.du = 1;
info.dv = 1;
info.width = w;
info.height = h;
UpdateInsert(*ce, 0, 0);
ce->locked = locked;
return true;
}
bool D3DImageCache::ResizeImage(D3DDevice *d3d, int nw, int nh, int id)
{
if (id < 0 || id >= _cache.Length())
return false;
assert(_cache[id].texture != NULL);
CacheEntry *ce = &_cache[id];
if (ce->width == nw && ce->height == nh)
return true;
LPDIRECT3DTEXTURE9 tex = NULL;
HRESULT hr;
if (FAILED(hr = d3d->GetDevice()->CreateTexture(nw, nh, 0, 0, D3DFMT_A8R8G8B8,
D3DPOOL_MANAGED, &tex, NULL)))
{
WRN("Failed to create texture: %X", hr);
return false;
}
assert(tex != NULL);
ce->texture->Release();
ce->texture = tex;
ce->width = nw;
ce->height = nh;
return true;
}
bool D3DImageCache::RequestInsert(CacheEntry &entry, int w, int h)
{
// If we already have large image entry
if (entry.width > _max_width || entry.height > _max_height)
return false;
// If requested size does not fit into this entry at all
if (entry.height - entry.cur_h < h + _margin * 2 || entry.width < w + _margin * 2)
return false;
// If requested size does not fit into the current line of the entry
if (entry.width - entry.cur_x < w + _margin * 2)
{
entry.cur_y = entry.cur_h + _margin;
entry.cur_x = _margin;
return true;
}
entry.cur_x += _margin;
return true;
}
bool D3DImageCache::CreateEntry(D3DDevice *d3d, CacheEntry &entry, int w, int h, bool exact_size)
{
int width = exact_size ? w : max(_max_width, w);
int height = exact_size ? h : max(_max_height, h);
HRESULT hr;
if (FAILED(hr = d3d->GetDevice()->CreateTexture(width, height, 0, 0, D3DFMT_A8R8G8B8,
D3DPOOL_MANAGED, &entry.texture, NULL)))
{
WRN("Failed to create texture: %X", hr);
return false;
}
entry.cur_x = entry.cur_y = entry.cur_h = 0;
entry.width = width;
entry.height = height;
entry.users = 0;
entry.locked = false;
entry.stage = 0;
return true;
}
bool D3DImageCache::InsertData(CacheEntry &entry, DWORD *data, int w, int h)
{
if (entry.texture == NULL)
return false;
RECT rc = {entry.cur_x, entry.cur_y, entry.cur_x + w, entry.cur_y + h};
D3DLOCKED_RECT lr;
if (FAILED(entry.texture->LockRect(0, &lr, &rc, 0)))
{
WRN("Failed to lock texture");
return false;
}
if (data != NULL)
{
for (int i = 0; i < h; i++)
CopyMemory(((BYTE *)lr.pBits) + i * lr.Pitch, data + i * w, sizeof(DWORD) * w);
}
else
{
for (int i = 0; i < h; i++)
ZeroMemory(((BYTE *)lr.pBits) + i * lr.Pitch, sizeof(DWORD) * w);
}
if (FAILED(entry.texture->UnlockRect(0)))
{
WRN("Failed to unlock texture");
return false;
}
return true;
}
bool D3DImageCache::RetrieveData(CacheEntry &entry, DWORD *data, int w, int h)
{
if (entry.texture == NULL || data == NULL)
return false;
RECT rc = {entry.cur_x, entry.cur_y, entry.cur_x + w, entry.cur_y + h};
D3DLOCKED_RECT lr;
if (FAILED(entry.texture->LockRect(0, &lr, &rc, D3DLOCK_READONLY)))
{
WRN("Failed to lock texture");
return false;
}
for (int i = 0; i < h; i++)
CopyMemory(data + i * w, ((BYTE *)lr.pBits) + i * lr.Pitch, sizeof(DWORD) * w);
if (FAILED(entry.texture->UnlockRect(0)))
{
WRN("Failed to unlock texture");
return false;
}
return true;
}
void D3DImageCache::UpdateInsert(CacheEntry &entry, int w, int h)
{
entry.cur_h = max(entry.cur_h, entry.cur_y + h + _margin);
entry.cur_x += w + _margin;
entry.users++;
}
bool D3DImageCache::UpdateImageData(CacheEntryInfo &info, DWORD *data)
{
assert(data != NULL);
if (info.id < 0 || info.id >= _cache.Length())
return false;
CacheEntry ce_copy = _cache[info.id];
ce_copy.cur_x = int(info.u * FLOAT(ce_copy.width));
ce_copy.cur_y = int(info.v * FLOAT(ce_copy.height));
return InsertData(ce_copy, data, info.width, info.height);
}
bool D3DImageCache::UpdateImageDataWithDirtyInfo(CacheEntryInfo &info, DWORD *data, POINT *dirty)
{
if (info.id < 0 || info.id >= _cache.Length())
return false;
CacheEntry &entry = _cache[info.id];
if (entry.texture == NULL)
return false;
RECT rc = {0, 0, entry.width, entry.height};
D3DLOCKED_RECT lr;
if (FAILED(entry.texture->LockRect(0, &lr, &rc, 0)))
{
WRN("Failed to lock texture");
return false;
}
if (data != NULL)
{
for (int i = 0; i < rc.bottom; i++)
{
if (dirty[i].x < 0 && dirty[i].y < 0)
continue;
if (dirty[i].x >= 0 && dirty[i].y >= 0)
{
CopyMemory(((BYTE *)lr.pBits) + i * lr.Pitch + dirty[i].x * 4,
data + i * rc.right + dirty[i].x, sizeof(DWORD) * (dirty[i].y - dirty[i].x + 1));
dirty[i].y = -dirty[i].y;
}
else if (dirty[i].x >= 0 && dirty[i].y < 0)
{
ZeroMemory(((BYTE *)lr.pBits) + i * lr.Pitch + dirty[i].x * 4,
sizeof(DWORD) * (-dirty[i].y - dirty[i].x + 1));
dirty[i].x = -dirty[i].x;
}
}
}
else
{
for (int i = 0; i < rc.bottom; i++)
{
if (dirty[i].x < 0 || dirty[i].y < 0)
continue;
ZeroMemory(((BYTE *)lr.pBits) + i * lr.Pitch + dirty[i].x * 4,
sizeof(DWORD) * (dirty[i].y - dirty[i].x + 1));
}
}
if (FAILED(entry.texture->UnlockRect(0)))
{
WRN("Failed to unlock texture");
return false;
}
return true;
}
bool D3DImageCache::UpdateImageDataDiscard(CacheEntryInfo &info, DWORD *data)
{
assert(data != NULL);
if (info.id < 0 || info.id >= _cache.Length())
return false;
CacheEntry &entry = _cache[info.id];
if (entry.texture == NULL)
return false;
RECT rc = {0, 0, entry.width, entry.height};
D3DLOCKED_RECT lr;
if (FAILED(entry.texture->LockRect(0, &lr, &rc, 0)))
{
WRN("Failed to lock texture");
return false;
}
for (int i = 0; i < rc.bottom; i++)
{
CopyMemory(((BYTE *)lr.pBits) + i * lr.Pitch,
data + i * rc.right, sizeof(DWORD) * rc.right);
}
if (FAILED(entry.texture->UnlockRect(0)))
{
WRN("Failed to unlock texture");
return false;
}
return true;
}
bool D3DImageCache::GetImageData(CacheEntryInfo &info, TArray<DWORD> &data)
{
if (info.id < 0 || info.id >= _cache.Length())
return false;
CacheEntry ce_copy = _cache[info.id];
ce_copy.cur_x = int(info.u * FLOAT(ce_copy.width));
ce_copy.cur_y = int(info.v * FLOAT(ce_copy.height));
data.Allocate(info.width * info.height);
return RetrieveData(ce_copy, data.Data(), info.width, info.height);
}