Files

994 lines
22 KiB
C++
Raw Permalink Normal View History

2019-01-18 17:03:37 +01:00
#include "gxcanvas.hpp"
#include "asmcoder.hpp"
#include "gxgraphics.hpp"
#include "gxruntime.hpp"
#include "gxfont.hpp"
2019-01-18 17:03:37 +01:00
#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)
2019-01-18 17:03:37 +01:00
{
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];
}