2003-07-28 01:47:41 +00:00
|
|
|
/* 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 : sprite.c
|
|
|
|
// Created : 23rd September 1996
|
|
|
|
// By : P.R.Porter
|
|
|
|
//
|
|
|
|
// Summary : This module holds the sprite drawing functions.
|
|
|
|
//
|
|
|
|
// Functions
|
|
|
|
// ---------
|
|
|
|
//
|
|
|
|
// --------------------------------------------------------------------------
|
|
|
|
//
|
|
|
|
// int32 CreateSurface(_spriteInfo *s, uint32 *surface)
|
|
|
|
//
|
|
|
|
// Creates a sprite surface in video memory (if possible) and returns it's
|
|
|
|
// handle in surface.
|
|
|
|
//
|
|
|
|
// ---------------------------------------------------------------------------
|
|
|
|
//
|
2003-09-08 06:38:16 +00:00
|
|
|
// int32 DrawSurface(_spriteInfo *s, uint32 surface, ScummVM::Rect *clipRect)
|
2003-07-28 01:47:41 +00:00
|
|
|
//
|
|
|
|
// Draws the sprite surface created earlier. If the surface has been lost,
|
|
|
|
// it is recreated.
|
|
|
|
//
|
|
|
|
// ---------------------------------------------------------------------------
|
|
|
|
//
|
|
|
|
// int32 DeleteSurface(uint32 surface)
|
|
|
|
//
|
|
|
|
// Deletes a surface from video memory.
|
|
|
|
//
|
|
|
|
// --------------------------------------------------------------------------
|
|
|
|
//
|
|
|
|
// int32 DrawSprite(_spriteInfo *s)
|
|
|
|
//
|
|
|
|
// Draws a sprite onto the screen. The _spriteInfo structure holds all of
|
|
|
|
// the information needed to draw the sprite - see driver96.h for details
|
|
|
|
//
|
|
|
|
//=============================================================================
|
|
|
|
|
2003-07-28 03:12:49 +00:00
|
|
|
#include "stdafx.h"
|
2003-07-28 01:47:41 +00:00
|
|
|
#include "driver96.h"
|
|
|
|
#include "d_draw.h"
|
|
|
|
#include "render.h"
|
|
|
|
#include "menu.h"
|
|
|
|
#include "palette.h"
|
2003-08-29 06:42:34 +00:00
|
|
|
#include "rdwin.h"
|
2003-07-28 01:47:41 +00:00
|
|
|
|
|
|
|
char shitColourTable[1024];
|
|
|
|
static uint8 *lightMask = 0;
|
|
|
|
|
2003-09-23 17:24:45 +00:00
|
|
|
// --------------------------------------------------------------------------
|
2003-07-28 01:47:41 +00:00
|
|
|
//
|
2003-09-23 17:24:45 +00:00
|
|
|
// int32 MirrorSprite(uint8 *dst, uint8 *src, int16 w, int16 h)
|
2003-07-28 01:47:41 +00:00
|
|
|
//
|
2003-09-23 17:24:45 +00:00
|
|
|
// This function takes the sprite pointed to by src and creates a mirror
|
|
|
|
// image of it in dst.
|
2003-07-28 01:47:41 +00:00
|
|
|
//
|
2003-09-23 17:24:45 +00:00
|
|
|
// --------------------------------------------------------------------------
|
2003-07-28 01:47:41 +00:00
|
|
|
|
2003-09-23 17:24:45 +00:00
|
|
|
int32 MirrorSprite(uint8 *dst, uint8 *src, int16 w, int16 h) {
|
2003-07-28 01:47:41 +00:00
|
|
|
int16 x, y;
|
|
|
|
|
2003-09-23 17:24:45 +00:00
|
|
|
for (y = 0; y < h; y++) {
|
|
|
|
for (x = 0; x < w; x++) {
|
|
|
|
*dst++ = *(src + w - x - 1);
|
2003-07-28 01:47:41 +00:00
|
|
|
}
|
|
|
|
src += w;
|
|
|
|
}
|
|
|
|
|
2003-09-23 17:24:45 +00:00
|
|
|
return RD_OK;
|
2003-07-28 01:47:41 +00:00
|
|
|
}
|
|
|
|
|
2003-09-23 17:24:45 +00:00
|
|
|
// --------------------------------------------------------------------------
|
2003-07-28 01:47:41 +00:00
|
|
|
//
|
2003-09-23 17:24:45 +00:00
|
|
|
// int32 DecompressRLE256(uint8 *dest, uint8 *source, int32 decompSize)
|
2003-07-28 01:47:41 +00:00
|
|
|
//
|
2003-09-23 17:24:45 +00:00
|
|
|
// This function takes a compressed frame of a sprite (with up to 256 colours)
|
|
|
|
// and decompresses it into the area of memory marked by the destination
|
|
|
|
// pointer. The decompSize is used to measure when the decompression process
|
|
|
|
// has completed.
|
2003-07-28 01:47:41 +00:00
|
|
|
//
|
2003-09-23 17:24:45 +00:00
|
|
|
// --------------------------------------------------------------------------
|
|
|
|
|
|
|
|
int32 DecompressRLE256(uint8 *dest, uint8 *source, int32 decompSize) {
|
2003-07-28 01:47:41 +00:00
|
|
|
// PARAMETERS:
|
2003-09-23 17:24:45 +00:00
|
|
|
// source points to the start of the sprite data for input
|
2003-07-28 01:47:41 +00:00
|
|
|
// decompSize gives size of decompressed data in bytes
|
2003-09-23 17:24:45 +00:00
|
|
|
// dest points to start of destination buffer for decompressed
|
|
|
|
// data
|
2003-07-28 01:47:41 +00:00
|
|
|
|
2003-09-23 17:24:45 +00:00
|
|
|
uint8 headerByte; // block header byte
|
|
|
|
uint8 *endDest = dest + decompSize; // pointer to byte after end of decomp buffer
|
2003-07-28 01:47:41 +00:00
|
|
|
int32 rv;
|
|
|
|
|
2003-09-23 17:24:45 +00:00
|
|
|
while(1) {
|
2003-07-28 01:47:41 +00:00
|
|
|
// FLAT block
|
2003-09-23 17:24:45 +00:00
|
|
|
// read FLAT block header & increment 'scan' to first pixel
|
|
|
|
// of block
|
|
|
|
headerByte = *source++;
|
2003-07-28 01:47:41 +00:00
|
|
|
|
2003-09-23 17:24:45 +00:00
|
|
|
// if this isn't a zero-length block
|
|
|
|
if (headerByte) {
|
|
|
|
if (dest + headerByte > endDest) {
|
2003-07-28 01:47:41 +00:00
|
|
|
rv = 1;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2003-09-23 17:24:45 +00:00
|
|
|
// set the next 'headerByte' pixels to the next colour
|
|
|
|
// at 'source'
|
|
|
|
memset(dest, *source, headerByte);
|
2003-07-28 01:47:41 +00:00
|
|
|
|
2003-09-23 17:24:45 +00:00
|
|
|
// increment destination pointer to just after this
|
|
|
|
// block
|
|
|
|
dest += headerByte;
|
|
|
|
|
|
|
|
// increment source pointer to just after this colour
|
|
|
|
source++;
|
|
|
|
|
|
|
|
// if we've decompressed all of the data
|
|
|
|
if (dest == endDest) {
|
|
|
|
rv = 0; // return "OK"
|
2003-07-28 01:47:41 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2003-09-23 17:24:45 +00:00
|
|
|
|
2003-07-28 01:47:41 +00:00
|
|
|
// RAW block
|
2003-09-23 17:24:45 +00:00
|
|
|
// read RAW block header & increment 'scan' to first pixel of
|
|
|
|
// block
|
|
|
|
headerByte = *source++;
|
2003-07-28 01:47:41 +00:00
|
|
|
|
2003-09-23 17:24:45 +00:00
|
|
|
// if this isn't a zero-length block
|
|
|
|
if (headerByte) {
|
|
|
|
if (dest + headerByte > endDest) {
|
2003-07-28 01:47:41 +00:00
|
|
|
rv = 1;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2003-09-23 17:24:45 +00:00
|
|
|
// copy the next 'headerByte' pixels from source to
|
|
|
|
// destination
|
|
|
|
memcpy(dest,source,headerByte);
|
|
|
|
|
|
|
|
// increment destination pointer to just after this
|
|
|
|
// block
|
|
|
|
dest += headerByte;
|
|
|
|
|
|
|
|
// increment source pointer to just after this block
|
|
|
|
source += headerByte;
|
2003-07-28 01:47:41 +00:00
|
|
|
|
2003-09-23 17:24:45 +00:00
|
|
|
// if we've decompressed all of the data
|
|
|
|
if (dest == endDest) {
|
|
|
|
rv = 0; // return "OK"
|
2003-07-28 01:47:41 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2003-09-23 17:24:45 +00:00
|
|
|
return rv;
|
2003-07-28 01:47:41 +00:00
|
|
|
}
|
|
|
|
|
2003-09-23 17:24:45 +00:00
|
|
|
// --------------------------------------------------------------------------
|
2003-07-28 01:47:41 +00:00
|
|
|
//
|
2003-09-23 17:24:45 +00:00
|
|
|
// void UnwindRaw16(uint8 *dest, uint8 *source, uint8 blockSize, uint8 *colTable)
|
2003-07-28 01:47:41 +00:00
|
|
|
//
|
2003-09-23 17:24:45 +00:00
|
|
|
// This function unwinds a run of colour 16 data into 256 colour palette data.
|
|
|
|
// --------------------------------------------------------------------------
|
2003-07-28 01:47:41 +00:00
|
|
|
|
2003-09-23 17:24:45 +00:00
|
|
|
void UnwindRaw16(uint8 *dest, uint8 *source, uint8 blockSize, uint8 *colTable) {
|
|
|
|
// for each pair of pixels
|
|
|
|
while (blockSize > 1) {
|
|
|
|
// 1st colour = number in table at position given by upper
|
|
|
|
// nibble of source byte
|
|
|
|
*dest++ = colTable[(*source) >> 4];
|
2003-07-28 01:47:41 +00:00
|
|
|
|
2003-09-23 17:24:45 +00:00
|
|
|
// 2nd colour = number in table at position given by lower
|
|
|
|
// nibble of source byte
|
|
|
|
*dest++ = colTable[(*source) & 0x0f];
|
2003-07-28 01:47:41 +00:00
|
|
|
|
2003-09-23 17:24:45 +00:00
|
|
|
// point to next source byte
|
|
|
|
source++;
|
2003-07-28 01:47:41 +00:00
|
|
|
|
2003-09-23 17:24:45 +00:00
|
|
|
// decrement count of how many pixels left to read
|
|
|
|
blockSize -= 2;
|
|
|
|
}
|
|
|
|
|
|
|
|
// if there's a final odd pixel
|
|
|
|
if (blockSize) {
|
|
|
|
// colour = number in table at position given by upper nibble
|
|
|
|
// of source byte
|
|
|
|
*dest++ = colTable[(*source) >> 4];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// --------------------------------------------------------------------------
|
2003-07-28 01:47:41 +00:00
|
|
|
//
|
2003-09-23 17:24:45 +00:00
|
|
|
// int32 DecompressRLE16(uint8 *dest, uint8 *source, int32 decompSize, uint8 *colTable)
|
2003-07-28 01:47:41 +00:00
|
|
|
//
|
2003-09-23 17:24:45 +00:00
|
|
|
// This function takes a compressed frame of a sprite (with up to 16 colours)
|
|
|
|
// and decompresses it into the area of memory marked by the destination
|
|
|
|
// pointer. The decompSize is used to measure when the decompression process
|
|
|
|
// has completed. The colour table which maps the 16 encoded colours to the
|
|
|
|
// current palette is passed in to colTable.
|
2003-07-28 01:47:41 +00:00
|
|
|
//
|
2003-09-23 17:24:45 +00:00
|
|
|
// --------------------------------------------------------------------------
|
|
|
|
|
|
|
|
int32 DecompressRLE16(uint8 *dest, uint8 *source, int32 decompSize, uint8 *colTable) {
|
2003-07-28 01:47:41 +00:00
|
|
|
// PARAMETERS:
|
2003-09-23 17:24:45 +00:00
|
|
|
// source points to the start of the sprite data for input
|
2003-07-28 01:47:41 +00:00
|
|
|
// decompSize gives size of decompressed data in bytes
|
2003-09-23 17:24:45 +00:00
|
|
|
// dest points to start of destination buffer for decompressed
|
|
|
|
// data
|
|
|
|
// colTable points to a 16-byte table of colours used to encode
|
|
|
|
// the RAW pixels into 4-bits each
|
2003-07-28 01:47:41 +00:00
|
|
|
|
2003-09-23 17:24:45 +00:00
|
|
|
uint8 headerByte; // block header byte
|
|
|
|
uint8 *endDest = dest + decompSize; // pointer to byte after end of decomp buffer
|
2003-07-28 01:47:41 +00:00
|
|
|
int32 rv;
|
|
|
|
|
2003-09-23 17:24:45 +00:00
|
|
|
while(1) {
|
2003-07-28 01:47:41 +00:00
|
|
|
// FLAT block
|
2003-09-23 17:24:45 +00:00
|
|
|
// read FLAT block header & increment 'scan' to first pixel
|
|
|
|
// of block
|
|
|
|
headerByte = *source++;
|
2003-07-28 01:47:41 +00:00
|
|
|
|
2003-09-23 17:24:45 +00:00
|
|
|
// if this isn't a zero-length block
|
|
|
|
if (headerByte) {
|
|
|
|
if (dest + headerByte > endDest) {
|
2003-07-28 01:47:41 +00:00
|
|
|
rv = 1;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2003-09-23 17:24:45 +00:00
|
|
|
// set the next 'headerByte' pixels to the next
|
|
|
|
// colour at 'source'
|
|
|
|
memset(dest, *source, headerByte);
|
|
|
|
|
|
|
|
// increment destination pointer to just after this
|
|
|
|
// block
|
|
|
|
dest += headerByte;
|
|
|
|
|
|
|
|
// increment source pointer to just after this colour
|
|
|
|
source++;
|
2003-07-28 01:47:41 +00:00
|
|
|
|
2003-09-23 17:24:45 +00:00
|
|
|
// if we've decompressed all of the data
|
|
|
|
if (dest == endDest) {
|
|
|
|
rv = 0; // return "OK"
|
2003-07-28 01:47:41 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2003-09-23 17:24:45 +00:00
|
|
|
|
2003-07-28 01:47:41 +00:00
|
|
|
// RAW block
|
2003-09-23 17:24:45 +00:00
|
|
|
// read RAW block header & increment 'scan' to first pixel of
|
|
|
|
// block
|
|
|
|
headerByte = *source++;
|
2003-07-28 01:47:41 +00:00
|
|
|
|
2003-09-23 17:24:45 +00:00
|
|
|
// if this isn't a zero-length block
|
|
|
|
if (headerByte) {
|
|
|
|
if (dest + headerByte > endDest) {
|
2003-07-28 01:47:41 +00:00
|
|
|
rv = 1;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2003-09-23 17:24:45 +00:00
|
|
|
// copy the next 'headerByte' pixels from source to
|
|
|
|
// destination (NB. 2 pixels per byte)
|
|
|
|
UnwindRaw16(dest, source, headerByte, colTable);
|
2003-07-28 01:47:41 +00:00
|
|
|
|
2003-09-23 17:24:45 +00:00
|
|
|
// increment destination pointer to just after this
|
|
|
|
// block
|
|
|
|
dest += headerByte;
|
2003-07-28 01:47:41 +00:00
|
|
|
|
2003-09-23 17:24:45 +00:00
|
|
|
// increment source pointer to just after this block
|
|
|
|
// (NB. headerByte gives pixels, so /2 for bytes)
|
|
|
|
source += (headerByte + 1) / 2;
|
2003-07-28 01:47:41 +00:00
|
|
|
|
2003-09-23 17:24:45 +00:00
|
|
|
// if we've decompressed all of the data
|
|
|
|
if (dest >= endDest) {
|
|
|
|
rv = 0; // return "OK"
|
|
|
|
break;
|
2003-07-28 01:47:41 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2003-09-23 17:24:45 +00:00
|
|
|
return rv;
|
2003-07-28 01:47:41 +00:00
|
|
|
}
|
|
|
|
|
2003-09-08 17:24:00 +00:00
|
|
|
// The surface functions are used by the in-game dialogs and for displaying
|
|
|
|
// cutscene subtitles. Everything that isn't needed for those cases (blending,
|
|
|
|
// scaling, etc.) has been removed.
|
2003-07-28 01:47:41 +00:00
|
|
|
|
2003-08-23 13:02:21 +00:00
|
|
|
int32 CreateSurface(_spriteInfo *s, uint8 **sprite) {
|
|
|
|
uint8 *newSprite;
|
2003-07-28 01:47:41 +00:00
|
|
|
|
2003-08-23 13:02:21 +00:00
|
|
|
*sprite = (uint8 *) malloc(s->w * s->h);
|
|
|
|
if (!*sprite)
|
|
|
|
return RDERR_OUTOFMEMORY;
|
2003-07-28 01:47:41 +00:00
|
|
|
|
2003-08-23 13:02:21 +00:00
|
|
|
if (s->type & RDSPR_NOCOMPRESSION) {
|
|
|
|
memcpy(*sprite, s->data, s->w * s->h);
|
|
|
|
} else {
|
2003-09-08 06:38:16 +00:00
|
|
|
if ((s->type >> 8) == (RDSPR_RLE16 >> 8)) {
|
2003-08-23 13:02:21 +00:00
|
|
|
if (DecompressRLE16(*sprite, s->data, s->w * s->h, s->colourTable)) {
|
|
|
|
free(*sprite);
|
|
|
|
return RDERR_DECOMPRESSION;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (DecompressRLE256(*sprite, s->data, s->w * s->h)) {
|
|
|
|
free(*sprite);
|
|
|
|
return RDERR_DECOMPRESSION;
|
|
|
|
}
|
2003-07-28 01:47:41 +00:00
|
|
|
}
|
|
|
|
|
2003-08-23 13:02:21 +00:00
|
|
|
if (s->type & RDSPR_FLIP) {
|
2003-07-28 01:47:41 +00:00
|
|
|
newSprite = (uint8 *) malloc(s->w * s->h);
|
2003-08-23 13:02:21 +00:00
|
|
|
if (!newSprite) {
|
|
|
|
free(*sprite);
|
|
|
|
return RDERR_OUTOFMEMORY;
|
2003-07-28 01:47:41 +00:00
|
|
|
}
|
2003-08-23 13:02:21 +00:00
|
|
|
MirrorSprite(newSprite, *sprite, s->w, s->h);
|
|
|
|
free(*sprite);
|
|
|
|
*sprite = newSprite;
|
2003-07-28 01:47:41 +00:00
|
|
|
}
|
|
|
|
}
|
2003-09-08 06:38:16 +00:00
|
|
|
|
2003-08-23 13:02:21 +00:00
|
|
|
return RD_OK;
|
2003-07-28 01:47:41 +00:00
|
|
|
}
|
|
|
|
|
2003-09-08 06:38:16 +00:00
|
|
|
void DrawSurface(_spriteInfo *s, uint8 *surface, ScummVM::Rect *clipRect) {
|
2003-08-23 13:02:21 +00:00
|
|
|
ScummVM::Rect rd, rs;
|
2003-09-08 06:38:16 +00:00
|
|
|
uint16 x, y, srcPitch;
|
|
|
|
uint8 *src, *dst;
|
2003-07-28 01:47:41 +00:00
|
|
|
|
|
|
|
rs.left = 0;
|
|
|
|
rs.right = s->w;
|
|
|
|
rs.top = 0;
|
|
|
|
rs.bottom = s->h;
|
2003-08-23 13:02:21 +00:00
|
|
|
|
2003-09-08 06:38:16 +00:00
|
|
|
srcPitch = s->w;
|
2003-07-28 01:47:41 +00:00
|
|
|
|
2003-09-08 17:18:38 +00:00
|
|
|
if (s->type & RDSPR_DISPLAYALIGN) {
|
|
|
|
rd.top = s->y;
|
|
|
|
rd.left = s->x;
|
|
|
|
} else {
|
|
|
|
rd.top = s->y - scrolly;
|
|
|
|
rd.left = s->x - scrollx;
|
|
|
|
}
|
|
|
|
|
2003-08-23 13:02:21 +00:00
|
|
|
rd.right = rd.left + rs.right;
|
|
|
|
rd.bottom = rd.top + rs.bottom;
|
|
|
|
|
2003-09-08 06:38:16 +00:00
|
|
|
if (clipRect) {
|
|
|
|
if (clipRect->left > rd.left) {
|
|
|
|
rs.left += (clipRect->left - rd.left);
|
|
|
|
rd.left = clipRect->left;
|
|
|
|
}
|
2003-07-28 01:47:41 +00:00
|
|
|
|
2003-09-08 06:38:16 +00:00
|
|
|
if (clipRect->top > rd.top) {
|
|
|
|
rs.top += (clipRect->top - rd.top);
|
|
|
|
rd.top = clipRect->top;
|
2003-07-28 01:47:41 +00:00
|
|
|
}
|
2003-08-23 13:02:21 +00:00
|
|
|
|
2003-09-08 06:38:16 +00:00
|
|
|
if (clipRect->right < rd.right) {
|
|
|
|
rd.right = clipRect->right;
|
|
|
|
}
|
2003-08-23 13:02:21 +00:00
|
|
|
|
2003-09-08 06:38:16 +00:00
|
|
|
if (clipRect->bottom < rd.bottom) {
|
|
|
|
rd.bottom = clipRect->bottom;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (rd.width() <= 0 || rd.height() <= 0)
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
src = surface + rs.top * srcPitch + rs.left;
|
2003-08-28 06:36:15 +00:00
|
|
|
dst = lpBackBuffer + screenWide * rd.top + rd.left;
|
2003-08-23 13:02:21 +00:00
|
|
|
|
2003-09-08 06:38:16 +00:00
|
|
|
// Surfaces are always transparent.
|
|
|
|
|
|
|
|
for (y = 0; y < rd.height(); y++) {
|
|
|
|
for (x = 0; x < rd.width(); x++) {
|
|
|
|
if (src[x])
|
|
|
|
dst[x] = src[x];
|
2003-07-28 01:47:41 +00:00
|
|
|
}
|
2003-08-23 13:02:21 +00:00
|
|
|
src += srcPitch;
|
2003-08-28 06:36:15 +00:00
|
|
|
dst += screenWide;
|
2003-07-28 01:47:41 +00:00
|
|
|
}
|
|
|
|
|
2003-09-08 06:38:16 +00:00
|
|
|
UploadRect(&rd);
|
2003-08-29 06:42:34 +00:00
|
|
|
SetNeedRedraw();
|
2003-07-28 01:47:41 +00:00
|
|
|
}
|
|
|
|
|
2003-09-08 06:38:16 +00:00
|
|
|
void DeleteSurface(uint8 *surface) {
|
2003-08-23 13:02:21 +00:00
|
|
|
free(surface);
|
2003-07-28 01:47:41 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#define SCALE_MAXWIDTH 512
|
|
|
|
#define SCALE_MAXHEIGHT 512
|
|
|
|
|
|
|
|
uint16 xScale[SCALE_MAXWIDTH];
|
|
|
|
uint16 yScale[SCALE_MAXHEIGHT];
|
|
|
|
|
2003-09-23 17:24:45 +00:00
|
|
|
// FIXME: I'm sure this could be optimized. There's plenty of data copying and
|
|
|
|
// mallocing here.
|
|
|
|
|
2003-08-20 06:51:35 +00:00
|
|
|
int32 DrawSprite(_spriteInfo *s) {
|
2003-08-19 08:47:09 +00:00
|
|
|
uint8 *src, *dst;
|
|
|
|
uint8 *sprite, *newSprite;
|
2003-08-26 06:53:00 +00:00
|
|
|
uint8 *backbuf = NULL;
|
2003-08-22 07:04:50 +00:00
|
|
|
uint8 red, green, blue;
|
2003-08-20 06:51:35 +00:00
|
|
|
uint16 scale;
|
2003-08-19 08:47:09 +00:00
|
|
|
int16 i, j;
|
2003-08-20 06:51:35 +00:00
|
|
|
uint16 srcPitch;
|
|
|
|
bool freeSprite = false;
|
2003-08-26 06:53:00 +00:00
|
|
|
bool clipped = false;
|
2003-08-19 08:47:09 +00:00
|
|
|
ScummVM::Rect rd, rs;
|
|
|
|
|
2003-08-20 06:51:35 +00:00
|
|
|
// -----------------------------------------------------------------
|
|
|
|
// Decompression and mirroring
|
|
|
|
// -----------------------------------------------------------------
|
2003-08-19 08:47:09 +00:00
|
|
|
|
|
|
|
if (s->type & RDSPR_NOCOMPRESSION)
|
|
|
|
sprite = s->data;
|
|
|
|
else {
|
|
|
|
sprite = (uint8 *) malloc(s->w * s->h);
|
2003-08-20 06:51:35 +00:00
|
|
|
freeSprite = true;
|
2003-08-19 08:47:09 +00:00
|
|
|
if (!sprite)
|
|
|
|
return RDERR_OUTOFMEMORY;
|
2003-09-08 06:38:16 +00:00
|
|
|
if ((s->type >> 8) == (RDSPR_RLE16 >> 8)) {
|
2003-08-20 06:51:35 +00:00
|
|
|
if (DecompressRLE16(sprite, s->data, s->w * s->h, s->colourTable)) {
|
|
|
|
free(sprite);
|
2003-08-19 08:47:09 +00:00
|
|
|
return RDERR_DECOMPRESSION;
|
2003-08-20 06:51:35 +00:00
|
|
|
}
|
2003-08-19 08:47:09 +00:00
|
|
|
} else {
|
2003-08-20 06:51:35 +00:00
|
|
|
if (DecompressRLE256(sprite, s->data, s->w * s->h)) {
|
|
|
|
free(sprite);
|
2003-08-19 08:47:09 +00:00
|
|
|
return RDERR_DECOMPRESSION;
|
2003-08-20 06:51:35 +00:00
|
|
|
}
|
2003-08-19 08:47:09 +00:00
|
|
|
}
|
2003-08-20 06:51:35 +00:00
|
|
|
}
|
2003-08-19 08:47:09 +00:00
|
|
|
|
2003-08-20 06:51:35 +00:00
|
|
|
if (s->type & RDSPR_FLIP) {
|
|
|
|
newSprite = (uint8 *) malloc(s->w * s->h);
|
|
|
|
if (newSprite == NULL) {
|
|
|
|
if (freeSprite)
|
2003-08-19 08:47:09 +00:00
|
|
|
free(sprite);
|
2003-08-20 06:51:35 +00:00
|
|
|
return RDERR_OUTOFMEMORY;
|
2003-08-19 08:47:09 +00:00
|
|
|
}
|
2003-08-20 06:51:35 +00:00
|
|
|
MirrorSprite(newSprite, sprite, s->w, s->h);
|
|
|
|
if (freeSprite)
|
|
|
|
free(sprite);
|
|
|
|
sprite = newSprite;
|
|
|
|
freeSprite = true;
|
2003-08-19 08:47:09 +00:00
|
|
|
}
|
|
|
|
|
2003-08-20 06:51:35 +00:00
|
|
|
// -----------------------------------------------------------------
|
|
|
|
// Positioning and clipping.
|
|
|
|
// -----------------------------------------------------------------
|
2003-08-19 08:47:09 +00:00
|
|
|
|
2003-08-20 06:51:35 +00:00
|
|
|
if (!(s->type & RDSPR_DISPLAYALIGN)) {
|
|
|
|
s->x += parallaxScrollx;
|
|
|
|
s->y += parallaxScrolly;
|
|
|
|
}
|
2003-08-19 08:47:09 +00:00
|
|
|
|
2003-08-20 06:51:35 +00:00
|
|
|
s->y += 40;
|
|
|
|
|
|
|
|
// A scale factor 0 or 256 means don't scale. Why do they use two
|
|
|
|
// different values to mean the same thing? Normalize it here for
|
|
|
|
// convenience.
|
|
|
|
|
|
|
|
scale = (s->scale == 0) ? 256 : s->scale;
|
|
|
|
|
|
|
|
rs.top = 0;
|
|
|
|
rs.left = 0;
|
|
|
|
|
|
|
|
if (scale != 256) {
|
|
|
|
rs.right = s->scaledWidth;
|
|
|
|
rs.bottom = s->scaledHeight;
|
|
|
|
srcPitch = s->scaledWidth;
|
|
|
|
} else {
|
2003-08-19 08:47:09 +00:00
|
|
|
rs.right = s->w;
|
2003-08-20 06:51:35 +00:00
|
|
|
rs.bottom = s->h;
|
|
|
|
srcPitch = s->w;
|
|
|
|
}
|
2003-08-19 08:47:09 +00:00
|
|
|
|
2003-08-20 06:51:35 +00:00
|
|
|
rd.top = s->y;
|
|
|
|
rd.left = s->x;
|
|
|
|
|
|
|
|
if (!(s->type & RDSPR_DISPLAYALIGN)) {
|
|
|
|
rd.top -= scrolly;
|
|
|
|
rd.left -= scrollx;
|
|
|
|
}
|
|
|
|
|
|
|
|
rd.right = rd.left + rs.right;
|
|
|
|
rd.bottom = rd.top + rs.bottom;
|
|
|
|
|
2003-08-30 10:23:40 +00:00
|
|
|
// Check if the sprite would end up completely outside the screen.
|
|
|
|
|
|
|
|
if (rd.left > 640 || rd.top > 440 || rd.right < 0 || rd.bottom < 40) {
|
|
|
|
if (freeSprite)
|
|
|
|
free(sprite);
|
|
|
|
return RD_OK;
|
|
|
|
}
|
|
|
|
|
2003-08-20 06:51:35 +00:00
|
|
|
if (rd.top < 40) {
|
2003-08-30 10:23:40 +00:00
|
|
|
rs.top = 40 - rd.top;
|
2003-08-20 06:51:35 +00:00
|
|
|
rd.top = 40;
|
2003-08-26 06:53:00 +00:00
|
|
|
clipped = true;
|
2003-08-20 06:51:35 +00:00
|
|
|
}
|
|
|
|
if (rd.bottom > 440) {
|
|
|
|
rd.bottom = 440;
|
2003-08-30 10:23:40 +00:00
|
|
|
rs.bottom = rs.top + (rd.bottom - rd.top);
|
2003-08-26 06:53:00 +00:00
|
|
|
clipped = true;
|
2003-08-20 06:51:35 +00:00
|
|
|
}
|
|
|
|
if (rd.left < 0) {
|
2003-08-30 10:23:40 +00:00
|
|
|
rs.left = -rd.left;
|
2003-08-20 06:51:35 +00:00
|
|
|
rd.left = 0;
|
2003-08-26 06:53:00 +00:00
|
|
|
clipped = true;
|
2003-08-20 06:51:35 +00:00
|
|
|
}
|
|
|
|
if (rd.right > 640) {
|
|
|
|
rd.right = 640;
|
2003-08-30 10:23:40 +00:00
|
|
|
rs.right = rs.left + (rd.right - rd.left);
|
2003-08-26 06:53:00 +00:00
|
|
|
clipped = true;
|
2003-08-20 06:51:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// -----------------------------------------------------------------
|
|
|
|
// Scaling
|
|
|
|
// -----------------------------------------------------------------
|
|
|
|
|
|
|
|
if (scale != 256) {
|
2003-08-26 06:53:00 +00:00
|
|
|
if ((renderCaps & RDBLTFX_ARITHMETICSTRETCH) && !clipped)
|
2003-08-28 06:36:15 +00:00
|
|
|
backbuf = lpBackBuffer + screenWide * rd.top + rd.left;
|
2003-08-26 06:53:00 +00:00
|
|
|
|
|
|
|
|
2003-08-20 06:51:35 +00:00
|
|
|
if (s->scaledWidth > SCALE_MAXWIDTH || s->scaledHeight > SCALE_MAXHEIGHT) {
|
|
|
|
if (freeSprite)
|
|
|
|
free(sprite);
|
|
|
|
return RDERR_NOTIMPLEMENTED;
|
2003-08-19 08:47:09 +00:00
|
|
|
}
|
2003-08-20 06:51:35 +00:00
|
|
|
|
|
|
|
newSprite = (uint8 *) malloc(s->scaledWidth * s->scaledHeight);
|
|
|
|
if (newSprite == NULL) {
|
|
|
|
if (freeSprite)
|
|
|
|
free(sprite);
|
|
|
|
return RDERR_OUTOFMEMORY;
|
2003-08-19 08:47:09 +00:00
|
|
|
}
|
2003-08-20 06:51:35 +00:00
|
|
|
|
|
|
|
if (scale < 256) {
|
2003-08-26 06:53:00 +00:00
|
|
|
SquashImage(newSprite, s->scaledWidth, s->scaledWidth, s->scaledHeight, sprite, s->w, s->w, s->h, backbuf);
|
2003-08-20 06:51:35 +00:00
|
|
|
} else {
|
|
|
|
if (s->scale > 512) {
|
|
|
|
if (freeSprite)
|
|
|
|
free(sprite);
|
|
|
|
return RDERR_INVALIDSCALING;
|
|
|
|
}
|
2003-08-26 06:53:00 +00:00
|
|
|
StretchImage(newSprite, s->scaledWidth, s->scaledWidth, s->scaledHeight, sprite, s->w, s->w, s->h, backbuf);
|
2003-08-19 08:47:09 +00:00
|
|
|
}
|
|
|
|
|
2003-08-20 06:51:35 +00:00
|
|
|
if (freeSprite)
|
|
|
|
free(sprite);
|
|
|
|
sprite = newSprite;
|
|
|
|
freeSprite = true;
|
|
|
|
}
|
|
|
|
|
2003-08-22 07:04:50 +00:00
|
|
|
// -----------------------------------------------------------------
|
|
|
|
// Light masking
|
|
|
|
// -----------------------------------------------------------------
|
|
|
|
|
|
|
|
// The light mask is an optional layer that covers the entire room
|
2003-08-30 10:23:40 +00:00
|
|
|
// and which is used to simulate light and shadows. Scaled sprites
|
|
|
|
// (actors, presumably) are always affected.
|
2003-08-22 07:04:50 +00:00
|
|
|
|
2003-08-26 06:53:00 +00:00
|
|
|
if ((renderCaps & RDBLTFX_SHADOWBLEND) && lightMask && (scale != 256 || (s->type & RDSPR_SHADOW))) {
|
2003-08-22 07:04:50 +00:00
|
|
|
uint8 *lightMap;
|
|
|
|
|
|
|
|
if (!freeSprite) {
|
|
|
|
newSprite = (uint8 *) malloc(s->w * s->h);
|
|
|
|
memcpy(newSprite, sprite, s->w * s->h);
|
|
|
|
sprite = newSprite;
|
|
|
|
freeSprite = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
src = sprite + rs.top * srcPitch + rs.left;
|
|
|
|
lightMap = lightMask + (rd.top + scrolly - 40) * locationWide + rd.left + scrollx;
|
|
|
|
|
2003-09-08 06:38:16 +00:00
|
|
|
for (i = 0; i < rs.height(); i++) {
|
|
|
|
for (j = 0; j < rs.width(); j++) {
|
2003-08-22 07:04:50 +00:00
|
|
|
if (src[j] && lightMap[j]) {
|
|
|
|
uint8 r = ((32 - lightMap[j]) * palCopy[src[j]][0]) >> 5;
|
|
|
|
uint8 g = ((32 - lightMap[j]) * palCopy[src[j]][1]) >> 5;
|
|
|
|
uint8 b = ((32 - lightMap[j]) * palCopy[src[j]][2]) >> 5;
|
|
|
|
src[j] = QuickMatch(r, g, b);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
src += srcPitch;
|
|
|
|
lightMap += locationWide;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2003-08-20 06:51:35 +00:00
|
|
|
// -----------------------------------------------------------------
|
|
|
|
// Drawing
|
|
|
|
// -----------------------------------------------------------------
|
|
|
|
|
2003-08-22 07:04:50 +00:00
|
|
|
src = sprite + rs.top * srcPitch + rs.left;
|
2003-08-28 06:36:15 +00:00
|
|
|
dst = lpBackBuffer + screenWide * rd.top + rd.left;
|
2003-08-20 06:51:35 +00:00
|
|
|
|
|
|
|
if (s->type & RDSPR_BLEND) {
|
2003-08-26 06:53:00 +00:00
|
|
|
if (renderCaps & RDBLTFX_ALLHARDWARE) {
|
2003-09-08 06:38:16 +00:00
|
|
|
for (i = 0; i < rs.height(); i++) {
|
|
|
|
for (j = 0; j < rs.width(); j++) {
|
2003-08-26 06:53:00 +00:00
|
|
|
if (src[j] && ((i & 1) == (j & 1)))
|
|
|
|
dst[j] = src[j];
|
2003-08-19 08:47:09 +00:00
|
|
|
}
|
2003-08-20 06:51:35 +00:00
|
|
|
src += srcPitch;
|
2003-08-28 06:36:15 +00:00
|
|
|
dst += screenWide;
|
2003-08-19 08:47:09 +00:00
|
|
|
}
|
2003-08-26 06:53:00 +00:00
|
|
|
} else {
|
|
|
|
if (s->blend & 0x01) {
|
|
|
|
red = s->blend >> 8;
|
2003-09-08 06:38:16 +00:00
|
|
|
for (i = 0; i < rs.height(); i++) {
|
|
|
|
for (j = 0; j < rs.width(); j++) {
|
2003-08-26 06:53:00 +00:00
|
|
|
if (src[j]) {
|
|
|
|
uint8 r = (palCopy[src[j]][0] * red + palCopy[dst[j]][0] * (8 - red)) >> 3;
|
|
|
|
uint8 g = (palCopy[src[j]][1] * red + palCopy[dst[j]][1] * (8 - red)) >> 3;
|
|
|
|
uint8 b = (palCopy[src[j]][2] * red + palCopy[dst[j]][2] * (8 - red)) >> 3;
|
|
|
|
dst[j] = QuickMatch(r, g, b);
|
|
|
|
}
|
2003-08-19 08:47:09 +00:00
|
|
|
}
|
2003-08-26 06:53:00 +00:00
|
|
|
src += srcPitch;
|
2003-08-28 06:36:15 +00:00
|
|
|
dst += screenWide;
|
2003-08-19 08:47:09 +00:00
|
|
|
}
|
2003-08-26 06:53:00 +00:00
|
|
|
} else if (s->blend & 0x02) {
|
|
|
|
// FIXME: This case looks bogus to me. The
|
|
|
|
// same value for the red, green and blue
|
|
|
|
// parameters, and we multiply with the source
|
|
|
|
// color's palette index rather than its color
|
|
|
|
// component.
|
|
|
|
//
|
|
|
|
// But as far as I can see, that's how the
|
|
|
|
// original
|
|
|
|
// code did it.
|
|
|
|
//
|
|
|
|
// Does anyone know where this case was used
|
|
|
|
// anyway?
|
|
|
|
|
|
|
|
red = palCopy[s->blend >> 8][0];
|
|
|
|
green = palCopy[s->blend >> 8][0];
|
|
|
|
blue = palCopy[s->blend >> 8][0];
|
2003-09-08 06:38:16 +00:00
|
|
|
for (i = 0; i < rs.height(); i++) {
|
|
|
|
for (j = 0; j < rs.width(); j++) {
|
2003-08-26 06:53:00 +00:00
|
|
|
if (src[j]) {
|
|
|
|
uint8 r = (src[j] * red + (16 - src[j]) * palCopy[dst[j]][0]) >> 4;
|
|
|
|
uint8 g = (src[j] * green + (16 - src[j]) * palCopy[dst[j]][1]) >> 4;
|
|
|
|
uint8 b = (src[j] * blue + (16 - src[j]) * palCopy[dst[j]][2]) >> 4;
|
|
|
|
dst[j] = QuickMatch(r, g, b);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
src += srcPitch;
|
2003-08-28 06:36:15 +00:00
|
|
|
dst += screenWide;
|
2003-08-26 06:53:00 +00:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
warning("DrawSprite: Invalid blended sprite");
|
|
|
|
if (freeSprite)
|
|
|
|
free(sprite);
|
|
|
|
return RDERR_UNKNOWNTYPE;
|
2003-08-19 08:47:09 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (s->type & RDSPR_TRANS) {
|
2003-09-08 06:38:16 +00:00
|
|
|
for (i = 0; i < rs.height(); i++) {
|
|
|
|
for (j = 0; j < rs.width(); j++) {
|
2003-08-19 08:47:09 +00:00
|
|
|
if (src[j])
|
|
|
|
dst[j] = src[j];
|
|
|
|
}
|
2003-08-20 06:51:35 +00:00
|
|
|
src += srcPitch;
|
2003-08-28 06:36:15 +00:00
|
|
|
dst += screenWide;
|
2003-08-19 08:47:09 +00:00
|
|
|
}
|
|
|
|
} else {
|
2003-09-08 06:38:16 +00:00
|
|
|
for (i = 0; i < rs.height(); i++) {
|
|
|
|
memcpy(dst, src, rs.width());
|
2003-08-20 06:51:35 +00:00
|
|
|
src += srcPitch;
|
2003-08-28 06:36:15 +00:00
|
|
|
dst += screenWide;
|
2003-08-19 08:47:09 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2003-08-20 06:51:35 +00:00
|
|
|
if (freeSprite)
|
|
|
|
free(sprite);
|
|
|
|
|
2003-08-28 06:36:15 +00:00
|
|
|
// UploadRect(&rd);
|
2003-08-29 06:42:34 +00:00
|
|
|
SetNeedRedraw();
|
2003-08-19 08:47:09 +00:00
|
|
|
|
2003-09-23 17:24:45 +00:00
|
|
|
return RD_OK;
|
2003-07-28 01:47:41 +00:00
|
|
|
}
|
|
|
|
|
2003-08-26 06:53:00 +00:00
|
|
|
int32 OpenLightMask(_spriteInfo *s) {
|
|
|
|
// FIXME: The light mask is only needed on higher graphics detail
|
|
|
|
// settings, so to save memory we could simply ignore it on lower
|
|
|
|
// settings. But then we need to figure out how to ensure that it
|
|
|
|
// is properly loaded if the user changes the settings in mid-game.
|
2003-07-28 01:47:41 +00:00
|
|
|
|
|
|
|
if (lightMask)
|
2003-08-26 06:53:00 +00:00
|
|
|
return RDERR_NOTCLOSED;
|
2003-07-28 01:47:41 +00:00
|
|
|
|
|
|
|
lightMask = (uint8 *) malloc(s->w * s->h);
|
2003-08-26 06:53:00 +00:00
|
|
|
if (!lightMask)
|
|
|
|
return RDERR_OUTOFMEMORY;
|
2003-07-28 01:47:41 +00:00
|
|
|
|
|
|
|
if (DecompressRLE256(lightMask, s->data, s->w * s->h))
|
2003-08-26 06:53:00 +00:00
|
|
|
return RDERR_DECOMPRESSION;
|
2003-07-28 01:47:41 +00:00
|
|
|
|
2003-08-26 06:53:00 +00:00
|
|
|
return RD_OK;
|
2003-07-28 01:47:41 +00:00
|
|
|
}
|
|
|
|
|
2003-08-26 06:53:00 +00:00
|
|
|
int32 CloseLightMask(void) {
|
2003-07-28 01:47:41 +00:00
|
|
|
if (!lightMask)
|
2003-08-26 06:53:00 +00:00
|
|
|
return RDERR_NOTOPEN;
|
2003-07-28 01:47:41 +00:00
|
|
|
|
|
|
|
free(lightMask);
|
|
|
|
lightMask = 0;
|
2003-08-26 06:53:00 +00:00
|
|
|
return RD_OK;
|
2003-07-28 01:47:41 +00:00
|
|
|
}
|