508 lines
13 KiB
C++
508 lines
13 KiB
C++
/* ScummVM - Graphic Adventure Engine
|
|
*
|
|
* ScummVM is the legal property of its developers, whose names
|
|
* are too numerous to list here. Please refer to the COPYRIGHT
|
|
* file distributed with this source distribution.
|
|
*
|
|
* This program is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*
|
|
*/
|
|
|
|
#include "graphics/managed_surface.h"
|
|
#include "common/textconsole.h"
|
|
#include "awe/gfx.h"
|
|
#include "awe/system_stub.h"
|
|
|
|
namespace Awe {
|
|
|
|
struct GraphicsSoft : public Gfx {
|
|
typedef void (GraphicsSoft:: *drawLine)(int16 x1, int16 x2, int16 y, uint8 col);
|
|
|
|
uint8 *_pagePtrs[4] = { nullptr };
|
|
uint8 *_drawPagePtr = { nullptr };
|
|
int _u = 0, _v = 0;
|
|
int _w = 0, _h = 0;
|
|
int _byteDepth = 0;
|
|
Color _pal[16];
|
|
bool _palChanged = false;
|
|
|
|
GraphicsSoft();
|
|
~GraphicsSoft();
|
|
|
|
int xScale(int x) const {
|
|
return (x * _u) >> 16;
|
|
}
|
|
int yScale(int y) const {
|
|
return (y * _v) >> 16;
|
|
}
|
|
|
|
void setSize(int w, int h);
|
|
void drawPolygon(uint8 color, const QuadStrip &qs);
|
|
void drawChar(uint8 c, uint16 x, uint16 y, uint8 color);
|
|
void drawSpriteMask(int x, int y, uint8 color, const uint8 *data);
|
|
void drawPoint(int16 x, int16 y, uint8 color);
|
|
void drawLineT(int16 x1, int16 x2, int16 y, uint8 color);
|
|
void drawLineN(int16 x1, int16 x2, int16 y, uint8 color);
|
|
void drawLineP(int16 x1, int16 x2, int16 y, uint8 color);
|
|
uint8 *getPagePtr(uint8 page);
|
|
int getPageSize() const {
|
|
return _w * _h * _byteDepth;
|
|
}
|
|
void setWorkPagePtr(uint8 page);
|
|
|
|
void init(int targetW, int targetH) override;
|
|
|
|
void setFont(const uint8 *src, int w, int h) override;
|
|
void setPalette(const Color *colors, int count) override;
|
|
void setSpriteAtlas(const uint8 *src, int w, int h, int xSize, int ySize) override;
|
|
void drawSprite(int buffer, int num, const Point *pt, uint8 color) override;
|
|
void drawBitmap(int buffer, const uint8 *data, int w, int h, int fmt) override;
|
|
void drawPoint(int buffer, uint8 color, const Point *pt) override;
|
|
void drawQuadStrip(int buffer, uint8 color, const QuadStrip *qs) override;
|
|
void drawStringChar(int buffer, uint8 color, char c, const Point *pt) override;
|
|
void clearBuffer(int num, uint8 color) override;
|
|
void copyBuffer(int dst, int src, int vscroll = 0) override;
|
|
void drawBuffer(int num, SystemStub *stub) override;
|
|
void drawRect(int num, uint8 color, const Point *pt, int w, int h) override;
|
|
void drawBitmapOverlay(const Graphics::Surface &src, int fmt, SystemStub *stub) override;
|
|
};
|
|
|
|
|
|
GraphicsSoft::GraphicsSoft() {
|
|
_fixUpPalette = FIXUP_PALETTE_NONE;
|
|
memset(_pagePtrs, 0, sizeof(_pagePtrs));
|
|
}
|
|
|
|
GraphicsSoft::~GraphicsSoft() {
|
|
for (int i = 0; i < 4; ++i) {
|
|
free(_pagePtrs[i]);
|
|
_pagePtrs[i] = nullptr;
|
|
}
|
|
}
|
|
|
|
void GraphicsSoft::setSize(int w, int h) {
|
|
_u = (w << 16) / GFX_W;
|
|
_v = (h << 16) / GFX_H;
|
|
_w = w;
|
|
_h = h;
|
|
_byteDepth = _format.bytesPerPixel;
|
|
assert(_byteDepth == 1 || _byteDepth == 2);
|
|
|
|
for (int i = 0; i < 4; ++i) {
|
|
uint8 *tmp = (uint8 *)realloc(_pagePtrs[i], getPageSize());
|
|
if (!tmp) {
|
|
error("Not enough memory to allocate offscreen buffers");
|
|
}
|
|
_pagePtrs[i] = tmp;
|
|
memset(_pagePtrs[i], 0, getPageSize());
|
|
}
|
|
setWorkPagePtr(2);
|
|
}
|
|
|
|
static uint32 calcStep(const Point &p1, const Point &p2, uint16 &dy) {
|
|
dy = p2.y - p1.y;
|
|
const uint16 delta = (dy <= 1) ? 1 : dy;
|
|
return ((p2.x - p1.x) * (0x4000 / delta)) << 2;
|
|
}
|
|
|
|
void GraphicsSoft::drawPolygon(uint8 color, const QuadStrip &quadStrip) {
|
|
QuadStrip qs = quadStrip;
|
|
if (_w != GFX_W || _h != GFX_H) {
|
|
for (int i = 0; i < qs.numVertices; ++i) {
|
|
qs.vertices[i].scale(_u, _v);
|
|
}
|
|
}
|
|
|
|
int i = 0;
|
|
int j = qs.numVertices - 1;
|
|
|
|
int16 x2 = qs.vertices[i].x;
|
|
int16 x1 = qs.vertices[j].x;
|
|
int16 hliney = MIN(qs.vertices[i].y, qs.vertices[j].y);
|
|
|
|
++i;
|
|
--j;
|
|
|
|
drawLine pdl;
|
|
switch (color) {
|
|
default:
|
|
pdl = &GraphicsSoft::drawLineN;
|
|
break;
|
|
case COL_PAGE:
|
|
pdl = &GraphicsSoft::drawLineP;
|
|
break;
|
|
case COL_ALPHA:
|
|
pdl = &GraphicsSoft::drawLineT;
|
|
break;
|
|
}
|
|
|
|
uint32 cpt1 = x1 << 16;
|
|
uint32 cpt2 = x2 << 16;
|
|
|
|
int numVertices = qs.numVertices;
|
|
while (true) {
|
|
numVertices -= 2;
|
|
if (numVertices == 0) {
|
|
return;
|
|
}
|
|
uint16 h;
|
|
const uint32 step1 = calcStep(qs.vertices[j + 1], qs.vertices[j], h);
|
|
const uint32 step2 = calcStep(qs.vertices[i - 1], qs.vertices[i], h);
|
|
|
|
++i;
|
|
--j;
|
|
|
|
cpt1 = (cpt1 & 0xFFFF0000) | 0x7FFF;
|
|
cpt2 = (cpt2 & 0xFFFF0000) | 0x8000;
|
|
|
|
if (h == 0) {
|
|
cpt1 += step1;
|
|
cpt2 += step2;
|
|
} else {
|
|
while (h--) {
|
|
if (hliney >= 0) {
|
|
x1 = cpt1 >> 16;
|
|
x2 = cpt2 >> 16;
|
|
if (x1 < _w && x2 >= 0) {
|
|
if (x1 < 0) x1 = 0;
|
|
if (x2 >= _w) x2 = _w - 1;
|
|
(this->*pdl)(x1, x2, hliney, color);
|
|
}
|
|
}
|
|
cpt1 += step1;
|
|
cpt2 += step2;
|
|
++hliney;
|
|
if (hliney >= _h) return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void GraphicsSoft::drawChar(uint8 c, uint16 x, uint16 y, uint8 color) {
|
|
if (x <= GFX_W - 8 && y <= GFX_H - 8) {
|
|
x = xScale(x);
|
|
y = yScale(y);
|
|
const uint8 *ft = FONT + (c - 0x20) * 8;
|
|
const int offset = (x + y * _w) * _byteDepth;
|
|
if (_byteDepth == 1) {
|
|
for (int j = 0; j < 8; ++j) {
|
|
const uint8 ch = ft[j];
|
|
for (int i = 0; i < 8; ++i) {
|
|
if (ch & (1 << (7 - i))) {
|
|
_drawPagePtr[offset + j * _w + i] = color;
|
|
}
|
|
}
|
|
}
|
|
} else if (_byteDepth == 2) {
|
|
const uint16 rgbColor = _format.RGBToColor(
|
|
_pal[color].r, _pal[color].g, _pal[color].b);
|
|
|
|
for (int j = 0; j < 8; ++j) {
|
|
const uint8 ch = ft[j];
|
|
for (int i = 0; i < 8; ++i) {
|
|
if (ch & (1 << (7 - i))) {
|
|
((uint16 *)(_drawPagePtr + offset))[j * _w + i] = rgbColor;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
void GraphicsSoft::drawSpriteMask(int x, int y, uint8 color, const uint8 *data) {
|
|
const int w = *data++;
|
|
x = xScale(x - w / 2);
|
|
const int h = *data++;
|
|
y = yScale(y - h / 2);
|
|
assert(_byteDepth == 1);
|
|
for (int j = 0; j < h; ++j) {
|
|
const int yoffset = y + j;
|
|
for (int i = 0; i <= w / 16; ++i) {
|
|
const uint16 mask = READ_BE_UINT16(data); data += 2;
|
|
if (yoffset < 0 || yoffset >= _h) {
|
|
continue;
|
|
}
|
|
const int xoffset = x + i * 16;
|
|
for (int b = 0; b < 16; ++b) {
|
|
if (xoffset + b < 0 || xoffset + b >= _w) {
|
|
continue;
|
|
}
|
|
if (mask & (1 << (15 - b))) {
|
|
_drawPagePtr[yoffset * _w + xoffset + b] = color;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static void blend_rgb555(uint16 *dst, const uint16 b) {
|
|
static const uint16 RB_MASK = 0x7c1f;
|
|
static const uint16 G_MASK = 0x03e0;
|
|
const uint16 a = *dst;
|
|
if ((a & 0x8000) == 0) { // use bit 15 to prevent additive blending
|
|
uint16 r = 0x8000;
|
|
r |= (((a & RB_MASK) + (b & RB_MASK)) >> 1) & RB_MASK;
|
|
r |= (((a & G_MASK) + (b & G_MASK)) >> 1) & G_MASK;
|
|
*dst = r;
|
|
}
|
|
}
|
|
|
|
void GraphicsSoft::drawPoint(int16 x, int16 y, uint8 color) {
|
|
x = xScale(x);
|
|
y = yScale(y);
|
|
const int offset = (y * _w + x) * _byteDepth;
|
|
if (_byteDepth == 1) {
|
|
switch (color) {
|
|
case COL_ALPHA:
|
|
_drawPagePtr[offset] |= 8;
|
|
break;
|
|
case COL_PAGE:
|
|
_drawPagePtr[offset] = *(_pagePtrs[0] + offset);
|
|
break;
|
|
default:
|
|
_drawPagePtr[offset] = color;
|
|
break;
|
|
}
|
|
} else if (_byteDepth == 2) {
|
|
switch (color) {
|
|
case COL_ALPHA: {
|
|
const Color &c = _pal[ALPHA_COLOR_INDEX];
|
|
const uint16 rgbColor = _format.RGBToColor(c.r, c.g, c.b);
|
|
blend_rgb555((uint16 *)(_drawPagePtr + offset), rgbColor);
|
|
break;
|
|
}
|
|
case COL_PAGE:
|
|
*(uint16 *)(_drawPagePtr + offset) = *(uint16 *)(_pagePtrs[0] + offset);
|
|
break;
|
|
default: {
|
|
const Color &c = _pal[color];
|
|
const uint16 rgbColor = _format.RGBToColor(c.r, c.g, c.b);
|
|
*(uint16 *)(_drawPagePtr + offset) = rgbColor;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void GraphicsSoft::drawLineT(int16 x1, int16 x2, int16 y, uint8 color) {
|
|
const int16 xmax = MAX(x1, x2);
|
|
const int16 xmin = MIN(x1, x2);
|
|
const int w = xmax - xmin + 1;
|
|
const int offset = (y * _w + xmin) * _byteDepth;
|
|
if (_byteDepth == 1) {
|
|
for (int i = 0; i < w; ++i) {
|
|
_drawPagePtr[offset + i] |= 8;
|
|
}
|
|
} else if (_byteDepth == 2) {
|
|
const Color &c = _pal[ALPHA_COLOR_INDEX];
|
|
const uint16 rgbColor = _format.RGBToColor(c.r, c.g, c.b);
|
|
|
|
uint16 *p = (uint16 *)(_drawPagePtr + offset);
|
|
for (int i = 0; i < w; ++i) {
|
|
blend_rgb555(p + i, rgbColor);
|
|
}
|
|
}
|
|
}
|
|
|
|
void GraphicsSoft::drawLineN(int16 x1, int16 x2, int16 y, uint8 color) {
|
|
const int16 xmax = MAX(x1, x2);
|
|
const int16 xmin = MIN(x1, x2);
|
|
const int w = xmax - xmin + 1;
|
|
const int offset = (y * _w + xmin) * _byteDepth;
|
|
if (_byteDepth == 1) {
|
|
memset(_drawPagePtr + offset, color, w);
|
|
} else if (_byteDepth == 2) {
|
|
const Color &c = _pal[color];
|
|
const uint16 rgbColor = _format.RGBToColor(c.r, c.g, c.b);
|
|
uint16 *p = (uint16 *)(_drawPagePtr + offset);
|
|
for (int i = 0; i < w; ++i) {
|
|
p[i] = rgbColor;
|
|
}
|
|
}
|
|
}
|
|
|
|
void GraphicsSoft::drawLineP(int16 x1, int16 x2, int16 y, uint8 color) {
|
|
if (_drawPagePtr == _pagePtrs[0]) {
|
|
return;
|
|
}
|
|
const int16 xmax = MAX(x1, x2);
|
|
const int16 xmin = MIN(x1, x2);
|
|
const int w = xmax - xmin + 1;
|
|
const int offset = (y * _w + xmin) * _byteDepth;
|
|
memcpy(_drawPagePtr + offset, _pagePtrs[0] + offset, w * _byteDepth);
|
|
}
|
|
|
|
uint8 *GraphicsSoft::getPagePtr(uint8 page) {
|
|
assert(page < 4);
|
|
return _pagePtrs[page];
|
|
}
|
|
|
|
void GraphicsSoft::setWorkPagePtr(uint8 page) {
|
|
_drawPagePtr = getPagePtr(page);
|
|
}
|
|
|
|
void GraphicsSoft::init(int targetW, int targetH) {
|
|
Gfx::init(targetW, targetH);
|
|
setSize(targetW, targetH);
|
|
}
|
|
|
|
void GraphicsSoft::setFont(const uint8 *src, int w, int h) {
|
|
if (_is1991) {
|
|
// no-op for 1991
|
|
}
|
|
}
|
|
|
|
void GraphicsSoft::setPalette(const Color *colors, int count) {
|
|
count = MIN(count, 16);
|
|
for (int i = 0; i < count; i++) {
|
|
_pal[i].r = colors[i].r;
|
|
_pal[i].g = colors[i].g;
|
|
_pal[i].b = colors[i].b;
|
|
}
|
|
_palChanged = true;
|
|
}
|
|
|
|
void GraphicsSoft::setSpriteAtlas(const uint8 *src, int w, int h, int xSize, int ySize) {
|
|
if (_is1991) {
|
|
// no-op for 1991
|
|
}
|
|
}
|
|
|
|
void GraphicsSoft::drawSprite(int buffer, int num, const Point *pt, uint8 color) {
|
|
if (_is1991) {
|
|
if (num < SHAPES_MASK_COUNT) {
|
|
setWorkPagePtr(buffer);
|
|
const uint8 *data = SHAPES_MASK_DATA + SHAPES_MASK_OFFSET[num];
|
|
drawSpriteMask(pt->x, pt->y, color, data);
|
|
}
|
|
}
|
|
}
|
|
|
|
void GraphicsSoft::drawBitmap(int buffer, const uint8 *data, int w, int h, int fmt) {
|
|
switch (_byteDepth) {
|
|
case 1:
|
|
if (fmt == FMT_CLUT && _w == w && _h == h) {
|
|
memcpy(getPagePtr(buffer), data, w * h);
|
|
return;
|
|
}
|
|
break;
|
|
case 2:
|
|
if (fmt == FMT_RGB555 && _w == w && _h == h) {
|
|
memcpy(getPagePtr(buffer), data, getPageSize());
|
|
return;
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
warning("GraphicsSoft::drawBitmap() unhandled fmt %d w %d h %d", fmt, w, h);
|
|
}
|
|
|
|
void GraphicsSoft::drawPoint(int buffer, uint8 color, const Point *pt) {
|
|
setWorkPagePtr(buffer);
|
|
drawPoint(pt->x, pt->y, color);
|
|
}
|
|
|
|
void GraphicsSoft::drawQuadStrip(int buffer, uint8 color, const QuadStrip *qs) {
|
|
setWorkPagePtr(buffer);
|
|
drawPolygon(color, *qs);
|
|
}
|
|
|
|
void GraphicsSoft::drawStringChar(int buffer, uint8 color, char c, const Point *pt) {
|
|
setWorkPagePtr(buffer);
|
|
drawChar(c, pt->x, pt->y, color);
|
|
}
|
|
|
|
void GraphicsSoft::clearBuffer(int num, uint8 color) {
|
|
if (_byteDepth == 1) {
|
|
memset(getPagePtr(num), color, getPageSize());
|
|
} else if (_byteDepth == 2) {
|
|
const Color &c = _pal[color];
|
|
const uint16 rgbColor = _format.RGBToColor(c.r, c.g, c.b);
|
|
uint16 *p = (uint16 *)getPagePtr(num);
|
|
for (int i = 0; i < _w * _h; ++i) {
|
|
p[i] = rgbColor;
|
|
}
|
|
}
|
|
}
|
|
|
|
void GraphicsSoft::copyBuffer(int dst, int src, int vscroll) {
|
|
if (vscroll == 0) {
|
|
memcpy(getPagePtr(dst), getPagePtr(src), getPageSize());
|
|
} else if (vscroll >= -199 && vscroll <= 199) {
|
|
const int dy = yScale(vscroll);
|
|
if (dy < 0) {
|
|
memcpy(getPagePtr(dst), getPagePtr(src) - dy * _w * _byteDepth, (_h + dy) * _w * _byteDepth);
|
|
} else {
|
|
memcpy(getPagePtr(dst) + dy * _w * _byteDepth, getPagePtr(src), (_h - dy) * _w * _byteDepth);
|
|
}
|
|
}
|
|
}
|
|
|
|
void GraphicsSoft::drawBuffer(int num, SystemStub *stub) {
|
|
int w, h;
|
|
float ar[4];
|
|
stub->prepareScreen(w, h, ar);
|
|
|
|
if (_palChanged) {
|
|
stub->setPalette(_pal);
|
|
_palChanged = false;
|
|
}
|
|
|
|
Graphics::Surface s;
|
|
s.setPixels(getPagePtr(num));
|
|
s.w = s.pitch = w;
|
|
s.h = h;
|
|
s.format = (_byteDepth == 1) ?
|
|
Graphics::PixelFormat::createFormatCLUT8() :
|
|
_format;
|
|
|
|
stub->setScreenPixels(s);
|
|
stub->updateScreen();
|
|
}
|
|
|
|
void GraphicsSoft::drawRect(int num, uint8 color, const Point *pt, int w, int h) {
|
|
assert(_byteDepth == 2);
|
|
setWorkPagePtr(num);
|
|
|
|
const Color &c = _pal[color];
|
|
const uint16 rgbColor = _format.RGBToColor(c.r, c.g, c.b);
|
|
const int x1 = xScale(pt->x);
|
|
const int y1 = yScale(pt->y);
|
|
const int x2 = xScale(pt->x + w - 1);
|
|
const int y2 = yScale(pt->y + h - 1);
|
|
// horizontal
|
|
for (int x = x1; x <= x2; ++x) {
|
|
*(uint16 *)(_drawPagePtr + (y1 * _w + x) * _byteDepth) = rgbColor;
|
|
*(uint16 *)(_drawPagePtr + (y2 * _w + x) * _byteDepth) = rgbColor;
|
|
}
|
|
// vertical
|
|
for (int y = y1; y <= y2; ++y) {
|
|
*(uint16 *)(_drawPagePtr + (y * _w + x1) * _byteDepth) = rgbColor;
|
|
*(uint16 *)(_drawPagePtr + (y * _w + x2) * _byteDepth) = rgbColor;
|
|
}
|
|
}
|
|
|
|
void GraphicsSoft::drawBitmapOverlay(const Graphics::Surface &src, int fmt, SystemStub *stub) {
|
|
if (fmt == FMT_RGB555) {
|
|
stub->setScreenPixels(src);
|
|
stub->updateScreen();
|
|
}
|
|
}
|
|
|
|
Gfx *GraphicsSoft_create() {
|
|
return new GraphicsSoft();
|
|
}
|
|
|
|
} // namespace Awe
|