Warn about pnglite-incompatible PNGs on load

This allows a larger range of PNGs to be loaded while still maintaining
backward compatibility with older clients by annoying the user.

This warning can be enabled by the `warn-pnglite-incompatible-images`
key in the https://info2.ddnet.tw/info JSON, if the key is not there or
the JSON hasn't been obtained yet, the warning is disabled. Since the
JSON is cached across restarts, it'll be effective for initially loaded
images from the second start.
This commit is contained in:
heinrich5991 2022-06-27 16:57:23 +02:00 committed by Dennis Felsing
parent 4292c9ed77
commit 994324b059
9 changed files with 77 additions and 31 deletions

View file

@ -2501,6 +2501,8 @@ void CClient::LoadDDNetInfo()
log_debug("info", "got global tcp ip address: %s", (const char *)ConnectingIp); log_debug("info", "got global tcp ip address: %s", (const char *)ConnectingIp);
} }
} }
const json_value &WarnPngliteIncompatibleImages = DDNetInfo["warn-pnglite-incompatible-images"];
Graphics()->WarnPngliteIncompatibleImages(WarnPngliteIncompatibleImages.type == json_boolean && (bool)WarnPngliteIncompatibleImages);
} }
int CClient::ConnectNetTypes() const int CClient::ConnectNetTypes() const
@ -3125,6 +3127,10 @@ void CClient::Run()
m_pEditor->Save(arg); m_pEditor->Save(arg);
return;*/ return;*/
m_ServerBrowser.OnInit();
// loads the existing ddnet info file if it exists
LoadDDNetInfo();
// load data // load data
if(!LoadData()) if(!LoadData())
return; return;
@ -3135,7 +3141,6 @@ void CClient::Run()
} }
GameClient()->OnInit(); GameClient()->OnInit();
m_ServerBrowser.OnInit();
m_pConsole->Print(IConsole::OUTPUT_LEVEL_STANDARD, "client", "version " GAME_RELEASE_VERSION " on " CONF_PLATFORM_STRING " " CONF_ARCH_STRING, ColorRGBA(0.7f, 0.7f, 1, 1.0f)); m_pConsole->Print(IConsole::OUTPUT_LEVEL_STANDARD, "client", "version " GAME_RELEASE_VERSION " on " CONF_PLATFORM_STRING " " CONF_ARCH_STRING, ColorRGBA(0.7f, 0.7f, 1, 1.0f));
if(GIT_SHORTREV_HASH) if(GIT_SHORTREV_HASH)
@ -3168,9 +3173,7 @@ void CClient::Run()
InitChecksum(); InitChecksum();
m_pConsole->InitChecksum(ChecksumData()); m_pConsole->InitChecksum(ChecksumData());
// loads the existing ddnet info file if it exists // request the new ddnet info from server if already past the welcome dialog
LoadDDNetInfo();
// but still request the new one from server
if(g_Config.m_ClShowWelcome) if(g_Config.m_ClShowWelcome)
g_Config.m_ClShowWelcome = 0; g_Config.m_ClShowWelcome = 0;
else else

View file

