scummvm/sword2/driver/render.cpp
Torbjörn Andersson ed698cb635 Added some code to stop producing interpolation frames if the scene has
already reached its scroll target. This keeps BS2 from using all available
CPU time all of the time.

It may still be that we need a mechanism for throttling the frame rate when
the scene is moving towards a scroll target, but my computer isn't really
fast enough to test that.

Two other bugs fixed in the process:

* I think the last frame of the render cycle was rendered, but not
  displayed. If so, that should be fixed now.

* I discovered that there are cases where we do need to clear the screen
  (e.g. at the "Meanwhile..." message when George has found out about the
  Glease Gallery), so I've re-enabled the function and disabled it in the
  render cycle.

svn-id: r9904
2003-08-29 06:42:34 +00:00

1244 lines
29 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 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];
// blockSurfaces stores an array of sub-blocks for each of the parallax layers.
typedef struct {
byte data[BLOCKWIDTH * BLOCKHEIGHT];
bool transparent;
} BlockSurface;
BlockSurface **blockSurfaces[MAXLAYERS] = { 0, 0, 0, 0, 0 };
void UploadRect(ScummVM::Rect *r) {
g_system->copy_rect(lpBackBuffer + r->top * screenWide + r->left,
screenWide, r->left, r->top, r->right - r->left, r->bottom - r->top);
}
void BlitBlockSurface(BlockSurface *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->data;
if (r->top < clip_rect->top) {
src -= BLOCKWIDTH * (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 = lpBackBuffer + r->top * screenWide + r->left;
int i, j;
if (s->transparent) {
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 += BLOCKWIDTH;
dst += screenWide;
}
} else {
for (i = 0; i < r->bottom - r->top; i++) {
memcpy(dst, src, r->right - r->left);
src += BLOCKWIDTH;
dst += screenWide;
}
}
// UploadRect(r);
SetNeedRedraw();
}
#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.
//
// Keeping them separate might be a good idea anyway, for readability.
//
// 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.
//
// The backbuf parameter points to the buffer where the image will eventually
// be drawn. This is only used at the highest graphics detail setting (and not
// always even then) and is used to help anti-alias the image.
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 (with or without anti-aliasing)
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 += screenWide;
}
} 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) {
byte *origDst = dst;
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
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;
}
}
// Anti-aliasing
if (backbuf) {
byte *newDst = (byte *) malloc(dstWidth * dstHeight);
if (!newDst)
return;
memcpy(newDst, origDst, dstWidth);
for (y = 1; y < dstHeight - 1; y++) {
src = origDst + y * dstPitch;
dst = newDst + y * dstWidth;
*dst++ = *src++;
for (x = 1; x < dstWidth - 1; x++) {
byte pt[5];
byte *p = backbuf + y * 640 + x;
int count = 0;
if (*src) {
count++;
pt[0] = *src;
} else
pt[0] = *p;
pt[1] = *(src - dstPitch);
if (pt[1] == 0)
pt[1] = *(p - 640);
else
count++;
pt[2] = *(src - 1);
if (pt[2] == 0)
pt[2] = *(p - 1);
else
count++;
pt[3] = *(src + 1);
if (pt[3] == 0)
pt[3] = *(p + 1);
else
count++;
pt[4] = *(src + dstPitch);
if (pt[4] == 0)
pt[4] = *(p + 640);
else
count++;
if (count) {
int red = palCopy[pt[0]][0] << 2;
int green = palCopy[pt[0]][1] << 2;
int blue = palCopy[pt[0]][2] << 2;
for (i = 1; i < 5; i++) {
red += palCopy[pt[i]][0];
green += palCopy[pt[i]][1];
blue += palCopy[pt[i]][2];
}
*dst++ = QuickMatch((uint8) (red >> 3), (uint8) (green >> 3), (uint8) (blue >> 3));
} else
*dst++ = 0;
src++;
}
*dst++ = *src++;
}
memcpy(dst, src, dstWidth);
src = newDst;
dst = origDst;
for (i = 0; i < dstHeight; i++) {
memcpy(dst, src, dstWidth);
dst += dstPitch;
src += dstWidth;
}
free(newDst);
}
}
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])
free(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;
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 = screenWide;
clip_rect.top = MENUDEEP;
clip_rect.bottom = screenDeep - MENUDEEP;
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;
BlitBlockSurface(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;
}
*/
// Uncomment this when benchmarking the drawing routines.
#define LIMIT_FRAME_RATE
int32 InitialiseRenderCycle(void) {
initialTime = SVM_timeGetTime();
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;
}
// FIXME: Move this to some better place?
void sleepUntil(int32 time) {
while ((int32) SVM_timeGetTime() < time) {
g_sword2->parseEvents();
// Make sure menu animations and fades don't suffer
ProcessMenu();
if (ServiceWindows() == RDERR_APPCLOSED)
break;
g_system->delay_msecs(10);
}
}
int32 EndRenderCycle(BOOL *end) {
int32 time;
time = SVM_timeGetTime();
renderTimeLog[renderCountIndex] = time - startTime;
startTime = time;
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;
totalTime += MILLISECSPERCYCLE;
initialTime = time;
#ifdef LIMIT_FRAME_RATE
} else if (scrollxTarget == scrollx && scrollyTarget == scrolly) {
// If we have already reached the scroll target sleep for the
// rest of the render cycle.
*end = TRUE;
sleepUntil(totalTime);
initialTime = SVM_timeGetTime();
totalTime += MILLISECSPERCYCLE;
#endif
} else {
*end = FALSE;
// This is an attempt to ensure that we always reach the scroll
// target. Otherwise the game frequently tries to pump out new
// interpolation frames without ever getting anywhere.
if (ABS(scrollx - scrollxTarget) <= 1 && ABS(scrolly - scrollyTarget) <= 1) {
scrollx = scrollxTarget;
scrolly = scrollyTarget;
} else {
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 to be needed. We copy the
// back buffer to the screen in ServiceWindows() instead.
return RD_OK;
}
int32 InitialiseBackgroundLayer(_parallax *p) {
uint8 *memchunk;
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] = (BlockSurface **) calloc(xblocks[layer] * yblocks[layer], sizeof(BlockSurface *));
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;
bool block_is_transparent = false;
data = memchunk + (p->w * BLOCKHEIGHT * (i / xblocks[layer])) + BLOCKWIDTH * (i % xblocks[layer]);
for (j = 0; j < BLOCKHEIGHT; j++) {
for (k = 0; k < BLOCKWIDTH; k++) {
if (data[j * p->w + k])
block_has_data = true;
else
block_is_transparent = true;
}
}
// Only assign a surface to the block if it contains data.
if (block_has_data) {
blockSurfaces[layer][i] = (BlockSurface *) malloc(sizeof(BlockSurface));
// Copy the data into the surfaces.
dst = blockSurfaces[layer][i]->data;
for (j = 0; j < BLOCKHEIGHT; j++) {
memcpy(dst, data, BLOCKWIDTH);
data += p->w;
dst += BLOCKWIDTH;
}
blockSurfaces[layer][i]->transparent = block_is_transparent;
} 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])
free(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);
}