gzdoom/src/f_wipe.cpp
Christoph Oelckers 1d4ab0cc2a - implemented proper scaling for the status bar itself.
This allows using the UI scale or its own value, like all other scaling values.
In addition there is a choice between preserving equal pixel size or aspect ratio because the squashed non-corrected versions tend to look odd, but since proper scaling requires ununiform pixel sizes it is an option.

- changed how status bar sizes are being handled.

This has to recalculate all scaling and positioning factors, which can cause problems if the drawer leaves with some temporary values that do not reflect the status bar as a whole.
Changed it so that the status bar stores the base values and restores them after drawing is complete.
2017-03-29 19:23:40 +02:00

518 lines
11 KiB
C++

// Emacs style mode select -*- C++ -*-
//-----------------------------------------------------------------------------
//
// $Id:$
//
// Copyright (C) 1993-1996 by id Software, Inc.
//
// This source is available for distribution and/or modification
// only under the terms of the DOOM Source Code License as
// published by id Software. All rights reserved.
//
// The source is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License
// for more details.
//
// $Log:$
//
// DESCRIPTION:
// Mission begin melt/wipe screen special effect.
//
//-----------------------------------------------------------------------------
#include "i_video.h"
#include "v_video.h"
#include "m_random.h"
#include "doomdef.h"
#include "f_wipe.h"
#include "c_cvars.h"
#include "templates.h"
#include "v_palette.h"
EXTERN_CVAR(Bool, r_blendmethod)
//
// SCREEN WIPE PACKAGE
//
static int CurrentWipeType;
static short *wipe_scr_start;
static short *wipe_scr_end;
static int *y;
// [RH] Fire Wipe
#define FIREWIDTH 64
#define FIREHEIGHT 64
static uint8_t *burnarray;
static int density;
static int burntime;
// [RH] Crossfade
static int fade;
// Melt -------------------------------------------------------------
// Match the strip sizes that oldschool Doom used on a 320x200 screen.
#define MELT_WIDTH 160
#define MELT_HEIGHT 200
void wipe_shittyColMajorXform (short *array)
{
int x, y;
short *dest;
int width = SCREENWIDTH / 2;
dest = new short[width*SCREENHEIGHT*2];
for(y = 0; y < SCREENHEIGHT; y++)
for(x = 0; x < width; x++)
dest[x*SCREENHEIGHT+y] = array[y*width+x];
memcpy(array, dest, SCREENWIDTH*SCREENHEIGHT);
delete[] dest;
}
bool wipe_initMelt (int ticks)
{
int i, r;
// copy start screen to main screen
screen->DrawBlock (0, 0, SCREENWIDTH, SCREENHEIGHT, (uint8_t *)wipe_scr_start);
// makes this wipe faster (in theory)
// to have stuff in column-major format
wipe_shittyColMajorXform (wipe_scr_start);
wipe_shittyColMajorXform (wipe_scr_end);
// setup initial column positions
// (y<0 => not ready to scroll yet)
y = new int[MELT_WIDTH];
y[0] = -(M_Random() & 15);
for (i = 1; i < MELT_WIDTH; i++)
{
r = (M_Random()%3) - 1;
y[i] = clamp(y[i-1] + r, -15, 0);
}
return 0;
}
bool wipe_doMelt (int ticks)
{
int i, j, dy, x;
const short *s;
short *d;
bool done = true;
while (ticks--)
{
done = true;
for (i = 0; i < MELT_WIDTH; i++)
{
if (y[i] < 0)
{
y[i]++;
done = false;
}
else if (y[i] < MELT_HEIGHT)
{
dy = (y[i] < 16) ? y[i]+1 : 8;
y[i] = MIN(y[i] + dy, MELT_HEIGHT);
done = false;
}
if (ticks == 0 && y[i] >= 0)
{ // Only draw for the final tick.
const int pitch = screen->GetPitch() / 2;
int sy = y[i] * SCREENHEIGHT / MELT_HEIGHT;
for (x = i * (SCREENWIDTH/2) / MELT_WIDTH; x < (i + 1) * (SCREENWIDTH/2) / MELT_WIDTH; ++x)
{
s = &wipe_scr_end[x*SCREENHEIGHT];
d = &((short *)screen->GetBuffer())[x];
for (j = sy; j != 0; --j)
{
*d = *(s++);
d += pitch;
}
s = &wipe_scr_start[x*SCREENHEIGHT];
for (j = SCREENHEIGHT - sy; j != 0; --j)
{
*d = *(s++);
d += pitch;
}
}
}
}
}
return done;
}
bool wipe_exitMelt (int ticks)
{
delete[] y;
return 0;
}
// Burn -------------------------------------------------------------
bool wipe_initBurn (int ticks)
{
burnarray = new uint8_t[FIREWIDTH * (FIREHEIGHT+5)];
memset (burnarray, 0, FIREWIDTH * (FIREHEIGHT+5));
density = 4;
burntime = 0;
return 0;
}
int wipe_CalcBurn (uint8_t *burnarray, int width, int height, int density)
{
// This is a modified version of the fire that was once used
// on the player setup menu.
static int voop;
int a, b;
uint8_t *from;
// generator
from = &burnarray[width * height];
b = voop;
voop += density / 3;
for (a = 0; a < density/8; a++)
{
unsigned int offs = (a+b) & (width - 1);
unsigned int v = M_Random();
v = MIN(from[offs] + 4 + (v & 15) + (v >> 3) + (M_Random() & 31), 255u);
from[offs] = from[width*2 + ((offs + width*3/2) & (width - 1))] = v;
}
density = MIN(density + 10, width * 7);
from = burnarray;
for (b = 0; b <= height; b += 2)
{
uint8_t *pixel = from;
// special case: first pixel on line
uint8_t *p = pixel + (width << 1);
unsigned int top = *p + *(p + width - 1) + *(p + 1);
unsigned int bottom = *(pixel + (width << 2));
unsigned int c1 = (top + bottom) >> 2;
if (c1 > 1) c1--;
*pixel = c1;
*(pixel + width) = (c1 + bottom) >> 1;
pixel++;
// main line loop
for (a = 1; a < width-1; a++)
{
// sum top pixels
p = pixel + (width << 1);
top = *p + *(p - 1) + *(p + 1);
// bottom pixel
bottom = *(pixel + (width << 2));
// combine pixels
c1 = (top + bottom) >> 2;
if (c1 > 1) c1--;
// store pixels
*pixel = c1;
*(pixel + width) = (c1 + bottom) >> 1; // interpolate
// next pixel
pixel++;
}
// special case: last pixel on line
p = pixel + (width << 1);
top = *p + *(p - 1) + *(p - width + 1);
bottom = *(pixel + (width << 2));
c1 = (top + bottom) >> 2;
if (c1 > 1) c1--;
*pixel = c1;
*(pixel + width) = (c1 + bottom) >> 1;
// next line
from += width << 1;
}
// Check for done-ness. (Every pixel with level 126 or higher counts as done.)
for (a = width * height, from = burnarray; a != 0; --a, ++from)
{
if (*from < 126)
{
return density;
}
}
return -1;
}
bool wipe_doBurn (int ticks)
{
bool done;
burntime += ticks;
ticks *= 2;
// Make the fire burn
done = false;
while (!done && ticks--)
{
density = wipe_CalcBurn(burnarray, FIREWIDTH, FIREHEIGHT, density);
done = (density < 0);
}
// Draw the screen
int xstep, ystep, firex, firey;
int x, y;
uint8_t *to, *fromold, *fromnew;
const int SHIFT = 16;
xstep = (FIREWIDTH << SHIFT) / SCREENWIDTH;
ystep = (FIREHEIGHT << SHIFT) / SCREENHEIGHT;
to = screen->GetBuffer();
fromold = (uint8_t *)wipe_scr_start;
fromnew = (uint8_t *)wipe_scr_end;
if (!r_blendmethod)
{
for (y = 0, firey = 0; y < SCREENHEIGHT; y++, firey += ystep)
{
for (x = 0, firex = 0; x < SCREENWIDTH; x++, firex += xstep)
{
int fglevel;
fglevel = burnarray[(firex>>SHIFT)+(firey>>SHIFT)*FIREWIDTH] / 2;
if (fglevel >= 63)
{
to[x] = fromnew[x];
}
else if (fglevel == 0)
{
to[x] = fromold[x];
done = false;
}
else
{
int bglevel = 64-fglevel;
uint32_t *fg2rgb = Col2RGB8[fglevel];
uint32_t *bg2rgb = Col2RGB8[bglevel];
uint32_t fg = fg2rgb[fromnew[x]];
uint32_t bg = bg2rgb[fromold[x]];
fg = (fg+bg) | 0x1f07c1f;
to[x] = RGB32k.All[fg & (fg>>15)];
done = false;
}
}
fromold += SCREENWIDTH;
fromnew += SCREENWIDTH;
to += SCREENPITCH;
}
}
else
{
for (y = 0, firey = 0; y < SCREENHEIGHT; y++, firey += ystep)
{
for (x = 0, firex = 0; x < SCREENWIDTH; x++, firex += xstep)
{
int fglevel;
fglevel = burnarray[(firex>>SHIFT)+(firey>>SHIFT)*FIREWIDTH] / 2;
if (fglevel >= 63)
{
to[x] = fromnew[x];
}
else if (fglevel == 0)
{
to[x] = fromold[x];
done = false;
}
else
{
int bglevel = 64-fglevel;
const PalEntry* pal = GPalette.BaseColors;
uint32_t fg = fromnew[x];
uint32_t bg = fromold[x];
int r = MIN((pal[fg].r * fglevel + pal[bg].r * bglevel) >> 8, 63);
int g = MIN((pal[fg].g * fglevel + pal[bg].g * bglevel) >> 8, 63);
int b = MIN((pal[fg].b * fglevel + pal[bg].b * bglevel) >> 8, 63);
to[x] = RGB256k.RGB[r][g][b];
done = false;
}
}
fromold += SCREENWIDTH;
fromnew += SCREENWIDTH;
to += SCREENPITCH;
}
}
return done || (burntime > 40);
}
bool wipe_exitBurn (int ticks)
{
delete[] burnarray;
return 0;
}
// Crossfade --------------------------------------------------------
bool wipe_initFade (int ticks)
{
fade = 0;
return 0;
}
bool wipe_doFade (int ticks)
{
fade += ticks * 2;
if (fade > 64)
{
screen->DrawBlock (0, 0, SCREENWIDTH, SCREENHEIGHT, (uint8_t *)wipe_scr_end);
return true;
}
else
{
int x, y;
int bglevel = 64 - fade;
uint32_t *fg2rgb = Col2RGB8[fade];
uint32_t *bg2rgb = Col2RGB8[bglevel];
uint8_t *fromnew = (uint8_t *)wipe_scr_end;
uint8_t *fromold = (uint8_t *)wipe_scr_start;
uint8_t *to = screen->GetBuffer();
const PalEntry *pal = GPalette.BaseColors;
if (!r_blendmethod)
{
for (y = 0; y < SCREENHEIGHT; y++)
{
for (x = 0; x < SCREENWIDTH; x++)
{
uint32_t fg = fg2rgb[fromnew[x]];
uint32_t bg = bg2rgb[fromold[x]];
fg = (fg+bg) | 0x1f07c1f;
to[x] = RGB32k.All[fg & (fg>>15)];
}
fromnew += SCREENWIDTH;
fromold += SCREENWIDTH;
to += SCREENPITCH;
}
}
else
{
for (y = 0; y < SCREENHEIGHT; y++)
{
for (x = 0; x < SCREENWIDTH; x++)
{
uint32_t fg = fromnew[x];
uint32_t bg = fromold[x];
int r = MIN((pal[fg].r * (64-bglevel) + pal[bg].r * bglevel) >> 8, 63);
int g = MIN((pal[fg].g * (64-bglevel) + pal[bg].g * bglevel) >> 8, 63);
int b = MIN((pal[fg].b * (64-bglevel) + pal[bg].b * bglevel) >> 8, 63);
to[x] = RGB256k.RGB[r][g][b];
}
fromnew += SCREENWIDTH;
fromold += SCREENWIDTH;
to += SCREENPITCH;
}
}
}
return false;
}
bool wipe_exitFade (int ticks)
{
return 0;
}
// General Wipe Functions -------------------------------------------
static bool (*wipes[])(int) =
{
wipe_initMelt, wipe_doMelt, wipe_exitMelt,
wipe_initBurn, wipe_doBurn, wipe_exitBurn,
wipe_initFade, wipe_doFade, wipe_exitFade
};
// Returns true if the wipe should be performed.
bool wipe_StartScreen (int type)
{
if (screen->IsBgra())
return false;
CurrentWipeType = clamp(type, 0, wipe_NUMWIPES - 1);
if (CurrentWipeType)
{
wipe_scr_start = new short[SCREENWIDTH * SCREENHEIGHT / 2];
screen->GetBlock (0, 0, SCREENWIDTH, SCREENHEIGHT, (uint8_t *)wipe_scr_start);
return true;
}
return false;
}
void wipe_EndScreen (void)
{
if (screen->IsBgra())
return;
if (CurrentWipeType)
{
wipe_scr_end = new short[SCREENWIDTH * SCREENHEIGHT / 2];
screen->GetBlock (0, 0, SCREENWIDTH, SCREENHEIGHT, (uint8_t *)wipe_scr_end);
screen->DrawBlock (0, 0, SCREENWIDTH, SCREENHEIGHT, (uint8_t *)wipe_scr_start); // restore start scr.
// Initialize the wipe
(*wipes[(CurrentWipeType-1)*3])(0);
}
}
// Returns true if the wipe is done.
bool wipe_ScreenWipe (int ticks)
{
bool rc;
if (screen->IsBgra())
return true;
if (CurrentWipeType == wipe_None)
return true;
// do a piece of wipe-in
rc = (*wipes[(CurrentWipeType-1)*3+1])(ticks);
return rc;
}
// Final things for the wipe
void wipe_Cleanup()
{
if (screen->IsBgra())
return;
if (wipe_scr_start != NULL)
{
delete[] wipe_scr_start;
wipe_scr_start = NULL;
}
if (wipe_scr_end != NULL)
{
delete[] wipe_scr_end;
wipe_scr_end = NULL;
}
if (CurrentWipeType > 0)
{
(*wipes[(CurrentWipeType-1)*3+2])(0);
}
}