@ -640,7 +640,8 @@ int CGraphics_Threaded::LoadPNG(CImageInfo *pImg, const char *pFilename, int Sto
uint8_t *pImgBuffer = NULL; uint8_t *pImgBuffer = NULL;
EImageFormat ImageFormat; EImageFormat ImageFormat;
if(::LoadPNG(ImageByteBuffer, pFilename, pImg->m_Width, pImg->m_Height, pImgBuffer, ImageFormat)) int PngliteIncompatible;
if(::LoadPNG(ImageByteBuffer, pFilename, PngliteIncompatible, pImg->m_Width, pImg->m_Height, pImgBuffer, ImageFormat))
{ {
pImg->m_pData = pImgBuffer; pImg->m_pData = pImgBuffer;
@ -653,6 +654,30 @@ int CGraphics_Threaded::LoadPNG(CImageInfo *pImg, const char *pFilename, int Sto
free(pImgBuffer); free(pImgBuffer);
return 0; return 0;
} }
if(m_WarnPngliteIncompatibleImages && PngliteIncompatible != 0)
{
SWarning Warning;
str_format(Warning.m_aWarningMsg, sizeof(Warning.m_aWarningMsg), Localize("\"%s\" is not compatible with pnglite and cannot be loaded by old DDNet versions: "), pFilename);
static const int FLAGS[] = {PNGLITE_COLOR_TYPE, PNGLITE_BIT_DEPTH, PNGLITE_INTERLACE_TYPE, PNGLITE_COMPRESSION_TYPE, PNGLITE_FILTER_TYPE};
static const char *EXPLANATION[] = {"color type", "bit depth", "interlace type", "compression type", "filter type"};
bool First = true;
for(int i = 0; i < (int)std::size(FLAGS); i++)
{
if((PngliteIncompatible & FLAGS[i]) != 0)
{
if(!First)
{
str_append(Warning.m_aWarningMsg, ", ", sizeof(Warning.m_aWarningMsg));
}
str_append(Warning.m_aWarningMsg, EXPLANATION[i], sizeof(Warning.m_aWarningMsg));
First = false;
}
}
str_append(Warning.m_aWarningMsg, Localize(" unsupported"), sizeof(Warning.m_aWarningMsg));
m_vWarnings.emplace_back(Warning);
}
} }
else else
{ {
@ -2513,6 +2538,11 @@ void CGraphics_Threaded::Maximize()
m_pBackend->Maximize(); m_pBackend->Maximize();
} }
void CGraphics_Threaded::WarnPngliteIncompatibleImages(bool Warn)
{
m_WarnPngliteIncompatibleImages = Warn;
}
void CGraphics_Threaded::SetWindowParams(int FullscreenMode, bool IsBorderless, bool AllowResizing) void CGraphics_Threaded::SetWindowParams(int FullscreenMode, bool IsBorderless, bool AllowResizing)
{ {
m_pBackend->SetWindowParams(FullscreenMode, IsBorderless, AllowResizing); m_pBackend->SetWindowParams(FullscreenMode, IsBorderless, AllowResizing);

View file

@ -824,6 +824,8 @@ class CGraphics_Threaded : public IEngineGraphics
std::vector<uint8_t> m_vSpriteHelper; std::vector<uint8_t> m_vSpriteHelper;
bool m_WarnPngliteIncompatibleImages = false;
std::vector<SWarning> m_vWarnings; std::vector<SWarning> m_vWarnings;
// is a non full windowed (in a sense that the viewport won't include the whole window), // is a non full windowed (in a sense that the viewport won't include the whole window),
@ -1249,6 +1251,7 @@ public:
int GetNumScreens() const override; int GetNumScreens() const override;
void Minimize() override; void Minimize() override;
void Maximize() override; void Maximize() override;
void WarnPngliteIncompatibleImages(bool Warn) override;
void SetWindowParams(int FullscreenMode, bool IsBorderless, bool AllowResizing) override; void SetWindowParams(int FullscreenMode, bool IsBorderless, bool AllowResizing) override;
bool SetWindowScreen(int Index) override; bool SetWindowScreen(int Index) override;
void Move(int x, int y) override; void Move(int x, int y) override;

View file

@ -79,12 +79,12 @@ static void LibPNGDeleteReadStruct(png_structp pPNGStruct, png_infop pPNGInfo)
png_destroy_read_struct(&pPNGStruct, NULL, NULL); png_destroy_read_struct(&pPNGStruct, NULL, NULL);
} }
static bool IsUnsupportedByPnglite(png_structp pPNGStruct, png_infop pPNGInfo) static int PngliteIncompatibility(png_structp pPNGStruct, png_infop pPNGInfo)
{ {
int ColorType = png_get_color_type(pPNGStruct, pPNGInfo); int ColorType = png_get_color_type(pPNGStruct, pPNGInfo);
int BitDepth = png_get_bit_depth(pPNGStruct, pPNGInfo); int BitDepth = png_get_bit_depth(pPNGStruct, pPNGInfo);
int InterlaceType = png_get_interlace_type(pPNGStruct, pPNGInfo); int InterlaceType = png_get_interlace_type(pPNGStruct, pPNGInfo);
bool Unsupported = false; int Result = 0;
switch(ColorType) switch(ColorType)
{ {
case PNG_COLOR_TYPE_GRAY: case PNG_COLOR_TYPE_GRAY:
@ -93,8 +93,8 @@ static bool IsUnsupportedByPnglite(png_structp pPNGStruct, png_infop pPNGInfo)
case PNG_COLOR_TYPE_GRAY_ALPHA: case PNG_COLOR_TYPE_GRAY_ALPHA:
break; break;
default: default:
log_error("png", "color type %d unsupported by pnglite", ColorType); log_debug("png", "color type %d unsupported by pnglite", ColorType);
Unsupported = true; Result |= PNGLITE_COLOR_TYPE;
} }
switch(BitDepth) switch(BitDepth)
@ -103,33 +103,29 @@ static bool IsUnsupportedByPnglite(png_structp pPNGStruct, png_infop pPNGInfo)
case 16: case 16:
break; break;
default: default:
log_error("png", "bit depth %d unsupported by pnglite", BitDepth); log_debug("png", "bit depth %d unsupported by pnglite", BitDepth);
Unsupported = true; Result |= PNGLITE_BIT_DEPTH;
} }
switch(InterlaceType) if(InterlaceType != PNG_INTERLACE_NONE)
{ {
case PNG_INTERLACE_NONE: log_debug("png", "interlace type %d unsupported by pnglite", InterlaceType);
break; Result |= PNGLITE_INTERLACE_TYPE;
default:
log_error("png", "interlace type %d unsupported by pnglite", InterlaceType);
Unsupported = true;
} }
if(png_get_compression_type(pPNGStruct, pPNGInfo) != PNG_COMPRESSION_TYPE_BASE)
if(png_get_compression_type(pPNGStruct, pPNGInfo) != PNG_COMPRESSION_TYPE_BASE || png_get_filter_type(pPNGStruct, pPNGInfo) != PNG_FILTER_TYPE_BASE)
{ {
log_error("png", "non-default compression type or non-default filter type unsupported by pnglite"); log_debug("png", "non-default compression type unsupported by pnglite");
Unsupported = true; Result |= PNGLITE_COMPRESSION_TYPE;
} }
if(png_get_filter_type(pPNGStruct, pPNGInfo) != PNG_FILTER_TYPE_BASE)
if(Unsupported)
{ {
log_error("png", "refusing to load PNG because it would be unsupported by pnglite"); log_debug("png", "non-default filter type unsupported by pnglite");
Result |= PNGLITE_FILTER_TYPE;
} }
return Unsupported; return Result;
} }
bool LoadPNG(SImageByteBuffer &ByteLoader, const char *pFileName, int &Width, int &Height, uint8_t *&pImageBuff, EImageFormat &ImageFormat) bool LoadPNG(SImageByteBuffer &ByteLoader, const char *pFileName, int &PngliteIncompatible, int &Width, int &Height, uint8_t *&pImageBuff, EImageFormat &ImageFormat)
{ {
png_structp pPNGStruct = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); png_structp pPNGStruct = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
@ -175,8 +171,9 @@ bool LoadPNG(SImageByteBuffer &ByteLoader, const char *pFileName, int &Width, in
Height = png_get_image_height(pPNGStruct, pPNGInfo); Height = png_get_image_height(pPNGStruct, pPNGInfo);
int ColorType = png_get_color_type(pPNGStruct, pPNGInfo); int ColorType = png_get_color_type(pPNGStruct, pPNGInfo);
png_byte BitDepth = png_get_bit_depth(pPNGStruct, pPNGInfo); png_byte BitDepth = png_get_bit_depth(pPNGStruct, pPNGInfo);
PngliteIncompatible = PngliteIncompatibility(pPNGStruct, pPNGInfo);
bool PNGErr = IsUnsupportedByPnglite(pPNGStruct, pPNGInfo); bool PNGErr = false;
if(BitDepth == 16) if(BitDepth == 16)
{ {

View file

@ -22,7 +22,16 @@ struct SImageByteBuffer
int m_Err; int m_Err;
}; };
bool LoadPNG(SImageByteBuffer &ByteLoader, const char *pFileName, int &Width, int &Height, uint8_t *&pImageBuff, EImageFormat &ImageFormat); enum
{
PNGLITE_COLOR_TYPE = 1 << 0,
PNGLITE_BIT_DEPTH = 1 << 1,
PNGLITE_INTERLACE_TYPE = 1 << 2,
PNGLITE_COMPRESSION_TYPE = 1 << 3,
PNGLITE_FILTER_TYPE = 1 << 4,
};
bool LoadPNG(SImageByteBuffer &ByteLoader, const char *pFileName, int &PngliteIncompatible, int &Width, int &Height, uint8_t *&pImageBuff, EImageFormat &ImageFormat);
bool SavePNG(EImageFormat ImageFormat, const uint8_t *pRawBuffer, SImageByteBuffer &WrittenBytes, int Width, int Height); bool SavePNG(EImageFormat ImageFormat, const uint8_t *pRawBuffer, SImageByteBuffer &WrittenBytes, int Width, int Height);
#endif // ENGINE_GFX_IMAGE_LOADER_H #endif // ENGINE_GFX_IMAGE_LOADER_H

View file

@ -251,6 +251,7 @@ public:
int WindowWidth() const { return m_ScreenWidth / m_ScreenHiDPIScale; } int WindowWidth() const { return m_ScreenWidth / m_ScreenHiDPIScale; }
int WindowHeight() const { return m_ScreenHeight / m_ScreenHiDPIScale; } int WindowHeight() const { return m_ScreenHeight / m_ScreenHiDPIScale; }
virtual void WarnPngliteIncompatibleImages(bool Warn) = 0;
virtual void SetWindowParams(int FullscreenMode, bool IsBorderless, bool AllowResizing) = 0; virtual void SetWindowParams(int FullscreenMode, bool IsBorderless, bool AllowResizing) = 0;
virtual bool SetWindowScreen(int Index) = 0; virtual bool SetWindowScreen(int Index) = 0;
virtual bool SetVSync(bool State) = 0; virtual bool SetVSync(bool State) = 0;

View file

@ -26,7 +26,8 @@ int DilateFile(const char *pFilename)
uint8_t *pImgBuffer = NULL; uint8_t *pImgBuffer = NULL;
EImageFormat ImageFormat; EImageFormat ImageFormat;
if(LoadPNG(ImageByteBuffer, pFilename, Img.m_Width, Img.m_Height, pImgBuffer, ImageFormat)) int PngliteIncompatible;
if(LoadPNG(ImageByteBuffer, pFilename, PngliteIncompatible, Img.m_Width, Img.m_Height, pImgBuffer, ImageFormat))
{ {
if(ImageFormat != IMAGE_FORMAT_RGBA) if(ImageFormat != IMAGE_FORMAT_RGBA)
{ {

View file

@ -43,7 +43,8 @@ int LoadPNG(CImageInfo *pImg, const char *pFilename)
uint8_t *pImgBuffer = NULL; uint8_t *pImgBuffer = NULL;
EImageFormat ImageFormat; EImageFormat ImageFormat;
if(LoadPNG(ImageByteBuffer, pFilename, pImg->m_Width, pImg->m_Height, pImgBuffer, ImageFormat)) int PngliteIncompatible;
if(LoadPNG(ImageByteBuffer, pFilename, PngliteIncompatible, pImg->m_Width, pImg->m_Height, pImgBuffer, ImageFormat))
{ {
pImg->m_pData = pImgBuffer; pImg->m_pData = pImgBuffer;

View file

@ -41,7 +41,8 @@ int LoadPNG(CImageInfo *pImg, const char *pFilename)
uint8_t *pImgBuffer = NULL; uint8_t *pImgBuffer = NULL;
EImageFormat ImageFormat; EImageFormat ImageFormat;
if(LoadPNG(ImageByteBuffer, pFilename, pImg->m_Width, pImg->m_Height, pImgBuffer, ImageFormat)) int PngliteIncompatible;
if(LoadPNG(ImageByteBuffer, pFilename, PngliteIncompatible, pImg->m_Width, pImg->m_Height, pImgBuffer, ImageFormat))
{ {
if((ImageFormat == IMAGE_FORMAT_RGBA || ImageFormat == IMAGE_FORMAT_RGB) && pImg->m_Width <= (2 << 13) && pImg->m_Height <= (2 << 13)) if((ImageFormat == IMAGE_FORMAT_RGBA || ImageFormat == IMAGE_FORMAT_RGB) && pImg->m_Width <= (2 << 13) && pImg->m_Height <= (2 << 13))
{ {