mirror of
https://github.com/libretro/scummvm.git
synced 2024-12-15 06:08:35 +00:00
1014 lines
27 KiB
C++
1014 lines
27 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
|
|
* aint32 with this program; if not, write to the Free Software
|
|
*
|
|
*
|
|
* Based on the original sources
|
|
* Faery Tale II -- The Halls of the Dead
|
|
* (c) 1993-1996 The Wyrmkeep Entertainment Co.
|
|
*/
|
|
|
|
#include "saga2/saga2.h"
|
|
#include "saga2/gdraw.h"
|
|
#include "saga2/gblitter.h"
|
|
|
|
namespace Saga2 {
|
|
|
|
#define TempAlloc malloc
|
|
#define TempFree free
|
|
|
|
|
|
void gPort::setMap(gPixelMap *newmap, bool inverted) {
|
|
_map = newmap;
|
|
_clip = Rect16(0, 0, _map->_size.x, _map->_size.y);
|
|
|
|
// Added by Talin to support inverted maps
|
|
if (inverted) {
|
|
_baseRow = _map->_data + _map->bytes() - _map->_size.x;
|
|
_rowMod = -_map->_size.x;
|
|
} else {
|
|
_baseRow = _map->_data;
|
|
_rowMod = _map->_size.x;
|
|
}
|
|
}
|
|
|
|
/****** gdraw.cpp/gPort::setState *********************************
|
|
*
|
|
* NAME
|
|
* gPort::setState -- restore the state of a gPort
|
|
*
|
|
* SYNOPSIS
|
|
* port.setState( state );
|
|
*
|
|
* void gPort::setState( gPenState & );
|
|
*
|
|
* FUNCTION
|
|
* This function restores the state of a gPort from a gPenState
|
|
* record. The record should have been filled in by an earlier
|
|
* call to gPort::getState. These two functions are intended for
|
|
* situations where it is neccessary to save and restore the
|
|
* drawing state of a gPort.
|
|
*
|
|
* INPUTS
|
|
* state A reference to the saved state record.
|
|
*
|
|
* RESULT
|
|
* none
|
|
*
|
|
* SEE ALSO
|
|
* gPort class
|
|
* gPort::getState
|
|
*
|
|
**********************************************************************
|
|
*/
|
|
void gPort::setState(gPenState &state) {
|
|
setColor(state.fgPen);
|
|
setBgColor(state.bgPen);
|
|
setOutlineColor(state.olPen);
|
|
setShadowColor(state.shPen);
|
|
setMode((enum draw_modes) state.drawMode);
|
|
}
|
|
|
|
/****** gdraw.cpp/gPort::getState *********************************
|
|
*
|
|
* NAME
|
|
* gPort::getState -- save the drawing state of a gPort
|
|
*
|
|
* SYNOPSIS
|
|
* port.getState( state );
|
|
*
|
|
* void gPort::getState( gPenState & );
|
|
*
|
|
* FUNCTION
|
|
* This function saves the "state" of a gPort into a small record.
|
|
* The values saved are all of the pens and the drawing mode.
|
|
*
|
|
* When used in conjunction with gPort::setState, this allows you
|
|
* to save and restore the state of a gPort.
|
|
*
|
|
* INPUTS
|
|
* state A reference to the state record to be filled in.
|
|
*
|
|
* RESULT
|
|
* none
|
|
*
|
|
* SEE ALSO
|
|
* gPort class
|
|
* gPort::setState
|
|
*
|
|
**********************************************************************
|
|
*/
|
|
void gPort::getState(gPenState &state) {
|
|
state.fgPen = _fgPen;
|
|
state.bgPen = _bgPen;
|
|
state.olPen = _olPen;
|
|
state.shPen = _shPen;
|
|
state.drawMode = _drawMode;
|
|
}
|
|
|
|
/****** gdraw.cpp/gPort::fillRect *********************************
|
|
*
|
|
* NAME
|
|
* gPort::fillRect -- fill a rectangle with a solid color.
|
|
*
|
|
* SYNOPSIS
|
|
* port.fillRect( rect );
|
|
* port.fillRect( x, y, w, h );
|
|
*
|
|
* void gPort::fillRect( const Rect16 );
|
|
* void gPort::fillRect( int16, int16, int16, int16 );
|
|
*
|
|
* FUNCTION
|
|
* Fill a rectangular area with a solid color.
|
|
* drawModeComplement is supported; All other draw modes cause
|
|
* a solid filling of pixels.
|
|
*
|
|
* INPUTS
|
|
* x,y,w,h The coordinates of the rectangle to fill
|
|
*
|
|
* rect The rectangle to fill as a Rect16
|
|
*
|
|
* RESULT
|
|
* none
|
|
*
|
|
* NOTES
|
|
* This function is currently in C and uses memset (for the gPort
|
|
* version only -- the subclasses all have more optimized routines).
|
|
* The base class rectfill should probably also be optimized.
|
|
*
|
|
* SEE ALSO
|
|
* gPort class
|
|
*
|
|
**********************************************************************
|
|
*/
|
|
|
|
// Your basic rectfill operation
|
|
// REM: Probably better to recode this in assembly?
|
|
|
|
void gPort::fillRect(const Rect16 r) {
|
|
Rect16 sect;
|
|
|
|
sect = intersect(_clip, r); // intersect with clip rect
|
|
sect.x += _origin.x; // apply origin translate
|
|
sect.y += _origin.y;
|
|
|
|
if (!sect.empty()) { // if result is non-empty
|
|
uint8 *addr = _baseRow + sect.y * _rowMod + sect.x;
|
|
|
|
if (_drawMode == drawModeComplement) { // Complement drawing mode
|
|
for (int h = sect.height;
|
|
h > 0;
|
|
h--,
|
|
addr += _rowMod) {
|
|
uint16 w = sect.width;
|
|
uint8 *put = addr;
|
|
|
|
while (w--) *put++ ^= _fgPen;
|
|
}
|
|
} else {
|
|
_FillRect(addr, _rowMod, sect.width, sect.height, _fgPen);
|
|
/*
|
|
for (int h = sect.height;
|
|
h > 0;
|
|
h--,
|
|
addr += _rowMod)
|
|
{
|
|
memset( addr, _fgPen, sect.width );
|
|
}
|
|
*/
|
|
}
|
|
}
|
|
}
|
|
|
|
/****** gdraw.cpp/gPort::frameRect ********************************
|
|
*
|
|
* NAME gPort::frameRect
|
|
*
|
|
* SYNOPSIS
|
|
* port.frameRect( rect, thickness );
|
|
* port.frameRect( x, y, w, h, thickness );
|
|
*
|
|
* void gPort::frameRect( const Rect16, int16 );
|
|
* void gPort::frameRect( int16, int16, int16, int16, int16 );
|
|
*
|
|
* FUNCTION
|
|
* This function draws a frame around a rectangle. The frame is
|
|
* contained completely within the area of the rectangle. The
|
|
* thickness parameter specifies how many pixel thick the frame
|
|
* is -- The thickness increases towards the center of the rectangle.
|
|
*
|
|
* For example, if the rectangle has an X of 0 and a width of 100,
|
|
* and the frame is 10 pixels thick, then the left edge of the frame
|
|
* will span pixels 0-9 and the right edge pixels 90-99.
|
|
*
|
|
* INPUTS
|
|
* x,y,w,h The coordinates of the rectangle to frame
|
|
*
|
|
* rect The rectangle to frame as a Rect16
|
|
*
|
|
* thickness The thickness of the frame.
|
|
*
|
|
* RESULT
|
|
* none
|
|
*
|
|
* SEE ALSO
|
|
* gPort class
|
|
*
|
|
**********************************************************************
|
|
*/
|
|
void gPort::frameRect(const Rect16 r, int16 thick) {
|
|
int16 dThick = thick * 2;
|
|
|
|
if (dThick >= r.width || dThick >= r.height) {
|
|
fillRect(r);
|
|
} else {
|
|
fillRect(Rect16(r.x, r.y, r.width, thick));
|
|
fillRect(Rect16(r.x, r.y + r.height - thick, r.width, thick));
|
|
fillRect(Rect16(r.x, r.y + thick, thick, r.height - dThick));
|
|
fillRect(Rect16(r.x + r.width - thick, r.y + thick,
|
|
thick, r.height - dThick));
|
|
}
|
|
}
|
|
|
|
/****** gdraw.cpp/gPort::hLine ************************************
|
|
*
|
|
* NAME
|
|
* gPort::hLine -- optimized rendering of horizontal lines
|
|
*
|
|
* SYNOPSIS
|
|
* port.hLine( x, y, width );
|
|
*
|
|
* void gPort::hLine( int16, int16, int16 );
|
|
*
|
|
* FUNCTION
|
|
* This function draws a 1-pixel-thick horizontal line starting
|
|
* from the coordinates at (x,y) and extending for "width" pixels.
|
|
* drawModeComplement is supported.
|
|
*
|
|
* Calling this function is faster that the general "line" routine.
|
|
* It is used in many of the GTools bevel-drawing functions.
|
|
*
|
|
* INPUTS
|
|
* x,y The left edge of the line
|
|
*
|
|
* width width of the line in pixels.
|
|
*
|
|
* RESULT
|
|
* none
|
|
*
|
|
* NOTES
|
|
* This function needs to be downcoded to assembly.
|
|
* Note: The VGA and SVGA subclass versions of this function
|
|
* are not as efficient (they just call RectFill).
|
|
*
|
|
* SEE ALSO
|
|
* gPort class
|
|
*
|
|
**********************************************************************
|
|
*/
|
|
void gPort::hLine(int16 x, int16 y, int16 width) {
|
|
Rect16 sect;
|
|
|
|
// Temporarily convert the coords into a rectangle, for
|
|
// easy clipping
|
|
|
|
sect = intersect(_clip, Rect16(x, y, width, 1));
|
|
sect.x += _origin.x; // apply origin translate
|
|
sect.y += _origin.y;
|
|
|
|
if (!sect.empty()) { // if result is non-empty
|
|
if (_drawMode == drawModeComplement) {
|
|
uint8 *addr = _baseRow + (y + _origin.y) * _rowMod + x + _origin.x;
|
|
|
|
while (sect.width--) *addr++ ^= _fgPen;
|
|
} else {
|
|
_HLine(_baseRow + sect.y * _rowMod + sect.x, sect.width, _fgPen);
|
|
|
|
/* memset( _baseRow + sect.y * _rowMod + sect.x,
|
|
_fgPen,
|
|
sect.width );
|
|
*/
|
|
}
|
|
}
|
|
}
|
|
|
|
/****** gdraw.cpp/gPort::vLine ************************************
|
|
*
|
|
* NAME
|
|
* gPort::vLine -- optimized rendering of vertical lines
|
|
*
|
|
* SYNOPSIS
|
|
* port.vLine( x, y, height );
|
|
*
|
|
* void gPort::vLine( int16, int16, int16 );
|
|
*
|
|
* FUNCTION
|
|
* This function draws a 1-pixel-thick vertical line starting
|
|
* from the coordinates at (x,y) and extending downward for
|
|
* "height" pixels. drawModeComplement is supported.
|
|
*
|
|
* Calling this function is faster that the general "line" routine.
|
|
* It is used in many of the GTools bevel-drawing functions.
|
|
*
|
|
* INPUTS
|
|
* x,y The left edge of the line
|
|
*
|
|
* height height of the line in pixels.
|
|
*
|
|
* RESULT
|
|
* none
|
|
*
|
|
* NOTES
|
|
* This function needs to be downcoded to assembly.
|
|
* Note: The VGA and SVGA subclass versions of this function
|
|
* are not as efficient (they just call RectFill).
|
|
*
|
|
* SEE ALSO
|
|
* gPort class
|
|
*
|
|
**********************************************************************
|
|
*/
|
|
void gPort::vLine(int16 x, int16 y, int16 height) {
|
|
int16 bottom = y + height;
|
|
uint8 *addr;
|
|
|
|
// Just for laughs, we'll do the clipping a different way
|
|
|
|
if (x < _clip.x || x >= _clip.x + _clip.width) return;
|
|
if (y < _clip.y) y = _clip.y;
|
|
if (bottom > _clip.y + _clip.height) bottom = _clip.y + _clip.height;
|
|
|
|
// And now, draw the line
|
|
|
|
if (_drawMode == drawModeComplement) {
|
|
for (addr = _baseRow + (y + _origin.y) * _rowMod + x + _origin.x;
|
|
y < bottom;
|
|
y++) {
|
|
*addr ^= _fgPen;
|
|
addr += _rowMod;
|
|
}
|
|
} else {
|
|
for (addr = _baseRow + (y + _origin.y) * _rowMod + x + _origin.x;
|
|
y < bottom;
|
|
y++) {
|
|
*addr = _fgPen;
|
|
addr += _rowMod;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* ======================================================================= *
|
|
gPort class (graphics drawing port)
|
|
* ======================================================================= */
|
|
|
|
// Bresenham line-drawing functions
|
|
|
|
/****** gdraw.cpp/gPort::line *************************************
|
|
*
|
|
* NAME
|
|
* gPort::line -- general line-drawing routine
|
|
*
|
|
* SYNOPSIS
|
|
* port.line( x1, y1, x2, y2 );
|
|
* port.line( startPoint, destPoint );
|
|
*
|
|
* void gPort::line( int16 x1, int16 y1, int16 x2, int16 y2 );
|
|
* void gPort::line( Point16 p, Point16 p );
|
|
*
|
|
* FUNCTION
|
|
* This is the general bresenham line-drawing function. It draws
|
|
* from a source point to a destination point. The pen position
|
|
* is not affected. Clipping and drawModeComplement are supported.
|
|
*
|
|
* INPUTS
|
|
* (first form)
|
|
*
|
|
* x1, y1 The starting point of the line
|
|
*
|
|
* x2, y2 The ending point of the line
|
|
*
|
|
* (second form)
|
|
*
|
|
* startPoint The starting point of the line as a Point16
|
|
*
|
|
* endPoint The ending point of the line as a Point16
|
|
*
|
|
* RESULT
|
|
*
|
|
* NOTES
|
|
* This function is still in C and not very efficient. The VGA and
|
|
* SVGA versions are also fairly inefficient.
|
|
*
|
|
* Also, I have not yet worked out the math to properly determine
|
|
* the error terms for clipped lines. This potentially could cause
|
|
* "jaggies" in the lines to match poorly when lines drawn in two
|
|
* adjacent clip rectangles meet. To fix this I interate the
|
|
* algorithm even for clipped pixels, which is a waste...
|
|
*
|
|
* SEE ALSO
|
|
* gPort class
|
|
* gPort::move
|
|
* gPort::moveTo
|
|
* gPort::draw
|
|
* gPort::drawTo
|
|
*
|
|
**********************************************************************
|
|
*/
|
|
void gPort::line(int16 x1, int16 y1, int16 x2, int16 y2) {
|
|
bool clipNeeded = false;
|
|
|
|
int16 xAbs, yAbs,
|
|
xMove, yMove,
|
|
xDir, yDir,
|
|
i;
|
|
|
|
int16 errTerm;
|
|
|
|
int16 clipRight = _clip.x + _clip.width,
|
|
clipBottom = _clip.y + _clip.height;
|
|
|
|
uint8 *addr;
|
|
|
|
if (x1 > x2) { // drawing left
|
|
if (x1 < _clip.x || x2 >= clipRight) return;
|
|
if (x2 < _clip.x || x1 >= clipRight) clipNeeded = true;
|
|
|
|
xDir = xMove = -1; // amount to adjust address
|
|
xAbs = x1 - x2; // length of line
|
|
} else { // drawing right
|
|
if (x2 < _clip.x || x1 >= clipRight) return;
|
|
if (x1 < _clip.x || x2 >= clipRight) clipNeeded = true;
|
|
|
|
xDir = xMove = 1; // amount to adjust address
|
|
xAbs = x2 - x1; // length of line
|
|
}
|
|
|
|
if (y1 > y2) { // drawing up
|
|
if (y1 < _clip.y || y2 >= clipBottom) return;
|
|
if (y2 < _clip.y || y1 >= clipBottom) clipNeeded = true;
|
|
|
|
yDir = -1;
|
|
yAbs = y1 - y2;
|
|
yMove = -_rowMod;
|
|
} else { // drawing down
|
|
if (y2 < _clip.y || y1 >= clipBottom) return;
|
|
if (y1 < _clip.y || y2 >= clipBottom) clipNeeded = true;
|
|
|
|
yDir = 1;
|
|
yAbs = y2 - y1;
|
|
yMove = _rowMod;
|
|
}
|
|
|
|
addr = _baseRow + (y1 + _origin.y) * _rowMod + x1 + _origin.x;
|
|
|
|
if (clipNeeded) { // clipping versions
|
|
if (xAbs > yAbs) {
|
|
errTerm = yAbs - (xAbs >> 1);
|
|
|
|
for (i = xAbs + 1; i > 0; i--) {
|
|
if (x1 >= _clip.x && x1 < clipRight
|
|
&& y1 >= _clip.y && y1 < clipBottom) {
|
|
if (_drawMode == drawModeComplement)
|
|
*addr ^= _fgPen;
|
|
else *addr = _fgPen;
|
|
}
|
|
|
|
if (errTerm > 0) {
|
|
y1 += yDir;
|
|
addr += yMove;
|
|
errTerm -= xAbs;
|
|
}
|
|
|
|
x1 += xDir;
|
|
addr += xMove;
|
|
errTerm += yAbs;
|
|
}
|
|
} else {
|
|
errTerm = xAbs - (yAbs >> 1);
|
|
|
|
for (i = yAbs + 1; i > 0; i--) {
|
|
if (x1 >= _clip.x && x1 < clipRight
|
|
&& y1 >= _clip.y && y1 < clipBottom) {
|
|
if (_drawMode == drawModeComplement)
|
|
*addr ^= _fgPen;
|
|
else *addr = _fgPen;
|
|
}
|
|
|
|
if (errTerm > 0) {
|
|
x1 += xDir;
|
|
addr += xMove;
|
|
errTerm -= yAbs;
|
|
}
|
|
|
|
y1 += yDir;
|
|
addr += yMove;
|
|
errTerm += xAbs;
|
|
}
|
|
}
|
|
} else { // non-clipping versions
|
|
if (xAbs > yAbs) {
|
|
errTerm = yAbs - (xAbs >> 1);
|
|
|
|
for (i = xAbs + 1; i > 0; i--) {
|
|
if (_drawMode == drawModeComplement)
|
|
*addr ^= _fgPen;
|
|
else *addr = _fgPen;
|
|
|
|
if (errTerm > 0) {
|
|
y1 += yDir;
|
|
addr += yMove;
|
|
errTerm -= xAbs;
|
|
}
|
|
|
|
x1 += xDir;
|
|
addr += xMove;
|
|
errTerm += yAbs;
|
|
}
|
|
} else {
|
|
errTerm = xAbs - (yAbs >> 1);
|
|
|
|
for (i = yAbs + 1; i > 0; i--) {
|
|
if (_drawMode == drawModeComplement)
|
|
*addr ^= _fgPen;
|
|
else *addr = _fgPen;
|
|
|
|
if (errTerm > 0) {
|
|
x1 += xDir;
|
|
addr += xMove;
|
|
errTerm -= yAbs;
|
|
}
|
|
|
|
y1 += yDir;
|
|
addr += yMove;
|
|
errTerm += xAbs;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/****** gdraw.cpp/gPort::bltPixels ********************************
|
|
*
|
|
* NAME
|
|
* gPort::bltPixels -- general blitting routine
|
|
*
|
|
* SYNOPSIS
|
|
* port.bltPixels( sourceMap, srcX, srcY, dstX, dstY, width, height );
|
|
*
|
|
* void gPort::bltPixels( gPixelMap &, int, int, int, int, int, int );
|
|
*
|
|
* FUNCTION
|
|
* This is it! The general blitting routine for gPorts. It obeys
|
|
* clipping and the drawModes (see gPort::setMode for more details
|
|
* on THAT).
|
|
*
|
|
* INPUTS
|
|
* sourceMap A gPixelMap containing the source image to blit.
|
|
*
|
|
* srcX, srcY The coordinates of the source image to start
|
|
* blitting from.
|
|
*
|
|
* dstX, dstY The
|
|
*
|
|
* RESULT
|
|
* none
|
|
*
|
|
* NOTES
|
|
* This is still in C. However, for C it's not bad. The subclass
|
|
* versions (VGA and SVGA) are in highly optimized assembly, though.
|
|
*
|
|
* SEE ALSO
|
|
* gPort class
|
|
*
|
|
**********************************************************************
|
|
*/
|
|
void gPort::bltPixels(
|
|
const gPixelMap &src,
|
|
int src_x,
|
|
int src_y,
|
|
int dst_x,
|
|
int dst_y,
|
|
int width,
|
|
int height) {
|
|
Rect16 r = Rect16(dst_x, dst_y, width, height),
|
|
sect;
|
|
uint8 *src_line,
|
|
*dst_line;
|
|
|
|
sect = intersect(_clip, r);
|
|
|
|
if (!sect.empty()) { // if result is non-empty
|
|
src_x += sect.x - r.x;
|
|
src_y += sect.y - r.y;
|
|
|
|
src_line = src._data + src_y * src._size.x + src_x;
|
|
dst_line = _baseRow
|
|
+ (sect.y + _origin.y) * _rowMod
|
|
+ sect.x + _origin.x;
|
|
|
|
if (_drawMode == drawModeMatte) { // Matte drawing mode
|
|
for (int h = sect.height; h > 0; h--, src_line += src._size.x, dst_line += _rowMod) {
|
|
uint8 *src_ptr = src_line,
|
|
*dst_ptr = dst_line;
|
|
|
|
for (int w = sect.width; w > 0; w--) {
|
|
if (*src_ptr)
|
|
*dst_ptr++ = *src_ptr++;
|
|
else
|
|
dst_ptr++, src_ptr++;
|
|
}
|
|
}
|
|
} else if (_drawMode == drawModeColor) { // Color drawing mode
|
|
// Draws single color, except where
|
|
for (int h = sect.height; // src pixels are transparent
|
|
h > 0;
|
|
h--,
|
|
src_line += src._size.x,
|
|
dst_line += _rowMod) {
|
|
uint8 *src_ptr = src_line,
|
|
*dst_ptr = dst_line;
|
|
|
|
for (int w = sect.width;
|
|
w > 0;
|
|
w--) {
|
|
if (*src_ptr++)
|
|
*dst_ptr++ = _fgPen;
|
|
else
|
|
dst_ptr++;
|
|
}
|
|
}
|
|
} else if (_drawMode == drawModeReplace) { // Replacement drawing mode
|
|
for (int h = sect.height; h > 0; h--, src_line += src._size.x, dst_line += _rowMod) {
|
|
memcpy(dst_line, src_line, sect.width);
|
|
}
|
|
} else if (_drawMode == drawModeComplement) { // Complement drawing mode
|
|
// Inverts pixels, except where
|
|
for (int h = sect.height; // src is transparent
|
|
h > 0;
|
|
h--,
|
|
src_line += src._size.x,
|
|
dst_line += _rowMod) {
|
|
uint8 *src_ptr = src_line,
|
|
*dst_ptr = dst_line;
|
|
|
|
for (int w = sect.width;
|
|
w > 0;
|
|
w--) {
|
|
if (*src_ptr++) *dst_ptr++ ^= _fgPen;
|
|
else dst_ptr++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/****** gdraw.cpp/gPort::bltPixelMask ********************************
|
|
*
|
|
* NAME
|
|
* gPort::bltPixelMask -- blit pixels through a mask
|
|
*
|
|
* SYNOPSIS
|
|
* port.bltPixelMask( sourceMap, maskMap, srcX, srcY,
|
|
* /c/ dstX, dstY, width, height );
|
|
*
|
|
* void gPort::bltPixels( gPixelMap &, gPixelMap &, int, int,
|
|
* /c/ int, int, int, int );
|
|
*
|
|
* FUNCTION
|
|
* This is similar to the bltPixels function, except it takes
|
|
* an additional parameter, which specifies a "mask" bitmap. The mask
|
|
* bitmap is assumed to be the same size and congruent to the
|
|
* the source pixel map.
|
|
*
|
|
* The mask map affects the blit as follows: Any time a pixel within
|
|
* the mask map is zero, the corresponding source pixel will not
|
|
* be blitted.
|
|
*
|
|
* INPUTS
|
|
* sourceMap A gPixelMap containing the source image to blit.
|
|
*
|
|
* maskMap A gPixelMap containing the image mask.
|
|
*
|
|
* srcX, srcY The coordinates of the source image to start
|
|
* blitting from.
|
|
*
|
|
* dstX, dstY The
|
|
*
|
|
* RESULT
|
|
* none
|
|
*
|
|
* NOTES
|
|
* This is still in C. However, for C it's not bad. The subclass
|
|
* versions (VGA and SVGA) are in highly optimized assembly, though.
|
|
*
|
|
* SEE ALSO
|
|
* gPort class
|
|
*
|
|
**********************************************************************
|
|
*/
|
|
void gPort::bltPixelMask(
|
|
gPixelMap &src,
|
|
gPixelMap &msk,
|
|
int src_x,
|
|
int src_y,
|
|
int dst_x,
|
|
int dst_y,
|
|
int width,
|
|
int height) {
|
|
Rect16 r = Rect16(dst_x, dst_y, width, height),
|
|
sect;
|
|
uint8 *src_line,
|
|
*dst_line,
|
|
*msk_line;
|
|
|
|
sect = intersect(_clip, r);
|
|
|
|
if (!sect.empty()) { // if result is non-empty
|
|
src_x += sect.x - r.x;
|
|
src_y += sect.y - r.y;
|
|
|
|
src_line = src._data + src_y * src._size.x + src_x;
|
|
msk_line = msk._data + src_y * msk._size.x + src_x;
|
|
dst_line = _baseRow
|
|
+ (sect.y + _origin.y) * _rowMod
|
|
+ sect.x + _origin.x;
|
|
|
|
for (int h = sect.height;
|
|
h > 0;
|
|
h--,
|
|
src_line += src._size.x,
|
|
dst_line += _rowMod,
|
|
msk_line += msk._size.x) {
|
|
uint8 *src_ptr = src_line,
|
|
*dst_ptr = dst_line,
|
|
*msk_ptr = msk_line;
|
|
|
|
for (int w = sect.width;
|
|
w > 0;
|
|
w--) {
|
|
if (*msk_ptr++) *dst_ptr++ = *src_ptr++;
|
|
else dst_ptr++, src_ptr++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/****** gdraw.cpp/gPort::scrollPixels *****************************
|
|
*
|
|
* NAME
|
|
* gPort::scrollPixels -- scroll pixels within a rectangle
|
|
*
|
|
* SYNOPSIS
|
|
* port.scrollPixels( rect, dx, dy );
|
|
*
|
|
* void gPort::scrollPixels( Rect16, int, int );
|
|
*
|
|
* FUNCTION
|
|
* This function scrolls the pixels within the rectangle "rect"
|
|
* in the direction (dx, dy). Thus, if dx and dy are positive, the
|
|
* image within the rectangle will appear to move to the lower right.
|
|
*
|
|
* The function does not attempt to clear or otherwise deal with
|
|
* "newly revealed" pixels.
|
|
*
|
|
* The rectangle is intersected with the current clip region
|
|
* before the operation proceeds.
|
|
*
|
|
* INPUTS
|
|
* rect The rectangle to limit the scrolling to.
|
|
*
|
|
* dx,dy The direction of the scroll.
|
|
*
|
|
* RESULT
|
|
* none
|
|
*
|
|
* NOTES
|
|
* This function is NOT IMPLEMENTED for the gDisplayPorts (i.e.
|
|
* the SVGA and VGA subclasses). The reason for this is the
|
|
* difficulty of implementing this on all SVGA cards (For example,
|
|
* if a card only has a single memory window, it would require an
|
|
* extra off-screen buffer to insure that all possible scrollPixels
|
|
* operations could succeed).
|
|
*
|
|
* SEE ALSO
|
|
* gPort class
|
|
*
|
|
**********************************************************************
|
|
*/
|
|
void gPort::scrollPixels(
|
|
const Rect16 r, // area to scroll
|
|
int dx, // amount to scroll by
|
|
int dy) {
|
|
Rect16 sect;
|
|
uint8 *src_ptr,
|
|
*dst_ptr;
|
|
|
|
sect = intersect(_clip, r);
|
|
if (dx == 0 && dy == 0) return;
|
|
|
|
if (!sect.empty()) { // if result is non-empty
|
|
uint16 w = sect.width,
|
|
h = sect.height,
|
|
mod = _rowMod;
|
|
Point16 src(sect.x + _origin.x, sect.y + _origin.y),
|
|
dst(sect.x + _origin.x, sect.y + _origin.y);
|
|
|
|
if (dx > 0) {
|
|
dst.x += dx;
|
|
w -= dx;
|
|
} else {
|
|
src.x -= dx;
|
|
w += dx;
|
|
}
|
|
|
|
if (dy > 0) {
|
|
dst.y += dy;
|
|
h -= dy;
|
|
} else {
|
|
src.y -= dy;
|
|
h += dy;
|
|
}
|
|
|
|
if (w <= 0 || h <= 0) return;
|
|
|
|
if (src.y > dst.y || (src.y == dst.y && src.x > dst.x)) {
|
|
src_ptr = _baseRow + src.y * mod + src.x;
|
|
dst_ptr = _baseRow + dst.y * mod + dst.x;
|
|
|
|
mod -= w;
|
|
|
|
while (h--) {
|
|
for (int w1 = w; w1 > 0; w1--) *dst_ptr++ = *src_ptr++;
|
|
src_ptr += mod;
|
|
dst_ptr += mod;
|
|
}
|
|
} else {
|
|
src_ptr = _baseRow + (src.y + h - 1) * mod + src.x + w;
|
|
dst_ptr = _baseRow + (dst.y + h - 1) * mod + dst.x + w;
|
|
|
|
mod -= w;
|
|
|
|
while (h--) {
|
|
for (int w1 = w; w1 > 0; w1--) *--dst_ptr = *--src_ptr;
|
|
src_ptr -= mod;
|
|
dst_ptr -= mod;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/* ======================================================================= *
|
|
Image Mapping
|
|
* ======================================================================= */
|
|
|
|
/****** gdraw.cpp/mapImage ****************************************
|
|
*
|
|
* NAME
|
|
* mapImage -- translate an image via a translation table
|
|
*
|
|
* SYNOPSIS
|
|
* mapImage( sourceMap, destMap, translation );
|
|
* void mapImage( sourePort, destPort, translation );
|
|
*
|
|
* void mapImage( gPixelMap &, gPixelMap &, gPen map[] )
|
|
* void mapImage( gPort, gPort, gPen map[] )
|
|
*
|
|
* FUNCTION
|
|
* Runs an image through a color translation. Right now it assumes
|
|
* that the source image has exactly the same dimensions as the
|
|
* destination image.
|
|
*
|
|
* INPUTS
|
|
* sourceMap The map to translate
|
|
*
|
|
* destMap Where to put the translated pixels
|
|
*
|
|
* translation The lookup table to translate the pixels through.
|
|
*
|
|
* (second form, which takes gPorts instead of gPixelMaps)
|
|
*
|
|
* sourcePort The source pixels (in a gPort)
|
|
*
|
|
* destPort The destination pixels (in a gPort)
|
|
*
|
|
* RESULT
|
|
*
|
|
* NOTES
|
|
* This could easily be downcoded.
|
|
*
|
|
**********************************************************************
|
|
*/
|
|
void mapImage(gPixelMap &from, gPixelMap &to, gPen map[]) {
|
|
int32 bytes = to.bytes();
|
|
uint8 *get = from._data,
|
|
*put = to._data;
|
|
|
|
while (bytes--) *put++ = map[*get++];
|
|
}
|
|
|
|
void mapImage(gPort &from, gPort &to, gPen map[]) {
|
|
mapImage(*from._map, *to._map, map);
|
|
}
|
|
|
|
/* ======================================================================= *
|
|
Temporary images
|
|
* ======================================================================= */
|
|
|
|
/****** gdraw.cpp/NewTempPort *************************************
|
|
*
|
|
* NAME
|
|
* NewTempPort -- create a new temporary gPort
|
|
*
|
|
* SYNOPSIS
|
|
* error = NewTempPort( port, width, height );
|
|
*
|
|
* errorCode NewTempPort( gPort &, int, int );
|
|
*
|
|
* FUNCTION
|
|
* This function takes a raw, uninitialized gPort, creates
|
|
* a temporary gPixelMap, and attaches it to the gPort. It's
|
|
* mainly used internally by gTools when it needs somewhere to
|
|
* temporarily stash an image.
|
|
*
|
|
* INPUTS
|
|
* port The port object to attach the temporary pixel map to.
|
|
*
|
|
* width,height The size of the pixel map to be created.
|
|
*
|
|
* RESULT
|
|
* If it succeed, the error will be "errOK".
|
|
*
|
|
* NOTES
|
|
* We should phase out the old method of handling errors and
|
|
* use the new exception-based error handling.
|
|
*
|
|
* SEE ALSO
|
|
* gPort class
|
|
* gPixelMap class
|
|
* DisposeTempPort
|
|
*
|
|
**********************************************************************
|
|
*/
|
|
bool NewTempPort(gPort &port, int width, int height) {
|
|
gPixelMap *map;
|
|
|
|
map = (gPixelMap *)TempAlloc(width * height + sizeof(gPixelMap));
|
|
if (map != nullptr) {
|
|
map->_data = (uint8 *)(map + 1);
|
|
map->_size.x = width;
|
|
map->_size.y = height;
|
|
port.setMap(map);
|
|
return true;
|
|
} else
|
|
return false;
|
|
}
|
|
|
|
/****** gdraw.cpp/DisposeTempPort *********************************
|
|
*
|
|
* NAME
|
|
* DisposeTempPort -- disposes of temporary pixel map
|
|
*
|
|
* SYNOPSIS
|
|
* DisposeTempPort( port );
|
|
*
|
|
* void DisposeTempPort( gPort & );
|
|
*
|
|
* FUNCTION
|
|
* This function disposes of the temporary pixel map which
|
|
* was created by NewTempPort.
|
|
*
|
|
* INPUTS
|
|
* port The same port that was passed to NewTempPort
|
|
*
|
|
* RESULT
|
|
* none
|
|
*
|
|
* SEE ALSO
|
|
* gPort class
|
|
* gPixelMap class
|
|
* NewTempPort
|
|
*
|
|
**********************************************************************
|
|
*/
|
|
void DisposeTempPort(gPort &port) {
|
|
if (port._map) TempFree(port._map);
|
|
port._map = nullptr;
|
|
}
|
|
|
|
} // end of namespace Saga2
|