Logo Search packages:      
Sourcecode: scummvm version File versions

gfx.cpp

/* 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 2
 * 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, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 *
 * $URL: https://scummvm.svn.sourceforge.net/svnroot/scummvm/scummvm/tags/release-0-13-0/engines/cine/gfx.cpp $
 * $Id: gfx.cpp 35648 2009-01-01 15:06:43Z sev $
 *
 */

#include "cine/cine.h"
#include "cine/bg.h"
#include "cine/bg_list.h"
#include "cine/various.h"
#include "cine/pal.h"

#include "common/endian.h"
#include "common/system.h"
#include "common/events.h"

#include "graphics/cursorman.h"

namespace Cine {

byte *collisionPage;
FWRenderer *renderer = NULL;

static const byte mouseCursorNormal[] = {
      0x00, 0x00, 0x40, 0x00, 0x60, 0x00, 0x70, 0x00,
      0x78, 0x00, 0x7C, 0x00, 0x7E, 0x00, 0x7F, 0x00,
      0x7F, 0x80, 0x7C, 0x00, 0x6C, 0x00, 0x46, 0x00,
      0x06, 0x00, 0x03, 0x00, 0x03, 0x00, 0x00, 0x00,
      0xC0, 0x00, 0xE0, 0x00, 0xF0, 0x00, 0xF8, 0x00,
      0xFC, 0x00, 0xFE, 0x00, 0xFF, 0x00, 0xFF, 0x80,
      0xFF, 0xC0, 0xFF, 0xC0, 0xFE, 0x00, 0xFF, 0x00,
      0xCF, 0x00, 0x07, 0x80, 0x07, 0x80, 0x03, 0x80
};

static const byte mouseCursorDisk[] = {
      0x7F, 0xFC, 0x9F, 0x12, 0x9F, 0x12, 0x9F, 0x12,
      0x9F, 0x12, 0x9F, 0xE2, 0x80, 0x02, 0x9F, 0xF2,
      0xA0, 0x0A, 0xA0, 0x0A, 0xA0, 0x0A, 0xA0, 0x0A,
      0xA0, 0x0A, 0xA0, 0x0A, 0x7F, 0xFC, 0x00, 0x00,
      0x7F, 0xFC, 0xFF, 0xFE, 0xFF, 0xFE, 0xFF, 0xFE,
      0xFF, 0xFE, 0xFF, 0xFE, 0xFF, 0xFE, 0xFF, 0xFE,
      0xFF, 0xFE, 0xFF, 0xFE, 0xFF, 0xFE, 0xFF, 0xFE,
      0xFF, 0xFE, 0xFF, 0xFE, 0x7F, 0xFC, 0x00, 0x00
};

static const byte mouseCursorCross[] = {
      0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00,
      0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x7C, 0x7C,
      0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00,
      0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x03, 0x80, 0x03, 0x80, 0x03, 0x80, 0x03, 0x80,
      0x03, 0x80, 0x03, 0x80, 0xFF, 0xFE, 0xFE, 0xFE,
      0xFF, 0xFE, 0x03, 0x80, 0x03, 0x80, 0x03, 0x80,
      0x03, 0x80, 0x03, 0x80, 0x03, 0x80, 0x00, 0x00
};

static const struct MouseCursor {
      int hotspotX;
      int hotspotY;
      const byte *bitmap;
} mouseCursors[] = {
      { 1, 1, mouseCursorNormal },
      { 0, 0, mouseCursorDisk },
      { 7, 7, mouseCursorCross }
};

static const byte cursorPalette[] = {
      0, 0, 0, 0xff,
      0xff, 0xff, 0xff, 0xff
};

/*! \brief Initialize renderer
 */
00093 FWRenderer::FWRenderer() : _background(NULL), _palette(NULL), _cmd(""),
      _cmdY(0), _messageBg(0), _backBuffer(new byte[_screenSize]),
      _activeLowPal(NULL), _changePal(0), _showCollisionPage(false) {

      assert(_backBuffer);

      memset(_backBuffer, 0, _screenSize);
      memset(_bgName, 0, sizeof (_bgName));
}

/* \brief Destroy renderer
 */
FWRenderer::~FWRenderer() {
      delete[] _background;
      delete[] _palette;
      delete[] _backBuffer;
      delete[] _activeLowPal;
}

/* \brief Reset renderer state
 */
void FWRenderer::clear() {
      delete[] _background;
      delete[] _palette;
      delete[] _activeLowPal;

      _background = NULL;
      _palette = NULL;
      _activeLowPal = NULL;

      memset(_backBuffer, 0, _screenSize);

      _cmd = "";
      _cmdY = 0;
      _messageBg = 0;
      _changePal = 0;
      _showCollisionPage = false;
}

/*! \brief Draw 1bpp sprite using selected color
 * \param obj Object info
 * \param fillColor Sprite color
 */
00136 void FWRenderer::fillSprite(const objectStruct &obj, uint8 color) {
      const byte *data = animDataTable[obj.frame].data();
      int x, y, width, height;

      x = obj.x;
      y = obj.y;
      width = animDataTable[obj.frame]._realWidth;
      height = animDataTable[obj.frame]._height;

      gfxFillSprite(data, width, height, _backBuffer, x, y, color);
}

/*! \brief Draw 1bpp sprite using selected color on background
 * \param obj Object info
 * \param fillColor Sprite color
 */
00152 void FWRenderer::incrustMask(const objectStruct &obj, uint8 color) {
      const byte *data = animDataTable[obj.frame].data();
      int x, y, width, height;

      x = obj.x;
      y = obj.y;
      width = animDataTable[obj.frame]._realWidth;
      height = animDataTable[obj.frame]._height;

      gfxFillSprite(data, width, height, _background, x, y, color);
}

/*! \brief Draw color sprite using with external mask
 * \param obj Object info
 * \param mask External mask
 */
00168 void FWRenderer::drawMaskedSprite(const objectStruct &obj, const byte *mask) {
      const byte *data = animDataTable[obj.frame].data();
      int x, y, width, height;

      x = obj.x;
      y = obj.y;
      width = animDataTable[obj.frame]._realWidth;
      height = animDataTable[obj.frame]._height;

      assert(mask);

      drawSpriteRaw(data, mask, width, height, _backBuffer, x, y);
}

/*! \brief Draw color sprite
 * \param obj Object info
 */
00185 void FWRenderer::drawSprite(const objectStruct &obj) {
      const byte *mask = animDataTable[obj.frame].mask();
      drawMaskedSprite(obj, mask);
}

/*! \brief Draw color sprite on background
 * \param obj Object info
 */
00193 void FWRenderer::incrustSprite(const objectStruct &obj) {
      const byte *data = animDataTable[obj.frame].data();
      const byte *mask = animDataTable[obj.frame].mask();
      int x, y, width, height;

      x = obj.x;
      y = obj.y;
      width = animDataTable[obj.frame]._realWidth;
      height = animDataTable[obj.frame]._height;

      // There was an assert(mask) here before but it made savegame loading
      // in Future Wars sometimes fail the assertion (e.g. see bug #2055912).
      // Not drawing sprites that have no mask seems to work, but not sure
      // if this is really a correct way to fix this.
      if (mask) {
            drawSpriteRaw(data, mask, width, height, _background, x, y);
      } else { // mask == NULL
            warning("FWRenderer::incrustSprite: Skipping maskless sprite (frame=%d)", obj.frame);
      }
}

/*! \brief Draw command box on screen
 */
00216 void FWRenderer::drawCommand() {
      unsigned int i;
      int x = 10, y = _cmdY;

      drawPlainBox(x, y, 301, 11, 0);
      drawBorder(x - 1, y - 1, 302, 12, 2);

      x += 2;
      y += 2;

      for (i = 0; i < _cmd.size(); i++) {
            x = drawChar(_cmd[i], x, y);
      }
}

/*! \brief Draw message in a box
 * \param str Message to draw
 * \param x Top left message box corner coordinate
 * \param y Top left message box corner coordinate
 * \param width Message box width
 * \param color Message box background color (Or if negative draws only the text)
 * \note Negative colors are used in Operation Stealth's timed cutscenes
 * (e.g. when first meeting The Movement for the Liberation of Santa Paragua).
 */
00240 void FWRenderer::drawMessage(const char *str, int x, int y, int width, int color) {
      int i, tx, ty, tw;
      int line = 0, words = 0, cw = 0;
      int space = 0, extraSpace = 0;

      if (color >= 0) {
            drawPlainBox(x, y, width, 4, color);
      }
      tx = x + 4;
      ty = str[0] ? y - 5 : y + 4;
      tw = width - 8;

      for (i = 0; str[i]; i++, line--) {
            // Fit line of text into textbox
            if (!line) {
                  while (str[i] == ' ') i++;
                  line = fitLine(str + i, tw, words, cw);

                  if ( str[i + line] != '\0' && str[i + line] != 0x7C && words) {
                        space = (tw - cw) / words;
                        extraSpace = (tw - cw) % words;
                  } else {
                        space = 5;
                        extraSpace = 0;
                  }

                  ty += 9;
                  if (color >= 0) {
                        drawPlainBox(x, ty, width, 9, color);
                  }
                  tx = x + 4;
            }

            // draw characters
            if (str[i] == ' ') {
                  tx += space + extraSpace;

                  if (extraSpace) {
                        extraSpace = 0;
                  }
            } else {
                  tx = drawChar(str[i], tx, ty);
            }
      }

      ty += 9;
      if (color >= 0) {
            drawPlainBox(x, ty, width, 4, color);
            drawDoubleBorder(x, y, width, ty - y + 4, 2);
      }
}

/*! \brief Draw rectangle on screen
 * \param x Top left corner coordinate
 * \param y Top left corner coordinate
 * \param width Rectangle width (Negative values draw the box horizontally flipped)
 * \param height Rectangle height (Negative values draw the box vertically flipped)
 * \param color Fill color
 * \note An on-screen rectangle's drawn width is always at least one.
 * \note An on-screen rectangle's drawn height is always at least one.
 */
00301 void FWRenderer::drawPlainBox(int x, int y, int width, int height, byte color) {
      // Make width's and height's absolute values at least one
      // which forces this function to always draw something if the
      // drawing position is inside screen bounds. This fixes at least
      // the showing of the oxygen gauge meter in Operation Stealth's
      // first arcade sequence where this function is called with a
      // height of zero.
      if (width == 0) {
            width = 1;
      }
      if (height == 0) {
            height = 1;
      }

      // Handle horizontally flipped boxes
      if (width < 0) {
            width = ABS(width);
            x -= width;
      }

      // Handle vertically flipped boxes
      if (height < 0) {
            height = ABS(height);
            y -= height;
      }

      // Clip the rectangle to screen dimensions
      Common::Rect boxRect(x, y, x + width, y + height);
      Common::Rect screenRect(320, 200);
      boxRect.clip(screenRect);

      // Draw the filled rectangle
      byte *dest = _backBuffer + boxRect.top * 320 + boxRect.left;
      for (int i = 0; i < boxRect.height(); i++) {
            memset(dest + i * 320, color, boxRect.width());
      }
}

/*! \brief Draw empty rectangle
 * \param x Top left corner coordinate
 * \param y Top left corner coordinate
 * \param width Rectangle width
 * \param height Rectangle height
 * \param color Line color
 */
00346 void FWRenderer::drawBorder(int x, int y, int width, int height, byte color) {
      drawLine(x, y, width, 1, color);
      drawLine(x, y + height, width, 1, color);
      drawLine(x, y, 1, height, color);
      drawLine(x + width, y, 1, height + 1, color);
}

/*! \brief Draw empty 2 color rectangle (inner line color is black)
 * \param x Top left corner coordinate
 * \param y Top left corner coordinate
 * \param width Rectangle width
 * \param height Rectangle height
 * \param color Outter line color
 */
00360 void FWRenderer::drawDoubleBorder(int x, int y, int width, int height, byte color) {
      drawBorder(x + 1, y + 1, width - 2, height - 2, 0);
      drawBorder(x, y, width, height, color);
}

/*! \brief Draw text character on screen
 * \param character Character to draw
 * \param x Character coordinate
 * \param y Character coordinate
 */
00370 int FWRenderer::drawChar(char character, int x, int y) {
      int width, idx;

      if (character == ' ') {
            x += 5;
      } else if ((width = g_cine->_textHandler.fontParamTable[(unsigned char)character].characterWidth)) {
            idx = g_cine->_textHandler.fontParamTable[(unsigned char)character].characterIdx;
            drawSpriteRaw(g_cine->_textHandler.textTable[idx][FONT_DATA], g_cine->_textHandler.textTable[idx][FONT_MASK], FONT_WIDTH, FONT_HEIGHT, _backBuffer, x, y);
            x += width + 1;
      }

      return x;
}

/*! \brief Draw Line
 * \param x Line end coordinate
 * \param y Line end coordinate
 * \param width Horizontal line length
 * \param height Vertical line length
 * \param color Line color
 * \note Either width or height must be equal to 1
 */
00392 void FWRenderer::drawLine(int x, int y, int width, int height, byte color) {
      // this line is a special case of rectangle ;-)
      drawPlainBox(x, y, width, height, color);
}

/*! \brief Hide invisible parts of the sprite
 * \param[in,out] mask Mask to be updated
 * \param it Overlay info from overlayList
 */
00401 void FWRenderer::remaskSprite(byte *mask, Common::List<overlay>::iterator it) {
      AnimData &sprite = animDataTable[objectTable[it->objIdx].frame];
      int x, y, width, height, idx;
      int mx, my, mw, mh;

      x = objectTable[it->objIdx].x;
      y = objectTable[it->objIdx].y;
      width = sprite._realWidth;
      height = sprite._height;

      for (++it; it != overlayList.end(); ++it) {
            if (it->type != 5) {
                  continue;
            }

            idx = ABS(objectTable[it->objIdx].frame);
            mx = objectTable[it->objIdx].x;
            my = objectTable[it->objIdx].y;
            mw = animDataTable[idx]._realWidth;
            mh = animDataTable[idx]._height;

            gfxUpdateSpriteMask(mask, x, y, width, height, animDataTable[idx].data(), mx, my, mw, mh);
      }
}

/*! \brief Draw background to backbuffer
 */
00428 void FWRenderer::drawBackground() {
      assert(_background);
      memcpy(_backBuffer, _background, _screenSize);
}

/*! \brief Draw one overlay
 * \param it Overlay info
 */
00436 void FWRenderer::renderOverlay(const Common::List<overlay>::iterator &it) {
      int idx, len, width;
      objectStruct *obj;
      AnimData *sprite;
      byte *mask;

      switch (it->type) {
      // color sprite
      case 0:
            if (objectTable[it->objIdx].frame < 0) {
                  return;
            }
            sprite = &animDataTable[objectTable[it->objIdx].frame];
            len = sprite->_realWidth * sprite->_height;
            mask = new byte[len];
            memcpy(mask, sprite->mask(), len);
            remaskSprite(mask, it);
            drawMaskedSprite(objectTable[it->objIdx], mask);
            delete[] mask;
            break;

      // game message
      case 2:
            if (it->objIdx >= messageTable.size()) {
                  return;
            }

            _messageLen += messageTable[it->objIdx].size();
            drawMessage(messageTable[it->objIdx].c_str(), it->x, it->y, it->width, it->color);
            waitForPlayerClick = 1;
            break;

      // action failure message
      case 3:
            idx = it->objIdx * 4 + g_cine->_rnd.getRandomNumber(3);
            len = strlen(failureMessages[idx]);
            _messageLen += len;
            width = 6 * len + 20;
            width = width > 300 ? 300 : width;

            drawMessage(failureMessages[idx], (320 - width) / 2, 80, width, 4);
            waitForPlayerClick = 1;
            break;

      // bitmap
      case 4:
            assert(it->objIdx < NUM_MAX_OBJECT);
            obj = &objectTable[it->objIdx];

            if (obj->frame < 0) {
                  return;
            }

            if (!animDataTable[obj->frame].data()) {
                  return;
            }

            fillSprite(*obj);
            break;
      }
}

/*! \brief Draw overlays
 */
00500 void FWRenderer::drawOverlays() {
      Common::List<overlay>::iterator it;

      for (it = overlayList.begin(); it != overlayList.end(); ++it) {
            renderOverlay(it);
      }
}

/*! \brief Draw another frame
 */
00510 void FWRenderer::drawFrame() {
      drawBackground();
      drawOverlays();

      if (!_cmd.empty()) {
            drawCommand();
      }

      if (_changePal) {
            refreshPalette();
      }

      blit();
}

/*!
 * \brief Turn on or off the showing of the collision page.
 * If turned on the blitting routine shows the collision page instead of the back buffer.
 * \note Useful for debugging collision page related problems.
 */
00530 void FWRenderer::showCollisionPage(bool state) {
      _showCollisionPage = state;
}

/*! \brief Update screen
 */
00536 void FWRenderer::blit() {
      // Show the back buffer or the collision page. Normally the back
      // buffer but showing the collision page is useful for debugging.
      byte *source = (_showCollisionPage ? collisionPage : _backBuffer);
      g_system->copyRectToScreen(source, 320, 0, 0, 320, 200);
}

/*! \brief Set player command string
 * \param cmd New command string
 */
00546 void FWRenderer::setCommand(Common::String cmd) {
      _cmd = cmd;
}

/*! \brief Refresh current palette
 */
00552 void FWRenderer::refreshPalette() {
      int i;
      byte pal[16*4];

      assert(_activeLowPal);

      for (i = 0; i < 16; i++) {
            pal[i * 4 + 2] = ((_activeLowPal[i] & 0x007) >> 0) * 32;
            pal[i * 4 + 1] = ((_activeLowPal[i] & 0x070) >> 4) * 32;
            pal[i * 4 + 0] = ((_activeLowPal[i] & 0x700) >> 8) * 32;
            pal[i * 4 + 3] = 0;
      }

      g_system->setPalette(pal, 0, 16);
      _changePal = 0;
}

/*! \brief Load palette of current background
 */
00571 void FWRenderer::reloadPalette() {
      assert(_palette);

      if (!_activeLowPal) {
            _activeLowPal = new uint16[_lowPalSize];
      }

      assert(_activeLowPal);

      memcpy(_activeLowPal, _palette, _lowPalSize * sizeof (uint16));
      _changePal = 1;
}

/*! \brief Load background into renderer
 * \param bg Raw background data
 */
00587 void FWRenderer::loadBg16(const byte *bg, const char *name, unsigned int idx) {
      assert(idx == 0);
      int i;

      if (!_background) {
            _background = new byte[_screenSize];
      }

      if (!_palette) {
            _palette = new uint16[_lowPalSize];
      }

      assert(_background && _palette);

      strcpy(_bgName, name);

      for (i = 0; i < _lowPalSize; i++, bg += 2) {
            _palette[i] = READ_BE_UINT16(bg);
      }

      gfxConvertSpriteToRaw(_background, bg, 160, 200);
}

/*! \brief Placeholder for Operation Stealth implementation
 */
00612 void FWRenderer::loadCt16(const byte *ct, const char *name) {
      error("Future Wars renderer doesn't support multiple backgrounds");
}

/*! \brief Placeholder for Operation Stealth implementation
 */
00618 void FWRenderer::loadBg256(const byte *bg, const char *name, unsigned int idx) {
      error("Future Wars renderer doesn't support multiple backgrounds");
}

/*! \brief Placeholder for Operation Stealth implementation
 */
00624 void FWRenderer::loadCt256(const byte *ct, const char *name) {
      error("Future Wars renderer doesn't support multiple backgrounds");
}

/*! \brief Placeholder for Operation Stealth implementation
 */
00630 void FWRenderer::selectBg(unsigned int idx) {
      error("Future Wars renderer doesn't support multiple backgrounds");
}

/*! \brief Placeholder for Operation Stealth implementation
 */
00636 void FWRenderer::selectScrollBg(unsigned int idx) {
      error("Future Wars renderer doesn't support multiple backgrounds");
}

/*! \brief Placeholder for Operation Stealth implementation
 */
00642 void FWRenderer::setScroll(unsigned int shift) {
      error("Future Wars renderer doesn't support multiple backgrounds");
}

/*! \brief Future Wars has no scrolling backgrounds so scroll value is always zero.
 */
00648 uint FWRenderer::getScroll() const {
      return 0;
}

/*! \brief Placeholder for Operation Stealth implementation
 */
00654 void FWRenderer::removeBg(unsigned int idx) {
      error("Future Wars renderer doesn't support multiple backgrounds");
}

void FWRenderer::saveBgNames(Common::OutSaveFile &fHandle) {
      fHandle.write(_bgName, 13);
}

const char *FWRenderer::getBgName(uint idx) const {
      assert(idx == 0);
      return _bgName;
}

/*! \brief Restore active and backup palette from save
 * \param fHandle Savefile open for reading
 */
00670 void FWRenderer::restorePalette(Common::SeekableReadStream &fHandle) {
      int i;

      if (!_palette) {
            _palette = new uint16[_lowPalSize];
      }

      if (!_activeLowPal) {
            _activeLowPal = new uint16[_lowPalSize];
      }

      assert(_palette && _activeLowPal);

      for (i = 0; i < _lowPalSize; i++) {
            _activeLowPal[i] = fHandle.readUint16BE();
      }

      for (i = 0; i < _lowPalSize; i++) {
            _palette[i] = fHandle.readUint16BE();
      }

      _changePal = 1;
}

/*! \brief Write active and backup palette to save
 * \param fHandle Savefile open for writing
 */
00697 void FWRenderer::savePalette(Common::OutSaveFile &fHandle) {
      int i;

      assert(_palette && _activeLowPal);

      for (i = 0; i < _lowPalSize; i++) {
            fHandle.writeUint16BE(_activeLowPal[i]);
      }

      for (i = 0; i < _lowPalSize; i++) {
            fHandle.writeUint16BE(_palette[i]);
      }
}

/*! \brief Write active and backup palette to save
 * \param fHandle Savefile open for writing
 */
00714 void OSRenderer::savePalette(Common::OutSaveFile &fHandle) {
      int i;

      assert(_activeHiPal);

      // Write the active 256 color palette.
      for (i = 0; i < _hiPalSize; i++) {
            fHandle.writeByte(_activeHiPal[i]);
      }

      // Write the active 256 color palette a second time.
      // FIXME: The backup 256 color palette should be saved here instead of the active one.
      for (i = 0; i < _hiPalSize; i++) {
            fHandle.writeByte(_activeHiPal[i]);
      }
}

/*! \brief Restore active and backup palette from save
 * \param fHandle Savefile open for reading
 */
00734 void OSRenderer::restorePalette(Common::SeekableReadStream &fHandle) {
      int i;

      if (!_activeHiPal) {
            _activeHiPal = new byte[_hiPalSize];
      }

      assert(_activeHiPal);

      for (i = 0; i < _hiPalSize; i++) {
            _activeHiPal[i] = fHandle.readByte();
      }

      // Jump over the backup 256 color palette.
      // FIXME: Load the backup 256 color palette and use it properly.
      fHandle.seek(_hiPalSize, SEEK_CUR);

      _changePal = 1;
}

/*! \brief Rotate active palette
 * \param a First color to rotate
 * \param b Last color to rotate
 * \param c Possibly rotation step, must be equal to 1 at the moment
 */
00759 void FWRenderer::rotatePalette(int a, int b, int c) {
      palRotate(_activeLowPal, a, b, c);
      refreshPalette();
}

/*! \brief Copy part of backup palette to active palette and transform
 * \param first First color to transform
 * \param last Last color to transform
 * \param r Red channel transformation
 * \param g Green channel transformation
 * \param b Blue channel transformation
 */
00771 void FWRenderer::transformPalette(int first, int last, int r, int g, int b) {
      if (!_activeLowPal) {
            _activeLowPal = new uint16[_lowPalSize];
            memset(_activeLowPal, 0, _lowPalSize * sizeof (uint16));
      }

      transformPaletteRange(_activeLowPal, _palette, first, last, r, g, b);
      refreshPalette();
}

/*! \brief Draw menu box, one item per line with possible highlight
 * \param items Menu items
 * \param height Item count
 * \param x Top left menu corner coordinate
 * \param y Top left menu corner coordinate
 * \param width Menu box width
 * \param selected Index of highlighted item (no highlight if less than 0)
 */
00789 void FWRenderer::drawMenu(const CommandeType *items, unsigned int height, int x, int y, int width, int selected) {
      int tx, ty, th = height * 9 + 10;
      unsigned int i, j;

      if (x + width > 319) {
            x = 319 - width;
      }

      if (y + th > 199) {
            y = 199 - th;
      }

      drawPlainBox(x, y, width, 4, _messageBg);

      ty = y + 4;

      for (i = 0; i < height; i++, ty += 9) {
            drawPlainBox(x, ty, width, 9, (int)i == selected ? 0 : _messageBg);
            tx = x + 4;

            for (j = 0; items[i][j]; j++) {
                  tx = drawChar(items[i][j], tx, ty);
            }
      }

      drawPlainBox(x, ty, width, 4, _messageBg);
      drawDoubleBorder(x, y, width, ty - y + 4, 2);
}

/*! \brief Draw text input box
 * \param info Input box message
 * \param input Text entered in the input area
 * \param cursor Cursor position in the input area
 * \param x Top left input box corner coordinate
 * \param y Top left input box corner coordinate
 * \param width Input box width
 */
00826 void FWRenderer::drawInputBox(const char *info, const char *input, int cursor, int x, int y, int width) {
      int i, tx, ty, tw;
      int line = 0, words = 0, cw = 0;
      int space = 0, extraSpace = 0;

      drawPlainBox(x, y, width, 4, _messageBg);
      tx = x + 4;
      ty = info[0] ? y - 5 : y + 4;
      tw = width - 8;

      // input box info message
      for (i = 0; info[i]; i++, line--) {
            // fit line of text
            if (!line) {
                  line = fitLine(info + i, tw, words, cw);

                  if ( info[i + line] != '\0' && words) {
                        space = (tw - cw) / words;
                        extraSpace = (tw - cw) % words;
                  } else {
                        space = 5;
                        extraSpace = 0;
                  }

                  ty += 9;
                  drawPlainBox(x, ty, width, 9, _messageBg);
                  tx = x + 4;
            }

            // draw characters
            if (info[i] == ' ') {
                  tx += space + extraSpace;

                  if (extraSpace) {
                        extraSpace = 0;
                  }
            } else {
                  tx = drawChar(info[i], tx, ty);
            }
      }

      // input area background
      ty += 9;
      drawPlainBox(x, ty, width, 9, _messageBg);
      drawPlainBox(x + 16, ty - 1, width - 32, 9, 0);
      tx = x + 20;

      // text in input area
      for (i = 0; input[i]; i++) {
            tx = drawChar(input[i], tx, ty);

            if (cursor == i + 2) {
                  drawLine(tx, ty - 1, 1, 9, 2);
            }
      }

      if (!input[0] || cursor == 1) {
            drawLine(x + 20, ty - 1, 1, 9, 2);
      }

      ty += 9;
      drawPlainBox(x, ty, width, 4, _messageBg);
      drawDoubleBorder(x, y, width, ty - y + 4, 2);
}

/*! \brief Fade to black
 */
00893 void FWRenderer::fadeToBlack() {
      // FIXME: _activeLowPal is invalid when starting Operation Stealth
      // Adding this sanity check fixes a crash when the game
      // starts, but I'm not sure if this is the best place to check it
      if (!_activeLowPal) {
            warning("_activeLowPal is invalid");
            return;
      }

      assert(_activeLowPal);

      for (int i = 0; i < 8; i++) {
            for (int j = 0; j < 16; j++) {
                  _activeLowPal[j] = transformColor(_activeLowPal[j], -1, -1, -1);
            }

            refreshPalette();
            g_system->updateScreen();
            g_system->delayMillis(50);
      }
}

/*! \brief Initialize Operation Stealth renderer
 */
00917 OSRenderer::OSRenderer() : _activeHiPal(NULL), _currentBg(0), _scrollBg(0),
      _bgShift(0) {

      int i;
      for (i = 0; i < 9; i++) {
            _bgTable[i].bg = NULL;
            _bgTable[i].lowPal = NULL;
            _bgTable[i].hiPal = NULL;
            memset(_bgTable[i].name, 0, sizeof (_bgTable[i].name));
      }
}

/*! \brief Destroy Operation Stealth renderer
 */
00931 OSRenderer::~OSRenderer() {
      delete[] _activeHiPal;

      for (int i = 0; i < 9; i++) {
            delete[] _bgTable[i].bg;
            delete[] _bgTable[i].lowPal;
            delete[] _bgTable[i].hiPal;
      }
}

/*! \brief Reset Operation Stealth renderer state
 */
00943 void OSRenderer::clear() {
      delete[] _activeHiPal;
      _activeHiPal = NULL;

      for (int i = 0; i < 9; i++) {
            delete[] _bgTable[i].bg;
            delete[] _bgTable[i].lowPal;
            delete[] _bgTable[i].hiPal;

            _bgTable[i].bg = NULL;
            _bgTable[i].lowPal = NULL;
            _bgTable[i].hiPal = NULL;
            memset(_bgTable[i].name, 0, sizeof (_bgTable[i].name));
      }

      _currentBg = 0;
      _scrollBg = 0;
      _bgShift = 0;

      FWRenderer::clear();
}

/*! \brief Draw 1bpp sprite using selected color on backgrounds
 * \param obj Object info
 * \param fillColor Sprite color
 */
00969 void OSRenderer::incrustMask(const objectStruct &obj, uint8 color) {
      const byte *data = animDataTable[obj.frame].data();
      int x, y, width, height;

      x = obj.x;
      y = obj.y;
      width = animDataTable[obj.frame]._realWidth;
      height = animDataTable[obj.frame]._height;

      if (_bgTable[_currentBg].bg) {
            gfxFillSprite(data, width, height, _bgTable[_currentBg].bg, x, y, color);
      }
}

/*! \brief Draw color sprite
 * \param obj Object info
 */
00986 void OSRenderer::drawSprite(const objectStruct &obj) {
      const byte *data = animDataTable[obj.frame].data();
      int x, y, width, height, transColor;

      x = obj.x;
      y = obj.y;
      transColor = obj.part;
      width = animDataTable[obj.frame]._realWidth;
      height = animDataTable[obj.frame]._height;

      drawSpriteRaw2(data, transColor, width, height, _backBuffer, x, y);
}

/*! \brief Draw color sprite
 * \param obj Object info
 */
01002 void OSRenderer::incrustSprite(const objectStruct &obj) {
      const byte *data = animDataTable[obj.frame].data();
      int x, y, width, height, transColor;

      x = obj.x;
      y = obj.y;
      transColor = obj.part;
      width = animDataTable[obj.frame]._realWidth;
      height = animDataTable[obj.frame]._height;

      if (_bgTable[_currentBg].bg) {
            drawSpriteRaw2(data, transColor, width, height, _bgTable[_currentBg].bg, x, y);
      }
}

/*! \brief Draw text character on screen
 * \param character Character to draw
 * \param x Character coordinate
 * \param y Character coordinate
 */
01022 int OSRenderer::drawChar(char character, int x, int y) {
      int width, idx;

      if (character == ' ') {
            x += 5;
      } else if ((width = g_cine->_textHandler.fontParamTable[(unsigned char)character].characterWidth)) {
            idx = g_cine->_textHandler.fontParamTable[(unsigned char)character].characterIdx;
            drawSpriteRaw2(g_cine->_textHandler.textTable[idx][FONT_DATA], 0, FONT_WIDTH, FONT_HEIGHT, _backBuffer, x, y);
            x += width + 1;
      }

      return x;
}

/*! \brief Draw background to backbuffer
 */
01038 void OSRenderer::drawBackground() {
      byte *main;

      main = _bgTable[_currentBg].bg;
      assert(main);

      if (!_bgShift) {
            memcpy(_backBuffer, main, _screenSize);
      } else {
            byte *scroll = _bgTable[_scrollBg].bg;
            int mainShift = _bgShift * _screenWidth;
            int mainSize = _screenSize - mainShift;

            assert(scroll);

            if (mainSize > 0) { // Just a precaution
                  memcpy(_backBuffer, main + mainShift, mainSize);
            }
            if (mainShift > 0) { // Just a precaution
                  memcpy(_backBuffer + mainSize, scroll, mainShift);
            }
      }
}

/*! \brief Draw one overlay
 * \param it Overlay info
 * \todo Add handling of type 22 overlays
 */
01066 void OSRenderer::renderOverlay(const Common::List<overlay>::iterator &it) {
      int len, idx, width, height;
      objectStruct *obj;
      AnimData *sprite;
      byte *mask;
      byte color;

      switch (it->type) {
      // color sprite
      case 0:
            if (objectTable[it->objIdx].frame < 0) {
                  break;
            }
            sprite = &animDataTable[objectTable[it->objIdx].frame];
            len = sprite->_realWidth * sprite->_height;
            mask = new byte[len];
            generateMask(sprite->data(), mask, len, objectTable[it->objIdx].part);
            remaskSprite(mask, it);
            drawMaskedSprite(objectTable[it->objIdx], mask);
            delete[] mask;
            break;

      // game message
      case 2:
            if (it->objIdx >= messageTable.size()) {
                  return;
            }

            _messageLen += messageTable[it->objIdx].size();
            drawMessage(messageTable[it->objIdx].c_str(), it->x, it->y, it->width, it->color);
            if (it->color >= 0) { // This test isn't in Future Wars's implementation
                  waitForPlayerClick = 1;
            }
            break;

      // action failure message
      case 3:
            idx = it->objIdx * 4 + g_cine->_rnd.getRandomNumber(3);
            len = strlen(failureMessages[idx]);
            _messageLen += len;
            width = 6 * len + 20;
            width = width > 300 ? 300 : width;

            // The used color here differs from Future Wars
            drawMessage(failureMessages[idx], (320 - width) / 2, 80, width, _messageBg);
            waitForPlayerClick = 1;
            break;

      // bitmap
      case 4:
            if (objectTable[it->objIdx].frame >= 0) {
                  FWRenderer::renderOverlay(it);
            }
            break;

      // masked background
      case 20:
            assert(it->objIdx < NUM_MAX_OBJECT);
            var5 = it->x; // A global variable updated here!
            obj = &objectTable[it->objIdx];
            sprite = &animDataTable[obj->frame];

            if (obj->frame < 0 || it->x < 0 || it->x > 8 || !_bgTable[it->x].bg || sprite->_bpp != 1) {
                  break;
            }

            maskBgOverlay(_bgTable[it->x].bg, sprite->data(), sprite->_realWidth, sprite->_height, _backBuffer, obj->x, obj->y);
            break;

      // FIXME: Implement correct drawing of type 21 overlays.
      // Type 21 overlays aren't just filled rectangles, I found their drawing routine
      // from Operation Stealth's drawSprite routine. So they're likely some kind of sprites
      // and it's just a coincidence that the oxygen meter during the first arcade sequence
      // works even somehow currently. I tried the original under DOSBox and the oxygen gauge
      // is a long red bar that gets shorter as the air runs out.
      case 21:
      // A filled rectangle:
      case 22:
            // TODO: Check it this implementation really works correctly (Some things might be wrong, needs testing).
            assert(it->objIdx < NUM_MAX_OBJECT);
            obj = &objectTable[it->objIdx];
            color = obj->part & 0x0F;
            width = obj->frame;
            height = obj->costume;
            drawPlainBox(obj->x, obj->y, width, height, color);
            debug(5, "renderOverlay: type=%d, x=%d, y=%d, width=%d, height=%d, color=%d",
                  it->type, obj->x, obj->y, width, height, color);
            break;

      // something else
      default:
            FWRenderer::renderOverlay(it);
            break;
      }
}

/*! \brief Refresh current palette
 */
01164 void OSRenderer::refreshPalette() {
      if (!_activeHiPal) {
            FWRenderer::refreshPalette();
            return;
      }

      int i;
      byte pal[256*4];

      for (i = 0; i < 256; i++) {
            pal[i * 4 + 0] = _activeHiPal[i * 3 + 0];
            pal[i * 4 + 1] = _activeHiPal[i * 3 + 1];
            pal[i * 4 + 2] = _activeHiPal[i * 3 + 2];
            pal[i * 4 + 3] = 0;
      }

      g_system->setPalette(pal, 0, 256);
      _changePal = 0;
}

/*! \brief Load palette of current background
 */
01186 void OSRenderer::reloadPalette() {
      // selected background in plane takeoff scene has swapped colors 12
      // and 14, shift background has it right
      palBg *bg = _bgShift ? &_bgTable[_scrollBg] : &_bgTable[_currentBg];

      assert(bg->lowPal || bg->hiPal);

      if (bg->lowPal) {
            if (!_activeLowPal) {
                  _activeLowPal = new uint16[_lowPalSize];
            }

            assert(_activeLowPal);

            delete[] _activeHiPal;
            _activeHiPal = NULL;

            memcpy(_activeLowPal, bg->lowPal, _lowPalSize * sizeof (uint16));
      } else {
            if (!_activeHiPal) {
                  _activeHiPal = new byte[_hiPalSize];
            }

            assert(_activeHiPal);

            delete[] _activeLowPal;
            _activeLowPal = NULL;

            memcpy(_activeHiPal, bg->hiPal, _hiPalSize);
      }
      _changePal = 1;
}

/*! \brief Rotate active palette
 * \param a First color to rotate
 * \param b Last color to rotate
 * \param c Possibly rotation step, must be equal to 1 at the moment
 */
01224 void OSRenderer::rotatePalette(int a, int b, int c) {
      if (_activeLowPal) {
            FWRenderer::rotatePalette(a, b, c);
            return;
      }

      palRotate(_activeHiPal, a, b, c);
      refreshPalette();
}

/*! \brief Copy part of backup palette to active palette and transform
 * \param first First color to transform
 * \param last Last color to transform
 * \param r Red channel transformation
 * \param g Green channel transformation
 * \param b Blue channel transformation
 */
01241 void OSRenderer::transformPalette(int first, int last, int r, int g, int b) {
      palBg *bg = _bgShift ? &_bgTable[_scrollBg] : &_bgTable[_currentBg];

      if (!bg->lowPal) {
            if (!_activeHiPal) {
                  _activeHiPal = new byte[_hiPalSize];
                  memset(_activeHiPal, 0, _hiPalSize);
            }

            delete[] _activeLowPal;
            _activeLowPal = NULL;

            transformPaletteRange(_activeHiPal, bg->hiPal, first, last, r, g, b);
      } else {
            if (!_activeLowPal) {
                  _activeLowPal = new uint16[_lowPalSize];
                  memset(_activeLowPal, 0, _lowPalSize * sizeof (uint16));
            }

            delete[] _activeHiPal;
            _activeHiPal = NULL;

            transformPaletteRange(_activeLowPal, bg->lowPal, first, last, r, g, b);
      }

      refreshPalette();
}

/*! \brief Load 16 color background into renderer
 * \param bg Raw background data
 * \param name Background filename
 * \param pos Background index
 */
01274 void OSRenderer::loadBg16(const byte *bg, const char *name, unsigned int idx) {
      int i;
      assert(idx < 9);

      if (!_bgTable[idx].bg) {
            _bgTable[idx].bg = new byte[_screenSize];
      }

      if (!_bgTable[idx].lowPal) {
            _bgTable[idx].lowPal = new uint16[_lowPalSize];
      }

      assert(_bgTable[idx].bg && _bgTable[idx].lowPal);

      delete[] _bgTable[idx].hiPal;
      _bgTable[idx].hiPal = NULL;

      strcpy(_bgTable[idx].name, name);

      for (i = 0; i < _lowPalSize; i++, bg += 2) {
            _bgTable[idx].lowPal[i] = READ_BE_UINT16(bg);
      }

      gfxConvertSpriteToRaw(_bgTable[idx].bg, bg, 160, 200);
}

/*! \brief Load 16 color CT data as background into renderer
 * \param ct Raw CT data
 * \param name Background filename
 */
01304 void OSRenderer::loadCt16(const byte *ct, const char *name) {
      loadBg16(ct, name, 8);
}

/*! \brief Load 256 color background into renderer
 * \param bg Raw background data
 * \param name Background filename
 * \param pos Background index
 */
01313 void OSRenderer::loadBg256(const byte *bg, const char *name, unsigned int idx) {
      assert(idx < 9);

      if (!_bgTable[idx].bg) {
            _bgTable[idx].bg = new byte[_screenSize];
      }

      if (!_bgTable[idx].hiPal) {
            _bgTable[idx].hiPal = new byte[_hiPalSize];
      }

      assert(_bgTable[idx].bg && _bgTable[idx].hiPal);

      delete[] _bgTable[idx].lowPal;
      _bgTable[idx].lowPal = NULL;

      strcpy(_bgTable[idx].name, name);
      memcpy(_bgTable[idx].hiPal, bg, _hiPalSize);
      memcpy(_bgTable[idx].bg, bg + _hiPalSize, _screenSize);
}

/*! \brief Load 256 color CT data as background into renderer
 * \param ct Raw CT data
 * \param name Background filename
 */
01338 void OSRenderer::loadCt256(const byte *ct, const char *name) {
      loadBg256(ct, name, 8);
}

/*! \brief Select active background and load its palette
 * \param idx Background index
 */
01345 void OSRenderer::selectBg(unsigned int idx) {
      assert(idx < 9 && _bgTable[idx].bg);
      assert(_bgTable[idx].lowPal || _bgTable[idx].hiPal);

      _currentBg = idx;
      reloadPalette();
}

/*! \brief Select scroll background
 * \param idx Scroll background index
 */
01356 void OSRenderer::selectScrollBg(unsigned int idx) {
      assert(idx < 9);

      if (_bgTable[idx].bg) {
            _scrollBg = idx;
      }
      reloadPalette();
}

/*! \brief Set background scroll
 * \param shift Background scroll in pixels
 */
01368 void OSRenderer::setScroll(unsigned int shift) {
      assert(shift <= 200);

      _bgShift = shift;
}

/*! \brief Get background scroll
 * \return Background scroll in pixels
 */
01377 uint OSRenderer::getScroll() const {
      return _bgShift;
}

/*! \brief Unload background from renderer
 * \param idx Background to unload
 */
01384 void OSRenderer::removeBg(unsigned int idx) {
      assert(idx > 0 && idx < 9);

      if (_currentBg == idx) {
            _currentBg = 0;
      }

      if (_scrollBg == idx) {
            _scrollBg = 0;
      }

      delete[] _bgTable[idx].bg;
      delete[] _bgTable[idx].lowPal;
      delete[] _bgTable[idx].hiPal;
      _bgTable[idx].bg = NULL;
      _bgTable[idx].lowPal = NULL;
      _bgTable[idx].hiPal = NULL;
      memset(_bgTable[idx].name, 0, sizeof (_bgTable[idx].name));
}

void OSRenderer::saveBgNames(Common::OutSaveFile &fHandle) {
      for (int i = 0; i < 8; i++) {
            fHandle.write(_bgTable[i].name, 13);
      }
}

const char *OSRenderer::getBgName(uint idx) const {
      assert(idx < 9);
      return _bgTable[idx].name;
}

/*! \brief Fade to black
 * \bug Operation Stealth sometimes seems to fade to black using
 * transformPalette resulting in double fadeout
 */
01419 void OSRenderer::fadeToBlack() {
      if (!_activeHiPal) {
            FWRenderer::fadeToBlack();
            return;
      }

      for (int i = 0; i < 8; i++) {
            for (int j = 0; j < _hiPalSize; j++) {
                  _activeHiPal[j] = CLIP(_activeHiPal[j] - 32, 0, 255);
            }

            refreshPalette();
            g_system->updateScreen();
            g_system->delayMillis(50);
      }
}

void setMouseCursor(int cursor) {
      static int currentMouseCursor = -1;
      assert(cursor >= 0 && cursor < 3);
      if (currentMouseCursor != cursor) {
            byte mouseCursor[16 * 16];
            const MouseCursor *mc = &mouseCursors[cursor];
            const byte *src = mc->bitmap;
            for (int i = 0; i < 32; ++i) {
                  int offs = i * 8;
                  for (byte mask = 0x80; mask != 0; mask >>= 1) {
                        if (src[0] & mask) {
                              mouseCursor[offs] = 1;
                        } else if (src[32] & mask) {
                              mouseCursor[offs] = 0;
                        } else {
                              mouseCursor[offs] = 0xFF;
                        }
                        ++offs;
                  }
                  ++src;
            }
            CursorMan.replaceCursor(mouseCursor, 16, 16, mc->hotspotX, mc->hotspotY);
            CursorMan.replaceCursorPalette(cursorPalette, 0, 2);
            currentMouseCursor = cursor;
      }
}

void gfxFillSprite(const byte *spritePtr, uint16 width, uint16 height, byte *page, int16 x, int16 y, uint8 fillColor) {
      int16 i, j;

      for (i = 0; i < height; i++) {
            byte *destPtr = page + x + y * 320;
            destPtr += i * 320;

            for (j = 0; j < width; j++) {
                  if (x + j >= 0 && x + j < 320 && i + y >= 0 && i + y < 200 && !*spritePtr) {
                        *destPtr = fillColor;
                  }

                  destPtr++;
                  spritePtr++;
            }
      }
}

void gfxDrawMaskedSprite(const byte *spritePtr, const byte *maskPtr, uint16 width, uint16 height, byte *page, int16 x, int16 y) {
      int16 i, j;

      for (i = 0; i < height; i++) {
            byte *destPtr = page + x + y * 320;
            destPtr += i * 320;

            for (j = 0; j < width; j++) {
                  if (x + j >= 0 && x + j < 320 && i + y >= 0 && i + y < 200 && *maskPtr == 0) {
                        *destPtr = *spritePtr;
                  }
                  ++destPtr;
                  ++spritePtr;
                  ++maskPtr;
            }
      }
}

void gfxUpdateSpriteMask(byte *destMask, int16 x, int16 y, int16 width, int16 height, const byte *srcMask, int16 xm, int16 ym, int16 maskWidth, int16 maskHeight) {
      int16 i, j, d, spritePitch, maskPitch;

      spritePitch = width;
      maskPitch = maskWidth;

      // crop update area to overlapping parts of masks
      if (y > ym) {
            d = y - ym;
            srcMask += d * maskPitch;
            maskHeight -= d;
      } else if (y < ym) {
            d = ym - y;
            destMask += d * spritePitch;
            height -= d;
      }

      if (x > xm) {
            d = x - xm;
            srcMask += d;
            maskWidth -= d;
      } else if (x < xm) {
            d = xm - x;
            destMask += d;
            width -= d;
      }

      // update mask
      for (j = 0; j < MIN(maskHeight, height); ++j) {
            for (i = 0; i < MIN(maskWidth, width); ++i) {
                  destMask[i] |= srcMask[i] ^ 1;
            }
            destMask += spritePitch;
            srcMask += maskPitch;
      }
}

void gfxUpdateIncrustMask(byte *destMask, int16 x, int16 y, int16 width, int16 height, const byte *srcMask, int16 xm, int16 ym, int16 maskWidth, int16 maskHeight) {
      int16 i, j, d, spritePitch, maskPitch;

      spritePitch = width;
      maskPitch = maskWidth;

      // crop update area to overlapping parts of masks
      if (y > ym) {
            d = y - ym;
            srcMask += d * maskPitch;
            maskHeight -= d;
      } else if (y < ym) {
            d = ym - y > height ? height : ym - y;
            memset(destMask, 1, d * spritePitch);
            destMask += d * spritePitch;
            height -= d;
      }

      if (x > xm) {
            d = x - xm;
            xm = x;
            srcMask += d;
            maskWidth -= d;
      }

      d = xm - x;
      maskWidth += d;

      // update mask
      for (j = 0; j < MIN(maskHeight, height); ++j) {
            for (i = 0; i < width; ++i) {
                  destMask[i] |= i < d || i >= maskWidth ? 1 : srcMask[i - d];
            }
            destMask += spritePitch;
            srcMask += maskPitch;
      }

      if (j < height) {
            memset(destMask, 1, (height - j) * spritePitch);
      }
}

void gfxDrawLine(int16 x1, int16 y1, int16 x2, int16 y2, byte color, byte *page) {
      if (x1 == x2) {
            if (y1 > y2) {
                  SWAP(y1, y2);
            }
            while (y1 <= y2) {
                  *(page + (y1 * 320 + x1)) = color;
                  y1++;
            }
      } else {
            if (x1 > x2) {
                  SWAP(x1, x2);
            }
            while (x1 <= x2) {
                  *(page + (y1 * 320 + x1)) = color;
                  x1++;
            }
      }

}

void gfxDrawPlainBoxRaw(int16 x1, int16 y1, int16 x2, int16 y2, byte color, byte *page) {
      int16 t;

      if (x1 > x2) {
            SWAP(x1, x2);
      }

      if (y1 > y2) {
            SWAP(y1, y2);
      }

      t = x1;
      while (y1 <= y2) {
            x1 = t;
            while (x1 <= x2) {
                  *(page + y1 * 320 + x1) = color;
                  x1++;
            }
            y1++;
      }
}

int16 gfxGetBit(int16 x, int16 y, const byte *ptr, int16 width) {
      const byte *ptrToData = (ptr) + y * width + x;

      if (x > width) {
            return 0;
      }

      if (*ptrToData) {
            return 0;
      }

      return 1;
}

void gfxResetRawPage(byte *pageRaw) {
      memset(pageRaw, 0, 320 * 200);
}

void gfxConvertSpriteToRaw(byte *dst, const byte *src, uint16 w, uint16 h) {
      // Output is 4 bits per pixel.
      // Pixels are in 16 pixel chunks (8 bytes of source per 16 pixels of output).
      // The source data is interleaved so that
      // 1st big-endian 16-bit value contains all bit position 0 values for 16 pixels,
      // 2nd big-endian 16-bit value contains all bit position 1 values for 16 pixels,
      // 3rd big-endian 16-bit value contains all bit position 2 values for 16 pixels,
      // 4th big-endian 16-bit value contains all bit position 3 values for 16 pixels.
      // 1st pixel's bits are in the 16th bits,
      // 2nd pixel's bits are in the 15th bits,
      // 3rd pixel's bits are in the 14th bits etc.
      for (int y = 0; y < h; ++y) {
            for (int x = 0; x < w / 8; ++x) {
                  for (int bit = 0; bit < 16; ++bit) {
                        uint8 color = 0;
                        for (int p = 0; p < 4; ++p) {
                              if (READ_BE_UINT16(src + p * 2) & (1 << (15 - bit))) {
                                    color |= 1 << p;
                              }
                        }
                        *dst++ = color;
                  }
                  src += 8;
            }
      }
}

void drawSpriteRaw(const byte *spritePtr, const byte *maskPtr, int16 width, int16 height, byte *page, int16 x, int16 y) {
      int16 i, j;

      // FIXME: Is it a bug if maskPtr == NULL?
      if (!maskPtr)
            warning("drawSpriteRaw: maskPtr == NULL");

      for (i = 0; i < height; i++) {
            byte *destPtr = page + x + y * 320;
            destPtr += i * 320;

            for (j = 0; j < width; j++) {
                  if ((!maskPtr || !(*maskPtr)) && x + j >= 0 && x + j < 320 && i + y >= 0 && i + y < 200) {
                        *(destPtr++) = *(spritePtr++);
                  } else {
                        destPtr++;
                        spritePtr++;
                  }

                  if (maskPtr)
                        maskPtr++;
            }
      }
}

void drawSpriteRaw2(const byte *spritePtr, byte transColor, int16 width, int16 height, byte *page, int16 x, int16 y) {
      int16 i, j;

      for (i = 0; i < height; i++) {
            byte *destPtr = page + x + y * 320;
            destPtr += i * 320;

            for (j = 0; j < width; j++) {
                  if ((*spritePtr != transColor) && (x + j >= 0 && x + j < 320 && i + y >= 0 && i + y < 200)) {
                        *destPtr = *spritePtr;
                  }
                  destPtr++;
                  spritePtr++;
            }
      }
}

void maskBgOverlay(const byte *bgPtr, const byte *maskPtr, int16 width, int16 height,
                           byte *page, int16 x, int16 y) {
      int16 i, j, tmpWidth, tmpHeight;
      Common::List<BGIncrust>::iterator it;
      byte *mask;
      const byte *backup = maskPtr;

      // background pass
      for (i = 0; i < height; i++) {
            byte *destPtr = page + x + y * 320;
            const byte *srcPtr = bgPtr + x + y * 320;
            destPtr += i * 320;
            srcPtr += i * 320;

            for (j = 0; j < width; j++) {
                  if ((!maskPtr || !(*maskPtr)) && (x + j >= 0
                              && x + j < 320 && i + y >= 0 && i + y < 200)) {
                        *destPtr = *srcPtr;
                  }

                  destPtr++;
                  srcPtr++;
                  maskPtr++;
            }
      }

      maskPtr = backup;

      // incrust pass
      for (it = bgIncrustList.begin(); it != bgIncrustList.end(); ++it) {
            tmpWidth = animDataTable[it->frame]._realWidth;
            tmpHeight = animDataTable[it->frame]._height;
            mask = (byte*)malloc(tmpWidth * tmpHeight);

            if (it->param == 0) {
                  generateMask(animDataTable[it->frame].data(), mask, tmpWidth * tmpHeight, it->part);
                  gfxUpdateIncrustMask(mask, it->x, it->y, tmpWidth, tmpHeight, maskPtr, x, y, width, height);
                  gfxDrawMaskedSprite(animDataTable[it->frame].data(), mask, tmpWidth, tmpHeight, page, it->x, it->y);
            } else {
                  memcpy(mask, animDataTable[it->frame].data(), tmpWidth * tmpHeight);
                  gfxUpdateIncrustMask(mask, it->x, it->y, tmpWidth, tmpHeight, maskPtr, x, y, width, height);
                  gfxFillSprite(mask, tmpWidth, tmpHeight, page, it->x, it->y);
            }

            free(mask);
      }
}

} // End of namespace Cine

Generated by  Doxygen 1.6.0   Back to index