scummvm/engines/icb/surface_manager.cpp
2021-12-26 21:19:38 +01:00

871 lines
29 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.
*
* Additional copyright for this file:
* Copyright (C) 1999-2000 Revolution Software Ltd.
* This code is based on source code created by Revolution Software,
* used with permission.
*
* 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
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "engines/icb/common/px_common.h"
#include "engines/icb/surface_manager.h"
#include "engines/icb/mission.h"
#include "engines/icb/global_objects.h"
#include "common/config-manager.h"
#include "common/rect.h"
#include "common/textconsole.h"
#include "common/system.h"
#include "common/util.h"
#include "common/file.h"
#include "image/bmp.h"
#include "engines/util.h"
namespace ICB {
#define FIRST_CLIENT_SURFACE 2
uint32 working_buffer_id;
uint32 bg_buffer_id;
uint32 effect_time; // Time spent doing postprocessing effects (fades ect)
uint32 flipTime;
_surface::~_surface() {
// Is the surface there
if (m_dds) {
m_dds->free();
delete m_dds;
}
}
_surface::_surface() {
m_dds = nullptr;
m_name = "Created";
m_width = 0;
m_height = 0;
m_locked = FALSE8;
}
void _surface_manager::PrintDebugLabel(const char *mess, uint32 c) {
static int32 y = 100;
if (mess == nullptr) {
y = c;
} else {
/* Get_surface_DC( working_buffer_id, dc );
SetBkColor( dc, 0x00000000 );
SetTextColor( dc, c );
TextOut( dc, 10, y, mess, strlen( mess ) );
Release_surface_DC( working_buffer_id, dc);*/
y += 15;
}
}
void _surface_manager::PrintTimer(char label, uint32 time, uint32 limit) {
/* static uint32 colours[] = { 0x0000ff00, // 0x - 1x
0x0000ffff, // 1x - 2x
0x000000ff, // 2x - 3x
0x00ffff00, // 3x - 4x
0x00ff0000, // 4x - 5x
0x00ffffff }; // 5x +
*/
static int32 x = 20;
if (limit == 0) {
x = 20;
} else {
float perc = (float)time / (float)limit;
int32 percIndex = (int32)perc;
if (percIndex < 0)
percIndex = 0;
if (percIndex > 5)
percIndex = 5;
char message[64];
sprintf(message, "%c%3.1f", label, perc);
/* Get_surface_DC( working_buffer_id, dc );
SetBkColor( dc, colours[percIndex] );
SetTextColor( dc, 0x01010101 );
TextOut( dc, x, 0, message, strlen( message ) );
Release_surface_DC( working_buffer_id, dc);*/
x += 54;
}
}
_surface_manager::_surface_manager() {
// Setup uninitialized pointers
screenSurface = nullptr;
// set these up only once
full_rect.left = 0;
full_rect.right = SCREEN_WIDTH;
full_rect.top = 0;
full_rect.bottom = SCREEN_DEPTH;
m_screen_rect.left = m_screen_rect.top = 0;
m_screen_rect.right = SCREEN_WIDTH;
m_screen_rect.bottom = SCREEN_DEPTH;
// Set up border properties
m_borders = m_screen_rect;
BorderRed() = (uint8)0x00;
BorderGreen() = (uint8)0x00;
BorderBlue() = (uint8)0x00;
BorderRed() = (uint8)0x00;
m_borderMode = 0;
// Set up the fade properties
FadeFromRed() = FadeFromGreen() = FadeFromBlue() = FadeToRed() = FadeToGreen() = FadeToBlue() = FadeAlpha() = (uint8)0x00;
FadeMode() = 0;
// Make sure the list of surfaces is empty
m_Surfaces.Reset();
// Set/Clear the MMX flag
m_hasMMX = TRUE8;
}
_surface_manager::~_surface_manager() {
// Print surface info
Zdebug("*SURFACE_MANAGER* Destroying Surface Manager with %d surfaces still active", m_Surfaces.GetNoItems());
// Release the surfaces ( the Reset call calls the destructor for each non null surface )
m_Surfaces.Reset();
// Finished
Zdebug("*SURFACE_MANAGER* Surface Manager Destroyed");
}
uint32 _surface_manager::Init_direct_draw() {
// Debug info
Zdebug("*SURFACE_MANAGER* Initalizing the SDL video interface");
g_system->setWindowCaption(Common::U32String("In Cold Blood (C)2000 Revolution Software Ltd"));
initGraphics(SCREEN_WIDTH, SCREEN_DEPTH, nullptr);
screenSurface = new Graphics::Surface();
screenSurface->create(SCREEN_WIDTH, SCREEN_DEPTH, Graphics::PixelFormat(4, 8, 8, 8, 8, 16, 8, 0, 24));
if (!screenSurface->getBasePtr(0, 0)) {
Fatal_error("Initialise Graphics::Surface::create failed");
}
// Make our back buffer type affair
m_Surfaces[0] = new _surface;
m_Surfaces[0]->m_locked = false;
m_Surfaces[0]->m_width = SCREEN_WIDTH;
m_Surfaces[0]->m_height = SCREEN_DEPTH;
m_Surfaces[0]->m_name = "backbuffer";
m_Surfaces[0]->m_dds = screenSurface;
working_buffer_id = 0;
// Finished
Zdebug("*SURFACE_MANAGER* SDL video interface Ready");
return true;
}
void _surface_manager::Reset_Effects() {
// Reset the border properties
m_borders = m_screen_rect;
BorderRed() = (uint8)0x00;
BorderGreen() = (uint8)0x00;
BorderBlue() = (uint8)0x00;
BorderRed() = (uint8)0x00;
m_borderMode = 0;
// Reset the fade properties
FadeFromRed() = (uint8)0x00;
FadeFromGreen() = (uint8)0x00;
FadeFromBlue() = (uint8)0x00;
FadeToRed() = (uint8)0x00;
FadeToGreen() = (uint8)0x00;
FadeToBlue() = (uint8)0x00;
FadeAlpha() = (uint8)0x00;
FadeMode() = 0;
}
void _surface_manager::Flip() {
// Draw Frame rate monitor if it's switched on
static uint32 g_fpsCounter = 0;
static float g_fpsTotalTime = 0.0f;
static float g_fpsEndTime = 0.0f;
static float g_fpsStartTime = 0.0f;
g_fpsEndTime = (float)GetMicroTimer();
float nowTime = (g_fpsEndTime - g_fpsStartTime) / 1000.0f;
g_fpsStartTime = g_fpsEndTime;
if ((nowTime > 1000.0f) || (nowTime < 1.0f))
nowTime = 83.0f;
g_fpsTotalTime += nowTime;
//float averageFps = (float)(g_fpsTotalTime / ((double)g_fpsCounter + 1.0f));
//float currentFPS = (float)(1000.0f / (double)nowTime);
//float averageFPS = (float)(1000.0f / (double)averageFps);
if ((++g_fpsCounter) > 0xffffff00) {
g_fpsTotalTime = 0.0f;
g_fpsCounter = 0;
}
// Print the timers
//char fpsMessage[64];
//HDC dc;
//sprintf( fpsMessage,"%3.1f FPS (%3.1f Average FPS)", currentFPS, averageFPS);
//surface_manager->Get_surface_DC( working_buffer_id, dc );
//SetBkColor( dc, 0x00000000 );
//SetTextColor( dc, 0x00c08020 );
//TextOut( dc, 420, 460, fpsMessage, strlen( fpsMessage ) );
//surface_manager->Release_surface_DC( working_buffer_id, dc);
flipTime = GetMicroTimer();
Graphics::Surface *dstBuf = screenSurface->convertTo(g_system->getScreenFormat());
g_system->copyRectToScreen(dstBuf->getPixels(), dstBuf->pitch, 0, 0, dstBuf->w, dstBuf->h);
g_system->updateScreen();
dstBuf->free();
delete dstBuf;
flipTime = GetMicroTimer() - flipTime;
PrintDebugLabel(nullptr, 0x00000000);
PrintTimer('\0', 0, 0);
}
// Returns 0 on error
uint32 _surface_manager::Create_new_surface(const char *name, uint32 width, uint32 height, uint32 /*type*/) {
// Find the next free slot
uint32 slot;
for (slot = FIRST_CLIENT_SURFACE; slot < m_Surfaces.GetNoItems() && m_Surfaces[slot] != nullptr; slot++)
;
// Create the new surface structure
m_Surfaces[slot] = new _surface;
// Initalize it
m_Surfaces[slot]->m_locked = false;
m_Surfaces[slot]->m_width = width;
m_Surfaces[slot]->m_height = height;
m_Surfaces[slot]->m_name = name;
m_Surfaces[slot]->m_dds = new Graphics::Surface;
m_Surfaces[slot]->m_dds->create(width, height, Graphics::PixelFormat(4, 8, 8, 8, 8, 16, 8, 0, 24));
if (m_Surfaces[slot]->m_dds)
return slot;
delete m_Surfaces[slot];
m_Surfaces[slot] = nullptr;
return 0;
}
void _surface_manager::Kill_surface(uint32 s_id) {
if (m_Surfaces[s_id] == nullptr)
return; // Already killed
delete m_Surfaces[s_id];
m_Surfaces[s_id] = nullptr;
}
uint8 *_surface_manager::Lock_surface(uint32 s_id) {
_surface *pSurface = m_Surfaces[s_id];
if (pSurface->m_locked)
Fatal_error("_surface_manager::Lock_surface( %s ) - Already locked and doesn't support multiple locking", (const char *)pSurface->m_name);
if (!m_Surfaces[s_id]->m_dds) {
Fatal_error("_surface_manager::Lock_surface( %s ) - surface is null :O", (const char *)m_Surfaces[s_id]->m_name);
error("Should exit with error-code -1");
}
pSurface->m_locked = TRUE8;
return ((uint8 *)pSurface->m_dds->getBasePtr(0, 0));
}
int32 _surface_manager::Get_pitch(uint32 s_id) {
if (m_Surfaces[s_id]->m_locked == false) {
Fatal_error("**Get_pitch %s - surface is unlocked :O", (const char *)m_Surfaces[s_id]->m_name);
error("Should exit with error-code -1");
}
if (!m_Surfaces[s_id]->m_dds) {
Fatal_error("**Get_pitch %s - surface is null :O", (const char *)m_Surfaces[s_id]->m_name);
error("Should exit with error-code -1");
}
return (m_Surfaces[s_id]->m_dds->pitch);
}
void _surface_manager::Unlock_surface(uint32 s_id) {
_surface *pSurface = m_Surfaces[s_id];
if (!pSurface->m_dds) {
Fatal_error("**Unlock_surface %s - surface is null :O", (const char *)m_Surfaces[s_id]->m_name);
error("Should exit with error-code -1");
}
m_Surfaces[s_id]->m_locked = FALSE8;
}
void _surface_manager::Fill_surface(uint32 s_id, uint32 rgb_value) {
m_Surfaces[s_id]->m_dds->fillRect(Common::Rect(0, 0, m_Surfaces[s_id]->m_dds->w, m_Surfaces[s_id]->m_dds->h), rgb_value);
}
static void copyRectToSurface(void *dstBuffer, const void *srcBuffer, int32 srcPitch, int32 dstPitch, int32 width, int32 height,
bool8 colorKeyEnable, uint32 colorKey) {
assert(srcBuffer);
assert(dstBuffer);
if (colorKeyEnable) {
const uint32 *src = (const uint32 *)srcBuffer;
uint32 *dst = (uint32 *)dstBuffer;
for (int32 h = 0; h < height; h++) {
for (int32 w = 0; w < width; w++) {
if (src[w] != colorKey && src[w] != 0)
dst[w] = src[w];
}
src += (srcPitch >> 2);
dst += (dstPitch >> 2);
}
} else {
const byte *src = (const byte *)srcBuffer;
byte *dst = (byte *)dstBuffer;
for (int32 h = 0; h < height; h++) {
memcpy(dst, src, width * 4);
src += srcPitch;
dst += dstPitch;
}
}
}
static void copyRectToSurface(Graphics::Surface *dstSurface, Graphics::Surface *srcSurface,
int32 destX, int32 destY, const Common::Rect subRect,
bool8 colorKeyEnable, uint32 colorKey) {
assert(srcSurface->format == dstSurface->format);
assert(srcSurface->format.bytesPerPixel == 4);
assert(destX >= 0 && destX < dstSurface->w);
assert(destY >= 0 && destY < dstSurface->h);
assert(subRect.height() > 0 && destY + subRect.height() <= dstSurface->h);
assert(subRect.width() > 0 && destX + subRect.width() <= dstSurface->w);
copyRectToSurface(dstSurface->getBasePtr(destX, destY), const_cast<void *>(srcSurface->getBasePtr(subRect.left, subRect.top)),
srcSurface->pitch, dstSurface->pitch, subRect.width(), subRect.height(), colorKeyEnable, colorKey);
}
void _surface_manager::Blit_surface_to_surface(uint32 from_id, uint32 to_id, LRECT *pSrcRect, LRECT *pDestRect, uint32 dwFlags) {
Common::Rect srcRect, dstRect;
if (pSrcRect) {
if ((pSrcRect->right - pSrcRect->left) < 1)
return;
if ((pSrcRect->bottom - pSrcRect->top) < 1)
return;
srcRect.left = pSrcRect->left;
srcRect.top = pSrcRect->top;
srcRect.right = pSrcRect->right;
srcRect.bottom = pSrcRect->bottom;
}
if (pDestRect) {
dstRect.left = pDestRect->left;
dstRect.top = pDestRect->top;
dstRect.right = pDestRect->right;
dstRect.bottom = pDestRect->bottom;
}
Graphics::Surface *dstSurface = m_Surfaces[to_id]->m_dds;
Graphics::Surface *srcSurface = m_Surfaces[from_id]->m_dds;
if (pDestRect) {
if (pSrcRect) {
copyRectToSurface(dstSurface, srcSurface, dstRect.left, dstRect.top, srcRect,
dwFlags != 0, m_Surfaces[from_id]->m_colorKey);
} else {
copyRectToSurface(dstSurface, srcSurface, dstRect.left, dstRect.top, Common::Rect(0, 0, srcSurface->w, srcSurface->h),
dwFlags != 0, m_Surfaces[from_id]->m_colorKey);
}
} else {
if (pSrcRect) {
uint16 dstX = dstSurface->w - srcRect.right;
copyRectToSurface(dstSurface, srcSurface, dstX, 0, srcRect,
dwFlags != 0, m_Surfaces[from_id]->m_colorKey);
if (dstX != 0) {
dstSurface->fillRect(Common::Rect(0, 0, dstX - 1, dstSurface->h), 0);
} else {
dstSurface->fillRect(Common::Rect(dstX + (dstSurface->w - srcRect.left), 0,
dstSurface->w, dstSurface->h), 0);
}
} else {
copyRectToSurface(dstSurface, srcSurface, 0, 0, Common::Rect(0, 0, srcSurface->w, srcSurface->h),
dwFlags != 0, m_Surfaces[from_id]->m_colorKey);
}
}
}
void _surface_manager::Blit_fillfx(uint32 surface_id, LRECT *rect, uint32 col) {
Common::Rect r;
if (rect) {
r.left = rect->left;
r.top = rect->top;
r.right = rect->right;
r.bottom = rect->bottom;
}
if (rect) {
m_Surfaces[surface_id]->m_dds->fillRect(r, col);
} else {
m_Surfaces[surface_id]->m_dds->fillRect(Common::Rect(0, 0, m_Surfaces[surface_id]->m_dds->w, m_Surfaces[surface_id]->m_dds->h), col);
}
}
void _surface_manager::Set_transparent_colour_key(uint32 nSurfaceID, uint32 nKey) {
m_Surfaces[nSurfaceID]->m_colorKey = nKey;
}
void _surface_manager::DrawEffects(uint32 surface_id) {
effect_time = GetMicroTimer();
// Are we fading ?
if (FadeMode() && FadeAlpha() != 0) {
if (FadeMode() == 1) {
/* Additive Fade
S'xy = Sxy + ( Alpha * Blend Colour )
*/
/* Linear Interpolation between FromColour and ToColour */
uint8 r = (uint8)(FadeFromRed() + ((FadeAlpha() * (FadeToRed() - FadeFromRed())) >> 8));
uint8 g = (uint8)(FadeFromGreen() + ((FadeAlpha() * (FadeToGreen() - FadeFromGreen())) >> 8));
uint8 b = (uint8)(FadeFromBlue() + ((FadeAlpha() * (FadeToBlue() - FadeFromBlue())) >> 8));
/* Move blend colours into MMX friendly structure */
uint8 additive[8];
additive[4] = additive[0] = b;
additive[5] = additive[1] = g;
additive[6] = additive[2] = r;
additive[7] = additive[3] = 0x00;
// Yep. Lock the working buffer
uint8 *pixels = Lock_surface(surface_id);
uint32 pitch = Get_pitch(surface_id);
#if 1
for (uint32 lines = 0; lines < SCREEN_DEPTH; lines++) {
for (int32 xPos = 0; xPos < SCREEN_WIDTH; xPos++) {
// 32-bit BGRA pixel
uint8 *pixel = &pixels[xPos * 4];
// Add from RGB components
for (int32 i = 0; i < 3; i++) {
pixel[i] = MIN(255, pixel[i] + additive[i]);
}
}
// Next line
pixels += pitch;
}
#else
_asm {
lea edi, additive ; // Get the address of the blend colour block
movq MM0, [edi] ; // Load the blend colour
mov esi, pixels ; // Fill esi with the address for the first row of screen memory
mov edx, SCREEN_DEPTH ; // Row counter
sub esi, 8 ; // Ecx is our index and counter, so move the pointer back to compensate for it's 1 out ness
mov eax, pitch ; // Distance to move from the end of one line to the start of the next
mov ebx, (SCREEN_WIDTH/2) ;
additive_fade_row_loop:
mov ecx, ebx ; // Pixel Counter ( 2 pixels at a time )
additive_fade_loop:
movq MM1, [esi + ecx * 8] ; // Load 2 pixels
paddusb MM1, MM0 ; // Add the blend colour (with byte saturation)
movq [esi + ecx * 8], MM1 ; // Store 2 pixels
dec ecx ; // Reduce counter/index
jne additive_fade_loop ; // On to the next 2 pixels
add esi, eax ; // Add the increment to get to the start of the next row
dec edx ; // Reduce the row counter
jne additive_fade_row_loop // And off again
EMMS; /* Reset FPU/MMX */
}
#endif
Unlock_surface(surface_id);
} else if (FadeMode() == 2) { /* SUBTRACTIVE FADE */
uint8 r = (uint8)(FadeFromRed() + ((FadeAlpha() * (FadeToRed() - FadeFromRed())) >> 8));
uint8 g = (uint8)(FadeFromGreen() + ((FadeAlpha() * (FadeToGreen() - FadeFromGreen())) >> 8));
uint8 b = (uint8)(FadeFromBlue() + ((FadeAlpha() * (FadeToBlue() - FadeFromBlue())) >> 8));
uint8 subtractive[8];
subtractive[4] = subtractive[0] = b;
subtractive[5] = subtractive[1] = g;
subtractive[6] = subtractive[2] = r;
subtractive[7] = subtractive[3] = 0x00;
// Yep. Lock the working buffer
uint8 *pixels = Lock_surface(surface_id);
uint32 pitch = Get_pitch(surface_id);
#if 1
for (uint32 lines = 0; lines < SCREEN_DEPTH; lines++) {
for (int32 xPos = 0; xPos < SCREEN_WIDTH; xPos++) {
// 32-bit BGRA pixel
uint8 *pixel = &pixels[xPos * 4];
// Subtract from RGB components
for (int32 i = 0; i < 3; i++) {
pixel[i] = MAX(0, pixel[i] - subtractive[i]);
}
}
// Next line
pixels += pitch;
}
#else
_asm {
lea edi, subtractive ; // Get the address of the blend colour block
movq MM0, [edi] ; // Load the blend colour
mov esi, pixels ; // Fill esi with the address for the first row of screen memory
mov edx, SCREEN_DEPTH ; // Row counter
sub esi, 8 ; // Ecx is our index and counter, so move the pointer back to compensate for it's 1 out ness
mov eax, pitch ; // Distance to move from the end of one line to the start of the next
mov ebx, (SCREEN_WIDTH/2) ;
subtractive_fade_row_loop:
mov ecx, ebx ; // Pixel Counter ( 2 pixels at a time )
subtractive_fade_loop:
movq MM1, [esi + ecx * 8] ; // Load 2 pixels
psubusb MM1, MM0 ; // Do the add
movq [esi + ecx * 8], MM1 ; // Store 2 pixels
dec ecx ; // Reduce counter/index
jne subtractive_fade_loop ; // On to the next 2 pixels
add esi, eax ; // Add the increment to get to the start of the next row
dec edx ; // Reduce the row counter
jne subtractive_fade_row_loop // And off again
EMMS; /* Reset FPU/MMX */
}
#endif
Unlock_surface(surface_id);
} else if (FadeMode() == 3) { /* ALPHA FADE */
// Flip FadeAlpha() so it's 0 for fully blended and 255 for no blend
uint8 fA = (uint8)(255 - FadeAlpha());
// Create multiplier mask
uint8 multiplier[8];
multiplier[4] = multiplier[0] = fA;
multiplier[5] = multiplier[1] = 0x00;
multiplier[6] = multiplier[2] = fA;
multiplier[7] = multiplier[3] = 0x00;
uint16 r = (uint16)(fA * FadeFromRed() + ((255 - fA) * FadeToRed()));
uint16 g = (uint16)(fA * FadeFromGreen() + ((255 - fA) * FadeToGreen()));
uint16 b = (uint16)(fA * FadeFromBlue() + ((255 - fA) * FadeToBlue()));
// Pre calculate the fade colour component ... 0 <= alpha <= 128
uint8 blendColour[8];
*((uint16 *)&blendColour[0]) = b;
*((uint16 *)&blendColour[2]) = g;
*((uint16 *)&blendColour[4]) = r;
*((uint16 *)&blendColour[6]) = (uint16)0x0000;
// Yep. Lock the working buffer
uint8 *pixels = Lock_surface(surface_id);
uint32 pitch = Get_pitch(surface_id);
#if 1
for (uint32 lines = 0; lines < SCREEN_DEPTH; lines++) {
for (int32 xPos = 0; xPos < SCREEN_WIDTH; xPos++) {
// 32-bit BGRA pixel
uint8 *pixel = &pixels[xPos * 4];
// Subtract from RGB components
for (int32 i = 0; i < 3; i++) {
pixel[i] = (pixel[i] * multiplier[i] + blendColour[i * 2]) >> 8;
}
}
// Next line
pixels += pitch;
}
#else
_asm {
mov ebx, (SCREEN_WIDTH/2);
lea esi, multiplier;
lea edi, blendColour;
movq MM0, [esi] ; // Load the fade subtractive thingy
movq MM1, [edi] ; // Load the fade subtractive thingy
mov esi, pixels ;
mov edx, SCREEN_DEPTH ; // Row counter
sub esi, 8 ; // Ecx is our index and counter, so move the pointer back to compensate for it's 1 out ness
pxor MM2, MM2 ; // Clear MM2
mov eax, pitch ; // Distance to move from the end of one line to the start of the next
alpha_fade_row_loop:
mov ecx, ebx ; // Pixel Counter ( 2 pixels at a time )
alpha_fade_loop:
movq MM3, [esi + ecx * 8] ; // Load 2 pixels
movq MM4, MM3
punpckhbw MM3, MM2 ; // Unpack the pixels into words
punpcklbw MM4, MM2 ; // Unpack the pixels into words
pmullw MM3, MM0
pmullw MM4, MM0
paddusw MM3, MM1
paddusw MM4, MM1
psrlw MM3, 8
psrlw MM4, 8
packuswb MM4, MM3
movq [esi + ecx * 8], MM4 ; // Store the
// result
dec ecx ; // Reduce counter
jne alpha_fade_loop ; // On to the next 2 pixels
add esi, eax ; // Add the increment to get to the start of the next row
dec edx ; // Reduce the row counter
jne alpha_fade_row_loop // And off again
EMMS ; // Clear/Set MMX/FPU flag
}
#endif
Unlock_surface(surface_id);
effect_time = (GetMicroTimer() - effect_time);
return;
}
}
if (FadeMode() == 4) { /* COLOUR FADE */
// Create the colour for this frame
uint8 r = (uint8)(FadeFromRed() + ((FadeToRed() - FadeFromRed()) * FadeAlpha()) / 255);
uint8 g = (uint8)(FadeFromGreen() + ((FadeToGreen() - FadeFromGreen()) * FadeAlpha()) / 255);
uint8 b = (uint8)(FadeFromBlue() + ((FadeToBlue() - FadeFromBlue()) * FadeAlpha()) / 255);
uint32 color = (r << 16) + (g << 8) + b;
Graphics::Surface *pSurf = m_Surfaces[surface_id]->m_dds;
pSurf->fillRect(Common::Rect(0, 0, pSurf->w, pSurf->h), color);
}
/* ADD Top/Bottom/Left/Right BORDERS */
if (BorderMode() == 0) {
// Add the borders ( if there are any )
LRECT rect;
rect.top = 0;
rect.left = 0;
rect.right = 640;
uint32 color = (BorderRed() << 16) + (BorderGreen() << 8) + BorderBlue();
Graphics::Surface *pSurf = m_Surfaces[surface_id]->m_dds;
// Top
rect.bottom = m_borders.top;
if (rect.top != rect.bottom) {
Common::Rect r;
r.left = rect.left;
r.top = rect.top;
r.right = rect.right;
r.bottom = rect.bottom;
pSurf->fillRect(r, color);
pSurf->fillRect(r, color);
}
// Bottom
rect.bottom = 480;
rect.top = m_borders.bottom;
if (rect.top != rect.bottom) {
Common::Rect r;
r.left = rect.left;
r.top = rect.top;
r.right = rect.right;
r.bottom = rect.bottom;
pSurf->fillRect(r, color);
}
} else if ((BorderMode() == 1 || BorderMode() == 2) && m_borders.top != 0) {
// Do we have MMX ?
if (HasMMX()) {
/* Alpha Blend
S'xy = ( Sxy * multiplier + blendedColour ) / 256
*/
// Flip FadeAlpha() so it's 0 for fully blended and 255 for no blend
uint8 fA = (uint8)(255 - BorderAlpha());
// Create multiplier mask
uint8 multiplier[8];
multiplier[4] = multiplier[0] = fA;
multiplier[5] = multiplier[1] = 0x00;
multiplier[6] = multiplier[2] = fA;
multiplier[7] = multiplier[3] = 0x00;
uint16 r = (uint16)(((255 - fA) * BorderRed()));
uint16 g = (uint16)(((255 - fA) * BorderGreen()));
uint16 b = (uint16)(((255 - fA) * BorderBlue()));
// Pre calculate the fade colour component ... 0 <= alpha <= 128
uint8 blendColour[8];
*((uint16 *)&blendColour[0]) = b;
*((uint16 *)&blendColour[2]) = g;
*((uint16 *)&blendColour[4]) = r;
*((uint16 *)&blendColour[6]) = (uint16)0x0000;
// Yep. Lock the working buffer
uint8 *pixels = Lock_surface(surface_id);
uint32 pitch = Get_pitch(surface_id);
#if 1
for (int32 xPos = 0; xPos < SCREEN_WIDTH; xPos++) {
// 32-bit BGRA pixel
uint8 *pixel = &pixels[xPos * 4];
// Subtract from RGB components
for (int32 i = 0; i < 3; i++) {
pixel[i] = (pixel[i] * multiplier[i] + blendColour[i * 2]) >> 8;
}
}
#else
uint32 rowCount = m_borders.top;
_asm {
mov ebx, (SCREEN_WIDTH/2) ; // How many operations per row ?
lea esi, multiplier ; // Get the address of the mulitplier array
lea edi, blendColour ; // Get the address of the blendcolour array
pxor MM2, MM2 ; // Clear MM2
movq MM0, [esi] ; // Load the multiplier
movq MM1, [edi] ; // Load the blended colour
mov esi, pPixels ; // Get a pointer to the pixels
mov edx, rowCount ; // Setup a count for the number of rows.
sub esi, 8 ; // Ecx is our index and counter, so move the pointer back to compensate for it's 1 out ness
pxor MM2, MM2 ; // Clear MM2 read for packing/unpacking
mov eax, rowStep ; // Distance to move from the start of one line to the start of the next
top_border_fade_row_loop:
mov ecx, ebx ; // Pixel Counter ( 2 pixels at a time )
top_border_fade_loop:
movq MM3, [esi + ecx * 8] ; // Load 2 pixels into MM3 & MM4
movq MM4, [esi + ecx * 8] ;
punpckhbw MM3, MM2 ; // Unpack the 2 pixels from bytes into words
punpcklbw MM4, MM2
pmullw MM3, MM0 ; // Multiply the pixels
pmullw MM4, MM0
paddusw MM3, MM1 ; // Add the blend colour
paddusw MM4, MM1
psrlw MM3, 8 ; // Divide by 256
psrlw MM4, 8
packuswb MM4, MM3 ; // Repack back from words to bytes
movq [esi + ecx * 8], MM4 ; // Store the 2 pixels
dec ecx ; // Reduce counter/index
jne top_border_fade_loop; // On to the next 2 pixels
add esi, eax ; // Move the base pointer on to the next line
dec edx ; // Decrement the row counter
jne top_border_fade_row_loop
}
#endif
// Blend the bottom half
pixels += pitch * m_borders.bottom;
#if 1
for (int32 xPos = 0; xPos < SCREEN_WIDTH; xPos++) {
// 32-bit BGRA pixel
uint8 *pixel = &pixels[xPos * 4];
// Subtract from RGB components
for (int32 i = 0; i < 3; i++) {
pixel[i] = (pixel[i] * multiplier[i] + blendColour[i * 2]) >> 8;
}
}
#else
_asm {
mov ebx, (SCREEN_WIDTH/2) ;
lea esi, multiplier ;
lea edi, blendColour ;
pxor MM2, MM2 ; // Clear MM2
movq MM0, [esi] ; // Load the multiplier
movq MM1, [edi] ; // Load the blended colour
mov esi, pPixels ;
mov edx, rowCount ;
sub esi, 8 ; // Ecx is our index and counter, so move the pointer back to compensate for it's 1 out ness
pxor MM2, MM2 ; // Clear MM2
mov eax, rowStep ; // Distance to move from the end of one line to the start of the next
bottom_border_fade_row_loop:
mov ecx, ebx ; // Pixel Counter ( 2 pixels at a time )
bottom_border_fade_loop:
movq MM3, [esi + ecx * 8] ; // Load 2 pixels into MM3 & MM4
movq MM4, [esi + ecx * 8] ;
punpckhbw MM3, MM2 ; // Unpack the 2 pixels from bytes into words
punpcklbw MM4, MM2
pmullw MM3, MM0 ; // Multiply the pixels
pmullw MM4, MM0
paddusw MM3, MM1 ; // Add the blend colour
paddusw MM4, MM1
psrlw MM3, 8 ; // Divide by 256
psrlw MM4, 8
packuswb MM4, MM3 ; // Repack back from words to bytes
movq [esi + ecx * 8], MM4 ; // Store the 2 pixels
dec ecx ; // Reduce counter -- optimize with decrement_and_jump ..... if I remember it.
jne bottom_border_fade_loop; // On to the next 2 pixels
add esi, eax
dec edx
jne bottom_border_fade_row_loop
EMMS;
}
#endif
Unlock_surface(surface_id);
}
}
effect_time = (GetMicroTimer() - effect_time);
}
void _surface_manager::LoadGFXInfo(Common::SeekableReadStream *stream) {
m_borders.left = stream->readSint32LE();
m_borders.top = stream->readSint32LE();
m_borders.right = stream->readSint32LE();
m_borders.bottom = stream->readSint32LE();
m_borderRed = stream->readByte();
m_borderGreen = stream->readByte();
m_borderBlue = stream->readByte();
m_borderAlpha = stream->readByte();
m_borderMode = stream->readUint32LE();
m_fadeMode = stream->readUint32LE();
m_fadeToRed = stream->readByte();
m_fadeToGreen = stream->readByte();
m_fadeToBlue = stream->readByte();
m_fadeFromRed = stream->readByte();
m_fadeFromGreen = stream->readByte();
m_fadeFromBlue = stream->readByte();
m_fadeAlpha = stream->readByte();
}
void _surface_manager::SaveGFXInfo(Common::WriteStream *stream) {
stream->writeSint32LE(m_borders.left);
stream->writeSint32LE(m_borders.top);
stream->writeSint32LE(m_borders.right);
stream->writeSint32LE(m_borders.bottom);
stream->writeByte(m_borderRed);
stream->writeByte(m_borderGreen);
stream->writeByte(m_borderBlue);
stream->writeByte(m_borderAlpha);
stream->writeUint32LE(m_borderMode);
stream->writeUint32LE(m_fadeMode);
stream->writeByte(m_fadeToRed);
stream->writeByte(m_fadeToGreen);
stream->writeByte(m_fadeToBlue);
stream->writeByte(m_fadeFromRed);
stream->writeByte(m_fadeFromGreen);
stream->writeByte(m_fadeFromBlue);
stream->writeByte(m_fadeAlpha);
}
void _surface_manager::RecordFrame(const char *path) {
Common::DumpFile dumpFile;
bool result = dumpFile.open(path);
if (result) {
result = Image::writeBMP(dumpFile, *screenSurface);
}
if (!result) {
Fatal_error("_surface_manager::RecordSurface( \"%s\" ) - Failed to create the output file", path);
}
}
} // End of namespace ICB