aboutsummaryrefslogtreecommitdiff
path: root/engines/scumm/gfx.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'engines/scumm/gfx.cpp')
-rw-r--r--engines/scumm/gfx.cpp317
1 files changed, 214 insertions, 103 deletions
diff --git a/engines/scumm/gfx.cpp b/engines/scumm/gfx.cpp
index 3547986ce3..c1e06609b9 100644
--- a/engines/scumm/gfx.cpp
+++ b/engines/scumm/gfx.cpp
@@ -35,9 +35,12 @@
#include "scumm/usage_bits.h"
#include "scumm/he/wiz_he.h"
#include "scumm/util.h"
-#ifdef __DS__
-#include "blitters.h"
-#endif
+
+#ifdef USE_ARM_GFX_ASM
+extern "C" void DrawStripToScreenARM(int height, int width, byte const* text, byte const* src, byte* dst,
+ int vsPitch, int vmScreenWidth, int textSurfacePitch);
+extern "C" void Copy8ColARM(byte* dst, int dstPitch, const byte* src, int height);
+#endif /* USE_ARM_GFX_ASM */
namespace Scumm {
@@ -47,6 +50,7 @@ static void fill(byte *dst, int dstPitch, byte color, int w, int h);
static void copy8Col(byte *dst, int dstPitch, const byte *src, int height);
static void clear8Col(byte *dst, int dstPitch, int height);
+static void ditherHerc(byte *src, byte *hercbuf, int srcPitch, int *x, int *y, int *width, int *height);
struct StripTable {
int offsets[160];
@@ -535,21 +539,18 @@ void ScummEngine::updateDirtyScreen(VirtScreenNumber slot) {
*/
void ScummEngine::drawStripToScreen(VirtScreen *vs, int x, int width, int top, int bottom) {
- if (bottom <= top)
+ // Short-circuit if nothing has to be drawn
+ if (bottom <= top || top >= vs->h)
return;
- if (top >= vs->h)
- return;
-
- assert(top >= 0 && bottom <= vs->h); // Paranoia checks
+ // Some paranoia checks
+ assert(top >= 0 && bottom <= vs->h);
assert(x >= 0 && width <= vs->pitch);
assert(_textSurface.pixels);
- assert(_compositeBuf);
+ // Perform some clipping
if (width > vs->w - x)
width = vs->w - x;
-
- // Clip to the visible part of the scene
if (top < _screenTop)
top = _screenTop;
if (bottom > _screenTop + _screenHeight)
@@ -559,64 +560,112 @@ void ScummEngine::drawStripToScreen(VirtScreen *vs, int x, int width, int top, i
int y = vs->topline + top - _screenTop;
int height = bottom - top;
- if (height <= 0 || width <= 0)
+ if (width <= 0 || height <= 0)
return;
- // Compute screen etc. buffer pointers
const byte *src = vs->getPixels(x, top);
- byte *dst = _compositeBuf + x + y * _screenWidth;
+ int m = _textSurfaceMultiplier;
+ byte *dst;
+ int vsPitch;
+ int pitch = vs->pitch;
+
+ if (_useCJKMode && _textSurfaceMultiplier == 2) {
+ dst = _fmtownsBuf;
+
+ scale2x(dst, _screenWidth * m, src, vs->pitch, width, height);
+ src = dst;
+ vsPitch = _screenWidth * m - width * m;
+
+ } else {
+ vsPitch = vs->pitch - width;
+ }
+
+ dst = _compositeBuf;
+
if (_game.version < 7) {
- // Handle the text mask in older games; newer (V7/V8) games do not use it anymore.
- const byte *text = (byte *)_textSurface.getBasePtr(x, y);
-
-#ifdef __DS__
- DS::asmDrawStripToScreen(height, width, text, src, dst, vs->pitch, _screenWidth, _textSurface.pitch);
-#else
+ // For The Dig, FT and COMI, we just blit everything to the screen at once.
+ // For older games, things are more complicated. First off, we need to
+ // deal with the _textSurface, which needs to be composited over the
+ // screen contents. Secondly, a rendering mode might be active, which
+ // means a filter has to be applied.
+
+ // Compute pointer to the text surface
+ assert(_compositeBuf);
+ const byte *text = (byte *)_textSurface.getBasePtr(x * m, y * m);
+
+ // The values x, width, etc. are all multiples of 8 at this point,
+ // so loop unrolloing might be a good idea...
+ assert(0 == ((long)text & 3));
+ assert(0 == (width & 3));
+
// Compose the text over the game graphics
- for (int h = 0; h < height; ++h) {
- for (int w = 0; w < width; ++w) {
- if (text[w] == CHARSET_MASK_TRANSPARENCY)
- dst[w] = src[w];
- else
- dst[w] = text[w];
+
+ // TODO: Optimize this code. There are several things that come immediately to mind:
+ // (1) Loop unrolling: We could read 4 or even 8 pixels at once, since everything is
+ // a multiple of 8 here.
+ // (2) More ASM versions (in particular, the ARM code for the NDS could be used on
+ // all ARM systems, couldn't it?)
+ // (3) Better encoding of the text surface data. This is the one with the biggest
+ // potential.
+ // (a) Keep an "isEmpty" marker for each pixel row in the _textSurface. The idea
+ // is that most rows won't contain any text data, so we can just use memcpy.
+ // (b) RLE encode the _textSurface row-wise. This is an improved variant of (a),
+ // but also more complicated to implement, and incurs a bigger overhead when
+ // writing to the text surface.
+#ifdef ARM_USE_GFX_ASM
+ DrawStripToScreenARM(height, width, text, src, dst, vs->pitch, width, _textSurface.pitch);
+#else
+ for (int h = 0; h < height * m; ++h) {
+ for (int w = 0; w < width * m; ++w) {
+ byte tmp = *text++;
+ if (tmp == CHARSET_MASK_TRANSPARENCY)
+ tmp = *src;
+ *dst++ = tmp;
+ src++;
}
- src += vs->pitch;
- dst += _screenWidth;
- text += _textSurface.pitch;
+ src += vsPitch;
+ text += _textSurface.pitch - width * m;
}
#endif
- } else {
- // Just do a simple blit in V7/V8 games.
- blit(dst, _screenWidth, src, vs->pitch, width, height);
- }
-
- if (_renderMode == Common::kRenderCGA)
- ditherCGA(_compositeBuf + x + y * _screenWidth, _screenWidth, x, y, width, height);
+ src = _compositeBuf;
+ pitch = width;
+
+ if (_renderMode == Common::kRenderHercA || _renderMode == Common::kRenderHercG) {
+ ditherHerc(_compositeBuf, _herculesBuf, width, &x, &y, &width, &height);
+
+ src = _herculesBuf + x + y * Common::kHercW;
+ pitch = Common::kHercW;
+
+ // center image on the screen
+ x += (Common::kHercW - _screenWidth * 2) / 2; // (720 - 320*2)/2 = 40
+ } else if (_useCJKMode && m == 2) {
+ pitch *= m;
+ x *= m;
+ y *= m;
+ width *= m;
+ height *= m;
+ } else {
+ if (_renderMode == Common::kRenderCGA)
+ ditherCGA(_compositeBuf, width, x, y, width, height);
+
+ // HACK: This is dirty hack which renders narrow NES rooms centered
+ // NES can address negative number strips and that poses problem for
+ // our code. So instead of adding zillions of fixes and potentially
+ // breaking other games, we shift it right at the rendering stage.
+ if ((_game.platform == Common::kPlatformNES) && (((_NESStartStrip > 0) && (vs->number == kMainVirtScreen)) || (vs->number == kTextVirtScreen))) {
+ x += 16;
+ while (x + width >= _screenWidth)
+ width -= 16;
+ if (width < 0)
+ return;
+ }
- if (_renderMode == Common::kRenderHercA || _renderMode == Common::kRenderHercG) {
- ditherHerc(_compositeBuf + x + y * _screenWidth, _herculesBuf, _screenWidth, &x, &y, &width, &height);
- // center image on the screen
- _system->copyRectToScreen(_herculesBuf + x + y * Common::kHercW,
- Common::kHercW, x + (Common::kHercW - _screenWidth * 2) / 2, y, width, height);
- } else {
- // Finally blit the whole thing to the screen
- int x1 = x;
-
- // HACK: This is dirty hack which renders narrow NES rooms centered
- // NES can address negative number strips and that poses problem for
- // our code. So instead of adding zillions of fixes and potentially
- // breaking other games, we shift it right at the rendering stage.
- if ((_game.platform == Common::kPlatformNES) && (((_NESStartStrip > 0) && (vs->number == kMainVirtScreen)) || (vs->number == kTextVirtScreen))) {
- x += 16;
- while (x + width >= _screenWidth)
- width -= 16;
- if (width < 0)
- return;
}
-
- _system->copyRectToScreen(_compositeBuf + x1 + y * _screenWidth, _screenWidth, x, y, width, height);
}
+
+ // Finally blit the whole thing to the screen
+ _system->copyRectToScreen(src, pitch, x, y, width, height);
}
// CGA
@@ -644,10 +693,10 @@ void ScummEngine::ditherCGA(byte *dst, int dstPitch, int x, int y, int width, in
for (int y1 = 0; y1 < height; y1++) {
ptr = dst + y1 * dstPitch;
- idx1 = (y + y1) % 2;
-
if (_game.version == 2)
idx1 = 0;
+ else
+ idx1 = (y + y1) % 2;
for (int x1 = 0; x1 < width; x1++) {
idx2 = (x + x1) % 2;
@@ -666,39 +715,52 @@ void ScummEngine::ditherCGA(byte *dst, int dstPitch, int x, int y, int width, in
// dd cccc0
// cccc1
// dddd0
-void ScummEngine::ditherHerc(byte *src, byte *hercbuf, int srcPitch, int *x, int *y, int *width, int *height) const {
+void ditherHerc(byte *src, byte *hercbuf, int srcPitch, int *x, int *y, int *width, int *height) {
byte *srcptr, *dstptr;
- int xo = *x, yo = *y, widtho = *width, heighto = *height;
- int idx1, idx2, dsty = 0, y1;
+ const int xo = *x, yo = *y, widtho = *width, heighto = *height;
+ int dsty = yo*2 - yo/4;
- // calculate dsty
- for (y1 = 0; y1 < yo; y1++) {
- dsty += 2;
- if (y1 % 4 == 3)
- dsty--;
- }
- *y = dsty;
- *x *= 2;
- *width *= 2;
- *height = 0;
+ for (int y1 = 0; y1 < heighto;) {
+ assert(dsty < Common::kHercH);
- for (y1 = 0; y1 < heighto;) {
srcptr = src + y1 * srcPitch;
dstptr = hercbuf + dsty * Common::kHercW + xo * 2;
- assert(dstptr < hercbuf + Common::kHercW * Common::kHercH + widtho * 2);
-
- idx1 = (dsty % 7) % 2;
+ const int idx1 = (dsty % 7) % 2;
for (int x1 = 0; x1 < widtho; x1++) {
- idx2 = (xo + x1) % 2;
- *dstptr++ = cgaDither[idx1][idx2][*srcptr & 0xF] >> 1;
- *dstptr++ = cgaDither[idx1][idx2][*srcptr & 0xF] & 0x1;
+ const int idx2 = (xo + x1) % 2;
+ const byte tmp = cgaDither[idx1][idx2][*srcptr & 0xF];
+ *dstptr++ = tmp >> 1;
+ *dstptr++ = tmp & 0x1;
srcptr++;
}
if (idx1 || dsty % 7 == 6)
y1++;
dsty++;
- (*height)++;
+ }
+
+ *x *= 2;
+ *y = yo*2 - yo/4;
+ *width *= 2;
+ *height = dsty - *y;
+}
+
+void ScummEngine::scale2x(byte *dst, int dstPitch, const byte *src, int srcPitch, int w, int h) {
+ byte *dstL1 = dst;
+ byte *dstL2 = dst + dstPitch;
+
+ int dstAdd = dstPitch * 2 - w * 2;
+ int srcAdd = srcPitch - w;
+
+ while (h--) {
+ for (int x = 0; x < w; ++x, dstL1 += 2, dstL2 += 2) {
+ uint16 col = *src++;
+ col |= col << 8;
+ *(uint16*)(dstL1) = col;
+ *(uint16*)(dstL2) = col;
+ }
+ dstL1 += dstAdd; dstL2 += dstAdd;
+ src += srcAdd;
}
}
@@ -1004,11 +1066,14 @@ static void fill(byte *dst, int dstPitch, byte color, int w, int h) {
}
}
+#ifdef ARM_USE_GFX_ASM
+
+#define copy8Col(A,B,C,D) copy8ColARM(A,B,C,D)
+
+#else
+
static void copy8Col(byte *dst, int dstPitch, const byte *src, int height) {
-#ifndef __DS__
-
-
do {
#if defined(SCUMM_NEED_ALIGNMENT)
memcpy(dst, src, 8);
@@ -1019,12 +1084,10 @@ static void copy8Col(byte *dst, int dstPitch, const byte *src, int height) {
dst += dstPitch;
src += dstPitch;
} while (--height);
-#else
- DS::asmCopy8Col(dst, dstPitch, src, height);
-#endif
-
}
+#endif /* ARM_USE_GFX_ASM */
+
static void clear8Col(byte *dst, int dstPitch, int height) {
do {
#if defined(SCUMM_NEED_ALIGNMENT)
@@ -1098,8 +1161,8 @@ void ScummEngine::drawBox(int x, int y, int x2, int y2, int color) {
error("can only copy bg to main window");
blit(backbuff, vs->pitch, bgbuff, vs->pitch, width, height);
if (_charset->_hasMask) {
- byte *mask = (byte *)_textSurface.getBasePtr(x, y - _screenTop);
- fill(mask, _textSurface.pitch, CHARSET_MASK_TRANSPARENCY, width, height);
+ byte *mask = (byte *)_textSurface.getBasePtr(x * _textSurfaceMultiplier, (y - _screenTop) * _textSurfaceMultiplier);
+ fill(mask, _textSurface.pitch, CHARSET_MASK_TRANSPARENCY, width * _textSurfaceMultiplier, height * _textSurfaceMultiplier);
}
} else if (_game.heversion >= 71) {
// Flags are used for different methods in HE games
@@ -3343,7 +3406,17 @@ void ScummEngine::dissolveEffect(int width, int height) {
for (i = 0; i < w * h; i++) {
x = offsets[i] % vs->pitch;
y = offsets[i] / vs->pitch;
- _system->copyRectToScreen(vs->getPixels(x, y), vs->pitch, x, y + vs->topline, width, height);
+
+ if (_useCJKMode && _textSurfaceMultiplier == 2) {
+ int m = _textSurfaceMultiplier;
+ byte *dst = _fmtownsBuf + x * m + y * m * _screenWidth * m;
+ scale2x(dst, _screenWidth * m, vs->getPixels(x, y), vs->pitch, width, height);
+
+ _system->copyRectToScreen(dst, _screenWidth * m, x * m, (y + vs->topline) * m, width * m, height * m);
+ } else {
+ _system->copyRectToScreen(vs->getPixels(x, y), vs->pitch, x, y + vs->topline, width, height);
+ }
+
if (++blits >= blits_before_refresh) {
blits = 0;
@@ -3372,16 +3445,30 @@ void ScummEngine::scrollEffect(int dir) {
step = (step * delay) / kScrolltime;
+ byte *src;
+ int m = _textSurfaceMultiplier;
+ int vsPitch = vs->pitch;
+
switch (dir) {
case 0:
//up
y = 1 + step;
while (y < vs->h) {
moveScreen(0, -step, vs->h);
- _system->copyRectToScreen(vs->getPixels(0, y - step),
- vs->pitch,
- 0, vs->h - step,
- vs->w, step);
+
+ src = vs->getPixels(0, y - step);
+ if (_useCJKMode && m == 2) {
+ int x1 = 0, y1 = vs->h - step;
+ byte *dst = _fmtownsBuf + x1 * m + y1 * m * _screenWidth * m;
+ scale2x(dst, _screenWidth * m, src, vs->pitch, vs->w, step);
+ src = dst;
+ vsPitch = _screenWidth * 2;
+ }
+
+ _system->copyRectToScreen(src,
+ vsPitch,
+ 0 * m, (vs->h - step) * m,
+ vs->w * m, step * m);
_system->updateScreen();
waitForTimer(delay);
@@ -3393,10 +3480,18 @@ void ScummEngine::scrollEffect(int dir) {
y = 1 + step;
while (y < vs->h) {
moveScreen(0, step, vs->h);
- _system->copyRectToScreen(vs->getPixels(0, vs->h - y),
- vs->pitch,
+ src = vs->getPixels(0, vs->h - y);
+ if (_useCJKMode && m == 2) {
+ int x1 = 0, y1 = 0;
+ byte *dst = _fmtownsBuf + x1 * m + y1 * m * _screenWidth * m;
+ scale2x(dst, _screenWidth * m, src, vs->pitch, vs->w, step);
+ src = dst;
+ vsPitch = _screenWidth * 2;
+ }
+ _system->copyRectToScreen(src,
+ vsPitch,
0, 0,
- vs->w, step);
+ vs->w * m, step * m);
_system->updateScreen();
waitForTimer(delay);
@@ -3408,10 +3503,18 @@ void ScummEngine::scrollEffect(int dir) {
x = 1 + step;
while (x < vs->w) {
moveScreen(-step, 0, vs->h);
- _system->copyRectToScreen(vs->getPixels(x - step, 0),
- vs->pitch,
- vs->w - step, 0,
- step, vs->h);
+ src = vs->getPixels(x - step, 0);
+ if (_useCJKMode && m == 2) {
+ int x1 = vs->w - step, y1 = 0;
+ byte *dst = _fmtownsBuf + x1 * m + y1 * m * _screenWidth * m;
+ scale2x(dst, _screenWidth * m, src, vs->pitch, step, vs->h);
+ src = dst;
+ vsPitch = _screenWidth * 2;
+ }
+ _system->copyRectToScreen(src,
+ vsPitch,
+ (vs->w - step) * m, 0,
+ step * m, vs->h * m);
_system->updateScreen();
waitForTimer(delay);
@@ -3423,8 +3526,16 @@ void ScummEngine::scrollEffect(int dir) {
x = 1 + step;
while (x < vs->w) {
moveScreen(step, 0, vs->h);
- _system->copyRectToScreen(vs->getPixels(vs->w - x, 0),
- vs->pitch,
+ src = vs->getPixels(vs->w - x, 0);
+ if (_useCJKMode && m == 2) {
+ int x1 = 0, y1 = 0;
+ byte *dst = _fmtownsBuf + x1 * m + y1 * m * _screenWidth * m;
+ scale2x(dst, _screenWidth * m, src, vs->pitch, step, vs->h);
+ src = dst;
+ vsPitch = _screenWidth * 2;
+ }
+ _system->copyRectToScreen(src,
+ vsPitch,
0, 0,
step, vs->h);
_system->updateScreen();