#include "evas_direct3d_image_cache.h" #include "evas_direct3d_device.h" #include Ref 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 &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); }