Initial commit
This commit is contained in:
507
engines/awe/graphics_soft.cpp
Normal file
507
engines/awe/graphics_soft.cpp
Normal file
@@ -0,0 +1,507 @@
|
||||
/* 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
|
||||
Reference in New Issue
Block a user