mirror of
https://github.com/ddnet/ddnet.git
synced 2024-11-18 22:18:19 +00:00
Merge #3327
3327: Check skin/sprite images for correctness r=def- a=Jupeyy I hope this helps with downloading things at wrong resolution again. Note that this commit is smaller than it seems, bcs of renaming files and moving code to it. Also this requires new translations. In the skin database are invalid skins too: ![screenshot_2020-11-18_07-51-02](https://user-images.githubusercontent.com/6654924/99495409-e7637e80-2972-11eb-91ec-f8bb02a80e83.png) ![screenshot_2020-11-18_07-51-05](https://user-images.githubusercontent.com/6654924/99495410-e7fc1500-2972-11eb-94b8-aedc98e8ef67.png) ## Checklist - [x] Tested the change ingame - [ ] Provided screenshots if it is a visual change - [ ] Tested in combination with possibly related configuration options - [ ] Written a unit test if it works standalone, system.c especially - [ ] Considered possible null pointers and out of bounds array indexing - [ ] Changed no physics that affect existing maps - [ ] Tested the change with [ASan+UBSan or valgrind's memcheck](https://github.com/ddnet/ddnet/#using-addresssanitizer--undefinedbehavioursanitizer-or-valgrinds-memcheck) (optional) Co-authored-by: Jupeyy <jupjopjap@gmail.com>
This commit is contained in:
commit
078247dd3e
|
@ -1490,8 +1490,6 @@ set_src(ENGINE_SHARED GLOB src/engine/shared
|
|||
datafile.h
|
||||
demo.cpp
|
||||
demo.h
|
||||
dilate.cpp
|
||||
dilate.h
|
||||
econ.cpp
|
||||
econ.h
|
||||
engine.cpp
|
||||
|
@ -1502,6 +1500,8 @@ set_src(ENGINE_SHARED GLOB src/engine/shared
|
|||
global_uuid_manager.cpp
|
||||
huffman.cpp
|
||||
huffman.h
|
||||
image_manipulation.cpp
|
||||
image_manipulation.h
|
||||
jobs.cpp
|
||||
jobs.h
|
||||
json.cpp
|
||||
|
|
|
@ -36,6 +36,8 @@
|
|||
#include "opengl_sl.h"
|
||||
#include "opengl_sl_program.h"
|
||||
|
||||
#include <engine/shared/image_manipulation.h>
|
||||
|
||||
#ifdef __MINGW32__
|
||||
extern "C" {
|
||||
int putenv(const char *);
|
||||
|
@ -177,19 +179,6 @@ bool CCommandProcessorFragment_General::RunCommand(const CCommandBuffer::SComman
|
|||
|
||||
// ------------ CCommandProcessorFragment_OpenGL
|
||||
|
||||
static int HighestBit(int OfVar)
|
||||
{
|
||||
if(!OfVar)
|
||||
return 0;
|
||||
|
||||
int RetV = 1;
|
||||
|
||||
while(OfVar >>= 1)
|
||||
RetV <<= 1;
|
||||
|
||||
return RetV;
|
||||
}
|
||||
|
||||
int CCommandProcessorFragment_OpenGL::TexFormatToOpenGLFormat(int TexFormat)
|
||||
{
|
||||
if(TexFormat == CCommandBuffer::TEXFORMAT_RGB)
|
||||
|
@ -212,127 +201,11 @@ int CCommandProcessorFragment_OpenGL::TexFormatToImageColorChannelCount(int TexF
|
|||
return 4;
|
||||
}
|
||||
|
||||
static float CubicHermite(float A, float B, float C, float D, float t)
|
||||
{
|
||||
float a = -A / 2.0f + (3.0f * B) / 2.0f - (3.0f * C) / 2.0f + D / 2.0f;
|
||||
float b = A - (5.0f * B) / 2.0f + 2.0f * C - D / 2.0f;
|
||||
float c = -A / 2.0f + C / 2.0f;
|
||||
float d = B;
|
||||
|
||||
return (a * t * t * t) + (b * t * t) + (c * t) + d;
|
||||
}
|
||||
|
||||
static void GetPixelClamped(uint8_t *pSourceImage, int x, int y, uint32_t W, uint32_t H, size_t BPP, uint8_t aTmp[])
|
||||
{
|
||||
x = clamp<int>(x, 0, (int)W - 1);
|
||||
y = clamp<int>(y, 0, (int)H - 1);
|
||||
|
||||
for(size_t i = 0; i < BPP; i++)
|
||||
{
|
||||
aTmp[i] = pSourceImage[x * BPP + (W * BPP * y) + i];
|
||||
}
|
||||
}
|
||||
|
||||
static void SampleBicubic(uint8_t *pSourceImage, float u, float v, uint32_t W, uint32_t H, size_t BPP, uint8_t aSample[])
|
||||
{
|
||||
float X = (u * W) - 0.5f;
|
||||
int xInt = (int)X;
|
||||
float xFract = X - floorf(X);
|
||||
|
||||
float Y = (v * H) - 0.5f;
|
||||
int yInt = (int)Y;
|
||||
float yFract = Y - floorf(Y);
|
||||
|
||||
uint8_t PX00[4];
|
||||
uint8_t PX10[4];
|
||||
uint8_t PX20[4];
|
||||
uint8_t PX30[4];
|
||||
|
||||
uint8_t PX01[4];
|
||||
uint8_t PX11[4];
|
||||
uint8_t PX21[4];
|
||||
uint8_t PX31[4];
|
||||
|
||||
uint8_t PX02[4];
|
||||
uint8_t PX12[4];
|
||||
uint8_t PX22[4];
|
||||
uint8_t PX32[4];
|
||||
|
||||
uint8_t PX03[4];
|
||||
uint8_t PX13[4];
|
||||
uint8_t PX23[4];
|
||||
uint8_t PX33[4];
|
||||
|
||||
GetPixelClamped(pSourceImage, xInt - 1, yInt - 1, W, H, BPP, PX00);
|
||||
GetPixelClamped(pSourceImage, xInt + 0, yInt - 1, W, H, BPP, PX10);
|
||||
GetPixelClamped(pSourceImage, xInt + 1, yInt - 1, W, H, BPP, PX20);
|
||||
GetPixelClamped(pSourceImage, xInt + 2, yInt - 1, W, H, BPP, PX30);
|
||||
|
||||
GetPixelClamped(pSourceImage, xInt - 1, yInt + 0, W, H, BPP, PX01);
|
||||
GetPixelClamped(pSourceImage, xInt + 0, yInt + 0, W, H, BPP, PX11);
|
||||
GetPixelClamped(pSourceImage, xInt + 1, yInt + 0, W, H, BPP, PX21);
|
||||
GetPixelClamped(pSourceImage, xInt + 2, yInt + 0, W, H, BPP, PX31);
|
||||
|
||||
GetPixelClamped(pSourceImage, xInt - 1, yInt + 1, W, H, BPP, PX02);
|
||||
GetPixelClamped(pSourceImage, xInt + 0, yInt + 1, W, H, BPP, PX12);
|
||||
GetPixelClamped(pSourceImage, xInt + 1, yInt + 1, W, H, BPP, PX22);
|
||||
GetPixelClamped(pSourceImage, xInt + 2, yInt + 1, W, H, BPP, PX32);
|
||||
|
||||
GetPixelClamped(pSourceImage, xInt - 1, yInt + 2, W, H, BPP, PX03);
|
||||
GetPixelClamped(pSourceImage, xInt + 0, yInt + 2, W, H, BPP, PX13);
|
||||
GetPixelClamped(pSourceImage, xInt + 1, yInt + 2, W, H, BPP, PX23);
|
||||
GetPixelClamped(pSourceImage, xInt + 2, yInt + 2, W, H, BPP, PX33);
|
||||
|
||||
for(size_t i = 0; i < BPP; i++)
|
||||
{
|
||||
float Clmn0 = CubicHermite(PX00[i], PX10[i], PX20[i], PX30[i], xFract);
|
||||
float Clmn1 = CubicHermite(PX01[i], PX11[i], PX21[i], PX31[i], xFract);
|
||||
float Clmn2 = CubicHermite(PX02[i], PX12[i], PX22[i], PX32[i], xFract);
|
||||
float Clmn3 = CubicHermite(PX03[i], PX13[i], PX23[i], PX33[i], xFract);
|
||||
|
||||
float Valuef = CubicHermite(Clmn0, Clmn1, Clmn2, Clmn3, yFract);
|
||||
|
||||
Valuef = clamp<float>(Valuef, 0.0f, 255.0f);
|
||||
|
||||
aSample[i] = (uint8_t)Valuef;
|
||||
}
|
||||
}
|
||||
|
||||
static void ResizeImage(uint8_t *pSourceImage, uint32_t SW, uint32_t SH, uint8_t *pDestinationImage, uint32_t W, uint32_t H, size_t BPP)
|
||||
{
|
||||
uint8_t aSample[4];
|
||||
int y, x;
|
||||
|
||||
for(y = 0; y < (int)H; ++y)
|
||||
{
|
||||
float v = (float)y / (float)(H - 1);
|
||||
|
||||
for(x = 0; x < (int)W; ++x)
|
||||
{
|
||||
float u = (float)x / (float)(W - 1);
|
||||
SampleBicubic(pSourceImage, u, v, SW, SH, BPP, aSample);
|
||||
|
||||
for(size_t i = 0; i < BPP; ++i)
|
||||
{
|
||||
pDestinationImage[x * BPP + ((W * BPP) * y) + i] = aSample[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void *CCommandProcessorFragment_OpenGL::Resize(int Width, int Height, int NewWidth, int NewHeight, int Format, const unsigned char *pData)
|
||||
{
|
||||
unsigned char *pTmpData;
|
||||
|
||||
int Bpp = TexFormatToImageColorChannelCount(Format);
|
||||
|
||||
// All calls to Resize() ensure width & height are > 0, Bpp is always > 0,
|
||||
// thus no allocation size 0 possible.
|
||||
pTmpData = (unsigned char *)malloc((size_t)NewWidth * NewHeight * Bpp); // NOLINT(clang-analyzer-optin.portability.UnixAPI)
|
||||
|
||||
ResizeImage((uint8_t *)pData, Width, Height, (uint8_t *)pTmpData, NewWidth, NewHeight, Bpp);
|
||||
|
||||
return pTmpData;
|
||||
return ResizeImage((const uint8_t *)pData, Width, Height, NewWidth, NewHeight, Bpp);
|
||||
}
|
||||
|
||||
bool CCommandProcessorFragment_OpenGL::IsTexturedState(const CCommandBuffer::SState &State)
|
||||
|
|
|
@ -22,6 +22,8 @@
|
|||
#include <game/generated/client_data7.h>
|
||||
#include <game/localization.h>
|
||||
|
||||
#include <engine/shared/image_manipulation.h>
|
||||
|
||||
#include <math.h> // cosf, sinf, log2f
|
||||
|
||||
#if defined(CONF_VIDEORECORDER)
|
||||
|
@ -572,6 +574,56 @@ int CGraphics_Threaded::LoadPNG(CImageInfo *pImg, const char *pFilename, int Sto
|
|||
return 1;
|
||||
}
|
||||
|
||||
bool CGraphics_Threaded::CheckImageDivisibility(const char *pFileName, CImageInfo &Img, int DivX, int DivY, bool AllowResize)
|
||||
{
|
||||
dbg_assert(DivX != 0 && DivY != 0, "Passing 0 to this function is not allowed.");
|
||||
bool ImageIsValid = true;
|
||||
bool WidthBroken = Img.m_Width == 0 || (Img.m_Width % DivX) != 0;
|
||||
bool HeightBroken = Img.m_Height == 0 || (Img.m_Height % DivY) != 0;
|
||||
if(WidthBroken || HeightBroken)
|
||||
{
|
||||
SWarning NewWarning;
|
||||
str_format(NewWarning.m_aWarningMsg, sizeof(NewWarning.m_aWarningMsg), Localize("The width of texture %s is not divisible by %d, or the height is not divisible by %d, which might cause visual bugs."), pFileName, DivX, DivY);
|
||||
|
||||
m_Warnings.emplace_back(NewWarning);
|
||||
|
||||
ImageIsValid = false;
|
||||
}
|
||||
|
||||
if(AllowResize && !ImageIsValid && Img.m_Width > 0 && Img.m_Height > 0)
|
||||
{
|
||||
int NewWidth = DivX;
|
||||
int NewHeight = DivY;
|
||||
if(WidthBroken)
|
||||
{
|
||||
NewWidth = maximum<int>(HighestBit(Img.m_Width), DivX);
|
||||
NewHeight = (NewWidth / DivX) * DivY;
|
||||
}
|
||||
else
|
||||
{
|
||||
NewHeight = maximum<int>(HighestBit(Img.m_Height), DivY);
|
||||
NewWidth = (NewHeight / DivY) * DivX;
|
||||
}
|
||||
|
||||
int ColorChannelCount = 4;
|
||||
if(Img.m_Format == CImageInfo::FORMAT_ALPHA)
|
||||
ColorChannelCount = 1;
|
||||
else if(Img.m_Format == CImageInfo::FORMAT_RGB)
|
||||
ColorChannelCount = 3;
|
||||
else if(Img.m_Format == CImageInfo::FORMAT_RGBA)
|
||||
ColorChannelCount = 4;
|
||||
|
||||
uint8_t *pNewImg = ResizeImage((uint8_t *)Img.m_pData, Img.m_Width, Img.m_Height, NewWidth, NewHeight, ColorChannelCount);
|
||||
free(Img.m_pData);
|
||||
Img.m_pData = pNewImg;
|
||||
Img.m_Width = NewWidth;
|
||||
Img.m_Height = NewHeight;
|
||||
ImageIsValid = true;
|
||||
}
|
||||
|
||||
return ImageIsValid;
|
||||
}
|
||||
|
||||
void CGraphics_Threaded::CopyTextureBufferSub(uint8_t *pDestBuffer, uint8_t *pSourceBuffer, int FullWidth, int FullHeight, int ColorChannelCount, int SubOffsetX, int SubOffsetY, int SubCopyWidth, int SubCopyHeight)
|
||||
{
|
||||
for(int Y = 0; Y < SubCopyHeight; ++Y)
|
||||
|
|
|
@ -845,6 +845,8 @@ public:
|
|||
IGraphics::CTextureHandle LoadTexture(const char *pFilename, int StorageType, int StoreFormat, int Flags) override;
|
||||
int LoadPNG(CImageInfo *pImg, const char *pFilename, int StorageType) override;
|
||||
|
||||
bool CheckImageDivisibility(const char *pFileName, CImageInfo &Img, int DivX, int DivY, bool AllowResize) override;
|
||||
|
||||
void CopyTextureBufferSub(uint8_t *pDestBuffer, uint8_t *pSourceBuffer, int FullWidth, int FullHeight, int ColorChannelCount, int SubOffsetX, int SubOffsetY, int SubCopyWidth, int SubCopyHeight) override;
|
||||
void CopyTextureFromTextureBufferSub(uint8_t *pDestBuffer, int DestWidth, int DestHeight, uint8_t *pSourceBuffer, int SrcWidth, int SrcHeight, int ColorChannelCount, int SrcSubOffsetX, int SrcSubOffsetY, int SrcSubCopyWidth, int SrcSubCopyHeight) override;
|
||||
|
||||
|
|
|
@ -222,6 +222,8 @@ public:
|
|||
|
||||
virtual int LoadPNG(CImageInfo *pImg, const char *pFilename, int StorageType) = 0;
|
||||
|
||||
virtual bool CheckImageDivisibility(const char *pFileName, CImageInfo &Img, int DivX, int DivY, bool AllowResize) = 0;
|
||||
|
||||
// destination and source buffer require to have the same width and height
|
||||
virtual void CopyTextureBufferSub(uint8_t *pDestBuffer, uint8_t *pSourceBuffer, int FullWidth, int FullHeight, int ColorChannelCount, int SubOffsetX, int SubOffsetY, int SubCopyWidth, int SubCopyHeight) = 0;
|
||||
|
||||
|
|
|
@ -1,89 +0,0 @@
|
|||
#include <base/math.h>
|
||||
#include <base/system.h>
|
||||
|
||||
static void Dilate(int w, int h, int BPP, unsigned char *pSrc, unsigned char *pDest, unsigned char AlphaThreshold = 30)
|
||||
{
|
||||
int ix, iy;
|
||||
const int aDirX[] = {0, -1, 1, 0};
|
||||
const int aDirY[] = {-1, 0, 0, 1};
|
||||
|
||||
int AlphaCompIndex = BPP - 1;
|
||||
|
||||
int m = 0;
|
||||
for(int y = 0; y < h; y++)
|
||||
{
|
||||
for(int x = 0; x < w; x++, m += BPP)
|
||||
{
|
||||
for(int i = 0; i < BPP; ++i)
|
||||
pDest[m + i] = pSrc[m + i];
|
||||
if(pSrc[m + AlphaCompIndex] > AlphaThreshold)
|
||||
continue;
|
||||
|
||||
int aSumOfOpaque[] = {0, 0, 0};
|
||||
int Counter = 0;
|
||||
for(int c = 0; c < 4; c++)
|
||||
{
|
||||
ix = clamp(x + aDirX[c], 0, w - 1);
|
||||
iy = clamp(y + aDirY[c], 0, h - 1);
|
||||
int k = iy * w * BPP + ix * BPP;
|
||||
if(pSrc[k + AlphaCompIndex] > AlphaThreshold)
|
||||
{
|
||||
for(int p = 0; p < BPP - 1; ++p)
|
||||
// Seems safe for BPP = 3, 4 which we use. clang-analyzer seems to
|
||||
// asssume being called with larger value. TODO: Can make this
|
||||
// safer anyway.
|
||||
aSumOfOpaque[p] += pSrc[k + p]; // NOLINT(clang-analyzer-core.uninitialized.Assign)
|
||||
++Counter;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(Counter > 0)
|
||||
{
|
||||
for(int i = 0; i < BPP - 1; ++i)
|
||||
{
|
||||
aSumOfOpaque[i] /= Counter;
|
||||
pDest[m + i] = (unsigned char)aSumOfOpaque[i];
|
||||
}
|
||||
|
||||
pDest[m + AlphaCompIndex] = 255;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void CopyColorValues(int w, int h, int BPP, unsigned char *pSrc, unsigned char *pDest)
|
||||
{
|
||||
int m = 0;
|
||||
for(int y = 0; y < h; y++)
|
||||
{
|
||||
for(int x = 0; x < w; x++, m += BPP)
|
||||
{
|
||||
for(int i = 0; i < BPP - 1; ++i)
|
||||
pDest[m + i] = pSrc[m + i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DilateImage(unsigned char *pImageBuff, int w, int h, int BPP)
|
||||
{
|
||||
unsigned char *apBuffer[2] = {NULL, NULL};
|
||||
|
||||
apBuffer[0] = (unsigned char *)malloc((size_t)w * h * sizeof(unsigned char) * BPP);
|
||||
apBuffer[1] = (unsigned char *)malloc((size_t)w * h * sizeof(unsigned char) * BPP);
|
||||
|
||||
unsigned char *pPixelBuff = (unsigned char *)pImageBuff;
|
||||
|
||||
Dilate(w, h, BPP, pPixelBuff, apBuffer[0]);
|
||||
|
||||
for(int i = 0; i < 5; i++)
|
||||
{
|
||||
Dilate(w, h, BPP, apBuffer[0], apBuffer[1]);
|
||||
Dilate(w, h, BPP, apBuffer[1], apBuffer[0]);
|
||||
}
|
||||
|
||||
CopyColorValues(w, h, BPP, apBuffer[0], pPixelBuff);
|
||||
|
||||
free(apBuffer[0]);
|
||||
free(apBuffer[1]);
|
||||
}
|
|
@ -1,6 +0,0 @@
|
|||
#ifndef ENGINE_SHARED_DILATE_H
|
||||
#define ENGINE_SHARED_DILATE_H
|
||||
|
||||
void DilateImage(unsigned char *pImageBuff, int w, int h, int BPP);
|
||||
|
||||
#endif
|
222
src/engine/shared/image_manipulation.cpp
Normal file
222
src/engine/shared/image_manipulation.cpp
Normal file
|
@ -0,0 +1,222 @@
|
|||
#include "image_manipulation.h"
|
||||
#include <base/math.h>
|
||||
#include <base/system.h>
|
||||
|
||||
static void Dilate(int w, int h, int BPP, unsigned char *pSrc, unsigned char *pDest, unsigned char AlphaThreshold = 30)
|
||||
{
|
||||
int ix, iy;
|
||||
const int aDirX[] = {0, -1, 1, 0};
|
||||
const int aDirY[] = {-1, 0, 0, 1};
|
||||
|
||||
int AlphaCompIndex = BPP - 1;
|
||||
|
||||
int m = 0;
|
||||
for(int y = 0; y < h; y++)
|
||||
{
|
||||
for(int x = 0; x < w; x++, m += BPP)
|
||||
{
|
||||
for(int i = 0; i < BPP; ++i)
|
||||
pDest[m + i] = pSrc[m + i];
|
||||
if(pSrc[m + AlphaCompIndex] > AlphaThreshold)
|
||||
continue;
|
||||
|
||||
int aSumOfOpaque[] = {0, 0, 0};
|
||||
int Counter = 0;
|
||||
for(int c = 0; c < 4; c++)
|
||||
{
|
||||
ix = clamp(x + aDirX[c], 0, w - 1);
|
||||
iy = clamp(y + aDirY[c], 0, h - 1);
|
||||
int k = iy * w * BPP + ix * BPP;
|
||||
if(pSrc[k + AlphaCompIndex] > AlphaThreshold)
|
||||
{
|
||||
for(int p = 0; p < BPP - 1; ++p)
|
||||
// Seems safe for BPP = 3, 4 which we use. clang-analyzer seems to
|
||||
// asssume being called with larger value. TODO: Can make this
|
||||
// safer anyway.
|
||||
aSumOfOpaque[p] += pSrc[k + p]; // NOLINT(clang-analyzer-core.uninitialized.Assign)
|
||||
++Counter;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(Counter > 0)
|
||||
{
|
||||
for(int i = 0; i < BPP - 1; ++i)
|
||||
{
|
||||
aSumOfOpaque[i] /= Counter;
|
||||
pDest[m + i] = (unsigned char)aSumOfOpaque[i];
|
||||
}
|
||||
|
||||
pDest[m + AlphaCompIndex] = 255;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void CopyColorValues(int w, int h, int BPP, unsigned char *pSrc, unsigned char *pDest)
|
||||
{
|
||||
int m = 0;
|
||||
for(int y = 0; y < h; y++)
|
||||
{
|
||||
for(int x = 0; x < w; x++, m += BPP)
|
||||
{
|
||||
for(int i = 0; i < BPP - 1; ++i)
|
||||
pDest[m + i] = pSrc[m + i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DilateImage(unsigned char *pImageBuff, int w, int h, int BPP)
|
||||
{
|
||||
unsigned char *apBuffer[2] = {NULL, NULL};
|
||||
|
||||
apBuffer[0] = (unsigned char *)malloc((size_t)w * h * sizeof(unsigned char) * BPP);
|
||||
apBuffer[1] = (unsigned char *)malloc((size_t)w * h * sizeof(unsigned char) * BPP);
|
||||
|
||||
unsigned char *pPixelBuff = (unsigned char *)pImageBuff;
|
||||
|
||||
Dilate(w, h, BPP, pPixelBuff, apBuffer[0]);
|
||||
|
||||
for(int i = 0; i < 5; i++)
|
||||
{
|
||||
Dilate(w, h, BPP, apBuffer[0], apBuffer[1]);
|
||||
Dilate(w, h, BPP, apBuffer[1], apBuffer[0]);
|
||||
}
|
||||
|
||||
CopyColorValues(w, h, BPP, apBuffer[0], pPixelBuff);
|
||||
|
||||
free(apBuffer[0]);
|
||||
free(apBuffer[1]);
|
||||
}
|
||||
|
||||
static float CubicHermite(float A, float B, float C, float D, float t)
|
||||
{
|
||||
float a = -A / 2.0f + (3.0f * B) / 2.0f - (3.0f * C) / 2.0f + D / 2.0f;
|
||||
float b = A - (5.0f * B) / 2.0f + 2.0f * C - D / 2.0f;
|
||||
float c = -A / 2.0f + C / 2.0f;
|
||||
float d = B;
|
||||
|
||||
return (a * t * t * t) + (b * t * t) + (c * t) + d;
|
||||
}
|
||||
|
||||
static void GetPixelClamped(const uint8_t *pSourceImage, int x, int y, uint32_t W, uint32_t H, size_t BPP, uint8_t aTmp[])
|
||||
{
|
||||
x = clamp<int>(x, 0, (int)W - 1);
|
||||
y = clamp<int>(y, 0, (int)H - 1);
|
||||
|
||||
for(size_t i = 0; i < BPP; i++)
|
||||
{
|
||||
aTmp[i] = pSourceImage[x * BPP + (W * BPP * y) + i];
|
||||
}
|
||||
}
|
||||
|
||||
static void SampleBicubic(const uint8_t *pSourceImage, float u, float v, uint32_t W, uint32_t H, size_t BPP, uint8_t aSample[])
|
||||
{
|
||||
float X = (u * W) - 0.5f;
|
||||
int xInt = (int)X;
|
||||
float xFract = X - floorf(X);
|
||||
|
||||
float Y = (v * H) - 0.5f;
|
||||
int yInt = (int)Y;
|
||||
float yFract = Y - floorf(Y);
|
||||
|
||||
uint8_t PX00[4];
|
||||
uint8_t PX10[4];
|
||||
uint8_t PX20[4];
|
||||
uint8_t PX30[4];
|
||||
|
||||
uint8_t PX01[4];
|
||||
uint8_t PX11[4];
|
||||
uint8_t PX21[4];
|
||||
uint8_t PX31[4];
|
||||
|
||||
uint8_t PX02[4];
|
||||
uint8_t PX12[4];
|
||||
uint8_t PX22[4];
|
||||
uint8_t PX32[4];
|
||||
|
||||
uint8_t PX03[4];
|
||||
uint8_t PX13[4];
|
||||
uint8_t PX23[4];
|
||||
uint8_t PX33[4];
|
||||
|
||||
GetPixelClamped(pSourceImage, xInt - 1, yInt - 1, W, H, BPP, PX00);
|
||||
GetPixelClamped(pSourceImage, xInt + 0, yInt - 1, W, H, BPP, PX10);
|
||||
GetPixelClamped(pSourceImage, xInt + 1, yInt - 1, W, H, BPP, PX20);
|
||||
GetPixelClamped(pSourceImage, xInt + 2, yInt - 1, W, H, BPP, PX30);
|
||||
|
||||
GetPixelClamped(pSourceImage, xInt - 1, yInt + 0, W, H, BPP, PX01);
|
||||
GetPixelClamped(pSourceImage, xInt + 0, yInt + 0, W, H, BPP, PX11);
|
||||
GetPixelClamped(pSourceImage, xInt + 1, yInt + 0, W, H, BPP, PX21);
|
||||
GetPixelClamped(pSourceImage, xInt + 2, yInt + 0, W, H, BPP, PX31);
|
||||
|
||||
GetPixelClamped(pSourceImage, xInt - 1, yInt + 1, W, H, BPP, PX02);
|
||||
GetPixelClamped(pSourceImage, xInt + 0, yInt + 1, W, H, BPP, PX12);
|
||||
GetPixelClamped(pSourceImage, xInt + 1, yInt + 1, W, H, BPP, PX22);
|
||||
GetPixelClamped(pSourceImage, xInt + 2, yInt + 1, W, H, BPP, PX32);
|
||||
|
||||
GetPixelClamped(pSourceImage, xInt - 1, yInt + 2, W, H, BPP, PX03);
|
||||
GetPixelClamped(pSourceImage, xInt + 0, yInt + 2, W, H, BPP, PX13);
|
||||
GetPixelClamped(pSourceImage, xInt + 1, yInt + 2, W, H, BPP, PX23);
|
||||
GetPixelClamped(pSourceImage, xInt + 2, yInt + 2, W, H, BPP, PX33);
|
||||
|
||||
for(size_t i = 0; i < BPP; i++)
|
||||
{
|
||||
float Clmn0 = CubicHermite(PX00[i], PX10[i], PX20[i], PX30[i], xFract);
|
||||
float Clmn1 = CubicHermite(PX01[i], PX11[i], PX21[i], PX31[i], xFract);
|
||||
float Clmn2 = CubicHermite(PX02[i], PX12[i], PX22[i], PX32[i], xFract);
|
||||
float Clmn3 = CubicHermite(PX03[i], PX13[i], PX23[i], PX33[i], xFract);
|
||||
|
||||
float Valuef = CubicHermite(Clmn0, Clmn1, Clmn2, Clmn3, yFract);
|
||||
|
||||
Valuef = clamp<float>(Valuef, 0.0f, 255.0f);
|
||||
|
||||
aSample[i] = (uint8_t)Valuef;
|
||||
}
|
||||
}
|
||||
|
||||
static void ResizeImage(const uint8_t *pSourceImage, uint32_t SW, uint32_t SH, uint8_t *pDestinationImage, uint32_t W, uint32_t H, size_t BPP)
|
||||
{
|
||||
uint8_t aSample[4];
|
||||
int y, x;
|
||||
|
||||
for(y = 0; y < (int)H; ++y)
|
||||
{
|
||||
float v = (float)y / (float)(H - 1);
|
||||
|
||||
for(x = 0; x < (int)W; ++x)
|
||||
{
|
||||
float u = (float)x / (float)(W - 1);
|
||||
SampleBicubic(pSourceImage, u, v, SW, SH, BPP, aSample);
|
||||
|
||||
for(size_t i = 0; i < BPP; ++i)
|
||||
{
|
||||
pDestinationImage[x * BPP + ((W * BPP) * y) + i] = aSample[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t *ResizeImage(const uint8_t *pImageData, int Width, int Height, int NewWidth, int NewHeight, int BPP)
|
||||
{
|
||||
// All calls to Resize() ensure width & height are > 0, BPP is always > 0,
|
||||
// thus no allocation size 0 possible.
|
||||
uint8_t *pTmpData = (uint8_t *)malloc((size_t)NewWidth * NewHeight * BPP); // NOLINT(clang-analyzer-optin.portability.UnixAPI)
|
||||
|
||||
ResizeImage(pImageData, Width, Height, pTmpData, NewWidth, NewHeight, BPP);
|
||||
|
||||
return pTmpData;
|
||||
}
|
||||
|
||||
int HighestBit(int OfVar)
|
||||
{
|
||||
if(!OfVar)
|
||||
return 0;
|
||||
|
||||
int RetV = 1;
|
||||
|
||||
while(OfVar >>= 1)
|
||||
RetV <<= 1;
|
||||
|
||||
return RetV;
|
||||
}
|
14
src/engine/shared/image_manipulation.h
Normal file
14
src/engine/shared/image_manipulation.h
Normal file
|
@ -0,0 +1,14 @@
|
|||
#ifndef ENGINE_SHARED_IMAGE_MANIPULATION_H
|
||||
#define ENGINE_SHARED_IMAGE_MANIPULATION_H
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
void DilateImage(unsigned char *pImageBuff, int w, int h, int BPP);
|
||||
|
||||
// returned pointer is allocated with malloc
|
||||
uint8_t *ResizeImage(const uint8_t *pImgData, int Width, int Height, int NewWidth, int NewHeight, int BPP);
|
||||
|
||||
int HighestBit(int OfVar);
|
||||
|
||||
#endif
|
|
@ -94,7 +94,7 @@ int CSkins::LoadSkin(const char *pName, const char *pPath, int DirType)
|
|||
{
|
||||
char aBuf[512];
|
||||
CImageInfo Info;
|
||||
if(!Graphics()->LoadPNG(&Info, pPath, DirType))
|
||||
if(!Graphics()->LoadPNG(&Info, pPath, DirType) || !Graphics()->CheckImageDivisibility(pPath, Info, g_pData->m_aSprites[SPRITE_TEE_BODY].m_pSet->m_Gridx, g_pData->m_aSprites[SPRITE_TEE_BODY].m_pSet->m_Gridy, true))
|
||||
{
|
||||
str_format(aBuf, sizeof(aBuf), "failed to load skin from %s", pName);
|
||||
Console()->Print(IConsole::OUTPUT_LEVEL_ADDINFO, "game", aBuf);
|
||||
|
|
|
@ -2659,7 +2659,7 @@ void CGameClient::LoadGameSkin(const char *pPath, bool AsDir)
|
|||
else
|
||||
LoadGameSkin(pPath, true);
|
||||
}
|
||||
else if(PngLoaded)
|
||||
else if(PngLoaded && Graphics()->CheckImageDivisibility(aPath, ImgInfo, g_pData->m_aSprites[SPRITE_HEALTH_FULL].m_pSet->m_Gridx, g_pData->m_aSprites[SPRITE_HEALTH_FULL].m_pSet->m_Gridy, true))
|
||||
{
|
||||
m_GameSkin.m_SpriteHealthFull = Graphics()->LoadSpriteTexture(ImgInfo, &g_pData->m_aSprites[SPRITE_HEALTH_FULL]);
|
||||
m_GameSkin.m_SpriteHealthEmpty = Graphics()->LoadSpriteTexture(ImgInfo, &g_pData->m_aSprites[SPRITE_HEALTH_EMPTY]);
|
||||
|
@ -2812,7 +2812,7 @@ void CGameClient::LoadEmoticonsSkin(const char *pPath, bool AsDir)
|
|||
else
|
||||
LoadEmoticonsSkin(pPath, true);
|
||||
}
|
||||
else if(PngLoaded)
|
||||
else if(PngLoaded && Graphics()->CheckImageDivisibility(aPath, ImgInfo, g_pData->m_aSprites[SPRITE_OOP].m_pSet->m_Gridx, g_pData->m_aSprites[SPRITE_OOP].m_pSet->m_Gridy, true))
|
||||
{
|
||||
for(int i = 0; i < 16; ++i)
|
||||
m_EmoticonsSkin.m_SpriteEmoticons[i] = Graphics()->LoadSpriteTexture(ImgInfo, &g_pData->m_aSprites[SPRITE_OOP + i]);
|
||||
|
@ -2866,7 +2866,7 @@ void CGameClient::LoadParticlesSkin(const char *pPath, bool AsDir)
|
|||
else
|
||||
LoadParticlesSkin(pPath, true);
|
||||
}
|
||||
else if(PngLoaded)
|
||||
else if(PngLoaded && Graphics()->CheckImageDivisibility(aPath, ImgInfo, g_pData->m_aSprites[SPRITE_PART_SLICE].m_pSet->m_Gridx, g_pData->m_aSprites[SPRITE_PART_SLICE].m_pSet->m_Gridy, true))
|
||||
{
|
||||
m_ParticlesSkin.m_SpriteParticleSlice = Graphics()->LoadSpriteTexture(ImgInfo, &g_pData->m_aSprites[SPRITE_PART_SLICE]);
|
||||
m_ParticlesSkin.m_SpriteParticleBall = Graphics()->LoadSpriteTexture(ImgInfo, &g_pData->m_aSprites[SPRITE_PART_BALL]);
|
||||
|
|
|
@ -30,7 +30,7 @@
|
|||
#include <game/generated/client_data.h>
|
||||
#include <game/localization.h>
|
||||
|
||||
#include <engine/shared/dilate.h>
|
||||
#include <engine/shared/image_manipulation.h>
|
||||
|
||||
#include "auto_map.h"
|
||||
#include "editor.h"
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
/* If you are missing that file, acquire a complete release at teeworlds.com. */
|
||||
#include <base/math.h>
|
||||
#include <base/system.h>
|
||||
#include <engine/shared/dilate.h>
|
||||
#include <engine/shared/image_manipulation.h>
|
||||
#include <pnglite.h>
|
||||
|
||||
int DilateFile(const char *pFileName)
|
||||
|
|
Loading…
Reference in a new issue