Files
2026-02-02 04:50:13 +01:00

1125 lines
25 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 "common/textconsole.h"
#include "common/system.h"
#include "engines/util.h"
#include "graphics/cursorman.h"
#include "graphics/managed_surface.h"
#include "qdengine/qdengine.h"
#include "qdengine/qd_fwd.h"
#include "qdengine/system/graphics/gr_dispatcher.h"
#include "qdengine/system/graphics/gr_font.h"
#include "qdengine/system/graphics/UI_TextParser.h"
namespace QDEngine {
void *grDispatcher::_default_mouse_cursor = nullptr;
grDispatcher::char_input_hanler_t grDispatcher::_input_handler = 0;
bool grDispatcher::_is_active = true; // We have system always active
grDispatcher::restore_handler_t grDispatcher::_restore_handler = 0;
grDispatcher *grDispatcher::_dispatcher_ptr;
grFont *grDispatcher::_default_font;
grDispatcher::grDispatcher() : _screenBuf(NULL),
#ifdef _GR_ENABLE_ZBUFFER
zbuffer_(NULL),
#endif
_hWnd(NULL),
_temp_buffer(0) {
_flags = 0;
_temp_buffer_size = 0;
_clipMode = 0;
_sizeX = _sizeY = 0;
_wndSizeX = _wndSizeY = 0;
_wndPosX = _wndPosY = 0;
_changes_mask_size_x = _changes_mask_size_y = 0;
_hide_mouse = false;
_mouse_cursor = NULL;
_pixel_format = GR_RGB565;
setClip();
if (!_dispatcher_ptr) _dispatcher_ptr = this;
}
grDispatcher::~grDispatcher() {
finit();
if (_dispatcher_ptr == this) _dispatcher_ptr = 0;
}
bool grDispatcher::finit() {
#ifdef _GR_ENABLE_ZBUFFER
free_zbuffer();
#endif
_flags &= ~GR_INITED;
_sizeX = _sizeY = 0;
_wndPosX = _wndPosY = 0;
delete _realScreenBuf;
_realScreenBuf = nullptr;
return true;
}
bool grDispatcher::init(int sx, int sy, grPixelFormat pixel_format) {
finit();
_pixel_format = pixel_format;
initGraphics(sx, sy, &g_engine->_pixelformat);
_realScreenBuf = new Graphics::ManagedSurface(sx, sy, g_engine->_pixelformat);
_screenBuf = _realScreenBuf;
_sizeX = sx;
_sizeY = sy;
_changes_mask_size_x = _sizeX >> kChangesMaskTileShift;
if (_sizeX % kChangesMaskTile) _changes_mask_size_x++;
_changes_mask_size_y = _sizeY >> kChangesMaskTileShift;
if (_sizeY % kChangesMaskTile) _changes_mask_size_y++;
_changes_mask.resize(_changes_mask_size_x * _changes_mask_size_y);
_flags &= ~GR_REINIT;
#ifdef _GR_ENABLE_ZBUFFER
alloc_zbuffer(_sizeX, _sizeY);
#endif
return true;
}
void grDispatcher::fill(int val) {
_screenBuf->clear(val);
}
bool grDispatcher::flush(int x, int y, int sx, int sy) {
int x1 = x + sx;
int y1 = y + sy;
if (x < 0)
x = 0;
if (y < 0)
y = 0;
if (x1 > _sizeX)
x1 = _sizeX;
if (y1 > _sizeY)
y1 = _sizeY;
debugC(8, kDebugGraphics, "grDispatcher::flush(%d, %d, %d, %d)", x, y, x1 - x, y1 - y);
g_system->copyRectToScreen(_screenBuf->getBasePtr(x, y), _screenBuf->pitch, x, y, x1 - x, y1 - y);
return true;
}
bool grDispatcher::flush() {
return flush(0, 0, _sizeX, _sizeY);
}
void grDispatcher::line(int x1, int y1, int x2, int y2, int col, int line_style, bool inverse_col) {
const int F_PREC = 16;
if (!clip_line(x1, y1, x2, y2)) return;
if (!inverse_col) {
if (x1 == x2 && y1 == y2) {
setPixelFast(x1, y1, col);
return;
}
if (abs(x2 - x1) > abs(y2 - y1)) {
int a = x2 - x1;
int b = y2 - y1;
int x = x1;
int y = (y1 << F_PREC) + (1 << (F_PREC - 1));
int incr = 1;
int k = (b << F_PREC) / a;
int v = 0;
if (x1 > x2) {
incr = -1;
k = -k;
a = -a;
}
do {
if (++v > line_style) {
setPixelFast(x, y >> F_PREC, col);
if (v >= line_style * 2)
v = 0;
}
x += incr;
y += k;
} while (--a >= 0);
} else {
int a = x2 - x1;
int b = y2 - y1;
int x = (x1 << F_PREC) + (1 << (F_PREC - 1));
int y = y1;
int incr = 1;
int k = (a << F_PREC) / b;
int v = 0;
if (y1 > y2) {
incr = -1;
k = -k;
b = -b;
}
do {
if (++v > line_style) {
setPixelFast(x >> F_PREC, y, col);
if (v >= line_style * 2)
v = 0;
}
y += incr;
x += k;
} while (--b >= 0);
}
} else {
if (x1 == x2 && y1 == y2) {
setPixelFast(x1, y1, col);
return;
}
if (abs(x2 - x1) > abs(y2 - y1)) {
int a = x2 - x1;
int b = y2 - y1;
int x = x1;
int y = (y1 << F_PREC) + (1 << (F_PREC - 1));
int incr = 1;
int k = (b << F_PREC) / a;
int v = 0;
if (x1 > x2) {
incr = -1;
k = -k;
a = -a;
}
do {
if (++v > line_style) {
setPixelFast(x, y >> F_PREC, col);
if (v >= line_style * 2)
v = 0;
} else
setPixelFast(x, y >> F_PREC, ~col);
x += incr;
y += k;
} while (--a >= 0);
} else {
int a = x2 - x1;
int b = y2 - y1;
int x = (x1 << F_PREC) + (1 << (F_PREC - 1));
int y = y1;
int incr = 1;
int k = (a << F_PREC) / b;
int v = 0;
if (y1 > y2) {
incr = -1;
k = -k;
b = -b;
}
do {
if (++v > line_style) {
setPixelFast(x >> F_PREC, y, col);
if (v >= line_style * 2)
v = 0;
} else
setPixelFast(x >> F_PREC, y, ~col);
y += incr;
x += k;
} while (--b >= 0);
}
}
}
void grDispatcher::lineTo(int x, int y, int len, int dir, int col, int line_style) {
int v;
switch (dir) {
case GR_LEFT:
v = x - len;
if (!clip_line(x, y, v, y)) return;
_screenBuf->vLine(x, y, y + len, col);
break;
case GR_TOP:
v = y - len;
if (!clip_line(x, y, x, v)) return;
_screenBuf->hLine(x, y, x + len, col);
break;
case GR_RIGHT:
v = x + len;
if (!clip_line(x, y, v, y)) return;
_screenBuf->vLine(x, y, y + len, col);
break;
case GR_BOTTOM:
v = y + len;
if (!clip_line(x, y, x, v)) return;
_screenBuf->hLine(x, y, x + len, col);
break;
}
}
void grDispatcher::rectangle(int x, int y, int sx, int sy, int outcol, int incol, int mode, int line_style) {
if (!sx || !sy) return;
lineTo(x, y, sx, GR_RIGHT, outcol, line_style);
lineTo(x, y, sy, GR_BOTTOM, outcol, line_style);
lineTo(x + sx - 1, y, sy, GR_BOTTOM, outcol, line_style);
lineTo(x, y + sy - 1, sx, GR_RIGHT, outcol, line_style);
if (mode == GR_FILLED) {
if (sx < 3) return;
erase(x + 1, y + 1, sx - 2, sy - 2, incol);
}
}
void grDispatcher::rectangleAlpha(int x, int y, int sx, int sy, uint32 color, int alpha) {
int px = 0;
int py = 0;
int psx = sx;
int psy = sy;
if (!clip_rectangle(x, y, px, py, psx, psy)) return;
int dx = 2;
int dy = 1;
byte mr, mg, mb;
if (_pixel_format == GR_RGB565) {
split_rgb565u(color, mr, mg, mb);
} else {
dx *= 2;
split_rgb888(color, mr, mg, mb);
}
mr = (mr * (255 - alpha)) >> 8;
mg = (mg * (255 - alpha)) >> 8;
mb = (mb * (255 - alpha)) >> 8;
uint32 mcl = make_rgb565u(mr, mg, mb);
for (int i = 0; i < psy; i++) {
byte *scr_buf = reinterpret_cast<byte *>(_screenBuf->getBasePtr(x, y));
for (int j = 0; j < psx; j++) {
if (_pixel_format == GR_RGB565) {
*(uint16 *)scr_buf = alpha_blend_565(mcl, *(uint16 *)scr_buf, alpha);
} else {
scr_buf[3] = mr + ((alpha * scr_buf[3]) >> 8);
scr_buf[2] = mg + ((alpha * scr_buf[2]) >> 8);
scr_buf[1] = mb + ((alpha * scr_buf[1]) >> 8);
}
scr_buf += dx;
}
y += dy;
}
}
void grDispatcher::erase(int x, int y, int sx, int sy, int col) {
if (_clipMode)
if (!clip_rectangle(x, y, sx, sy))
return;
_screenBuf->fillRect(Common::Rect(x, y, x + sx, y + sy), col);
return;
}
void grDispatcher::surfaceOverride(Graphics::ManagedSurface *target) {
_screenBuf = target;
}
void grDispatcher::resetSurfaceOverride() {
_screenBuf = _realScreenBuf;
}
void grDispatcher::setPixel(int x, int y, int col) {
if (_clipMode && !clipCheck(x, y)) return;
if (_pixel_format == GR_RGB565) {
uint16 *p = (uint16 *)(_screenBuf->getBasePtr(x, y));
*p = col;
} else {
uint32 *p = (uint32 *)(_screenBuf->getBasePtr(x, y));
*p = col << 8;
}
}
void grDispatcher::setPixelFast(byte* buf, uint32 col) {
if (_pixel_format == GR_RGB565) {
uint16 *p = (uint16 *)buf;
*p = col;
} else {
uint32 *p = (uint32 *)buf;
*p = col << 8;
}
}
void grDispatcher::setPixelFast(int x, int y, int col) {
if (_pixel_format == GR_RGB565) {
uint16 *p = (uint16 *)(_screenBuf->getBasePtr(x, y));
*p = col;
} else {
// NOTE
// The original had blue and red colors swapped in this method
uint32 *p = (uint32 *)(_screenBuf->getBasePtr(x, y));
*p = col << 8;
}
}
void grDispatcher::setPixelFast(int x, int y, int r, int g, int b) {
if (_pixel_format == GR_RGB565) {
uint16 *p = (uint16 *)(_screenBuf->getBasePtr(x, y));
*p = (((r >> 3) << 11) + ((g >> 2) << 5) + ((b >> 3) << 0));
} else {
uint32 *p = (uint32 *)(_screenBuf->getBasePtr(x, y));
*p = ((r << 24) | (g << 16) | (b << 8));
}
}
void grDispatcher::setPixel(int x, int y, int r, int g, int b) {
if (_clipMode && !clipCheck(x, y)) return;
if (_pixel_format == GR_RGB565) {
uint16 *p = (uint16 *)(_screenBuf->getBasePtr(x * 2, y));
*p = (((r >> 3) << 11) + ((g >> 2) << 5) + ((b >> 3) << 0));
} else {
uint32 *p = (uint32 *)(_screenBuf->getBasePtr(x, y));
*p = ((r << 24) + (g << 16) + (b << 8));
}
}
void grDispatcher::getPixel(int x, int y, uint32 &col) {
if (_pixel_format == GR_RGB565)
col = *(uint16 *)(_screenBuf->getBasePtr(x, y));
else
col = *(uint32 *)(_screenBuf->getBasePtr(x, y));
}
void grDispatcher::getPixel(int x, int y, uint16 &col) {
col = *(uint16 *)(_screenBuf->getBasePtr(x, y));
}
void grDispatcher::getPixel(int x, int y, byte &r, byte &g, byte &b) {
if (_pixel_format == GR_RGB565) {
uint16 col = *(uint16 *)(_screenBuf->getBasePtr(x, y));
split_rgb565u(col, r, g, b);
} else {
byte *p = (byte *)(_screenBuf->getBasePtr(x, y));
r = p[3];
g = p[2];
b = p[1];
}
}
bool grDispatcher::clip_line(int &x0, int &y0, int &x1, int &y1) const {
int x = 0, y = 0;
bool accept = false, done = false;
int outcodeOut;
int outcode0 = clip_out_code(x0, y0);
int outcode1 = clip_out_code(x1, y1);
do {
if (outcode0 == 0 && outcode1 == 0) {
accept = true;
done = true;
} else {
if ((outcode0 & outcode1) != 0)
done = true;
else {
if (outcode0)
outcodeOut = outcode0;
else
outcodeOut = outcode1;
if (clTOP & outcodeOut) {
x = x0 + (x1 - x0) * (_clipCoords[3] - y0 - 1) / (y1 - y0);
y = _clipCoords[3] - 1;
} else if (clBOTTOM & outcodeOut) {
x = x0 + (x1 - x0) * (_clipCoords[1] - y0) / (y1 - y0);
y = _clipCoords[1];
}
if (clRIGHT & outcodeOut) {
y = y0 + (y1 - y0) * (_clipCoords[2] - x0 - 1) / (x1 - x0);
x = _clipCoords[2] - 1;
} else if (clLEFT & outcodeOut) {
y = y0 + (y1 - y0) * (_clipCoords[0] - x0) / (x1 - x0);
x = _clipCoords[0];
}
if (outcodeOut == outcode0) {
x0 = x;
y0 = y;
outcode0 = clip_out_code(x, y);
} else {
x1 = x;
y1 = y;
outcode1 = clip_out_code(x, y);
}
}
}
} while (!done);
return accept;
}
bool grDispatcher::clip_line(int &x0, int &y0, int &z0, int &x1, int &y1, int &z1) const {
int x = 0, y = 0, z = 0;
bool accept = false, done = false;
int outcodeOut;
int outcode0 = clip_out_code(x0, y0);
int outcode1 = clip_out_code(x1, y1);
do {
if (outcode0 == 0 && outcode1 == 0) {
accept = true;
done = true;
} else {
if ((outcode0 & outcode1) != 0)
done = true;
else {
if (outcode0)
outcodeOut = outcode0;
else
outcodeOut = outcode1;
if (clTOP & outcodeOut) {
x = x0 + (x1 - x0) * (_clipCoords[3] - y0 - 1) / (y1 - y0);
z = z0 + (z1 - z0) * (_clipCoords[3] - y0 - 1) / (y1 - y0);
y = _clipCoords[3] - 1;
} else if (clBOTTOM & outcodeOut) {
x = x0 + (x1 - x0) * (_clipCoords[1] - y0) / (y1 - y0);
z = z0 + (z1 - z0) * (_clipCoords[1] - y0) / (y1 - y0);
y = _clipCoords[1];
}
if (clRIGHT & outcodeOut) {
y = y0 + (y1 - y0) * (_clipCoords[2] - x0 - 1) / (x1 - x0);
z = z0 + (z1 - z0) * (_clipCoords[2] - x0 - 1) / (x1 - x0);
x = _clipCoords[2] - 1;
} else if (clLEFT & outcodeOut) {
y = y0 + (y1 - y0) * (_clipCoords[0] - x0) / (x1 - x0);
z = z0 + (z1 - z0) * (_clipCoords[0] - x0) / (x1 - x0);
x = _clipCoords[0];
}
if (outcodeOut == outcode0) {
x0 = x;
y0 = y;
z0 = z;
outcode0 = clip_out_code(x, y);
} else {
x1 = x;
y1 = y;
z1 = z;
outcode1 = clip_out_code(x, y);
}
}
}
} while (!done);
return accept;
}
#ifdef _GR_ENABLE_ZBUFFER
void grDispatcher::Line_z(int x1, int y1, int z1, int x2, int y2, int z2, int col, int line_style) {
const F_PREC = 16;
if (!clip_line(x1, y1, z1, x2, y2, z2)) return;
if (x1 == x2 && y1 == y2) {
int z = (z1 < z2) ? z1 : z2;
if (get_z(x1, y1) > z)
setPixelFast(x1, y1, col);
return;
}
if (abs(x2 - x1) > abs(y2 - y1)) {
int a = x2 - x1;
int x = x1;
int y = (y1 << F_PREC) + (1 << (F_PREC - 1));
int incr = 1;
int k = ((y2 - y1) << F_PREC) / a;
int v = 0;
int z = z1 << F_PREC;
int dz = ((z2 - z1) << F_PREC) / a;
if (x1 > x2) {
incr = -1;
k = -k;
a = -a;
dz = -dz;
}
do {
if (++v > line_style) {
if (get_z(x, y >> F_PREC) >= (z >> F_PREC))
setPixelFast(x, y >> F_PREC, col);
v = 0;
}
x += incr;
y += k;
z += dz;
} while (--a >= 0);
} else {
int b = y2 - y1;
int x = (x1 << F_PREC) + (1 << (F_PREC - 1));
int y = y1;
int incr = 1;
int k = ((x2 - x1) << F_PREC) / b;
int v = 0;
int z = z1 << F_PREC;
int dz = ((z2 - z1) << F_PREC) / b;
if (y1 > y2) {
incr = -1;
k = -k;
b = -b;
dz = -dz;
}
do {
if (++v > line_style) {
if (get_z(x >> F_PREC, y) >= (z >> F_PREC))
setPixelFast(x >> F_PREC, y, col);
v = 0;
}
y += incr;
x += k;
z += dz;
} while (--b >= 0);
}
}
#endif
bool grDispatcher::clip_rectangle(int &x, int &y, int &pic_x, int &pic_y, int &pic_sx, int &pic_sy) const {
if (x < _clipCoords[0]) {
pic_x += _clipCoords[0] - x;
pic_sx += x - _clipCoords[0];
x = _clipCoords[0];
}
if (x + pic_sx >= _clipCoords[2])
pic_sx += _clipCoords[2] - (x + pic_sx);
// pic_sx += _clipCoords[2] - 1 - (x + pic_sx);
if (y < _clipCoords[1]) {
pic_y += _clipCoords[1] - y;
pic_sy += y - _clipCoords[1];
y = _clipCoords[1];
}
if (y + pic_sy >= _clipCoords[3])
pic_sy += _clipCoords[3] - (y + pic_sy);
// pic_sy += _clipCoords[3] - 1 - (y + pic_sy);
if (pic_x >= 0 && pic_y >= 0 && pic_sx > 0 && pic_sy > 0)
return true;
return false;
}
#ifdef _GR_ENABLE_ZBUFFER
bool grDispatcher::alloc_zbuffer(int sx, int sy) {
if (zbuffer_) delete zbuffer_;
zbuffer_ = new zbuf_t[sx * sy];
return true;
}
bool grDispatcher::free_zbuffer() {
if (zbuffer_) delete zbuffer_;
zbuffer_ = 0;
return true;
}
bool grDispatcher::clear_zbuffer() {
zbuf_t *p = zbuffer_;
for (int i = 0; i < _sizeY; i++) {
for (int j = 0; j < _sizeX; j++) {
*p++ = GR_ZBUFFER_MAX_Z;
}
}
return true;
}
#endif
void grDispatcher::clear_changes_mask() {
Common::fill(_changes_mask.begin(), _changes_mask.end(), 0);
}
void grDispatcher::build_changed_regions() {
_changed_regions.clear();
bool flag = true;
while (flag) {
flag = false;
changes_mask_t::iterator it = Common::find(_changes_mask.begin(), _changes_mask.end(), 1);
if (it != _changes_mask.end()) {
int x = (it - _changes_mask.begin()) % _changes_mask_size_x;
int y = (it - _changes_mask.begin()) / _changes_mask_size_x;
changes_mask_t::iterator it1 = Common::find(it, it + (_changes_mask_size_x - x), 0);
int sx = it1 - it;
int sy = 0;
for (int i = 0; i < _changes_mask_size_y - y; i++, sy++) {
changes_mask_t::iterator it2 = Common::find(it, it1, 0);
if (it2 == it1)
Common::fill(it, it1, 0);
else
break;
if (i < _changes_mask_size_y - y - 1) {
it += _changes_mask_size_x;
it1 += _changes_mask_size_x;
}
}
x <<= kChangesMaskTileShift;
y <<= kChangesMaskTileShift;
sx <<= kChangesMaskTileShift;
sy <<= kChangesMaskTileShift;
_changed_regions.push_back(grScreenRegion(x + sx / 2, y + sy / 2, sx, sy));
flag = true;
}
}
}
bool grDispatcher::invalidate_region(const grScreenRegion &reg) {
int x = reg.min_x();
int y = reg.min_y();
int sx = reg.size_x();
int sy = reg.size_y();
if (clip_rectangle(x, y, sx, sy)) {
sx = ((x + sx) >> kChangesMaskTileShift) - (x >> kChangesMaskTileShift) + 1;
sy = ((y + sy) >> kChangesMaskTileShift) - (y >> kChangesMaskTileShift) + 1;
x >>= kChangesMaskTileShift;
y >>= kChangesMaskTileShift;
if (x + sx > _changes_mask_size_x) sx = _changes_mask_size_x - x;
if (y + sy > _changes_mask_size_y) sy = _changes_mask_size_y - y;
if (sx <= 0 || sy <= 0) return false;
changes_mask_t::iterator it = _changes_mask.begin() + (x + y * _changes_mask_size_x);
for (int i = 0; i < sy; i++) {
auto it1 = it;
for (int j = 0; j < sx; j++) {
*it1 = true;
++it1;
}
if (i < sy - 1)
it += _changes_mask_size_x;
}
}
return true;
}
bool grDispatcher::flushChanges() {
for (regions_container_t::const_iterator it = _changed_regions.begin(); it != _changed_regions.end(); ++it)
flush(it->min_x() - 1, it->min_y() - 1, it->size_x() + 2, it->size_y() + 2);
return true;
}
// TODO
bool grDispatcher::convert_sprite(grPixelFormat src_fmt, grPixelFormat &dest_fmt, int sx, int sy, byte *data, bool &has_alpha) {
if (dest_fmt == GR_RGB888 && (src_fmt == GR_ARGB1555 || src_fmt == GR_RGB565)) {
if (has_alpha)
dest_fmt = GR_ARGB8888;
} else if (dest_fmt == GR_ARGB8888 && (src_fmt == GR_ARGB1555 || src_fmt == GR_RGB565)) {
if (!has_alpha)
dest_fmt = GR_RGB888;
}
switch (dest_fmt) {
case GR_ARGB1555:
if (src_fmt == GR_RGB565) {
uint16 *p = reinterpret_cast<uint16 *>(data);
for (int i = 0; i < sx * sy; i++) {
byte r, g, b;
split_rgb565u(*p, r, g, b);
*p++ = make_rgb555u(r, g, b);
if (has_alpha) p++;
}
} else if (src_fmt == GR_RGB888) {
byte *dp = data;
uint16 *p = reinterpret_cast<uint16 *>(data);
for (int i = 0; i < sx * sy; i++) {
*p++ = make_rgb555u(dp[2], dp[1], dp[0]);
dp += 3;
}
has_alpha = false;
} else if (src_fmt == GR_ARGB8888) {
byte *dp = data;
uint16 *p = reinterpret_cast<uint16 *>(data);
for (int i = 0; i < sx * sy; i++) {
*p++ = make_rgb555u(dp[2], dp[1], dp[0]);
*p++ >>= 8;
dp += 4;
}
has_alpha = true;
}
return true;
case GR_RGB565:
if (src_fmt == GR_ARGB1555) {
uint16 *p = reinterpret_cast<uint16 *>(data);
for (int i = 0; i < sx * sy; i++) {
byte r, g, b;
split_rgb555u(*p, r, g, b);
*p++ = make_rgb565u(r, g, b);
if (has_alpha) p++;
}
} else if (src_fmt == GR_RGB888) {
byte *dp = data;
uint16 *p = reinterpret_cast<uint16 *>(data);
for (int i = 0; i < sx * sy; i++) {
*p++ = make_rgb565u(dp[2], dp[1], dp[0]);
dp += 3;
}
has_alpha = false;
} else if (src_fmt == GR_ARGB8888) {
byte *dp = data;
uint16 *p = reinterpret_cast<uint16 *>(data);
for (int i = 0; i < sx * sy; i++) {
*p++ = make_rgb565u(dp[2], dp[1], dp[0]);
*p++ >>= 8;
dp += 4;
}
has_alpha = true;
}
return true;
case GR_RGB888:
if (src_fmt == GR_ARGB1555 || src_fmt == GR_RGB565) {
uint16 *p = reinterpret_cast<uint16 *>(data) + sx * sy - 1;
byte *dp = data + sx * sy * 3 - 1;
for (int i = 0; i < sx * sy; i++) {
byte r, g, b;
if (src_fmt == GR_ARGB1555)
split_rgb555u(*p--, r, g, b);
else
split_rgb565u(*p--, r, g, b);
*dp-- = r;
*dp-- = g;
*dp-- = b;
}
return true;
}
break;
case GR_ARGB8888:
case GR_RGBA8888:
if (src_fmt == GR_ARGB1555 || src_fmt == GR_RGB565) {
uint16 *p = reinterpret_cast<uint16 *>(data);
for (int i = 0; i < sx * sy; i++) {
p++;
*p++ <<= 8;
}
p = reinterpret_cast<uint16 *>(data);
byte *dp = data;
for (int i = 0; i < sx * sy; i++) {
byte r, g, b;
if (src_fmt == GR_ARGB1555)
split_rgb555u(*p++, r, g, b);
else
split_rgb565u(*p++, r, g, b);
p++;
if (dest_fmt == GR_ARGB8888) {
dp[0] = b;
dp[1] = g;
dp[2] = r;
} else if (dest_fmt == GR_RGBA8888) {
dp[1] = b;
dp[2] = g;
dp[3] = r;
}
dp += 4;
}
return true;
}
break;
}
return false;
}
grFont *grDispatcher::load_font(const char *file_name) {
grFont *p = new grFont;
if (!p->load(file_name)) {
delete p;
p = 0;
}
return p;
}
char *grDispatcher::temp_buffer(int size) {
if (size <= 0) size = 1;
if (size > _temp_buffer_size) {
delete[] _temp_buffer;
_temp_buffer = new char[size];
_temp_buffer_size = size;
}
return _temp_buffer;
}
bool grDispatcher::drawText(int x, int y, uint32 color, const char *str, int hspace, int vspace, const grFont *font) {
if (!font)
font = _default_font;
if (!font || !font->alpha_buffer())
return false;
const byte *str_buf = reinterpret_cast<const byte *>(str);
int x0 = x;
int sz = strlen(str);
for (int i = 0; i < sz; i++) {
if (str_buf[i] != '\n') {
const grScreenRegion &rg = font->find_char(str_buf[i]);
int dx = rg.size_x();
if (str_buf[i] == ' ')
x += font->size_x() / 2 + hspace;
else if (dx) {
putChar(x, y, color, font->alpha_buffer_size_x(), font->alpha_buffer_size_y(), font->alpha_buffer(), rg);
x += dx + hspace;
}
} else {
x = x0;
y += font->size_y() + vspace;
}
}
return true;
}
bool grDispatcher::drawAlignedText(int x, int y, int sx, int sy, uint32 color, const char *str, grTextAlign align, int hspace, int vspace, const grFont *font) {
if (!font)
font = _default_font;
if (!font || !font->alpha_buffer())
return false;
debugC(4, kDebugText, "grDispatcher::drawAlignedText([%d, %d], [%d, %d], color: %d, '%s', align: %d, ...", x, y, sx, sy, color, transCyrillic(str), align);
const byte *str_buf = reinterpret_cast<const byte *>(str);
if (!sx)
sx = textWidth(str, hspace, font);
int x0 = x;
int delta_x = 0;
int sz = strlen(str);
switch (align) {
case GR_ALIGN_CENTER:
delta_x = (sx - textWidth(str, hspace, font, true)) / 2;
break;
case GR_ALIGN_RIGHT:
delta_x = sx - textWidth(str, hspace, font, true);
break;
default:
break;
}
for (int i = 0; i < sz; i++) {
if (str_buf[i] != '\n') {
const grScreenRegion &rg = font->find_char(str_buf[i]);
int dx = rg.size_x();
if (str_buf[i] == ' ') {
x += font->size_x() / 2 + hspace;
} else if (dx) {
putChar(x + delta_x, y, color, font->alpha_buffer_size_x(), font->alpha_buffer_size_y(), font->alpha_buffer(), rg);
x += dx + hspace;
}
} else {
x = x0;
y += font->size_y() + vspace;
switch (align) {
case GR_ALIGN_CENTER:
delta_x = (sx - textWidth(str + i + 1, hspace, font, true)) / 2;
break;
case GR_ALIGN_RIGHT:
delta_x = sx - textWidth(str + i + 1, hspace, font, true);
break;
default:
break;
}
}
}
return true;
}
bool grDispatcher::drawParsedText(int x, int y, int sx, int sy, uint32 color, const UI_TextParser *parser, grTextAlign align, const grFont *font) {
if (!font)
font = _default_font;
int hSize = parser->fontHeight();
y -= hSize;
//bool skipToNextLine = false;
int cur_x = x;
for (OutNodes::const_iterator it = parser->outNodes().begin(); it != parser->outNodes().end(); ++it) {
switch (it->type) {
case OutNode::NEW_LINE:
//skipToNextLine = false;
y += hSize;
switch (align) {
case GR_ALIGN_LEFT:
cur_x = x;
break;
case GR_ALIGN_CENTER:
cur_x = x + (sx - it->width) / 2;
break;
case GR_ALIGN_RIGHT:
cur_x = x + sx - it->width - 1;
break;
default:
break;
}
break;
case OutNode::TEXT: {
//if (skipToNextLine)
// break;
Common::String str(it->nl.begin, it->nl.end - it->nl.begin);
drawText(cur_x, y, color, str.c_str(), 0, 0, font);
//int ssx = textWidth(str.c_str(), 0, font);
//int ssx1 = it->width;
cur_x += it->width;
}
break;
case OutNode::COLOR:
color = it->color;
break;
}
}
return true;
}
int grDispatcher::textWidth(const char *str, int hspace, const grFont *font, bool first_string_only) const {
if (!font)
font = _default_font;
if (!font)
return false;
const byte *str_buf = (const byte *)str;
int sx = 0, sx_max = 0;
int sz = strlen(str);
for (int i = 0; i < sz; i++) {
if (str_buf[i] != '\n') {
const grScreenRegion &rg = font->find_char(str_buf[i]);
int dx = rg.size_x();
if (str_buf[i] == ' ')
sx += font->size_x() / 2;
else
sx += dx + hspace;
} else {
if (first_string_only)
return sx;
if (sx_max < sx) sx_max = sx;
sx = 0;
}
}
if (sx_max < sx) sx_max = sx;
return sx_max;
}
int grDispatcher::textHeight(const char *str, int vspace, const grFont *font) const {
if (!font)
font = _default_font;
if (!font)
return false;
int sy = font->size_y() + vspace;
for (uint i = 0; i < strlen(str); i++) {
if (str[i] == '\n')
sy += font->size_y() + vspace;
}
return sy;
}
} // namespace QDEngine