More Thin3D work (textures etc)

This commit is contained in:
Henrik Rydgard 2014-08-17 21:28:34 +02:00
parent 29a60b5952
commit 54b298a63e
10 changed files with 242 additions and 35 deletions

View File

@ -54,7 +54,7 @@ static unsigned int log2i(unsigned int val) {
return ret;
}
int LoadZIMPtr(uint8_t *zim, int datasize, int *width, int *height, int *flags, uint8 **image) {
int LoadZIMPtr(const uint8_t *zim, int datasize, int *width, int *height, int *flags, uint8 **image) {
if (zim[0] != 'Z' || zim[1] != 'I' || zim[2] != 'M' || zim[3] != 'G') {
ELOG("Not a ZIM file");
return 0;

View File

@ -48,4 +48,4 @@ enum {
// Watch out! If the image has mipmaps, multiple values will be written
// to width, height, and image, as if they were arrays, up to 12 (max texture size is 4096 which is 2^12).
int LoadZIM(const char *filename, int *width, int *height, int *flags, uint8_t **image);
int LoadZIMPtr(char *zim, int datasize, int *width, int *height, int *flags, uint8_t **image);
int LoadZIMPtr(const uint8_t *zim, int datasize, int *width, int *height, int *flags, uint8_t **image);

View File

@ -211,6 +211,17 @@ void Matrix4x4::setOrtho(float left, float right, float bottom, float top, float
wz = -(far + near) / (far - near);
}
void Matrix4x4::setOrthoD3D(float left, float right, float bottom, float top, float near, float far) {
setIdentity();
xx = 2.0f / (right - left);
yy = 2.0f / (top - bottom);
zz = 1.0f / (far - near);
wx = -(right + left) / (right - left);
wy = -(top + bottom) / (top - bottom);
//wz = -(far + near) / (far - near);
wz = -near / (far - near);
}
void Matrix4x4::setProjectionInf(const float near_plane, const float fov_horiz, const float aspect) {
empty();
float f = fov_horiz*0.5f;

View File

@ -112,6 +112,7 @@ public:
void setProjectionD3D(float near_plane, float far_plane, float fov_horiz, float aspect = 0.75f);
void setProjectionInf(float near_plane, float fov_horiz, float aspect = 0.75f);
void setOrtho(float left, float right, float bottom, float top, float near, float far);
void setOrthoD3D(float left, float right, float bottom, float top, float near, float far);
void setShadow(float Lx, float Ly, float Lz, float Lw) {
float Pa=0;
float Pb=1;

View File

@ -1,6 +1,7 @@
#include <thin3d/thin3d.h>
#include "image/zim_load.h"
#include "image/png_load.h"
static const char * const glsl_fsTexCol =
"varying vec4 oColor0;\n"
@ -39,7 +40,7 @@ static const char * const glsl_vsCol =
static const char * const hlslVsCol =
"struct VS_INPUT { float3 Position : POSITION; float4 Color0 : COLOR0; };\n"
"struct VS_OUTPUT { float4 Position : POSITION; float4 Color0 : COLOR0; };\n"
"float4x4 WorldViewProj : WORLDVIEWPROJ;\n"
"float4x4 WorldViewProj;\n"
"VS_OUTPUT main(VS_INPUT input) {\n"
" VS_OUTPUT output;\n"
" output.Position = mul(float4(input.Position, 1.0), WorldViewProj);\n"
@ -63,7 +64,7 @@ static const char * const glsl_vsTexCol =
static const char * const hlslVsTexCol =
"struct VS_INPUT { float3 Position : POSITION; float2 Texcoord0 : TEXCOORD0; float4 Color0 : COLOR0; };\n"
"struct VS_OUTPUT { float4 Position : POSITION; float2 Texcoord0 : TEXCOORD0; float4 Color0 : COLOR0; };\n"
"float4x4 WorldViewProj : WORLDVIEWPROJ;\n"
"float4x4 WorldViewProj;\n"
"VS_OUTPUT main(VS_INPUT input) {\n"
" VS_OUTPUT output;\n"
" output.Position = mul(float4(input.Position, 1.0), WorldViewProj);\n"
@ -118,7 +119,7 @@ static T3DImageFormat ZimToT3DFormat(int zim) {
}
}
Thin3DTexture *Thin3DContext::CreateTextureFromFile(const char *filename) {
Thin3DTexture *Thin3DContext::CreateTextureFromFile(const char *filename, T3DFileType type) {
int width[16], height[16], flags;
uint8_t *image[16] = { nullptr };
@ -136,3 +137,39 @@ Thin3DTexture *Thin3DContext::CreateTextureFromFile(const char *filename) {
}
return tex;
}
Thin3DTexture *Thin3DContext::CreateTextureFromFileData(const char *data, int size, T3DFileType type) {
int width[16], height[16], zim_flags = 0;
uint8_t *image[16] = { nullptr };
int num_levels = 0;
T3DImageFormat fmt;
switch (type) {
case ZIM:
num_levels = LoadZIMPtr((const uint8_t *)data, size, width, height, &zim_flags, image);
fmt = ZimToT3DFormat(zim_flags & ZIM_FORMAT_MASK);
break;
case PNG:
if (1 != pngLoadPtr((const unsigned char *)data, size, &width[0], &height[0], &image[0], false)) {
return false;
}
num_levels = 1;
fmt = RGBA8888;
break;
}
if (num_levels == 0) {
return NULL;
}
Thin3DTexture *tex = CreateTexture(LINEAR2D, fmt, width[0], height[0], 1, num_levels);
for (int i = 0; i < num_levels; i++) {
tex->SetImageData(0, 0, 0, width[i], height[i], 1, i, width[i] * 4, image[i]);
free(image[i]);
}
tex->Finalize(zim_flags);
return tex;
}

View File

@ -149,6 +149,13 @@ class Thin3DTexture : public Thin3DObject {
public:
virtual void SetImageData(int x, int y, int z, int width, int height, int depth, int level, int stride, uint8_t *data) = 0;
virtual void AutoGenMipmaps() = 0;
virtual void Finalize(int zim_flags) = 0; // TODO: Tidy up
int Width() { return width_; }
int Height() { return height_; }
int Depth() { return depth_; }
protected:
int width_, height_, depth_;
};
struct Thin3DVertexComponent {
@ -220,6 +227,13 @@ enum T3DCullMode : uint8_t {
CCW,
};
enum T3DFileType {
PNG,
JPEG,
ZIM,
DETECT,
};
class Thin3DContext : public Thin3DObject {
public:
virtual ~Thin3DContext();
@ -232,7 +246,8 @@ public:
virtual Thin3DTexture *CreateTexture(T3DTextureType type, T3DImageFormat format, int width, int height, int depth, int mipLevels) = 0;
// Common Thin3D function, uses CreateTexture
Thin3DTexture *CreateTextureFromFile(const char *filename);
Thin3DTexture *CreateTextureFromFile(const char *filename, T3DFileType fileType);
Thin3DTexture *CreateTextureFromFileData(const char *data, int size, T3DFileType fileType);
// Note that these DO NOT AddRef so you must not ->Release presets unless you manually AddRef them.
Thin3DBlendState *GetBlendStatePreset(T3DBlendStatePreset preset) { return bsPresets_[preset]; }

View File

@ -4,6 +4,7 @@
#include <d3dx9.h>
#include "base/logging.h"
#include "math/lin/matrix4x4.h"
#include "thin3d/thin3d.h"
#include "thin3d/d3dx9_loader.h"
@ -115,11 +116,11 @@ public:
ibuffer_->Unlock();
}
}
void BindVertex(LPDIRECT3DDEVICE9 device, int vertexSize, int offset = 0) {
void BindAsVertexBuf(LPDIRECT3DDEVICE9 device, int vertexSize, int offset = 0) {
if (vbuffer_)
device->SetStreamSource(0, vbuffer_, offset, vertexSize);
}
void BindIndex(LPDIRECT3DDEVICE9 device) {
void BindAsIndexBuf(LPDIRECT3DDEVICE9 device) {
if (ibuffer_)
device->SetIndices(ibuffer_);
}
@ -132,8 +133,11 @@ private:
class Thin3DDX9VertexFormat : public Thin3DVertexFormat {
public:
Thin3DDX9VertexFormat(const std::vector<Thin3DVertexComponent> &components, int stride);
Thin3DDX9VertexFormat(LPDIRECT3DDEVICE9 device, const std::vector<Thin3DVertexComponent> &components, int stride);
int GetStride() const { return stride_; }
void Apply(LPDIRECT3DDEVICE9 device) {
device->SetVertexDeclaration(decl_);
}
private:
LPDIRECT3DVERTEXDECLARATION9 decl_;
int stride_;
@ -158,6 +162,8 @@ public:
device->SetVertexShader(vshader_);
}
}
void SetVector(LPDIRECT3DDEVICE9 device, const char *name, float *value, int n);
void SetMatrix4x4(LPDIRECT3DDEVICE9 device, const char *name, const Matrix4x4 &value);
private:
bool isPixelShader_;
@ -168,20 +174,23 @@ private:
class Thin3DDX9ShaderSet : public Thin3DShaderSet {
public:
Thin3DDX9ShaderSet(LPDIRECT3DDEVICE9 device) : device_(device) {}
Thin3DDX9Shader *vshader;
Thin3DDX9Shader *pshader;
void Apply(LPDIRECT3DDEVICE9 device);
void SetVector(const char *name, float *value, int n);
void SetMatrix4x4(const char *name, const Matrix4x4 &value);
void SetVector(const char *name, float *value, int n) { vshader->SetVector(device_, name, value, n); pshader->SetVector(device_, name, value, n); }
void SetMatrix4x4(const char *name, const Matrix4x4 &value) { vshader->SetMatrix4x4(device_, name, value); } // pshaders don't usually have matrices
private:
LPDIRECT3DDEVICE9 device_;
};
class Thin3DDX9Texture : public Thin3DTexture {
public:
Thin3DDX9Texture(LPDIRECT3DDEVICE9 device, T3DTextureType type, T3DImageFormat format, int width, int height, int depth, int mipLevels);
void SetImageData(int x, int y, int z, int width, int height, int depth, int level, int stride, uint8_t *data) override;
void AutoGenMipmaps() {}
void AutoGenMipmaps() override {}
void SetToSampler(LPDIRECT3DDEVICE9 device, int sampler);
void Finalize(int zim_flags) override {}
private:
T3DTextureType type_;
@ -193,7 +202,7 @@ private:
D3DFORMAT FormatToD3D(T3DImageFormat fmt) {
switch (fmt) {
case RGBA8888: return D3DFMT_A8B8G8R8;
case RGBA8888: return D3DFMT_A8R8G8B8;
case D24S8: return D3DFMT_D24S8;
case D16: return D3DFMT_D16;
default: return D3DFMT_UNKNOWN;
@ -202,6 +211,10 @@ D3DFORMAT FormatToD3D(T3DImageFormat fmt) {
Thin3DDX9Texture::Thin3DDX9Texture(LPDIRECT3DDEVICE9 device, T3DTextureType type, T3DImageFormat format, int width, int height, int depth, int mipLevels)
: type_(type) {
width_ = width;
height_ = height;
depth_ = depth;
tex_ = NULL;
fmt_ = FormatToD3D(format);
HRESULT hr;
switch (type_) {
@ -219,10 +232,29 @@ Thin3DDX9Texture::Thin3DDX9Texture(LPDIRECT3DDEVICE9 device, T3DTextureType type
}
void Thin3DDX9Texture::SetImageData(int x, int y, int z, int width, int height, int depth, int level, int stride, uint8_t *data) {
if (!tex_) {
return;
}
if (level == 0) {
width_ = width;
height_ = height;
depth_ = depth;
}
switch (type_) {
case LINEAR2D:
{
D3DLOCKED_RECT rect;
if (x == 0 && y == 0) {
tex_->LockRect(level, &rect, NULL, D3DLOCK_DISCARD);
for (int i = 0; i < height; i++) {
memcpy((uint8_t *)rect.pBits + rect.Pitch * i, data + stride * i, width * 4);
}
tex_->UnlockRect(level);
}
// tex_->LockRect()
break;
}
default:
// TODO
@ -323,7 +355,7 @@ Thin3DShader *Thin3DDX9Context::CreateFragmentShader(const char *glsl_source, co
}
Thin3DShaderSet *Thin3DDX9Context::CreateShaderSet(Thin3DShader *vshader, Thin3DShader *fshader) {
Thin3DDX9ShaderSet *shaderSet = new Thin3DDX9ShaderSet();
Thin3DDX9ShaderSet *shaderSet = new Thin3DDX9ShaderSet(device_);
shaderSet->vshader = static_cast<Thin3DDX9Shader *>(vshader);
shaderSet->pshader = static_cast<Thin3DDX9Shader *>(fshader);
return shaderSet;
@ -338,7 +370,7 @@ Thin3DDepthStencilState *Thin3DDX9Context::CreateDepthStencilState(bool depthTes
}
Thin3DVertexFormat *Thin3DDX9Context::CreateVertexFormat(const std::vector<Thin3DVertexComponent> &components, int stride) {
Thin3DDX9VertexFormat *fmt = new Thin3DDX9VertexFormat(components, stride);
Thin3DDX9VertexFormat *fmt = new Thin3DDX9VertexFormat(device_, components, stride);
return fmt;
}
@ -404,15 +436,24 @@ static int VertexDataTypeToD3DType(T3DVertexDataType type) {
}
}
Thin3DDX9VertexFormat::Thin3DDX9VertexFormat(const std::vector<Thin3DVertexComponent> &components, int stride) {
D3DVERTEXELEMENT9 *elements = new D3DVERTEXELEMENT9[components.size()];
for (int i = 0; i < components.size(); i++) {
Thin3DDX9VertexFormat::Thin3DDX9VertexFormat(LPDIRECT3DDEVICE9 device, const std::vector<Thin3DVertexComponent> &components, int stride) {
D3DVERTEXELEMENT9 *elements = new D3DVERTEXELEMENT9[components.size() + 1];
int i;
for (i = 0; i < components.size(); i++) {
elements[i].Stream = 0;
elements[i].Offset = components[i].offset;
elements[i].Method = D3DDECLMETHOD_DEFAULT;
SemanticToD3D9UsageAndIndex(components[i].semantic, &elements[i].Usage, &elements[i].UsageIndex);
elements[i].Type = VertexDataTypeToD3DType(components[i].type);
}
D3DVERTEXELEMENT9 end = D3DDECL_END();
// Zero the last one.
memcpy(&elements[i], &end, sizeof(elements[i]));
HRESULT hr = device->CreateVertexDeclaration(elements, &decl_);
if (FAILED(hr)) {
ELOG("Error creating vertex decl");
}
stride_ = stride;
}
@ -429,27 +470,35 @@ void Thin3DDX9Context::SetRenderState(T3DRenderState rs, uint32_t value) {
switch (rs) {
case T3DRenderState::CULL_MODE:
switch (value) {
case T3DCullMode::CCW: device_->SetRenderState(D3DRS_CULLMODE, D3DCULL_CCW);
case T3DCullMode::CW: device_->SetRenderState(D3DRS_CULLMODE, D3DCULL_CW);
case T3DCullMode::NO_CULL: device_->SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE);
case T3DCullMode::CCW: device_->SetRenderState(D3DRS_CULLMODE, D3DCULL_CCW); break;
case T3DCullMode::CW: device_->SetRenderState(D3DRS_CULLMODE, D3DCULL_CW); break;
case T3DCullMode::NO_CULL: device_->SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE); break;
}
break;
}
}
void Thin3DDX9Context::Draw(T3DPrimitive prim, Thin3DShaderSet *pipeline, Thin3DVertexFormat *format, Thin3DBuffer *vdata, int vertexCount, int offset) {
void Thin3DDX9Context::Draw(T3DPrimitive prim, Thin3DShaderSet *shaderSet, Thin3DVertexFormat *format, Thin3DBuffer *vdata, int vertexCount, int offset) {
Thin3DDX9Buffer *vbuf = static_cast<Thin3DDX9Buffer *>(vdata);
Thin3DDX9VertexFormat *fmt = static_cast<Thin3DDX9VertexFormat *>(format);
vbuf->BindVertex(device_, fmt->GetStride(), offset);
Thin3DDX9ShaderSet *ss = static_cast<Thin3DDX9ShaderSet*>(shaderSet);
ss->Apply(device_);
fmt->Apply(device_);
vbuf->BindAsVertexBuf(device_, fmt->GetStride(), offset);
device_->DrawPrimitive(primToD3D9[prim], offset, vertexCount / 3);
}
void Thin3DDX9Context::DrawIndexed(T3DPrimitive prim, Thin3DShaderSet *pipeline, Thin3DVertexFormat *format, Thin3DBuffer *vdata, Thin3DBuffer *idata, int vertexCount, int offset) {
void Thin3DDX9Context::DrawIndexed(T3DPrimitive prim, Thin3DShaderSet *shaderSet, Thin3DVertexFormat *format, Thin3DBuffer *vdata, Thin3DBuffer *idata, int vertexCount, int offset) {
Thin3DDX9Buffer *vbuf = static_cast<Thin3DDX9Buffer *>(vdata);
Thin3DDX9Buffer *ibuf = static_cast<Thin3DDX9Buffer *>(idata);
Thin3DDX9VertexFormat *fmt = static_cast<Thin3DDX9VertexFormat *>(format);
vbuf->BindVertex(device_, fmt->GetStride(), offset);
ibuf->BindIndex(device_);
Thin3DDX9ShaderSet *ss = static_cast<Thin3DDX9ShaderSet*>(shaderSet);
ss->Apply(device_);
fmt->Apply(device_);
vbuf->BindAsVertexBuf(device_, fmt->GetStride(), offset);
ibuf->BindAsIndexBuf(device_);
device_->DrawIndexedPrimitive(primToD3D9[prim], 0, 0, vertexCount, 0, vertexCount / 3);
}
@ -518,14 +567,33 @@ bool Thin3DDX9Shader::Compile(LPDIRECT3DDEVICE9 device, const char *source, cons
success = SUCCEEDED(result);
}
D3DXCONSTANTTABLE_DESC desc;
constantTable_->GetDesc(&desc);
//D3DXCONSTANT_DESC *descs = new D3DXCONSTANT_DESC[desc.Constants];
//constantTable_->GetConstantDesc()
for (int i = 0; i < desc.Constants; i++) {
D3DXHANDLE c = constantTable_->GetConstant(NULL, i);
D3DXCONSTANT_DESC cdesc;
UINT count = 1;
constantTable_->GetConstantDesc(c, &cdesc, &count);
ILOG("%s", cdesc.Name);
}
codeBuffer->Release();
return true;
}
void Thin3DDX9ShaderSet::SetVector(const char *name, float *value, int n) {
void Thin3DDX9Shader::SetVector(LPDIRECT3DDEVICE9 device, const char *name, float *value, int n) {
D3DXHANDLE handle = constantTable_->GetConstantByName(NULL, name);
if (handle) {
constantTable_->SetFloatArray(device, handle, value, n);
}
}
void Thin3DDX9ShaderSet::SetMatrix4x4(const char *name, const Matrix4x4 &value) {
void Thin3DDX9Shader::SetMatrix4x4(LPDIRECT3DDEVICE9 device, const char *name, const Matrix4x4 &value) {
D3DXHANDLE handle = constantTable_->GetConstantByName(NULL, name);
if (handle) {
constantTable_->SetFloatArray(device, handle, value.getReadPtr(), 16);
}
}

View File

@ -4,6 +4,7 @@
#include <map>
#include "base/logging.h"
#include "image/zim_load.h"
#include "math/lin/matrix4x4.h"
#include "thin3d/thin3d.h"
#include "gfx_es2/gl_state.h"
@ -317,12 +318,17 @@ public:
void SetImageData(int x, int y, int z, int width, int height, int depth, int level, int stride, uint8_t *data) override;
void AutoGenMipmaps() override;
GLuint tex_;
void Bind() {
glBindTexture(target_, tex_);
}
void Finalize(int zim_flags);
private:
GLuint tex_;
GLuint target_;
T3DImageFormat format_;
int width_, height_, depth_, mipLevels_;
int mipLevels_;
};
Thin3DTexture *Thin3DGLContext::CreateTexture(T3DTextureType type, T3DImageFormat format, int width, int height, int depth, int mipLevels) {
@ -336,6 +342,12 @@ void Thin3DGLTexture::AutoGenMipmaps() {
}
void Thin3DGLTexture::SetImageData(int x, int y, int z, int width, int height, int depth, int level, int stride, uint8_t *data) {
if (level == 0) {
width_ = width;
height_ = height;
depth_ = depth;
}
int internalFormat;
int format;
int type;
@ -357,6 +369,25 @@ void Thin3DGLTexture::SetImageData(int x, int y, int z, int width, int height, i
}
}
bool isPowerOf2(int n) {
return n == 1 || (n & (n - 1)) == 0;
}
void Thin3DGLTexture::Finalize(int zim_flags) {
GLenum wrap = GL_REPEAT;
if ((zim_flags & ZIM_CLAMP) || !isPowerOf2(width_) || !isPowerOf2(height_))
wrap = GL_CLAMP_TO_EDGE;
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, wrap);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, wrap);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
if ((zim_flags & (ZIM_HAS_MIPS | ZIM_GEN_MIPS))) {
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST);
} else {
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
}
}
void Thin3DGLVertexFormat::Compile() {
int sem = 0;
for (int i = 0; i < (int)components_.size(); i++) {
@ -408,10 +439,10 @@ Thin3DShaderSet *Thin3DGLContext::CreateShaderSet(Thin3DShader *vshader, Thin3DS
}
void Thin3DGLContext::SetTextures(int start, int count, Thin3DTexture **textures) {
for (int i = start; i < count; i++) {
for (int i = start; i < start + count; i++) {
Thin3DGLTexture *glTex = static_cast<Thin3DGLTexture *>(textures[i]);
glActiveTexture(GL_TEXTURE0 + i);
glBindTexture(GL_TEXTURE_2D, glTex->tex_);
glTex->Bind();
}
glActiveTexture(GL_TEXTURE0);
}
@ -545,7 +576,6 @@ void Thin3DGLContext::Draw(T3DPrimitive prim, Thin3DShaderSet *pipeline, Thin3DV
vbuf->Bind();
fmt->Apply();
pipe->Apply();
glDrawArrays(primToGL[prim], offset, vertexCount);

View File

@ -11,6 +11,7 @@
#include "ui/ui.h"
#include "ui/view.h"
#include "ui/ui_context.h"
#include "thin3d/thin3d.h"
#include "base/NativeApp.h"
namespace UI {
@ -535,6 +536,28 @@ void TextureView::Draw(UIContext &dc) {
}
}
void Thin3DTextureView::GetContentDimensions(const UIContext &dc, float &w, float &h) const {
// TODO: involve sizemode
if (texture_) {
w = (float)texture_->Width();
h = (float)texture_->Height();
} else {
w = 16;
h = 16;
}
}
void Thin3DTextureView::Draw(UIContext &dc) {
// TODO: involve sizemode
if (texture_) {
dc.Flush();
dc.GetThin3DContext()->SetTexture(0, texture_);
dc.Draw()->Rect(bounds_.x, bounds_.y, bounds_.w, bounds_.h, color_);
dc.Flush();
dc.RebindTexture();
}
}
ImageFileView::ImageFileView(std::string filename, ImageSizeMode sizeMode, LayoutParams *layoutParams)
: InertView(layoutParams), color_(0xFFFFFFFF), sizeMode_(sizeMode) {
texture_ = new Texture();

View File

@ -33,6 +33,9 @@ class DrawBuffer;
class Texture;
class UIContext;
class Thin3DTexture;
// I don't generally like namespaces but I think we do need one for UI, so many potentially-clashing names.
namespace UI {
@ -725,6 +728,25 @@ private:
ImageSizeMode sizeMode_;
};
// TextureView takes a texture that is assumed to be alive during the lifetime
// of the view.
class Thin3DTextureView : public InertView {
public:
Thin3DTextureView(Thin3DTexture *texture, ImageSizeMode sizeMode, LayoutParams *layoutParams = 0)
: InertView(layoutParams), texture_(texture), sizeMode_(sizeMode) {}
virtual void GetContentDimensions(const UIContext &dc, float &w, float &h) const;
virtual void Draw(UIContext &dc);
void SetTexture(Thin3DTexture *texture) { texture_ = texture; }
void SetColor(uint32_t color) { color_ = color; }
private:
Thin3DTexture *texture_;
uint32_t color_;
ImageSizeMode sizeMode_;
};
// ImageFileView takes a filename and keeps track of the texture by itself.
class ImageFileView : public InertView {
public: