mirror of
https://github.com/TerryCavanagh/VVVVVV.git
synced 2024-06-17 10:08:29 +02:00
colourTransform is a struct with only one member, a Uint32. The issue with `Uint32`s is that it requires a bunch of bit shifting logic to edit the colors. The issue with bit shifting logic is that people have a tendency to hardcode the shift amounts instead of using the shift amount variables of the SDL_PixelFormat, which makes it annoying to change the color masks of surfaces. This commit fixes both issues by unhardcoding the bit shift amounts in DrawPixel and ReadPixel, and by axing the `Uint32`s in favor of using SDL_Color. According to the SDL_PixelFormat documentation ( https://wiki.libsdl.org/SDL2/SDL_PixelFormat ), the logic to read and draw to pixels from colors below 32-bit was just wrong. Specifically, for 8-bit, there's a color palette used instead of some intrinsic color information stored in the pixel itself. But we shouldn't need that logic anyways because we don't use colors below 32-bit. So I axed that too.
482 lines
12 KiB
C++
482 lines
12 KiB
C++
#include <SDL.h>
|
|
#include <stddef.h>
|
|
#include <stdlib.h>
|
|
|
|
#include "Alloc.h"
|
|
#include "Graphics.h"
|
|
#include "Maths.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;
|
|
}
|
|
|
|
static SDL_Surface* RecreateSurface(SDL_Surface* surface)
|
|
{
|
|
if (surface == NULL)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
return RecreateSurfaceWithDimensions(
|
|
surface,
|
|
surface->w,
|
|
surface->h
|
|
);
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
static void DrawPixel(SDL_Surface* surface, const int x, const int y, const SDL_Color color)
|
|
{
|
|
const SDL_PixelFormat* fmt = surface->format;
|
|
const int bpp = fmt->BytesPerPixel;
|
|
Uint32* pixel = (Uint32*) ((Uint8*) surface->pixels + y * surface->pitch + x * bpp);
|
|
const Uint32 packed =
|
|
(color.r << fmt->Rshift) |
|
|
(color.g << fmt->Gshift) |
|
|
(color.b << fmt->Bshift) |
|
|
(color.a << fmt->Ashift);
|
|
|
|
switch (bpp)
|
|
{
|
|
case 1:
|
|
case 2:
|
|
case 3:
|
|
SDL_assert(0 && "Non-32-bit colors not supported!");
|
|
return;
|
|
|
|
case 4:
|
|
*pixel = packed;
|
|
break;
|
|
}
|
|
}
|
|
|
|
SDL_Color ReadPixel(const SDL_Surface* surface, const int x, const int y)
|
|
{
|
|
const SDL_PixelFormat* fmt = surface->format;
|
|
const int bpp = surface->format->BytesPerPixel;
|
|
const Uint32* pixel = (Uint32*) ((Uint8*) surface->pixels + y * surface->pitch + x * bpp);
|
|
|
|
switch (bpp)
|
|
{
|
|
case 1:
|
|
case 2:
|
|
case 3:
|
|
{
|
|
SDL_assert(0 && "Non-32-bit colors not supported!");
|
|
const SDL_Color color = {0, 0, 0, 0};
|
|
return color;
|
|
}
|
|
|
|
case 4:
|
|
{
|
|
const SDL_Color color = {
|
|
(Uint8) ((*pixel & fmt->Rmask) >> fmt->Rshift),
|
|
(Uint8) ((*pixel & fmt->Gmask) >> fmt->Gshift),
|
|
(Uint8) ((*pixel & fmt->Bmask) >> fmt->Bshift),
|
|
(Uint8) ((*pixel & fmt->Amask) >> fmt->Ashift)
|
|
};
|
|
return color;
|
|
}
|
|
}
|
|
|
|
/* shouldn't happen, but avoids warnings */
|
|
const SDL_Color color = {0, 0, 0, 0};
|
|
return color;
|
|
}
|
|
|
|
SDL_Surface * ScaleSurface( SDL_Surface *_surface, int Width, int Height, SDL_Surface * Dest )
|
|
{
|
|
if(!_surface || !Width || !Height)
|
|
return 0;
|
|
|
|
SDL_Surface *_ret;
|
|
if(Dest == NULL)
|
|
{
|
|
_ret = RecreateSurfaceWithDimensions(_surface, Width, Height);
|
|
if(_ret == NULL)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
}
|
|
else
|
|
{
|
|
_ret = Dest;
|
|
}
|
|
|
|
SDL_BlitScaled(_surface, NULL, _ret, NULL);
|
|
|
|
return _ret;
|
|
}
|
|
|
|
SDL_Surface * FlipSurfaceVerticle(SDL_Surface* _src)
|
|
{
|
|
SDL_Surface * ret = RecreateSurface(_src);
|
|
if(ret == NULL)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
for(Sint32 y = 0; y < _src->h; y++)
|
|
{
|
|
for(Sint32 x = 0; x < _src->w; x++)
|
|
{
|
|
DrawPixel(ret, x ,(_src->h-1) - y ,ReadPixel(_src, x, y));
|
|
}
|
|
|
|
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
void BlitSurfaceStandard( SDL_Surface* _src, SDL_Rect* _srcRect, SDL_Surface* _dest, SDL_Rect* _destRect )
|
|
{
|
|
SDL_BlitSurface( _src, _srcRect, _dest, _destRect );
|
|
}
|
|
|
|
void BlitSurfaceColoured(
|
|
SDL_Surface* src,
|
|
const SDL_Rect* src_rect,
|
|
SDL_Surface* dest,
|
|
SDL_Rect* dest_rect,
|
|
const SDL_Color color
|
|
) {
|
|
SDL_Surface* tempsurface = RecreateSurface(src);
|
|
|
|
for (int x = 0; x < tempsurface->w; x++)
|
|
{
|
|
for (int y = 0; y < tempsurface->h; y++)
|
|
{
|
|
const SDL_Color pixel = ReadPixel(src, x, y);
|
|
const float div1 = pixel.a / 255.0f;
|
|
const float div2 = color.a / 255.0f;
|
|
const Uint8 alpha = (div1 * div2) * 255.0f;
|
|
const SDL_Color result = {color.r, color.g, color.b, alpha};
|
|
DrawPixel(tempsurface, x, y, result);
|
|
}
|
|
}
|
|
|
|
SDL_BlitSurface(tempsurface, src_rect, dest, dest_rect);
|
|
VVV_freefunc(SDL_FreeSurface, tempsurface);
|
|
}
|
|
|
|
void BlitSurfaceTinted(
|
|
SDL_Surface* src,
|
|
const SDL_Rect* src_rect,
|
|
SDL_Surface* dest,
|
|
SDL_Rect* dest_rect,
|
|
const SDL_Color color
|
|
) {
|
|
SDL_Surface* tempsurface = RecreateSurface(src);
|
|
|
|
for (int x = 0; x < tempsurface->w; x++)
|
|
{
|
|
for (int y = 0; y < tempsurface->h; y++)
|
|
{
|
|
const SDL_Color pixel = ReadPixel(src, x, y);
|
|
|
|
double red = pixel.r * 0.299;
|
|
double green = pixel.g * 0.587;
|
|
double blue = pixel.b * 0.114;
|
|
|
|
const double gray = SDL_floor(red + green + blue + 0.5);
|
|
|
|
red = gray * color.r / 255.0;
|
|
green = gray * color.g / 255.0;
|
|
blue = gray * color.b / 255.0;
|
|
|
|
red = SDL_clamp(red, 0, 255);
|
|
green = SDL_clamp(green, 0, 255);
|
|
blue = SDL_clamp(blue, 0, 255);
|
|
|
|
const float div1 = pixel.a / 255.0f;
|
|
const float div2 = color.a / 255.0f;
|
|
const Uint8 alpha = (div1 * div2) * 255.0f;
|
|
|
|
const SDL_Color result = {(Uint8) red, (Uint8) green, (Uint8) blue, alpha};
|
|
DrawPixel(tempsurface, x, y, result);
|
|
}
|
|
}
|
|
|
|
SDL_BlitSurface(tempsurface, src_rect, dest, dest_rect);
|
|
VVV_freefunc(SDL_FreeSurface, tempsurface);
|
|
}
|
|
|
|
|
|
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;
|
|
}
|
|
}
|
|
}
|
|
|
|
SDL_Surface* ApplyFilter(SDL_Surface* src)
|
|
{
|
|
SDL_Surface* ret = RecreateSurface(src);
|
|
|
|
const int red_offset = rand() % 4;
|
|
|
|
for (int x = 0; x < ret->w; x++)
|
|
{
|
|
for (int y = 0; y < ret->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(ret, x, y, color);
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
void FillRect( SDL_Surface* _surface, const int _x, const int _y, const int _w, const int _h, const int r, int g, int b )
|
|
{
|
|
SDL_Rect rect = {_x, _y, _w, _h};
|
|
Uint32 color = SDL_MapRGB(_surface->format, r, g, b);
|
|
SDL_FillRect(_surface, &rect, color);
|
|
}
|
|
|
|
void FillRect( SDL_Surface* _surface, const int r, int g, int b )
|
|
{
|
|
Uint32 color = SDL_MapRGB(_surface->format, r, g, b);
|
|
SDL_FillRect(_surface, NULL, color);
|
|
}
|
|
|
|
void FillRect( SDL_Surface* _surface, SDL_Rect& _rect, const int r, int g, int b )
|
|
{
|
|
Uint32 color = SDL_MapRGB(_surface->format, r, g, b);
|
|
SDL_FillRect(_surface, &_rect, color);
|
|
}
|
|
|
|
void FillRect(SDL_Surface* surface, const SDL_Rect rect, const SDL_Color color)
|
|
{
|
|
const Uint32 mapped = SDL_MapRGBA(surface->format, color.r, color.g, color.b, color.a);
|
|
SDL_FillRect(surface, &rect, mapped);
|
|
}
|
|
|
|
void FillRect(SDL_Surface* surface, const SDL_Color color)
|
|
{
|
|
const Uint32 mapped = SDL_MapRGBA(surface->format, color.r, color.g, color.b, color.a);
|
|
SDL_FillRect(surface, NULL, mapped);
|
|
}
|
|
|
|
void FillRect(SDL_Surface* surface, const int x, const int y, const int w, const int h, const SDL_Color color)
|
|
{
|
|
const SDL_Rect rect = {x, y, w, h};
|
|
const Uint32 mapped = SDL_MapRGBA(surface->format, color.r, color.g, color.b, color.a);
|
|
SDL_FillRect(surface, &rect, mapped);
|
|
}
|
|
|
|
void FillRect(SDL_Surface* surface, const int r, const int g, const int b, const int a)
|
|
{
|
|
const Uint32 mapped = SDL_MapRGBA(surface->format, r, g, b, a);
|
|
SDL_FillRect(surface, NULL, mapped);
|
|
}
|
|
|
|
void ClearSurface(SDL_Surface* surface)
|
|
{
|
|
SDL_FillRect(surface, NULL, 0x00000000);
|
|
}
|
|
|
|
void ScrollSurface( SDL_Surface* _src, int _pX, int _pY )
|
|
{
|
|
SDL_Surface* part1 = NULL;
|
|
|
|
SDL_Rect rect1;
|
|
SDL_Rect rect2;
|
|
//scrolling up;
|
|
if(_pY < 0)
|
|
{
|
|
setRect(rect2, 0, 0, _src->w, _src->h - _pY);
|
|
|
|
part1 = GetSubSurface(_src, rect2.x, rect2.y, rect2.w, rect2.h);
|
|
|
|
SDL_Rect destrect1;
|
|
|
|
SDL_SetSurfaceBlendMode(part1, SDL_BLENDMODE_NONE);
|
|
|
|
setRect(destrect1, 0, _pY, _pX, _src->h);
|
|
|
|
SDL_BlitSurface (part1, NULL, _src, &destrect1);
|
|
}
|
|
|
|
else if(_pY > 0)
|
|
{
|
|
|
|
setRect(rect1, 0, 0, _src->w, _src->h - _pY);
|
|
|
|
part1 = GetSubSurface(_src, rect1.x, rect1.y, rect1.w, rect1.h);
|
|
|
|
SDL_Rect destrect1;
|
|
|
|
SDL_SetSurfaceBlendMode(part1, SDL_BLENDMODE_NONE);
|
|
|
|
setRect(destrect1, _pX, _pY, _src->w, _src->h - _pY);
|
|
|
|
SDL_BlitSurface (part1, NULL, _src, &destrect1);
|
|
|
|
}
|
|
|
|
//Right
|
|
else if(_pX <= 0)
|
|
{
|
|
setRect(rect2, 0, 0, _src->w - _pX, _src->h );
|
|
|
|
part1 = GetSubSurface(_src, rect2.x, rect2.y, rect2.w, rect2.h);
|
|
|
|
SDL_Rect destrect1;
|
|
|
|
SDL_SetSurfaceBlendMode(part1, SDL_BLENDMODE_NONE);
|
|
|
|
setRect(destrect1, _pX, 0, _src->w - _pX, _src->h);
|
|
|
|
SDL_BlitSurface (part1, NULL, _src, &destrect1);
|
|
}
|
|
|
|
else if(_pX > 0)
|
|
{
|
|
|
|
setRect(rect1, _pX, 0, _src->w - _pX, _src->h );
|
|
|
|
part1 = GetSubSurface(_src, rect1.x, rect1.y, rect1.w, rect1.h);
|
|
|
|
SDL_Rect destrect1;
|
|
|
|
SDL_SetSurfaceBlendMode(part1, SDL_BLENDMODE_NONE);
|
|
|
|
setRect(destrect1, 0, 0, _src->w - _pX, _src->h);
|
|
|
|
SDL_BlitSurface (part1, NULL, _src, &destrect1);
|
|
|
|
}
|
|
//Cleanup temp surface
|
|
if (part1)
|
|
{
|
|
VVV_freefunc(SDL_FreeSurface, part1);
|
|
}
|
|
|
|
}
|