mirror of
https://github.com/TerryCavanagh/VVVVVV.git
synced 2024-11-05 18:59:41 +01:00
93ec2c6cca
The plan is to have Steam screenshots always be 2x, but in the VVVVVV screenshots directory (for F6 keybind) save both 1x and 2x. Again, just for now, the 2x screenshot is being saved to a temporary location for testing and will get proper timestamps later.
385 lines
9.6 KiB
C++
385 lines
9.6 KiB
C++
#include <SDL.h>
|
|
#include <stddef.h>
|
|
#include <stdlib.h>
|
|
|
|
#include "Alloc.h"
|
|
#include "Constants.h"
|
|
#include "Graphics.h"
|
|
#include "Maths.h"
|
|
#include "Screen.h"
|
|
#include "UtilityClass.h"
|
|
#include "Vlogging.h"
|
|
|
|
|
|
|
|
|
|
void setRect( SDL_Rect& _r, int x, int y, int w, int h )
|
|
{
|
|
_r.x = x;
|
|
_r.y = y;
|
|
_r.w = w;
|
|
_r.h = h;
|
|
}
|
|
|
|
static SDL_Surface* RecreateSurfaceWithDimensions(
|
|
SDL_Surface* surface,
|
|
const int width,
|
|
const int height
|
|
) {
|
|
SDL_Surface* retval;
|
|
SDL_BlendMode blend_mode;
|
|
|
|
if (surface == NULL)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
retval = SDL_CreateRGBSurface(
|
|
surface->flags,
|
|
width,
|
|
height,
|
|
surface->format->BitsPerPixel,
|
|
surface->format->Rmask,
|
|
surface->format->Gmask,
|
|
surface->format->Bmask,
|
|
surface->format->Amask
|
|
);
|
|
|
|
if (retval == NULL)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
SDL_GetSurfaceBlendMode(surface, &blend_mode);
|
|
SDL_SetSurfaceBlendMode(retval, blend_mode);
|
|
|
|
return retval;
|
|
}
|
|
|
|
SDL_Surface* GetSubSurface( SDL_Surface* metaSurface, int x, int y, int width, int height )
|
|
{
|
|
// Create an SDL_Rect with the area of the _surface
|
|
SDL_Rect area;
|
|
area.x = x;
|
|
area.y = y;
|
|
area.w = width;
|
|
area.h = height;
|
|
|
|
//Convert to the correct display format after nabbing the new _surface or we will slow things down.
|
|
SDL_Surface* preSurface = RecreateSurfaceWithDimensions(
|
|
metaSurface,
|
|
width,
|
|
height
|
|
);
|
|
|
|
// Lastly, apply the area from the meta _surface onto the whole of the sub _surface.
|
|
SDL_BlitSurface(metaSurface, &area, preSurface, 0);
|
|
|
|
// Return the new Bitmap _surface
|
|
return preSurface;
|
|
}
|
|
|
|
void DrawPixel(SDL_Surface* surface, const int x, const int y, const SDL_Color color)
|
|
{
|
|
const SDL_Point point = {x, y};
|
|
const SDL_Rect rect = {0, 0, surface->w, surface->h};
|
|
const bool inbounds = SDL_PointInRect(&point, &rect);
|
|
if (!inbounds)
|
|
{
|
|
WHINE_ONCE_ARGS((
|
|
"Pixel draw is not inbounds: "
|
|
"Attempted to draw to %i,%i in %ix%i surface",
|
|
x, y, surface->w, surface->h
|
|
));
|
|
SDL_assert(0 && "Pixel draw is not inbounds!");
|
|
return;
|
|
}
|
|
|
|
const SDL_PixelFormat* fmt = surface->format;
|
|
const int bpp = fmt->BytesPerPixel;
|
|
Uint8* pixel = (Uint8*) surface->pixels + y * surface->pitch + x * bpp;
|
|
Uint32* pixel32 = (Uint32*) pixel;
|
|
|
|
switch (bpp)
|
|
{
|
|
case 1:
|
|
case 2:
|
|
SDL_assert(0 && "Colors other than 24- or 32- bit unsupported!");
|
|
break;
|
|
|
|
case 3:
|
|
{
|
|
const Uint32 single = SDL_MapRGB(fmt, color.r, color.g, color.b);
|
|
pixel[0] = (single & 0xFF0000) >> 16;
|
|
pixel[1] = (single & 0x00FF00) >> 8;
|
|
pixel[2] = (single & 0x0000FF) >> 0;
|
|
break;
|
|
}
|
|
|
|
case 4:
|
|
*pixel32 = SDL_MapRGBA(fmt, color.r, color.g, color.b, color.a);
|
|
}
|
|
}
|
|
|
|
SDL_Color ReadPixel(const SDL_Surface* surface, const int x, const int y)
|
|
{
|
|
SDL_Color color = {0, 0, 0, 0};
|
|
const SDL_Point point = {x, y};
|
|
const SDL_Rect rect = {0, 0, surface->w, surface->h};
|
|
const bool inbounds = SDL_PointInRect(&point, &rect);
|
|
if (!inbounds)
|
|
{
|
|
WHINE_ONCE_ARGS((
|
|
"Pixel read is not inbounds: "
|
|
"Attempted to read %i,%i in %ix%i surface",
|
|
x, y, surface->w, surface->h
|
|
));
|
|
SDL_assert(0 && "Pixel read is not inbounds!");
|
|
return color;
|
|
}
|
|
|
|
const SDL_PixelFormat* fmt = surface->format;
|
|
const int bpp = surface->format->BytesPerPixel;
|
|
const Uint8* pixel = (Uint8*) surface->pixels + y * surface->pitch + x * bpp;
|
|
const Uint32* pixel32 = (Uint32*) pixel;
|
|
|
|
switch (bpp)
|
|
{
|
|
case 1:
|
|
case 2:
|
|
SDL_assert(0 && "Colors other than 24- or 32- bit unsupported!");
|
|
break;
|
|
|
|
case 3:
|
|
{
|
|
const Uint32 single = (pixel[0] << 16) | (pixel[1] << 8) | (pixel[2] << 0);
|
|
SDL_GetRGB(single, fmt, &color.r, &color.g, &color.b);
|
|
color.a = 255;
|
|
break;
|
|
}
|
|
|
|
case 4:
|
|
SDL_GetRGBA(*pixel32, fmt, &color.r, &color.g, &color.b, &color.a);
|
|
}
|
|
|
|
return color;
|
|
}
|
|
|
|
static int oldscrollamount = 0;
|
|
static int scrollamount = 0;
|
|
static bool isscrolling = 0;
|
|
|
|
void UpdateFilter(void)
|
|
{
|
|
if (rand() % 4000 < 8)
|
|
{
|
|
isscrolling = true;
|
|
}
|
|
|
|
oldscrollamount = scrollamount;
|
|
if(isscrolling == true)
|
|
{
|
|
scrollamount += 20;
|
|
if(scrollamount > 240)
|
|
{
|
|
scrollamount = 0;
|
|
oldscrollamount = 0;
|
|
isscrolling = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
void ApplyFilter(SDL_Surface** src, SDL_Surface** dest)
|
|
{
|
|
if (src == NULL || dest == NULL)
|
|
{
|
|
SDL_assert(0 && "NULL src or dest!");
|
|
return;
|
|
}
|
|
|
|
if (*src == NULL)
|
|
{
|
|
*src = SDL_CreateRGBSurface(0, SCREEN_WIDTH_PIXELS, SCREEN_HEIGHT_PIXELS, 32, 0, 0, 0, 0);
|
|
}
|
|
if (*dest == NULL)
|
|
{
|
|
*dest = SDL_CreateRGBSurface(0, SCREEN_WIDTH_PIXELS, SCREEN_HEIGHT_PIXELS, 32, 0, 0, 0, 0);
|
|
}
|
|
if (*src == NULL || *dest == NULL)
|
|
{
|
|
WHINE_ONCE_ARGS(("Could not create temporary surfaces: %s", SDL_GetError()));
|
|
return;
|
|
}
|
|
|
|
const int result = SDL_RenderReadPixels(gameScreen.m_renderer, NULL, 0, (*src)->pixels, (*src)->pitch);
|
|
if (result != 0)
|
|
{
|
|
SDL_FreeSurface(*src);
|
|
WHINE_ONCE_ARGS(("Could not read pixels from renderer: %s", SDL_GetError()));
|
|
return;
|
|
}
|
|
|
|
const int red_offset = rand() % 4;
|
|
|
|
for (int x = 0; x < (*src)->w; x++)
|
|
{
|
|
for (int y = 0; y < (*src)->h; y++)
|
|
{
|
|
const int sampley = (y + (int) graphics.lerp(oldscrollamount, scrollamount)) % 240;
|
|
|
|
const SDL_Color pixel = ReadPixel(*src, x, sampley);
|
|
|
|
Uint8 green = pixel.g;
|
|
Uint8 blue = pixel.b;
|
|
|
|
const SDL_Color pixel_offset = ReadPixel(*src, SDL_min(x + red_offset, 319), sampley);
|
|
Uint8 red = pixel_offset.r;
|
|
|
|
double mult;
|
|
int tmp; /* needed to avoid char overflow */
|
|
if (isscrolling && sampley > 220 && ((rand() % 10) < 4))
|
|
{
|
|
mult = 0.6;
|
|
}
|
|
else
|
|
{
|
|
mult = 0.2;
|
|
}
|
|
|
|
tmp = red + fRandom() * mult * 254;
|
|
red = SDL_min(tmp, 255);
|
|
tmp = green + fRandom() * mult * 254;
|
|
green = SDL_min(tmp, 255);
|
|
tmp = blue + fRandom() * mult * 254;
|
|
blue = SDL_min(tmp, 255);
|
|
|
|
if (y % 2 == 0)
|
|
{
|
|
red = (Uint8) (red / 1.2f);
|
|
green = (Uint8) (green / 1.2f);
|
|
blue = (Uint8) (blue / 1.2f);
|
|
}
|
|
|
|
int distX = (int) ((SDL_abs(160.0f - x) / 160.0f) * 16);
|
|
int distY = (int) ((SDL_abs(120.0f - y) / 120.0f) * 32);
|
|
|
|
red = SDL_max(red - (distX + distY), 0);
|
|
green = SDL_max(green - (distX + distY), 0);
|
|
blue = SDL_max(blue - (distX + distY), 0);
|
|
|
|
const SDL_Color color = {red, green, blue, pixel.a};
|
|
DrawPixel(*dest, x, y, color);
|
|
}
|
|
}
|
|
|
|
SDL_UpdateTexture(graphics.gameTexture, NULL, (*dest)->pixels, (*dest)->pitch);
|
|
}
|
|
|
|
bool TakeScreenshot(SDL_Surface** surface)
|
|
{
|
|
if (surface == NULL)
|
|
{
|
|
SDL_assert(0 && "surface is NULL!");
|
|
return false;
|
|
}
|
|
|
|
int width = 0;
|
|
int height = 0;
|
|
int result = graphics.query_texture(
|
|
graphics.gameTexture, NULL, NULL, &width, &height
|
|
);
|
|
if (result != 0)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (*surface == NULL)
|
|
{
|
|
*surface = SDL_CreateRGBSurface(0, width, height, 24, 0, 0, 0, 0);
|
|
if (*surface == NULL)
|
|
{
|
|
WHINE_ONCE_ARGS(
|
|
("Could not create temporary surface: %s", SDL_GetError())
|
|
);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if ((*surface)->w != width || (*surface)->h != height)
|
|
{
|
|
SDL_assert(0 && "Width and height of surface and texture mismatch!");
|
|
return false;
|
|
}
|
|
|
|
result = graphics.set_render_target(graphics.gameTexture);
|
|
if (result != 0)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
result = SDL_RenderReadPixels(
|
|
gameScreen.m_renderer, NULL, SDL_PIXELFORMAT_RGB24,
|
|
(*surface)->pixels, (*surface)->pitch
|
|
);
|
|
if (result != 0)
|
|
{
|
|
WHINE_ONCE_ARGS(
|
|
("Could not read pixels from renderer: %s", SDL_GetError())
|
|
);
|
|
return false;
|
|
}
|
|
|
|
/* Need to manually vertically reverse pixels in Flip Mode. */
|
|
if (graphics.flipmode)
|
|
{
|
|
for (int x = 0; x < (*surface)->w; x++)
|
|
{
|
|
for (int y = 0; y < (*surface)->h / 2; y++)
|
|
{
|
|
const SDL_Color upper = ReadPixel(*surface, x, y);
|
|
const SDL_Color lower = ReadPixel(*surface, x, (*surface)->h - 1 - y);
|
|
DrawPixel(*surface, x, y, lower);
|
|
DrawPixel(*surface, x, (*surface)->h - 1 - y, upper);
|
|
}
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool UpscaleScreenshot2x(SDL_Surface* src, SDL_Surface** dest)
|
|
{
|
|
if (src == NULL)
|
|
{
|
|
SDL_assert(0 && "src is NULL!");
|
|
return false;
|
|
}
|
|
if (dest == NULL)
|
|
{
|
|
SDL_assert(0 && "dest is NULL!");
|
|
return false;
|
|
}
|
|
|
|
if (*dest == NULL)
|
|
{
|
|
*dest = SDL_CreateRGBSurface(
|
|
0, src->w * 2, src->h * 2, src->format->BitsPerPixel, 0, 0, 0, 0
|
|
);
|
|
if (*dest == NULL)
|
|
{
|
|
WHINE_ONCE_ARGS(
|
|
("Could not create temporary surface: %s", SDL_GetError())
|
|
);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
int result = SDL_BlitScaled(src, NULL, *dest, NULL);
|
|
if (result != 0)
|
|
{
|
|
WHINE_ONCE_ARGS(("Could not blit surface: %s", SDL_GetError()));
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|