// Force the layered windows APIs to be visible. #define _WIN32_WINNT 0x0500 #include "evas_engine.h" #include #include "evas_direct3d_device.h" #include "evas_direct3d_context.h" #include "evas_direct3d_shader_pack.h" #include "evas_direct3d_scene.h" #include "evas_direct3d_image_cache.h" #include "evas_direct3d_object_line.h" #include "evas_direct3d_object_rect.h" #include "evas_direct3d_object_image.h" #include "evas_direct3d_vertex_buffer_cache.h" #include "evas_direct3d_object_font.h" // Internal structure that joins two types of objects struct ImagePtr { Ref ref; RGBA_Image *img; }; struct DevicePtr { Ref device; Ref scene; Ref context; Ref image_cache; Ref shader_pack; Ref vb_cache; int fonts_buffer_image_id; // Layered windows cannot render D3D in the normal way bool layered; // Window shape mask struct { // Width and height may be different from target size int width; int height; // Pointer to external memory location, dont do anything with it unsigned char *mask; } shape; // GDI output target struct { HBITMAP image; HDC hdc; BITMAPINFO info; BYTE *data; } dib; }; DevicePtr *SelectDevice(Direct3DDeviceHandler d3d) { DevicePtr *dev_ptr = (DevicePtr *)d3d; D3DImageCache::SetCurrent(dev_ptr->image_cache); D3DShaderPack::SetCurrent(dev_ptr->shader_pack); D3DVertexBufferCache::SetCurrent(dev_ptr->vb_cache); return dev_ptr; } void DeleteDIBObjects(DevicePtr *dev_ptr) { assert(dev_ptr != NULL); if (dev_ptr->dib.image != NULL) DeleteObject(dev_ptr->dib.image); if (dev_ptr->dib.hdc != NULL) DeleteDC(dev_ptr->dib.hdc); ZeroMemory(&dev_ptr->dib, sizeof(dev_ptr->dib)); } bool CreateDIBObjects(DevicePtr *dev_ptr) { assert(dev_ptr != NULL); if ((dev_ptr->dib.hdc = CreateCompatibleDC(NULL)) == NULL) { Log("Failed to create compatible DC"); return false; } ZeroMemory(&dev_ptr->dib.info, sizeof(dev_ptr->dib.info)); dev_ptr->dib.info.bmiHeader.biSize = sizeof(dev_ptr->dib.info.bmiHeader); dev_ptr->dib.info.bmiHeader.biBitCount = 32; dev_ptr->dib.info.bmiHeader.biWidth = dev_ptr->device->GetWidth(); dev_ptr->dib.info.bmiHeader.biHeight = -dev_ptr->device->GetHeight(); dev_ptr->dib.info.bmiHeader.biCompression = BI_RGB; dev_ptr->dib.info.bmiHeader.biPlanes = 1; if ((dev_ptr->dib.image = CreateDIBSection(dev_ptr->dib.hdc, &dev_ptr->dib.info, DIB_RGB_COLORS, (void **)&dev_ptr->dib.data, NULL, 0)) == NULL) { Log("Failed to create dib section"); DeleteDIBObjects(dev_ptr); return false; } assert(dev_ptr->dib.data != NULL); GdiFlush(); return true; } extern "C" { Direct3DDeviceHandler evas_direct3d_init(HWND window, int depth, int fullscreen) { Ref device = new D3DDevice(); if (!device->Init(window, depth, fullscreen == 1)) return NULL; D3DImageCache::SetCurrent(NULL); D3DShaderPack::SetCurrent(NULL); D3DVertexBufferCache::SetCurrent(NULL); if (!D3DShaderPack::Current()->Initialize(device)) { Log("Failed to build shader pack"); device->Destroy(); return NULL; } DevicePtr *dev_ptr = new DevicePtr; ZeroMemory(dev_ptr, sizeof(DevicePtr)); dev_ptr->device = device; dev_ptr->scene = new D3DScene(); dev_ptr->context = new D3DContext(); dev_ptr->image_cache = D3DImageCache::Current(); dev_ptr->shader_pack = D3DShaderPack::Current(); dev_ptr->vb_cache = D3DVertexBufferCache::Current(); dev_ptr->fonts_buffer_image_id = -1; D3DImageCache::CacheEntryInfo info; if (!D3DImageCache::Current()->CreateImage(device, device->GetWidth(), device->GetHeight(), true, info)) { Log("Failed to create fonts image buffer"); return NULL; } dev_ptr->fonts_buffer_image_id = info.id; D3DImageCache::Current()->SetImageStage(info.id, 1); return (Direct3DDeviceHandler)dev_ptr; } void evas_direct3d_free(Direct3DDeviceHandler d3d) { DevicePtr *dev_ptr = SelectDevice(d3d); DeleteDIBObjects(dev_ptr); dev_ptr->context = NULL; dev_ptr->scene = NULL; dev_ptr->image_cache = NULL; dev_ptr->shader_pack = NULL; D3DShaderPack::Current()->Uninitialize(); D3DImageCache::Current()->Uninitialize(); D3DVertexBufferCache::Current()->Uninitialize(); D3DShaderPack::SetCurrent(NULL); D3DImageCache::SetCurrent(NULL); D3DVertexBufferCache::SetCurrent(NULL); dev_ptr->device = NULL; delete dev_ptr; Log("uninitialized"); } void evas_direct3d_resize(Direct3DDeviceHandler d3d, int width, int height) { DevicePtr *dev_ptr = SelectDevice(d3d); D3DDevice *device = dev_ptr->device; if (!device->Reset(width, height, -1)) { Log("Failed to resize"); return; } if (!D3DImageCache::Current()->ResizeImage(device, width, height, dev_ptr->fonts_buffer_image_id)) { Log("Failed to resize fonts image buffer"); } if (dev_ptr->layered) { DeleteDIBObjects(dev_ptr); if (!CreateDIBObjects(dev_ptr)) Log("Failed to create dib objects"); } } void evas_direct3d_set_fullscreen(Direct3DDeviceHandler d3d, int width, int height, int fullscreen) { DevicePtr *dev_ptr = SelectDevice(d3d); D3DDevice *device = dev_ptr->device; if (width < 0) width = ::GetSystemMetrics(SM_CXSCREEN); if (height < 0) height = ::GetSystemMetrics(SM_CYSCREEN); if (!device->Reset(width, height, fullscreen)) { Log("Failed to resize"); return; } if (!D3DImageCache::Current()->ResizeImage(device, width, height, dev_ptr->fonts_buffer_image_id)) { Log("Failed to resize fonts image buffer"); } if (fullscreen == 0) InvalidateRect(HWND_DESKTOP, NULL, TRUE); } void evas_direct3d_set_layered(Direct3DDeviceHandler d3d, int layered, int mask_width, int mask_height, unsigned char *mask) { DevicePtr *dev_ptr = SelectDevice(d3d); dev_ptr->layered = (layered != 0); dev_ptr->shape.width = mask_width; dev_ptr->shape.height = mask_height; dev_ptr->shape.mask = mask; if (dev_ptr->layered && dev_ptr->dib.data == NULL) CreateDIBObjects(dev_ptr); else if (!dev_ptr->layered) DeleteDIBObjects(dev_ptr); } void evas_direct3d_context_color_set(Direct3DDeviceHandler d3d, int r, int g, int b, int a) { DevicePtr *dev_ptr = SelectDevice(d3d); dev_ptr->context->color = ((a & 0xff) << 24) | ((r & 0xff) << 16) | ((g & 0xff) << 8) | (b & 0xff); } void evas_direct3d_context_set_multiplier(Direct3DDeviceHandler d3d, int r, int g, int b, int a) { DevicePtr *dev_ptr = SelectDevice(d3d); dev_ptr->context->color_mul = ((a & 0xff) << 24) | ((r & 0xff) << 16) | ((g & 0xff) << 8) | (b & 0xff); } void evas_direct3d_render_all(Direct3DDeviceHandler d3d) { Log("render"); assert(d3d != NULL); DevicePtr *dev_ptr = SelectDevice(d3d); D3DDevice *device = dev_ptr->device; D3DScene *scene = dev_ptr->scene; if (!device->Begin()) return; device->GetDevice()->SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE); device->GetDevice()->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA); device->GetDevice()->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA); //device->GetDevice()->SetSamplerState(1, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR); //device->GetDevice()->SetSamplerState(1, D3DSAMP_MINFILTER, D3DTEXF_LINEAR); D3DObjectLine::BeginCache(); D3DObjectRect::BeginCache(); D3DObjectImage::BeginCache(); scene->DrawAll(device); D3DObjectLine::EndCache(device); D3DObjectRect::EndCache(device); D3DObjectImage::EndCache(device); D3DObjectFont::EndCache(device); device->End(); if (dev_ptr->layered && !device->GetFullscreen() && dev_ptr->dib.data != NULL) { HDC hdc = GetDC(device->GetWindow()); if (hdc != NULL) { POINT dest = {0, 0}; POINT src = {0, 0}; SIZE client = {device->GetWidth(), device->GetHeight()}; BLENDFUNCTION blend_func = {AC_SRC_OVER, 0, 255, AC_SRC_ALPHA}; if (device->GetRenderData().Length() == client.cx * client.cy) { CopyMemory(dev_ptr->dib.data, device->GetRenderData().Data(), sizeof(DWORD) * client.cx * client.cy); } for (int i = 0; i < client.cy; i++) { for (int j = 0; j < client.cx; j++) { int mask_i = int(dev_ptr->shape.height * float(i) / float(client.cy)); int mask_j = int(dev_ptr->shape.width * float(j) / float(client.cx)); if (mask_i < 0) mask_i = 0; else if (mask_i >= dev_ptr->shape.height) mask_i = dev_ptr->shape.height - 1; if (mask_j < 0) mask_j = 0; else if (mask_j >= dev_ptr->shape.width) mask_j = dev_ptr->shape.width - 1; BYTE mask_b = dev_ptr->shape.mask[mask_i * dev_ptr->shape.width + mask_j]; float alpha = float(mask_b) / 255.f; dev_ptr->dib.data[j * 4 + 0 + i * 4 * client.cx] = BYTE(float(dev_ptr->dib.data[j * 4 + 0 + i * 4 * client.cx]) * alpha); dev_ptr->dib.data[j * 4 + 1 + i * 4 * client.cx] = BYTE(float(dev_ptr->dib.data[j * 4 + 1 + i * 4 * client.cx]) * alpha); dev_ptr->dib.data[j * 4 + 2 + i * 4 * client.cx] = BYTE(float(dev_ptr->dib.data[j * 4 + 2 + i * 4 * client.cx]) * alpha); dev_ptr->dib.data[j * 4 + 3 + i * 4 * client.cx] = mask_b; } } HGDIOBJ prev_obj = SelectObject(dev_ptr->dib.hdc, dev_ptr->dib.image); ClientToScreen(device->GetWindow(), &dest); UpdateLayeredWindow(device->GetWindow(), hdc, &dest, &client, dev_ptr->dib.hdc, &src, 0, &blend_func, ULW_ALPHA); SelectObject(dev_ptr->dib.hdc, prev_obj); ReleaseDC(device->GetWindow(), hdc); } } scene->FreeObjects(); } void evas_direct3d_line_draw(Direct3DDeviceHandler d3d, int x1, int y1, int x2, int y2) { DevicePtr *dev_ptr = SelectDevice(d3d); D3DDevice *device = dev_ptr->device; D3DScene *scene = dev_ptr->scene; D3DContext *context = dev_ptr->context; Ref line = scene->GetFreeObject(); if (line == NULL) { line = new D3DObjectLine(); scene->AddObject(line); Log("New line object (total objects: %d)", scene->GetObjectCount()); } else { line->SetFree(false); Log("Line reused (object: %p)", line.Addr()); } line->Setup( 2.f * float(x1) / float(device->GetWidth()) - 1.f, 2.f * (1.f - float(y1) / float(device->GetHeight())) - 1.f, 2.f * float(x2) / float(device->GetWidth()) - 1.f, 2.f * (1.f - float(y2) / float(device->GetHeight())) - 1.f, context->color); } void evas_direct3d_rectangle_draw(Direct3DDeviceHandler d3d, int x, int y, int w, int h) { DevicePtr *dev_ptr = SelectDevice(d3d); D3DDevice *device = dev_ptr->device; D3DScene *scene = dev_ptr->scene; D3DContext *context = dev_ptr->context; Ref rect = scene->GetFreeObject(); if (rect == NULL) { rect = new D3DObjectRect(); scene->AddObject(rect); Log("New rect object (total objects: %d)", scene->GetObjectCount()); } else { rect->SetFree(false); Log("Rect reused (object: %p)", rect.Addr()); } rect->Setup( 2.f * float(x) / float(device->GetWidth()) - 1.f, 2.f * (1.f - float(y) / float(device->GetHeight())) - 1.f, 2.f * float(w) / float(device->GetWidth()), -2.f * float(h) / float(device->GetHeight()), context->color); } Direct3DImageHandler evas_direct3d_image_load(Direct3DDeviceHandler d3d, const char *file, const char *key, int *error, Evas_Image_Load_Opts *lo) { DevicePtr *dev_ptr = SelectDevice(d3d); D3DDevice *device = dev_ptr->device; D3DScene *scene = dev_ptr->scene; RGBA_Image *evas_image = evas_common_load_image_from_file(file, key, lo); if (evas_image == NULL) { Log("Failed to load image from %s", file); return NULL; } int image_width = evas_image->cache_entry.w; int image_height = evas_image->cache_entry.h; assert(image_width > 0 && image_height > 0); D3DImageCache::CacheEntryInfo info; ZeroMemory(&info, sizeof(info)); info.id = -1; Ref image = new D3DObjectImage(); image->Init(info.u, info.v, info.du, info.dv, info.id, info.width, info.height, evas_image->cache_entry.file); image->SetFree(true); scene->AddObject(image); Log("New image object (total objects: %d)", scene->GetObjectCount()); ImagePtr *ptr = new ImagePtr; ptr->ref = image; ptr->img = evas_image; return (Direct3DImageHandler)ptr; } Direct3DImageHandler evas_direct3d_image_new_from_data(Direct3DDeviceHandler d3d, int w, int h, DWORD *image_data, int alpha, int cspace) { DevicePtr *dev_ptr = SelectDevice(d3d); D3DDevice *device = dev_ptr->device; D3DScene *scene = dev_ptr->scene; int image_width = w; int image_height = h; assert(image_width > 0 && image_height > 0); Ref image = new D3DObjectImage(); D3DImageCache::CacheEntryInfo info; ZeroMemory(&info, sizeof(info)); if (!D3DImageCache::Current()->InsertImage(device, image_data, image_width, image_height, info)) { Log("Couldnt add image to the cache"); return NULL; } char buf[64]; sprintf(buf, "%p", image_data); image->Init(info.u, info.v, info.du, info.dv, info.id, info.width, info.height, buf); image->SetFree(true); scene->AddObject(image); Log("New image object (total objects: %d)", scene->GetObjectCount()); ImagePtr *ptr = new ImagePtr; ptr->ref = image; ptr->img = NULL; return (Direct3DImageHandler)ptr; } Direct3DImageHandler evas_direct3d_image_new_from_copied_data(Direct3DDeviceHandler d3d, int w, int h, DWORD *image_data, int alpha, int cspace) { return evas_direct3d_image_new_from_data(d3d, w, h, image_data, alpha, cspace); } void evas_direct3d_image_free(Direct3DDeviceHandler d3d, Direct3DImageHandler image) { DevicePtr *dev_ptr = SelectDevice(d3d); D3DDevice *device = dev_ptr->device; D3DScene *scene = dev_ptr->scene; ImagePtr *ptr = (ImagePtr *)image; Ref image_ref = ptr->ref; assert(!image_ref.IsNull()); scene->DeleteObject(image_ref); delete ptr; } void evas_direct3d_image_data_put(Direct3DDeviceHandler d3d, Direct3DImageHandler image, DWORD *image_data) { ImagePtr *ptr = (ImagePtr *)image; Ref image_ref = ptr->ref; //assert(!image_ref.IsNull()); if (image_ref.IsNull()) return; if (!image_ref->UpdateImageData(image_data)) Log("Failed to update image data"); } void evas_direct3d_image_data_get(Direct3DDeviceHandler d3d, Direct3DImageHandler image, int to_write, DATA32 **image_data) { ImagePtr *ptr = (ImagePtr *)image; Ref image_ref = ptr->ref; if (image_ref.IsNull()) return; if (image_data == NULL) return; assert(sizeof(DATA32) == sizeof(DWORD)); *image_data = (DATA32 *)image_ref->GetImageData(); } void evas_direct3d_image_draw(Direct3DDeviceHandler d3d, Direct3DImageHandler image, int src_x, int src_y, int src_w, int src_h, int dst_x, int dst_y, int dst_w, int dst_h, int smooth) { ImagePtr *ptr = (ImagePtr *)image; Ref image_ref = ptr->ref; RGBA_Image *evas_image = ptr->img; DevicePtr *dev_ptr = SelectDevice(d3d); D3DDevice *device = dev_ptr->device; D3DScene *scene = dev_ptr->scene; //assert(!image_ref.IsNull()); if (image_ref.IsNull()) return; if (!image_ref->IsValid()) { TArray images; scene->GetObjectsOfType(images); bool found = false; for (int i = 0; i < images.Length(); i++) { if (images[i]->IsValid() && _stricmp(images[i]->GetSource(), image_ref->GetSource()) == 0) { images[i]->CopyTo(image_ref); found = true; Log("Image object info reused, source: \"%s\"", image_ref->GetSource()); break; } } if (!found && evas_image != NULL) evas_cache_image_load_data(&evas_image->cache_entry); } // If the image object wasnt initialized yet if (evas_image != NULL && evas_image->image.data != NULL && !image_ref->IsValid()) { D3DImageCache::CacheEntryInfo info; ZeroMemory(&info, sizeof(info)); if (!D3DImageCache::Current()->InsertImage(device, (DWORD *)evas_image->image.data, evas_image->cache_entry.w, evas_image->cache_entry.h, info)) { Log("Couldnt add image to the cache"); return; } image_ref->Init(info.u, info.v, info.du, info.dv, info.id, info.width, info.height, evas_image->cache_entry.file); } // Do not draw invalid objects if (!image_ref->IsValid()) { image_ref->SetFree(true); return; } image_ref->Setup( 2.f * float(dst_x) / float(device->GetWidth()) - 1.f, 2.f * (1.f - float(dst_y) / float(device->GetHeight())) - 1.f, 2.f * float(dst_w) / float(device->GetWidth()), -2.f * float(dst_h) / float(device->GetHeight()), src_x, src_y, src_w, src_h); image_ref->SetupColorFilter(dev_ptr->context->color_mul); image_ref->SetFree(false); } void evas_direct3d_image_size_get(Direct3DImageHandler image, int *w, int *h) { ImagePtr *ptr = (ImagePtr *)image; if (ptr == NULL) return; if (ptr->img != NULL) { if (w != NULL) *w = ptr->img->cache_entry.w; if (h != NULL) *h = ptr->img->cache_entry.h; } else if (!ptr->ref.IsNull()) { if (w != NULL) *w = ptr->ref->GetWidth(); if (h != NULL) *h = ptr->ref->GetHeight(); } } void evas_direct3d_image_border_set(Direct3DDeviceHandler d3d, Direct3DImageHandler image, int l, int r, int t, int b) { ImagePtr *ptr = (ImagePtr *)image; Ref image_ref = ptr->ref; DevicePtr *dev_ptr = SelectDevice(d3d); D3DDevice *device = dev_ptr->device; if (image_ref.IsNull()) return; int im_w, im_h; if (ptr->img != NULL) { im_w = ptr->img->cache_entry.w; im_h = ptr->img->cache_entry.h; } else { im_w = image_ref->GetWidth(); im_h = image_ref->GetHeight(); } image_ref->SetupBorder( D3DXVECTOR4( 2.f * float(l) / float(device->GetWidth()), -2.f * float(t) / float(device->GetHeight()), 2.f * float(r) / float(device->GetWidth()), -2.f * float(b) / float(device->GetHeight())), D3DXVECTOR4(float(l) / float(im_w), float(t) / float(im_h), float(r) / float(im_w), float(b) / float(im_h))); } void evas_direct3d_image_border_get(Direct3DDeviceHandler d3d, Direct3DImageHandler image, int *l, int *r, int *t, int *b) { ImagePtr *ptr = (ImagePtr *)image; Ref image_ref = ptr->ref; DevicePtr *dev_ptr = SelectDevice(d3d); D3DDevice *device = dev_ptr->device; if (image_ref.IsNull()) return; assert(l != NULL && r != NULL && b != NULL && t != NULL); *l = (int)(0.5f * image_ref->GetBorderLeft() * device->GetWidth()); *r = (int)(0.5f * image_ref->GetBorderRight() * device->GetWidth()); *t = (int)(-0.5f * image_ref->GetBorderTop() * device->GetHeight()); *b = (int)(-0.5f * image_ref->GetBorderBottom() * device->GetHeight()); } Direct3DFontGlyphHandler evas_direct3d_font_texture_new(Direct3DDeviceHandler d3d, RGBA_Font_Glyph *fg) { DevicePtr *dev_ptr = SelectDevice(d3d); D3DContext *context = dev_ptr->context; D3DDevice *device = dev_ptr->device; if (context->font.IsNull()) return NULL; D3DObjectFont *font = (D3DObjectFont *)context->font.Addr(); // This is not reliable //D3DObjectFont::Glyph *glyph = (D3DObjectFont::Glyph *)fg->ext_dat; D3DObjectFont::Glyph *glyph = font->GetGlyph(fg); if (glyph != NULL) { assert(glyph->Compare(fg)); return glyph; } glyph = font->AddGlyph(device, fg, fg->glyph_out->bitmap.buffer, fg->glyph_out->bitmap.width, fg->glyph_out->bitmap.rows, fg->glyph_out->bitmap.pitch); return (Direct3DFontGlyphHandler)glyph; } void evas_direct3d_font_texture_free(Direct3DFontGlyphHandler ft) { if (ft == NULL) return; D3DObjectFont::Glyph *glyph = (D3DObjectFont::Glyph *)ft; RGBA_Font_Glyph *fg = (RGBA_Font_Glyph *)glyph->Source(); fg->ext_dat = NULL; } void evas_direct3d_font_texture_draw(Direct3DDeviceHandler d3d, void *, void *, RGBA_Font_Glyph *fg, int x, int y) { DevicePtr *dev_ptr = SelectDevice(d3d); D3DContext *context = dev_ptr->context; D3DDevice *device = dev_ptr->device; if (context->font.IsNull()) return; D3DObjectFont *font = (D3DObjectFont *)context->font.Addr(); D3DObjectFont::Glyph *glyph = (D3DObjectFont::Glyph *)fg->ext_dat; if (glyph == NULL) return; assert(glyph->Compare(fg)); font->SetColor(context->color); font->PushForDraw(glyph, x, y); font->SetFree(false); } void evas_direct3d_select_or_create_font(Direct3DDeviceHandler d3d, void *font) { // The Plan // 1. Create D3DObjectFont with source string = "%p" (font) // 2. Or find this object in the scene // 3. On each texture_new call - fill internal texture with glyph, remember the // glyph pointer - we'll use it. DevicePtr *dev_ptr = SelectDevice(d3d); D3DContext *context = dev_ptr->context; if (!context->font.IsNull() && ((D3DObjectFont *)context->font.Addr())->Compare(font)) return; D3DScene *scene = dev_ptr->scene; static TArray fonts; scene->GetObjectsOfType(fonts); for (int i = 0; i < fonts.Length(); i++) { if (fonts[i]->Compare(font)) { context->font = fonts[i]; return; } } D3DDevice *device = dev_ptr->device; assert(dev_ptr->fonts_buffer_image_id >= 0); Ref new_font = new D3DObjectFont(font, dev_ptr->fonts_buffer_image_id); scene->AddObject(new_font); context->font = new_font; } void evas_direct3d_font_free(Direct3DDeviceHandler d3d, void *font) { DevicePtr *dev_ptr = SelectDevice(d3d); D3DContext *context = dev_ptr->context; D3DScene *scene = dev_ptr->scene; if (context->font.IsNull() || !((D3DObjectFont *)context->font.Addr())->Compare(font)) { D3DScene *scene = dev_ptr->scene; static TArray fonts; scene->GetObjectsOfType(fonts); for (int i = 0; i < fonts.Length(); i++) { if (fonts[i]->Compare(font)) { context->font = fonts[i]; break; } } } scene->DeleteObject(context->font); context->font = NULL; } } // extern "C"