/* 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/>.
*
*/
#ifndef SCUMM_GFX_H
#define SCUMM_GFX_H
#include "common/system.h"
#include "common/list.h"
#include "graphics/surface.h"
namespace Scumm {
class ScummEngine;
enum HerculesDimensions {
kHercWidth = 720,
kHercHeight = 350
};
/** Camera modes */
enum {
kNormalCameraMode = 1,
kFollowActorCameraMode = 2,
kPanningCameraMode = 3
};
/** Camera state data */
struct CameraData {
Common::Point _cur;
Common::Point _dest;
Common::Point _accel;
Common::Point _last;
int _leftTrigger, _rightTrigger;
byte _follows, _mode;
bool _movingToActor;
void reset() {
_cur.x = _cur.y = 0;
_dest.x = _dest.y = 0;
_accel.x = _accel.y = 0;
_last.x = _last.y = 0;
_leftTrigger = 0;
_rightTrigger = 0;
_follows = 0;
_mode = 0;
_movingToActor = 0;
}
};
/** Virtual screen identifiers */
enum VirtScreenNumber {
kMainVirtScreen = 0, // The 'stage'
kTextVirtScreen = 1, // In V0-V3 games: the area where text is printed
kVerbVirtScreen = 2, // The verb area
kUnkVirtScreen = 3 // ?? Not sure what this one is good for...
};
/**
* In all Scumm games, one to four virtual screen (or 'windows') together make
* up the content of the actual screen. Thinking of virtual screens as fixed
* size, fixed location windows might help understanding them. Typical, in all
* scumm games there is either one single virtual screen covering the entire
* real screen (mostly in all newer games, e.g. Sam & Max, and all V7+ games).
* The classic setup consists of three virtual screens: one at the top of the
* screen, where all conversation texts are printed; then the main one (which
* I like calling 'the stage', since all the actors are doing their stuff
* there), and finally the lower part of the real screen is taken up by the
* verb area.
* Finally, in V5 games and some V6 games, it's almost the same as in the
* original games, except that there is no separate conversation area.
*
* If you now wonder what the last screen is/was good for: I am not 100% sure,
* but it appears that it was used by the original engine to display stuff
* like the pause message, or questions ("Do you really want to restart?").
* It seems that it is not used at all by ScummVM, so we probably could just
* get rid of it and save a couple kilobytes of RAM.
*
* Each of these virtual screens has a fixed number or id (see also
* \ref VirtScreenNumber).
*/
struct VirtScreen : Graphics::Surface {
/**
* The unique id of this screen (corresponds to its position in the
* ScummEngine:_virtscr array).
*/
VirtScreenNumber number;
/**
* Vertical position of the virtual screen. Tells how much the virtual
* screen is shifted along the y axis relative to the real screen.
*/
uint16 topline;
/**
* Horizontal scroll offset, tells how far the screen is scrolled to the
* right. Only used for the main screen. After all, verbs and the
* conversation text box don't have to scroll.
*/
uint16 xstart;
/**
* Flag indicating whether this screen has a back buffer or not. This is
* yet another feature which is only used by the main screen.
* Strictly spoken one could remove this variable and replace checks
* on it with checks on backBuf. But since some code needs to temporarily
* disable the backBuf (so it can abuse drawBitmap; see drawVerbBitmap()
* and useIm01Cursor()), we keep it (at least for now).
*/
bool hasTwoBuffers;
/**
* Pointer to the screen's back buffer, if it has one (see also
* the hasTwoBuffers member).
* The backBuf is used by drawBitmap to store the background graphics of
* the active room. This eases redrawing: whenever a portion of the screen
* has to be redrawn, first a copy from the backBuf content to screenPtr is
* performed. Then, any objects/actors in that area are redrawn atop that.
*/
byte *backBuf;
/**
* Array containing for each visible strip of this virtual screen the
* coordinate at which the dirty region of that strip starts.
* 't' stands for 'top' - the top coordinate of the dirty region.
* This together with bdirty is used to do efficient redrawing of
* the screen.
*/
uint16 tdirty[80 + 1];
/**
* Array containing for each visible strip of this virtual screen the
* coordinate at which the dirty region of that strip end.
* 'b' stands for 'bottom' - the bottom coordinate of the dirty region.
* This together with tdirty is used to do efficient redrawing of
* the screen.
*/
uint16 bdirty[80 + 1];
void clear() {
// FIXME: Call Graphics::Surface clear / constructor?
number = kMainVirtScreen;
topline = 0;
xstart = 0;
hasTwoBuffers = false;
backBuf = nullptr;
for (uint i = 0; i < ARRAYSIZE(tdirty); i++) tdirty[i] = 0;
for (uint i = 0; i < ARRAYSIZE(bdirty); i++) bdirty[i] = 0;
}
/**
* Convenience method to set the whole tdirty and bdirty arrays to one
* specific value each. This is mostly used to mark every as dirty in
* a single step, like so:
* vs->setDirtyRange(0, vs->height);
* or to mark everything as clean, like so:
* vs->setDirtyRange(0, 0);
*/
void setDirtyRange(int top, int bottom) {
for (int i = 0; i < 80 + 1; i++) {
tdirty[i] = top;
bdirty[i] = bottom;
}
}
byte *getPixels(int x, int y) const {
return (byte *)pixels + y * pitch + (xstart + x) * format.bytesPerPixel;
}
byte *getBackPixels(int x, int y) const {
return (byte *)backBuf + y * pitch + (xstart + x) * format.bytesPerPixel;
}
};
/** Palette cycles */
struct ColorCycle {
uint16 delay;
uint16 counter;
uint16 flags;
byte start;
byte end;
};
struct StripTable;
#define CHARSET_MASK_TRANSPARENCY 0xFD
#define CHARSET_MASK_TRANSPARENCY_32 0xFDFDFDFD
class Gdi {
protected:
ScummEngine *_vm;
byte _paletteMod;
byte *_roomPalette;
byte _transparentColor;
byte _decomp_shr, _decomp_mask;
uint32 _vertStripNextInc;
bool _zbufferDisabled;
/** Flag which is true when an object is being rendered, false otherwise. */
bool _objectMode;
public:
/** Flag which is true when loading objects or titles for distaff, in PCEngine version of Loom. */
bool _distaff;
int _numZBuffer;
int _imgBufOffs[8];
int32 _numStrips;
protected:
/* Bitmap decompressors */
bool decompressBitmap(byte *dst, int dstPitch, const byte *src, int numLinesToProcess);
void drawStripEGA(byte *dst, int dstPitch, const byte *src, int height) const;
void drawStripComplex(byte *dst, int dstPitch, const byte *src, int height, const bool transpCheck) const;
void drawStripBasicH(byte *dst, int dstPitch, const byte *src, int height, const bool transpCheck) const;
void drawStripBasicV(byte *dst, int dstPitch, const byte *src, int height, const bool transpCheck) const;
void drawStripRaw(byte *dst, int dstPitch, const byte *src, int height, const bool transpCheck) const;
void unkDecode8(byte *dst, int dstPitch, const byte *src, int height) const;
void unkDecode9(byte *dst, int dstPitch, const byte *src, int height) const;
void unkDecode10(byte *dst, int dstPitch, const byte *src, int height) const;
void unkDecode11(byte *dst, int dstPitch, const byte *src, int height) const;
void drawStrip3DO(byte *dst, int dstPitch, const byte *src, int height, const bool transpCheck) const;
void drawStripHE(byte *dst, int dstPitch, const byte *src, int width, int height, const bool transpCheck) const;
virtual void writeRoomColor(byte *dst, byte color) const;
/* Mask decompressors */
void decompressMaskImgOr(byte *dst, const byte *src, int height) const;
void decompressMaskImg(byte *dst, const byte *src, int height) const;
/* Misc */
int getZPlanes(const byte *smap_ptr, const byte *zplane_list[9], bool bmapImage) const;
virtual bool drawStrip(byte *dstPtr, VirtScreen *vs,
int x, int y, const int width, const int height,
int stripnr, const byte *smap_ptr);
virtual void decodeMask(int x, int y, const int width, const int height,
int stripnr, int numzbuf, const byte *zplane_list[9],
bool transpStrip, byte flag);
virtual void prepareDrawBitmap(const byte *ptr, VirtScreen *vs,
const int x, const int y, const int width, const int height,
int stripnr, int numstrip);
public:
Gdi(ScummEngine *vm);
virtual ~Gdi();
virtual void init();
virtual void roomChanged(byte *roomptr);
virtual void loadTiles(byte *roomptr);
void setTransparentColor(byte transparentColor) { _transparentColor = transparentColor; }
void drawBitmap(const byte *ptr, VirtScreen *vs, int x, int y, const int width, const int height,
int stripnr, int numstrip, byte flag);
#ifdef ENABLE_HE
void drawBMAPBg(const byte *ptr, VirtScreen *vs);
void drawBMAPObject(const byte *ptr, VirtScreen *vs, int obj, int x, int y, int w, int h);
#endif
byte *getMaskBuffer(int x, int y, int z);
void disableZBuffer() { _zbufferDisabled = true; }
void enableZBuffer() { _zbufferDisabled = false; }
void resetBackground(int top, int bottom, int strip);
enum DrawBitmapFlags {
dbAllowMaskOr = 1 << 0,
dbDrawMaskOnAll = 1 << 1,
dbObjectMode = 2 << 2
};
};
class GdiHE : public Gdi {
protected:
const byte *_tmskPtr;
protected:
void decompressTMSK(byte *dst, const byte *tmsk, const byte *src, int height) const;
void decodeMask(int x, int y, const int width, const int height,
int stripnr, int numzbuf, const byte *zplane_list[9],
bool transpStrip, byte flag) override;
void prepareDrawBitmap(const byte *ptr, VirtScreen *vs,
const int x, const int y, const int width, const int height,
int stripnr, int numstrip) override;
public:
GdiHE(ScummEngine *vm);
};
class GdiNES : public Gdi {
protected:
struct {
byte nametable[16][64], nametableObj[16][64];
byte attributes[64], attributesObj[64];
byte masktable[16][8], masktableObj[16][8];
int objX;
bool hasmask;
} _NES;
protected:
void decodeNESGfx(const byte *room);
void decodeNESObject(const byte *ptr, int xpos, int ypos, int width, int height);
void drawStripNES(byte *dst, byte *mask, int dstPitch, int stripnr, int top, int height);
void drawStripNESMask(byte *dst, int stripnr, int top, int height) const;
bool drawStrip(byte *dstPtr, VirtScreen *vs,
int x, int y, const int width, const int height,
int stripnr, const byte *smap_ptr) override;
void decodeMask(int x, int y, const int width, const int height,
int stripnr, int numzbuf, const byte *zplane_list[9],
bool transpStrip, byte flag) override;
void prepareDrawBitmap(const byte *ptr, VirtScreen *vs,
const int x, const int y, const int width, const int height,
int stripnr, int numstrip) override;
public:
GdiNES(ScummEngine *vm);
void roomChanged(byte *roomptr) override;
};
#ifdef USE_RGB_COLOR
class GdiPCEngine : public Gdi {
protected:
struct {
uint16 nametable[4096], nametableObj[512];
byte colortable[4096], colortableObj[512];
uint16 masktable[4096], masktableObj[512];
int maskIDSize;
int numTiles;
int numMasks;
byte *roomTiles, *staffTiles;
byte *masks;
} _PCE;
protected:
void decodePCEngineGfx(const byte *room);
void decodeStrip(const byte *ptr, uint16 *tiles, byte *colors, uint16 *masks, int numRows, bool isObject);
void setTileData(byte *tile, int index, byte byte0, byte byte1);
void decodePCEngineTileData(const byte *ptr);
void decodePCEngineMaskData(const byte *ptr);
void decodePCEngineObject(const byte *ptr, int xpos, int ypos, int width, int height);
void drawStripPCEngine(byte *dst, byte *mask, int dstPitch, int stripnr, int top, int height);
void drawStripPCEngineMask(byte *dst, int stripnr, int top, int height) const;
bool drawStrip(byte *dstPtr, VirtScreen *vs,
int x, int y, const int width, const int height,
int stripnr, const byte *smap_ptr) override;
void decodeMask(int x, int y, const int width, const int height,
int stripnr, int numzbuf, const byte *zplane_list[9],
bool transpStrip, byte flag) override;
void prepareDrawBitmap(const byte *ptr, VirtScreen *vs,
const int x, const int y, const int width, const int height,
int stripnr, int numstrip) override;
public:
GdiPCEngine(ScummEngine *vm);
~GdiPCEngine() override;
void loadTiles(byte *roomptr) override;
void roomChanged(byte *roomptr) override;
};
#endif
class GdiV1 : public Gdi {
protected:
/** Render settings which are specific to the v0/v1 graphic decoders. */
struct {
byte colors[4];
byte charMap[2048], objectMap[2048], picMap[4096], colorMap[4096];
byte maskMap[4096], maskChar[4096];
} _V1;
protected:
void decodeV1Gfx(const byte *src, byte *dst, int size) const;
void drawStripV1Object(byte *dst, int dstPitch, int stripnr, int width, int height);
void drawStripV1Background(byte *dst, int dstPitch, int stripnr, int height);
void drawStripV1Mask(byte *dst, int stripnr, int width, int height) const;
bool drawStrip(byte *dstPtr, VirtScreen *vs,
int x, int y, const int width, const int height,
int stripnr, const byte *smap_ptr) override;
void decodeMask(int x, int y, const int width, const int height,
int stripnr, int numzbuf, const byte *zplane_list[9],
bool transpStrip, byte flag) override;
void prepareDrawBitmap(const byte *ptr, VirtScreen *vs,
const int x, const int y, const int width, const int height,
int stripnr, int numstrip) override;
public:
GdiV1(ScummEngine *vm);
void roomChanged(byte *roomptr) override;
};
class GdiV2 : public Gdi {
protected:
/** For V2 games, we cache offsets into the room graphics, to speed up things. */
StripTable *_roomStrips;
protected:
StripTable *generateStripTable(const byte *src, int width, int height, StripTable *table) const;
bool drawStrip(byte *dstPtr, VirtScreen *vs,
int x, int y, const int width, const int height,
int stripnr, const byte *smap_ptr) override;
void decodeMask(int x, int y, const int width, const int height,
int stripnr, int numzbuf, const byte *zplane_list[9],
bool transpStrip, byte flag) override;
void prepareDrawBitmap(const byte *ptr, VirtScreen *vs,
const int x, const int y, const int width, const int height,
int stripnr, int numstrip) override;
public:
GdiV2(ScummEngine *vm);
~GdiV2() override;
void roomChanged(byte *roomptr) override;
};
#ifdef USE_RGB_COLOR
class GdiHE16bit : public GdiHE {
protected:
void writeRoomColor(byte *dst, byte color) const override;
public:
GdiHE16bit(ScummEngine *vm);
};
#endif
#ifndef DISABLE_TOWNS_DUAL_LAYER_MODE
// Helper class for FM-Towns output (required for specific hardware effects like switching graphics layers on and off).
class TownsScreen {
public:
enum {
kDirtyRectsMax = 20,
kFullRedraw = (kDirtyRectsMax + 1)
};
public:
TownsScreen(OSystem *system);
~TownsScreen();
void setupLayer(int layer, int width, int height, int scaleW, int scaleH, int numCol, void *srcPal = 0);
void clearLayer(int layer);
void fillLayerRect(int layer, int x, int y, int w, int h, int col);
void addDirtyRect(int x, int y, int w, int h);
void toggleLayers(int flags);
void scrollLayers(int flags, int offset, bool fast);
void update();
bool isScrolling(int direction, int threshold = 0) const { return (direction == 0) ? _scrollRemainder != threshold : (direction == 1 ? _scrollRemainder > threshold : _scrollRemainder < threshold); }
uint8 *getLayerPixels(int layer, int x, int y) const;
int getLayerPitch(int layer) const { return (layer >= 0 && layer < 2) ? _layers[layer].pitch : 0; }
int getLayerWidth(int layer) const { return (layer >= 0 && layer < 2) ? _layers[layer].width : 0; }
int getLayerHeight(int layer) const { return (layer >= 0 && layer < 2) ? _layers[layer].height : 0; }
int getLayerBpp(int layer) const { return (layer >= 0 && layer < 2) ? _layers[layer].bpp : 0; }
int getLayerScaleW(int layer) const { return (layer >= 0 && layer < 2) ? _layers[layer].scaleW : 0; }
int getLayerScaleH(int layer) const { return (layer >= 0 && layer < 2) ? _layers[layer].scaleH : 0; }
private:
struct TownsScreenLayer {
uint8 *pixels;
uint8 *palette;
int pitch;
int width;
int height;
int bpp;
int numCol;
int hScroll;
uint8 scaleW;
uint8 scaleH;
bool onBottom;
bool enabled;
bool ready;
uint16 *bltTmpPal;
} _layers[2];
template<typename dstPixelType, typename srcPixelType, int scaleW, int scaleH, bool col4bit> void transferRect(uint8 *dst, TownsScreenLayer *l, int x, int y, int w, int h);
template<typename dstPixelType> void updateScreenBuffer();
#ifdef USE_RGB_COLOR
void update16BitPalette();
uint16 calc16BitColor(const uint8 *palEntry);
#endif
int _height;
int _width;
int _pitch;
uint16 _scrollOffset;
int _scrollRemainder;
bool _semiSmoothScroll;
Graphics::PixelFormat _pixelFormat;
int _numDirtyRects;
Common::List<Common::Rect> _dirtyRects;
OSystem *_system;
};
#endif // DISABLE_TOWNS_DUAL_LAYER_MODE
} // End of namespace Scumm
#endif