sprites are drawn, but I think that's how it should be. 1: No bells or whistles. 2: This setting adds sprite blending, e.g. the smoke at the docks or the display cases at the Glease Gallery. 3: This setting adds light map support, e.g. when walking under the shack at the docks. 4: This setting adds better scaling algorithms. The first three settings should work fine now. In fact, the third setting is what we used to implement. The fourth setting still needs work and testing. I've added code for downscaling case, but frankly I'm not convinced the result is any better than with the simpler scaler. I usually can't even tell the difference. Of course, my translation of the original code could very well be buggy. svn-id: r9867
1145 lines
26 KiB
C++
1145 lines
26 KiB
C++
/* Copyright (C) 1994-2003 Revolution Software Ltd
|
|
*
|
|
* 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
*
|
|
* $Header$
|
|
*/
|
|
|
|
//=============================================================================
|
|
//
|
|
// Filename : render.c
|
|
// Created : 26th September 1996
|
|
// By : P.R.Porter
|
|
//
|
|
// Summary : This module holds the functions which deal with rendering
|
|
// the background and parallax layers, and controlling the
|
|
// speed of the scroll (number of frames)
|
|
//
|
|
// Version Date By Description
|
|
// ------- --------- --- -----------------------------------------------
|
|
// 1.0 26-Sep-96 PRP The functions have been moved here from d_draw
|
|
// because this is a more sensible place for them.
|
|
//
|
|
// 1.1 04-Oct-96 PRP Added direct path to ddraw.h
|
|
//
|
|
// 1.2 09-Oct-96 PRP Moved SOFTWARE_SCREEN_BUFFER into driver96.h,
|
|
// and renamed to RD_SOFTWARESCREENBUFFER.
|
|
//
|
|
// 1.3 23-Oct-96 PRP Moved definition of _parallaxLine structure to
|
|
// render.h. Got rid of the divide by zero error.
|
|
//
|
|
// 1.4 05-Nov-96 PRP Moved defines of RENDERWIDE, RENDERDEEP, etc.,
|
|
// to render.h
|
|
//
|
|
// 1.5 15-Nov-96 PRP Definition of menu size is now obtained from
|
|
// menu.h
|
|
//
|
|
// 1.6 18-Nov-96 PRP Changed the direct draw interface to
|
|
// IDirectDraw2. Added the PlotPoint and
|
|
// DrawLine functions.
|
|
//
|
|
// 1,7 21-Nov-96 PRP Implemented the DrawLine function.
|
|
//
|
|
// 1.8 24-Jan-97 PRP Added parallaxScrollx and parallaxScrolly to
|
|
// define the offset for linking sprites to
|
|
// parallax layers.
|
|
//
|
|
// 1.9 06-Feb-97 PRP Added rendering functions to draw from display
|
|
// memory. Made the code switchable so that if
|
|
// the hardware is not available, the old software
|
|
// rendering functions are used.
|
|
//
|
|
// 1.10 06-Feb-97 PRP Fixed a bug with releasing the surfaces for a
|
|
// room.
|
|
//
|
|
// 1.11 06-Feb-97 PRP Implemented PlotPoint and DrawLine for hardware
|
|
// version.
|
|
//
|
|
// 1.12 07-Feb-97 PRP Implemented function to restore the background
|
|
// layers. Changed the layer rendering code to
|
|
// restore the layers when they've gone.
|
|
//
|
|
// 1.13 07-Feb-97 PRP Fixed a bug in the layer renderer, which
|
|
// dropped out when the first block in a layer was
|
|
// blank.
|
|
//
|
|
// 1.14 10-Feb-97 PRP Changed the calls to the direct draw error
|
|
// reporting code, and made the rendering dependant
|
|
// upon the hardware functions availability.
|
|
//
|
|
// 1.15 12-Feb-97 PRP Fixed rendering position bug.
|
|
//
|
|
// 1.16 18-Mar-97 PRP Fixed the smooth scroll, and render timing.
|
|
// Added a function to initialise the render timer
|
|
//
|
|
// 1.17 18-Mar-97 PRP Added code to profile the rendering functions.
|
|
//
|
|
// 1.18 04-Apr-97 PRP Restored the primary surface and back buffer
|
|
// before the parallax layers.
|
|
//
|
|
// 1.19 07-Apr-97 PRP No changes.
|
|
//
|
|
// 1.20 08-Apr-97 JEL Made location wide and location deep global.
|
|
//
|
|
// 1.21 10-Apr-97 PRP Added the return value RDERR_LOCKFAILED to
|
|
// the CopyScreenBuffer function.
|
|
//
|
|
// 1.22 11-Apr-97 PRP Added function to clear the software render
|
|
// buffer.
|
|
//
|
|
// 1.23 11-Apr-97 PRP Fixed benign redefinition of type bug
|
|
//
|
|
// 1.24 13-Jun-97 PRP Fixed switching between hardware and software
|
|
// rendering during gameplay.
|
|
//
|
|
// 1.25 30-Jun-97 JEL Tried to fix sc23 sprite position bug, but no changes made in the end
|
|
//
|
|
// 1.26 08-July-97 JEL Fixed bug in RenderParallax for when not gotTheFocus, to prevent crash when switching task in NT
|
|
//
|
|
// Functions
|
|
// ---------
|
|
//
|
|
// ---------------------------------------------------------------------------
|
|
//
|
|
// int32 RenderParallax(_parallax *p, int16 layer)
|
|
//
|
|
// Draws a parallax layer at the current position determined by the scroll.
|
|
// A parallax can be either foreground, background or the main screen.
|
|
//
|
|
// ---------------------------------------------------------------------------
|
|
//
|
|
// int32 CopyScreenBuffer(void)
|
|
//
|
|
// Copies the screen buffer to screen memory. This function should be called
|
|
// when the drawing should be done to the back buffer. It only does this
|
|
// when we are using a software screen buffer.
|
|
//
|
|
// ---------------------------------------------------------------------------
|
|
//
|
|
// int32 SetScrollTarget(int16 sx, int16 sy)
|
|
//
|
|
// Sets the scroll target position for the end of the game cycle. The drivers
|
|
// will then automatically scroll as many times as it can to reach this
|
|
// position in the allotted time.
|
|
//
|
|
// --------------------------------------------------------------------------
|
|
//
|
|
// int32 StartRenderCycle(void)
|
|
//
|
|
// This function should be called when the game engine is ready to start
|
|
// the render cycle.
|
|
//
|
|
// --------------------------------------------------------------------------
|
|
//
|
|
// int32 EndRenderCycle(BOOL *end)
|
|
//
|
|
// This function should be called at the end of the render cycle. If the
|
|
// render cycle is to be terminated, the function sets *end to 1. Otherwise,
|
|
// the render cycle should continue.
|
|
//
|
|
// --------------------------------------------------------------------------
|
|
//
|
|
// int32 SetLocationMetrics(uint16 w, uint16 h)
|
|
//
|
|
// This function tells the drivers the size of the background screen for the
|
|
// current location.
|
|
//
|
|
// --------------------------------------------------------------------------
|
|
//
|
|
// int32 PlotPoint(uint16 x, uint16 y, uint8 colour)
|
|
//
|
|
// Plots the point x,y in relation to the top left corner of the background.
|
|
//
|
|
// --------------------------------------------------------------------------
|
|
//
|
|
// int32 DrawLine(int16 x1, int16 y1, int16 x2, int16 y2, uint8 colour)
|
|
//
|
|
// Draws a line from the point x1,y1 to x2,y2 of the specified colour.
|
|
//
|
|
// --------------------------------------------------------------------------
|
|
//
|
|
// int32 InitialiseBackgroundLayer(_parallax *p)
|
|
//
|
|
// This function should be called five times with either the parallax layer
|
|
// or a NULL pointer in order of background parallax to foreground parallax.
|
|
//
|
|
// --------------------------------------------------------------------------
|
|
//
|
|
// int32 CloseBackgroundLayer(void)
|
|
//
|
|
// Should be called once after leaving a room to free up video memory.
|
|
//
|
|
// --------------------------------------------------------------------------
|
|
//
|
|
// int32 PlotDots(int16 x, int16 y, int16 count)
|
|
//
|
|
// Plots 'count' dots at the position x,y.
|
|
//
|
|
//=============================================================================
|
|
|
|
|
|
|
|
|
|
#define WIN32_LEAN_AND_MEAN
|
|
|
|
//#include <windows.h>
|
|
//#include <windowsx.h>
|
|
//#include <mmsystem.h>
|
|
#include <stdio.h>
|
|
|
|
//#include "ddraw.h"
|
|
|
|
#include "stdafx.h"
|
|
#include "driver96.h"
|
|
#include "d_draw.h"
|
|
#include "rdwin.h"
|
|
#include "_mouse.h"
|
|
#include "render.h"
|
|
#include "menu.h"
|
|
#include "../sword2.h"
|
|
|
|
|
|
|
|
#define MILLISECSPERCYCLE 83
|
|
|
|
|
|
|
|
#if PROFILING == 1
|
|
int32 profileCopyScreenBuffer = 0;
|
|
int32 profileRenderLayers = 0;
|
|
int32 profileSpriteRender = 0;
|
|
int32 profileDecompression = 0;
|
|
#endif
|
|
|
|
|
|
|
|
|
|
uint8 myScreenBuffer[RENDERWIDE * RENDERDEEP];
|
|
|
|
|
|
|
|
|
|
|
|
// Scroll variables. scrollx and scrolly hold the current scroll position,
|
|
// and scrollxTarget and scrollyTarget are the target position for the end
|
|
// of the game cycle.
|
|
|
|
//int16 scrollx;
|
|
//int16 scrolly;
|
|
extern int16 scrollx;
|
|
extern int16 scrolly;
|
|
int16 parallaxScrollx;
|
|
int16 parallaxScrolly;
|
|
int16 locationWide;
|
|
int16 locationDeep;
|
|
|
|
static int16 scrollxTarget;
|
|
static int16 scrollyTarget;
|
|
static int16 scrollxOld;
|
|
static int16 scrollyOld;
|
|
static uint16 layer = 0;
|
|
|
|
|
|
|
|
#define RENDERAVERAGETOTAL 4
|
|
int32 renderCountIndex = 0;
|
|
int32 renderTimeLog[RENDERAVERAGETOTAL] = {60, 60, 60, 60};
|
|
int32 initialTime;
|
|
int32 startTime;
|
|
int32 originTime;
|
|
int32 totalTime;
|
|
int32 renderAverageTime = 60;
|
|
int32 framesPerGameCycle;
|
|
int32 renderTooSlow;
|
|
|
|
|
|
|
|
|
|
#define BLOCKWIDTH 64
|
|
#define BLOCKHEIGHT 64
|
|
#define BLOCKWBITS 6
|
|
#define BLOCKHBITS 6
|
|
#define MAXLAYERS 5
|
|
|
|
uint8 xblocks[MAXLAYERS];
|
|
uint8 yblocks[MAXLAYERS];
|
|
uint8 restoreLayer[MAXLAYERS];
|
|
|
|
// Each layer is composed by several sub-blocks
|
|
|
|
Surface **blockSurfaces[MAXLAYERS] = { 0, 0, 0, 0, 0 };
|
|
|
|
|
|
|
|
void Surface::clear() {
|
|
memset(_pixels, 0, _width * _height);
|
|
g_sword2->_system->copy_rect(_pixels, _width, 0, 0, _width, _height);
|
|
}
|
|
|
|
void Surface::blit(Surface *s, ScummVM::Rect *r) {
|
|
ScummVM::Rect clip_rect;
|
|
|
|
clip_rect.left = 0;
|
|
clip_rect.top = 0;
|
|
clip_rect.right = 640;
|
|
clip_rect.bottom = 480;
|
|
|
|
blit(s, r, &clip_rect);
|
|
}
|
|
|
|
void Surface::blit(Surface *s, ScummVM::Rect *r, ScummVM::Rect *clip_rect) {
|
|
if (r->top > clip_rect->bottom || r->left > clip_rect->right || r->bottom <= clip_rect->top || r->right <= clip_rect->left)
|
|
return;
|
|
|
|
byte *src = s->_pixels;
|
|
|
|
if (r->top < clip_rect->top) {
|
|
src -= s->_width * (r->top - clip_rect->top);
|
|
r->top = clip_rect->top;
|
|
}
|
|
if (r->left < clip_rect->left) {
|
|
src -= (r->left - clip_rect->left);
|
|
r->left = clip_rect->left;
|
|
}
|
|
if (r->bottom > clip_rect->bottom)
|
|
r->bottom = clip_rect->bottom;
|
|
if (r->right > clip_rect->right)
|
|
r->right = clip_rect->right;
|
|
|
|
byte *dst = _pixels + r->top * _width + r->left;
|
|
int i, j;
|
|
|
|
// FIXME: We first render the data to the back buffer, and then copy
|
|
// it to the backend. Since the same area will probably be copied
|
|
// several times, as each new parallax layer is rendered, this may be
|
|
// a bit inefficient.
|
|
|
|
for (i = 0; i < r->bottom - r->top; i++) {
|
|
for (j = 0; j < r->right - r->left; j++) {
|
|
if (src[j])
|
|
dst[j] = src[j];
|
|
}
|
|
src += s->_width;
|
|
dst += _width;
|
|
}
|
|
|
|
UploadRect(r);
|
|
}
|
|
|
|
void UploadRect(ScummVM::Rect *r) {
|
|
g_system->copy_rect(lpBackBuffer->_pixels + r->top * lpBackBuffer->_width + r->left,
|
|
lpBackBuffer->_width, r->left, r->top, r->right - r->left, r->bottom - r->top);
|
|
}
|
|
|
|
#define SCALE_MAXWIDTH 512
|
|
#define SCALE_MAXHEIGHT 512
|
|
|
|
static uint16 xScale[SCALE_MAXWIDTH];
|
|
static uint16 yScale[SCALE_MAXHEIGHT];
|
|
|
|
// I've made the scaling two separate functions because there were cases from
|
|
// DrawSprite() where it wasn't obvious if the sprite should grow or shrink,
|
|
// which caused crashes.
|
|
//
|
|
// Maybe the functions can be merged later?
|
|
//
|
|
// The code is based on the original DrawSprite() code, so apart from not
|
|
// knowing if I got it right, I don't know how good the original really is.
|
|
|
|
void SquashImage(byte *dst, uint16 dstPitch, uint16 dstWidth, uint16 dstHeight, byte *src, uint16 srcPitch, uint16 srcWidth, uint16 srcHeight, byte *backbuf) {
|
|
int32 ince, incne, d;
|
|
int16 x, y;
|
|
|
|
// Work out the x-scale
|
|
|
|
ince = 2 * dstWidth;
|
|
incne = 2 * (dstWidth - srcWidth);
|
|
d = 2 * dstWidth - srcWidth;
|
|
x = y = 0;
|
|
xScale[y] = x;
|
|
|
|
while (x < srcWidth) {
|
|
if (d <= 0) {
|
|
d += ince;
|
|
x++;
|
|
} else {
|
|
d += incne;
|
|
x++;
|
|
y++;
|
|
}
|
|
xScale[y] = x;
|
|
}
|
|
|
|
// Work out the y-scale
|
|
|
|
ince = 2 * dstHeight;
|
|
incne = 2 * (dstHeight - srcHeight);
|
|
d = 2 * dstHeight - srcHeight;
|
|
x = y = 0;
|
|
yScale[y] = x;
|
|
|
|
while (x < srcHeight) {
|
|
if (d <= 0) {
|
|
d += ince;
|
|
x++;
|
|
} else {
|
|
d += incne;
|
|
x++;
|
|
y++;
|
|
}
|
|
yScale[y] = x;
|
|
}
|
|
|
|
// Copy the image
|
|
|
|
if (backbuf) {
|
|
for (y = 0; y < dstHeight; y++) {
|
|
for (x = 0; x < dstWidth; x++) {
|
|
uint8 p;
|
|
uint8 p1 = 0;
|
|
int count = 0;
|
|
int spriteCount = 0;
|
|
int red = 0;
|
|
int green = 0;
|
|
int blue = 0;
|
|
int i, j;
|
|
|
|
for (j = yScale[y]; j < yScale[y + 1]; j++) {
|
|
for (i = xScale[x]; i < xScale[x + 1]; i++) {
|
|
p = src[j * srcPitch + i];
|
|
if (p) {
|
|
red += palCopy[p][0];
|
|
green += palCopy[p][1];
|
|
blue += palCopy[p][2];
|
|
p1 = p;
|
|
spriteCount++;
|
|
} else {
|
|
red += palCopy[backbuf[x]][0];
|
|
green += palCopy[backbuf[x]][1];
|
|
blue += palCopy[backbuf[x]][2];
|
|
}
|
|
count++;
|
|
}
|
|
}
|
|
if (spriteCount == 0)
|
|
dst[x] = 0;
|
|
else if (spriteCount == 1)
|
|
dst[x] = p1;
|
|
else
|
|
dst[x] = QuickMatch((uint8) (red / count), (uint8) (green / count), (uint8) (blue / count));
|
|
}
|
|
dst += dstPitch;
|
|
backbuf += lpBackBuffer->_width;
|
|
}
|
|
} else {
|
|
for (y = 0; y < dstHeight; y++) {
|
|
for (x = 0; x < dstWidth; x++) {
|
|
dst[x] = src[yScale[y] * srcPitch + xScale[x]];
|
|
}
|
|
dst += dstPitch;
|
|
}
|
|
}
|
|
}
|
|
|
|
void StretchImage(byte *dst, uint16 dstPitch, uint16 dstWidth, uint16 dstHeight, byte *src, uint16 srcPitch, uint16 srcWidth, uint16 srcHeight, byte *backbuf) {
|
|
int32 ince, incne, d;
|
|
int16 x, y, i, j, k;
|
|
|
|
// Work out the x-scale
|
|
|
|
ince = 2 * srcWidth;
|
|
incne = 2 * (srcWidth - dstWidth);
|
|
d = 2 * srcWidth - dstWidth;
|
|
x = y = 0;
|
|
xScale[y] = x;
|
|
|
|
while (x < dstWidth) {
|
|
if (d <= 0) {
|
|
d += ince;
|
|
x++;
|
|
} else {
|
|
d += incne;
|
|
x++;
|
|
y++;
|
|
xScale[y] = x;
|
|
}
|
|
}
|
|
|
|
// Work out the y-scale
|
|
|
|
ince = 2 * srcHeight;
|
|
incne = 2 * (srcHeight - dstHeight);
|
|
d = 2 * srcHeight - dstHeight;
|
|
x = y = 0;
|
|
yScale[y] = x;
|
|
while (x < dstHeight) {
|
|
if (d <= 0) {
|
|
d += ince;
|
|
x++;
|
|
} else {
|
|
d += incne;
|
|
x++;
|
|
y++;
|
|
yScale[y] = x;
|
|
}
|
|
}
|
|
|
|
// Copy the image
|
|
|
|
// FIXME: If backbuf != NULL the image should be anti-aliased
|
|
|
|
for (y = 0; y < srcHeight; y++) {
|
|
for (j = yScale[y]; j < yScale[y + 1]; j++) {
|
|
k = 0;
|
|
for (x = 0; x < srcWidth; x++) {
|
|
for (i = xScale[x]; i < xScale[x + 1]; i++) {
|
|
dst[k++] = src[y * srcPitch + x];
|
|
}
|
|
}
|
|
dst += dstPitch;
|
|
}
|
|
}
|
|
}
|
|
|
|
int32 RestoreBackgroundLayer(_parallax *p, int16 l)
|
|
{
|
|
int16 oldLayer = layer;
|
|
int16 i;
|
|
|
|
debug(2, "RestoreBackgroundLayer %d", l);
|
|
|
|
layer = l;
|
|
if (blockSurfaces[l]) {
|
|
for (i = 0; i < xblocks[l] * yblocks[l]; i++)
|
|
if (blockSurfaces[l][i])
|
|
delete blockSurfaces[l][i];
|
|
|
|
free(blockSurfaces[l]);
|
|
blockSurfaces[l] = NULL;
|
|
}
|
|
InitialiseBackgroundLayer(p);
|
|
layer = oldLayer;
|
|
return RD_OK;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
int32 PlotPoint(uint16 x, uint16 y, uint8 colour)
|
|
|
|
{
|
|
warning("stub PlotPoint( %d, %d, %d )", x, y, colour);
|
|
/*
|
|
int16 newx, newy;
|
|
|
|
newx = x - scrollx;
|
|
newy = y - scrolly;
|
|
|
|
if ((newx < 0) || (newx > RENDERWIDE) || (newy < 0) || (newy > RENDERDEEP))
|
|
{
|
|
return(RD_OK);
|
|
}
|
|
if (renderCaps & RDBLTFX_ALLHARDWARE)
|
|
{
|
|
DDSURFACEDESC ddsd;
|
|
HRESULT hr;
|
|
|
|
ddsd.dwSize = sizeof(DDSURFACEDESC);
|
|
hr = IDirectDrawSurface2_Lock(lpBackBuffer, NULL, &ddsd, DDLOCK_SURFACEMEMORYPTR | DDLOCK_WAIT, NULL);
|
|
if (hr != DD_OK)
|
|
{
|
|
hr = IDirectDrawSurface2_Lock(lpBackBuffer, NULL, &ddsd, DDLOCK_SURFACEMEMORYPTR | DDLOCK_WAIT, NULL);
|
|
}
|
|
|
|
if (hr == DD_OK)
|
|
{
|
|
|
|
*((uint8 *) ddsd.lpSurface + (newy + 40) * ddsd.lPitch + newx) = colour;
|
|
IDirectDrawSurface2_Unlock(lpBackBuffer, ddsd.lpSurface);
|
|
}
|
|
}
|
|
else
|
|
myScreenBuffer[newy * RENDERWIDE + newx] = colour;
|
|
*/
|
|
return(RD_OK);
|
|
|
|
}
|
|
|
|
|
|
// Uses Bressnham's incremental algorithm!
|
|
int32 DrawLine(int16 x0, int16 y0, int16 x1, int16 y1, uint8 colour)
|
|
|
|
{
|
|
warning("stub DrawLine( %d, %d, %d, %d, %d )", x0, y0, x1, y1, colour);
|
|
/*
|
|
int dx, dy;
|
|
int dxmod, dymod;
|
|
int ince, incne;
|
|
int d;
|
|
int x, y;
|
|
int addTo;
|
|
DDSURFACEDESC ddsd;
|
|
HRESULT hr;
|
|
|
|
x1 -= scrollx;
|
|
y1 -= scrolly;
|
|
x0 -= scrollx;
|
|
y0 -= scrolly;
|
|
|
|
|
|
// Lock the surface if we're rendering to the back buffer.
|
|
|
|
if (renderCaps & RDBLTFX_ALLHARDWARE)
|
|
{
|
|
ddsd.dwSize = sizeof(DDSURFACEDESC);
|
|
hr = IDirectDrawSurface2_Lock(lpBackBuffer, NULL, &ddsd, DDLOCK_SURFACEMEMORYPTR | DDLOCK_WAIT, NULL);
|
|
if (hr != DD_OK)
|
|
{
|
|
hr = IDirectDrawSurface2_Lock(lpBackBuffer, NULL, &ddsd, DDLOCK_SURFACEMEMORYPTR | DDLOCK_WAIT, NULL);
|
|
}
|
|
if (hr != DD_OK)
|
|
return(RD_OK);
|
|
|
|
(uint8 *) ddsd.lpSurface += (40 * ddsd.lPitch);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//Make sure we're going from left to right
|
|
if (x1 < x0)
|
|
{
|
|
x = x1;
|
|
x1 = x0;
|
|
x0 = x;
|
|
y = y1;
|
|
y1 = y0;
|
|
y0 = y;
|
|
}
|
|
dx = x1 - x0;
|
|
dy = y1 - y0;
|
|
|
|
if (dx < 0)
|
|
dxmod = -dx;
|
|
else
|
|
dxmod = dx;
|
|
|
|
if (dy < 0)
|
|
dymod = -dy;
|
|
else
|
|
dymod = dy;
|
|
|
|
if (dxmod >= dymod)
|
|
{
|
|
if (dy > 0)
|
|
{
|
|
d = 2 * dy - dx;
|
|
ince = 2 * dy;
|
|
incne = 2 * (dy - dx);
|
|
x = x0;
|
|
y = y0;
|
|
if ((x >= 0) && (x < RENDERWIDE) && (y >= 0) && (y < RENDERDEEP))
|
|
if (renderCaps & RDBLTFX_ALLHARDWARE)
|
|
*((uint8 *) ddsd.lpSurface + y * ddsd.lPitch + x) = colour;
|
|
else
|
|
myScreenBuffer[y * RENDERWIDE + x] = colour;
|
|
while (x < x1)
|
|
{
|
|
if (d <= 0)
|
|
{
|
|
d += ince;
|
|
x += 1;
|
|
}
|
|
else
|
|
{
|
|
d += incne;
|
|
x += 1;
|
|
y += 1;
|
|
}
|
|
if ((x >= 0) && (x < RENDERWIDE) && (y >= 0) && (y < RENDERDEEP))
|
|
if (renderCaps & RDBLTFX_ALLHARDWARE)
|
|
*((uint8 *) ddsd.lpSurface + y * ddsd.lPitch + x) = colour;
|
|
else
|
|
myScreenBuffer[y * RENDERWIDE + x] = colour;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
addTo = y0;
|
|
y0 = 0;
|
|
y1 -= addTo;
|
|
y1 = -y1;
|
|
dy = y1 - y0;
|
|
|
|
d = 2 * dy - dx;
|
|
ince = 2 * dy;
|
|
incne = 2 * (dy - dx);
|
|
x = x0;
|
|
y = y0;
|
|
if ((x >= 0) && (x < RENDERWIDE) && (addTo - y >= 0) && (addTo - y < RENDERDEEP))
|
|
if (renderCaps & RDBLTFX_ALLHARDWARE)
|
|
*((uint8 *) ddsd.lpSurface + (addTo - y) * ddsd.lPitch + x) = colour;
|
|
else
|
|
myScreenBuffer[(addTo - y) * RENDERWIDE + x] = colour;
|
|
while (x < x1)
|
|
{
|
|
if (d <= 0)
|
|
{
|
|
d += ince;
|
|
x += 1;
|
|
}
|
|
else
|
|
{
|
|
d += incne;
|
|
x += 1;
|
|
y += 1;
|
|
}
|
|
if ((x >= 0) && (x < RENDERWIDE) && (addTo - y >= 0) && (addTo - y < RENDERDEEP))
|
|
if (renderCaps & RDBLTFX_ALLHARDWARE)
|
|
*((uint8 *) ddsd.lpSurface + (addTo - y) * ddsd.lPitch + x) = colour;
|
|
else
|
|
myScreenBuffer[(addTo - y) * RENDERWIDE + x] = colour;
|
|
}
|
|
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//OK, y is now going to be the single increment.
|
|
// Ensure the line is going top to bottom
|
|
if (y1 < y0)
|
|
{
|
|
x = x1;
|
|
x1 = x0;
|
|
x0 = x;
|
|
y = y1;
|
|
y1 = y0;
|
|
y0 = y;
|
|
}
|
|
dx = x1 - x0;
|
|
dy = y1 - y0;
|
|
|
|
if (dx > 0)
|
|
{
|
|
d = 2 * dx - dy;
|
|
ince = 2 * dx;
|
|
incne = 2 * (dx - dy);
|
|
x = x0;
|
|
y = y0;
|
|
if ((x >= 0) && (x < RENDERWIDE) && (y >= 0) && (y < RENDERDEEP))
|
|
if (renderCaps & RDBLTFX_ALLHARDWARE)
|
|
*((uint8 *) ddsd.lpSurface + y * ddsd.lPitch + x) = colour;
|
|
else
|
|
myScreenBuffer[y * RENDERWIDE + x] = colour;
|
|
while (y < y1)
|
|
{
|
|
if (d <= 0)
|
|
{
|
|
d += ince;
|
|
y += 1;
|
|
}
|
|
else
|
|
{
|
|
d += incne;
|
|
x += 1;
|
|
y += 1;
|
|
}
|
|
if ((x >= 0) && (x < RENDERWIDE) && (y >= 0) && (y < RENDERDEEP))
|
|
if (renderCaps & RDBLTFX_ALLHARDWARE)
|
|
*((uint8 *) ddsd.lpSurface + y * ddsd.lPitch + x) = colour;
|
|
else
|
|
myScreenBuffer[y * RENDERWIDE + x] = colour;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
addTo = x0;
|
|
x0 = 0;
|
|
x1 -= addTo;
|
|
x1 = -x1;
|
|
dx = x1 - x0;
|
|
|
|
d = 2 * dx - dy;
|
|
ince = 2 * dx;
|
|
incne = 2 * (dx - dy);
|
|
x = x0;
|
|
y = y0;
|
|
if ((addTo - x >= 0) && (addTo - x < RENDERWIDE) && (y >= 0) && (y < RENDERDEEP))
|
|
if (renderCaps & RDBLTFX_ALLHARDWARE)
|
|
*((uint8 *) ddsd.lpSurface + y * ddsd.lPitch + addTo - x) = colour;
|
|
else
|
|
myScreenBuffer[y * RENDERWIDE + addTo - x] = colour;
|
|
while (y < y1)
|
|
{
|
|
if (d <= 0)
|
|
{
|
|
d += ince;
|
|
y += 1;
|
|
}
|
|
else
|
|
{
|
|
d += incne;
|
|
x += 1;
|
|
y += 1;
|
|
}
|
|
if ((addTo - x >= 0) && (addTo - x < RENDERWIDE) && (y >= 0) && (y < RENDERDEEP))
|
|
if (renderCaps & RDBLTFX_ALLHARDWARE)
|
|
*((uint8 *) ddsd.lpSurface + y * ddsd.lPitch + addTo - x) = colour;
|
|
else
|
|
myScreenBuffer[y * RENDERWIDE + addTo - x] = colour;
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (renderCaps & RDBLTFX_ALLHARDWARE)
|
|
{
|
|
(uint8 *) ddsd.lpSurface -= (40 * ddsd.lPitch);
|
|
IDirectDrawSurface2_Unlock(lpBackBuffer, ddsd.lpSurface);
|
|
}
|
|
*/
|
|
return(RD_OK);
|
|
|
|
}
|
|
|
|
|
|
|
|
int32 SetLocationMetrics(uint16 w, uint16 h)
|
|
|
|
{
|
|
|
|
locationWide = w;
|
|
locationDeep = h;
|
|
|
|
return(RD_OK);
|
|
|
|
}
|
|
|
|
|
|
|
|
int32 RenderParallax(_parallax *p, int16 l) {
|
|
int16 x, y;
|
|
int16 i, j;
|
|
ScummVM::Rect r;
|
|
|
|
debug(9, "RenderParallax %d", l);
|
|
|
|
if (locationWide == screenWide)
|
|
x = 0;
|
|
else
|
|
x = ((int32) ((p->w - screenWide) * scrollx) / (int32) (locationWide - screenWide));
|
|
|
|
if (locationDeep == (screenDeep - MENUDEEP * 2))
|
|
y = 0;
|
|
else
|
|
y = ((int32) ((p->h - (screenDeep - MENUDEEP * 2)) * scrolly) / (int32) (locationDeep - (screenDeep - MENUDEEP * 2)));
|
|
|
|
ScummVM::Rect clip_rect;
|
|
|
|
// Leave enough space for the top and bottom menues
|
|
|
|
clip_rect.left = 0;
|
|
clip_rect.right = 640;
|
|
clip_rect.top = 40;
|
|
clip_rect.bottom = 440;
|
|
|
|
for (j = 0; j < yblocks[l]; j++) {
|
|
for (i = 0; i < xblocks[l]; i++) {
|
|
if (blockSurfaces[l][i + j * xblocks[l]]) {
|
|
r.left = i * BLOCKWIDTH - x;
|
|
r.right = r.left + BLOCKWIDTH;
|
|
r.top = j * BLOCKHEIGHT - y + 40;
|
|
r.bottom = r.top + BLOCKHEIGHT;
|
|
lpBackBuffer->blit(blockSurfaces[l][i + j * xblocks[l]], &r, &clip_rect);
|
|
}
|
|
}
|
|
}
|
|
|
|
parallaxScrollx = scrollx - x;
|
|
parallaxScrolly = scrolly - y;
|
|
|
|
return(RD_OK);
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
#define LOGSIZE 10
|
|
int32 previousTimeLog[LOGSIZE];
|
|
int32 renderCycleStartLog[LOGSIZE];
|
|
int32 lastRenderTimeLog[LOGSIZE];
|
|
int32 renderCycleEndLog[LOGSIZE];
|
|
int32 timeLeftLog[LOGSIZE];
|
|
int32 scrollxOldLog[LOGSIZE];
|
|
int32 scrollxLog[LOGSIZE];
|
|
int32 scrollxTargetLog[LOGSIZE];
|
|
|
|
|
|
void LogMe(int32 in)
|
|
{
|
|
static int32 i;
|
|
|
|
if (in == 0)
|
|
i = 0;
|
|
|
|
previousTimeLog[i] = previousTime;
|
|
aveRenderCycleLog[i] = aveRenderCycle;
|
|
renderCycleStartLog[i] = renderCycleStart;
|
|
lastRenderTimeLog[i] = lastRenderTime;
|
|
renderCycleEndLog[i] = renderCycleEnd;
|
|
timeLeftLog[i] = timeLeft;
|
|
scrollxOldLog[i] = scrollxOld;
|
|
scrollxLog[i] = scrollx;
|
|
scrollxTargetLog[i] = scrollxTarget;
|
|
|
|
if (++i == LOGSIZE)
|
|
i = 0;
|
|
|
|
}
|
|
*/
|
|
|
|
|
|
int32 InitialiseRenderCycle(void) {
|
|
initialTime = SVM_timeGetTime();
|
|
originTime = initialTime;
|
|
totalTime = initialTime + MILLISECSPERCYCLE;
|
|
return RD_OK;
|
|
}
|
|
|
|
|
|
|
|
int32 StartRenderCycle(void) {
|
|
scrollxOld = scrollx;
|
|
scrollyOld = scrolly;
|
|
|
|
startTime = SVM_timeGetTime();
|
|
|
|
if (startTime + renderAverageTime >= totalTime) {
|
|
scrollx = scrollxTarget;
|
|
scrolly = scrollyTarget;
|
|
renderTooSlow = 1;
|
|
} else {
|
|
scrollx = (int16) (scrollxOld + ((scrollxTarget - scrollxOld) * (startTime - initialTime + renderAverageTime)) / (totalTime - initialTime));
|
|
scrolly = (int16) (scrollyOld + ((scrollyTarget - scrollyOld) * (startTime - initialTime + renderAverageTime)) / (totalTime - initialTime));
|
|
renderTooSlow = 0;
|
|
}
|
|
|
|
framesPerGameCycle = 0;
|
|
return RD_OK;
|
|
}
|
|
|
|
|
|
|
|
int32 EndRenderCycle(BOOL *end) {
|
|
int32 time;
|
|
|
|
time = SVM_timeGetTime();
|
|
renderTimeLog[renderCountIndex] = time - startTime;
|
|
startTime += renderTimeLog[renderCountIndex];
|
|
renderAverageTime = (renderTimeLog[0] + renderTimeLog[1] + renderTimeLog[2] + renderTimeLog[3]) >> 2;
|
|
|
|
framesPerGameCycle += 1;
|
|
|
|
if (++renderCountIndex == RENDERAVERAGETOTAL)
|
|
renderCountIndex = 0;
|
|
|
|
if (renderTooSlow) {
|
|
*end = TRUE;
|
|
InitialiseRenderCycle();
|
|
} else if (startTime + renderAverageTime >= totalTime) {
|
|
*end = TRUE;
|
|
originTime = totalTime;
|
|
totalTime += MILLISECSPERCYCLE;
|
|
initialTime = time;
|
|
} else {
|
|
*end = FALSE;
|
|
scrollx = (int16) (scrollxOld + ((scrollxTarget - scrollxOld) * (startTime - initialTime + renderAverageTime)) / (totalTime - initialTime));
|
|
scrolly = (int16) (scrollyOld + ((scrollyTarget - scrollyOld) * (startTime - initialTime + renderAverageTime)) / (totalTime - initialTime));
|
|
}
|
|
|
|
return RD_OK;
|
|
}
|
|
|
|
|
|
int32 SetScrollTarget(int16 sx, int16 sy) {
|
|
scrollxTarget = sx;
|
|
scrollyTarget = sy;
|
|
return RD_OK;
|
|
}
|
|
|
|
int32 CopyScreenBuffer(void) {
|
|
// FIXME: This function no longer seems needed. Calling copy_rect()
|
|
// for the whole screen is slower than the current approach. Not by
|
|
// much, but still...
|
|
return RD_OK;
|
|
}
|
|
|
|
|
|
int32 InitialiseBackgroundLayer(_parallax *p) {
|
|
uint8 *memchunk;
|
|
uint32 *quaddata;
|
|
uint8 zeros;
|
|
uint16 count;
|
|
uint16 i, j, k;
|
|
uint16 x;
|
|
uint8 *data;
|
|
uint8 *dst;
|
|
_parallaxLine *line;
|
|
|
|
debug(2, "InitialiseBackgroundLayer");
|
|
|
|
// This function is called to re-initialise the layers if they have
|
|
// been lost. We know this if the layers have already been assigned.
|
|
|
|
// TODO: Can layers still be lost, or is that a DirectDraw-ism?
|
|
|
|
if (layer == MAXLAYERS)
|
|
CloseBackgroundLayer();
|
|
|
|
if (!p) {
|
|
layer++;
|
|
return RD_OK;
|
|
}
|
|
|
|
xblocks[layer] = (p->w + BLOCKWIDTH - 1) >> BLOCKWBITS;
|
|
yblocks[layer] = (p->h + BLOCKHEIGHT - 1) >> BLOCKHBITS;
|
|
|
|
blockSurfaces[layer] = (Surface **) calloc(xblocks[layer] * yblocks[layer], sizeof(Surface *));
|
|
if (!blockSurfaces[layer])
|
|
return RDERR_OUTOFMEMORY;
|
|
|
|
// Decode the parallax layer into a large chunk of memory
|
|
|
|
memchunk = (uint8 *) malloc(xblocks[layer] * BLOCKWIDTH * yblocks[layer] * BLOCKHEIGHT);
|
|
if (!memchunk)
|
|
return RDERR_OUTOFMEMORY;
|
|
|
|
// We clear not the entire memory chunk, but enough of it to store
|
|
// the entire parallax layer.
|
|
|
|
memset(memchunk, 0, p->w * p->h);
|
|
|
|
for (i = 0; i < p->h; i++) {
|
|
if (p->offset[i] == 0)
|
|
continue;
|
|
|
|
line = (_parallaxLine *) ((uint8 *) p + p->offset[i]);
|
|
data = (uint8 *) line + sizeof(_parallaxLine);
|
|
x = line->offset;
|
|
|
|
dst = memchunk + i * p->w + x;
|
|
|
|
zeros = 0;
|
|
if (line->packets == 0) {
|
|
memcpy(dst, data, p->w);
|
|
continue;
|
|
}
|
|
|
|
for (j = 0; j < line->packets; j++) {
|
|
if (zeros) {
|
|
dst += *data;
|
|
x += *data;
|
|
data++;
|
|
zeros = 0;
|
|
} else if (*data == 0) {
|
|
data++;
|
|
zeros = 1;
|
|
} else {
|
|
count = *data++;
|
|
memcpy(dst, data, count);
|
|
data += count;
|
|
dst += count;
|
|
x += count;
|
|
zeros = 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Now create the surfaces!
|
|
|
|
for (i = 0; i < xblocks[layer] * yblocks[layer]; i++) {
|
|
bool block_has_data = false;
|
|
|
|
data = memchunk + (p->w * BLOCKHEIGHT * (i / xblocks[layer])) + BLOCKWIDTH * (i % xblocks[layer]);
|
|
|
|
quaddata = (uint32 *) data;
|
|
|
|
for (j = 0; j < BLOCKHEIGHT; j++) {
|
|
for (k = 0; k < BLOCKWIDTH / 4; k++) {
|
|
if (*quaddata) {
|
|
block_has_data = true;
|
|
goto bailout;
|
|
}
|
|
quaddata++;
|
|
}
|
|
quaddata += ((p->w - BLOCKWIDTH) / 4);
|
|
}
|
|
|
|
bailout:
|
|
|
|
// Only assign a surface to the block if it contains data.
|
|
|
|
if (block_has_data) {
|
|
blockSurfaces[layer][i] = new Surface(BLOCKWIDTH, BLOCKHEIGHT);
|
|
|
|
// Copy the data into the surfaces.
|
|
dst = blockSurfaces[layer][i]->_pixels;
|
|
for (j = 0; j < BLOCKHEIGHT; j++) {
|
|
memcpy(dst, data, BLOCKWIDTH);
|
|
data += p->w;
|
|
dst += BLOCKWIDTH;
|
|
}
|
|
} else
|
|
blockSurfaces[layer][i] = NULL;
|
|
}
|
|
free(memchunk);
|
|
layer++;
|
|
return RD_OK;
|
|
|
|
}
|
|
|
|
|
|
int32 CloseBackgroundLayer(void) {
|
|
debug(2, "CloseBackgroundLayer");
|
|
|
|
int16 i, j;
|
|
|
|
for (j = 0; j < MAXLAYERS; j++) {
|
|
if (blockSurfaces[j]) {
|
|
for (i = 0; i < xblocks[j] * yblocks[j]; i++)
|
|
if (blockSurfaces[j][i])
|
|
delete blockSurfaces[j][i];
|
|
free(blockSurfaces[j]);
|
|
blockSurfaces[j] = NULL;
|
|
}
|
|
}
|
|
|
|
layer = 0;
|
|
return RD_OK;
|
|
}
|
|
|
|
|
|
int32 EraseSoftwareScreenBuffer(void)
|
|
{
|
|
memset(myScreenBuffer, 0, RENDERWIDE * RENDERDEEP);
|
|
return(RD_OK);
|
|
}
|