994 lines
22 KiB
C++
994 lines
22 KiB
C++
#include "gxcanvas.hpp"
|
|
#include "asmcoder.hpp"
|
|
#include "gxgraphics.hpp"
|
|
#include "gxruntime.hpp"
|
|
#include "gxfont.hpp"
|
|
|
|
#define DEBUG_BITMASK
|
|
|
|
static int canvas_cnt;
|
|
static DDBLTFX bltfx = {sizeof(DDBLTFX)};
|
|
|
|
extern gxRuntime* gx_runtime;
|
|
|
|
static unsigned FWMS[] = {0xffffffff, 0x7fffffff, 0x3fffffff, 0x1fffffff, 0x0fffffff, 0x07ffffff, 0x03ffffff,
|
|
0x01ffffff, 0x00ffffff, 0x007fffff, 0x003fffff, 0x001fffff, 0x000fffff, 0x0007ffff,
|
|
0x0003ffff, 0x0001ffff, 0x0000ffff, 0x00007fff, 0x00003fff, 0x00001fff, 0x00000fff,
|
|
0x000007ff, 0x000003ff, 0x000001ff, 0x000000ff, 0x0000007f, 0x0000003f, 0x0000001f,
|
|
0x0000000f, 0x00000007, 0x00000003, 0x00000001};
|
|
static unsigned LWMS[] = {0x80000000, 0xc0000000, 0xe0000000, 0xf0000000, 0xf8000000, 0xfc000000, 0xfe000000,
|
|
0xff000000, 0xff800000, 0xffc00000, 0xffe00000, 0xfff00000, 0xfff80000, 0xfffc0000,
|
|
0xfffe0000, 0xffff0000, 0xffff8000, 0xffffc000, 0xffffe000, 0xfffff000, 0xfffff800,
|
|
0xfffffc00, 0xfffffe00, 0xffffff00, 0xffffff80, 0xffffffc0, 0xffffffe0, 0xfffffff0,
|
|
0xfffffff8, 0xfffffffc, 0xfffffffe, 0xffffffff};
|
|
|
|
static void calcShifts(unsigned mask, unsigned char* shr, unsigned char* shl)
|
|
{
|
|
if (mask) {
|
|
for (*shl = 0; !(mask & 1); ++*shl, mask >>= 1) {
|
|
}
|
|
for (*shr = 8; mask & 1; --*shr, mask >>= 1) {
|
|
}
|
|
} else
|
|
*shr = *shl = 0;
|
|
}
|
|
|
|
struct Rect : public RECT {
|
|
Rect() {}
|
|
Rect(int x, int y, int w, int h)
|
|
{
|
|
left = x;
|
|
top = y;
|
|
right = x + w;
|
|
bottom = y + h;
|
|
}
|
|
};
|
|
|
|
static bool clip(const RECT& viewport, RECT* d)
|
|
{
|
|
if (d->right <= d->left || d->bottom <= d->top || d->left >= viewport.right || d->right <= viewport.left
|
|
|| d->top >= viewport.bottom || d->bottom <= viewport.top)
|
|
return false;
|
|
if (d->left < viewport.left)
|
|
d->left = viewport.left;
|
|
if (d->right > viewport.right)
|
|
d->right = viewport.right;
|
|
if (d->top < viewport.top)
|
|
d->top = viewport.top;
|
|
if (d->bottom > viewport.bottom)
|
|
d->bottom = viewport.bottom;
|
|
return true;
|
|
}
|
|
|
|
static bool clip(const RECT& viewport, RECT* d, RECT* s)
|
|
{
|
|
if (d->right <= d->left || d->bottom <= d->top || d->left >= viewport.right || d->right <= viewport.left
|
|
|| d->top >= viewport.bottom || d->bottom <= viewport.top)
|
|
return false;
|
|
int dx, dy;
|
|
if ((dx = viewport.left - d->left) > 0) {
|
|
d->left += dx;
|
|
s->left += dx;
|
|
}
|
|
if ((dx = viewport.right - d->right) < 0) {
|
|
d->right += dx;
|
|
s->right += dx;
|
|
}
|
|
if ((dy = viewport.top - d->top) > 0) {
|
|
d->top += dy;
|
|
s->top += dy;
|
|
}
|
|
if ((dy = viewport.bottom - d->bottom) < 0) {
|
|
d->bottom += dy;
|
|
s->bottom += dy;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
gxCanvas::gxCanvas(gxGraphics* g, IDirectDrawSurface7* s, int f)
|
|
: graphics(g), main_surf(s), surf(0), z_surf(0), flags(f), cube_mode(CUBEMODE_REFLECTION | CUBESPACE_WORLD),
|
|
t_surf(0), cm_mask(0), locked_cnt(0), mod_cnt(0), remip_cnt(0)
|
|
{
|
|
if (flags & CANVAS_TEX_CUBE) {
|
|
cube_surfs[2] = main_surf;
|
|
for (int k = 0; k < 6; ++k) {
|
|
if (k == 2)
|
|
continue;
|
|
DWORD n;
|
|
switch (k) {
|
|
case 0:
|
|
n = DDSCAPS2_CUBEMAP_NEGATIVEX;
|
|
break;
|
|
case 1:
|
|
n = DDSCAPS2_CUBEMAP_POSITIVEZ;
|
|
break;
|
|
case 2:
|
|
n = DDSCAPS2_CUBEMAP_POSITIVEX;
|
|
break;
|
|
case 3:
|
|
n = DDSCAPS2_CUBEMAP_NEGATIVEZ;
|
|
break;
|
|
case 4:
|
|
n = DDSCAPS2_CUBEMAP_POSITIVEY;
|
|
break;
|
|
case 5:
|
|
n = DDSCAPS2_CUBEMAP_NEGATIVEY;
|
|
break;
|
|
default:
|
|
return;
|
|
}
|
|
DDSCAPS2 caps = {0};
|
|
caps.dwCaps2 = DDSCAPS2_CUBEMAP | n;
|
|
main_surf->GetAttachedSurface(&caps, &cube_surfs[k]);
|
|
}
|
|
surf = cube_surfs[1];
|
|
} else {
|
|
surf = main_surf;
|
|
memset(cube_surfs, 0, sizeof(cube_surfs));
|
|
}
|
|
|
|
DDSURFACEDESC2 desc = {sizeof(desc)};
|
|
surf->GetSurfaceDesc(&desc);
|
|
format.setFormat(desc.ddpfPixelFormat);
|
|
|
|
// Create Z-Buffer
|
|
if (flags & CANVAS_3DRENDER) {
|
|
DDSURFACEDESC2 zdesc = {sizeof(zdesc)};
|
|
zdesc.dwWidth = desc.dwWidth;
|
|
zdesc.dwHeight = desc.dwHeight;
|
|
zdesc.ddpfPixelFormat = g->zbuffFmt;
|
|
zdesc.dwFlags = DDSD_PIXELFORMAT | DDSD_WIDTH | DDSD_HEIGHT | DDSD_CAPS;
|
|
zdesc.ddsCaps.dwCaps =
|
|
DDSCAPS_VIDEOMEMORY | DDSCAPS_3DDEVICE | DDSD_PIXELFORMAT | DDSD_WIDTH | DDSD_HEIGHT | DDSCAPS_ZBUFFER;
|
|
|
|
if (g->dirDraw->CreateSurface(&zdesc, &z_surf, 0) >= 0) {
|
|
surf->AddAttachedSurface(z_surf);
|
|
}
|
|
}
|
|
|
|
clip_rect.left = clip_rect.top = 0;
|
|
clip_rect.right = desc.dwWidth;
|
|
clip_rect.bottom = desc.dwHeight;
|
|
cm_pitch = (clip_rect.right + 31) / 32 + 1;
|
|
setMask(0);
|
|
setColor(~0);
|
|
setClsColor(0);
|
|
setOrigin(0, 0);
|
|
setHandle(0, 0);
|
|
setFont(graphics->getDefaultFont());
|
|
setViewport(0, 0, getWidth(), getHeight());
|
|
if (flags & gxCanvas::CANVAS_TEXTURE)
|
|
ddUtil::buildMipMaps(surf);
|
|
}
|
|
|
|
gxCanvas::~gxCanvas()
|
|
{
|
|
delete[] cm_mask;
|
|
if (locked_cnt)
|
|
surf->Unlock(0);
|
|
if (t_surf)
|
|
t_surf->Release();
|
|
releaseZBuffer();
|
|
main_surf->Release();
|
|
}
|
|
|
|
void gxCanvas::backup() const
|
|
{
|
|
if (flags & CANVAS_TEX_CUBE)
|
|
return;
|
|
|
|
if (!t_surf) {
|
|
DDSURFACEDESC2 desc = {sizeof(desc)};
|
|
if (surf->GetSurfaceDesc(&desc) < 0)
|
|
return;
|
|
if (desc.ddsCaps.dwCaps & DDSCAPS_SYSTEMMEMORY)
|
|
return;
|
|
|
|
DDSURFACEDESC2 t_desc = {sizeof(t_desc)};
|
|
t_desc.dwFlags = DDSD_CAPS | DDSD_PIXELFORMAT | DDSD_WIDTH | DDSD_HEIGHT;
|
|
t_desc.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN | DDSCAPS_SYSTEMMEMORY;
|
|
t_desc.dwWidth = desc.dwWidth;
|
|
t_desc.dwHeight = desc.dwHeight;
|
|
t_desc.ddpfPixelFormat = desc.ddpfPixelFormat;
|
|
|
|
if (graphics->dirDraw->CreateSurface(&t_desc, &t_surf, 0) < 0) {
|
|
t_surf = 0;
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (t_surf->Blt(0, surf, 0, DDBLT_WAIT, 0) < 0)
|
|
return;
|
|
}
|
|
|
|
void gxCanvas::restore() const
|
|
{
|
|
if (!t_surf)
|
|
return;
|
|
|
|
if (surf->Blt(0, t_surf, 0, DDBLT_WAIT, 0) < 0)
|
|
return;
|
|
}
|
|
|
|
IDirectDrawSurface7* gxCanvas::getSurface() const
|
|
{
|
|
return surf;
|
|
}
|
|
|
|
IDirectDrawSurface7* gxCanvas::getTexSurface() const
|
|
{
|
|
if (mod_cnt == remip_cnt)
|
|
return main_surf;
|
|
ddUtil::buildMipMaps(surf);
|
|
remip_cnt = mod_cnt;
|
|
return main_surf;
|
|
}
|
|
|
|
bool gxCanvas::clip(RECT* d) const
|
|
{
|
|
return ::clip(viewport, d);
|
|
}
|
|
|
|
bool gxCanvas::clip(RECT* d, RECT* s) const
|
|
{
|
|
return ::clip(viewport, d, s);
|
|
}
|
|
|
|
void gxCanvas::updateBitMask(const RECT& r) const
|
|
{
|
|
int w = r.right - r.left;
|
|
if (w <= 0)
|
|
return;
|
|
int h = r.bottom - r.top;
|
|
if (h <= 0)
|
|
return;
|
|
|
|
lock();
|
|
RECT t = r;
|
|
t.left &= ~31;
|
|
t.right = (t.right + 31) & ~31;
|
|
w = (t.right - t.left) / 32;
|
|
unsigned char* src_row = locked_surf + t.top * locked_pitch + t.left * format.getPitch();
|
|
unsigned* dest_row = cm_mask + t.top * cm_pitch + t.left / 32;
|
|
unsigned mask_argb = format.toARGB(mask_surf) & 0xffffff;
|
|
|
|
#ifdef _DEBUG_BITMASK
|
|
unsigned* cm_mask_end = cm_mask + cm_pitch * clip_rect.bottom;
|
|
#endif
|
|
|
|
while (h--) {
|
|
unsigned* dest = dest_row;
|
|
unsigned char* src = src_row;
|
|
for (int c = 0; c < w; ++c) {
|
|
unsigned mask = 0;
|
|
for (int x = 0; x < 32; ++x) {
|
|
unsigned pix = format.getPixel(src) & 0xffffff;
|
|
mask = (mask << 1) | (pix != mask_argb);
|
|
src += format.getPitch();
|
|
}
|
|
#ifdef _DEBUG_BITMASK
|
|
if (dest < cm_mask || dest >= cm_mask_end) {
|
|
gx_runtime->debugError("gxCanvas::updateBitMask dest out of range");
|
|
}
|
|
#endif
|
|
*dest++ = mask;
|
|
}
|
|
dest_row += cm_pitch;
|
|
src_row += locked_pitch;
|
|
}
|
|
unlock();
|
|
}
|
|
|
|
void gxCanvas::setModify(int n)
|
|
{
|
|
mod_cnt = n;
|
|
}
|
|
|
|
int gxCanvas::getModify() const
|
|
{
|
|
return mod_cnt;
|
|
}
|
|
|
|
bool gxCanvas::attachZBuffer()
|
|
{
|
|
if (z_surf)
|
|
return true;
|
|
DDSURFACEDESC2 desc = {sizeof(desc)};
|
|
desc.dwFlags = DDSD_WIDTH | DDSD_HEIGHT | DDSD_CAPS | DDSD_PIXELFORMAT;
|
|
desc.dwWidth = getWidth();
|
|
desc.dwHeight = getHeight();
|
|
desc.ddsCaps.dwCaps = DDSCAPS_ZBUFFER | DDSCAPS_VIDEOMEMORY;
|
|
desc.ddpfPixelFormat = graphics->zbuffFmt;
|
|
if (graphics->dirDraw->CreateSurface(&desc, &z_surf, 0) < 0)
|
|
return false;
|
|
surf->AddAttachedSurface(z_surf);
|
|
return true;
|
|
}
|
|
|
|
void gxCanvas::releaseZBuffer()
|
|
{
|
|
if (!z_surf)
|
|
return;
|
|
surf->DeleteAttachedSurface(0, z_surf);
|
|
z_surf->Release();
|
|
z_surf = 0;
|
|
}
|
|
|
|
void gxCanvas::damage(const RECT& r) const
|
|
{
|
|
++mod_cnt;
|
|
if (cm_mask)
|
|
updateBitMask(r);
|
|
}
|
|
|
|
void gxCanvas::setFont(gxFont* f)
|
|
{
|
|
font = f;
|
|
}
|
|
|
|
void gxCanvas::setMask(unsigned argb)
|
|
{
|
|
mask_surf = format.fromARGB(argb);
|
|
}
|
|
|
|
void gxCanvas::setColor(unsigned argb)
|
|
{
|
|
argb |= 0xff000000;
|
|
color_argb = argb;
|
|
color_surf = format.fromARGB(argb);
|
|
}
|
|
|
|
void gxCanvas::setClsColor(unsigned argb)
|
|
{
|
|
argb |= 0xff000000;
|
|
clsColor_surf = format.fromARGB(argb);
|
|
}
|
|
|
|
void gxCanvas::setOrigin(int x, int y)
|
|
{
|
|
origin_x = x;
|
|
origin_y = y;
|
|
}
|
|
|
|
void gxCanvas::setHandle(int x, int y)
|
|
{
|
|
handle_x = x;
|
|
handle_y = y;
|
|
}
|
|
|
|
void gxCanvas::setViewport(int x, int y, int w, int h)
|
|
{
|
|
Rect r(x, y, w, h);
|
|
if (!::clip(clip_rect, &r))
|
|
r = Rect(0, 0, 0, 0);
|
|
viewport = r;
|
|
}
|
|
|
|
//renderering primitives
|
|
void gxCanvas::cls()
|
|
{
|
|
bltfx.dwFillColor = clsColor_surf;
|
|
surf->Blt(&viewport, 0, 0, DDBLT_WAIT | DDBLT_COLORFILL, &bltfx);
|
|
damage(viewport);
|
|
}
|
|
|
|
void gxCanvas::plot(int x, int y)
|
|
{
|
|
x += origin_x;
|
|
if (x < viewport.left || x >= viewport.right)
|
|
return;
|
|
y += origin_y;
|
|
if (y < viewport.top || y >= viewport.bottom)
|
|
return;
|
|
bltfx.dwFillColor = color_surf;
|
|
Rect dest(x, y, 1, 1);
|
|
surf->Blt(&dest, 0, 0, DDBLT_WAIT | DDBLT_COLORFILL, &bltfx);
|
|
damage(dest);
|
|
}
|
|
|
|
void gxCanvas::line(int x0, int y0, int x1, int y1)
|
|
{
|
|
int ddf, padj, sadj;
|
|
int dx, dy, sx, sy, ax, ay;
|
|
|
|
x0 += origin_x;
|
|
y0 += origin_y;
|
|
x1 += origin_x;
|
|
y1 += origin_y;
|
|
|
|
int cx0, cx1, cy0, cy1, clip0, clip1;
|
|
|
|
cx0 = viewport.left;
|
|
cx1 = viewport.right - 1;
|
|
cy0 = viewport.top;
|
|
cy1 = viewport.bottom - 1;
|
|
|
|
while (true) {
|
|
clip0 = 0;
|
|
clip1 = 0;
|
|
|
|
if (y0 > cy1)
|
|
clip0 |= 1;
|
|
else if (y0 < cy0)
|
|
clip0 |= 2;
|
|
if (x0 > cx1)
|
|
clip0 |= 4;
|
|
else if (x0 < cx0)
|
|
clip0 |= 8;
|
|
if (y1 > cy1)
|
|
clip1 |= 1;
|
|
else if (y1 < cy0)
|
|
clip1 |= 2;
|
|
if (x1 > cx1)
|
|
clip1 |= 4;
|
|
else if (x1 < cx0)
|
|
clip1 |= 8;
|
|
|
|
if ((clip0 | clip1) == 0)
|
|
break; //draw line
|
|
if ((clip0 & clip1) != 0)
|
|
return; //outside
|
|
|
|
if ((clip0 & 1) == 1) {
|
|
x0 = x0 + ((x1 - x0) * (cy1 - y0)) / (y1 - y0);
|
|
y0 = cy1;
|
|
continue;
|
|
}
|
|
if ((clip0 & 2) == 2) {
|
|
x0 = x0 + ((x1 - x0) * (cy0 - y0)) / (y1 - y0);
|
|
y0 = cy0;
|
|
continue;
|
|
}
|
|
if ((clip0 & 4) == 4) {
|
|
y0 = y0 + ((y1 - y0) * (cx1 - x0)) / (x1 - x0);
|
|
x0 = cx1;
|
|
continue;
|
|
}
|
|
if ((clip0 & 8) == 8) {
|
|
y0 = y0 + ((y1 - y0) * (cx0 - x0)) / (x1 - x0);
|
|
x0 = cx0;
|
|
continue;
|
|
}
|
|
|
|
if ((clip1 & 1) == 1) {
|
|
x1 = x0 + ((x1 - x0) * (cy1 - y0)) / (y1 - y0);
|
|
y1 = cy1;
|
|
continue;
|
|
}
|
|
if ((clip1 & 2) == 2) {
|
|
x1 = x0 + ((x1 - x0) * (cy0 - y0)) / (y1 - y0);
|
|
y1 = cy0;
|
|
continue;
|
|
}
|
|
if ((clip1 & 4) == 4) {
|
|
y1 = y0 + ((y1 - y0) * (cx1 - x0)) / (x1 - x0);
|
|
x1 = cx1;
|
|
continue;
|
|
}
|
|
if ((clip1 & 8) == 8) {
|
|
y1 = y0 + ((y1 - y0) * (cx0 - x0)) / (x1 - x0);
|
|
x1 = cx0;
|
|
continue;
|
|
}
|
|
}
|
|
|
|
dx = x1 - x0;
|
|
dy = y1 - y0;
|
|
if ((dx | dy) == 0) {
|
|
setPixel(x0, y0, color_argb);
|
|
return;
|
|
}
|
|
|
|
if (dx >= 0) {
|
|
sx = 1;
|
|
ax = dx;
|
|
} else {
|
|
sx = -1;
|
|
ax = -dx;
|
|
}
|
|
if (dy >= 0) {
|
|
sy = 1;
|
|
ay = dy;
|
|
} else {
|
|
sy = -1;
|
|
ay = -dy;
|
|
}
|
|
|
|
lock();
|
|
if (ax > ay) {
|
|
ddf = -ax;
|
|
sadj = ax + ax;
|
|
padj = ay + ay;
|
|
while (ax-- >= 0) {
|
|
setPixelFast(x0, y0, color_argb);
|
|
x0 += sx;
|
|
ddf += padj;
|
|
if (ddf >= 0) {
|
|
y0 += sy;
|
|
ddf -= sadj;
|
|
}
|
|
}
|
|
} else {
|
|
ddf = -ay;
|
|
sadj = ay + ay;
|
|
padj = ax + ax;
|
|
while (ay-- >= 0) {
|
|
setPixelFast(x0, y0, color_argb);
|
|
y0 += sy;
|
|
ddf += padj;
|
|
if (ddf >= 0) {
|
|
x0 += sx;
|
|
ddf -= sadj;
|
|
}
|
|
}
|
|
}
|
|
unlock();
|
|
}
|
|
|
|
void gxCanvas::rect(int x, int y, int w, int h, bool solid)
|
|
{
|
|
x += origin_x;
|
|
y += origin_y;
|
|
Rect dest(x, y, w, h);
|
|
if (!clip(&dest))
|
|
return;
|
|
|
|
bltfx.dwFillColor = color_surf;
|
|
|
|
if (solid) {
|
|
surf->Blt(&dest, 0, 0, DDBLT_WAIT | DDBLT_COLORFILL, &bltfx);
|
|
damage(dest);
|
|
return;
|
|
}
|
|
Rect r1(x, y, w, 1);
|
|
if (clip(&r1)) {
|
|
surf->Blt(&r1, 0, 0, DDBLT_WAIT | DDBLT_COLORFILL, &bltfx);
|
|
}
|
|
Rect r2(x, y, 1, h);
|
|
if (clip(&r2)) {
|
|
surf->Blt(&r2, 0, 0, DDBLT_WAIT | DDBLT_COLORFILL, &bltfx);
|
|
}
|
|
Rect r3(x + w - 1, y, 1, h);
|
|
if (clip(&r3)) {
|
|
surf->Blt(&r3, 0, 0, DDBLT_WAIT | DDBLT_COLORFILL, &bltfx);
|
|
}
|
|
Rect r4(x, y + h - 1, w, 1);
|
|
if (clip(&r4)) {
|
|
surf->Blt(&r4, 0, 0, DDBLT_WAIT | DDBLT_COLORFILL, &bltfx);
|
|
}
|
|
damage(dest);
|
|
}
|
|
|
|
void gxCanvas::oval(int x1, int y1, int w, int h, bool solid)
|
|
{
|
|
x1 += origin_x;
|
|
y1 += origin_y;
|
|
Rect dest(x1, y1, w, h);
|
|
if (!clip(&dest))
|
|
return;
|
|
|
|
bltfx.dwFillColor = color_surf;
|
|
|
|
float xr = w * .5f, yr = h * .5f, ar = (float)w / (float)h;
|
|
float cx = x1 + xr + .5f, cy = y1 + yr - .5f, rsq = yr * yr, y;
|
|
|
|
if (solid) {
|
|
y = dest.top - cy;
|
|
for (int t = dest.top; t < dest.bottom; ++y, ++t) {
|
|
float x = sqrt(rsq - y * y) * ar;
|
|
int xa = floor(cx - x), xb = floor(cx + x);
|
|
if (xb <= xa || xa >= viewport.right || xb <= viewport.left)
|
|
continue;
|
|
Rect dr;
|
|
dr.top = t;
|
|
dr.bottom = t + 1;
|
|
dr.left = xa < viewport.left ? viewport.left : xa;
|
|
dr.right = xb > viewport.right ? viewport.right : xb;
|
|
surf->Blt(&dr, 0, 0, DDBLT_WAIT | DDBLT_COLORFILL, &bltfx);
|
|
}
|
|
damage(dest);
|
|
return;
|
|
}
|
|
|
|
int p_xa, p_xb, t, hh = floor(cy);
|
|
|
|
p_xa = p_xb = cx;
|
|
t = dest.top;
|
|
y = t - cy;
|
|
if (dest.top > y1) {
|
|
--t;
|
|
--y;
|
|
}
|
|
for (; t <= hh; ++y, ++t) {
|
|
float x = sqrt(rsq - y * y) * ar;
|
|
int xa = floor(cx - x), xb = floor(cx + x);
|
|
Rect r1(xa, t, p_xa - xa, 1);
|
|
if (r1.right <= r1.left)
|
|
r1.right = r1.left + 1;
|
|
if (clip(&r1))
|
|
surf->Blt(&r1, 0, 0, DDBLT_WAIT | DDBLT_COLORFILL, &bltfx);
|
|
Rect r2(p_xb, t, xb - p_xb, 1);
|
|
if (r2.left >= r2.right)
|
|
r2.left = r2.right - 1;
|
|
if (clip(&r2))
|
|
surf->Blt(&r2, 0, 0, DDBLT_WAIT | DDBLT_COLORFILL, &bltfx);
|
|
p_xa = xa;
|
|
p_xb = xb;
|
|
}
|
|
|
|
p_xa = p_xb = cx;
|
|
t = dest.bottom - 1;
|
|
y = t - cy;
|
|
if (dest.bottom < y1 + h) {
|
|
++t;
|
|
++y;
|
|
}
|
|
for (; t > hh; --y, --t) {
|
|
float x = sqrt(rsq - y * y) * ar;
|
|
int xa = floor(cx - x), xb = floor(cx + x);
|
|
Rect r1(xa, t, p_xa - xa, 1);
|
|
if (r1.right <= r1.left)
|
|
r1.right = r1.left + 1;
|
|
if (clip(&r1))
|
|
surf->Blt(&r1, 0, 0, DDBLT_WAIT | DDBLT_COLORFILL, &bltfx);
|
|
Rect r2(p_xb, t, xb - p_xb, 1);
|
|
if (r2.left >= r2.right)
|
|
r2.left = r2.right - 1;
|
|
if (clip(&r2))
|
|
surf->Blt(&r2, 0, 0, DDBLT_WAIT | DDBLT_COLORFILL, &bltfx);
|
|
p_xa = xa;
|
|
p_xb = xb;
|
|
}
|
|
damage(dest);
|
|
}
|
|
|
|
void gxCanvas::blit(int x, int y, gxCanvas* src, int src_x, int src_y, int src_w, int src_h, bool solid)
|
|
{
|
|
x += origin_x - src->handle_x;
|
|
y += origin_y - src->handle_y;
|
|
Rect dest_r(x, y, src_w, src_h), src_r(src_x, src_y, src_w, src_h);
|
|
if (!clip(&dest_r, &src_r))
|
|
return;
|
|
if (!::clip(src->clip_rect, &src_r, &dest_r))
|
|
return;
|
|
|
|
if (solid) {
|
|
surf->Blt(&dest_r, src->surf, &src_r, DDBLT_WAIT, 0);
|
|
} else {
|
|
bltfx.ddckSrcColorkey.dwColorSpaceLowValue = bltfx.ddckSrcColorkey.dwColorSpaceHighValue = src->mask_surf;
|
|
surf->Blt(&dest_r, src->surf, &src_r, DDBLT_WAIT | DDBLT_KEYSRCOVERRIDE, &bltfx);
|
|
}
|
|
damage(dest_r);
|
|
}
|
|
|
|
void gxCanvas::text(int x, int y, const std::string& t)
|
|
{
|
|
int ty = y + origin_y;
|
|
if (ty >= viewport.bottom)
|
|
return;
|
|
if (ty + font->getHeight() <= viewport.top)
|
|
return;
|
|
|
|
int tx = x + origin_x;
|
|
if (tx >= viewport.right)
|
|
return;
|
|
|
|
int b = 0, w;
|
|
while (b < t.size() && tx + (w = font->charWidth(t[b])) <= viewport.left) {
|
|
tx += w;
|
|
x += w;
|
|
++b;
|
|
}
|
|
int e = b;
|
|
while (e < t.size() && tx < viewport.right) {
|
|
tx += font->charWidth(t[e]);
|
|
++e;
|
|
}
|
|
|
|
if (e > b)
|
|
font->render(this, format.toARGB(color_surf), x, y, t.substr(b, e - b));
|
|
}
|
|
|
|
int gxCanvas::getWidth() const
|
|
{
|
|
return clip_rect.right;
|
|
}
|
|
|
|
int gxCanvas::getHeight() const
|
|
{
|
|
return clip_rect.bottom;
|
|
}
|
|
|
|
int gxCanvas::getDepth() const
|
|
{
|
|
return format.getDepth();
|
|
}
|
|
|
|
void gxCanvas::getOrigin(int* x, int* y) const
|
|
{
|
|
*x = origin_x;
|
|
*y = origin_y;
|
|
}
|
|
|
|
void gxCanvas::getHandle(int* x, int* y) const
|
|
{
|
|
*x = handle_x;
|
|
*y = handle_y;
|
|
}
|
|
|
|
void gxCanvas::getViewport(int* x, int* y, int* w, int* h) const
|
|
{
|
|
*x = viewport.left;
|
|
*y = viewport.top;
|
|
*w = viewport.right - viewport.left;
|
|
*h = viewport.bottom - viewport.top;
|
|
}
|
|
|
|
unsigned gxCanvas::getMask() const
|
|
{
|
|
return format.toARGB(mask_surf);
|
|
}
|
|
|
|
unsigned gxCanvas::getColor() const
|
|
{
|
|
return format.toARGB(color_surf);
|
|
}
|
|
|
|
unsigned gxCanvas::getClsColor() const
|
|
{
|
|
return format.toARGB(clsColor_surf);
|
|
}
|
|
|
|
bool gxCanvas::collide(int x1, int y1, const gxCanvas* i2, int x2, int y2, bool solid) const
|
|
{
|
|
x1 -= handle_x;
|
|
x2 -= i2->handle_x;
|
|
if (x1 + clip_rect.right <= x2 || x1 >= x2 + i2->clip_rect.right)
|
|
return false;
|
|
y1 -= handle_y;
|
|
y2 -= i2->handle_y;
|
|
if (y1 + clip_rect.bottom <= y2 || y1 >= y2 + i2->clip_rect.bottom)
|
|
return false;
|
|
|
|
if (solid)
|
|
return true;
|
|
|
|
if (!cm_mask) {
|
|
cm_mask = new unsigned[cm_pitch * clip_rect.bottom];
|
|
updateBitMask(clip_rect);
|
|
}
|
|
if (!i2->cm_mask) {
|
|
i2->cm_mask = new unsigned[i2->cm_pitch * i2->clip_rect.bottom];
|
|
i2->updateBitMask(i2->clip_rect);
|
|
}
|
|
|
|
const gxCanvas* i1 = this;
|
|
|
|
//to keep me sane!
|
|
if (x1 > x2) {
|
|
std::swap(x1, x2);
|
|
std::swap(y1, y2);
|
|
std::swap(i1, i2);
|
|
}
|
|
|
|
Rect r1, r2, ir;
|
|
r1.left = x1;
|
|
r1.top = y1;
|
|
r1.right = x1 + i1->clip_rect.right;
|
|
r1.bottom = y1 + i1->clip_rect.bottom;
|
|
r2.left = x2;
|
|
r2.top = y2;
|
|
r2.right = x2 + i2->clip_rect.right;
|
|
r2.bottom = y2 + i2->clip_rect.bottom;
|
|
ir.left = r1.left > r2.left ? r1.left : r2.left;
|
|
ir.right = r1.right < r2.right ? r1.right : r2.right;
|
|
ir.top = r1.top > r2.top ? r1.top : r2.top;
|
|
ir.bottom = r1.bottom < r2.bottom ? r1.bottom : r2.bottom;
|
|
|
|
unsigned *s1 = i1->cm_mask, *s2 = i2->cm_mask;
|
|
int i1_pitch = i1->cm_pitch, i2_pitch = i2->cm_pitch;
|
|
s1 += (ir.top - r1.top) * i1_pitch;
|
|
s2 += (ir.top - r2.top) * i2_pitch;
|
|
|
|
int startx = ir.left - r1.left;
|
|
int stopx = ir.right - r1.left - 1;
|
|
int shr = startx & 31;
|
|
int shl = 32 - shr;
|
|
int cnt = stopx / 32 - startx / 32;
|
|
unsigned lwm = LWMS[stopx & 31];
|
|
|
|
#ifdef _DEBUG_BITMASK
|
|
unsigned* cm_mask_end1 = i1->cm_mask + i1_pitch * i1->clip_rect.bottom;
|
|
unsigned* cm_mask_end2 = i2->cm_mask + i2_pitch * i2->clip_rect.bottom;
|
|
#endif
|
|
|
|
s1 += startx / 32;
|
|
for (int y = ir.top; y < ir.bottom; ++y) {
|
|
unsigned p = 0;
|
|
unsigned *row1 = s1, *row2 = s2;
|
|
for (int x = 0; x < cnt; ++x) {
|
|
#ifdef _DEBUG_BITMASK
|
|
if (row1 < i1->cm_mask || row2 < i2->cm_mask) {
|
|
gx_runtime->debugError("gxCanvas::collide row underflow");
|
|
}
|
|
if (row1 >= cm_mask_end1 || row2 >= cm_mask_end2) {
|
|
gx_runtime->debugError("gxCanvas::collide row overflow");
|
|
}
|
|
#endif
|
|
unsigned n = *row2++;
|
|
if (((n >> shr) | p) & *row1++)
|
|
return true;
|
|
p = shl < 32 ? n << shl : 0;
|
|
}
|
|
#ifdef _DEBUG_BITMASK
|
|
if (row1 < i1->cm_mask || row2 < i2->cm_mask) {
|
|
gx_runtime->debugError("gxCanvas::collide row underflow");
|
|
}
|
|
if (row1 >= cm_mask_end1 || row2 >= cm_mask_end2) {
|
|
gx_runtime->debugError("gxCanvas::collide row overflow");
|
|
}
|
|
#endif
|
|
if (((*row2 >> shr) | p) & *row1 & lwm)
|
|
return true;
|
|
s1 += i1_pitch;
|
|
s2 += i2_pitch;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool gxCanvas::rect_collide(int x1, int y1, int x2, int y2, int w2, int h2, bool solid) const
|
|
{
|
|
x1 -= handle_x;
|
|
if (x1 + clip_rect.right <= x2 || x1 >= x2 + w2)
|
|
return false;
|
|
y1 -= handle_y;
|
|
if (y1 + clip_rect.bottom <= y2 || y1 >= y2 + h2)
|
|
return false;
|
|
|
|
if (solid)
|
|
return true;
|
|
|
|
Rect r1(x1, y1, clip_rect.right, clip_rect.bottom), r2(x2, y2, w2, h2), ir;
|
|
|
|
ir.left = r1.left > r2.left ? r1.left : r2.left;
|
|
ir.right = r1.right < r2.right ? r1.right : r2.right;
|
|
ir.top = r1.top > r2.top ? r1.top : r2.top;
|
|
ir.bottom = r1.bottom < r2.bottom ? r1.bottom : r2.bottom;
|
|
|
|
if (!cm_mask) {
|
|
cm_mask = new unsigned[cm_pitch * clip_rect.bottom];
|
|
updateBitMask(clip_rect);
|
|
}
|
|
|
|
unsigned* s1 = cm_mask + (ir.top - r1.top) * cm_pitch;
|
|
|
|
int startx = ir.left - r1.left;
|
|
int stopx = ir.right - r1.left - 1;
|
|
int cnt = stopx / 32 - startx / 32;
|
|
unsigned fwm = FWMS[startx & 31];
|
|
unsigned lwm = LWMS[stopx & 31];
|
|
|
|
if (!cnt) {
|
|
fwm &= lwm;
|
|
lwm = 0;
|
|
}
|
|
|
|
s1 += startx / 32;
|
|
for (int h = ir.top; h < ir.bottom; ++h) {
|
|
unsigned* row = s1;
|
|
if (*row & fwm)
|
|
return true;
|
|
for (int x = 1; x < cnt; ++x) {
|
|
if (*++row)
|
|
return true;
|
|
}
|
|
if (lwm && (*++row & lwm))
|
|
return true;
|
|
s1 += cm_pitch;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool gxCanvas::lock() const
|
|
{
|
|
if (!locked_cnt++) {
|
|
DDSURFACEDESC2 desc = {sizeof(desc)};
|
|
if (surf->Lock(0, &desc, DDLOCK_WAIT | DDLOCK_NOSYSLOCK, 0) < 0) {
|
|
--locked_cnt;
|
|
return false;
|
|
}
|
|
locked_pitch = desc.lPitch;
|
|
locked_surf = (unsigned char*)desc.lpSurface;
|
|
lock_mod_cnt = mod_cnt;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void gxCanvas::unlock() const
|
|
{
|
|
if (locked_cnt == 1) {
|
|
if (lock_mod_cnt != mod_cnt && cm_mask)
|
|
updateBitMask(clip_rect);
|
|
surf->Unlock(0);
|
|
}
|
|
--locked_cnt;
|
|
}
|
|
|
|
void gxCanvas::setPixel(int x, int y, unsigned argb)
|
|
{
|
|
x += origin_x;
|
|
if (x < viewport.left || x >= viewport.right)
|
|
return;
|
|
y += origin_y;
|
|
if (y < viewport.top || y >= viewport.bottom)
|
|
return;
|
|
//lock();
|
|
setPixelFast(x, y, argb);
|
|
//unlock();
|
|
}
|
|
|
|
unsigned gxCanvas::getPixel(int x, int y) const
|
|
{
|
|
x += origin_x;
|
|
if (x < viewport.left || x >= viewport.right)
|
|
return format.toARGB(mask_surf);
|
|
y += origin_y;
|
|
if (y < viewport.top || y >= viewport.bottom)
|
|
return format.toARGB(mask_surf);
|
|
//lock();
|
|
unsigned p = getPixelFast(x, y);
|
|
//unlock();
|
|
return p;
|
|
}
|
|
|
|
void gxCanvas::copyPixelFast(int x, int y, gxCanvas* src, int src_x, int src_y)
|
|
{
|
|
switch (format.getDepth()) {
|
|
case 16:
|
|
*(short*)(locked_surf + y * locked_pitch + x * 2) =
|
|
*(short*)(src->locked_surf + src_y * src->locked_pitch + src_x * 2);
|
|
break;
|
|
case 24: {
|
|
unsigned char* p = locked_surf + y * locked_pitch + x * 3;
|
|
unsigned char* t = src->locked_surf + src_y * src->locked_pitch + src_x * 3;
|
|
*(short*)p = *(short*)t;
|
|
*(char*)(p + 2) = *(char*)(t + 2);
|
|
} break;
|
|
case 32:
|
|
*(int*)(locked_surf + y * locked_pitch + x * 4) =
|
|
*(int*)(src->locked_surf + src_y * src->locked_pitch + src_x * 4);
|
|
break;
|
|
}
|
|
}
|
|
|
|
void gxCanvas::copyPixel(int x, int y, gxCanvas* src, int src_x, int src_y)
|
|
{
|
|
x += origin_x;
|
|
if (x < viewport.left || x >= viewport.right)
|
|
return;
|
|
y += origin_y;
|
|
if (y < viewport.top || y >= viewport.bottom)
|
|
return;
|
|
src_x += src->origin_x;
|
|
if (src_x < src->viewport.left || src_x >= src->viewport.right)
|
|
return;
|
|
src_y += src->origin_y;
|
|
if (src_y < src->viewport.top || src_y >= src->viewport.bottom)
|
|
return;
|
|
//lock();
|
|
//src->lock();
|
|
copyPixelFast(x, y, src, src_x, src_y);
|
|
//src->unlock();
|
|
//unlock();
|
|
}
|
|
|
|
void gxCanvas::setCubeMode(int mode)
|
|
{
|
|
cube_mode = mode;
|
|
}
|
|
|
|
void gxCanvas::setCubeFace(int face)
|
|
{
|
|
getTexSurface();
|
|
surf = cube_surfs[face];
|
|
} |