From 687878d657430869b6ed13c916197c5ab0f233bb Mon Sep 17 00:00:00 2001 From: NSDeathman Date: Thu, 12 Mar 2026 23:22:14 +0300 Subject: [PATCH] 4JRender lib source code --- Minecraft.Client/Minecraft.Client.vcxproj | 9 +- .../Minecraft.Client.vcxproj.filters | 6 + Minecraft.Client/Minecraft.cpp | 2 +- Minecraft.Client/Minecraft.h | 2 +- .../Windows64/4JLibs/inc/Render/4J_Render.cpp | 503 + .../Windows64/4JLibs/inc/Render/4J_Render.h | 302 + .../4JLibs/inc/Render/CompiledShaders.h | 39 + .../Windows64/4JLibs/inc/Render/PS_ForceLOD.h | 316 + .../4JLibs/inc/Render/PS_ScreenClear.h | 138 + .../4JLibs/inc/Render/PS_ScreenSpace.h | 141 + .../Windows64/4JLibs/inc/Render/PS_Standard.h | 306 + .../4JLibs/inc/Render/PS_TextureProjection.h | 312 + .../Windows64/4JLibs/inc/Render/Profiler.h | 46 + .../4JLibs/inc/Render/Render.vcxproj | 378 + .../4JLibs/inc/Render/Render.vcxproj.filters | 211 + .../Windows64/4JLibs/inc/Render/Renderer.h | 494 + .../4JLibs/inc/Render/RendererCBuff.cpp | 844 + .../4JLibs/inc/Render/RendererCore.cpp | 1049 + .../4JLibs/inc/Render/RendererMatrix.cpp | 144 + .../4JLibs/inc/Render/RendererState.cpp | 688 + .../4JLibs/inc/Render/RendererTexture.cpp | 333 + .../4JLibs/inc/Render/RendererVertex.cpp | 185 + .../4JLibs/inc/Render/VS_Compressed.h | 514 + .../inc/Render/VS_PF3_TF2_CB4_NB4_XW1.h | 609 + .../Render/VS_PF3_TF2_CB4_NB4_XW1_LIGHTING.h | 777 + .../Render/VS_PF3_TF2_CB4_NB4_XW1_TEXGEN.h | 727 + .../4JLibs/inc/Render/VS_ScreenClear.h | 138 + .../4JLibs/inc/Render/VS_ScreenSpace.h | 208 + .../Windows64/4JLibs/inc/Render/libpng/png.c | 4298 ++++ .../Windows64/4JLibs/inc/Render/libpng/png.h | 3305 +++ .../4JLibs/inc/Render/libpng/pngconf.h | 616 + .../4JLibs/inc/Render/libpng/pngdebug.h | 157 + .../4JLibs/inc/Render/libpng/pngerror.c | 932 + .../4JLibs/inc/Render/libpng/pngget.c | 1177 ++ .../4JLibs/inc/Render/libpng/pnginfo.h | 260 + .../4JLibs/inc/Render/libpng/pnglibconf.h | 211 + .../4JLibs/inc/Render/libpng/pngmem.c | 277 + .../4JLibs/inc/Render/libpng/pngpread.c | 1291 ++ .../4JLibs/inc/Render/libpng/pngpriv.h | 1913 ++ .../4JLibs/inc/Render/libpng/pngread.c | 4001 ++++ .../4JLibs/inc/Render/libpng/pngrio.c | 118 + .../4JLibs/inc/Render/libpng/pngrtran.c | 5101 +++++ .../4JLibs/inc/Render/libpng/pngrutil.c | 4462 ++++ .../4JLibs/inc/Render/libpng/pngset.c | 1606 ++ .../4JLibs/inc/Render/libpng/pngstruct.h | 489 + .../4JLibs/inc/Render/libpng/pngtest.c | 1971 ++ .../4JLibs/inc/Render/libpng/pngtrans.c | 841 + .../4JLibs/inc/Render/libpng/pngwio.c | 164 + .../4JLibs/inc/Render/libpng/pngwrite.c | 2341 +++ .../4JLibs/inc/Render/libpng/pngwtran.c | 637 + .../4JLibs/inc/Render/libpng/pngwutil.c | 3023 +++ .../Render/microprofile/microprofile.config.h | 4 + .../inc/Render/microprofile/microprofile.cpp | 16191 +++++++++++++++ .../inc/Render/microprofile/microprofile.h | 2058 ++ .../Render/microprofile/microprofile_html.h | 17160 ++++++++++++++++ .../Render/microprofile/microprofile_icons.h | 333 + .../inc/Render/microprofile/stb/stb_sprintf.h | 1906 ++ .../4JLibs/inc/Render/shaders/build.bat | 21 + .../4JLibs/inc/Render/shaders/main_PS.hlsl | 84 + .../4JLibs/inc/Render/shaders/main_VS.hlsl | 196 + .../4JLibs/inc/Render/shaders/screen_PS.hlsl | 40 + .../4JLibs/inc/Render/shaders/screen_VS.hlsl | 48 + .../Windows64/4JLibs/inc/Render/stdafx.cpp | 25 + .../Windows64/4JLibs/inc/Render/stdafx.h | 39 + .../Windows64/4JLibs/inc/Render/zlib/zconf.h | 553 + .../Windows64/4JLibs/inc/Render/zlib/zlib.h | 2057 ++ .../Windows64/4JLibs/libs/4J_Render_PC.lib | Bin 5561880 -> 9223278 bytes Minecraft.World/Minecraft.World.vcxproj | 7 +- MinecraftConsoles.sln | 160 +- 69 files changed, 89487 insertions(+), 7 deletions(-) create mode 100644 Minecraft.Client/Windows64/4JLibs/inc/Render/4J_Render.cpp create mode 100644 Minecraft.Client/Windows64/4JLibs/inc/Render/4J_Render.h create mode 100644 Minecraft.Client/Windows64/4JLibs/inc/Render/CompiledShaders.h create mode 100644 Minecraft.Client/Windows64/4JLibs/inc/Render/PS_ForceLOD.h create mode 100644 Minecraft.Client/Windows64/4JLibs/inc/Render/PS_ScreenClear.h create mode 100644 Minecraft.Client/Windows64/4JLibs/inc/Render/PS_ScreenSpace.h create mode 100644 Minecraft.Client/Windows64/4JLibs/inc/Render/PS_Standard.h create mode 100644 Minecraft.Client/Windows64/4JLibs/inc/Render/PS_TextureProjection.h create mode 100644 Minecraft.Client/Windows64/4JLibs/inc/Render/Profiler.h create mode 100644 Minecraft.Client/Windows64/4JLibs/inc/Render/Render.vcxproj create mode 100644 Minecraft.Client/Windows64/4JLibs/inc/Render/Render.vcxproj.filters create mode 100644 Minecraft.Client/Windows64/4JLibs/inc/Render/Renderer.h create mode 100644 Minecraft.Client/Windows64/4JLibs/inc/Render/RendererCBuff.cpp create mode 100644 Minecraft.Client/Windows64/4JLibs/inc/Render/RendererCore.cpp create mode 100644 Minecraft.Client/Windows64/4JLibs/inc/Render/RendererMatrix.cpp create mode 100644 Minecraft.Client/Windows64/4JLibs/inc/Render/RendererState.cpp create mode 100644 Minecraft.Client/Windows64/4JLibs/inc/Render/RendererTexture.cpp create mode 100644 Minecraft.Client/Windows64/4JLibs/inc/Render/RendererVertex.cpp create mode 100644 Minecraft.Client/Windows64/4JLibs/inc/Render/VS_Compressed.h create mode 100644 Minecraft.Client/Windows64/4JLibs/inc/Render/VS_PF3_TF2_CB4_NB4_XW1.h create mode 100644 Minecraft.Client/Windows64/4JLibs/inc/Render/VS_PF3_TF2_CB4_NB4_XW1_LIGHTING.h create mode 100644 Minecraft.Client/Windows64/4JLibs/inc/Render/VS_PF3_TF2_CB4_NB4_XW1_TEXGEN.h create mode 100644 Minecraft.Client/Windows64/4JLibs/inc/Render/VS_ScreenClear.h create mode 100644 Minecraft.Client/Windows64/4JLibs/inc/Render/VS_ScreenSpace.h create mode 100644 Minecraft.Client/Windows64/4JLibs/inc/Render/libpng/png.c create mode 100644 Minecraft.Client/Windows64/4JLibs/inc/Render/libpng/png.h create mode 100644 Minecraft.Client/Windows64/4JLibs/inc/Render/libpng/pngconf.h create mode 100644 Minecraft.Client/Windows64/4JLibs/inc/Render/libpng/pngdebug.h create mode 100644 Minecraft.Client/Windows64/4JLibs/inc/Render/libpng/pngerror.c create mode 100644 Minecraft.Client/Windows64/4JLibs/inc/Render/libpng/pngget.c create mode 100644 Minecraft.Client/Windows64/4JLibs/inc/Render/libpng/pnginfo.h create mode 100644 Minecraft.Client/Windows64/4JLibs/inc/Render/libpng/pnglibconf.h create mode 100644 Minecraft.Client/Windows64/4JLibs/inc/Render/libpng/pngmem.c create mode 100644 Minecraft.Client/Windows64/4JLibs/inc/Render/libpng/pngpread.c create mode 100644 Minecraft.Client/Windows64/4JLibs/inc/Render/libpng/pngpriv.h create mode 100644 Minecraft.Client/Windows64/4JLibs/inc/Render/libpng/pngread.c create mode 100644 Minecraft.Client/Windows64/4JLibs/inc/Render/libpng/pngrio.c create mode 100644 Minecraft.Client/Windows64/4JLibs/inc/Render/libpng/pngrtran.c create mode 100644 Minecraft.Client/Windows64/4JLibs/inc/Render/libpng/pngrutil.c create mode 100644 Minecraft.Client/Windows64/4JLibs/inc/Render/libpng/pngset.c create mode 100644 Minecraft.Client/Windows64/4JLibs/inc/Render/libpng/pngstruct.h create mode 100644 Minecraft.Client/Windows64/4JLibs/inc/Render/libpng/pngtest.c create mode 100644 Minecraft.Client/Windows64/4JLibs/inc/Render/libpng/pngtrans.c create mode 100644 Minecraft.Client/Windows64/4JLibs/inc/Render/libpng/pngwio.c create mode 100644 Minecraft.Client/Windows64/4JLibs/inc/Render/libpng/pngwrite.c create mode 100644 Minecraft.Client/Windows64/4JLibs/inc/Render/libpng/pngwtran.c create mode 100644 Minecraft.Client/Windows64/4JLibs/inc/Render/libpng/pngwutil.c create mode 100644 Minecraft.Client/Windows64/4JLibs/inc/Render/microprofile/microprofile.config.h create mode 100644 Minecraft.Client/Windows64/4JLibs/inc/Render/microprofile/microprofile.cpp create mode 100644 Minecraft.Client/Windows64/4JLibs/inc/Render/microprofile/microprofile.h create mode 100644 Minecraft.Client/Windows64/4JLibs/inc/Render/microprofile/microprofile_html.h create mode 100644 Minecraft.Client/Windows64/4JLibs/inc/Render/microprofile/microprofile_icons.h create mode 100644 Minecraft.Client/Windows64/4JLibs/inc/Render/microprofile/stb/stb_sprintf.h create mode 100644 Minecraft.Client/Windows64/4JLibs/inc/Render/shaders/build.bat create mode 100644 Minecraft.Client/Windows64/4JLibs/inc/Render/shaders/main_PS.hlsl create mode 100644 Minecraft.Client/Windows64/4JLibs/inc/Render/shaders/main_VS.hlsl create mode 100644 Minecraft.Client/Windows64/4JLibs/inc/Render/shaders/screen_PS.hlsl create mode 100644 Minecraft.Client/Windows64/4JLibs/inc/Render/shaders/screen_VS.hlsl create mode 100644 Minecraft.Client/Windows64/4JLibs/inc/Render/stdafx.cpp create mode 100644 Minecraft.Client/Windows64/4JLibs/inc/Render/stdafx.h create mode 100644 Minecraft.Client/Windows64/4JLibs/inc/Render/zlib/zconf.h create mode 100644 Minecraft.Client/Windows64/4JLibs/inc/Render/zlib/zlib.h diff --git a/Minecraft.Client/Minecraft.Client.vcxproj b/Minecraft.Client/Minecraft.Client.vcxproj index d97cbc383..84fb26c83 100644 --- a/Minecraft.Client/Minecraft.Client.vcxproj +++ b/Minecraft.Client/Minecraft.Client.vcxproj @@ -235,6 +235,7 @@ SAK Xbox360Proj title + 10.0 @@ -309,7 +310,7 @@ Application MultiByte - v143 + v142 true @@ -784,6 +785,7 @@ false false false + false true @@ -1804,6 +1806,7 @@ xcopy /q /y /i /s /e $(ProjectDir)DurangoMedia\CU $(LayoutDir)Image\Loose\CUtrue /FS /Ob3 %(AdditionalOptions) stdcpp17 + true true @@ -1811,7 +1814,7 @@ xcopy /q /y /i /s /e $(ProjectDir)DurangoMedia\CU $(LayoutDir)Image\Loose\CUlegacy_stdio_definitions.lib;d3d11.lib;d3dcompiler.lib;..\Minecraft.World\x64_Release\Minecraft.World.lib;XInput9_1_0.lib;Windows64\Iggy\lib\iggy_w64.lib;%(AdditionalDependencies) NotSet false - UseLinkTimeCodeGeneration + UseFastLinkTimeCodeGeneration Run postbuild script @@ -5719,6 +5722,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CU + @@ -28454,6 +28458,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CU + true true diff --git a/Minecraft.Client/Minecraft.Client.vcxproj.filters b/Minecraft.Client/Minecraft.Client.vcxproj.filters index 23b754fa9..e2d9f972a 100644 --- a/Minecraft.Client/Minecraft.Client.vcxproj.filters +++ b/Minecraft.Client/Minecraft.Client.vcxproj.filters @@ -3793,6 +3793,9 @@ Header Files + + Common\Source Files\Audio + @@ -5949,6 +5952,9 @@ include\lce_filesystem + + Common\Source Files\Audio + diff --git a/Minecraft.Client/Minecraft.cpp b/Minecraft.Client/Minecraft.cpp index f46246b40..27987d797 100644 --- a/Minecraft.Client/Minecraft.cpp +++ b/Minecraft.Client/Minecraft.cpp @@ -150,7 +150,7 @@ Minecraft::Minecraft(Component *mouseComponent, Canvas *parent, MinecraftApplet progressRenderer = nullptr; gameRenderer = nullptr; bgLoader = nullptr; - + ticks = 0; // 4J-PB - moved into the local player //missTime = 0; diff --git a/Minecraft.Client/Minecraft.h b/Minecraft.Client/Minecraft.h index 2c5203d82..b00f02359 100644 --- a/Minecraft.Client/Minecraft.h +++ b/Minecraft.Client/Minecraft.h @@ -51,7 +51,7 @@ private: enum OS{ linux, solaris, windows, macos, unknown, xbox }; - + static ResourceLocation DEFAULT_FONT_LOCATION; static ResourceLocation ALT_FONT_LOCATION; diff --git a/Minecraft.Client/Windows64/4JLibs/inc/Render/4J_Render.cpp b/Minecraft.Client/Windows64/4JLibs/inc/Render/4J_Render.cpp new file mode 100644 index 000000000..8eaab0b9e --- /dev/null +++ b/Minecraft.Client/Windows64/4JLibs/inc/Render/4J_Render.cpp @@ -0,0 +1,503 @@ +/* +MIT License + +Copyright (c) 2026 Patoke + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +#include "stdafx.h" +#include "4J_Render.h" +#include "Renderer.h" + +C4JRender RenderManager; + +void C4JRender::Tick() +{ + InternalRenderManager.CBuffTick(); +} + +void C4JRender::UpdateGamma(unsigned short usGamma) +{ + InternalRenderManager.UpdateGamma(usGamma); +} + +void C4JRender::MatrixMode(int type) +{ + InternalRenderManager.MatrixMode(type); +} + +void C4JRender::MatrixSetIdentity() +{ + InternalRenderManager.MatrixSetIdentity(); +} + +void C4JRender::MatrixTranslate(float x, float y, float z) +{ + InternalRenderManager.MatrixTranslate(x, y, z); +} + +void C4JRender::MatrixRotate(float angle, float x, float y, float z) +{ + InternalRenderManager.MatrixRotate(angle, x, y, z); +} + +void C4JRender::MatrixScale(float x, float y, float z) +{ + InternalRenderManager.MatrixScale(x, y, z); +} + +void C4JRender::MatrixPerspective(float fovy, float aspect, float zNear, float zFar) +{ + InternalRenderManager.MatrixPerspective(fovy, aspect, zNear, zFar); +} + +void C4JRender::MatrixOrthogonal(float left, float right, float bottom, float top, float zNear, float zFar) +{ + InternalRenderManager.MatrixOrthogonal(left, right, bottom, top, zNear, zFar); +} + +void C4JRender::MatrixPop() +{ + InternalRenderManager.MatrixPop(); +} + +void C4JRender::MatrixPush() +{ + InternalRenderManager.MatrixPush(); +} + +void C4JRender::MatrixMult(float* mat) +{ + InternalRenderManager.MatrixMult(mat); +} + +const float* C4JRender::MatrixGet(int type) +{ + return InternalRenderManager.MatrixGet(type); +} + +void C4JRender::Set_matrixDirty() +{ + InternalRenderManager.Set_matrixDirty(); +} + +void C4JRender::Initialise(ID3D11Device* pDevice, IDXGISwapChain* pSwapChain) +{ + InternalRenderManager.Initialise(pDevice, pSwapChain); +} + +void C4JRender::InitialiseContext() +{ + InternalRenderManager.InitialiseContext(false); +} + +void C4JRender::StartFrame() +{ + InternalRenderManager.StartFrame(); +} + +void C4JRender::DoScreenGrabOnNextPresent() +{ + InternalRenderManager.DoScreenGrabOnNextPresent(); +} + +void C4JRender::Present() +{ + InternalRenderManager.Present(); +} + +void C4JRender::Clear(int flags, D3D11_RECT* pRect) +{ + InternalRenderManager.Clear(flags, pRect); +} + +void C4JRender::SetClearColour(const float colourRGBA[4]) +{ + InternalRenderManager.SetClearColour(colourRGBA); +} + +bool C4JRender::IsWidescreen() +{ + return InternalRenderManager.IsWidescreen(); +} + +bool C4JRender::IsHiDef() +{ + return InternalRenderManager.IsHiDef(); +} + +void C4JRender::CaptureThumbnail(ImageFileBuffer* pngOut) +{ + InternalRenderManager.CaptureThumbnail(pngOut); +} + +void C4JRender::CaptureScreen(ImageFileBuffer* jpgOut, XSOCIAL_PREVIEWIMAGE* previewOut) +{ + InternalRenderManager.CaptureScreen(jpgOut, previewOut); +} + +void C4JRender::BeginConditionalSurvey(int identifier) +{ + InternalRenderManager.BeginConditionalSurvey(identifier); +} + +void C4JRender::EndConditionalSurvey() +{ + InternalRenderManager.EndConditionalSurvey(); +} + +void C4JRender::BeginConditionalRendering(int identifier) +{ + InternalRenderManager.BeginConditionalRendering(identifier); +} + +void C4JRender::EndConditionalRendering() +{ + InternalRenderManager.EndConditionalRendering(); +} + +void C4JRender::DrawVertices(ePrimitiveType PrimitiveType, int count, void* dataIn, eVertexType vType, ePixelShaderType psType) +{ + InternalRenderManager.DrawVertices(PrimitiveType, count, dataIn, vType, psType); +} + +void C4JRender::DrawVertexBuffer(ePrimitiveType PrimitiveType, int count, ID3D11Buffer* buffer, eVertexType vType, ePixelShaderType psType) +{ + InternalRenderManager.DrawVertexBuffer(PrimitiveType, count, buffer, vType, psType); +} + +void C4JRender::CBuffLockStaticCreations() +{ + InternalRenderManager.CBuffLockStaticCreations(); +} + +int C4JRender::CBuffCreate(int count) +{ + return InternalRenderManager.CBuffCreate(count); +} + +void C4JRender::CBuffDelete(int first, int count) +{ + InternalRenderManager.CBuffDelete(first, count); +} + +void C4JRender::CBuffStart(int index, bool full) +{ + InternalRenderManager.CBuffStart(index, full); +} + +void C4JRender::CBuffClear(int index) +{ + InternalRenderManager.CBuffClear(index); +} + +int C4JRender::CBuffSize(int index) +{ + return InternalRenderManager.CBuffSize(index); +} + +void C4JRender::CBuffEnd() +{ + InternalRenderManager.CBuffEnd(); +} + +bool C4JRender::CBuffCall(int index, bool full) +{ + return InternalRenderManager.CBuffCall(index, full); +} + +void C4JRender::CBuffTick() +{ + InternalRenderManager.CBuffTick(); +} + +void C4JRender::CBuffDeferredModeStart() +{ + InternalRenderManager.CBuffDeferredModeStart(); +} + +void C4JRender::CBuffDeferredModeEnd() +{ + InternalRenderManager.CBuffDeferredModeEnd(); +} + +int C4JRender::TextureCreate() +{ + return InternalRenderManager.TextureCreate(); +} + +void C4JRender::TextureFree(int idx) +{ + InternalRenderManager.TextureFree(idx); +} + +void C4JRender::TextureBind(int idx) +{ + InternalRenderManager.TextureBind(idx); +} + +void C4JRender::TextureBindVertex(int idx) +{ + InternalRenderManager.TextureBindVertex(idx); +} + +void C4JRender::TextureSetTextureLevels(int levels) +{ + InternalRenderManager.TextureSetTextureLevels(levels); +} + +int C4JRender::TextureGetTextureLevels() +{ + return InternalRenderManager.TextureGetTextureLevels(); +} + +void C4JRender::TextureData(int width, int height, void* data, int level, eTextureFormat format) +{ + InternalRenderManager.TextureData(width, height, data, level, format); +} + +void C4JRender::TextureDataUpdate(int xoffset, int yoffset, int width, int height, void* data, int level) +{ + InternalRenderManager.TextureDataUpdate(xoffset, yoffset, width, height, data, level); +} + +void C4JRender::TextureSetParam(int param, int value) +{ + InternalRenderManager.TextureSetParam(param, value); +} + +void C4JRender::TextureDynamicUpdateStart() +{ + InternalRenderManager.TextureDynamicUpdateStart(); +} + +void C4JRender::TextureDynamicUpdateEnd() +{ + InternalRenderManager.TextureDynamicUpdateEnd(); +} + +HRESULT C4JRender::LoadTextureData(const char* szFilename, D3DXIMAGE_INFO* pSrcInfo, int** ppDataOut) +{ + return InternalRenderManager.LoadTextureData(szFilename, pSrcInfo, ppDataOut); +} + +HRESULT C4JRender::LoadTextureData(BYTE* pbData, DWORD dwBytes, D3DXIMAGE_INFO* pSrcInfo, int** ppDataOut) +{ + return InternalRenderManager.LoadTextureData(pbData, dwBytes, pSrcInfo, ppDataOut); +} + +HRESULT C4JRender::SaveTextureData(const char* szFilename, D3DXIMAGE_INFO* pSrcInfo, int* ppDataOut) +{ + return InternalRenderManager.SaveTextureData(szFilename, pSrcInfo, ppDataOut); +} + +HRESULT C4JRender::SaveTextureDataToMemory(void* pOutput, int outputCapacity, int* outputLength, int width, int height, int* ppDataIn) +{ + return InternalRenderManager.SaveTextureDataToMemory(pOutput, outputCapacity, outputLength, width, height, ppDataIn); +} + +void C4JRender::TextureGetStats() +{ +} + +ID3D11ShaderResourceView* C4JRender::TextureGetTexture(int idx) +{ + return InternalRenderManager.TextureGetTexture(idx); +} + +void C4JRender::StateSetColour(float r, float g, float b, float a) +{ + InternalRenderManager.StateSetColour(r, g, b, a); +} + +void C4JRender::StateSetDepthMask(bool enable) +{ + InternalRenderManager.StateSetDepthMask(enable); +} + +void C4JRender::StateSetBlendEnable(bool enable) +{ + InternalRenderManager.StateSetBlendEnable(enable); +} + +void C4JRender::StateSetBlendFunc(int src, int dst) +{ + InternalRenderManager.StateSetBlendFunc(src, dst); +} + +void C4JRender::StateSetBlendFactor(unsigned int colour) +{ + InternalRenderManager.StateSetBlendFactor(colour); +} + +void C4JRender::StateSetAlphaFunc(int func, float param) +{ + InternalRenderManager.StateSetAlphaFunc(func, param); +} + +void C4JRender::StateSetDepthFunc(int func) +{ + InternalRenderManager.StateSetDepthFunc(func); +} + +void C4JRender::StateSetFaceCull(bool enable) +{ + InternalRenderManager.StateSetFaceCull(enable); +} + +void C4JRender::StateSetFaceCullCW(bool enable) +{ + InternalRenderManager.StateSetFaceCullCW(enable); +} + +void C4JRender::StateSetLineWidth(float width) +{ + InternalRenderManager.StateSetLineWidth(width); +} + +void C4JRender::StateSetWriteEnable(bool red, bool green, bool blue, bool alpha) +{ + InternalRenderManager.StateSetWriteEnable(red, green, blue, alpha); +} + +void C4JRender::StateSetDepthTestEnable(bool enable) +{ + InternalRenderManager.StateSetDepthTestEnable(enable); +} + +void C4JRender::StateSetAlphaTestEnable(bool enable) +{ + InternalRenderManager.StateSetAlphaTestEnable(enable); +} + +void C4JRender::StateSetDepthSlopeAndBias(float slope, float bias) +{ + InternalRenderManager.StateSetDepthSlopeAndBias(slope, bias); +} + +void C4JRender::StateSetFogEnable(bool enable) +{ + InternalRenderManager.StateSetFogEnable(enable); +} + +void C4JRender::StateSetFogMode(int mode) +{ + InternalRenderManager.StateSetFogMode(mode); +} + +void C4JRender::StateSetFogNearDistance(float dist) +{ + InternalRenderManager.StateSetFogNearDistance(dist); +} + +void C4JRender::StateSetFogFarDistance(float dist) +{ + InternalRenderManager.StateSetFogFarDistance(dist); +} + +void C4JRender::StateSetFogDensity(float density) +{ + InternalRenderManager.StateSetFogDensity(density); +} + +void C4JRender::StateSetFogColour(float red, float green, float blue) +{ + InternalRenderManager.StateSetFogColour(red, green, blue); +} + +void C4JRender::StateSetLightingEnable(bool enable) +{ + InternalRenderManager.StateSetLightingEnable(enable); +} + +void C4JRender::StateSetVertexTextureUV(float u, float v) +{ + InternalRenderManager.StateSetVertexTextureUV(u, v); +} + +void C4JRender::StateSetLightColour(int light, float red, float green, float blue) +{ + InternalRenderManager.StateSetLightColour(light, red, green, blue); +} + +void C4JRender::StateSetLightAmbientColour(float red, float green, float blue) +{ + InternalRenderManager.StateSetLightAmbientColour(red, green, blue); +} + +void C4JRender::StateSetLightDirection(int light, float x, float y, float z) +{ + InternalRenderManager.StateSetLightDirection(light, x, y, z); +} + +void C4JRender::StateSetLightEnable(int light, bool enable) +{ + InternalRenderManager.StateSetLightEnable(light, enable); +} + +void C4JRender::StateSetViewport(eViewportType viewportType) +{ + InternalRenderManager.StateSetViewport(viewportType); +} + +void C4JRender::StateSetEnableViewportClipPlanes(bool enable) +{ + InternalRenderManager.StateSetEnableViewportClipPlanes(enable); +} + +void C4JRender::StateSetTexGenCol(int col, float x, float y, float z, float w, bool eyeSpace) +{ + InternalRenderManager.StateSetTexGenCol(col, x, y, z, w, eyeSpace); +} + +void C4JRender::StateSetStencil(int Function, uint8_t stencil_ref, uint8_t stencil_func_mask, uint8_t stencil_write_mask) +{ + InternalRenderManager.StateSetStencil((D3D11_COMPARISON_FUNC)Function, stencil_ref, stencil_func_mask, stencil_write_mask); +} + +void C4JRender::StateSetForceLOD(int LOD) +{ + InternalRenderManager.StateSetForceLOD(LOD); +} + +void C4JRender::BeginEvent(LPCWSTR eventName) +{ + InternalRenderManager.BeginEvent(eventName); +} + +void C4JRender::EndEvent() +{ + InternalRenderManager.EndEvent(); +} + +void C4JRender::Suspend() +{ + InternalRenderManager.Suspend(); +} + +bool C4JRender::Suspended() +{ + return InternalRenderManager.Suspended(); +} + +void C4JRender::Resume() +{ + InternalRenderManager.Resume(); +} diff --git a/Minecraft.Client/Windows64/4JLibs/inc/Render/4J_Render.h b/Minecraft.Client/Windows64/4JLibs/inc/Render/4J_Render.h new file mode 100644 index 000000000..fb16ccb32 --- /dev/null +++ b/Minecraft.Client/Windows64/4JLibs/inc/Render/4J_Render.h @@ -0,0 +1,302 @@ +#pragma once + +class ImageFileBuffer +{ +public: + enum EImageType + { + e_typePNG, + e_typeJPG + }; + + EImageType m_type; + void* m_pBuffer; + int m_bufferSize; + + int GetType() { return m_type; } + void *GetBufferPointer() { return m_pBuffer; } + int GetBufferSize() { return m_bufferSize; } + void Release() { free(m_pBuffer); m_pBuffer = nullptr; } + bool Allocated() { return m_pBuffer != nullptr; } +}; + +typedef struct +{ + int Width; + int Height; +}D3DXIMAGE_INFO; + +typedef struct _XSOCIAL_PREVIEWIMAGE { + BYTE *pBytes; + DWORD Pitch; + DWORD Width; + DWORD Height; +// D3DFORMAT Format; +} XSOCIAL_PREVIEWIMAGE, *PXSOCIAL_PREVIEWIMAGE; + +class C4JRender +{ +public: + void Tick(); + void UpdateGamma(unsigned short usGamma); + + // Matrix stack + void MatrixMode(int type); + void MatrixSetIdentity(); + void MatrixTranslate(float x,float y,float z); + void MatrixRotate(float angle, float x, float y, float z); + void MatrixScale(float x, float y, float z); + void MatrixPerspective(float fovy, float aspect, float zNear, float zFar); + void MatrixOrthogonal(float left,float right,float bottom,float top,float zNear,float zFar); + void MatrixPop(); + void MatrixPush(); + void MatrixMult(float *mat); + const float *MatrixGet(int type); + void Set_matrixDirty(); + + // Core + void Initialise(ID3D11Device *pDevice, IDXGISwapChain *pSwapChain); + void InitialiseContext(); + void StartFrame(); + void DoScreenGrabOnNextPresent(); + void Present(); + void Clear(int flags, D3D11_RECT *pRect = nullptr); + void SetClearColour(const float colourRGBA[4]); + bool IsWidescreen(); + bool IsHiDef(); + void CaptureThumbnail(ImageFileBuffer *pngOut); + void CaptureScreen(ImageFileBuffer *jpgOut, XSOCIAL_PREVIEWIMAGE *previewOut); + void BeginConditionalSurvey(int identifier); + void EndConditionalSurvey(); + void BeginConditionalRendering(int identifier); + void EndConditionalRendering(); + + // Vertex data handling + typedef enum + { + VERTEX_TYPE_PF3_TF2_CB4_NB4_XW1, // Position 3 x float, texture 2 x float, colour 4 x byte, normal 4 x byte, padding 1 DWORD + VERTEX_TYPE_COMPRESSED, // Compressed format - see comment at top of VS_PS3_TS2_CS1.hlsl for description of layout + VERTEX_TYPE_PF3_TF2_CB4_NB4_XW1_LIT, // as VERTEX_TYPE_PF3_TF2_CB4_NB4_XW1 with lighting applied, + VERTEX_TYPE_PF3_TF2_CB4_NB4_XW1_TEXGEN, // as VERTEX_TYPE_PF3_TF2_CB4_NB4_XW1 with tex gen + VERTEX_TYPE_COUNT + } eVertexType; + + // Pixel shader + typedef enum + { + PIXEL_SHADER_TYPE_STANDARD, + PIXEL_SHADER_TYPE_PROJECTION, + PIXEL_SHADER_TYPE_FORCELOD, + PIXEL_SHADER_COUNT + } ePixelShaderType; + + typedef enum + { + VIEWPORT_TYPE_FULLSCREEN, + VIEWPORT_TYPE_SPLIT_TOP, + VIEWPORT_TYPE_SPLIT_BOTTOM, + VIEWPORT_TYPE_SPLIT_LEFT, + VIEWPORT_TYPE_SPLIT_RIGHT, + VIEWPORT_TYPE_QUADRANT_TOP_LEFT, + VIEWPORT_TYPE_QUADRANT_TOP_RIGHT, + VIEWPORT_TYPE_QUADRANT_BOTTOM_LEFT, + VIEWPORT_TYPE_QUADRANT_BOTTOM_RIGHT, + } eViewportType; + + typedef enum + { + PRIMITIVE_TYPE_TRIANGLE_LIST, + PRIMITIVE_TYPE_TRIANGLE_STRIP, + PRIMITIVE_TYPE_TRIANGLE_FAN, + PRIMITIVE_TYPE_QUAD_LIST, + PRIMITIVE_TYPE_LINE_LIST, + PRIMITIVE_TYPE_LINE_STRIP, + PRIMITIVE_TYPE_COUNT + } ePrimitiveType; + + void DrawVertices(ePrimitiveType PrimitiveType, int count, void *dataIn, eVertexType vType, C4JRender::ePixelShaderType psType); + void DrawVertexBuffer(ePrimitiveType PrimitiveType, int count, ID3D11Buffer *buffer, C4JRender::eVertexType vType, C4JRender::ePixelShaderType psType); + + // Command buffers + void CBuffLockStaticCreations(); + int CBuffCreate(int count); + void CBuffDelete(int first, int count); + void CBuffStart(int index, bool full = false); + void CBuffClear(int index); + int CBuffSize(int index); + void CBuffEnd(); + bool CBuffCall(int index, bool full = true); + void CBuffTick(); + void CBuffDeferredModeStart(); + void CBuffDeferredModeEnd(); + + typedef enum + { + TEXTURE_FORMAT_RxGyBzAw, // Normal 32-bit RGBA texture, 8 bits per component + /* Don't think these are all directly available on D3D 11 - leaving for now + TEXTURE_FORMAT_R0G0B0Ax, // One 8-bit component mapped to alpha channel, R=G=B=0 + TEXTURE_FORMAT_R1G1B1Ax, // One 8-bit component mapped to alpha channel, R=G=B=1 + TEXTURE_FORMAT_RxGxBxAx, // One 8-bit component mapped to all channels + */ + MAX_TEXTURE_FORMATS + } eTextureFormat; + + // Textures + int TextureCreate(); + void TextureFree(int idx); + void TextureBind(int idx); + void TextureBindVertex(int idx); + void TextureSetTextureLevels(int levels); + int TextureGetTextureLevels(); + void TextureData(int width, int height, void *data, int level, eTextureFormat format = TEXTURE_FORMAT_RxGyBzAw); + void TextureDataUpdate(int xoffset, int yoffset, int width, int height, void *data, int level); + void TextureSetParam(int param, int value); + void TextureDynamicUpdateStart(); + void TextureDynamicUpdateEnd(); + HRESULT LoadTextureData(const char *szFilename,D3DXIMAGE_INFO *pSrcInfo, int **ppDataOut); + HRESULT LoadTextureData(BYTE *pbData, DWORD dwBytes,D3DXIMAGE_INFO *pSrcInfo, int **ppDataOut); + HRESULT SaveTextureData(const char *szFilename, D3DXIMAGE_INFO *pSrcInfo, int *ppDataOut); + HRESULT SaveTextureDataToMemory(void *pOutput, int outputCapacity, int *outputLength, int width, int height, int *ppDataIn); + void TextureGetStats(); + ID3D11ShaderResourceView *TextureGetTexture(int idx); + + // State control + void StateSetColour(float r, float g, float b, float a); + void StateSetDepthMask(bool enable); + void StateSetBlendEnable(bool enable); + void StateSetBlendFunc(int src, int dst); + void StateSetBlendFactor(unsigned int colour); + void StateSetAlphaFunc(int func, float param); + void StateSetDepthFunc(int func); + void StateSetFaceCull(bool enable); + void StateSetFaceCullCW(bool enable); + void StateSetLineWidth(float width); + void StateSetWriteEnable(bool red, bool green, bool blue, bool alpha); + void StateSetDepthTestEnable(bool enable); + void StateSetAlphaTestEnable(bool enable); + void StateSetDepthSlopeAndBias(float slope, float bias); + void StateSetFogEnable(bool enable); + void StateSetFogMode(int mode); + void StateSetFogNearDistance(float dist); + void StateSetFogFarDistance(float dist); + void StateSetFogDensity(float density); + void StateSetFogColour(float red, float green, float blue); + void StateSetLightingEnable(bool enable); + void StateSetVertexTextureUV( float u, float v); + void StateSetLightColour(int light, float red, float green, float blue); + void StateSetLightAmbientColour(float red, float green, float blue); + void StateSetLightDirection(int light, float x, float y, float z); + void StateSetLightEnable(int light, bool enable); + void StateSetViewport(eViewportType viewportType); + void StateSetEnableViewportClipPlanes(bool enable); + void StateSetTexGenCol(int col, float x, float y, float z, float w, bool eyeSpace); + void StateSetStencil(int Function, uint8_t stencil_ref, uint8_t stencil_func_mask, uint8_t stencil_write_mask); + void StateSetForceLOD(int LOD); + + // Event tracking + void BeginEvent(LPCWSTR eventName); + void EndEvent(); + + // PLM event handling + void Suspend(); + bool Suspended(); + void Resume(); +}; + + +const int GL_MODELVIEW_MATRIX = 0; +const int GL_PROJECTION_MATRIX = 1; +const int GL_MODELVIEW = 0; +const int GL_PROJECTION = 1; +const int GL_TEXTURE = 2; + +// These things required for tex gen + +const int GL_S = 0; +const int GL_T = 1; +const int GL_R = 2; +const int GL_Q = 3; + +const int GL_TEXTURE_GEN_S = 0; +const int GL_TEXTURE_GEN_T = 1; +const int GL_TEXTURE_GEN_Q = 2; +const int GL_TEXTURE_GEN_R = 3; + +const int GL_TEXTURE_GEN_MODE = 0; +const int GL_OBJECT_LINEAR = 0; +const int GL_EYE_LINEAR = 1; +const int GL_OBJECT_PLANE = 0; +const int GL_EYE_PLANE = 1; + + +// These things are used by glEnable/glDisable so must be different and non-zero (zero is used by things we haven't assigned yet) +const int GL_TEXTURE_2D = 1; +const int GL_BLEND = 2; +const int GL_CULL_FACE = 3; +const int GL_ALPHA_TEST = 4; +const int GL_DEPTH_TEST = 5; +const int GL_FOG = 6; +const int GL_LIGHTING = 7; +const int GL_LIGHT0 = 8; +const int GL_LIGHT1 = 9; + +const int CLEAR_DEPTH_FLAG = 1; +const int CLEAR_COLOUR_FLAG = 2; + +const int GL_DEPTH_BUFFER_BIT = CLEAR_DEPTH_FLAG; +const int GL_COLOR_BUFFER_BIT = CLEAR_COLOUR_FLAG; + +const int GL_SRC_ALPHA = D3D11_BLEND_SRC_ALPHA; +const int GL_ONE_MINUS_SRC_ALPHA = D3D11_BLEND_INV_SRC_ALPHA; +const int GL_ONE = D3D11_BLEND_ONE; +const int GL_ZERO = D3D11_BLEND_ZERO; +const int GL_DST_ALPHA = D3D11_BLEND_DEST_ALPHA; +const int GL_SRC_COLOR = D3D11_BLEND_SRC_COLOR; +const int GL_DST_COLOR = D3D11_BLEND_DEST_COLOR; +const int GL_ONE_MINUS_DST_COLOR = D3D11_BLEND_INV_DEST_COLOR; +const int GL_ONE_MINUS_SRC_COLOR = D3D11_BLEND_INV_SRC_COLOR; +const int GL_CONSTANT_ALPHA = D3D11_BLEND_BLEND_FACTOR; +const int GL_ONE_MINUS_CONSTANT_ALPHA = D3D11_BLEND_INV_BLEND_FACTOR; + +const int GL_GREATER = D3D11_COMPARISON_GREATER; +const int GL_EQUAL = D3D11_COMPARISON_EQUAL; +const int GL_LEQUAL = D3D11_COMPARISON_LESS_EQUAL; +const int GL_GEQUAL = D3D11_COMPARISON_GREATER_EQUAL; +const int GL_ALWAYS = D3D11_COMPARISON_ALWAYS; + +const int GL_TEXTURE_MIN_FILTER = 1; +const int GL_TEXTURE_MAG_FILTER = 2; +const int GL_TEXTURE_WRAP_S = 3; +const int GL_TEXTURE_WRAP_T = 4; + +const int GL_NEAREST = 0; +const int GL_LINEAR = 1; +const int GL_EXP = 2; +const int GL_NEAREST_MIPMAP_LINEAR = 0; // TODO - mipmapping bit of this + +const int GL_CLAMP = 0; +const int GL_REPEAT = 1; + +const int GL_FOG_START = 1; +const int GL_FOG_END = 2; +const int GL_FOG_MODE = 3; +const int GL_FOG_DENSITY = 4; +const int GL_FOG_COLOR = 5; + +const int GL_POSITION = 1; +const int GL_AMBIENT = 2; +const int GL_DIFFUSE = 3; +const int GL_SPECULAR = 4; + +const int GL_LIGHT_MODEL_AMBIENT = 1; + +const int GL_LINES = C4JRender::PRIMITIVE_TYPE_LINE_LIST; +const int GL_LINE_STRIP = C4JRender::PRIMITIVE_TYPE_LINE_STRIP; +const int GL_QUADS = C4JRender::PRIMITIVE_TYPE_QUAD_LIST; +const int GL_TRIANGLE_FAN = C4JRender::PRIMITIVE_TYPE_TRIANGLE_FAN; +const int GL_TRIANGLE_STRIP = C4JRender::PRIMITIVE_TYPE_TRIANGLE_STRIP; + +// Singleton +extern C4JRender RenderManager; + + diff --git a/Minecraft.Client/Windows64/4JLibs/inc/Render/CompiledShaders.h b/Minecraft.Client/Windows64/4JLibs/inc/Render/CompiledShaders.h new file mode 100644 index 000000000..bd16be7ea --- /dev/null +++ b/Minecraft.Client/Windows64/4JLibs/inc/Render/CompiledShaders.h @@ -0,0 +1,39 @@ +#pragma once +/* +MIT License + +Copyright (c) 2026 Patoke + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +// Vertex shaders +#include "VS_Compressed.h" +#include "VS_PF3_TF2_CB4_NB4_XW1.h" +#include "VS_PF3_TF2_CB4_NB4_XW1_LIGHTING.h" +#include "VS_PF3_TF2_CB4_NB4_XW1_TEXGEN.h" +#include "VS_ScreenClear.h" +#include "VS_ScreenSpace.h" + +// Pixel shaders +#include "PS_Standard.h" +#include "PS_TextureProjection.h" +#include "PS_ForceLOD.h" +#include "PS_ScreenSpace.h" +#include "PS_ScreenClear.h" \ No newline at end of file diff --git a/Minecraft.Client/Windows64/4JLibs/inc/Render/PS_ForceLOD.h b/Minecraft.Client/Windows64/4JLibs/inc/Render/PS_ForceLOD.h new file mode 100644 index 000000000..a5312691d --- /dev/null +++ b/Minecraft.Client/Windows64/4JLibs/inc/Render/PS_ForceLOD.h @@ -0,0 +1,316 @@ +#if 0 +// +// Generated by Microsoft (R) HLSL Shader Compiler 10.1 +// +// +// Buffer Definitions: +// +// cbuffer diffuse +// { +// +// float4 diffuse_colour; // Offset: 0 Size: 16 +// +// } +// +// cbuffer fog +// { +// +// float4 fog_colour; // Offset: 0 Size: 16 +// +// } +// +// cbuffer alphaTest +// { +// +// float4 alphaTestRef; // Offset: 0 Size: 16 +// +// } +// +// cbuffer forcedLOD +// { +// +// float4 forcedLod; // Offset: 0 Size: 16 +// +// } +// +// +// Resource Bindings: +// +// Name Type Format Dim HLSL Bind Count +// ------------------------------ ---------- ------- ----------- -------------- ------ +// diffuse_sampler_s sampler NA NA s0 1 +// diffuse_texture texture float4 2d t0 1 +// diffuse cbuffer NA NA cb0 1 +// fog cbuffer NA NA cb1 1 +// alphaTest cbuffer NA NA cb3 1 +// forcedLOD cbuffer NA NA cb5 1 +// +// +// +// Input signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------- ------ +// SV_POSITION 0 xyzw 0 POS float +// COLOR 0 xyzw 1 NONE float xyzw +// TEXCOORD 0 xyzw 2 NONE float xyz +// +// +// Output signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------- ------ +// SV_TARGET 0 xyzw 0 TARGET float xyzw +// +ps_4_0 +dcl_constantbuffer CB0[1], immediateIndexed +dcl_constantbuffer CB1[1], immediateIndexed +dcl_constantbuffer CB3[1], immediateIndexed +dcl_constantbuffer CB5[1], immediateIndexed +dcl_sampler s0, mode_default +dcl_resource_texture2d (float,float,float,float) t0 +dcl_input_ps linear v1.xyzw +dcl_input_ps linear centroid v2.xyz +dcl_output o0.xyzw +dcl_temps 2 +sample_l r0.xyzw, v2.xyxx, t0.xyzw, s0, cb5[0].x +mul r0.xyzw, r0.xyzw, cb0[0].xyzw +mul r0.w, r0.w, v1.w +lt r1.x, r0.w, cb3[0].w +discard_nz r1.x +mad r0.xyz, r0.xyzx, v1.xyzx, -cb1[0].xyzx +mad o0.xyz, v2.zzzz, r0.xyzx, cb1[0].xyzx +mov o0.w, r0.w +ret +// Approximately 9 instruction slots used +#endif + +const BYTE g_main_PS_ForceLOD[] = +{ + 68, 88, 66, 67, 157, 130, + 164, 12, 133, 77, 125, 1, + 170, 54, 126, 189, 157, 249, + 222, 87, 1, 0, 0, 0, + 76, 5, 0, 0, 5, 0, + 0, 0, 52, 0, 0, 0, + 140, 2, 0, 0, 0, 3, + 0, 0, 52, 3, 0, 0, + 208, 4, 0, 0, 82, 68, + 69, 70, 80, 2, 0, 0, + 4, 0, 0, 0, 32, 1, + 0, 0, 6, 0, 0, 0, + 28, 0, 0, 0, 0, 4, + 255, 255, 0, 129, 0, 0, + 38, 2, 0, 0, 220, 0, + 0, 0, 3, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 1, 0, + 0, 0, 1, 0, 0, 0, + 238, 0, 0, 0, 2, 0, + 0, 0, 5, 0, 0, 0, + 4, 0, 0, 0, 255, 255, + 255, 255, 0, 0, 0, 0, + 1, 0, 0, 0, 13, 0, + 0, 0, 254, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 1, 0, 0, 0, + 1, 0, 0, 0, 6, 1, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 1, 0, 0, 0, 1, 0, + 0, 0, 1, 0, 0, 0, + 10, 1, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 3, 0, 0, 0, + 1, 0, 0, 0, 1, 0, + 0, 0, 20, 1, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 5, 0, + 0, 0, 1, 0, 0, 0, + 1, 0, 0, 0, 100, 105, + 102, 102, 117, 115, 101, 95, + 115, 97, 109, 112, 108, 101, + 114, 95, 115, 0, 100, 105, + 102, 102, 117, 115, 101, 95, + 116, 101, 120, 116, 117, 114, + 101, 0, 100, 105, 102, 102, + 117, 115, 101, 0, 102, 111, + 103, 0, 97, 108, 112, 104, + 97, 84, 101, 115, 116, 0, + 102, 111, 114, 99, 101, 100, + 76, 79, 68, 0, 171, 171, + 254, 0, 0, 0, 1, 0, + 0, 0, 128, 1, 0, 0, + 16, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 6, 1, 0, 0, 1, 0, + 0, 0, 184, 1, 0, 0, + 16, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 10, 1, 0, 0, 1, 0, + 0, 0, 220, 1, 0, 0, + 16, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 20, 1, 0, 0, 1, 0, + 0, 0, 4, 2, 0, 0, + 16, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 152, 1, 0, 0, 0, 0, + 0, 0, 16, 0, 0, 0, + 2, 0, 0, 0, 168, 1, + 0, 0, 0, 0, 0, 0, + 100, 105, 102, 102, 117, 115, + 101, 95, 99, 111, 108, 111, + 117, 114, 0, 171, 1, 0, + 3, 0, 1, 0, 4, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 208, 1, 0, 0, + 0, 0, 0, 0, 16, 0, + 0, 0, 2, 0, 0, 0, + 168, 1, 0, 0, 0, 0, + 0, 0, 102, 111, 103, 95, + 99, 111, 108, 111, 117, 114, + 0, 171, 244, 1, 0, 0, + 0, 0, 0, 0, 16, 0, + 0, 0, 2, 0, 0, 0, + 168, 1, 0, 0, 0, 0, + 0, 0, 97, 108, 112, 104, + 97, 84, 101, 115, 116, 82, + 101, 102, 0, 171, 171, 171, + 28, 2, 0, 0, 0, 0, + 0, 0, 16, 0, 0, 0, + 2, 0, 0, 0, 168, 1, + 0, 0, 0, 0, 0, 0, + 102, 111, 114, 99, 101, 100, + 76, 111, 100, 0, 77, 105, + 99, 114, 111, 115, 111, 102, + 116, 32, 40, 82, 41, 32, + 72, 76, 83, 76, 32, 83, + 104, 97, 100, 101, 114, 32, + 67, 111, 109, 112, 105, 108, + 101, 114, 32, 49, 48, 46, + 49, 0, 171, 171, 73, 83, + 71, 78, 108, 0, 0, 0, + 3, 0, 0, 0, 8, 0, + 0, 0, 80, 0, 0, 0, + 0, 0, 0, 0, 1, 0, + 0, 0, 3, 0, 0, 0, + 0, 0, 0, 0, 15, 0, + 0, 0, 92, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 3, 0, 0, 0, + 1, 0, 0, 0, 15, 15, + 0, 0, 98, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 3, 0, 0, 0, + 2, 0, 0, 0, 15, 7, + 0, 0, 83, 86, 95, 80, + 79, 83, 73, 84, 73, 79, + 78, 0, 67, 79, 76, 79, + 82, 0, 84, 69, 88, 67, + 79, 79, 82, 68, 0, 171, + 79, 83, 71, 78, 44, 0, + 0, 0, 1, 0, 0, 0, + 8, 0, 0, 0, 32, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 3, 0, + 0, 0, 0, 0, 0, 0, + 15, 0, 0, 0, 83, 86, + 95, 84, 65, 82, 71, 69, + 84, 0, 171, 171, 83, 72, + 68, 82, 148, 1, 0, 0, + 64, 0, 0, 0, 101, 0, + 0, 0, 89, 0, 0, 4, + 70, 142, 32, 0, 0, 0, + 0, 0, 1, 0, 0, 0, + 89, 0, 0, 4, 70, 142, + 32, 0, 1, 0, 0, 0, + 1, 0, 0, 0, 89, 0, + 0, 4, 70, 142, 32, 0, + 3, 0, 0, 0, 1, 0, + 0, 0, 89, 0, 0, 4, + 70, 142, 32, 0, 5, 0, + 0, 0, 1, 0, 0, 0, + 90, 0, 0, 3, 0, 96, + 16, 0, 0, 0, 0, 0, + 88, 24, 0, 4, 0, 112, + 16, 0, 0, 0, 0, 0, + 85, 85, 0, 0, 98, 16, + 0, 3, 242, 16, 16, 0, + 1, 0, 0, 0, 98, 24, + 0, 3, 114, 16, 16, 0, + 2, 0, 0, 0, 101, 0, + 0, 3, 242, 32, 16, 0, + 0, 0, 0, 0, 104, 0, + 0, 2, 2, 0, 0, 0, + 72, 0, 0, 12, 242, 0, + 16, 0, 0, 0, 0, 0, + 70, 16, 16, 0, 2, 0, + 0, 0, 70, 126, 16, 0, + 0, 0, 0, 0, 0, 96, + 16, 0, 0, 0, 0, 0, + 10, 128, 32, 0, 5, 0, + 0, 0, 0, 0, 0, 0, + 56, 0, 0, 8, 242, 0, + 16, 0, 0, 0, 0, 0, + 70, 14, 16, 0, 0, 0, + 0, 0, 70, 142, 32, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 56, 0, 0, 7, + 130, 0, 16, 0, 0, 0, + 0, 0, 58, 0, 16, 0, + 0, 0, 0, 0, 58, 16, + 16, 0, 1, 0, 0, 0, + 49, 0, 0, 8, 18, 0, + 16, 0, 1, 0, 0, 0, + 58, 0, 16, 0, 0, 0, + 0, 0, 58, 128, 32, 0, + 3, 0, 0, 0, 0, 0, + 0, 0, 13, 0, 4, 3, + 10, 0, 16, 0, 1, 0, + 0, 0, 50, 0, 0, 11, + 114, 0, 16, 0, 0, 0, + 0, 0, 70, 2, 16, 0, + 0, 0, 0, 0, 70, 18, + 16, 0, 1, 0, 0, 0, + 70, 130, 32, 128, 65, 0, + 0, 0, 1, 0, 0, 0, + 0, 0, 0, 0, 50, 0, + 0, 10, 114, 32, 16, 0, + 0, 0, 0, 0, 166, 26, + 16, 0, 2, 0, 0, 0, + 70, 2, 16, 0, 0, 0, + 0, 0, 70, 130, 32, 0, + 1, 0, 0, 0, 0, 0, + 0, 0, 54, 0, 0, 5, + 130, 32, 16, 0, 0, 0, + 0, 0, 58, 0, 16, 0, + 0, 0, 0, 0, 62, 0, + 0, 1, 83, 84, 65, 84, + 116, 0, 0, 0, 9, 0, + 0, 0, 2, 0, 0, 0, + 0, 0, 0, 0, 3, 0, + 0, 0, 5, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 1, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 1, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 1, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0 +}; diff --git a/Minecraft.Client/Windows64/4JLibs/inc/Render/PS_ScreenClear.h b/Minecraft.Client/Windows64/4JLibs/inc/Render/PS_ScreenClear.h new file mode 100644 index 000000000..f245dc134 --- /dev/null +++ b/Minecraft.Client/Windows64/4JLibs/inc/Render/PS_ScreenClear.h @@ -0,0 +1,138 @@ +#if 0 +// +// Generated by Microsoft (R) HLSL Shader Compiler 10.1 +// +// +// Buffer Definitions: +// +// cbuffer cbuff +// { +// +// float4 clearColour; // Offset: 0 Size: 16 +// +// } +// +// +// Resource Bindings: +// +// Name Type Format Dim HLSL Bind Count +// ------------------------------ ---------- ------- ----------- -------------- ------ +// cbuff cbuffer NA NA cb4 1 +// +// +// +// Input signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------- ------ +// SV_POSITION 0 xyzw 0 POS float +// +// +// Output signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------- ------ +// SV_TARGET 0 xyzw 0 TARGET float xyzw +// +ps_4_0 +dcl_constantbuffer CB4[1], immediateIndexed +dcl_output o0.xyzw +mov o0.xyzw, cb4[0].xyzw +ret +// Approximately 2 instruction slots used +#endif + +const BYTE g_main_PS_ScreenClear[] = +{ + 68, 88, 66, 67, 97, 155, + 77, 8, 208, 147, 116, 141, + 80, 3, 25, 165, 35, 253, + 88, 0, 1, 0, 0, 0, + 32, 2, 0, 0, 5, 0, + 0, 0, 52, 0, 0, 0, + 244, 0, 0, 0, 40, 1, + 0, 0, 92, 1, 0, 0, + 164, 1, 0, 0, 82, 68, + 69, 70, 184, 0, 0, 0, + 1, 0, 0, 0, 68, 0, + 0, 0, 1, 0, 0, 0, + 28, 0, 0, 0, 0, 4, + 255, 255, 0, 129, 0, 0, + 144, 0, 0, 0, 60, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 4, 0, 0, 0, 1, 0, + 0, 0, 1, 0, 0, 0, + 99, 98, 117, 102, 102, 0, + 171, 171, 60, 0, 0, 0, + 1, 0, 0, 0, 92, 0, + 0, 0, 16, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 116, 0, 0, 0, + 0, 0, 0, 0, 16, 0, + 0, 0, 2, 0, 0, 0, + 128, 0, 0, 0, 0, 0, + 0, 0, 99, 108, 101, 97, + 114, 67, 111, 108, 111, 117, + 114, 0, 1, 0, 3, 0, + 1, 0, 4, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 77, 105, 99, 114, 111, 115, + 111, 102, 116, 32, 40, 82, + 41, 32, 72, 76, 83, 76, + 32, 83, 104, 97, 100, 101, + 114, 32, 67, 111, 109, 112, + 105, 108, 101, 114, 32, 49, + 48, 46, 49, 0, 73, 83, + 71, 78, 44, 0, 0, 0, + 1, 0, 0, 0, 8, 0, + 0, 0, 32, 0, 0, 0, + 0, 0, 0, 0, 1, 0, + 0, 0, 3, 0, 0, 0, + 0, 0, 0, 0, 15, 0, + 0, 0, 83, 86, 95, 80, + 79, 83, 73, 84, 73, 79, + 78, 0, 79, 83, 71, 78, + 44, 0, 0, 0, 1, 0, + 0, 0, 8, 0, 0, 0, + 32, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 3, 0, 0, 0, 0, 0, + 0, 0, 15, 0, 0, 0, + 83, 86, 95, 84, 65, 82, + 71, 69, 84, 0, 171, 171, + 83, 72, 68, 82, 64, 0, + 0, 0, 64, 0, 0, 0, + 16, 0, 0, 0, 89, 0, + 0, 4, 70, 142, 32, 0, + 4, 0, 0, 0, 1, 0, + 0, 0, 101, 0, 0, 3, + 242, 32, 16, 0, 0, 0, + 0, 0, 54, 0, 0, 6, + 242, 32, 16, 0, 0, 0, + 0, 0, 70, 142, 32, 0, + 4, 0, 0, 0, 0, 0, + 0, 0, 62, 0, 0, 1, + 83, 84, 65, 84, 116, 0, + 0, 0, 2, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 1, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 1, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 1, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0 +}; diff --git a/Minecraft.Client/Windows64/4JLibs/inc/Render/PS_ScreenSpace.h b/Minecraft.Client/Windows64/4JLibs/inc/Render/PS_ScreenSpace.h new file mode 100644 index 000000000..20ab7b9f6 --- /dev/null +++ b/Minecraft.Client/Windows64/4JLibs/inc/Render/PS_ScreenSpace.h @@ -0,0 +1,141 @@ +#if 0 +// +// Generated by Microsoft (R) HLSL Shader Compiler 10.1 +// +// +// Resource Bindings: +// +// Name Type Format Dim HLSL Bind Count +// ------------------------------ ---------- ------- ----------- -------------- ------ +// screen_sampler_s sampler NA NA s0 1 +// screen_texture texture float4 2d t0 1 +// +// +// +// Input signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------- ------ +// SV_POSITION 0 xyzw 0 POS float +// TEXCOORD 0 xy 1 NONE float xy +// +// +// Output signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------- ------ +// SV_TARGET 0 xyzw 0 TARGET float xyzw +// +ps_4_0 +dcl_sampler s0, mode_default +dcl_resource_texture2d (float,float,float,float) t0 +dcl_input_ps linear v1.xy +dcl_output o0.xyzw +sample o0.xyzw, v1.xyxx, t0.xyzw, s0 +ret +// Approximately 2 instruction slots used +#endif + +const BYTE g_main_PS_ScreenSpace[] = +{ + 68, 88, 66, 67, 218, 122, + 219, 87, 187, 98, 219, 145, + 124, 106, 151, 15, 168, 5, + 151, 141, 1, 0, 0, 0, + 84, 2, 0, 0, 5, 0, + 0, 0, 52, 0, 0, 0, + 224, 0, 0, 0, 56, 1, + 0, 0, 108, 1, 0, 0, + 216, 1, 0, 0, 82, 68, + 69, 70, 164, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 2, 0, 0, 0, + 28, 0, 0, 0, 0, 4, + 255, 255, 0, 129, 0, 0, + 124, 0, 0, 0, 92, 0, + 0, 0, 3, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 1, 0, + 0, 0, 1, 0, 0, 0, + 109, 0, 0, 0, 2, 0, + 0, 0, 5, 0, 0, 0, + 4, 0, 0, 0, 255, 255, + 255, 255, 0, 0, 0, 0, + 1, 0, 0, 0, 13, 0, + 0, 0, 115, 99, 114, 101, + 101, 110, 95, 115, 97, 109, + 112, 108, 101, 114, 95, 115, + 0, 115, 99, 114, 101, 101, + 110, 95, 116, 101, 120, 116, + 117, 114, 101, 0, 77, 105, + 99, 114, 111, 115, 111, 102, + 116, 32, 40, 82, 41, 32, + 72, 76, 83, 76, 32, 83, + 104, 97, 100, 101, 114, 32, + 67, 111, 109, 112, 105, 108, + 101, 114, 32, 49, 48, 46, + 49, 0, 73, 83, 71, 78, + 80, 0, 0, 0, 2, 0, + 0, 0, 8, 0, 0, 0, + 56, 0, 0, 0, 0, 0, + 0, 0, 1, 0, 0, 0, + 3, 0, 0, 0, 0, 0, + 0, 0, 15, 0, 0, 0, + 68, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 3, 0, 0, 0, 1, 0, + 0, 0, 3, 3, 0, 0, + 83, 86, 95, 80, 79, 83, + 73, 84, 73, 79, 78, 0, + 84, 69, 88, 67, 79, 79, + 82, 68, 0, 171, 171, 171, + 79, 83, 71, 78, 44, 0, + 0, 0, 1, 0, 0, 0, + 8, 0, 0, 0, 32, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 3, 0, + 0, 0, 0, 0, 0, 0, + 15, 0, 0, 0, 83, 86, + 95, 84, 65, 82, 71, 69, + 84, 0, 171, 171, 83, 72, + 68, 82, 100, 0, 0, 0, + 64, 0, 0, 0, 25, 0, + 0, 0, 90, 0, 0, 3, + 0, 96, 16, 0, 0, 0, + 0, 0, 88, 24, 0, 4, + 0, 112, 16, 0, 0, 0, + 0, 0, 85, 85, 0, 0, + 98, 16, 0, 3, 50, 16, + 16, 0, 1, 0, 0, 0, + 101, 0, 0, 3, 242, 32, + 16, 0, 0, 0, 0, 0, + 69, 0, 0, 9, 242, 32, + 16, 0, 0, 0, 0, 0, + 70, 16, 16, 0, 1, 0, + 0, 0, 70, 126, 16, 0, + 0, 0, 0, 0, 0, 96, + 16, 0, 0, 0, 0, 0, + 62, 0, 0, 1, 83, 84, + 65, 84, 116, 0, 0, 0, + 2, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 2, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 1, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 1, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0 +}; diff --git a/Minecraft.Client/Windows64/4JLibs/inc/Render/PS_Standard.h b/Minecraft.Client/Windows64/4JLibs/inc/Render/PS_Standard.h new file mode 100644 index 000000000..031b2feb4 --- /dev/null +++ b/Minecraft.Client/Windows64/4JLibs/inc/Render/PS_Standard.h @@ -0,0 +1,306 @@ +#if 0 +// +// Generated by Microsoft (R) HLSL Shader Compiler 10.1 +// +// +// Buffer Definitions: +// +// cbuffer diffuse +// { +// +// float4 diffuse_colour; // Offset: 0 Size: 16 +// +// } +// +// cbuffer fog +// { +// +// float4 fog_colour; // Offset: 0 Size: 16 +// +// } +// +// cbuffer alphaTest +// { +// +// float4 alphaTestRef; // Offset: 0 Size: 16 +// +// } +// +// +// Resource Bindings: +// +// Name Type Format Dim HLSL Bind Count +// ------------------------------ ---------- ------- ----------- -------------- ------ +// diffuse_sampler_s sampler NA NA s0 1 +// diffuse_texture texture float4 2d t0 1 +// diffuse cbuffer NA NA cb0 1 +// fog cbuffer NA NA cb1 1 +// alphaTest cbuffer NA NA cb3 1 +// +// +// +// Input signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------- ------ +// SV_POSITION 0 xyzw 0 POS float +// COLOR 0 xyzw 1 NONE float xyzw +// TEXCOORD 0 xyzw 2 NONE float xyz +// +// +// Output signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------- ------ +// SV_TARGET 0 xyzw 0 TARGET float xyzw +// +ps_4_0 +dcl_constantbuffer CB0[1], immediateIndexed +dcl_constantbuffer CB1[1], immediateIndexed +dcl_constantbuffer CB3[1], immediateIndexed +dcl_sampler s0, mode_default +dcl_resource_texture2d (float,float,float,float) t0 +dcl_input_ps linear v1.xyzw +dcl_input_ps linear centroid v2.xyz +dcl_output o0.xyzw +dcl_temps 3 +lt r0.x, l(1.000000), v2.x +sample_l r1.xyzw, v2.xyxx, t0.xyzw, s0, l(0.000000) +sample r2.xyzw, v2.xyxx, t0.xyzw, s0 +movc r0.xyzw, r0.xxxx, r1.xyzw, r2.xyzw +mul r0.xyzw, r0.xyzw, cb0[0].xyzw +mul r0.w, r0.w, v1.w +lt r1.x, r0.w, cb3[0].w +discard_nz r1.x +mad r0.xyz, r0.xyzx, v1.xyzx, -cb1[0].xyzx +mad o0.xyz, v2.zzzz, r0.xyzx, cb1[0].xyzx +mov o0.w, r0.w +ret +// Approximately 12 instruction slots used +#endif + +const BYTE g_main_PS_Standard[] = +{ + 68, 88, 66, 67, 25, 178, + 30, 251, 12, 109, 125, 177, + 117, 147, 183, 223, 204, 75, + 206, 134, 1, 0, 0, 0, + 52, 5, 0, 0, 5, 0, + 0, 0, 52, 0, 0, 0, + 36, 2, 0, 0, 152, 2, + 0, 0, 204, 2, 0, 0, + 184, 4, 0, 0, 82, 68, + 69, 70, 232, 1, 0, 0, + 3, 0, 0, 0, 244, 0, + 0, 0, 5, 0, 0, 0, + 28, 0, 0, 0, 0, 4, + 255, 255, 0, 129, 0, 0, + 189, 1, 0, 0, 188, 0, + 0, 0, 3, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 1, 0, + 0, 0, 1, 0, 0, 0, + 206, 0, 0, 0, 2, 0, + 0, 0, 5, 0, 0, 0, + 4, 0, 0, 0, 255, 255, + 255, 255, 0, 0, 0, 0, + 1, 0, 0, 0, 13, 0, + 0, 0, 222, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 1, 0, 0, 0, + 1, 0, 0, 0, 230, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 1, 0, 0, 0, 1, 0, + 0, 0, 1, 0, 0, 0, + 234, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 3, 0, 0, 0, + 1, 0, 0, 0, 1, 0, + 0, 0, 100, 105, 102, 102, + 117, 115, 101, 95, 115, 97, + 109, 112, 108, 101, 114, 95, + 115, 0, 100, 105, 102, 102, + 117, 115, 101, 95, 116, 101, + 120, 116, 117, 114, 101, 0, + 100, 105, 102, 102, 117, 115, + 101, 0, 102, 111, 103, 0, + 97, 108, 112, 104, 97, 84, + 101, 115, 116, 0, 222, 0, + 0, 0, 1, 0, 0, 0, + 60, 1, 0, 0, 16, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 230, 0, + 0, 0, 1, 0, 0, 0, + 116, 1, 0, 0, 16, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 234, 0, + 0, 0, 1, 0, 0, 0, + 152, 1, 0, 0, 16, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 84, 1, + 0, 0, 0, 0, 0, 0, + 16, 0, 0, 0, 2, 0, + 0, 0, 100, 1, 0, 0, + 0, 0, 0, 0, 100, 105, + 102, 102, 117, 115, 101, 95, + 99, 111, 108, 111, 117, 114, + 0, 171, 1, 0, 3, 0, + 1, 0, 4, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 140, 1, 0, 0, 0, 0, + 0, 0, 16, 0, 0, 0, + 2, 0, 0, 0, 100, 1, + 0, 0, 0, 0, 0, 0, + 102, 111, 103, 95, 99, 111, + 108, 111, 117, 114, 0, 171, + 176, 1, 0, 0, 0, 0, + 0, 0, 16, 0, 0, 0, + 2, 0, 0, 0, 100, 1, + 0, 0, 0, 0, 0, 0, + 97, 108, 112, 104, 97, 84, + 101, 115, 116, 82, 101, 102, + 0, 77, 105, 99, 114, 111, + 115, 111, 102, 116, 32, 40, + 82, 41, 32, 72, 76, 83, + 76, 32, 83, 104, 97, 100, + 101, 114, 32, 67, 111, 109, + 112, 105, 108, 101, 114, 32, + 49, 48, 46, 49, 0, 171, + 171, 171, 73, 83, 71, 78, + 108, 0, 0, 0, 3, 0, + 0, 0, 8, 0, 0, 0, + 80, 0, 0, 0, 0, 0, + 0, 0, 1, 0, 0, 0, + 3, 0, 0, 0, 0, 0, + 0, 0, 15, 0, 0, 0, + 92, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 3, 0, 0, 0, 1, 0, + 0, 0, 15, 15, 0, 0, + 98, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 3, 0, 0, 0, 2, 0, + 0, 0, 15, 7, 0, 0, + 83, 86, 95, 80, 79, 83, + 73, 84, 73, 79, 78, 0, + 67, 79, 76, 79, 82, 0, + 84, 69, 88, 67, 79, 79, + 82, 68, 0, 171, 79, 83, + 71, 78, 44, 0, 0, 0, + 1, 0, 0, 0, 8, 0, + 0, 0, 32, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 3, 0, 0, 0, + 0, 0, 0, 0, 15, 0, + 0, 0, 83, 86, 95, 84, + 65, 82, 71, 69, 84, 0, + 171, 171, 83, 72, 68, 82, + 228, 1, 0, 0, 64, 0, + 0, 0, 121, 0, 0, 0, + 89, 0, 0, 4, 70, 142, + 32, 0, 0, 0, 0, 0, + 1, 0, 0, 0, 89, 0, + 0, 4, 70, 142, 32, 0, + 1, 0, 0, 0, 1, 0, + 0, 0, 89, 0, 0, 4, + 70, 142, 32, 0, 3, 0, + 0, 0, 1, 0, 0, 0, + 90, 0, 0, 3, 0, 96, + 16, 0, 0, 0, 0, 0, + 88, 24, 0, 4, 0, 112, + 16, 0, 0, 0, 0, 0, + 85, 85, 0, 0, 98, 16, + 0, 3, 242, 16, 16, 0, + 1, 0, 0, 0, 98, 24, + 0, 3, 114, 16, 16, 0, + 2, 0, 0, 0, 101, 0, + 0, 3, 242, 32, 16, 0, + 0, 0, 0, 0, 104, 0, + 0, 2, 3, 0, 0, 0, + 49, 0, 0, 7, 18, 0, + 16, 0, 0, 0, 0, 0, + 1, 64, 0, 0, 0, 0, + 128, 63, 10, 16, 16, 0, + 2, 0, 0, 0, 72, 0, + 0, 11, 242, 0, 16, 0, + 1, 0, 0, 0, 70, 16, + 16, 0, 2, 0, 0, 0, + 70, 126, 16, 0, 0, 0, + 0, 0, 0, 96, 16, 0, + 0, 0, 0, 0, 1, 64, + 0, 0, 0, 0, 0, 0, + 69, 0, 0, 9, 242, 0, + 16, 0, 2, 0, 0, 0, + 70, 16, 16, 0, 2, 0, + 0, 0, 70, 126, 16, 0, + 0, 0, 0, 0, 0, 96, + 16, 0, 0, 0, 0, 0, + 55, 0, 0, 9, 242, 0, + 16, 0, 0, 0, 0, 0, + 6, 0, 16, 0, 0, 0, + 0, 0, 70, 14, 16, 0, + 1, 0, 0, 0, 70, 14, + 16, 0, 2, 0, 0, 0, + 56, 0, 0, 8, 242, 0, + 16, 0, 0, 0, 0, 0, + 70, 14, 16, 0, 0, 0, + 0, 0, 70, 142, 32, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 56, 0, 0, 7, + 130, 0, 16, 0, 0, 0, + 0, 0, 58, 0, 16, 0, + 0, 0, 0, 0, 58, 16, + 16, 0, 1, 0, 0, 0, + 49, 0, 0, 8, 18, 0, + 16, 0, 1, 0, 0, 0, + 58, 0, 16, 0, 0, 0, + 0, 0, 58, 128, 32, 0, + 3, 0, 0, 0, 0, 0, + 0, 0, 13, 0, 4, 3, + 10, 0, 16, 0, 1, 0, + 0, 0, 50, 0, 0, 11, + 114, 0, 16, 0, 0, 0, + 0, 0, 70, 2, 16, 0, + 0, 0, 0, 0, 70, 18, + 16, 0, 1, 0, 0, 0, + 70, 130, 32, 128, 65, 0, + 0, 0, 1, 0, 0, 0, + 0, 0, 0, 0, 50, 0, + 0, 10, 114, 32, 16, 0, + 0, 0, 0, 0, 166, 26, + 16, 0, 2, 0, 0, 0, + 70, 2, 16, 0, 0, 0, + 0, 0, 70, 130, 32, 0, + 1, 0, 0, 0, 0, 0, + 0, 0, 54, 0, 0, 5, + 130, 32, 16, 0, 0, 0, + 0, 0, 58, 0, 16, 0, + 0, 0, 0, 0, 62, 0, + 0, 1, 83, 84, 65, 84, + 116, 0, 0, 0, 12, 0, + 0, 0, 3, 0, 0, 0, + 0, 0, 0, 0, 3, 0, + 0, 0, 6, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 1, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 1, 0, 0, 0, 1, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 1, 0, 0, 0, + 1, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0 +}; diff --git a/Minecraft.Client/Windows64/4JLibs/inc/Render/PS_TextureProjection.h b/Minecraft.Client/Windows64/4JLibs/inc/Render/PS_TextureProjection.h new file mode 100644 index 000000000..2c62b802b --- /dev/null +++ b/Minecraft.Client/Windows64/4JLibs/inc/Render/PS_TextureProjection.h @@ -0,0 +1,312 @@ +#if 0 +// +// Generated by Microsoft (R) HLSL Shader Compiler 10.1 +// +// +// Buffer Definitions: +// +// cbuffer diffuse +// { +// +// float4 diffuse_colour; // Offset: 0 Size: 16 +// +// } +// +// cbuffer fog +// { +// +// float4 fog_colour; // Offset: 0 Size: 16 +// +// } +// +// cbuffer alphaTest +// { +// +// float4 alphaTestRef; // Offset: 0 Size: 16 +// +// } +// +// +// Resource Bindings: +// +// Name Type Format Dim HLSL Bind Count +// ------------------------------ ---------- ------- ----------- -------------- ------ +// diffuse_sampler_s sampler NA NA s0 1 +// diffuse_texture texture float4 2d t0 1 +// diffuse cbuffer NA NA cb0 1 +// fog cbuffer NA NA cb1 1 +// alphaTest cbuffer NA NA cb3 1 +// +// +// +// Input signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------- ------ +// SV_POSITION 0 xyzw 0 POS float +// COLOR 0 xyzw 1 NONE float xyzw +// TEXCOORD 0 xyzw 2 NONE float xyzw +// +// +// Output signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------- ------ +// SV_TARGET 0 xyzw 0 TARGET float xyzw +// +ps_4_0 +dcl_constantbuffer CB0[1], immediateIndexed +dcl_constantbuffer CB1[1], immediateIndexed +dcl_constantbuffer CB3[1], immediateIndexed +dcl_sampler s0, mode_default +dcl_resource_texture2d (float,float,float,float) t0 +dcl_input_ps linear v1.xyzw +dcl_input_ps linear centroid v2.xyzw +dcl_output o0.xyzw +dcl_temps 3 +div r0.xy, v2.xyxx, v2.wwww +lt r0.z, l(1.000000), r0.x +sample_l r1.xyzw, r0.xyxx, t0.xyzw, s0, l(0.000000) +sample r2.xyzw, r0.xyxx, t0.xyzw, s0 +movc r0.xyzw, r0.zzzz, r1.xyzw, r2.xyzw +mul r0.xyzw, r0.xyzw, cb0[0].xyzw +mul r0.w, r0.w, v1.w +lt r1.x, r0.w, cb3[0].w +discard_nz r1.x +mad r0.xyz, r0.xyzx, v1.xyzx, -cb1[0].xyzx +mad o0.xyz, v2.zzzz, r0.xyzx, cb1[0].xyzx +mov o0.w, r0.w +ret +// Approximately 13 instruction slots used +#endif + +const BYTE g_main_PS_TextureProjection[] = +{ + 68, 88, 66, 67, 227, 37, + 171, 212, 122, 143, 66, 86, + 246, 87, 18, 62, 25, 189, + 11, 210, 1, 0, 0, 0, + 80, 5, 0, 0, 5, 0, + 0, 0, 52, 0, 0, 0, + 36, 2, 0, 0, 152, 2, + 0, 0, 204, 2, 0, 0, + 212, 4, 0, 0, 82, 68, + 69, 70, 232, 1, 0, 0, + 3, 0, 0, 0, 244, 0, + 0, 0, 5, 0, 0, 0, + 28, 0, 0, 0, 0, 4, + 255, 255, 0, 129, 0, 0, + 189, 1, 0, 0, 188, 0, + 0, 0, 3, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 1, 0, + 0, 0, 1, 0, 0, 0, + 206, 0, 0, 0, 2, 0, + 0, 0, 5, 0, 0, 0, + 4, 0, 0, 0, 255, 255, + 255, 255, 0, 0, 0, 0, + 1, 0, 0, 0, 13, 0, + 0, 0, 222, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 1, 0, 0, 0, + 1, 0, 0, 0, 230, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 1, 0, 0, 0, 1, 0, + 0, 0, 1, 0, 0, 0, + 234, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 3, 0, 0, 0, + 1, 0, 0, 0, 1, 0, + 0, 0, 100, 105, 102, 102, + 117, 115, 101, 95, 115, 97, + 109, 112, 108, 101, 114, 95, + 115, 0, 100, 105, 102, 102, + 117, 115, 101, 95, 116, 101, + 120, 116, 117, 114, 101, 0, + 100, 105, 102, 102, 117, 115, + 101, 0, 102, 111, 103, 0, + 97, 108, 112, 104, 97, 84, + 101, 115, 116, 0, 222, 0, + 0, 0, 1, 0, 0, 0, + 60, 1, 0, 0, 16, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 230, 0, + 0, 0, 1, 0, 0, 0, + 116, 1, 0, 0, 16, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 234, 0, + 0, 0, 1, 0, 0, 0, + 152, 1, 0, 0, 16, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 84, 1, + 0, 0, 0, 0, 0, 0, + 16, 0, 0, 0, 2, 0, + 0, 0, 100, 1, 0, 0, + 0, 0, 0, 0, 100, 105, + 102, 102, 117, 115, 101, 95, + 99, 111, 108, 111, 117, 114, + 0, 171, 1, 0, 3, 0, + 1, 0, 4, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 140, 1, 0, 0, 0, 0, + 0, 0, 16, 0, 0, 0, + 2, 0, 0, 0, 100, 1, + 0, 0, 0, 0, 0, 0, + 102, 111, 103, 95, 99, 111, + 108, 111, 117, 114, 0, 171, + 176, 1, 0, 0, 0, 0, + 0, 0, 16, 0, 0, 0, + 2, 0, 0, 0, 100, 1, + 0, 0, 0, 0, 0, 0, + 97, 108, 112, 104, 97, 84, + 101, 115, 116, 82, 101, 102, + 0, 77, 105, 99, 114, 111, + 115, 111, 102, 116, 32, 40, + 82, 41, 32, 72, 76, 83, + 76, 32, 83, 104, 97, 100, + 101, 114, 32, 67, 111, 109, + 112, 105, 108, 101, 114, 32, + 49, 48, 46, 49, 0, 171, + 171, 171, 73, 83, 71, 78, + 108, 0, 0, 0, 3, 0, + 0, 0, 8, 0, 0, 0, + 80, 0, 0, 0, 0, 0, + 0, 0, 1, 0, 0, 0, + 3, 0, 0, 0, 0, 0, + 0, 0, 15, 0, 0, 0, + 92, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 3, 0, 0, 0, 1, 0, + 0, 0, 15, 15, 0, 0, + 98, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 3, 0, 0, 0, 2, 0, + 0, 0, 15, 15, 0, 0, + 83, 86, 95, 80, 79, 83, + 73, 84, 73, 79, 78, 0, + 67, 79, 76, 79, 82, 0, + 84, 69, 88, 67, 79, 79, + 82, 68, 0, 171, 79, 83, + 71, 78, 44, 0, 0, 0, + 1, 0, 0, 0, 8, 0, + 0, 0, 32, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 3, 0, 0, 0, + 0, 0, 0, 0, 15, 0, + 0, 0, 83, 86, 95, 84, + 65, 82, 71, 69, 84, 0, + 171, 171, 83, 72, 68, 82, + 0, 2, 0, 0, 64, 0, + 0, 0, 128, 0, 0, 0, + 89, 0, 0, 4, 70, 142, + 32, 0, 0, 0, 0, 0, + 1, 0, 0, 0, 89, 0, + 0, 4, 70, 142, 32, 0, + 1, 0, 0, 0, 1, 0, + 0, 0, 89, 0, 0, 4, + 70, 142, 32, 0, 3, 0, + 0, 0, 1, 0, 0, 0, + 90, 0, 0, 3, 0, 96, + 16, 0, 0, 0, 0, 0, + 88, 24, 0, 4, 0, 112, + 16, 0, 0, 0, 0, 0, + 85, 85, 0, 0, 98, 16, + 0, 3, 242, 16, 16, 0, + 1, 0, 0, 0, 98, 24, + 0, 3, 242, 16, 16, 0, + 2, 0, 0, 0, 101, 0, + 0, 3, 242, 32, 16, 0, + 0, 0, 0, 0, 104, 0, + 0, 2, 3, 0, 0, 0, + 14, 0, 0, 7, 50, 0, + 16, 0, 0, 0, 0, 0, + 70, 16, 16, 0, 2, 0, + 0, 0, 246, 31, 16, 0, + 2, 0, 0, 0, 49, 0, + 0, 7, 66, 0, 16, 0, + 0, 0, 0, 0, 1, 64, + 0, 0, 0, 0, 128, 63, + 10, 0, 16, 0, 0, 0, + 0, 0, 72, 0, 0, 11, + 242, 0, 16, 0, 1, 0, + 0, 0, 70, 0, 16, 0, + 0, 0, 0, 0, 70, 126, + 16, 0, 0, 0, 0, 0, + 0, 96, 16, 0, 0, 0, + 0, 0, 1, 64, 0, 0, + 0, 0, 0, 0, 69, 0, + 0, 9, 242, 0, 16, 0, + 2, 0, 0, 0, 70, 0, + 16, 0, 0, 0, 0, 0, + 70, 126, 16, 0, 0, 0, + 0, 0, 0, 96, 16, 0, + 0, 0, 0, 0, 55, 0, + 0, 9, 242, 0, 16, 0, + 0, 0, 0, 0, 166, 10, + 16, 0, 0, 0, 0, 0, + 70, 14, 16, 0, 1, 0, + 0, 0, 70, 14, 16, 0, + 2, 0, 0, 0, 56, 0, + 0, 8, 242, 0, 16, 0, + 0, 0, 0, 0, 70, 14, + 16, 0, 0, 0, 0, 0, + 70, 142, 32, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 56, 0, 0, 7, 130, 0, + 16, 0, 0, 0, 0, 0, + 58, 0, 16, 0, 0, 0, + 0, 0, 58, 16, 16, 0, + 1, 0, 0, 0, 49, 0, + 0, 8, 18, 0, 16, 0, + 1, 0, 0, 0, 58, 0, + 16, 0, 0, 0, 0, 0, + 58, 128, 32, 0, 3, 0, + 0, 0, 0, 0, 0, 0, + 13, 0, 4, 3, 10, 0, + 16, 0, 1, 0, 0, 0, + 50, 0, 0, 11, 114, 0, + 16, 0, 0, 0, 0, 0, + 70, 2, 16, 0, 0, 0, + 0, 0, 70, 18, 16, 0, + 1, 0, 0, 0, 70, 130, + 32, 128, 65, 0, 0, 0, + 1, 0, 0, 0, 0, 0, + 0, 0, 50, 0, 0, 10, + 114, 32, 16, 0, 0, 0, + 0, 0, 166, 26, 16, 0, + 2, 0, 0, 0, 70, 2, + 16, 0, 0, 0, 0, 0, + 70, 130, 32, 0, 1, 0, + 0, 0, 0, 0, 0, 0, + 54, 0, 0, 5, 130, 32, + 16, 0, 0, 0, 0, 0, + 58, 0, 16, 0, 0, 0, + 0, 0, 62, 0, 0, 1, + 83, 84, 65, 84, 116, 0, + 0, 0, 13, 0, 0, 0, + 3, 0, 0, 0, 0, 0, + 0, 0, 3, 0, 0, 0, + 7, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 1, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 1, 0, + 0, 0, 1, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 1, 0, 0, 0, 1, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0 +}; diff --git a/Minecraft.Client/Windows64/4JLibs/inc/Render/Profiler.h b/Minecraft.Client/Windows64/4JLibs/inc/Render/Profiler.h new file mode 100644 index 000000000..f1e56035d --- /dev/null +++ b/Minecraft.Client/Windows64/4JLibs/inc/Render/Profiler.h @@ -0,0 +1,46 @@ +#pragma once +/* +MIT License + +Copyright (c) 2026 Patoke + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +// enable only if you know what you're doing +// requires linking Minecraft.Client against Ws2_32.lib +// stats are available at http://127.0.0.1:1338/ + +//#define ENABLE_PROFILING + +#ifdef ENABLE_PROFILING +#include "microprofile/microprofile.h" +#define PROFILER_INIT() \ + do \ + { \ + MicroProfileOnThreadCreate("MainRenderThread"); \ + MicroProfileSetEnableAllGroups(true); \ + } while (0) +#define PROFILER_FLIP() MicroProfileFlip(NULL); +#define PROFILER_SCOPE(group, name, color) MICROPROFILE_SCOPEI(group, name, color); +#else +#define PROFILER_INIT() ((void)0); +#define PROFILER_FLIP() ((void)0); +#define PROFILER_SCOPE(group, name, color) ((void)0); +#endif diff --git a/Minecraft.Client/Windows64/4JLibs/inc/Render/Render.vcxproj b/Minecraft.Client/Windows64/4JLibs/inc/Render/Render.vcxproj new file mode 100644 index 000000000..a09a54b3b --- /dev/null +++ b/Minecraft.Client/Windows64/4JLibs/inc/Render/Render.vcxproj @@ -0,0 +1,378 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + 18.0 + Win32Proj + {cc99eb47-7231-4067-8717-c6bf4abde338} + render + 10.0 + Render_PC + + + + StaticLibrary + true + v142 + Unicode + + + StaticLibrary + false + v142 + true + Unicode + + + StaticLibrary + true + v142 + Unicode + + + StaticLibrary + false + v142 + true + Unicode + + + + + + + + + + + + + + + + + + + + + 4J_$(ProjectName)_d + + + 4J_$(ProjectName) + $(SolutionDir)Minecraft.Client\Windows64\4JLibs\libs + + + + Level3 + true + WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions) + true + stdcpp20 + Use + pch.h + + + + + true + + + + + Level3 + true + true + true + WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions) + true + stdcpp20 + Use + pch.h + + + + + true + + + + + Level3 + true + _CRT_SECURE_NO_WARNINGS;_DEBUG;_LIB;%(PreprocessorDefinitions) + true + stdcpp20 + Use + stdafx.h + true + stdafx.h + MultiThreadedDebug + libpng\;zlib\;microprofile\;%(AdditionalIncludeDirectories) + Default + true + + + + + true + + + + cd shaders && build.bat + Build Shaders + + + + + Level3 + true + true + true + _CRT_SECURE_NO_WARNINGS;NDEBUG;_LIB;%(PreprocessorDefinitions) + true + stdcpp20 + Use + stdafx.h + true + stdafx.h + MultiThreaded + libpng\;zlib\;microprofile\;%(AdditionalIncludeDirectories) + + + + + true + + + + cd shaders && build.bat + + + Build Shaders + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Default + + + NotUsing + NotUsing + + + + + Default + + + NotUsing + NotUsing + + + + + Default + + + NotUsing + NotUsing + + + + + Default + + + NotUsing + NotUsing + + + + + Default + + + NotUsing + NotUsing + + + + + Default + + + NotUsing + NotUsing + + + + + Default + + + NotUsing + NotUsing + + + + + Default + + + NotUsing + NotUsing + + + + + Default + + + NotUsing + NotUsing + + + + + Default + + + NotUsing + NotUsing + + + + + Default + + + NotUsing + NotUsing + + + + + Default + + + NotUsing + NotUsing + + + + + Default + + + NotUsing + NotUsing + + + + + Default + + + NotUsing + NotUsing + + + + + Default + + + NotUsing + NotUsing + + + + + Default + + + NotUsing + NotUsing + + + + + + + + + + + + Create + Create + Create + Create + + + + + Document + + + Document + + + Document + + + Document + + + + + + \ No newline at end of file diff --git a/Minecraft.Client/Windows64/4JLibs/inc/Render/Render.vcxproj.filters b/Minecraft.Client/Windows64/4JLibs/inc/Render/Render.vcxproj.filters new file mode 100644 index 000000000..57c929b68 --- /dev/null +++ b/Minecraft.Client/Windows64/4JLibs/inc/Render/Render.vcxproj.filters @@ -0,0 +1,211 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd + + + {e5716c14-38f4-49f7-bf1a-916fbc7613a8} + + + {0681221a-1efa-43e4-b0f7-0b525172e486} + + + {0629c87a-576d-4163-8c60-dad190ee97d0} + + + {8be558c7-c6e6-4ef7-bfcb-83d29f079240} + + + {6cbd8b95-ff51-42d2-a66f-282d3a11c042} + + + {3b83a35a-3f4e-4ae3-b7fa-17a84f509af5} + + + {a7d5eaa5-01ec-4f81-93cc-0699c5eabc2c} + + + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files\zlib + + + Header Files\zlib + + + Header Files\libpng + + + Header Files\libpng + + + Header Files\libpng + + + Header Files\libpng + + + Header Files\libpng + + + Header Files\libpng + + + Header Files\libpng + + + Header Files\microprofile + + + Header Files\microprofile + + + Header Files\microprofile + + + Header Files + + + Header Files\generated + + + Header Files\generated + + + Header Files\generated + + + Header Files\generated + + + Header Files\generated + + + Header Files\generated + + + Header Files\generated + + + Header Files\generated + + + Header Files\generated + + + Header Files\generated + + + Header Files\generated + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files\libpng + + + Source Files\libpng + + + Source Files\libpng + + + Source Files\libpng + + + Source Files\libpng + + + Source Files\libpng + + + Source Files\libpng + + + Source Files\libpng + + + Source Files\libpng + + + Source Files\libpng + + + Source Files\libpng + + + Source Files\libpng + + + Source Files\libpng + + + Source Files\libpng + + + Source Files\libpng + + + Source Files\libpng + + + Source Files\microprofile + + + + + Shaders + + + Shaders + + + Shaders + + + Shaders + + + \ No newline at end of file diff --git a/Minecraft.Client/Windows64/4JLibs/inc/Render/Renderer.h b/Minecraft.Client/Windows64/4JLibs/inc/Render/Renderer.h new file mode 100644 index 000000000..66378bdf9 --- /dev/null +++ b/Minecraft.Client/Windows64/4JLibs/inc/Render/Renderer.h @@ -0,0 +1,494 @@ +#pragma once +/* +MIT License + +Copyright (c) 2026 Patoke + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +#include "4J_Render.h" +#include "Profiler.h" +#include +#include +#include + +#define MATRIX_MODE_MODELVIEW 0 +#define MATRIX_MODE_MODELVIEW_PROJECTION 1 +#define MATRIX_MODE_MODELVIEW_TEXTURE 2 +#define MATRIX_MODE_MODELVIEW_CBUFF 3 +#define MATRIX_MODE_MODELVIEW_MAX 4 + +#define STACK_TYPES 4 +#define STACK_SIZE 16 +#define MAX_TEXTURES 512 + +#define NUM_THUMBNAIL_DOWNSAMPLES 4 // the amount of downsamples we do to the thumbnail texture +#define THUMBNAIL_MAX_QUALITY 0 // 1920x1080 +#define THUMBNAIL_DOWNSAMPLE_QUALITY_1 1 // 512x512 +#define THUMBNAIL_DOWNSAMPLE_QUALITY_2 2 // 256x256 +#define THUMBNAIL_DOWNSAMPLE_QUALITY_3 3 // 128x128 +#define THUMBNAIL_DOWNSAMPLE_TARGET 4 // 64x64 + +#define NUM_COMMAND_HANDLES 0x800000 +#define MAX_COMMAND_BUFFERS 16000 + +class Renderer +{ +public: + struct Context; + struct CommandBuffer; + + void UpdateGamma(unsigned short usGamma); + void MatrixMode(int type); + void MatrixSetIdentity(); + void MatrixTranslate(float x, float y, float z); + void MatrixRotate(float angle, float x, float y, float z); + void MatrixScale(float x, float y, float z); + void MatrixPerspective(float fovy, float aspect, float zNear, float zFar); + void MatrixOrthogonal(float left, float right, float bottom, float top, float zNear, float zFar); + void MatrixPop(); + void MatrixPush(); + void MatrixMult(float *mat); + const float *MatrixGet(int type); + void Set_matrixDirty(); + void Initialise(ID3D11Device *pDevice, IDXGISwapChain *pSwapChain); + ID3D11DeviceContext *InitialiseContext(bool fromPresent); + void StartFrame(); + void DoScreenGrabOnNextPresent(); + void Present(); + void Clear(int flags, D3D11_RECT *pRect); + void SetClearColour(const float colourRGBA[4]); + bool IsWidescreen(); + bool IsHiDef(); + void CaptureThumbnail(ImageFileBuffer *pngOut); + void CaptureScreen(ImageFileBuffer *jpgOut, XSOCIAL_PREVIEWIMAGE *previewOut); + void BeginConditionalSurvey(int identifier); + void EndConditionalSurvey(); + void BeginConditionalRendering(int identifier); + void EndConditionalRendering(); + void DrawVertices(C4JRender::ePrimitiveType PrimitiveType, int count, void *dataIn, C4JRender::eVertexType vType, + C4JRender::ePixelShaderType psType); + void DrawVertexBuffer(C4JRender::ePrimitiveType PrimitiveType, int count, ID3D11Buffer *buffer, C4JRender::eVertexType vType, + C4JRender::ePixelShaderType psType); + void CBuffLockStaticCreations(); + int CBuffCreate(int count); + void CBuffDelete(int first, int count); + void CBuffStart(int index, bool full); + void CBuffClear(int index); + int CBuffSize(int index); + void CBuffEnd(); + bool CBuffCall(int index, bool full); + void CBuffTick(); + void CBuffDeferredModeStart(); + void CBuffDeferredModeEnd(); + int TextureCreate(); + void TextureFree(int idx); + void TextureBind(int idx); + void TextureBindVertex(int idx); + void TextureSetTextureLevels(int levels); + int TextureGetTextureLevels(); + void TextureSetParam(int param, int value); + void TextureDynamicUpdateStart(); + void TextureDynamicUpdateEnd(); + void TextureData(int width, int height, void *data, int level, C4JRender::eTextureFormat format); + void TextureDataUpdate(int xoffset, int yoffset, int width, int height, void *data, int level); + HRESULT LoadTextureData(const char *szFilename, D3DXIMAGE_INFO *pSrcInfo, int **ppDataOut); + HRESULT LoadTextureData(BYTE *pbData, DWORD dwBytes, D3DXIMAGE_INFO *pSrcInfo, int **ppDataOut); + HRESULT SaveTextureData(const char *szFilename, D3DXIMAGE_INFO *pSrcInfo, int *ppDataOut); + HRESULT SaveTextureDataToMemory(void *pOutput, int outputCapacity, int *outputLength, int width, int height, int *ppDataIn); + void TextureGetStats(); + ID3D11ShaderResourceView *TextureGetTexture(int idx); + void StateSetColour(float r, float g, float b, float a); + void StateSetDepthMask(bool enable); + void StateSetBlendEnable(bool enable); + void StateSetBlendFunc(int src, int dst); + void StateSetBlendFactor(unsigned int colour); + void StateSetAlphaFunc(int func, float param); + void StateSetDepthFunc(int func); + void StateSetFaceCull(bool enable); + void StateSetFaceCullCW(bool enable); + void StateSetLineWidth(float width); + void StateSetWriteEnable(bool red, bool green, bool blue, bool alpha); + void StateSetDepthTestEnable(bool enable); + void StateSetAlphaTestEnable(bool enable); + void StateSetDepthSlopeAndBias(float slope, float bias); + void StateSetFogEnable(bool enable); + void StateSetFogMode(int mode); + void StateSetFogNearDistance(float dist); + void StateSetFogFarDistance(float dist); + void StateSetFogDensity(float density); + void StateSetFogColour(float red, float green, float blue); + void StateSetLightingEnable(bool enable); + void StateSetVertexTextureUV(float u, float v); + void StateSetLightColour(int light, float red, float green, float blue); + void StateSetLightAmbientColour(float red, float green, float blue); + void StateSetLightDirection(int light, float x, float y, float z); + void StateSetLightEnable(int light, bool enable); + void StateSetViewport(C4JRender::eViewportType viewportType); + void StateSetEnableViewportClipPlanes(bool enable); + void StateSetTexGenCol(int col, float x, float y, float z, float w, bool eyeSpace); + void StateSetStencil(D3D11_COMPARISON_FUNC function, uint8_t stencil_ref, uint8_t stencil_func_mask, uint8_t stencil_write_mask); + void StateSetForceLOD(int LOD); + void BeginEvent(LPCWSTR eventName); + void EndEvent(); + void Suspend(); + bool Suspended(); + void Resume(); + void StateUpdate(); +private: + void SetupShaders(); + void ConvertLinearToPng(ImageFileBuffer *pngOut, unsigned char *linearData, unsigned int width, unsigned int height); + void DrawVertexSetup(C4JRender::eVertexType vType, C4JRender::ePixelShaderType psType, C4JRender::ePrimitiveType primitiveType, int *count, + bool *drawIndexed); + void UpdateTexGenState(); + void UpdateLightingState(); + void UpdateViewportState(); + void UpdateFogState(); + void UpdateTextureState(bool vertexSampler); + void MultWithStack(DirectX::XMMATRIX matrix); + ID3D11DepthStencilState *GetManagedDepthStencilState(); + ID3D11BlendState *GetManagedBlendState(); + ID3D11RasterizerState *GetManagedRasterizerState(); + ID3D11SamplerState *GetManagedSamplerState(); + void DeleteInternalBuffer(int index); + Renderer::Context &getContext(); +public: + + enum eTextureSamplerFlags + { + SAMPLER_PARAM_CLAMP_U = 1 << 0, + SAMPLER_PARAM_CLAMP_V = 1 << 1, + SAMPLER_PARAM_LINEAR_FILTER = 1 << 2, + SAMPLER_PARAM_LINEAR_MIPS = 1 << 3, + }; + + struct Texture + { + bool allocated; + ID3D11Texture2D *texture; + ID3D11ShaderResourceView *view; + DWORD textureFlags; + DWORD mipLevels; + DWORD textureFormat; + DWORD samplerParams; + }; + + struct TexgenCBuffer + { + DirectX::XMMATRIX unk0; + DirectX::XMMATRIX unk1; + }; + + enum eCommandType + { + COMMAND_ADD_MATRIX, + COMMAND_ADD_VERTICES, + COMMAND_BIND_TEXTURE, + COMMAND_SET_COLOR, + COMMAND_SET_DEPTH_FUNC, + COMMAND_SET_DEPTH_MASK, + COMMAND_SET_DEPTH_TEST, + COMMAND_SET_LIGHTING_ENABLE, + COMMAND_SET_LIGHT_ENABLE, + COMMAND_SET_LIGHT_DIRECTION, + COMMAND_SET_LIGHT_COLOUR, + COMMAND_SET_LIGHT_AMBIENT_COLOUR, + COMMAND_SET_BLEND_ENABLE, + COMMAND_SET_BLEND_FUNC, + COMMAND_SET_BLEND_FACTOR, + COMMAND_SET_FACE_CULL, + }; + + struct CommandBuffer + { + CommandBuffer(bool full); + ~CommandBuffer(); + void StartRecording(); + void EndRecording(ID3D11Device *device); + std::uint64_t GetAllocated(); + bool IsBusy(); + void AddMatrix(const float *matrix); + void AddVertices(unsigned int stride, unsigned int count, void *dataIn, Renderer::Context &c); + void BindTexture(int idx); + void SetColor(float r, float g, float b, float a); + void SetDepthFunc(int func); + void SetDepthMask(bool enable); + void SetDepthTestEnable(bool enable); + void SetLightingEnable(bool enable); + void SetLightEnable(int light, bool enable); + void SetLightDirection(int light, float x, float y, float z); + void SetLightColour(int light, float r, float g, float b); + void SetLightAmbientColour(float r, float g, float b); + void SetBlendEnable(bool enable); + void SetBlendFunc(int src, int dst); + void SetBlendFactor(unsigned int factor); + void SetFaceCull(bool enable); + void Render(C4JRender::eVertexType vType, Renderer::Context &c, int primitiveType); + + struct Command + { + Renderer::eCommandType m_command_type; + BYTE commandPadding[12]; + + union + { + BYTE data[64]; + + struct + { + float m_matrix[16]; + // DirectX::XMMATRIX m_matrix; + } add_matrix; + + struct + { + unsigned int m_vertex_index_start; + unsigned int m_vertex_count; + } add_vertices; + + struct + { + unsigned int m_texture_index; + } bind_texture; + + struct + { + float m_color[4]; + } set_color; + + struct + { + int m_depth_func; + } set_depth_func; + + struct + { + bool m_enable; + } set_depth_mask; + + struct + { + bool m_enable; + } set_depth_test; + + struct + { + bool m_enable; + } set_lighting_enable; + + struct + { + int m_light_index; + bool m_enable; + } set_light_enable; + + struct + { + int m_light_index; + float padding[3]; + float m_direction[4]; + } set_light_direction; + + struct + { + int m_light_index; + float m_color[3]; + } set_light_colour; + + struct + { + BYTE padding; + float m_color[3]; + } set_light_ambient_colour; + + struct + { + bool m_enable; + } set_blend_enable; + + struct + { + int m_src; + int m_dst; + } set_blend_func; + + struct + { + unsigned int m_blend_factor; + } set_blend_factor; + + struct + { + bool m_enable; + } set_face_cull; + }; + }; + ID3D11Buffer *m_vertexBuffer; + void *m_vertexData; + std::uint64_t m_vertexDataLength; + std::vector m_commands; + std::uint64_t m_allocated; + BYTE isActive; + }; + + struct DeferredCBuff + { + Renderer::CommandBuffer *m_command_buf; + int m_vertex_index; + int m_vertex_type; + int m_primitive_type; + DirectX::XMMATRIX m_matrix; + }; + + struct Context + { + static const unsigned int VERTEX_BUFFER_SIZE = 0x100000; + + Context(ID3D11Device *device, ID3D11DeviceContext *deviceContext); + + ID3D11DeviceContext *m_pDeviceContext; + ID3DUserDefinedAnnotation *userAnnotation; + int annotateDepth; + DirectX::XMMATRIX matrixStacks[MATRIX_MODE_MODELVIEW_MAX][STACK_SIZE]; + bool matrixDirty[MATRIX_MODE_MODELVIEW_MAX]; + DWORD stackPos[MATRIX_MODE_MODELVIEW_MAX]; + DWORD stackType; + DWORD textureIdx; + bool faceCullEnabled; + bool depthTestEnabled; + bool alphaTestEnabled; + float alphaReference; + bool depthWriteEnabled; + bool fogEnabled; + float fogNearDistance; + float fogFarDistance; + float fogDensity; + float fogColourRed; + float fogColourBlue; + float fogColourGreen; + DWORD fogMode; + bool lightingEnabled; + bool lightEnabled[2]; + bool lightingDirty; + DWORD forcedLOD; + BYTE paddingAfterForceLOD[4]; + DirectX::XMFLOAT4 lightDirection[2]; + DirectX::XMFLOAT4 lightColour[2]; + DirectX::XMFLOAT4 lightAmbientColour; + ID3D11Buffer *m_modelViewMatrix; + ID3D11Buffer *m_localTransformMatrix; + ID3D11Buffer *m_projectionMatrix; + ID3D11Buffer *m_textureMatrix; + ID3D11Buffer *m_vertexTexcoordBuffer; + ID3D11Buffer *m_fogParamsBuffer; + ID3D11Buffer *m_lightingStateBuffer; + ID3D11Buffer *m_texGenMatricesBuffer; + ID3D11Buffer *m_compressedTranslationBuffer; + ID3D11Buffer *m_thumbnailBoundsBuffer; + ID3D11Buffer *m_tintColorBuffer; + ID3D11Buffer *m_fogColourBuffer; + ID3D11Buffer *m_unkColorBuffer; + ID3D11Buffer *m_alphaTestBuffer; + ID3D11Buffer *m_clearColorBuffer; + ID3D11Buffer *m_forcedLODBuffer; + uint64_t dynamicVertexBase; + DWORD dynamicVertexOffset; + ID3D11Buffer *dynamicVertexBuffer; + DirectX::XMMATRIX texGenMatrices[2]; + Renderer::CommandBuffer *commandBuffer; + DWORD recordingBufferIndex; + DWORD recordingVertexType; + DWORD recordingPrimitiveType; + bool deferredModeEnabled; + std::vector deferredBuffers; + D3D11_BLEND_DESC blendDesc; + D3D11_DEPTH_STENCIL_DESC depthStencilDesc; + D3D11_RASTERIZER_DESC rasterizerDesc; + float blendFactor[4]; + }; + + static DWORD tlsIdx; + static _RTL_CRITICAL_SECTION totalAllocCS; + static DWORD s_auiWidths[]; + static DWORD s_auiHeights[]; + static DXGI_FORMAT textureFormats[]; + static D3D_PRIMITIVE_TOPOLOGY g_topologies[]; + static int totalAlloc; + static const float PI; + + float m_fClearColor[4]; + ID3D11Device *m_pDevice; + ID3D11DeviceContext *m_pDeviceContext; + IDXGISwapChain *m_pSwapChain; + ID3D11RenderTargetView *mainRenderTargetView; + ID3D11RenderTargetView *downSampleRenderTargetViews[NUM_THUMBNAIL_DOWNSAMPLES]; + ID3D11ShaderResourceView *mainShaderResourceView; + ID3D11ShaderResourceView *downSampleShaderResourceViews[NUM_THUMBNAIL_DOWNSAMPLES]; + ID3D11Texture2D *downSampleTextures[NUM_THUMBNAIL_DOWNSAMPLES]; + ID3D11DepthStencilView *depthStencilView; + ID3D11VertexShader **vertexShaderTable; + ID3D11VertexShader *screenSpaceVertexShader; + ID3D11VertexShader *screenClearVertexShader; + ID3D11PixelShader **pixelShaderTable; + ID3D11PixelShader *screenSpacePixelShader; + ID3D11PixelShader *screenClearPixelShader; + unsigned int *vertexStrideTable; + ID3D11InputLayout **inputLayoutTable; + ID3D11Buffer *quadIndexBuffer; + ID3D11Buffer *fanIndexBuffer; + DWORD defaultTextureIndex; + WORD reservedRendererWord0; + BYTE paddingAfterRendererWord0[2]; + DWORD presentCount; + BYTE rendererFlag0; + BYTE paddingAfterRendererFlag0[3]; + _RTL_CRITICAL_SECTION m_commandBufferCS; + DWORD activeVertexType; + DWORD activePixelType; + C4JRender::eViewportType m_ViewportType; + BYTE reservedRendererByte0; + BYTE paddingAfterViewportType[3]; + Renderer::Texture m_textures[512]; + DWORD backBufferWidth; + DWORD backBufferHeight; + BYTE reservedRendererByte1; + BYTE paddingAfterRendererByte1[3]; + DWORD reservedRendererDword1; + int16_t *m_vertexIdxToBufferIdx; + CommandBuffer **m_commandBuffers; + DirectX::XMMATRIX *m_commandMatrices; + int *m_bufferIdxToVertexIdx; + uint8_t *m_commandPrimitiveTypes; + uint8_t *m_commandVertexTypes; + DWORD m_currentCommandBuffer; + DWORD m_numBuffersToDeallocate; + std::unordered_map managedBlendStates; + std::unordered_map managedDepthStencilStates; + std::unordered_map managedSamplerStates; + std::unordered_map managedRasterizerStates; + bool m_bShouldScreenGrabNextFrame; + bool m_bSuspended; + + // @Patoke add + ID3D11Texture2D *m_backBufferTexture; +}; + +// Singleton +extern Renderer InternalRenderManager; diff --git a/Minecraft.Client/Windows64/4JLibs/inc/Render/RendererCBuff.cpp b/Minecraft.Client/Windows64/4JLibs/inc/Render/RendererCBuff.cpp new file mode 100644 index 000000000..6a1ab183d --- /dev/null +++ b/Minecraft.Client/Windows64/4JLibs/inc/Render/RendererCBuff.cpp @@ -0,0 +1,844 @@ +/* +MIT License + +Copyright (c) 2026 Patoke + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +#include "stdafx.h" +#include "Renderer.h" + +#include +#include +#include + +Renderer::CommandBuffer::CommandBuffer(bool full) + : m_vertexBuffer(NULL) + , m_vertexData(NULL) + , m_vertexDataLength(0) + , m_commands() + , m_allocated(0x1000) + , isActive(full ? 1 : 0) +{ + m_vertexData = std::malloc(m_allocated); + EnterCriticalSection(&Renderer::totalAllocCS); + Renderer::totalAlloc += static_cast(m_allocated); + LeaveCriticalSection(&Renderer::totalAllocCS); +} + +Renderer::CommandBuffer::~CommandBuffer() +{ + if (m_vertexBuffer) + m_vertexBuffer->Release(); + + std::free(m_vertexData); + + EnterCriticalSection(&Renderer::totalAllocCS); + Renderer::totalAlloc -= static_cast(m_allocated); + LeaveCriticalSection(&Renderer::totalAllocCS); +} + +void Renderer::CommandBuffer::AddMatrix(const float *matrix) +{ + Command command = {}; + command.m_command_type = COMMAND_ADD_MATRIX; + std::memcpy(command.add_matrix.m_matrix, matrix, sizeof(command.add_matrix.m_matrix)); + m_commands.push_back(command); +} + +void Renderer::CommandBuffer::AddVertices(unsigned int stride, unsigned int count, void *dataIn, Renderer::Context &c) +{ + PROFILER_SCOPE("Renderer::CommandBuffer::AddVertices", "AddVertices", MP_ORANGE); + + if (c.matrixDirty[MATRIX_MODE_MODELVIEW_CBUFF]) + { + AddMatrix(InternalRenderManager.MatrixGet(MATRIX_MODE_MODELVIEW_CBUFF)); + c.matrixDirty[MATRIX_MODE_MODELVIEW_CBUFF] = false; + } + + const std::uint64_t vertexOffset = m_vertexDataLength; + const std::uint64_t copySize = std::uint64_t(stride) * std::uint64_t(count); + + Command command = {}; + command.m_command_type = COMMAND_ADD_VERTICES; + command.add_vertices.m_vertex_index_start = static_cast(vertexOffset); + command.add_vertices.m_vertex_count = count; + + m_vertexDataLength = vertexOffset + copySize; + if (m_vertexDataLength > m_allocated) + { + EnterCriticalSection(&Renderer::totalAllocCS); + Renderer::totalAlloc -= static_cast(m_allocated); + LeaveCriticalSection(&Renderer::totalAllocCS); + + m_allocated = ((m_vertexDataLength + (0x1000 - 1)) & ~(0x1000 - 1)); + m_vertexData = std::realloc(m_vertexData, m_allocated); + + EnterCriticalSection(&Renderer::totalAllocCS); + Renderer::totalAlloc += static_cast(m_allocated); + LeaveCriticalSection(&Renderer::totalAllocCS); + } + + const std::size_t byteCount = std::size_t(stride) * std::size_t(count); + std::memcpy(static_cast(m_vertexData) + vertexOffset, dataIn, byteCount); + m_commands.push_back(command); +} + +void Renderer::CommandBuffer::BindTexture(int idx) +{ + Command command = {}; + command.m_command_type = COMMAND_BIND_TEXTURE; + command.bind_texture.m_texture_index = idx; + m_commands.push_back(command); +} + +bool Renderer::CBuffCall(int index, bool full) +{ + EnterCriticalSection(&m_commandBufferCS); + + bool result = false; + const int commandIndex = m_vertexIdxToBufferIdx[index]; + if (commandIndex >= 0) + { + Renderer::Context &c = getContext(); + const std::uint8_t vertexType = m_commandVertexTypes[commandIndex]; + const std::uint8_t primitiveType = m_commandPrimitiveTypes[commandIndex]; + + if (full) + { + if (c.matrixDirty[MATRIX_MODE_MODELVIEW]) + { + D3D11_MAPPED_SUBRESOURCE mappedMatrix0 = {}; + c.m_pDeviceContext->Map(c.m_modelViewMatrix, 0, D3D11_MAP_WRITE_DISCARD, 0, &mappedMatrix0); + std::memcpy(mappedMatrix0.pData, MatrixGet(MATRIX_MODE_MODELVIEW), sizeof(DirectX::XMMATRIX)); + c.m_pDeviceContext->Unmap(c.m_modelViewMatrix, 0); + c.matrixDirty[MATRIX_MODE_MODELVIEW] = false; + } + + if (c.matrixDirty[MATRIX_MODE_MODELVIEW_PROJECTION]) + { + D3D11_MAPPED_SUBRESOURCE mappedMatrix2 = {}; + c.m_pDeviceContext->Map(c.m_projectionMatrix, 0, D3D11_MAP_WRITE_DISCARD, 0, &mappedMatrix2); + std::memcpy(mappedMatrix2.pData, MatrixGet(MATRIX_MODE_MODELVIEW_PROJECTION), sizeof(DirectX::XMMATRIX)); + c.m_pDeviceContext->Unmap(c.m_projectionMatrix, 0); + c.matrixDirty[MATRIX_MODE_MODELVIEW_PROJECTION] = false; + } + + if (c.matrixDirty[MATRIX_MODE_MODELVIEW_TEXTURE]) + { + D3D11_MAPPED_SUBRESOURCE mappedMatrix3 = {}; + c.m_pDeviceContext->Map(c.m_textureMatrix, 0, D3D11_MAP_WRITE_DISCARD, 0, &mappedMatrix3); + std::memcpy(mappedMatrix3.pData, MatrixGet(MATRIX_MODE_MODELVIEW_TEXTURE), sizeof(DirectX::XMMATRIX)); + c.m_pDeviceContext->Unmap(c.m_textureMatrix, 0); + c.matrixDirty[MATRIX_MODE_MODELVIEW_TEXTURE] = false; + } + + UpdateFogState(); + UpdateLightingState(); + UpdateViewportState(); + UpdateTexGenState(); + + if (vertexType != activeVertexType) + { + c.m_pDeviceContext->VSSetShader(vertexShaderTable[vertexType], NULL, 0); + c.m_pDeviceContext->IASetInputLayout(inputLayoutTable[vertexType]); + activeVertexType = vertexType; + } + + int pixelType = 0; + if (static_cast(c.forcedLOD) > -1) + { + const float forcedLod[4] = {static_cast(static_cast(c.forcedLOD)), 0.0f, 0.0f, 0.0f}; + D3D11_MAPPED_SUBRESOURCE mappedAux4 = {}; + c.m_pDeviceContext->Map(c.m_forcedLODBuffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &mappedAux4); + std::memcpy(mappedAux4.pData, forcedLod, sizeof(forcedLod)); + c.m_pDeviceContext->Unmap(c.m_forcedLODBuffer, 0); + pixelType = C4JRender::PIXEL_SHADER_TYPE_FORCELOD; + } + + if (static_cast(pixelType) != activePixelType) + { + c.m_pDeviceContext->PSSetShader(pixelShaderTable[pixelType], NULL, 0); + activePixelType = pixelType; + } + + c.m_pDeviceContext->IASetPrimitiveTopology(g_topologies[primitiveType]); + + ID3D11Buffer *indexBuffer = NULL; + if (primitiveType == C4JRender::PRIMITIVE_TYPE_QUAD_LIST) + indexBuffer = quadIndexBuffer; + else if (primitiveType == C4JRender::PRIMITIVE_TYPE_TRIANGLE_FAN) + indexBuffer = fanIndexBuffer; + + c.m_pDeviceContext->IASetIndexBuffer(indexBuffer, DXGI_FORMAT_R16_UINT, 0); + } + + m_commandBuffers[commandIndex]->Render(static_cast(vertexType), c, primitiveType); + + if (full) + { + MultWithStack(m_commandMatrices[commandIndex]); + c.matrixStacks[MATRIX_MODE_MODELVIEW_CBUFF][0] = DirectX::XMMatrixIdentity(); + c.matrixDirty[MATRIX_MODE_MODELVIEW_CBUFF] = true; + } + + result = true; + } + + LeaveCriticalSection(&m_commandBufferCS); + return result; +} + +void Renderer::CBuffClear(int index) +{ + EnterCriticalSection(&m_commandBufferCS); + + const int internalIndex = m_vertexIdxToBufferIdx[index]; + if (internalIndex >= 0) + { + DeleteInternalBuffer(internalIndex); + m_vertexIdxToBufferIdx[index] = static_cast(-2); + } + + LeaveCriticalSection(&m_commandBufferCS); +} + +int Renderer::CBuffCreate(int count) +{ + EnterCriticalSection(&m_commandBufferCS); + + int first = reservedRendererDword1; + if (first < NUM_COMMAND_HANDLES) + { + int probe = first; + int end = first + count; + while (true) + { + assert(first < NUM_COMMAND_HANDLES); + + int cursor = probe; + while (cursor < end && cursor < NUM_COMMAND_HANDLES && m_vertexIdxToBufferIdx[cursor] == static_cast(-1)) + { + ++cursor; + } + + if (cursor >= end) + break; + + ++first; + ++probe; + ++end; + if (first >= NUM_COMMAND_HANDLES || end > NUM_COMMAND_HANDLES) + { + first = -1; + break; + } + } + + if (first >= 0) + { + const int allocationEnd = first + count; + for (int i = first; i < allocationEnd; ++i) + m_vertexIdxToBufferIdx[i] = static_cast(-2); + + if (reservedRendererByte1) + reservedRendererDword1 = allocationEnd; + } + } + else + { + first = -1; + } + + LeaveCriticalSection(&m_commandBufferCS); + return first; +} + +void Renderer::CBuffDeferredModeEnd() +{ + Renderer::Context &c = getContext(); + if (!c.deferredModeEnabled) + return; + + EnterCriticalSection(&m_commandBufferCS); + c.deferredModeEnabled = false; + + for (std::vector::const_iterator it = c.deferredBuffers.begin(); it != c.deferredBuffers.end(); ++it) + { + const Renderer::DeferredCBuff &deferred = *it; + const int existingIndex = m_vertexIdxToBufferIdx[deferred.m_vertex_index]; + if (existingIndex >= 0) + DeleteInternalBuffer(existingIndex); + + if (static_cast(m_currentCommandBuffer + m_numBuffersToDeallocate + 10) > MAX_COMMAND_BUFFERS) + DebugBreak(); + + const int internalSlot = m_currentCommandBuffer; + ++m_currentCommandBuffer; + + m_vertexIdxToBufferIdx[deferred.m_vertex_index] = static_cast(internalSlot); + m_bufferIdxToVertexIdx[internalSlot] = deferred.m_vertex_index; + m_commandVertexTypes[internalSlot] = static_cast(deferred.m_vertex_type); + m_commandPrimitiveTypes[internalSlot] = static_cast(deferred.m_primitive_type); + m_commandBuffers[internalSlot] = deferred.m_command_buf; + m_commandMatrices[internalSlot] = deferred.m_matrix; + } + + c.deferredBuffers.clear(); + LeaveCriticalSection(&m_commandBufferCS); +} + +void Renderer::CBuffDeferredModeStart() +{ + getContext().deferredModeEnabled = true; +} + +void Renderer::CBuffDelete(int first, int count) +{ + EnterCriticalSection(&m_commandBufferCS); + + const int end = first + count; + for (int i = first; i < end; ++i) + { + const int internalIndex = m_vertexIdxToBufferIdx[i]; + if (internalIndex >= 0) + DeleteInternalBuffer(internalIndex); + + m_vertexIdxToBufferIdx[i] = static_cast(-1); + } + + LeaveCriticalSection(&m_commandBufferCS); +} + +void Renderer::CBuffEnd() +{ + Renderer::Context &c = getContext(); + + assert(c.stackType == MATRIX_MODE_MODELVIEW_CBUFF); + assert(c.stackPos[MATRIX_MODE_MODELVIEW_CBUFF] == 0); + + EnterCriticalSection(&m_commandBufferCS); + + if (c.deferredModeEnabled) + { + Renderer::DeferredCBuff deferred; + deferred.m_command_buf = c.commandBuffer; + deferred.m_vertex_index = c.recordingBufferIndex; + deferred.m_vertex_type = c.recordingVertexType; + deferred.m_primitive_type = c.recordingPrimitiveType; + deferred.m_matrix = c.matrixStacks[MATRIX_MODE_MODELVIEW_CBUFF][0]; + c.deferredBuffers.push_back(deferred); + } + else + { + const int existingIndex = m_vertexIdxToBufferIdx[c.recordingBufferIndex]; + if (existingIndex >= 0) + DeleteInternalBuffer(existingIndex); + + if (static_cast(m_currentCommandBuffer + m_numBuffersToDeallocate + 10) > MAX_COMMAND_BUFFERS) + DebugBreak(); + + const int internalSlot = m_currentCommandBuffer; + ++m_currentCommandBuffer; + + m_vertexIdxToBufferIdx[c.recordingBufferIndex] = static_cast(internalSlot); + m_bufferIdxToVertexIdx[internalSlot] = c.recordingBufferIndex; + m_commandVertexTypes[internalSlot] = static_cast(c.recordingVertexType); + m_commandPrimitiveTypes[internalSlot] = static_cast(c.recordingPrimitiveType); + m_commandBuffers[internalSlot] = c.commandBuffer; + m_commandMatrices[internalSlot] = c.matrixStacks[MATRIX_MODE_MODELVIEW_CBUFF][0]; + } + + c.stackType = MATRIX_MODE_MODELVIEW; + c.commandBuffer->EndRecording(m_pDevice); + c.commandBuffer = NULL; + + LeaveCriticalSection(&m_commandBufferCS); +} + +void Renderer::CBuffLockStaticCreations() +{ + reservedRendererByte1 = 0; +} + +int Renderer::CBuffSize(int index) +{ + if (index == -1) + return totalAlloc < 0 ? 0 : totalAlloc; + + unsigned int size = 0; + EnterCriticalSection(&m_commandBufferCS); + const int commandIndex = m_vertexIdxToBufferIdx[index]; + if (commandIndex >= 0) + size = static_cast(m_commandBuffers[commandIndex]->GetAllocated()); + LeaveCriticalSection(&m_commandBufferCS); + return size; +} + +void Renderer::CBuffStart(int index, bool full) +{ + Renderer::Context &c = getContext(); + c.commandBuffer = new (std::nothrow) Renderer::CommandBuffer(full); + c.recordingBufferIndex = index; + + assert(c.stackType == MATRIX_MODE_MODELVIEW); + + c.stackType = MATRIX_MODE_MODELVIEW_CBUFF; + c.stackPos[MATRIX_MODE_MODELVIEW_CBUFF] = 0; + c.matrixStacks[MATRIX_MODE_MODELVIEW_CBUFF][0] = DirectX::XMMatrixIdentity(); + c.matrixDirty[MATRIX_MODE_MODELVIEW_CBUFF] = true; +} + +void Renderer::CBuffTick() +{ + EnterCriticalSection(&m_commandBufferCS); + + int completedDeletes = 0; + if (m_numBuffersToDeallocate > 0) + { + int deleteIdx = MAX_COMMAND_BUFFERS - 1; + do + { + Renderer::CommandBuffer *buffer = m_commandBuffers[deleteIdx]; + if (buffer) + delete buffer; + m_commandBuffers[deleteIdx] = NULL; + + if (--m_numBuffersToDeallocate == 0) + { + ++completedDeletes; + --deleteIdx; + } + else + { + m_commandBuffers[deleteIdx] = m_commandBuffers[(MAX_COMMAND_BUFFERS - 1) - static_cast(m_numBuffersToDeallocate)]; + } + } while (completedDeletes < static_cast(m_numBuffersToDeallocate)); + } + + LeaveCriticalSection(&m_commandBufferCS); +} + +void Renderer::DeleteInternalBuffer(int index) +{ + EnterCriticalSection(&m_commandBufferCS); + + ++m_numBuffersToDeallocate; + const int index_delete = MAX_COMMAND_BUFFERS - static_cast(m_numBuffersToDeallocate); + + m_commandBuffers[index_delete] = m_commandBuffers[index]; + m_commandMatrices[index_delete] = m_commandMatrices[index]; + + if (m_currentCommandBuffer-- != 1) + { + const int mappedFrom = m_currentCommandBuffer; + + m_commandBuffers[index] = m_commandBuffers[mappedFrom]; + m_commandMatrices[index] = m_commandMatrices[mappedFrom]; + m_commandVertexTypes[index] = m_commandVertexTypes[mappedFrom]; + m_commandPrimitiveTypes[index] = m_commandPrimitiveTypes[mappedFrom]; + + const int commandIndex = m_bufferIdxToVertexIdx[mappedFrom]; + m_vertexIdxToBufferIdx[commandIndex] = static_cast(index); + m_bufferIdxToVertexIdx[index] = commandIndex; + } + + LeaveCriticalSection(&m_commandBufferCS); +} + +void Renderer::CommandBuffer::EndRecording(ID3D11Device *device) +{ + if (m_vertexDataLength != 0) + { + D3D11_BUFFER_DESC desc = {}; + desc.ByteWidth = static_cast(m_vertexDataLength); + desc.Usage = D3D11_USAGE_IMMUTABLE; + desc.BindFlags = D3D11_BIND_VERTEX_BUFFER; + + D3D11_SUBRESOURCE_DATA data = {}; + data.pSysMem = m_vertexData; + device->CreateBuffer(&desc, &data, &m_vertexBuffer); + } + + std::free(m_vertexData); + m_vertexData = NULL; +} + +std::uint64_t Renderer::CommandBuffer::GetAllocated() +{ + return m_allocated; +} + +bool Renderer::CommandBuffer::IsBusy() +{ + return false; +} + +void Renderer::CommandBuffer::Render(C4JRender::eVertexType vType, Renderer::Context &c, int primitiveType) +{ + PROFILER_SCOPE("Renderer::CommandBuffer::Render", "Render", MP_ORANGE); + + if (!m_vertexBuffer) + return; + + int drawVertexType = vType; + int shaderVertexType = drawVertexType; + bool matrixOverride = false; + + for (const Command &command : m_commands) + { + PROFILER_SCOPE("Renderer::CommandBuffer::Render", "ProcessCommand", MP_ORANGE); + + switch (command.m_command_type) + { + case COMMAND_ADD_MATRIX: + { + if (drawVertexType == C4JRender::VERTEX_TYPE_COMPRESSED) + { + const float row[4] = { + command.add_matrix.m_matrix[12], command.add_matrix.m_matrix[13], + command.add_matrix.m_matrix[14], command.add_matrix.m_matrix[15] + }; + D3D11_MAPPED_SUBRESOURCE mappedAux0 = {}; + c.m_pDeviceContext->Map(c.m_compressedTranslationBuffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &mappedAux0); + std::memcpy(mappedAux0.pData, row, sizeof(row)); + c.m_pDeviceContext->Unmap(c.m_compressedTranslationBuffer, 0); + } + else + { + D3D11_MAPPED_SUBRESOURCE mappedMatrix1 = {}; + c.m_pDeviceContext->Map(c.m_localTransformMatrix, 0, D3D11_MAP_WRITE_DISCARD, 0, &mappedMatrix1); + std::memcpy(mappedMatrix1.pData, command.add_matrix.m_matrix, sizeof(command.add_matrix.m_matrix)); + c.m_pDeviceContext->Unmap(c.m_localTransformMatrix, 0); + matrixOverride = true; + } + break; + } + case COMMAND_ADD_VERTICES: + { + if (isActive) + { + InternalRenderManager.UpdateLightingState(); + + if (drawVertexType == C4JRender::VERTEX_TYPE_PF3_TF2_CB4_NB4_XW1) + { + if (c.lightingEnabled) + { + drawVertexType = C4JRender::VERTEX_TYPE_PF3_TF2_CB4_NB4_XW1_LIT; + shaderVertexType = C4JRender::VERTEX_TYPE_PF3_TF2_CB4_NB4_XW1_LIT; + } + } + else if (drawVertexType == C4JRender::VERTEX_TYPE_PF3_TF2_CB4_NB4_XW1_LIT && !c.lightingEnabled) + { + drawVertexType = C4JRender::VERTEX_TYPE_PF3_TF2_CB4_NB4_XW1; + shaderVertexType = C4JRender::VERTEX_TYPE_PF3_TF2_CB4_NB4_XW1; + } + + if (static_cast(drawVertexType) != InternalRenderManager.activeVertexType) + { + c.m_pDeviceContext->VSSetShader(InternalRenderManager.vertexShaderTable[shaderVertexType], NULL, 0); + c.m_pDeviceContext->IASetInputLayout(InternalRenderManager.inputLayoutTable[shaderVertexType]); + InternalRenderManager.activeVertexType = drawVertexType; + } + } + + unsigned int drawCount = command.add_vertices.m_vertex_count; + bool drawIndexed = false; + if (primitiveType == C4JRender::PRIMITIVE_TYPE_QUAD_LIST) + { + drawCount = (drawCount * 6) / 4; + drawIndexed = true; + } + else if (primitiveType == C4JRender::PRIMITIVE_TYPE_TRIANGLE_FAN) + { + drawCount = (drawCount - 2) * 3; + drawIndexed = true; + } + + ID3D11Buffer *buffer = m_vertexBuffer; + const UINT stride = InternalRenderManager.vertexStrideTable[drawVertexType]; + const UINT offset = command.add_vertices.m_vertex_index_start; + c.m_pDeviceContext->IASetVertexBuffers(0, 1, &buffer, &stride, &offset); + + if (drawIndexed) + c.m_pDeviceContext->DrawIndexed(drawCount, 0, 0); + else + c.m_pDeviceContext->Draw(drawCount, 0); + break; + } + case COMMAND_BIND_TEXTURE: + { + c.textureIdx = command.bind_texture.m_texture_index; + ID3D11ShaderResourceView *view = InternalRenderManager.m_textures[c.textureIdx].view; + c.m_pDeviceContext->PSSetShaderResources(0, 1, &view); + InternalRenderManager.UpdateTextureState(false); + break; + } + case COMMAND_SET_COLOR: + { + D3D11_MAPPED_SUBRESOURCE mappedColour = {}; + c.m_pDeviceContext->Map(c.m_tintColorBuffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &mappedColour); + std::memcpy(mappedColour.pData, command.set_color.m_color, sizeof(command.set_color.m_color)); + c.m_pDeviceContext->Unmap(c.m_tintColorBuffer, 0); + break; + } + case COMMAND_SET_DEPTH_FUNC: + { + c.depthStencilDesc.DepthFunc = static_cast(command.set_depth_func.m_depth_func); + c.m_pDeviceContext->OMSetDepthStencilState(InternalRenderManager.GetManagedDepthStencilState(), 0); + break; + } + case COMMAND_SET_DEPTH_MASK: + { + c.depthWriteEnabled = command.set_depth_mask.m_enable; + c.depthStencilDesc.DepthWriteMask = command.set_depth_mask.m_enable ? D3D11_DEPTH_WRITE_MASK_ALL : D3D11_DEPTH_WRITE_MASK_ZERO; + c.m_pDeviceContext->OMSetDepthStencilState(InternalRenderManager.GetManagedDepthStencilState(), 0); + break; + } + case COMMAND_SET_DEPTH_TEST: + { + c.depthTestEnabled = command.set_depth_test.m_enable; + c.depthStencilDesc.DepthEnable = command.set_depth_test.m_enable; + c.m_pDeviceContext->OMSetDepthStencilState(InternalRenderManager.GetManagedDepthStencilState(), 0); + break; + } + case COMMAND_SET_LIGHTING_ENABLE: + { + c.lightingEnabled = command.set_lighting_enable.m_enable; + break; + } + case COMMAND_SET_LIGHT_ENABLE: + { + const int light = command.set_light_enable.m_light_index; + if (light >= 0 && light < 2) + { + c.lightEnabled[light] = command.set_light_enable.m_enable; + c.lightingDirty = true; + } + break; + } + case COMMAND_SET_LIGHT_DIRECTION: + { + const int light = command.set_light_direction.m_light_index; + if (light >= 0 && light < 2) + { + c.lightDirection[light].x = command.set_light_direction.m_direction[0]; + c.lightDirection[light].y = command.set_light_direction.m_direction[1]; + c.lightDirection[light].z = command.set_light_direction.m_direction[2]; + c.lightDirection[light].w = command.set_light_direction.m_direction[3]; + c.lightingDirty = true; + } + break; + } + case COMMAND_SET_LIGHT_COLOUR: + { + const int light = command.set_light_colour.m_light_index; + if (light >= 0 && light < 2) + { + c.lightColour[light].x = command.set_light_colour.m_color[0]; + c.lightColour[light].y = command.set_light_colour.m_color[1]; + c.lightColour[light].z = command.set_light_colour.m_color[2]; + c.lightColour[light].w = 1.0f; + c.lightingDirty = true; + } + break; + } + case COMMAND_SET_LIGHT_AMBIENT_COLOUR: + { + c.lightAmbientColour.x = command.set_light_ambient_colour.m_color[0]; + c.lightAmbientColour.y = command.set_light_ambient_colour.m_color[1]; + c.lightAmbientColour.z = command.set_light_ambient_colour.m_color[2]; + c.lightAmbientColour.w = 1.0f; + c.lightingDirty = true; + break; + } + case COMMAND_SET_BLEND_ENABLE: + { + c.blendDesc.RenderTarget[0].BlendEnable = command.set_blend_enable.m_enable; + break; + } + case COMMAND_SET_BLEND_FUNC: + { + c.blendDesc.RenderTarget[0].SrcBlend = static_cast(command.set_blend_func.m_src); + c.blendDesc.RenderTarget[0].DestBlend = static_cast(command.set_blend_func.m_dst); + c.m_pDeviceContext->OMSetBlendState(InternalRenderManager.GetManagedBlendState(), c.blendFactor, 0xFFFFFFFF); + break; + } + case COMMAND_SET_BLEND_FACTOR: + { + const unsigned int factor = command.set_blend_factor.m_blend_factor; + c.blendFactor[0] = float((factor >> 0) & 0xFF) / 255.0f; + c.blendFactor[1] = float((factor >> 8) & 0xFF) / 255.0f; + c.blendFactor[2] = float((factor >> 16) & 0xFF) / 255.0f; + c.blendFactor[3] = float((factor >> 24) & 0xFF) / 255.0f; + c.m_pDeviceContext->OMSetBlendState(InternalRenderManager.GetManagedBlendState(), c.blendFactor, 0xFFFFFFFF); + break; + } + case COMMAND_SET_FACE_CULL: + { + c.rasterizerDesc.CullMode = command.set_face_cull.m_enable ? D3D11_CULL_BACK : D3D11_CULL_NONE; + c.m_pDeviceContext->RSSetState(InternalRenderManager.GetManagedRasterizerState()); + c.faceCullEnabled = command.set_face_cull.m_enable; + break; + } + default: + break; + } + } + + if (matrixOverride) + { + const DirectX::XMMATRIX identity = DirectX::XMMatrixIdentity(); + D3D11_MAPPED_SUBRESOURCE mappedIdentity = {}; + c.m_pDeviceContext->Map(c.m_localTransformMatrix, 0, D3D11_MAP_WRITE_DISCARD, 0, &mappedIdentity); + std::memcpy(mappedIdentity.pData, &identity, sizeof(identity)); + c.m_pDeviceContext->Unmap(c.m_localTransformMatrix, 0); + } +} + +void Renderer::CommandBuffer::SetBlendEnable(bool enable) +{ + Command command = {}; + command.m_command_type = COMMAND_SET_BLEND_ENABLE; + command.set_blend_enable.m_enable = enable; + m_commands.push_back(command); +} + +void Renderer::CommandBuffer::SetBlendFactor(unsigned int factor) +{ + Command command = {}; + command.m_command_type = COMMAND_SET_BLEND_FACTOR; + command.set_blend_factor.m_blend_factor = factor; + m_commands.push_back(command); +} + +void Renderer::CommandBuffer::SetBlendFunc(int src, int dst) +{ + Command command = {}; + command.m_command_type = COMMAND_SET_BLEND_FUNC; + command.set_blend_func.m_src = src; + command.set_blend_func.m_dst = dst; + m_commands.push_back(command); +} + +void Renderer::CommandBuffer::SetColor(float r, float g, float b, float a) +{ + Command command = {}; + command.m_command_type = COMMAND_SET_COLOR; + command.set_color.m_color[0] = r; + command.set_color.m_color[1] = g; + command.set_color.m_color[2] = b; + command.set_color.m_color[3] = a; + m_commands.push_back(command); +} + +void Renderer::CommandBuffer::SetDepthFunc(int func) +{ + Command command = {}; + command.m_command_type = COMMAND_SET_DEPTH_FUNC; + command.set_depth_func.m_depth_func = func; + m_commands.push_back(command); +} + +void Renderer::CommandBuffer::SetDepthMask(bool enable) +{ + Command command = {}; + command.m_command_type = COMMAND_SET_DEPTH_MASK; + command.set_depth_mask.m_enable = enable; + m_commands.push_back(command); +} + +void Renderer::CommandBuffer::SetDepthTestEnable(bool enable) +{ + Command command = {}; + command.m_command_type = COMMAND_SET_DEPTH_TEST; + command.set_depth_test.m_enable = enable; + m_commands.push_back(command); +} + +void Renderer::CommandBuffer::SetFaceCull(bool enable) +{ + Command command = {}; + command.m_command_type = COMMAND_SET_FACE_CULL; + command.set_face_cull.m_enable = enable; + m_commands.push_back(command); +} + +void Renderer::CommandBuffer::SetLightAmbientColour(float r, float g, float b) +{ + Command command = {}; + command.m_command_type = COMMAND_SET_LIGHT_AMBIENT_COLOUR; + command.set_light_ambient_colour.m_color[0] = r; + command.set_light_ambient_colour.m_color[1] = g; + command.set_light_ambient_colour.m_color[2] = b; + m_commands.push_back(command); +} + +void Renderer::CommandBuffer::SetLightColour(int light, float r, float g, float b) +{ + Command command = {}; + command.m_command_type = COMMAND_SET_LIGHT_COLOUR; + command.set_light_colour.m_light_index = light; + command.set_light_colour.m_color[0] = r; + command.set_light_colour.m_color[1] = g; + command.set_light_colour.m_color[2] = b; + m_commands.push_back(command); +} + +void Renderer::CommandBuffer::SetLightDirection(int light, float x, float y, float z) +{ + Renderer::Context &c = InternalRenderManager.getContext(); + const std::uint32_t depth = c.stackPos[MATRIX_MODE_MODELVIEW_CBUFF]; + const DirectX::XMMATRIX &matrix = c.matrixStacks[MATRIX_MODE_MODELVIEW_CBUFF][depth]; + + DirectX::XMVECTOR direction = DirectX::XMVectorSet(x, y, z, 0.0f); + direction = DirectX::XMVector3TransformNormal(direction, matrix); + direction = DirectX::XMVector3Normalize(direction); + + Command command = {}; + command.m_command_type = COMMAND_SET_LIGHT_DIRECTION; + command.set_light_direction.m_light_index = light; + DirectX::XMFLOAT4 outDirection; + DirectX::XMStoreFloat4(&outDirection, direction); + command.set_light_direction.m_direction[0] = outDirection.x; + command.set_light_direction.m_direction[1] = outDirection.y; + command.set_light_direction.m_direction[2] = outDirection.z; + command.set_light_direction.m_direction[3] = outDirection.w; + m_commands.push_back(command); +} + +void Renderer::CommandBuffer::SetLightEnable(int light, bool enable) +{ + Command command = {}; + command.m_command_type = COMMAND_SET_LIGHT_ENABLE; + command.set_light_enable.m_light_index = light; + command.set_light_enable.m_enable = enable; + m_commands.push_back(command); +} + +void Renderer::CommandBuffer::SetLightingEnable(bool enable) +{ + Command command = {}; + command.m_command_type = COMMAND_SET_LIGHTING_ENABLE; + command.set_lighting_enable.m_enable = enable; + m_commands.push_back(command); +} + +void Renderer::CommandBuffer::StartRecording() {} diff --git a/Minecraft.Client/Windows64/4JLibs/inc/Render/RendererCore.cpp b/Minecraft.Client/Windows64/4JLibs/inc/Render/RendererCore.cpp new file mode 100644 index 000000000..0cbf944bb --- /dev/null +++ b/Minecraft.Client/Windows64/4JLibs/inc/Render/RendererCore.cpp @@ -0,0 +1,1049 @@ +/* +MIT License + +Copyright (c) 2026 Patoke + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +#include "stdafx.h" +#include "Renderer.h" +#include "CompiledShaders.h" + +Renderer InternalRenderManager; + +DWORD Renderer::tlsIdx = TlsAlloc(); +_RTL_CRITICAL_SECTION Renderer::totalAllocCS = {}; + +DWORD Renderer::s_auiWidths[] = { 1920, 512, 256, 128, 64 }; +DWORD Renderer::s_auiHeights[] = { 1080, 512, 256, 128, 64 }; +int Renderer::totalAlloc = 0; +const float Renderer::PI = 3.14159274f; + +D3D11_INPUT_ELEMENT_DESC g_vertex_PTN_Elements_PF3_TF2_CB4_NB4_XW1[] = { + {"POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0}, + {"TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 12, D3D11_INPUT_PER_VERTEX_DATA, 0}, + {"COLOR", 0, DXGI_FORMAT_R8G8B8A8_UNORM, 0, 20, D3D11_INPUT_PER_VERTEX_DATA, 0}, + {"NORMAL", 0, DXGI_FORMAT_R8G8B8A8_SNORM, 0, 24, D3D11_INPUT_PER_VERTEX_DATA, 0}, + {"TEXCOORD", 1, DXGI_FORMAT_R16G16_SINT, 0, 28, D3D11_INPUT_PER_VERTEX_DATA, 0}, +}; + +D3D11_INPUT_ELEMENT_DESC g_vertex_PTN_Elements_Compressed[] = { + {"POSITION", 0, DXGI_FORMAT_R16G16B16A16_SINT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0}, + {"TEXCOORD", 0, DXGI_FORMAT_R16G16B16A16_SINT, 0, 8, D3D11_INPUT_PER_VERTEX_DATA, 0}, +}; + +D3D11_PRIMITIVE_TOPOLOGY Renderer::g_topologies[C4JRender::PRIMITIVE_TYPE_COUNT] = { + D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST, D3D11_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP, D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST, + D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST, D3D11_PRIMITIVE_TOPOLOGY_LINELIST, D3D11_PRIMITIVE_TOPOLOGY_LINESTRIP, +}; + +static const unsigned int kVertexBufferSize = 0x100000; +static const unsigned int kScreenGrabWidth = 1920; +static const unsigned int kScreenGrabHeight = 1080; +static const unsigned int kThumbnailSize = 64; + +static const unsigned int g_vertexStrides[C4JRender::VERTEX_TYPE_COUNT] = { 32, 16, 32, 32 }; + +Renderer::Context::Context(ID3D11Device* device, ID3D11DeviceContext* deviceContext) + : m_pDeviceContext(deviceContext) + , userAnnotation(NULL) + , annotateDepth(0) + , stackType(0) + , textureIdx(0) + , faceCullEnabled(true) + , depthTestEnabled(true) + , depthWriteEnabled(true) + , alphaTestEnabled(false) + , alphaReference(1.0f) + , fogEnabled(false) + , fogNearDistance(0.0f) + , fogFarDistance(0.0f) + , fogDensity(0.0f) + , fogColourRed(0.0f) + , fogColourGreen(0.0f) + , fogColourBlue(0.0f) + , fogMode(0) + , lightingEnabled(false) + , lightingDirty(false) + , forcedLOD(-1) + , m_modelViewMatrix(NULL) + , m_localTransformMatrix(NULL) + , m_projectionMatrix(NULL) + , m_textureMatrix(NULL) + , m_vertexTexcoordBuffer(NULL) + , m_fogParamsBuffer(NULL) + , m_lightingStateBuffer(NULL) + , m_texGenMatricesBuffer(NULL) + , m_compressedTranslationBuffer(NULL) + , m_thumbnailBoundsBuffer(NULL) + , m_tintColorBuffer(NULL) + , m_fogColourBuffer(NULL) + , m_unkColorBuffer(NULL) + , m_alphaTestBuffer(NULL) + , m_clearColorBuffer(NULL) + , m_forcedLODBuffer(NULL) + , dynamicVertexBase(0) + , dynamicVertexOffset(0) + , dynamicVertexBuffer(NULL) + , commandBuffer(NULL) + , recordingBufferIndex(0) + , recordingVertexType(0) + , recordingPrimitiveType(0) + , deferredModeEnabled(false) + , deferredBuffers() +{ + deviceContext->QueryInterface(IID_PPV_ARGS(&userAnnotation)); + std::memset(matrixStacks, 0, sizeof(matrixStacks)); + std::memset(matrixDirty, 0, sizeof(matrixDirty)); + std::memset(stackPos, 0, sizeof(stackPos)); + std::memset(lightEnabled, 0, sizeof(lightEnabled)); + std::memset(lightDirection, 0, sizeof(lightDirection)); + std::memset(lightColour, 0, sizeof(lightColour)); + std::memset(&lightAmbientColour, 0, sizeof(lightAmbientColour)); + std::memset(texGenMatrices, 0, sizeof(texGenMatrices)); + std::memset(&blendDesc, 0, sizeof(blendDesc)); + std::memset(&depthStencilDesc, 0, sizeof(depthStencilDesc)); + std::memset(&rasterizerDesc, 0, sizeof(rasterizerDesc)); + blendFactor[0] = 0.0f; + blendFactor[1] = 0.0f; + blendFactor[2] = 0.0f; + blendFactor[3] = 0.0f; + + const DirectX::XMMATRIX identity = DirectX::XMMatrixIdentity(); + for (UINT i = 0; i < MATRIX_MODE_MODELVIEW_MAX; ++i) + { + matrixStacks[i][0] = identity; + stackPos[i] = 0; + } + + blendDesc.AlphaToCoverageEnable = false; + blendDesc.IndependentBlendEnable = false; + blendDesc.RenderTarget[0].BlendEnable = false; + blendDesc.RenderTarget[0].SrcBlend = D3D11_BLEND_ONE; + blendDesc.RenderTarget[0].DestBlend = D3D11_BLEND_ZERO; + blendDesc.RenderTarget[0].BlendOp = D3D11_BLEND_OP_ADD; + blendDesc.RenderTarget[0].SrcBlendAlpha = D3D11_BLEND_ONE; + blendDesc.RenderTarget[0].DestBlendAlpha = D3D11_BLEND_ZERO; + blendDesc.RenderTarget[0].BlendOpAlpha = D3D11_BLEND_OP_ADD; + blendDesc.RenderTarget[0].RenderTargetWriteMask = D3D11_COLOR_WRITE_ENABLE_ALL; + + depthStencilDesc.DepthEnable = true; + depthStencilDesc.DepthWriteMask = D3D11_DEPTH_WRITE_MASK_ALL; + depthStencilDesc.DepthFunc = D3D11_COMPARISON_LESS; + depthStencilDesc.StencilEnable = false; + depthStencilDesc.StencilReadMask = 0xFF; + depthStencilDesc.StencilWriteMask = 0xFF; + depthStencilDesc.FrontFace.StencilFailOp = D3D11_STENCIL_OP_KEEP; + depthStencilDesc.FrontFace.StencilDepthFailOp = D3D11_STENCIL_OP_KEEP; + depthStencilDesc.FrontFace.StencilPassOp = D3D11_STENCIL_OP_KEEP; + depthStencilDesc.FrontFace.StencilFunc = D3D11_COMPARISON_ALWAYS; + depthStencilDesc.BackFace.StencilFailOp = D3D11_STENCIL_OP_KEEP; + depthStencilDesc.BackFace.StencilDepthFailOp = D3D11_STENCIL_OP_KEEP; + depthStencilDesc.BackFace.StencilPassOp = D3D11_STENCIL_OP_KEEP; + depthStencilDesc.BackFace.StencilFunc = D3D11_COMPARISON_ALWAYS; + + rasterizerDesc.FillMode = D3D11_FILL_SOLID; + rasterizerDesc.CullMode = D3D11_CULL_BACK; + rasterizerDesc.FrontCounterClockwise = true; + rasterizerDesc.DepthBias = 0; + rasterizerDesc.DepthBiasClamp = 0.0f; + rasterizerDesc.SlopeScaledDepthBias = 0.0f; + rasterizerDesc.DepthClipEnable = true; + rasterizerDesc.ScissorEnable = false; + rasterizerDesc.MultisampleEnable = true; + rasterizerDesc.AntialiasedLineEnable = false; + + std::memset(lightDirection, 0, sizeof(lightDirection)); + std::memset(lightColour, 0, sizeof(lightColour)); + std::memset(&lightAmbientColour, 0, sizeof(lightAmbientColour)); + std::memset(texGenMatrices, 0, sizeof(texGenMatrices)); + + const float zero4[4] = {0.0f, 0.0f, 0.0f, 0.0f}; + const float one4[4] = {1.0f, 1.0f, 1.0f, 1.0f}; + const float alpha4[4] = {0.0f, 0.0f, 0.0f, 1.0f}; + + D3D11_BUFFER_DESC cbDesc = {}; + cbDesc.Usage = D3D11_USAGE_DYNAMIC; + cbDesc.BindFlags = D3D11_BIND_CONSTANT_BUFFER; + cbDesc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE; + + D3D11_SUBRESOURCE_DATA cbData = {}; + cbDesc.ByteWidth = sizeof(DirectX::XMMATRIX); + cbData.pSysMem = &identity; + device->CreateBuffer(&cbDesc, &cbData, &m_modelViewMatrix); + device->CreateBuffer(&cbDesc, &cbData, &m_localTransformMatrix); + device->CreateBuffer(&cbDesc, &cbData, &m_projectionMatrix); + device->CreateBuffer(&cbDesc, &cbData, &m_textureMatrix); + + cbDesc.ByteWidth = sizeof(zero4); + cbData.pSysMem = zero4; + device->CreateBuffer(&cbDesc, &cbData, &m_vertexTexcoordBuffer); + device->CreateBuffer(&cbDesc, &cbData, &m_fogParamsBuffer); + + const UINT lightingBytes = sizeof(lightDirection) + sizeof(lightColour) + sizeof(lightAmbientColour); + cbDesc.ByteWidth = lightingBytes; + cbData.pSysMem = lightDirection; + device->CreateBuffer(&cbDesc, &cbData, &m_lightingStateBuffer); + + cbDesc.ByteWidth = sizeof(texGenMatrices); + cbData.pSysMem = texGenMatrices; + device->CreateBuffer(&cbDesc, &cbData, &m_texGenMatricesBuffer); + + cbDesc.ByteWidth = sizeof(zero4); + cbData.pSysMem = zero4; + device->CreateBuffer(&cbDesc, &cbData, &m_compressedTranslationBuffer); + device->CreateBuffer(&cbDesc, &cbData, &m_thumbnailBoundsBuffer); + + cbDesc.ByteWidth = sizeof(one4); + cbData.pSysMem = one4; + device->CreateBuffer(&cbDesc, &cbData, &m_tintColorBuffer); + device->CreateBuffer(&cbDesc, &cbData, &m_fogColourBuffer); + device->CreateBuffer(&cbDesc, &cbData, &m_unkColorBuffer); + + cbDesc.ByteWidth = sizeof(alpha4); + cbData.pSysMem = alpha4; + device->CreateBuffer(&cbDesc, &cbData, &m_alphaTestBuffer); + + cbDesc.ByteWidth = sizeof(zero4); + cbData.pSysMem = zero4; + device->CreateBuffer(&cbDesc, &cbData, &m_clearColorBuffer); + device->CreateBuffer(&cbDesc, &cbData, &m_forcedLODBuffer); + + deviceContext->VSSetConstantBuffers(0, 10, &m_modelViewMatrix); + deviceContext->PSSetConstantBuffers(0, 6, &m_tintColorBuffer); + + { + void *dynamicVertexPtr = operator new[](kVertexBufferSize); + dynamicVertexBase = reinterpret_cast(dynamicVertexPtr); + } + dynamicVertexOffset = 0; + + D3D11_BUFFER_DESC vbDesc = {}; + vbDesc.ByteWidth = kVertexBufferSize; + vbDesc.Usage = D3D11_USAGE_DYNAMIC; + vbDesc.BindFlags = D3D11_BIND_VERTEX_BUFFER; + vbDesc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE; + device->CreateBuffer(&vbDesc, NULL, &dynamicVertexBuffer); +} + +void Renderer::BeginConditionalRendering(int) {} + +void Renderer::BeginConditionalSurvey(int) {} + +void Renderer::BeginEvent(LPCWSTR eventName) +{ + Renderer::Context *c = reinterpret_cast(TlsGetValue(Renderer::tlsIdx)); + if (c && c->m_pDeviceContext->GetType() != D3D11_DEVICE_CONTEXT_DEFERRED) + { + c->userAnnotation->BeginEvent(eventName); + ++c->annotateDepth; + } +} + +void Renderer::CaptureScreen(ImageFileBuffer *, XSOCIAL_PREVIEWIMAGE *) {} + +void Renderer::CaptureThumbnail(ImageFileBuffer *pngOut) +{ + Renderer::Context &c = getContext(); + + // @Patoke fix: bind render target to a proper backbuffer texture + ID3D11Resource *actualBackBuffer = NULL; + mainRenderTargetView->GetResource(&actualBackBuffer); + + // copy the backbuffer contents + c.m_pDeviceContext->CopyResource(m_backBufferTexture, actualBackBuffer); + actualBackBuffer->Release(); + + float left; + float bottom; + float right; + float top; + + switch (m_ViewportType) + { + case C4JRender::VIEWPORT_TYPE_FULLSCREEN: + left = 0.0f; + bottom = 0.0f; + right = 1.0f; + top = 1.0f; + break; + case C4JRender::VIEWPORT_TYPE_SPLIT_TOP: + left = 0.0f; + bottom = 0.0f; + right = 1.0f; + top = 0.5f; + break; + case C4JRender::VIEWPORT_TYPE_SPLIT_BOTTOM: + left = 0.0f; + bottom = 0.5f; + right = 1.0f; + top = 1.0f; + break; + case C4JRender::VIEWPORT_TYPE_SPLIT_LEFT: + left = 0.0f; + bottom = 0.0f; + right = 0.5f; + top = 1.0f; + break; + case C4JRender::VIEWPORT_TYPE_SPLIT_RIGHT: + left = 0.0f; + bottom = 0.0f; + right = 0.5f; + top = 1.0f; + break; + case C4JRender::VIEWPORT_TYPE_QUADRANT_TOP_LEFT: + left = 0.0f; + bottom = 0.0f; + right = 0.5f; + top = 0.5f; + break; + case C4JRender::VIEWPORT_TYPE_QUADRANT_TOP_RIGHT: + left = 0.5f; + bottom = 0.0f; + right = 1.0f; + top = 0.5f; + break; + case C4JRender::VIEWPORT_TYPE_QUADRANT_BOTTOM_LEFT: + left = 0.0f; + right = 0.5f; + bottom = 0.5f; + top = 1.0f; + break; + case C4JRender::VIEWPORT_TYPE_QUADRANT_BOTTOM_RIGHT: + left = 0.5f; + right = 1.0f; + bottom = 0.5f; + top = 1.0f; + break; + default: + break; + } + + float aspectRatio = IsWidescreen() ? (16.0f / 9.0f) : (4.0f / 3.0f); + + right *= aspectRatio; + left *= aspectRatio; + + float width = right - left; + float height = top - bottom; + + if (height > width) + { + float diff = (height - width) * 0.5f; + bottom += diff; + top -= diff; + } + else + { + float diff = (width - height) * 0.5f; + left += diff; + right -= diff; + } + + left /= aspectRatio; + right /= aspectRatio; + + ID3D11BlendState *blendState = NULL; + ID3D11DepthStencilState *depthState = NULL; + ID3D11RasterizerState *rasterizerState = NULL; + ID3D11SamplerState *samplerState = NULL; + ID3D11Texture2D *stagingTexture = NULL; + + D3D11_BLEND_DESC blendDesc = {}; + blendDesc.RenderTarget[0].BlendEnable = false; + blendDesc.RenderTarget[0].SrcBlend = D3D11_BLEND_ONE; + blendDesc.RenderTarget[0].DestBlend = D3D11_BLEND_ZERO; + blendDesc.RenderTarget[0].BlendOp = D3D11_BLEND_OP_ADD; + blendDesc.RenderTarget[0].SrcBlendAlpha = D3D11_BLEND_ONE; + blendDesc.RenderTarget[0].DestBlendAlpha = D3D11_BLEND_ZERO; + blendDesc.RenderTarget[0].BlendOpAlpha = D3D11_BLEND_OP_ADD; + blendDesc.RenderTarget[0].RenderTargetWriteMask = D3D11_COLOR_WRITE_ENABLE_ALL; + m_pDevice->CreateBlendState(&blendDesc, &blendState); + + D3D11_DEPTH_STENCIL_DESC depthDesc = {}; + depthDesc.DepthEnable = false; + depthDesc.DepthWriteMask = D3D11_DEPTH_WRITE_MASK_ZERO; + depthDesc.DepthFunc = D3D11_COMPARISON_ALWAYS; + depthDesc.StencilEnable = false; + depthDesc.StencilReadMask = 0xFF; + depthDesc.StencilWriteMask = 0xFF; + depthDesc.FrontFace.StencilFailOp = D3D11_STENCIL_OP_KEEP; + depthDesc.FrontFace.StencilDepthFailOp = D3D11_STENCIL_OP_KEEP; + depthDesc.FrontFace.StencilPassOp = D3D11_STENCIL_OP_KEEP; + depthDesc.FrontFace.StencilFunc = D3D11_COMPARISON_ALWAYS; + depthDesc.BackFace = depthDesc.FrontFace; + m_pDevice->CreateDepthStencilState(&depthDesc, &depthState); + + D3D11_RASTERIZER_DESC rasterDesc = {}; + rasterDesc.FillMode = D3D11_FILL_SOLID; + rasterDesc.CullMode = D3D11_CULL_NONE; + rasterDesc.DepthClipEnable = true; + rasterDesc.MultisampleEnable = true; + m_pDevice->CreateRasterizerState(&rasterDesc, &rasterizerState); + + D3D11_SAMPLER_DESC samplerDesc = {}; + samplerDesc.Filter = D3D11_FILTER_MIN_MAG_MIP_LINEAR; + samplerDesc.AddressU = D3D11_TEXTURE_ADDRESS_CLAMP; + samplerDesc.AddressV = D3D11_TEXTURE_ADDRESS_CLAMP; + samplerDesc.AddressW = D3D11_TEXTURE_ADDRESS_CLAMP; + samplerDesc.MaxAnisotropy = 16; + samplerDesc.ComparisonFunc = D3D11_COMPARISON_ALWAYS; + samplerDesc.MinLOD = -(std::numeric_limits::max)(); + samplerDesc.MaxLOD = (std::numeric_limits::max)(); + m_pDevice->CreateSamplerState(&samplerDesc, &samplerState); + + c.m_pDeviceContext->VSSetShader(screenSpaceVertexShader, NULL, 0); + c.m_pDeviceContext->IASetInputLayout(NULL); + c.m_pDeviceContext->PSSetShader(screenSpacePixelShader, NULL, 0); + c.m_pDeviceContext->OMSetBlendState(blendState, NULL, -1); + c.m_pDeviceContext->OMSetDepthStencilState(depthState, 0); + c.m_pDeviceContext->RSSetState(rasterizerState); + blendState->Release(); + depthState->Release(); + rasterizerState->Release(); + + // @Patoke add: just to make sure, set the render target shader resource to null to avoid any potential read/write hazards + ID3D11ShaderResourceView *nullSRV[1] = {nullptr}; + c.m_pDeviceContext->PSSetShaderResources(0, 1, nullSRV); + + for (UINT i = 0; i < NUM_THUMBNAIL_DOWNSAMPLES; ++i) + { + D3D11_VIEWPORT viewport = {}; + viewport.TopLeftX = 0.0f; + viewport.TopLeftY = 0.0f; + viewport.Width = (float)s_auiWidths[i + 1]; + viewport.Height = (float)s_auiHeights[i + 1]; + viewport.MinDepth = 0.0f; + viewport.MaxDepth = 1.0f; + + c.m_pDeviceContext->PSSetShaderResources(0, 1, nullSRV); + + c.m_pDeviceContext->OMSetRenderTargets(1, &downSampleRenderTargetViews[i], NULL); + c.m_pDeviceContext->RSSetViewports(1, &viewport); + + ID3D11ShaderResourceView *inputTexture = (i == 0) ? mainShaderResourceView : downSampleShaderResourceViews[i - 1]; + c.m_pDeviceContext->PSSetShaderResources(0, 1, &inputTexture); + c.m_pDeviceContext->PSSetSamplers(0, 1, &samplerState); + c.m_pDeviceContext->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP); + + D3D11_MAPPED_SUBRESOURCE mapped = {}; + c.m_pDeviceContext->Map(c.m_thumbnailBoundsBuffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &mapped); + + float *constants = static_cast(mapped.pData); + // @Patoke fix: the shader code zooms by 2x every iteration, so we keep zooming in over and over again if we use 1.0f, so we adjust by + // doing 2.0f like a boss + if (i == 0) + { + constants[0] = left; + constants[1] = bottom; + constants[2] = (right - left) * 2.0f; + constants[3] = (top - bottom) * 2.0f; + } + else + { + constants[0] = 0.0f; + constants[1] = 0.0f; + constants[2] = 2.0f; + constants[3] = 2.0f; + } + + c.m_pDeviceContext->Unmap(c.m_thumbnailBoundsBuffer, 0); + + // @Patoke fix: the shader expects the bounds buffer to be at slot 9 for vertex shader and slot 0 for pixel shader, so we need to set it there + // instead of the usual slot 0 + c.m_pDeviceContext->VSSetConstantBuffers(9, 1, &c.m_thumbnailBoundsBuffer); + c.m_pDeviceContext->PSSetConstantBuffers(0, 1, &c.m_thumbnailBoundsBuffer); + + c.m_pDeviceContext->Draw(4, 0); + } + + D3D11_TEXTURE2D_DESC texDesc = {}; + downSampleTextures[THUMBNAIL_DOWNSAMPLE_QUALITY_3]->GetDesc(&texDesc); + texDesc.Usage = D3D11_USAGE_STAGING; + texDesc.BindFlags = 0; + texDesc.CPUAccessFlags = D3D11_CPU_ACCESS_READ | D3D11_CPU_ACCESS_WRITE; + texDesc.MiscFlags = 0; + m_pDevice->CreateTexture2D(&texDesc, NULL, &stagingTexture); + + const unsigned int stride = kThumbnailSize * 4; + unsigned char *linearData = new unsigned char[kThumbnailSize * stride]; + + if (stagingTexture) + { + c.m_pDeviceContext->CopyResource(stagingTexture, downSampleTextures[THUMBNAIL_DOWNSAMPLE_QUALITY_3]); + + D3D11_MAPPED_SUBRESOURCE mapped = {}; + if (SUCCEEDED(c.m_pDeviceContext->Map(stagingTexture, 0, D3D11_MAP_READ, 0, &mapped))) + { + for (UINT y = 0; y < kThumbnailSize; ++y) + { + unsigned char *dstRow = linearData + (y * stride); + const unsigned char *srcRow = reinterpret_cast(mapped.pData) + (y * mapped.RowPitch); + + std::memcpy(dstRow, srcRow, stride); + for (UINT x = 0; x < kThumbnailSize; ++x) + { + dstRow[(x * 4) + 3] = 0xFF; + } + } + c.m_pDeviceContext->Unmap(stagingTexture, 0); + } + } + + ConvertLinearToPng(pngOut, linearData, kThumbnailSize, kThumbnailSize); + delete[] linearData; + + stagingTexture->Release(); + samplerState->Release(); + + ID3D11BlendState *restoredBlendState = NULL; + ID3D11DepthStencilState *restoredDepthState = NULL; + ID3D11RasterizerState *restoredRasterizerState = NULL; + + m_pDevice->CreateBlendState(&c.blendDesc, &restoredBlendState); + c.m_pDeviceContext->OMSetBlendState(restoredBlendState, c.blendFactor, 0xFFFFFFFF); + restoredBlendState->Release(); + + m_pDevice->CreateDepthStencilState(&c.depthStencilDesc, &restoredDepthState); + c.m_pDeviceContext->OMSetDepthStencilState(restoredDepthState, 0); + restoredDepthState->Release(); + + m_pDevice->CreateRasterizerState(&c.rasterizerDesc, &restoredRasterizerState); + c.m_pDeviceContext->RSSetState(restoredRasterizerState); + restoredRasterizerState->Release(); + + D3D11_VIEWPORT viewport = {}; + viewport.TopLeftX = 0.0f; + viewport.TopLeftY = 0.0f; + viewport.Width = static_cast(backBufferWidth); + viewport.Height = static_cast(backBufferHeight); + viewport.MinDepth = 0.0f; + viewport.MaxDepth = 1.0f; + + c.m_pDeviceContext->RSSetViewports(1, &viewport); + c.m_pDeviceContext->OMSetRenderTargets(1, &mainRenderTargetView, depthStencilView); + + activeVertexType = -1; + activePixelType = -1; +} + +void Renderer::Clear(int flags, D3D11_RECT *) +{ + PROFILER_SCOPE("Renderer::Clear", "Clear", MP_MAGENTA); + + Renderer::Context *c = reinterpret_cast(TlsGetValue(Renderer::tlsIdx)); + unsigned char clearFlags = static_cast(flags); + + ID3D11BlendState *blendState = NULL; + ID3D11DepthStencilState *depthState = NULL; + ID3D11RasterizerState *rasterizerState = NULL; + + PROFILER_SCOPE("Renderer::Clear", "Blend", MP_MAGENTA); + D3D11_BLEND_DESC blendDesc = {}; + blendDesc.AlphaToCoverageEnable = false; + blendDesc.IndependentBlendEnable = false; + blendDesc.RenderTarget[0].BlendEnable = false; + blendDesc.RenderTarget[0].SrcBlend = D3D11_BLEND_ONE; + blendDesc.RenderTarget[0].DestBlend = D3D11_BLEND_ZERO; + blendDesc.RenderTarget[0].BlendOp = D3D11_BLEND_OP_ADD; + blendDesc.RenderTarget[0].SrcBlendAlpha = D3D11_BLEND_ONE; + blendDesc.RenderTarget[0].DestBlendAlpha = D3D11_BLEND_ZERO; + blendDesc.RenderTarget[0].BlendOpAlpha = D3D11_BLEND_OP_ADD; + blendDesc.RenderTarget[0].RenderTargetWriteMask = (clearFlags & CLEAR_COLOUR_FLAG) ? D3D11_COLOR_WRITE_ENABLE_ALL : 0; + m_pDevice->CreateBlendState(&blendDesc, &blendState); + + PROFILER_SCOPE("Renderer::Clear", "Depth", MP_MAGENTA); + D3D11_DEPTH_STENCIL_DESC depthDesc = {}; + depthDesc.DepthEnable = (clearFlags & CLEAR_DEPTH_FLAG) ? true : false; + depthDesc.DepthWriteMask = depthDesc.DepthEnable ? D3D11_DEPTH_WRITE_MASK_ALL : D3D11_DEPTH_WRITE_MASK_ZERO; + depthDesc.DepthFunc = D3D11_COMPARISON_ALWAYS; + depthDesc.StencilEnable = false; + depthDesc.StencilReadMask = 0xFF; + depthDesc.StencilWriteMask = 0xFF; + depthDesc.FrontFace.StencilFailOp = D3D11_STENCIL_OP_KEEP; + depthDesc.FrontFace.StencilDepthFailOp = D3D11_STENCIL_OP_KEEP; + depthDesc.FrontFace.StencilPassOp = D3D11_STENCIL_OP_KEEP; + depthDesc.FrontFace.StencilFunc = D3D11_COMPARISON_ALWAYS; + depthDesc.BackFace.StencilFailOp = D3D11_STENCIL_OP_KEEP; + depthDesc.BackFace.StencilDepthFailOp = D3D11_STENCIL_OP_KEEP; + depthDesc.BackFace.StencilPassOp = D3D11_STENCIL_OP_KEEP; + depthDesc.BackFace.StencilFunc = D3D11_COMPARISON_ALWAYS; + m_pDevice->CreateDepthStencilState(&depthDesc, &depthState); + + PROFILER_SCOPE("Renderer::Clear", "Rasterizer", MP_MAGENTA); + D3D11_RASTERIZER_DESC rasterDesc = {}; + rasterDesc.FillMode = D3D11_FILL_SOLID; + rasterDesc.CullMode = D3D11_CULL_NONE; + rasterDesc.DepthClipEnable = true; + rasterDesc.MultisampleEnable = true; + m_pDevice->CreateRasterizerState(&rasterDesc, &rasterizerState); + + PROFILER_SCOPE("Renderer::Clear", "DrawClearQuad", MP_MAGENTA); + m_pDeviceContext->VSSetShader(screenClearVertexShader, NULL, 0); + m_pDeviceContext->IASetInputLayout(NULL); + m_pDeviceContext->PSSetShader(screenClearPixelShader, NULL, 0); + m_pDeviceContext->OMSetBlendState(blendState, NULL, 0xFFFFFFFF); + m_pDeviceContext->OMSetDepthStencilState(depthState, 0); + m_pDeviceContext->RSSetState(rasterizerState); + c->m_pDeviceContext->PSSetShaderResources(0, 0, NULL); + c->m_pDeviceContext->IASetVertexBuffers(0, 0, NULL, NULL, NULL); + m_pDeviceContext->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP); + m_pDeviceContext->Draw(4, 0); + + blendState->Release(); + depthState->Release(); + rasterizerState->Release(); + + ID3D11BlendState *restoredBlendState = NULL; + ID3D11DepthStencilState *restoredDepthState = NULL; + ID3D11RasterizerState *restoredRasterizerState = NULL; + + m_pDevice->CreateBlendState(&c->blendDesc, &restoredBlendState); + m_pDeviceContext->OMSetBlendState(restoredBlendState, c->blendFactor, 0xFFFFFFFF); + restoredBlendState->Release(); + + m_pDevice->CreateDepthStencilState(&c->depthStencilDesc, &restoredDepthState); + m_pDeviceContext->OMSetDepthStencilState(restoredDepthState, 0); + restoredDepthState->Release(); + + m_pDevice->CreateRasterizerState(&c->rasterizerDesc, &restoredRasterizerState); + m_pDeviceContext->RSSetState(restoredRasterizerState); + restoredRasterizerState->Release(); + + activeVertexType = -1; + activePixelType = -1; +} + +void Renderer::ConvertLinearToPng(ImageFileBuffer *pngOut, unsigned char *linearData, unsigned int width, unsigned int height) +{ + const size_t dataSize = static_cast(width) * static_cast(height) * 4; + const size_t outputCapacity = (dataSize * 24) / 20 + 256; + + void *outputBuffer = std::malloc(outputCapacity); + int outputLength = 0; + + SaveTextureDataToMemory( + outputBuffer, + static_cast(outputCapacity), + &outputLength, + static_cast(width), + static_cast(height), + reinterpret_cast(linearData) + ); + + pngOut->m_type = ImageFileBuffer::e_typePNG; + pngOut->m_pBuffer = outputBuffer; + pngOut->m_bufferSize = outputLength; +} + +void Renderer::DoScreenGrabOnNextPresent() +{ + m_bShouldScreenGrabNextFrame = true; +} + +void Renderer::EndConditionalRendering() {} +void Renderer::EndConditionalSurvey() {} + +void Renderer::EndEvent() +{ + Renderer::Context *c = reinterpret_cast(TlsGetValue(Renderer::tlsIdx)); + if (c && c->m_pDeviceContext->GetType() != D3D11_DEVICE_CONTEXT_DEFERRED) + { + c->userAnnotation->EndEvent(); + if (--c->annotateDepth < 0) + c->annotateDepth = 0; + } +} + +void Renderer::Initialise(ID3D11Device *pDevice, IDXGISwapChain *pSwapChain) +{ + m_pDevice = pDevice; + m_pDeviceContext = InitialiseContext(true); + m_pSwapChain = pSwapChain; + + PROFILER_INIT(); + + m_vertexIdxToBufferIdx = new int16_t[NUM_COMMAND_HANDLES]; + m_commandBuffers = new CommandBuffer *[MAX_COMMAND_BUFFERS]; + m_commandMatrices = new DirectX::XMMATRIX[MAX_COMMAND_BUFFERS]; + m_bufferIdxToVertexIdx = new int[MAX_COMMAND_BUFFERS]; + m_commandPrimitiveTypes = new uint8_t[MAX_COMMAND_BUFFERS]; + m_commandVertexTypes = new uint8_t[MAX_COMMAND_BUFFERS]; + + std::memset(m_vertexIdxToBufferIdx, 0xFF, NUM_COMMAND_HANDLES * sizeof(int16_t)); + std::memset(m_commandBuffers, 0, MAX_COMMAND_BUFFERS * sizeof(CommandBuffer *)); + std::memset(m_bufferIdxToVertexIdx, 0, MAX_COMMAND_BUFFERS * sizeof(int)); + std::memset(m_commandPrimitiveTypes, 0, MAX_COMMAND_BUFFERS * sizeof(uint8_t)); + std::memset(m_commandVertexTypes, 0, MAX_COMMAND_BUFFERS * sizeof(uint8_t)); + + m_numBuffersToDeallocate = 0; + m_bShouldScreenGrabNextFrame = false; + + SetupShaders(); + const float clearColour[4] = {0.0f, 0.0f, 0.0f, 0.0f}; + SetClearColour(clearColour); + + m_pDeviceContext->OMGetRenderTargets(1, &mainRenderTargetView, &depthStencilView); + + D3D11_RENDER_TARGET_VIEW_DESC rtvDesc = {}; + std::memset(&rtvDesc, 0, sizeof(rtvDesc)); + rtvDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; + rtvDesc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2D; + rtvDesc.Texture2D.MipSlice = 0; + + D3D11_SHADER_RESOURCE_VIEW_DESC srvDesc = {}; + std::memset(&srvDesc, 0, sizeof(srvDesc)); + srvDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; + srvDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D; + srvDesc.Texture2D.MostDetailedMip = 0; + srvDesc.Texture2D.MipLevels = 1; + + ID3D11Resource *backBufferResource = NULL; + ID3D11Texture2D *backBufferTexture = NULL; + mainRenderTargetView->GetResource(&backBufferResource); + backBufferResource->QueryInterface(IID_PPV_ARGS(&backBufferTexture)); + + D3D11_TEXTURE2D_DESC backDesc = {}; + backBufferTexture->GetDesc(&backDesc); + backBufferWidth = backDesc.Width; + backBufferHeight = backDesc.Height; + + // @Patoke fix: we can't bind the backbuffer directly as a shader resource, so we create a new texture with the same dimensions and format and + // copy the backbuffer contents to it, then we can bind that texture as a shader resource for the thumbnail generation + D3D11_TEXTURE2D_DESC safeDesc = backDesc; + safeDesc.BindFlags = D3D11_BIND_SHADER_RESOURCE; + safeDesc.Usage = D3D11_USAGE_DEFAULT; + safeDesc.SampleDesc.Count = 1; // no MSAA + safeDesc.SampleDesc.Quality = 0; + + m_pDevice->CreateTexture2D(&safeDesc, NULL, &m_backBufferTexture); + + m_pDevice->CreateShaderResourceView(m_backBufferTexture, NULL, &mainShaderResourceView); + + downSampleTextures[THUMBNAIL_MAX_QUALITY] = m_backBufferTexture; + + //m_pDevice->CreateRenderTargetView(backBufferTexture, &rtvDesc, &renderTargetView); + + backBufferTexture->Release(); + backBufferResource->Release(); + + D3D11_TEXTURE2D_DESC desc = {}; + desc.Width = 0; + desc.Height = 0; + desc.ArraySize = 1; + desc.MipLevels = 1; + desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; + desc.SampleDesc.Count = 1; + desc.SampleDesc.Quality = 0; + desc.Usage = D3D11_USAGE_DEFAULT; + desc.BindFlags = D3D11_BIND_RENDER_TARGET | D3D11_BIND_SHADER_RESOURCE; + desc.CPUAccessFlags = 0; + desc.MiscFlags = 0; + for (UINT i = 0; i < NUM_THUMBNAIL_DOWNSAMPLES; ++i) + { + desc.Width = s_auiWidths[i + 1]; + desc.Height = s_auiHeights[i + 1]; + + // @Patoke fix: before these would fail and our views would be nullptrs + m_pDevice->CreateTexture2D(&desc, NULL, &downSampleTextures[i]); + m_pDevice->CreateRenderTargetView(downSampleTextures[i], NULL, &downSampleRenderTargetViews[i]); + m_pDevice->CreateShaderResourceView(downSampleTextures[i], NULL, &downSampleShaderResourceViews[i]); + } + + std::memset(m_textures, 0, sizeof(m_textures)); + defaultTextureIndex = TextureCreate(); + TextureBind(defaultTextureIndex); + + unsigned char *defaultTextureData = new unsigned char[0x400]; + std::memset(defaultTextureData, 0xFF, 0x400); + TextureData(16, 16, defaultTextureData, 0, C4JRender::TEXTURE_FORMAT_RxGyBzAw); + delete[] defaultTextureData; + + presentCount = 0; + rendererFlag0 = 0; + reservedRendererWord0 = 10922; + StateSetViewport(C4JRender::VIEWPORT_TYPE_FULLSCREEN); + StateSetVertexTextureUV(0.0f, 0.0f); + TextureBindVertex(-1); + + InitializeCriticalSection(&m_commandBufferCS); + + reservedRendererDword1 = 0; + activeVertexType = -1; + reservedRendererByte1 = 1; + activePixelType = -1; + reservedRendererByte0 = 0; + + unsigned short *quadIndices = new unsigned short[0x18000]; + for (UINT i = 0; i < 0x4000; ++i) + { + unsigned short base = static_cast(i * 4); + unsigned int offset = i * 6; + quadIndices[offset + 0] = base; + quadIndices[offset + 1] = base + 1; + quadIndices[offset + 2] = base + 3; + quadIndices[offset + 3] = base + 1; + quadIndices[offset + 4] = base + 2; + quadIndices[offset + 5] = base + 3; + } + + D3D11_BUFFER_DESC quadIndexDesc = {}; + quadIndexDesc.ByteWidth = 0x30000; + quadIndexDesc.Usage = D3D11_USAGE_IMMUTABLE; + quadIndexDesc.BindFlags = D3D11_BIND_INDEX_BUFFER; + quadIndexDesc.CPUAccessFlags = 0; + quadIndexDesc.MiscFlags = 0; + + D3D11_SUBRESOURCE_DATA quadIndexData = {}; + quadIndexData.pSysMem = quadIndices; + HRESULT hr = m_pDevice->CreateBuffer(&quadIndexDesc, &quadIndexData, &quadIndexBuffer); + assert(hr >= 0); + delete[] quadIndices; + + unsigned short *fanIndices = new unsigned short[0x2FFFA]; + for (UINT i = 0; i < 65534; ++i) + { + unsigned int offset = i * 3; + fanIndices[offset + 0] = 0; + fanIndices[offset + 1] = static_cast(i + 1); + fanIndices[offset + 2] = static_cast(i + 2); + } + + D3D11_BUFFER_DESC fanIndexDesc = {}; + fanIndexDesc.ByteWidth = 0x5FFF4; + fanIndexDesc.Usage = D3D11_USAGE_IMMUTABLE; + fanIndexDesc.BindFlags = D3D11_BIND_INDEX_BUFFER; + + D3D11_SUBRESOURCE_DATA fanIndexData = {}; + fanIndexData.pSysMem = fanIndices; + m_pDevice->CreateBuffer(&fanIndexDesc, &fanIndexData, &fanIndexBuffer); + delete[] fanIndices; + + InitializeCriticalSection(&Renderer::totalAllocCS); +} + +ID3D11DeviceContext *Renderer::InitialiseContext(bool fromPresent) +{ + ID3D11DeviceContext *deviceContext = NULL; + + if (fromPresent) + m_pDevice->GetImmediateContext(&deviceContext); + else + m_pDevice->CreateDeferredContext(0, &deviceContext); + + Renderer::Context *c = new Renderer::Context(m_pDevice, deviceContext); + TlsSetValue(Renderer::tlsIdx, c); + + return deviceContext; +} + +bool Renderer::IsHiDef() +{ + return true; +} + +bool Renderer::IsWidescreen() +{ + return true; +} + +void Renderer::Present() +{ + PROFILER_SCOPE("Renderer::Present", "Present", MP_MAGENTA); + + if (m_bShouldScreenGrabNextFrame) + { + PROFILER_SCOPE("Renderer::Present", "ScreenGrab", MP_MAGENTA); + + unsigned char *linearData = new unsigned char[kScreenGrabWidth * kScreenGrabHeight * 4]; + + ID3D11Resource *backBufferResource = NULL; + ID3D11Texture2D *backBuffer = NULL; + ID3D11Texture2D *stagingTexture = NULL; + + mainRenderTargetView->GetResource(&backBufferResource); + backBufferResource->QueryInterface(IID_PPV_ARGS(&backBuffer)); + D3D11_TEXTURE2D_DESC desc = {}; + backBuffer->GetDesc(&desc); + desc.Usage = D3D11_USAGE_STAGING; + desc.BindFlags = 0; + desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ | D3D11_CPU_ACCESS_WRITE; + desc.MiscFlags = 0; + m_pDevice->CreateTexture2D(&desc, NULL, &stagingTexture); + backBufferResource->Release(); + + if (stagingTexture) + { + PROFILER_SCOPE("Renderer::Present", "CopyResource", MP_MAGENTA); + m_pDeviceContext->CopyResource(stagingTexture, backBuffer); + + D3D11_MAPPED_SUBRESOURCE mapped = {}; + m_pDeviceContext->Map(stagingTexture, 0, D3D11_MAP_READ_WRITE, 0, &mapped); + const unsigned char* src = reinterpret_cast(mapped.pData); + + for (UINT y = 0; y < kScreenGrabHeight; ++y) + { + unsigned char *dstRow = linearData + y * kScreenGrabWidth * 4; + const unsigned char *srcRow = src + y * mapped.RowPitch; + std::memcpy(dstRow, srcRow, kScreenGrabWidth * 4); + + for (UINT x = 0; x < kScreenGrabWidth; ++x) + dstRow[x * 4 + 3] = 0xFF; + } + + m_pDeviceContext->Unmap(stagingTexture, 0); + } + + // @Patoke add + char curDir[256]; + GetCurrentDirectoryA(sizeof(curDir), curDir); + + char ssPath[512]; + sprintf(ssPath, "%s/Windows64/GameHDD/", curDir); + + _SYSTEMTIME UTCSysTime; + GetSystemTime(&UTCSysTime); + + char fileName[304]; + sprintf(fileName, "%s/%4d-%02d-%02d-%02d-%02d-%02d.png", ssPath, UTCSysTime.wYear, UTCSysTime.wMonth, UTCSysTime.wDay, UTCSysTime.wHour, UTCSysTime.wMinute, + UTCSysTime.wSecond); + + D3DXIMAGE_INFO info; + info.Width = kScreenGrabWidth; + info.Height = kScreenGrabHeight; + SaveTextureData(fileName, &info, reinterpret_cast(linearData)); + + delete[] linearData; + + m_bShouldScreenGrabNextFrame = false; + } + + m_pSwapChain->Present(1, 0); + ++presentCount; +} + +void Renderer::Resume() +{ + m_bSuspended = false; +} + +void Renderer::SetClearColour(const float colourRGBA[4]) +{ + for (int i = 0; i < 4; ++i) + m_fClearColor[i] = colourRGBA[i]; + + Renderer::Context *c = reinterpret_cast(TlsGetValue(Renderer::tlsIdx)); + if (c) + { + D3D11_MAPPED_SUBRESOURCE mapped = {}; + c->m_pDeviceContext->Map(c->m_clearColorBuffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &mapped); + *(DirectX::XMVECTOR *)mapped.pData = DirectX::XMVectorSet(colourRGBA[0], colourRGBA[1], colourRGBA[2], colourRGBA[3]); + c->m_pDeviceContext->Unmap(c->m_clearColorBuffer, 0); + } +} + +void Renderer::SetupShaders() +{ + vertexShaderTable = new ID3D11VertexShader *[C4JRender::VERTEX_TYPE_COUNT]; + vertexStrideTable = new unsigned int[C4JRender::VERTEX_TYPE_COUNT]; + for (UINT i = 0; i < C4JRender::VERTEX_TYPE_COUNT; ++i) + vertexStrideTable[i] = g_vertexStrides[i]; + inputLayoutTable = new ID3D11InputLayout *[C4JRender::VERTEX_TYPE_COUNT]; + pixelShaderTable = new ID3D11PixelShader *[C4JRender::PIXEL_SHADER_COUNT]; + + m_pDevice->CreateVertexShader(g_main_VS_PF3_TF2_CB4_NB4_XW1, sizeof(g_main_VS_PF3_TF2_CB4_NB4_XW1), NULL, &vertexShaderTable[C4JRender::VERTEX_TYPE_PF3_TF2_CB4_NB4_XW1]); + m_pDevice->CreateVertexShader(g_main_VS_Compressed, sizeof(g_main_VS_Compressed), NULL, &vertexShaderTable[C4JRender::VERTEX_TYPE_COMPRESSED]); + m_pDevice->CreateVertexShader(g_main_VS_PF3_TF2_CB4_NB4_XW1_LIGHTING, sizeof(g_main_VS_PF3_TF2_CB4_NB4_XW1_LIGHTING), NULL, &vertexShaderTable[C4JRender::VERTEX_TYPE_PF3_TF2_CB4_NB4_XW1_LIT]); + m_pDevice->CreateVertexShader(g_main_VS_PF3_TF2_CB4_NB4_XW1_TEXGEN, sizeof(g_main_VS_PF3_TF2_CB4_NB4_XW1_TEXGEN), NULL, &vertexShaderTable[C4JRender::VERTEX_TYPE_PF3_TF2_CB4_NB4_XW1_TEXGEN]); + m_pDevice->CreateVertexShader(g_main_VS_ScreenSpace, sizeof(g_main_VS_ScreenSpace), NULL, &screenSpaceVertexShader); + m_pDevice->CreateVertexShader(g_main_VS_ScreenClear, sizeof(g_main_VS_ScreenClear), NULL, &screenClearVertexShader); + + m_pDevice->CreatePixelShader(g_main_PS_Standard, sizeof(g_main_PS_Standard), NULL, &pixelShaderTable[C4JRender::PIXEL_SHADER_TYPE_STANDARD]); + m_pDevice->CreatePixelShader(g_main_PS_TextureProjection, sizeof(g_main_PS_TextureProjection), NULL, &pixelShaderTable[C4JRender::PIXEL_SHADER_TYPE_PROJECTION]); + m_pDevice->CreatePixelShader(g_main_PS_ForceLOD, sizeof(g_main_PS_ForceLOD), NULL, &pixelShaderTable[C4JRender::PIXEL_SHADER_TYPE_FORCELOD]); + m_pDevice->CreatePixelShader(g_main_PS_ScreenSpace, sizeof(g_main_PS_ScreenSpace), NULL, &screenSpacePixelShader); + m_pDevice->CreatePixelShader(g_main_PS_ScreenClear, sizeof(g_main_PS_ScreenClear), NULL, &screenClearPixelShader); + + m_pDevice->CreateInputLayout(g_vertex_PTN_Elements_PF3_TF2_CB4_NB4_XW1, 5, g_main_VS_PF3_TF2_CB4_NB4_XW1, sizeof(g_main_VS_PF3_TF2_CB4_NB4_XW1), &inputLayoutTable[C4JRender::VERTEX_TYPE_PF3_TF2_CB4_NB4_XW1]); + m_pDevice->CreateInputLayout(g_vertex_PTN_Elements_Compressed, 2, g_main_VS_Compressed, sizeof(g_main_VS_Compressed), &inputLayoutTable[C4JRender::VERTEX_TYPE_COMPRESSED]); + m_pDevice->CreateInputLayout(g_vertex_PTN_Elements_PF3_TF2_CB4_NB4_XW1, 5, g_main_VS_PF3_TF2_CB4_NB4_XW1_LIGHTING, sizeof(g_main_VS_PF3_TF2_CB4_NB4_XW1_LIGHTING), &inputLayoutTable[C4JRender::VERTEX_TYPE_PF3_TF2_CB4_NB4_XW1_LIT]); + m_pDevice->CreateInputLayout(g_vertex_PTN_Elements_PF3_TF2_CB4_NB4_XW1, 5, g_main_VS_PF3_TF2_CB4_NB4_XW1_TEXGEN, sizeof(g_main_VS_PF3_TF2_CB4_NB4_XW1_TEXGEN), &inputLayoutTable[C4JRender::VERTEX_TYPE_PF3_TF2_CB4_NB4_XW1_TEXGEN]); +} + +void Renderer::StartFrame() +{ + PROFILER_SCOPE("Renderer::StartFrame", "StartFrame", MP_MAGENTA); + + Renderer::Context &c = getContext(); + + activeVertexType = -1; + activePixelType = -1; + + TextureBindVertex(-1); + TextureBind(-1); + + PROFILER_SCOPE("Renderer::StartFrame", "State", MP_MAGENTA); + + StateSetColour(1.0f, 1.0f, 1.0f, 1.0f); + StateSetDepthMask(true); + StateSetBlendEnable(true); + StateSetBlendFunc(D3D11_BLEND_SRC_ALPHA, D3D11_BLEND_INV_SRC_ALPHA); + StateSetBlendFactor(0xFFFFFFFF); + StateSetAlphaFunc(D3D11_COMPARISON_GREATER, 0.1f); + StateSetDepthFunc(D3D11_COMPARISON_LESS_EQUAL); + StateSetFaceCull(true); + StateSetLineWidth(1.0f); + StateSetWriteEnable(true, true, true, true); + StateSetDepthTestEnable(false); + StateSetAlphaTestEnable(true); + + c.m_pDeviceContext->VSSetConstantBuffers(0, 10, &c.m_modelViewMatrix); + c.m_pDeviceContext->PSSetConstantBuffers(0, 6, &c.m_tintColorBuffer); + + D3D11_VIEWPORT viewport = {}; + viewport.TopLeftX = 0.0f; + viewport.TopLeftY = 0.0f; + viewport.Width = static_cast(backBufferWidth); + viewport.Height = static_cast(backBufferHeight); + viewport.MinDepth = 0.0f; + viewport.MaxDepth = 1.0f; + c.m_pDeviceContext->RSSetViewports(1, &viewport); + c.m_pDeviceContext->OMSetRenderTargets(1, &mainRenderTargetView, depthStencilView); + + PROFILER_FLIP(); +} + +void Renderer::Suspend() +{ + m_bSuspended = true; +} + +bool Renderer::Suspended() +{ + return m_bSuspended; +} + +void Renderer::UpdateGamma(unsigned short) {} + +Renderer::Context &Renderer::getContext() +{ + return *reinterpret_cast(TlsGetValue(Renderer::tlsIdx)); +} + diff --git a/Minecraft.Client/Windows64/4JLibs/inc/Render/RendererMatrix.cpp b/Minecraft.Client/Windows64/4JLibs/inc/Render/RendererMatrix.cpp new file mode 100644 index 000000000..667edaad9 --- /dev/null +++ b/Minecraft.Client/Windows64/4JLibs/inc/Render/RendererMatrix.cpp @@ -0,0 +1,144 @@ +/* +MIT License + +Copyright (c) 2026 Patoke + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +#include "stdafx.h" +#include "Renderer.h" + +#include + +const float *Renderer::MatrixGet(int type) +{ + Context &c = getContext(); + const int depth = c.stackPos[type]; + const DirectX::XMMATRIX &matrix = c.matrixStacks[type][depth]; + return &matrix.r[0].m128_f32[0]; +} + +void Renderer::MatrixMode(int type) +{ + Context &c = getContext(); + assert(type >= 0); + assert(type < STACK_TYPES); + c.stackType = type; +} + +void Renderer::MatrixMult(float *mat) +{ + DirectX::XMMATRIX matrix; + std::memcpy(&matrix, mat, sizeof(matrix)); + MultWithStack(matrix); +} + +void Renderer::MatrixOrthogonal(float left, float right, float bottom, float top, float zNear, float zFar) +{ + const DirectX::XMMATRIX matrix = DirectX::XMMatrixOrthographicOffCenterRH(left, right, bottom, top, zNear, zFar); + MultWithStack(matrix); +} + +void Renderer::MatrixPerspective(float fovy, float aspect, float zNear, float zFar) +{ + const float fovRadians = fovy * (PI / 180.0f); + const DirectX::XMMATRIX matrix = DirectX::XMMatrixPerspectiveFovRH(fovRadians, aspect, zNear, zFar); + MultWithStack(matrix); +} + +void Renderer::MatrixPop() +{ + Context &c = getContext(); + + assert(c.stackPos[c.stackType] > 0); + + const int mode = c.stackType; + --c.stackPos[mode]; + c.matrixDirty[mode] = true; +} + +void Renderer::MatrixPush() +{ + Context &c = getContext(); + + assert(c.stackPos[c.stackType] < (STACK_SIZE - 1)); + + const int mode = c.stackType; + const int depth = c.stackPos[mode]; + c.matrixStacks[mode][depth + 1] = c.matrixStacks[mode][depth]; + ++c.stackPos[mode]; +} + +void Renderer::MatrixRotate(float angle, float x, float y, float z) +{ + const DirectX::XMVECTOR axis = DirectX::XMVectorSet(x, y, z, 0.0f); + const DirectX::XMMATRIX matrix = DirectX::XMMatrixRotationAxis(axis, angle); + MultWithStack(matrix); +} + +void Renderer::MatrixScale(float x, float y, float z) +{ + const DirectX::XMMATRIX matrix = DirectX::XMMatrixScaling(x, y, z); + MultWithStack(matrix); +} + +void Renderer::MatrixSetIdentity() +{ + Context &c = getContext(); + const int mode = c.stackType; + const int depth = c.stackPos[mode]; + c.matrixStacks[mode][depth] = DirectX::XMMatrixIdentity(); + c.matrixDirty[mode] = true; +} + +void Renderer::MatrixTranslate(float x, float y, float z) +{ + const DirectX::XMMATRIX matrix = DirectX::XMMatrixTranslation(x, y, z); + MultWithStack(matrix); +} + +void Renderer::MultWithStack(DirectX::XMMATRIX matrix) +{ + Context &c = getContext(); + const int mode = c.stackType; + const int depth = c.stackPos[mode]; + DirectX::XMMATRIX ¤t = c.matrixStacks[mode][depth]; + current = DirectX::XMMatrixMultiply(matrix, current); + c.matrixDirty[mode] = true; +} + +void Renderer::Set_matrixDirty() +{ + Context &c = getContext(); + const DirectX::XMMATRIX identity = DirectX::XMMatrixIdentity(); + + c.matrixStacks[MATRIX_MODE_MODELVIEW][0] = identity; + c.matrixStacks[MATRIX_MODE_MODELVIEW_PROJECTION][0] = identity; + c.matrixStacks[MATRIX_MODE_MODELVIEW_TEXTURE][0] = identity; + c.matrixStacks[MATRIX_MODE_MODELVIEW_CBUFF][0] = identity; + + c.matrixDirty[MATRIX_MODE_MODELVIEW] = true; + c.matrixDirty[MATRIX_MODE_MODELVIEW_PROJECTION] = true; + c.matrixDirty[MATRIX_MODE_MODELVIEW_TEXTURE] = true; + c.matrixDirty[MATRIX_MODE_MODELVIEW_CBUFF] = true; + + activeVertexType = -1; + activePixelType = -1; +} diff --git a/Minecraft.Client/Windows64/4JLibs/inc/Render/RendererState.cpp b/Minecraft.Client/Windows64/4JLibs/inc/Render/RendererState.cpp new file mode 100644 index 000000000..49ab0b52f --- /dev/null +++ b/Minecraft.Client/Windows64/4JLibs/inc/Render/RendererState.cpp @@ -0,0 +1,688 @@ +/* +MIT License + +Copyright (c) 2026 Patoke + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +#include "stdafx.h" +#include "Renderer.h" + +#include +#include + + +ID3D11BlendState *Renderer::GetManagedBlendState() +{ + PROFILER_SCOPE("Renderer::GetManagedBlendState", "GetManagedBlendState", MP_ORCHID1); + Context &c = getContext(); + const D3D11_RENDER_TARGET_BLEND_DESC &rtBlend = c.blendDesc.RenderTarget[0]; + + const int key = (rtBlend.BlendEnable ? 1 : 0) | ((static_cast(rtBlend.SrcBlend) & 0x1F) << 1) | + ((static_cast(rtBlend.DestBlend) & 0x1F) << 6) | ((static_cast(rtBlend.RenderTargetWriteMask) & 0x0F) << 11); + + auto it = managedBlendStates.find(key); + if (it != managedBlendStates.end()) + return it->second; + + ID3D11BlendState *state = NULL; + m_pDevice->CreateBlendState(&c.blendDesc, &state); + managedBlendStates.emplace(key, state); + return state; +} + + +ID3D11DepthStencilState *Renderer::GetManagedDepthStencilState() +{ + PROFILER_SCOPE("Renderer::GetManagedBlendState", "GetManagedDepthStencilState", MP_ORCHID1); + Context &c = getContext(); + + const int key = (c.depthStencilDesc.DepthEnable ? 2 : 0) | ((static_cast(c.depthStencilDesc.DepthFunc) & 0x0F) << 2) | + (c.depthStencilDesc.DepthWriteMask == D3D11_DEPTH_WRITE_MASK_ALL ? 1 : 0); + + auto it = managedDepthStencilStates.find(key); + if (it != managedDepthStencilStates.end()) + return it->second; + + ID3D11DepthStencilState *state = NULL; + m_pDevice->CreateDepthStencilState(&c.depthStencilDesc, &state); + managedDepthStencilStates.emplace(key, state); + return state; +} + + +ID3D11RasterizerState *Renderer::GetManagedRasterizerState() +{ + PROFILER_SCOPE("Renderer::GetManagedRasterizerState", "GetManagedRasterizerState", MP_ORCHID1); + Context &c = getContext(); + + const int key = (static_cast(c.rasterizerDesc.DepthBias)) | + (static_cast(static_cast(c.rasterizerDesc.SlopeScaledDepthBias)) << 8) | + ((static_cast(c.rasterizerDesc.CullMode) & 0x03) << 16); + + auto it = managedRasterizerStates.find(key); + if (it != managedRasterizerStates.end()) + return it->second; + + ID3D11RasterizerState *state = NULL; + m_pDevice->CreateRasterizerState(&c.rasterizerDesc, &state); + managedRasterizerStates.emplace(key, state); + return state; +} + + +ID3D11SamplerState *Renderer::GetManagedSamplerState() +{ + PROFILER_SCOPE("Renderer::GetManagedSamplerState", "GetManagedSamplerState", MP_ORCHID1); + Context &c = getContext(); + const int key = m_textures[c.textureIdx].samplerParams; + + auto it = managedSamplerStates.find(key); + if (it != managedSamplerStates.end()) + return it->second; + + const bool clampU = (key & SAMPLER_PARAM_CLAMP_U) != 0; + const bool clampV = (key & SAMPLER_PARAM_CLAMP_V) != 0; + const bool linearFilter = (key & SAMPLER_PARAM_LINEAR_FILTER) != 0; + const bool mipLinear = (key & SAMPLER_PARAM_LINEAR_MIPS) != 0; + const int filterBits = (mipLinear != 0 ? (linearFilter ? D3D11_FILTER_MIN_MAG_MIP_LINEAR : D3D11_FILTER_MIN_POINT_MAG_MIP_LINEAR) + : (linearFilter ? D3D11_FILTER_MIN_LINEAR_MAG_POINT_MIP_LINEAR : D3D11_FILTER_MIN_MAG_POINT_MIP_LINEAR)); + + D3D11_SAMPLER_DESC desc = {}; + desc.Filter = static_cast(filterBits); + desc.AddressU = clampU ? D3D11_TEXTURE_ADDRESS_CLAMP : D3D11_TEXTURE_ADDRESS_WRAP; + desc.AddressV = clampV ? D3D11_TEXTURE_ADDRESS_CLAMP : D3D11_TEXTURE_ADDRESS_WRAP; + desc.AddressW = D3D11_TEXTURE_ADDRESS_CLAMP; + desc.MipLODBias = 0.0f; + desc.MaxAnisotropy = 16; + desc.ComparisonFunc = D3D11_COMPARISON_NEVER; + desc.BorderColor[0] = 0.0f; + desc.BorderColor[1] = 0.0f; + desc.BorderColor[2] = 0.0f; + desc.BorderColor[3] = 0.0f; + desc.MinLOD = -(std::numeric_limits::max)(); + desc.MaxLOD = (std::numeric_limits::max)(); + + ID3D11SamplerState *state = NULL; + m_pDevice->CreateSamplerState(&desc, &state); + managedSamplerStates.emplace(key, state); + return state; +} + + +void Renderer::StateSetAlphaFunc(int, float param) +{ + Context &c = getContext(); + c.alphaReference = param; + + const float alpha[4] = {0.0f, 0.0f, 0.0f, c.alphaTestEnabled ? c.alphaReference : 0.0f}; + D3D11_MAPPED_SUBRESOURCE mapped = {}; + c.m_pDeviceContext->Map(c.m_alphaTestBuffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &mapped); + std::memcpy(mapped.pData, alpha, sizeof(alpha)); + c.m_pDeviceContext->Unmap(c.m_alphaTestBuffer, 0); +} + + +void Renderer::StateSetAlphaTestEnable(bool enable) +{ + Context &c = getContext(); + c.alphaTestEnabled = enable; + + const float alpha[4] = {0.0f, 0.0f, 0.0f, enable ? c.alphaReference : 0.0f}; + D3D11_MAPPED_SUBRESOURCE mapped = {}; + c.m_pDeviceContext->Map(c.m_alphaTestBuffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &mapped); + std::memcpy(mapped.pData, alpha, sizeof(alpha)); + c.m_pDeviceContext->Unmap(c.m_alphaTestBuffer, 0); +} + + +void Renderer::StateSetBlendEnable(bool enable) +{ + Context &c = getContext(); + if (c.commandBuffer != NULL && c.commandBuffer->isActive != 0) + { + c.commandBuffer->SetBlendEnable(enable); + return; + } + + c.blendDesc.RenderTarget[0].BlendEnable = enable; + c.m_pDeviceContext->OMSetBlendState(GetManagedBlendState(), c.blendFactor, 0xFFFFFFFF); +} + + +void Renderer::StateSetBlendFactor(unsigned int colour) +{ + Context &c = getContext(); + if (c.commandBuffer != NULL && c.commandBuffer->isActive != 0) + { + c.commandBuffer->SetBlendFactor(colour); + return; + } + + const float scale = 255.0f; + c.blendFactor[0] = static_cast((colour >> 0) & 0xFF) / scale; + c.blendFactor[1] = static_cast((colour >> 8) & 0xFF) / scale; + c.blendFactor[2] = static_cast((colour >> 16) & 0xFF) / scale; + c.blendFactor[3] = static_cast((colour >> 24) & 0xFF) / scale; + c.m_pDeviceContext->OMSetBlendState(GetManagedBlendState(), c.blendFactor, 0xFFFFFFFF); +} + + +void Renderer::StateSetBlendFunc(int src, int dst) +{ + Context &c = getContext(); + if (c.commandBuffer != NULL && c.commandBuffer->isActive != 0) + { + c.commandBuffer->SetBlendFunc(src, dst); + return; + } + + c.blendDesc.RenderTarget[0].SrcBlend = static_cast(src); + c.blendDesc.RenderTarget[0].DestBlend = static_cast(dst); + c.m_pDeviceContext->OMSetBlendState(GetManagedBlendState(), c.blendFactor, 0xFFFFFFFF); +} + + +void Renderer::StateSetColour(float r, float g, float b, float a) +{ + Context &c = getContext(); + if (c.commandBuffer != NULL && c.commandBuffer->isActive != 0) + { + c.commandBuffer->SetColor(r, g, b, a); + return; + } + + ID3D11DeviceContext *d3d11 = c.m_pDeviceContext; + const float colour[4] = {r, g, b, a}; + + D3D11_MAPPED_SUBRESOURCE mapped = {}; + d3d11->Map(c.m_tintColorBuffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &mapped); + std::memcpy(mapped.pData, colour, sizeof(colour)); + d3d11->Unmap(c.m_tintColorBuffer, 0); +} + + +void Renderer::StateSetDepthFunc(int func) +{ + Context &c = getContext(); + if (c.commandBuffer != NULL && c.commandBuffer->isActive != 0) + { + c.commandBuffer->SetDepthFunc(func); + return; + } + + c.depthStencilDesc.DepthFunc = static_cast(func); + c.m_pDeviceContext->OMSetDepthStencilState(GetManagedDepthStencilState(), 0); +} + + +void Renderer::StateSetDepthMask(bool enable) +{ + Context &c = getContext(); + if (c.commandBuffer != NULL && c.commandBuffer->isActive != 0) + { + c.commandBuffer->SetDepthMask(enable); + return; + } + + c.depthStencilDesc.DepthWriteMask = enable ? D3D11_DEPTH_WRITE_MASK_ALL : D3D11_DEPTH_WRITE_MASK_ZERO; + c.m_pDeviceContext->OMSetDepthStencilState(GetManagedDepthStencilState(), 0); + c.depthWriteEnabled = enable; +} + + +void Renderer::StateSetDepthSlopeAndBias(float slope, float bias) +{ + Context &c = getContext(); + + const float scale = 65536.0f; + c.rasterizerDesc.DepthBias = static_cast(bias * scale); + c.rasterizerDesc.SlopeScaledDepthBias = slope * scale; + c.m_pDeviceContext->RSSetState(GetManagedRasterizerState()); +} + + +void Renderer::StateSetDepthTestEnable(bool enable) +{ + Context &c = getContext(); + if (c.commandBuffer != NULL && c.commandBuffer->isActive != 0) + { + c.commandBuffer->SetDepthTestEnable(enable); + return; + } + + c.depthStencilDesc.DepthEnable = enable; + c.m_pDeviceContext->OMSetDepthStencilState(GetManagedDepthStencilState(), 0); + c.depthTestEnabled = enable; +} + + +void Renderer::StateSetEnableViewportClipPlanes(bool) {} + + +void Renderer::StateSetFaceCull(bool enable) +{ + Context &c = getContext(); + if (c.commandBuffer != NULL && c.commandBuffer->isActive != 0) + { + c.commandBuffer->SetFaceCull(enable); + return; + } + + c.rasterizerDesc.CullMode = enable ? D3D11_CULL_BACK : D3D11_CULL_NONE; + c.m_pDeviceContext->RSSetState(GetManagedRasterizerState()); + c.faceCullEnabled = enable; +} + + +void Renderer::StateSetFaceCullCW(bool enable) +{ + Context &c = getContext(); + if (c.faceCullEnabled) + c.rasterizerDesc.CullMode = enable ? D3D11_CULL_BACK : D3D11_CULL_FRONT; + else + c.rasterizerDesc.CullMode = D3D11_CULL_NONE; + + c.m_pDeviceContext->RSSetState(GetManagedRasterizerState()); +} + + +void Renderer::StateSetFogColour(float red, float green, float blue) +{ + Context &c = getContext(); + c.fogColourRed = red; + c.fogColourBlue = blue; + c.fogColourGreen = green; +} + + +void Renderer::StateSetFogDensity(float density) +{ + Context &c = getContext(); + c.fogDensity = density; +} + + +void Renderer::StateSetFogEnable(bool enable) +{ + Context &c = getContext(); + c.fogEnabled = enable; +} + + +void Renderer::StateSetFogFarDistance(float dist) +{ + Context &c = getContext(); + c.fogFarDistance = dist; +} + + +void Renderer::StateSetFogMode(int mode) +{ + Context &c = getContext(); + c.fogMode = mode; +} + + +void Renderer::StateSetFogNearDistance(float dist) +{ + Context &c = getContext(); + c.fogNearDistance = dist; +} + + +void Renderer::StateSetForceLOD(int LOD) +{ + Context &c = getContext(); + c.forcedLOD = LOD; +} + + +void Renderer::StateSetLightAmbientColour(float red, float green, float blue) +{ + Context &c = getContext(); + if (c.commandBuffer != NULL && c.commandBuffer->isActive != 0) + { + c.commandBuffer->SetLightAmbientColour(red, green, blue); + return; + } + + c.lightAmbientColour.x = red; + c.lightAmbientColour.y = green; + c.lightAmbientColour.z = blue; + c.lightAmbientColour.w = 1.0f; + c.lightingDirty = true; +} + + +void Renderer::StateSetLightColour(int light, float red, float green, float blue) +{ + if (light >= 2) + return; + + Context &c = getContext(); + if (c.commandBuffer != NULL && c.commandBuffer->isActive != 0) + { + c.commandBuffer->SetLightColour(light, red, green, blue); + return; + } + + c.lightColour[light].x = red; + c.lightColour[light].y = green; + c.lightColour[light].z = blue; + c.lightColour[light].w = 1.0f; + c.lightingDirty = true; +} + + +void Renderer::StateSetLightDirection(int light, float x, float y, float z) +{ + if (light >= 2) + return; + + Context &c = getContext(); + if (c.commandBuffer != NULL && c.commandBuffer->isActive != 0) + { + c.commandBuffer->SetLightDirection(light, x, y, z); + return; + } + + const std::uint32_t stackIndex = c.stackPos[MATRIX_MODE_MODELVIEW]; + const DirectX::XMMATRIX &modelView = c.matrixStacks[MATRIX_MODE_MODELVIEW][stackIndex]; + const DirectX::XMVECTOR direction = DirectX::XMVectorSet(x, y, z, 0.0f); + const DirectX::XMVECTOR transformed = DirectX::XMVector3TransformNormal(direction, modelView); + const DirectX::XMVECTOR normalized = DirectX::XMVector3Normalize(transformed); + + DirectX::XMStoreFloat4(&c.lightDirection[light], normalized); + c.lightingDirty = true; +} + + +void Renderer::StateSetLightEnable(int light, bool enable) +{ + if (light >= 2) + return; + + Context &c = getContext(); + if (c.commandBuffer != NULL && c.commandBuffer->isActive != 0) + { + c.commandBuffer->SetLightEnable(light, enable); + return; + } + + c.lightEnabled[light] = enable; + c.lightingDirty = true; +} + + +void Renderer::StateSetLightingEnable(bool enable) +{ + Context &c = getContext(); + if (c.commandBuffer != NULL && c.commandBuffer->isActive != 0) + { + c.commandBuffer->SetLightingEnable(enable); + return; + } + + c.lightingEnabled = enable; +} + + +void Renderer::StateSetLineWidth(float) {} + + +void Renderer::StateSetStencil(D3D11_COMPARISON_FUNC function, uint8_t stencil_ref, uint8_t stencil_func_mask, uint8_t stencil_write_mask) +{ + Context &c = getContext(); + + D3D11_DEPTH_STENCIL_DESC desc = c.depthStencilDesc; + desc.StencilEnable = true; + desc.StencilReadMask = stencil_func_mask; + desc.StencilWriteMask = stencil_write_mask; + desc.FrontFace.StencilFunc = function; + desc.BackFace.StencilFunc = function; + + ID3D11DepthStencilState *state = NULL; + m_pDevice->CreateDepthStencilState(&desc, &state); + m_pDeviceContext->OMSetDepthStencilState(state, stencil_ref); + if (state != NULL) state->Release(); +} + + +void Renderer::StateSetTexGenCol(int col, float x, float y, float z, float w, bool eyeSpace) +{ + Context &c = getContext(); + + DirectX::XMVECTOR plane = DirectX::XMVectorSet(x, y, z, w); + if (eyeSpace) + { + DirectX::XMFLOAT4X4 modelView; + std::memset(&modelView, 0, sizeof(modelView)); + std::memcpy(&modelView, MatrixGet(MATRIX_MODE_MODELVIEW), sizeof(modelView)); + + DirectX::XMVECTOR determinant = DirectX::XMVectorZero(); + const DirectX::XMMATRIX inverse = DirectX::XMMatrixInverse(&determinant, DirectX::XMLoadFloat4x4(&modelView)); + plane = DirectX::XMVector4Transform(plane, inverse); + } + + DirectX::XMFLOAT4 transformed; + DirectX::XMStoreFloat4(&transformed, plane); + + const int activeSet = eyeSpace ? 0 : 1; + const int inactiveSet = eyeSpace ? 1 : 0; + + float *active = reinterpret_cast(&c.texGenMatrices[activeSet]); + active[col + 0] = transformed.x; + active[col + 4] = transformed.y; + active[col + 8] = transformed.z; + active[col + 12] = transformed.w; + + float *inactive = reinterpret_cast(&c.texGenMatrices[inactiveSet]); + inactive[col + 0] = 0.0f; + inactive[col + 4] = 0.0f; + inactive[col + 8] = 0.0f; + inactive[col + 12] = 0.0f; +} + + +void Renderer::StateSetVertexTextureUV(float u, float v) +{ + Context &c = getContext(); + const float texgen[4] = {u - 1.0f, v - 1.0f, 0.0f, 0.0f}; + + D3D11_MAPPED_SUBRESOURCE mapped = {}; + c.m_pDeviceContext->Map(c.m_vertexTexcoordBuffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &mapped); + std::memcpy(mapped.pData, texgen, sizeof(texgen)); + c.m_pDeviceContext->Unmap(c.m_vertexTexcoordBuffer, 0); +} + + +void Renderer::StateSetViewport(C4JRender::eViewportType viewportType) +{ + getContext(); + m_ViewportType = viewportType; + + const float fullWidth = static_cast(backBufferWidth); + const float fullHeight = static_cast(backBufferHeight); + + float x = 0.0f; + float y = 0.0f; + float width = fullWidth; + float height = fullHeight; + + switch (viewportType) + { + case C4JRender::VIEWPORT_TYPE_FULLSCREEN: + break; + case C4JRender::VIEWPORT_TYPE_SPLIT_TOP: + height = fullHeight * 0.5f; + break; + case C4JRender::VIEWPORT_TYPE_SPLIT_BOTTOM: + y = fullHeight * 0.5f; + height = fullHeight * 0.5f; + break; + case C4JRender::VIEWPORT_TYPE_SPLIT_LEFT: + width = fullWidth * 0.5f; + break; + case C4JRender::VIEWPORT_TYPE_SPLIT_RIGHT: + x = fullWidth * 0.5f; + width = fullWidth * 0.5f; + break; + case C4JRender::VIEWPORT_TYPE_QUADRANT_TOP_LEFT: + width = fullWidth * 0.5f; + height = fullHeight * 0.5f; + break; + case C4JRender::VIEWPORT_TYPE_QUADRANT_TOP_RIGHT: + x = fullWidth * 0.5f; + width = fullWidth * 0.5f; + height = fullHeight * 0.5f; + break; + case C4JRender::VIEWPORT_TYPE_QUADRANT_BOTTOM_LEFT: + y = fullHeight * 0.5f; + width = fullWidth * 0.5f; + height = fullHeight * 0.5f; + break; + case C4JRender::VIEWPORT_TYPE_QUADRANT_BOTTOM_RIGHT: + x = fullWidth * 0.5f; + y = fullHeight * 0.5f; + width = fullWidth * 0.5f; + height = fullHeight * 0.5f; + break; + default: + break; + } + + D3D11_VIEWPORT viewport = {}; + viewport.TopLeftX = x; + viewport.TopLeftY = y; + viewport.Width = width; + viewport.Height = height; + viewport.MinDepth = 0.0f; + viewport.MaxDepth = 1.0f; + + m_pDeviceContext->RSSetViewports(1, &viewport); + m_pDeviceContext->OMSetRenderTargets(1, &mainRenderTargetView, depthStencilView); +} + + +void Renderer::StateSetWriteEnable(bool red, bool green, bool blue, bool alpha) +{ + Context &c = getContext(); + + std::uint8_t mask = 0; + mask |= red ? 0x1 : 0; + mask |= green ? 0x2 : 0; + mask |= blue ? 0x4 : 0; + mask |= alpha ? 0x8 : 0; + + c.blendDesc.RenderTarget[0].RenderTargetWriteMask = mask; + c.m_pDeviceContext->OMSetBlendState(GetManagedBlendState(), c.blendFactor, 0xFFFFFFFF); +} + + +void Renderer::StateUpdate() +{ + PROFILER_SCOPE("Renderer::StateUpdate", "StateUpdate", MP_ORCHID1); + Context &c = getContext(); + StateSetFaceCull(c.faceCullEnabled); + StateSetDepthMask(c.depthWriteEnabled); + StateSetDepthTestEnable(c.depthTestEnabled); + StateSetAlphaTestEnable(c.alphaTestEnabled); +} + +void Renderer::UpdateFogState() +{ + PROFILER_SCOPE("Renderer::UpdateFogState", "UpdateFogState", MP_ORCHID1); + Context &c = getContext(); + ID3D11DeviceContext *d3d11 = c.m_pDeviceContext; + + float fogParams[4] = {}; + if (c.fogEnabled) + { + if (c.fogMode == 1) + { + fogParams[0] = c.fogFarDistance; + fogParams[1] = 1.0f / (c.fogFarDistance - c.fogNearDistance); + fogParams[2] = 1.0f; + } + else + { + fogParams[0] = c.fogDensity; + fogParams[2] = 2.0f; + } + } + + const float fogColour[4] = {c.fogColourRed, c.fogColourGreen, c.fogColourBlue, 1.0f}; + + D3D11_MAPPED_SUBRESOURCE mapped = {}; + d3d11->Map(c.m_fogParamsBuffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &mapped); + std::memcpy(mapped.pData, fogParams, sizeof(fogParams)); + d3d11->Unmap(c.m_fogParamsBuffer, 0); + + d3d11->Map(c.m_fogColourBuffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &mapped); + std::memcpy(mapped.pData, fogColour, sizeof(fogColour)); + d3d11->Unmap(c.m_fogColourBuffer, 0); +} + + +void Renderer::UpdateLightingState() +{ + PROFILER_SCOPE("Renderer::UpdateLightingState", "UpdateLightingState", MP_ORCHID1); + Context &c = getContext(); + if (!c.lightingDirty || !c.lightingEnabled) + { + return; + } + + if (!c.lightEnabled[0]) + { + std::memset(&c.lightDirection[0], 0, sizeof(c.lightDirection[0])); + std::memset(&c.lightColour[0], 0, sizeof(c.lightColour[0])); + } + + if (!c.lightEnabled[1]) + { + std::memset(&c.lightDirection[1], 0, sizeof(c.lightDirection[1])); + std::memset(&c.lightColour[1], 0, sizeof(c.lightColour[1])); + } + + const std::size_t lightingBytes = sizeof(c.lightDirection) + sizeof(c.lightColour) + sizeof(c.lightAmbientColour); + D3D11_MAPPED_SUBRESOURCE mapped = {}; + c.m_pDeviceContext->Map(c.m_lightingStateBuffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &mapped); + std::memmove(mapped.pData, c.lightDirection, lightingBytes); + c.m_pDeviceContext->Unmap(c.m_lightingStateBuffer, 0); + + c.lightingDirty = false; +} + + +void Renderer::UpdateTexGenState() +{ + PROFILER_SCOPE("Renderer::UpdateTexGenState", "UpdateTexGenState", MP_ORCHID1); + Context &c = getContext(); + + D3D11_MAPPED_SUBRESOURCE mapped = {}; + c.m_pDeviceContext->Map(c.m_texGenMatricesBuffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &mapped); + c.m_pDeviceContext->Unmap(c.m_texGenMatricesBuffer, 0); + std::memcpy(mapped.pData, c.texGenMatrices, sizeof(c.texGenMatrices)); +} + + +void Renderer::UpdateViewportState() {} \ No newline at end of file diff --git a/Minecraft.Client/Windows64/4JLibs/inc/Render/RendererTexture.cpp b/Minecraft.Client/Windows64/4JLibs/inc/Render/RendererTexture.cpp new file mode 100644 index 000000000..f666f1fc6 --- /dev/null +++ b/Minecraft.Client/Windows64/4JLibs/inc/Render/RendererTexture.cpp @@ -0,0 +1,333 @@ +/* +MIT License + +Copyright (c) 2026 Patoke + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +#include "stdafx.h" +#include "Renderer.h" +#include "libpng/png.h" + +unsigned char* dataStart; +unsigned char *dataCurr; +unsigned char *dataEnd; + +DXGI_FORMAT Renderer::textureFormats[] = { + DXGI_FORMAT_R8G8B8A8_UNORM, + DXGI_FORMAT_UNKNOWN +}; + +// these are here because they are used before they are defined +// its fine like that and the order of everything matches ida so we have to put these here +void user_flush_data(png_struct_def *png_ptr); +void user_write_data(png_struct_def *png_ptr, unsigned char *src, size_t length); + +HRESULT Renderer::LoadTextureData(BYTE* pbData, DWORD dwBytes, D3DXIMAGE_INFO* pSrcInfo, int** ppDataOut) +{ + PROFILER_SCOPE("Renderer::LoadTextureData_Memory", "LoadTextureData_Memory", MP_PURPLE4); + png_image image; + memset(&image, 0, sizeof(image)); + image.version = PNG_IMAGE_VERSION; + + png_image_begin_read_from_memory(&image, pbData, dwBytes); + + if (PNG_IMAGE_FAILED(image)) + return E_FAIL; + + image.format = PNG_FORMAT_BGRA; + + *ppDataOut = new int[image.width * image.height]; + if (!*ppDataOut || !png_image_finish_read(&image, NULL, *ppDataOut, NULL, NULL)) + return E_FAIL; + + pSrcInfo->Width = image.width; + pSrcInfo->Height = image.height; + return S_OK; +} + +HRESULT Renderer::LoadTextureData(const char* szFilename, D3DXIMAGE_INFO* pSrcInfo, int** ppDataOut) +{ + PROFILER_SCOPE("Renderer::LoadTextureData_File", "LoadTextureData_File", MP_PURPLE4); + png_image image; + memset(&image, 0, sizeof(image)); + image.version = PNG_IMAGE_VERSION; + + png_image_begin_read_from_file(&image, szFilename); + + if (PNG_IMAGE_FAILED(image)) + return E_FAIL; + + image.format = PNG_FORMAT_BGRA; + + *ppDataOut = new int[image.width * image.height]; + if (!*ppDataOut || !png_image_finish_read(&image, NULL, *ppDataOut, NULL, NULL)) + return E_FAIL; + + pSrcInfo->Width = image.width; + pSrcInfo->Height = image.height; + return S_OK; +} + +HRESULT Renderer::SaveTextureData(const char* szFilename, D3DXIMAGE_INFO* pSrcInfo, int* ppDataOut) +{ + PROFILER_SCOPE("Renderer::SaveTextureData", "SaveTextureData", MP_PURPLE4); + png_image image; + memset(&image, 0, sizeof(image)); + + image.width = pSrcInfo->Width; + image.height = pSrcInfo->Height; + image.version = PNG_IMAGE_VERSION; + image.format = PNG_FORMAT_RGBA; + + png_image_write_to_file(&image, szFilename, NULL, ppDataOut, NULL, NULL); + return S_OK; +} + +HRESULT Renderer::SaveTextureDataToMemory(void* pOutput, int outputCapacity, int* outputLength, int width, int height, int* ppDataIn) +{ + PROFILER_SCOPE("Renderer::SaveTextureDataToMemory", "SaveTextureDataToMemory", MP_PURPLE4); + png_image image; + memset(&image, 0, sizeof(image)); + + image.width = width; + image.height = height; + dataEnd = (BYTE *)pOutput + outputCapacity; + image.version = PNG_IMAGE_VERSION; + image.format = PNG_FORMAT_RGBA; + dataStart = (BYTE*)pOutput; + dataCurr = (BYTE*)pOutput; + + png_image_write_to_stdio(&image, NULL, NULL, ppDataIn, NULL, NULL, user_write_data, user_flush_data); + + *outputLength = (int)(dataCurr - dataStart); + return S_OK; +} + +void Renderer::TextureBind(int idx) +{ + PROFILER_SCOPE("Renderer::TextureBind", "TextureBind", MP_PURPLE4); + if (idx == -1) + idx = defaultTextureIndex; + + Context& c = getContext(); + + if (c.commandBuffer && c.commandBuffer->isActive) + c.commandBuffer->BindTexture(idx); + + c.textureIdx = idx; + c.m_pDeviceContext->PSSetShaderResources(0, 1, &m_textures[idx].view); + + UpdateTextureState(false); +} + +void Renderer::TextureBindVertex(int idx) +{ + PROFILER_SCOPE("Renderer::TextureBindVertex", "TextureBindVertex", MP_PURPLE4); + if (idx == -1) + idx = defaultTextureIndex; + + Context& c = getContext(); + + c.textureIdx = idx; + c.m_pDeviceContext->VSSetShaderResources(0, 1, &m_textures[idx].view); + + UpdateTextureState(true); +} + +int Renderer::TextureCreate() +{ + PROFILER_SCOPE("Renderer::TextureCreate", "TextureCreate", MP_PURPLE4); + for (int i = 0; i < MAX_TEXTURES; i++) + { + if (!m_textures[i].allocated) + { + m_textures[i].texture = NULL; + m_textures[i].allocated = true; + m_textures[i].mipLevels = 1; + m_textures[i].samplerParams = 0; + return i; + } + } + return -1; +} + +void Renderer::TextureData(int width, int height, void* data, int level, C4JRender::eTextureFormat format) +{ + PROFILER_SCOPE("Renderer::TextureData", "TextureData", MP_PURPLE4); + Context& c = getContext(); + int idx = c.textureIdx; + + m_textures[idx].textureFormat = format; + + if (level == 0) + { + D3D11_TEXTURE2D_DESC desc; + desc.Width = width; + desc.Height = height; + desc.MipLevels = m_textures[idx].mipLevels; + desc.ArraySize = 1; + desc.Format = textureFormats[format]; + desc.SampleDesc.Count = 1; + desc.SampleDesc.Quality = 0; + desc.Usage = D3D11_USAGE_DEFAULT; + desc.BindFlags = D3D11_BIND_SHADER_RESOURCE; + desc.CPUAccessFlags = 0; + desc.MiscFlags = 0; + + m_pDevice->CreateTexture2D(&desc, NULL, &m_textures[idx].texture); + m_pDevice->CreateShaderResourceView(m_textures[idx].texture, NULL, &m_textures[idx].view); + } + + c.m_pDeviceContext->UpdateSubresource( + m_textures[idx].texture, + level, + NULL, + data, + static_cast(width * 4), + static_cast(width * height * 4) + ); +} + +void Renderer::TextureDataUpdate(int xoffset, int yoffset, int width, int height, void* data, int level) +{ + PROFILER_SCOPE("Renderer::TextureDataUpdate", "TextureDataUpdate", MP_PURPLE4); + Context& c = getContext(); + int idx = c.textureIdx; + + D3D11_TEXTURE2D_DESC desc = {}; + m_textures[idx].texture->GetDesc(&desc); + + D3D11_BOX box = {}; + box.left = xoffset; + box.top = yoffset; + box.right = xoffset + width; + box.bottom = yoffset + height; + box.front = 0; + box.back = 1; + + c.m_pDeviceContext->UpdateSubresource( + m_textures[idx].texture, + level, + &box, + data, + static_cast(width * 4), + static_cast(width * height * 4) + ); +} + +void Renderer::TextureDynamicUpdateEnd() {} +void Renderer::TextureDynamicUpdateStart() {} + +void Renderer::TextureFree(int idx) +{ + PROFILER_SCOPE("Renderer::TextureFree", "TextureFree", MP_PURPLE4); + m_textures[idx].texture->Release(); + m_textures[idx].view->Release(); + m_textures[idx].view = NULL; + m_textures[idx].allocated = false; + m_textures[idx].texture = NULL; +} + +void Renderer::TextureGetStats() {} + +ID3D11ShaderResourceView* Renderer::TextureGetTexture(int idx) +{ + if (idx < MAX_TEXTURES) { + if (m_textures[idx].allocated) return m_textures[idx].view; + } + return NULL; +} + +int Renderer::TextureGetTextureLevels() +{ + Context& c = getContext(); + return m_textures[c.textureIdx].mipLevels; +} + +void Renderer::TextureSetParam(int param, int value) +{ + Context& c = getContext(); + int idx = c.textureIdx; + + switch (param) + { + case GL_TEXTURE_MIN_FILTER: + m_textures[idx].samplerParams &= ~4u; + if (value == GL_LINEAR) + m_textures[idx].samplerParams |= 4u; + break; + case GL_TEXTURE_MAG_FILTER: + m_textures[idx].samplerParams &= ~8u; + if (value == GL_LINEAR) + m_textures[idx].samplerParams |= 8u; + break; + case GL_TEXTURE_WRAP_S: + m_textures[idx].samplerParams &= ~1u; + if (value == GL_CLAMP) + m_textures[idx].samplerParams |= 1u; + break; + case GL_TEXTURE_WRAP_T: + m_textures[idx].samplerParams &= ~2u; + if (value == GL_CLAMP) + m_textures[idx].samplerParams |= 2u; + break; + } +} + +void Renderer::TextureSetTextureLevels(int levels) +{ + Context& c = getContext(); + m_textures[c.textureIdx].mipLevels = levels; +} + +void Renderer::UpdateTextureState(bool bVertex) +{ + Context& c = getContext(); + ID3D11SamplerState* pSampler = GetManagedSamplerState(); + + if (bVertex) + c.m_pDeviceContext->VSSetSamplers(0, 1, &pSampler); + else + c.m_pDeviceContext->PSSetSamplers(0, 1, &pSampler); +} + +void user_flush_data(png_struct_def* png_ptr) {} +void user_write_data(png_struct_def* png_ptr, unsigned char* src, size_t length) +{ + int bytesToWrite = static_cast(dataEnd - dataCurr); + if (static_cast(length) < bytesToWrite) + bytesToWrite = (int)length; + + std::memcpy(dataCurr, src, bytesToWrite); + dataCurr += bytesToWrite; +} + +int user_write_data_bytes_written() +{ + return static_cast(dataCurr - dataStart); +} + +void user_write_data_init(unsigned char* pBuffer, int size) +{ + dataStart = pBuffer; + dataCurr = pBuffer; + dataEnd = pBuffer + size; +} \ No newline at end of file diff --git a/Minecraft.Client/Windows64/4JLibs/inc/Render/RendererVertex.cpp b/Minecraft.Client/Windows64/4JLibs/inc/Render/RendererVertex.cpp new file mode 100644 index 000000000..84b492867 --- /dev/null +++ b/Minecraft.Client/Windows64/4JLibs/inc/Render/RendererVertex.cpp @@ -0,0 +1,185 @@ +/* +MIT License + +Copyright (c) 2026 Patoke + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +#include "stdafx.h" +#include "Renderer.h" + +void Renderer::DrawVertexBuffer(C4JRender::ePrimitiveType PrimitiveType, int count, ID3D11Buffer *buffer, C4JRender::eVertexType vType, + C4JRender::ePixelShaderType psType) +{ + PROFILER_SCOPE("Renderer::DrawVertexBuffer", "DrawVertexBuffer", MP_RED2); + Renderer::Context &c = getContext(); + ID3D11DeviceContext *d3d11 = c.m_pDeviceContext; + + int drawCount = count; + bool indexed = false; + + PROFILER_SCOPE("Renderer::DrawVertexBuffer", "DrawVertexSetup", MP_RED2); + DrawVertexSetup(vType, psType, PrimitiveType, &drawCount, &indexed); + StateUpdate(); + + const UINT stride = vertexStrideTable[vType]; + const UINT offset = 0; + d3d11->IASetVertexBuffers(0, 1, &buffer, &stride, &offset); + + if (indexed) + d3d11->DrawIndexed(drawCount, 0, 0); + else + d3d11->Draw(count, 0); +} + +void Renderer::DrawVertexSetup(C4JRender::eVertexType vType, C4JRender::ePixelShaderType psType, C4JRender::ePrimitiveType PrimitiveType, int *count, + bool *indexed) +{ + PROFILER_SCOPE("Renderer::DrawVertexSetup", "DrawVertexSetup", MP_RED2); + Renderer::Context &c = getContext(); + ID3D11DeviceContext *d3d11 = c.m_pDeviceContext; + + C4JRender::eVertexType effectiveVertexType = vType; + if (effectiveVertexType == C4JRender::VERTEX_TYPE_PF3_TF2_CB4_NB4_XW1 && c.lightingEnabled) + effectiveVertexType = C4JRender::VERTEX_TYPE_PF3_TF2_CB4_NB4_XW1_LIT; + + if (effectiveVertexType != activeVertexType) + { + d3d11->VSSetShader(vertexShaderTable[effectiveVertexType], NULL, 0); + d3d11->IASetInputLayout(inputLayoutTable[effectiveVertexType]); + activeVertexType = effectiveVertexType; + } + + if (psType != activePixelType) + { + d3d11->PSSetShader(pixelShaderTable[psType], NULL, 0); + activePixelType = psType; + } + + D3D11_MAPPED_SUBRESOURCE mapped = {}; + + if (c.matrixDirty[MATRIX_MODE_MODELVIEW]) + { + d3d11->Map(c.m_modelViewMatrix, 0, D3D11_MAP_WRITE_DISCARD, 0, &mapped); + memcpy(mapped.pData, MatrixGet(MATRIX_MODE_MODELVIEW), sizeof(DirectX::XMMATRIX)); + d3d11->Unmap(c.m_modelViewMatrix, 0); + c.matrixDirty[MATRIX_MODE_MODELVIEW] = false; + } + + if (c.matrixDirty[MATRIX_MODE_MODELVIEW_PROJECTION]) + { + d3d11->Map(c.m_projectionMatrix, 0, D3D11_MAP_WRITE_DISCARD, 0, &mapped); + memcpy(mapped.pData, MatrixGet(MATRIX_MODE_MODELVIEW_PROJECTION), sizeof(DirectX::XMMATRIX)); + d3d11->Unmap(c.m_projectionMatrix, 0); + c.matrixDirty[MATRIX_MODE_MODELVIEW_PROJECTION] = false; + } + + if (c.matrixDirty[MATRIX_MODE_MODELVIEW_TEXTURE]) + { + d3d11->Map(c.m_textureMatrix, 0, D3D11_MAP_WRITE_DISCARD, 0, &mapped); + memcpy(mapped.pData, MatrixGet(MATRIX_MODE_MODELVIEW_TEXTURE), sizeof(DirectX::XMMATRIX)); + d3d11->Unmap(c.m_textureMatrix, 0); + c.matrixDirty[MATRIX_MODE_MODELVIEW_TEXTURE] = false; + } + + UpdateFogState(); + UpdateViewportState(); + UpdateLightingState(); + UpdateTexGenState(); + + d3d11->IASetPrimitiveTopology(g_topologies[PrimitiveType]); + + if (PrimitiveType == C4JRender::PRIMITIVE_TYPE_QUAD_LIST) + { + d3d11->IASetIndexBuffer(quadIndexBuffer, DXGI_FORMAT_R16_UINT, 0); + *count = (*count * 6) / 4; + *indexed = true; + return; + } + + if (PrimitiveType == C4JRender::PRIMITIVE_TYPE_TRIANGLE_FAN) + { + d3d11->IASetIndexBuffer(fanIndexBuffer, DXGI_FORMAT_R16_UINT, 0); + *count = (*count - 2) * 3; + *indexed = true; + return; + } + + d3d11->IASetIndexBuffer(NULL, DXGI_FORMAT_R16_UINT, 0); + *indexed = false; +} + +void Renderer::DrawVertices(C4JRender::ePrimitiveType PrimitiveType, int count, void *vertices, C4JRender::eVertexType vType, + C4JRender::ePixelShaderType psType) +{ + PROFILER_SCOPE("Renderer::DrawVertices", "DrawVertices", MP_RED2); + Renderer::Context &c = getContext(); + ID3D11DeviceContext *d3d11 = c.m_pDeviceContext; + Renderer::CommandBuffer *commandBuffer = c.commandBuffer; + + if (commandBuffer != NULL) + { + C4JRender::eVertexType effectiveVertexType = vType; + if (effectiveVertexType == C4JRender::VERTEX_TYPE_PF3_TF2_CB4_NB4_XW1 && c.lightingEnabled) + effectiveVertexType = C4JRender::VERTEX_TYPE_PF3_TF2_CB4_NB4_XW1_LIT; + + c.recordingPrimitiveType = PrimitiveType; + c.recordingVertexType = effectiveVertexType; + const UINT stride = vertexStrideTable[effectiveVertexType]; + commandBuffer->AddVertices(stride, static_cast(count), vertices, c); + return; + } + + int drawCount = count; + bool indexed = false; + + PROFILER_SCOPE("Renderer::DrawVertices", "DrawVertexSetup", MP_RED2); + DrawVertexSetup(vType, psType, PrimitiveType, &drawCount, &indexed); + + const UINT stride = vertexStrideTable[vType]; + const UINT vertexBytes = stride * static_cast(count); + + assert(vertexBytes <= Context::VERTEX_BUFFER_SIZE); + + UINT vertexOffset = c.dynamicVertexOffset; + if (vertexOffset + vertexBytes > Context::VERTEX_BUFFER_SIZE) + vertexOffset = 0; + + D3D11_MAPPED_SUBRESOURCE mapped = {}; + const D3D11_MAP mapType = vertexOffset == 0 ? D3D11_MAP_WRITE_DISCARD : D3D11_MAP_WRITE_NO_OVERWRITE; + const HRESULT hr = d3d11->Map(c.dynamicVertexBuffer, 0, mapType, 0, &mapped); + if (FAILED(hr)) + printf("ERROR: 0x%x\n", static_cast(hr)); + + memcpy(reinterpret_cast(mapped.pData) + vertexOffset, vertices, vertexBytes); + d3d11->Unmap(c.dynamicVertexBuffer, 0); + + StateUpdate(); + + ID3D11Buffer *dynamicBuffer = c.dynamicVertexBuffer; + d3d11->IASetVertexBuffers(0, 1, &dynamicBuffer, &stride, &vertexOffset); + + if (indexed) + d3d11->DrawIndexed(drawCount, 0, 0); + else + d3d11->Draw(count, 0); + + c.dynamicVertexOffset = vertexOffset + vertexBytes; +} diff --git a/Minecraft.Client/Windows64/4JLibs/inc/Render/VS_Compressed.h b/Minecraft.Client/Windows64/4JLibs/inc/Render/VS_Compressed.h new file mode 100644 index 000000000..4e604b69b --- /dev/null +++ b/Minecraft.Client/Windows64/4JLibs/inc/Render/VS_Compressed.h @@ -0,0 +1,514 @@ +#if 0 +// +// Generated by Microsoft (R) HLSL Shader Compiler 10.1 +// +// +// Buffer Definitions: +// +// cbuffer positionTransformWV +// { +// +// float4x4 matWorldView; // Offset: 0 Size: 64 +// +// } +// +// cbuffer positionTransformProj +// { +// +// float4x4 matProjection; // Offset: 0 Size: 64 +// +// } +// +// cbuffer textureUV2 +// { +// +// float4 vecUVT2; // Offset: 0 Size: 16 +// +// } +// +// cbuffer fog +// { +// +// float4 vecFog; // Offset: 0 Size: 16 +// +// } +// +// cbuffer positionTransform2 +// { +// +// float4 vecWV2Trans; // Offset: 0 Size: 16 +// +// } +// +// +// Resource Bindings: +// +// Name Type Format Dim HLSL Bind Count +// ------------------------------ ---------- ------- ----------- -------------- ------ +// light_sampler_s sampler NA NA s0 1 +// light_texture texture float4 2d t0 1 +// positionTransformWV cbuffer NA NA cb0 1 +// positionTransformProj cbuffer NA NA cb2 1 +// textureUV2 cbuffer NA NA cb4 1 +// fog cbuffer NA NA cb5 1 +// positionTransform2 cbuffer NA NA cb8 1 +// +// +// +// Input signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------- ------ +// POSITION 0 xyzw 0 NONE int xyzw +// TEXCOORD 0 xyzw 1 NONE int xyzw +// +// +// Output signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------- ------ +// SV_POSITION 0 xyzw 0 POS float xyzw +// COLOR 0 xyzw 1 NONE float xyzw +// TEXCOORD 0 xyzw 2 NONE float xyzw +// +vs_4_0 +dcl_constantbuffer CB0[4], immediateIndexed +dcl_constantbuffer CB2[4], immediateIndexed +dcl_constantbuffer CB4[1], immediateIndexed +dcl_constantbuffer CB5[1], immediateIndexed +dcl_constantbuffer CB8[1], immediateIndexed +dcl_sampler s0, mode_default +dcl_resource_texture2d (float,float,float,float) t0 +dcl_input v0.xyzw +dcl_input v1.xyzw +dcl_output_siv o0.xyzw, position +dcl_output o1.xyzw +dcl_output o2.xyzw +dcl_temps 2 +itof r0.xyz, v0.xyzx +mad r0.xyz, r0.xyzx, l(0.000977, 0.000977, 0.000977, 0.000000), cb8[0].xyzx +mul r1.xyzw, r0.yyyy, cb0[1].xyzw +mad r1.xyzw, cb0[0].xyzw, r0.xxxx, r1.xyzw +mad r0.xyzw, cb0[2].xyzw, r0.zzzz, r1.xyzw +add r0.xyzw, r0.xyzw, cb0[3].xyzw +mul r1.xyzw, r0.yyyy, cb2[1].xyzw +mad r1.xyzw, cb2[0].xyzw, r0.xxxx, r1.xyzw +mad r1.xyzw, cb2[2].xyzw, r0.zzzz, r1.xyzw +mad o0.xyzw, cb2[3].xyzw, r0.wwww, r1.xyzw +iadd r0.x, v0.w, l(0x00008000) +itof r0.x, r0.x +mul r0.xyw, r0.xxxx, l(0.000015, 0.000488, 0.000000, 0.031250) +frc r0.xyw, r0.xyxw +itof r1.xyzw, v1.zwxy +mul r1.xyzw, r1.xyzw, l(0.003906, 0.003906, 0.000122, 0.000122) +max r1.xy, r1.xyxx, cb4[0].xyxx +mov o2.xy, r1.zwzz +frc r1.xy, r1.xyxx +sample_l r1.xyzw, r1.xyxx, t0.xyzw, s0, l(0.000000) +mul o1.xyz, r0.xywx, r1.xyzx +mov o1.w, l(1.000000) +add r0.x, r0.z, cb5[0].x +mul r0.y, r0.z, cb5[0].x +mul r0.y, r0.y, l(1.442695) +exp r0.y, r0.y +min r0.y, r0.y, l(1.000000) +mul_sat r0.x, r0.x, cb5[0].y +eq r0.zw, cb5[0].zzzz, l(0.000000, 0.000000, 2.000000, 1.000000) +movc r0.y, r0.z, r0.y, l(1.000000) +movc o2.z, r0.w, r0.x, r0.y +mov o2.w, l(1.000000) +ret +// Approximately 33 instruction slots used +#endif + +const BYTE g_main_VS_Compressed[] = +{ + 68, 88, 66, 67, 127, 164, + 194, 107, 100, 107, 180, 27, + 86, 97, 97, 141, 85, 66, + 128, 127, 1, 0, 0, 0, + 20, 9, 0, 0, 5, 0, + 0, 0, 52, 0, 0, 0, + 24, 3, 0, 0, 108, 3, + 0, 0, 224, 3, 0, 0, + 152, 8, 0, 0, 82, 68, + 69, 70, 220, 2, 0, 0, + 5, 0, 0, 0, 104, 1, + 0, 0, 7, 0, 0, 0, + 28, 0, 0, 0, 0, 4, + 254, 255, 0, 129, 0, 0, + 180, 2, 0, 0, 252, 0, + 0, 0, 3, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 1, 0, + 0, 0, 1, 0, 0, 0, + 12, 1, 0, 0, 2, 0, + 0, 0, 5, 0, 0, 0, + 4, 0, 0, 0, 255, 255, + 255, 255, 0, 0, 0, 0, + 1, 0, 0, 0, 13, 0, + 0, 0, 26, 1, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 1, 0, 0, 0, + 1, 0, 0, 0, 46, 1, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 2, 0, 0, 0, 1, 0, + 0, 0, 1, 0, 0, 0, + 68, 1, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 4, 0, 0, 0, + 1, 0, 0, 0, 1, 0, + 0, 0, 79, 1, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 5, 0, + 0, 0, 1, 0, 0, 0, + 1, 0, 0, 0, 83, 1, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 8, 0, 0, 0, 1, 0, + 0, 0, 1, 0, 0, 0, + 108, 105, 103, 104, 116, 95, + 115, 97, 109, 112, 108, 101, + 114, 95, 115, 0, 108, 105, + 103, 104, 116, 95, 116, 101, + 120, 116, 117, 114, 101, 0, + 112, 111, 115, 105, 116, 105, + 111, 110, 84, 114, 97, 110, + 115, 102, 111, 114, 109, 87, + 86, 0, 112, 111, 115, 105, + 116, 105, 111, 110, 84, 114, + 97, 110, 115, 102, 111, 114, + 109, 80, 114, 111, 106, 0, + 116, 101, 120, 116, 117, 114, + 101, 85, 86, 50, 0, 102, + 111, 103, 0, 112, 111, 115, + 105, 116, 105, 111, 110, 84, + 114, 97, 110, 115, 102, 111, + 114, 109, 50, 0, 171, 171, + 26, 1, 0, 0, 1, 0, + 0, 0, 224, 1, 0, 0, + 64, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 46, 1, 0, 0, 1, 0, + 0, 0, 24, 2, 0, 0, + 64, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 68, 1, 0, 0, 1, 0, + 0, 0, 64, 2, 0, 0, + 16, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 79, 1, 0, 0, 1, 0, + 0, 0, 112, 2, 0, 0, + 16, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 83, 1, 0, 0, 1, 0, + 0, 0, 144, 2, 0, 0, + 16, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 248, 1, 0, 0, 0, 0, + 0, 0, 64, 0, 0, 0, + 2, 0, 0, 0, 8, 2, + 0, 0, 0, 0, 0, 0, + 109, 97, 116, 87, 111, 114, + 108, 100, 86, 105, 101, 119, + 0, 171, 171, 171, 3, 0, + 3, 0, 4, 0, 4, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 48, 2, 0, 0, + 0, 0, 0, 0, 64, 0, + 0, 0, 2, 0, 0, 0, + 8, 2, 0, 0, 0, 0, + 0, 0, 109, 97, 116, 80, + 114, 111, 106, 101, 99, 116, + 105, 111, 110, 0, 171, 171, + 88, 2, 0, 0, 0, 0, + 0, 0, 16, 0, 0, 0, + 2, 0, 0, 0, 96, 2, + 0, 0, 0, 0, 0, 0, + 118, 101, 99, 85, 86, 84, + 50, 0, 1, 0, 3, 0, + 1, 0, 4, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 136, 2, 0, 0, 0, 0, + 0, 0, 16, 0, 0, 0, + 2, 0, 0, 0, 96, 2, + 0, 0, 0, 0, 0, 0, + 118, 101, 99, 70, 111, 103, + 0, 171, 168, 2, 0, 0, + 0, 0, 0, 0, 16, 0, + 0, 0, 2, 0, 0, 0, + 96, 2, 0, 0, 0, 0, + 0, 0, 118, 101, 99, 87, + 86, 50, 84, 114, 97, 110, + 115, 0, 77, 105, 99, 114, + 111, 115, 111, 102, 116, 32, + 40, 82, 41, 32, 72, 76, + 83, 76, 32, 83, 104, 97, + 100, 101, 114, 32, 67, 111, + 109, 112, 105, 108, 101, 114, + 32, 49, 48, 46, 49, 0, + 73, 83, 71, 78, 76, 0, + 0, 0, 2, 0, 0, 0, + 8, 0, 0, 0, 56, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 2, 0, + 0, 0, 0, 0, 0, 0, + 15, 15, 0, 0, 65, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 2, 0, + 0, 0, 1, 0, 0, 0, + 15, 15, 0, 0, 80, 79, + 83, 73, 84, 73, 79, 78, + 0, 84, 69, 88, 67, 79, + 79, 82, 68, 0, 171, 171, + 79, 83, 71, 78, 108, 0, + 0, 0, 3, 0, 0, 0, + 8, 0, 0, 0, 80, 0, + 0, 0, 0, 0, 0, 0, + 1, 0, 0, 0, 3, 0, + 0, 0, 0, 0, 0, 0, + 15, 0, 0, 0, 92, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 3, 0, + 0, 0, 1, 0, 0, 0, + 15, 0, 0, 0, 98, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 3, 0, + 0, 0, 2, 0, 0, 0, + 15, 0, 0, 0, 83, 86, + 95, 80, 79, 83, 73, 84, + 73, 79, 78, 0, 67, 79, + 76, 79, 82, 0, 84, 69, + 88, 67, 79, 79, 82, 68, + 0, 171, 83, 72, 68, 82, + 176, 4, 0, 0, 64, 0, + 1, 0, 44, 1, 0, 0, + 89, 0, 0, 4, 70, 142, + 32, 0, 0, 0, 0, 0, + 4, 0, 0, 0, 89, 0, + 0, 4, 70, 142, 32, 0, + 2, 0, 0, 0, 4, 0, + 0, 0, 89, 0, 0, 4, + 70, 142, 32, 0, 4, 0, + 0, 0, 1, 0, 0, 0, + 89, 0, 0, 4, 70, 142, + 32, 0, 5, 0, 0, 0, + 1, 0, 0, 0, 89, 0, + 0, 4, 70, 142, 32, 0, + 8, 0, 0, 0, 1, 0, + 0, 0, 90, 0, 0, 3, + 0, 96, 16, 0, 0, 0, + 0, 0, 88, 24, 0, 4, + 0, 112, 16, 0, 0, 0, + 0, 0, 85, 85, 0, 0, + 95, 0, 0, 3, 242, 16, + 16, 0, 0, 0, 0, 0, + 95, 0, 0, 3, 242, 16, + 16, 0, 1, 0, 0, 0, + 103, 0, 0, 4, 242, 32, + 16, 0, 0, 0, 0, 0, + 1, 0, 0, 0, 101, 0, + 0, 3, 242, 32, 16, 0, + 1, 0, 0, 0, 101, 0, + 0, 3, 242, 32, 16, 0, + 2, 0, 0, 0, 104, 0, + 0, 2, 2, 0, 0, 0, + 43, 0, 0, 5, 114, 0, + 16, 0, 0, 0, 0, 0, + 70, 18, 16, 0, 0, 0, + 0, 0, 50, 0, 0, 13, + 114, 0, 16, 0, 0, 0, + 0, 0, 70, 2, 16, 0, + 0, 0, 0, 0, 2, 64, + 0, 0, 0, 0, 128, 58, + 0, 0, 128, 58, 0, 0, + 128, 58, 0, 0, 0, 0, + 70, 130, 32, 0, 8, 0, + 0, 0, 0, 0, 0, 0, + 56, 0, 0, 8, 242, 0, + 16, 0, 1, 0, 0, 0, + 86, 5, 16, 0, 0, 0, + 0, 0, 70, 142, 32, 0, + 0, 0, 0, 0, 1, 0, + 0, 0, 50, 0, 0, 10, + 242, 0, 16, 0, 1, 0, + 0, 0, 70, 142, 32, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 6, 0, 16, 0, + 0, 0, 0, 0, 70, 14, + 16, 0, 1, 0, 0, 0, + 50, 0, 0, 10, 242, 0, + 16, 0, 0, 0, 0, 0, + 70, 142, 32, 0, 0, 0, + 0, 0, 2, 0, 0, 0, + 166, 10, 16, 0, 0, 0, + 0, 0, 70, 14, 16, 0, + 1, 0, 0, 0, 0, 0, + 0, 8, 242, 0, 16, 0, + 0, 0, 0, 0, 70, 14, + 16, 0, 0, 0, 0, 0, + 70, 142, 32, 0, 0, 0, + 0, 0, 3, 0, 0, 0, + 56, 0, 0, 8, 242, 0, + 16, 0, 1, 0, 0, 0, + 86, 5, 16, 0, 0, 0, + 0, 0, 70, 142, 32, 0, + 2, 0, 0, 0, 1, 0, + 0, 0, 50, 0, 0, 10, + 242, 0, 16, 0, 1, 0, + 0, 0, 70, 142, 32, 0, + 2, 0, 0, 0, 0, 0, + 0, 0, 6, 0, 16, 0, + 0, 0, 0, 0, 70, 14, + 16, 0, 1, 0, 0, 0, + 50, 0, 0, 10, 242, 0, + 16, 0, 1, 0, 0, 0, + 70, 142, 32, 0, 2, 0, + 0, 0, 2, 0, 0, 0, + 166, 10, 16, 0, 0, 0, + 0, 0, 70, 14, 16, 0, + 1, 0, 0, 0, 50, 0, + 0, 10, 242, 32, 16, 0, + 0, 0, 0, 0, 70, 142, + 32, 0, 2, 0, 0, 0, + 3, 0, 0, 0, 246, 15, + 16, 0, 0, 0, 0, 0, + 70, 14, 16, 0, 1, 0, + 0, 0, 30, 0, 0, 7, + 18, 0, 16, 0, 0, 0, + 0, 0, 58, 16, 16, 0, + 0, 0, 0, 0, 1, 64, + 0, 0, 0, 128, 0, 0, + 43, 0, 0, 5, 18, 0, + 16, 0, 0, 0, 0, 0, + 10, 0, 16, 0, 0, 0, + 0, 0, 56, 0, 0, 10, + 178, 0, 16, 0, 0, 0, + 0, 0, 6, 0, 16, 0, + 0, 0, 0, 0, 2, 64, + 0, 0, 0, 0, 128, 55, + 0, 0, 0, 58, 0, 0, + 0, 0, 0, 0, 0, 61, + 26, 0, 0, 5, 178, 0, + 16, 0, 0, 0, 0, 0, + 70, 12, 16, 0, 0, 0, + 0, 0, 43, 0, 0, 5, + 242, 0, 16, 0, 1, 0, + 0, 0, 230, 20, 16, 0, + 1, 0, 0, 0, 56, 0, + 0, 10, 242, 0, 16, 0, + 1, 0, 0, 0, 70, 14, + 16, 0, 1, 0, 0, 0, + 2, 64, 0, 0, 0, 0, + 128, 59, 0, 0, 128, 59, + 0, 0, 0, 57, 0, 0, + 0, 57, 52, 0, 0, 8, + 50, 0, 16, 0, 1, 0, + 0, 0, 70, 0, 16, 0, + 1, 0, 0, 0, 70, 128, + 32, 0, 4, 0, 0, 0, + 0, 0, 0, 0, 54, 0, + 0, 5, 50, 32, 16, 0, + 2, 0, 0, 0, 230, 10, + 16, 0, 1, 0, 0, 0, + 26, 0, 0, 5, 50, 0, + 16, 0, 1, 0, 0, 0, + 70, 0, 16, 0, 1, 0, + 0, 0, 72, 0, 0, 11, + 242, 0, 16, 0, 1, 0, + 0, 0, 70, 0, 16, 0, + 1, 0, 0, 0, 70, 126, + 16, 0, 0, 0, 0, 0, + 0, 96, 16, 0, 0, 0, + 0, 0, 1, 64, 0, 0, + 0, 0, 0, 0, 56, 0, + 0, 7, 114, 32, 16, 0, + 1, 0, 0, 0, 70, 3, + 16, 0, 0, 0, 0, 0, + 70, 2, 16, 0, 1, 0, + 0, 0, 54, 0, 0, 5, + 130, 32, 16, 0, 1, 0, + 0, 0, 1, 64, 0, 0, + 0, 0, 128, 63, 0, 0, + 0, 8, 18, 0, 16, 0, + 0, 0, 0, 0, 42, 0, + 16, 0, 0, 0, 0, 0, + 10, 128, 32, 0, 5, 0, + 0, 0, 0, 0, 0, 0, + 56, 0, 0, 8, 34, 0, + 16, 0, 0, 0, 0, 0, + 42, 0, 16, 0, 0, 0, + 0, 0, 10, 128, 32, 0, + 5, 0, 0, 0, 0, 0, + 0, 0, 56, 0, 0, 7, + 34, 0, 16, 0, 0, 0, + 0, 0, 26, 0, 16, 0, + 0, 0, 0, 0, 1, 64, + 0, 0, 59, 170, 184, 63, + 25, 0, 0, 5, 34, 0, + 16, 0, 0, 0, 0, 0, + 26, 0, 16, 0, 0, 0, + 0, 0, 51, 0, 0, 7, + 34, 0, 16, 0, 0, 0, + 0, 0, 26, 0, 16, 0, + 0, 0, 0, 0, 1, 64, + 0, 0, 0, 0, 128, 63, + 56, 32, 0, 8, 18, 0, + 16, 0, 0, 0, 0, 0, + 10, 0, 16, 0, 0, 0, + 0, 0, 26, 128, 32, 0, + 5, 0, 0, 0, 0, 0, + 0, 0, 24, 0, 0, 11, + 194, 0, 16, 0, 0, 0, + 0, 0, 166, 138, 32, 0, + 5, 0, 0, 0, 0, 0, + 0, 0, 2, 64, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 64, + 0, 0, 128, 63, 55, 0, + 0, 9, 34, 0, 16, 0, + 0, 0, 0, 0, 42, 0, + 16, 0, 0, 0, 0, 0, + 26, 0, 16, 0, 0, 0, + 0, 0, 1, 64, 0, 0, + 0, 0, 128, 63, 55, 0, + 0, 9, 66, 32, 16, 0, + 2, 0, 0, 0, 58, 0, + 16, 0, 0, 0, 0, 0, + 10, 0, 16, 0, 0, 0, + 0, 0, 26, 0, 16, 0, + 0, 0, 0, 0, 54, 0, + 0, 5, 130, 32, 16, 0, + 2, 0, 0, 0, 1, 64, + 0, 0, 0, 0, 128, 63, + 62, 0, 0, 1, 83, 84, + 65, 84, 116, 0, 0, 0, + 33, 0, 0, 0, 2, 0, + 0, 0, 0, 0, 0, 0, + 5, 0, 0, 0, 22, 0, + 0, 0, 1, 0, 0, 0, + 0, 0, 0, 0, 1, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 1, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 3, 0, + 0, 0, 2, 0, 0, 0, + 5, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0 +}; diff --git a/Minecraft.Client/Windows64/4JLibs/inc/Render/VS_PF3_TF2_CB4_NB4_XW1.h b/Minecraft.Client/Windows64/4JLibs/inc/Render/VS_PF3_TF2_CB4_NB4_XW1.h new file mode 100644 index 000000000..bd6d4d88d --- /dev/null +++ b/Minecraft.Client/Windows64/4JLibs/inc/Render/VS_PF3_TF2_CB4_NB4_XW1.h @@ -0,0 +1,609 @@ +#if 0 +// +// Generated by Microsoft (R) HLSL Shader Compiler 10.1 +// +// +// Buffer Definitions: +// +// cbuffer positionTransformWV +// { +// +// float4x4 matWorldView; // Offset: 0 Size: 64 +// +// } +// +// cbuffer positionTransformWV2 +// { +// +// float4x4 matWorldView2; // Offset: 0 Size: 64 +// +// } +// +// cbuffer positionTransformProj +// { +// +// float4x4 matProjection; // Offset: 0 Size: 64 +// +// } +// +// cbuffer textureTransform +// { +// +// float4x4 matUV; // Offset: 0 Size: 64 +// +// } +// +// cbuffer textureUV2 +// { +// +// float4 vecUVT2; // Offset: 0 Size: 16 +// +// } +// +// cbuffer fog +// { +// +// float4 vecFog; // Offset: 0 Size: 16 +// +// } +// +// +// Resource Bindings: +// +// Name Type Format Dim HLSL Bind Count +// ------------------------------ ---------- ------- ----------- -------------- ------ +// light_sampler_s sampler NA NA s0 1 +// light_texture texture float4 2d t0 1 +// positionTransformWV cbuffer NA NA cb0 1 +// positionTransformWV2 cbuffer NA NA cb1 1 +// positionTransformProj cbuffer NA NA cb2 1 +// textureTransform cbuffer NA NA cb3 1 +// textureUV2 cbuffer NA NA cb4 1 +// fog cbuffer NA NA cb5 1 +// +// +// +// Input signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------- ------ +// POSITION 0 xyzw 0 NONE float xyzw +// COLOR 0 xyzw 1 NONE float xyzw +// NORMAL 0 xyz 2 NONE float +// TEXCOORD 0 xyzw 3 NONE float xyzw +// TEXCOORD 1 xy 4 NONE int xy +// +// +// Output signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------- ------ +// SV_POSITION 0 xyzw 0 POS float xyzw +// COLOR 0 xyzw 1 NONE float xyzw +// TEXCOORD 0 xyzw 2 NONE float xyzw +// +vs_4_0 +dcl_constantbuffer CB0[4], immediateIndexed +dcl_constantbuffer CB1[4], immediateIndexed +dcl_constantbuffer CB2[4], immediateIndexed +dcl_constantbuffer CB3[4], immediateIndexed +dcl_constantbuffer CB4[1], immediateIndexed +dcl_constantbuffer CB5[1], immediateIndexed +dcl_sampler s0, mode_default +dcl_resource_texture2d (float,float,float,float) t0 +dcl_input v0.xyzw +dcl_input v1.xyzw +dcl_input v3.xyzw +dcl_input v4.xy +dcl_output_siv o0.xyzw, position +dcl_output o1.xyzw +dcl_output o2.xyzw +dcl_temps 2 +mul r0.xyzw, v0.yyyy, cb1[1].xyzw +mad r0.xyzw, cb1[0].xyzw, v0.xxxx, r0.xyzw +mad r0.xyzw, cb1[2].xyzw, v0.zzzz, r0.xyzw +mad r0.xyzw, cb1[3].xyzw, v0.wwww, r0.xyzw +mul r1.xyzw, r0.yyyy, cb0[1].xyzw +mad r1.xyzw, cb0[0].xyzw, r0.xxxx, r1.xyzw +mad r1.xyzw, cb0[2].xyzw, r0.zzzz, r1.xyzw +mad r0.xyzw, cb0[3].xyzw, r0.wwww, r1.xyzw +mul r1.xyzw, r0.yyyy, cb2[1].xyzw +mad r1.xyzw, cb2[0].xyzw, r0.xxxx, r1.xyzw +mad r1.xyzw, cb2[2].xyzw, r0.zzzz, r1.xyzw +mad o0.xyzw, cb2[3].xyzw, r0.wwww, r1.xyzw +itof r0.xy, v4.xyxx +mul r0.xy, r0.xyxx, l(0.003906, 0.003906, 0.000000, 0.000000) +max r0.xy, r0.xyxx, cb4[0].xyxx +frc r0.xy, r0.xyxx +sample_l r1.xyzw, r0.xyxx, t0.xyzw, s0, l(0.000000) +mov r1.w, l(1.000000) +mul o1.xyzw, r1.xyzw, v1.wzyx +add r0.x, r0.z, cb5[0].x +mul r0.y, r0.z, cb5[0].x +mul r0.y, r0.y, l(1.442695) +exp r0.y, r0.y +min r0.y, r0.y, l(1.000000) +mul_sat r0.x, r0.x, cb5[0].y +eq r0.zw, cb5[0].zzzz, l(0.000000, 0.000000, 2.000000, 1.000000) +movc r0.y, r0.z, r0.y, l(1.000000) +movc o2.z, r0.w, r0.x, r0.y +mov r0.x, cb3[0].x +mov r0.y, cb3[1].x +mov r0.z, cb3[2].x +mov r0.w, cb3[3].x +dp4 o2.x, r0.xyzw, v3.xyzw +mov r0.x, cb3[0].y +mov r0.y, cb3[1].y +mov r0.z, cb3[2].y +mov r0.w, cb3[3].y +dp4 o2.y, r0.xyzw, v3.xyzw +mov o2.w, l(1.000000) +ret +// Approximately 40 instruction slots used +#endif + +const BYTE g_main_VS_PF3_TF2_CB4_NB4_XW1[] = +{ + 68, 88, 66, 67, 12, 241, + 96, 96, 178, 195, 206, 202, + 179, 226, 12, 92, 63, 252, + 30, 185, 1, 0, 0, 0, + 208, 10, 0, 0, 5, 0, + 0, 0, 52, 0, 0, 0, + 136, 3, 0, 0, 48, 4, + 0, 0, 164, 4, 0, 0, + 84, 10, 0, 0, 82, 68, + 69, 70, 76, 3, 0, 0, + 6, 0, 0, 0, 156, 1, + 0, 0, 8, 0, 0, 0, + 28, 0, 0, 0, 0, 4, + 254, 255, 0, 129, 0, 0, + 35, 3, 0, 0, 28, 1, + 0, 0, 3, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 1, 0, + 0, 0, 1, 0, 0, 0, + 44, 1, 0, 0, 2, 0, + 0, 0, 5, 0, 0, 0, + 4, 0, 0, 0, 255, 255, + 255, 255, 0, 0, 0, 0, + 1, 0, 0, 0, 13, 0, + 0, 0, 58, 1, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 1, 0, 0, 0, + 1, 0, 0, 0, 78, 1, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 1, 0, 0, 0, 1, 0, + 0, 0, 1, 0, 0, 0, + 99, 1, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 2, 0, 0, 0, + 1, 0, 0, 0, 1, 0, + 0, 0, 121, 1, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 3, 0, + 0, 0, 1, 0, 0, 0, + 1, 0, 0, 0, 138, 1, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 4, 0, 0, 0, 1, 0, + 0, 0, 1, 0, 0, 0, + 149, 1, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 5, 0, 0, 0, + 1, 0, 0, 0, 1, 0, + 0, 0, 108, 105, 103, 104, + 116, 95, 115, 97, 109, 112, + 108, 101, 114, 95, 115, 0, + 108, 105, 103, 104, 116, 95, + 116, 101, 120, 116, 117, 114, + 101, 0, 112, 111, 115, 105, + 116, 105, 111, 110, 84, 114, + 97, 110, 115, 102, 111, 114, + 109, 87, 86, 0, 112, 111, + 115, 105, 116, 105, 111, 110, + 84, 114, 97, 110, 115, 102, + 111, 114, 109, 87, 86, 50, + 0, 112, 111, 115, 105, 116, + 105, 111, 110, 84, 114, 97, + 110, 115, 102, 111, 114, 109, + 80, 114, 111, 106, 0, 116, + 101, 120, 116, 117, 114, 101, + 84, 114, 97, 110, 115, 102, + 111, 114, 109, 0, 116, 101, + 120, 116, 117, 114, 101, 85, + 86, 50, 0, 102, 111, 103, + 0, 171, 171, 171, 58, 1, + 0, 0, 1, 0, 0, 0, + 44, 2, 0, 0, 64, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 78, 1, + 0, 0, 1, 0, 0, 0, + 100, 2, 0, 0, 64, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 99, 1, + 0, 0, 1, 0, 0, 0, + 140, 2, 0, 0, 64, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 121, 1, + 0, 0, 1, 0, 0, 0, + 180, 2, 0, 0, 64, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 138, 1, + 0, 0, 1, 0, 0, 0, + 212, 2, 0, 0, 16, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 149, 1, + 0, 0, 1, 0, 0, 0, + 4, 3, 0, 0, 16, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 68, 2, + 0, 0, 0, 0, 0, 0, + 64, 0, 0, 0, 2, 0, + 0, 0, 84, 2, 0, 0, + 0, 0, 0, 0, 109, 97, + 116, 87, 111, 114, 108, 100, + 86, 105, 101, 119, 0, 171, + 171, 171, 3, 0, 3, 0, + 4, 0, 4, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 124, 2, 0, 0, 0, 0, + 0, 0, 64, 0, 0, 0, + 2, 0, 0, 0, 84, 2, + 0, 0, 0, 0, 0, 0, + 109, 97, 116, 87, 111, 114, + 108, 100, 86, 105, 101, 119, + 50, 0, 171, 171, 164, 2, + 0, 0, 0, 0, 0, 0, + 64, 0, 0, 0, 2, 0, + 0, 0, 84, 2, 0, 0, + 0, 0, 0, 0, 109, 97, + 116, 80, 114, 111, 106, 101, + 99, 116, 105, 111, 110, 0, + 171, 171, 204, 2, 0, 0, + 0, 0, 0, 0, 64, 0, + 0, 0, 2, 0, 0, 0, + 84, 2, 0, 0, 0, 0, + 0, 0, 109, 97, 116, 85, + 86, 0, 171, 171, 236, 2, + 0, 0, 0, 0, 0, 0, + 16, 0, 0, 0, 2, 0, + 0, 0, 244, 2, 0, 0, + 0, 0, 0, 0, 118, 101, + 99, 85, 86, 84, 50, 0, + 1, 0, 3, 0, 1, 0, + 4, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 28, 3, + 0, 0, 0, 0, 0, 0, + 16, 0, 0, 0, 2, 0, + 0, 0, 244, 2, 0, 0, + 0, 0, 0, 0, 118, 101, + 99, 70, 111, 103, 0, 77, + 105, 99, 114, 111, 115, 111, + 102, 116, 32, 40, 82, 41, + 32, 72, 76, 83, 76, 32, + 83, 104, 97, 100, 101, 114, + 32, 67, 111, 109, 112, 105, + 108, 101, 114, 32, 49, 48, + 46, 49, 0, 171, 73, 83, + 71, 78, 160, 0, 0, 0, + 5, 0, 0, 0, 8, 0, + 0, 0, 128, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 3, 0, 0, 0, + 0, 0, 0, 0, 15, 15, + 0, 0, 137, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 3, 0, 0, 0, + 1, 0, 0, 0, 15, 15, + 0, 0, 143, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 3, 0, 0, 0, + 2, 0, 0, 0, 7, 0, + 0, 0, 150, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 3, 0, 0, 0, + 3, 0, 0, 0, 15, 15, + 0, 0, 150, 0, 0, 0, + 1, 0, 0, 0, 0, 0, + 0, 0, 2, 0, 0, 0, + 4, 0, 0, 0, 3, 3, + 0, 0, 80, 79, 83, 73, + 84, 73, 79, 78, 0, 67, + 79, 76, 79, 82, 0, 78, + 79, 82, 77, 65, 76, 0, + 84, 69, 88, 67, 79, 79, + 82, 68, 0, 171, 79, 83, + 71, 78, 108, 0, 0, 0, + 3, 0, 0, 0, 8, 0, + 0, 0, 80, 0, 0, 0, + 0, 0, 0, 0, 1, 0, + 0, 0, 3, 0, 0, 0, + 0, 0, 0, 0, 15, 0, + 0, 0, 92, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 3, 0, 0, 0, + 1, 0, 0, 0, 15, 0, + 0, 0, 98, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 3, 0, 0, 0, + 2, 0, 0, 0, 15, 0, + 0, 0, 83, 86, 95, 80, + 79, 83, 73, 84, 73, 79, + 78, 0, 67, 79, 76, 79, + 82, 0, 84, 69, 88, 67, + 79, 79, 82, 68, 0, 171, + 83, 72, 68, 82, 168, 5, + 0, 0, 64, 0, 1, 0, + 106, 1, 0, 0, 89, 0, + 0, 4, 70, 142, 32, 0, + 0, 0, 0, 0, 4, 0, + 0, 0, 89, 0, 0, 4, + 70, 142, 32, 0, 1, 0, + 0, 0, 4, 0, 0, 0, + 89, 0, 0, 4, 70, 142, + 32, 0, 2, 0, 0, 0, + 4, 0, 0, 0, 89, 0, + 0, 4, 70, 142, 32, 0, + 3, 0, 0, 0, 4, 0, + 0, 0, 89, 0, 0, 4, + 70, 142, 32, 0, 4, 0, + 0, 0, 1, 0, 0, 0, + 89, 0, 0, 4, 70, 142, + 32, 0, 5, 0, 0, 0, + 1, 0, 0, 0, 90, 0, + 0, 3, 0, 96, 16, 0, + 0, 0, 0, 0, 88, 24, + 0, 4, 0, 112, 16, 0, + 0, 0, 0, 0, 85, 85, + 0, 0, 95, 0, 0, 3, + 242, 16, 16, 0, 0, 0, + 0, 0, 95, 0, 0, 3, + 242, 16, 16, 0, 1, 0, + 0, 0, 95, 0, 0, 3, + 242, 16, 16, 0, 3, 0, + 0, 0, 95, 0, 0, 3, + 50, 16, 16, 0, 4, 0, + 0, 0, 103, 0, 0, 4, + 242, 32, 16, 0, 0, 0, + 0, 0, 1, 0, 0, 0, + 101, 0, 0, 3, 242, 32, + 16, 0, 1, 0, 0, 0, + 101, 0, 0, 3, 242, 32, + 16, 0, 2, 0, 0, 0, + 104, 0, 0, 2, 2, 0, + 0, 0, 56, 0, 0, 8, + 242, 0, 16, 0, 0, 0, + 0, 0, 86, 21, 16, 0, + 0, 0, 0, 0, 70, 142, + 32, 0, 1, 0, 0, 0, + 1, 0, 0, 0, 50, 0, + 0, 10, 242, 0, 16, 0, + 0, 0, 0, 0, 70, 142, + 32, 0, 1, 0, 0, 0, + 0, 0, 0, 0, 6, 16, + 16, 0, 0, 0, 0, 0, + 70, 14, 16, 0, 0, 0, + 0, 0, 50, 0, 0, 10, + 242, 0, 16, 0, 0, 0, + 0, 0, 70, 142, 32, 0, + 1, 0, 0, 0, 2, 0, + 0, 0, 166, 26, 16, 0, + 0, 0, 0, 0, 70, 14, + 16, 0, 0, 0, 0, 0, + 50, 0, 0, 10, 242, 0, + 16, 0, 0, 0, 0, 0, + 70, 142, 32, 0, 1, 0, + 0, 0, 3, 0, 0, 0, + 246, 31, 16, 0, 0, 0, + 0, 0, 70, 14, 16, 0, + 0, 0, 0, 0, 56, 0, + 0, 8, 242, 0, 16, 0, + 1, 0, 0, 0, 86, 5, + 16, 0, 0, 0, 0, 0, + 70, 142, 32, 0, 0, 0, + 0, 0, 1, 0, 0, 0, + 50, 0, 0, 10, 242, 0, + 16, 0, 1, 0, 0, 0, + 70, 142, 32, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 6, 0, 16, 0, 0, 0, + 0, 0, 70, 14, 16, 0, + 1, 0, 0, 0, 50, 0, + 0, 10, 242, 0, 16, 0, + 1, 0, 0, 0, 70, 142, + 32, 0, 0, 0, 0, 0, + 2, 0, 0, 0, 166, 10, + 16, 0, 0, 0, 0, 0, + 70, 14, 16, 0, 1, 0, + 0, 0, 50, 0, 0, 10, + 242, 0, 16, 0, 0, 0, + 0, 0, 70, 142, 32, 0, + 0, 0, 0, 0, 3, 0, + 0, 0, 246, 15, 16, 0, + 0, 0, 0, 0, 70, 14, + 16, 0, 1, 0, 0, 0, + 56, 0, 0, 8, 242, 0, + 16, 0, 1, 0, 0, 0, + 86, 5, 16, 0, 0, 0, + 0, 0, 70, 142, 32, 0, + 2, 0, 0, 0, 1, 0, + 0, 0, 50, 0, 0, 10, + 242, 0, 16, 0, 1, 0, + 0, 0, 70, 142, 32, 0, + 2, 0, 0, 0, 0, 0, + 0, 0, 6, 0, 16, 0, + 0, 0, 0, 0, 70, 14, + 16, 0, 1, 0, 0, 0, + 50, 0, 0, 10, 242, 0, + 16, 0, 1, 0, 0, 0, + 70, 142, 32, 0, 2, 0, + 0, 0, 2, 0, 0, 0, + 166, 10, 16, 0, 0, 0, + 0, 0, 70, 14, 16, 0, + 1, 0, 0, 0, 50, 0, + 0, 10, 242, 32, 16, 0, + 0, 0, 0, 0, 70, 142, + 32, 0, 2, 0, 0, 0, + 3, 0, 0, 0, 246, 15, + 16, 0, 0, 0, 0, 0, + 70, 14, 16, 0, 1, 0, + 0, 0, 43, 0, 0, 5, + 50, 0, 16, 0, 0, 0, + 0, 0, 70, 16, 16, 0, + 4, 0, 0, 0, 56, 0, + 0, 10, 50, 0, 16, 0, + 0, 0, 0, 0, 70, 0, + 16, 0, 0, 0, 0, 0, + 2, 64, 0, 0, 0, 0, + 128, 59, 0, 0, 128, 59, + 0, 0, 0, 0, 0, 0, + 0, 0, 52, 0, 0, 8, + 50, 0, 16, 0, 0, 0, + 0, 0, 70, 0, 16, 0, + 0, 0, 0, 0, 70, 128, + 32, 0, 4, 0, 0, 0, + 0, 0, 0, 0, 26, 0, + 0, 5, 50, 0, 16, 0, + 0, 0, 0, 0, 70, 0, + 16, 0, 0, 0, 0, 0, + 72, 0, 0, 11, 242, 0, + 16, 0, 1, 0, 0, 0, + 70, 0, 16, 0, 0, 0, + 0, 0, 70, 126, 16, 0, + 0, 0, 0, 0, 0, 96, + 16, 0, 0, 0, 0, 0, + 1, 64, 0, 0, 0, 0, + 0, 0, 54, 0, 0, 5, + 130, 0, 16, 0, 1, 0, + 0, 0, 1, 64, 0, 0, + 0, 0, 128, 63, 56, 0, + 0, 7, 242, 32, 16, 0, + 1, 0, 0, 0, 70, 14, + 16, 0, 1, 0, 0, 0, + 182, 17, 16, 0, 1, 0, + 0, 0, 0, 0, 0, 8, + 18, 0, 16, 0, 0, 0, + 0, 0, 42, 0, 16, 0, + 0, 0, 0, 0, 10, 128, + 32, 0, 5, 0, 0, 0, + 0, 0, 0, 0, 56, 0, + 0, 8, 34, 0, 16, 0, + 0, 0, 0, 0, 42, 0, + 16, 0, 0, 0, 0, 0, + 10, 128, 32, 0, 5, 0, + 0, 0, 0, 0, 0, 0, + 56, 0, 0, 7, 34, 0, + 16, 0, 0, 0, 0, 0, + 26, 0, 16, 0, 0, 0, + 0, 0, 1, 64, 0, 0, + 59, 170, 184, 63, 25, 0, + 0, 5, 34, 0, 16, 0, + 0, 0, 0, 0, 26, 0, + 16, 0, 0, 0, 0, 0, + 51, 0, 0, 7, 34, 0, + 16, 0, 0, 0, 0, 0, + 26, 0, 16, 0, 0, 0, + 0, 0, 1, 64, 0, 0, + 0, 0, 128, 63, 56, 32, + 0, 8, 18, 0, 16, 0, + 0, 0, 0, 0, 10, 0, + 16, 0, 0, 0, 0, 0, + 26, 128, 32, 0, 5, 0, + 0, 0, 0, 0, 0, 0, + 24, 0, 0, 11, 194, 0, + 16, 0, 0, 0, 0, 0, + 166, 138, 32, 0, 5, 0, + 0, 0, 0, 0, 0, 0, + 2, 64, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 64, 0, 0, + 128, 63, 55, 0, 0, 9, + 34, 0, 16, 0, 0, 0, + 0, 0, 42, 0, 16, 0, + 0, 0, 0, 0, 26, 0, + 16, 0, 0, 0, 0, 0, + 1, 64, 0, 0, 0, 0, + 128, 63, 55, 0, 0, 9, + 66, 32, 16, 0, 2, 0, + 0, 0, 58, 0, 16, 0, + 0, 0, 0, 0, 10, 0, + 16, 0, 0, 0, 0, 0, + 26, 0, 16, 0, 0, 0, + 0, 0, 54, 0, 0, 6, + 18, 0, 16, 0, 0, 0, + 0, 0, 10, 128, 32, 0, + 3, 0, 0, 0, 0, 0, + 0, 0, 54, 0, 0, 6, + 34, 0, 16, 0, 0, 0, + 0, 0, 10, 128, 32, 0, + 3, 0, 0, 0, 1, 0, + 0, 0, 54, 0, 0, 6, + 66, 0, 16, 0, 0, 0, + 0, 0, 10, 128, 32, 0, + 3, 0, 0, 0, 2, 0, + 0, 0, 54, 0, 0, 6, + 130, 0, 16, 0, 0, 0, + 0, 0, 10, 128, 32, 0, + 3, 0, 0, 0, 3, 0, + 0, 0, 17, 0, 0, 7, + 18, 32, 16, 0, 2, 0, + 0, 0, 70, 14, 16, 0, + 0, 0, 0, 0, 70, 30, + 16, 0, 3, 0, 0, 0, + 54, 0, 0, 6, 18, 0, + 16, 0, 0, 0, 0, 0, + 26, 128, 32, 0, 3, 0, + 0, 0, 0, 0, 0, 0, + 54, 0, 0, 6, 34, 0, + 16, 0, 0, 0, 0, 0, + 26, 128, 32, 0, 3, 0, + 0, 0, 1, 0, 0, 0, + 54, 0, 0, 6, 66, 0, + 16, 0, 0, 0, 0, 0, + 26, 128, 32, 0, 3, 0, + 0, 0, 2, 0, 0, 0, + 54, 0, 0, 6, 130, 0, + 16, 0, 0, 0, 0, 0, + 26, 128, 32, 0, 3, 0, + 0, 0, 3, 0, 0, 0, + 17, 0, 0, 7, 34, 32, + 16, 0, 2, 0, 0, 0, + 70, 14, 16, 0, 0, 0, + 0, 0, 70, 30, 16, 0, + 3, 0, 0, 0, 54, 0, + 0, 5, 130, 32, 16, 0, + 2, 0, 0, 0, 1, 64, + 0, 0, 0, 0, 128, 63, + 62, 0, 0, 1, 83, 84, + 65, 84, 116, 0, 0, 0, + 40, 0, 0, 0, 2, 0, + 0, 0, 0, 0, 0, 0, + 7, 0, 0, 0, 25, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 1, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 1, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 10, 0, + 0, 0, 2, 0, 0, 0, + 2, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0 +}; diff --git a/Minecraft.Client/Windows64/4JLibs/inc/Render/VS_PF3_TF2_CB4_NB4_XW1_LIGHTING.h b/Minecraft.Client/Windows64/4JLibs/inc/Render/VS_PF3_TF2_CB4_NB4_XW1_LIGHTING.h new file mode 100644 index 000000000..22c190c92 --- /dev/null +++ b/Minecraft.Client/Windows64/4JLibs/inc/Render/VS_PF3_TF2_CB4_NB4_XW1_LIGHTING.h @@ -0,0 +1,777 @@ +#if 0 +// +// Generated by Microsoft (R) HLSL Shader Compiler 10.1 +// +// +// Buffer Definitions: +// +// cbuffer positionTransformWV +// { +// +// float4x4 matWorldView; // Offset: 0 Size: 64 +// +// } +// +// cbuffer positionTransformWV2 +// { +// +// float4x4 matWorldView2; // Offset: 0 Size: 64 +// +// } +// +// cbuffer positionTransformProj +// { +// +// float4x4 matProjection; // Offset: 0 Size: 64 +// +// } +// +// cbuffer textureTransform +// { +// +// float4x4 matUV; // Offset: 0 Size: 64 +// +// } +// +// cbuffer textureUV2 +// { +// +// float4 vecUVT2; // Offset: 0 Size: 16 +// +// } +// +// cbuffer fog +// { +// +// float4 vecFog; // Offset: 0 Size: 16 +// +// } +// +// cbuffer lighting +// { +// +// float3 vecLight0; // Offset: 0 Size: 12 +// float3 vecLight1; // Offset: 16 Size: 12 +// float4 vecLight0Col; // Offset: 32 Size: 16 +// float4 vecLight1Col; // Offset: 48 Size: 16 +// float4 vecLightAmbientCol; // Offset: 64 Size: 16 +// +// } +// +// +// Resource Bindings: +// +// Name Type Format Dim HLSL Bind Count +// ------------------------------ ---------- ------- ----------- -------------- ------ +// light_sampler_s sampler NA NA s0 1 +// light_texture texture float4 2d t0 1 +// positionTransformWV cbuffer NA NA cb0 1 +// positionTransformWV2 cbuffer NA NA cb1 1 +// positionTransformProj cbuffer NA NA cb2 1 +// textureTransform cbuffer NA NA cb3 1 +// textureUV2 cbuffer NA NA cb4 1 +// fog cbuffer NA NA cb5 1 +// lighting cbuffer NA NA cb6 1 +// +// +// +// Input signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------- ------ +// POSITION 0 xyzw 0 NONE float xyzw +// COLOR 0 xyzw 1 NONE float yzw +// NORMAL 0 xyz 2 NONE float xyz +// TEXCOORD 0 xyzw 3 NONE float xyzw +// TEXCOORD 1 xy 4 NONE int xy +// +// +// Output signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------- ------ +// SV_POSITION 0 xyzw 0 POS float xyzw +// COLOR 0 xyzw 1 NONE float xyzw +// TEXCOORD 0 xyzw 2 NONE float xyzw +// +vs_4_0 +dcl_constantbuffer CB0[4], immediateIndexed +dcl_constantbuffer CB1[4], immediateIndexed +dcl_constantbuffer CB2[4], immediateIndexed +dcl_constantbuffer CB3[4], immediateIndexed +dcl_constantbuffer CB4[1], immediateIndexed +dcl_constantbuffer CB5[1], immediateIndexed +dcl_constantbuffer CB6[5], immediateIndexed +dcl_sampler s0, mode_default +dcl_resource_texture2d (float,float,float,float) t0 +dcl_input v0.xyzw +dcl_input v1.yzw +dcl_input v2.xyz +dcl_input v3.xyzw +dcl_input v4.xy +dcl_output_siv o0.xyzw, position +dcl_output o1.xyzw +dcl_output o2.xyzw +dcl_temps 3 +mul r0.xyzw, v0.yyyy, cb1[1].xyzw +mad r0.xyzw, cb1[0].xyzw, v0.xxxx, r0.xyzw +mad r0.xyzw, cb1[2].xyzw, v0.zzzz, r0.xyzw +mad r0.xyzw, cb1[3].xyzw, v0.wwww, r0.xyzw +mul r1.xyzw, r0.yyyy, cb0[1].xyzw +mad r1.xyzw, cb0[0].xyzw, r0.xxxx, r1.xyzw +mad r1.xyzw, cb0[2].xyzw, r0.zzzz, r1.xyzw +mad r0.xyzw, cb0[3].xyzw, r0.wwww, r1.xyzw +mul r1.xyzw, r0.yyyy, cb2[1].xyzw +mad r1.xyzw, cb2[0].xyzw, r0.xxxx, r1.xyzw +mad r1.xyzw, cb2[2].xyzw, r0.zzzz, r1.xyzw +mad o0.xyzw, cb2[3].xyzw, r0.wwww, r1.xyzw +mul r0.xyw, v2.yyyy, cb1[1].xyxz +mad r0.xyw, cb1[0].xyxz, v2.xxxx, r0.xyxw +mad r0.xyw, cb1[2].xyxz, v2.zzzz, r0.xyxw +mul r1.xyz, r0.yyyy, cb0[1].xyzx +mad r1.xyz, cb0[0].xyzx, r0.xxxx, r1.xyzx +mad r0.xyw, cb0[2].xyxz, r0.wwww, r1.xyxz +dp3 r1.x, r0.xywx, r0.xywx +rsq r1.x, r1.x +mul r0.xyw, r0.xyxw, r1.xxxx +dp3 r1.x, cb6[0].xyzx, r0.xywx +dp3 r0.x, cb6[1].xyzx, r0.xywx +max r0.x, r0.x, l(0.000000) +max r0.y, r1.x, l(0.000000) +mad r1.xyzw, r0.yyyy, cb6[2].xyzw, cb6[4].xyzw +mad_sat r1.xyzw, r0.xxxx, cb6[3].xyzw, r1.xyzw +itof r0.xy, v4.xyxx +mul r0.xy, r0.xyxx, l(0.003906, 0.003906, 0.000000, 0.000000) +max r0.xy, r0.xyxx, cb4[0].xyxx +frc r0.xy, r0.xyxx +sample_l r2.xyzw, r0.xyxx, t0.xyzw, s0, l(0.000000) +mul r0.xyw, r2.xyxz, v1.wzwy +mul o1.xyz, r0.xywx, r1.xyzx +mov o1.w, r1.w +add r0.x, r0.z, cb5[0].x +mul r0.y, r0.z, cb5[0].x +mul r0.y, r0.y, l(1.442695) +exp r0.y, r0.y +min r0.y, r0.y, l(1.000000) +mul_sat r0.x, r0.x, cb5[0].y +eq r0.zw, cb5[0].zzzz, l(0.000000, 0.000000, 2.000000, 1.000000) +movc r0.y, r0.z, r0.y, l(1.000000) +movc o2.z, r0.w, r0.x, r0.y +mov r0.x, cb3[0].x +mov r0.y, cb3[1].x +mov r0.z, cb3[2].x +mov r0.w, cb3[3].x +dp4 o2.x, r0.xyzw, v3.xyzw +mov r0.x, cb3[0].y +mov r0.y, cb3[1].y +mov r0.z, cb3[2].y +mov r0.w, cb3[3].y +dp4 o2.y, r0.xyzw, v3.xyzw +mov o2.w, l(1.000000) +ret +// Approximately 56 instruction slots used +#endif + +const BYTE g_main_VS_PF3_TF2_CB4_NB4_XW1_LIGHTING[] = +{ + 68, 88, 66, 67, 51, 218, + 224, 13, 10, 69, 75, 75, + 52, 148, 15, 146, 244, 231, + 206, 110, 1, 0, 0, 0, + 12, 14, 0, 0, 5, 0, + 0, 0, 52, 0, 0, 0, + 148, 4, 0, 0, 60, 5, + 0, 0, 176, 5, 0, 0, + 144, 13, 0, 0, 82, 68, + 69, 70, 88, 4, 0, 0, + 7, 0, 0, 0, 196, 1, + 0, 0, 9, 0, 0, 0, + 28, 0, 0, 0, 0, 4, + 254, 255, 0, 129, 0, 0, + 47, 4, 0, 0, 60, 1, + 0, 0, 3, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 1, 0, + 0, 0, 1, 0, 0, 0, + 76, 1, 0, 0, 2, 0, + 0, 0, 5, 0, 0, 0, + 4, 0, 0, 0, 255, 255, + 255, 255, 0, 0, 0, 0, + 1, 0, 0, 0, 13, 0, + 0, 0, 90, 1, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 1, 0, 0, 0, + 1, 0, 0, 0, 110, 1, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 1, 0, 0, 0, 1, 0, + 0, 0, 1, 0, 0, 0, + 131, 1, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 2, 0, 0, 0, + 1, 0, 0, 0, 1, 0, + 0, 0, 153, 1, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 3, 0, + 0, 0, 1, 0, 0, 0, + 1, 0, 0, 0, 170, 1, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 4, 0, 0, 0, 1, 0, + 0, 0, 1, 0, 0, 0, + 181, 1, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 5, 0, 0, 0, + 1, 0, 0, 0, 1, 0, + 0, 0, 185, 1, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 6, 0, + 0, 0, 1, 0, 0, 0, + 1, 0, 0, 0, 108, 105, + 103, 104, 116, 95, 115, 97, + 109, 112, 108, 101, 114, 95, + 115, 0, 108, 105, 103, 104, + 116, 95, 116, 101, 120, 116, + 117, 114, 101, 0, 112, 111, + 115, 105, 116, 105, 111, 110, + 84, 114, 97, 110, 115, 102, + 111, 114, 109, 87, 86, 0, + 112, 111, 115, 105, 116, 105, + 111, 110, 84, 114, 97, 110, + 115, 102, 111, 114, 109, 87, + 86, 50, 0, 112, 111, 115, + 105, 116, 105, 111, 110, 84, + 114, 97, 110, 115, 102, 111, + 114, 109, 80, 114, 111, 106, + 0, 116, 101, 120, 116, 117, + 114, 101, 84, 114, 97, 110, + 115, 102, 111, 114, 109, 0, + 116, 101, 120, 116, 117, 114, + 101, 85, 86, 50, 0, 102, + 111, 103, 0, 108, 105, 103, + 104, 116, 105, 110, 103, 0, + 171, 171, 90, 1, 0, 0, + 1, 0, 0, 0, 108, 2, + 0, 0, 64, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 110, 1, 0, 0, + 1, 0, 0, 0, 164, 2, + 0, 0, 64, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 131, 1, 0, 0, + 1, 0, 0, 0, 204, 2, + 0, 0, 64, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 153, 1, 0, 0, + 1, 0, 0, 0, 244, 2, + 0, 0, 64, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 170, 1, 0, 0, + 1, 0, 0, 0, 20, 3, + 0, 0, 16, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 181, 1, 0, 0, + 1, 0, 0, 0, 68, 3, + 0, 0, 16, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 185, 1, 0, 0, + 5, 0, 0, 0, 100, 3, + 0, 0, 80, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 132, 2, 0, 0, + 0, 0, 0, 0, 64, 0, + 0, 0, 2, 0, 0, 0, + 148, 2, 0, 0, 0, 0, + 0, 0, 109, 97, 116, 87, + 111, 114, 108, 100, 86, 105, + 101, 119, 0, 171, 171, 171, + 3, 0, 3, 0, 4, 0, + 4, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 188, 2, + 0, 0, 0, 0, 0, 0, + 64, 0, 0, 0, 2, 0, + 0, 0, 148, 2, 0, 0, + 0, 0, 0, 0, 109, 97, + 116, 87, 111, 114, 108, 100, + 86, 105, 101, 119, 50, 0, + 171, 171, 228, 2, 0, 0, + 0, 0, 0, 0, 64, 0, + 0, 0, 2, 0, 0, 0, + 148, 2, 0, 0, 0, 0, + 0, 0, 109, 97, 116, 80, + 114, 111, 106, 101, 99, 116, + 105, 111, 110, 0, 171, 171, + 12, 3, 0, 0, 0, 0, + 0, 0, 64, 0, 0, 0, + 2, 0, 0, 0, 148, 2, + 0, 0, 0, 0, 0, 0, + 109, 97, 116, 85, 86, 0, + 171, 171, 44, 3, 0, 0, + 0, 0, 0, 0, 16, 0, + 0, 0, 2, 0, 0, 0, + 52, 3, 0, 0, 0, 0, + 0, 0, 118, 101, 99, 85, + 86, 84, 50, 0, 1, 0, + 3, 0, 1, 0, 4, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 92, 3, 0, 0, + 0, 0, 0, 0, 16, 0, + 0, 0, 2, 0, 0, 0, + 52, 3, 0, 0, 0, 0, + 0, 0, 118, 101, 99, 70, + 111, 103, 0, 171, 220, 3, + 0, 0, 0, 0, 0, 0, + 12, 0, 0, 0, 2, 0, + 0, 0, 232, 3, 0, 0, + 0, 0, 0, 0, 248, 3, + 0, 0, 16, 0, 0, 0, + 12, 0, 0, 0, 2, 0, + 0, 0, 232, 3, 0, 0, + 0, 0, 0, 0, 2, 4, + 0, 0, 32, 0, 0, 0, + 16, 0, 0, 0, 2, 0, + 0, 0, 52, 3, 0, 0, + 0, 0, 0, 0, 15, 4, + 0, 0, 48, 0, 0, 0, + 16, 0, 0, 0, 2, 0, + 0, 0, 52, 3, 0, 0, + 0, 0, 0, 0, 28, 4, + 0, 0, 64, 0, 0, 0, + 16, 0, 0, 0, 2, 0, + 0, 0, 52, 3, 0, 0, + 0, 0, 0, 0, 118, 101, + 99, 76, 105, 103, 104, 116, + 48, 0, 171, 171, 1, 0, + 3, 0, 1, 0, 3, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 118, 101, 99, 76, + 105, 103, 104, 116, 49, 0, + 118, 101, 99, 76, 105, 103, + 104, 116, 48, 67, 111, 108, + 0, 118, 101, 99, 76, 105, + 103, 104, 116, 49, 67, 111, + 108, 0, 118, 101, 99, 76, + 105, 103, 104, 116, 65, 109, + 98, 105, 101, 110, 116, 67, + 111, 108, 0, 77, 105, 99, + 114, 111, 115, 111, 102, 116, + 32, 40, 82, 41, 32, 72, + 76, 83, 76, 32, 83, 104, + 97, 100, 101, 114, 32, 67, + 111, 109, 112, 105, 108, 101, + 114, 32, 49, 48, 46, 49, + 0, 171, 73, 83, 71, 78, + 160, 0, 0, 0, 5, 0, + 0, 0, 8, 0, 0, 0, + 128, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 3, 0, 0, 0, 0, 0, + 0, 0, 15, 15, 0, 0, + 137, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 3, 0, 0, 0, 1, 0, + 0, 0, 15, 14, 0, 0, + 143, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 3, 0, 0, 0, 2, 0, + 0, 0, 7, 7, 0, 0, + 150, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 3, 0, 0, 0, 3, 0, + 0, 0, 15, 15, 0, 0, + 150, 0, 0, 0, 1, 0, + 0, 0, 0, 0, 0, 0, + 2, 0, 0, 0, 4, 0, + 0, 0, 3, 3, 0, 0, + 80, 79, 83, 73, 84, 73, + 79, 78, 0, 67, 79, 76, + 79, 82, 0, 78, 79, 82, + 77, 65, 76, 0, 84, 69, + 88, 67, 79, 79, 82, 68, + 0, 171, 79, 83, 71, 78, + 108, 0, 0, 0, 3, 0, + 0, 0, 8, 0, 0, 0, + 80, 0, 0, 0, 0, 0, + 0, 0, 1, 0, 0, 0, + 3, 0, 0, 0, 0, 0, + 0, 0, 15, 0, 0, 0, + 92, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 3, 0, 0, 0, 1, 0, + 0, 0, 15, 0, 0, 0, + 98, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 3, 0, 0, 0, 2, 0, + 0, 0, 15, 0, 0, 0, + 83, 86, 95, 80, 79, 83, + 73, 84, 73, 79, 78, 0, + 67, 79, 76, 79, 82, 0, + 84, 69, 88, 67, 79, 79, + 82, 68, 0, 171, 83, 72, + 68, 82, 216, 7, 0, 0, + 64, 0, 1, 0, 246, 1, + 0, 0, 89, 0, 0, 4, + 70, 142, 32, 0, 0, 0, + 0, 0, 4, 0, 0, 0, + 89, 0, 0, 4, 70, 142, + 32, 0, 1, 0, 0, 0, + 4, 0, 0, 0, 89, 0, + 0, 4, 70, 142, 32, 0, + 2, 0, 0, 0, 4, 0, + 0, 0, 89, 0, 0, 4, + 70, 142, 32, 0, 3, 0, + 0, 0, 4, 0, 0, 0, + 89, 0, 0, 4, 70, 142, + 32, 0, 4, 0, 0, 0, + 1, 0, 0, 0, 89, 0, + 0, 4, 70, 142, 32, 0, + 5, 0, 0, 0, 1, 0, + 0, 0, 89, 0, 0, 4, + 70, 142, 32, 0, 6, 0, + 0, 0, 5, 0, 0, 0, + 90, 0, 0, 3, 0, 96, + 16, 0, 0, 0, 0, 0, + 88, 24, 0, 4, 0, 112, + 16, 0, 0, 0, 0, 0, + 85, 85, 0, 0, 95, 0, + 0, 3, 242, 16, 16, 0, + 0, 0, 0, 0, 95, 0, + 0, 3, 226, 16, 16, 0, + 1, 0, 0, 0, 95, 0, + 0, 3, 114, 16, 16, 0, + 2, 0, 0, 0, 95, 0, + 0, 3, 242, 16, 16, 0, + 3, 0, 0, 0, 95, 0, + 0, 3, 50, 16, 16, 0, + 4, 0, 0, 0, 103, 0, + 0, 4, 242, 32, 16, 0, + 0, 0, 0, 0, 1, 0, + 0, 0, 101, 0, 0, 3, + 242, 32, 16, 0, 1, 0, + 0, 0, 101, 0, 0, 3, + 242, 32, 16, 0, 2, 0, + 0, 0, 104, 0, 0, 2, + 3, 0, 0, 0, 56, 0, + 0, 8, 242, 0, 16, 0, + 0, 0, 0, 0, 86, 21, + 16, 0, 0, 0, 0, 0, + 70, 142, 32, 0, 1, 0, + 0, 0, 1, 0, 0, 0, + 50, 0, 0, 10, 242, 0, + 16, 0, 0, 0, 0, 0, + 70, 142, 32, 0, 1, 0, + 0, 0, 0, 0, 0, 0, + 6, 16, 16, 0, 0, 0, + 0, 0, 70, 14, 16, 0, + 0, 0, 0, 0, 50, 0, + 0, 10, 242, 0, 16, 0, + 0, 0, 0, 0, 70, 142, + 32, 0, 1, 0, 0, 0, + 2, 0, 0, 0, 166, 26, + 16, 0, 0, 0, 0, 0, + 70, 14, 16, 0, 0, 0, + 0, 0, 50, 0, 0, 10, + 242, 0, 16, 0, 0, 0, + 0, 0, 70, 142, 32, 0, + 1, 0, 0, 0, 3, 0, + 0, 0, 246, 31, 16, 0, + 0, 0, 0, 0, 70, 14, + 16, 0, 0, 0, 0, 0, + 56, 0, 0, 8, 242, 0, + 16, 0, 1, 0, 0, 0, + 86, 5, 16, 0, 0, 0, + 0, 0, 70, 142, 32, 0, + 0, 0, 0, 0, 1, 0, + 0, 0, 50, 0, 0, 10, + 242, 0, 16, 0, 1, 0, + 0, 0, 70, 142, 32, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 6, 0, 16, 0, + 0, 0, 0, 0, 70, 14, + 16, 0, 1, 0, 0, 0, + 50, 0, 0, 10, 242, 0, + 16, 0, 1, 0, 0, 0, + 70, 142, 32, 0, 0, 0, + 0, 0, 2, 0, 0, 0, + 166, 10, 16, 0, 0, 0, + 0, 0, 70, 14, 16, 0, + 1, 0, 0, 0, 50, 0, + 0, 10, 242, 0, 16, 0, + 0, 0, 0, 0, 70, 142, + 32, 0, 0, 0, 0, 0, + 3, 0, 0, 0, 246, 15, + 16, 0, 0, 0, 0, 0, + 70, 14, 16, 0, 1, 0, + 0, 0, 56, 0, 0, 8, + 242, 0, 16, 0, 1, 0, + 0, 0, 86, 5, 16, 0, + 0, 0, 0, 0, 70, 142, + 32, 0, 2, 0, 0, 0, + 1, 0, 0, 0, 50, 0, + 0, 10, 242, 0, 16, 0, + 1, 0, 0, 0, 70, 142, + 32, 0, 2, 0, 0, 0, + 0, 0, 0, 0, 6, 0, + 16, 0, 0, 0, 0, 0, + 70, 14, 16, 0, 1, 0, + 0, 0, 50, 0, 0, 10, + 242, 0, 16, 0, 1, 0, + 0, 0, 70, 142, 32, 0, + 2, 0, 0, 0, 2, 0, + 0, 0, 166, 10, 16, 0, + 0, 0, 0, 0, 70, 14, + 16, 0, 1, 0, 0, 0, + 50, 0, 0, 10, 242, 32, + 16, 0, 0, 0, 0, 0, + 70, 142, 32, 0, 2, 0, + 0, 0, 3, 0, 0, 0, + 246, 15, 16, 0, 0, 0, + 0, 0, 70, 14, 16, 0, + 1, 0, 0, 0, 56, 0, + 0, 8, 178, 0, 16, 0, + 0, 0, 0, 0, 86, 21, + 16, 0, 2, 0, 0, 0, + 70, 136, 32, 0, 1, 0, + 0, 0, 1, 0, 0, 0, + 50, 0, 0, 10, 178, 0, + 16, 0, 0, 0, 0, 0, + 70, 136, 32, 0, 1, 0, + 0, 0, 0, 0, 0, 0, + 6, 16, 16, 0, 2, 0, + 0, 0, 70, 12, 16, 0, + 0, 0, 0, 0, 50, 0, + 0, 10, 178, 0, 16, 0, + 0, 0, 0, 0, 70, 136, + 32, 0, 1, 0, 0, 0, + 2, 0, 0, 0, 166, 26, + 16, 0, 2, 0, 0, 0, + 70, 12, 16, 0, 0, 0, + 0, 0, 56, 0, 0, 8, + 114, 0, 16, 0, 1, 0, + 0, 0, 86, 5, 16, 0, + 0, 0, 0, 0, 70, 130, + 32, 0, 0, 0, 0, 0, + 1, 0, 0, 0, 50, 0, + 0, 10, 114, 0, 16, 0, + 1, 0, 0, 0, 70, 130, + 32, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 6, 0, + 16, 0, 0, 0, 0, 0, + 70, 2, 16, 0, 1, 0, + 0, 0, 50, 0, 0, 10, + 178, 0, 16, 0, 0, 0, + 0, 0, 70, 136, 32, 0, + 0, 0, 0, 0, 2, 0, + 0, 0, 246, 15, 16, 0, + 0, 0, 0, 0, 70, 8, + 16, 0, 1, 0, 0, 0, + 16, 0, 0, 7, 18, 0, + 16, 0, 1, 0, 0, 0, + 70, 3, 16, 0, 0, 0, + 0, 0, 70, 3, 16, 0, + 0, 0, 0, 0, 68, 0, + 0, 5, 18, 0, 16, 0, + 1, 0, 0, 0, 10, 0, + 16, 0, 1, 0, 0, 0, + 56, 0, 0, 7, 178, 0, + 16, 0, 0, 0, 0, 0, + 70, 12, 16, 0, 0, 0, + 0, 0, 6, 0, 16, 0, + 1, 0, 0, 0, 16, 0, + 0, 8, 18, 0, 16, 0, + 1, 0, 0, 0, 70, 130, + 32, 0, 6, 0, 0, 0, + 0, 0, 0, 0, 70, 3, + 16, 0, 0, 0, 0, 0, + 16, 0, 0, 8, 18, 0, + 16, 0, 0, 0, 0, 0, + 70, 130, 32, 0, 6, 0, + 0, 0, 1, 0, 0, 0, + 70, 3, 16, 0, 0, 0, + 0, 0, 52, 0, 0, 7, + 18, 0, 16, 0, 0, 0, + 0, 0, 10, 0, 16, 0, + 0, 0, 0, 0, 1, 64, + 0, 0, 0, 0, 0, 0, + 52, 0, 0, 7, 34, 0, + 16, 0, 0, 0, 0, 0, + 10, 0, 16, 0, 1, 0, + 0, 0, 1, 64, 0, 0, + 0, 0, 0, 0, 50, 0, + 0, 11, 242, 0, 16, 0, + 1, 0, 0, 0, 86, 5, + 16, 0, 0, 0, 0, 0, + 70, 142, 32, 0, 6, 0, + 0, 0, 2, 0, 0, 0, + 70, 142, 32, 0, 6, 0, + 0, 0, 4, 0, 0, 0, + 50, 32, 0, 10, 242, 0, + 16, 0, 1, 0, 0, 0, + 6, 0, 16, 0, 0, 0, + 0, 0, 70, 142, 32, 0, + 6, 0, 0, 0, 3, 0, + 0, 0, 70, 14, 16, 0, + 1, 0, 0, 0, 43, 0, + 0, 5, 50, 0, 16, 0, + 0, 0, 0, 0, 70, 16, + 16, 0, 4, 0, 0, 0, + 56, 0, 0, 10, 50, 0, + 16, 0, 0, 0, 0, 0, + 70, 0, 16, 0, 0, 0, + 0, 0, 2, 64, 0, 0, + 0, 0, 128, 59, 0, 0, + 128, 59, 0, 0, 0, 0, + 0, 0, 0, 0, 52, 0, + 0, 8, 50, 0, 16, 0, + 0, 0, 0, 0, 70, 0, + 16, 0, 0, 0, 0, 0, + 70, 128, 32, 0, 4, 0, + 0, 0, 0, 0, 0, 0, + 26, 0, 0, 5, 50, 0, + 16, 0, 0, 0, 0, 0, + 70, 0, 16, 0, 0, 0, + 0, 0, 72, 0, 0, 11, + 242, 0, 16, 0, 2, 0, + 0, 0, 70, 0, 16, 0, + 0, 0, 0, 0, 70, 126, + 16, 0, 0, 0, 0, 0, + 0, 96, 16, 0, 0, 0, + 0, 0, 1, 64, 0, 0, + 0, 0, 0, 0, 56, 0, + 0, 7, 178, 0, 16, 0, + 0, 0, 0, 0, 70, 8, + 16, 0, 2, 0, 0, 0, + 182, 23, 16, 0, 1, 0, + 0, 0, 56, 0, 0, 7, + 114, 32, 16, 0, 1, 0, + 0, 0, 70, 3, 16, 0, + 0, 0, 0, 0, 70, 2, + 16, 0, 1, 0, 0, 0, + 54, 0, 0, 5, 130, 32, + 16, 0, 1, 0, 0, 0, + 58, 0, 16, 0, 1, 0, + 0, 0, 0, 0, 0, 8, + 18, 0, 16, 0, 0, 0, + 0, 0, 42, 0, 16, 0, + 0, 0, 0, 0, 10, 128, + 32, 0, 5, 0, 0, 0, + 0, 0, 0, 0, 56, 0, + 0, 8, 34, 0, 16, 0, + 0, 0, 0, 0, 42, 0, + 16, 0, 0, 0, 0, 0, + 10, 128, 32, 0, 5, 0, + 0, 0, 0, 0, 0, 0, + 56, 0, 0, 7, 34, 0, + 16, 0, 0, 0, 0, 0, + 26, 0, 16, 0, 0, 0, + 0, 0, 1, 64, 0, 0, + 59, 170, 184, 63, 25, 0, + 0, 5, 34, 0, 16, 0, + 0, 0, 0, 0, 26, 0, + 16, 0, 0, 0, 0, 0, + 51, 0, 0, 7, 34, 0, + 16, 0, 0, 0, 0, 0, + 26, 0, 16, 0, 0, 0, + 0, 0, 1, 64, 0, 0, + 0, 0, 128, 63, 56, 32, + 0, 8, 18, 0, 16, 0, + 0, 0, 0, 0, 10, 0, + 16, 0, 0, 0, 0, 0, + 26, 128, 32, 0, 5, 0, + 0, 0, 0, 0, 0, 0, + 24, 0, 0, 11, 194, 0, + 16, 0, 0, 0, 0, 0, + 166, 138, 32, 0, 5, 0, + 0, 0, 0, 0, 0, 0, + 2, 64, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 64, 0, 0, + 128, 63, 55, 0, 0, 9, + 34, 0, 16, 0, 0, 0, + 0, 0, 42, 0, 16, 0, + 0, 0, 0, 0, 26, 0, + 16, 0, 0, 0, 0, 0, + 1, 64, 0, 0, 0, 0, + 128, 63, 55, 0, 0, 9, + 66, 32, 16, 0, 2, 0, + 0, 0, 58, 0, 16, 0, + 0, 0, 0, 0, 10, 0, + 16, 0, 0, 0, 0, 0, + 26, 0, 16, 0, 0, 0, + 0, 0, 54, 0, 0, 6, + 18, 0, 16, 0, 0, 0, + 0, 0, 10, 128, 32, 0, + 3, 0, 0, 0, 0, 0, + 0, 0, 54, 0, 0, 6, + 34, 0, 16, 0, 0, 0, + 0, 0, 10, 128, 32, 0, + 3, 0, 0, 0, 1, 0, + 0, 0, 54, 0, 0, 6, + 66, 0, 16, 0, 0, 0, + 0, 0, 10, 128, 32, 0, + 3, 0, 0, 0, 2, 0, + 0, 0, 54, 0, 0, 6, + 130, 0, 16, 0, 0, 0, + 0, 0, 10, 128, 32, 0, + 3, 0, 0, 0, 3, 0, + 0, 0, 17, 0, 0, 7, + 18, 32, 16, 0, 2, 0, + 0, 0, 70, 14, 16, 0, + 0, 0, 0, 0, 70, 30, + 16, 0, 3, 0, 0, 0, + 54, 0, 0, 6, 18, 0, + 16, 0, 0, 0, 0, 0, + 26, 128, 32, 0, 3, 0, + 0, 0, 0, 0, 0, 0, + 54, 0, 0, 6, 34, 0, + 16, 0, 0, 0, 0, 0, + 26, 128, 32, 0, 3, 0, + 0, 0, 1, 0, 0, 0, + 54, 0, 0, 6, 66, 0, + 16, 0, 0, 0, 0, 0, + 26, 128, 32, 0, 3, 0, + 0, 0, 2, 0, 0, 0, + 54, 0, 0, 6, 130, 0, + 16, 0, 0, 0, 0, 0, + 26, 128, 32, 0, 3, 0, + 0, 0, 3, 0, 0, 0, + 17, 0, 0, 7, 34, 32, + 16, 0, 2, 0, 0, 0, + 70, 14, 16, 0, 0, 0, + 0, 0, 70, 30, 16, 0, + 3, 0, 0, 0, 54, 0, + 0, 5, 130, 32, 16, 0, + 2, 0, 0, 0, 1, 64, + 0, 0, 0, 0, 128, 63, + 62, 0, 0, 1, 83, 84, + 65, 84, 116, 0, 0, 0, + 56, 0, 0, 0, 3, 0, + 0, 0, 0, 0, 0, 0, + 8, 0, 0, 0, 41, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 1, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 1, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 10, 0, + 0, 0, 2, 0, 0, 0, + 2, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0 +}; diff --git a/Minecraft.Client/Windows64/4JLibs/inc/Render/VS_PF3_TF2_CB4_NB4_XW1_TEXGEN.h b/Minecraft.Client/Windows64/4JLibs/inc/Render/VS_PF3_TF2_CB4_NB4_XW1_TEXGEN.h new file mode 100644 index 000000000..ad5314872 --- /dev/null +++ b/Minecraft.Client/Windows64/4JLibs/inc/Render/VS_PF3_TF2_CB4_NB4_XW1_TEXGEN.h @@ -0,0 +1,727 @@ +#if 0 +// +// Generated by Microsoft (R) HLSL Shader Compiler 10.1 +// +// +// Buffer Definitions: +// +// cbuffer positionTransformWV +// { +// +// float4x4 matWorldView; // Offset: 0 Size: 64 +// +// } +// +// cbuffer positionTransformWV2 +// { +// +// float4x4 matWorldView2; // Offset: 0 Size: 64 +// +// } +// +// cbuffer positionTransformProj +// { +// +// float4x4 matProjection; // Offset: 0 Size: 64 +// +// } +// +// cbuffer textureTransform +// { +// +// float4x4 matUV; // Offset: 0 Size: 64 +// +// } +// +// cbuffer textureUV2 +// { +// +// float4 vecUVT2; // Offset: 0 Size: 16 +// +// } +// +// cbuffer fog +// { +// +// float4 vecFog; // Offset: 0 Size: 16 +// +// } +// +// cbuffer texgen +// { +// +// float4x4 matTexGenView; // Offset: 0 Size: 64 +// float4x4 matTexGenObj; // Offset: 64 Size: 64 +// +// } +// +// +// Resource Bindings: +// +// Name Type Format Dim HLSL Bind Count +// ------------------------------ ---------- ------- ----------- -------------- ------ +// light_sampler_s sampler NA NA s0 1 +// light_texture texture float4 2d t0 1 +// positionTransformWV cbuffer NA NA cb0 1 +// positionTransformWV2 cbuffer NA NA cb1 1 +// positionTransformProj cbuffer NA NA cb2 1 +// textureTransform cbuffer NA NA cb3 1 +// textureUV2 cbuffer NA NA cb4 1 +// fog cbuffer NA NA cb5 1 +// texgen cbuffer NA NA cb7 1 +// +// +// +// Input signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------- ------ +// POSITION 0 xyzw 0 NONE float xyzw +// COLOR 0 xyzw 1 NONE float xyzw +// NORMAL 0 xyz 2 NONE float +// TEXCOORD 0 xyzw 3 NONE float +// TEXCOORD 1 xy 4 NONE int xy +// +// +// Output signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------- ------ +// SV_POSITION 0 xyzw 0 POS float xyzw +// COLOR 0 xyzw 1 NONE float xyzw +// TEXCOORD 0 xyzw 2 NONE float xyzw +// +vs_4_0 +dcl_constantbuffer CB0[4], immediateIndexed +dcl_constantbuffer CB1[4], immediateIndexed +dcl_constantbuffer CB2[4], immediateIndexed +dcl_constantbuffer CB3[4], immediateIndexed +dcl_constantbuffer CB4[1], immediateIndexed +dcl_constantbuffer CB5[1], immediateIndexed +dcl_constantbuffer CB7[8], immediateIndexed +dcl_sampler s0, mode_default +dcl_resource_texture2d (float,float,float,float) t0 +dcl_input v0.xyzw +dcl_input v1.xyzw +dcl_input v4.xy +dcl_output_siv o0.xyzw, position +dcl_output o1.xyzw +dcl_output o2.xyzw +dcl_temps 3 +mul r0.xyzw, v0.yyyy, cb1[1].xyzw +mad r0.xyzw, cb1[0].xyzw, v0.xxxx, r0.xyzw +mad r0.xyzw, cb1[2].xyzw, v0.zzzz, r0.xyzw +mad r0.xyzw, cb1[3].xyzw, v0.wwww, r0.xyzw +mul r1.xyzw, r0.yyyy, cb0[1].xyzw +mad r1.xyzw, cb0[0].xyzw, r0.xxxx, r1.xyzw +mad r1.xyzw, cb0[2].xyzw, r0.zzzz, r1.xyzw +mad r0.xyzw, cb0[3].xyzw, r0.wwww, r1.xyzw +mul r1.xyzw, r0.yyyy, cb2[1].xyzw +mad r1.xyzw, cb2[0].xyzw, r0.xxxx, r1.xyzw +mad r1.xyzw, cb2[2].xyzw, r0.zzzz, r1.xyzw +mad o0.xyzw, cb2[3].xyzw, r0.wwww, r1.xyzw +itof r1.xy, v4.xyxx +mul r1.xy, r1.xyxx, l(0.003906, 0.003906, 0.000000, 0.000000) +max r1.xy, r1.xyxx, cb4[0].xyxx +frc r1.xy, r1.xyxx +sample_l r1.xyzw, r1.xyxx, t0.xyzw, s0, l(0.000000) +mov r1.w, l(1.000000) +mul o1.xyzw, r1.xyzw, v1.wzyx +mul r1.xyzw, r0.yyyy, cb7[1].xyzw +mad r1.xyzw, cb7[0].xyzw, r0.xxxx, r1.xyzw +mad r1.xyzw, cb7[2].xyzw, r0.zzzz, r1.xyzw +mad r1.xyzw, cb7[3].xyzw, r0.wwww, r1.xyzw +mul r2.xyzw, v0.yyyy, cb7[5].xyzw +mad r2.xyzw, cb7[4].xyzw, v0.xxxx, r2.xyzw +mad r2.xyzw, cb7[6].xyzw, v0.zzzz, r2.xyzw +mad r2.xyzw, cb7[7].xyzw, v0.wwww, r2.xyzw +add r1.xyzw, r1.xyzw, r2.xyzw +mov r2.x, cb3[0].x +mov r2.y, cb3[1].x +mov r2.z, cb3[2].x +mov r2.w, cb3[3].x +dp4 o2.x, r2.xyzw, r1.xyzw +mov r2.x, cb3[0].y +mov r2.y, cb3[1].y +mov r2.z, cb3[2].y +mov r2.w, cb3[3].y +dp4 o2.y, r2.xyzw, r1.xyzw +mov r2.x, cb3[0].w +mov r2.y, cb3[1].w +mov r2.z, cb3[2].w +mov r2.w, cb3[3].w +dp4 o2.w, r2.xyzw, r1.xyzw +add r0.x, r0.z, cb5[0].x +mul r0.y, r0.z, cb5[0].x +mul r0.y, r0.y, l(1.442695) +exp r0.y, r0.y +min r0.y, r0.y, l(1.000000) +mul_sat r0.x, r0.x, cb5[0].y +eq r0.zw, cb5[0].zzzz, l(0.000000, 0.000000, 2.000000, 1.000000) +movc r0.y, r0.z, r0.y, l(1.000000) +movc o2.z, r0.w, r0.x, r0.y +ret +// Approximately 53 instruction slots used +#endif + +const BYTE g_main_VS_PF3_TF2_CB4_NB4_XW1_TEXGEN[] = +{ + 68, 88, 66, 67, 210, 154, + 29, 52, 96, 3, 148, 25, + 100, 148, 246, 128, 215, 220, + 27, 138, 1, 0, 0, 0, + 16, 13, 0, 0, 5, 0, + 0, 0, 52, 0, 0, 0, + 16, 4, 0, 0, 184, 4, + 0, 0, 44, 5, 0, 0, + 148, 12, 0, 0, 82, 68, + 69, 70, 212, 3, 0, 0, + 7, 0, 0, 0, 192, 1, + 0, 0, 9, 0, 0, 0, + 28, 0, 0, 0, 0, 4, + 254, 255, 0, 129, 0, 0, + 171, 3, 0, 0, 60, 1, + 0, 0, 3, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 1, 0, + 0, 0, 1, 0, 0, 0, + 76, 1, 0, 0, 2, 0, + 0, 0, 5, 0, 0, 0, + 4, 0, 0, 0, 255, 255, + 255, 255, 0, 0, 0, 0, + 1, 0, 0, 0, 13, 0, + 0, 0, 90, 1, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 1, 0, 0, 0, + 1, 0, 0, 0, 110, 1, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 1, 0, 0, 0, 1, 0, + 0, 0, 1, 0, 0, 0, + 131, 1, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 2, 0, 0, 0, + 1, 0, 0, 0, 1, 0, + 0, 0, 153, 1, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 3, 0, + 0, 0, 1, 0, 0, 0, + 1, 0, 0, 0, 170, 1, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 4, 0, 0, 0, 1, 0, + 0, 0, 1, 0, 0, 0, + 181, 1, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 5, 0, 0, 0, + 1, 0, 0, 0, 1, 0, + 0, 0, 185, 1, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 7, 0, + 0, 0, 1, 0, 0, 0, + 1, 0, 0, 0, 108, 105, + 103, 104, 116, 95, 115, 97, + 109, 112, 108, 101, 114, 95, + 115, 0, 108, 105, 103, 104, + 116, 95, 116, 101, 120, 116, + 117, 114, 101, 0, 112, 111, + 115, 105, 116, 105, 111, 110, + 84, 114, 97, 110, 115, 102, + 111, 114, 109, 87, 86, 0, + 112, 111, 115, 105, 116, 105, + 111, 110, 84, 114, 97, 110, + 115, 102, 111, 114, 109, 87, + 86, 50, 0, 112, 111, 115, + 105, 116, 105, 111, 110, 84, + 114, 97, 110, 115, 102, 111, + 114, 109, 80, 114, 111, 106, + 0, 116, 101, 120, 116, 117, + 114, 101, 84, 114, 97, 110, + 115, 102, 111, 114, 109, 0, + 116, 101, 120, 116, 117, 114, + 101, 85, 86, 50, 0, 102, + 111, 103, 0, 116, 101, 120, + 103, 101, 110, 0, 90, 1, + 0, 0, 1, 0, 0, 0, + 104, 2, 0, 0, 64, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 110, 1, + 0, 0, 1, 0, 0, 0, + 160, 2, 0, 0, 64, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 131, 1, + 0, 0, 1, 0, 0, 0, + 200, 2, 0, 0, 64, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 153, 1, + 0, 0, 1, 0, 0, 0, + 240, 2, 0, 0, 64, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 170, 1, + 0, 0, 1, 0, 0, 0, + 16, 3, 0, 0, 16, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 181, 1, + 0, 0, 1, 0, 0, 0, + 64, 3, 0, 0, 16, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 185, 1, + 0, 0, 2, 0, 0, 0, + 96, 3, 0, 0, 128, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 128, 2, + 0, 0, 0, 0, 0, 0, + 64, 0, 0, 0, 2, 0, + 0, 0, 144, 2, 0, 0, + 0, 0, 0, 0, 109, 97, + 116, 87, 111, 114, 108, 100, + 86, 105, 101, 119, 0, 171, + 171, 171, 3, 0, 3, 0, + 4, 0, 4, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 184, 2, 0, 0, 0, 0, + 0, 0, 64, 0, 0, 0, + 2, 0, 0, 0, 144, 2, + 0, 0, 0, 0, 0, 0, + 109, 97, 116, 87, 111, 114, + 108, 100, 86, 105, 101, 119, + 50, 0, 171, 171, 224, 2, + 0, 0, 0, 0, 0, 0, + 64, 0, 0, 0, 2, 0, + 0, 0, 144, 2, 0, 0, + 0, 0, 0, 0, 109, 97, + 116, 80, 114, 111, 106, 101, + 99, 116, 105, 111, 110, 0, + 171, 171, 8, 3, 0, 0, + 0, 0, 0, 0, 64, 0, + 0, 0, 2, 0, 0, 0, + 144, 2, 0, 0, 0, 0, + 0, 0, 109, 97, 116, 85, + 86, 0, 171, 171, 40, 3, + 0, 0, 0, 0, 0, 0, + 16, 0, 0, 0, 2, 0, + 0, 0, 48, 3, 0, 0, + 0, 0, 0, 0, 118, 101, + 99, 85, 86, 84, 50, 0, + 1, 0, 3, 0, 1, 0, + 4, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 88, 3, + 0, 0, 0, 0, 0, 0, + 16, 0, 0, 0, 2, 0, + 0, 0, 48, 3, 0, 0, + 0, 0, 0, 0, 118, 101, + 99, 70, 111, 103, 0, 171, + 144, 3, 0, 0, 0, 0, + 0, 0, 64, 0, 0, 0, + 2, 0, 0, 0, 144, 2, + 0, 0, 0, 0, 0, 0, + 158, 3, 0, 0, 64, 0, + 0, 0, 64, 0, 0, 0, + 2, 0, 0, 0, 144, 2, + 0, 0, 0, 0, 0, 0, + 109, 97, 116, 84, 101, 120, + 71, 101, 110, 86, 105, 101, + 119, 0, 109, 97, 116, 84, + 101, 120, 71, 101, 110, 79, + 98, 106, 0, 77, 105, 99, + 114, 111, 115, 111, 102, 116, + 32, 40, 82, 41, 32, 72, + 76, 83, 76, 32, 83, 104, + 97, 100, 101, 114, 32, 67, + 111, 109, 112, 105, 108, 101, + 114, 32, 49, 48, 46, 49, + 0, 171, 73, 83, 71, 78, + 160, 0, 0, 0, 5, 0, + 0, 0, 8, 0, 0, 0, + 128, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 3, 0, 0, 0, 0, 0, + 0, 0, 15, 15, 0, 0, + 137, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 3, 0, 0, 0, 1, 0, + 0, 0, 15, 15, 0, 0, + 143, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 3, 0, 0, 0, 2, 0, + 0, 0, 7, 0, 0, 0, + 150, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 3, 0, 0, 0, 3, 0, + 0, 0, 15, 0, 0, 0, + 150, 0, 0, 0, 1, 0, + 0, 0, 0, 0, 0, 0, + 2, 0, 0, 0, 4, 0, + 0, 0, 3, 3, 0, 0, + 80, 79, 83, 73, 84, 73, + 79, 78, 0, 67, 79, 76, + 79, 82, 0, 78, 79, 82, + 77, 65, 76, 0, 84, 69, + 88, 67, 79, 79, 82, 68, + 0, 171, 79, 83, 71, 78, + 108, 0, 0, 0, 3, 0, + 0, 0, 8, 0, 0, 0, + 80, 0, 0, 0, 0, 0, + 0, 0, 1, 0, 0, 0, + 3, 0, 0, 0, 0, 0, + 0, 0, 15, 0, 0, 0, + 92, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 3, 0, 0, 0, 1, 0, + 0, 0, 15, 0, 0, 0, + 98, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 3, 0, 0, 0, 2, 0, + 0, 0, 15, 0, 0, 0, + 83, 86, 95, 80, 79, 83, + 73, 84, 73, 79, 78, 0, + 67, 79, 76, 79, 82, 0, + 84, 69, 88, 67, 79, 79, + 82, 68, 0, 171, 83, 72, + 68, 82, 96, 7, 0, 0, + 64, 0, 1, 0, 216, 1, + 0, 0, 89, 0, 0, 4, + 70, 142, 32, 0, 0, 0, + 0, 0, 4, 0, 0, 0, + 89, 0, 0, 4, 70, 142, + 32, 0, 1, 0, 0, 0, + 4, 0, 0, 0, 89, 0, + 0, 4, 70, 142, 32, 0, + 2, 0, 0, 0, 4, 0, + 0, 0, 89, 0, 0, 4, + 70, 142, 32, 0, 3, 0, + 0, 0, 4, 0, 0, 0, + 89, 0, 0, 4, 70, 142, + 32, 0, 4, 0, 0, 0, + 1, 0, 0, 0, 89, 0, + 0, 4, 70, 142, 32, 0, + 5, 0, 0, 0, 1, 0, + 0, 0, 89, 0, 0, 4, + 70, 142, 32, 0, 7, 0, + 0, 0, 8, 0, 0, 0, + 90, 0, 0, 3, 0, 96, + 16, 0, 0, 0, 0, 0, + 88, 24, 0, 4, 0, 112, + 16, 0, 0, 0, 0, 0, + 85, 85, 0, 0, 95, 0, + 0, 3, 242, 16, 16, 0, + 0, 0, 0, 0, 95, 0, + 0, 3, 242, 16, 16, 0, + 1, 0, 0, 0, 95, 0, + 0, 3, 50, 16, 16, 0, + 4, 0, 0, 0, 103, 0, + 0, 4, 242, 32, 16, 0, + 0, 0, 0, 0, 1, 0, + 0, 0, 101, 0, 0, 3, + 242, 32, 16, 0, 1, 0, + 0, 0, 101, 0, 0, 3, + 242, 32, 16, 0, 2, 0, + 0, 0, 104, 0, 0, 2, + 3, 0, 0, 0, 56, 0, + 0, 8, 242, 0, 16, 0, + 0, 0, 0, 0, 86, 21, + 16, 0, 0, 0, 0, 0, + 70, 142, 32, 0, 1, 0, + 0, 0, 1, 0, 0, 0, + 50, 0, 0, 10, 242, 0, + 16, 0, 0, 0, 0, 0, + 70, 142, 32, 0, 1, 0, + 0, 0, 0, 0, 0, 0, + 6, 16, 16, 0, 0, 0, + 0, 0, 70, 14, 16, 0, + 0, 0, 0, 0, 50, 0, + 0, 10, 242, 0, 16, 0, + 0, 0, 0, 0, 70, 142, + 32, 0, 1, 0, 0, 0, + 2, 0, 0, 0, 166, 26, + 16, 0, 0, 0, 0, 0, + 70, 14, 16, 0, 0, 0, + 0, 0, 50, 0, 0, 10, + 242, 0, 16, 0, 0, 0, + 0, 0, 70, 142, 32, 0, + 1, 0, 0, 0, 3, 0, + 0, 0, 246, 31, 16, 0, + 0, 0, 0, 0, 70, 14, + 16, 0, 0, 0, 0, 0, + 56, 0, 0, 8, 242, 0, + 16, 0, 1, 0, 0, 0, + 86, 5, 16, 0, 0, 0, + 0, 0, 70, 142, 32, 0, + 0, 0, 0, 0, 1, 0, + 0, 0, 50, 0, 0, 10, + 242, 0, 16, 0, 1, 0, + 0, 0, 70, 142, 32, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 6, 0, 16, 0, + 0, 0, 0, 0, 70, 14, + 16, 0, 1, 0, 0, 0, + 50, 0, 0, 10, 242, 0, + 16, 0, 1, 0, 0, 0, + 70, 142, 32, 0, 0, 0, + 0, 0, 2, 0, 0, 0, + 166, 10, 16, 0, 0, 0, + 0, 0, 70, 14, 16, 0, + 1, 0, 0, 0, 50, 0, + 0, 10, 242, 0, 16, 0, + 0, 0, 0, 0, 70, 142, + 32, 0, 0, 0, 0, 0, + 3, 0, 0, 0, 246, 15, + 16, 0, 0, 0, 0, 0, + 70, 14, 16, 0, 1, 0, + 0, 0, 56, 0, 0, 8, + 242, 0, 16, 0, 1, 0, + 0, 0, 86, 5, 16, 0, + 0, 0, 0, 0, 70, 142, + 32, 0, 2, 0, 0, 0, + 1, 0, 0, 0, 50, 0, + 0, 10, 242, 0, 16, 0, + 1, 0, 0, 0, 70, 142, + 32, 0, 2, 0, 0, 0, + 0, 0, 0, 0, 6, 0, + 16, 0, 0, 0, 0, 0, + 70, 14, 16, 0, 1, 0, + 0, 0, 50, 0, 0, 10, + 242, 0, 16, 0, 1, 0, + 0, 0, 70, 142, 32, 0, + 2, 0, 0, 0, 2, 0, + 0, 0, 166, 10, 16, 0, + 0, 0, 0, 0, 70, 14, + 16, 0, 1, 0, 0, 0, + 50, 0, 0, 10, 242, 32, + 16, 0, 0, 0, 0, 0, + 70, 142, 32, 0, 2, 0, + 0, 0, 3, 0, 0, 0, + 246, 15, 16, 0, 0, 0, + 0, 0, 70, 14, 16, 0, + 1, 0, 0, 0, 43, 0, + 0, 5, 50, 0, 16, 0, + 1, 0, 0, 0, 70, 16, + 16, 0, 4, 0, 0, 0, + 56, 0, 0, 10, 50, 0, + 16, 0, 1, 0, 0, 0, + 70, 0, 16, 0, 1, 0, + 0, 0, 2, 64, 0, 0, + 0, 0, 128, 59, 0, 0, + 128, 59, 0, 0, 0, 0, + 0, 0, 0, 0, 52, 0, + 0, 8, 50, 0, 16, 0, + 1, 0, 0, 0, 70, 0, + 16, 0, 1, 0, 0, 0, + 70, 128, 32, 0, 4, 0, + 0, 0, 0, 0, 0, 0, + 26, 0, 0, 5, 50, 0, + 16, 0, 1, 0, 0, 0, + 70, 0, 16, 0, 1, 0, + 0, 0, 72, 0, 0, 11, + 242, 0, 16, 0, 1, 0, + 0, 0, 70, 0, 16, 0, + 1, 0, 0, 0, 70, 126, + 16, 0, 0, 0, 0, 0, + 0, 96, 16, 0, 0, 0, + 0, 0, 1, 64, 0, 0, + 0, 0, 0, 0, 54, 0, + 0, 5, 130, 0, 16, 0, + 1, 0, 0, 0, 1, 64, + 0, 0, 0, 0, 128, 63, + 56, 0, 0, 7, 242, 32, + 16, 0, 1, 0, 0, 0, + 70, 14, 16, 0, 1, 0, + 0, 0, 182, 17, 16, 0, + 1, 0, 0, 0, 56, 0, + 0, 8, 242, 0, 16, 0, + 1, 0, 0, 0, 86, 5, + 16, 0, 0, 0, 0, 0, + 70, 142, 32, 0, 7, 0, + 0, 0, 1, 0, 0, 0, + 50, 0, 0, 10, 242, 0, + 16, 0, 1, 0, 0, 0, + 70, 142, 32, 0, 7, 0, + 0, 0, 0, 0, 0, 0, + 6, 0, 16, 0, 0, 0, + 0, 0, 70, 14, 16, 0, + 1, 0, 0, 0, 50, 0, + 0, 10, 242, 0, 16, 0, + 1, 0, 0, 0, 70, 142, + 32, 0, 7, 0, 0, 0, + 2, 0, 0, 0, 166, 10, + 16, 0, 0, 0, 0, 0, + 70, 14, 16, 0, 1, 0, + 0, 0, 50, 0, 0, 10, + 242, 0, 16, 0, 1, 0, + 0, 0, 70, 142, 32, 0, + 7, 0, 0, 0, 3, 0, + 0, 0, 246, 15, 16, 0, + 0, 0, 0, 0, 70, 14, + 16, 0, 1, 0, 0, 0, + 56, 0, 0, 8, 242, 0, + 16, 0, 2, 0, 0, 0, + 86, 21, 16, 0, 0, 0, + 0, 0, 70, 142, 32, 0, + 7, 0, 0, 0, 5, 0, + 0, 0, 50, 0, 0, 10, + 242, 0, 16, 0, 2, 0, + 0, 0, 70, 142, 32, 0, + 7, 0, 0, 0, 4, 0, + 0, 0, 6, 16, 16, 0, + 0, 0, 0, 0, 70, 14, + 16, 0, 2, 0, 0, 0, + 50, 0, 0, 10, 242, 0, + 16, 0, 2, 0, 0, 0, + 70, 142, 32, 0, 7, 0, + 0, 0, 6, 0, 0, 0, + 166, 26, 16, 0, 0, 0, + 0, 0, 70, 14, 16, 0, + 2, 0, 0, 0, 50, 0, + 0, 10, 242, 0, 16, 0, + 2, 0, 0, 0, 70, 142, + 32, 0, 7, 0, 0, 0, + 7, 0, 0, 0, 246, 31, + 16, 0, 0, 0, 0, 0, + 70, 14, 16, 0, 2, 0, + 0, 0, 0, 0, 0, 7, + 242, 0, 16, 0, 1, 0, + 0, 0, 70, 14, 16, 0, + 1, 0, 0, 0, 70, 14, + 16, 0, 2, 0, 0, 0, + 54, 0, 0, 6, 18, 0, + 16, 0, 2, 0, 0, 0, + 10, 128, 32, 0, 3, 0, + 0, 0, 0, 0, 0, 0, + 54, 0, 0, 6, 34, 0, + 16, 0, 2, 0, 0, 0, + 10, 128, 32, 0, 3, 0, + 0, 0, 1, 0, 0, 0, + 54, 0, 0, 6, 66, 0, + 16, 0, 2, 0, 0, 0, + 10, 128, 32, 0, 3, 0, + 0, 0, 2, 0, 0, 0, + 54, 0, 0, 6, 130, 0, + 16, 0, 2, 0, 0, 0, + 10, 128, 32, 0, 3, 0, + 0, 0, 3, 0, 0, 0, + 17, 0, 0, 7, 18, 32, + 16, 0, 2, 0, 0, 0, + 70, 14, 16, 0, 2, 0, + 0, 0, 70, 14, 16, 0, + 1, 0, 0, 0, 54, 0, + 0, 6, 18, 0, 16, 0, + 2, 0, 0, 0, 26, 128, + 32, 0, 3, 0, 0, 0, + 0, 0, 0, 0, 54, 0, + 0, 6, 34, 0, 16, 0, + 2, 0, 0, 0, 26, 128, + 32, 0, 3, 0, 0, 0, + 1, 0, 0, 0, 54, 0, + 0, 6, 66, 0, 16, 0, + 2, 0, 0, 0, 26, 128, + 32, 0, 3, 0, 0, 0, + 2, 0, 0, 0, 54, 0, + 0, 6, 130, 0, 16, 0, + 2, 0, 0, 0, 26, 128, + 32, 0, 3, 0, 0, 0, + 3, 0, 0, 0, 17, 0, + 0, 7, 34, 32, 16, 0, + 2, 0, 0, 0, 70, 14, + 16, 0, 2, 0, 0, 0, + 70, 14, 16, 0, 1, 0, + 0, 0, 54, 0, 0, 6, + 18, 0, 16, 0, 2, 0, + 0, 0, 58, 128, 32, 0, + 3, 0, 0, 0, 0, 0, + 0, 0, 54, 0, 0, 6, + 34, 0, 16, 0, 2, 0, + 0, 0, 58, 128, 32, 0, + 3, 0, 0, 0, 1, 0, + 0, 0, 54, 0, 0, 6, + 66, 0, 16, 0, 2, 0, + 0, 0, 58, 128, 32, 0, + 3, 0, 0, 0, 2, 0, + 0, 0, 54, 0, 0, 6, + 130, 0, 16, 0, 2, 0, + 0, 0, 58, 128, 32, 0, + 3, 0, 0, 0, 3, 0, + 0, 0, 17, 0, 0, 7, + 130, 32, 16, 0, 2, 0, + 0, 0, 70, 14, 16, 0, + 2, 0, 0, 0, 70, 14, + 16, 0, 1, 0, 0, 0, + 0, 0, 0, 8, 18, 0, + 16, 0, 0, 0, 0, 0, + 42, 0, 16, 0, 0, 0, + 0, 0, 10, 128, 32, 0, + 5, 0, 0, 0, 0, 0, + 0, 0, 56, 0, 0, 8, + 34, 0, 16, 0, 0, 0, + 0, 0, 42, 0, 16, 0, + 0, 0, 0, 0, 10, 128, + 32, 0, 5, 0, 0, 0, + 0, 0, 0, 0, 56, 0, + 0, 7, 34, 0, 16, 0, + 0, 0, 0, 0, 26, 0, + 16, 0, 0, 0, 0, 0, + 1, 64, 0, 0, 59, 170, + 184, 63, 25, 0, 0, 5, + 34, 0, 16, 0, 0, 0, + 0, 0, 26, 0, 16, 0, + 0, 0, 0, 0, 51, 0, + 0, 7, 34, 0, 16, 0, + 0, 0, 0, 0, 26, 0, + 16, 0, 0, 0, 0, 0, + 1, 64, 0, 0, 0, 0, + 128, 63, 56, 32, 0, 8, + 18, 0, 16, 0, 0, 0, + 0, 0, 10, 0, 16, 0, + 0, 0, 0, 0, 26, 128, + 32, 0, 5, 0, 0, 0, + 0, 0, 0, 0, 24, 0, + 0, 11, 194, 0, 16, 0, + 0, 0, 0, 0, 166, 138, + 32, 0, 5, 0, 0, 0, + 0, 0, 0, 0, 2, 64, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 64, 0, 0, 128, 63, + 55, 0, 0, 9, 34, 0, + 16, 0, 0, 0, 0, 0, + 42, 0, 16, 0, 0, 0, + 0, 0, 26, 0, 16, 0, + 0, 0, 0, 0, 1, 64, + 0, 0, 0, 0, 128, 63, + 55, 0, 0, 9, 66, 32, + 16, 0, 2, 0, 0, 0, + 58, 0, 16, 0, 0, 0, + 0, 0, 10, 0, 16, 0, + 0, 0, 0, 0, 26, 0, + 16, 0, 0, 0, 0, 0, + 62, 0, 0, 1, 83, 84, + 65, 84, 116, 0, 0, 0, + 53, 0, 0, 0, 3, 0, + 0, 0, 0, 0, 0, 0, + 6, 0, 0, 0, 35, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 1, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 1, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 13, 0, + 0, 0, 2, 0, 0, 0, + 2, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0 +}; diff --git a/Minecraft.Client/Windows64/4JLibs/inc/Render/VS_ScreenClear.h b/Minecraft.Client/Windows64/4JLibs/inc/Render/VS_ScreenClear.h new file mode 100644 index 000000000..6469f7911 --- /dev/null +++ b/Minecraft.Client/Windows64/4JLibs/inc/Render/VS_ScreenClear.h @@ -0,0 +1,138 @@ +#if 0 +// +// Generated by Microsoft (R) HLSL Shader Compiler 10.1 +// +// +// +// Input signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------- ------ +// SV_VertexID 0 x 0 VERTID uint x +// +// +// Output signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------- ------ +// SV_POSITION 0 xyzw 0 POS float xyzw +// +vs_4_0 +dcl_input_sgv v0.x, vertex_id +dcl_output_siv o0.xyzw, position +dcl_temps 1 +ishl r0.x, v0.x, l(1) +and r0.x, r0.x, l(2) +and r0.z, v0.x, l(2) +utof r0.xy, r0.xzxx +mad o0.xy, r0.xyxx, l(2.000000, -2.000000, 0.000000, 0.000000), l(-1.000000, 1.000000, 0.000000, 0.000000) +mov o0.zw, l(0,0,1.000000,1.000000) +ret +// Approximately 7 instruction slots used +#endif + +const BYTE g_main_VS_ScreenClear[] = +{ + 68, 88, 66, 67, 223, 115, + 183, 16, 218, 19, 23, 38, + 165, 246, 165, 141, 230, 7, + 225, 60, 1, 0, 0, 0, + 100, 2, 0, 0, 5, 0, + 0, 0, 52, 0, 0, 0, + 128, 0, 0, 0, 180, 0, + 0, 0, 232, 0, 0, 0, + 232, 1, 0, 0, 82, 68, + 69, 70, 68, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 28, 0, 0, 0, 0, 4, + 254, 255, 0, 129, 0, 0, + 28, 0, 0, 0, 77, 105, + 99, 114, 111, 115, 111, 102, + 116, 32, 40, 82, 41, 32, + 72, 76, 83, 76, 32, 83, + 104, 97, 100, 101, 114, 32, + 67, 111, 109, 112, 105, 108, + 101, 114, 32, 49, 48, 46, + 49, 0, 73, 83, 71, 78, + 44, 0, 0, 0, 1, 0, + 0, 0, 8, 0, 0, 0, + 32, 0, 0, 0, 0, 0, + 0, 0, 6, 0, 0, 0, + 1, 0, 0, 0, 0, 0, + 0, 0, 1, 1, 0, 0, + 83, 86, 95, 86, 101, 114, + 116, 101, 120, 73, 68, 0, + 79, 83, 71, 78, 44, 0, + 0, 0, 1, 0, 0, 0, + 8, 0, 0, 0, 32, 0, + 0, 0, 0, 0, 0, 0, + 1, 0, 0, 0, 3, 0, + 0, 0, 0, 0, 0, 0, + 15, 0, 0, 0, 83, 86, + 95, 80, 79, 83, 73, 84, + 73, 79, 78, 0, 83, 72, + 68, 82, 248, 0, 0, 0, + 64, 0, 1, 0, 62, 0, + 0, 0, 96, 0, 0, 4, + 18, 16, 16, 0, 0, 0, + 0, 0, 6, 0, 0, 0, + 103, 0, 0, 4, 242, 32, + 16, 0, 0, 0, 0, 0, + 1, 0, 0, 0, 104, 0, + 0, 2, 1, 0, 0, 0, + 41, 0, 0, 7, 18, 0, + 16, 0, 0, 0, 0, 0, + 10, 16, 16, 0, 0, 0, + 0, 0, 1, 64, 0, 0, + 1, 0, 0, 0, 1, 0, + 0, 7, 18, 0, 16, 0, + 0, 0, 0, 0, 10, 0, + 16, 0, 0, 0, 0, 0, + 1, 64, 0, 0, 2, 0, + 0, 0, 1, 0, 0, 7, + 66, 0, 16, 0, 0, 0, + 0, 0, 10, 16, 16, 0, + 0, 0, 0, 0, 1, 64, + 0, 0, 2, 0, 0, 0, + 86, 0, 0, 5, 50, 0, + 16, 0, 0, 0, 0, 0, + 134, 0, 16, 0, 0, 0, + 0, 0, 50, 0, 0, 15, + 50, 32, 16, 0, 0, 0, + 0, 0, 70, 0, 16, 0, + 0, 0, 0, 0, 2, 64, + 0, 0, 0, 0, 0, 64, + 0, 0, 0, 192, 0, 0, + 0, 0, 0, 0, 0, 0, + 2, 64, 0, 0, 0, 0, + 128, 191, 0, 0, 128, 63, + 0, 0, 0, 0, 0, 0, + 0, 0, 54, 0, 0, 8, + 194, 32, 16, 0, 0, 0, + 0, 0, 2, 64, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 128, 63, + 0, 0, 128, 63, 62, 0, + 0, 1, 83, 84, 65, 84, + 116, 0, 0, 0, 7, 0, + 0, 0, 1, 0, 0, 0, + 0, 0, 0, 0, 2, 0, + 0, 0, 1, 0, 0, 0, + 1, 0, 0, 0, 2, 0, + 0, 0, 1, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 1, 0, 0, 0, + 0, 0, 0, 0, 1, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0 +}; diff --git a/Minecraft.Client/Windows64/4JLibs/inc/Render/VS_ScreenSpace.h b/Minecraft.Client/Windows64/4JLibs/inc/Render/VS_ScreenSpace.h new file mode 100644 index 000000000..f5abc4ac4 --- /dev/null +++ b/Minecraft.Client/Windows64/4JLibs/inc/Render/VS_ScreenSpace.h @@ -0,0 +1,208 @@ +#if 0 +// +// Generated by Microsoft (R) HLSL Shader Compiler 10.1 +// +// +// Buffer Definitions: +// +// cbuffer screenspace_constants +// { +// +// float4 v_scaleoffset; // Offset: 0 Size: 16 +// +// } +// +// +// Resource Bindings: +// +// Name Type Format Dim HLSL Bind Count +// ------------------------------ ---------- ------- ----------- -------------- ------ +// screenspace_constants cbuffer NA NA cb9 1 +// +// +// +// Input signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------- ------ +// SV_VertexID 0 x 0 VERTID uint x +// +// +// Output signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------- ------ +// SV_POSITION 0 xyzw 0 POS float xyzw +// TEXCOORD 0 xy 1 NONE float xy +// +vs_4_0 +dcl_constantbuffer CB9[1], immediateIndexed +dcl_input_sgv v0.x, vertex_id +dcl_output_siv o0.xyzw, position +dcl_output o1.xy +dcl_temps 1 +ishl r0.x, v0.x, l(1) +and r0.x, r0.x, l(2) +and r0.z, v0.x, l(2) +utof r0.xy, r0.xzxx +mad o0.xy, r0.xyxx, l(2.000000, -2.000000, 0.000000, 0.000000), l(-1.000000, 1.000000, 0.000000, 0.000000) +mul r0.xy, r0.xyxx, cb9[0].zwzz +mad o1.xy, r0.xyxx, l(0.500000, 0.500000, 0.000000, 0.000000), cb9[0].xyxx +mov o0.zw, l(0,0,1.000000,1.000000) +ret +// Approximately 9 instruction slots used +#endif + +const BYTE g_main_VS_ScreenSpace[] = +{ + 68, 88, 66, 67, 37, 30, + 105, 189, 236, 181, 127, 58, + 95, 144, 208, 39, 252, 185, + 160, 237, 1, 0, 0, 0, + 128, 3, 0, 0, 5, 0, + 0, 0, 52, 0, 0, 0, + 8, 1, 0, 0, 60, 1, + 0, 0, 148, 1, 0, 0, + 4, 3, 0, 0, 82, 68, + 69, 70, 204, 0, 0, 0, + 1, 0, 0, 0, 84, 0, + 0, 0, 1, 0, 0, 0, + 28, 0, 0, 0, 0, 4, + 254, 255, 0, 129, 0, 0, + 164, 0, 0, 0, 60, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 9, 0, 0, 0, 1, 0, + 0, 0, 1, 0, 0, 0, + 115, 99, 114, 101, 101, 110, + 115, 112, 97, 99, 101, 95, + 99, 111, 110, 115, 116, 97, + 110, 116, 115, 0, 171, 171, + 60, 0, 0, 0, 1, 0, + 0, 0, 108, 0, 0, 0, + 16, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 132, 0, 0, 0, 0, 0, + 0, 0, 16, 0, 0, 0, + 2, 0, 0, 0, 148, 0, + 0, 0, 0, 0, 0, 0, + 118, 95, 115, 99, 97, 108, + 101, 111, 102, 102, 115, 101, + 116, 0, 171, 171, 1, 0, + 3, 0, 1, 0, 4, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 77, 105, 99, 114, + 111, 115, 111, 102, 116, 32, + 40, 82, 41, 32, 72, 76, + 83, 76, 32, 83, 104, 97, + 100, 101, 114, 32, 67, 111, + 109, 112, 105, 108, 101, 114, + 32, 49, 48, 46, 49, 0, + 73, 83, 71, 78, 44, 0, + 0, 0, 1, 0, 0, 0, + 8, 0, 0, 0, 32, 0, + 0, 0, 0, 0, 0, 0, + 6, 0, 0, 0, 1, 0, + 0, 0, 0, 0, 0, 0, + 1, 1, 0, 0, 83, 86, + 95, 86, 101, 114, 116, 101, + 120, 73, 68, 0, 79, 83, + 71, 78, 80, 0, 0, 0, + 2, 0, 0, 0, 8, 0, + 0, 0, 56, 0, 0, 0, + 0, 0, 0, 0, 1, 0, + 0, 0, 3, 0, 0, 0, + 0, 0, 0, 0, 15, 0, + 0, 0, 68, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 3, 0, 0, 0, + 1, 0, 0, 0, 3, 12, + 0, 0, 83, 86, 95, 80, + 79, 83, 73, 84, 73, 79, + 78, 0, 84, 69, 88, 67, + 79, 79, 82, 68, 0, 171, + 171, 171, 83, 72, 68, 82, + 104, 1, 0, 0, 64, 0, + 1, 0, 90, 0, 0, 0, + 89, 0, 0, 4, 70, 142, + 32, 0, 9, 0, 0, 0, + 1, 0, 0, 0, 96, 0, + 0, 4, 18, 16, 16, 0, + 0, 0, 0, 0, 6, 0, + 0, 0, 103, 0, 0, 4, + 242, 32, 16, 0, 0, 0, + 0, 0, 1, 0, 0, 0, + 101, 0, 0, 3, 50, 32, + 16, 0, 1, 0, 0, 0, + 104, 0, 0, 2, 1, 0, + 0, 0, 41, 0, 0, 7, + 18, 0, 16, 0, 0, 0, + 0, 0, 10, 16, 16, 0, + 0, 0, 0, 0, 1, 64, + 0, 0, 1, 0, 0, 0, + 1, 0, 0, 7, 18, 0, + 16, 0, 0, 0, 0, 0, + 10, 0, 16, 0, 0, 0, + 0, 0, 1, 64, 0, 0, + 2, 0, 0, 0, 1, 0, + 0, 7, 66, 0, 16, 0, + 0, 0, 0, 0, 10, 16, + 16, 0, 0, 0, 0, 0, + 1, 64, 0, 0, 2, 0, + 0, 0, 86, 0, 0, 5, + 50, 0, 16, 0, 0, 0, + 0, 0, 134, 0, 16, 0, + 0, 0, 0, 0, 50, 0, + 0, 15, 50, 32, 16, 0, + 0, 0, 0, 0, 70, 0, + 16, 0, 0, 0, 0, 0, + 2, 64, 0, 0, 0, 0, + 0, 64, 0, 0, 0, 192, + 0, 0, 0, 0, 0, 0, + 0, 0, 2, 64, 0, 0, + 0, 0, 128, 191, 0, 0, + 128, 63, 0, 0, 0, 0, + 0, 0, 0, 0, 56, 0, + 0, 8, 50, 0, 16, 0, + 0, 0, 0, 0, 70, 0, + 16, 0, 0, 0, 0, 0, + 230, 138, 32, 0, 9, 0, + 0, 0, 0, 0, 0, 0, + 50, 0, 0, 13, 50, 32, + 16, 0, 1, 0, 0, 0, + 70, 0, 16, 0, 0, 0, + 0, 0, 2, 64, 0, 0, + 0, 0, 0, 63, 0, 0, + 0, 63, 0, 0, 0, 0, + 0, 0, 0, 0, 70, 128, + 32, 0, 9, 0, 0, 0, + 0, 0, 0, 0, 54, 0, + 0, 8, 194, 32, 16, 0, + 0, 0, 0, 0, 2, 64, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 128, 63, 0, 0, 128, 63, + 62, 0, 0, 1, 83, 84, + 65, 84, 116, 0, 0, 0, + 9, 0, 0, 0, 1, 0, + 0, 0, 0, 0, 0, 0, + 3, 0, 0, 0, 3, 0, + 0, 0, 1, 0, 0, 0, + 2, 0, 0, 0, 1, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 1, 0, + 0, 0, 0, 0, 0, 0, + 1, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0 +}; diff --git a/Minecraft.Client/Windows64/4JLibs/inc/Render/libpng/png.c b/Minecraft.Client/Windows64/4JLibs/inc/Render/libpng/png.c new file mode 100644 index 000000000..d429c527c --- /dev/null +++ b/Minecraft.Client/Windows64/4JLibs/inc/Render/libpng/png.c @@ -0,0 +1,4298 @@ + +/* png.c - location for general purpose libpng functions + * + * Last changed in libpng 1.6.2 [April 25, 2013] + * Copyright (c) 1998-2013 Glenn Randers-Pehrson + * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) + * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) + * + * This code is released under the libpng license. + * For conditions of distribution and use, see the disclaimer + * and license in png.h + */ + +#include "pngpriv.h" + +/* Generate a compiler error if there is an old png.h in the search path. */ +typedef png_libpng_version_1_6_2 Your_png_h_is_not_version_1_6_2; + +/* Tells libpng that we have already handled the first "num_bytes" bytes + * of the PNG file signature. If the PNG data is embedded into another + * stream we can set num_bytes = 8 so that libpng will not attempt to read + * or write any of the magic bytes before it starts on the IHDR. + */ + +#ifdef PNG_READ_SUPPORTED +void PNGAPI +png_set_sig_bytes(png_structrp png_ptr, int num_bytes) +{ + png_debug(1, "in png_set_sig_bytes"); + + if (png_ptr == NULL) + return; + + if (num_bytes > 8) + png_error(png_ptr, "Too many bytes for PNG signature"); + + png_ptr->sig_bytes = (png_byte)(num_bytes < 0 ? 0 : num_bytes); +} + +/* Checks whether the supplied bytes match the PNG signature. We allow + * checking less than the full 8-byte signature so that those apps that + * already read the first few bytes of a file to determine the file type + * can simply check the remaining bytes for extra assurance. Returns + * an integer less than, equal to, or greater than zero if sig is found, + * respectively, to be less than, to match, or be greater than the correct + * PNG signature (this is the same behavior as strcmp, memcmp, etc). + */ +int PNGAPI +png_sig_cmp(png_const_bytep sig, png_size_t start, png_size_t num_to_check) +{ + png_byte png_signature[8] = {137, 80, 78, 71, 13, 10, 26, 10}; + + if (num_to_check > 8) + num_to_check = 8; + + else if (num_to_check < 1) + return (-1); + + if (start > 7) + return (-1); + + if (start + num_to_check > 8) + num_to_check = 8 - start; + + return ((int)(memcmp(&sig[start], &png_signature[start], num_to_check))); +} + +#endif /* PNG_READ_SUPPORTED */ + +#if defined(PNG_READ_SUPPORTED) || defined(PNG_WRITE_SUPPORTED) +/* Function to allocate memory for zlib */ +PNG_FUNCTION(voidpf /* PRIVATE */, +png_zalloc,(voidpf png_ptr, uInt items, uInt size),PNG_ALLOCATED) +{ + png_alloc_size_t num_bytes = size; + + if (png_ptr == NULL) + return NULL; + + if (items >= (~(png_alloc_size_t)0)/size) + { + png_warning (png_voidcast(png_structrp, png_ptr), + "Potential overflow in png_zalloc()"); + return NULL; + } + + num_bytes *= items; + return png_malloc_warn(png_voidcast(png_structrp, png_ptr), num_bytes); +} + +/* Function to free memory for zlib */ +void /* PRIVATE */ +png_zfree(voidpf png_ptr, voidpf ptr) +{ + png_free(png_voidcast(png_const_structrp,png_ptr), ptr); +} + +/* Reset the CRC variable to 32 bits of 1's. Care must be taken + * in case CRC is > 32 bits to leave the top bits 0. + */ +void /* PRIVATE */ +png_reset_crc(png_structrp png_ptr) +{ + /* The cast is safe because the crc is a 32 bit value. */ + png_ptr->crc = (png_uint_32)crc32(0, Z_NULL, 0); +} + +/* Calculate the CRC over a section of data. We can only pass as + * much data to this routine as the largest single buffer size. We + * also check that this data will actually be used before going to the + * trouble of calculating it. + */ +void /* PRIVATE */ +png_calculate_crc(png_structrp png_ptr, png_const_bytep ptr, png_size_t length) +{ + int need_crc = 1; + + if (PNG_CHUNK_ANCILLARY(png_ptr->chunk_name)) + { + if ((png_ptr->flags & PNG_FLAG_CRC_ANCILLARY_MASK) == + (PNG_FLAG_CRC_ANCILLARY_USE | PNG_FLAG_CRC_ANCILLARY_NOWARN)) + need_crc = 0; + } + + else /* critical */ + { + if (png_ptr->flags & PNG_FLAG_CRC_CRITICAL_IGNORE) + need_crc = 0; + } + + /* 'uLong' is defined in zlib.h as unsigned long; this means that on some + * systems it is a 64 bit value. crc32, however, returns 32 bits so the + * following cast is safe. 'uInt' may be no more than 16 bits, so it is + * necessary to perform a loop here. + */ + if (need_crc && length > 0) + { + uLong crc = png_ptr->crc; /* Should never issue a warning */ + + do + { + uInt safe_length = (uInt)length; + if (safe_length == 0) + safe_length = (uInt)-1; /* evil, but safe */ + + crc = crc32(crc, ptr, safe_length); + + /* The following should never issue compiler warnings; if they do the + * target system has characteristics that will probably violate other + * assumptions within the libpng code. + */ + ptr += safe_length; + length -= safe_length; + } + while (length > 0); + + /* And the following is always safe because the crc is only 32 bits. */ + png_ptr->crc = (png_uint_32)crc; + } +} + +/* Check a user supplied version number, called from both read and write + * functions that create a png_struct. + */ +int +png_user_version_check(png_structrp png_ptr, png_const_charp user_png_ver) +{ + if (user_png_ver) + { + int i = 0; + + do + { + if (user_png_ver[i] != png_libpng_ver[i]) + png_ptr->flags |= PNG_FLAG_LIBRARY_MISMATCH; + } while (png_libpng_ver[i++]); + } + + else + png_ptr->flags |= PNG_FLAG_LIBRARY_MISMATCH; + + if (png_ptr->flags & PNG_FLAG_LIBRARY_MISMATCH) + { + /* Libpng 0.90 and later are binary incompatible with libpng 0.89, so + * we must recompile any applications that use any older library version. + * For versions after libpng 1.0, we will be compatible, so we need + * only check the first and third digits (note that when we reach version + * 1.10 we will need to check the fourth symbol, namely user_png_ver[3]). + */ + if (user_png_ver == NULL || user_png_ver[0] != png_libpng_ver[0] || + (user_png_ver[0] == '1' && (user_png_ver[2] != png_libpng_ver[2] || + user_png_ver[3] != png_libpng_ver[3])) || + (user_png_ver[0] == '0' && user_png_ver[2] < '9')) + { +#ifdef PNG_WARNINGS_SUPPORTED + size_t pos = 0; + char m[128]; + + pos = png_safecat(m, (sizeof m), pos, + "Application built with libpng-"); + pos = png_safecat(m, (sizeof m), pos, user_png_ver); + pos = png_safecat(m, (sizeof m), pos, " but running with "); + pos = png_safecat(m, (sizeof m), pos, png_libpng_ver); + + png_warning(png_ptr, m); +#endif + +#ifdef PNG_ERROR_NUMBERS_SUPPORTED + png_ptr->flags = 0; +#endif + + return 0; + } + } + + /* Success return. */ + return 1; +} + +/* Generic function to create a png_struct for either read or write - this + * contains the common initialization. + */ +PNG_FUNCTION(png_structp /* PRIVATE */, +png_create_png_struct,(png_const_charp user_png_ver, png_voidp error_ptr, + png_error_ptr error_fn, png_error_ptr warn_fn, png_voidp mem_ptr, + png_malloc_ptr malloc_fn, png_free_ptr free_fn),PNG_ALLOCATED) +{ + png_struct create_struct; +# ifdef PNG_SETJMP_SUPPORTED + jmp_buf create_jmp_buf; +# endif + + /* This temporary stack-allocated structure is used to provide a place to + * build enough context to allow the user provided memory allocator (if any) + * to be called. + */ + memset(&create_struct, 0, (sizeof create_struct)); + + /* Added at libpng-1.2.6 */ +# ifdef PNG_USER_LIMITS_SUPPORTED + create_struct.user_width_max = PNG_USER_WIDTH_MAX; + create_struct.user_height_max = PNG_USER_HEIGHT_MAX; + +# ifdef PNG_USER_CHUNK_CACHE_MAX + /* Added at libpng-1.2.43 and 1.4.0 */ + create_struct.user_chunk_cache_max = PNG_USER_CHUNK_CACHE_MAX; +# endif + +# ifdef PNG_USER_CHUNK_MALLOC_MAX + /* Added at libpng-1.2.43 and 1.4.1, required only for read but exists + * in png_struct regardless. + */ + create_struct.user_chunk_malloc_max = PNG_USER_CHUNK_MALLOC_MAX; +# endif +# endif + + /* The following two API calls simply set fields in png_struct, so it is safe + * to do them now even though error handling is not yet set up. + */ +# ifdef PNG_USER_MEM_SUPPORTED + png_set_mem_fn(&create_struct, mem_ptr, malloc_fn, free_fn); +# endif + + /* (*error_fn) can return control to the caller after the error_ptr is set, + * this will result in a memory leak unless the error_fn does something + * extremely sophisticated. The design lacks merit but is implicit in the + * API. + */ + png_set_error_fn(&create_struct, error_ptr, error_fn, warn_fn); + +# ifdef PNG_SETJMP_SUPPORTED + if (!setjmp(create_jmp_buf)) + { + /* Temporarily fake out the longjmp information until we have + * successfully completed this function. This only works if we have + * setjmp() support compiled in, but it is safe - this stuff should + * never happen. + */ + create_struct.jmp_buf_ptr = &create_jmp_buf; + create_struct.jmp_buf_size = 0; /*stack allocation*/ + create_struct.longjmp_fn = longjmp; +# else + { +# endif + /* Call the general version checker (shared with read and write code): + */ + if (png_user_version_check(&create_struct, user_png_ver)) + { + png_structrp png_ptr = png_voidcast(png_structrp, + png_malloc_warn(&create_struct, (sizeof *png_ptr))); + + if (png_ptr != NULL) + { + /* png_ptr->zstream holds a back-pointer to the png_struct, so + * this can only be done now: + */ + create_struct.zstream.zalloc = png_zalloc; + create_struct.zstream.zfree = png_zfree; + create_struct.zstream.opaque = png_ptr; + +# ifdef PNG_SETJMP_SUPPORTED + /* Eliminate the local error handling: */ + create_struct.jmp_buf_ptr = NULL; + create_struct.jmp_buf_size = 0; + create_struct.longjmp_fn = 0; +# endif + + *png_ptr = create_struct; + + /* This is the successful return point */ + return png_ptr; + } + } + } + + /* A longjmp because of a bug in the application storage allocator or a + * simple failure to allocate the png_struct. + */ + return NULL; +} + +/* Allocate the memory for an info_struct for the application. */ +PNG_FUNCTION(png_infop,PNGAPI +png_create_info_struct,(png_const_structrp png_ptr),PNG_ALLOCATED) +{ + png_inforp info_ptr; + + png_debug(1, "in png_create_info_struct"); + + if (png_ptr == NULL) + return NULL; + + /* Use the internal API that does not (or at least should not) error out, so + * that this call always returns ok. The application typically sets up the + * error handling *after* creating the info_struct because this is the way it + * has always been done in 'example.c'. + */ + info_ptr = png_voidcast(png_inforp, png_malloc_base(png_ptr, + (sizeof *info_ptr))); + + if (info_ptr != NULL) + memset(info_ptr, 0, (sizeof *info_ptr)); + + return info_ptr; +} + +/* This function frees the memory associated with a single info struct. + * Normally, one would use either png_destroy_read_struct() or + * png_destroy_write_struct() to free an info struct, but this may be + * useful for some applications. From libpng 1.6.0 this function is also used + * internally to implement the png_info release part of the 'struct' destroy + * APIs. This ensures that all possible approaches free the same data (all of + * it). + */ +void PNGAPI +png_destroy_info_struct(png_const_structrp png_ptr, png_infopp info_ptr_ptr) +{ + png_inforp info_ptr = NULL; + + png_debug(1, "in png_destroy_info_struct"); + + if (png_ptr == NULL) + return; + + if (info_ptr_ptr != NULL) + info_ptr = *info_ptr_ptr; + + if (info_ptr != NULL) + { + /* Do this first in case of an error below; if the app implements its own + * memory management this can lead to png_free calling png_error, which + * will abort this routine and return control to the app error handler. + * An infinite loop may result if it then tries to free the same info + * ptr. + */ + *info_ptr_ptr = NULL; + + png_free_data(png_ptr, info_ptr, PNG_FREE_ALL, -1); + memset(info_ptr, 0, (sizeof *info_ptr)); + png_free(png_ptr, info_ptr); + } +} + +/* Initialize the info structure. This is now an internal function (0.89) + * and applications using it are urged to use png_create_info_struct() + * instead. Use deprecated in 1.6.0, internal use removed (used internally it + * is just a memset). + * + * NOTE: it is almost inconceivable that this API is used because it bypasses + * the user-memory mechanism and the user error handling/warning mechanisms in + * those cases where it does anything other than a memset. + */ +PNG_FUNCTION(void,PNGAPI +png_info_init_3,(png_infopp ptr_ptr, png_size_t png_info_struct_size), + PNG_DEPRECATED) +{ + png_inforp info_ptr = *ptr_ptr; + + png_debug(1, "in png_info_init_3"); + + if (info_ptr == NULL) + return; + + if ((sizeof (png_info)) > png_info_struct_size) + { + *ptr_ptr = NULL; + /* The following line is why this API should not be used: */ + free(info_ptr); + info_ptr = png_voidcast(png_inforp, png_malloc_base(NULL, + (sizeof *info_ptr))); + *ptr_ptr = info_ptr; + } + + /* Set everything to 0 */ + memset(info_ptr, 0, (sizeof *info_ptr)); +} + +/* The following API is not called internally */ +void PNGAPI +png_data_freer(png_const_structrp png_ptr, png_inforp info_ptr, + int freer, png_uint_32 mask) +{ + png_debug(1, "in png_data_freer"); + + if (png_ptr == NULL || info_ptr == NULL) + return; + + if (freer == PNG_DESTROY_WILL_FREE_DATA) + info_ptr->free_me |= mask; + + else if (freer == PNG_USER_WILL_FREE_DATA) + info_ptr->free_me &= ~mask; + + else + png_error(png_ptr, "Unknown freer parameter in png_data_freer"); +} + +void PNGAPI +png_free_data(png_const_structrp png_ptr, png_inforp info_ptr, png_uint_32 mask, + int num) +{ + png_debug(1, "in png_free_data"); + + if (png_ptr == NULL || info_ptr == NULL) + return; + +#ifdef PNG_TEXT_SUPPORTED + /* Free text item num or (if num == -1) all text items */ + if ((mask & PNG_FREE_TEXT) & info_ptr->free_me) + { + if (num != -1) + { + if (info_ptr->text && info_ptr->text[num].key) + { + png_free(png_ptr, info_ptr->text[num].key); + info_ptr->text[num].key = NULL; + } + } + + else + { + int i; + for (i = 0; i < info_ptr->num_text; i++) + png_free_data(png_ptr, info_ptr, PNG_FREE_TEXT, i); + png_free(png_ptr, info_ptr->text); + info_ptr->text = NULL; + info_ptr->num_text=0; + } + } +#endif + +#ifdef PNG_tRNS_SUPPORTED + /* Free any tRNS entry */ + if ((mask & PNG_FREE_TRNS) & info_ptr->free_me) + { + png_free(png_ptr, info_ptr->trans_alpha); + info_ptr->trans_alpha = NULL; + info_ptr->valid &= ~PNG_INFO_tRNS; + } +#endif + +#ifdef PNG_sCAL_SUPPORTED + /* Free any sCAL entry */ + if ((mask & PNG_FREE_SCAL) & info_ptr->free_me) + { + png_free(png_ptr, info_ptr->scal_s_width); + png_free(png_ptr, info_ptr->scal_s_height); + info_ptr->scal_s_width = NULL; + info_ptr->scal_s_height = NULL; + info_ptr->valid &= ~PNG_INFO_sCAL; + } +#endif + +#ifdef PNG_pCAL_SUPPORTED + /* Free any pCAL entry */ + if ((mask & PNG_FREE_PCAL) & info_ptr->free_me) + { + png_free(png_ptr, info_ptr->pcal_purpose); + png_free(png_ptr, info_ptr->pcal_units); + info_ptr->pcal_purpose = NULL; + info_ptr->pcal_units = NULL; + if (info_ptr->pcal_params != NULL) + { + unsigned int i; + for (i = 0; i < info_ptr->pcal_nparams; i++) + { + png_free(png_ptr, info_ptr->pcal_params[i]); + info_ptr->pcal_params[i] = NULL; + } + png_free(png_ptr, info_ptr->pcal_params); + info_ptr->pcal_params = NULL; + } + info_ptr->valid &= ~PNG_INFO_pCAL; + } +#endif + +#ifdef PNG_iCCP_SUPPORTED + /* Free any profile entry */ + if ((mask & PNG_FREE_ICCP) & info_ptr->free_me) + { + png_free(png_ptr, info_ptr->iccp_name); + png_free(png_ptr, info_ptr->iccp_profile); + info_ptr->iccp_name = NULL; + info_ptr->iccp_profile = NULL; + info_ptr->valid &= ~PNG_INFO_iCCP; + } +#endif + +#ifdef PNG_sPLT_SUPPORTED + /* Free a given sPLT entry, or (if num == -1) all sPLT entries */ + if ((mask & PNG_FREE_SPLT) & info_ptr->free_me) + { + if (num != -1) + { + if (info_ptr->splt_palettes) + { + png_free(png_ptr, info_ptr->splt_palettes[num].name); + png_free(png_ptr, info_ptr->splt_palettes[num].entries); + info_ptr->splt_palettes[num].name = NULL; + info_ptr->splt_palettes[num].entries = NULL; + } + } + + else + { + if (info_ptr->splt_palettes_num) + { + int i; + for (i = 0; i < info_ptr->splt_palettes_num; i++) + png_free_data(png_ptr, info_ptr, PNG_FREE_SPLT, (int)i); + + png_free(png_ptr, info_ptr->splt_palettes); + info_ptr->splt_palettes = NULL; + info_ptr->splt_palettes_num = 0; + } + info_ptr->valid &= ~PNG_INFO_sPLT; + } + } +#endif + +#ifdef PNG_STORE_UNKNOWN_CHUNKS_SUPPORTED + if ((mask & PNG_FREE_UNKN) & info_ptr->free_me) + { + if (num != -1) + { + if (info_ptr->unknown_chunks) + { + png_free(png_ptr, info_ptr->unknown_chunks[num].data); + info_ptr->unknown_chunks[num].data = NULL; + } + } + + else + { + int i; + + if (info_ptr->unknown_chunks_num) + { + for (i = 0; i < info_ptr->unknown_chunks_num; i++) + png_free_data(png_ptr, info_ptr, PNG_FREE_UNKN, (int)i); + + png_free(png_ptr, info_ptr->unknown_chunks); + info_ptr->unknown_chunks = NULL; + info_ptr->unknown_chunks_num = 0; + } + } + } +#endif + +#ifdef PNG_hIST_SUPPORTED + /* Free any hIST entry */ + if ((mask & PNG_FREE_HIST) & info_ptr->free_me) + { + png_free(png_ptr, info_ptr->hist); + info_ptr->hist = NULL; + info_ptr->valid &= ~PNG_INFO_hIST; + } +#endif + + /* Free any PLTE entry that was internally allocated */ + if ((mask & PNG_FREE_PLTE) & info_ptr->free_me) + { + png_free(png_ptr, info_ptr->palette); + info_ptr->palette = NULL; + info_ptr->valid &= ~PNG_INFO_PLTE; + info_ptr->num_palette = 0; + } + +#ifdef PNG_INFO_IMAGE_SUPPORTED + /* Free any image bits attached to the info structure */ + if ((mask & PNG_FREE_ROWS) & info_ptr->free_me) + { + if (info_ptr->row_pointers) + { + png_uint_32 row; + for (row = 0; row < info_ptr->height; row++) + { + png_free(png_ptr, info_ptr->row_pointers[row]); + info_ptr->row_pointers[row] = NULL; + } + png_free(png_ptr, info_ptr->row_pointers); + info_ptr->row_pointers = NULL; + } + info_ptr->valid &= ~PNG_INFO_IDAT; + } +#endif + + if (num != -1) + mask &= ~PNG_FREE_MUL; + + info_ptr->free_me &= ~mask; +} +#endif /* defined(PNG_READ_SUPPORTED) || defined(PNG_WRITE_SUPPORTED) */ + +/* This function returns a pointer to the io_ptr associated with the user + * functions. The application should free any memory associated with this + * pointer before png_write_destroy() or png_read_destroy() are called. + */ +png_voidp PNGAPI +png_get_io_ptr(png_const_structrp png_ptr) +{ + if (png_ptr == NULL) + return (NULL); + + return (png_ptr->io_ptr); +} + +#if defined(PNG_READ_SUPPORTED) || defined(PNG_WRITE_SUPPORTED) +# ifdef PNG_STDIO_SUPPORTED +/* Initialize the default input/output functions for the PNG file. If you + * use your own read or write routines, you can call either png_set_read_fn() + * or png_set_write_fn() instead of png_init_io(). If you have defined + * PNG_NO_STDIO or otherwise disabled PNG_STDIO_SUPPORTED, you must use a + * function of your own because "FILE *" isn't necessarily available. + */ +void PNGAPI +png_init_io(png_structrp png_ptr, png_FILE_p fp) +{ + png_debug(1, "in png_init_io"); + + if (png_ptr == NULL) + return; + + png_ptr->io_ptr = (png_voidp)fp; +} +# endif + +#ifdef PNG_SAVE_INT_32_SUPPORTED +/* The png_save_int_32 function assumes integers are stored in two's + * complement format. If this isn't the case, then this routine needs to + * be modified to write data in two's complement format. Note that, + * the following works correctly even if png_int_32 has more than 32 bits + * (compare the more complex code required on read for sign extension.) + */ +void PNGAPI +png_save_int_32(png_bytep buf, png_int_32 i) +{ + buf[0] = (png_byte)((i >> 24) & 0xff); + buf[1] = (png_byte)((i >> 16) & 0xff); + buf[2] = (png_byte)((i >> 8) & 0xff); + buf[3] = (png_byte)(i & 0xff); +} +#endif + +# ifdef PNG_TIME_RFC1123_SUPPORTED +/* Convert the supplied time into an RFC 1123 string suitable for use in + * a "Creation Time" or other text-based time string. + */ +int PNGAPI +png_convert_to_rfc1123_buffer(char out[29], png_const_timep ptime) +{ + static PNG_CONST char short_months[12][4] = + {"Jan", "Feb", "Mar", "Apr", "May", "Jun", + "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"}; + + if (out == NULL) + return 0; + + if (ptime->year > 9999 /* RFC1123 limitation */ || + ptime->month == 0 || ptime->month > 12 || + ptime->day == 0 || ptime->day > 31 || + ptime->hour > 23 || ptime->minute > 59 || + ptime->second > 60) + return 0; + + { + size_t pos = 0; + char number_buf[5]; /* enough for a four-digit year */ + +# define APPEND_STRING(string) pos = png_safecat(out, 29, pos, (string)) +# define APPEND_NUMBER(format, value)\ + APPEND_STRING(PNG_FORMAT_NUMBER(number_buf, format, (value))) +# define APPEND(ch) if (pos < 28) out[pos++] = (ch) + + APPEND_NUMBER(PNG_NUMBER_FORMAT_u, (unsigned)ptime->day); + APPEND(' '); + APPEND_STRING(short_months[(ptime->month - 1)]); + APPEND(' '); + APPEND_NUMBER(PNG_NUMBER_FORMAT_u, ptime->year); + APPEND(' '); + APPEND_NUMBER(PNG_NUMBER_FORMAT_02u, (unsigned)ptime->hour); + APPEND(':'); + APPEND_NUMBER(PNG_NUMBER_FORMAT_02u, (unsigned)ptime->minute); + APPEND(':'); + APPEND_NUMBER(PNG_NUMBER_FORMAT_02u, (unsigned)ptime->second); + APPEND_STRING(" +0000"); /* This reliably terminates the buffer */ + +# undef APPEND +# undef APPEND_NUMBER +# undef APPEND_STRING + } + + return 1; +} + +# if PNG_LIBPNG_VER < 10700 +/* To do: remove the following from libpng-1.7 */ +/* Original API that uses a private buffer in png_struct. + * Deprecated because it causes png_struct to carry a spurious temporary + * buffer (png_struct::time_buffer), better to have the caller pass this in. + */ +png_const_charp PNGAPI +png_convert_to_rfc1123(png_structrp png_ptr, png_const_timep ptime) +{ + if (png_ptr != NULL) + { + /* The only failure above if png_ptr != NULL is from an invalid ptime */ + if (!png_convert_to_rfc1123_buffer(png_ptr->time_buffer, ptime)) + png_warning(png_ptr, "Ignoring invalid time value"); + + else + return png_ptr->time_buffer; + } + + return NULL; +} +# endif +# endif /* PNG_TIME_RFC1123_SUPPORTED */ + +#endif /* defined(PNG_READ_SUPPORTED) || defined(PNG_WRITE_SUPPORTED) */ + +png_const_charp PNGAPI +png_get_copyright(png_const_structrp png_ptr) +{ + PNG_UNUSED(png_ptr) /* Silence compiler warning about unused png_ptr */ +#ifdef PNG_STRING_COPYRIGHT + return PNG_STRING_COPYRIGHT +#else +# ifdef __STDC__ + return PNG_STRING_NEWLINE \ + "libpng version 1.6.2 - April 25, 2013" PNG_STRING_NEWLINE \ + "Copyright (c) 1998-2013 Glenn Randers-Pehrson" PNG_STRING_NEWLINE \ + "Copyright (c) 1996-1997 Andreas Dilger" PNG_STRING_NEWLINE \ + "Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc." \ + PNG_STRING_NEWLINE; +# else + return "libpng version 1.6.2 - April 25, 2013\ + Copyright (c) 1998-2013 Glenn Randers-Pehrson\ + Copyright (c) 1996-1997 Andreas Dilger\ + Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc."; +# endif +#endif +} + +/* The following return the library version as a short string in the + * format 1.0.0 through 99.99.99zz. To get the version of *.h files + * used with your application, print out PNG_LIBPNG_VER_STRING, which + * is defined in png.h. + * Note: now there is no difference between png_get_libpng_ver() and + * png_get_header_ver(). Due to the version_nn_nn_nn typedef guard, + * it is guaranteed that png.c uses the correct version of png.h. + */ +png_const_charp PNGAPI +png_get_libpng_ver(png_const_structrp png_ptr) +{ + /* Version of *.c files used when building libpng */ + return png_get_header_ver(png_ptr); +} + +png_const_charp PNGAPI +png_get_header_ver(png_const_structrp png_ptr) +{ + /* Version of *.h files used when building libpng */ + PNG_UNUSED(png_ptr) /* Silence compiler warning about unused png_ptr */ + return PNG_LIBPNG_VER_STRING; +} + +png_const_charp PNGAPI +png_get_header_version(png_const_structrp png_ptr) +{ + /* Returns longer string containing both version and date */ + PNG_UNUSED(png_ptr) /* Silence compiler warning about unused png_ptr */ +#ifdef __STDC__ + return PNG_HEADER_VERSION_STRING +# ifndef PNG_READ_SUPPORTED + " (NO READ SUPPORT)" +# endif + PNG_STRING_NEWLINE; +#else + return PNG_HEADER_VERSION_STRING; +#endif +} + +#ifdef PNG_SET_UNKNOWN_CHUNKS_SUPPORTED +int PNGAPI +png_handle_as_unknown(png_const_structrp png_ptr, png_const_bytep chunk_name) +{ + /* Check chunk_name and return "keep" value if it's on the list, else 0 */ + png_const_bytep p, p_end; + + if (png_ptr == NULL || chunk_name == NULL || png_ptr->num_chunk_list == 0) + return PNG_HANDLE_CHUNK_AS_DEFAULT; + + p_end = png_ptr->chunk_list; + p = p_end + png_ptr->num_chunk_list*5; /* beyond end */ + + /* The code is the fifth byte after each four byte string. Historically this + * code was always searched from the end of the list, this is no longer + * necessary because the 'set' routine handles duplicate entries correcty. + */ + do /* num_chunk_list > 0, so at least one */ + { + p -= 5; + + if (!memcmp(chunk_name, p, 4)) + return p[4]; + } + while (p > p_end); + + /* This means that known chunks should be processed and unknown chunks should + * be handled according to the value of png_ptr->unknown_default; this can be + * confusing because, as a result, there are two levels of defaulting for + * unknown chunks. + */ + return PNG_HANDLE_CHUNK_AS_DEFAULT; +} + +#ifdef PNG_READ_UNKNOWN_CHUNKS_SUPPORTED +int /* PRIVATE */ +png_chunk_unknown_handling(png_const_structrp png_ptr, png_uint_32 chunk_name) +{ + png_byte chunk_string[5]; + + PNG_CSTRING_FROM_CHUNK(chunk_string, chunk_name); + return png_handle_as_unknown(png_ptr, chunk_string); +} +#endif /* READ_UNKNOWN_CHUNKS */ +#endif /* SET_UNKNOWN_CHUNKS */ + +#ifdef PNG_READ_SUPPORTED +/* This function, added to libpng-1.0.6g, is untested. */ +int PNGAPI +png_reset_zstream(png_structrp png_ptr) +{ + if (png_ptr == NULL) + return Z_STREAM_ERROR; + + /* WARNING: this resets the window bits to the maximum! */ + return (inflateReset(&png_ptr->zstream)); +} +#endif /* PNG_READ_SUPPORTED */ + +/* This function was added to libpng-1.0.7 */ +png_uint_32 PNGAPI +png_access_version_number(void) +{ + /* Version of *.c files used when building libpng */ + return((png_uint_32)PNG_LIBPNG_VER); +} + + + +#if defined(PNG_READ_SUPPORTED) || defined(PNG_WRITE_SUPPORTED) +/* Ensure that png_ptr->zstream.msg holds some appropriate error message string. + * If it doesn't 'ret' is used to set it to something appropriate, even in cases + * like Z_OK or Z_STREAM_END where the error code is apparently a success code. + */ +void /* PRIVATE */ +png_zstream_error(png_structrp png_ptr, int ret) +{ + /* Translate 'ret' into an appropriate error string, priority is given to the + * one in zstream if set. This always returns a string, even in cases like + * Z_OK or Z_STREAM_END where the error code is a success code. + */ + if (png_ptr->zstream.msg == NULL) switch (ret) + { + default: + case Z_OK: + png_ptr->zstream.msg = PNGZ_MSG_CAST("unexpected zlib return code"); + break; + + case Z_STREAM_END: + /* Normal exit */ + png_ptr->zstream.msg = PNGZ_MSG_CAST("unexpected end of LZ stream"); + break; + + case Z_NEED_DICT: + /* This means the deflate stream did not have a dictionary; this + * indicates a bogus PNG. + */ + png_ptr->zstream.msg = PNGZ_MSG_CAST("missing LZ dictionary"); + break; + + case Z_ERRNO: + /* gz APIs only: should not happen */ + png_ptr->zstream.msg = PNGZ_MSG_CAST("zlib IO error"); + break; + + case Z_STREAM_ERROR: + /* internal libpng error */ + png_ptr->zstream.msg = PNGZ_MSG_CAST("bad parameters to zlib"); + break; + + case Z_DATA_ERROR: + png_ptr->zstream.msg = PNGZ_MSG_CAST("damaged LZ stream"); + break; + + case Z_MEM_ERROR: + png_ptr->zstream.msg = PNGZ_MSG_CAST("insufficient memory"); + break; + + case Z_BUF_ERROR: + /* End of input or output; not a problem if the caller is doing + * incremental read or write. + */ + png_ptr->zstream.msg = PNGZ_MSG_CAST("truncated"); + break; + + case Z_VERSION_ERROR: + png_ptr->zstream.msg = PNGZ_MSG_CAST("unsupported zlib version"); + break; + + case PNG_UNEXPECTED_ZLIB_RETURN: + /* Compile errors here mean that zlib now uses the value co-opted in + * pngpriv.h for PNG_UNEXPECTED_ZLIB_RETURN; update the switch above + * and change pngpriv.h. Note that this message is "... return", + * whereas the default/Z_OK one is "... return code". + */ + png_ptr->zstream.msg = PNGZ_MSG_CAST("unexpected zlib return"); + break; + } +} + +/* png_convert_size: a PNGAPI but no longer in png.h, so deleted + * at libpng 1.5.5! + */ + +/* Added at libpng version 1.2.34 and 1.4.0 (moved from pngset.c) */ +#ifdef PNG_GAMMA_SUPPORTED /* always set if COLORSPACE */ +static int +png_colorspace_check_gamma(png_const_structrp png_ptr, + png_colorspacerp colorspace, png_fixed_point gAMA, int from) + /* This is called to check a new gamma value against an existing one. The + * routine returns false if the new gamma value should not be written. + * + * 'from' says where the new gamma value comes from: + * + * 0: the new gamma value is the libpng estimate for an ICC profile + * 1: the new gamma value comes from a gAMA chunk + * 2: the new gamma value comes from an sRGB chunk + */ +{ + png_fixed_point gtest; + + if ((colorspace->flags & PNG_COLORSPACE_HAVE_GAMMA) != 0 && + (!png_muldiv(>est, colorspace->gamma, PNG_FP_1, gAMA) || + png_gamma_significant(gtest))) + { + /* Either this is an sRGB image, in which case the calculated gamma + * approximation should match, or this is an image with a profile and the + * value libpng calculates for the gamma of the profile does not match the + * value recorded in the file. The former, sRGB, case is an error, the + * latter is just a warning. + */ + if ((colorspace->flags & PNG_COLORSPACE_FROM_sRGB) != 0 || from == 2) + { + png_chunk_report(png_ptr, "gamma value does not match sRGB", + PNG_CHUNK_ERROR); + /* Do not overwrite an sRGB value */ + return from == 2; + } + + else /* sRGB tag not involved */ + { + png_chunk_report(png_ptr, "gamma value does not match libpng estimate", + PNG_CHUNK_WARNING); + return from == 1; + } + } + + return 1; +} + +void /* PRIVATE */ +png_colorspace_set_gamma(png_const_structrp png_ptr, + png_colorspacerp colorspace, png_fixed_point gAMA) +{ + /* Changed in libpng-1.5.4 to limit the values to ensure overflow can't + * occur. Since the fixed point representation is assymetrical it is + * possible for 1/gamma to overflow the limit of 21474 and this means the + * gamma value must be at least 5/100000 and hence at most 20000.0. For + * safety the limits here are a little narrower. The values are 0.00016 to + * 6250.0, which are truly ridiculous gamma values (and will produce + * displays that are all black or all white.) + * + * In 1.6.0 this test replaces the ones in pngrutil.c, in the gAMA chunk + * handling code, which only required the value to be >0. + */ + png_const_charp errmsg; + + if (gAMA < 16 || gAMA > 625000000) + errmsg = "gamma value out of range"; + +# ifdef PNG_READ_gAMA_SUPPORTED + /* Allow the application to set the gamma value more than once */ + else if ((png_ptr->mode & PNG_IS_READ_STRUCT) != 0 && + (colorspace->flags & PNG_COLORSPACE_FROM_gAMA) != 0) + errmsg = "duplicate"; +# endif + + /* Do nothing if the colorspace is already invalid */ + else if (colorspace->flags & PNG_COLORSPACE_INVALID) + return; + + else + { + if (png_colorspace_check_gamma(png_ptr, colorspace, gAMA, 1/*from gAMA*/)) + { + /* Store this gamma value. */ + colorspace->gamma = gAMA; + colorspace->flags |= + (PNG_COLORSPACE_HAVE_GAMMA | PNG_COLORSPACE_FROM_gAMA); + } + + /* At present if the check_gamma test fails the gamma of the colorspace is + * not updated however the colorspace is not invalidated. This + * corresponds to the case where the existing gamma comes from an sRGB + * chunk or profile. An error message has already been output. + */ + return; + } + + /* Error exit - errmsg has been set. */ + colorspace->flags |= PNG_COLORSPACE_INVALID; + png_chunk_report(png_ptr, errmsg, PNG_CHUNK_WRITE_ERROR); +} + +void /* PRIVATE */ +png_colorspace_sync_info(png_const_structrp png_ptr, png_inforp info_ptr) +{ + if (info_ptr->colorspace.flags & PNG_COLORSPACE_INVALID) + { + /* Everything is invalid */ + info_ptr->valid &= ~(PNG_INFO_gAMA|PNG_INFO_cHRM|PNG_INFO_sRGB| + PNG_INFO_iCCP); + +# ifdef PNG_COLORSPACE_SUPPORTED + /* Clean up the iCCP profile now if it won't be used. */ + png_free_data(png_ptr, info_ptr, PNG_FREE_ICCP, -1/*not used*/); +# else + PNG_UNUSED(png_ptr) +# endif + } + + else + { +# ifdef PNG_COLORSPACE_SUPPORTED + /* Leave the INFO_iCCP flag set if the pngset.c code has already set + * it; this allows a PNG to contain a profile which matches sRGB and + * yet still have that profile retrievable by the application. + */ + if (info_ptr->colorspace.flags & PNG_COLORSPACE_MATCHES_sRGB) + info_ptr->valid |= PNG_INFO_sRGB; + + else + info_ptr->valid &= ~PNG_INFO_sRGB; + + if (info_ptr->colorspace.flags & PNG_COLORSPACE_HAVE_ENDPOINTS) + info_ptr->valid |= PNG_INFO_cHRM; + + else + info_ptr->valid &= ~PNG_INFO_cHRM; +# endif + + if (info_ptr->colorspace.flags & PNG_COLORSPACE_HAVE_GAMMA) + info_ptr->valid |= PNG_INFO_gAMA; + + else + info_ptr->valid &= ~PNG_INFO_gAMA; + } +} + +#ifdef PNG_READ_SUPPORTED +void /* PRIVATE */ +png_colorspace_sync(png_const_structrp png_ptr, png_inforp info_ptr) +{ + if (info_ptr == NULL) /* reduce code size; check here not in the caller */ + return; + + info_ptr->colorspace = png_ptr->colorspace; + png_colorspace_sync_info(png_ptr, info_ptr); +} +#endif +#endif + +#ifdef PNG_COLORSPACE_SUPPORTED +/* Added at libpng-1.5.5 to support read and write of true CIEXYZ values for + * cHRM, as opposed to using chromaticities. These internal APIs return + * non-zero on a parameter error. The X, Y and Z values are required to be + * positive and less than 1.0. + */ +static int +png_xy_from_XYZ(png_xy *xy, const png_XYZ *XYZ) +{ + png_int_32 d, dwhite, whiteX, whiteY; + + d = XYZ->red_X + XYZ->red_Y + XYZ->red_Z; + if (!png_muldiv(&xy->redx, XYZ->red_X, PNG_FP_1, d)) return 1; + if (!png_muldiv(&xy->redy, XYZ->red_Y, PNG_FP_1, d)) return 1; + dwhite = d; + whiteX = XYZ->red_X; + whiteY = XYZ->red_Y; + + d = XYZ->green_X + XYZ->green_Y + XYZ->green_Z; + if (!png_muldiv(&xy->greenx, XYZ->green_X, PNG_FP_1, d)) return 1; + if (!png_muldiv(&xy->greeny, XYZ->green_Y, PNG_FP_1, d)) return 1; + dwhite += d; + whiteX += XYZ->green_X; + whiteY += XYZ->green_Y; + + d = XYZ->blue_X + XYZ->blue_Y + XYZ->blue_Z; + if (!png_muldiv(&xy->bluex, XYZ->blue_X, PNG_FP_1, d)) return 1; + if (!png_muldiv(&xy->bluey, XYZ->blue_Y, PNG_FP_1, d)) return 1; + dwhite += d; + whiteX += XYZ->blue_X; + whiteY += XYZ->blue_Y; + + /* The reference white is simply the sum of the end-point (X,Y,Z) vectors, + * thus: + */ + if (!png_muldiv(&xy->whitex, whiteX, PNG_FP_1, dwhite)) return 1; + if (!png_muldiv(&xy->whitey, whiteY, PNG_FP_1, dwhite)) return 1; + + return 0; +} + +static int +png_XYZ_from_xy(png_XYZ *XYZ, const png_xy *xy) +{ + png_fixed_point red_inverse, green_inverse, blue_scale; + png_fixed_point left, right, denominator; + + /* Check xy and, implicitly, z. Note that wide gamut color spaces typically + * have end points with 0 tristimulus values (these are impossible end + * points, but they are used to cover the possible colors.) + */ + if (xy->redx < 0 || xy->redx > PNG_FP_1) return 1; + if (xy->redy < 0 || xy->redy > PNG_FP_1-xy->redx) return 1; + if (xy->greenx < 0 || xy->greenx > PNG_FP_1) return 1; + if (xy->greeny < 0 || xy->greeny > PNG_FP_1-xy->greenx) return 1; + if (xy->bluex < 0 || xy->bluex > PNG_FP_1) return 1; + if (xy->bluey < 0 || xy->bluey > PNG_FP_1-xy->bluex) return 1; + if (xy->whitex < 0 || xy->whitex > PNG_FP_1) return 1; + if (xy->whitey < 0 || xy->whitey > PNG_FP_1-xy->whitex) return 1; + + /* The reverse calculation is more difficult because the original tristimulus + * value had 9 independent values (red,green,blue)x(X,Y,Z) however only 8 + * derived values were recorded in the cHRM chunk; + * (red,green,blue,white)x(x,y). This loses one degree of freedom and + * therefore an arbitrary ninth value has to be introduced to undo the + * original transformations. + * + * Think of the original end-points as points in (X,Y,Z) space. The + * chromaticity values (c) have the property: + * + * C + * c = --------- + * X + Y + Z + * + * For each c (x,y,z) from the corresponding original C (X,Y,Z). Thus the + * three chromaticity values (x,y,z) for each end-point obey the + * relationship: + * + * x + y + z = 1 + * + * This describes the plane in (X,Y,Z) space that intersects each axis at the + * value 1.0; call this the chromaticity plane. Thus the chromaticity + * calculation has scaled each end-point so that it is on the x+y+z=1 plane + * and chromaticity is the intersection of the vector from the origin to the + * (X,Y,Z) value with the chromaticity plane. + * + * To fully invert the chromaticity calculation we would need the three + * end-point scale factors, (red-scale, green-scale, blue-scale), but these + * were not recorded. Instead we calculated the reference white (X,Y,Z) and + * recorded the chromaticity of this. The reference white (X,Y,Z) would have + * given all three of the scale factors since: + * + * color-C = color-c * color-scale + * white-C = red-C + green-C + blue-C + * = red-c*red-scale + green-c*green-scale + blue-c*blue-scale + * + * But cHRM records only white-x and white-y, so we have lost the white scale + * factor: + * + * white-C = white-c*white-scale + * + * To handle this the inverse transformation makes an arbitrary assumption + * about white-scale: + * + * Assume: white-Y = 1.0 + * Hence: white-scale = 1/white-y + * Or: red-Y + green-Y + blue-Y = 1.0 + * + * Notice the last statement of the assumption gives an equation in three of + * the nine values we want to calculate. 8 more equations come from the + * above routine as summarised at the top above (the chromaticity + * calculation): + * + * Given: color-x = color-X / (color-X + color-Y + color-Z) + * Hence: (color-x - 1)*color-X + color.x*color-Y + color.x*color-Z = 0 + * + * This is 9 simultaneous equations in the 9 variables "color-C" and can be + * solved by Cramer's rule. Cramer's rule requires calculating 10 9x9 matrix + * determinants, however this is not as bad as it seems because only 28 of + * the total of 90 terms in the various matrices are non-zero. Nevertheless + * Cramer's rule is notoriously numerically unstable because the determinant + * calculation involves the difference of large, but similar, numbers. It is + * difficult to be sure that the calculation is stable for real world values + * and it is certain that it becomes unstable where the end points are close + * together. + * + * So this code uses the perhaps slightly less optimal but more + * understandable and totally obvious approach of calculating color-scale. + * + * This algorithm depends on the precision in white-scale and that is + * (1/white-y), so we can immediately see that as white-y approaches 0 the + * accuracy inherent in the cHRM chunk drops off substantially. + * + * libpng arithmetic: a simple invertion of the above equations + * ------------------------------------------------------------ + * + * white_scale = 1/white-y + * white-X = white-x * white-scale + * white-Y = 1.0 + * white-Z = (1 - white-x - white-y) * white_scale + * + * white-C = red-C + green-C + blue-C + * = red-c*red-scale + green-c*green-scale + blue-c*blue-scale + * + * This gives us three equations in (red-scale,green-scale,blue-scale) where + * all the coefficients are now known: + * + * red-x*red-scale + green-x*green-scale + blue-x*blue-scale + * = white-x/white-y + * red-y*red-scale + green-y*green-scale + blue-y*blue-scale = 1 + * red-z*red-scale + green-z*green-scale + blue-z*blue-scale + * = (1 - white-x - white-y)/white-y + * + * In the last equation color-z is (1 - color-x - color-y) so we can add all + * three equations together to get an alternative third: + * + * red-scale + green-scale + blue-scale = 1/white-y = white-scale + * + * So now we have a Cramer's rule solution where the determinants are just + * 3x3 - far more tractible. Unfortunately 3x3 determinants still involve + * multiplication of three coefficients so we can't guarantee to avoid + * overflow in the libpng fixed point representation. Using Cramer's rule in + * floating point is probably a good choice here, but it's not an option for + * fixed point. Instead proceed to simplify the first two equations by + * eliminating what is likely to be the largest value, blue-scale: + * + * blue-scale = white-scale - red-scale - green-scale + * + * Hence: + * + * (red-x - blue-x)*red-scale + (green-x - blue-x)*green-scale = + * (white-x - blue-x)*white-scale + * + * (red-y - blue-y)*red-scale + (green-y - blue-y)*green-scale = + * 1 - blue-y*white-scale + * + * And now we can trivially solve for (red-scale,green-scale): + * + * green-scale = + * (white-x - blue-x)*white-scale - (red-x - blue-x)*red-scale + * ----------------------------------------------------------- + * green-x - blue-x + * + * red-scale = + * 1 - blue-y*white-scale - (green-y - blue-y) * green-scale + * --------------------------------------------------------- + * red-y - blue-y + * + * Hence: + * + * red-scale = + * ( (green-x - blue-x) * (white-y - blue-y) - + * (green-y - blue-y) * (white-x - blue-x) ) / white-y + * ------------------------------------------------------------------------- + * (green-x - blue-x)*(red-y - blue-y)-(green-y - blue-y)*(red-x - blue-x) + * + * green-scale = + * ( (red-y - blue-y) * (white-x - blue-x) - + * (red-x - blue-x) * (white-y - blue-y) ) / white-y + * ------------------------------------------------------------------------- + * (green-x - blue-x)*(red-y - blue-y)-(green-y - blue-y)*(red-x - blue-x) + * + * Accuracy: + * The input values have 5 decimal digits of accuracy. The values are all in + * the range 0 < value < 1, so simple products are in the same range but may + * need up to 10 decimal digits to preserve the original precision and avoid + * underflow. Because we are using a 32-bit signed representation we cannot + * match this; the best is a little over 9 decimal digits, less than 10. + * + * The approach used here is to preserve the maximum precision within the + * signed representation. Because the red-scale calculation above uses the + * difference between two products of values that must be in the range -1..+1 + * it is sufficient to divide the product by 7; ceil(100,000/32767*2). The + * factor is irrelevant in the calculation because it is applied to both + * numerator and denominator. + * + * Note that the values of the differences of the products of the + * chromaticities in the above equations tend to be small, for example for + * the sRGB chromaticities they are: + * + * red numerator: -0.04751 + * green numerator: -0.08788 + * denominator: -0.2241 (without white-y multiplication) + * + * The resultant Y coefficients from the chromaticities of some widely used + * color space definitions are (to 15 decimal places): + * + * sRGB + * 0.212639005871510 0.715168678767756 0.072192315360734 + * Kodak ProPhoto + * 0.288071128229293 0.711843217810102 0.000085653960605 + * Adobe RGB + * 0.297344975250536 0.627363566255466 0.075291458493998 + * Adobe Wide Gamut RGB + * 0.258728243040113 0.724682314948566 0.016589442011321 + */ + /* By the argument, above overflow should be impossible here. The return + * value of 2 indicates an internal error to the caller. + */ + if (!png_muldiv(&left, xy->greenx-xy->bluex, xy->redy - xy->bluey, 7)) + return 2; + if (!png_muldiv(&right, xy->greeny-xy->bluey, xy->redx - xy->bluex, 7)) + return 2; + denominator = left - right; + + /* Now find the red numerator. */ + if (!png_muldiv(&left, xy->greenx-xy->bluex, xy->whitey-xy->bluey, 7)) + return 2; + if (!png_muldiv(&right, xy->greeny-xy->bluey, xy->whitex-xy->bluex, 7)) + return 2; + + /* Overflow is possible here and it indicates an extreme set of PNG cHRM + * chunk values. This calculation actually returns the reciprocal of the + * scale value because this allows us to delay the multiplication of white-y + * into the denominator, which tends to produce a small number. + */ + if (!png_muldiv(&red_inverse, xy->whitey, denominator, left-right) || + red_inverse <= xy->whitey /* r+g+b scales = white scale */) + return 1; + + /* Similarly for green_inverse: */ + if (!png_muldiv(&left, xy->redy-xy->bluey, xy->whitex-xy->bluex, 7)) + return 2; + if (!png_muldiv(&right, xy->redx-xy->bluex, xy->whitey-xy->bluey, 7)) + return 2; + if (!png_muldiv(&green_inverse, xy->whitey, denominator, left-right) || + green_inverse <= xy->whitey) + return 1; + + /* And the blue scale, the checks above guarantee this can't overflow but it + * can still produce 0 for extreme cHRM values. + */ + blue_scale = png_reciprocal(xy->whitey) - png_reciprocal(red_inverse) - + png_reciprocal(green_inverse); + if (blue_scale <= 0) return 1; + + + /* And fill in the png_XYZ: */ + if (!png_muldiv(&XYZ->red_X, xy->redx, PNG_FP_1, red_inverse)) return 1; + if (!png_muldiv(&XYZ->red_Y, xy->redy, PNG_FP_1, red_inverse)) return 1; + if (!png_muldiv(&XYZ->red_Z, PNG_FP_1 - xy->redx - xy->redy, PNG_FP_1, + red_inverse)) + return 1; + + if (!png_muldiv(&XYZ->green_X, xy->greenx, PNG_FP_1, green_inverse)) + return 1; + if (!png_muldiv(&XYZ->green_Y, xy->greeny, PNG_FP_1, green_inverse)) + return 1; + if (!png_muldiv(&XYZ->green_Z, PNG_FP_1 - xy->greenx - xy->greeny, PNG_FP_1, + green_inverse)) + return 1; + + if (!png_muldiv(&XYZ->blue_X, xy->bluex, blue_scale, PNG_FP_1)) return 1; + if (!png_muldiv(&XYZ->blue_Y, xy->bluey, blue_scale, PNG_FP_1)) return 1; + if (!png_muldiv(&XYZ->blue_Z, PNG_FP_1 - xy->bluex - xy->bluey, blue_scale, + PNG_FP_1)) + return 1; + + return 0; /*success*/ +} + +static int +png_XYZ_normalize(png_XYZ *XYZ) +{ + png_int_32 Y; + + if (XYZ->red_Y < 0 || XYZ->green_Y < 0 || XYZ->blue_Y < 0 || + XYZ->red_X < 0 || XYZ->green_X < 0 || XYZ->blue_X < 0 || + XYZ->red_Z < 0 || XYZ->green_Z < 0 || XYZ->blue_Z < 0) + return 1; + + /* Normalize by scaling so the sum of the end-point Y values is PNG_FP_1. + * IMPLEMENTATION NOTE: ANSI requires signed overflow not to occur, therefore + * relying on addition of two positive values producing a negative one is not + * safe. + */ + Y = XYZ->red_Y; + if (0x7fffffff - Y < XYZ->green_X) return 1; + Y += XYZ->green_Y; + if (0x7fffffff - Y < XYZ->blue_X) return 1; + Y += XYZ->blue_Y; + + if (Y != PNG_FP_1) + { + if (!png_muldiv(&XYZ->red_X, XYZ->red_X, PNG_FP_1, Y)) return 1; + if (!png_muldiv(&XYZ->red_Y, XYZ->red_Y, PNG_FP_1, Y)) return 1; + if (!png_muldiv(&XYZ->red_Z, XYZ->red_Z, PNG_FP_1, Y)) return 1; + + if (!png_muldiv(&XYZ->green_X, XYZ->green_X, PNG_FP_1, Y)) return 1; + if (!png_muldiv(&XYZ->green_Y, XYZ->green_Y, PNG_FP_1, Y)) return 1; + if (!png_muldiv(&XYZ->green_Z, XYZ->green_Z, PNG_FP_1, Y)) return 1; + + if (!png_muldiv(&XYZ->blue_X, XYZ->blue_X, PNG_FP_1, Y)) return 1; + if (!png_muldiv(&XYZ->blue_Y, XYZ->blue_Y, PNG_FP_1, Y)) return 1; + if (!png_muldiv(&XYZ->blue_Z, XYZ->blue_Z, PNG_FP_1, Y)) return 1; + } + + return 0; +} + +static int +png_colorspace_endpoints_match(const png_xy *xy1, const png_xy *xy2, int delta) +{ + /* Allow an error of +/-0.01 (absolute value) on each chromaticity */ + return !(PNG_OUT_OF_RANGE(xy1->whitex, xy2->whitex,delta) || + PNG_OUT_OF_RANGE(xy1->whitey, xy2->whitey,delta) || + PNG_OUT_OF_RANGE(xy1->redx, xy2->redx, delta) || + PNG_OUT_OF_RANGE(xy1->redy, xy2->redy, delta) || + PNG_OUT_OF_RANGE(xy1->greenx, xy2->greenx,delta) || + PNG_OUT_OF_RANGE(xy1->greeny, xy2->greeny,delta) || + PNG_OUT_OF_RANGE(xy1->bluex, xy2->bluex, delta) || + PNG_OUT_OF_RANGE(xy1->bluey, xy2->bluey, delta)); +} + +/* Added in libpng-1.6.0, a different check for the validity of a set of cHRM + * chunk chromaticities. Earlier checks used to simply look for the overflow + * condition (where the determinant of the matrix to solve for XYZ ends up zero + * because the chromaticity values are not all distinct.) Despite this it is + * theoretically possible to produce chromaticities that are apparently valid + * but that rapidly degrade to invalid, potentially crashing, sets because of + * arithmetic inaccuracies when calculations are performed on them. The new + * check is to round-trip xy -> XYZ -> xy and then check that the result is + * within a small percentage of the original. + */ +static int +png_colorspace_check_xy(png_XYZ *XYZ, const png_xy *xy) +{ + int result; + png_xy xy_test; + + /* As a side-effect this routine also returns the XYZ endpoints. */ + result = png_XYZ_from_xy(XYZ, xy); + if (result) return result; + + result = png_xy_from_XYZ(&xy_test, XYZ); + if (result) return result; + + if (png_colorspace_endpoints_match(xy, &xy_test, + 5/*actually, the math is pretty accurate*/)) + return 0; + + /* Too much slip */ + return 1; +} + +/* This is the check going the other way. The XYZ is modified to normalize it + * (another side-effect) and the xy chromaticities are returned. + */ +static int +png_colorspace_check_XYZ(png_xy *xy, png_XYZ *XYZ) +{ + int result; + png_XYZ XYZtemp; + + result = png_XYZ_normalize(XYZ); + if (result) return result; + + result = png_xy_from_XYZ(xy, XYZ); + if (result) return result; + + XYZtemp = *XYZ; + return png_colorspace_check_xy(&XYZtemp, xy); +} + +/* Used to check for an endpoint match against sRGB */ +static const png_xy sRGB_xy = /* From ITU-R BT.709-3 */ +{ + /* color x y */ + /* red */ 64000, 33000, + /* green */ 30000, 60000, + /* blue */ 15000, 6000, + /* white */ 31270, 32900 +}; + +static int +png_colorspace_set_xy_and_XYZ(png_const_structrp png_ptr, + png_colorspacerp colorspace, const png_xy *xy, const png_XYZ *XYZ, + int preferred) +{ + if (colorspace->flags & PNG_COLORSPACE_INVALID) + return 0; + + /* The consistency check is performed on the chromaticities; this factors out + * variations because of the normalization (or not) of the end point Y + * values. + */ + if (preferred < 2 && (colorspace->flags & PNG_COLORSPACE_HAVE_ENDPOINTS)) + { + /* The end points must be reasonably close to any we already have. The + * following allows an error of up to +/-.001 + */ + if (!png_colorspace_endpoints_match(xy, &colorspace->end_points_xy, 100)) + { + colorspace->flags |= PNG_COLORSPACE_INVALID; + png_benign_error(png_ptr, "inconsistent chromaticities"); + return 0; /* failed */ + } + + /* Only overwrite with preferred values */ + if (!preferred) + return 1; /* ok, but no change */ + } + + colorspace->end_points_xy = *xy; + colorspace->end_points_XYZ = *XYZ; + colorspace->flags |= PNG_COLORSPACE_HAVE_ENDPOINTS; + + /* The end points are normally quoted to two decimal digits, so allow +/-0.01 + * on this test. + */ + if (png_colorspace_endpoints_match(xy, &sRGB_xy, 1000)) + colorspace->flags |= PNG_COLORSPACE_ENDPOINTS_MATCH_sRGB; + + else + colorspace->flags &= PNG_COLORSPACE_CANCEL( + PNG_COLORSPACE_ENDPOINTS_MATCH_sRGB); + + return 2; /* ok and changed */ +} + +int /* PRIVATE */ +png_colorspace_set_chromaticities(png_const_structrp png_ptr, + png_colorspacerp colorspace, const png_xy *xy, int preferred) +{ + /* We must check the end points to ensure they are reasonable - in the past + * color management systems have crashed as a result of getting bogus + * colorant values, while this isn't the fault of libpng it is the + * responsibility of libpng because PNG carries the bomb and libpng is in a + * position to protect against it. + */ + png_XYZ XYZ; + + switch (png_colorspace_check_xy(&XYZ, xy)) + { + case 0: /* success */ + return png_colorspace_set_xy_and_XYZ(png_ptr, colorspace, xy, &XYZ, + preferred); + + case 1: + /* We can't invert the chromaticities so we can't produce value XYZ + * values. Likely as not a color management system will fail too. + */ + colorspace->flags |= PNG_COLORSPACE_INVALID; + png_benign_error(png_ptr, "invalid chromaticities"); + break; + + default: + /* libpng is broken; this should be a warning but if it happens we + * want error reports so for the moment it is an error. + */ + colorspace->flags |= PNG_COLORSPACE_INVALID; + png_error(png_ptr, "internal error checking chromaticities"); + break; + } + + return 0; /* failed */ +} + +int /* PRIVATE */ +png_colorspace_set_endpoints(png_const_structrp png_ptr, + png_colorspacerp colorspace, const png_XYZ *XYZ_in, int preferred) +{ + png_XYZ XYZ = *XYZ_in; + png_xy xy; + + switch (png_colorspace_check_XYZ(&xy, &XYZ)) + { + case 0: + return png_colorspace_set_xy_and_XYZ(png_ptr, colorspace, &xy, &XYZ, + preferred); + + case 1: + /* End points are invalid. */ + colorspace->flags |= PNG_COLORSPACE_INVALID; + png_benign_error(png_ptr, "invalid end points"); + break; + + default: + colorspace->flags |= PNG_COLORSPACE_INVALID; + png_error(png_ptr, "internal error checking chromaticities"); + break; + } + + return 0; /* failed */ +} + +#if defined(PNG_sRGB_SUPPORTED) || defined(PNG_iCCP_SUPPORTED) +/* Error message generation */ +static char +png_icc_tag_char(png_uint_32 byte) +{ + byte &= 0xff; + if (byte >= 32 && byte <= 126) + return (char)byte; + else + return '?'; +} + +static void +png_icc_tag_name(char *name, png_uint_32 tag) +{ + name[0] = '\''; + name[1] = png_icc_tag_char(tag >> 24); + name[2] = png_icc_tag_char(tag >> 16); + name[3] = png_icc_tag_char(tag >> 8); + name[4] = png_icc_tag_char(tag ); + name[5] = '\''; +} + +static int +is_ICC_signature_char(png_alloc_size_t it) +{ + return it == 32 || (it >= 48 && it <= 57) || (it >= 65 && it <= 90) || + (it >= 97 && it <= 122); +} + +static int is_ICC_signature(png_alloc_size_t it) +{ + return is_ICC_signature_char(it >> 24) /* checks all the top bits */ && + is_ICC_signature_char((it >> 16) & 0xff) && + is_ICC_signature_char((it >> 8) & 0xff) && + is_ICC_signature_char(it & 0xff); +} + +static int +png_icc_profile_error(png_const_structrp png_ptr, png_colorspacerp colorspace, + png_const_charp name, png_alloc_size_t value, png_const_charp reason) +{ + size_t pos; + char message[196]; /* see below for calculation */ + + if (colorspace != NULL) + colorspace->flags |= PNG_COLORSPACE_INVALID; + + pos = png_safecat(message, (sizeof message), 0, "profile '"); /* 9 chars */ + pos = png_safecat(message, pos+79, pos, name); /* Truncate to 79 chars */ + pos = png_safecat(message, (sizeof message), pos, "': "); /* +2 = 90 */ + if (is_ICC_signature(value)) + { + /* So 'value' is at most 4 bytes and the following cast is safe */ + png_icc_tag_name(message+pos, (png_uint_32)value); + pos += 6; /* total +8; less than the else clause */ + message[pos++] = ':'; + message[pos++] = ' '; + } +# ifdef PNG_WARNINGS_SUPPORTED + else + { + char number[PNG_NUMBER_BUFFER_SIZE]; /* +24 = 114*/ + + pos = png_safecat(message, (sizeof message), pos, + png_format_number(number, number+(sizeof number), + PNG_NUMBER_FORMAT_x, value)); + pos = png_safecat(message, (sizeof message), pos, "h: "); /*+2 = 116*/ + } +# endif + /* The 'reason' is an arbitrary message, allow +79 maximum 195 */ + pos = png_safecat(message, (sizeof message), pos, reason); + + /* This is recoverable, but make it unconditionally an app_error on write to + * avoid writing invalid ICC profiles into PNG files. (I.e. we handle them + * on read, with a warning, but on write unless the app turns off + * application errors the PNG won't be written.) + */ + png_chunk_report(png_ptr, message, + (colorspace != NULL) ? PNG_CHUNK_ERROR : PNG_CHUNK_WRITE_ERROR); + + return 0; +} +#endif /* sRGB || iCCP */ + +#ifdef PNG_sRGB_SUPPORTED +int /* PRIVATE */ +png_colorspace_set_sRGB(png_const_structrp png_ptr, png_colorspacerp colorspace, + int intent) +{ + /* sRGB sets known gamma, end points and (from the chunk) intent. */ + /* IMPORTANT: these are not necessarily the values found in an ICC profile + * because ICC profiles store values adapted to a D50 environment; it is + * expected that the ICC profile mediaWhitePointTag will be D50, see the + * checks and code elsewhere to understand this better. + * + * These XYZ values, which are accurate to 5dp, produce rgb to gray + * coefficients of (6968,23435,2366), which are reduced (because they add up + * to 32769 not 32768) to (6968,23434,2366). These are the values that + * libpng has traditionally used (and are the best values given the 15bit + * algorithm used by the rgb to gray code.) + */ + static const png_XYZ sRGB_XYZ = /* D65 XYZ (*not* the D50 adapted values!) */ + { + /* color X Y Z */ + /* red */ 41239, 21264, 1933, + /* green */ 35758, 71517, 11919, + /* blue */ 18048, 7219, 95053 + }; + + /* Do nothing if the colorspace is already invalidated. */ + if (colorspace->flags & PNG_COLORSPACE_INVALID) + return 0; + + /* Check the intent, then check for existing settings. It is valid for the + * PNG file to have cHRM or gAMA chunks along with sRGB, but the values must + * be consistent with the correct values. If, however, this function is + * called below because an iCCP chunk matches sRGB then it is quite + * conceivable that an older app recorded incorrect gAMA and cHRM because of + * an incorrect calculation based on the values in the profile - this does + * *not* invalidate the profile (though it still produces an error, which can + * be ignored.) + */ + if (intent < 0 || intent >= PNG_sRGB_INTENT_LAST) + return png_icc_profile_error(png_ptr, colorspace, "sRGB", + (unsigned)intent, "invalid sRGB rendering intent"); + + if ((colorspace->flags & PNG_COLORSPACE_HAVE_INTENT) != 0 && + colorspace->rendering_intent != intent) + return png_icc_profile_error(png_ptr, colorspace, "sRGB", + (unsigned)intent, "inconsistent rendering intents"); + + if ((colorspace->flags & PNG_COLORSPACE_FROM_sRGB) != 0) + { + png_benign_error(png_ptr, "duplicate sRGB information ignored"); + return 0; + } + + /* If the standard sRGB cHRM chunk does not match the one from the PNG file + * warn but overwrite the value with the correct one. + */ + if ((colorspace->flags & PNG_COLORSPACE_HAVE_ENDPOINTS) != 0 && + !png_colorspace_endpoints_match(&sRGB_xy, &colorspace->end_points_xy, + 100)) + png_chunk_report(png_ptr, "cHRM chunk does not match sRGB", + PNG_CHUNK_ERROR); + + /* This check is just done for the error reporting - the routine always + * returns true when the 'from' argument corresponds to sRGB (2). + */ + (void)png_colorspace_check_gamma(png_ptr, colorspace, PNG_GAMMA_sRGB_INVERSE, + 2/*from sRGB*/); + + /* intent: bugs in GCC force 'int' to be used as the parameter type. */ + colorspace->rendering_intent = (png_uint_16)intent; + colorspace->flags |= PNG_COLORSPACE_HAVE_INTENT; + + /* endpoints */ + colorspace->end_points_xy = sRGB_xy; + colorspace->end_points_XYZ = sRGB_XYZ; + colorspace->flags |= + (PNG_COLORSPACE_HAVE_ENDPOINTS|PNG_COLORSPACE_ENDPOINTS_MATCH_sRGB); + + /* gamma */ + colorspace->gamma = PNG_GAMMA_sRGB_INVERSE; + colorspace->flags |= PNG_COLORSPACE_HAVE_GAMMA; + + /* Finally record that we have an sRGB profile */ + colorspace->flags |= + (PNG_COLORSPACE_MATCHES_sRGB|PNG_COLORSPACE_FROM_sRGB); + + return 1; /* set */ +} +#endif /* sRGB */ + +#ifdef PNG_iCCP_SUPPORTED +/* Encoded value of D50 as an ICC XYZNumber. From the ICC 2010 spec the value + * is XYZ(0.9642,1.0,0.8249), which scales to: + * + * (63189.8112, 65536, 54060.6464) + */ +static const png_byte D50_nCIEXYZ[12] = + { 0x00, 0x00, 0xf6, 0xd6, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0xd3, 0x2d }; + +int /* PRIVATE */ +png_icc_check_length(png_const_structrp png_ptr, png_colorspacerp colorspace, + png_const_charp name, png_uint_32 profile_length) +{ + if (profile_length < 132) + return png_icc_profile_error(png_ptr, colorspace, name, profile_length, + "too short"); + + if (profile_length & 3) + return png_icc_profile_error(png_ptr, colorspace, name, profile_length, + "invalid length"); + + return 1; +} + +int /* PRIVATE */ +png_icc_check_header(png_const_structrp png_ptr, png_colorspacerp colorspace, + png_const_charp name, png_uint_32 profile_length, + png_const_bytep profile/* first 132 bytes only */, int color_type) +{ + png_uint_32 temp; + + /* Length check; this cannot be ignored in this code because profile_length + * is used later to check the tag table, so even if the profile seems over + * long profile_length from the caller must be correct. The caller can fix + * this up on read or write by just passing in the profile header length. + */ + temp = png_get_uint_32(profile); + if (temp != profile_length) + return png_icc_profile_error(png_ptr, colorspace, name, temp, + "length does not match profile"); + + temp = png_get_uint_32(profile+128); /* tag count: 12 bytes/tag */ + if (temp > 357913930 || /* (2^32-4-132)/12: maximum possible tag count */ + profile_length < 132+12*temp) /* truncated tag table */ + return png_icc_profile_error(png_ptr, colorspace, name, temp, + "tag count too large"); + + /* The 'intent' must be valid or we can't store it, ICC limits the intent to + * 16 bits. + */ + temp = png_get_uint_32(profile+64); + if (temp >= 0xffff) /* The ICC limit */ + return png_icc_profile_error(png_ptr, colorspace, name, temp, + "invalid rendering intent"); + + /* This is just a warning because the profile may be valid in future + * versions. + */ + if (temp >= PNG_sRGB_INTENT_LAST) + (void)png_icc_profile_error(png_ptr, NULL, name, temp, + "intent outside defined range"); + + /* At this point the tag table can't be checked because it hasn't necessarily + * been loaded; however, various header fields can be checked. These checks + * are for values permitted by the PNG spec in an ICC profile; the PNG spec + * restricts the profiles that can be passed in an iCCP chunk (they must be + * appropriate to processing PNG data!) + */ + + /* Data checks (could be skipped). These checks must be independent of the + * version number; however, the version number doesn't accomodate changes in + * the header fields (just the known tags and the interpretation of the + * data.) + */ + temp = png_get_uint_32(profile+36); /* signature 'ascp' */ + if (temp != 0x61637370) + return png_icc_profile_error(png_ptr, colorspace, name, temp, + "invalid signature"); + + /* Currently the PCS illuminant/adopted white point (the computational + * white point) are required to be D50, + * however the profile contains a record of the illuminant so perhaps ICC + * expects to be able to change this in the future (despite the rationale in + * the introduction for using a fixed PCS adopted white.) Consequently the + * following is just a warning. + */ + if (memcmp(profile+68, D50_nCIEXYZ, 12) != 0) + (void)png_icc_profile_error(png_ptr, NULL, name, 0/*no tag value*/, + "PCS illuminant is not D50"); + + /* The PNG spec requires this: + * "If the iCCP chunk is present, the image samples conform to the colour + * space represented by the embedded ICC profile as defined by the + * International Color Consortium [ICC]. The colour space of the ICC profile + * shall be an RGB colour space for colour images (PNG colour types 2, 3, and + * 6), or a greyscale colour space for greyscale images (PNG colour types 0 + * and 4)." + * + * This checking code ensures the embedded profile (on either read or write) + * conforms to the specification requirements. Notice that an ICC 'gray' + * color-space profile contains the information to transform the monochrome + * data to XYZ or L*a*b (according to which PCS the profile uses) and this + * should be used in preference to the standard libpng K channel replication + * into R, G and B channels. + * + * Previously it was suggested that an RGB profile on grayscale data could be + * handled. However it it is clear that using an RGB profile in this context + * must be an error - there is no specification of what it means. Thus it is + * almost certainly more correct to ignore the profile. + */ + temp = png_get_uint_32(profile+16); /* data colour space field */ + switch (temp) + { + case 0x52474220: /* 'RGB ' */ + if (!(color_type & PNG_COLOR_MASK_COLOR)) + return png_icc_profile_error(png_ptr, colorspace, name, temp, + "RGB color space not permitted on grayscale PNG"); + break; + + case 0x47524159: /* 'GRAY' */ + if (color_type & PNG_COLOR_MASK_COLOR) + return png_icc_profile_error(png_ptr, colorspace, name, temp, + "Gray color space not permitted on RGB PNG"); + break; + + default: + return png_icc_profile_error(png_ptr, colorspace, name, temp, + "invalid ICC profile color space"); + } + + /* It is up to the application to check that the profile class matches the + * application requirements; the spec provides no guidance, but it's pretty + * weird if the profile is not scanner ('scnr'), monitor ('mntr'), printer + * ('prtr') or 'spac' (for generic color spaces). Issue a warning in these + * cases. Issue an error for device link or abstract profiles - these don't + * contain the records necessary to transform the color-space to anything + * other than the target device (and not even that for an abstract profile). + * Profiles of these classes may not be embedded in images. + */ + temp = png_get_uint_32(profile+12); /* profile/device class */ + switch (temp) + { + case 0x73636E72: /* 'scnr' */ + case 0x6D6E7472: /* 'mntr' */ + case 0x70727472: /* 'prtr' */ + case 0x73706163: /* 'spac' */ + /* All supported */ + break; + + case 0x61627374: /* 'abst' */ + /* May not be embedded in an image */ + return png_icc_profile_error(png_ptr, colorspace, name, temp, + "invalid embedded Abstract ICC profile"); + + case 0x6C696E6B: /* 'link' */ + /* DeviceLink profiles cannnot be interpreted in a non-device specific + * fashion, if an app uses the AToB0Tag in the profile the results are + * undefined unless the result is sent to the intended device, + * therefore a DeviceLink profile should not be found embedded in a + * PNG. + */ + return png_icc_profile_error(png_ptr, colorspace, name, temp, + "unexpected DeviceLink ICC profile class"); + + case 0x6E6D636C: /* 'nmcl' */ + /* A NamedColor profile is also device specific, however it doesn't + * contain an AToB0 tag that is open to misintrepretation. Almost + * certainly it will fail the tests below. + */ + (void)png_icc_profile_error(png_ptr, NULL, name, temp, + "unexpected NamedColor ICC profile class"); + break; + + default: + /* To allow for future enhancements to the profile accept unrecognized + * profile classes with a warning, these then hit the test below on the + * tag content to ensure they are backward compatible with one of the + * understood profiles. + */ + (void)png_icc_profile_error(png_ptr, NULL, name, temp, + "unrecognized ICC profile class"); + break; + } + + /* For any profile other than a device link one the PCS must be encoded + * either in XYZ or Lab. + */ + temp = png_get_uint_32(profile+20); + switch (temp) + { + case 0x58595A20: /* 'XYZ ' */ + case 0x4C616220: /* 'Lab ' */ + break; + + default: + return png_icc_profile_error(png_ptr, colorspace, name, temp, + "unexpected ICC PCS encoding"); + } + + return 1; +} + +int /* PRIVATE */ +png_icc_check_tag_table(png_const_structrp png_ptr, png_colorspacerp colorspace, + png_const_charp name, png_uint_32 profile_length, + png_const_bytep profile /* header plus whole tag table */) +{ + png_uint_32 tag_count = png_get_uint_32(profile+128); + png_uint_32 itag; + png_const_bytep tag = profile+132; /* The first tag */ + + /* First scan all the tags in the table and add bits to the icc_info value + * (temporarily in 'tags'). + */ + for (itag=0; itag < tag_count; ++itag, tag += 12) + { + png_uint_32 tag_id = png_get_uint_32(tag+0); + png_uint_32 tag_start = png_get_uint_32(tag+4); /* must be aligned */ + png_uint_32 tag_length = png_get_uint_32(tag+8);/* not padded */ + + /* The ICC specification does not exclude zero length tags, therefore the + * start might actually be anywhere if there is no data, but this would be + * a clear abuse of the intent of the standard so the start is checked for + * being in range. All defined tag types have an 8 byte header - a 4 byte + * type signature then 0. + */ + if ((tag_start & 3) != 0) + { + /* CNHP730S.icc shipped with Microsoft Windows 64 violates this, it is + * only a warning here because libpng does not care about the + * alignment. + */ + (void)png_icc_profile_error(png_ptr, NULL, name, tag_id, + "ICC profile tag start not a multiple of 4"); + } + + /* This is a hard error; potentially it can cause read outside the + * profile. + */ + if (tag_start > profile_length || tag_length > profile_length - tag_start) + return png_icc_profile_error(png_ptr, colorspace, name, tag_id, + "ICC profile tag outside profile"); + } + + return 1; /* success, maybe with warnings */ +} + +#ifdef PNG_sRGB_SUPPORTED +/* Information about the known ICC sRGB profiles */ +static const struct +{ + png_uint_32 adler, crc, length; + png_uint_32 md5[4]; + png_byte have_md5; + png_byte is_broken; + png_uint_16 intent; + +# define PNG_MD5(a,b,c,d) { a, b, c, d }, (a!=0)||(b!=0)||(c!=0)||(d!=0) +# define PNG_ICC_CHECKSUM(adler, crc, md5, intent, broke, date, length, fname)\ + { adler, crc, length, md5, broke, intent }, + +} png_sRGB_checks[] = +{ + /* This data comes from contrib/tools/checksum-icc run on downloads of + * all four ICC sRGB profiles from www.color.org. + */ + /* adler32, crc32, MD5[4], intent, date, length, file-name */ + PNG_ICC_CHECKSUM(0x0a3fd9f6, 0x3b8772b9, + PNG_MD5(0x29f83dde, 0xaff255ae, 0x7842fae4, 0xca83390d), 0, 0, + "2009/03/27 21:36:31", 3048, "sRGB_IEC61966-2-1_black_scaled.icc") + + /* ICC sRGB v2 perceptual no black-compensation: */ + PNG_ICC_CHECKSUM(0x4909e5e1, 0x427ebb21, + PNG_MD5(0xc95bd637, 0xe95d8a3b, 0x0df38f99, 0xc1320389), 1, 0, + "2009/03/27 21:37:45", 3052, "sRGB_IEC61966-2-1_no_black_scaling.icc") + + PNG_ICC_CHECKSUM(0xfd2144a1, 0x306fd8ae, + PNG_MD5(0xfc663378, 0x37e2886b, 0xfd72e983, 0x8228f1b8), 0, 0, + "2009/08/10 17:28:01", 60988, "sRGB_v4_ICC_preference_displayclass.icc") + + /* ICC sRGB v4 perceptual */ + PNG_ICC_CHECKSUM(0x209c35d2, 0xbbef7812, + PNG_MD5(0x34562abf, 0x994ccd06, 0x6d2c5721, 0xd0d68c5d), 0, 0, + "2007/07/25 00:05:37", 60960, "sRGB_v4_ICC_preference.icc") + + /* The following profiles have no known MD5 checksum. If there is a match + * on the (empty) MD5 the other fields are used to attempt a match and + * a warning is produced. The first two of these profiles have a 'cprt' tag + * which suggests that they were also made by Hewlett Packard. + */ + PNG_ICC_CHECKSUM(0xa054d762, 0x5d5129ce, + PNG_MD5(0x00000000, 0x00000000, 0x00000000, 0x00000000), 1, 0, + "2004/07/21 18:57:42", 3024, "sRGB_IEC61966-2-1_noBPC.icc") + + /* This is a 'mntr' (display) profile with a mediaWhitePointTag that does not + * match the D50 PCS illuminant in the header (it is in fact the D65 values, + * so the white point is recorded as the un-adapted value.) The profiles + * below only differ in one byte - the intent - and are basically the same as + * the previous profile except for the mediaWhitePointTag error and a missing + * chromaticAdaptationTag. + */ + PNG_ICC_CHECKSUM(0xf784f3fb, 0x182ea552, + PNG_MD5(0x00000000, 0x00000000, 0x00000000, 0x00000000), 0, 1/*broken*/, + "1998/02/09 06:49:00", 3144, "HP-Microsoft sRGB v2 perceptual") + + PNG_ICC_CHECKSUM(0x0398f3fc, 0xf29e526d, + PNG_MD5(0x00000000, 0x00000000, 0x00000000, 0x00000000), 1, 1/*broken*/, + "1998/02/09 06:49:00", 3144, "HP-Microsoft sRGB v2 media-relative") +}; + +static int +png_compare_ICC_profile_with_sRGB(png_const_structrp png_ptr, + png_const_bytep profile, uLong adler) +{ + /* The quick check is to verify just the MD5 signature and trust the + * rest of the data. Because the profile has already been verified for + * correctness this is safe. png_colorspace_set_sRGB will check the 'intent' + * field too, so if the profile has been edited with an intent not defined + * by sRGB (but maybe defined by a later ICC specification) the read of + * the profile will fail at that point. + */ + png_uint_32 length = 0; + png_uint_32 intent = 0x10000; /* invalid */ +#if PNG_sRGB_PROFILE_CHECKS > 1 + uLong crc = 0; /* the value for 0 length data */ +#endif + unsigned int i; + + for (i=0; i < (sizeof png_sRGB_checks) / (sizeof png_sRGB_checks[0]); ++i) + { + if (png_get_uint_32(profile+84) == png_sRGB_checks[i].md5[0] && + png_get_uint_32(profile+88) == png_sRGB_checks[i].md5[1] && + png_get_uint_32(profile+92) == png_sRGB_checks[i].md5[2] && + png_get_uint_32(profile+96) == png_sRGB_checks[i].md5[3]) + { + /* This may be one of the old HP profiles without an MD5, in that + * case we can only use the length and Adler32 (note that these + * are not used by default if there is an MD5!) + */ +# if PNG_sRGB_PROFILE_CHECKS == 0 + if (png_sRGB_checks[i].have_md5) + return 1+png_sRGB_checks[i].is_broken; +# endif + + /* Profile is unsigned or more checks have been configured in. */ + if (length == 0) + { + length = png_get_uint_32(profile); + intent = png_get_uint_32(profile+64); + } + + /* Length *and* intent must match */ + if (length == png_sRGB_checks[i].length && + intent == png_sRGB_checks[i].intent) + { + /* Now calculate the adler32 if not done already. */ + if (adler == 0) + { + adler = adler32(0, NULL, 0); + adler = adler32(adler, profile, length); + } + + if (adler == png_sRGB_checks[i].adler) + { + /* These basic checks suggest that the data has not been + * modified, but if the check level is more than 1 perform + * our own crc32 checksum on the data. + */ +# if PNG_sRGB_PROFILE_CHECKS > 1 + if (crc == 0) + { + crc = crc32(0, NULL, 0); + crc = crc32(crc, profile, length); + } + + /* So this check must pass for the 'return' below to happen. + */ + if (crc == png_sRGB_checks[i].crc) +# endif + { + if (png_sRGB_checks[i].is_broken) + { + /* These profiles are known to have bad data that may cause + * problems if they are used, therefore attempt to + * discourage their use, skip the 'have_md5' warning below, + * which is made irrelevant by this error. + */ + png_chunk_report(png_ptr, "known incorrect sRGB profile", + PNG_CHUNK_ERROR); + } + + /* Warn that this being done; this isn't even an error since + * the profile is perfectly valid, but it would be nice if + * people used the up-to-date ones. + */ + else if (!png_sRGB_checks[i].have_md5) + { + png_chunk_report(png_ptr, + "out-of-date sRGB profile with no signature", + PNG_CHUNK_WARNING); + } + + return 1+png_sRGB_checks[i].is_broken; + } + } + } + +# if PNG_sRGB_PROFILE_CHECKS > 0 + /* The signature matched, but the profile had been changed in some + * way. This is an apparent violation of the ICC terms of use and, + * anyway, probably indicates a data error or uninformed hacking. + */ + if (png_sRGB_checks[i].have_md5) + png_benign_error(png_ptr, + "copyright violation: edited ICC profile ignored"); +# endif + } + } + + return 0; /* no match */ +} +#endif + +#ifdef PNG_sRGB_SUPPORTED +void /* PRIVATE */ +png_icc_set_sRGB(png_const_structrp png_ptr, + png_colorspacerp colorspace, png_const_bytep profile, uLong adler) +{ + /* Is this profile one of the known ICC sRGB profiles? If it is, just set + * the sRGB information. + */ + if (png_compare_ICC_profile_with_sRGB(png_ptr, profile, adler)) + (void)png_colorspace_set_sRGB(png_ptr, colorspace, + (int)/*already checked*/png_get_uint_32(profile+64)); +} +#endif /* PNG_READ_sRGB_SUPPORTED */ + +int /* PRIVATE */ +png_colorspace_set_ICC(png_const_structrp png_ptr, png_colorspacerp colorspace, + png_const_charp name, png_uint_32 profile_length, png_const_bytep profile, + int color_type) +{ + if (colorspace->flags & PNG_COLORSPACE_INVALID) + return 0; + + if (png_icc_check_length(png_ptr, colorspace, name, profile_length) && + png_icc_check_header(png_ptr, colorspace, name, profile_length, profile, + color_type) && + png_icc_check_tag_table(png_ptr, colorspace, name, profile_length, + profile)) + { +# ifdef PNG_sRGB_SUPPORTED + /* If no sRGB support, don't try storing sRGB information */ + png_icc_set_sRGB(png_ptr, colorspace, profile, 0); +# endif + return 1; + } + + /* Failure case */ + return 0; +} +#endif /* iCCP */ + +#ifdef PNG_READ_RGB_TO_GRAY_SUPPORTED +void /* PRIVATE */ +png_colorspace_set_rgb_coefficients(png_structrp png_ptr) +{ + /* Set the rgb_to_gray coefficients from the colorspace. */ + if (!png_ptr->rgb_to_gray_coefficients_set && + (png_ptr->colorspace.flags & PNG_COLORSPACE_HAVE_ENDPOINTS) != 0) + { + /* png_set_background has not been called, get the coefficients from the Y + * values of the colorspace colorants. + */ + png_fixed_point r = png_ptr->colorspace.end_points_XYZ.red_Y; + png_fixed_point g = png_ptr->colorspace.end_points_XYZ.green_Y; + png_fixed_point b = png_ptr->colorspace.end_points_XYZ.blue_Y; + png_fixed_point total = r+g+b; + + if (total > 0 && + r >= 0 && png_muldiv(&r, r, 32768, total) && r >= 0 && r <= 32768 && + g >= 0 && png_muldiv(&g, g, 32768, total) && g >= 0 && g <= 32768 && + b >= 0 && png_muldiv(&b, b, 32768, total) && b >= 0 && b <= 32768 && + r+g+b <= 32769) + { + /* We allow 0 coefficients here. r+g+b may be 32769 if two or + * all of the coefficients were rounded up. Handle this by + * reducing the *largest* coefficient by 1; this matches the + * approach used for the default coefficients in pngrtran.c + */ + int add = 0; + + if (r+g+b > 32768) + add = -1; + else if (r+g+b < 32768) + add = 1; + + if (add != 0) + { + if (g >= r && g >= b) + g += add; + else if (r >= g && r >= b) + r += add; + else + b += add; + } + + /* Check for an internal error. */ + if (r+g+b != 32768) + png_error(png_ptr, + "internal error handling cHRM coefficients"); + + else + { + png_ptr->rgb_to_gray_red_coeff = (png_uint_16)r; + png_ptr->rgb_to_gray_green_coeff = (png_uint_16)g; + } + } + + /* This is a png_error at present even though it could be ignored - + * it should never happen, but it is important that if it does, the + * bug is fixed. + */ + else + png_error(png_ptr, "internal error handling cHRM->XYZ"); + } +} +#endif + +#endif /* COLORSPACE */ + +void /* PRIVATE */ +png_check_IHDR(png_const_structrp png_ptr, + png_uint_32 width, png_uint_32 height, int bit_depth, + int color_type, int interlace_type, int compression_type, + int filter_type) +{ + int error = 0; + + /* Check for width and height valid values */ + if (width == 0) + { + png_warning(png_ptr, "Image width is zero in IHDR"); + error = 1; + } + + if (height == 0) + { + png_warning(png_ptr, "Image height is zero in IHDR"); + error = 1; + } + +# ifdef PNG_SET_USER_LIMITS_SUPPORTED + if (width > png_ptr->user_width_max) + +# else + if (width > PNG_USER_WIDTH_MAX) +# endif + { + png_warning(png_ptr, "Image width exceeds user limit in IHDR"); + error = 1; + } + +# ifdef PNG_SET_USER_LIMITS_SUPPORTED + if (height > png_ptr->user_height_max) +# else + if (height > PNG_USER_HEIGHT_MAX) +# endif + { + png_warning(png_ptr, "Image height exceeds user limit in IHDR"); + error = 1; + } + + if (width > PNG_UINT_31_MAX) + { + png_warning(png_ptr, "Invalid image width in IHDR"); + error = 1; + } + + if (height > PNG_UINT_31_MAX) + { + png_warning(png_ptr, "Invalid image height in IHDR"); + error = 1; + } + + if (width > (PNG_UINT_32_MAX + >> 3) /* 8-byte RGBA pixels */ + - 48 /* bigrowbuf hack */ + - 1 /* filter byte */ + - 7*8 /* rounding of width to multiple of 8 pixels */ + - 8) /* extra max_pixel_depth pad */ + png_warning(png_ptr, "Width is too large for libpng to process pixels"); + + /* Check other values */ + if (bit_depth != 1 && bit_depth != 2 && bit_depth != 4 && + bit_depth != 8 && bit_depth != 16) + { + png_warning(png_ptr, "Invalid bit depth in IHDR"); + error = 1; + } + + if (color_type < 0 || color_type == 1 || + color_type == 5 || color_type > 6) + { + png_warning(png_ptr, "Invalid color type in IHDR"); + error = 1; + } + + if (((color_type == PNG_COLOR_TYPE_PALETTE) && bit_depth > 8) || + ((color_type == PNG_COLOR_TYPE_RGB || + color_type == PNG_COLOR_TYPE_GRAY_ALPHA || + color_type == PNG_COLOR_TYPE_RGB_ALPHA) && bit_depth < 8)) + { + png_warning(png_ptr, "Invalid color type/bit depth combination in IHDR"); + error = 1; + } + + if (interlace_type >= PNG_INTERLACE_LAST) + { + png_warning(png_ptr, "Unknown interlace method in IHDR"); + error = 1; + } + + if (compression_type != PNG_COMPRESSION_TYPE_BASE) + { + png_warning(png_ptr, "Unknown compression method in IHDR"); + error = 1; + } + +# ifdef PNG_MNG_FEATURES_SUPPORTED + /* Accept filter_method 64 (intrapixel differencing) only if + * 1. Libpng was compiled with PNG_MNG_FEATURES_SUPPORTED and + * 2. Libpng did not read a PNG signature (this filter_method is only + * used in PNG datastreams that are embedded in MNG datastreams) and + * 3. The application called png_permit_mng_features with a mask that + * included PNG_FLAG_MNG_FILTER_64 and + * 4. The filter_method is 64 and + * 5. The color_type is RGB or RGBA + */ + if ((png_ptr->mode & PNG_HAVE_PNG_SIGNATURE) && + png_ptr->mng_features_permitted) + png_warning(png_ptr, "MNG features are not allowed in a PNG datastream"); + + if (filter_type != PNG_FILTER_TYPE_BASE) + { + if (!((png_ptr->mng_features_permitted & PNG_FLAG_MNG_FILTER_64) && + (filter_type == PNG_INTRAPIXEL_DIFFERENCING) && + ((png_ptr->mode & PNG_HAVE_PNG_SIGNATURE) == 0) && + (color_type == PNG_COLOR_TYPE_RGB || + color_type == PNG_COLOR_TYPE_RGB_ALPHA))) + { + png_warning(png_ptr, "Unknown filter method in IHDR"); + error = 1; + } + + if (png_ptr->mode & PNG_HAVE_PNG_SIGNATURE) + { + png_warning(png_ptr, "Invalid filter method in IHDR"); + error = 1; + } + } + +# else + if (filter_type != PNG_FILTER_TYPE_BASE) + { + png_warning(png_ptr, "Unknown filter method in IHDR"); + error = 1; + } +# endif + + if (error == 1) + png_error(png_ptr, "Invalid IHDR data"); +} + +#if defined(PNG_sCAL_SUPPORTED) || defined(PNG_pCAL_SUPPORTED) +/* ASCII to fp functions */ +/* Check an ASCII formated floating point value, see the more detailed + * comments in pngpriv.h + */ +/* The following is used internally to preserve the sticky flags */ +#define png_fp_add(state, flags) ((state) |= (flags)) +#define png_fp_set(state, value) ((state) = (value) | ((state) & PNG_FP_STICKY)) + +int /* PRIVATE */ +png_check_fp_number(png_const_charp string, png_size_t size, int *statep, + png_size_tp whereami) +{ + int state = *statep; + png_size_t i = *whereami; + + while (i < size) + { + int type; + /* First find the type of the next character */ + switch (string[i]) + { + case 43: type = PNG_FP_SAW_SIGN; break; + case 45: type = PNG_FP_SAW_SIGN + PNG_FP_NEGATIVE; break; + case 46: type = PNG_FP_SAW_DOT; break; + case 48: type = PNG_FP_SAW_DIGIT; break; + case 49: case 50: case 51: case 52: + case 53: case 54: case 55: case 56: + case 57: type = PNG_FP_SAW_DIGIT + PNG_FP_NONZERO; break; + case 69: + case 101: type = PNG_FP_SAW_E; break; + default: goto PNG_FP_End; + } + + /* Now deal with this type according to the current + * state, the type is arranged to not overlap the + * bits of the PNG_FP_STATE. + */ + switch ((state & PNG_FP_STATE) + (type & PNG_FP_SAW_ANY)) + { + case PNG_FP_INTEGER + PNG_FP_SAW_SIGN: + if (state & PNG_FP_SAW_ANY) + goto PNG_FP_End; /* not a part of the number */ + + png_fp_add(state, type); + break; + + case PNG_FP_INTEGER + PNG_FP_SAW_DOT: + /* Ok as trailer, ok as lead of fraction. */ + if (state & PNG_FP_SAW_DOT) /* two dots */ + goto PNG_FP_End; + + else if (state & PNG_FP_SAW_DIGIT) /* trailing dot? */ + png_fp_add(state, type); + + else + png_fp_set(state, PNG_FP_FRACTION | type); + + break; + + case PNG_FP_INTEGER + PNG_FP_SAW_DIGIT: + if (state & PNG_FP_SAW_DOT) /* delayed fraction */ + png_fp_set(state, PNG_FP_FRACTION | PNG_FP_SAW_DOT); + + png_fp_add(state, type | PNG_FP_WAS_VALID); + + break; + + case PNG_FP_INTEGER + PNG_FP_SAW_E: + if ((state & PNG_FP_SAW_DIGIT) == 0) + goto PNG_FP_End; + + png_fp_set(state, PNG_FP_EXPONENT); + + break; + + /* case PNG_FP_FRACTION + PNG_FP_SAW_SIGN: + goto PNG_FP_End; ** no sign in fraction */ + + /* case PNG_FP_FRACTION + PNG_FP_SAW_DOT: + goto PNG_FP_End; ** Because SAW_DOT is always set */ + + case PNG_FP_FRACTION + PNG_FP_SAW_DIGIT: + png_fp_add(state, type | PNG_FP_WAS_VALID); + break; + + case PNG_FP_FRACTION + PNG_FP_SAW_E: + /* This is correct because the trailing '.' on an + * integer is handled above - so we can only get here + * with the sequence ".E" (with no preceding digits). + */ + if ((state & PNG_FP_SAW_DIGIT) == 0) + goto PNG_FP_End; + + png_fp_set(state, PNG_FP_EXPONENT); + + break; + + case PNG_FP_EXPONENT + PNG_FP_SAW_SIGN: + if (state & PNG_FP_SAW_ANY) + goto PNG_FP_End; /* not a part of the number */ + + png_fp_add(state, PNG_FP_SAW_SIGN); + + break; + + /* case PNG_FP_EXPONENT + PNG_FP_SAW_DOT: + goto PNG_FP_End; */ + + case PNG_FP_EXPONENT + PNG_FP_SAW_DIGIT: + png_fp_add(state, PNG_FP_SAW_DIGIT | PNG_FP_WAS_VALID); + + break; + + /* case PNG_FP_EXPONEXT + PNG_FP_SAW_E: + goto PNG_FP_End; */ + + default: goto PNG_FP_End; /* I.e. break 2 */ + } + + /* The character seems ok, continue. */ + ++i; + } + +PNG_FP_End: + /* Here at the end, update the state and return the correct + * return code. + */ + *statep = state; + *whereami = i; + + return (state & PNG_FP_SAW_DIGIT) != 0; +} + + +/* The same but for a complete string. */ +int +png_check_fp_string(png_const_charp string, png_size_t size) +{ + int state=0; + png_size_t char_index=0; + + if (png_check_fp_number(string, size, &state, &char_index) && + (char_index == size || string[char_index] == 0)) + return state /* must be non-zero - see above */; + + return 0; /* i.e. fail */ +} +#endif /* pCAL or sCAL */ + +#ifdef PNG_sCAL_SUPPORTED +# ifdef PNG_FLOATING_POINT_SUPPORTED +/* Utility used below - a simple accurate power of ten from an integral + * exponent. + */ +static double +png_pow10(int power) +{ + int recip = 0; + double d = 1; + + /* Handle negative exponent with a reciprocal at the end because + * 10 is exact whereas .1 is inexact in base 2 + */ + if (power < 0) + { + if (power < DBL_MIN_10_EXP) return 0; + recip = 1, power = -power; + } + + if (power > 0) + { + /* Decompose power bitwise. */ + double mult = 10; + do + { + if (power & 1) d *= mult; + mult *= mult; + power >>= 1; + } + while (power > 0); + + if (recip) d = 1/d; + } + /* else power is 0 and d is 1 */ + + return d; +} + +/* Function to format a floating point value in ASCII with a given + * precision. + */ +void /* PRIVATE */ +png_ascii_from_fp(png_const_structrp png_ptr, png_charp ascii, png_size_t size, + double fp, unsigned int precision) +{ + /* We use standard functions from math.h, but not printf because + * that would require stdio. The caller must supply a buffer of + * sufficient size or we will png_error. The tests on size and + * the space in ascii[] consumed are indicated below. + */ + if (precision < 1) + precision = DBL_DIG; + + /* Enforce the limit of the implementation precision too. */ + if (precision > DBL_DIG+1) + precision = DBL_DIG+1; + + /* Basic sanity checks */ + if (size >= precision+5) /* See the requirements below. */ + { + if (fp < 0) + { + fp = -fp; + *ascii++ = 45; /* '-' PLUS 1 TOTAL 1 */ + --size; + } + + if (fp >= DBL_MIN && fp <= DBL_MAX) + { + int exp_b10; /* A base 10 exponent */ + double base; /* 10^exp_b10 */ + + /* First extract a base 10 exponent of the number, + * the calculation below rounds down when converting + * from base 2 to base 10 (multiply by log10(2) - + * 0.3010, but 77/256 is 0.3008, so exp_b10 needs to + * be increased. Note that the arithmetic shift + * performs a floor() unlike C arithmetic - using a + * C multiply would break the following for negative + * exponents. + */ + (void)frexp(fp, &exp_b10); /* exponent to base 2 */ + + exp_b10 = (exp_b10 * 77) >> 8; /* <= exponent to base 10 */ + + /* Avoid underflow here. */ + base = png_pow10(exp_b10); /* May underflow */ + + while (base < DBL_MIN || base < fp) + { + /* And this may overflow. */ + double test = png_pow10(exp_b10+1); + + if (test <= DBL_MAX) + ++exp_b10, base = test; + + else + break; + } + + /* Normalize fp and correct exp_b10, after this fp is in the + * range [.1,1) and exp_b10 is both the exponent and the digit + * *before* which the decimal point should be inserted + * (starting with 0 for the first digit). Note that this + * works even if 10^exp_b10 is out of range because of the + * test on DBL_MAX above. + */ + fp /= base; + while (fp >= 1) fp /= 10, ++exp_b10; + + /* Because of the code above fp may, at this point, be + * less than .1, this is ok because the code below can + * handle the leading zeros this generates, so no attempt + * is made to correct that here. + */ + + { + int czero, clead, cdigits; + char exponent[10]; + + /* Allow up to two leading zeros - this will not lengthen + * the number compared to using E-n. + */ + if (exp_b10 < 0 && exp_b10 > -3) /* PLUS 3 TOTAL 4 */ + { + czero = -exp_b10; /* PLUS 2 digits: TOTAL 3 */ + exp_b10 = 0; /* Dot added below before first output. */ + } + else + czero = 0; /* No zeros to add */ + + /* Generate the digit list, stripping trailing zeros and + * inserting a '.' before a digit if the exponent is 0. + */ + clead = czero; /* Count of leading zeros */ + cdigits = 0; /* Count of digits in list. */ + + do + { + double d; + + fp *= 10; + /* Use modf here, not floor and subtract, so that + * the separation is done in one step. At the end + * of the loop don't break the number into parts so + * that the final digit is rounded. + */ + if (cdigits+czero-clead+1 < (int)precision) + fp = modf(fp, &d); + + else + { + d = floor(fp + .5); + + if (d > 9) + { + /* Rounding up to 10, handle that here. */ + if (czero > 0) + { + --czero, d = 1; + if (cdigits == 0) --clead; + } + else + { + while (cdigits > 0 && d > 9) + { + int ch = *--ascii; + + if (exp_b10 != (-1)) + ++exp_b10; + + else if (ch == 46) + { + ch = *--ascii, ++size; + /* Advance exp_b10 to '1', so that the + * decimal point happens after the + * previous digit. + */ + exp_b10 = 1; + } + + --cdigits; + d = ch - 47; /* I.e. 1+(ch-48) */ + } + + /* Did we reach the beginning? If so adjust the + * exponent but take into account the leading + * decimal point. + */ + if (d > 9) /* cdigits == 0 */ + { + if (exp_b10 == (-1)) + { + /* Leading decimal point (plus zeros?), if + * we lose the decimal point here it must + * be reentered below. + */ + int ch = *--ascii; + + if (ch == 46) + ++size, exp_b10 = 1; + + /* Else lost a leading zero, so 'exp_b10' is + * still ok at (-1) + */ + } + else + ++exp_b10; + + /* In all cases we output a '1' */ + d = 1; + } + } + } + fp = 0; /* Guarantees termination below. */ + } + + if (d == 0) + { + ++czero; + if (cdigits == 0) ++clead; + } + else + { + /* Included embedded zeros in the digit count. */ + cdigits += czero - clead; + clead = 0; + + while (czero > 0) + { + /* exp_b10 == (-1) means we just output the decimal + * place - after the DP don't adjust 'exp_b10' any + * more! + */ + if (exp_b10 != (-1)) + { + if (exp_b10 == 0) *ascii++ = 46, --size; + /* PLUS 1: TOTAL 4 */ + --exp_b10; + } + *ascii++ = 48, --czero; + } + + if (exp_b10 != (-1)) + { + if (exp_b10 == 0) *ascii++ = 46, --size; /* counted + above */ + --exp_b10; + } + *ascii++ = (char)(48 + (int)d), ++cdigits; + } + } + while (cdigits+czero-clead < (int)precision && fp > DBL_MIN); + + /* The total output count (max) is now 4+precision */ + + /* Check for an exponent, if we don't need one we are + * done and just need to terminate the string. At + * this point exp_b10==(-1) is effectively if flag - it got + * to '-1' because of the decrement after outputing + * the decimal point above (the exponent required is + * *not* -1!) + */ + if (exp_b10 >= (-1) && exp_b10 <= 2) + { + /* The following only happens if we didn't output the + * leading zeros above for negative exponent, so this + * doest add to the digit requirement. Note that the + * two zeros here can only be output if the two leading + * zeros were *not* output, so this doesn't increase + * the output count. + */ + while (--exp_b10 >= 0) *ascii++ = 48; + + *ascii = 0; + + /* Total buffer requirement (including the '\0') is + * 5+precision - see check at the start. + */ + return; + } + + /* Here if an exponent is required, adjust size for + * the digits we output but did not count. The total + * digit output here so far is at most 1+precision - no + * decimal point and no leading or trailing zeros have + * been output. + */ + size -= cdigits; + + *ascii++ = 69, --size; /* 'E': PLUS 1 TOTAL 2+precision */ + + /* The following use of an unsigned temporary avoids ambiguities in + * the signed arithmetic on exp_b10 and permits GCC at least to do + * better optimization. + */ + { + unsigned int uexp_b10; + + if (exp_b10 < 0) + { + *ascii++ = 45, --size; /* '-': PLUS 1 TOTAL 3+precision */ + uexp_b10 = -exp_b10; + } + + else + uexp_b10 = exp_b10; + + cdigits = 0; + + while (uexp_b10 > 0) + { + exponent[cdigits++] = (char)(48 + uexp_b10 % 10); + uexp_b10 /= 10; + } + } + + /* Need another size check here for the exponent digits, so + * this need not be considered above. + */ + if ((int)size > cdigits) + { + while (cdigits > 0) *ascii++ = exponent[--cdigits]; + + *ascii = 0; + + return; + } + } + } + else if (!(fp >= DBL_MIN)) + { + *ascii++ = 48; /* '0' */ + *ascii = 0; + return; + } + else + { + *ascii++ = 105; /* 'i' */ + *ascii++ = 110; /* 'n' */ + *ascii++ = 102; /* 'f' */ + *ascii = 0; + return; + } + } + + /* Here on buffer too small. */ + png_error(png_ptr, "ASCII conversion buffer too small"); +} + +# endif /* FLOATING_POINT */ + +# ifdef PNG_FIXED_POINT_SUPPORTED +/* Function to format a fixed point value in ASCII. + */ +void /* PRIVATE */ +png_ascii_from_fixed(png_const_structrp png_ptr, png_charp ascii, + png_size_t size, png_fixed_point fp) +{ + /* Require space for 10 decimal digits, a decimal point, a minus sign and a + * trailing \0, 13 characters: + */ + if (size > 12) + { + png_uint_32 num; + + /* Avoid overflow here on the minimum integer. */ + if (fp < 0) + *ascii++ = 45, --size, num = -fp; + else + num = fp; + + if (num <= 0x80000000) /* else overflowed */ + { + unsigned int ndigits = 0, first = 16 /* flag value */; + char digits[10]; + + while (num) + { + /* Split the low digit off num: */ + unsigned int tmp = num/10; + num -= tmp*10; + digits[ndigits++] = (char)(48 + num); + /* Record the first non-zero digit, note that this is a number + * starting at 1, it's not actually the array index. + */ + if (first == 16 && num > 0) + first = ndigits; + num = tmp; + } + + if (ndigits > 0) + { + while (ndigits > 5) *ascii++ = digits[--ndigits]; + /* The remaining digits are fractional digits, ndigits is '5' or + * smaller at this point. It is certainly not zero. Check for a + * non-zero fractional digit: + */ + if (first <= 5) + { + unsigned int i; + *ascii++ = 46; /* decimal point */ + /* ndigits may be <5 for small numbers, output leading zeros + * then ndigits digits to first: + */ + i = 5; + while (ndigits < i) *ascii++ = 48, --i; + while (ndigits >= first) *ascii++ = digits[--ndigits]; + /* Don't output the trailing zeros! */ + } + } + else + *ascii++ = 48; + + /* And null terminate the string: */ + *ascii = 0; + return; + } + } + + /* Here on buffer too small. */ + png_error(png_ptr, "ASCII conversion buffer too small"); +} +# endif /* FIXED_POINT */ +#endif /* READ_SCAL */ + +#if defined(PNG_FLOATING_POINT_SUPPORTED) && \ + !defined(PNG_FIXED_POINT_MACRO_SUPPORTED) && \ + (defined(PNG_gAMA_SUPPORTED) || defined(PNG_cHRM_SUPPORTED) || \ + defined(PNG_sCAL_SUPPORTED) || defined(PNG_READ_BACKGROUND_SUPPORTED) || \ + defined(PNG_READ_RGB_TO_GRAY_SUPPORTED)) || \ + (defined(PNG_sCAL_SUPPORTED) && \ + defined(PNG_FLOATING_ARITHMETIC_SUPPORTED)) +png_fixed_point +png_fixed(png_const_structrp png_ptr, double fp, png_const_charp text) +{ + double r = floor(100000 * fp + .5); + + if (r > 2147483647. || r < -2147483648.) + png_fixed_error(png_ptr, text); + + return (png_fixed_point)r; +} +#endif + +#if defined(PNG_READ_GAMMA_SUPPORTED) || \ + defined(PNG_INCH_CONVERSIONS_SUPPORTED) || defined(PNG_READ_pHYs_SUPPORTED) +/* muldiv functions */ +/* This API takes signed arguments and rounds the result to the nearest + * integer (or, for a fixed point number - the standard argument - to + * the nearest .00001). Overflow and divide by zero are signalled in + * the result, a boolean - true on success, false on overflow. + */ +int +png_muldiv(png_fixed_point_p res, png_fixed_point a, png_int_32 times, + png_int_32 divisor) +{ + /* Return a * times / divisor, rounded. */ + if (divisor != 0) + { + if (a == 0 || times == 0) + { + *res = 0; + return 1; + } + else + { +#ifdef PNG_FLOATING_ARITHMETIC_SUPPORTED + double r = a; + r *= times; + r /= divisor; + r = floor(r+.5); + + /* A png_fixed_point is a 32-bit integer. */ + if (r <= 2147483647. && r >= -2147483648.) + { + *res = (png_fixed_point)r; + return 1; + } +#else + int negative = 0; + png_uint_32 A, T, D; + png_uint_32 s16, s32, s00; + + if (a < 0) + negative = 1, A = -a; + else + A = a; + + if (times < 0) + negative = !negative, T = -times; + else + T = times; + + if (divisor < 0) + negative = !negative, D = -divisor; + else + D = divisor; + + /* Following can't overflow because the arguments only + * have 31 bits each, however the result may be 32 bits. + */ + s16 = (A >> 16) * (T & 0xffff) + + (A & 0xffff) * (T >> 16); + /* Can't overflow because the a*times bit is only 30 + * bits at most. + */ + s32 = (A >> 16) * (T >> 16) + (s16 >> 16); + s00 = (A & 0xffff) * (T & 0xffff); + + s16 = (s16 & 0xffff) << 16; + s00 += s16; + + if (s00 < s16) + ++s32; /* carry */ + + if (s32 < D) /* else overflow */ + { + /* s32.s00 is now the 64-bit product, do a standard + * division, we know that s32 < D, so the maximum + * required shift is 31. + */ + int bitshift = 32; + png_fixed_point result = 0; /* NOTE: signed */ + + while (--bitshift >= 0) + { + png_uint_32 d32, d00; + + if (bitshift > 0) + d32 = D >> (32-bitshift), d00 = D << bitshift; + + else + d32 = 0, d00 = D; + + if (s32 > d32) + { + if (s00 < d00) --s32; /* carry */ + s32 -= d32, s00 -= d00, result += 1<= d00) + s32 = 0, s00 -= d00, result += 1<= (D >> 1)) + ++result; + + if (negative) + result = -result; + + /* Check for overflow. */ + if ((negative && result <= 0) || (!negative && result >= 0)) + { + *res = result; + return 1; + } + } +#endif + } + } + + return 0; +} +#endif /* READ_GAMMA || INCH_CONVERSIONS */ + +#if defined(PNG_READ_GAMMA_SUPPORTED) || defined(PNG_INCH_CONVERSIONS_SUPPORTED) +/* The following is for when the caller doesn't much care about the + * result. + */ +png_fixed_point +png_muldiv_warn(png_const_structrp png_ptr, png_fixed_point a, png_int_32 times, + png_int_32 divisor) +{ + png_fixed_point result; + + if (png_muldiv(&result, a, times, divisor)) + return result; + + png_warning(png_ptr, "fixed point overflow ignored"); + return 0; +} +#endif + +#ifdef PNG_GAMMA_SUPPORTED /* more fixed point functions for gamma */ +/* Calculate a reciprocal, return 0 on div-by-zero or overflow. */ +png_fixed_point +png_reciprocal(png_fixed_point a) +{ +#ifdef PNG_FLOATING_ARITHMETIC_SUPPORTED + double r = floor(1E10/a+.5); + + if (r <= 2147483647. && r >= -2147483648.) + return (png_fixed_point)r; +#else + png_fixed_point res; + + if (png_muldiv(&res, 100000, 100000, a)) + return res; +#endif + + return 0; /* error/overflow */ +} + +/* This is the shared test on whether a gamma value is 'significant' - whether + * it is worth doing gamma correction. + */ +int /* PRIVATE */ +png_gamma_significant(png_fixed_point gamma_val) +{ + return gamma_val < PNG_FP_1 - PNG_GAMMA_THRESHOLD_FIXED || + gamma_val > PNG_FP_1 + PNG_GAMMA_THRESHOLD_FIXED; +} +#endif + +#ifdef PNG_READ_GAMMA_SUPPORTED +/* A local convenience routine. */ +static png_fixed_point +png_product2(png_fixed_point a, png_fixed_point b) +{ + /* The required result is 1/a * 1/b; the following preserves accuracy. */ +#ifdef PNG_FLOATING_ARITHMETIC_SUPPORTED + double r = a * 1E-5; + r *= b; + r = floor(r+.5); + + if (r <= 2147483647. && r >= -2147483648.) + return (png_fixed_point)r; +#else + png_fixed_point res; + + if (png_muldiv(&res, a, b, 100000)) + return res; +#endif + + return 0; /* overflow */ +} + +/* The inverse of the above. */ +png_fixed_point +png_reciprocal2(png_fixed_point a, png_fixed_point b) +{ + /* The required result is 1/a * 1/b; the following preserves accuracy. */ +#ifdef PNG_FLOATING_ARITHMETIC_SUPPORTED + double r = 1E15/a; + r /= b; + r = floor(r+.5); + + if (r <= 2147483647. && r >= -2147483648.) + return (png_fixed_point)r; +#else + /* This may overflow because the range of png_fixed_point isn't symmetric, + * but this API is only used for the product of file and screen gamma so it + * doesn't matter that the smallest number it can produce is 1/21474, not + * 1/100000 + */ + png_fixed_point res = png_product2(a, b); + + if (res != 0) + return png_reciprocal(res); +#endif + + return 0; /* overflow */ +} +#endif /* READ_GAMMA */ + +#ifdef PNG_READ_GAMMA_SUPPORTED /* gamma table code */ +#ifndef PNG_FLOATING_ARITHMETIC_SUPPORTED +/* Fixed point gamma. + * + * The code to calculate the tables used below can be found in the shell script + * contrib/tools/intgamma.sh + * + * To calculate gamma this code implements fast log() and exp() calls using only + * fixed point arithmetic. This code has sufficient precision for either 8-bit + * or 16-bit sample values. + * + * The tables used here were calculated using simple 'bc' programs, but C double + * precision floating point arithmetic would work fine. + * + * 8-bit log table + * This is a table of -log(value/255)/log(2) for 'value' in the range 128 to + * 255, so it's the base 2 logarithm of a normalized 8-bit floating point + * mantissa. The numbers are 32-bit fractions. + */ +static const png_uint_32 +png_8bit_l2[128] = +{ + 4270715492U, 4222494797U, 4174646467U, 4127164793U, 4080044201U, 4033279239U, + 3986864580U, 3940795015U, 3895065449U, 3849670902U, 3804606499U, 3759867474U, + 3715449162U, 3671346997U, 3627556511U, 3584073329U, 3540893168U, 3498011834U, + 3455425220U, 3413129301U, 3371120137U, 3329393864U, 3287946700U, 3246774933U, + 3205874930U, 3165243125U, 3124876025U, 3084770202U, 3044922296U, 3005329011U, + 2965987113U, 2926893432U, 2888044853U, 2849438323U, 2811070844U, 2772939474U, + 2735041326U, 2697373562U, 2659933400U, 2622718104U, 2585724991U, 2548951424U, + 2512394810U, 2476052606U, 2439922311U, 2404001468U, 2368287663U, 2332778523U, + 2297471715U, 2262364947U, 2227455964U, 2192742551U, 2158222529U, 2123893754U, + 2089754119U, 2055801552U, 2022034013U, 1988449497U, 1955046031U, 1921821672U, + 1888774511U, 1855902668U, 1823204291U, 1790677560U, 1758320682U, 1726131893U, + 1694109454U, 1662251657U, 1630556815U, 1599023271U, 1567649391U, 1536433567U, + 1505374214U, 1474469770U, 1443718700U, 1413119487U, 1382670639U, 1352370686U, + 1322218179U, 1292211689U, 1262349810U, 1232631153U, 1203054352U, 1173618059U, + 1144320946U, 1115161701U, 1086139034U, 1057251672U, 1028498358U, 999877854U, + 971388940U, 943030410U, 914801076U, 886699767U, 858725327U, 830876614U, + 803152505U, 775551890U, 748073672U, 720716771U, 693480120U, 666362667U, + 639363374U, 612481215U, 585715177U, 559064263U, 532527486U, 506103872U, + 479792461U, 453592303U, 427502463U, 401522014U, 375650043U, 349885648U, + 324227938U, 298676034U, 273229066U, 247886176U, 222646516U, 197509248U, + 172473545U, 147538590U, 122703574U, 97967701U, 73330182U, 48790236U, + 24347096U, 0U + +#if 0 + /* The following are the values for 16-bit tables - these work fine for the + * 8-bit conversions but produce very slightly larger errors in the 16-bit + * log (about 1.2 as opposed to 0.7 absolute error in the final value). To + * use these all the shifts below must be adjusted appropriately. + */ + 65166, 64430, 63700, 62976, 62257, 61543, 60835, 60132, 59434, 58741, 58054, + 57371, 56693, 56020, 55352, 54689, 54030, 53375, 52726, 52080, 51439, 50803, + 50170, 49542, 48918, 48298, 47682, 47070, 46462, 45858, 45257, 44661, 44068, + 43479, 42894, 42312, 41733, 41159, 40587, 40020, 39455, 38894, 38336, 37782, + 37230, 36682, 36137, 35595, 35057, 34521, 33988, 33459, 32932, 32408, 31887, + 31369, 30854, 30341, 29832, 29325, 28820, 28319, 27820, 27324, 26830, 26339, + 25850, 25364, 24880, 24399, 23920, 23444, 22970, 22499, 22029, 21562, 21098, + 20636, 20175, 19718, 19262, 18808, 18357, 17908, 17461, 17016, 16573, 16132, + 15694, 15257, 14822, 14390, 13959, 13530, 13103, 12678, 12255, 11834, 11415, + 10997, 10582, 10168, 9756, 9346, 8937, 8531, 8126, 7723, 7321, 6921, 6523, + 6127, 5732, 5339, 4947, 4557, 4169, 3782, 3397, 3014, 2632, 2251, 1872, 1495, + 1119, 744, 372 +#endif +}; + +static png_int_32 +png_log8bit(unsigned int x) +{ + unsigned int lg2 = 0; + /* Each time 'x' is multiplied by 2, 1 must be subtracted off the final log, + * because the log is actually negate that means adding 1. The final + * returned value thus has the range 0 (for 255 input) to 7.994 (for 1 + * input), return -1 for the overflow (log 0) case, - so the result is + * always at most 19 bits. + */ + if ((x &= 0xff) == 0) + return -1; + + if ((x & 0xf0) == 0) + lg2 = 4, x <<= 4; + + if ((x & 0xc0) == 0) + lg2 += 2, x <<= 2; + + if ((x & 0x80) == 0) + lg2 += 1, x <<= 1; + + /* result is at most 19 bits, so this cast is safe: */ + return (png_int_32)((lg2 << 16) + ((png_8bit_l2[x-128]+32768)>>16)); +} + +/* The above gives exact (to 16 binary places) log2 values for 8-bit images, + * for 16-bit images we use the most significant 8 bits of the 16-bit value to + * get an approximation then multiply the approximation by a correction factor + * determined by the remaining up to 8 bits. This requires an additional step + * in the 16-bit case. + * + * We want log2(value/65535), we have log2(v'/255), where: + * + * value = v' * 256 + v'' + * = v' * f + * + * So f is value/v', which is equal to (256+v''/v') since v' is in the range 128 + * to 255 and v'' is in the range 0 to 255 f will be in the range 256 to less + * than 258. The final factor also needs to correct for the fact that our 8-bit + * value is scaled by 255, whereas the 16-bit values must be scaled by 65535. + * + * This gives a final formula using a calculated value 'x' which is value/v' and + * scaling by 65536 to match the above table: + * + * log2(x/257) * 65536 + * + * Since these numbers are so close to '1' we can use simple linear + * interpolation between the two end values 256/257 (result -368.61) and 258/257 + * (result 367.179). The values used below are scaled by a further 64 to give + * 16-bit precision in the interpolation: + * + * Start (256): -23591 + * Zero (257): 0 + * End (258): 23499 + */ +static png_int_32 +png_log16bit(png_uint_32 x) +{ + unsigned int lg2 = 0; + + /* As above, but now the input has 16 bits. */ + if ((x &= 0xffff) == 0) + return -1; + + if ((x & 0xff00) == 0) + lg2 = 8, x <<= 8; + + if ((x & 0xf000) == 0) + lg2 += 4, x <<= 4; + + if ((x & 0xc000) == 0) + lg2 += 2, x <<= 2; + + if ((x & 0x8000) == 0) + lg2 += 1, x <<= 1; + + /* Calculate the base logarithm from the top 8 bits as a 28-bit fractional + * value. + */ + lg2 <<= 28; + lg2 += (png_8bit_l2[(x>>8)-128]+8) >> 4; + + /* Now we need to interpolate the factor, this requires a division by the top + * 8 bits. Do this with maximum precision. + */ + x = ((x << 16) + (x >> 9)) / (x >> 8); + + /* Since we divided by the top 8 bits of 'x' there will be a '1' at 1<<24, + * the value at 1<<16 (ignoring this) will be 0 or 1; this gives us exactly + * 16 bits to interpolate to get the low bits of the result. Round the + * answer. Note that the end point values are scaled by 64 to retain overall + * precision and that 'lg2' is current scaled by an extra 12 bits, so adjust + * the overall scaling by 6-12. Round at every step. + */ + x -= 1U << 24; + + if (x <= 65536U) /* <= '257' */ + lg2 += ((23591U * (65536U-x)) + (1U << (16+6-12-1))) >> (16+6-12); + + else + lg2 -= ((23499U * (x-65536U)) + (1U << (16+6-12-1))) >> (16+6-12); + + /* Safe, because the result can't have more than 20 bits: */ + return (png_int_32)((lg2 + 2048) >> 12); +} + +/* The 'exp()' case must invert the above, taking a 20-bit fixed point + * logarithmic value and returning a 16 or 8-bit number as appropriate. In + * each case only the low 16 bits are relevant - the fraction - since the + * integer bits (the top 4) simply determine a shift. + * + * The worst case is the 16-bit distinction between 65535 and 65534, this + * requires perhaps spurious accuracty in the decoding of the logarithm to + * distinguish log2(65535/65534.5) - 10^-5 or 17 bits. There is little chance + * of getting this accuracy in practice. + * + * To deal with this the following exp() function works out the exponent of the + * frational part of the logarithm by using an accurate 32-bit value from the + * top four fractional bits then multiplying in the remaining bits. + */ +static const png_uint_32 +png_32bit_exp[16] = +{ + /* NOTE: the first entry is deliberately set to the maximum 32-bit value. */ + 4294967295U, 4112874773U, 3938502376U, 3771522796U, 3611622603U, 3458501653U, + 3311872529U, 3171459999U, 3037000500U, 2908241642U, 2784941738U, 2666869345U, + 2553802834U, 2445529972U, 2341847524U, 2242560872U +}; + +/* Adjustment table; provided to explain the numbers in the code below. */ +#if 0 +for (i=11;i>=0;--i){ print i, " ", (1 - e(-(2^i)/65536*l(2))) * 2^(32-i), "\n"} + 11 44937.64284865548751208448 + 10 45180.98734845585101160448 + 9 45303.31936980687359311872 + 8 45364.65110595323018870784 + 7 45395.35850361789624614912 + 6 45410.72259715102037508096 + 5 45418.40724413220722311168 + 4 45422.25021786898173001728 + 3 45424.17186732298419044352 + 2 45425.13273269940811464704 + 1 45425.61317555035558641664 + 0 45425.85339951654943850496 +#endif + +static png_uint_32 +png_exp(png_fixed_point x) +{ + if (x > 0 && x <= 0xfffff) /* Else overflow or zero (underflow) */ + { + /* Obtain a 4-bit approximation */ + png_uint_32 e = png_32bit_exp[(x >> 12) & 0xf]; + + /* Incorporate the low 12 bits - these decrease the returned value by + * multiplying by a number less than 1 if the bit is set. The multiplier + * is determined by the above table and the shift. Notice that the values + * converge on 45426 and this is used to allow linear interpolation of the + * low bits. + */ + if (x & 0x800) + e -= (((e >> 16) * 44938U) + 16U) >> 5; + + if (x & 0x400) + e -= (((e >> 16) * 45181U) + 32U) >> 6; + + if (x & 0x200) + e -= (((e >> 16) * 45303U) + 64U) >> 7; + + if (x & 0x100) + e -= (((e >> 16) * 45365U) + 128U) >> 8; + + if (x & 0x080) + e -= (((e >> 16) * 45395U) + 256U) >> 9; + + if (x & 0x040) + e -= (((e >> 16) * 45410U) + 512U) >> 10; + + /* And handle the low 6 bits in a single block. */ + e -= (((e >> 16) * 355U * (x & 0x3fU)) + 256U) >> 9; + + /* Handle the upper bits of x. */ + e >>= x >> 16; + return e; + } + + /* Check for overflow */ + if (x <= 0) + return png_32bit_exp[0]; + + /* Else underflow */ + return 0; +} + +static png_byte +png_exp8bit(png_fixed_point lg2) +{ + /* Get a 32-bit value: */ + png_uint_32 x = png_exp(lg2); + + /* Convert the 32-bit value to 0..255 by multiplying by 256-1, note that the + * second, rounding, step can't overflow because of the first, subtraction, + * step. + */ + x -= x >> 8; + return (png_byte)((x + 0x7fffffU) >> 24); +} + +static png_uint_16 +png_exp16bit(png_fixed_point lg2) +{ + /* Get a 32-bit value: */ + png_uint_32 x = png_exp(lg2); + + /* Convert the 32-bit value to 0..65535 by multiplying by 65536-1: */ + x -= x >> 16; + return (png_uint_16)((x + 32767U) >> 16); +} +#endif /* FLOATING_ARITHMETIC */ + +png_byte +png_gamma_8bit_correct(unsigned int value, png_fixed_point gamma_val) +{ + if (value > 0 && value < 255) + { +# ifdef PNG_FLOATING_ARITHMETIC_SUPPORTED + double r = floor(255*pow(value/255.,gamma_val*.00001)+.5); + return (png_byte)r; +# else + png_int_32 lg2 = png_log8bit(value); + png_fixed_point res; + + if (png_muldiv(&res, gamma_val, lg2, PNG_FP_1)) + return png_exp8bit(res); + + /* Overflow. */ + value = 0; +# endif + } + + return (png_byte)value; +} + +png_uint_16 +png_gamma_16bit_correct(unsigned int value, png_fixed_point gamma_val) +{ + if (value > 0 && value < 65535) + { +# ifdef PNG_FLOATING_ARITHMETIC_SUPPORTED + double r = floor(65535*pow(value/65535.,gamma_val*.00001)+.5); + return (png_uint_16)r; +# else + png_int_32 lg2 = png_log16bit(value); + png_fixed_point res; + + if (png_muldiv(&res, gamma_val, lg2, PNG_FP_1)) + return png_exp16bit(res); + + /* Overflow. */ + value = 0; +# endif + } + + return (png_uint_16)value; +} + +/* This does the right thing based on the bit_depth field of the + * png_struct, interpreting values as 8-bit or 16-bit. While the result + * is nominally a 16-bit value if bit depth is 8 then the result is + * 8-bit (as are the arguments.) + */ +png_uint_16 /* PRIVATE */ +png_gamma_correct(png_structrp png_ptr, unsigned int value, + png_fixed_point gamma_val) +{ + if (png_ptr->bit_depth == 8) + return png_gamma_8bit_correct(value, gamma_val); + + else + return png_gamma_16bit_correct(value, gamma_val); +} + +/* Internal function to build a single 16-bit table - the table consists of + * 'num' 256 entry subtables, where 'num' is determined by 'shift' - the amount + * to shift the input values right (or 16-number_of_signifiant_bits). + * + * The caller is responsible for ensuring that the table gets cleaned up on + * png_error (i.e. if one of the mallocs below fails) - i.e. the *table argument + * should be somewhere that will be cleaned. + */ +static void +png_build_16bit_table(png_structrp png_ptr, png_uint_16pp *ptable, + PNG_CONST unsigned int shift, PNG_CONST png_fixed_point gamma_val) +{ + /* Various values derived from 'shift': */ + PNG_CONST unsigned int num = 1U << (8U - shift); + PNG_CONST unsigned int max = (1U << (16U - shift))-1U; + PNG_CONST unsigned int max_by_2 = 1U << (15U-shift); + unsigned int i; + + png_uint_16pp table = *ptable = + (png_uint_16pp)png_calloc(png_ptr, num * (sizeof (png_uint_16p))); + + for (i = 0; i < num; i++) + { + png_uint_16p sub_table = table[i] = + (png_uint_16p)png_malloc(png_ptr, 256 * (sizeof (png_uint_16))); + + /* The 'threshold' test is repeated here because it can arise for one of + * the 16-bit tables even if the others don't hit it. + */ + if (png_gamma_significant(gamma_val)) + { + /* The old code would overflow at the end and this would cause the + * 'pow' function to return a result >1, resulting in an + * arithmetic error. This code follows the spec exactly; ig is + * the recovered input sample, it always has 8-16 bits. + * + * We want input * 65535/max, rounded, the arithmetic fits in 32 + * bits (unsigned) so long as max <= 32767. + */ + unsigned int j; + for (j = 0; j < 256; j++) + { + png_uint_32 ig = (j << (8-shift)) + i; +# ifdef PNG_FLOATING_ARITHMETIC_SUPPORTED + /* Inline the 'max' scaling operation: */ + double d = floor(65535*pow(ig/(double)max, gamma_val*.00001)+.5); + sub_table[j] = (png_uint_16)d; +# else + if (shift) + ig = (ig * 65535U + max_by_2)/max; + + sub_table[j] = png_gamma_16bit_correct(ig, gamma_val); +# endif + } + } + else + { + /* We must still build a table, but do it the fast way. */ + unsigned int j; + + for (j = 0; j < 256; j++) + { + png_uint_32 ig = (j << (8-shift)) + i; + + if (shift) + ig = (ig * 65535U + max_by_2)/max; + + sub_table[j] = (png_uint_16)ig; + } + } + } +} + +/* NOTE: this function expects the *inverse* of the overall gamma transformation + * required. + */ +static void +png_build_16to8_table(png_structrp png_ptr, png_uint_16pp *ptable, + PNG_CONST unsigned int shift, PNG_CONST png_fixed_point gamma_val) +{ + PNG_CONST unsigned int num = 1U << (8U - shift); + PNG_CONST unsigned int max = (1U << (16U - shift))-1U; + unsigned int i; + png_uint_32 last; + + png_uint_16pp table = *ptable = + (png_uint_16pp)png_calloc(png_ptr, num * (sizeof (png_uint_16p))); + + /* 'num' is the number of tables and also the number of low bits of low + * bits of the input 16-bit value used to select a table. Each table is + * itself index by the high 8 bits of the value. + */ + for (i = 0; i < num; i++) + table[i] = (png_uint_16p)png_malloc(png_ptr, + 256 * (sizeof (png_uint_16))); + + /* 'gamma_val' is set to the reciprocal of the value calculated above, so + * pow(out,g) is an *input* value. 'last' is the last input value set. + * + * In the loop 'i' is used to find output values. Since the output is + * 8-bit there are only 256 possible values. The tables are set up to + * select the closest possible output value for each input by finding + * the input value at the boundary between each pair of output values + * and filling the table up to that boundary with the lower output + * value. + * + * The boundary values are 0.5,1.5..253.5,254.5. Since these are 9-bit + * values the code below uses a 16-bit value in i; the values start at + * 128.5 (for 0.5) and step by 257, for a total of 254 values (the last + * entries are filled with 255). Start i at 128 and fill all 'last' + * table entries <= 'max' + */ + last = 0; + for (i = 0; i < 255; ++i) /* 8-bit output value */ + { + /* Find the corresponding maximum input value */ + png_uint_16 out = (png_uint_16)(i * 257U); /* 16-bit output value */ + + /* Find the boundary value in 16 bits: */ + png_uint_32 bound = png_gamma_16bit_correct(out+128U, gamma_val); + + /* Adjust (round) to (16-shift) bits: */ + bound = (bound * max + 32768U)/65535U + 1U; + + while (last < bound) + { + table[last & (0xffU >> shift)][last >> (8U - shift)] = out; + last++; + } + } + + /* And fill in the final entries. */ + while (last < (num << 8)) + { + table[last & (0xff >> shift)][last >> (8U - shift)] = 65535U; + last++; + } +} + +/* Build a single 8-bit table: same as the 16-bit case but much simpler (and + * typically much faster). Note that libpng currently does no sBIT processing + * (apparently contrary to the spec) so a 256 entry table is always generated. + */ +static void +png_build_8bit_table(png_structrp png_ptr, png_bytepp ptable, + PNG_CONST png_fixed_point gamma_val) +{ + unsigned int i; + png_bytep table = *ptable = (png_bytep)png_malloc(png_ptr, 256); + + if (png_gamma_significant(gamma_val)) for (i=0; i<256; i++) + table[i] = png_gamma_8bit_correct(i, gamma_val); + + else for (i=0; i<256; ++i) + table[i] = (png_byte)i; +} + +/* Used from png_read_destroy and below to release the memory used by the gamma + * tables. + */ +void /* PRIVATE */ +png_destroy_gamma_table(png_structrp png_ptr) +{ + png_free(png_ptr, png_ptr->gamma_table); + png_ptr->gamma_table = NULL; + + if (png_ptr->gamma_16_table != NULL) + { + int i; + int istop = (1 << (8 - png_ptr->gamma_shift)); + for (i = 0; i < istop; i++) + { + png_free(png_ptr, png_ptr->gamma_16_table[i]); + } + png_free(png_ptr, png_ptr->gamma_16_table); + png_ptr->gamma_16_table = NULL; + } + +#if defined(PNG_READ_BACKGROUND_SUPPORTED) || \ + defined(PNG_READ_ALPHA_MODE_SUPPORTED) || \ + defined(PNG_READ_RGB_TO_GRAY_SUPPORTED) + png_free(png_ptr, png_ptr->gamma_from_1); + png_ptr->gamma_from_1 = NULL; + png_free(png_ptr, png_ptr->gamma_to_1); + png_ptr->gamma_to_1 = NULL; + + if (png_ptr->gamma_16_from_1 != NULL) + { + int i; + int istop = (1 << (8 - png_ptr->gamma_shift)); + for (i = 0; i < istop; i++) + { + png_free(png_ptr, png_ptr->gamma_16_from_1[i]); + } + png_free(png_ptr, png_ptr->gamma_16_from_1); + png_ptr->gamma_16_from_1 = NULL; + } + if (png_ptr->gamma_16_to_1 != NULL) + { + int i; + int istop = (1 << (8 - png_ptr->gamma_shift)); + for (i = 0; i < istop; i++) + { + png_free(png_ptr, png_ptr->gamma_16_to_1[i]); + } + png_free(png_ptr, png_ptr->gamma_16_to_1); + png_ptr->gamma_16_to_1 = NULL; + } +#endif /* READ_BACKGROUND || READ_ALPHA_MODE || RGB_TO_GRAY */ +} + +/* We build the 8- or 16-bit gamma tables here. Note that for 16-bit + * tables, we don't make a full table if we are reducing to 8-bit in + * the future. Note also how the gamma_16 tables are segmented so that + * we don't need to allocate > 64K chunks for a full 16-bit table. + */ +void /* PRIVATE */ +png_build_gamma_table(png_structrp png_ptr, int bit_depth) +{ + png_debug(1, "in png_build_gamma_table"); + + /* Remove any existing table; this copes with multiple calls to + * png_read_update_info. The warning is because building the gamma tables + * multiple times is a performance hit - it's harmless but the ability to call + * png_read_update_info() multiple times is new in 1.5.6 so it seems sensible + * to warn if the app introduces such a hit. + */ + if (png_ptr->gamma_table != NULL || png_ptr->gamma_16_table != NULL) + { + png_warning(png_ptr, "gamma table being rebuilt"); + png_destroy_gamma_table(png_ptr); + } + + if (bit_depth <= 8) + { + png_build_8bit_table(png_ptr, &png_ptr->gamma_table, + png_ptr->screen_gamma > 0 ? png_reciprocal2(png_ptr->colorspace.gamma, + png_ptr->screen_gamma) : PNG_FP_1); + +#if defined(PNG_READ_BACKGROUND_SUPPORTED) || \ + defined(PNG_READ_ALPHA_MODE_SUPPORTED) || \ + defined(PNG_READ_RGB_TO_GRAY_SUPPORTED) + if (png_ptr->transformations & (PNG_COMPOSE | PNG_RGB_TO_GRAY)) + { + png_build_8bit_table(png_ptr, &png_ptr->gamma_to_1, + png_reciprocal(png_ptr->colorspace.gamma)); + + png_build_8bit_table(png_ptr, &png_ptr->gamma_from_1, + png_ptr->screen_gamma > 0 ? png_reciprocal(png_ptr->screen_gamma) : + png_ptr->colorspace.gamma/* Probably doing rgb_to_gray */); + } +#endif /* READ_BACKGROUND || READ_ALPHA_MODE || RGB_TO_GRAY */ + } + else + { + png_byte shift, sig_bit; + + if (png_ptr->color_type & PNG_COLOR_MASK_COLOR) + { + sig_bit = png_ptr->sig_bit.red; + + if (png_ptr->sig_bit.green > sig_bit) + sig_bit = png_ptr->sig_bit.green; + + if (png_ptr->sig_bit.blue > sig_bit) + sig_bit = png_ptr->sig_bit.blue; + } + else + sig_bit = png_ptr->sig_bit.gray; + + /* 16-bit gamma code uses this equation: + * + * ov = table[(iv & 0xff) >> gamma_shift][iv >> 8] + * + * Where 'iv' is the input color value and 'ov' is the output value - + * pow(iv, gamma). + * + * Thus the gamma table consists of up to 256 256 entry tables. The table + * is selected by the (8-gamma_shift) most significant of the low 8 bits of + * the color value then indexed by the upper 8 bits: + * + * table[low bits][high 8 bits] + * + * So the table 'n' corresponds to all those 'iv' of: + * + * ..<(n+1 << gamma_shift)-1> + * + */ + if (sig_bit > 0 && sig_bit < 16U) + shift = (png_byte)(16U - sig_bit); /* shift == insignificant bits */ + + else + shift = 0; /* keep all 16 bits */ + + if (png_ptr->transformations & (PNG_16_TO_8 | PNG_SCALE_16_TO_8)) + { + /* PNG_MAX_GAMMA_8 is the number of bits to keep - effectively + * the significant bits in the *input* when the output will + * eventually be 8 bits. By default it is 11. + */ + if (shift < (16U - PNG_MAX_GAMMA_8)) + shift = (16U - PNG_MAX_GAMMA_8); + } + + if (shift > 8U) + shift = 8U; /* Guarantees at least one table! */ + + png_ptr->gamma_shift = shift; + +#ifdef PNG_16BIT_SUPPORTED + /* NOTE: prior to 1.5.4 this test used to include PNG_BACKGROUND (now + * PNG_COMPOSE). This effectively smashed the background calculation for + * 16-bit output because the 8-bit table assumes the result will be reduced + * to 8 bits. + */ + if (png_ptr->transformations & (PNG_16_TO_8 | PNG_SCALE_16_TO_8)) +#endif + png_build_16to8_table(png_ptr, &png_ptr->gamma_16_table, shift, + png_ptr->screen_gamma > 0 ? png_product2(png_ptr->colorspace.gamma, + png_ptr->screen_gamma) : PNG_FP_1); + +#ifdef PNG_16BIT_SUPPORTED + else + png_build_16bit_table(png_ptr, &png_ptr->gamma_16_table, shift, + png_ptr->screen_gamma > 0 ? png_reciprocal2(png_ptr->colorspace.gamma, + png_ptr->screen_gamma) : PNG_FP_1); +#endif + +#if defined(PNG_READ_BACKGROUND_SUPPORTED) || \ + defined(PNG_READ_ALPHA_MODE_SUPPORTED) || \ + defined(PNG_READ_RGB_TO_GRAY_SUPPORTED) + if (png_ptr->transformations & (PNG_COMPOSE | PNG_RGB_TO_GRAY)) + { + png_build_16bit_table(png_ptr, &png_ptr->gamma_16_to_1, shift, + png_reciprocal(png_ptr->colorspace.gamma)); + + /* Notice that the '16 from 1' table should be full precision, however + * the lookup on this table still uses gamma_shift, so it can't be. + * TODO: fix this. + */ + png_build_16bit_table(png_ptr, &png_ptr->gamma_16_from_1, shift, + png_ptr->screen_gamma > 0 ? png_reciprocal(png_ptr->screen_gamma) : + png_ptr->colorspace.gamma/* Probably doing rgb_to_gray */); + } +#endif /* READ_BACKGROUND || READ_ALPHA_MODE || RGB_TO_GRAY */ + } +} +#endif /* READ_GAMMA */ + +/* HARDWARE OPTION SUPPORT */ +#ifdef PNG_SET_OPTION_SUPPORTED +int PNGAPI +png_set_option(png_structrp png_ptr, int option, int onoff) +{ + if (png_ptr != NULL && option >= 0 && option < PNG_OPTION_NEXT && + (option & 1) == 0) + { + int mask = 3 << option; + int setting = (2 + (onoff != 0)) << option; + int current = png_ptr->options; + + png_ptr->options = (png_byte)((current & ~mask) | setting); + + return (current & mask) >> option; + } + + return PNG_OPTION_INVALID; +} +#endif + +/* sRGB support */ +#if defined(PNG_SIMPLIFIED_READ_SUPPORTED) ||\ + defined(PNG_SIMPLIFIED_WRITE_SUPPORTED) +/* sRGB conversion tables; these are machine generated with the code in + * contrib/tools/makesRGB.c. The actual sRGB transfer curve defined in the + * specification (see the article at http://en.wikipedia.org/wiki/SRGB) + * is used, not the gamma=1/2.2 approximation use elsewhere in libpng. + * The sRGB to linear table is exact (to the nearest 16 bit linear fraction). + * The inverse (linear to sRGB) table has accuracies as follows: + * + * For all possible (255*65535+1) input values: + * + * error: -0.515566 - 0.625971, 79441 (0.475369%) of readings inexact + * + * For the input values corresponding to the 65536 16-bit values: + * + * error: -0.513727 - 0.607759, 308 (0.469978%) of readings inexact + * + * In all cases the inexact readings are off by one. + */ + +#ifdef PNG_SIMPLIFIED_READ_SUPPORTED +/* The convert-to-sRGB table is only currently required for read. */ +const png_uint_16 png_sRGB_table[256] = +{ + 0,20,40,60,80,99,119,139, + 159,179,199,219,241,264,288,313, + 340,367,396,427,458,491,526,562, + 599,637,677,718,761,805,851,898, + 947,997,1048,1101,1156,1212,1270,1330, + 1391,1453,1517,1583,1651,1720,1790,1863, + 1937,2013,2090,2170,2250,2333,2418,2504, + 2592,2681,2773,2866,2961,3058,3157,3258, + 3360,3464,3570,3678,3788,3900,4014,4129, + 4247,4366,4488,4611,4736,4864,4993,5124, + 5257,5392,5530,5669,5810,5953,6099,6246, + 6395,6547,6700,6856,7014,7174,7335,7500, + 7666,7834,8004,8177,8352,8528,8708,8889, + 9072,9258,9445,9635,9828,10022,10219,10417, + 10619,10822,11028,11235,11446,11658,11873,12090, + 12309,12530,12754,12980,13209,13440,13673,13909, + 14146,14387,14629,14874,15122,15371,15623,15878, + 16135,16394,16656,16920,17187,17456,17727,18001, + 18277,18556,18837,19121,19407,19696,19987,20281, + 20577,20876,21177,21481,21787,22096,22407,22721, + 23038,23357,23678,24002,24329,24658,24990,25325, + 25662,26001,26344,26688,27036,27386,27739,28094, + 28452,28813,29176,29542,29911,30282,30656,31033, + 31412,31794,32179,32567,32957,33350,33745,34143, + 34544,34948,35355,35764,36176,36591,37008,37429, + 37852,38278,38706,39138,39572,40009,40449,40891, + 41337,41785,42236,42690,43147,43606,44069,44534, + 45002,45473,45947,46423,46903,47385,47871,48359, + 48850,49344,49841,50341,50844,51349,51858,52369, + 52884,53401,53921,54445,54971,55500,56032,56567, + 57105,57646,58190,58737,59287,59840,60396,60955, + 61517,62082,62650,63221,63795,64372,64952,65535 +}; + +#endif /* simplified read only */ + +/* The base/delta tables are required for both read and write (but currently + * only the simplified versions.) + */ +const png_uint_16 png_sRGB_base[512] = +{ + 128,1782,3383,4644,5675,6564,7357,8074, + 8732,9346,9921,10463,10977,11466,11935,12384, + 12816,13233,13634,14024,14402,14769,15125,15473, + 15812,16142,16466,16781,17090,17393,17690,17981, + 18266,18546,18822,19093,19359,19621,19879,20133, + 20383,20630,20873,21113,21349,21583,21813,22041, + 22265,22487,22707,22923,23138,23350,23559,23767, + 23972,24175,24376,24575,24772,24967,25160,25352, + 25542,25730,25916,26101,26284,26465,26645,26823, + 27000,27176,27350,27523,27695,27865,28034,28201, + 28368,28533,28697,28860,29021,29182,29341,29500, + 29657,29813,29969,30123,30276,30429,30580,30730, + 30880,31028,31176,31323,31469,31614,31758,31902, + 32045,32186,32327,32468,32607,32746,32884,33021, + 33158,33294,33429,33564,33697,33831,33963,34095, + 34226,34357,34486,34616,34744,34873,35000,35127, + 35253,35379,35504,35629,35753,35876,35999,36122, + 36244,36365,36486,36606,36726,36845,36964,37083, + 37201,37318,37435,37551,37668,37783,37898,38013, + 38127,38241,38354,38467,38580,38692,38803,38915, + 39026,39136,39246,39356,39465,39574,39682,39790, + 39898,40005,40112,40219,40325,40431,40537,40642, + 40747,40851,40955,41059,41163,41266,41369,41471, + 41573,41675,41777,41878,41979,42079,42179,42279, + 42379,42478,42577,42676,42775,42873,42971,43068, + 43165,43262,43359,43456,43552,43648,43743,43839, + 43934,44028,44123,44217,44311,44405,44499,44592, + 44685,44778,44870,44962,45054,45146,45238,45329, + 45420,45511,45601,45692,45782,45872,45961,46051, + 46140,46229,46318,46406,46494,46583,46670,46758, + 46846,46933,47020,47107,47193,47280,47366,47452, + 47538,47623,47709,47794,47879,47964,48048,48133, + 48217,48301,48385,48468,48552,48635,48718,48801, + 48884,48966,49048,49131,49213,49294,49376,49458, + 49539,49620,49701,49782,49862,49943,50023,50103, + 50183,50263,50342,50422,50501,50580,50659,50738, + 50816,50895,50973,51051,51129,51207,51285,51362, + 51439,51517,51594,51671,51747,51824,51900,51977, + 52053,52129,52205,52280,52356,52432,52507,52582, + 52657,52732,52807,52881,52956,53030,53104,53178, + 53252,53326,53400,53473,53546,53620,53693,53766, + 53839,53911,53984,54056,54129,54201,54273,54345, + 54417,54489,54560,54632,54703,54774,54845,54916, + 54987,55058,55129,55199,55269,55340,55410,55480, + 55550,55620,55689,55759,55828,55898,55967,56036, + 56105,56174,56243,56311,56380,56448,56517,56585, + 56653,56721,56789,56857,56924,56992,57059,57127, + 57194,57261,57328,57395,57462,57529,57595,57662, + 57728,57795,57861,57927,57993,58059,58125,58191, + 58256,58322,58387,58453,58518,58583,58648,58713, + 58778,58843,58908,58972,59037,59101,59165,59230, + 59294,59358,59422,59486,59549,59613,59677,59740, + 59804,59867,59930,59993,60056,60119,60182,60245, + 60308,60370,60433,60495,60558,60620,60682,60744, + 60806,60868,60930,60992,61054,61115,61177,61238, + 61300,61361,61422,61483,61544,61605,61666,61727, + 61788,61848,61909,61969,62030,62090,62150,62211, + 62271,62331,62391,62450,62510,62570,62630,62689, + 62749,62808,62867,62927,62986,63045,63104,63163, + 63222,63281,63340,63398,63457,63515,63574,63632, + 63691,63749,63807,63865,63923,63981,64039,64097, + 64155,64212,64270,64328,64385,64443,64500,64557, + 64614,64672,64729,64786,64843,64900,64956,65013, + 65070,65126,65183,65239,65296,65352,65409,65465 +}; + +const png_byte png_sRGB_delta[512] = +{ + 207,201,158,129,113,100,90,82,77,72,68,64,61,59,56,54, + 52,50,49,47,46,45,43,42,41,40,39,39,38,37,36,36, + 35,34,34,33,33,32,32,31,31,30,30,30,29,29,28,28, + 28,27,27,27,27,26,26,26,25,25,25,25,24,24,24,24, + 23,23,23,23,23,22,22,22,22,22,22,21,21,21,21,21, + 21,20,20,20,20,20,20,20,20,19,19,19,19,19,19,19, + 19,18,18,18,18,18,18,18,18,18,18,17,17,17,17,17, + 17,17,17,17,17,17,16,16,16,16,16,16,16,16,16,16, + 16,16,16,16,15,15,15,15,15,15,15,15,15,15,15,15, + 15,15,15,15,14,14,14,14,14,14,14,14,14,14,14,14, + 14,14,14,14,14,14,14,13,13,13,13,13,13,13,13,13, + 13,13,13,13,13,13,13,13,13,13,13,13,13,13,12,12, + 12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12, + 12,12,12,12,12,12,12,12,12,12,12,12,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, + 10,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, + 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, + 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, + 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, + 9,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, + 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, + 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, + 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, + 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, + 8,8,8,8,8,8,8,8,8,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7 +}; +#endif /* SIMPLIFIED READ/WRITE sRGB support */ + +/* SIMPLIFIED READ/WRITE SUPPORT */ +#if defined(PNG_SIMPLIFIED_READ_SUPPORTED) ||\ + defined(PNG_SIMPLIFIED_WRITE_SUPPORTED) +static int +png_image_free_function(png_voidp argument) +{ + png_imagep image = png_voidcast(png_imagep, argument); + png_controlp cp = image->opaque; + png_control c; + + /* Double check that we have a png_ptr - it should be impossible to get here + * without one. + */ + if (cp->png_ptr == NULL) + return 0; + + /* First free any data held in the control structure. */ +# ifdef PNG_STDIO_SUPPORTED + if (cp->owned_file) + { + FILE *fp = png_voidcast(FILE*, cp->png_ptr->io_ptr); + cp->owned_file = 0; + + /* Ignore errors here. */ + if (fp != NULL) + { + cp->png_ptr->io_ptr = NULL; + (void)fclose(fp); + } + } +# endif + + /* Copy the control structure so that the original, allocated, version can be + * safely freed. Notice that a png_error here stops the remainder of the + * cleanup, but this is probably fine because that would indicate bad memory + * problems anyway. + */ + c = *cp; + image->opaque = &c; + png_free(c.png_ptr, cp); + + /* Then the structures, calling the correct API. */ + if (c.for_write) + { +# ifdef PNG_SIMPLIFIED_WRITE_SUPPORTED + png_destroy_write_struct(&c.png_ptr, &c.info_ptr); +# else + png_error(c.png_ptr, "simplified write not supported"); +# endif + } + else + { +# ifdef PNG_SIMPLIFIED_READ_SUPPORTED + png_destroy_read_struct(&c.png_ptr, &c.info_ptr, NULL); +# else + png_error(c.png_ptr, "simplified read not supported"); +# endif + } + + /* Success. */ + return 1; +} + +void PNGAPI +png_image_free(png_imagep image) +{ + /* Safely call the real function, but only if doing so is safe at this point + * (if not inside an error handling context). Otherwise assume + * png_safe_execute will call this API after the return. + */ + if (image != NULL && image->opaque != NULL && + image->opaque->error_buf == NULL) + { + /* Ignore errors here: */ + (void)png_safe_execute(image, png_image_free_function, image); + image->opaque = NULL; + } +} + +int /* PRIVATE */ +png_image_error(png_imagep image, png_const_charp error_message) +{ + /* Utility to log an error. */ + png_safecat(image->message, (sizeof image->message), 0, error_message); + image->warning_or_error |= PNG_IMAGE_ERROR; + png_image_free(image); + return 0; +} + +#endif /* SIMPLIFIED READ/WRITE */ +#endif /* defined(PNG_READ_SUPPORTED) || defined(PNG_WRITE_SUPPORTED) */ diff --git a/Minecraft.Client/Windows64/4JLibs/inc/Render/libpng/png.h b/Minecraft.Client/Windows64/4JLibs/inc/Render/libpng/png.h new file mode 100644 index 000000000..68a0772a8 --- /dev/null +++ b/Minecraft.Client/Windows64/4JLibs/inc/Render/libpng/png.h @@ -0,0 +1,3305 @@ + +/* png.h - header file for PNG reference library + * + * libpng version 1.6.2 - April 25, 2013 + * Copyright (c) 1998-2013 Glenn Randers-Pehrson + * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) + * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) + * + * This code is released under the libpng license (See LICENSE, below) + * + * Authors and maintainers: + * libpng versions 0.71, May 1995, through 0.88, January 1996: Guy Schalnat + * libpng versions 0.89c, June 1996, through 0.96, May 1997: Andreas Dilger + * libpng versions 0.97, January 1998, through 1.6.2 - April 25, 2013: Glenn + * See also "Contributing Authors", below. + * + * Note about libpng version numbers: + * + * Due to various miscommunications, unforeseen code incompatibilities + * and occasional factors outside the authors' control, version numbering + * on the library has not always been consistent and straightforward. + * The following table summarizes matters since version 0.89c, which was + * the first widely used release: + * + * source png.h png.h shared-lib + * version string int version + * ------- ------ ----- ---------- + * 0.89c "1.0 beta 3" 0.89 89 1.0.89 + * 0.90 "1.0 beta 4" 0.90 90 0.90 [should have been 2.0.90] + * 0.95 "1.0 beta 5" 0.95 95 0.95 [should have been 2.0.95] + * 0.96 "1.0 beta 6" 0.96 96 0.96 [should have been 2.0.96] + * 0.97b "1.00.97 beta 7" 1.00.97 97 1.0.1 [should have been 2.0.97] + * 0.97c 0.97 97 2.0.97 + * 0.98 0.98 98 2.0.98 + * 0.99 0.99 98 2.0.99 + * 0.99a-m 0.99 99 2.0.99 + * 1.00 1.00 100 2.1.0 [100 should be 10000] + * 1.0.0 (from here on, the 100 2.1.0 [100 should be 10000] + * 1.0.1 png.h string is 10001 2.1.0 + * 1.0.1a-e identical to the 10002 from here on, the shared library + * 1.0.2 source version) 10002 is 2.V where V is the source code + * 1.0.2a-b 10003 version, except as noted. + * 1.0.3 10003 + * 1.0.3a-d 10004 + * 1.0.4 10004 + * 1.0.4a-f 10005 + * 1.0.5 (+ 2 patches) 10005 + * 1.0.5a-d 10006 + * 1.0.5e-r 10100 (not source compatible) + * 1.0.5s-v 10006 (not binary compatible) + * 1.0.6 (+ 3 patches) 10006 (still binary incompatible) + * 1.0.6d-f 10007 (still binary incompatible) + * 1.0.6g 10007 + * 1.0.6h 10007 10.6h (testing xy.z so-numbering) + * 1.0.6i 10007 10.6i + * 1.0.6j 10007 2.1.0.6j (incompatible with 1.0.0) + * 1.0.7beta11-14 DLLNUM 10007 2.1.0.7beta11-14 (binary compatible) + * 1.0.7beta15-18 1 10007 2.1.0.7beta15-18 (binary compatible) + * 1.0.7rc1-2 1 10007 2.1.0.7rc1-2 (binary compatible) + * 1.0.7 1 10007 (still compatible) + * 1.0.8beta1-4 1 10008 2.1.0.8beta1-4 + * 1.0.8rc1 1 10008 2.1.0.8rc1 + * 1.0.8 1 10008 2.1.0.8 + * 1.0.9beta1-6 1 10009 2.1.0.9beta1-6 + * 1.0.9rc1 1 10009 2.1.0.9rc1 + * 1.0.9beta7-10 1 10009 2.1.0.9beta7-10 + * 1.0.9rc2 1 10009 2.1.0.9rc2 + * 1.0.9 1 10009 2.1.0.9 + * 1.0.10beta1 1 10010 2.1.0.10beta1 + * 1.0.10rc1 1 10010 2.1.0.10rc1 + * 1.0.10 1 10010 2.1.0.10 + * 1.0.11beta1-3 1 10011 2.1.0.11beta1-3 + * 1.0.11rc1 1 10011 2.1.0.11rc1 + * 1.0.11 1 10011 2.1.0.11 + * 1.0.12beta1-2 2 10012 2.1.0.12beta1-2 + * 1.0.12rc1 2 10012 2.1.0.12rc1 + * 1.0.12 2 10012 2.1.0.12 + * 1.1.0a-f - 10100 2.1.1.0a-f (branch abandoned) + * 1.2.0beta1-2 2 10200 2.1.2.0beta1-2 + * 1.2.0beta3-5 3 10200 3.1.2.0beta3-5 + * 1.2.0rc1 3 10200 3.1.2.0rc1 + * 1.2.0 3 10200 3.1.2.0 + * 1.2.1beta1-4 3 10201 3.1.2.1beta1-4 + * 1.2.1rc1-2 3 10201 3.1.2.1rc1-2 + * 1.2.1 3 10201 3.1.2.1 + * 1.2.2beta1-6 12 10202 12.so.0.1.2.2beta1-6 + * 1.0.13beta1 10 10013 10.so.0.1.0.13beta1 + * 1.0.13rc1 10 10013 10.so.0.1.0.13rc1 + * 1.2.2rc1 12 10202 12.so.0.1.2.2rc1 + * 1.0.13 10 10013 10.so.0.1.0.13 + * 1.2.2 12 10202 12.so.0.1.2.2 + * 1.2.3rc1-6 12 10203 12.so.0.1.2.3rc1-6 + * 1.2.3 12 10203 12.so.0.1.2.3 + * 1.2.4beta1-3 13 10204 12.so.0.1.2.4beta1-3 + * 1.0.14rc1 13 10014 10.so.0.1.0.14rc1 + * 1.2.4rc1 13 10204 12.so.0.1.2.4rc1 + * 1.0.14 10 10014 10.so.0.1.0.14 + * 1.2.4 13 10204 12.so.0.1.2.4 + * 1.2.5beta1-2 13 10205 12.so.0.1.2.5beta1-2 + * 1.0.15rc1-3 10 10015 10.so.0.1.0.15rc1-3 + * 1.2.5rc1-3 13 10205 12.so.0.1.2.5rc1-3 + * 1.0.15 10 10015 10.so.0.1.0.15 + * 1.2.5 13 10205 12.so.0.1.2.5 + * 1.2.6beta1-4 13 10206 12.so.0.1.2.6beta1-4 + * 1.0.16 10 10016 10.so.0.1.0.16 + * 1.2.6 13 10206 12.so.0.1.2.6 + * 1.2.7beta1-2 13 10207 12.so.0.1.2.7beta1-2 + * 1.0.17rc1 10 10017 12.so.0.1.0.17rc1 + * 1.2.7rc1 13 10207 12.so.0.1.2.7rc1 + * 1.0.17 10 10017 12.so.0.1.0.17 + * 1.2.7 13 10207 12.so.0.1.2.7 + * 1.2.8beta1-5 13 10208 12.so.0.1.2.8beta1-5 + * 1.0.18rc1-5 10 10018 12.so.0.1.0.18rc1-5 + * 1.2.8rc1-5 13 10208 12.so.0.1.2.8rc1-5 + * 1.0.18 10 10018 12.so.0.1.0.18 + * 1.2.8 13 10208 12.so.0.1.2.8 + * 1.2.9beta1-3 13 10209 12.so.0.1.2.9beta1-3 + * 1.2.9beta4-11 13 10209 12.so.0.9[.0] + * 1.2.9rc1 13 10209 12.so.0.9[.0] + * 1.2.9 13 10209 12.so.0.9[.0] + * 1.2.10beta1-7 13 10210 12.so.0.10[.0] + * 1.2.10rc1-2 13 10210 12.so.0.10[.0] + * 1.2.10 13 10210 12.so.0.10[.0] + * 1.4.0beta1-5 14 10400 14.so.0.0[.0] + * 1.2.11beta1-4 13 10211 12.so.0.11[.0] + * 1.4.0beta7-8 14 10400 14.so.0.0[.0] + * 1.2.11 13 10211 12.so.0.11[.0] + * 1.2.12 13 10212 12.so.0.12[.0] + * 1.4.0beta9-14 14 10400 14.so.0.0[.0] + * 1.2.13 13 10213 12.so.0.13[.0] + * 1.4.0beta15-36 14 10400 14.so.0.0[.0] + * 1.4.0beta37-87 14 10400 14.so.14.0[.0] + * 1.4.0rc01 14 10400 14.so.14.0[.0] + * 1.4.0beta88-109 14 10400 14.so.14.0[.0] + * 1.4.0rc02-08 14 10400 14.so.14.0[.0] + * 1.4.0 14 10400 14.so.14.0[.0] + * 1.4.1beta01-03 14 10401 14.so.14.1[.0] + * 1.4.1rc01 14 10401 14.so.14.1[.0] + * 1.4.1beta04-12 14 10401 14.so.14.1[.0] + * 1.4.1 14 10401 14.so.14.1[.0] + * 1.4.2 14 10402 14.so.14.2[.0] + * 1.4.3 14 10403 14.so.14.3[.0] + * 1.4.4 14 10404 14.so.14.4[.0] + * 1.5.0beta01-58 15 10500 15.so.15.0[.0] + * 1.5.0rc01-07 15 10500 15.so.15.0[.0] + * 1.5.0 15 10500 15.so.15.0[.0] + * 1.5.1beta01-11 15 10501 15.so.15.1[.0] + * 1.5.1rc01-02 15 10501 15.so.15.1[.0] + * 1.5.1 15 10501 15.so.15.1[.0] + * 1.5.2beta01-03 15 10502 15.so.15.2[.0] + * 1.5.2rc01-03 15 10502 15.so.15.2[.0] + * 1.5.2 15 10502 15.so.15.2[.0] + * 1.5.3beta01-10 15 10503 15.so.15.3[.0] + * 1.5.3rc01-02 15 10503 15.so.15.3[.0] + * 1.5.3beta11 15 10503 15.so.15.3[.0] + * 1.5.3 [omitted] + * 1.5.4beta01-08 15 10504 15.so.15.4[.0] + * 1.5.4rc01 15 10504 15.so.15.4[.0] + * 1.5.4 15 10504 15.so.15.4[.0] + * 1.5.5beta01-08 15 10505 15.so.15.5[.0] + * 1.5.5rc01 15 10505 15.so.15.5[.0] + * 1.5.5 15 10505 15.so.15.5[.0] + * 1.5.6beta01-07 15 10506 15.so.15.6[.0] + * 1.5.6rc01-03 15 10506 15.so.15.6[.0] + * 1.5.6 15 10506 15.so.15.6[.0] + * 1.5.7beta01-05 15 10507 15.so.15.7[.0] + * 1.5.7rc01-03 15 10507 15.so.15.7[.0] + * 1.5.7 15 10507 15.so.15.7[.0] + * 1.6.0beta01-40 16 10600 16.so.16.0[.0] + * 1.6.0rc01-08 16 10600 16.so.16.0[.0] + * 1.6.0 16 10600 16.so.16.0[.0] + * 1.6.1beta01-09 16 10601 16.so.16.1[.0] + * 1.6.1rc01 16 10601 16.so.16.1[.0] + * 1.6.1 16 10601 16.so.16.1[.0] + * 1.6.2beta01 16 10602 16.so.16.2[.0] + * 1.6.2rc01-06 16 10602 16.so.16.2[.0] + * 1.6.2 16 10602 16.so.16.2[.0] + * + * Henceforth the source version will match the shared-library major + * and minor numbers; the shared-library major version number will be + * used for changes in backward compatibility, as it is intended. The + * PNG_LIBPNG_VER macro, which is not used within libpng but is available + * for applications, is an unsigned integer of the form xyyzz corresponding + * to the source version x.y.z (leading zeros in y and z). Beta versions + * were given the previous public release number plus a letter, until + * version 1.0.6j; from then on they were given the upcoming public + * release number plus "betaNN" or "rcNN". + * + * Binary incompatibility exists only when applications make direct access + * to the info_ptr or png_ptr members through png.h, and the compiled + * application is loaded with a different version of the library. + * + * DLLNUM will change each time there are forward or backward changes + * in binary compatibility (e.g., when a new feature is added). + * + * See libpng-manual.txt or libpng.3 for more information. The PNG + * specification is available as a W3C Recommendation and as an ISO + * Specification, defines should NOT be changed. + */ +#define PNG_INFO_gAMA 0x0001 +#define PNG_INFO_sBIT 0x0002 +#define PNG_INFO_cHRM 0x0004 +#define PNG_INFO_PLTE 0x0008 +#define PNG_INFO_tRNS 0x0010 +#define PNG_INFO_bKGD 0x0020 +#define PNG_INFO_hIST 0x0040 +#define PNG_INFO_pHYs 0x0080 +#define PNG_INFO_oFFs 0x0100 +#define PNG_INFO_tIME 0x0200 +#define PNG_INFO_pCAL 0x0400 +#define PNG_INFO_sRGB 0x0800 /* GR-P, 0.96a */ +#define PNG_INFO_iCCP 0x1000 /* ESR, 1.0.6 */ +#define PNG_INFO_sPLT 0x2000 /* ESR, 1.0.6 */ +#define PNG_INFO_sCAL 0x4000 /* ESR, 1.0.6 */ +#define PNG_INFO_IDAT 0x8000 /* ESR, 1.0.6 */ + +/* This is used for the transformation routines, as some of them + * change these values for the row. It also should enable using + * the routines for other purposes. + */ +typedef struct png_row_info_struct +{ + png_uint_32 width; /* width of row */ + png_size_t rowbytes; /* number of bytes in row */ + png_byte color_type; /* color type of row */ + png_byte bit_depth; /* bit depth of row */ + png_byte channels; /* number of channels (1, 2, 3, or 4) */ + png_byte pixel_depth; /* bits per pixel (depth * channels) */ +} png_row_info; + +typedef png_row_info * png_row_infop; +typedef png_row_info * * png_row_infopp; + +/* These are the function types for the I/O functions and for the functions + * that allow the user to override the default I/O functions with his or her + * own. The png_error_ptr type should match that of user-supplied warning + * and error functions, while the png_rw_ptr type should match that of the + * user read/write data functions. Note that the 'write' function must not + * modify the buffer it is passed. The 'read' function, on the other hand, is + * expected to return the read data in the buffer. + */ +typedef PNG_CALLBACK(void, *png_error_ptr, (png_structp, png_const_charp)); +typedef PNG_CALLBACK(void, *png_rw_ptr, (png_structp, png_bytep, png_size_t)); +typedef PNG_CALLBACK(void, *png_flush_ptr, (png_structp)); +typedef PNG_CALLBACK(void, *png_read_status_ptr, (png_structp, png_uint_32, + int)); +typedef PNG_CALLBACK(void, *png_write_status_ptr, (png_structp, png_uint_32, + int)); + +#ifdef PNG_PROGRESSIVE_READ_SUPPORTED +typedef PNG_CALLBACK(void, *png_progressive_info_ptr, (png_structp, png_infop)); +typedef PNG_CALLBACK(void, *png_progressive_end_ptr, (png_structp, png_infop)); + +/* The following callback receives png_uint_32 row_number, int pass for the + * png_bytep data of the row. When transforming an interlaced image the + * row number is the row number within the sub-image of the interlace pass, so + * the value will increase to the height of the sub-image (not the full image) + * then reset to 0 for the next pass. + * + * Use PNG_ROW_FROM_PASS_ROW(row, pass) and PNG_COL_FROM_PASS_COL(col, pass) to + * find the output pixel (x,y) given an interlaced sub-image pixel + * (row,col,pass). (See below for these macros.) + */ +typedef PNG_CALLBACK(void, *png_progressive_row_ptr, (png_structp, png_bytep, + png_uint_32, int)); +#endif + +#if defined(PNG_READ_USER_TRANSFORM_SUPPORTED) || \ + defined(PNG_WRITE_USER_TRANSFORM_SUPPORTED) +typedef PNG_CALLBACK(void, *png_user_transform_ptr, (png_structp, png_row_infop, + png_bytep)); +#endif + +#ifdef PNG_USER_CHUNKS_SUPPORTED +typedef PNG_CALLBACK(int, *png_user_chunk_ptr, (png_structp, + png_unknown_chunkp)); +#endif +#ifdef PNG_UNKNOWN_CHUNKS_SUPPORTED +/* not used anywhere */ +/* typedef PNG_CALLBACK(void, *png_unknown_chunk_ptr, (png_structp)); */ +#endif + +#ifdef PNG_SETJMP_SUPPORTED +/* This must match the function definition in , and the application + * must include this before png.h to obtain the definition of jmp_buf. The + * function is required to be PNG_NORETURN, but this is not checked. If the + * function does return the application will crash via an abort() or similar + * system level call. + * + * If you get a warning here while building the library you may need to make + * changes to ensure that pnglibconf.h records the calling convention used by + * your compiler. This may be very difficult - try using a different compiler + * to build the library! + */ +PNG_FUNCTION(void, (PNGCAPI *png_longjmp_ptr), PNGARG((jmp_buf, int)), typedef); +#endif + +/* Transform masks for the high-level interface */ +#define PNG_TRANSFORM_IDENTITY 0x0000 /* read and write */ +#define PNG_TRANSFORM_STRIP_16 0x0001 /* read only */ +#define PNG_TRANSFORM_STRIP_ALPHA 0x0002 /* read only */ +#define PNG_TRANSFORM_PACKING 0x0004 /* read and write */ +#define PNG_TRANSFORM_PACKSWAP 0x0008 /* read and write */ +#define PNG_TRANSFORM_EXPAND 0x0010 /* read only */ +#define PNG_TRANSFORM_INVERT_MONO 0x0020 /* read and write */ +#define PNG_TRANSFORM_SHIFT 0x0040 /* read and write */ +#define PNG_TRANSFORM_BGR 0x0080 /* read and write */ +#define PNG_TRANSFORM_SWAP_ALPHA 0x0100 /* read and write */ +#define PNG_TRANSFORM_SWAP_ENDIAN 0x0200 /* read and write */ +#define PNG_TRANSFORM_INVERT_ALPHA 0x0400 /* read and write */ +#define PNG_TRANSFORM_STRIP_FILLER 0x0800 /* write only */ +/* Added to libpng-1.2.34 */ +#define PNG_TRANSFORM_STRIP_FILLER_BEFORE PNG_TRANSFORM_STRIP_FILLER +#define PNG_TRANSFORM_STRIP_FILLER_AFTER 0x1000 /* write only */ +/* Added to libpng-1.4.0 */ +#define PNG_TRANSFORM_GRAY_TO_RGB 0x2000 /* read only */ +/* Added to libpng-1.5.4 */ +#define PNG_TRANSFORM_EXPAND_16 0x4000 /* read only */ +#define PNG_TRANSFORM_SCALE_16 0x8000 /* read only */ + +/* Flags for MNG supported features */ +#define PNG_FLAG_MNG_EMPTY_PLTE 0x01 +#define PNG_FLAG_MNG_FILTER_64 0x04 +#define PNG_ALL_MNG_FEATURES 0x05 + +/* NOTE: prior to 1.5 these functions had no 'API' style declaration, + * this allowed the zlib default functions to be used on Windows + * platforms. In 1.5 the zlib default malloc (which just calls malloc and + * ignores the first argument) should be completely compatible with the + * following. + */ +typedef PNG_CALLBACK(png_voidp, *png_malloc_ptr, (png_structp, + png_alloc_size_t)); +typedef PNG_CALLBACK(void, *png_free_ptr, (png_structp, png_voidp)); + +/* Section 3: exported functions + * Here are the function definitions most commonly used. This is not + * the place to find out how to use libpng. See libpng-manual.txt for the + * full explanation, see example.c for the summary. This just provides + * a simple one line description of the use of each function. + * + * The PNG_EXPORT() and PNG_EXPORTA() macros used below are defined in + * pngconf.h and in the *.dfn files in the scripts directory. + * + * PNG_EXPORT(ordinal, type, name, (args)); + * + * ordinal: ordinal that is used while building + * *.def files. The ordinal value is only + * relevant when preprocessing png.h with + * the *.dfn files for building symbol table + * entries, and are removed by pngconf.h. + * type: return type of the function + * name: function name + * args: function arguments, with types + * + * When we wish to append attributes to a function prototype we use + * the PNG_EXPORTA() macro instead. + * + * PNG_EXPORTA(ordinal, type, name, (args), attributes); + * + * ordinal, type, name, and args: same as in PNG_EXPORT(). + * attributes: function attributes + */ + +/* Returns the version number of the library */ +PNG_EXPORT(1, png_uint_32, png_access_version_number, (void)); + +/* Tell lib we have already handled the first magic bytes. + * Handling more than 8 bytes from the beginning of the file is an error. + */ +PNG_EXPORT(2, void, png_set_sig_bytes, (png_structrp png_ptr, int num_bytes)); + +/* Check sig[start] through sig[start + num_to_check - 1] to see if it's a + * PNG file. Returns zero if the supplied bytes match the 8-byte PNG + * signature, and non-zero otherwise. Having num_to_check == 0 or + * start > 7 will always fail (ie return non-zero). + */ +PNG_EXPORT(3, int, png_sig_cmp, (png_const_bytep sig, png_size_t start, + png_size_t num_to_check)); + +/* Simple signature checking function. This is the same as calling + * png_check_sig(sig, n) := !png_sig_cmp(sig, 0, n). + */ +#define png_check_sig(sig, n) !png_sig_cmp((sig), 0, (n)) + +/* Allocate and initialize png_ptr struct for reading, and any other memory. */ +PNG_EXPORTA(4, png_structp, png_create_read_struct, + (png_const_charp user_png_ver, png_voidp error_ptr, + png_error_ptr error_fn, png_error_ptr warn_fn), + PNG_ALLOCATED); + +/* Allocate and initialize png_ptr struct for writing, and any other memory */ +PNG_EXPORTA(5, png_structp, png_create_write_struct, + (png_const_charp user_png_ver, png_voidp error_ptr, png_error_ptr error_fn, + png_error_ptr warn_fn), + PNG_ALLOCATED); + +PNG_EXPORT(6, png_size_t, png_get_compression_buffer_size, + (png_const_structrp png_ptr)); + +PNG_EXPORT(7, void, png_set_compression_buffer_size, (png_structrp png_ptr, + png_size_t size)); + +/* Moved from pngconf.h in 1.4.0 and modified to ensure setjmp/longjmp + * match up. + */ +#ifdef PNG_SETJMP_SUPPORTED +/* This function returns the jmp_buf built in to *png_ptr. It must be + * supplied with an appropriate 'longjmp' function to use on that jmp_buf + * unless the default error function is overridden in which case NULL is + * acceptable. The size of the jmp_buf is checked against the actual size + * allocated by the library - the call will return NULL on a mismatch + * indicating an ABI mismatch. + */ +PNG_EXPORT(8, jmp_buf*, png_set_longjmp_fn, (png_structrp png_ptr, + png_longjmp_ptr longjmp_fn, size_t jmp_buf_size)); +# define png_jmpbuf(png_ptr) \ + (*png_set_longjmp_fn((png_ptr), longjmp, (sizeof (jmp_buf)))) +#else +# define png_jmpbuf(png_ptr) \ + (LIBPNG_WAS_COMPILED_WITH__PNG_NO_SETJMP) +#endif +/* This function should be used by libpng applications in place of + * longjmp(png_ptr->jmpbuf, val). If longjmp_fn() has been set, it + * will use it; otherwise it will call PNG_ABORT(). This function was + * added in libpng-1.5.0. + */ +PNG_EXPORTA(9, void, png_longjmp, (png_const_structrp png_ptr, int val), + PNG_NORETURN); + +#ifdef PNG_READ_SUPPORTED +/* Reset the compression stream */ +PNG_EXPORTA(10, int, png_reset_zstream, (png_structrp png_ptr), PNG_DEPRECATED); +#endif + +/* New functions added in libpng-1.0.2 (not enabled by default until 1.2.0) */ +#ifdef PNG_USER_MEM_SUPPORTED +PNG_EXPORTA(11, png_structp, png_create_read_struct_2, + (png_const_charp user_png_ver, png_voidp error_ptr, png_error_ptr error_fn, + png_error_ptr warn_fn, + png_voidp mem_ptr, png_malloc_ptr malloc_fn, png_free_ptr free_fn), + PNG_ALLOCATED); +PNG_EXPORTA(12, png_structp, png_create_write_struct_2, + (png_const_charp user_png_ver, png_voidp error_ptr, png_error_ptr error_fn, + png_error_ptr warn_fn, + png_voidp mem_ptr, png_malloc_ptr malloc_fn, png_free_ptr free_fn), + PNG_ALLOCATED); +#endif + +/* Write the PNG file signature. */ +PNG_EXPORT(13, void, png_write_sig, (png_structrp png_ptr)); + +/* Write a PNG chunk - size, type, (optional) data, CRC. */ +PNG_EXPORT(14, void, png_write_chunk, (png_structrp png_ptr, png_const_bytep + chunk_name, png_const_bytep data, png_size_t length)); + +/* Write the start of a PNG chunk - length and chunk name. */ +PNG_EXPORT(15, void, png_write_chunk_start, (png_structrp png_ptr, + png_const_bytep chunk_name, png_uint_32 length)); + +/* Write the data of a PNG chunk started with png_write_chunk_start(). */ +PNG_EXPORT(16, void, png_write_chunk_data, (png_structrp png_ptr, + png_const_bytep data, png_size_t length)); + +/* Finish a chunk started with png_write_chunk_start() (includes CRC). */ +PNG_EXPORT(17, void, png_write_chunk_end, (png_structrp png_ptr)); + +/* Allocate and initialize the info structure */ +PNG_EXPORTA(18, png_infop, png_create_info_struct, (png_const_structrp png_ptr), + PNG_ALLOCATED); + +/* DEPRECATED: this function allowed init structures to be created using the + * default allocation method (typically malloc). Use is deprecated in 1.6.0 and + * the API will be removed in the future. + */ +PNG_EXPORTA(19, void, png_info_init_3, (png_infopp info_ptr, + png_size_t png_info_struct_size), PNG_DEPRECATED); + +/* Writes all the PNG information before the image. */ +PNG_EXPORT(20, void, png_write_info_before_PLTE, + (png_structrp png_ptr, png_const_inforp info_ptr)); +PNG_EXPORT(21, void, png_write_info, + (png_structrp png_ptr, png_const_inforp info_ptr)); + +#ifdef PNG_SEQUENTIAL_READ_SUPPORTED +/* Read the information before the actual image data. */ +PNG_EXPORT(22, void, png_read_info, + (png_structrp png_ptr, png_inforp info_ptr)); +#endif + +#ifdef PNG_TIME_RFC1123_SUPPORTED + /* Convert to a US string format: there is no localization support in this + * routine. The original implementation used a 29 character buffer in + * png_struct, this will be removed in future versions. + */ +#if PNG_LIBPNG_VER < 10700 +/* To do: remove this from libpng17 (and from libpng17/png.c and pngstruct.h) */ +PNG_EXPORTA(23, png_const_charp, png_convert_to_rfc1123, (png_structrp png_ptr, + png_const_timep ptime),PNG_DEPRECATED); +#endif +PNG_EXPORT(241, int, png_convert_to_rfc1123_buffer, (char out[29], + png_const_timep ptime)); +#endif + +#ifdef PNG_CONVERT_tIME_SUPPORTED +/* Convert from a struct tm to png_time */ +PNG_EXPORT(24, void, png_convert_from_struct_tm, (png_timep ptime, + const struct tm * ttime)); + +/* Convert from time_t to png_time. Uses gmtime() */ +PNG_EXPORT(25, void, png_convert_from_time_t, (png_timep ptime, time_t ttime)); +#endif /* PNG_CONVERT_tIME_SUPPORTED */ + +#ifdef PNG_READ_EXPAND_SUPPORTED +/* Expand data to 24-bit RGB, or 8-bit grayscale, with alpha if available. */ +PNG_EXPORT(26, void, png_set_expand, (png_structrp png_ptr)); +PNG_EXPORT(27, void, png_set_expand_gray_1_2_4_to_8, (png_structrp png_ptr)); +PNG_EXPORT(28, void, png_set_palette_to_rgb, (png_structrp png_ptr)); +PNG_EXPORT(29, void, png_set_tRNS_to_alpha, (png_structrp png_ptr)); +#endif + +#ifdef PNG_READ_EXPAND_16_SUPPORTED +/* Expand to 16-bit channels, forces conversion of palette to RGB and expansion + * of a tRNS chunk if present. + */ +PNG_EXPORT(221, void, png_set_expand_16, (png_structrp png_ptr)); +#endif + +#if defined(PNG_READ_BGR_SUPPORTED) || defined(PNG_WRITE_BGR_SUPPORTED) +/* Use blue, green, red order for pixels. */ +PNG_EXPORT(30, void, png_set_bgr, (png_structrp png_ptr)); +#endif + +#ifdef PNG_READ_GRAY_TO_RGB_SUPPORTED +/* Expand the grayscale to 24-bit RGB if necessary. */ +PNG_EXPORT(31, void, png_set_gray_to_rgb, (png_structrp png_ptr)); +#endif + +#ifdef PNG_READ_RGB_TO_GRAY_SUPPORTED +/* Reduce RGB to grayscale. */ +#define PNG_ERROR_ACTION_NONE 1 +#define PNG_ERROR_ACTION_WARN 2 +#define PNG_ERROR_ACTION_ERROR 3 +#define PNG_RGB_TO_GRAY_DEFAULT (-1)/*for red/green coefficients*/ + +PNG_FP_EXPORT(32, void, png_set_rgb_to_gray, (png_structrp png_ptr, + int error_action, double red, double green)) +PNG_FIXED_EXPORT(33, void, png_set_rgb_to_gray_fixed, (png_structrp png_ptr, + int error_action, png_fixed_point red, png_fixed_point green)) + +PNG_EXPORT(34, png_byte, png_get_rgb_to_gray_status, (png_const_structrp + png_ptr)); +#endif + +#ifdef PNG_BUILD_GRAYSCALE_PALETTE_SUPPORTED +PNG_EXPORT(35, void, png_build_grayscale_palette, (int bit_depth, + png_colorp palette)); +#endif + +#ifdef PNG_READ_ALPHA_MODE_SUPPORTED +/* How the alpha channel is interpreted - this affects how the color channels of + * a PNG file are returned when an alpha channel, or tRNS chunk in a palette + * file, is present. + * + * This has no effect on the way pixels are written into a PNG output + * datastream. The color samples in a PNG datastream are never premultiplied + * with the alpha samples. + * + * The default is to return data according to the PNG specification: the alpha + * channel is a linear measure of the contribution of the pixel to the + * corresponding composited pixel. The gamma encoded color channels must be + * scaled according to the contribution and to do this it is necessary to undo + * the encoding, scale the color values, perform the composition and reencode + * the values. This is the 'PNG' mode. + * + * The alternative is to 'associate' the alpha with the color information by + * storing color channel values that have been scaled by the alpha. The + * advantage is that the color channels can be resampled (the image can be + * scaled) in this form. The disadvantage is that normal practice is to store + * linear, not (gamma) encoded, values and this requires 16-bit channels for + * still images rather than the 8-bit channels that are just about sufficient if + * gamma encoding is used. In addition all non-transparent pixel values, + * including completely opaque ones, must be gamma encoded to produce the final + * image. This is the 'STANDARD', 'ASSOCIATED' or 'PREMULTIPLIED' mode (the + * latter being the two common names for associated alpha color channels.) + * + * Since it is not necessary to perform arithmetic on opaque color values so + * long as they are not to be resampled and are in the final color space it is + * possible to optimize the handling of alpha by storing the opaque pixels in + * the PNG format (adjusted for the output color space) while storing partially + * opaque pixels in the standard, linear, format. The accuracy required for + * standard alpha composition is relatively low, because the pixels are + * isolated, therefore typically the accuracy loss in storing 8-bit linear + * values is acceptable. (This is not true if the alpha channel is used to + * simulate transparency over large areas - use 16 bits or the PNG mode in + * this case!) This is the 'OPTIMIZED' mode. For this mode a pixel is + * treated as opaque only if the alpha value is equal to the maximum value. + * + * The final choice is to gamma encode the alpha channel as well. This is + * broken because, in practice, no implementation that uses this choice + * correctly undoes the encoding before handling alpha composition. Use this + * choice only if other serious errors in the software or hardware you use + * mandate it; the typical serious error is for dark halos to appear around + * opaque areas of the composited PNG image because of arithmetic overflow. + * + * The API function png_set_alpha_mode specifies which of these choices to use + * with an enumerated 'mode' value and the gamma of the required output: + */ +#define PNG_ALPHA_PNG 0 /* according to the PNG standard */ +#define PNG_ALPHA_STANDARD 1 /* according to Porter/Duff */ +#define PNG_ALPHA_ASSOCIATED 1 /* as above; this is the normal practice */ +#define PNG_ALPHA_PREMULTIPLIED 1 /* as above */ +#define PNG_ALPHA_OPTIMIZED 2 /* 'PNG' for opaque pixels, else 'STANDARD' */ +#define PNG_ALPHA_BROKEN 3 /* the alpha channel is gamma encoded */ + +PNG_FP_EXPORT(227, void, png_set_alpha_mode, (png_structrp png_ptr, int mode, + double output_gamma)) +PNG_FIXED_EXPORT(228, void, png_set_alpha_mode_fixed, (png_structrp png_ptr, + int mode, png_fixed_point output_gamma)) +#endif + +#if defined(PNG_GAMMA_SUPPORTED) || defined(PNG_READ_ALPHA_MODE_SUPPORTED) +/* The output_gamma value is a screen gamma in libpng terminology: it expresses + * how to decode the output values, not how they are encoded. The values used + * correspond to the normal numbers used to describe the overall gamma of a + * computer display system; for example 2.2 for an sRGB conformant system. The + * values are scaled by 100000 in the _fixed version of the API (so 220000 for + * sRGB.) + * + * The inverse of the value is always used to provide a default for the PNG file + * encoding if it has no gAMA chunk and if png_set_gamma() has not been called + * to override the PNG gamma information. + * + * When the ALPHA_OPTIMIZED mode is selected the output gamma is used to encode + * opaque pixels however pixels with lower alpha values are not encoded, + * regardless of the output gamma setting. + * + * When the standard Porter Duff handling is requested with mode 1 the output + * encoding is set to be linear and the output_gamma value is only relevant + * as a default for input data that has no gamma information. The linear output + * encoding will be overridden if png_set_gamma() is called - the results may be + * highly unexpected! + * + * The following numbers are derived from the sRGB standard and the research + * behind it. sRGB is defined to be approximated by a PNG gAMA chunk value of + * 0.45455 (1/2.2) for PNG. The value implicitly includes any viewing + * correction required to take account of any differences in the color + * environment of the original scene and the intended display environment; the + * value expresses how to *decode* the image for display, not how the original + * data was *encoded*. + * + * sRGB provides a peg for the PNG standard by defining a viewing environment. + * sRGB itself, and earlier TV standards, actually use a more complex transform + * (a linear portion then a gamma 2.4 power law) than PNG can express. (PNG is + * limited to simple power laws.) By saying that an image for direct display on + * an sRGB conformant system should be stored with a gAMA chunk value of 45455 + * (11.3.3.2 and 11.3.3.5 of the ISO PNG specification) the PNG specification + * makes it possible to derive values for other display systems and + * environments. + * + * The Mac value is deduced from the sRGB based on an assumption that the actual + * extra viewing correction used in early Mac display systems was implemented as + * a power 1.45 lookup table. + * + * Any system where a programmable lookup table is used or where the behavior of + * the final display device characteristics can be changed requires system + * specific code to obtain the current characteristic. However this can be + * difficult and most PNG gamma correction only requires an approximate value. + * + * By default, if png_set_alpha_mode() is not called, libpng assumes that all + * values are unencoded, linear, values and that the output device also has a + * linear characteristic. This is only very rarely correct - it is invariably + * better to call png_set_alpha_mode() with PNG_DEFAULT_sRGB than rely on the + * default if you don't know what the right answer is! + * + * The special value PNG_GAMMA_MAC_18 indicates an older Mac system (pre Mac OS + * 10.6) which used a correction table to implement a somewhat lower gamma on an + * otherwise sRGB system. + * + * Both these values are reserved (not simple gamma values) in order to allow + * more precise correction internally in the future. + * + * NOTE: the following values can be passed to either the fixed or floating + * point APIs, but the floating point API will also accept floating point + * values. + */ +#define PNG_DEFAULT_sRGB -1 /* sRGB gamma and color space */ +#define PNG_GAMMA_MAC_18 -2 /* Old Mac '1.8' gamma and color space */ +#define PNG_GAMMA_sRGB 220000 /* Television standards--matches sRGB gamma */ +#define PNG_GAMMA_LINEAR PNG_FP_1 /* Linear */ +#endif + +/* The following are examples of calls to png_set_alpha_mode to achieve the + * required overall gamma correction and, where necessary, alpha + * premultiplication. + * + * png_set_alpha_mode(pp, PNG_ALPHA_PNG, PNG_DEFAULT_sRGB); + * This is the default libpng handling of the alpha channel - it is not + * pre-multiplied into the color components. In addition the call states + * that the output is for a sRGB system and causes all PNG files without gAMA + * chunks to be assumed to be encoded using sRGB. + * + * png_set_alpha_mode(pp, PNG_ALPHA_PNG, PNG_GAMMA_MAC); + * In this case the output is assumed to be something like an sRGB conformant + * display preceeded by a power-law lookup table of power 1.45. This is how + * early Mac systems behaved. + * + * png_set_alpha_mode(pp, PNG_ALPHA_STANDARD, PNG_GAMMA_LINEAR); + * This is the classic Jim Blinn approach and will work in academic + * environments where everything is done by the book. It has the shortcoming + * of assuming that input PNG data with no gamma information is linear - this + * is unlikely to be correct unless the PNG files where generated locally. + * Most of the time the output precision will be so low as to show + * significant banding in dark areas of the image. + * + * png_set_expand_16(pp); + * png_set_alpha_mode(pp, PNG_ALPHA_STANDARD, PNG_DEFAULT_sRGB); + * This is a somewhat more realistic Jim Blinn inspired approach. PNG files + * are assumed to have the sRGB encoding if not marked with a gamma value and + * the output is always 16 bits per component. This permits accurate scaling + * and processing of the data. If you know that your input PNG files were + * generated locally you might need to replace PNG_DEFAULT_sRGB with the + * correct value for your system. + * + * png_set_alpha_mode(pp, PNG_ALPHA_OPTIMIZED, PNG_DEFAULT_sRGB); + * If you just need to composite the PNG image onto an existing background + * and if you control the code that does this you can use the optimization + * setting. In this case you just copy completely opaque pixels to the + * output. For pixels that are not completely transparent (you just skip + * those) you do the composition math using png_composite or png_composite_16 + * below then encode the resultant 8-bit or 16-bit values to match the output + * encoding. + * + * Other cases + * If neither the PNG nor the standard linear encoding work for you because + * of the software or hardware you use then you have a big problem. The PNG + * case will probably result in halos around the image. The linear encoding + * will probably result in a washed out, too bright, image (it's actually too + * contrasty.) Try the ALPHA_OPTIMIZED mode above - this will probably + * substantially reduce the halos. Alternatively try: + * + * png_set_alpha_mode(pp, PNG_ALPHA_BROKEN, PNG_DEFAULT_sRGB); + * This option will also reduce the halos, but there will be slight dark + * halos round the opaque parts of the image where the background is light. + * In the OPTIMIZED mode the halos will be light halos where the background + * is dark. Take your pick - the halos are unavoidable unless you can get + * your hardware/software fixed! (The OPTIMIZED approach is slightly + * faster.) + * + * When the default gamma of PNG files doesn't match the output gamma. + * If you have PNG files with no gamma information png_set_alpha_mode allows + * you to provide a default gamma, but it also sets the ouput gamma to the + * matching value. If you know your PNG files have a gamma that doesn't + * match the output you can take advantage of the fact that + * png_set_alpha_mode always sets the output gamma but only sets the PNG + * default if it is not already set: + * + * png_set_alpha_mode(pp, PNG_ALPHA_PNG, PNG_DEFAULT_sRGB); + * png_set_alpha_mode(pp, PNG_ALPHA_PNG, PNG_GAMMA_MAC); + * The first call sets both the default and the output gamma values, the + * second call overrides the output gamma without changing the default. This + * is easier than achieving the same effect with png_set_gamma. You must use + * PNG_ALPHA_PNG for the first call - internal checking in png_set_alpha will + * fire if more than one call to png_set_alpha_mode and png_set_background is + * made in the same read operation, however multiple calls with PNG_ALPHA_PNG + * are ignored. + */ + +#ifdef PNG_READ_STRIP_ALPHA_SUPPORTED +PNG_EXPORT(36, void, png_set_strip_alpha, (png_structrp png_ptr)); +#endif + +#if defined(PNG_READ_SWAP_ALPHA_SUPPORTED) || \ + defined(PNG_WRITE_SWAP_ALPHA_SUPPORTED) +PNG_EXPORT(37, void, png_set_swap_alpha, (png_structrp png_ptr)); +#endif + +#if defined(PNG_READ_INVERT_ALPHA_SUPPORTED) || \ + defined(PNG_WRITE_INVERT_ALPHA_SUPPORTED) +PNG_EXPORT(38, void, png_set_invert_alpha, (png_structrp png_ptr)); +#endif + +#if defined(PNG_READ_FILLER_SUPPORTED) || defined(PNG_WRITE_FILLER_SUPPORTED) +/* Add a filler byte to 8-bit Gray or 24-bit RGB images. */ +PNG_EXPORT(39, void, png_set_filler, (png_structrp png_ptr, png_uint_32 filler, + int flags)); +/* The values of the PNG_FILLER_ defines should NOT be changed */ +# define PNG_FILLER_BEFORE 0 +# define PNG_FILLER_AFTER 1 +/* Add an alpha byte to 8-bit Gray or 24-bit RGB images. */ +PNG_EXPORT(40, void, png_set_add_alpha, (png_structrp png_ptr, + png_uint_32 filler, int flags)); +#endif /* PNG_READ_FILLER_SUPPORTED || PNG_WRITE_FILLER_SUPPORTED */ + +#if defined(PNG_READ_SWAP_SUPPORTED) || defined(PNG_WRITE_SWAP_SUPPORTED) +/* Swap bytes in 16-bit depth files. */ +PNG_EXPORT(41, void, png_set_swap, (png_structrp png_ptr)); +#endif + +#if defined(PNG_READ_PACK_SUPPORTED) || defined(PNG_WRITE_PACK_SUPPORTED) +/* Use 1 byte per pixel in 1, 2, or 4-bit depth files. */ +PNG_EXPORT(42, void, png_set_packing, (png_structrp png_ptr)); +#endif + +#if defined(PNG_READ_PACKSWAP_SUPPORTED) || \ + defined(PNG_WRITE_PACKSWAP_SUPPORTED) +/* Swap packing order of pixels in bytes. */ +PNG_EXPORT(43, void, png_set_packswap, (png_structrp png_ptr)); +#endif + +#if defined(PNG_READ_SHIFT_SUPPORTED) || defined(PNG_WRITE_SHIFT_SUPPORTED) +/* Converts files to legal bit depths. */ +PNG_EXPORT(44, void, png_set_shift, (png_structrp png_ptr, png_const_color_8p + true_bits)); +#endif + +#if defined(PNG_READ_INTERLACING_SUPPORTED) || \ + defined(PNG_WRITE_INTERLACING_SUPPORTED) +/* Have the code handle the interlacing. Returns the number of passes. + * MUST be called before png_read_update_info or png_start_read_image, + * otherwise it will not have the desired effect. Note that it is still + * necessary to call png_read_row or png_read_rows png_get_image_height + * times for each pass. +*/ +PNG_EXPORT(45, int, png_set_interlace_handling, (png_structrp png_ptr)); +#endif + +#if defined(PNG_READ_INVERT_SUPPORTED) || defined(PNG_WRITE_INVERT_SUPPORTED) +/* Invert monochrome files */ +PNG_EXPORT(46, void, png_set_invert_mono, (png_structrp png_ptr)); +#endif + +#ifdef PNG_READ_BACKGROUND_SUPPORTED +/* Handle alpha and tRNS by replacing with a background color. Prior to + * libpng-1.5.4 this API must not be called before the PNG file header has been + * read. Doing so will result in unexpected behavior and possible warnings or + * errors if the PNG file contains a bKGD chunk. + */ +PNG_FP_EXPORT(47, void, png_set_background, (png_structrp png_ptr, + png_const_color_16p background_color, int background_gamma_code, + int need_expand, double background_gamma)) +PNG_FIXED_EXPORT(215, void, png_set_background_fixed, (png_structrp png_ptr, + png_const_color_16p background_color, int background_gamma_code, + int need_expand, png_fixed_point background_gamma)) +#endif +#ifdef PNG_READ_BACKGROUND_SUPPORTED +# define PNG_BACKGROUND_GAMMA_UNKNOWN 0 +# define PNG_BACKGROUND_GAMMA_SCREEN 1 +# define PNG_BACKGROUND_GAMMA_FILE 2 +# define PNG_BACKGROUND_GAMMA_UNIQUE 3 +#endif + +#ifdef PNG_READ_SCALE_16_TO_8_SUPPORTED +/* Scale a 16-bit depth file down to 8-bit, accurately. */ +PNG_EXPORT(229, void, png_set_scale_16, (png_structrp png_ptr)); +#endif + +#ifdef PNG_READ_STRIP_16_TO_8_SUPPORTED +#define PNG_READ_16_TO_8 SUPPORTED /* Name prior to 1.5.4 */ +/* Strip the second byte of information from a 16-bit depth file. */ +PNG_EXPORT(48, void, png_set_strip_16, (png_structrp png_ptr)); +#endif + +#ifdef PNG_READ_QUANTIZE_SUPPORTED +/* Turn on quantizing, and reduce the palette to the number of colors + * available. + */ +PNG_EXPORT(49, void, png_set_quantize, (png_structrp png_ptr, + png_colorp palette, int num_palette, int maximum_colors, + png_const_uint_16p histogram, int full_quantize)); +#endif + +#ifdef PNG_READ_GAMMA_SUPPORTED +/* The threshold on gamma processing is configurable but hard-wired into the + * library. The following is the floating point variant. + */ +#define PNG_GAMMA_THRESHOLD (PNG_GAMMA_THRESHOLD_FIXED*.00001) + +/* Handle gamma correction. Screen_gamma=(display_exponent). + * NOTE: this API simply sets the screen and file gamma values. It will + * therefore override the value for gamma in a PNG file if it is called after + * the file header has been read - use with care - call before reading the PNG + * file for best results! + * + * These routines accept the same gamma values as png_set_alpha_mode (described + * above). The PNG_GAMMA_ defines and PNG_DEFAULT_sRGB can be passed to either + * API (floating point or fixed.) Notice, however, that the 'file_gamma' value + * is the inverse of a 'screen gamma' value. + */ +PNG_FP_EXPORT(50, void, png_set_gamma, (png_structrp png_ptr, + double screen_gamma, double override_file_gamma)) +PNG_FIXED_EXPORT(208, void, png_set_gamma_fixed, (png_structrp png_ptr, + png_fixed_point screen_gamma, png_fixed_point override_file_gamma)) +#endif + +#ifdef PNG_WRITE_FLUSH_SUPPORTED +/* Set how many lines between output flushes - 0 for no flushing */ +PNG_EXPORT(51, void, png_set_flush, (png_structrp png_ptr, int nrows)); +/* Flush the current PNG output buffer */ +PNG_EXPORT(52, void, png_write_flush, (png_structrp png_ptr)); +#endif + +/* Optional update palette with requested transformations */ +PNG_EXPORT(53, void, png_start_read_image, (png_structrp png_ptr)); + +/* Optional call to update the users info structure */ +PNG_EXPORT(54, void, png_read_update_info, (png_structrp png_ptr, + png_inforp info_ptr)); + +#ifdef PNG_SEQUENTIAL_READ_SUPPORTED +/* Read one or more rows of image data. */ +PNG_EXPORT(55, void, png_read_rows, (png_structrp png_ptr, png_bytepp row, + png_bytepp display_row, png_uint_32 num_rows)); +#endif + +#ifdef PNG_SEQUENTIAL_READ_SUPPORTED +/* Read a row of data. */ +PNG_EXPORT(56, void, png_read_row, (png_structrp png_ptr, png_bytep row, + png_bytep display_row)); +#endif + +#ifdef PNG_SEQUENTIAL_READ_SUPPORTED +/* Read the whole image into memory at once. */ +PNG_EXPORT(57, void, png_read_image, (png_structrp png_ptr, png_bytepp image)); +#endif + +/* Write a row of image data */ +PNG_EXPORT(58, void, png_write_row, (png_structrp png_ptr, + png_const_bytep row)); + +/* Write a few rows of image data: (*row) is not written; however, the type + * is declared as writeable to maintain compatibility with previous versions + * of libpng and to allow the 'display_row' array from read_rows to be passed + * unchanged to write_rows. + */ +PNG_EXPORT(59, void, png_write_rows, (png_structrp png_ptr, png_bytepp row, + png_uint_32 num_rows)); + +/* Write the image data */ +PNG_EXPORT(60, void, png_write_image, (png_structrp png_ptr, png_bytepp image)); + +/* Write the end of the PNG file. */ +PNG_EXPORT(61, void, png_write_end, (png_structrp png_ptr, + png_inforp info_ptr)); + +#ifdef PNG_SEQUENTIAL_READ_SUPPORTED +/* Read the end of the PNG file. */ +PNG_EXPORT(62, void, png_read_end, (png_structrp png_ptr, png_inforp info_ptr)); +#endif + +/* Free any memory associated with the png_info_struct */ +PNG_EXPORT(63, void, png_destroy_info_struct, (png_const_structrp png_ptr, + png_infopp info_ptr_ptr)); + +/* Free any memory associated with the png_struct and the png_info_structs */ +PNG_EXPORT(64, void, png_destroy_read_struct, (png_structpp png_ptr_ptr, + png_infopp info_ptr_ptr, png_infopp end_info_ptr_ptr)); + +/* Free any memory associated with the png_struct and the png_info_structs */ +PNG_EXPORT(65, void, png_destroy_write_struct, (png_structpp png_ptr_ptr, + png_infopp info_ptr_ptr)); + +/* Set the libpng method of handling chunk CRC errors */ +PNG_EXPORT(66, void, png_set_crc_action, (png_structrp png_ptr, int crit_action, + int ancil_action)); + +/* Values for png_set_crc_action() say how to handle CRC errors in + * ancillary and critical chunks, and whether to use the data contained + * therein. Note that it is impossible to "discard" data in a critical + * chunk. For versions prior to 0.90, the action was always error/quit, + * whereas in version 0.90 and later, the action for CRC errors in ancillary + * chunks is warn/discard. These values should NOT be changed. + * + * value action:critical action:ancillary + */ +#define PNG_CRC_DEFAULT 0 /* error/quit warn/discard data */ +#define PNG_CRC_ERROR_QUIT 1 /* error/quit error/quit */ +#define PNG_CRC_WARN_DISCARD 2 /* (INVALID) warn/discard data */ +#define PNG_CRC_WARN_USE 3 /* warn/use data warn/use data */ +#define PNG_CRC_QUIET_USE 4 /* quiet/use data quiet/use data */ +#define PNG_CRC_NO_CHANGE 5 /* use current value use current value */ + +/* These functions give the user control over the scan-line filtering in + * libpng and the compression methods used by zlib. These functions are + * mainly useful for testing, as the defaults should work with most users. + * Those users who are tight on memory or want faster performance at the + * expense of compression can modify them. See the compression library + * header file (zlib.h) for an explination of the compression functions. + */ + +/* Set the filtering method(s) used by libpng. Currently, the only valid + * value for "method" is 0. + */ +PNG_EXPORT(67, void, png_set_filter, (png_structrp png_ptr, int method, + int filters)); + +/* Flags for png_set_filter() to say which filters to use. The flags + * are chosen so that they don't conflict with real filter types + * below, in case they are supplied instead of the #defined constants. + * These values should NOT be changed. + */ +#define PNG_NO_FILTERS 0x00 +#define PNG_FILTER_NONE 0x08 +#define PNG_FILTER_SUB 0x10 +#define PNG_FILTER_UP 0x20 +#define PNG_FILTER_AVG 0x40 +#define PNG_FILTER_PAETH 0x80 +#define PNG_ALL_FILTERS (PNG_FILTER_NONE | PNG_FILTER_SUB | PNG_FILTER_UP | \ + PNG_FILTER_AVG | PNG_FILTER_PAETH) + +/* Filter values (not flags) - used in pngwrite.c, pngwutil.c for now. + * These defines should NOT be changed. + */ +#define PNG_FILTER_VALUE_NONE 0 +#define PNG_FILTER_VALUE_SUB 1 +#define PNG_FILTER_VALUE_UP 2 +#define PNG_FILTER_VALUE_AVG 3 +#define PNG_FILTER_VALUE_PAETH 4 +#define PNG_FILTER_VALUE_LAST 5 + +#ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED /* EXPERIMENTAL */ +/* The "heuristic_method" is given by one of the PNG_FILTER_HEURISTIC_ + * defines, either the default (minimum-sum-of-absolute-differences), or + * the experimental method (weighted-minimum-sum-of-absolute-differences). + * + * Weights are factors >= 1.0, indicating how important it is to keep the + * filter type consistent between rows. Larger numbers mean the current + * filter is that many times as likely to be the same as the "num_weights" + * previous filters. This is cumulative for each previous row with a weight. + * There needs to be "num_weights" values in "filter_weights", or it can be + * NULL if the weights aren't being specified. Weights have no influence on + * the selection of the first row filter. Well chosen weights can (in theory) + * improve the compression for a given image. + * + * Costs are factors >= 1.0 indicating the relative decoding costs of a + * filter type. Higher costs indicate more decoding expense, and are + * therefore less likely to be selected over a filter with lower computational + * costs. There needs to be a value in "filter_costs" for each valid filter + * type (given by PNG_FILTER_VALUE_LAST), or it can be NULL if you aren't + * setting the costs. Costs try to improve the speed of decompression without + * unduly increasing the compressed image size. + * + * A negative weight or cost indicates the default value is to be used, and + * values in the range [0.0, 1.0) indicate the value is to remain unchanged. + * The default values for both weights and costs are currently 1.0, but may + * change if good general weighting/cost heuristics can be found. If both + * the weights and costs are set to 1.0, this degenerates the WEIGHTED method + * to the UNWEIGHTED method, but with added encoding time/computation. + */ +PNG_FP_EXPORT(68, void, png_set_filter_heuristics, (png_structrp png_ptr, + int heuristic_method, int num_weights, png_const_doublep filter_weights, + png_const_doublep filter_costs)) +PNG_FIXED_EXPORT(209, void, png_set_filter_heuristics_fixed, + (png_structrp png_ptr, int heuristic_method, int num_weights, + png_const_fixed_point_p filter_weights, + png_const_fixed_point_p filter_costs)) +#endif /* PNG_WRITE_WEIGHTED_FILTER_SUPPORTED */ + +/* Heuristic used for row filter selection. These defines should NOT be + * changed. + */ +#define PNG_FILTER_HEURISTIC_DEFAULT 0 /* Currently "UNWEIGHTED" */ +#define PNG_FILTER_HEURISTIC_UNWEIGHTED 1 /* Used by libpng < 0.95 */ +#define PNG_FILTER_HEURISTIC_WEIGHTED 2 /* Experimental feature */ +#define PNG_FILTER_HEURISTIC_LAST 3 /* Not a valid value */ + +#ifdef PNG_WRITE_SUPPORTED +/* Set the library compression level. Currently, valid values range from + * 0 - 9, corresponding directly to the zlib compression levels 0 - 9 + * (0 - no compression, 9 - "maximal" compression). Note that tests have + * shown that zlib compression levels 3-6 usually perform as well as level 9 + * for PNG images, and do considerably fewer caclulations. In the future, + * these values may not correspond directly to the zlib compression levels. + */ +PNG_EXPORT(69, void, png_set_compression_level, (png_structrp png_ptr, + int level)); + +PNG_EXPORT(70, void, png_set_compression_mem_level, (png_structrp png_ptr, + int mem_level)); + +PNG_EXPORT(71, void, png_set_compression_strategy, (png_structrp png_ptr, + int strategy)); + +/* If PNG_WRITE_OPTIMIZE_CMF_SUPPORTED is defined, libpng will use a + * smaller value of window_bits if it can do so safely. + */ +PNG_EXPORT(72, void, png_set_compression_window_bits, (png_structrp png_ptr, + int window_bits)); + +PNG_EXPORT(73, void, png_set_compression_method, (png_structrp png_ptr, + int method)); +#endif + +#ifdef PNG_WRITE_CUSTOMIZE_ZTXT_COMPRESSION_SUPPORTED +/* Also set zlib parameters for compressing non-IDAT chunks */ +PNG_EXPORT(222, void, png_set_text_compression_level, (png_structrp png_ptr, + int level)); + +PNG_EXPORT(223, void, png_set_text_compression_mem_level, (png_structrp png_ptr, + int mem_level)); + +PNG_EXPORT(224, void, png_set_text_compression_strategy, (png_structrp png_ptr, + int strategy)); + +/* If PNG_WRITE_OPTIMIZE_CMF_SUPPORTED is defined, libpng will use a + * smaller value of window_bits if it can do so safely. + */ +PNG_EXPORT(225, void, png_set_text_compression_window_bits, + (png_structrp png_ptr, int window_bits)); + +PNG_EXPORT(226, void, png_set_text_compression_method, (png_structrp png_ptr, + int method)); +#endif /* PNG_WRITE_CUSTOMIZE_ZTXT_COMPRESSION_SUPPORTED */ + +/* These next functions are called for input/output, memory, and error + * handling. They are in the file pngrio.c, pngwio.c, and pngerror.c, + * and call standard C I/O routines such as fread(), fwrite(), and + * fprintf(). These functions can be made to use other I/O routines + * at run time for those applications that need to handle I/O in a + * different manner by calling png_set_???_fn(). See libpng-manual.txt for + * more information. + */ + +#ifdef PNG_STDIO_SUPPORTED +/* Initialize the input/output for the PNG file to the default functions. */ +PNG_EXPORT(74, void, png_init_io, (png_structrp png_ptr, png_FILE_p fp)); +#endif + +/* Replace the (error and abort), and warning functions with user + * supplied functions. If no messages are to be printed you must still + * write and use replacement functions. The replacement error_fn should + * still do a longjmp to the last setjmp location if you are using this + * method of error handling. If error_fn or warning_fn is NULL, the + * default function will be used. + */ + +PNG_EXPORT(75, void, png_set_error_fn, (png_structrp png_ptr, + png_voidp error_ptr, png_error_ptr error_fn, png_error_ptr warning_fn)); + +/* Return the user pointer associated with the error functions */ +PNG_EXPORT(76, png_voidp, png_get_error_ptr, (png_const_structrp png_ptr)); + +/* Replace the default data output functions with a user supplied one(s). + * If buffered output is not used, then output_flush_fn can be set to NULL. + * If PNG_WRITE_FLUSH_SUPPORTED is not defined at libpng compile time + * output_flush_fn will be ignored (and thus can be NULL). + * It is probably a mistake to use NULL for output_flush_fn if + * write_data_fn is not also NULL unless you have built libpng with + * PNG_WRITE_FLUSH_SUPPORTED undefined, because in this case libpng's + * default flush function, which uses the standard *FILE structure, will + * be used. + */ +PNG_EXPORT(77, void, png_set_write_fn, (png_structrp png_ptr, png_voidp io_ptr, + png_rw_ptr write_data_fn, png_flush_ptr output_flush_fn)); + +/* Replace the default data input function with a user supplied one. */ +PNG_EXPORT(78, void, png_set_read_fn, (png_structrp png_ptr, png_voidp io_ptr, + png_rw_ptr read_data_fn)); + +/* Return the user pointer associated with the I/O functions */ +PNG_EXPORT(79, png_voidp, png_get_io_ptr, (png_const_structrp png_ptr)); + +PNG_EXPORT(80, void, png_set_read_status_fn, (png_structrp png_ptr, + png_read_status_ptr read_row_fn)); + +PNG_EXPORT(81, void, png_set_write_status_fn, (png_structrp png_ptr, + png_write_status_ptr write_row_fn)); + +#ifdef PNG_USER_MEM_SUPPORTED +/* Replace the default memory allocation functions with user supplied one(s). */ +PNG_EXPORT(82, void, png_set_mem_fn, (png_structrp png_ptr, png_voidp mem_ptr, + png_malloc_ptr malloc_fn, png_free_ptr free_fn)); +/* Return the user pointer associated with the memory functions */ +PNG_EXPORT(83, png_voidp, png_get_mem_ptr, (png_const_structrp png_ptr)); +#endif + +#ifdef PNG_READ_USER_TRANSFORM_SUPPORTED +PNG_EXPORT(84, void, png_set_read_user_transform_fn, (png_structrp png_ptr, + png_user_transform_ptr read_user_transform_fn)); +#endif + +#ifdef PNG_WRITE_USER_TRANSFORM_SUPPORTED +PNG_EXPORT(85, void, png_set_write_user_transform_fn, (png_structrp png_ptr, + png_user_transform_ptr write_user_transform_fn)); +#endif + +#ifdef PNG_USER_TRANSFORM_PTR_SUPPORTED +PNG_EXPORT(86, void, png_set_user_transform_info, (png_structrp png_ptr, + png_voidp user_transform_ptr, int user_transform_depth, + int user_transform_channels)); +/* Return the user pointer associated with the user transform functions */ +PNG_EXPORT(87, png_voidp, png_get_user_transform_ptr, + (png_const_structrp png_ptr)); +#endif + +#ifdef PNG_USER_TRANSFORM_INFO_SUPPORTED +/* Return information about the row currently being processed. Note that these + * APIs do not fail but will return unexpected results if called outside a user + * transform callback. Also note that when transforming an interlaced image the + * row number is the row number within the sub-image of the interlace pass, so + * the value will increase to the height of the sub-image (not the full image) + * then reset to 0 for the next pass. + * + * Use PNG_ROW_FROM_PASS_ROW(row, pass) and PNG_COL_FROM_PASS_COL(col, pass) to + * find the output pixel (x,y) given an interlaced sub-image pixel + * (row,col,pass). (See below for these macros.) + */ +PNG_EXPORT(217, png_uint_32, png_get_current_row_number, (png_const_structrp)); +PNG_EXPORT(218, png_byte, png_get_current_pass_number, (png_const_structrp)); +#endif + +#ifdef PNG_READ_USER_CHUNKS_SUPPORTED +/* This callback is called only for *unknown* chunks. If + * PNG_HANDLE_AS_UNKNOWN_SUPPORTED is set then it is possible to set known + * chunks to be treated as unknown, however in this case the callback must do + * any processing required by the chunk (e.g. by calling the appropriate + * png_set_ APIs.) + * + * There is no write support - on write, by default, all the chunks in the + * 'unknown' list are written in the specified position. + * + * The integer return from the callback function is interpreted thus: + * + * negative: An error occured, png_chunk_error will be called. + * zero: The chunk was not handled, the chunk will be saved. A critical + * chunk will cause an error at this point unless it is to be saved. + * positive: The chunk was handled, libpng will ignore/discard it. + * + * See "INTERACTION WTIH USER CHUNK CALLBACKS" below for important notes about + * how this behavior will change in libpng 1.7 + */ +PNG_EXPORT(88, void, png_set_read_user_chunk_fn, (png_structrp png_ptr, + png_voidp user_chunk_ptr, png_user_chunk_ptr read_user_chunk_fn)); +#endif + +#ifdef PNG_USER_CHUNKS_SUPPORTED +PNG_EXPORT(89, png_voidp, png_get_user_chunk_ptr, (png_const_structrp png_ptr)); +#endif + +#ifdef PNG_PROGRESSIVE_READ_SUPPORTED +/* Sets the function callbacks for the push reader, and a pointer to a + * user-defined structure available to the callback functions. + */ +PNG_EXPORT(90, void, png_set_progressive_read_fn, (png_structrp png_ptr, + png_voidp progressive_ptr, png_progressive_info_ptr info_fn, + png_progressive_row_ptr row_fn, png_progressive_end_ptr end_fn)); + +/* Returns the user pointer associated with the push read functions */ +PNG_EXPORT(91, png_voidp, png_get_progressive_ptr, + (png_const_structrp png_ptr)); + +/* Function to be called when data becomes available */ +PNG_EXPORT(92, void, png_process_data, (png_structrp png_ptr, + png_inforp info_ptr, png_bytep buffer, png_size_t buffer_size)); + +/* A function which may be called *only* within png_process_data to stop the + * processing of any more data. The function returns the number of bytes + * remaining, excluding any that libpng has cached internally. A subsequent + * call to png_process_data must supply these bytes again. If the argument + * 'save' is set to true the routine will first save all the pending data and + * will always return 0. + */ +PNG_EXPORT(219, png_size_t, png_process_data_pause, (png_structrp, int save)); + +/* A function which may be called *only* outside (after) a call to + * png_process_data. It returns the number of bytes of data to skip in the + * input. Normally it will return 0, but if it returns a non-zero value the + * application must skip than number of bytes of input data and pass the + * following data to the next call to png_process_data. + */ +PNG_EXPORT(220, png_uint_32, png_process_data_skip, (png_structrp)); + +#ifdef PNG_READ_INTERLACING_SUPPORTED +/* Function that combines rows. 'new_row' is a flag that should come from + * the callback and be non-NULL if anything needs to be done; the library + * stores its own version of the new data internally and ignores the passed + * in value. + */ +PNG_EXPORT(93, void, png_progressive_combine_row, (png_const_structrp png_ptr, + png_bytep old_row, png_const_bytep new_row)); +#endif /* PNG_READ_INTERLACING_SUPPORTED */ +#endif /* PNG_PROGRESSIVE_READ_SUPPORTED */ + +PNG_EXPORTA(94, png_voidp, png_malloc, (png_const_structrp png_ptr, + png_alloc_size_t size), PNG_ALLOCATED); +/* Added at libpng version 1.4.0 */ +PNG_EXPORTA(95, png_voidp, png_calloc, (png_const_structrp png_ptr, + png_alloc_size_t size), PNG_ALLOCATED); + +/* Added at libpng version 1.2.4 */ +PNG_EXPORTA(96, png_voidp, png_malloc_warn, (png_const_structrp png_ptr, + png_alloc_size_t size), PNG_ALLOCATED); + +/* Frees a pointer allocated by png_malloc() */ +PNG_EXPORT(97, void, png_free, (png_const_structrp png_ptr, png_voidp ptr)); + +/* Free data that was allocated internally */ +PNG_EXPORT(98, void, png_free_data, (png_const_structrp png_ptr, + png_inforp info_ptr, png_uint_32 free_me, int num)); + +/* Reassign responsibility for freeing existing data, whether allocated + * by libpng or by the application; this works on the png_info structure passed + * in, it does not change the state for other png_info structures. + * + * It is unlikely that this function works correctly as of 1.6.0 and using it + * may result either in memory leaks or double free of allocated data. + */ +PNG_EXPORTA(99, void, png_data_freer, (png_const_structrp png_ptr, + png_inforp info_ptr, int freer, png_uint_32 mask), PNG_DEPRECATED); + +/* Assignments for png_data_freer */ +#define PNG_DESTROY_WILL_FREE_DATA 1 +#define PNG_SET_WILL_FREE_DATA 1 +#define PNG_USER_WILL_FREE_DATA 2 +/* Flags for png_ptr->free_me and info_ptr->free_me */ +#define PNG_FREE_HIST 0x0008 +#define PNG_FREE_ICCP 0x0010 +#define PNG_FREE_SPLT 0x0020 +#define PNG_FREE_ROWS 0x0040 +#define PNG_FREE_PCAL 0x0080 +#define PNG_FREE_SCAL 0x0100 +#ifdef PNG_STORE_UNKNOWN_CHUNKS_SUPPORTED +# define PNG_FREE_UNKN 0x0200 +#endif +/* PNG_FREE_LIST 0x0400 removed in 1.6.0 because it is ignored */ +#define PNG_FREE_PLTE 0x1000 +#define PNG_FREE_TRNS 0x2000 +#define PNG_FREE_TEXT 0x4000 +#define PNG_FREE_ALL 0x7fff +#define PNG_FREE_MUL 0x4220 /* PNG_FREE_SPLT|PNG_FREE_TEXT|PNG_FREE_UNKN */ + +#ifdef PNG_USER_MEM_SUPPORTED +PNG_EXPORTA(100, png_voidp, png_malloc_default, (png_const_structrp png_ptr, + png_alloc_size_t size), PNG_ALLOCATED PNG_DEPRECATED); +PNG_EXPORTA(101, void, png_free_default, (png_const_structrp png_ptr, + png_voidp ptr), PNG_DEPRECATED); +#endif + +#ifdef PNG_ERROR_TEXT_SUPPORTED +/* Fatal error in PNG image of libpng - can't continue */ +PNG_EXPORTA(102, void, png_error, (png_const_structrp png_ptr, + png_const_charp error_message), PNG_NORETURN); + +/* The same, but the chunk name is prepended to the error string. */ +PNG_EXPORTA(103, void, png_chunk_error, (png_const_structrp png_ptr, + png_const_charp error_message), PNG_NORETURN); + +#else +/* Fatal error in PNG image of libpng - can't continue */ +PNG_EXPORTA(104, void, png_err, (png_const_structrp png_ptr), PNG_NORETURN); +#endif + +#ifdef PNG_WARNINGS_SUPPORTED +/* Non-fatal error in libpng. Can continue, but may have a problem. */ +PNG_EXPORT(105, void, png_warning, (png_const_structrp png_ptr, + png_const_charp warning_message)); + +/* Non-fatal error in libpng, chunk name is prepended to message. */ +PNG_EXPORT(106, void, png_chunk_warning, (png_const_structrp png_ptr, + png_const_charp warning_message)); +#endif + +#ifdef PNG_BENIGN_ERRORS_SUPPORTED +/* Benign error in libpng. Can continue, but may have a problem. + * User can choose whether to handle as a fatal error or as a warning. */ +PNG_EXPORT(107, void, png_benign_error, (png_const_structrp png_ptr, + png_const_charp warning_message)); + +#ifdef PNG_READ_SUPPORTED +/* Same, chunk name is prepended to message (only during read) */ +PNG_EXPORT(108, void, png_chunk_benign_error, (png_const_structrp png_ptr, + png_const_charp warning_message)); +#endif + +PNG_EXPORT(109, void, png_set_benign_errors, + (png_structrp png_ptr, int allowed)); +#else +# ifdef PNG_ALLOW_BENIGN_ERRORS +# define png_benign_error png_warning +# define png_chunk_benign_error png_chunk_warning +# else +# define png_benign_error png_error +# define png_chunk_benign_error png_chunk_error +# endif +#endif + +/* The png_set_ functions are for storing values in the png_info_struct. + * Similarly, the png_get_ calls are used to read values from the + * png_info_struct, either storing the parameters in the passed variables, or + * setting pointers into the png_info_struct where the data is stored. The + * png_get_ functions return a non-zero value if the data was available + * in info_ptr, or return zero and do not change any of the parameters if the + * data was not available. + * + * These functions should be used instead of directly accessing png_info + * to avoid problems with future changes in the size and internal layout of + * png_info_struct. + */ +/* Returns "flag" if chunk data is valid in info_ptr. */ +PNG_EXPORT(110, png_uint_32, png_get_valid, (png_const_structrp png_ptr, + png_const_inforp info_ptr, png_uint_32 flag)); + +/* Returns number of bytes needed to hold a transformed row. */ +PNG_EXPORT(111, png_size_t, png_get_rowbytes, (png_const_structrp png_ptr, + png_const_inforp info_ptr)); + +#ifdef PNG_INFO_IMAGE_SUPPORTED +/* Returns row_pointers, which is an array of pointers to scanlines that was + * returned from png_read_png(). + */ +PNG_EXPORT(112, png_bytepp, png_get_rows, (png_const_structrp png_ptr, + png_const_inforp info_ptr)); + +/* Set row_pointers, which is an array of pointers to scanlines for use + * by png_write_png(). + */ +PNG_EXPORT(113, void, png_set_rows, (png_const_structrp png_ptr, + png_inforp info_ptr, png_bytepp row_pointers)); +#endif + +/* Returns number of color channels in image. */ +PNG_EXPORT(114, png_byte, png_get_channels, (png_const_structrp png_ptr, + png_const_inforp info_ptr)); + +#ifdef PNG_EASY_ACCESS_SUPPORTED +/* Returns image width in pixels. */ +PNG_EXPORT(115, png_uint_32, png_get_image_width, (png_const_structrp png_ptr, + png_const_inforp info_ptr)); + +/* Returns image height in pixels. */ +PNG_EXPORT(116, png_uint_32, png_get_image_height, (png_const_structrp png_ptr, + png_const_inforp info_ptr)); + +/* Returns image bit_depth. */ +PNG_EXPORT(117, png_byte, png_get_bit_depth, (png_const_structrp png_ptr, + png_const_inforp info_ptr)); + +/* Returns image color_type. */ +PNG_EXPORT(118, png_byte, png_get_color_type, (png_const_structrp png_ptr, + png_const_inforp info_ptr)); + +/* Returns image filter_type. */ +PNG_EXPORT(119, png_byte, png_get_filter_type, (png_const_structrp png_ptr, + png_const_inforp info_ptr)); + +/* Returns image interlace_type. */ +PNG_EXPORT(120, png_byte, png_get_interlace_type, (png_const_structrp png_ptr, + png_const_inforp info_ptr)); + +/* Returns image compression_type. */ +PNG_EXPORT(121, png_byte, png_get_compression_type, (png_const_structrp png_ptr, + png_const_inforp info_ptr)); + +/* Returns image resolution in pixels per meter, from pHYs chunk data. */ +PNG_EXPORT(122, png_uint_32, png_get_pixels_per_meter, + (png_const_structrp png_ptr, png_const_inforp info_ptr)); +PNG_EXPORT(123, png_uint_32, png_get_x_pixels_per_meter, + (png_const_structrp png_ptr, png_const_inforp info_ptr)); +PNG_EXPORT(124, png_uint_32, png_get_y_pixels_per_meter, + (png_const_structrp png_ptr, png_const_inforp info_ptr)); + +/* Returns pixel aspect ratio, computed from pHYs chunk data. */ +PNG_FP_EXPORT(125, float, png_get_pixel_aspect_ratio, + (png_const_structrp png_ptr, png_const_inforp info_ptr)) +PNG_FIXED_EXPORT(210, png_fixed_point, png_get_pixel_aspect_ratio_fixed, + (png_const_structrp png_ptr, png_const_inforp info_ptr)) + +/* Returns image x, y offset in pixels or microns, from oFFs chunk data. */ +PNG_EXPORT(126, png_int_32, png_get_x_offset_pixels, + (png_const_structrp png_ptr, png_const_inforp info_ptr)); +PNG_EXPORT(127, png_int_32, png_get_y_offset_pixels, + (png_const_structrp png_ptr, png_const_inforp info_ptr)); +PNG_EXPORT(128, png_int_32, png_get_x_offset_microns, + (png_const_structrp png_ptr, png_const_inforp info_ptr)); +PNG_EXPORT(129, png_int_32, png_get_y_offset_microns, + (png_const_structrp png_ptr, png_const_inforp info_ptr)); + +#endif /* PNG_EASY_ACCESS_SUPPORTED */ + +#ifdef PNG_READ_SUPPORTED +/* Returns pointer to signature string read from PNG header */ +PNG_EXPORT(130, png_const_bytep, png_get_signature, (png_const_structrp png_ptr, + png_const_inforp info_ptr)); +#endif + +#ifdef PNG_bKGD_SUPPORTED +PNG_EXPORT(131, png_uint_32, png_get_bKGD, (png_const_structrp png_ptr, + png_inforp info_ptr, png_color_16p *background)); +#endif + +#ifdef PNG_bKGD_SUPPORTED +PNG_EXPORT(132, void, png_set_bKGD, (png_const_structrp png_ptr, + png_inforp info_ptr, png_const_color_16p background)); +#endif + +#ifdef PNG_cHRM_SUPPORTED +PNG_FP_EXPORT(133, png_uint_32, png_get_cHRM, (png_const_structrp png_ptr, + png_const_inforp info_ptr, double *white_x, double *white_y, double *red_x, + double *red_y, double *green_x, double *green_y, double *blue_x, + double *blue_y)) +PNG_FP_EXPORT(230, png_uint_32, png_get_cHRM_XYZ, (png_const_structrp png_ptr, + png_const_inforp info_ptr, double *red_X, double *red_Y, double *red_Z, + double *green_X, double *green_Y, double *green_Z, double *blue_X, + double *blue_Y, double *blue_Z)) +PNG_FIXED_EXPORT(134, png_uint_32, png_get_cHRM_fixed, + (png_const_structrp png_ptr, png_const_inforp info_ptr, + png_fixed_point *int_white_x, png_fixed_point *int_white_y, + png_fixed_point *int_red_x, png_fixed_point *int_red_y, + png_fixed_point *int_green_x, png_fixed_point *int_green_y, + png_fixed_point *int_blue_x, png_fixed_point *int_blue_y)) +PNG_FIXED_EXPORT(231, png_uint_32, png_get_cHRM_XYZ_fixed, + (png_const_structrp png_ptr, png_const_inforp info_ptr, + png_fixed_point *int_red_X, png_fixed_point *int_red_Y, + png_fixed_point *int_red_Z, png_fixed_point *int_green_X, + png_fixed_point *int_green_Y, png_fixed_point *int_green_Z, + png_fixed_point *int_blue_X, png_fixed_point *int_blue_Y, + png_fixed_point *int_blue_Z)) +#endif + +#ifdef PNG_cHRM_SUPPORTED +PNG_FP_EXPORT(135, void, png_set_cHRM, (png_const_structrp png_ptr, + png_inforp info_ptr, + double white_x, double white_y, double red_x, double red_y, double green_x, + double green_y, double blue_x, double blue_y)) +PNG_FP_EXPORT(232, void, png_set_cHRM_XYZ, (png_const_structrp png_ptr, + png_inforp info_ptr, double red_X, double red_Y, double red_Z, + double green_X, double green_Y, double green_Z, double blue_X, + double blue_Y, double blue_Z)) +PNG_FIXED_EXPORT(136, void, png_set_cHRM_fixed, (png_const_structrp png_ptr, + png_inforp info_ptr, png_fixed_point int_white_x, + png_fixed_point int_white_y, png_fixed_point int_red_x, + png_fixed_point int_red_y, png_fixed_point int_green_x, + png_fixed_point int_green_y, png_fixed_point int_blue_x, + png_fixed_point int_blue_y)) +PNG_FIXED_EXPORT(233, void, png_set_cHRM_XYZ_fixed, (png_const_structrp png_ptr, + png_inforp info_ptr, png_fixed_point int_red_X, png_fixed_point int_red_Y, + png_fixed_point int_red_Z, png_fixed_point int_green_X, + png_fixed_point int_green_Y, png_fixed_point int_green_Z, + png_fixed_point int_blue_X, png_fixed_point int_blue_Y, + png_fixed_point int_blue_Z)) +#endif + +#ifdef PNG_gAMA_SUPPORTED +PNG_FP_EXPORT(137, png_uint_32, png_get_gAMA, (png_const_structrp png_ptr, + png_const_inforp info_ptr, double *file_gamma)) +PNG_FIXED_EXPORT(138, png_uint_32, png_get_gAMA_fixed, + (png_const_structrp png_ptr, png_const_inforp info_ptr, + png_fixed_point *int_file_gamma)) +#endif + +#ifdef PNG_gAMA_SUPPORTED +PNG_FP_EXPORT(139, void, png_set_gAMA, (png_const_structrp png_ptr, + png_inforp info_ptr, double file_gamma)) +PNG_FIXED_EXPORT(140, void, png_set_gAMA_fixed, (png_const_structrp png_ptr, + png_inforp info_ptr, png_fixed_point int_file_gamma)) +#endif + +#ifdef PNG_hIST_SUPPORTED +PNG_EXPORT(141, png_uint_32, png_get_hIST, (png_const_structrp png_ptr, + png_inforp info_ptr, png_uint_16p *hist)); +#endif + +#ifdef PNG_hIST_SUPPORTED +PNG_EXPORT(142, void, png_set_hIST, (png_const_structrp png_ptr, + png_inforp info_ptr, png_const_uint_16p hist)); +#endif + +PNG_EXPORT(143, png_uint_32, png_get_IHDR, (png_const_structrp png_ptr, + png_const_inforp info_ptr, png_uint_32 *width, png_uint_32 *height, + int *bit_depth, int *color_type, int *interlace_method, + int *compression_method, int *filter_method)); + +PNG_EXPORT(144, void, png_set_IHDR, (png_const_structrp png_ptr, + png_inforp info_ptr, png_uint_32 width, png_uint_32 height, int bit_depth, + int color_type, int interlace_method, int compression_method, + int filter_method)); + +#ifdef PNG_oFFs_SUPPORTED +PNG_EXPORT(145, png_uint_32, png_get_oFFs, (png_const_structrp png_ptr, + png_const_inforp info_ptr, png_int_32 *offset_x, png_int_32 *offset_y, + int *unit_type)); +#endif + +#ifdef PNG_oFFs_SUPPORTED +PNG_EXPORT(146, void, png_set_oFFs, (png_const_structrp png_ptr, + png_inforp info_ptr, png_int_32 offset_x, png_int_32 offset_y, + int unit_type)); +#endif + +#ifdef PNG_pCAL_SUPPORTED +PNG_EXPORT(147, png_uint_32, png_get_pCAL, (png_const_structrp png_ptr, + png_inforp info_ptr, png_charp *purpose, png_int_32 *X0, + png_int_32 *X1, int *type, int *nparams, png_charp *units, + png_charpp *params)); +#endif + +#ifdef PNG_pCAL_SUPPORTED +PNG_EXPORT(148, void, png_set_pCAL, (png_const_structrp png_ptr, + png_inforp info_ptr, png_const_charp purpose, png_int_32 X0, png_int_32 X1, + int type, int nparams, png_const_charp units, png_charpp params)); +#endif + +#ifdef PNG_pHYs_SUPPORTED +PNG_EXPORT(149, png_uint_32, png_get_pHYs, (png_const_structrp png_ptr, + png_const_inforp info_ptr, png_uint_32 *res_x, png_uint_32 *res_y, + int *unit_type)); +#endif + +#ifdef PNG_pHYs_SUPPORTED +PNG_EXPORT(150, void, png_set_pHYs, (png_const_structrp png_ptr, + png_inforp info_ptr, png_uint_32 res_x, png_uint_32 res_y, int unit_type)); +#endif + +PNG_EXPORT(151, png_uint_32, png_get_PLTE, (png_const_structrp png_ptr, + png_inforp info_ptr, png_colorp *palette, int *num_palette)); + +PNG_EXPORT(152, void, png_set_PLTE, (png_structrp png_ptr, + png_inforp info_ptr, png_const_colorp palette, int num_palette)); + +#ifdef PNG_sBIT_SUPPORTED +PNG_EXPORT(153, png_uint_32, png_get_sBIT, (png_const_structrp png_ptr, + png_inforp info_ptr, png_color_8p *sig_bit)); +#endif + +#ifdef PNG_sBIT_SUPPORTED +PNG_EXPORT(154, void, png_set_sBIT, (png_const_structrp png_ptr, + png_inforp info_ptr, png_const_color_8p sig_bit)); +#endif + +#ifdef PNG_sRGB_SUPPORTED +PNG_EXPORT(155, png_uint_32, png_get_sRGB, (png_const_structrp png_ptr, + png_const_inforp info_ptr, int *file_srgb_intent)); +#endif + +#ifdef PNG_sRGB_SUPPORTED +PNG_EXPORT(156, void, png_set_sRGB, (png_const_structrp png_ptr, + png_inforp info_ptr, int srgb_intent)); +PNG_EXPORT(157, void, png_set_sRGB_gAMA_and_cHRM, (png_const_structrp png_ptr, + png_inforp info_ptr, int srgb_intent)); +#endif + +#ifdef PNG_iCCP_SUPPORTED +PNG_EXPORT(158, png_uint_32, png_get_iCCP, (png_const_structrp png_ptr, + png_inforp info_ptr, png_charpp name, int *compression_type, + png_bytepp profile, png_uint_32 *proflen)); +#endif + +#ifdef PNG_iCCP_SUPPORTED +PNG_EXPORT(159, void, png_set_iCCP, (png_const_structrp png_ptr, + png_inforp info_ptr, png_const_charp name, int compression_type, + png_const_bytep profile, png_uint_32 proflen)); +#endif + +#ifdef PNG_sPLT_SUPPORTED +PNG_EXPORT(160, int, png_get_sPLT, (png_const_structrp png_ptr, + png_inforp info_ptr, png_sPLT_tpp entries)); +#endif + +#ifdef PNG_sPLT_SUPPORTED +PNG_EXPORT(161, void, png_set_sPLT, (png_const_structrp png_ptr, + png_inforp info_ptr, png_const_sPLT_tp entries, int nentries)); +#endif + +#ifdef PNG_TEXT_SUPPORTED +/* png_get_text also returns the number of text chunks in *num_text */ +PNG_EXPORT(162, int, png_get_text, (png_const_structrp png_ptr, + png_inforp info_ptr, png_textp *text_ptr, int *num_text)); +#endif + +/* Note while png_set_text() will accept a structure whose text, + * language, and translated keywords are NULL pointers, the structure + * returned by png_get_text will always contain regular + * zero-terminated C strings. They might be empty strings but + * they will never be NULL pointers. + */ + +#ifdef PNG_TEXT_SUPPORTED +PNG_EXPORT(163, void, png_set_text, (png_const_structrp png_ptr, + png_inforp info_ptr, png_const_textp text_ptr, int num_text)); +#endif + +#ifdef PNG_tIME_SUPPORTED +PNG_EXPORT(164, png_uint_32, png_get_tIME, (png_const_structrp png_ptr, + png_inforp info_ptr, png_timep *mod_time)); +#endif + +#ifdef PNG_tIME_SUPPORTED +PNG_EXPORT(165, void, png_set_tIME, (png_const_structrp png_ptr, + png_inforp info_ptr, png_const_timep mod_time)); +#endif + +#ifdef PNG_tRNS_SUPPORTED +PNG_EXPORT(166, png_uint_32, png_get_tRNS, (png_const_structrp png_ptr, + png_inforp info_ptr, png_bytep *trans_alpha, int *num_trans, + png_color_16p *trans_color)); +#endif + +#ifdef PNG_tRNS_SUPPORTED +PNG_EXPORT(167, void, png_set_tRNS, (png_structrp png_ptr, + png_inforp info_ptr, png_const_bytep trans_alpha, int num_trans, + png_const_color_16p trans_color)); +#endif + +#ifdef PNG_sCAL_SUPPORTED +PNG_FP_EXPORT(168, png_uint_32, png_get_sCAL, (png_const_structrp png_ptr, + png_const_inforp info_ptr, int *unit, double *width, double *height)) +#if defined(PNG_FLOATING_ARITHMETIC_SUPPORTED) || \ + defined(PNG_FLOATING_POINT_SUPPORTED) +/* NOTE: this API is currently implemented using floating point arithmetic, + * consequently it can only be used on systems with floating point support. + * In any case the range of values supported by png_fixed_point is small and it + * is highly recommended that png_get_sCAL_s be used instead. + */ +PNG_FIXED_EXPORT(214, png_uint_32, png_get_sCAL_fixed, + (png_const_structrp png_ptr, png_const_inforp info_ptr, int *unit, + png_fixed_point *width, png_fixed_point *height)) +#endif +PNG_EXPORT(169, png_uint_32, png_get_sCAL_s, + (png_const_structrp png_ptr, png_const_inforp info_ptr, int *unit, + png_charpp swidth, png_charpp sheight)); + +PNG_FP_EXPORT(170, void, png_set_sCAL, (png_const_structrp png_ptr, + png_inforp info_ptr, int unit, double width, double height)) +PNG_FIXED_EXPORT(213, void, png_set_sCAL_fixed, (png_const_structrp png_ptr, + png_inforp info_ptr, int unit, png_fixed_point width, + png_fixed_point height)) +PNG_EXPORT(171, void, png_set_sCAL_s, (png_const_structrp png_ptr, + png_inforp info_ptr, int unit, + png_const_charp swidth, png_const_charp sheight)); +#endif /* PNG_sCAL_SUPPORTED */ + +#ifdef PNG_SET_UNKNOWN_CHUNKS_SUPPORTED +/* Provide the default handling for all unknown chunks or, optionally, for + * specific unknown chunks. + * + * NOTE: prior to 1.6.0 the handling specified for particular chunks on read was + * ignored and the default was used, the per-chunk setting only had an effect on + * write. If you wish to have chunk-specific handling on read in code that must + * work on earlier versions you must use a user chunk callback to specify the + * desired handling (keep or discard.) + * + * The 'keep' parameter is a PNG_HANDLE_CHUNK_ value as listed below. The + * parameter is interpreted as follows: + * + * READ: + * PNG_HANDLE_CHUNK_AS_DEFAULT: + * Known chunks: do normal libpng processing, do not keep the chunk (but + * see the comments below about PNG_HANDLE_AS_UNKNOWN_SUPPORTED) + * Unknown chunks: for a specific chunk use the global default, when used + * as the default discard the chunk data. + * PNG_HANDLE_CHUNK_NEVER: + * Discard the chunk data. + * PNG_HANDLE_CHUNK_IF_SAFE: + * Keep the chunk data if the chunk is not critical else raise a chunk + * error. + * PNG_HANDLE_CHUNK_ALWAYS: + * Keep the chunk data. + * + * If the chunk data is saved it can be retrieved using png_get_unknown_chunks, + * below. Notice that specifying "AS_DEFAULT" as a global default is equivalent + * to specifying "NEVER", however when "AS_DEFAULT" is used for specific chunks + * it simply resets the behavior to the libpng default. + * + * INTERACTION WTIH USER CHUNK CALLBACKS: + * The per-chunk handling is always used when there is a png_user_chunk_ptr + * callback and the callback returns 0; the chunk is then always stored *unless* + * it is critical and the per-chunk setting is other than ALWAYS. Notice that + * the global default is *not* used in this case. (In effect the per-chunk + * value is incremented to at least IF_SAFE.) + * + * IMPORTANT NOTE: this behavior will change in libpng 1.7 - the global and + * per-chunk defaults will be honored. If you want to preserve the current + * behavior when your callback returns 0 you must set PNG_HANDLE_CHUNK_IF_SAFE + * as the default - if you don't do this libpng 1.6 will issue a warning. + * + * If you want unhandled unknown chunks to be discarded in libpng 1.6 and + * earlier simply return '1' (handled). + * + * PNG_HANDLE_AS_UNKNOWN_SUPPORTED: + * If this is *not* set known chunks will always be handled by libpng and + * will never be stored in the unknown chunk list. Known chunks listed to + * png_set_keep_unknown_chunks will have no effect. If it is set then known + * chunks listed with a keep other than AS_DEFAULT will *never* be processed + * by libpng, in addition critical chunks must either be processed by the + * callback or saved. + * + * The IHDR and IEND chunks must not be listed. Because this turns off the + * default handling for chunks that would otherwise be recognized the + * behavior of libpng transformations may well become incorrect! + * + * WRITE: + * When writing chunks the options only apply to the chunks specified by + * png_set_unknown_chunks (below), libpng will *always* write known chunks + * required by png_set_ calls and will always write the core critical chunks + * (as required for PLTE). + * + * Each chunk in the png_set_unknown_chunks list is looked up in the + * png_set_keep_unknown_chunks list to find the keep setting, this is then + * interpreted as follows: + * + * PNG_HANDLE_CHUNK_AS_DEFAULT: + * Write safe-to-copy chunks and write other chunks if the global + * default is set to _ALWAYS, otherwise don't write this chunk. + * PNG_HANDLE_CHUNK_NEVER: + * Do not write the chunk. + * PNG_HANDLE_CHUNK_IF_SAFE: + * Write the chunk if it is safe-to-copy, otherwise do not write it. + * PNG_HANDLE_CHUNK_ALWAYS: + * Write the chunk. + * + * Note that the default behavior is effectively the opposite of the read case - + * in read unknown chunks are not stored by default, in write they are written + * by default. Also the behavior of PNG_HANDLE_CHUNK_IF_SAFE is very different + * - on write the safe-to-copy bit is checked, on read the critical bit is + * checked and on read if the chunk is critical an error will be raised. + * + * num_chunks: + * =========== + * If num_chunks is positive, then the "keep" parameter specifies the manner + * for handling only those chunks appearing in the chunk_list array, + * otherwise the chunk list array is ignored. + * + * If num_chunks is 0 the "keep" parameter specifies the default behavior for + * unknown chunks, as described above. + * + * If num_chunks is negative, then the "keep" parameter specifies the manner + * for handling all unknown chunks plus all chunks recognized by libpng + * except for the IHDR, PLTE, tRNS, IDAT, and IEND chunks (which continue to + * be processed by libpng. + */ +PNG_EXPORT(172, void, png_set_keep_unknown_chunks, (png_structrp png_ptr, + int keep, png_const_bytep chunk_list, int num_chunks)); + +/* The "keep" PNG_HANDLE_CHUNK_ parameter for the specified chunk is returned; + * the result is therefore true (non-zero) if special handling is required, + * false for the default handling. + */ +PNG_EXPORT(173, int, png_handle_as_unknown, (png_const_structrp png_ptr, + png_const_bytep chunk_name)); +#endif + +#ifdef PNG_STORE_UNKNOWN_CHUNKS_SUPPORTED +PNG_EXPORT(174, void, png_set_unknown_chunks, (png_const_structrp png_ptr, + png_inforp info_ptr, png_const_unknown_chunkp unknowns, + int num_unknowns)); + /* NOTE: prior to 1.6.0 this routine set the 'location' field of the added + * unknowns to the location currently stored in the png_struct. This is + * invariably the wrong value on write. To fix this call the following API + * for each chunk in the list with the correct location. If you know your + * code won't be compiled on earlier versions you can rely on + * png_set_unknown_chunks(write-ptr, png_get_unknown_chunks(read-ptr)) doing + * the correct thing. + */ + +PNG_EXPORT(175, void, png_set_unknown_chunk_location, + (png_const_structrp png_ptr, png_inforp info_ptr, int chunk, int location)); + +PNG_EXPORT(176, int, png_get_unknown_chunks, (png_const_structrp png_ptr, + png_inforp info_ptr, png_unknown_chunkpp entries)); +#endif + +/* Png_free_data() will turn off the "valid" flag for anything it frees. + * If you need to turn it off for a chunk that your application has freed, + * you can use png_set_invalid(png_ptr, info_ptr, PNG_INFO_CHNK); + */ +PNG_EXPORT(177, void, png_set_invalid, (png_const_structrp png_ptr, + png_inforp info_ptr, int mask)); + +#ifdef PNG_INFO_IMAGE_SUPPORTED +/* The "params" pointer is currently not used and is for future expansion. */ +PNG_EXPORT(178, void, png_read_png, (png_structrp png_ptr, png_inforp info_ptr, + int transforms, png_voidp params)); +PNG_EXPORT(179, void, png_write_png, (png_structrp png_ptr, png_inforp info_ptr, + int transforms, png_voidp params)); +#endif + +PNG_EXPORT(180, png_const_charp, png_get_copyright, + (png_const_structrp png_ptr)); +PNG_EXPORT(181, png_const_charp, png_get_header_ver, + (png_const_structrp png_ptr)); +PNG_EXPORT(182, png_const_charp, png_get_header_version, + (png_const_structrp png_ptr)); +PNG_EXPORT(183, png_const_charp, png_get_libpng_ver, + (png_const_structrp png_ptr)); + +#ifdef PNG_MNG_FEATURES_SUPPORTED +PNG_EXPORT(184, png_uint_32, png_permit_mng_features, (png_structrp png_ptr, + png_uint_32 mng_features_permitted)); +#endif + +/* For use in png_set_keep_unknown, added to version 1.2.6 */ +#define PNG_HANDLE_CHUNK_AS_DEFAULT 0 +#define PNG_HANDLE_CHUNK_NEVER 1 +#define PNG_HANDLE_CHUNK_IF_SAFE 2 +#define PNG_HANDLE_CHUNK_ALWAYS 3 +#define PNG_HANDLE_CHUNK_LAST 4 + +/* Strip the prepended error numbers ("#nnn ") from error and warning + * messages before passing them to the error or warning handler. + */ +#ifdef PNG_ERROR_NUMBERS_SUPPORTED +PNG_EXPORT(185, void, png_set_strip_error_numbers, (png_structrp png_ptr, + png_uint_32 strip_mode)); +#endif + +/* Added in libpng-1.2.6 */ +#ifdef PNG_SET_USER_LIMITS_SUPPORTED +PNG_EXPORT(186, void, png_set_user_limits, (png_structrp png_ptr, + png_uint_32 user_width_max, png_uint_32 user_height_max)); +PNG_EXPORT(187, png_uint_32, png_get_user_width_max, + (png_const_structrp png_ptr)); +PNG_EXPORT(188, png_uint_32, png_get_user_height_max, + (png_const_structrp png_ptr)); +/* Added in libpng-1.4.0 */ +PNG_EXPORT(189, void, png_set_chunk_cache_max, (png_structrp png_ptr, + png_uint_32 user_chunk_cache_max)); +PNG_EXPORT(190, png_uint_32, png_get_chunk_cache_max, + (png_const_structrp png_ptr)); +/* Added in libpng-1.4.1 */ +PNG_EXPORT(191, void, png_set_chunk_malloc_max, (png_structrp png_ptr, + png_alloc_size_t user_chunk_cache_max)); +PNG_EXPORT(192, png_alloc_size_t, png_get_chunk_malloc_max, + (png_const_structrp png_ptr)); +#endif + +#if defined(PNG_INCH_CONVERSIONS_SUPPORTED) +PNG_EXPORT(193, png_uint_32, png_get_pixels_per_inch, + (png_const_structrp png_ptr, png_const_inforp info_ptr)); + +PNG_EXPORT(194, png_uint_32, png_get_x_pixels_per_inch, + (png_const_structrp png_ptr, png_const_inforp info_ptr)); + +PNG_EXPORT(195, png_uint_32, png_get_y_pixels_per_inch, + (png_const_structrp png_ptr, png_const_inforp info_ptr)); + +PNG_FP_EXPORT(196, float, png_get_x_offset_inches, + (png_const_structrp png_ptr, png_const_inforp info_ptr)) +#ifdef PNG_FIXED_POINT_SUPPORTED /* otherwise not implemented. */ +PNG_FIXED_EXPORT(211, png_fixed_point, png_get_x_offset_inches_fixed, + (png_const_structrp png_ptr, png_const_inforp info_ptr)) +#endif + +PNG_FP_EXPORT(197, float, png_get_y_offset_inches, (png_const_structrp png_ptr, + png_const_inforp info_ptr)) +#ifdef PNG_FIXED_POINT_SUPPORTED /* otherwise not implemented. */ +PNG_FIXED_EXPORT(212, png_fixed_point, png_get_y_offset_inches_fixed, + (png_const_structrp png_ptr, png_const_inforp info_ptr)) +#endif + +# ifdef PNG_pHYs_SUPPORTED +PNG_EXPORT(198, png_uint_32, png_get_pHYs_dpi, (png_const_structrp png_ptr, + png_const_inforp info_ptr, png_uint_32 *res_x, png_uint_32 *res_y, + int *unit_type)); +# endif /* PNG_pHYs_SUPPORTED */ +#endif /* PNG_INCH_CONVERSIONS_SUPPORTED */ + +/* Added in libpng-1.4.0 */ +#ifdef PNG_IO_STATE_SUPPORTED +PNG_EXPORT(199, png_uint_32, png_get_io_state, (png_const_structrp png_ptr)); + +/* Removed from libpng 1.6; use png_get_io_chunk_type. */ +PNG_REMOVED(200, png_const_bytep, png_get_io_chunk_name, (png_structrp png_ptr), + PNG_DEPRECATED) + +PNG_EXPORT(216, png_uint_32, png_get_io_chunk_type, + (png_const_structrp png_ptr)); + +/* The flags returned by png_get_io_state() are the following: */ +# define PNG_IO_NONE 0x0000 /* no I/O at this moment */ +# define PNG_IO_READING 0x0001 /* currently reading */ +# define PNG_IO_WRITING 0x0002 /* currently writing */ +# define PNG_IO_SIGNATURE 0x0010 /* currently at the file signature */ +# define PNG_IO_CHUNK_HDR 0x0020 /* currently at the chunk header */ +# define PNG_IO_CHUNK_DATA 0x0040 /* currently at the chunk data */ +# define PNG_IO_CHUNK_CRC 0x0080 /* currently at the chunk crc */ +# define PNG_IO_MASK_OP 0x000f /* current operation: reading/writing */ +# define PNG_IO_MASK_LOC 0x00f0 /* current location: sig/hdr/data/crc */ +#endif /* ?PNG_IO_STATE_SUPPORTED */ + +/* Interlace support. The following macros are always defined so that if + * libpng interlace handling is turned off the macros may be used to handle + * interlaced images within the application. + */ +#define PNG_INTERLACE_ADAM7_PASSES 7 + +/* Two macros to return the first row and first column of the original, + * full, image which appears in a given pass. 'pass' is in the range 0 + * to 6 and the result is in the range 0 to 7. + */ +#define PNG_PASS_START_ROW(pass) (((1&~(pass))<<(3-((pass)>>1)))&7) +#define PNG_PASS_START_COL(pass) (((1& (pass))<<(3-(((pass)+1)>>1)))&7) + +/* A macro to return the offset between pixels in the output row for a pair of + * pixels in the input - effectively the inverse of the 'COL_SHIFT' macro that + * follows. Note that ROW_OFFSET is the offset from one row to the next whereas + * COL_OFFSET is from one column to the next, within a row. + */ +#define PNG_PASS_ROW_OFFSET(pass) ((pass)>2?(8>>(((pass)-1)>>1)):8) +#define PNG_PASS_COL_OFFSET(pass) (1<<((7-(pass))>>1)) + +/* Two macros to help evaluate the number of rows or columns in each + * pass. This is expressed as a shift - effectively log2 of the number or + * rows or columns in each 8x8 tile of the original image. + */ +#define PNG_PASS_ROW_SHIFT(pass) ((pass)>2?(8-(pass))>>1:3) +#define PNG_PASS_COL_SHIFT(pass) ((pass)>1?(7-(pass))>>1:3) + +/* Hence two macros to determine the number of rows or columns in a given + * pass of an image given its height or width. In fact these macros may + * return non-zero even though the sub-image is empty, because the other + * dimension may be empty for a small image. + */ +#define PNG_PASS_ROWS(height, pass) (((height)+(((1<>PNG_PASS_ROW_SHIFT(pass)) +#define PNG_PASS_COLS(width, pass) (((width)+(((1<>PNG_PASS_COL_SHIFT(pass)) + +/* For the reader row callbacks (both progressive and sequential) it is + * necessary to find the row in the output image given a row in an interlaced + * image, so two more macros: + */ +#define PNG_ROW_FROM_PASS_ROW(y_in, pass) \ + (((y_in)<>(((7-(off))-(pass))<<2)) & 0xF) | \ + ((0x01145AF0>>(((7-(off))-(pass))<<2)) & 0xF0)) + +#define PNG_ROW_IN_INTERLACE_PASS(y, pass) \ + ((PNG_PASS_MASK(pass,0) >> ((y)&7)) & 1) +#define PNG_COL_IN_INTERLACE_PASS(x, pass) \ + ((PNG_PASS_MASK(pass,1) >> ((x)&7)) & 1) + +#ifdef PNG_READ_COMPOSITE_NODIV_SUPPORTED +/* With these routines we avoid an integer divide, which will be slower on + * most machines. However, it does take more operations than the corresponding + * divide method, so it may be slower on a few RISC systems. There are two + * shifts (by 8 or 16 bits) and an addition, versus a single integer divide. + * + * Note that the rounding factors are NOT supposed to be the same! 128 and + * 32768 are correct for the NODIV code; 127 and 32767 are correct for the + * standard method. + * + * [Optimized code by Greg Roelofs and Mark Adler...blame us for bugs. :-) ] + */ + + /* fg and bg should be in `gamma 1.0' space; alpha is the opacity */ + +# define png_composite(composite, fg, alpha, bg) \ + { png_uint_16 temp = (png_uint_16)((png_uint_16)(fg) \ + * (png_uint_16)(alpha) \ + + (png_uint_16)(bg)*(png_uint_16)(255 \ + - (png_uint_16)(alpha)) + 128); \ + (composite) = (png_byte)((temp + (temp >> 8)) >> 8); } + +# define png_composite_16(composite, fg, alpha, bg) \ + { png_uint_32 temp = (png_uint_32)((png_uint_32)(fg) \ + * (png_uint_32)(alpha) \ + + (png_uint_32)(bg)*(65535 \ + - (png_uint_32)(alpha)) + 32768); \ + (composite) = (png_uint_16)((temp + (temp >> 16)) >> 16); } + +#else /* Standard method using integer division */ + +# define png_composite(composite, fg, alpha, bg) \ + (composite) = (png_byte)(((png_uint_16)(fg) * (png_uint_16)(alpha) + \ + (png_uint_16)(bg) * (png_uint_16)(255 - (png_uint_16)(alpha)) + \ + 127) / 255) + +# define png_composite_16(composite, fg, alpha, bg) \ + (composite) = (png_uint_16)(((png_uint_32)(fg) * (png_uint_32)(alpha) + \ + (png_uint_32)(bg)*(png_uint_32)(65535 - (png_uint_32)(alpha)) + \ + 32767) / 65535) +#endif /* PNG_READ_COMPOSITE_NODIV_SUPPORTED */ + +#ifdef PNG_READ_INT_FUNCTIONS_SUPPORTED +PNG_EXPORT(201, png_uint_32, png_get_uint_32, (png_const_bytep buf)); +PNG_EXPORT(202, png_uint_16, png_get_uint_16, (png_const_bytep buf)); +PNG_EXPORT(203, png_int_32, png_get_int_32, (png_const_bytep buf)); +#endif + +PNG_EXPORT(204, png_uint_32, png_get_uint_31, (png_const_structrp png_ptr, + png_const_bytep buf)); +/* No png_get_int_16 -- may be added if there's a real need for it. */ + +/* Place a 32-bit number into a buffer in PNG byte order (big-endian). */ +#ifdef PNG_WRITE_INT_FUNCTIONS_SUPPORTED +PNG_EXPORT(205, void, png_save_uint_32, (png_bytep buf, png_uint_32 i)); +#endif +#ifdef PNG_SAVE_INT_32_SUPPORTED +PNG_EXPORT(206, void, png_save_int_32, (png_bytep buf, png_int_32 i)); +#endif + +/* Place a 16-bit number into a buffer in PNG byte order. + * The parameter is declared unsigned int, not png_uint_16, + * just to avoid potential problems on pre-ANSI C compilers. + */ +#ifdef PNG_WRITE_INT_FUNCTIONS_SUPPORTED +PNG_EXPORT(207, void, png_save_uint_16, (png_bytep buf, unsigned int i)); +/* No png_save_int_16 -- may be added if there's a real need for it. */ +#endif + +#ifdef PNG_USE_READ_MACROS +/* Inline macros to do direct reads of bytes from the input buffer. + * The png_get_int_32() routine assumes we are using two's complement + * format for negative values, which is almost certainly true. + */ +# define PNG_get_uint_32(buf) \ + (((png_uint_32)(*(buf)) << 24) + \ + ((png_uint_32)(*((buf) + 1)) << 16) + \ + ((png_uint_32)(*((buf) + 2)) << 8) + \ + ((png_uint_32)(*((buf) + 3)))) + + /* From libpng-1.4.0 until 1.4.4, the png_get_uint_16 macro (but not the + * function) incorrectly returned a value of type png_uint_32. + */ +# define PNG_get_uint_16(buf) \ + ((png_uint_16) \ + (((unsigned int)(*(buf)) << 8) + \ + ((unsigned int)(*((buf) + 1))))) + +# define PNG_get_int_32(buf) \ + ((png_int_32)((*(buf) & 0x80) \ + ? -((png_int_32)((png_get_uint_32(buf) ^ 0xffffffffL) + 1)) \ + : (png_int_32)png_get_uint_32(buf))) + + /* If PNG_PREFIX is defined the same thing as below happens in pnglibconf.h, + * but defining a macro name prefixed with PNG_PREFIX. + */ +# ifndef PNG_PREFIX +# define png_get_uint_32(buf) PNG_get_uint_32(buf) +# define png_get_uint_16(buf) PNG_get_uint_16(buf) +# define png_get_int_32(buf) PNG_get_int_32(buf) +# endif +#else +# ifdef PNG_PREFIX + /* No macros; revert to the (redefined) function */ +# define PNG_get_uint_32 (png_get_uint_32) +# define PNG_get_uint_16 (png_get_uint_16) +# define PNG_get_int_32 (png_get_int_32) +# endif +#endif + +/******************************************************************************* + * SIMPLIFIED API + ******************************************************************************* + * + * Please read the documentation in libpng-manual.txt (TODO: write said + * documentation) if you don't understand what follows. + * + * The simplified API hides the details of both libpng and the PNG file format + * itself. It allows PNG files to be read into a very limited number of + * in-memory bitmap formats or to be written from the same formats. If these + * formats do not accomodate your needs then you can, and should, use the more + * sophisticated APIs above - these support a wide variety of in-memory formats + * and a wide variety of sophisticated transformations to those formats as well + * as a wide variety of APIs to manipulate ancillary information. + * + * To read a PNG file using the simplified API: + * + * 1) Declare a 'png_image' structure (see below) on the stack and set the + * version field to PNG_IMAGE_VERSION. + * 2) Call the appropriate png_image_begin_read... function. + * 3) Set the png_image 'format' member to the required sample format. + * 4) Allocate a buffer for the image and, if required, the color-map. + * 5) Call png_image_finish_read to read the image and, if required, the + * color-map into your buffers. + * + * There are no restrictions on the format of the PNG input itself; all valid + * color types, bit depths, and interlace methods are acceptable, and the + * input image is transformed as necessary to the requested in-memory format + * during the png_image_finish_read() step. The only caveat is that if you + * request a color-mapped image from a PNG that is full-color or makes + * complex use of an alpha channel the transformation is extremely lossy and the + * result may look terrible. + * + * To write a PNG file using the simplified API: + * + * 1) Declare a 'png_image' structure on the stack and memset() it to all zero. + * 2) Initialize the members of the structure that describe the image, setting + * the 'format' member to the format of the image samples. + * 3) Call the appropriate png_image_write... function with a pointer to the + * image and, if necessary, the color-map to write the PNG data. + * + * png_image is a structure that describes the in-memory format of an image + * when it is being read or defines the in-memory format of an image that you + * need to write: + */ +#define PNG_IMAGE_VERSION 1 + +typedef struct png_control *png_controlp; +typedef struct +{ + png_controlp opaque; /* Initialize to NULL, free with png_image_free */ + png_uint_32 version; /* Set to PNG_IMAGE_VERSION */ + png_uint_32 width; /* Image width in pixels (columns) */ + png_uint_32 height; /* Image height in pixels (rows) */ + png_uint_32 format; /* Image format as defined below */ + png_uint_32 flags; /* A bit mask containing informational flags */ + png_uint_32 colormap_entries; + /* Number of entries in the color-map */ + + /* In the event of an error or warning the following field will be set to a + * non-zero value and the 'message' field will contain a '\0' terminated + * string with the libpng error or warning message. If both warnings and + * an error were encountered, only the error is recorded. If there + * are multiple warnings, only the first one is recorded. + * + * The upper 30 bits of this value are reserved, the low two bits contain + * a value as follows: + */ +# define PNG_IMAGE_WARNING 1 +# define PNG_IMAGE_ERROR 2 + /* + * The result is a two bit code such that a value more than 1 indicates + * a failure in the API just called: + * + * 0 - no warning or error + * 1 - warning + * 2 - error + * 3 - error preceded by warning + */ +# define PNG_IMAGE_FAILED(png_cntrl) ((((png_cntrl).warning_or_error)&0x03)>1) + + png_uint_32 warning_or_error; + + char message[64]; +} png_image, *png_imagep; + +/* The samples of the image have one to four channels whose components have + * original values in the range 0 to 1.0: + * + * 1: A single gray or luminance channel (G). + * 2: A gray/luminance channel and an alpha channel (GA). + * 3: Three red, green, blue color channels (RGB). + * 4: Three color channels and an alpha channel (RGBA). + * + * The components are encoded in one of two ways: + * + * a) As a small integer, value 0..255, contained in a single byte. For the + * alpha channel the original value is simply value/255. For the color or + * luminance channels the value is encoded according to the sRGB specification + * and matches the 8-bit format expected by typical display devices. + * + * The color/gray channels are not scaled (pre-multiplied) by the alpha + * channel and are suitable for passing to color management software. + * + * b) As a value in the range 0..65535, contained in a 2-byte integer. All + * channels can be converted to the original value by dividing by 65535; all + * channels are linear. Color channels use the RGB encoding (RGB end-points) of + * the sRGB specification. This encoding is identified by the + * PNG_FORMAT_FLAG_LINEAR flag below. + * + * When the simplified API needs to convert between sRGB and linear colorspaces, + * the actual sRGB transfer curve defined in the sRGB specification (see the + * article at http://en.wikipedia.org/wiki/SRGB) is used, not the gamma=1/2.2 + * approximation used elsewhere in libpng. + * + * When an alpha channel is present it is expected to denote pixel coverage + * of the color or luminance channels and is returned as an associated alpha + * channel: the color/gray channels are scaled (pre-multiplied) by the alpha + * value. + * + * The samples are either contained directly in the image data, between 1 and 8 + * bytes per pixel according to the encoding, or are held in a color-map indexed + * by bytes in the image data. In the case of a color-map the color-map entries + * are individual samples, encoded as above, and the image data has one byte per + * pixel to select the relevant sample from the color-map. + */ + +/* PNG_FORMAT_* + * + * #defines to be used in png_image::format. Each #define identifies a + * particular layout of sample data and, if present, alpha values. There are + * separate defines for each of the two component encodings. + * + * A format is built up using single bit flag values. All combinations are + * valid. Formats can be built up from the flag values or you can use one of + * the predefined values below. When testing formats always use the FORMAT_FLAG + * macros to test for individual features - future versions of the library may + * add new flags. + * + * When reading or writing color-mapped images the format should be set to the + * format of the entries in the color-map then png_image_{read,write}_colormap + * called to read or write the color-map and set the format correctly for the + * image data. Do not set the PNG_FORMAT_FLAG_COLORMAP bit directly! + * + * NOTE: libpng can be built with particular features disabled, if you see + * compiler errors because the definition of one of the following flags has been + * compiled out it is because libpng does not have the required support. It is + * possible, however, for the libpng configuration to enable the format on just + * read or just write; in that case you may see an error at run time. You can + * guard against this by checking for the definition of the appropriate + * "_SUPPORTED" macro, one of: + * + * PNG_SIMPLIFIED_{READ,WRITE}_{BGR,AFIRST}_SUPPORTED + */ +#define PNG_FORMAT_FLAG_ALPHA 0x01U /* format with an alpha channel */ +#define PNG_FORMAT_FLAG_COLOR 0x02U /* color format: otherwise grayscale */ +#define PNG_FORMAT_FLAG_LINEAR 0x04U /* 2 byte channels else 1 byte */ +#define PNG_FORMAT_FLAG_COLORMAP 0x08U /* image data is color-mapped */ + +#ifdef PNG_FORMAT_BGR_SUPPORTED +# define PNG_FORMAT_FLAG_BGR 0x10U /* BGR colors, else order is RGB */ +#endif + +#ifdef PNG_FORMAT_AFIRST_SUPPORTED +# define PNG_FORMAT_FLAG_AFIRST 0x20U /* alpha channel comes first */ +#endif + +/* Commonly used formats have predefined macros. + * + * First the single byte (sRGB) formats: + */ +#define PNG_FORMAT_GRAY 0 +#define PNG_FORMAT_GA PNG_FORMAT_FLAG_ALPHA +#define PNG_FORMAT_AG (PNG_FORMAT_GA|PNG_FORMAT_FLAG_AFIRST) +#define PNG_FORMAT_RGB PNG_FORMAT_FLAG_COLOR +#define PNG_FORMAT_BGR (PNG_FORMAT_FLAG_COLOR|PNG_FORMAT_FLAG_BGR) +#define PNG_FORMAT_RGBA (PNG_FORMAT_RGB|PNG_FORMAT_FLAG_ALPHA) +#define PNG_FORMAT_ARGB (PNG_FORMAT_RGBA|PNG_FORMAT_FLAG_AFIRST) +#define PNG_FORMAT_BGRA (PNG_FORMAT_BGR|PNG_FORMAT_FLAG_ALPHA) +#define PNG_FORMAT_ABGR (PNG_FORMAT_BGRA|PNG_FORMAT_FLAG_AFIRST) + +/* Then the linear 2-byte formats. When naming these "Y" is used to + * indicate a luminance (gray) channel. + */ +#define PNG_FORMAT_LINEAR_Y PNG_FORMAT_FLAG_LINEAR +#define PNG_FORMAT_LINEAR_Y_ALPHA (PNG_FORMAT_FLAG_LINEAR|PNG_FORMAT_FLAG_ALPHA) +#define PNG_FORMAT_LINEAR_RGB (PNG_FORMAT_FLAG_LINEAR|PNG_FORMAT_FLAG_COLOR) +#define PNG_FORMAT_LINEAR_RGB_ALPHA \ + (PNG_FORMAT_FLAG_LINEAR|PNG_FORMAT_FLAG_COLOR|PNG_FORMAT_FLAG_ALPHA) + +/* With color-mapped formats the image data is one byte for each pixel, the byte + * is an index into the color-map which is formatted as above. To obtain a + * color-mapped format it is sufficient just to add the PNG_FOMAT_FLAG_COLORMAP + * to one of the above definitions, or you can use one of the definitions below. + */ +#define PNG_FORMAT_RGB_COLORMAP (PNG_FORMAT_RGB|PNG_FORMAT_FLAG_COLORMAP) +#define PNG_FORMAT_BGR_COLORMAP (PNG_FORMAT_BGR|PNG_FORMAT_FLAG_COLORMAP) +#define PNG_FORMAT_RGBA_COLORMAP (PNG_FORMAT_RGBA|PNG_FORMAT_FLAG_COLORMAP) +#define PNG_FORMAT_ARGB_COLORMAP (PNG_FORMAT_ARGB|PNG_FORMAT_FLAG_COLORMAP) +#define PNG_FORMAT_BGRA_COLORMAP (PNG_FORMAT_BGRA|PNG_FORMAT_FLAG_COLORMAP) +#define PNG_FORMAT_ABGR_COLORMAP (PNG_FORMAT_ABGR|PNG_FORMAT_FLAG_COLORMAP) + +/* PNG_IMAGE macros + * + * These are convenience macros to derive information from a png_image + * structure. The PNG_IMAGE_SAMPLE_ macros return values appropriate to the + * actual image sample values - either the entries in the color-map or the + * pixels in the image. The PNG_IMAGE_PIXEL_ macros return corresponding values + * for the pixels and will always return 1 for color-mapped formats. The + * remaining macros return information about the rows in the image and the + * complete image. + * + * NOTE: All the macros that take a png_image::format parameter are compile time + * constants if the format parameter is, itself, a constant. Therefore these + * macros can be used in array declarations and case labels where required. + * Similarly the macros are also pre-processor constants (sizeof is not used) so + * they can be used in #if tests. + * + * First the information about the samples. + */ +#define PNG_IMAGE_SAMPLE_CHANNELS(fmt)\ + (((fmt)&(PNG_FORMAT_FLAG_COLOR|PNG_FORMAT_FLAG_ALPHA))+1) + /* Return the total number of channels in a given format: 1..4 */ + +#define PNG_IMAGE_SAMPLE_COMPONENT_SIZE(fmt)\ + ((((fmt) & PNG_FORMAT_FLAG_LINEAR) >> 2)+1) + /* Return the size in bytes of a single component of a pixel or color-map + * entry (as appropriate) in the image: 1 or 2. + */ + +#define PNG_IMAGE_SAMPLE_SIZE(fmt)\ + (PNG_IMAGE_SAMPLE_CHANNELS(fmt) * PNG_IMAGE_SAMPLE_COMPONENT_SIZE(fmt)) + /* This is the size of the sample data for one sample. If the image is + * color-mapped it is the size of one color-map entry (and image pixels are + * one byte in size), otherwise it is the size of one image pixel. + */ + +#define PNG_IMAGE_MAXIMUM_COLORMAP_COMPONENTS(fmt)\ + (PNG_IMAGE_SAMPLE_CHANNELS(fmt) * 256) + /* The maximum size of the color-map required by the format expressed in a + * count of components. This can be used to compile-time allocate a + * color-map: + * + * png_uint_16 colormap[PNG_IMAGE_MAXIMUM_COLORMAP_COMPONENTS(linear_fmt)]; + * + * png_byte colormap[PNG_IMAGE_MAXIMUM_COLORMAP_COMPONENTS(sRGB_fmt)]; + * + * Alternatively use the PNG_IMAGE_COLORMAP_SIZE macro below to use the + * information from one of the png_image_begin_read_ APIs and dynamically + * allocate the required memory. + */ + +/* Corresponding information about the pixels */ +#define PNG_IMAGE_PIXEL_(test,fmt)\ + (((fmt)&PNG_FORMAT_FLAG_COLORMAP)?1:test(fmt)) + +#define PNG_IMAGE_PIXEL_CHANNELS(fmt)\ + PNG_IMAGE_PIXEL_(PNG_IMAGE_SAMPLE_CHANNELS,fmt) + /* The number of separate channels (components) in a pixel; 1 for a + * color-mapped image. + */ + +#define PNG_IMAGE_PIXEL_COMPONENT_SIZE(fmt)\ + PNG_IMAGE_PIXEL_(PNG_IMAGE_SAMPLE_COMPONENT_SIZE,fmt) + /* The size, in bytes, of each component in a pixel; 1 for a color-mapped + * image. + */ + +#define PNG_IMAGE_PIXEL_SIZE(fmt) PNG_IMAGE_PIXEL_(PNG_IMAGE_SAMPLE_SIZE,fmt) + /* The size, in bytes, of a complete pixel; 1 for a color-mapped image. */ + +/* Information about the whole row, or whole image */ +#define PNG_IMAGE_ROW_STRIDE(image)\ + (PNG_IMAGE_PIXEL_CHANNELS((image).format) * (image).width) + /* Return the total number of components in a single row of the image; this + * is the minimum 'row stride', the minimum count of components between each + * row. For a color-mapped image this is the minimum number of bytes in a + * row. + */ + +#define PNG_IMAGE_BUFFER_SIZE(image, row_stride)\ + (PNG_IMAGE_PIXEL_COMPONENT_SIZE((image).format)*(image).height*(row_stride)) + /* Return the size, in bytes, of an image buffer given a png_image and a row + * stride - the number of components to leave space for in each row. + */ + +#define PNG_IMAGE_SIZE(image)\ + PNG_IMAGE_BUFFER_SIZE(image, PNG_IMAGE_ROW_STRIDE(image)) + /* Return the size, in bytes, of the image in memory given just a png_image; + * the row stride is the minimum stride required for the image. + */ + +#define PNG_IMAGE_COLORMAP_SIZE(image)\ + (PNG_IMAGE_SAMPLE_SIZE((image).format) * (image).colormap_entries) + /* Return the size, in bytes, of the color-map of this image. If the image + * format is not a color-map format this will return a size sufficient for + * 256 entries in the given format; check PNG_FORMAT_FLAG_COLORMAP if + * you don't want to allocate a color-map in this case. + */ + +/* PNG_IMAGE_FLAG_* + * + * Flags containing additional information about the image are held in the + * 'flags' field of png_image. + */ +#define PNG_IMAGE_FLAG_COLORSPACE_NOT_sRGB 0x01 + /* This indicates the the RGB values of the in-memory bitmap do not + * correspond to the red, green and blue end-points defined by sRGB. + */ + +#define PNG_IMAGE_FLAG_FAST 0x02 + /* On write emphasise speed over compression; the resultant PNG file will be + * larger but will be produced significantly faster, particular for large + * images. Do not use this option for images which will be distributed, only + * used it when producing intermediate files that will be read back in + * repeatedly. For a typical 24-bit image the option will double the read + * speed at the cost of increasing the image size by 25%, however for many + * more compressible images the PNG file can be 10 times larger with only a + * slight speed gain. + */ + +#define PNG_IMAGE_FLAG_16BIT_sRGB 0x04 + /* On read if the image is a 16-bit per component image and there is no gAMA + * or sRGB chunk assume that the components are sRGB encoded. Notice that + * images output by the simplified API always have gamma information; setting + * this flag only affects the interpretation of 16-bit images from an + * external source. It is recommended that the application expose this flag + * to the user; the user can normally easily recognize the difference between + * linear and sRGB encoding. This flag has no effect on write - the data + * passed to the write APIs must have the correct encoding (as defined + * above.) + * + * If the flag is not set (the default) input 16-bit per component data is + * assumed to be linear. + * + * NOTE: the flag can only be set after the png_image_begin_read_ call, + * because that call initializes the 'flags' field. + */ + +#ifdef PNG_SIMPLIFIED_READ_SUPPORTED +/* READ APIs + * --------- + * + * The png_image passed to the read APIs must have been initialized by setting + * the png_controlp field 'opaque' to NULL (or, safer, memset the whole thing.) + */ +#ifdef PNG_STDIO_SUPPORTED +PNG_EXPORT(234, int, png_image_begin_read_from_file, (png_imagep image, + const char *file_name)); + /* The named file is opened for read and the image header is filled in + * from the PNG header in the file. + */ + +PNG_EXPORT(235, int, png_image_begin_read_from_stdio, (png_imagep image, + FILE* file)); + /* The PNG header is read from the stdio FILE object. */ +#endif /* PNG_STDIO_SUPPORTED */ + +PNG_EXPORT(236, int, png_image_begin_read_from_memory, (png_imagep image, + png_const_voidp memory, png_size_t size)); + /* The PNG header is read from the given memory buffer. */ + +PNG_EXPORT(237, int, png_image_finish_read, (png_imagep image, + png_const_colorp background, void *buffer, png_int_32 row_stride, + void *colormap)); + /* Finish reading the image into the supplied buffer and clean up the + * png_image structure. + * + * row_stride is the step, in byte or 2-byte units as appropriate, + * between adjacent rows. A positive stride indicates that the top-most row + * is first in the buffer - the normal top-down arrangement. A negative + * stride indicates that the bottom-most row is first in the buffer. + * + * background need only be supplied if an alpha channel must be removed from + * a png_byte format and the removal is to be done by compositing on a solid + * color; otherwise it may be NULL and any composition will be done directly + * onto the buffer. The value is an sRGB color to use for the background, + * for grayscale output the green channel is used. + * + * background must be supplied when an alpha channel must be removed from a + * single byte color-mapped output format, in other words if: + * + * 1) The original format from png_image_begin_read_from_* had + * PNG_FORMAT_FLAG_ALPHA set. + * 2) The format set by the application does not. + * 3) The format set by the application has PNG_FORMAT_FLAG_COLORMAP set and + * PNG_FORMAT_FLAG_LINEAR *not* set. + * + * For linear output removing the alpha channel is always done by compositing + * on black and background is ignored. + * + * colormap must be supplied when PNG_FORMAT_FLAG_COLORMAP is set. It must + * be at least the size (in bytes) returned by PNG_IMAGE_COLORMAP_SIZE. + * image->colormap_entries will be updated to the actual number of entries + * written to the colormap; this may be less than the original value. + */ + +PNG_EXPORT(238, void, png_image_free, (png_imagep image)); + /* Free any data allocated by libpng in image->opaque, setting the pointer to + * NULL. May be called at any time after the structure is initialized. + */ +#endif /* PNG_SIMPLIFIED_READ_SUPPORTED */ + +#ifdef PNG_SIMPLIFIED_WRITE_SUPPORTED +/* WRITE APIS + * ---------- + * For write you must initialize a png_image structure to describe the image to + * be written. To do this use memset to set the whole structure to 0 then + * initialize fields describing your image. + * + * version: must be set to PNG_IMAGE_VERSION + * opaque: must be initialized to NULL + * width: image width in pixels + * height: image height in rows + * format: the format of the data (image and color-map) you wish to write + * flags: set to 0 unless one of the defined flags applies; set + * PNG_IMAGE_FLAG_COLORSPACE_NOT_sRGB for color format images where the RGB + * values do not correspond to the colors in sRGB. + * colormap_entries: set to the number of entries in the color-map (0 to 256) + */ +PNG_EXPORT(239, int, png_image_write_to_file, (png_imagep image, + const char *file, int convert_to_8bit, const void *buffer, + png_int_32 row_stride, const void *colormap)); + /* Write the image to the named file. */ + +// 4j studios: callback support +PNG_EXPORT(240, int, png_image_write_to_stdio, (png_imagep image, FILE *file, + int convert_to_8_bit, const void *buffer, png_int_32 row_stride, + const void *colormap, /*begin 4j change*/ png_rw_ptr write_fn, png_flush_ptr flush_fn /*end 4j change*/)); + /* Write the image to the given (FILE*). */ + +/* With both write APIs if image is in one of the linear formats with 16-bit + * data then setting convert_to_8_bit will cause the output to be an 8-bit PNG + * gamma encoded according to the sRGB specification, otherwise a 16-bit linear + * encoded PNG file is written. + * + * With color-mapped data formats the colormap parameter point to a color-map + * with at least image->colormap_entries encoded in the specified format. If + * the format is linear the written PNG color-map will be converted to sRGB + * regardless of the convert_to_8_bit flag. + * + * With all APIs row_stride is handled as in the read APIs - it is the spacing + * from one row to the next in component sized units (1 or 2 bytes) and if + * negative indicates a bottom-up row layout in the buffer. + * + * Note that the write API does not support interlacing or sub-8-bit pixels. + */ +#endif /* PNG_SIMPLIFIED_WRITE_SUPPORTED */ +/******************************************************************************* + * END OF SIMPLIFIED API + ******************************************************************************/ + +#ifdef PNG_CHECK_FOR_INVALID_INDEX_SUPPORTED +PNG_EXPORT(242, void, png_set_check_for_invalid_index, + (png_structrp png_ptr, int allowed)); +# ifdef PNG_GET_PALETTE_MAX_SUPPORTED +PNG_EXPORT(243, int, png_get_palette_max, (png_const_structp png_ptr, + png_const_infop info_ptr)); +# endif +#endif /* CHECK_FOR_INVALID_INDEX */ + +/******************************************************************************* + * IMPLEMENTATION OPTIONS + ******************************************************************************* + * + * Support for arbitrary implementation-specific optimizations. The API allows + * particular options to be turned on or off. 'Option' is the number of the + * option and 'onoff' is 0 (off) or non-0 (on). The value returned is given + * by the PNG_OPTION_ defines below. + * + * HARDWARE: normally hardware capabilites, such as the Intel SSE instructions, + * are detected at run time, however sometimes it may be impossible + * to do this in user mode, in which case it is necessary to discover + * the capabilities in an OS specific way. Such capabilities are + * listed here when libpng has support for them and must be turned + * ON by the application if present. + * + * SOFTWARE: sometimes software optimizations actually result in performance + * decrease on some architectures or systems, or with some sets of + * PNG images. 'Software' options allow such optimizations to be + * selected at run time. + */ +#ifdef PNG_SET_OPTION_SUPPORTED +#ifdef PNG_ARM_NEON_API_SUPPORTED +# define PNG_ARM_NEON 0 /* HARDWARE: ARM Neon SIMD instructions supported */ +#endif +#define PNG_OPTION_NEXT 2 /* Next option - numbers must be even */ + +/* Return values: NOTE: there are four values and 'off' is *not* zero */ +#define PNG_OPTION_UNSET 0 /* Unset - defaults to off */ +#define PNG_OPTION_INVALID 1 /* Option number out of range */ +#define PNG_OPTION_OFF 2 +#define PNG_OPTION_ON 3 + +PNG_EXPORT(244, int, png_set_option, (png_structrp png_ptr, int option, + int onoff)); +#endif + +/******************************************************************************* + * END OF HARDWARE OPTIONS + ******************************************************************************/ + +/* Maintainer: Put new public prototypes here ^, in libpng.3, and project + * defs, scripts/pnglibconf.h, and scripts/pnglibconf.h.prebuilt + */ + +/* The last ordinal number (this is the *last* one already used; the next + * one to use is one more than this.) Maintainer, remember to add an entry to + * scripts/symbols.def as well. + */ +#ifdef PNG_EXPORT_LAST_ORDINAL + PNG_EXPORT_LAST_ORDINAL(244); +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* PNG_VERSION_INFO_ONLY */ +/* Do not put anything past this line */ +#endif /* PNG_H */ diff --git a/Minecraft.Client/Windows64/4JLibs/inc/Render/libpng/pngconf.h b/Minecraft.Client/Windows64/4JLibs/inc/Render/libpng/pngconf.h new file mode 100644 index 000000000..31f996757 --- /dev/null +++ b/Minecraft.Client/Windows64/4JLibs/inc/Render/libpng/pngconf.h @@ -0,0 +1,616 @@ + +/* pngconf.h - machine configurable file for libpng + * + * libpng version 1.6.2 - April 25, 2013 + * + * Copyright (c) 1998-2013 Glenn Randers-Pehrson + * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) + * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) + * + * This code is released under the libpng license. + * For conditions of distribution and use, see the disclaimer + * and license in png.h + * + */ + +/* Any machine specific code is near the front of this file, so if you + * are configuring libpng for a machine, you may want to read the section + * starting here down to where it starts to typedef png_color, png_text, + * and png_info. + */ + +#ifndef PNGCONF_H +#define PNGCONF_H + +/* To do: Do all of this in scripts/pnglibconf.dfa */ +#ifdef PNG_SAFE_LIMITS_SUPPORTED +# ifdef PNG_USER_WIDTH_MAX +# undef PNG_USER_WIDTH_MAX +# define PNG_USER_WIDTH_MAX 1000000L +# endif +# ifdef PNG_USER_HEIGHT_MAX +# undef PNG_USER_HEIGHT_MAX +# define PNG_USER_HEIGHT_MAX 1000000L +# endif +# ifdef PNG_USER_CHUNK_MALLOC_MAX +# undef PNG_USER_CHUNK_MALLOC_MAX +# define PNG_USER_CHUNK_MALLOC_MAX 4000000L +# endif +# ifdef PNG_USER_CHUNK_CACHE_MAX +# undef PNG_USER_CHUNK_CACHE_MAX +# define PNG_USER_CHUNK_CACHE_MAX 128 +# endif +#endif + +#ifndef PNG_BUILDING_SYMBOL_TABLE /* else includes may cause problems */ + +/* From libpng 1.6.0 libpng requires an ANSI X3.159-1989 ("ISOC90") compliant C + * compiler for correct compilation. The following header files are required by + * the standard. If your compiler doesn't provide these header files, or they + * do not match the standard, you will need to provide/improve them. + */ +#include +#include + +/* Library header files. These header files are all defined by ISOC90; libpng + * expects conformant implementations, however, an ISOC90 conformant system need + * not provide these header files if the functionality cannot be implemented. + * In this case it will be necessary to disable the relevant parts of libpng in + * the build of pnglibconf.h. + * + * Prior to 1.6.0 string.h was included here; the API changes in 1.6.0 to not + * include this unnecessary header file. + */ + +#ifdef PNG_STDIO_SUPPORTED + /* Required for the definition of FILE: */ +# include +#endif + +#ifdef PNG_SETJMP_SUPPORTED + /* Required for the definition of jmp_buf and the declaration of longjmp: */ +# include +#endif + +#ifdef PNG_CONVERT_tIME_SUPPORTED + /* Required for struct tm: */ +# include +#endif + +#endif /* PNG_BUILDING_SYMBOL_TABLE */ + +/* Prior to 1.6.0 it was possible to turn off 'const' in declarations using + * PNG_NO_CONST; this is no longer supported except for data declarations which + * apparently still cause problems in 2011 on some compilers. + */ +#define PNG_CONST const /* backward compatibility only */ + +/* This controls optimization of the reading of 16 and 32 bit values + * from PNG files. It can be set on a per-app-file basis - it + * just changes whether a macro is used when the function is called. + * The library builder sets the default; if read functions are not + * built into the library the macro implementation is forced on. + */ +#ifndef PNG_READ_INT_FUNCTIONS_SUPPORTED +# define PNG_USE_READ_MACROS +#endif +#if !defined(PNG_NO_USE_READ_MACROS) && !defined(PNG_USE_READ_MACROS) +# if PNG_DEFAULT_READ_MACROS +# define PNG_USE_READ_MACROS +# endif +#endif + +/* COMPILER SPECIFIC OPTIONS. + * + * These options are provided so that a variety of difficult compilers + * can be used. Some are fixed at build time (e.g. PNG_API_RULE + * below) but still have compiler specific implementations, others + * may be changed on a per-file basis when compiling against libpng. + */ + +/* The PNGARG macro was used in versions of libpng prior to 1.6.0 to protect + * against legacy (pre ISOC90) compilers that did not understand function + * prototypes. It is not required for modern C compilers. + */ +#ifndef PNGARG +# define PNGARG(arglist) arglist +#endif + +/* Function calling conventions. + * ============================= + * Normally it is not necessary to specify to the compiler how to call + * a function - it just does it - however on x86 systems derived from + * Microsoft and Borland C compilers ('IBM PC', 'DOS', 'Windows' systems + * and some others) there are multiple ways to call a function and the + * default can be changed on the compiler command line. For this reason + * libpng specifies the calling convention of every exported function and + * every function called via a user supplied function pointer. This is + * done in this file by defining the following macros: + * + * PNGAPI Calling convention for exported functions. + * PNGCBAPI Calling convention for user provided (callback) functions. + * PNGCAPI Calling convention used by the ANSI-C library (required + * for longjmp callbacks and sometimes used internally to + * specify the calling convention for zlib). + * + * These macros should never be overridden. If it is necessary to + * change calling convention in a private build this can be done + * by setting PNG_API_RULE (which defaults to 0) to one of the values + * below to select the correct 'API' variants. + * + * PNG_API_RULE=0 Use PNGCAPI - the 'C' calling convention - throughout. + * This is correct in every known environment. + * PNG_API_RULE=1 Use the operating system convention for PNGAPI and + * the 'C' calling convention (from PNGCAPI) for + * callbacks (PNGCBAPI). This is no longer required + * in any known environment - if it has to be used + * please post an explanation of the problem to the + * libpng mailing list. + * + * These cases only differ if the operating system does not use the C + * calling convention, at present this just means the above cases + * (x86 DOS/Windows sytems) and, even then, this does not apply to + * Cygwin running on those systems. + * + * Note that the value must be defined in pnglibconf.h so that what + * the application uses to call the library matches the conventions + * set when building the library. + */ + +/* Symbol export + * ============= + * When building a shared library it is almost always necessary to tell + * the compiler which symbols to export. The png.h macro 'PNG_EXPORT' + * is used to mark the symbols. On some systems these symbols can be + * extracted at link time and need no special processing by the compiler, + * on other systems the symbols are flagged by the compiler and just + * the declaration requires a special tag applied (unfortunately) in a + * compiler dependent way. Some systems can do either. + * + * A small number of older systems also require a symbol from a DLL to + * be flagged to the program that calls it. This is a problem because + * we do not know in the header file included by application code that + * the symbol will come from a shared library, as opposed to a statically + * linked one. For this reason the application must tell us by setting + * the magic flag PNG_USE_DLL to turn on the special processing before + * it includes png.h. + * + * Four additional macros are used to make this happen: + * + * PNG_IMPEXP The magic (if any) to cause a symbol to be exported from + * the build or imported if PNG_USE_DLL is set - compiler + * and system specific. + * + * PNG_EXPORT_TYPE(type) A macro that pre or appends PNG_IMPEXP to + * 'type', compiler specific. + * + * PNG_DLL_EXPORT Set to the magic to use during a libpng build to + * make a symbol exported from the DLL. Not used in the + * public header files; see pngpriv.h for how it is used + * in the libpng build. + * + * PNG_DLL_IMPORT Set to the magic to force the libpng symbols to come + * from a DLL - used to define PNG_IMPEXP when + * PNG_USE_DLL is set. + */ + +/* System specific discovery. + * ========================== + * This code is used at build time to find PNG_IMPEXP, the API settings + * and PNG_EXPORT_TYPE(), it may also set a macro to indicate the DLL + * import processing is possible. On Windows systems it also sets + * compiler-specific macros to the values required to change the calling + * conventions of the various functions. + */ +#if defined(_Windows) || defined(_WINDOWS) || defined(WIN32) ||\ + defined(_WIN32) || defined(__WIN32__) || defined(__CYGWIN__) + /* Windows system (DOS doesn't support DLLs). Includes builds under Cygwin or + * MinGW on any architecture currently supported by Windows. Also includes + * Watcom builds but these need special treatment because they are not + * compatible with GCC or Visual C because of different calling conventions. + */ +# if PNG_API_RULE == 2 + /* If this line results in an error, either because __watcall is not + * understood or because of a redefine just below you cannot use *this* + * build of the library with the compiler you are using. *This* build was + * build using Watcom and applications must also be built using Watcom! + */ +# define PNGCAPI __watcall +# endif + +# if defined(__GNUC__) || (defined(_MSC_VER) && (_MSC_VER >= 800)) +# define PNGCAPI __cdecl +# if PNG_API_RULE == 1 + /* If this line results in an error __stdcall is not understood and + * PNG_API_RULE should not have been set to '1'. + */ +# define PNGAPI __stdcall +# endif +# else + /* An older compiler, or one not detected (erroneously) above, + * if necessary override on the command line to get the correct + * variants for the compiler. + */ +# ifndef PNGCAPI +# define PNGCAPI _cdecl +# endif +# if PNG_API_RULE == 1 && !defined(PNGAPI) +# define PNGAPI _stdcall +# endif +# endif /* compiler/api */ + /* NOTE: PNGCBAPI always defaults to PNGCAPI. */ + +# if defined(PNGAPI) && !defined(PNG_USER_PRIVATEBUILD) +# error "PNG_USER_PRIVATEBUILD must be defined if PNGAPI is changed" +# endif + +# if (defined(_MSC_VER) && _MSC_VER < 800) ||\ + (defined(__BORLANDC__) && __BORLANDC__ < 0x500) + /* older Borland and MSC + * compilers used '__export' and required this to be after + * the type. + */ +# ifndef PNG_EXPORT_TYPE +# define PNG_EXPORT_TYPE(type) type PNG_IMPEXP +# endif +# define PNG_DLL_EXPORT __export +# else /* newer compiler */ +# define PNG_DLL_EXPORT __declspec(dllexport) +# ifndef PNG_DLL_IMPORT +# define PNG_DLL_IMPORT __declspec(dllimport) +# endif +# endif /* compiler */ + +#else /* !Windows */ +# if (defined(__IBMC__) || defined(__IBMCPP__)) && defined(__OS2__) +# define PNGAPI _System +# else /* !Windows/x86 && !OS/2 */ + /* Use the defaults, or define PNG*API on the command line (but + * this will have to be done for every compile!) + */ +# endif /* other system, !OS/2 */ +#endif /* !Windows/x86 */ + +/* Now do all the defaulting . */ +#ifndef PNGCAPI +# define PNGCAPI +#endif +#ifndef PNGCBAPI +# define PNGCBAPI PNGCAPI +#endif +#ifndef PNGAPI +# define PNGAPI PNGCAPI +#endif + +/* PNG_IMPEXP may be set on the compilation system command line or (if not set) + * then in an internal header file when building the library, otherwise (when + * using the library) it is set here. + */ +#ifndef PNG_IMPEXP +# if defined(PNG_USE_DLL) && defined(PNG_DLL_IMPORT) + /* This forces use of a DLL, disallowing static linking */ +# define PNG_IMPEXP PNG_DLL_IMPORT +# endif + +# ifndef PNG_IMPEXP +# define PNG_IMPEXP +# endif +#endif + +/* In 1.5.2 the definition of PNG_FUNCTION has been changed to always treat + * 'attributes' as a storage class - the attributes go at the start of the + * function definition, and attributes are always appended regardless of the + * compiler. This considerably simplifies these macros but may cause problems + * if any compilers both need function attributes and fail to handle them as + * a storage class (this is unlikely.) + */ +#ifndef PNG_FUNCTION +# define PNG_FUNCTION(type, name, args, attributes) attributes type name args +#endif + +#ifndef PNG_EXPORT_TYPE +# define PNG_EXPORT_TYPE(type) PNG_IMPEXP type +#endif + + /* The ordinal value is only relevant when preprocessing png.h for symbol + * table entries, so we discard it here. See the .dfn files in the + * scripts directory. + */ +#ifndef PNG_EXPORTA + +# define PNG_EXPORTA(ordinal, type, name, args, attributes)\ + PNG_FUNCTION(PNG_EXPORT_TYPE(type),(PNGAPI name),PNGARG(args), \ + extern attributes) +#endif + +/* ANSI-C (C90) does not permit a macro to be invoked with an empty argument, + * so make something non-empty to satisfy the requirement: + */ +#define PNG_EMPTY /*empty list*/ + +#define PNG_EXPORT(ordinal, type, name, args)\ + PNG_EXPORTA(ordinal, type, name, args, PNG_EMPTY) + +/* Use PNG_REMOVED to comment out a removed interface. */ +#ifndef PNG_REMOVED +# define PNG_REMOVED(ordinal, type, name, args, attributes) +#endif + +#ifndef PNG_CALLBACK +# define PNG_CALLBACK(type, name, args) type (PNGCBAPI name) PNGARG(args) +#endif + +/* Support for compiler specific function attributes. These are used + * so that where compiler support is available incorrect use of API + * functions in png.h will generate compiler warnings. + * + * Added at libpng-1.2.41. + */ + +#ifndef PNG_NO_PEDANTIC_WARNINGS +# ifndef PNG_PEDANTIC_WARNINGS_SUPPORTED +# define PNG_PEDANTIC_WARNINGS_SUPPORTED +# endif +#endif + +#ifdef PNG_PEDANTIC_WARNINGS_SUPPORTED + /* Support for compiler specific function attributes. These are used + * so that where compiler support is available, incorrect use of API + * functions in png.h will generate compiler warnings. Added at libpng + * version 1.2.41. Disabling these removes the warnings but may also produce + * less efficient code. + */ +# if defined(__GNUC__) +# ifndef PNG_USE_RESULT +# define PNG_USE_RESULT __attribute__((__warn_unused_result__)) +# endif +# ifndef PNG_NORETURN +# define PNG_NORETURN __attribute__((__noreturn__)) +# endif +# if __GNUC__ >= 3 +# ifndef PNG_ALLOCATED +# define PNG_ALLOCATED __attribute__((__malloc__)) +# endif +# ifndef PNG_DEPRECATED +# define PNG_DEPRECATED __attribute__((__deprecated__)) +# endif +# ifndef PNG_PRIVATE +# if 0 /* Doesn't work so we use deprecated instead*/ +# define PNG_PRIVATE \ + __attribute__((warning("This function is not exported by libpng."))) +# else +# define PNG_PRIVATE \ + __attribute__((__deprecated__)) +# endif +# endif +# if ((__GNUC__ != 3) || !defined(__GNUC_MINOR__) || (__GNUC_MINOR__ >= 1)) +# ifndef PNG_RESTRICT +# define PNG_RESTRICT __restrict +# endif +# endif /* __GNUC__ == 3.0 */ +# endif /* __GNUC__ >= 3 */ + +# elif defined(_MSC_VER) && (_MSC_VER >= 1300) +# ifndef PNG_USE_RESULT +# define PNG_USE_RESULT /* not supported */ +# endif +# ifndef PNG_NORETURN +# define PNG_NORETURN __declspec(noreturn) +# endif +# ifndef PNG_ALLOCATED +# if (_MSC_VER >= 1400) +# define PNG_ALLOCATED __declspec(restrict) +# endif +# endif +# ifndef PNG_DEPRECATED +# define PNG_DEPRECATED __declspec(deprecated) +# endif +# ifndef PNG_PRIVATE +# define PNG_PRIVATE __declspec(deprecated) +# endif +# ifndef PNG_RESTRICT +# if (_MSC_VER >= 1400) +# define PNG_RESTRICT __restrict +# endif +# endif + +# elif defined(__WATCOMC__) +# ifndef PNG_RESTRICT +# define PNG_RESTRICT __restrict +# endif +# endif /* _MSC_VER */ +#endif /* PNG_PEDANTIC_WARNINGS */ + +#ifndef PNG_DEPRECATED +# define PNG_DEPRECATED /* Use of this function is deprecated */ +#endif +#ifndef PNG_USE_RESULT +# define PNG_USE_RESULT /* The result of this function must be checked */ +#endif +#ifndef PNG_NORETURN +# define PNG_NORETURN /* This function does not return */ +#endif +#ifndef PNG_ALLOCATED +# define PNG_ALLOCATED /* The result of the function is new memory */ +#endif +#ifndef PNG_PRIVATE +# define PNG_PRIVATE /* This is a private libpng function */ +#endif +#ifndef PNG_RESTRICT +# define PNG_RESTRICT /* The C99 "restrict" feature */ +#endif +#ifndef PNG_FP_EXPORT /* A floating point API. */ +# ifdef PNG_FLOATING_POINT_SUPPORTED +# define PNG_FP_EXPORT(ordinal, type, name, args)\ + PNG_EXPORT(ordinal, type, name, args); +# else /* No floating point APIs */ +# define PNG_FP_EXPORT(ordinal, type, name, args) +# endif +#endif +#ifndef PNG_FIXED_EXPORT /* A fixed point API. */ +# ifdef PNG_FIXED_POINT_SUPPORTED +# define PNG_FIXED_EXPORT(ordinal, type, name, args)\ + PNG_EXPORT(ordinal, type, name, args); +# else /* No fixed point APIs */ +# define PNG_FIXED_EXPORT(ordinal, type, name, args) +# endif +#endif + +#ifndef PNG_BUILDING_SYMBOL_TABLE +/* Some typedefs to get us started. These should be safe on most of the common + * platforms. + * + * png_uint_32 and png_int_32 may, currently, be larger than required to hold a + * 32-bit value however this is not normally advisable. + * + * png_uint_16 and png_int_16 should always be two bytes in size - this is + * verified at library build time. + * + * png_byte must always be one byte in size. + * + * The checks below use constants from limits.h, as defined by the ISOC90 + * standard. + */ +#if CHAR_BIT == 8 && UCHAR_MAX == 255 + typedef unsigned char png_byte; +#else +# error "libpng requires 8 bit bytes" +#endif + +#if INT_MIN == -32768 && INT_MAX == 32767 + typedef int png_int_16; +#elif SHRT_MIN == -32768 && SHRT_MAX == 32767 + typedef short png_int_16; +#else +# error "libpng requires a signed 16 bit type" +#endif + +#if UINT_MAX == 65535 + typedef unsigned int png_uint_16; +#elif USHRT_MAX == 65535 + typedef unsigned short png_uint_16; +#else +# error "libpng requires an unsigned 16 bit type" +#endif + +#if INT_MIN < -2147483646 && INT_MAX > 2147483646 + typedef int png_int_32; +#elif LONG_MIN < -2147483646 && LONG_MAX > 2147483646 + typedef long int png_int_32; +#else +# error "libpng requires a signed 32 bit (or more) type" +#endif + +#if UINT_MAX > 4294967294 + typedef unsigned int png_uint_32; +#elif ULONG_MAX > 4294967294 + typedef unsigned long int png_uint_32; +#else +# error "libpng requires an unsigned 32 bit (or more) type" +#endif + +/* Prior to 1.6.0 it was possible to disable the use of size_t, 1.6.0, however, + * requires an ISOC90 compiler and relies on consistent behavior of sizeof. + */ +typedef size_t png_size_t; +typedef ptrdiff_t png_ptrdiff_t; + +/* libpng needs to know the maximum value of 'size_t' and this controls the + * definition of png_alloc_size_t, below. This maximum value of size_t limits + * but does not control the maximum allocations the library makes - there is + * direct application control of this through png_set_user_limits(). + */ +#ifndef PNG_SMALL_SIZE_T + /* Compiler specific tests for systems where size_t is known to be less than + * 32 bits (some of these systems may no longer work because of the lack of + * 'far' support; see above.) + */ +# if (defined(__TURBOC__) && !defined(__FLAT__)) ||\ + (defined(_MSC_VER) && defined(MAXSEG_64K)) +# define PNG_SMALL_SIZE_T +# endif +#endif + +/* png_alloc_size_t is guaranteed to be no smaller than png_size_t, and no + * smaller than png_uint_32. Casts from png_size_t or png_uint_32 to + * png_alloc_size_t are not necessary; in fact, it is recommended not to use + * them at all so that the compiler can complain when something turns out to be + * problematic. + * + * Casts in the other direction (from png_alloc_size_t to png_size_t or + * png_uint_32) should be explicitly applied; however, we do not expect to + * encounter practical situations that require such conversions. + * + * PNG_SMALL_SIZE_T must be defined if the maximum value of size_t is less than + * 4294967295 - i.e. less than the maximum value of png_uint_32. + */ +#ifdef PNG_SMALL_SIZE_T + typedef png_uint_32 png_alloc_size_t; +#else + typedef png_size_t png_alloc_size_t; +#endif + +/* Prior to 1.6.0 libpng offered limited support for Microsoft C compiler + * implementations of Intel CPU specific support of user-mode segmented address + * spaces, where 16-bit pointers address more than 65536 bytes of memory using + * separate 'segment' registers. The implementation requires two different + * types of pointer (only one of which includes the segment value.) + * + * If required this support is available in version 1.2 of libpng and may be + * available in versions through 1.5, although the correctness of the code has + * not been verified recently. + */ + +/* Typedef for floating-point numbers that are converted to fixed-point with a + * multiple of 100,000, e.g., gamma + */ +typedef png_int_32 png_fixed_point; + +/* Add typedefs for pointers */ +typedef void * png_voidp; +typedef const void * png_const_voidp; +typedef png_byte * png_bytep; +typedef const png_byte * png_const_bytep; +typedef png_uint_32 * png_uint_32p; +typedef const png_uint_32 * png_const_uint_32p; +typedef png_int_32 * png_int_32p; +typedef const png_int_32 * png_const_int_32p; +typedef png_uint_16 * png_uint_16p; +typedef const png_uint_16 * png_const_uint_16p; +typedef png_int_16 * png_int_16p; +typedef const png_int_16 * png_const_int_16p; +typedef char * png_charp; +typedef const char * png_const_charp; +typedef png_fixed_point * png_fixed_point_p; +typedef const png_fixed_point * png_const_fixed_point_p; +typedef png_size_t * png_size_tp; +typedef const png_size_t * png_const_size_tp; + +#ifdef PNG_STDIO_SUPPORTED +typedef FILE * png_FILE_p; +#endif + +#ifdef PNG_FLOATING_POINT_SUPPORTED +typedef double * png_doublep; +typedef const double * png_const_doublep; +#endif + +/* Pointers to pointers; i.e. arrays */ +typedef png_byte * * png_bytepp; +typedef png_uint_32 * * png_uint_32pp; +typedef png_int_32 * * png_int_32pp; +typedef png_uint_16 * * png_uint_16pp; +typedef png_int_16 * * png_int_16pp; +typedef const char * * png_const_charpp; +typedef char * * png_charpp; +typedef png_fixed_point * * png_fixed_point_pp; +#ifdef PNG_FLOATING_POINT_SUPPORTED +typedef double * * png_doublepp; +#endif + +/* Pointers to pointers to pointers; i.e., pointer to array */ +typedef char * * * png_charppp; + +#endif /* PNG_BUILDING_SYMBOL_TABLE */ + +#endif /* PNGCONF_H */ diff --git a/Minecraft.Client/Windows64/4JLibs/inc/Render/libpng/pngdebug.h b/Minecraft.Client/Windows64/4JLibs/inc/Render/libpng/pngdebug.h new file mode 100644 index 000000000..16f81fdd1 --- /dev/null +++ b/Minecraft.Client/Windows64/4JLibs/inc/Render/libpng/pngdebug.h @@ -0,0 +1,157 @@ + +/* pngdebug.h - Debugging macros for libpng, also used in pngtest.c + * + * Copyright (c) 1998-2011 Glenn Randers-Pehrson + * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) + * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) + * + * Last changed in libpng 1.5.0 [January 6, 2011] + * + * This code is released under the libpng license. + * For conditions of distribution and use, see the disclaimer + * and license in png.h + */ + +/* Define PNG_DEBUG at compile time for debugging information. Higher + * numbers for PNG_DEBUG mean more debugging information. This has + * only been added since version 0.95 so it is not implemented throughout + * libpng yet, but more support will be added as needed. + * + * png_debug[1-2]?(level, message ,arg{0-2}) + * Expands to a statement (either a simple expression or a compound + * do..while(0) statement) that outputs a message with parameter + * substitution if PNG_DEBUG is defined to 2 or more. If PNG_DEBUG + * is undefined, 0 or 1 every png_debug expands to a simple expression + * (actually ((void)0)). + * + * level: level of detail of message, starting at 0. A level 'n' + * message is preceded by 'n' tab characters (not implemented + * on Microsoft compilers unless PNG_DEBUG_FILE is also + * defined, to allow debug DLL compilation with no standard IO). + * message: a printf(3) style text string. A trailing '\n' is added + * to the message. + * arg: 0 to 2 arguments for printf(3) style substitution in message. + */ +#ifndef PNGDEBUG_H +#define PNGDEBUG_H +/* These settings control the formatting of messages in png.c and pngerror.c */ +/* Moved to pngdebug.h at 1.5.0 */ +# ifndef PNG_LITERAL_SHARP +# define PNG_LITERAL_SHARP 0x23 +# endif +# ifndef PNG_LITERAL_LEFT_SQUARE_BRACKET +# define PNG_LITERAL_LEFT_SQUARE_BRACKET 0x5b +# endif +# ifndef PNG_LITERAL_RIGHT_SQUARE_BRACKET +# define PNG_LITERAL_RIGHT_SQUARE_BRACKET 0x5d +# endif +# ifndef PNG_STRING_NEWLINE +# define PNG_STRING_NEWLINE "\n" +# endif + +#ifdef PNG_DEBUG +# if (PNG_DEBUG > 0) +# if !defined(PNG_DEBUG_FILE) && defined(_MSC_VER) +# include +# if (PNG_DEBUG > 1) +# ifndef _DEBUG +# define _DEBUG +# endif +# ifndef png_debug +# define png_debug(l,m) _RPT0(_CRT_WARN,m PNG_STRING_NEWLINE) +# endif +# ifndef png_debug1 +# define png_debug1(l,m,p1) _RPT1(_CRT_WARN,m PNG_STRING_NEWLINE,p1) +# endif +# ifndef png_debug2 +# define png_debug2(l,m,p1,p2) \ + _RPT2(_CRT_WARN,m PNG_STRING_NEWLINE,p1,p2) +# endif +# endif +# else /* PNG_DEBUG_FILE || !_MSC_VER */ +# ifndef PNG_STDIO_SUPPORTED +# include /* not included yet */ +# endif +# ifndef PNG_DEBUG_FILE +# define PNG_DEBUG_FILE stderr +# endif /* PNG_DEBUG_FILE */ + +# if (PNG_DEBUG > 1) +/* Note: ["%s"m PNG_STRING_NEWLINE] probably does not work on + * non-ISO compilers + */ +# ifdef __STDC__ +# ifndef png_debug +# define png_debug(l,m) \ + do { \ + int num_tabs=l; \ + fprintf(PNG_DEBUG_FILE,"%s"m PNG_STRING_NEWLINE,(num_tabs==1 ? "\t" : \ + (num_tabs==2 ? "\t\t":(num_tabs>2 ? "\t\t\t":"")))); \ + } while (0) +# endif +# ifndef png_debug1 +# define png_debug1(l,m,p1) \ + do { \ + int num_tabs=l; \ + fprintf(PNG_DEBUG_FILE,"%s"m PNG_STRING_NEWLINE,(num_tabs==1 ? "\t" : \ + (num_tabs==2 ? "\t\t":(num_tabs>2 ? "\t\t\t":""))),p1); \ + } while (0) +# endif +# ifndef png_debug2 +# define png_debug2(l,m,p1,p2) \ + do { \ + int num_tabs=l; \ + fprintf(PNG_DEBUG_FILE,"%s"m PNG_STRING_NEWLINE,(num_tabs==1 ? "\t" : \ + (num_tabs==2 ? "\t\t":(num_tabs>2 ? "\t\t\t":""))),p1,p2); \ + } while (0) +# endif +# else /* __STDC __ */ +# ifndef png_debug +# define png_debug(l,m) \ + do { \ + int num_tabs=l; \ + char format[256]; \ + snprintf(format,256,"%s%s%s",(num_tabs==1 ? "\t" : \ + (num_tabs==2 ? "\t\t":(num_tabs>2 ? "\t\t\t":""))), \ + m,PNG_STRING_NEWLINE); \ + fprintf(PNG_DEBUG_FILE,format); \ + } while (0) +# endif +# ifndef png_debug1 +# define png_debug1(l,m,p1) \ + do { \ + int num_tabs=l; \ + char format[256]; \ + snprintf(format,256,"%s%s%s",(num_tabs==1 ? "\t" : \ + (num_tabs==2 ? "\t\t":(num_tabs>2 ? "\t\t\t":""))), \ + m,PNG_STRING_NEWLINE); \ + fprintf(PNG_DEBUG_FILE,format,p1); \ + } while (0) +# endif +# ifndef png_debug2 +# define png_debug2(l,m,p1,p2) \ + do { \ + int num_tabs=l; \ + char format[256]; \ + snprintf(format,256,"%s%s%s",(num_tabs==1 ? "\t" : \ + (num_tabs==2 ? "\t\t":(num_tabs>2 ? "\t\t\t":""))), \ + m,PNG_STRING_NEWLINE); \ + fprintf(PNG_DEBUG_FILE,format,p1,p2); \ + } while (0) +# endif +# endif /* __STDC __ */ +# endif /* (PNG_DEBUG > 1) */ + +# endif /* _MSC_VER */ +# endif /* (PNG_DEBUG > 0) */ +#endif /* PNG_DEBUG */ +#ifndef png_debug +# define png_debug(l, m) ((void)0) +#endif +#ifndef png_debug1 +# define png_debug1(l, m, p1) ((void)0) +#endif +#ifndef png_debug2 +# define png_debug2(l, m, p1, p2) ((void)0) +#endif +#endif /* PNGDEBUG_H */ diff --git a/Minecraft.Client/Windows64/4JLibs/inc/Render/libpng/pngerror.c b/Minecraft.Client/Windows64/4JLibs/inc/Render/libpng/pngerror.c new file mode 100644 index 000000000..f469206ee --- /dev/null +++ b/Minecraft.Client/Windows64/4JLibs/inc/Render/libpng/pngerror.c @@ -0,0 +1,932 @@ + +/* pngerror.c - stub functions for i/o and memory allocation + * + * Last changed in libpng 1.6.1 [March 28, 2013] + * Copyright (c) 1998-2013 Glenn Randers-Pehrson + * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) + * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) + * + * This code is released under the libpng license. + * For conditions of distribution and use, see the disclaimer + * and license in png.h + * + * This file provides a location for all error handling. Users who + * need special error handling are expected to write replacement functions + * and use png_set_error_fn() to use those functions. See the instructions + * at each function. + */ + +#include "pngpriv.h" + +#if defined(PNG_READ_SUPPORTED) || defined(PNG_WRITE_SUPPORTED) + +static PNG_FUNCTION(void, png_default_error,PNGARG((png_const_structrp png_ptr, + png_const_charp error_message)),PNG_NORETURN); + +#ifdef PNG_WARNINGS_SUPPORTED +static void /* PRIVATE */ +png_default_warning PNGARG((png_const_structrp png_ptr, + png_const_charp warning_message)); +#endif /* PNG_WARNINGS_SUPPORTED */ + +/* This function is called whenever there is a fatal error. This function + * should not be changed. If there is a need to handle errors differently, + * you should supply a replacement error function and use png_set_error_fn() + * to replace the error function at run-time. + */ +#ifdef PNG_ERROR_TEXT_SUPPORTED +PNG_FUNCTION(void,PNGAPI +png_error,(png_const_structrp png_ptr, png_const_charp error_message), + PNG_NORETURN) +{ +#ifdef PNG_ERROR_NUMBERS_SUPPORTED + char msg[16]; + if (png_ptr != NULL) + { + if (png_ptr->flags& + (PNG_FLAG_STRIP_ERROR_NUMBERS|PNG_FLAG_STRIP_ERROR_TEXT)) + { + if (*error_message == PNG_LITERAL_SHARP) + { + /* Strip "#nnnn " from beginning of error message. */ + int offset; + for (offset = 1; offset<15; offset++) + if (error_message[offset] == ' ') + break; + + if (png_ptr->flags&PNG_FLAG_STRIP_ERROR_TEXT) + { + int i; + for (i = 0; i < offset - 1; i++) + msg[i] = error_message[i + 1]; + msg[i - 1] = '\0'; + error_message = msg; + } + + else + error_message += offset; + } + + else + { + if (png_ptr->flags&PNG_FLAG_STRIP_ERROR_TEXT) + { + msg[0] = '0'; + msg[1] = '\0'; + error_message = msg; + } + } + } + } +#endif + if (png_ptr != NULL && png_ptr->error_fn != NULL) + (*(png_ptr->error_fn))(png_constcast(png_structrp,png_ptr), + error_message); + + /* If the custom handler doesn't exist, or if it returns, + use the default handler, which will not return. */ + png_default_error(png_ptr, error_message); +} +#else +PNG_FUNCTION(void,PNGAPI +png_err,(png_const_structrp png_ptr),PNG_NORETURN) +{ + /* Prior to 1.5.2 the error_fn received a NULL pointer, expressed + * erroneously as '\0', instead of the empty string "". This was + * apparently an error, introduced in libpng-1.2.20, and png_default_error + * will crash in this case. + */ + if (png_ptr != NULL && png_ptr->error_fn != NULL) + (*(png_ptr->error_fn))(png_constcast(png_structrp,png_ptr), ""); + + /* If the custom handler doesn't exist, or if it returns, + use the default handler, which will not return. */ + png_default_error(png_ptr, ""); +} +#endif /* PNG_ERROR_TEXT_SUPPORTED */ + +/* Utility to safely appends strings to a buffer. This never errors out so + * error checking is not required in the caller. + */ +size_t +png_safecat(png_charp buffer, size_t bufsize, size_t pos, + png_const_charp string) +{ + if (buffer != NULL && pos < bufsize) + { + if (string != NULL) + while (*string != '\0' && pos < bufsize-1) + buffer[pos++] = *string++; + + buffer[pos] = '\0'; + } + + return pos; +} + +#if defined(PNG_WARNINGS_SUPPORTED) || defined(PNG_TIME_RFC1123_SUPPORTED) +/* Utility to dump an unsigned value into a buffer, given a start pointer and + * and end pointer (which should point just *beyond* the end of the buffer!) + * Returns the pointer to the start of the formatted string. + */ +png_charp +png_format_number(png_const_charp start, png_charp end, int format, + png_alloc_size_t number) +{ + int count = 0; /* number of digits output */ + int mincount = 1; /* minimum number required */ + int output = 0; /* digit output (for the fixed point format) */ + + *--end = '\0'; + + /* This is written so that the loop always runs at least once, even with + * number zero. + */ + while (end > start && (number != 0 || count < mincount)) + { + + static const char digits[] = "0123456789ABCDEF"; + + switch (format) + { + case PNG_NUMBER_FORMAT_fixed: + /* Needs five digits (the fraction) */ + mincount = 5; + if (output || number % 10 != 0) + { + *--end = digits[number % 10]; + output = 1; + } + number /= 10; + break; + + case PNG_NUMBER_FORMAT_02u: + /* Expects at least 2 digits. */ + mincount = 2; + /* FALL THROUGH */ + + case PNG_NUMBER_FORMAT_u: + *--end = digits[number % 10]; + number /= 10; + break; + + case PNG_NUMBER_FORMAT_02x: + /* This format expects at least two digits */ + mincount = 2; + /* FALL THROUGH */ + + case PNG_NUMBER_FORMAT_x: + *--end = digits[number & 0xf]; + number >>= 4; + break; + + default: /* an error */ + number = 0; + break; + } + + /* Keep track of the number of digits added */ + ++count; + + /* Float a fixed number here: */ + if (format == PNG_NUMBER_FORMAT_fixed) if (count == 5) if (end > start) + { + /* End of the fraction, but maybe nothing was output? In that case + * drop the decimal point. If the number is a true zero handle that + * here. + */ + if (output) + *--end = '.'; + else if (number == 0) /* and !output */ + *--end = '0'; + } + } + + return end; +} +#endif + +#ifdef PNG_WARNINGS_SUPPORTED +/* This function is called whenever there is a non-fatal error. This function + * should not be changed. If there is a need to handle warnings differently, + * you should supply a replacement warning function and use + * png_set_error_fn() to replace the warning function at run-time. + */ +void PNGAPI +png_warning(png_const_structrp png_ptr, png_const_charp warning_message) +{ + int offset = 0; + if (png_ptr != NULL) + { +#ifdef PNG_ERROR_NUMBERS_SUPPORTED + if (png_ptr->flags& + (PNG_FLAG_STRIP_ERROR_NUMBERS|PNG_FLAG_STRIP_ERROR_TEXT)) +#endif + { + if (*warning_message == PNG_LITERAL_SHARP) + { + for (offset = 1; offset < 15; offset++) + if (warning_message[offset] == ' ') + break; + } + } + } + if (png_ptr != NULL && png_ptr->warning_fn != NULL) + (*(png_ptr->warning_fn))(png_constcast(png_structrp,png_ptr), + warning_message + offset); + else + png_default_warning(png_ptr, warning_message + offset); +} + +/* These functions support 'formatted' warning messages with up to + * PNG_WARNING_PARAMETER_COUNT parameters. In the format string the parameter + * is introduced by @, where 'number' starts at 1. This follows the + * standard established by X/Open for internationalizable error messages. + */ +void +png_warning_parameter(png_warning_parameters p, int number, + png_const_charp string) +{ + if (number > 0 && number <= PNG_WARNING_PARAMETER_COUNT) + (void)png_safecat(p[number-1], (sizeof p[number-1]), 0, string); +} + +void +png_warning_parameter_unsigned(png_warning_parameters p, int number, int format, + png_alloc_size_t value) +{ + char buffer[PNG_NUMBER_BUFFER_SIZE]; + png_warning_parameter(p, number, PNG_FORMAT_NUMBER(buffer, format, value)); +} + +void +png_warning_parameter_signed(png_warning_parameters p, int number, int format, + png_int_32 value) +{ + png_alloc_size_t u; + png_charp str; + char buffer[PNG_NUMBER_BUFFER_SIZE]; + + /* Avoid overflow by doing the negate in a png_alloc_size_t: */ + u = (png_alloc_size_t)value; + if (value < 0) + u = ~u + 1; + + str = PNG_FORMAT_NUMBER(buffer, format, u); + + if (value < 0 && str > buffer) + *--str = '-'; + + png_warning_parameter(p, number, str); +} + +void +png_formatted_warning(png_const_structrp png_ptr, png_warning_parameters p, + png_const_charp message) +{ + /* The internal buffer is just 192 bytes - enough for all our messages, + * overflow doesn't happen because this code checks! If someone figures + * out how to send us a message longer than 192 bytes, all that will + * happen is that the message will be truncated appropriately. + */ + size_t i = 0; /* Index in the msg[] buffer: */ + char msg[192]; + + /* Each iteration through the following loop writes at most one character + * to msg[i++] then returns here to validate that there is still space for + * the trailing '\0'. It may (in the case of a parameter) read more than + * one character from message[]; it must check for '\0' and continue to the + * test if it finds the end of string. + */ + while (i<(sizeof msg)-1 && *message != '\0') + { + /* '@' at end of string is now just printed (previously it was skipped); + * it is an error in the calling code to terminate the string with @. + */ + if (p != NULL && *message == '@' && message[1] != '\0') + { + int parameter_char = *++message; /* Consume the '@' */ + static const char valid_parameters[] = "123456789"; + int parameter = 0; + + /* Search for the parameter digit, the index in the string is the + * parameter to use. + */ + while (valid_parameters[parameter] != parameter_char && + valid_parameters[parameter] != '\0') + ++parameter; + + /* If the parameter digit is out of range it will just get printed. */ + if (parameter < PNG_WARNING_PARAMETER_COUNT) + { + /* Append this parameter */ + png_const_charp parm = p[parameter]; + png_const_charp pend = p[parameter] + (sizeof p[parameter]); + + /* No need to copy the trailing '\0' here, but there is no guarantee + * that parm[] has been initialized, so there is no guarantee of a + * trailing '\0': + */ + while (i<(sizeof msg)-1 && *parm != '\0' && parm < pend) + msg[i++] = *parm++; + + /* Consume the parameter digit too: */ + ++message; + continue; + } + + /* else not a parameter and there is a character after the @ sign; just + * copy that. This is known not to be '\0' because of the test above. + */ + } + + /* At this point *message can't be '\0', even in the bad parameter case + * above where there is a lone '@' at the end of the message string. + */ + msg[i++] = *message++; + } + + /* i is always less than (sizeof msg), so: */ + msg[i] = '\0'; + + /* And this is the formatted message. It may be larger than + * PNG_MAX_ERROR_TEXT, but that is only used for 'chunk' errors and these + * are not (currently) formatted. + */ + png_warning(png_ptr, msg); +} +#endif /* PNG_WARNINGS_SUPPORTED */ + +#ifdef PNG_BENIGN_ERRORS_SUPPORTED +void PNGAPI +png_benign_error(png_const_structrp png_ptr, png_const_charp error_message) +{ + if (png_ptr->flags & PNG_FLAG_BENIGN_ERRORS_WARN) + { +# ifdef PNG_READ_SUPPORTED + if ((png_ptr->mode & PNG_IS_READ_STRUCT) != 0 && + png_ptr->chunk_name != 0) + png_chunk_warning(png_ptr, error_message); + else +# endif + png_warning(png_ptr, error_message); + } + + else + { +# ifdef PNG_READ_SUPPORTED + if ((png_ptr->mode & PNG_IS_READ_STRUCT) != 0 && + png_ptr->chunk_name != 0) + png_chunk_error(png_ptr, error_message); + else +# endif + png_error(png_ptr, error_message); + } +} + +void /* PRIVATE */ +png_app_warning(png_const_structrp png_ptr, png_const_charp error_message) +{ + if (png_ptr->flags & PNG_FLAG_APP_WARNINGS_WARN) + png_warning(png_ptr, error_message); + else + png_error(png_ptr, error_message); +} + +void /* PRIVATE */ +png_app_error(png_const_structrp png_ptr, png_const_charp error_message) +{ + if (png_ptr->flags & PNG_FLAG_APP_ERRORS_WARN) + png_warning(png_ptr, error_message); + else + png_error(png_ptr, error_message); +} +#endif /* BENIGN_ERRORS */ + +/* These utilities are used internally to build an error message that relates + * to the current chunk. The chunk name comes from png_ptr->chunk_name, + * this is used to prefix the message. The message is limited in length + * to 63 bytes, the name characters are output as hex digits wrapped in [] + * if the character is invalid. + */ +#define isnonalpha(c) ((c) < 65 || (c) > 122 || ((c) > 90 && (c) < 97)) +static PNG_CONST char png_digit[16] = { + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', + 'A', 'B', 'C', 'D', 'E', 'F' +}; + +#define PNG_MAX_ERROR_TEXT 196 /* Currently limited be profile_error in png.c */ +#if defined(PNG_WARNINGS_SUPPORTED) || defined(PNG_ERROR_TEXT_SUPPORTED) +static void /* PRIVATE */ +png_format_buffer(png_const_structrp png_ptr, png_charp buffer, png_const_charp + error_message) +{ + png_uint_32 chunk_name = png_ptr->chunk_name; + int iout = 0, ishift = 24; + + while (ishift >= 0) + { + int c = (int)(chunk_name >> ishift) & 0xff; + + ishift -= 8; + if (isnonalpha(c)) + { + buffer[iout++] = PNG_LITERAL_LEFT_SQUARE_BRACKET; + buffer[iout++] = png_digit[(c & 0xf0) >> 4]; + buffer[iout++] = png_digit[c & 0x0f]; + buffer[iout++] = PNG_LITERAL_RIGHT_SQUARE_BRACKET; + } + + else + { + buffer[iout++] = (char)c; + } + } + + if (error_message == NULL) + buffer[iout] = '\0'; + + else + { + int iin = 0; + + buffer[iout++] = ':'; + buffer[iout++] = ' '; + + while (iin < PNG_MAX_ERROR_TEXT-1 && error_message[iin] != '\0') + buffer[iout++] = error_message[iin++]; + + /* iin < PNG_MAX_ERROR_TEXT, so the following is safe: */ + buffer[iout] = '\0'; + } +} +#endif /* PNG_WARNINGS_SUPPORTED || PNG_ERROR_TEXT_SUPPORTED */ + +#if defined(PNG_READ_SUPPORTED) && defined(PNG_ERROR_TEXT_SUPPORTED) +PNG_FUNCTION(void,PNGAPI +png_chunk_error,(png_const_structrp png_ptr, png_const_charp error_message), + PNG_NORETURN) +{ + char msg[18+PNG_MAX_ERROR_TEXT]; + if (png_ptr == NULL) + png_error(png_ptr, error_message); + + else + { + png_format_buffer(png_ptr, msg, error_message); + png_error(png_ptr, msg); + } +} +#endif /* PNG_READ_SUPPORTED && PNG_ERROR_TEXT_SUPPORTED */ + +#ifdef PNG_WARNINGS_SUPPORTED +void PNGAPI +png_chunk_warning(png_const_structrp png_ptr, png_const_charp warning_message) +{ + char msg[18+PNG_MAX_ERROR_TEXT]; + if (png_ptr == NULL) + png_warning(png_ptr, warning_message); + + else + { + png_format_buffer(png_ptr, msg, warning_message); + png_warning(png_ptr, msg); + } +} +#endif /* PNG_WARNINGS_SUPPORTED */ + +#ifdef PNG_READ_SUPPORTED +#ifdef PNG_BENIGN_ERRORS_SUPPORTED +void PNGAPI +png_chunk_benign_error(png_const_structrp png_ptr, png_const_charp + error_message) +{ + if (png_ptr->flags & PNG_FLAG_BENIGN_ERRORS_WARN) + png_chunk_warning(png_ptr, error_message); + + else + png_chunk_error(png_ptr, error_message); +} +#endif +#endif /* PNG_READ_SUPPORTED */ + +void /* PRIVATE */ +png_chunk_report(png_const_structrp png_ptr, png_const_charp message, int error) +{ + /* This is always supported, but for just read or just write it + * unconditionally does the right thing. + */ +# if defined(PNG_READ_SUPPORTED) && defined(PNG_WRITE_SUPPORTED) + if (png_ptr->mode & PNG_IS_READ_STRUCT) +# endif + +# ifdef PNG_READ_SUPPORTED + { + if (error < PNG_CHUNK_ERROR) + png_chunk_warning(png_ptr, message); + + else + png_chunk_benign_error(png_ptr, message); + } +# endif + +# if defined(PNG_READ_SUPPORTED) && defined(PNG_WRITE_SUPPORTED) + else if (!(png_ptr->mode & PNG_IS_READ_STRUCT)) +# endif + +# ifdef PNG_WRITE_SUPPORTED + { + if (error < PNG_CHUNK_WRITE_ERROR) + png_app_warning(png_ptr, message); + + else + png_app_error(png_ptr, message); + } +# endif +} + +#ifdef PNG_ERROR_TEXT_SUPPORTED +#ifdef PNG_FLOATING_POINT_SUPPORTED +PNG_FUNCTION(void, +png_fixed_error,(png_const_structrp png_ptr, png_const_charp name),PNG_NORETURN) +{ +# define fixed_message "fixed point overflow in " +# define fixed_message_ln ((sizeof fixed_message)-1) + int iin; + char msg[fixed_message_ln+PNG_MAX_ERROR_TEXT]; + memcpy(msg, fixed_message, fixed_message_ln); + iin = 0; + if (name != NULL) while (iin < (PNG_MAX_ERROR_TEXT-1) && name[iin] != 0) + { + msg[fixed_message_ln + iin] = name[iin]; + ++iin; + } + msg[fixed_message_ln + iin] = 0; + png_error(png_ptr, msg); +} +#endif +#endif + +#ifdef PNG_SETJMP_SUPPORTED +/* This API only exists if ANSI-C style error handling is used, + * otherwise it is necessary for png_default_error to be overridden. + */ +jmp_buf* PNGAPI +png_set_longjmp_fn(png_structrp png_ptr, png_longjmp_ptr longjmp_fn, + size_t jmp_buf_size) +{ + /* From libpng 1.6.0 the app gets one chance to set a 'jmpbuf_size' value + * and it must not change after that. Libpng doesn't care how big the + * buffer is, just that it doesn't change. + * + * If the buffer size is no *larger* than the size of jmp_buf when libpng is + * compiled a built in jmp_buf is returned; this preserves the pre-1.6.0 + * semantics that this call will not fail. If the size is larger, however, + * the buffer is allocated and this may fail, causing the function to return + * NULL. + */ + if (png_ptr == NULL) + return NULL; + + if (png_ptr->jmp_buf_ptr == NULL) + { + png_ptr->jmp_buf_size = 0; /* not allocated */ + + if (jmp_buf_size <= (sizeof png_ptr->jmp_buf_local)) + png_ptr->jmp_buf_ptr = &png_ptr->jmp_buf_local; + + else + { + png_ptr->jmp_buf_ptr = png_voidcast(jmp_buf *, + png_malloc_warn(png_ptr, jmp_buf_size)); + + if (png_ptr->jmp_buf_ptr == NULL) + return NULL; /* new NULL return on OOM */ + + png_ptr->jmp_buf_size = jmp_buf_size; + } + } + + else /* Already allocated: check the size */ + { + size_t size = png_ptr->jmp_buf_size; + + if (size == 0) + { + size = (sizeof png_ptr->jmp_buf_local); + if (png_ptr->jmp_buf_ptr != &png_ptr->jmp_buf_local) + { + /* This is an internal error in libpng: somehow we have been left + * with a stack allocated jmp_buf when the application regained + * control. It's always possible to fix this up, but for the moment + * this is a png_error because that makes it easy to detect. + */ + png_error(png_ptr, "Libpng jmp_buf still allocated"); + /* png_ptr->jmp_buf_ptr = &png_ptr->jmp_buf_local; */ + } + } + + if (size != jmp_buf_size) + { + png_warning(png_ptr, "Application jmp_buf size changed"); + return NULL; /* caller will probably crash: no choice here */ + } + } + + /* Finally fill in the function, now we have a satisfactory buffer. It is + * valid to change the function on every call. + */ + png_ptr->longjmp_fn = longjmp_fn; + return png_ptr->jmp_buf_ptr; +} + +void /* PRIVATE */ +png_free_jmpbuf(png_structrp png_ptr) +{ + if (png_ptr != NULL) + { + jmp_buf *jb = png_ptr->jmp_buf_ptr; + + /* A size of 0 is used to indicate a local, stack, allocation of the + * pointer; used here and in png.c + */ + if (jb != NULL && png_ptr->jmp_buf_size > 0) + { + + /* This stuff is so that a failure to free the error control structure + * does not leave libpng in a state with no valid error handling: the + * free always succeeds, if there is an error it gets ignored. + */ + if (jb != &png_ptr->jmp_buf_local) + { + /* Make an internal, libpng, jmp_buf to return here */ + jmp_buf free_jmp_buf; + + if (!setjmp(free_jmp_buf)) + { + png_ptr->jmp_buf_ptr = &free_jmp_buf; /* come back here */ + png_ptr->jmp_buf_size = 0; /* stack allocation */ + png_ptr->longjmp_fn = longjmp; + png_free(png_ptr, jb); /* Return to setjmp on error */ + } + } + } + + /* *Always* cancel everything out: */ + png_ptr->jmp_buf_size = 0; + png_ptr->jmp_buf_ptr = NULL; + png_ptr->longjmp_fn = 0; + } +} +#endif + +/* This is the default error handling function. Note that replacements for + * this function MUST NOT RETURN, or the program will likely crash. This + * function is used by default, or if the program supplies NULL for the + * error function pointer in png_set_error_fn(). + */ +static PNG_FUNCTION(void /* PRIVATE */, +png_default_error,(png_const_structrp png_ptr, png_const_charp error_message), + PNG_NORETURN) +{ +#ifdef PNG_CONSOLE_IO_SUPPORTED +#ifdef PNG_ERROR_NUMBERS_SUPPORTED + /* Check on NULL only added in 1.5.4 */ + if (error_message != NULL && *error_message == PNG_LITERAL_SHARP) + { + /* Strip "#nnnn " from beginning of error message. */ + int offset; + char error_number[16]; + for (offset = 0; offset<15; offset++) + { + error_number[offset] = error_message[offset + 1]; + if (error_message[offset] == ' ') + break; + } + + if ((offset > 1) && (offset < 15)) + { + error_number[offset - 1] = '\0'; + fprintf(stderr, "libpng error no. %s: %s", + error_number, error_message + offset + 1); + fprintf(stderr, PNG_STRING_NEWLINE); + } + + else + { + fprintf(stderr, "libpng error: %s, offset=%d", + error_message, offset); + fprintf(stderr, PNG_STRING_NEWLINE); + } + } + else +#endif + { + fprintf(stderr, "libpng error: %s", error_message ? error_message : + "undefined"); + fprintf(stderr, PNG_STRING_NEWLINE); + } +#else + PNG_UNUSED(error_message) /* Make compiler happy */ +#endif + png_longjmp(png_ptr, 1); +} + +PNG_FUNCTION(void,PNGAPI +png_longjmp,(png_const_structrp png_ptr, int val),PNG_NORETURN) +{ +#ifdef PNG_SETJMP_SUPPORTED + if (png_ptr && png_ptr->longjmp_fn && png_ptr->jmp_buf_ptr) + png_ptr->longjmp_fn(*png_ptr->jmp_buf_ptr, val); +#endif + + /* Here if not setjmp support or if png_ptr is null. */ + PNG_ABORT(); +} + +#ifdef PNG_WARNINGS_SUPPORTED +/* This function is called when there is a warning, but the library thinks + * it can continue anyway. Replacement functions don't have to do anything + * here if you don't want them to. In the default configuration, png_ptr is + * not used, but it is passed in case it may be useful. + */ +static void /* PRIVATE */ +png_default_warning(png_const_structrp png_ptr, png_const_charp warning_message) +{ +#ifdef PNG_CONSOLE_IO_SUPPORTED +# ifdef PNG_ERROR_NUMBERS_SUPPORTED + if (*warning_message == PNG_LITERAL_SHARP) + { + int offset; + char warning_number[16]; + for (offset = 0; offset < 15; offset++) + { + warning_number[offset] = warning_message[offset + 1]; + if (warning_message[offset] == ' ') + break; + } + + if ((offset > 1) && (offset < 15)) + { + warning_number[offset + 1] = '\0'; + fprintf(stderr, "libpng warning no. %s: %s", + warning_number, warning_message + offset); + fprintf(stderr, PNG_STRING_NEWLINE); + } + + else + { + fprintf(stderr, "libpng warning: %s", + warning_message); + fprintf(stderr, PNG_STRING_NEWLINE); + } + } + else +# endif + + { + fprintf(stderr, "libpng warning: %s", warning_message); + fprintf(stderr, PNG_STRING_NEWLINE); + } +#else + PNG_UNUSED(warning_message) /* Make compiler happy */ +#endif + PNG_UNUSED(png_ptr) /* Make compiler happy */ +} +#endif /* PNG_WARNINGS_SUPPORTED */ + +/* This function is called when the application wants to use another method + * of handling errors and warnings. Note that the error function MUST NOT + * return to the calling routine or serious problems will occur. The return + * method used in the default routine calls longjmp(png_ptr->jmp_buf_ptr, 1) + */ +void PNGAPI +png_set_error_fn(png_structrp png_ptr, png_voidp error_ptr, + png_error_ptr error_fn, png_error_ptr warning_fn) +{ + if (png_ptr == NULL) + return; + + png_ptr->error_ptr = error_ptr; + png_ptr->error_fn = error_fn; +#ifdef PNG_WARNINGS_SUPPORTED + png_ptr->warning_fn = warning_fn; +#else + PNG_UNUSED(warning_fn) +#endif +} + + +/* This function returns a pointer to the error_ptr associated with the user + * functions. The application should free any memory associated with this + * pointer before png_write_destroy and png_read_destroy are called. + */ +png_voidp PNGAPI +png_get_error_ptr(png_const_structrp png_ptr) +{ + if (png_ptr == NULL) + return NULL; + + return ((png_voidp)png_ptr->error_ptr); +} + + +#ifdef PNG_ERROR_NUMBERS_SUPPORTED +void PNGAPI +png_set_strip_error_numbers(png_structrp png_ptr, png_uint_32 strip_mode) +{ + if (png_ptr != NULL) + { + png_ptr->flags &= + ((~(PNG_FLAG_STRIP_ERROR_NUMBERS | + PNG_FLAG_STRIP_ERROR_TEXT))&strip_mode); + } +} +#endif + +#if defined(PNG_SIMPLIFIED_READ_SUPPORTED) ||\ + defined(PNG_SIMPLIFIED_WRITE_SUPPORTED) + /* Currently the above both depend on SETJMP_SUPPORTED, however it would be + * possible to implement without setjmp support just so long as there is some + * way to handle the error return here: + */ +PNG_FUNCTION(void /* PRIVATE */, +png_safe_error,(png_structp png_nonconst_ptr, png_const_charp error_message), + PNG_NORETURN) +{ + const png_const_structrp png_ptr = png_nonconst_ptr; + png_imagep image = png_voidcast(png_imagep, png_ptr->error_ptr); + + /* An error is always logged here, overwriting anything (typically a warning) + * that is already there: + */ + if (image != NULL) + { + png_safecat(image->message, (sizeof image->message), 0, error_message); + image->warning_or_error |= PNG_IMAGE_ERROR; + + /* Retrieve the jmp_buf from within the png_control, making this work for + * C++ compilation too is pretty tricky: C++ wants a pointer to the first + * element of a jmp_buf, but C doesn't tell us the type of that. + */ + if (image->opaque != NULL && image->opaque->error_buf != NULL) + longjmp(png_control_jmp_buf(image->opaque), 1); + + /* Missing longjmp buffer, the following is to help debugging: */ + { + size_t pos = png_safecat(image->message, (sizeof image->message), 0, + "bad longjmp: "); + png_safecat(image->message, (sizeof image->message), pos, + error_message); + } + } + + /* Here on an internal programming error. */ + abort(); +} + +#ifdef PNG_WARNINGS_SUPPORTED +void /* PRIVATE */ +png_safe_warning(png_structp png_nonconst_ptr, png_const_charp warning_message) +{ + const png_const_structrp png_ptr = png_nonconst_ptr; + png_imagep image = png_voidcast(png_imagep, png_ptr->error_ptr); + + /* A warning is only logged if there is no prior warning or error. */ + if (image->warning_or_error == 0) + { + png_safecat(image->message, (sizeof image->message), 0, warning_message); + image->warning_or_error |= PNG_IMAGE_WARNING; + } +} +#endif + +int /* PRIVATE */ +png_safe_execute(png_imagep image_in, int (*function)(png_voidp), png_voidp arg) +{ + volatile png_imagep image = image_in; + volatile int result; + volatile png_voidp saved_error_buf; + jmp_buf safe_jmpbuf; + + /* Safely execute function(arg) with png_error returning to this function. */ + saved_error_buf = image->opaque->error_buf; + result = setjmp(safe_jmpbuf) == 0; + + if (result) + { + + image->opaque->error_buf = safe_jmpbuf; + result = function(arg); + } + + image->opaque->error_buf = saved_error_buf; + + /* And do the cleanup prior to any failure return. */ + if (!result) + png_image_free(image); + + return result; +} +#endif /* SIMPLIFIED READ/WRITE */ +#endif /* PNG_READ_SUPPORTED || PNG_WRITE_SUPPORTED */ diff --git a/Minecraft.Client/Windows64/4JLibs/inc/Render/libpng/pngget.c b/Minecraft.Client/Windows64/4JLibs/inc/Render/libpng/pngget.c new file mode 100644 index 000000000..aca63a958 --- /dev/null +++ b/Minecraft.Client/Windows64/4JLibs/inc/Render/libpng/pngget.c @@ -0,0 +1,1177 @@ + +/* pngget.c - retrieval of values from info struct + * + * Last changed in libpng 1.6.1 [March 28, 2013] + * Copyright (c) 1998-2013 Glenn Randers-Pehrson + * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) + * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) + * + * This code is released under the libpng license. + * For conditions of distribution and use, see the disclaimer + * and license in png.h + * + */ + +#include "pngpriv.h" + +#if defined(PNG_READ_SUPPORTED) || defined(PNG_WRITE_SUPPORTED) + +png_uint_32 PNGAPI +png_get_valid(png_const_structrp png_ptr, png_const_inforp info_ptr, + png_uint_32 flag) +{ + if (png_ptr != NULL && info_ptr != NULL) + return(info_ptr->valid & flag); + + return(0); +} + +png_size_t PNGAPI +png_get_rowbytes(png_const_structrp png_ptr, png_const_inforp info_ptr) +{ + if (png_ptr != NULL && info_ptr != NULL) + return(info_ptr->rowbytes); + + return(0); +} + +#ifdef PNG_INFO_IMAGE_SUPPORTED +png_bytepp PNGAPI +png_get_rows(png_const_structrp png_ptr, png_const_inforp info_ptr) +{ + if (png_ptr != NULL && info_ptr != NULL) + return(info_ptr->row_pointers); + + return(0); +} +#endif + +#ifdef PNG_EASY_ACCESS_SUPPORTED +/* Easy access to info, added in libpng-0.99 */ +png_uint_32 PNGAPI +png_get_image_width(png_const_structrp png_ptr, png_const_inforp info_ptr) +{ + if (png_ptr != NULL && info_ptr != NULL) + return info_ptr->width; + + return (0); +} + +png_uint_32 PNGAPI +png_get_image_height(png_const_structrp png_ptr, png_const_inforp info_ptr) +{ + if (png_ptr != NULL && info_ptr != NULL) + return info_ptr->height; + + return (0); +} + +png_byte PNGAPI +png_get_bit_depth(png_const_structrp png_ptr, png_const_inforp info_ptr) +{ + if (png_ptr != NULL && info_ptr != NULL) + return info_ptr->bit_depth; + + return (0); +} + +png_byte PNGAPI +png_get_color_type(png_const_structrp png_ptr, png_const_inforp info_ptr) +{ + if (png_ptr != NULL && info_ptr != NULL) + return info_ptr->color_type; + + return (0); +} + +png_byte PNGAPI +png_get_filter_type(png_const_structrp png_ptr, png_const_inforp info_ptr) +{ + if (png_ptr != NULL && info_ptr != NULL) + return info_ptr->filter_type; + + return (0); +} + +png_byte PNGAPI +png_get_interlace_type(png_const_structrp png_ptr, png_const_inforp info_ptr) +{ + if (png_ptr != NULL && info_ptr != NULL) + return info_ptr->interlace_type; + + return (0); +} + +png_byte PNGAPI +png_get_compression_type(png_const_structrp png_ptr, png_const_inforp info_ptr) +{ + if (png_ptr != NULL && info_ptr != NULL) + return info_ptr->compression_type; + + return (0); +} + +png_uint_32 PNGAPI +png_get_x_pixels_per_meter(png_const_structrp png_ptr, png_const_inforp + info_ptr) +{ +#ifdef PNG_pHYs_SUPPORTED + if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_pHYs)) + { + png_debug1(1, "in %s retrieval function", + "png_get_x_pixels_per_meter"); + + if (info_ptr->phys_unit_type == PNG_RESOLUTION_METER) + return (info_ptr->x_pixels_per_unit); + } +#endif + + return (0); +} + +png_uint_32 PNGAPI +png_get_y_pixels_per_meter(png_const_structrp png_ptr, png_const_inforp + info_ptr) +{ +#ifdef PNG_pHYs_SUPPORTED + if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_pHYs)) + { + png_debug1(1, "in %s retrieval function", + "png_get_y_pixels_per_meter"); + + if (info_ptr->phys_unit_type == PNG_RESOLUTION_METER) + return (info_ptr->y_pixels_per_unit); + } +#endif + + return (0); +} + +png_uint_32 PNGAPI +png_get_pixels_per_meter(png_const_structrp png_ptr, png_const_inforp info_ptr) +{ +#ifdef PNG_pHYs_SUPPORTED + if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_pHYs)) + { + png_debug1(1, "in %s retrieval function", "png_get_pixels_per_meter"); + + if (info_ptr->phys_unit_type == PNG_RESOLUTION_METER && + info_ptr->x_pixels_per_unit == info_ptr->y_pixels_per_unit) + return (info_ptr->x_pixels_per_unit); + } +#endif + + return (0); +} + +#ifdef PNG_FLOATING_POINT_SUPPORTED +float PNGAPI +png_get_pixel_aspect_ratio(png_const_structrp png_ptr, png_const_inforp + info_ptr) +{ +#ifdef PNG_READ_pHYs_SUPPORTED + if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_pHYs)) + { + png_debug1(1, "in %s retrieval function", "png_get_aspect_ratio"); + + if (info_ptr->x_pixels_per_unit != 0) + return ((float)((float)info_ptr->y_pixels_per_unit + /(float)info_ptr->x_pixels_per_unit)); + } +#else + PNG_UNUSED(png_ptr) + PNG_UNUSED(info_ptr) +#endif + + return ((float)0.0); +} +#endif + +#ifdef PNG_FIXED_POINT_SUPPORTED +png_fixed_point PNGAPI +png_get_pixel_aspect_ratio_fixed(png_const_structrp png_ptr, + png_const_inforp info_ptr) +{ +#ifdef PNG_READ_pHYs_SUPPORTED + if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_pHYs) + && info_ptr->x_pixels_per_unit > 0 && info_ptr->y_pixels_per_unit > 0 + && info_ptr->x_pixels_per_unit <= PNG_UINT_31_MAX + && info_ptr->y_pixels_per_unit <= PNG_UINT_31_MAX) + { + png_fixed_point res; + + png_debug1(1, "in %s retrieval function", "png_get_aspect_ratio_fixed"); + + /* The following casts work because a PNG 4 byte integer only has a valid + * range of 0..2^31-1; otherwise the cast might overflow. + */ + if (png_muldiv(&res, (png_int_32)info_ptr->y_pixels_per_unit, PNG_FP_1, + (png_int_32)info_ptr->x_pixels_per_unit)) + return res; + } +#else + PNG_UNUSED(png_ptr) + PNG_UNUSED(info_ptr) +#endif + + return 0; +} +#endif + +png_int_32 PNGAPI +png_get_x_offset_microns(png_const_structrp png_ptr, png_const_inforp info_ptr) +{ +#ifdef PNG_oFFs_SUPPORTED + if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_oFFs)) + { + png_debug1(1, "in %s retrieval function", "png_get_x_offset_microns"); + + if (info_ptr->offset_unit_type == PNG_OFFSET_MICROMETER) + return (info_ptr->x_offset); + } +#endif + + return (0); +} + +png_int_32 PNGAPI +png_get_y_offset_microns(png_const_structrp png_ptr, png_const_inforp info_ptr) +{ +#ifdef PNG_oFFs_SUPPORTED + if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_oFFs)) + { + png_debug1(1, "in %s retrieval function", "png_get_y_offset_microns"); + + if (info_ptr->offset_unit_type == PNG_OFFSET_MICROMETER) + return (info_ptr->y_offset); + } +#endif + + return (0); +} + +png_int_32 PNGAPI +png_get_x_offset_pixels(png_const_structrp png_ptr, png_const_inforp info_ptr) +{ +#ifdef PNG_oFFs_SUPPORTED + if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_oFFs)) + { + png_debug1(1, "in %s retrieval function", "png_get_x_offset_pixels"); + + if (info_ptr->offset_unit_type == PNG_OFFSET_PIXEL) + return (info_ptr->x_offset); + } +#endif + + return (0); +} + +png_int_32 PNGAPI +png_get_y_offset_pixels(png_const_structrp png_ptr, png_const_inforp info_ptr) +{ +#ifdef PNG_oFFs_SUPPORTED + if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_oFFs)) + { + png_debug1(1, "in %s retrieval function", "png_get_y_offset_pixels"); + + if (info_ptr->offset_unit_type == PNG_OFFSET_PIXEL) + return (info_ptr->y_offset); + } +#endif + + return (0); +} + +#ifdef PNG_INCH_CONVERSIONS_SUPPORTED +static png_uint_32 +ppi_from_ppm(png_uint_32 ppm) +{ +#if 0 + /* The conversion is *(2.54/100), in binary (32 digits): + * .00000110100000001001110101001001 + */ + png_uint_32 t1001, t1101; + ppm >>= 1; /* .1 */ + t1001 = ppm + (ppm >> 3); /* .1001 */ + t1101 = t1001 + (ppm >> 1); /* .1101 */ + ppm >>= 20; /* .000000000000000000001 */ + t1101 += t1101 >> 15; /* .1101000000000001101 */ + t1001 >>= 11; /* .000000000001001 */ + t1001 += t1001 >> 12; /* .000000000001001000000001001 */ + ppm += t1001; /* .000000000001001000001001001 */ + ppm += t1101; /* .110100000001001110101001001 */ + return (ppm + 16) >> 5;/* .00000110100000001001110101001001 */ +#else + /* The argument is a PNG unsigned integer, so it is not permitted + * to be bigger than 2^31. + */ + png_fixed_point result; + if (ppm <= PNG_UINT_31_MAX && png_muldiv(&result, (png_int_32)ppm, 127, + 5000)) + return result; + + /* Overflow. */ + return 0; +#endif +} + +png_uint_32 PNGAPI +png_get_pixels_per_inch(png_const_structrp png_ptr, png_const_inforp info_ptr) +{ + return ppi_from_ppm(png_get_pixels_per_meter(png_ptr, info_ptr)); +} + +png_uint_32 PNGAPI +png_get_x_pixels_per_inch(png_const_structrp png_ptr, png_const_inforp info_ptr) +{ + return ppi_from_ppm(png_get_x_pixels_per_meter(png_ptr, info_ptr)); +} + +png_uint_32 PNGAPI +png_get_y_pixels_per_inch(png_const_structrp png_ptr, png_const_inforp info_ptr) +{ + return ppi_from_ppm(png_get_y_pixels_per_meter(png_ptr, info_ptr)); +} + +#ifdef PNG_FIXED_POINT_SUPPORTED +static png_fixed_point +png_fixed_inches_from_microns(png_const_structrp png_ptr, png_int_32 microns) +{ + /* Convert from metres * 1,000,000 to inches * 100,000, meters to + * inches is simply *(100/2.54), so we want *(10/2.54) == 500/127. + * Notice that this can overflow - a warning is output and 0 is + * returned. + */ + return png_muldiv_warn(png_ptr, microns, 500, 127); +} + +png_fixed_point PNGAPI +png_get_x_offset_inches_fixed(png_const_structrp png_ptr, + png_const_inforp info_ptr) +{ + return png_fixed_inches_from_microns(png_ptr, + png_get_x_offset_microns(png_ptr, info_ptr)); +} +#endif + +#ifdef PNG_FIXED_POINT_SUPPORTED +png_fixed_point PNGAPI +png_get_y_offset_inches_fixed(png_const_structrp png_ptr, + png_const_inforp info_ptr) +{ + return png_fixed_inches_from_microns(png_ptr, + png_get_y_offset_microns(png_ptr, info_ptr)); +} +#endif + +#ifdef PNG_FLOATING_POINT_SUPPORTED +float PNGAPI +png_get_x_offset_inches(png_const_structrp png_ptr, png_const_inforp info_ptr) +{ + /* To avoid the overflow do the conversion directly in floating + * point. + */ + return (float)(png_get_x_offset_microns(png_ptr, info_ptr) * .00003937); +} +#endif + +#ifdef PNG_FLOATING_POINT_SUPPORTED +float PNGAPI +png_get_y_offset_inches(png_const_structrp png_ptr, png_const_inforp info_ptr) +{ + /* To avoid the overflow do the conversion directly in floating + * point. + */ + return (float)(png_get_y_offset_microns(png_ptr, info_ptr) * .00003937); +} +#endif + +#ifdef PNG_pHYs_SUPPORTED +png_uint_32 PNGAPI +png_get_pHYs_dpi(png_const_structrp png_ptr, png_const_inforp info_ptr, + png_uint_32 *res_x, png_uint_32 *res_y, int *unit_type) +{ + png_uint_32 retval = 0; + + if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_pHYs)) + { + png_debug1(1, "in %s retrieval function", "pHYs"); + + if (res_x != NULL) + { + *res_x = info_ptr->x_pixels_per_unit; + retval |= PNG_INFO_pHYs; + } + + if (res_y != NULL) + { + *res_y = info_ptr->y_pixels_per_unit; + retval |= PNG_INFO_pHYs; + } + + if (unit_type != NULL) + { + *unit_type = (int)info_ptr->phys_unit_type; + retval |= PNG_INFO_pHYs; + + if (*unit_type == 1) + { + if (res_x != NULL) *res_x = (png_uint_32)(*res_x * .0254 + .50); + if (res_y != NULL) *res_y = (png_uint_32)(*res_y * .0254 + .50); + } + } + } + + return (retval); +} +#endif /* PNG_pHYs_SUPPORTED */ +#endif /* PNG_INCH_CONVERSIONS_SUPPORTED */ + +/* png_get_channels really belongs in here, too, but it's been around longer */ + +#endif /* PNG_EASY_ACCESS_SUPPORTED */ + + +png_byte PNGAPI +png_get_channels(png_const_structrp png_ptr, png_const_inforp info_ptr) +{ + if (png_ptr != NULL && info_ptr != NULL) + return(info_ptr->channels); + + return (0); +} + +#ifdef PNG_READ_SUPPORTED +png_const_bytep PNGAPI +png_get_signature(png_const_structrp png_ptr, png_const_inforp info_ptr) +{ + if (png_ptr != NULL && info_ptr != NULL) + return(info_ptr->signature); + + return (NULL); +} +#endif + +#ifdef PNG_bKGD_SUPPORTED +png_uint_32 PNGAPI +png_get_bKGD(png_const_structrp png_ptr, png_inforp info_ptr, + png_color_16p *background) +{ + if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_bKGD) + && background != NULL) + { + png_debug1(1, "in %s retrieval function", "bKGD"); + + *background = &(info_ptr->background); + return (PNG_INFO_bKGD); + } + + return (0); +} +#endif + +#ifdef PNG_cHRM_SUPPORTED +/* The XYZ APIs were added in 1.5.5 to take advantage of the code added at the + * same time to correct the rgb grayscale coefficient defaults obtained from the + * cHRM chunk in 1.5.4 + */ +# ifdef PNG_FLOATING_POINT_SUPPORTED +png_uint_32 PNGAPI +png_get_cHRM(png_const_structrp png_ptr, png_const_inforp info_ptr, + double *white_x, double *white_y, double *red_x, double *red_y, + double *green_x, double *green_y, double *blue_x, double *blue_y) +{ + /* Quiet API change: this code used to only return the end points if a cHRM + * chunk was present, but the end points can also come from iCCP or sRGB + * chunks, so in 1.6.0 the png_get_ APIs return the end points regardless and + * the png_set_ APIs merely check that set end points are mutually + * consistent. + */ + if (png_ptr != NULL && info_ptr != NULL && + (info_ptr->colorspace.flags & PNG_COLORSPACE_HAVE_ENDPOINTS)) + { + png_debug1(1, "in %s retrieval function", "cHRM"); + + if (white_x != NULL) + *white_x = png_float(png_ptr, + info_ptr->colorspace.end_points_xy.whitex, "cHRM white X"); + if (white_y != NULL) + *white_y = png_float(png_ptr, + info_ptr->colorspace.end_points_xy.whitey, "cHRM white Y"); + if (red_x != NULL) + *red_x = png_float(png_ptr, info_ptr->colorspace.end_points_xy.redx, + "cHRM red X"); + if (red_y != NULL) + *red_y = png_float(png_ptr, info_ptr->colorspace.end_points_xy.redy, + "cHRM red Y"); + if (green_x != NULL) + *green_x = png_float(png_ptr, + info_ptr->colorspace.end_points_xy.greenx, "cHRM green X"); + if (green_y != NULL) + *green_y = png_float(png_ptr, + info_ptr->colorspace.end_points_xy.greeny, "cHRM green Y"); + if (blue_x != NULL) + *blue_x = png_float(png_ptr, info_ptr->colorspace.end_points_xy.bluex, + "cHRM blue X"); + if (blue_y != NULL) + *blue_y = png_float(png_ptr, info_ptr->colorspace.end_points_xy.bluey, + "cHRM blue Y"); + return (PNG_INFO_cHRM); + } + + return (0); +} + +png_uint_32 PNGAPI +png_get_cHRM_XYZ(png_const_structrp png_ptr, png_const_inforp info_ptr, + double *red_X, double *red_Y, double *red_Z, double *green_X, + double *green_Y, double *green_Z, double *blue_X, double *blue_Y, + double *blue_Z) +{ + if (png_ptr != NULL && info_ptr != NULL && + (info_ptr->colorspace.flags & PNG_COLORSPACE_HAVE_ENDPOINTS)) + { + png_debug1(1, "in %s retrieval function", "cHRM_XYZ(float)"); + + if (red_X != NULL) + *red_X = png_float(png_ptr, info_ptr->colorspace.end_points_XYZ.red_X, + "cHRM red X"); + if (red_Y != NULL) + *red_Y = png_float(png_ptr, info_ptr->colorspace.end_points_XYZ.red_Y, + "cHRM red Y"); + if (red_Z != NULL) + *red_Z = png_float(png_ptr, info_ptr->colorspace.end_points_XYZ.red_Z, + "cHRM red Z"); + if (green_X != NULL) + *green_X = png_float(png_ptr, + info_ptr->colorspace.end_points_XYZ.green_X, "cHRM green X"); + if (green_Y != NULL) + *green_Y = png_float(png_ptr, + info_ptr->colorspace.end_points_XYZ.green_Y, "cHRM green Y"); + if (green_Z != NULL) + *green_Z = png_float(png_ptr, + info_ptr->colorspace.end_points_XYZ.green_Z, "cHRM green Z"); + if (blue_X != NULL) + *blue_X = png_float(png_ptr, + info_ptr->colorspace.end_points_XYZ.blue_X, "cHRM blue X"); + if (blue_Y != NULL) + *blue_Y = png_float(png_ptr, + info_ptr->colorspace.end_points_XYZ.blue_Y, "cHRM blue Y"); + if (blue_Z != NULL) + *blue_Z = png_float(png_ptr, + info_ptr->colorspace.end_points_XYZ.blue_Z, "cHRM blue Z"); + return (PNG_INFO_cHRM); + } + + return (0); +} +# endif + +# ifdef PNG_FIXED_POINT_SUPPORTED +png_uint_32 PNGAPI +png_get_cHRM_XYZ_fixed(png_const_structrp png_ptr, png_const_inforp info_ptr, + png_fixed_point *int_red_X, png_fixed_point *int_red_Y, + png_fixed_point *int_red_Z, png_fixed_point *int_green_X, + png_fixed_point *int_green_Y, png_fixed_point *int_green_Z, + png_fixed_point *int_blue_X, png_fixed_point *int_blue_Y, + png_fixed_point *int_blue_Z) +{ + if (png_ptr != NULL && info_ptr != NULL && + (info_ptr->colorspace.flags & PNG_COLORSPACE_HAVE_ENDPOINTS)) + { + png_debug1(1, "in %s retrieval function", "cHRM_XYZ"); + + if (int_red_X != NULL) + *int_red_X = info_ptr->colorspace.end_points_XYZ.red_X; + if (int_red_Y != NULL) + *int_red_Y = info_ptr->colorspace.end_points_XYZ.red_Y; + if (int_red_Z != NULL) + *int_red_Z = info_ptr->colorspace.end_points_XYZ.red_Z; + if (int_green_X != NULL) + *int_green_X = info_ptr->colorspace.end_points_XYZ.green_X; + if (int_green_Y != NULL) + *int_green_Y = info_ptr->colorspace.end_points_XYZ.green_Y; + if (int_green_Z != NULL) + *int_green_Z = info_ptr->colorspace.end_points_XYZ.green_Z; + if (int_blue_X != NULL) + *int_blue_X = info_ptr->colorspace.end_points_XYZ.blue_X; + if (int_blue_Y != NULL) + *int_blue_Y = info_ptr->colorspace.end_points_XYZ.blue_Y; + if (int_blue_Z != NULL) + *int_blue_Z = info_ptr->colorspace.end_points_XYZ.blue_Z; + return (PNG_INFO_cHRM); + } + + return (0); +} + +png_uint_32 PNGAPI +png_get_cHRM_fixed(png_const_structrp png_ptr, png_const_inforp info_ptr, + png_fixed_point *white_x, png_fixed_point *white_y, png_fixed_point *red_x, + png_fixed_point *red_y, png_fixed_point *green_x, png_fixed_point *green_y, + png_fixed_point *blue_x, png_fixed_point *blue_y) +{ + png_debug1(1, "in %s retrieval function", "cHRM"); + + if (png_ptr != NULL && info_ptr != NULL && + (info_ptr->colorspace.flags & PNG_COLORSPACE_HAVE_ENDPOINTS)) + { + if (white_x != NULL) + *white_x = info_ptr->colorspace.end_points_xy.whitex; + if (white_y != NULL) + *white_y = info_ptr->colorspace.end_points_xy.whitey; + if (red_x != NULL) + *red_x = info_ptr->colorspace.end_points_xy.redx; + if (red_y != NULL) + *red_y = info_ptr->colorspace.end_points_xy.redy; + if (green_x != NULL) + *green_x = info_ptr->colorspace.end_points_xy.greenx; + if (green_y != NULL) + *green_y = info_ptr->colorspace.end_points_xy.greeny; + if (blue_x != NULL) + *blue_x = info_ptr->colorspace.end_points_xy.bluex; + if (blue_y != NULL) + *blue_y = info_ptr->colorspace.end_points_xy.bluey; + return (PNG_INFO_cHRM); + } + + return (0); +} +# endif +#endif + +#ifdef PNG_gAMA_SUPPORTED +# ifdef PNG_FIXED_POINT_SUPPORTED +png_uint_32 PNGAPI +png_get_gAMA_fixed(png_const_structrp png_ptr, png_const_inforp info_ptr, + png_fixed_point *file_gamma) +{ + png_debug1(1, "in %s retrieval function", "gAMA"); + + if (png_ptr != NULL && info_ptr != NULL && + (info_ptr->colorspace.flags & PNG_COLORSPACE_HAVE_GAMMA) && + file_gamma != NULL) + { + *file_gamma = info_ptr->colorspace.gamma; + return (PNG_INFO_gAMA); + } + + return (0); +} +# endif + +# ifdef PNG_FLOATING_POINT_SUPPORTED +png_uint_32 PNGAPI +png_get_gAMA(png_const_structrp png_ptr, png_const_inforp info_ptr, + double *file_gamma) +{ + png_debug1(1, "in %s retrieval function", "gAMA(float)"); + + if (png_ptr != NULL && info_ptr != NULL && + (info_ptr->colorspace.flags & PNG_COLORSPACE_HAVE_GAMMA) && + file_gamma != NULL) + { + *file_gamma = png_float(png_ptr, info_ptr->colorspace.gamma, + "png_get_gAMA"); + return (PNG_INFO_gAMA); + } + + return (0); +} +# endif +#endif + +#ifdef PNG_sRGB_SUPPORTED +png_uint_32 PNGAPI +png_get_sRGB(png_const_structrp png_ptr, png_const_inforp info_ptr, + int *file_srgb_intent) +{ + png_debug1(1, "in %s retrieval function", "sRGB"); + + if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_sRGB) + && file_srgb_intent != NULL) + { + *file_srgb_intent = info_ptr->colorspace.rendering_intent; + return (PNG_INFO_sRGB); + } + + return (0); +} +#endif + +#ifdef PNG_iCCP_SUPPORTED +png_uint_32 PNGAPI +png_get_iCCP(png_const_structrp png_ptr, png_inforp info_ptr, + png_charpp name, int *compression_type, + png_bytepp profile, png_uint_32 *proflen) +{ + png_debug1(1, "in %s retrieval function", "iCCP"); + + if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_iCCP) + && name != NULL && compression_type != NULL && profile != NULL && + proflen != NULL) + { + *name = info_ptr->iccp_name; + *profile = info_ptr->iccp_profile; + *proflen = png_get_uint_32(info_ptr->iccp_profile); + /* This is somewhat irrelevant since the profile data returned has + * actually been uncompressed. + */ + *compression_type = PNG_COMPRESSION_TYPE_BASE; + return (PNG_INFO_iCCP); + } + + return (0); +} +#endif + +#ifdef PNG_sPLT_SUPPORTED +int PNGAPI +png_get_sPLT(png_const_structrp png_ptr, png_inforp info_ptr, + png_sPLT_tpp spalettes) +{ + if (png_ptr != NULL && info_ptr != NULL && spalettes != NULL) + { + *spalettes = info_ptr->splt_palettes; + return info_ptr->splt_palettes_num; + } + + return (0); +} +#endif + +#ifdef PNG_hIST_SUPPORTED +png_uint_32 PNGAPI +png_get_hIST(png_const_structrp png_ptr, png_inforp info_ptr, + png_uint_16p *hist) +{ + png_debug1(1, "in %s retrieval function", "hIST"); + + if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_hIST) + && hist != NULL) + { + *hist = info_ptr->hist; + return (PNG_INFO_hIST); + } + + return (0); +} +#endif + +png_uint_32 PNGAPI +png_get_IHDR(png_const_structrp png_ptr, png_const_inforp info_ptr, + png_uint_32 *width, png_uint_32 *height, int *bit_depth, + int *color_type, int *interlace_type, int *compression_type, + int *filter_type) +{ + png_debug1(1, "in %s retrieval function", "IHDR"); + + if (png_ptr == NULL || info_ptr == NULL || width == NULL || + height == NULL || bit_depth == NULL || color_type == NULL) + return (0); + + *width = info_ptr->width; + *height = info_ptr->height; + *bit_depth = info_ptr->bit_depth; + *color_type = info_ptr->color_type; + + if (compression_type != NULL) + *compression_type = info_ptr->compression_type; + + if (filter_type != NULL) + *filter_type = info_ptr->filter_type; + + if (interlace_type != NULL) + *interlace_type = info_ptr->interlace_type; + + /* This is redundant if we can be sure that the info_ptr values were all + * assigned in png_set_IHDR(). We do the check anyhow in case an + * application has ignored our advice not to mess with the members + * of info_ptr directly. + */ + png_check_IHDR(png_ptr, info_ptr->width, info_ptr->height, + info_ptr->bit_depth, info_ptr->color_type, info_ptr->interlace_type, + info_ptr->compression_type, info_ptr->filter_type); + + return (1); +} + +#ifdef PNG_oFFs_SUPPORTED +png_uint_32 PNGAPI +png_get_oFFs(png_const_structrp png_ptr, png_const_inforp info_ptr, + png_int_32 *offset_x, png_int_32 *offset_y, int *unit_type) +{ + png_debug1(1, "in %s retrieval function", "oFFs"); + + if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_oFFs) + && offset_x != NULL && offset_y != NULL && unit_type != NULL) + { + *offset_x = info_ptr->x_offset; + *offset_y = info_ptr->y_offset; + *unit_type = (int)info_ptr->offset_unit_type; + return (PNG_INFO_oFFs); + } + + return (0); +} +#endif + +#ifdef PNG_pCAL_SUPPORTED +png_uint_32 PNGAPI +png_get_pCAL(png_const_structrp png_ptr, png_inforp info_ptr, + png_charp *purpose, png_int_32 *X0, png_int_32 *X1, int *type, int *nparams, + png_charp *units, png_charpp *params) +{ + png_debug1(1, "in %s retrieval function", "pCAL"); + + if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_pCAL) + && purpose != NULL && X0 != NULL && X1 != NULL && type != NULL && + nparams != NULL && units != NULL && params != NULL) + { + *purpose = info_ptr->pcal_purpose; + *X0 = info_ptr->pcal_X0; + *X1 = info_ptr->pcal_X1; + *type = (int)info_ptr->pcal_type; + *nparams = (int)info_ptr->pcal_nparams; + *units = info_ptr->pcal_units; + *params = info_ptr->pcal_params; + return (PNG_INFO_pCAL); + } + + return (0); +} +#endif + +#ifdef PNG_sCAL_SUPPORTED +# ifdef PNG_FIXED_POINT_SUPPORTED +# if defined(PNG_FLOATING_ARITHMETIC_SUPPORTED) || \ + defined(PNG_FLOATING_POINT_SUPPORTED) +png_uint_32 PNGAPI +png_get_sCAL_fixed(png_const_structrp png_ptr, png_const_inforp info_ptr, + int *unit, png_fixed_point *width, png_fixed_point *height) +{ + if (png_ptr != NULL && info_ptr != NULL && + (info_ptr->valid & PNG_INFO_sCAL)) + { + *unit = info_ptr->scal_unit; + /*TODO: make this work without FP support; the API is currently eliminated + * if neither floating point APIs nor internal floating point arithmetic + * are enabled. + */ + *width = png_fixed(png_ptr, atof(info_ptr->scal_s_width), "sCAL width"); + *height = png_fixed(png_ptr, atof(info_ptr->scal_s_height), + "sCAL height"); + return (PNG_INFO_sCAL); + } + + return(0); +} +# endif /* FLOATING_ARITHMETIC */ +# endif /* FIXED_POINT */ +# ifdef PNG_FLOATING_POINT_SUPPORTED +png_uint_32 PNGAPI +png_get_sCAL(png_const_structrp png_ptr, png_const_inforp info_ptr, + int *unit, double *width, double *height) +{ + if (png_ptr != NULL && info_ptr != NULL && + (info_ptr->valid & PNG_INFO_sCAL)) + { + *unit = info_ptr->scal_unit; + *width = atof(info_ptr->scal_s_width); + *height = atof(info_ptr->scal_s_height); + return (PNG_INFO_sCAL); + } + + return(0); +} +# endif /* FLOATING POINT */ +png_uint_32 PNGAPI +png_get_sCAL_s(png_const_structrp png_ptr, png_const_inforp info_ptr, + int *unit, png_charpp width, png_charpp height) +{ + if (png_ptr != NULL && info_ptr != NULL && + (info_ptr->valid & PNG_INFO_sCAL)) + { + *unit = info_ptr->scal_unit; + *width = info_ptr->scal_s_width; + *height = info_ptr->scal_s_height; + return (PNG_INFO_sCAL); + } + + return(0); +} +#endif /* sCAL */ + +#ifdef PNG_pHYs_SUPPORTED +png_uint_32 PNGAPI +png_get_pHYs(png_const_structrp png_ptr, png_const_inforp info_ptr, + png_uint_32 *res_x, png_uint_32 *res_y, int *unit_type) +{ + png_uint_32 retval = 0; + + png_debug1(1, "in %s retrieval function", "pHYs"); + + if (png_ptr != NULL && info_ptr != NULL && + (info_ptr->valid & PNG_INFO_pHYs)) + { + if (res_x != NULL) + { + *res_x = info_ptr->x_pixels_per_unit; + retval |= PNG_INFO_pHYs; + } + + if (res_y != NULL) + { + *res_y = info_ptr->y_pixels_per_unit; + retval |= PNG_INFO_pHYs; + } + + if (unit_type != NULL) + { + *unit_type = (int)info_ptr->phys_unit_type; + retval |= PNG_INFO_pHYs; + } + } + + return (retval); +} +#endif /* pHYs */ + +png_uint_32 PNGAPI +png_get_PLTE(png_const_structrp png_ptr, png_inforp info_ptr, + png_colorp *palette, int *num_palette) +{ + png_debug1(1, "in %s retrieval function", "PLTE"); + + if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_PLTE) + && palette != NULL) + { + *palette = info_ptr->palette; + *num_palette = info_ptr->num_palette; + png_debug1(3, "num_palette = %d", *num_palette); + return (PNG_INFO_PLTE); + } + + return (0); +} + +#ifdef PNG_sBIT_SUPPORTED +png_uint_32 PNGAPI +png_get_sBIT(png_const_structrp png_ptr, png_inforp info_ptr, + png_color_8p *sig_bit) +{ + png_debug1(1, "in %s retrieval function", "sBIT"); + + if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_sBIT) + && sig_bit != NULL) + { + *sig_bit = &(info_ptr->sig_bit); + return (PNG_INFO_sBIT); + } + + return (0); +} +#endif + +#ifdef PNG_TEXT_SUPPORTED +int PNGAPI +png_get_text(png_const_structrp png_ptr, png_inforp info_ptr, + png_textp *text_ptr, int *num_text) +{ + if (png_ptr != NULL && info_ptr != NULL && info_ptr->num_text > 0) + { + png_debug1(1, "in 0x%lx retrieval function", + (unsigned long)png_ptr->chunk_name); + + if (text_ptr != NULL) + *text_ptr = info_ptr->text; + + if (num_text != NULL) + *num_text = info_ptr->num_text; + + return info_ptr->num_text; + } + + if (num_text != NULL) + *num_text = 0; + + return(0); +} +#endif + +#ifdef PNG_tIME_SUPPORTED +png_uint_32 PNGAPI +png_get_tIME(png_const_structrp png_ptr, png_inforp info_ptr, + png_timep *mod_time) +{ + png_debug1(1, "in %s retrieval function", "tIME"); + + if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_tIME) + && mod_time != NULL) + { + *mod_time = &(info_ptr->mod_time); + return (PNG_INFO_tIME); + } + + return (0); +} +#endif + +#ifdef PNG_tRNS_SUPPORTED +png_uint_32 PNGAPI +png_get_tRNS(png_const_structrp png_ptr, png_inforp info_ptr, + png_bytep *trans_alpha, int *num_trans, png_color_16p *trans_color) +{ + png_uint_32 retval = 0; + if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_tRNS)) + { + png_debug1(1, "in %s retrieval function", "tRNS"); + + if (info_ptr->color_type == PNG_COLOR_TYPE_PALETTE) + { + if (trans_alpha != NULL) + { + *trans_alpha = info_ptr->trans_alpha; + retval |= PNG_INFO_tRNS; + } + + if (trans_color != NULL) + *trans_color = &(info_ptr->trans_color); + } + + else /* if (info_ptr->color_type != PNG_COLOR_TYPE_PALETTE) */ + { + if (trans_color != NULL) + { + *trans_color = &(info_ptr->trans_color); + retval |= PNG_INFO_tRNS; + } + + if (trans_alpha != NULL) + *trans_alpha = NULL; + } + + if (num_trans != NULL) + { + *num_trans = info_ptr->num_trans; + retval |= PNG_INFO_tRNS; + } + } + + return (retval); +} +#endif + +#ifdef PNG_STORE_UNKNOWN_CHUNKS_SUPPORTED +int PNGAPI +png_get_unknown_chunks(png_const_structrp png_ptr, png_inforp info_ptr, + png_unknown_chunkpp unknowns) +{ + if (png_ptr != NULL && info_ptr != NULL && unknowns != NULL) + { + *unknowns = info_ptr->unknown_chunks; + return info_ptr->unknown_chunks_num; + } + + return (0); +} +#endif + +#ifdef PNG_READ_RGB_TO_GRAY_SUPPORTED +png_byte PNGAPI +png_get_rgb_to_gray_status (png_const_structrp png_ptr) +{ + return (png_byte)(png_ptr ? png_ptr->rgb_to_gray_status : 0); +} +#endif + +#ifdef PNG_USER_CHUNKS_SUPPORTED +png_voidp PNGAPI +png_get_user_chunk_ptr(png_const_structrp png_ptr) +{ + return (png_ptr ? png_ptr->user_chunk_ptr : NULL); +} +#endif + +png_size_t PNGAPI +png_get_compression_buffer_size(png_const_structrp png_ptr) +{ + if (png_ptr == NULL) + return 0; + +# ifdef PNG_WRITE_SUPPORTED + if (png_ptr->mode & PNG_IS_READ_STRUCT) +# endif + { +# ifdef PNG_SEQUENTIAL_READ_SUPPORTED + return png_ptr->IDAT_read_size; +# else + return PNG_IDAT_READ_SIZE; +# endif + } + +# ifdef PNG_WRITE_SUPPORTED + else + return png_ptr->zbuffer_size; +# endif +} + +#ifdef PNG_SET_USER_LIMITS_SUPPORTED +/* These functions were added to libpng 1.2.6 and were enabled + * by default in libpng-1.4.0 */ +png_uint_32 PNGAPI +png_get_user_width_max (png_const_structrp png_ptr) +{ + return (png_ptr ? png_ptr->user_width_max : 0); +} + +png_uint_32 PNGAPI +png_get_user_height_max (png_const_structrp png_ptr) +{ + return (png_ptr ? png_ptr->user_height_max : 0); +} + +/* This function was added to libpng 1.4.0 */ +png_uint_32 PNGAPI +png_get_chunk_cache_max (png_const_structrp png_ptr) +{ + return (png_ptr ? png_ptr->user_chunk_cache_max : 0); +} + +/* This function was added to libpng 1.4.1 */ +png_alloc_size_t PNGAPI +png_get_chunk_malloc_max (png_const_structrp png_ptr) +{ + return (png_ptr ? png_ptr->user_chunk_malloc_max : 0); +} +#endif /* ?PNG_SET_USER_LIMITS_SUPPORTED */ + +/* These functions were added to libpng 1.4.0 */ +#ifdef PNG_IO_STATE_SUPPORTED +png_uint_32 PNGAPI +png_get_io_state (png_const_structrp png_ptr) +{ + return png_ptr->io_state; +} + +png_uint_32 PNGAPI +png_get_io_chunk_type (png_const_structrp png_ptr) +{ + return png_ptr->chunk_name; +} +#endif /* ?PNG_IO_STATE_SUPPORTED */ + +#ifdef PNG_CHECK_FOR_INVALID_INDEX_SUPPORTED +# ifdef PNG_GET_PALETTE_MAX_SUPPORTED +int PNGAPI +png_get_palette_max(png_const_structp png_ptr, png_const_infop info_ptr) +{ + if (png_ptr != NULL && info_ptr != NULL) + return png_ptr->num_palette_max; + + return (-1); +} +# endif +#endif + +#endif /* PNG_READ_SUPPORTED || PNG_WRITE_SUPPORTED */ diff --git a/Minecraft.Client/Windows64/4JLibs/inc/Render/libpng/pnginfo.h b/Minecraft.Client/Windows64/4JLibs/inc/Render/libpng/pnginfo.h new file mode 100644 index 000000000..26bf26502 --- /dev/null +++ b/Minecraft.Client/Windows64/4JLibs/inc/Render/libpng/pnginfo.h @@ -0,0 +1,260 @@ + +/* pnginfo.h - header file for PNG reference library + * + * Copyright (c) 1998-2013 Glenn Randers-Pehrson + * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) + * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) + * + * Last changed in libpng 1.6.1 [March 28, 2013] + * + * This code is released under the libpng license. + * For conditions of distribution and use, see the disclaimer + * and license in png.h + */ + + /* png_info is a structure that holds the information in a PNG file so + * that the application can find out the characteristics of the image. + * If you are reading the file, this structure will tell you what is + * in the PNG file. If you are writing the file, fill in the information + * you want to put into the PNG file, using png_set_*() functions, then + * call png_write_info(). + * + * The names chosen should be very close to the PNG specification, so + * consult that document for information about the meaning of each field. + * + * With libpng < 0.95, it was only possible to directly set and read the + * the values in the png_info_struct, which meant that the contents and + * order of the values had to remain fixed. With libpng 0.95 and later, + * however, there are now functions that abstract the contents of + * png_info_struct from the application, so this makes it easier to use + * libpng with dynamic libraries, and even makes it possible to use + * libraries that don't have all of the libpng ancillary chunk-handing + * functionality. In libpng-1.5.0 this was moved into a separate private + * file that is not visible to applications. + * + * The following members may have allocated storage attached that should be + * cleaned up before the structure is discarded: palette, trans, text, + * pcal_purpose, pcal_units, pcal_params, hist, iccp_name, iccp_profile, + * splt_palettes, scal_unit, row_pointers, and unknowns. By default, these + * are automatically freed when the info structure is deallocated, if they were + * allocated internally by libpng. This behavior can be changed by means + * of the png_data_freer() function. + * + * More allocation details: all the chunk-reading functions that + * change these members go through the corresponding png_set_* + * functions. A function to clear these members is available: see + * png_free_data(). The png_set_* functions do not depend on being + * able to point info structure members to any of the storage they are + * passed (they make their own copies), EXCEPT that the png_set_text + * functions use the same storage passed to them in the text_ptr or + * itxt_ptr structure argument, and the png_set_rows and png_set_unknowns + * functions do not make their own copies. + */ +#ifndef PNGINFO_H +#define PNGINFO_H + +struct png_info_def +{ + /* The following are necessary for every PNG file */ + png_uint_32 width; /* width of image in pixels (from IHDR) */ + png_uint_32 height; /* height of image in pixels (from IHDR) */ + png_uint_32 valid; /* valid chunk data (see PNG_INFO_ below) */ + png_size_t rowbytes; /* bytes needed to hold an untransformed row */ + png_colorp palette; /* array of color values (valid & PNG_INFO_PLTE) */ + png_uint_16 num_palette; /* number of color entries in "palette" (PLTE) */ + png_uint_16 num_trans; /* number of transparent palette color (tRNS) */ + png_byte bit_depth; /* 1, 2, 4, 8, or 16 bits/channel (from IHDR) */ + png_byte color_type; /* see PNG_COLOR_TYPE_ below (from IHDR) */ + /* The following three should have been named *_method not *_type */ + png_byte compression_type; /* must be PNG_COMPRESSION_TYPE_BASE (IHDR) */ + png_byte filter_type; /* must be PNG_FILTER_TYPE_BASE (from IHDR) */ + png_byte interlace_type; /* One of PNG_INTERLACE_NONE, PNG_INTERLACE_ADAM7 */ + + /* The following are set by png_set_IHDR, called from the application on + * write, but the are never actually used by the write code. + */ + png_byte channels; /* number of data channels per pixel (1, 2, 3, 4) */ + png_byte pixel_depth; /* number of bits per pixel */ + png_byte spare_byte; /* to align the data, and for future use */ + +#ifdef PNG_READ_SUPPORTED + /* This is never set during write */ + png_byte signature[8]; /* magic bytes read by libpng from start of file */ +#endif + + /* The rest of the data is optional. If you are reading, check the + * valid field to see if the information in these are valid. If you + * are writing, set the valid field to those chunks you want written, + * and initialize the appropriate fields below. + */ + +#if defined(PNG_COLORSPACE_SUPPORTED) || defined(PNG_GAMMA_SUPPORTED) + /* png_colorspace only contains 'flags' if neither GAMMA or COLORSPACE are + * defined. When COLORSPACE is switched on all the colorspace-defining + * chunks should be enabled, when GAMMA is switched on all the gamma-defining + * chunks should be enabled. If this is not done it becomes possible to read + * inconsistent PNG files and assign a probably incorrect interpretation to + * the information. (In other words, by carefully choosing which chunks to + * recognize the system configuration can select an interpretation for PNG + * files containing ambiguous data and this will result in inconsistent + * behavior between different libpng builds!) + */ + png_colorspace colorspace; +#endif + +#ifdef PNG_iCCP_SUPPORTED + /* iCCP chunk data. */ + png_charp iccp_name; /* profile name */ + png_bytep iccp_profile; /* International Color Consortium profile data */ + png_uint_32 iccp_proflen; /* ICC profile data length */ +#endif + +#ifdef PNG_TEXT_SUPPORTED + /* The tEXt, and zTXt chunks contain human-readable textual data in + * uncompressed, compressed, and optionally compressed forms, respectively. + * The data in "text" is an array of pointers to uncompressed, + * null-terminated C strings. Each chunk has a keyword that describes the + * textual data contained in that chunk. Keywords are not required to be + * unique, and the text string may be empty. Any number of text chunks may + * be in an image. + */ + int num_text; /* number of comments read or comments to write */ + int max_text; /* current size of text array */ + png_textp text; /* array of comments read or comments to write */ +#endif /* PNG_TEXT_SUPPORTED */ + +#ifdef PNG_tIME_SUPPORTED + /* The tIME chunk holds the last time the displayed image data was + * modified. See the png_time struct for the contents of this struct. + */ + png_time mod_time; +#endif + +#ifdef PNG_sBIT_SUPPORTED + /* The sBIT chunk specifies the number of significant high-order bits + * in the pixel data. Values are in the range [1, bit_depth], and are + * only specified for the channels in the pixel data. The contents of + * the low-order bits is not specified. Data is valid if + * (valid & PNG_INFO_sBIT) is non-zero. + */ + png_color_8 sig_bit; /* significant bits in color channels */ +#endif + +#if defined(PNG_tRNS_SUPPORTED) || defined(PNG_READ_EXPAND_SUPPORTED) || \ +defined(PNG_READ_BACKGROUND_SUPPORTED) + /* The tRNS chunk supplies transparency data for paletted images and + * other image types that don't need a full alpha channel. There are + * "num_trans" transparency values for a paletted image, stored in the + * same order as the palette colors, starting from index 0. Values + * for the data are in the range [0, 255], ranging from fully transparent + * to fully opaque, respectively. For non-paletted images, there is a + * single color specified that should be treated as fully transparent. + * Data is valid if (valid & PNG_INFO_tRNS) is non-zero. + */ + png_bytep trans_alpha; /* alpha values for paletted image */ + png_color_16 trans_color; /* transparent color for non-palette image */ +#endif + +#if defined(PNG_bKGD_SUPPORTED) || defined(PNG_READ_BACKGROUND_SUPPORTED) + /* The bKGD chunk gives the suggested image background color if the + * display program does not have its own background color and the image + * is needs to composited onto a background before display. The colors + * in "background" are normally in the same color space/depth as the + * pixel data. Data is valid if (valid & PNG_INFO_bKGD) is non-zero. + */ + png_color_16 background; +#endif + +#ifdef PNG_oFFs_SUPPORTED + /* The oFFs chunk gives the offset in "offset_unit_type" units rightwards + * and downwards from the top-left corner of the display, page, or other + * application-specific co-ordinate space. See the PNG_OFFSET_ defines + * below for the unit types. Valid if (valid & PNG_INFO_oFFs) non-zero. + */ + png_int_32 x_offset; /* x offset on page */ + png_int_32 y_offset; /* y offset on page */ + png_byte offset_unit_type; /* offset units type */ +#endif + +#ifdef PNG_pHYs_SUPPORTED + /* The pHYs chunk gives the physical pixel density of the image for + * display or printing in "phys_unit_type" units (see PNG_RESOLUTION_ + * defines below). Data is valid if (valid & PNG_INFO_pHYs) is non-zero. + */ + png_uint_32 x_pixels_per_unit; /* horizontal pixel density */ + png_uint_32 y_pixels_per_unit; /* vertical pixel density */ + png_byte phys_unit_type; /* resolution type (see PNG_RESOLUTION_ below) */ +#endif + +#ifdef PNG_hIST_SUPPORTED + /* The hIST chunk contains the relative frequency or importance of the + * various palette entries, so that a viewer can intelligently select a + * reduced-color palette, if required. Data is an array of "num_palette" + * values in the range [0,65535]. Data valid if (valid & PNG_INFO_hIST) + * is non-zero. + */ + png_uint_16p hist; +#endif + +#ifdef PNG_pCAL_SUPPORTED + /* The pCAL chunk describes a transformation between the stored pixel + * values and original physical data values used to create the image. + * The integer range [0, 2^bit_depth - 1] maps to the floating-point + * range given by [pcal_X0, pcal_X1], and are further transformed by a + * (possibly non-linear) transformation function given by "pcal_type" + * and "pcal_params" into "pcal_units". Please see the PNG_EQUATION_ + * defines below, and the PNG-Group's PNG extensions document for a + * complete description of the transformations and how they should be + * implemented, and for a description of the ASCII parameter strings. + * Data values are valid if (valid & PNG_INFO_pCAL) non-zero. + */ + png_charp pcal_purpose; /* pCAL chunk description string */ + png_int_32 pcal_X0; /* minimum value */ + png_int_32 pcal_X1; /* maximum value */ + png_charp pcal_units; /* Latin-1 string giving physical units */ + png_charpp pcal_params; /* ASCII strings containing parameter values */ + png_byte pcal_type; /* equation type (see PNG_EQUATION_ below) */ + png_byte pcal_nparams; /* number of parameters given in pcal_params */ +#endif + +/* New members added in libpng-1.0.6 */ + png_uint_32 free_me; /* flags items libpng is responsible for freeing */ + +#ifdef PNG_STORE_UNKNOWN_CHUNKS_SUPPORTED + /* Storage for unknown chunks that the library doesn't recognize. */ + png_unknown_chunkp unknown_chunks; + + /* The type of this field is limited by the type of + * png_struct::user_chunk_cache_max, else overflow can occur. + */ + int unknown_chunks_num; +#endif + +#ifdef PNG_sPLT_SUPPORTED + /* Data on sPLT chunks (there may be more than one). */ + png_sPLT_tp splt_palettes; + int splt_palettes_num; /* Match type returned by png_get API */ +#endif + +#ifdef PNG_sCAL_SUPPORTED + /* The sCAL chunk describes the actual physical dimensions of the + * subject matter of the graphic. The chunk contains a unit specification + * a byte value, and two ASCII strings representing floating-point + * values. The values are width and height corresponsing to one pixel + * in the image. Data values are valid if (valid & PNG_INFO_sCAL) is + * non-zero. + */ + png_byte scal_unit; /* unit of physical scale */ + png_charp scal_s_width; /* string containing height */ + png_charp scal_s_height; /* string containing width */ +#endif + +#ifdef PNG_INFO_IMAGE_SUPPORTED + /* Memory has been allocated if (valid & PNG_ALLOCATED_INFO_ROWS) + non-zero */ + /* Data valid if (valid & PNG_INFO_IDAT) non-zero */ + png_bytepp row_pointers; /* the image bits */ +#endif + +}; +#endif /* PNGINFO_H */ diff --git a/Minecraft.Client/Windows64/4JLibs/inc/Render/libpng/pnglibconf.h b/Minecraft.Client/Windows64/4JLibs/inc/Render/libpng/pnglibconf.h new file mode 100644 index 000000000..d616b57b6 --- /dev/null +++ b/Minecraft.Client/Windows64/4JLibs/inc/Render/libpng/pnglibconf.h @@ -0,0 +1,211 @@ +/* libpng 1.6.2 STANDARD API DEFINITION */ + +/* pnglibconf.h - library build configuration */ + +/* Libpng version 1.6.2 - April 25, 2013 */ + +/* Copyright (c) 1998-2013 Glenn Randers-Pehrson */ + +/* This code is released under the libpng license. */ +/* For conditions of distribution and use, see the disclaimer */ +/* and license in png.h */ + +/* pnglibconf.h */ +/* Machine generated file: DO NOT EDIT */ +/* Derived from: scripts/pnglibconf.dfa */ +#ifndef PNGLCONF_H +#define PNGLCONF_H +/* options */ +#define PNG_16BIT_SUPPORTED +#define PNG_ALIGNED_MEMORY_SUPPORTED +/*#undef PNG_ARM_NEON_API_SUPPORTED*/ +/*#undef PNG_ARM_NEON_CHECK_SUPPORTED*/ +/*#undef PNG_ARM_NEON_SUPPORTED*/ +#define PNG_BENIGN_ERRORS_SUPPORTED +#define PNG_BENIGN_READ_ERRORS_SUPPORTED +/*#undef PNG_BENIGN_WRITE_ERRORS_SUPPORTED*/ +#define PNG_BUILD_GRAYSCALE_PALETTE_SUPPORTED +#define PNG_CHECK_FOR_INVALID_INDEX_SUPPORTED +#define PNG_COLORSPACE_SUPPORTED +//#define PNG_CONSOLE_IO_SUPPORTED +#define PNG_CONVERT_tIME_SUPPORTED +#define PNG_EASY_ACCESS_SUPPORTED +/*#undef PNG_ERROR_NUMBERS_SUPPORTED*/ +#define PNG_ERROR_TEXT_SUPPORTED +#define PNG_FIXED_POINT_SUPPORTED +#define PNG_FLOATING_ARITHMETIC_SUPPORTED +#define PNG_FLOATING_POINT_SUPPORTED +#define PNG_FORMAT_AFIRST_SUPPORTED +#define PNG_FORMAT_BGR_SUPPORTED +#define PNG_GAMMA_SUPPORTED +#define PNG_GET_PALETTE_MAX_SUPPORTED +#define PNG_HANDLE_AS_UNKNOWN_SUPPORTED +#define PNG_INCH_CONVERSIONS_SUPPORTED +#define PNG_INFO_IMAGE_SUPPORTED +#define PNG_IO_STATE_SUPPORTED +#define PNG_MNG_FEATURES_SUPPORTED +#define PNG_POINTER_INDEXING_SUPPORTED +#define PNG_PROGRESSIVE_READ_SUPPORTED +#define PNG_READ_16BIT_SUPPORTED +#define PNG_READ_ALPHA_MODE_SUPPORTED +#define PNG_READ_ANCILLARY_CHUNKS_SUPPORTED +#define PNG_READ_BACKGROUND_SUPPORTED +#define PNG_READ_BGR_SUPPORTED +#define PNG_READ_CHECK_FOR_INVALID_INDEX_SUPPORTED +#define PNG_READ_COMPOSITE_NODIV_SUPPORTED +#define PNG_READ_COMPRESSED_TEXT_SUPPORTED +#define PNG_READ_EXPAND_16_SUPPORTED +#define PNG_READ_EXPAND_SUPPORTED +#define PNG_READ_FILLER_SUPPORTED +#define PNG_READ_GAMMA_SUPPORTED +#define PNG_READ_GET_PALETTE_MAX_SUPPORTED +#define PNG_READ_GRAY_TO_RGB_SUPPORTED +#define PNG_READ_INTERLACING_SUPPORTED +#define PNG_READ_INT_FUNCTIONS_SUPPORTED +#define PNG_READ_INVERT_ALPHA_SUPPORTED +#define PNG_READ_INVERT_SUPPORTED +#define PNG_READ_OPT_PLTE_SUPPORTED +#define PNG_READ_PACKSWAP_SUPPORTED +#define PNG_READ_PACK_SUPPORTED +#define PNG_READ_QUANTIZE_SUPPORTED +#define PNG_READ_RGB_TO_GRAY_SUPPORTED +#define PNG_READ_SCALE_16_TO_8_SUPPORTED +#define PNG_READ_SHIFT_SUPPORTED +#define PNG_READ_STRIP_16_TO_8_SUPPORTED +#define PNG_READ_STRIP_ALPHA_SUPPORTED +#define PNG_READ_SUPPORTED +#define PNG_READ_SWAP_ALPHA_SUPPORTED +#define PNG_READ_SWAP_SUPPORTED +#define PNG_READ_TEXT_SUPPORTED +#define PNG_READ_TRANSFORMS_SUPPORTED +#define PNG_READ_UNKNOWN_CHUNKS_SUPPORTED +#define PNG_READ_USER_CHUNKS_SUPPORTED +#define PNG_READ_USER_TRANSFORM_SUPPORTED +#define PNG_READ_bKGD_SUPPORTED +#define PNG_READ_cHRM_SUPPORTED +#define PNG_READ_gAMA_SUPPORTED +#define PNG_READ_hIST_SUPPORTED +#define PNG_READ_iCCP_SUPPORTED +#define PNG_READ_iTXt_SUPPORTED +#define PNG_READ_oFFs_SUPPORTED +#define PNG_READ_pCAL_SUPPORTED +#define PNG_READ_pHYs_SUPPORTED +#define PNG_READ_sBIT_SUPPORTED +#define PNG_READ_sCAL_SUPPORTED +#define PNG_READ_sPLT_SUPPORTED +#define PNG_READ_sRGB_SUPPORTED +#define PNG_READ_tEXt_SUPPORTED +#define PNG_READ_tIME_SUPPORTED +#define PNG_READ_tRNS_SUPPORTED +#define PNG_READ_zTXt_SUPPORTED +/*#undef PNG_SAFE_LIMITS_SUPPORTED*/ +#define PNG_SAVE_INT_32_SUPPORTED +#define PNG_SAVE_UNKNOWN_CHUNKS_SUPPORTED +#define PNG_SEQUENTIAL_READ_SUPPORTED +#define PNG_SETJMP_SUPPORTED +#define PNG_SET_CHUNK_CACHE_LIMIT_SUPPORTED +#define PNG_SET_CHUNK_MALLOC_LIMIT_SUPPORTED +/*#undef PNG_SET_OPTION_SUPPORTED*/ +#define PNG_SET_UNKNOWN_CHUNKS_SUPPORTED +#define PNG_SET_USER_LIMITS_SUPPORTED +#define PNG_SIMPLIFIED_READ_AFIRST_SUPPORTED +#define PNG_SIMPLIFIED_READ_BGR_SUPPORTED +#define PNG_SIMPLIFIED_READ_SUPPORTED +#define PNG_SIMPLIFIED_WRITE_AFIRST_SUPPORTED +#define PNG_SIMPLIFIED_WRITE_BGR_SUPPORTED +#define PNG_SIMPLIFIED_WRITE_SUPPORTED +#define PNG_STDIO_SUPPORTED +#define PNG_STORE_UNKNOWN_CHUNKS_SUPPORTED +#define PNG_TEXT_SUPPORTED +#define PNG_TIME_RFC1123_SUPPORTED +#define PNG_UNKNOWN_CHUNKS_SUPPORTED +#define PNG_USER_CHUNKS_SUPPORTED +#define PNG_USER_LIMITS_SUPPORTED +#define PNG_USER_MEM_SUPPORTED +#define PNG_USER_TRANSFORM_INFO_SUPPORTED +#define PNG_USER_TRANSFORM_PTR_SUPPORTED +#define PNG_WARNINGS_SUPPORTED +#define PNG_WRITE_16BIT_SUPPORTED +#define PNG_WRITE_ANCILLARY_CHUNKS_SUPPORTED +#define PNG_WRITE_BGR_SUPPORTED +#define PNG_WRITE_CHECK_FOR_INVALID_INDEX_SUPPORTED +#define PNG_WRITE_COMPRESSED_TEXT_SUPPORTED +#define PNG_WRITE_CUSTOMIZE_ZTXT_COMPRESSION_SUPPORTED +#define PNG_WRITE_FILLER_SUPPORTED +#define PNG_WRITE_FILTER_SUPPORTED +#define PNG_WRITE_FLUSH_SUPPORTED +#define PNG_WRITE_GET_PALETTE_MAX_SUPPORTED +#define PNG_WRITE_INTERLACING_SUPPORTED +#define PNG_WRITE_INT_FUNCTIONS_SUPPORTED +#define PNG_WRITE_INVERT_ALPHA_SUPPORTED +#define PNG_WRITE_INVERT_SUPPORTED +#define PNG_WRITE_OPTIMIZE_CMF_SUPPORTED +#define PNG_WRITE_PACKSWAP_SUPPORTED +#define PNG_WRITE_PACK_SUPPORTED +#define PNG_WRITE_SHIFT_SUPPORTED +#define PNG_WRITE_SUPPORTED +#define PNG_WRITE_SWAP_ALPHA_SUPPORTED +#define PNG_WRITE_SWAP_SUPPORTED +#define PNG_WRITE_TEXT_SUPPORTED +#define PNG_WRITE_TRANSFORMS_SUPPORTED +#define PNG_WRITE_UNKNOWN_CHUNKS_SUPPORTED +#define PNG_WRITE_USER_TRANSFORM_SUPPORTED +#define PNG_WRITE_WEIGHTED_FILTER_SUPPORTED +#define PNG_WRITE_bKGD_SUPPORTED +#define PNG_WRITE_cHRM_SUPPORTED +#define PNG_WRITE_gAMA_SUPPORTED +#define PNG_WRITE_hIST_SUPPORTED +#define PNG_WRITE_iCCP_SUPPORTED +#define PNG_WRITE_iTXt_SUPPORTED +#define PNG_WRITE_oFFs_SUPPORTED +#define PNG_WRITE_pCAL_SUPPORTED +#define PNG_WRITE_pHYs_SUPPORTED +#define PNG_WRITE_sBIT_SUPPORTED +#define PNG_WRITE_sCAL_SUPPORTED +#define PNG_WRITE_sPLT_SUPPORTED +#define PNG_WRITE_sRGB_SUPPORTED +#define PNG_WRITE_tEXt_SUPPORTED +#define PNG_WRITE_tIME_SUPPORTED +#define PNG_WRITE_tRNS_SUPPORTED +#define PNG_WRITE_zTXt_SUPPORTED +#define PNG_bKGD_SUPPORTED +#define PNG_cHRM_SUPPORTED +#define PNG_gAMA_SUPPORTED +#define PNG_hIST_SUPPORTED +#define PNG_iCCP_SUPPORTED +#define PNG_iTXt_SUPPORTED +#define PNG_oFFs_SUPPORTED +#define PNG_pCAL_SUPPORTED +#define PNG_pHYs_SUPPORTED +#define PNG_sBIT_SUPPORTED +#define PNG_sCAL_SUPPORTED +#define PNG_sPLT_SUPPORTED +#define PNG_sRGB_SUPPORTED +#define PNG_tEXt_SUPPORTED +#define PNG_tIME_SUPPORTED +#define PNG_tRNS_SUPPORTED +#define PNG_zTXt_SUPPORTED +/* end of options */ +/* settings */ +#define PNG_API_RULE 0 +#define PNG_CALLOC_SUPPORTED +#define PNG_COST_SHIFT 3 +#define PNG_DEFAULT_READ_MACROS 1 +#define PNG_GAMMA_THRESHOLD_FIXED 5000 +#define PNG_IDAT_READ_SIZE PNG_ZBUF_SIZE +#define PNG_INFLATE_BUF_SIZE 1024 +#define PNG_MAX_GAMMA_8 11 +#define PNG_QUANTIZE_BLUE_BITS 5 +#define PNG_QUANTIZE_GREEN_BITS 5 +#define PNG_QUANTIZE_RED_BITS 5 +#define PNG_TEXT_Z_DEFAULT_COMPRESSION (-1) +#define PNG_TEXT_Z_DEFAULT_STRATEGY 0 +#define PNG_WEIGHT_SHIFT 8 +#define PNG_ZBUF_SIZE 8192 +#define PNG_Z_DEFAULT_COMPRESSION (-1) +#define PNG_Z_DEFAULT_NOFILTER_STRATEGY 0 +#define PNG_Z_DEFAULT_STRATEGY 1 +#define PNG_sCAL_PRECISION 5 +#define PNG_sRGB_PROFILE_CHECKS 2 +/* end of settings */ +#endif /* PNGLCONF_H */ \ No newline at end of file diff --git a/Minecraft.Client/Windows64/4JLibs/inc/Render/libpng/pngmem.c b/Minecraft.Client/Windows64/4JLibs/inc/Render/libpng/pngmem.c new file mode 100644 index 000000000..b9b3efb44 --- /dev/null +++ b/Minecraft.Client/Windows64/4JLibs/inc/Render/libpng/pngmem.c @@ -0,0 +1,277 @@ + +/* pngmem.c - stub functions for memory allocation + * + * Last changed in libpng 1.6.0 [February 14, 2013] + * Copyright (c) 1998-2013 Glenn Randers-Pehrson + * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) + * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) + * + * This code is released under the libpng license. + * For conditions of distribution and use, see the disclaimer + * and license in png.h + * + * This file provides a location for all memory allocation. Users who + * need special memory handling are expected to supply replacement + * functions for png_malloc() and png_free(), and to use + * png_create_read_struct_2() and png_create_write_struct_2() to + * identify the replacement functions. + */ + +#include "pngpriv.h" + +#if defined(PNG_READ_SUPPORTED) || defined(PNG_WRITE_SUPPORTED) +/* Free a png_struct */ +void /* PRIVATE */ +png_destroy_png_struct(png_structrp png_ptr) +{ + if (png_ptr != NULL) + { + /* png_free might call png_error and may certainly call + * png_get_mem_ptr, so fake a temporary png_struct to support this. + */ + png_struct dummy_struct = *png_ptr; + memset(png_ptr, 0, (sizeof *png_ptr)); + png_free(&dummy_struct, png_ptr); + +# ifdef PNG_SETJMP_SUPPORTED + /* We may have a jmp_buf left to deallocate. */ + png_free_jmpbuf(&dummy_struct); +# endif + } +} + +/* Allocate memory. For reasonable files, size should never exceed + * 64K. However, zlib may allocate more then 64K if you don't tell + * it not to. See zconf.h and png.h for more information. zlib does + * need to allocate exactly 64K, so whatever you call here must + * have the ability to do that. + */ +PNG_FUNCTION(png_voidp,PNGAPI +png_calloc,(png_const_structrp png_ptr, png_alloc_size_t size),PNG_ALLOCATED) +{ + png_voidp ret; + + ret = png_malloc(png_ptr, size); + + if (ret != NULL) + memset(ret, 0, size); + + return ret; +} + +/* png_malloc_base, an internal function added at libpng 1.6.0, does the work of + * allocating memory, taking into account limits and PNG_USER_MEM_SUPPORTED. + * Checking and error handling must happen outside this routine; it returns NULL + * if the allocation cannot be done (for any reason.) + */ +PNG_FUNCTION(png_voidp /* PRIVATE */, +png_malloc_base,(png_const_structrp png_ptr, png_alloc_size_t size), + PNG_ALLOCATED) +{ + /* Moved to png_malloc_base from png_malloc_default in 1.6.0; the DOS + * allocators have also been removed in 1.6.0, so any 16-bit system now has + * to implement a user memory handler. This checks to be sure it isn't + * called with big numbers. + */ +#ifdef PNG_USER_MEM_SUPPORTED + PNG_UNUSED(png_ptr) +#endif + if (size > 0 && size <= PNG_SIZE_MAX +# ifdef PNG_MAX_MALLOC_64K + && size <= 65536U +# endif + ) + { +#ifdef PNG_USER_MEM_SUPPORTED + if (png_ptr != NULL && png_ptr->malloc_fn != NULL) + return png_ptr->malloc_fn(png_constcast(png_structrp,png_ptr), size); + + else +#endif + return malloc((size_t)size); /* checked for truncation above */ + } + + else + return NULL; +} + +/* This is really here only to work round a spurious warning in GCC 4.6 and 4.7 + * that arises because of the checks in png_realloc_array that are repeated in + * png_malloc_array. + */ +static png_voidp +png_malloc_array_checked(png_const_structrp png_ptr, int nelements, + size_t element_size) +{ + png_alloc_size_t req = nelements; /* known to be > 0 */ + + if (req <= PNG_SIZE_MAX/element_size) + return png_malloc_base(png_ptr, req * element_size); + + /* The failure case when the request is too large */ + return NULL; +} + +PNG_FUNCTION(png_voidp /* PRIVATE */, +png_malloc_array,(png_const_structrp png_ptr, int nelements, + size_t element_size),PNG_ALLOCATED) +{ + if (nelements <= 0 || element_size == 0) + png_error(png_ptr, "internal error: array alloc"); + + return png_malloc_array_checked(png_ptr, nelements, element_size); +} + +PNG_FUNCTION(png_voidp /* PRIVATE */, +png_realloc_array,(png_const_structrp png_ptr, png_const_voidp old_array, + int old_elements, int add_elements, size_t element_size),PNG_ALLOCATED) +{ + /* These are internal errors: */ + if (add_elements <= 0 || element_size == 0 || old_elements < 0 || + (old_array == NULL && old_elements > 0)) + png_error(png_ptr, "internal error: array realloc"); + + /* Check for overflow on the elements count (so the caller does not have to + * check.) + */ + if (add_elements <= INT_MAX - old_elements) + { + png_voidp new_array = png_malloc_array_checked(png_ptr, + old_elements+add_elements, element_size); + + if (new_array != NULL) + { + /* Because png_malloc_array worked the size calculations below cannot + * overflow. + */ + if (old_elements > 0) + memcpy(new_array, old_array, element_size*(unsigned)old_elements); + + memset((char*)new_array + element_size*(unsigned)old_elements, 0, + element_size*(unsigned)add_elements); + + return new_array; + } + } + + return NULL; /* error */ +} + +/* Various functions that have different error handling are derived from this. + * png_malloc always exists, but if PNG_USER_MEM_SUPPORTED is defined a separate + * function png_malloc_default is also provided. + */ +PNG_FUNCTION(png_voidp,PNGAPI +png_malloc,(png_const_structrp png_ptr, png_alloc_size_t size),PNG_ALLOCATED) +{ + png_voidp ret; + + if (png_ptr == NULL) + return NULL; + + ret = png_malloc_base(png_ptr, size); + + if (ret == NULL) + png_error(png_ptr, "Out of memory"); /* 'm' means png_malloc */ + + return ret; +} + +#ifdef PNG_USER_MEM_SUPPORTED +PNG_FUNCTION(png_voidp,PNGAPI +png_malloc_default,(png_const_structrp png_ptr, png_alloc_size_t size), + PNG_ALLOCATED PNG_DEPRECATED) +{ + png_voidp ret; + + if (png_ptr == NULL) + return NULL; + + /* Passing 'NULL' here bypasses the application provided memory handler. */ + ret = png_malloc_base(NULL/*use malloc*/, size); + + if (ret == NULL) + png_error(png_ptr, "Out of Memory"); /* 'M' means png_malloc_default */ + + return ret; +} +#endif /* PNG_USER_MEM_SUPPORTED */ + +/* This function was added at libpng version 1.2.3. The png_malloc_warn() + * function will issue a png_warning and return NULL instead of issuing a + * png_error, if it fails to allocate the requested memory. + */ +PNG_FUNCTION(png_voidp,PNGAPI +png_malloc_warn,(png_const_structrp png_ptr, png_alloc_size_t size), + PNG_ALLOCATED) +{ + if (png_ptr != NULL) + { + png_voidp ret = png_malloc_base(png_ptr, size); + + if (ret != NULL) + return ret; + + png_warning(png_ptr, "Out of memory"); + } + + return NULL; +} + +/* Free a pointer allocated by png_malloc(). If ptr is NULL, return + * without taking any action. + */ +void PNGAPI +png_free(png_const_structrp png_ptr, png_voidp ptr) +{ + if (png_ptr == NULL || ptr == NULL) + return; + +#ifdef PNG_USER_MEM_SUPPORTED + if (png_ptr->free_fn != NULL) + png_ptr->free_fn(png_constcast(png_structrp,png_ptr), ptr); + + else + png_free_default(png_ptr, ptr); +} + +PNG_FUNCTION(void,PNGAPI +png_free_default,(png_const_structrp png_ptr, png_voidp ptr),PNG_DEPRECATED) +{ + if (png_ptr == NULL || ptr == NULL) + return; +#endif /* PNG_USER_MEM_SUPPORTED */ + + free(ptr); +} + +#ifdef PNG_USER_MEM_SUPPORTED +/* This function is called when the application wants to use another method + * of allocating and freeing memory. + */ +void PNGAPI +png_set_mem_fn(png_structrp png_ptr, png_voidp mem_ptr, png_malloc_ptr + malloc_fn, png_free_ptr free_fn) +{ + if (png_ptr != NULL) + { + png_ptr->mem_ptr = mem_ptr; + png_ptr->malloc_fn = malloc_fn; + png_ptr->free_fn = free_fn; + } +} + +/* This function returns a pointer to the mem_ptr associated with the user + * functions. The application should free any memory associated with this + * pointer before png_write_destroy and png_read_destroy are called. + */ +png_voidp PNGAPI +png_get_mem_ptr(png_const_structrp png_ptr) +{ + if (png_ptr == NULL) + return NULL; + + return png_ptr->mem_ptr; +} +#endif /* PNG_USER_MEM_SUPPORTED */ +#endif /* PNG_READ_SUPPORTED || PNG_WRITE_SUPPORTED */ diff --git a/Minecraft.Client/Windows64/4JLibs/inc/Render/libpng/pngpread.c b/Minecraft.Client/Windows64/4JLibs/inc/Render/libpng/pngpread.c new file mode 100644 index 000000000..f132ce600 --- /dev/null +++ b/Minecraft.Client/Windows64/4JLibs/inc/Render/libpng/pngpread.c @@ -0,0 +1,1291 @@ + +/* pngpread.c - read a png file in push mode + * + * Last changed in libpng 1.6.0 [February 14, 2013] + * Copyright (c) 1998-2013 Glenn Randers-Pehrson + * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) + * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) + * + * This code is released under the libpng license. + * For conditions of distribution and use, see the disclaimer + * and license in png.h + */ + +#include "pngpriv.h" + +#ifdef PNG_PROGRESSIVE_READ_SUPPORTED + +/* Push model modes */ +#define PNG_READ_SIG_MODE 0 +#define PNG_READ_CHUNK_MODE 1 +#define PNG_READ_IDAT_MODE 2 +#define PNG_SKIP_MODE 3 +#define PNG_READ_tEXt_MODE 4 +#define PNG_READ_zTXt_MODE 5 +#define PNG_READ_DONE_MODE 6 +#define PNG_READ_iTXt_MODE 7 +#define PNG_ERROR_MODE 8 + +void PNGAPI +png_process_data(png_structrp png_ptr, png_inforp info_ptr, + png_bytep buffer, png_size_t buffer_size) +{ + if (png_ptr == NULL || info_ptr == NULL) + return; + + png_push_restore_buffer(png_ptr, buffer, buffer_size); + + while (png_ptr->buffer_size) + { + png_process_some_data(png_ptr, info_ptr); + } +} + +png_size_t PNGAPI +png_process_data_pause(png_structrp png_ptr, int save) +{ + if (png_ptr != NULL) + { + /* It's easiest for the caller if we do the save, then the caller doesn't + * have to supply the same data again: + */ + if (save) + png_push_save_buffer(png_ptr); + else + { + /* This includes any pending saved bytes: */ + png_size_t remaining = png_ptr->buffer_size; + png_ptr->buffer_size = 0; + + /* So subtract the saved buffer size, unless all the data + * is actually 'saved', in which case we just return 0 + */ + if (png_ptr->save_buffer_size < remaining) + return remaining - png_ptr->save_buffer_size; + } + } + + return 0; +} + +png_uint_32 PNGAPI +png_process_data_skip(png_structrp png_ptr) +{ + png_uint_32 remaining = 0; + + if (png_ptr != NULL && png_ptr->process_mode == PNG_SKIP_MODE && + png_ptr->skip_length > 0) + { + /* At the end of png_process_data the buffer size must be 0 (see the loop + * above) so we can detect a broken call here: + */ + if (png_ptr->buffer_size != 0) + png_error(png_ptr, + "png_process_data_skip called inside png_process_data"); + + /* If is impossible for there to be a saved buffer at this point - + * otherwise we could not be in SKIP mode. This will also happen if + * png_process_skip is called inside png_process_data (but only very + * rarely.) + */ + if (png_ptr->save_buffer_size != 0) + png_error(png_ptr, "png_process_data_skip called with saved data"); + + remaining = png_ptr->skip_length; + png_ptr->skip_length = 0; + png_ptr->process_mode = PNG_READ_CHUNK_MODE; + } + + return remaining; +} + +/* What we do with the incoming data depends on what we were previously + * doing before we ran out of data... + */ +void /* PRIVATE */ +png_process_some_data(png_structrp png_ptr, png_inforp info_ptr) +{ + if (png_ptr == NULL) + return; + + switch (png_ptr->process_mode) + { + case PNG_READ_SIG_MODE: + { + png_push_read_sig(png_ptr, info_ptr); + break; + } + + case PNG_READ_CHUNK_MODE: + { + png_push_read_chunk(png_ptr, info_ptr); + break; + } + + case PNG_READ_IDAT_MODE: + { + png_push_read_IDAT(png_ptr); + break; + } + + case PNG_SKIP_MODE: + { + png_push_crc_finish(png_ptr); + break; + } + + default: + { + png_ptr->buffer_size = 0; + break; + } + } +} + +/* Read any remaining signature bytes from the stream and compare them with + * the correct PNG signature. It is possible that this routine is called + * with bytes already read from the signature, either because they have been + * checked by the calling application, or because of multiple calls to this + * routine. + */ +void /* PRIVATE */ +png_push_read_sig(png_structrp png_ptr, png_inforp info_ptr) +{ + png_size_t num_checked = png_ptr->sig_bytes, + num_to_check = 8 - num_checked; + + if (png_ptr->buffer_size < num_to_check) + { + num_to_check = png_ptr->buffer_size; + } + + png_push_fill_buffer(png_ptr, &(info_ptr->signature[num_checked]), + num_to_check); + png_ptr->sig_bytes = (png_byte)(png_ptr->sig_bytes + num_to_check); + + if (png_sig_cmp(info_ptr->signature, num_checked, num_to_check)) + { + if (num_checked < 4 && + png_sig_cmp(info_ptr->signature, num_checked, num_to_check - 4)) + png_error(png_ptr, "Not a PNG file"); + + else + png_error(png_ptr, "PNG file corrupted by ASCII conversion"); + } + else + { + if (png_ptr->sig_bytes >= 8) + { + png_ptr->process_mode = PNG_READ_CHUNK_MODE; + } + } +} + +void /* PRIVATE */ +png_push_read_chunk(png_structrp png_ptr, png_inforp info_ptr) +{ + png_uint_32 chunk_name; +#ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED + int keep; /* unknown handling method */ +#endif + + /* First we make sure we have enough data for the 4 byte chunk name + * and the 4 byte chunk length before proceeding with decoding the + * chunk data. To fully decode each of these chunks, we also make + * sure we have enough data in the buffer for the 4 byte CRC at the + * end of every chunk (except IDAT, which is handled separately). + */ + if (!(png_ptr->mode & PNG_HAVE_CHUNK_HEADER)) + { + png_byte chunk_length[4]; + png_byte chunk_tag[4]; + + if (png_ptr->buffer_size < 8) + { + png_push_save_buffer(png_ptr); + return; + } + + png_push_fill_buffer(png_ptr, chunk_length, 4); + png_ptr->push_length = png_get_uint_31(png_ptr, chunk_length); + png_reset_crc(png_ptr); + png_crc_read(png_ptr, chunk_tag, 4); + png_ptr->chunk_name = PNG_CHUNK_FROM_STRING(chunk_tag); + png_check_chunk_name(png_ptr, png_ptr->chunk_name); + png_ptr->mode |= PNG_HAVE_CHUNK_HEADER; + } + + chunk_name = png_ptr->chunk_name; + + if (chunk_name == png_IDAT) + { + if (png_ptr->mode & PNG_AFTER_IDAT) + png_ptr->mode |= PNG_HAVE_CHUNK_AFTER_IDAT; + + /* If we reach an IDAT chunk, this means we have read all of the + * header chunks, and we can start reading the image (or if this + * is called after the image has been read - we have an error). + */ + if (!(png_ptr->mode & PNG_HAVE_IHDR)) + png_error(png_ptr, "Missing IHDR before IDAT"); + + else if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE && + !(png_ptr->mode & PNG_HAVE_PLTE)) + png_error(png_ptr, "Missing PLTE before IDAT"); + + png_ptr->mode |= PNG_HAVE_IDAT; + + if (!(png_ptr->mode & PNG_HAVE_CHUNK_AFTER_IDAT)) + if (png_ptr->push_length == 0) + return; + + if (png_ptr->mode & PNG_AFTER_IDAT) + png_benign_error(png_ptr, "Too many IDATs found"); + } + + if (chunk_name == png_IHDR) + { + if (png_ptr->push_length != 13) + png_error(png_ptr, "Invalid IHDR length"); + + if (png_ptr->push_length + 4 > png_ptr->buffer_size) + { + png_push_save_buffer(png_ptr); + return; + } + + png_handle_IHDR(png_ptr, info_ptr, png_ptr->push_length); + } + + else if (chunk_name == png_IEND) + { + if (png_ptr->push_length + 4 > png_ptr->buffer_size) + { + png_push_save_buffer(png_ptr); + return; + } + + png_handle_IEND(png_ptr, info_ptr, png_ptr->push_length); + + png_ptr->process_mode = PNG_READ_DONE_MODE; + png_push_have_end(png_ptr, info_ptr); + } + +#ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED + else if ((keep = png_chunk_unknown_handling(png_ptr, chunk_name)) != 0) + { + if (png_ptr->push_length + 4 > png_ptr->buffer_size) + { + png_push_save_buffer(png_ptr); + return; + } + + png_handle_unknown(png_ptr, info_ptr, png_ptr->push_length, keep); + + if (chunk_name == png_PLTE) + png_ptr->mode |= PNG_HAVE_PLTE; + } + +#endif + else if (chunk_name == png_PLTE) + { + if (png_ptr->push_length + 4 > png_ptr->buffer_size) + { + png_push_save_buffer(png_ptr); + return; + } + png_handle_PLTE(png_ptr, info_ptr, png_ptr->push_length); + } + + else if (chunk_name == png_IDAT) + { + png_ptr->idat_size = png_ptr->push_length; + png_ptr->process_mode = PNG_READ_IDAT_MODE; + png_push_have_info(png_ptr, info_ptr); + png_ptr->zstream.avail_out = + (uInt) PNG_ROWBYTES(png_ptr->pixel_depth, + png_ptr->iwidth) + 1; + png_ptr->zstream.next_out = png_ptr->row_buf; + return; + } + +#ifdef PNG_READ_gAMA_SUPPORTED + else if (png_ptr->chunk_name == png_gAMA) + { + if (png_ptr->push_length + 4 > png_ptr->buffer_size) + { + png_push_save_buffer(png_ptr); + return; + } + + png_handle_gAMA(png_ptr, info_ptr, png_ptr->push_length); + } + +#endif +#ifdef PNG_READ_sBIT_SUPPORTED + else if (png_ptr->chunk_name == png_sBIT) + { + if (png_ptr->push_length + 4 > png_ptr->buffer_size) + { + png_push_save_buffer(png_ptr); + return; + } + + png_handle_sBIT(png_ptr, info_ptr, png_ptr->push_length); + } + +#endif +#ifdef PNG_READ_cHRM_SUPPORTED + else if (png_ptr->chunk_name == png_cHRM) + { + if (png_ptr->push_length + 4 > png_ptr->buffer_size) + { + png_push_save_buffer(png_ptr); + return; + } + + png_handle_cHRM(png_ptr, info_ptr, png_ptr->push_length); + } + +#endif +#ifdef PNG_READ_sRGB_SUPPORTED + else if (chunk_name == png_sRGB) + { + if (png_ptr->push_length + 4 > png_ptr->buffer_size) + { + png_push_save_buffer(png_ptr); + return; + } + + png_handle_sRGB(png_ptr, info_ptr, png_ptr->push_length); + } + +#endif +#ifdef PNG_READ_iCCP_SUPPORTED + else if (png_ptr->chunk_name == png_iCCP) + { + if (png_ptr->push_length + 4 > png_ptr->buffer_size) + { + png_push_save_buffer(png_ptr); + return; + } + + png_handle_iCCP(png_ptr, info_ptr, png_ptr->push_length); + } + +#endif +#ifdef PNG_READ_sPLT_SUPPORTED + else if (chunk_name == png_sPLT) + { + if (png_ptr->push_length + 4 > png_ptr->buffer_size) + { + png_push_save_buffer(png_ptr); + return; + } + + png_handle_sPLT(png_ptr, info_ptr, png_ptr->push_length); + } + +#endif +#ifdef PNG_READ_tRNS_SUPPORTED + else if (chunk_name == png_tRNS) + { + if (png_ptr->push_length + 4 > png_ptr->buffer_size) + { + png_push_save_buffer(png_ptr); + return; + } + + png_handle_tRNS(png_ptr, info_ptr, png_ptr->push_length); + } + +#endif +#ifdef PNG_READ_bKGD_SUPPORTED + else if (chunk_name == png_bKGD) + { + if (png_ptr->push_length + 4 > png_ptr->buffer_size) + { + png_push_save_buffer(png_ptr); + return; + } + + png_handle_bKGD(png_ptr, info_ptr, png_ptr->push_length); + } + +#endif +#ifdef PNG_READ_hIST_SUPPORTED + else if (chunk_name == png_hIST) + { + if (png_ptr->push_length + 4 > png_ptr->buffer_size) + { + png_push_save_buffer(png_ptr); + return; + } + + png_handle_hIST(png_ptr, info_ptr, png_ptr->push_length); + } + +#endif +#ifdef PNG_READ_pHYs_SUPPORTED + else if (chunk_name == png_pHYs) + { + if (png_ptr->push_length + 4 > png_ptr->buffer_size) + { + png_push_save_buffer(png_ptr); + return; + } + + png_handle_pHYs(png_ptr, info_ptr, png_ptr->push_length); + } + +#endif +#ifdef PNG_READ_oFFs_SUPPORTED + else if (chunk_name == png_oFFs) + { + if (png_ptr->push_length + 4 > png_ptr->buffer_size) + { + png_push_save_buffer(png_ptr); + return; + } + + png_handle_oFFs(png_ptr, info_ptr, png_ptr->push_length); + } +#endif + +#ifdef PNG_READ_pCAL_SUPPORTED + else if (chunk_name == png_pCAL) + { + if (png_ptr->push_length + 4 > png_ptr->buffer_size) + { + png_push_save_buffer(png_ptr); + return; + } + + png_handle_pCAL(png_ptr, info_ptr, png_ptr->push_length); + } + +#endif +#ifdef PNG_READ_sCAL_SUPPORTED + else if (chunk_name == png_sCAL) + { + if (png_ptr->push_length + 4 > png_ptr->buffer_size) + { + png_push_save_buffer(png_ptr); + return; + } + + png_handle_sCAL(png_ptr, info_ptr, png_ptr->push_length); + } + +#endif +#ifdef PNG_READ_tIME_SUPPORTED + else if (chunk_name == png_tIME) + { + if (png_ptr->push_length + 4 > png_ptr->buffer_size) + { + png_push_save_buffer(png_ptr); + return; + } + + png_handle_tIME(png_ptr, info_ptr, png_ptr->push_length); + } + +#endif +#ifdef PNG_READ_tEXt_SUPPORTED + else if (chunk_name == png_tEXt) + { + if (png_ptr->push_length + 4 > png_ptr->buffer_size) + { + png_push_save_buffer(png_ptr); + return; + } + + png_handle_tEXt(png_ptr, info_ptr, png_ptr->push_length); + } + +#endif +#ifdef PNG_READ_zTXt_SUPPORTED + else if (chunk_name == png_zTXt) + { + if (png_ptr->push_length + 4 > png_ptr->buffer_size) + { + png_push_save_buffer(png_ptr); + return; + } + + png_handle_zTXt(png_ptr, info_ptr, png_ptr->push_length); + } + +#endif +#ifdef PNG_READ_iTXt_SUPPORTED + else if (chunk_name == png_iTXt) + { + if (png_ptr->push_length + 4 > png_ptr->buffer_size) + { + png_push_save_buffer(png_ptr); + return; + } + + png_handle_iTXt(png_ptr, info_ptr, png_ptr->push_length); + } + +#endif + else + { + if (png_ptr->push_length + 4 > png_ptr->buffer_size) + { + png_push_save_buffer(png_ptr); + return; + } + png_handle_unknown(png_ptr, info_ptr, png_ptr->push_length, + PNG_HANDLE_CHUNK_AS_DEFAULT); + } + + png_ptr->mode &= ~PNG_HAVE_CHUNK_HEADER; +} + +void /* PRIVATE */ +png_push_crc_skip(png_structrp png_ptr, png_uint_32 skip) +{ + png_ptr->process_mode = PNG_SKIP_MODE; + png_ptr->skip_length = skip; +} + +void /* PRIVATE */ +png_push_crc_finish(png_structrp png_ptr) +{ + if (png_ptr->skip_length && png_ptr->save_buffer_size) + { + png_size_t save_size = png_ptr->save_buffer_size; + png_uint_32 skip_length = png_ptr->skip_length; + + /* We want the smaller of 'skip_length' and 'save_buffer_size', but + * they are of different types and we don't know which variable has the + * fewest bits. Carefully select the smaller and cast it to the type of + * the larger - this cannot overflow. Do not cast in the following test + * - it will break on either 16 or 64 bit platforms. + */ + if (skip_length < save_size) + save_size = (png_size_t)skip_length; + + else + skip_length = (png_uint_32)save_size; + + png_calculate_crc(png_ptr, png_ptr->save_buffer_ptr, save_size); + + png_ptr->skip_length -= skip_length; + png_ptr->buffer_size -= save_size; + png_ptr->save_buffer_size -= save_size; + png_ptr->save_buffer_ptr += save_size; + } + if (png_ptr->skip_length && png_ptr->current_buffer_size) + { + png_size_t save_size = png_ptr->current_buffer_size; + png_uint_32 skip_length = png_ptr->skip_length; + + /* We want the smaller of 'skip_length' and 'current_buffer_size', here, + * the same problem exists as above and the same solution. + */ + if (skip_length < save_size) + save_size = (png_size_t)skip_length; + + else + skip_length = (png_uint_32)save_size; + + png_calculate_crc(png_ptr, png_ptr->current_buffer_ptr, save_size); + + png_ptr->skip_length -= skip_length; + png_ptr->buffer_size -= save_size; + png_ptr->current_buffer_size -= save_size; + png_ptr->current_buffer_ptr += save_size; + } + if (!png_ptr->skip_length) + { + if (png_ptr->buffer_size < 4) + { + png_push_save_buffer(png_ptr); + return; + } + + png_crc_finish(png_ptr, 0); + png_ptr->process_mode = PNG_READ_CHUNK_MODE; + } +} + +void PNGCBAPI +png_push_fill_buffer(png_structp png_ptr, png_bytep buffer, png_size_t length) +{ + png_bytep ptr; + + if (png_ptr == NULL) + return; + + ptr = buffer; + if (png_ptr->save_buffer_size) + { + png_size_t save_size; + + if (length < png_ptr->save_buffer_size) + save_size = length; + + else + save_size = png_ptr->save_buffer_size; + + memcpy(ptr, png_ptr->save_buffer_ptr, save_size); + length -= save_size; + ptr += save_size; + png_ptr->buffer_size -= save_size; + png_ptr->save_buffer_size -= save_size; + png_ptr->save_buffer_ptr += save_size; + } + if (length && png_ptr->current_buffer_size) + { + png_size_t save_size; + + if (length < png_ptr->current_buffer_size) + save_size = length; + + else + save_size = png_ptr->current_buffer_size; + + memcpy(ptr, png_ptr->current_buffer_ptr, save_size); + png_ptr->buffer_size -= save_size; + png_ptr->current_buffer_size -= save_size; + png_ptr->current_buffer_ptr += save_size; + } +} + +void /* PRIVATE */ +png_push_save_buffer(png_structrp png_ptr) +{ + if (png_ptr->save_buffer_size) + { + if (png_ptr->save_buffer_ptr != png_ptr->save_buffer) + { + png_size_t i, istop; + png_bytep sp; + png_bytep dp; + + istop = png_ptr->save_buffer_size; + for (i = 0, sp = png_ptr->save_buffer_ptr, dp = png_ptr->save_buffer; + i < istop; i++, sp++, dp++) + { + *dp = *sp; + } + } + } + if (png_ptr->save_buffer_size + png_ptr->current_buffer_size > + png_ptr->save_buffer_max) + { + png_size_t new_max; + png_bytep old_buffer; + + if (png_ptr->save_buffer_size > PNG_SIZE_MAX - + (png_ptr->current_buffer_size + 256)) + { + png_error(png_ptr, "Potential overflow of save_buffer"); + } + + new_max = png_ptr->save_buffer_size + png_ptr->current_buffer_size + 256; + old_buffer = png_ptr->save_buffer; + png_ptr->save_buffer = (png_bytep)png_malloc_warn(png_ptr, + (png_size_t)new_max); + + if (png_ptr->save_buffer == NULL) + { + png_free(png_ptr, old_buffer); + png_error(png_ptr, "Insufficient memory for save_buffer"); + } + + memcpy(png_ptr->save_buffer, old_buffer, png_ptr->save_buffer_size); + png_free(png_ptr, old_buffer); + png_ptr->save_buffer_max = new_max; + } + if (png_ptr->current_buffer_size) + { + memcpy(png_ptr->save_buffer + png_ptr->save_buffer_size, + png_ptr->current_buffer_ptr, png_ptr->current_buffer_size); + png_ptr->save_buffer_size += png_ptr->current_buffer_size; + png_ptr->current_buffer_size = 0; + } + png_ptr->save_buffer_ptr = png_ptr->save_buffer; + png_ptr->buffer_size = 0; +} + +void /* PRIVATE */ +png_push_restore_buffer(png_structrp png_ptr, png_bytep buffer, + png_size_t buffer_length) +{ + png_ptr->current_buffer = buffer; + png_ptr->current_buffer_size = buffer_length; + png_ptr->buffer_size = buffer_length + png_ptr->save_buffer_size; + png_ptr->current_buffer_ptr = png_ptr->current_buffer; +} + +void /* PRIVATE */ +png_push_read_IDAT(png_structrp png_ptr) +{ + if (!(png_ptr->mode & PNG_HAVE_CHUNK_HEADER)) + { + png_byte chunk_length[4]; + png_byte chunk_tag[4]; + + /* TODO: this code can be commoned up with the same code in push_read */ + if (png_ptr->buffer_size < 8) + { + png_push_save_buffer(png_ptr); + return; + } + + png_push_fill_buffer(png_ptr, chunk_length, 4); + png_ptr->push_length = png_get_uint_31(png_ptr, chunk_length); + png_reset_crc(png_ptr); + png_crc_read(png_ptr, chunk_tag, 4); + png_ptr->chunk_name = PNG_CHUNK_FROM_STRING(chunk_tag); + png_ptr->mode |= PNG_HAVE_CHUNK_HEADER; + + if (png_ptr->chunk_name != png_IDAT) + { + png_ptr->process_mode = PNG_READ_CHUNK_MODE; + + if (!(png_ptr->flags & PNG_FLAG_ZSTREAM_ENDED)) + png_error(png_ptr, "Not enough compressed data"); + + return; + } + + png_ptr->idat_size = png_ptr->push_length; + } + + if (png_ptr->idat_size && png_ptr->save_buffer_size) + { + png_size_t save_size = png_ptr->save_buffer_size; + png_uint_32 idat_size = png_ptr->idat_size; + + /* We want the smaller of 'idat_size' and 'current_buffer_size', but they + * are of different types and we don't know which variable has the fewest + * bits. Carefully select the smaller and cast it to the type of the + * larger - this cannot overflow. Do not cast in the following test - it + * will break on either 16 or 64 bit platforms. + */ + if (idat_size < save_size) + save_size = (png_size_t)idat_size; + + else + idat_size = (png_uint_32)save_size; + + png_calculate_crc(png_ptr, png_ptr->save_buffer_ptr, save_size); + + png_process_IDAT_data(png_ptr, png_ptr->save_buffer_ptr, save_size); + + png_ptr->idat_size -= idat_size; + png_ptr->buffer_size -= save_size; + png_ptr->save_buffer_size -= save_size; + png_ptr->save_buffer_ptr += save_size; + } + + if (png_ptr->idat_size && png_ptr->current_buffer_size) + { + png_size_t save_size = png_ptr->current_buffer_size; + png_uint_32 idat_size = png_ptr->idat_size; + + /* We want the smaller of 'idat_size' and 'current_buffer_size', but they + * are of different types and we don't know which variable has the fewest + * bits. Carefully select the smaller and cast it to the type of the + * larger - this cannot overflow. + */ + if (idat_size < save_size) + save_size = (png_size_t)idat_size; + + else + idat_size = (png_uint_32)save_size; + + png_calculate_crc(png_ptr, png_ptr->current_buffer_ptr, save_size); + + png_process_IDAT_data(png_ptr, png_ptr->current_buffer_ptr, save_size); + + png_ptr->idat_size -= idat_size; + png_ptr->buffer_size -= save_size; + png_ptr->current_buffer_size -= save_size; + png_ptr->current_buffer_ptr += save_size; + } + if (!png_ptr->idat_size) + { + if (png_ptr->buffer_size < 4) + { + png_push_save_buffer(png_ptr); + return; + } + + png_crc_finish(png_ptr, 0); + png_ptr->mode &= ~PNG_HAVE_CHUNK_HEADER; + png_ptr->mode |= PNG_AFTER_IDAT; + png_ptr->zowner = 0; + } +} + +void /* PRIVATE */ +png_process_IDAT_data(png_structrp png_ptr, png_bytep buffer, + png_size_t buffer_length) +{ + /* The caller checks for a non-zero buffer length. */ + if (!(buffer_length > 0) || buffer == NULL) + png_error(png_ptr, "No IDAT data (internal error)"); + + /* This routine must process all the data it has been given + * before returning, calling the row callback as required to + * handle the uncompressed results. + */ + png_ptr->zstream.next_in = buffer; + /* TODO: WARNING: TRUNCATION ERROR: DANGER WILL ROBINSON: */ + png_ptr->zstream.avail_in = (uInt)buffer_length; + + /* Keep going until the decompressed data is all processed + * or the stream marked as finished. + */ + while (png_ptr->zstream.avail_in > 0 && + !(png_ptr->flags & PNG_FLAG_ZSTREAM_ENDED)) + { + int ret; + + /* We have data for zlib, but we must check that zlib + * has someplace to put the results. It doesn't matter + * if we don't expect any results -- it may be the input + * data is just the LZ end code. + */ + if (!(png_ptr->zstream.avail_out > 0)) + { + /* TODO: WARNING: TRUNCATION ERROR: DANGER WILL ROBINSON: */ + png_ptr->zstream.avail_out = (uInt)(PNG_ROWBYTES(png_ptr->pixel_depth, + png_ptr->iwidth) + 1); + + png_ptr->zstream.next_out = png_ptr->row_buf; + } + + /* Using Z_SYNC_FLUSH here means that an unterminated + * LZ stream (a stream with a missing end code) can still + * be handled, otherwise (Z_NO_FLUSH) a future zlib + * implementation might defer output and therefore + * change the current behavior (see comments in inflate.c + * for why this doesn't happen at present with zlib 1.2.5). + */ + ret = inflate(&png_ptr->zstream, Z_SYNC_FLUSH); + + /* Check for any failure before proceeding. */ + if (ret != Z_OK && ret != Z_STREAM_END) + { + /* Terminate the decompression. */ + png_ptr->flags |= PNG_FLAG_ZSTREAM_ENDED; + png_ptr->zowner = 0; + + /* This may be a truncated stream (missing or + * damaged end code). Treat that as a warning. + */ + if (png_ptr->row_number >= png_ptr->num_rows || + png_ptr->pass > 6) + png_warning(png_ptr, "Truncated compressed data in IDAT"); + + else + png_error(png_ptr, "Decompression error in IDAT"); + + /* Skip the check on unprocessed input */ + return; + } + + /* Did inflate output any data? */ + if (png_ptr->zstream.next_out != png_ptr->row_buf) + { + /* Is this unexpected data after the last row? + * If it is, artificially terminate the LZ output + * here. + */ + if (png_ptr->row_number >= png_ptr->num_rows || + png_ptr->pass > 6) + { + /* Extra data. */ + png_warning(png_ptr, "Extra compressed data in IDAT"); + png_ptr->flags |= PNG_FLAG_ZSTREAM_ENDED; + png_ptr->zowner = 0; + + /* Do no more processing; skip the unprocessed + * input check below. + */ + return; + } + + /* Do we have a complete row? */ + if (png_ptr->zstream.avail_out == 0) + png_push_process_row(png_ptr); + } + + /* And check for the end of the stream. */ + if (ret == Z_STREAM_END) + png_ptr->flags |= PNG_FLAG_ZSTREAM_ENDED; + } + + /* All the data should have been processed, if anything + * is left at this point we have bytes of IDAT data + * after the zlib end code. + */ + if (png_ptr->zstream.avail_in > 0) + png_warning(png_ptr, "Extra compression data in IDAT"); +} + +void /* PRIVATE */ +png_push_process_row(png_structrp png_ptr) +{ + /* 1.5.6: row_info moved out of png_struct to a local here. */ + png_row_info row_info; + + row_info.width = png_ptr->iwidth; /* NOTE: width of current interlaced row */ + row_info.color_type = png_ptr->color_type; + row_info.bit_depth = png_ptr->bit_depth; + row_info.channels = png_ptr->channels; + row_info.pixel_depth = png_ptr->pixel_depth; + row_info.rowbytes = PNG_ROWBYTES(row_info.pixel_depth, row_info.width); + + if (png_ptr->row_buf[0] > PNG_FILTER_VALUE_NONE) + { + if (png_ptr->row_buf[0] < PNG_FILTER_VALUE_LAST) + png_read_filter_row(png_ptr, &row_info, png_ptr->row_buf + 1, + png_ptr->prev_row + 1, png_ptr->row_buf[0]); + else + png_error(png_ptr, "bad adaptive filter value"); + } + + /* libpng 1.5.6: the following line was copying png_ptr->rowbytes before + * 1.5.6, while the buffer really is this big in current versions of libpng + * it may not be in the future, so this was changed just to copy the + * interlaced row count: + */ + memcpy(png_ptr->prev_row, png_ptr->row_buf, row_info.rowbytes + 1); + +#ifdef PNG_READ_TRANSFORMS_SUPPORTED + if (png_ptr->transformations) + png_do_read_transformations(png_ptr, &row_info); +#endif + + /* The transformed pixel depth should match the depth now in row_info. */ + if (png_ptr->transformed_pixel_depth == 0) + { + png_ptr->transformed_pixel_depth = row_info.pixel_depth; + if (row_info.pixel_depth > png_ptr->maximum_pixel_depth) + png_error(png_ptr, "progressive row overflow"); + } + + else if (png_ptr->transformed_pixel_depth != row_info.pixel_depth) + png_error(png_ptr, "internal progressive row size calculation error"); + + +#ifdef PNG_READ_INTERLACING_SUPPORTED + /* Blow up interlaced rows to full size */ + if (png_ptr->interlaced && (png_ptr->transformations & PNG_INTERLACE)) + { + if (png_ptr->pass < 6) + png_do_read_interlace(&row_info, png_ptr->row_buf + 1, png_ptr->pass, + png_ptr->transformations); + + switch (png_ptr->pass) + { + case 0: + { + int i; + for (i = 0; i < 8 && png_ptr->pass == 0; i++) + { + png_push_have_row(png_ptr, png_ptr->row_buf + 1); + png_read_push_finish_row(png_ptr); /* Updates png_ptr->pass */ + } + + if (png_ptr->pass == 2) /* Pass 1 might be empty */ + { + for (i = 0; i < 4 && png_ptr->pass == 2; i++) + { + png_push_have_row(png_ptr, NULL); + png_read_push_finish_row(png_ptr); + } + } + + if (png_ptr->pass == 4 && png_ptr->height <= 4) + { + for (i = 0; i < 2 && png_ptr->pass == 4; i++) + { + png_push_have_row(png_ptr, NULL); + png_read_push_finish_row(png_ptr); + } + } + + if (png_ptr->pass == 6 && png_ptr->height <= 4) + { + png_push_have_row(png_ptr, NULL); + png_read_push_finish_row(png_ptr); + } + + break; + } + + case 1: + { + int i; + for (i = 0; i < 8 && png_ptr->pass == 1; i++) + { + png_push_have_row(png_ptr, png_ptr->row_buf + 1); + png_read_push_finish_row(png_ptr); + } + + if (png_ptr->pass == 2) /* Skip top 4 generated rows */ + { + for (i = 0; i < 4 && png_ptr->pass == 2; i++) + { + png_push_have_row(png_ptr, NULL); + png_read_push_finish_row(png_ptr); + } + } + + break; + } + + case 2: + { + int i; + + for (i = 0; i < 4 && png_ptr->pass == 2; i++) + { + png_push_have_row(png_ptr, png_ptr->row_buf + 1); + png_read_push_finish_row(png_ptr); + } + + for (i = 0; i < 4 && png_ptr->pass == 2; i++) + { + png_push_have_row(png_ptr, NULL); + png_read_push_finish_row(png_ptr); + } + + if (png_ptr->pass == 4) /* Pass 3 might be empty */ + { + for (i = 0; i < 2 && png_ptr->pass == 4; i++) + { + png_push_have_row(png_ptr, NULL); + png_read_push_finish_row(png_ptr); + } + } + + break; + } + + case 3: + { + int i; + + for (i = 0; i < 4 && png_ptr->pass == 3; i++) + { + png_push_have_row(png_ptr, png_ptr->row_buf + 1); + png_read_push_finish_row(png_ptr); + } + + if (png_ptr->pass == 4) /* Skip top two generated rows */ + { + for (i = 0; i < 2 && png_ptr->pass == 4; i++) + { + png_push_have_row(png_ptr, NULL); + png_read_push_finish_row(png_ptr); + } + } + + break; + } + + case 4: + { + int i; + + for (i = 0; i < 2 && png_ptr->pass == 4; i++) + { + png_push_have_row(png_ptr, png_ptr->row_buf + 1); + png_read_push_finish_row(png_ptr); + } + + for (i = 0; i < 2 && png_ptr->pass == 4; i++) + { + png_push_have_row(png_ptr, NULL); + png_read_push_finish_row(png_ptr); + } + + if (png_ptr->pass == 6) /* Pass 5 might be empty */ + { + png_push_have_row(png_ptr, NULL); + png_read_push_finish_row(png_ptr); + } + + break; + } + + case 5: + { + int i; + + for (i = 0; i < 2 && png_ptr->pass == 5; i++) + { + png_push_have_row(png_ptr, png_ptr->row_buf + 1); + png_read_push_finish_row(png_ptr); + } + + if (png_ptr->pass == 6) /* Skip top generated row */ + { + png_push_have_row(png_ptr, NULL); + png_read_push_finish_row(png_ptr); + } + + break; + } + + default: + case 6: + { + png_push_have_row(png_ptr, png_ptr->row_buf + 1); + png_read_push_finish_row(png_ptr); + + if (png_ptr->pass != 6) + break; + + png_push_have_row(png_ptr, NULL); + png_read_push_finish_row(png_ptr); + } + } + } + else +#endif + { + png_push_have_row(png_ptr, png_ptr->row_buf + 1); + png_read_push_finish_row(png_ptr); + } +} + +void /* PRIVATE */ +png_read_push_finish_row(png_structrp png_ptr) +{ +#ifdef PNG_READ_INTERLACING_SUPPORTED + /* Arrays to facilitate easy interlacing - use pass (0 - 6) as index */ + + /* Start of interlace block */ + static PNG_CONST png_byte png_pass_start[] = {0, 4, 0, 2, 0, 1, 0}; + + /* Offset to next interlace block */ + static PNG_CONST png_byte png_pass_inc[] = {8, 8, 4, 4, 2, 2, 1}; + + /* Start of interlace block in the y direction */ + static PNG_CONST png_byte png_pass_ystart[] = {0, 0, 4, 0, 2, 0, 1}; + + /* Offset to next interlace block in the y direction */ + static PNG_CONST png_byte png_pass_yinc[] = {8, 8, 8, 4, 4, 2, 2}; + + /* Height of interlace block. This is not currently used - if you need + * it, uncomment it here and in png.h + static PNG_CONST png_byte png_pass_height[] = {8, 8, 4, 4, 2, 2, 1}; + */ +#endif + + png_ptr->row_number++; + if (png_ptr->row_number < png_ptr->num_rows) + return; + +#ifdef PNG_READ_INTERLACING_SUPPORTED + if (png_ptr->interlaced) + { + png_ptr->row_number = 0; + memset(png_ptr->prev_row, 0, png_ptr->rowbytes + 1); + + do + { + png_ptr->pass++; + if ((png_ptr->pass == 1 && png_ptr->width < 5) || + (png_ptr->pass == 3 && png_ptr->width < 3) || + (png_ptr->pass == 5 && png_ptr->width < 2)) + png_ptr->pass++; + + if (png_ptr->pass > 7) + png_ptr->pass--; + + if (png_ptr->pass >= 7) + break; + + png_ptr->iwidth = (png_ptr->width + + png_pass_inc[png_ptr->pass] - 1 - + png_pass_start[png_ptr->pass]) / + png_pass_inc[png_ptr->pass]; + + if (png_ptr->transformations & PNG_INTERLACE) + break; + + png_ptr->num_rows = (png_ptr->height + + png_pass_yinc[png_ptr->pass] - 1 - + png_pass_ystart[png_ptr->pass]) / + png_pass_yinc[png_ptr->pass]; + + } while (png_ptr->iwidth == 0 || png_ptr->num_rows == 0); + } +#endif /* PNG_READ_INTERLACING_SUPPORTED */ +} + +void /* PRIVATE */ +png_push_have_info(png_structrp png_ptr, png_inforp info_ptr) +{ + if (png_ptr->info_fn != NULL) + (*(png_ptr->info_fn))(png_ptr, info_ptr); +} + +void /* PRIVATE */ +png_push_have_end(png_structrp png_ptr, png_inforp info_ptr) +{ + if (png_ptr->end_fn != NULL) + (*(png_ptr->end_fn))(png_ptr, info_ptr); +} + +void /* PRIVATE */ +png_push_have_row(png_structrp png_ptr, png_bytep row) +{ + if (png_ptr->row_fn != NULL) + (*(png_ptr->row_fn))(png_ptr, row, png_ptr->row_number, + (int)png_ptr->pass); +} + +#ifdef PNG_READ_INTERLACING_SUPPORTED +void PNGAPI +png_progressive_combine_row(png_const_structrp png_ptr, png_bytep old_row, + png_const_bytep new_row) +{ + if (png_ptr == NULL) + return; + + /* new_row is a flag here - if it is NULL then the app callback was called + * from an empty row (see the calls to png_struct::row_fn below), otherwise + * it must be png_ptr->row_buf+1 + */ + if (new_row != NULL) + png_combine_row(png_ptr, old_row, 1/*display*/); +} +#endif /* PNG_READ_INTERLACING_SUPPORTED */ + +void PNGAPI +png_set_progressive_read_fn(png_structrp png_ptr, png_voidp progressive_ptr, + png_progressive_info_ptr info_fn, png_progressive_row_ptr row_fn, + png_progressive_end_ptr end_fn) +{ + if (png_ptr == NULL) + return; + + png_ptr->info_fn = info_fn; + png_ptr->row_fn = row_fn; + png_ptr->end_fn = end_fn; + + png_set_read_fn(png_ptr, progressive_ptr, png_push_fill_buffer); +} + +png_voidp PNGAPI +png_get_progressive_ptr(png_const_structrp png_ptr) +{ + if (png_ptr == NULL) + return (NULL); + + return png_ptr->io_ptr; +} +#endif /* PNG_PROGRESSIVE_READ_SUPPORTED */ diff --git a/Minecraft.Client/Windows64/4JLibs/inc/Render/libpng/pngpriv.h b/Minecraft.Client/Windows64/4JLibs/inc/Render/libpng/pngpriv.h new file mode 100644 index 000000000..d06284d1e --- /dev/null +++ b/Minecraft.Client/Windows64/4JLibs/inc/Render/libpng/pngpriv.h @@ -0,0 +1,1913 @@ + +/* pngpriv.h - private declarations for use inside libpng + * + * For conditions of distribution and use, see copyright notice in png.h + * Copyright (c) 1998-2013 Glenn Randers-Pehrson + * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) + * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) + * + * Last changed in libpng 1.6.2 [April 25, 2013] + * + * This code is released under the libpng license. + * For conditions of distribution and use, see the disclaimer + * and license in png.h + */ + +/* The symbols declared in this file (including the functions declared + * as extern) are PRIVATE. They are not part of the libpng public + * interface, and are not recommended for use by regular applications. + * Some of them may become public in the future; others may stay private, + * change in an incompatible way, or even disappear. + * Although the libpng users are not forbidden to include this header, + * they should be well aware of the issues that may arise from doing so. + */ + +#ifndef PNGPRIV_H +#define PNGPRIV_H + +/* Feature Test Macros. The following are defined here to ensure that correctly + * implemented libraries reveal the APIs libpng needs to build and hide those + * that are not needed and potentially damaging to the compilation. + * + * Feature Test Macros must be defined before any system header is included (see + * POSIX 1003.1 2.8.2 "POSIX Symbols." + * + * These macros only have an effect if the operating system supports either + * POSIX 1003.1 or C99, or both. On other operating systems (particularly + * Windows/Visual Studio) there is no effect; the OS specific tests below are + * still required (as of 2011-05-02.) + */ +#define _POSIX_SOURCE 1 /* Just the POSIX 1003.1 and C89 APIs */ + +#ifndef PNG_VERSION_INFO_ONLY +/* Standard library headers not required by png.h: */ +# include +# include +#endif + +#define PNGLIB_BUILD /*libpng is being built, not used*/ + +/* If HAVE_CONFIG_H is defined during the build then the build system must + * provide an appropriate "config.h" file on the include path. The header file + * must provide definitions as required below (search for "HAVE_CONFIG_H"); + * see configure.ac for more details of the requirements. The macro + * "PNG_NO_CONFIG_H" is provided for maintainers to test for dependencies on + * 'configure'; define this macro to prevent the configure build including the + * configure generated config.h. Libpng is expected to compile without *any* + * special build system support on a reasonably ANSI-C compliant system. + */ +#if defined(HAVE_CONFIG_H) && !defined(PNG_NO_CONFIG_H) +# include + + /* Pick up the definition of 'restrict' from config.h if it was read: */ +# define PNG_RESTRICT restrict +#endif + +/* To support symbol prefixing it is necessary to know *before* including png.h + * whether the fixed point (and maybe other) APIs are exported, because if they + * are not internal definitions may be required. This is handled below just + * before png.h is included, but load the configuration now if it is available. + */ +#ifndef PNGLCONF_H +# include "pnglibconf.h" +#endif + +/* Local renames may change non-exported API functions from png.h */ +#if defined(PNG_PREFIX) && !defined(PNGPREFIX_H) +# include "pngprefix.h" +#endif + +#ifdef PNG_USER_CONFIG +# include "pngusr.h" + /* These should have been defined in pngusr.h */ +# ifndef PNG_USER_PRIVATEBUILD +# define PNG_USER_PRIVATEBUILD "Custom libpng build" +# endif +# ifndef PNG_USER_DLLFNAME_POSTFIX +# define PNG_USER_DLLFNAME_POSTFIX "Cb" +# endif +#endif + +/* Is this a build of a DLL where compilation of the object modules requires + * different preprocessor settings to those required for a simple library? If + * so PNG_BUILD_DLL must be set. + * + * If libpng is used inside a DLL but that DLL does not export the libpng APIs + * PNG_BUILD_DLL must not be set. To avoid the code below kicking in build a + * static library of libpng then link the DLL against that. + */ +#ifndef PNG_BUILD_DLL +# ifdef DLL_EXPORT + /* This is set by libtool when files are compiled for a DLL; libtool + * always compiles twice, even on systems where it isn't necessary. Set + * PNG_BUILD_DLL in case it is necessary: + */ +# define PNG_BUILD_DLL +# else +# ifdef _WINDLL + /* This is set by the Microsoft Visual Studio IDE in projects that + * build a DLL. It can't easily be removed from those projects (it + * isn't visible in the Visual Studio UI) so it is a fairly reliable + * indication that PNG_IMPEXP needs to be set to the DLL export + * attributes. + */ +# define PNG_BUILD_DLL +# else +# ifdef __DLL__ + /* This is set by the Borland C system when compiling for a DLL + * (as above.) + */ +# define PNG_BUILD_DLL +# else + /* Add additional compiler cases here. */ +# endif +# endif +# endif +#endif /* Setting PNG_BUILD_DLL if required */ + +/* See pngconf.h for more details: the builder of the library may set this on + * the command line to the right thing for the specific compilation system or it + * may be automagically set above (at present we know of no system where it does + * need to be set on the command line.) + * + * PNG_IMPEXP must be set here when building the library to prevent pngconf.h + * setting it to the "import" setting for a DLL build. + */ +#ifndef PNG_IMPEXP +# ifdef PNG_BUILD_DLL +# define PNG_IMPEXP PNG_DLL_EXPORT +# else + /* Not building a DLL, or the DLL doesn't require specific export + * definitions. + */ +# define PNG_IMPEXP +# endif +#endif + +/* No warnings for private or deprecated functions in the build: */ +#ifndef PNG_DEPRECATED +# define PNG_DEPRECATED +#endif +#ifndef PNG_PRIVATE +# define PNG_PRIVATE +#endif + +/* Symbol preprocessing support. + * + * To enable listing global, but internal, symbols the following macros should + * always be used to declare an extern data or function object in this file. + */ +#ifndef PNG_INTERNAL_DATA +# define PNG_INTERNAL_DATA(type, name, array) extern type name array +#endif + +#ifndef PNG_INTERNAL_FUNCTION +# define PNG_INTERNAL_FUNCTION(type, name, args, attributes)\ + extern PNG_FUNCTION(type, name, args, PNG_EMPTY attributes) +#endif + +/* If floating or fixed point APIs are disabled they may still be compiled + * internally. To handle this make sure they are declared as the appropriate + * internal extern function (otherwise the symbol prefixing stuff won't work and + * the functions will be used without definitions.) + * + * NOTE: although all the API functions are declared here they are not all + * actually built! Because the declarations are still made it is necessary to + * fake out types that they depend on. + */ +#ifndef PNG_FP_EXPORT +# ifndef PNG_FLOATING_POINT_SUPPORTED +# define PNG_FP_EXPORT(ordinal, type, name, args)\ + PNG_INTERNAL_FUNCTION(type, name, args, PNG_EMPTY); +# ifndef PNG_VERSION_INFO_ONLY + typedef struct png_incomplete png_double; + typedef png_double* png_doublep; + typedef const png_double* png_const_doublep; + typedef png_double** png_doublepp; +# endif +# endif +#endif +#ifndef PNG_FIXED_EXPORT +# ifndef PNG_FIXED_POINT_SUPPORTED +# define PNG_FIXED_EXPORT(ordinal, type, name, args)\ + PNG_INTERNAL_FUNCTION(type, name, args, PNG_EMPTY); +# endif +#endif + +#include "png.h" + +/* pngconf.h does not set PNG_DLL_EXPORT unless it is required, so: */ +#ifndef PNG_DLL_EXPORT +# define PNG_DLL_EXPORT +#endif + +/* SECURITY and SAFETY: + * + * By default libpng is built without any internal limits on image size, + * individual heap (png_malloc) allocations or the total amount of memory used. + * If PNG_SAFE_LIMITS_SUPPORTED is defined, however, the limits below are used + * (unless individually overridden). These limits are believed to be fairly + * safe, but builders of secure systems should verify the values against the + * real system capabilities. + */ +#ifdef PNG_SAFE_LIMITS_SUPPORTED + /* 'safe' limits */ +# ifndef PNG_USER_WIDTH_MAX +# define PNG_USER_WIDTH_MAX 1000000 +# endif +# ifndef PNG_USER_HEIGHT_MAX +# define PNG_USER_HEIGHT_MAX 1000000 +# endif +# ifndef PNG_USER_CHUNK_CACHE_MAX +# define PNG_USER_CHUNK_CACHE_MAX 128 +# endif +# ifndef PNG_USER_CHUNK_MALLOC_MAX +# define PNG_USER_CHUNK_MALLOC_MAX 8000000 +# endif +#else + /* values for no limits */ +# ifndef PNG_USER_WIDTH_MAX +# define PNG_USER_WIDTH_MAX 0x7fffffff +# endif +# ifndef PNG_USER_HEIGHT_MAX +# define PNG_USER_HEIGHT_MAX 0x7fffffff +# endif +# ifndef PNG_USER_CHUNK_CACHE_MAX +# define PNG_USER_CHUNK_CACHE_MAX 0 +# endif +# ifndef PNG_USER_CHUNK_MALLOC_MAX +# define PNG_USER_CHUNK_MALLOC_MAX 0 +# endif +#endif + +/* Moved to pngpriv.h at libpng-1.5.0 */ +/* NOTE: some of these may have been used in external applications as + * these definitions were exposed in pngconf.h prior to 1.5. + */ + +/* If you are running on a machine where you cannot allocate more + * than 64K of memory at once, uncomment this. While libpng will not + * normally need that much memory in a chunk (unless you load up a very + * large file), zlib needs to know how big of a chunk it can use, and + * libpng thus makes sure to check any memory allocation to verify it + * will fit into memory. + * + * zlib provides 'MAXSEG_64K' which, if defined, indicates the + * same limit and pngconf.h (already included) sets the limit + * if certain operating systems are detected. + */ +#if defined(MAXSEG_64K) && !defined(PNG_MAX_MALLOC_64K) +# define PNG_MAX_MALLOC_64K +#endif + +#ifndef PNG_UNUSED +/* Unused formal parameter warnings are silenced using the following macro + * which is expected to have no bad effects on performance (optimizing + * compilers will probably remove it entirely). Note that if you replace + * it with something other than whitespace, you must include the terminating + * semicolon. + */ +# define PNG_UNUSED(param) (void)param; +#endif + +/* Just a little check that someone hasn't tried to define something + * contradictory. + */ +#if (PNG_ZBUF_SIZE > 65536L) && defined(PNG_MAX_MALLOC_64K) +# undef PNG_ZBUF_SIZE +# define PNG_ZBUF_SIZE 65536L +#endif + +/* If warnings or errors are turned off the code is disabled or redirected here. + * From 1.5.4 functions have been added to allow very limited formatting of + * error and warning messages - this code will also be disabled here. + */ +#ifdef PNG_WARNINGS_SUPPORTED +# define PNG_WARNING_PARAMETERS(p) png_warning_parameters p; +#else +# define png_warning(s1,s2) ((void)(s1)) +# define png_chunk_warning(s1,s2) ((void)(s1)) +# define png_warning_parameter(p,number,string) ((void)0) +# define png_warning_parameter_unsigned(p,number,format,value) ((void)0) +# define png_warning_parameter_signed(p,number,format,value) ((void)0) +# define png_formatted_warning(pp,p,message) ((void)(pp)) +# define PNG_WARNING_PARAMETERS(p) +#endif +#ifndef PNG_ERROR_TEXT_SUPPORTED +# define png_error(s1,s2) png_err(s1) +# define png_chunk_error(s1,s2) png_err(s1) +# define png_fixed_error(s1,s2) png_err(s1) +#endif + +/* C allows up-casts from (void*) to any pointer and (const void*) to any + * pointer to a const object. C++ regards this as a type error and requires an + * explicit, static, cast and provides the static_cast<> rune to ensure that + * const is not cast away. + */ +#ifdef __cplusplus +# define png_voidcast(type, value) static_cast(value) +# define png_constcast(type, value) const_cast(value) +# define png_aligncast(type, value) \ + static_cast(static_cast(value)) +# define png_aligncastconst(type, value) \ + static_cast(static_cast(value)) +#else +# define png_voidcast(type, value) (value) +# define png_constcast(type, value) ((type)(value)) +# define png_aligncast(type, value) ((void*)(value)) +# define png_aligncastconst(type, value) ((const void*)(value)) +#endif /* __cplusplus */ + +/* Some fixed point APIs are still required even if not exported because + * they get used by the corresponding floating point APIs. This magic + * deals with this: + */ +#ifdef PNG_FIXED_POINT_SUPPORTED +# define PNGFAPI PNGAPI +#else +# define PNGFAPI /* PRIVATE */ +#endif + +#ifndef PNG_VERSION_INFO_ONLY +/* Other defines specific to compilers can go here. Try to keep + * them inside an appropriate ifdef/endif pair for portability. + */ +#if defined(PNG_FLOATING_POINT_SUPPORTED) ||\ + defined(PNG_FLOATING_ARITHMETIC_SUPPORTED) + /* png.c requires the following ANSI-C constants if the conversion of + * floating point to ASCII is implemented therein: + * + * DBL_DIG Maximum number of decimal digits (can be set to any constant) + * DBL_MIN Smallest normalized fp number (can be set to an arbitrary value) + * DBL_MAX Maximum floating point number (can be set to an arbitrary value) + */ +# include + +# if (defined(__MWERKS__) && defined(macintosh)) || defined(applec) || \ + defined(THINK_C) || defined(__SC__) || defined(TARGET_OS_MAC) + /* We need to check that hasn't already been included earlier + * as it seems it doesn't agree with , yet we should really use + * if possible. + */ +# if !defined(__MATH_H__) && !defined(__MATH_H) && !defined(__cmath__) +# include +# endif +# else +# include +# endif +# if defined(_AMIGA) && defined(__SASC) && defined(_M68881) + /* Amiga SAS/C: We must include builtin FPU functions when compiling using + * MATH=68881 + */ +# include +# endif +#endif + +/* This provides the non-ANSI (far) memory allocation routines. */ +#if defined(__TURBOC__) && defined(__MSDOS__) +# include +# include +#endif + +#if defined(WIN32) || defined(_Windows) || defined(_WINDOWS) || \ + defined(_WIN32) || defined(__WIN32__) +# include /* defines _WINDOWS_ macro */ +#endif +#endif /* PNG_VERSION_INFO_ONLY */ + +/* Moved here around 1.5.0beta36 from pngconf.h */ +/* Users may want to use these so they are not private. Any library + * functions that are passed far data must be model-independent. + */ + +/* Memory model/platform independent fns */ +#ifndef PNG_ABORT +# ifdef _WINDOWS_ +# define PNG_ABORT() ExitProcess(0) +# else +# define PNG_ABORT() abort() +# endif +#endif + +/* These macros may need to be architecture dependent. */ +#define PNG_ALIGN_NONE 0 /* do not use data alignment */ +#define PNG_ALIGN_ALWAYS 1 /* assume unaligned accesses are OK */ +#ifdef offsetof +# define PNG_ALIGN_OFFSET 2 /* use offsetof to determine alignment */ +#else +# define PNG_ALIGN_OFFSET -1 /* prevent the use of this */ +#endif +#define PNG_ALIGN_SIZE 3 /* use sizeof to determine alignment */ + +#ifndef PNG_ALIGN_TYPE + /* Default to using aligned access optimizations and requiring alignment to a + * multiple of the data type size. Override in a compiler specific fashion + * if necessary by inserting tests here: + */ +# define PNG_ALIGN_TYPE PNG_ALIGN_SIZE +#endif + +#if PNG_ALIGN_TYPE == PNG_ALIGN_SIZE + /* This is used because in some compiler implementations non-aligned + * structure members are supported, so the offsetof approach below fails. + * Set PNG_ALIGN_SIZE=0 for compiler combinations where unaligned access + * is good for performance. Do not do this unless you have tested the result + * and understand it. + */ +# define png_alignof(type) (sizeof (type)) +#else +# if PNG_ALIGN_TYPE == PNG_ALIGN_OFFSET +# define png_alignof(type) offsetof(struct{char c; type t;}, t) +# else +# if PNG_ALIGN_TYPE == PNG_ALIGN_ALWAYS +# define png_alignof(type) (1) +# endif + /* Else leave png_alignof undefined to prevent use thereof */ +# endif +#endif + +/* This implicitly assumes alignment is always to a power of 2. */ +#ifdef png_alignof +# define png_isaligned(ptr, type)\ + ((((const char*)ptr-(const char*)0) & (png_alignof(type)-1)) == 0) +#else +# define png_isaligned(ptr, type) 0 +#endif + +/* End of memory model/platform independent support */ +/* End of 1.5.0beta36 move from pngconf.h */ + +/* CONSTANTS and UTILITY MACROS + * These are used internally by libpng and not exposed in the API + */ + +/* Various modes of operation. Note that after an init, mode is set to + * zero automatically when the structure is created. Three of these + * are defined in png.h because they need to be visible to applications + * that call png_set_unknown_chunk(). + */ +/* #define PNG_HAVE_IHDR 0x01 (defined in png.h) */ +/* #define PNG_HAVE_PLTE 0x02 (defined in png.h) */ +#define PNG_HAVE_IDAT 0x04 +/* #define PNG_AFTER_IDAT 0x08 (defined in png.h) */ +#define PNG_HAVE_IEND 0x10 + /* 0x20 (unused) */ + /* 0x40 (unused) */ + /* 0x80 (unused) */ +#define PNG_HAVE_CHUNK_HEADER 0x100 +#define PNG_WROTE_tIME 0x200 +#define PNG_WROTE_INFO_BEFORE_PLTE 0x400 +#define PNG_BACKGROUND_IS_GRAY 0x800 +#define PNG_HAVE_PNG_SIGNATURE 0x1000 +#define PNG_HAVE_CHUNK_AFTER_IDAT 0x2000 /* Have another chunk after IDAT */ + /* 0x4000 (unused) */ +#define PNG_IS_READ_STRUCT 0x8000 /* Else is a write struct */ + +/* Flags for the transformations the PNG library does on the image data */ +#define PNG_BGR 0x0001 +#define PNG_INTERLACE 0x0002 +#define PNG_PACK 0x0004 +#define PNG_SHIFT 0x0008 +#define PNG_SWAP_BYTES 0x0010 +#define PNG_INVERT_MONO 0x0020 +#define PNG_QUANTIZE 0x0040 +#define PNG_COMPOSE 0x0080 /* Was PNG_BACKGROUND */ +#define PNG_BACKGROUND_EXPAND 0x0100 +#define PNG_EXPAND_16 0x0200 /* Added to libpng 1.5.2 */ +#define PNG_16_TO_8 0x0400 /* Becomes 'chop' in 1.5.4 */ +#define PNG_RGBA 0x0800 +#define PNG_EXPAND 0x1000 +#define PNG_GAMMA 0x2000 +#define PNG_GRAY_TO_RGB 0x4000 +#define PNG_FILLER 0x8000 +#define PNG_PACKSWAP 0x10000 +#define PNG_SWAP_ALPHA 0x20000 +#define PNG_STRIP_ALPHA 0x40000 +#define PNG_INVERT_ALPHA 0x80000 +#define PNG_USER_TRANSFORM 0x100000 +#define PNG_RGB_TO_GRAY_ERR 0x200000 +#define PNG_RGB_TO_GRAY_WARN 0x400000 +#define PNG_RGB_TO_GRAY 0x600000 /* two bits, RGB_TO_GRAY_ERR|WARN */ +#define PNG_ENCODE_ALPHA 0x800000 /* Added to libpng-1.5.4 */ +#define PNG_ADD_ALPHA 0x1000000 /* Added to libpng-1.2.7 */ +#define PNG_EXPAND_tRNS 0x2000000 /* Added to libpng-1.2.9 */ +#define PNG_SCALE_16_TO_8 0x4000000 /* Added to libpng-1.5.4 */ + /* 0x8000000 unused */ + /* 0x10000000 unused */ + /* 0x20000000 unused */ + /* 0x40000000 unused */ +/* Flags for png_create_struct */ +#define PNG_STRUCT_PNG 0x0001 +#define PNG_STRUCT_INFO 0x0002 + +/* Scaling factor for filter heuristic weighting calculations */ +#define PNG_WEIGHT_FACTOR (1<<(PNG_WEIGHT_SHIFT)) +#define PNG_COST_FACTOR (1<<(PNG_COST_SHIFT)) + +/* Flags for the png_ptr->flags rather than declaring a byte for each one */ +#define PNG_FLAG_ZLIB_CUSTOM_STRATEGY 0x0001 +#define PNG_FLAG_ZSTREAM_INITIALIZED 0x0002 /* Added to libpng-1.6.0 */ + /* 0x0004 unused */ +#define PNG_FLAG_ZSTREAM_ENDED 0x0008 /* Added to libpng-1.6.0 */ + /* 0x0010 unused */ + /* 0x0020 unused */ +#define PNG_FLAG_ROW_INIT 0x0040 +#define PNG_FLAG_FILLER_AFTER 0x0080 +#define PNG_FLAG_CRC_ANCILLARY_USE 0x0100 +#define PNG_FLAG_CRC_ANCILLARY_NOWARN 0x0200 +#define PNG_FLAG_CRC_CRITICAL_USE 0x0400 +#define PNG_FLAG_CRC_CRITICAL_IGNORE 0x0800 +#define PNG_FLAG_ASSUME_sRGB 0x1000 /* Added to libpng-1.5.4 */ +#define PNG_FLAG_OPTIMIZE_ALPHA 0x2000 /* Added to libpng-1.5.4 */ +#define PNG_FLAG_DETECT_UNINITIALIZED 0x4000 /* Added to libpng-1.5.4 */ +/* #define PNG_FLAG_KEEP_UNKNOWN_CHUNKS 0x8000 */ +/* #define PNG_FLAG_KEEP_UNSAFE_CHUNKS 0x10000 */ +#define PNG_FLAG_LIBRARY_MISMATCH 0x20000 +#define PNG_FLAG_STRIP_ERROR_NUMBERS 0x40000 +#define PNG_FLAG_STRIP_ERROR_TEXT 0x80000 +#define PNG_FLAG_BENIGN_ERRORS_WARN 0x100000 /* Added to libpng-1.4.0 */ +#define PNG_FLAG_APP_WARNINGS_WARN 0x200000 /* Added to libpng-1.6.0 */ +#define PNG_FLAG_APP_ERRORS_WARN 0x400000 /* Added to libpng-1.6.0 */ + /* 0x800000 unused */ + /* 0x1000000 unused */ + /* 0x2000000 unused */ + /* 0x4000000 unused */ + /* 0x8000000 unused */ + /* 0x10000000 unused */ + /* 0x20000000 unused */ + /* 0x40000000 unused */ + +#define PNG_FLAG_CRC_ANCILLARY_MASK (PNG_FLAG_CRC_ANCILLARY_USE | \ + PNG_FLAG_CRC_ANCILLARY_NOWARN) + +#define PNG_FLAG_CRC_CRITICAL_MASK (PNG_FLAG_CRC_CRITICAL_USE | \ + PNG_FLAG_CRC_CRITICAL_IGNORE) + +#define PNG_FLAG_CRC_MASK (PNG_FLAG_CRC_ANCILLARY_MASK | \ + PNG_FLAG_CRC_CRITICAL_MASK) + +/* Save typing and make code easier to understand */ + +#define PNG_COLOR_DIST(c1, c2) (abs((int)((c1).red) - (int)((c2).red)) + \ + abs((int)((c1).green) - (int)((c2).green)) + \ + abs((int)((c1).blue) - (int)((c2).blue))) + +/* Added to libpng-1.6.0: scale a 16-bit value in the range 0..65535 to 0..255 + * by dividing by 257 *with rounding*. This macro is exact for the given range. + * See the discourse in pngrtran.c png_do_scale_16_to_8. The values in the + * macro were established by experiment (modifying the added value). The macro + * has a second variant that takes a value already scaled by 255 and divides by + * 65535 - this has a maximum error of .502. Over the range 0..65535*65535 it + * only gives off-by-one errors and only for 0.5% (1 in 200) of the values. + */ +#define PNG_DIV65535(v24) (((v24) + 32895) >> 16) +#define PNG_DIV257(v16) PNG_DIV65535((png_uint_32)(v16) * 255) + +/* Added to libpng-1.2.6 JB */ +#define PNG_ROWBYTES(pixel_bits, width) \ + ((pixel_bits) >= 8 ? \ + ((png_size_t)(width) * (((png_size_t)(pixel_bits)) >> 3)) : \ + (( ((png_size_t)(width) * ((png_size_t)(pixel_bits))) + 7) >> 3) ) + +/* PNG_OUT_OF_RANGE returns true if value is outside the range + * ideal-delta..ideal+delta. Each argument is evaluated twice. + * "ideal" and "delta" should be constants, normally simple + * integers, "value" a variable. Added to libpng-1.2.6 JB + */ +#define PNG_OUT_OF_RANGE(value, ideal, delta) \ + ( (value) < (ideal)-(delta) || (value) > (ideal)+(delta) ) + +/* Conversions between fixed and floating point, only defined if + * required (to make sure the code doesn't accidentally use float + * when it is supposedly disabled.) + */ +#ifdef PNG_FLOATING_POINT_SUPPORTED +/* The floating point conversion can't overflow, though it can and + * does lose accuracy relative to the original fixed point value. + * In practice this doesn't matter because png_fixed_point only + * stores numbers with very low precision. The png_ptr and s + * arguments are unused by default but are there in case error + * checking becomes a requirement. + */ +#define png_float(png_ptr, fixed, s) (.00001 * (fixed)) + +/* The fixed point conversion performs range checking and evaluates + * its argument multiple times, so must be used with care. The + * range checking uses the PNG specification values for a signed + * 32 bit fixed point value except that the values are deliberately + * rounded-to-zero to an integral value - 21474 (21474.83 is roughly + * (2^31-1) * 100000). 's' is a string that describes the value being + * converted. + * + * NOTE: this macro will raise a png_error if the range check fails, + * therefore it is normally only appropriate to use this on values + * that come from API calls or other sources where an out of range + * error indicates a programming error, not a data error! + * + * NOTE: by default this is off - the macro is not used - because the + * function call saves a lot of code. + */ +#ifdef PNG_FIXED_POINT_MACRO_SUPPORTED +#define png_fixed(png_ptr, fp, s) ((fp) <= 21474 && (fp) >= -21474 ?\ + ((png_fixed_point)(100000 * (fp))) : (png_fixed_error(png_ptr, s),0)) +#endif +/* else the corresponding function is defined below, inside the scope of the + * cplusplus test. + */ +#endif + +/* Constants for known chunk types. If you need to add a chunk, define the name + * here. For historical reasons these constants have the form png_; i.e. + * the prefix is lower case. Please use decimal values as the parameters to + * match the ISO PNG specification and to avoid relying on the C locale + * interpretation of character values. + * + * Prior to 1.5.6 these constants were strings, as of 1.5.6 png_uint_32 values + * are computed and a new macro (PNG_STRING_FROM_CHUNK) added to allow a string + * to be generated if required. + * + * PNG_32b correctly produces a value shifted by up to 24 bits, even on + * architectures where (int) is only 16 bits. + */ +#define PNG_32b(b,s) ((png_uint_32)(b) << (s)) +#define PNG_CHUNK(b1,b2,b3,b4) \ + (PNG_32b(b1,24) | PNG_32b(b2,16) | PNG_32b(b3,8) | PNG_32b(b4,0)) + +#define png_IHDR PNG_CHUNK( 73, 72, 68, 82) +#define png_IDAT PNG_CHUNK( 73, 68, 65, 84) +#define png_IEND PNG_CHUNK( 73, 69, 78, 68) +#define png_PLTE PNG_CHUNK( 80, 76, 84, 69) +#define png_bKGD PNG_CHUNK( 98, 75, 71, 68) +#define png_cHRM PNG_CHUNK( 99, 72, 82, 77) +#define png_gAMA PNG_CHUNK(103, 65, 77, 65) +#define png_hIST PNG_CHUNK(104, 73, 83, 84) +#define png_iCCP PNG_CHUNK(105, 67, 67, 80) +#define png_iTXt PNG_CHUNK(105, 84, 88, 116) +#define png_oFFs PNG_CHUNK(111, 70, 70, 115) +#define png_pCAL PNG_CHUNK(112, 67, 65, 76) +#define png_sCAL PNG_CHUNK(115, 67, 65, 76) +#define png_pHYs PNG_CHUNK(112, 72, 89, 115) +#define png_sBIT PNG_CHUNK(115, 66, 73, 84) +#define png_sPLT PNG_CHUNK(115, 80, 76, 84) +#define png_sRGB PNG_CHUNK(115, 82, 71, 66) +#define png_sTER PNG_CHUNK(115, 84, 69, 82) +#define png_tEXt PNG_CHUNK(116, 69, 88, 116) +#define png_tIME PNG_CHUNK(116, 73, 77, 69) +#define png_tRNS PNG_CHUNK(116, 82, 78, 83) +#define png_zTXt PNG_CHUNK(122, 84, 88, 116) + +/* The following will work on (signed char*) strings, whereas the get_uint_32 + * macro will fail on top-bit-set values because of the sign extension. + */ +#define PNG_CHUNK_FROM_STRING(s)\ + PNG_CHUNK(0xff&(s)[0], 0xff&(s)[1], 0xff&(s)[2], 0xff&(s)[3]) + +/* This uses (char), not (png_byte) to avoid warnings on systems where (char) is + * signed and the argument is a (char[]) This macro will fail miserably on + * systems where (char) is more than 8 bits. + */ +#define PNG_STRING_FROM_CHUNK(s,c)\ + (void)(((char*)(s))[0]=(char)((c)>>24), ((char*)(s))[1]=(char)((c)>>16),\ + ((char*)(s))[2]=(char)((c)>>8), ((char*)(s))[3]=(char)((c))) + +/* Do the same but terminate with a null character. */ +#define PNG_CSTRING_FROM_CHUNK(s,c)\ + (void)(PNG_STRING_FROM_CHUNK(s,c), ((char*)(s))[4] = 0) + +/* Test on flag values as defined in the spec (section 5.4): */ +#define PNG_CHUNK_ANCILLARY(c) (1 & ((c) >> 29)) +#define PNG_CHUNK_CRITICAL(c) (!PNG_CHUNK_ANCILLARY(c)) +#define PNG_CHUNK_PRIVATE(c) (1 & ((c) >> 21)) +#define PNG_CHUNK_RESERVED(c) (1 & ((c) >> 13)) +#define PNG_CHUNK_SAFE_TO_COPY(c) (1 & ((c) >> 5)) + +/* Gamma values (new at libpng-1.5.4): */ +#define PNG_GAMMA_MAC_OLD 151724 /* Assume '1.8' is really 2.2/1.45! */ +#define PNG_GAMMA_MAC_INVERSE 65909 +#define PNG_GAMMA_sRGB_INVERSE 45455 + +/* Almost everything below is C specific; the #defines above can be used in + * non-C code (so long as it is C-preprocessed) the rest of this stuff cannot. + */ +#ifndef PNG_VERSION_INFO_ONLY + +#include "pngstruct.h" +#include "pnginfo.h" + +/* This is used for 16 bit gamma tables -- only the top level pointers are + * const; this could be changed: + */ +typedef const png_uint_16p * png_const_uint_16pp; + +/* Added to libpng-1.5.7: sRGB conversion tables */ +#if defined(PNG_SIMPLIFIED_READ_SUPPORTED) ||\ + defined(PNG_SIMPLIFIED_WRITE_SUPPORTED) +#ifdef PNG_SIMPLIFIED_READ_SUPPORTED +PNG_INTERNAL_DATA(const png_uint_16, png_sRGB_table, [256]); + /* Convert from an sRGB encoded value 0..255 to a 16-bit linear value, + * 0..65535. This table gives the closest 16-bit answers (no errors). + */ +#endif + +PNG_INTERNAL_DATA(const png_uint_16, png_sRGB_base, [512]); +PNG_INTERNAL_DATA(const png_byte, png_sRGB_delta, [512]); + +#define PNG_sRGB_FROM_LINEAR(linear) ((png_byte)((png_sRGB_base[(linear)>>15] +\ + ((((linear)&0x7fff)*png_sRGB_delta[(linear)>>15])>>12)) >> 8)) + /* Given a value 'linear' in the range 0..255*65535 calculate the 8-bit sRGB + * encoded value with maximum error 0.646365. Note that the input is not a + * 16-bit value; it has been multiplied by 255! */ +#endif /* PNG_SIMPLIFIED_READ/WRITE */ + + +/* Inhibit C++ name-mangling for libpng functions but not for system calls. */ +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/* Internal functions; these are not exported from a DLL however because they + * are used within several of the C source files they have to be C extern. + * + * All of these functions must be declared with PNG_INTERNAL_FUNCTION. + */ + +/* Zlib support */ +#define PNG_UNEXPECTED_ZLIB_RETURN (-7) +PNG_INTERNAL_FUNCTION(void, png_zstream_error,(png_structrp png_ptr, int ret), + PNG_EMPTY); + /* Used by the zlib handling functions to ensure that z_stream::msg is always + * set before they return. + */ + +#ifdef PNG_WRITE_SUPPORTED +PNG_INTERNAL_FUNCTION(void,png_free_buffer_list,(png_structrp png_ptr, + png_compression_bufferp *list),PNG_EMPTY); + /* Free the buffer list used by the compressed write code. */ +#endif + +#if defined(PNG_FLOATING_POINT_SUPPORTED) && \ + !defined(PNG_FIXED_POINT_MACRO_SUPPORTED) && \ + (defined(PNG_gAMA_SUPPORTED) || defined(PNG_cHRM_SUPPORTED) || \ + defined(PNG_sCAL_SUPPORTED) || defined(PNG_READ_BACKGROUND_SUPPORTED) || \ + defined(PNG_READ_RGB_TO_GRAY_SUPPORTED)) || \ + (defined(PNG_sCAL_SUPPORTED) && \ + defined(PNG_FLOATING_ARITHMETIC_SUPPORTED)) +PNG_INTERNAL_FUNCTION(png_fixed_point,png_fixed,(png_const_structrp png_ptr, + double fp, png_const_charp text),PNG_EMPTY); +#endif + +/* Check the user version string for compatibility, returns false if the version + * numbers aren't compatible. + */ +PNG_INTERNAL_FUNCTION(int,png_user_version_check,(png_structrp png_ptr, + png_const_charp user_png_ver),PNG_EMPTY); + +/* Internal base allocator - no messages, NULL on failure to allocate. This + * does, however, call the application provided allocator and that could call + * png_error (although that would be a bug in the application implementation.) + */ +PNG_INTERNAL_FUNCTION(png_voidp,png_malloc_base,(png_const_structrp png_ptr, + png_alloc_size_t size),PNG_ALLOCATED); + +#if defined(PNG_TEXT_SUPPORTED) || defined(PNG_sPLT_SUPPORTED) ||\ + defined(PNG_STORE_UNKNOWN_CHUNKS_SUPPORTED) +/* Internal array allocator, outputs no error or warning messages on failure, + * just returns NULL. + */ +PNG_INTERNAL_FUNCTION(png_voidp,png_malloc_array,(png_const_structrp png_ptr, + int nelements, size_t element_size),PNG_ALLOCATED); + +/* The same but an existing array is extended by add_elements. This function + * also memsets the new elements to 0 and copies the old elements. The old + * array is not freed or altered. + */ +PNG_INTERNAL_FUNCTION(png_voidp,png_realloc_array,(png_const_structrp png_ptr, + png_const_voidp array, int old_elements, int add_elements, + size_t element_size),PNG_ALLOCATED); +#endif /* text, sPLT or unknown chunks */ + +/* Magic to create a struct when there is no struct to call the user supplied + * memory allocators. Because error handling has not been set up the memory + * handlers can't safely call png_error, but this is an obscure and undocumented + * restriction so libpng has to assume that the 'free' handler, at least, might + * call png_error. + */ +PNG_INTERNAL_FUNCTION(png_structp,png_create_png_struct, + (png_const_charp user_png_ver, png_voidp error_ptr, png_error_ptr error_fn, + png_error_ptr warn_fn, png_voidp mem_ptr, png_malloc_ptr malloc_fn, + png_free_ptr free_fn),PNG_ALLOCATED); + +/* Free memory from internal libpng struct */ +PNG_INTERNAL_FUNCTION(void,png_destroy_png_struct,(png_structrp png_ptr), + PNG_EMPTY); + +/* Free an allocated jmp_buf (always succeeds) */ +PNG_INTERNAL_FUNCTION(void,png_free_jmpbuf,(png_structrp png_ptr),PNG_EMPTY); + +/* Function to allocate memory for zlib. PNGAPI is disallowed. */ +PNG_INTERNAL_FUNCTION(voidpf,png_zalloc,(voidpf png_ptr, uInt items, uInt size), + PNG_ALLOCATED); + +/* Function to free memory for zlib. PNGAPI is disallowed. */ +PNG_INTERNAL_FUNCTION(void,png_zfree,(voidpf png_ptr, voidpf ptr),PNG_EMPTY); + +/* Next four functions are used internally as callbacks. PNGCBAPI is required + * but not PNG_EXPORT. PNGAPI added at libpng version 1.2.3, changed to + * PNGCBAPI at 1.5.0 + */ + +PNG_INTERNAL_FUNCTION(void PNGCBAPI,png_default_read_data,(png_structp png_ptr, + png_bytep data, png_size_t length),PNG_EMPTY); + +#ifdef PNG_PROGRESSIVE_READ_SUPPORTED +PNG_INTERNAL_FUNCTION(void PNGCBAPI,png_push_fill_buffer,(png_structp png_ptr, + png_bytep buffer, png_size_t length),PNG_EMPTY); +#endif + +PNG_INTERNAL_FUNCTION(void PNGCBAPI,png_default_write_data,(png_structp png_ptr, + png_bytep data, png_size_t length),PNG_EMPTY); + +#ifdef PNG_WRITE_FLUSH_SUPPORTED +# ifdef PNG_STDIO_SUPPORTED +PNG_INTERNAL_FUNCTION(void PNGCBAPI,png_default_flush,(png_structp png_ptr), + PNG_EMPTY); +# endif +#endif + +/* Reset the CRC variable */ +PNG_INTERNAL_FUNCTION(void,png_reset_crc,(png_structrp png_ptr),PNG_EMPTY); + +/* Write the "data" buffer to whatever output you are using */ +PNG_INTERNAL_FUNCTION(void,png_write_data,(png_structrp png_ptr, + png_const_bytep data, png_size_t length),PNG_EMPTY); + +/* Read and check the PNG file signature */ +PNG_INTERNAL_FUNCTION(void,png_read_sig,(png_structrp png_ptr, + png_inforp info_ptr),PNG_EMPTY); + +/* Read the chunk header (length + type name) */ +PNG_INTERNAL_FUNCTION(png_uint_32,png_read_chunk_header,(png_structrp png_ptr), + PNG_EMPTY); + +/* Read data from whatever input you are using into the "data" buffer */ +PNG_INTERNAL_FUNCTION(void,png_read_data,(png_structrp png_ptr, png_bytep data, + png_size_t length),PNG_EMPTY); + +/* Read bytes into buf, and update png_ptr->crc */ +PNG_INTERNAL_FUNCTION(void,png_crc_read,(png_structrp png_ptr, png_bytep buf, + png_uint_32 length),PNG_EMPTY); + +/* Read "skip" bytes, read the file crc, and (optionally) verify png_ptr->crc */ +PNG_INTERNAL_FUNCTION(int,png_crc_finish,(png_structrp png_ptr, + png_uint_32 skip),PNG_EMPTY); + +/* Read the CRC from the file and compare it to the libpng calculated CRC */ +PNG_INTERNAL_FUNCTION(int,png_crc_error,(png_structrp png_ptr),PNG_EMPTY); + +/* Calculate the CRC over a section of data. Note that we are only + * passing a maximum of 64K on systems that have this as a memory limit, + * since this is the maximum buffer size we can specify. + */ +PNG_INTERNAL_FUNCTION(void,png_calculate_crc,(png_structrp png_ptr, + png_const_bytep ptr, png_size_t length),PNG_EMPTY); + +#ifdef PNG_WRITE_FLUSH_SUPPORTED +PNG_INTERNAL_FUNCTION(void,png_flush,(png_structrp png_ptr),PNG_EMPTY); +#endif + +/* Write various chunks */ + +/* Write the IHDR chunk, and update the png_struct with the necessary + * information. + */ +PNG_INTERNAL_FUNCTION(void,png_write_IHDR,(png_structrp png_ptr, + png_uint_32 width, png_uint_32 height, int bit_depth, int color_type, + int compression_method, int filter_method, int interlace_method),PNG_EMPTY); + +PNG_INTERNAL_FUNCTION(void,png_write_PLTE,(png_structrp png_ptr, + png_const_colorp palette, png_uint_32 num_pal),PNG_EMPTY); + +PNG_INTERNAL_FUNCTION(void,png_compress_IDAT,(png_structrp png_ptr, + png_const_bytep row_data, png_alloc_size_t row_data_length, int flush), + PNG_EMPTY); + +PNG_INTERNAL_FUNCTION(void,png_write_IEND,(png_structrp png_ptr),PNG_EMPTY); + +#ifdef PNG_WRITE_gAMA_SUPPORTED +PNG_INTERNAL_FUNCTION(void,png_write_gAMA_fixed,(png_structrp png_ptr, + png_fixed_point file_gamma),PNG_EMPTY); +#endif + +#ifdef PNG_WRITE_sBIT_SUPPORTED +PNG_INTERNAL_FUNCTION(void,png_write_sBIT,(png_structrp png_ptr, + png_const_color_8p sbit, int color_type),PNG_EMPTY); +#endif + +#ifdef PNG_WRITE_cHRM_SUPPORTED +PNG_INTERNAL_FUNCTION(void,png_write_cHRM_fixed,(png_structrp png_ptr, + const png_xy *xy), PNG_EMPTY); + /* The xy value must have been previously validated */ +#endif + +#ifdef PNG_WRITE_sRGB_SUPPORTED +PNG_INTERNAL_FUNCTION(void,png_write_sRGB,(png_structrp png_ptr, + int intent),PNG_EMPTY); +#endif + +#ifdef PNG_WRITE_iCCP_SUPPORTED +PNG_INTERNAL_FUNCTION(void,png_write_iCCP,(png_structrp png_ptr, + png_const_charp name, png_const_bytep profile), PNG_EMPTY); + /* The profile must have been previously validated for correctness, the + * length comes from the first four bytes. Only the base, deflate, + * compression is supported. + */ +#endif + +#ifdef PNG_WRITE_sPLT_SUPPORTED +PNG_INTERNAL_FUNCTION(void,png_write_sPLT,(png_structrp png_ptr, + png_const_sPLT_tp palette),PNG_EMPTY); +#endif + +#ifdef PNG_WRITE_tRNS_SUPPORTED +PNG_INTERNAL_FUNCTION(void,png_write_tRNS,(png_structrp png_ptr, + png_const_bytep trans, png_const_color_16p values, int number, + int color_type),PNG_EMPTY); +#endif + +#ifdef PNG_WRITE_bKGD_SUPPORTED +PNG_INTERNAL_FUNCTION(void,png_write_bKGD,(png_structrp png_ptr, + png_const_color_16p values, int color_type),PNG_EMPTY); +#endif + +#ifdef PNG_WRITE_hIST_SUPPORTED +PNG_INTERNAL_FUNCTION(void,png_write_hIST,(png_structrp png_ptr, + png_const_uint_16p hist, int num_hist),PNG_EMPTY); +#endif + +/* Chunks that have keywords */ +#ifdef PNG_WRITE_tEXt_SUPPORTED +PNG_INTERNAL_FUNCTION(void,png_write_tEXt,(png_structrp png_ptr, + png_const_charp key, png_const_charp text, png_size_t text_len),PNG_EMPTY); +#endif + +#ifdef PNG_WRITE_zTXt_SUPPORTED +PNG_INTERNAL_FUNCTION(void,png_write_zTXt,(png_structrp png_ptr, png_const_charp + key, png_const_charp text, png_size_t text_len, int compression),PNG_EMPTY); +#endif + +#ifdef PNG_WRITE_iTXt_SUPPORTED +PNG_INTERNAL_FUNCTION(void,png_write_iTXt,(png_structrp png_ptr, + int compression, png_const_charp key, png_const_charp lang, + png_const_charp lang_key, png_const_charp text),PNG_EMPTY); +#endif + +#ifdef PNG_TEXT_SUPPORTED /* Added at version 1.0.14 and 1.2.4 */ +PNG_INTERNAL_FUNCTION(int,png_set_text_2,(png_const_structrp png_ptr, + png_inforp info_ptr, png_const_textp text_ptr, int num_text),PNG_EMPTY); +#endif + +#ifdef PNG_WRITE_oFFs_SUPPORTED +PNG_INTERNAL_FUNCTION(void,png_write_oFFs,(png_structrp png_ptr, + png_int_32 x_offset, png_int_32 y_offset, int unit_type),PNG_EMPTY); +#endif + +#ifdef PNG_WRITE_pCAL_SUPPORTED +PNG_INTERNAL_FUNCTION(void,png_write_pCAL,(png_structrp png_ptr, + png_charp purpose, png_int_32 X0, png_int_32 X1, int type, int nparams, + png_const_charp units, png_charpp params),PNG_EMPTY); +#endif + +#ifdef PNG_WRITE_pHYs_SUPPORTED +PNG_INTERNAL_FUNCTION(void,png_write_pHYs,(png_structrp png_ptr, + png_uint_32 x_pixels_per_unit, png_uint_32 y_pixels_per_unit, + int unit_type),PNG_EMPTY); +#endif + +#ifdef PNG_WRITE_tIME_SUPPORTED +PNG_INTERNAL_FUNCTION(void,png_write_tIME,(png_structrp png_ptr, + png_const_timep mod_time),PNG_EMPTY); +#endif + +#ifdef PNG_WRITE_sCAL_SUPPORTED +PNG_INTERNAL_FUNCTION(void,png_write_sCAL_s,(png_structrp png_ptr, + int unit, png_const_charp width, png_const_charp height),PNG_EMPTY); +#endif + +/* Called when finished processing a row of data */ +PNG_INTERNAL_FUNCTION(void,png_write_finish_row,(png_structrp png_ptr), + PNG_EMPTY); + +/* Internal use only. Called before first row of data */ +PNG_INTERNAL_FUNCTION(void,png_write_start_row,(png_structrp png_ptr), + PNG_EMPTY); + +/* Combine a row of data, dealing with alpha, etc. if requested. 'row' is an + * array of png_ptr->width pixels. If the image is not interlaced or this + * is the final pass this just does a memcpy, otherwise the "display" flag + * is used to determine whether to copy pixels that are not in the current pass. + * + * Because 'png_do_read_interlace' (below) replicates pixels this allows this + * function to achieve the documented 'blocky' appearance during interlaced read + * if display is 1 and the 'sparkle' appearance, where existing pixels in 'row' + * are not changed if they are not in the current pass, when display is 0. + * + * 'display' must be 0 or 1, otherwise the memcpy will be done regardless. + * + * The API always reads from the png_struct row buffer and always assumes that + * it is full width (png_do_read_interlace has already been called.) + * + * This function is only ever used to write to row buffers provided by the + * caller of the relevant libpng API and the row must have already been + * transformed by the read transformations. + * + * The PNG_USE_COMPILE_TIME_MASKS option causes generation of pre-computed + * bitmasks for use within the code, otherwise runtime generated masks are used. + * The default is compile time masks. + */ +#ifndef PNG_USE_COMPILE_TIME_MASKS +# define PNG_USE_COMPILE_TIME_MASKS 1 +#endif +PNG_INTERNAL_FUNCTION(void,png_combine_row,(png_const_structrp png_ptr, + png_bytep row, int display),PNG_EMPTY); + +#ifdef PNG_READ_INTERLACING_SUPPORTED +/* Expand an interlaced row: the 'row_info' describes the pass data that has + * been read in and must correspond to the pixels in 'row', the pixels are + * expanded (moved apart) in 'row' to match the final layout, when doing this + * the pixels are *replicated* to the intervening space. This is essential for + * the correct operation of png_combine_row, above. + */ +PNG_INTERNAL_FUNCTION(void,png_do_read_interlace,(png_row_infop row_info, + png_bytep row, int pass, png_uint_32 transformations),PNG_EMPTY); +#endif + +/* GRR TO DO (2.0 or whenever): simplify other internal calling interfaces */ + +#ifdef PNG_WRITE_INTERLACING_SUPPORTED +/* Grab pixels out of a row for an interlaced pass */ +PNG_INTERNAL_FUNCTION(void,png_do_write_interlace,(png_row_infop row_info, + png_bytep row, int pass),PNG_EMPTY); +#endif + +/* Unfilter a row: check the filter value before calling this, there is no point + * calling it for PNG_FILTER_VALUE_NONE. + */ +PNG_INTERNAL_FUNCTION(void,png_read_filter_row,(png_structrp pp, png_row_infop + row_info, png_bytep row, png_const_bytep prev_row, int filter),PNG_EMPTY); + +PNG_INTERNAL_FUNCTION(void,png_read_filter_row_up_neon,(png_row_infop row_info, + png_bytep row, png_const_bytep prev_row),PNG_EMPTY); +PNG_INTERNAL_FUNCTION(void,png_read_filter_row_sub3_neon,(png_row_infop + row_info, png_bytep row, png_const_bytep prev_row),PNG_EMPTY); +PNG_INTERNAL_FUNCTION(void,png_read_filter_row_sub4_neon,(png_row_infop + row_info, png_bytep row, png_const_bytep prev_row),PNG_EMPTY); +PNG_INTERNAL_FUNCTION(void,png_read_filter_row_avg3_neon,(png_row_infop + row_info, png_bytep row, png_const_bytep prev_row),PNG_EMPTY); +PNG_INTERNAL_FUNCTION(void,png_read_filter_row_avg4_neon,(png_row_infop + row_info, png_bytep row, png_const_bytep prev_row),PNG_EMPTY); +PNG_INTERNAL_FUNCTION(void,png_read_filter_row_paeth3_neon,(png_row_infop + row_info, png_bytep row, png_const_bytep prev_row),PNG_EMPTY); +PNG_INTERNAL_FUNCTION(void,png_read_filter_row_paeth4_neon,(png_row_infop + row_info, png_bytep row, png_const_bytep prev_row),PNG_EMPTY); + +/* Choose the best filter to use and filter the row data */ +PNG_INTERNAL_FUNCTION(void,png_write_find_filter,(png_structrp png_ptr, + png_row_infop row_info),PNG_EMPTY); + +#ifdef PNG_SEQUENTIAL_READ_SUPPORTED +PNG_INTERNAL_FUNCTION(void,png_read_IDAT_data,(png_structrp png_ptr, + png_bytep output, png_alloc_size_t avail_out),PNG_EMPTY); + /* Read 'avail_out' bytes of data from the IDAT stream. If the output buffer + * is NULL the function checks, instead, for the end of the stream. In this + * case a benign error will be issued if the stream end is not found or if + * extra data has to be consumed. + */ +PNG_INTERNAL_FUNCTION(void,png_read_finish_IDAT,(png_structrp png_ptr), + PNG_EMPTY); + /* This cleans up when the IDAT LZ stream does not end when the last image + * byte is read; there is still some pending input. + */ + +PNG_INTERNAL_FUNCTION(void,png_read_finish_row,(png_structrp png_ptr), + PNG_EMPTY); + /* Finish a row while reading, dealing with interlacing passes, etc. */ +#endif + +/* Initialize the row buffers, etc. */ +PNG_INTERNAL_FUNCTION(void,png_read_start_row,(png_structrp png_ptr),PNG_EMPTY); + +#ifdef PNG_READ_TRANSFORMS_SUPPORTED +/* Optional call to update the users info structure */ +PNG_INTERNAL_FUNCTION(void,png_read_transform_info,(png_structrp png_ptr, + png_inforp info_ptr),PNG_EMPTY); +#endif + +/* These are the functions that do the transformations */ +#ifdef PNG_READ_FILLER_SUPPORTED +PNG_INTERNAL_FUNCTION(void,png_do_read_filler,(png_row_infop row_info, + png_bytep row, png_uint_32 filler, png_uint_32 flags),PNG_EMPTY); +#endif + +#ifdef PNG_READ_SWAP_ALPHA_SUPPORTED +PNG_INTERNAL_FUNCTION(void,png_do_read_swap_alpha,(png_row_infop row_info, + png_bytep row),PNG_EMPTY); +#endif + +#ifdef PNG_WRITE_SWAP_ALPHA_SUPPORTED +PNG_INTERNAL_FUNCTION(void,png_do_write_swap_alpha,(png_row_infop row_info, + png_bytep row),PNG_EMPTY); +#endif + +#ifdef PNG_READ_INVERT_ALPHA_SUPPORTED +PNG_INTERNAL_FUNCTION(void,png_do_read_invert_alpha,(png_row_infop row_info, + png_bytep row),PNG_EMPTY); +#endif + +#ifdef PNG_WRITE_INVERT_ALPHA_SUPPORTED +PNG_INTERNAL_FUNCTION(void,png_do_write_invert_alpha,(png_row_infop row_info, + png_bytep row),PNG_EMPTY); +#endif + +#if defined(PNG_WRITE_FILLER_SUPPORTED) || \ + defined(PNG_READ_STRIP_ALPHA_SUPPORTED) +PNG_INTERNAL_FUNCTION(void,png_do_strip_channel,(png_row_infop row_info, + png_bytep row, int at_start),PNG_EMPTY); +#endif + +#ifdef PNG_16BIT_SUPPORTED +#if defined(PNG_READ_SWAP_SUPPORTED) || defined(PNG_WRITE_SWAP_SUPPORTED) +PNG_INTERNAL_FUNCTION(void,png_do_swap,(png_row_infop row_info, + png_bytep row),PNG_EMPTY); +#endif +#endif + +#if defined(PNG_READ_PACKSWAP_SUPPORTED) || \ + defined(PNG_WRITE_PACKSWAP_SUPPORTED) +PNG_INTERNAL_FUNCTION(void,png_do_packswap,(png_row_infop row_info, + png_bytep row),PNG_EMPTY); +#endif + +#ifdef PNG_READ_RGB_TO_GRAY_SUPPORTED +PNG_INTERNAL_FUNCTION(int,png_do_rgb_to_gray,(png_structrp png_ptr, + png_row_infop row_info, png_bytep row),PNG_EMPTY); +#endif + +#ifdef PNG_READ_GRAY_TO_RGB_SUPPORTED +PNG_INTERNAL_FUNCTION(void,png_do_gray_to_rgb,(png_row_infop row_info, + png_bytep row),PNG_EMPTY); +#endif + +#ifdef PNG_READ_PACK_SUPPORTED +PNG_INTERNAL_FUNCTION(void,png_do_unpack,(png_row_infop row_info, + png_bytep row),PNG_EMPTY); +#endif + +#ifdef PNG_READ_SHIFT_SUPPORTED +PNG_INTERNAL_FUNCTION(void,png_do_unshift,(png_row_infop row_info, + png_bytep row, png_const_color_8p sig_bits),PNG_EMPTY); +#endif + +#if defined(PNG_READ_INVERT_SUPPORTED) || defined(PNG_WRITE_INVERT_SUPPORTED) +PNG_INTERNAL_FUNCTION(void,png_do_invert,(png_row_infop row_info, + png_bytep row),PNG_EMPTY); +#endif + +#ifdef PNG_READ_SCALE_16_TO_8_SUPPORTED +PNG_INTERNAL_FUNCTION(void,png_do_scale_16_to_8,(png_row_infop row_info, + png_bytep row),PNG_EMPTY); +#endif + +#ifdef PNG_READ_STRIP_16_TO_8_SUPPORTED +PNG_INTERNAL_FUNCTION(void,png_do_chop,(png_row_infop row_info, + png_bytep row),PNG_EMPTY); +#endif + +#ifdef PNG_READ_QUANTIZE_SUPPORTED +PNG_INTERNAL_FUNCTION(void,png_do_quantize,(png_row_infop row_info, + png_bytep row, png_const_bytep palette_lookup, + png_const_bytep quantize_lookup),PNG_EMPTY); + +# ifdef PNG_CORRECT_PALETTE_SUPPORTED +PNG_INTERNAL_FUNCTION(void,png_correct_palette,(png_structrp png_ptr, + png_colorp palette, int num_palette),PNG_EMPTY); +# endif +#endif + +#if defined(PNG_READ_BGR_SUPPORTED) || defined(PNG_WRITE_BGR_SUPPORTED) +PNG_INTERNAL_FUNCTION(void,png_do_bgr,(png_row_infop row_info, + png_bytep row),PNG_EMPTY); +#endif + +#ifdef PNG_WRITE_PACK_SUPPORTED +PNG_INTERNAL_FUNCTION(void,png_do_pack,(png_row_infop row_info, + png_bytep row, png_uint_32 bit_depth),PNG_EMPTY); +#endif + +#ifdef PNG_WRITE_SHIFT_SUPPORTED +PNG_INTERNAL_FUNCTION(void,png_do_shift,(png_row_infop row_info, + png_bytep row, png_const_color_8p bit_depth),PNG_EMPTY); +#endif + +#if defined(PNG_READ_BACKGROUND_SUPPORTED) ||\ + defined(PNG_READ_ALPHA_MODE_SUPPORTED) +PNG_INTERNAL_FUNCTION(void,png_do_compose,(png_row_infop row_info, + png_bytep row, png_structrp png_ptr),PNG_EMPTY); +#endif + +#ifdef PNG_READ_GAMMA_SUPPORTED +PNG_INTERNAL_FUNCTION(void,png_do_gamma,(png_row_infop row_info, + png_bytep row, png_structrp png_ptr),PNG_EMPTY); +#endif + +#ifdef PNG_READ_ALPHA_MODE_SUPPORTED +PNG_INTERNAL_FUNCTION(void,png_do_encode_alpha,(png_row_infop row_info, + png_bytep row, png_structrp png_ptr),PNG_EMPTY); +#endif + +#ifdef PNG_READ_EXPAND_SUPPORTED +PNG_INTERNAL_FUNCTION(void,png_do_expand_palette,(png_row_infop row_info, + png_bytep row, png_const_colorp palette, png_const_bytep trans, + int num_trans),PNG_EMPTY); +PNG_INTERNAL_FUNCTION(void,png_do_expand,(png_row_infop row_info, + png_bytep row, png_const_color_16p trans_color),PNG_EMPTY); +#endif + +#ifdef PNG_READ_EXPAND_16_SUPPORTED +PNG_INTERNAL_FUNCTION(void,png_do_expand_16,(png_row_infop row_info, + png_bytep row),PNG_EMPTY); +#endif + +/* The following decodes the appropriate chunks, and does error correction, + * then calls the appropriate callback for the chunk if it is valid. + */ + +/* Decode the IHDR chunk */ +PNG_INTERNAL_FUNCTION(void,png_handle_IHDR,(png_structrp png_ptr, + png_inforp info_ptr, png_uint_32 length),PNG_EMPTY); +PNG_INTERNAL_FUNCTION(void,png_handle_PLTE,(png_structrp png_ptr, + png_inforp info_ptr, png_uint_32 length),PNG_EMPTY); +PNG_INTERNAL_FUNCTION(void,png_handle_IEND,(png_structrp png_ptr, + png_inforp info_ptr, png_uint_32 length),PNG_EMPTY); + +#ifdef PNG_READ_bKGD_SUPPORTED +PNG_INTERNAL_FUNCTION(void,png_handle_bKGD,(png_structrp png_ptr, + png_inforp info_ptr, png_uint_32 length),PNG_EMPTY); +#endif + +#ifdef PNG_READ_cHRM_SUPPORTED +PNG_INTERNAL_FUNCTION(void,png_handle_cHRM,(png_structrp png_ptr, + png_inforp info_ptr, png_uint_32 length),PNG_EMPTY); +#endif + +#ifdef PNG_READ_gAMA_SUPPORTED +PNG_INTERNAL_FUNCTION(void,png_handle_gAMA,(png_structrp png_ptr, + png_inforp info_ptr, png_uint_32 length),PNG_EMPTY); +#endif + +#ifdef PNG_READ_hIST_SUPPORTED +PNG_INTERNAL_FUNCTION(void,png_handle_hIST,(png_structrp png_ptr, + png_inforp info_ptr, png_uint_32 length),PNG_EMPTY); +#endif + +#ifdef PNG_READ_iCCP_SUPPORTED +PNG_INTERNAL_FUNCTION(void,png_handle_iCCP,(png_structrp png_ptr, + png_inforp info_ptr, png_uint_32 length),PNG_EMPTY); +#endif /* PNG_READ_iCCP_SUPPORTED */ + +#ifdef PNG_READ_iTXt_SUPPORTED +PNG_INTERNAL_FUNCTION(void,png_handle_iTXt,(png_structrp png_ptr, + png_inforp info_ptr, png_uint_32 length),PNG_EMPTY); +#endif + +#ifdef PNG_READ_oFFs_SUPPORTED +PNG_INTERNAL_FUNCTION(void,png_handle_oFFs,(png_structrp png_ptr, + png_inforp info_ptr, png_uint_32 length),PNG_EMPTY); +#endif + +#ifdef PNG_READ_pCAL_SUPPORTED +PNG_INTERNAL_FUNCTION(void,png_handle_pCAL,(png_structrp png_ptr, + png_inforp info_ptr, png_uint_32 length),PNG_EMPTY); +#endif + +#ifdef PNG_READ_pHYs_SUPPORTED +PNG_INTERNAL_FUNCTION(void,png_handle_pHYs,(png_structrp png_ptr, + png_inforp info_ptr, png_uint_32 length),PNG_EMPTY); +#endif + +#ifdef PNG_READ_sBIT_SUPPORTED +PNG_INTERNAL_FUNCTION(void,png_handle_sBIT,(png_structrp png_ptr, + png_inforp info_ptr, png_uint_32 length),PNG_EMPTY); +#endif + +#ifdef PNG_READ_sCAL_SUPPORTED +PNG_INTERNAL_FUNCTION(void,png_handle_sCAL,(png_structrp png_ptr, + png_inforp info_ptr, png_uint_32 length),PNG_EMPTY); +#endif + +#ifdef PNG_READ_sPLT_SUPPORTED +PNG_INTERNAL_FUNCTION(void,png_handle_sPLT,(png_structrp png_ptr, + png_inforp info_ptr, png_uint_32 length),PNG_EMPTY); +#endif /* PNG_READ_sPLT_SUPPORTED */ + +#ifdef PNG_READ_sRGB_SUPPORTED +PNG_INTERNAL_FUNCTION(void,png_handle_sRGB,(png_structrp png_ptr, + png_inforp info_ptr, png_uint_32 length),PNG_EMPTY); +#endif + +#ifdef PNG_READ_tEXt_SUPPORTED +PNG_INTERNAL_FUNCTION(void,png_handle_tEXt,(png_structrp png_ptr, + png_inforp info_ptr, png_uint_32 length),PNG_EMPTY); +#endif + +#ifdef PNG_READ_tIME_SUPPORTED +PNG_INTERNAL_FUNCTION(void,png_handle_tIME,(png_structrp png_ptr, + png_inforp info_ptr, png_uint_32 length),PNG_EMPTY); +#endif + +#ifdef PNG_READ_tRNS_SUPPORTED +PNG_INTERNAL_FUNCTION(void,png_handle_tRNS,(png_structrp png_ptr, + png_inforp info_ptr, png_uint_32 length),PNG_EMPTY); +#endif + +#ifdef PNG_READ_zTXt_SUPPORTED +PNG_INTERNAL_FUNCTION(void,png_handle_zTXt,(png_structrp png_ptr, + png_inforp info_ptr, png_uint_32 length),PNG_EMPTY); +#endif + +PNG_INTERNAL_FUNCTION(void,png_check_chunk_name,(png_structrp png_ptr, + png_uint_32 chunk_name),PNG_EMPTY); + +#ifdef PNG_READ_SUPPORTED +PNG_INTERNAL_FUNCTION(void,png_handle_unknown,(png_structrp png_ptr, + png_inforp info_ptr, png_uint_32 length, int keep),PNG_EMPTY); + /* This is the function that gets called for unknown chunks. The 'keep' + * argument is either non-zero for a known chunk that has been set to be + * handled as unknown or zero for an unknown chunk. By default the function + * just skips the chunk or errors out if it is critical. + */ + +#ifdef PNG_READ_UNKNOWN_CHUNKS_SUPPORTED +#ifdef PNG_SET_UNKNOWN_CHUNKS_SUPPORTED +PNG_INTERNAL_FUNCTION(int,png_chunk_unknown_handling, + (png_const_structrp png_ptr, png_uint_32 chunk_name),PNG_EMPTY); + /* Exactly as the API png_handle_as_unknown() except that the argument is a + * 32-bit chunk name, not a string. + */ +#endif +#endif /* PNG_READ_UNKNOWN_CHUNKS_SUPPORTED */ +#endif /* PNG_READ_SUPPORTED */ + +/* Handle the transformations for reading and writing */ +#ifdef PNG_READ_TRANSFORMS_SUPPORTED +PNG_INTERNAL_FUNCTION(void,png_do_read_transformations,(png_structrp png_ptr, + png_row_infop row_info),PNG_EMPTY); +#endif +#ifdef PNG_WRITE_TRANSFORMS_SUPPORTED +PNG_INTERNAL_FUNCTION(void,png_do_write_transformations,(png_structrp png_ptr, + png_row_infop row_info),PNG_EMPTY); +#endif + +#ifdef PNG_READ_TRANSFORMS_SUPPORTED +PNG_INTERNAL_FUNCTION(void,png_init_read_transformations,(png_structrp png_ptr), + PNG_EMPTY); +#endif + +#ifdef PNG_PROGRESSIVE_READ_SUPPORTED +PNG_INTERNAL_FUNCTION(void,png_push_read_chunk,(png_structrp png_ptr, + png_inforp info_ptr),PNG_EMPTY); +PNG_INTERNAL_FUNCTION(void,png_push_read_sig,(png_structrp png_ptr, + png_inforp info_ptr),PNG_EMPTY); +PNG_INTERNAL_FUNCTION(void,png_push_check_crc,(png_structrp png_ptr),PNG_EMPTY); +PNG_INTERNAL_FUNCTION(void,png_push_crc_skip,(png_structrp png_ptr, + png_uint_32 length),PNG_EMPTY); +PNG_INTERNAL_FUNCTION(void,png_push_crc_finish,(png_structrp png_ptr), + PNG_EMPTY); +PNG_INTERNAL_FUNCTION(void,png_push_save_buffer,(png_structrp png_ptr), + PNG_EMPTY); +PNG_INTERNAL_FUNCTION(void,png_push_restore_buffer,(png_structrp png_ptr, + png_bytep buffer, png_size_t buffer_length),PNG_EMPTY); +PNG_INTERNAL_FUNCTION(void,png_push_read_IDAT,(png_structrp png_ptr),PNG_EMPTY); +PNG_INTERNAL_FUNCTION(void,png_process_IDAT_data,(png_structrp png_ptr, + png_bytep buffer, png_size_t buffer_length),PNG_EMPTY); +PNG_INTERNAL_FUNCTION(void,png_push_process_row,(png_structrp png_ptr), + PNG_EMPTY); +PNG_INTERNAL_FUNCTION(void,png_push_handle_unknown,(png_structrp png_ptr, + png_inforp info_ptr, png_uint_32 length),PNG_EMPTY); +PNG_INTERNAL_FUNCTION(void,png_push_have_info,(png_structrp png_ptr, + png_inforp info_ptr),PNG_EMPTY); +PNG_INTERNAL_FUNCTION(void,png_push_have_end,(png_structrp png_ptr, + png_inforp info_ptr),PNG_EMPTY); +PNG_INTERNAL_FUNCTION(void,png_push_have_row,(png_structrp png_ptr, + png_bytep row),PNG_EMPTY); +PNG_INTERNAL_FUNCTION(void,png_push_read_end,(png_structrp png_ptr, + png_inforp info_ptr),PNG_EMPTY); +PNG_INTERNAL_FUNCTION(void,png_process_some_data,(png_structrp png_ptr, + png_inforp info_ptr),PNG_EMPTY); +PNG_INTERNAL_FUNCTION(void,png_read_push_finish_row,(png_structrp png_ptr), + PNG_EMPTY); +# ifdef PNG_READ_tEXt_SUPPORTED +PNG_INTERNAL_FUNCTION(void,png_push_handle_tEXt,(png_structrp png_ptr, + png_inforp info_ptr, png_uint_32 length),PNG_EMPTY); +PNG_INTERNAL_FUNCTION(void,png_push_read_tEXt,(png_structrp png_ptr, + png_inforp info_ptr),PNG_EMPTY); +# endif +# ifdef PNG_READ_zTXt_SUPPORTED +PNG_INTERNAL_FUNCTION(void,png_push_handle_zTXt,(png_structrp png_ptr, + png_inforp info_ptr, png_uint_32 length),PNG_EMPTY); +PNG_INTERNAL_FUNCTION(void,png_push_read_zTXt,(png_structrp png_ptr, + png_inforp info_ptr),PNG_EMPTY); +# endif +# ifdef PNG_READ_iTXt_SUPPORTED +PNG_INTERNAL_FUNCTION(void,png_push_handle_iTXt,(png_structrp png_ptr, + png_inforp info_ptr, png_uint_32 length),PNG_EMPTY); +PNG_INTERNAL_FUNCTION(void,png_push_read_iTXt,(png_structrp png_ptr, + png_inforp info_ptr),PNG_EMPTY); +# endif + +#endif /* PNG_PROGRESSIVE_READ_SUPPORTED */ + +#ifdef PNG_MNG_FEATURES_SUPPORTED +PNG_INTERNAL_FUNCTION(void,png_do_read_intrapixel,(png_row_infop row_info, + png_bytep row),PNG_EMPTY); +PNG_INTERNAL_FUNCTION(void,png_do_write_intrapixel,(png_row_infop row_info, + png_bytep row),PNG_EMPTY); +#endif + +/* Added at libpng version 1.6.0 */ +#ifdef PNG_GAMMA_SUPPORTED +PNG_INTERNAL_FUNCTION(void,png_colorspace_set_gamma,(png_const_structrp png_ptr, + png_colorspacerp colorspace, png_fixed_point gAMA), PNG_EMPTY); + /* Set the colorspace gamma with a value provided by the application or by + * the gAMA chunk on read. The value will override anything set by an ICC + * profile. + */ + +PNG_INTERNAL_FUNCTION(void,png_colorspace_sync_info,(png_const_structrp png_ptr, + png_inforp info_ptr), PNG_EMPTY); + /* Synchronize the info 'valid' flags with the colorspace */ + +PNG_INTERNAL_FUNCTION(void,png_colorspace_sync,(png_const_structrp png_ptr, + png_inforp info_ptr), PNG_EMPTY); + /* Copy the png_struct colorspace to the info_struct and call the above to + * synchronize the flags. Checks for NULL info_ptr and does nothing. + */ +#endif + +/* Added at libpng version 1.4.0 */ +#ifdef PNG_COLORSPACE_SUPPORTED +/* These internal functions are for maintaining the colorspace structure within + * a png_info or png_struct (or, indeed, both). + */ +PNG_INTERNAL_FUNCTION(int,png_colorspace_set_chromaticities, + (png_const_structrp png_ptr, png_colorspacerp colorspace, const png_xy *xy, + int preferred), PNG_EMPTY); + +PNG_INTERNAL_FUNCTION(int,png_colorspace_set_endpoints, + (png_const_structrp png_ptr, png_colorspacerp colorspace, const png_XYZ *XYZ, + int preferred), PNG_EMPTY); + +#ifdef PNG_sRGB_SUPPORTED +PNG_INTERNAL_FUNCTION(int,png_colorspace_set_sRGB,(png_const_structrp png_ptr, + png_colorspacerp colorspace, int intent), PNG_EMPTY); + /* This does set the colorspace gAMA and cHRM values too, but doesn't set the + * flags to write them, if it returns false there was a problem and an error + * message has already been output (but the colorspace may still need to be + * synced to record the invalid flag). + */ +#endif /* sRGB */ + +#ifdef PNG_iCCP_SUPPORTED +PNG_INTERNAL_FUNCTION(int,png_colorspace_set_ICC,(png_const_structrp png_ptr, + png_colorspacerp colorspace, png_const_charp name, + png_uint_32 profile_length, png_const_bytep profile, int color_type), + PNG_EMPTY); + /* The 'name' is used for information only */ + +/* Routines for checking parts of an ICC profile. */ +PNG_INTERNAL_FUNCTION(int,png_icc_check_length,(png_const_structrp png_ptr, + png_colorspacerp colorspace, png_const_charp name, + png_uint_32 profile_length), PNG_EMPTY); +PNG_INTERNAL_FUNCTION(int,png_icc_check_header,(png_const_structrp png_ptr, + png_colorspacerp colorspace, png_const_charp name, + png_uint_32 profile_length, + png_const_bytep profile /* first 132 bytes only */, int color_type), + PNG_EMPTY); +PNG_INTERNAL_FUNCTION(int,png_icc_check_tag_table,(png_const_structrp png_ptr, + png_colorspacerp colorspace, png_const_charp name, + png_uint_32 profile_length, + png_const_bytep profile /* header plus whole tag table */), PNG_EMPTY); +#ifdef PNG_sRGB_SUPPORTED +PNG_INTERNAL_FUNCTION(void,png_icc_set_sRGB,( + png_const_structrp png_ptr, png_colorspacerp colorspace, + png_const_bytep profile, uLong adler), PNG_EMPTY); + /* 'adler' is the Adler32 checksum of the uncompressed profile data. It may + * be zero to indicate that it is not available. It is used, if provided, + * as a fast check on the profile when checking to see if it is sRGB. + */ +#endif +#endif /* iCCP */ + +#ifdef PNG_READ_RGB_TO_GRAY_SUPPORTED +PNG_INTERNAL_FUNCTION(void,png_colorspace_set_rgb_coefficients, + (png_structrp png_ptr), PNG_EMPTY); + /* Set the rgb_to_gray coefficients from the colorspace Y values */ +#endif /* READ_RGB_TO_GRAY */ +#endif /* COLORSPACE */ + +/* Added at libpng version 1.4.0 */ +PNG_INTERNAL_FUNCTION(void,png_check_IHDR,(png_const_structrp png_ptr, + png_uint_32 width, png_uint_32 height, int bit_depth, + int color_type, int interlace_type, int compression_type, + int filter_type),PNG_EMPTY); + +/* Added at libpng version 1.5.10 */ +#if defined(PNG_READ_CHECK_FOR_INVALID_INDEX_SUPPORTED) || \ + defined(PNG_WRITE_CHECK_FOR_INVALID_INDEX_SUPPORTED) +PNG_INTERNAL_FUNCTION(void,png_do_check_palette_indexes, + (png_structrp png_ptr, png_row_infop row_info),PNG_EMPTY); +#endif + +#if defined(PNG_FLOATING_POINT_SUPPORTED) && defined(PNG_ERROR_TEXT_SUPPORTED) +PNG_INTERNAL_FUNCTION(void,png_fixed_error,(png_const_structrp png_ptr, + png_const_charp name),PNG_NORETURN); +#endif + +/* Puts 'string' into 'buffer' at buffer[pos], taking care never to overwrite + * the end. Always leaves the buffer nul terminated. Never errors out (and + * there is no error code.) + */ +PNG_INTERNAL_FUNCTION(size_t,png_safecat,(png_charp buffer, size_t bufsize, + size_t pos, png_const_charp string),PNG_EMPTY); + +/* Various internal functions to handle formatted warning messages, currently + * only implemented for warnings. + */ +#if defined(PNG_WARNINGS_SUPPORTED) || defined(PNG_TIME_RFC1123_SUPPORTED) +/* Utility to dump an unsigned value into a buffer, given a start pointer and + * and end pointer (which should point just *beyond* the end of the buffer!) + * Returns the pointer to the start of the formatted string. This utility only + * does unsigned values. + */ +PNG_INTERNAL_FUNCTION(png_charp,png_format_number,(png_const_charp start, + png_charp end, int format, png_alloc_size_t number),PNG_EMPTY); + +/* Convenience macro that takes an array: */ +#define PNG_FORMAT_NUMBER(buffer,format,number) \ + png_format_number(buffer, buffer + (sizeof buffer), format, number) + +/* Suggested size for a number buffer (enough for 64 bits and a sign!) */ +#define PNG_NUMBER_BUFFER_SIZE 24 + +/* These are the integer formats currently supported, the name is formed from + * the standard printf(3) format string. + */ +#define PNG_NUMBER_FORMAT_u 1 /* chose unsigned API! */ +#define PNG_NUMBER_FORMAT_02u 2 +#define PNG_NUMBER_FORMAT_d 1 /* chose signed API! */ +#define PNG_NUMBER_FORMAT_02d 2 +#define PNG_NUMBER_FORMAT_x 3 +#define PNG_NUMBER_FORMAT_02x 4 +#define PNG_NUMBER_FORMAT_fixed 5 /* choose the signed API */ +#endif + +#ifdef PNG_WARNINGS_SUPPORTED +/* New defines and members adding in libpng-1.5.4 */ +# define PNG_WARNING_PARAMETER_SIZE 32 +# define PNG_WARNING_PARAMETER_COUNT 8 /* Maximum 9; see pngerror.c */ + +/* An l-value of this type has to be passed to the APIs below to cache the + * values of the parameters to a formatted warning message. + */ +typedef char png_warning_parameters[PNG_WARNING_PARAMETER_COUNT][ + PNG_WARNING_PARAMETER_SIZE]; + +PNG_INTERNAL_FUNCTION(void,png_warning_parameter,(png_warning_parameters p, + int number, png_const_charp string),PNG_EMPTY); + /* Parameters are limited in size to PNG_WARNING_PARAMETER_SIZE characters, + * including the trailing '\0'. + */ +PNG_INTERNAL_FUNCTION(void,png_warning_parameter_unsigned, + (png_warning_parameters p, int number, int format, png_alloc_size_t value), + PNG_EMPTY); + /* Use png_alloc_size_t because it is an unsigned type as big as any we + * need to output. Use the following for a signed value. + */ +PNG_INTERNAL_FUNCTION(void,png_warning_parameter_signed, + (png_warning_parameters p, int number, int format, png_int_32 value), + PNG_EMPTY); + +PNG_INTERNAL_FUNCTION(void,png_formatted_warning,(png_const_structrp png_ptr, + png_warning_parameters p, png_const_charp message),PNG_EMPTY); + /* 'message' follows the X/Open approach of using @1, @2 to insert + * parameters previously supplied using the above functions. Errors in + * specifying the parameters will simply result in garbage substitutions. + */ +#endif + +#ifdef PNG_BENIGN_ERRORS_SUPPORTED +/* Application errors (new in 1.6); use these functions (declared below) for + * errors in the parameters or order of API function calls on read. The + * 'warning' should be used for an error that can be handled completely; the + * 'error' for one which can be handled safely but which may lose application + * information or settings. + * + * By default these both result in a png_error call prior to release, while in a + * released version the 'warning' is just a warning. However if the application + * explicitly disables benign errors (explicitly permitting the code to lose + * information) they both turn into warnings. + * + * If benign errors aren't supported they end up as the corresponding base call + * (png_warning or png_error.) + */ +PNG_INTERNAL_FUNCTION(void,png_app_warning,(png_const_structrp png_ptr, + png_const_charp message),PNG_EMPTY); + /* The application provided invalid parameters to an API function or called + * an API function at the wrong time, libpng can completely recover. + */ + +PNG_INTERNAL_FUNCTION(void,png_app_error,(png_const_structrp png_ptr, + png_const_charp message),PNG_EMPTY); + /* As above but libpng will ignore the call, or attempt some other partial + * recovery from the error. + */ +#else +# define png_app_warning(pp,s) png_warning(pp,s) +# define png_app_error(pp,s) png_error(pp,s) +#endif + +PNG_INTERNAL_FUNCTION(void,png_chunk_report,(png_const_structrp png_ptr, + png_const_charp message, int error),PNG_EMPTY); + /* Report a recoverable issue in chunk data. On read this is used to report + * a problem found while reading a particular chunk and the + * png_chunk_benign_error or png_chunk_warning function is used as + * appropriate. On write this is used to report an error that comes from + * data set via an application call to a png_set_ API and png_app_error or + * png_app_warning is used as appropriate. + * + * The 'error' parameter must have one of the following values: + */ +#define PNG_CHUNK_WARNING 0 /* never an error */ +#define PNG_CHUNK_WRITE_ERROR 1 /* an error only on write */ +#define PNG_CHUNK_ERROR 2 /* always an error */ + +/* ASCII to FP interfaces, currently only implemented if sCAL + * support is required. + */ +#if defined(PNG_sCAL_SUPPORTED) +/* MAX_DIGITS is actually the maximum number of characters in an sCAL + * width or height, derived from the precision (number of significant + * digits - a build time settable option) and assumptions about the + * maximum ridiculous exponent. + */ +#define PNG_sCAL_MAX_DIGITS (PNG_sCAL_PRECISION+1/*.*/+1/*E*/+10/*exponent*/) + +#ifdef PNG_FLOATING_POINT_SUPPORTED +PNG_INTERNAL_FUNCTION(void,png_ascii_from_fp,(png_const_structrp png_ptr, + png_charp ascii, png_size_t size, double fp, unsigned int precision), + PNG_EMPTY); +#endif /* FLOATING_POINT */ + +#ifdef PNG_FIXED_POINT_SUPPORTED +PNG_INTERNAL_FUNCTION(void,png_ascii_from_fixed,(png_const_structrp png_ptr, + png_charp ascii, png_size_t size, png_fixed_point fp),PNG_EMPTY); +#endif /* FIXED_POINT */ +#endif /* sCAL */ + +#if defined(PNG_sCAL_SUPPORTED) || defined(PNG_pCAL_SUPPORTED) +/* An internal API to validate the format of a floating point number. + * The result is the index of the next character. If the number is + * not valid it will be the index of a character in the supposed number. + * + * The format of a number is defined in the PNG extensions specification + * and this API is strictly conformant to that spec, not anyone elses! + * + * The format as a regular expression is: + * + * [+-]?[0-9]+.?([Ee][+-]?[0-9]+)? + * + * or: + * + * [+-]?.[0-9]+(.[0-9]+)?([Ee][+-]?[0-9]+)? + * + * The complexity is that either integer or fraction must be present and the + * fraction is permitted to have no digits only if the integer is present. + * + * NOTE: The dangling E problem. + * There is a PNG valid floating point number in the following: + * + * PNG floating point numbers are not greedy. + * + * Working this out requires *TWO* character lookahead (because of the + * sign), the parser does not do this - it will fail at the 'r' - this + * doesn't matter for PNG sCAL chunk values, but it requires more care + * if the value were ever to be embedded in something more complex. Use + * ANSI-C strtod if you need the lookahead. + */ +/* State table for the parser. */ +#define PNG_FP_INTEGER 0 /* before or in integer */ +#define PNG_FP_FRACTION 1 /* before or in fraction */ +#define PNG_FP_EXPONENT 2 /* before or in exponent */ +#define PNG_FP_STATE 3 /* mask for the above */ +#define PNG_FP_SAW_SIGN 4 /* Saw +/- in current state */ +#define PNG_FP_SAW_DIGIT 8 /* Saw a digit in current state */ +#define PNG_FP_SAW_DOT 16 /* Saw a dot in current state */ +#define PNG_FP_SAW_E 32 /* Saw an E (or e) in current state */ +#define PNG_FP_SAW_ANY 60 /* Saw any of the above 4 */ + +/* These three values don't affect the parser. They are set but not used. + */ +#define PNG_FP_WAS_VALID 64 /* Preceding substring is a valid fp number */ +#define PNG_FP_NEGATIVE 128 /* A negative number, including "-0" */ +#define PNG_FP_NONZERO 256 /* A non-zero value */ +#define PNG_FP_STICKY 448 /* The above three flags */ + +/* This is available for the caller to store in 'state' if required. Do not + * call the parser after setting it (the parser sometimes clears it.) + */ +#define PNG_FP_INVALID 512 /* Available for callers as a distinct value */ + +/* Result codes for the parser (boolean - true meants ok, false means + * not ok yet.) + */ +#define PNG_FP_MAYBE 0 /* The number may be valid in the future */ +#define PNG_FP_OK 1 /* The number is valid */ + +/* Tests on the sticky non-zero and negative flags. To pass these checks + * the state must also indicate that the whole number is valid - this is + * achieved by testing PNG_FP_SAW_DIGIT (see the implementation for why this + * is equivalent to PNG_FP_OK above.) + */ +#define PNG_FP_NZ_MASK (PNG_FP_SAW_DIGIT | PNG_FP_NEGATIVE | PNG_FP_NONZERO) + /* NZ_MASK: the string is valid and a non-zero negative value */ +#define PNG_FP_Z_MASK (PNG_FP_SAW_DIGIT | PNG_FP_NONZERO) + /* Z MASK: the string is valid and a non-zero value. */ + /* PNG_FP_SAW_DIGIT: the string is valid. */ +#define PNG_FP_IS_ZERO(state) (((state) & PNG_FP_Z_MASK) == PNG_FP_SAW_DIGIT) +#define PNG_FP_IS_POSITIVE(state) (((state) & PNG_FP_NZ_MASK) == PNG_FP_Z_MASK) +#define PNG_FP_IS_NEGATIVE(state) (((state) & PNG_FP_NZ_MASK) == PNG_FP_NZ_MASK) + +/* The actual parser. This can be called repeatedly. It updates + * the index into the string and the state variable (which must + * be initialized to 0). It returns a result code, as above. There + * is no point calling the parser any more if it fails to advance to + * the end of the string - it is stuck on an invalid character (or + * terminated by '\0'). + * + * Note that the pointer will consume an E or even an E+ and then leave + * a 'maybe' state even though a preceding integer.fraction is valid. + * The PNG_FP_WAS_VALID flag indicates that a preceding substring was + * a valid number. It's possible to recover from this by calling + * the parser again (from the start, with state 0) but with a string + * that omits the last character (i.e. set the size to the index of + * the problem character.) This has not been tested within libpng. + */ +PNG_INTERNAL_FUNCTION(int,png_check_fp_number,(png_const_charp string, + png_size_t size, int *statep, png_size_tp whereami),PNG_EMPTY); + +/* This is the same but it checks a complete string and returns true + * only if it just contains a floating point number. As of 1.5.4 this + * function also returns the state at the end of parsing the number if + * it was valid (otherwise it returns 0.) This can be used for testing + * for negative or zero values using the sticky flag. + */ +PNG_INTERNAL_FUNCTION(int,png_check_fp_string,(png_const_charp string, + png_size_t size),PNG_EMPTY); +#endif /* pCAL || sCAL */ + +#if defined(PNG_READ_GAMMA_SUPPORTED) ||\ + defined(PNG_INCH_CONVERSIONS_SUPPORTED) || defined(PNG_READ_pHYs_SUPPORTED) +/* Added at libpng version 1.5.0 */ +/* This is a utility to provide a*times/div (rounded) and indicate + * if there is an overflow. The result is a boolean - false (0) + * for overflow, true (1) if no overflow, in which case *res + * holds the result. + */ +PNG_INTERNAL_FUNCTION(int,png_muldiv,(png_fixed_point_p res, png_fixed_point a, + png_int_32 multiplied_by, png_int_32 divided_by),PNG_EMPTY); +#endif + +#if defined(PNG_READ_GAMMA_SUPPORTED) || defined(PNG_INCH_CONVERSIONS_SUPPORTED) +/* Same deal, but issue a warning on overflow and return 0. */ +PNG_INTERNAL_FUNCTION(png_fixed_point,png_muldiv_warn, + (png_const_structrp png_ptr, png_fixed_point a, png_int_32 multiplied_by, + png_int_32 divided_by),PNG_EMPTY); +#endif + +#ifdef PNG_GAMMA_SUPPORTED +/* Calculate a reciprocal - used for gamma values. This returns + * 0 if the argument is 0 in order to maintain an undefined value; + * there are no warnings. + */ +PNG_INTERNAL_FUNCTION(png_fixed_point,png_reciprocal,(png_fixed_point a), + PNG_EMPTY); + +#ifdef PNG_READ_GAMMA_SUPPORTED +/* The same but gives a reciprocal of the product of two fixed point + * values. Accuracy is suitable for gamma calculations but this is + * not exact - use png_muldiv for that. Only required at present on read. + */ +PNG_INTERNAL_FUNCTION(png_fixed_point,png_reciprocal2,(png_fixed_point a, + png_fixed_point b),PNG_EMPTY); +#endif + +/* Return true if the gamma value is significantly different from 1.0 */ +PNG_INTERNAL_FUNCTION(int,png_gamma_significant,(png_fixed_point gamma_value), + PNG_EMPTY); +#endif + +#ifdef PNG_READ_GAMMA_SUPPORTED +/* Internal fixed point gamma correction. These APIs are called as + * required to convert single values - they don't need to be fast, + * they are not used when processing image pixel values. + * + * While the input is an 'unsigned' value it must actually be the + * correct bit value - 0..255 or 0..65535 as required. + */ +PNG_INTERNAL_FUNCTION(png_uint_16,png_gamma_correct,(png_structrp png_ptr, + unsigned int value, png_fixed_point gamma_value),PNG_EMPTY); +PNG_INTERNAL_FUNCTION(png_uint_16,png_gamma_16bit_correct,(unsigned int value, + png_fixed_point gamma_value),PNG_EMPTY); +PNG_INTERNAL_FUNCTION(png_byte,png_gamma_8bit_correct,(unsigned int value, + png_fixed_point gamma_value),PNG_EMPTY); +PNG_INTERNAL_FUNCTION(void,png_destroy_gamma_table,(png_structrp png_ptr), + PNG_EMPTY); +PNG_INTERNAL_FUNCTION(void,png_build_gamma_table,(png_structrp png_ptr, + int bit_depth),PNG_EMPTY); +#endif + +/* SIMPLIFIED READ/WRITE SUPPORT */ +#if defined(PNG_SIMPLIFIED_READ_SUPPORTED) ||\ + defined(PNG_SIMPLIFIED_WRITE_SUPPORTED) +/* The internal structure that png_image::opaque points to. */ +typedef struct png_control +{ + png_structp png_ptr; + png_infop info_ptr; + png_voidp error_buf; /* Always a jmp_buf at present. */ + + png_const_bytep memory; /* Memory buffer. */ + png_size_t size; /* Size of the memory buffer. */ + + unsigned int for_write :1; /* Otherwise it is a read structure */ + unsigned int owned_file :1; /* We own the file in io_ptr */ +} png_control; + +/* Return the pointer to the jmp_buf from a png_control: necessary because C + * does not reveal the type of the elements of jmp_buf. + */ +#ifdef __cplusplus +# define png_control_jmp_buf(pc) (((jmp_buf*)((pc)->error_buf))[0]) +#else +# define png_control_jmp_buf(pc) ((pc)->error_buf) +#endif + +/* Utility to safely execute a piece of libpng code catching and logging any + * errors that might occur. Returns true on success, false on failure (either + * of the function or as a result of a png_error.) + */ +PNG_INTERNAL_FUNCTION(void,png_safe_error,(png_structp png_ptr, + png_const_charp error_message),PNG_NORETURN); + +#ifdef PNG_WARNINGS_SUPPORTED +PNG_INTERNAL_FUNCTION(void,png_safe_warning,(png_structp png_ptr, + png_const_charp warning_message),PNG_EMPTY); +#else +# define png_safe_warning 0/*dummy argument*/ +#endif + +PNG_INTERNAL_FUNCTION(int,png_safe_execute,(png_imagep image, + int (*function)(png_voidp), png_voidp arg),PNG_EMPTY); + +/* Utility to log an error; this also cleans up the png_image; the function + * always returns 0 (false). + */ +PNG_INTERNAL_FUNCTION(int,png_image_error,(png_imagep image, + png_const_charp error_message),PNG_EMPTY); + +#ifndef PNG_SIMPLIFIED_READ_SUPPORTED +/* png_image_free is used by the write code but not exported */ +PNG_INTERNAL_FUNCTION(void, png_image_free, (png_imagep image), PNG_EMPTY); +#endif /* !SIMPLIFIED_READ */ + +#endif /* SIMPLIFIED READ/WRITE */ + +#ifdef PNG_FILTER_OPTIMIZATIONS +PNG_INTERNAL_FUNCTION(void, PNG_FILTER_OPTIMIZATIONS, (png_structp png_ptr, + unsigned int bpp), PNG_EMPTY); + /* This is the initialization function for hardware specific optimizations, + * one implementation (for ARM NEON machines) is contained in + * arm/filter_neon.c. It need not be defined - the generic code will be used + * if not. + */ +#endif + +/* Maintainer: Put new private prototypes here ^ */ + +#include "pngdebug.h" + +#ifdef __cplusplus +} +#endif + +#endif /* PNG_VERSION_INFO_ONLY */ +#endif /* PNGPRIV_H */ diff --git a/Minecraft.Client/Windows64/4JLibs/inc/Render/libpng/pngread.c b/Minecraft.Client/Windows64/4JLibs/inc/Render/libpng/pngread.c new file mode 100644 index 000000000..0a00439a6 --- /dev/null +++ b/Minecraft.Client/Windows64/4JLibs/inc/Render/libpng/pngread.c @@ -0,0 +1,4001 @@ + +/* pngread.c - read a PNG file + * + * Last changed in libpng 1.6.1 [March 28, 2013] + * Copyright (c) 1998-2013 Glenn Randers-Pehrson + * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) + * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) + * + * This code is released under the libpng license. + * For conditions of distribution and use, see the disclaimer + * and license in png.h + * + * This file contains routines that an application calls directly to + * read a PNG file or stream. + */ + +#include "pngpriv.h" +#if defined(PNG_SIMPLIFIED_READ_SUPPORTED) && defined(PNG_STDIO_SUPPORTED) +# include +#endif + +#ifdef PNG_READ_SUPPORTED + +/* Create a PNG structure for reading, and allocate any memory needed. */ +PNG_FUNCTION(png_structp,PNGAPI +png_create_read_struct,(png_const_charp user_png_ver, png_voidp error_ptr, + png_error_ptr error_fn, png_error_ptr warn_fn),PNG_ALLOCATED) +{ +#ifndef PNG_USER_MEM_SUPPORTED + png_structp png_ptr = png_create_png_struct(user_png_ver, error_ptr, + error_fn, warn_fn, NULL, NULL, NULL); +#else + return png_create_read_struct_2(user_png_ver, error_ptr, error_fn, + warn_fn, NULL, NULL, NULL); +} + +/* Alternate create PNG structure for reading, and allocate any memory + * needed. + */ +PNG_FUNCTION(png_structp,PNGAPI +png_create_read_struct_2,(png_const_charp user_png_ver, png_voidp error_ptr, + png_error_ptr error_fn, png_error_ptr warn_fn, png_voidp mem_ptr, + png_malloc_ptr malloc_fn, png_free_ptr free_fn),PNG_ALLOCATED) +{ + png_structp png_ptr = png_create_png_struct(user_png_ver, error_ptr, + error_fn, warn_fn, mem_ptr, malloc_fn, free_fn); +#endif /* PNG_USER_MEM_SUPPORTED */ + + if (png_ptr != NULL) + { + png_ptr->mode = PNG_IS_READ_STRUCT; + + /* Added in libpng-1.6.0; this can be used to detect a read structure if + * required (it will be zero in a write structure.) + */ +# ifdef PNG_SEQUENTIAL_READ_SUPPORTED + png_ptr->IDAT_read_size = PNG_IDAT_READ_SIZE; +# endif + +# ifdef PNG_BENIGN_READ_ERRORS_SUPPORTED + png_ptr->flags |= PNG_FLAG_BENIGN_ERRORS_WARN; + + /* In stable builds only warn if an application error can be completely + * handled. + */ +# if PNG_LIBPNG_BUILD_BASE_TYPE >= PNG_LIBPNG_BUILD_RC + png_ptr->flags |= PNG_FLAG_APP_WARNINGS_WARN; +# endif +# endif + + /* TODO: delay this, it can be done in png_init_io (if the app doesn't + * do it itself) avoiding setting the default function if it is not + * required. + */ + png_set_read_fn(png_ptr, NULL, NULL); + } + + return png_ptr; +} + + +#ifdef PNG_SEQUENTIAL_READ_SUPPORTED +/* Read the information before the actual image data. This has been + * changed in v0.90 to allow reading a file that already has the magic + * bytes read from the stream. You can tell libpng how many bytes have + * been read from the beginning of the stream (up to the maximum of 8) + * via png_set_sig_bytes(), and we will only check the remaining bytes + * here. The application can then have access to the signature bytes we + * read if it is determined that this isn't a valid PNG file. + */ +void PNGAPI +png_read_info(png_structrp png_ptr, png_inforp info_ptr) +{ +#ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED + int keep; +#endif + + png_debug(1, "in png_read_info"); + + if (png_ptr == NULL || info_ptr == NULL) + return; + + /* Read and check the PNG file signature. */ + png_read_sig(png_ptr, info_ptr); + + for (;;) + { + png_uint_32 length = png_read_chunk_header(png_ptr); + png_uint_32 chunk_name = png_ptr->chunk_name; + + /* IDAT logic needs to happen here to simplify getting the two flags + * right. + */ + if (chunk_name == png_IDAT) + { + if (!(png_ptr->mode & PNG_HAVE_IHDR)) + png_chunk_error(png_ptr, "Missing IHDR before IDAT"); + + else if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE && + !(png_ptr->mode & PNG_HAVE_PLTE)) + png_chunk_error(png_ptr, "Missing PLTE before IDAT"); + + else if (png_ptr->mode & PNG_AFTER_IDAT) + png_chunk_benign_error(png_ptr, "Too many IDATs found"); + + png_ptr->mode |= PNG_HAVE_IDAT; + } + + else if (png_ptr->mode & PNG_HAVE_IDAT) + png_ptr->mode |= PNG_AFTER_IDAT; + + /* This should be a binary subdivision search or a hash for + * matching the chunk name rather than a linear search. + */ + if (chunk_name == png_IHDR) + png_handle_IHDR(png_ptr, info_ptr, length); + + else if (chunk_name == png_IEND) + png_handle_IEND(png_ptr, info_ptr, length); + +#ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED + else if ((keep = png_chunk_unknown_handling(png_ptr, chunk_name)) != 0) + { + png_handle_unknown(png_ptr, info_ptr, length, keep); + + if (chunk_name == png_PLTE) + png_ptr->mode |= PNG_HAVE_PLTE; + + else if (chunk_name == png_IDAT) + { + png_ptr->idat_size = 0; /* It has been consumed */ + break; + } + } +#endif + else if (chunk_name == png_PLTE) + png_handle_PLTE(png_ptr, info_ptr, length); + + else if (chunk_name == png_IDAT) + { + png_ptr->idat_size = length; + break; + } + +#ifdef PNG_READ_bKGD_SUPPORTED + else if (chunk_name == png_bKGD) + png_handle_bKGD(png_ptr, info_ptr, length); +#endif + +#ifdef PNG_READ_cHRM_SUPPORTED + else if (chunk_name == png_cHRM) + png_handle_cHRM(png_ptr, info_ptr, length); +#endif + +#ifdef PNG_READ_gAMA_SUPPORTED + else if (chunk_name == png_gAMA) + png_handle_gAMA(png_ptr, info_ptr, length); +#endif + +#ifdef PNG_READ_hIST_SUPPORTED + else if (chunk_name == png_hIST) + png_handle_hIST(png_ptr, info_ptr, length); +#endif + +#ifdef PNG_READ_oFFs_SUPPORTED + else if (chunk_name == png_oFFs) + png_handle_oFFs(png_ptr, info_ptr, length); +#endif + +#ifdef PNG_READ_pCAL_SUPPORTED + else if (chunk_name == png_pCAL) + png_handle_pCAL(png_ptr, info_ptr, length); +#endif + +#ifdef PNG_READ_sCAL_SUPPORTED + else if (chunk_name == png_sCAL) + png_handle_sCAL(png_ptr, info_ptr, length); +#endif + +#ifdef PNG_READ_pHYs_SUPPORTED + else if (chunk_name == png_pHYs) + png_handle_pHYs(png_ptr, info_ptr, length); +#endif + +#ifdef PNG_READ_sBIT_SUPPORTED + else if (chunk_name == png_sBIT) + png_handle_sBIT(png_ptr, info_ptr, length); +#endif + +#ifdef PNG_READ_sRGB_SUPPORTED + else if (chunk_name == png_sRGB) + png_handle_sRGB(png_ptr, info_ptr, length); +#endif + +#ifdef PNG_READ_iCCP_SUPPORTED + else if (chunk_name == png_iCCP) + png_handle_iCCP(png_ptr, info_ptr, length); +#endif + +#ifdef PNG_READ_sPLT_SUPPORTED + else if (chunk_name == png_sPLT) + png_handle_sPLT(png_ptr, info_ptr, length); +#endif + +#ifdef PNG_READ_tEXt_SUPPORTED + else if (chunk_name == png_tEXt) + png_handle_tEXt(png_ptr, info_ptr, length); +#endif + +#ifdef PNG_READ_tIME_SUPPORTED + else if (chunk_name == png_tIME) + png_handle_tIME(png_ptr, info_ptr, length); +#endif + +#ifdef PNG_READ_tRNS_SUPPORTED + else if (chunk_name == png_tRNS) + png_handle_tRNS(png_ptr, info_ptr, length); +#endif + +#ifdef PNG_READ_zTXt_SUPPORTED + else if (chunk_name == png_zTXt) + png_handle_zTXt(png_ptr, info_ptr, length); +#endif + +#ifdef PNG_READ_iTXt_SUPPORTED + else if (chunk_name == png_iTXt) + png_handle_iTXt(png_ptr, info_ptr, length); +#endif + + else + png_handle_unknown(png_ptr, info_ptr, length, + PNG_HANDLE_CHUNK_AS_DEFAULT); + } +} +#endif /* PNG_SEQUENTIAL_READ_SUPPORTED */ + +/* Optional call to update the users info_ptr structure */ +void PNGAPI +png_read_update_info(png_structrp png_ptr, png_inforp info_ptr) +{ + png_debug(1, "in png_read_update_info"); + + if (png_ptr != NULL) + { + if ((png_ptr->flags & PNG_FLAG_ROW_INIT) == 0) + { + png_read_start_row(png_ptr); + +# ifdef PNG_READ_TRANSFORMS_SUPPORTED + png_read_transform_info(png_ptr, info_ptr); +# else + PNG_UNUSED(info_ptr) +# endif + } + + /* New in 1.6.0 this avoids the bug of doing the initializations twice */ + else + png_app_error(png_ptr, + "png_read_update_info/png_start_read_image: duplicate call"); + } +} + +#ifdef PNG_SEQUENTIAL_READ_SUPPORTED +/* Initialize palette, background, etc, after transformations + * are set, but before any reading takes place. This allows + * the user to obtain a gamma-corrected palette, for example. + * If the user doesn't call this, we will do it ourselves. + */ +void PNGAPI +png_start_read_image(png_structrp png_ptr) +{ + png_debug(1, "in png_start_read_image"); + + if (png_ptr != NULL) + { + if ((png_ptr->flags & PNG_FLAG_ROW_INIT) == 0) + png_read_start_row(png_ptr); + + /* New in 1.6.0 this avoids the bug of doing the initializations twice */ + else + png_app_error(png_ptr, + "png_start_read_image/png_read_update_info: duplicate call"); + } +} +#endif /* PNG_SEQUENTIAL_READ_SUPPORTED */ + +#ifdef PNG_SEQUENTIAL_READ_SUPPORTED +void PNGAPI +png_read_row(png_structrp png_ptr, png_bytep row, png_bytep dsp_row) +{ + png_row_info row_info; + + if (png_ptr == NULL) + return; + + png_debug2(1, "in png_read_row (row %lu, pass %d)", + (unsigned long)png_ptr->row_number, png_ptr->pass); + + /* png_read_start_row sets the information (in particular iwidth) for this + * interlace pass. + */ + if (!(png_ptr->flags & PNG_FLAG_ROW_INIT)) + png_read_start_row(png_ptr); + + /* 1.5.6: row_info moved out of png_struct to a local here. */ + row_info.width = png_ptr->iwidth; /* NOTE: width of current interlaced row */ + row_info.color_type = png_ptr->color_type; + row_info.bit_depth = png_ptr->bit_depth; + row_info.channels = png_ptr->channels; + row_info.pixel_depth = png_ptr->pixel_depth; + row_info.rowbytes = PNG_ROWBYTES(row_info.pixel_depth, row_info.width); + + if (png_ptr->row_number == 0 && png_ptr->pass == 0) + { + /* Check for transforms that have been set but were defined out */ +#if defined(PNG_WRITE_INVERT_SUPPORTED) && !defined(PNG_READ_INVERT_SUPPORTED) + if (png_ptr->transformations & PNG_INVERT_MONO) + png_warning(png_ptr, "PNG_READ_INVERT_SUPPORTED is not defined"); +#endif + +#if defined(PNG_WRITE_FILLER_SUPPORTED) && !defined(PNG_READ_FILLER_SUPPORTED) + if (png_ptr->transformations & PNG_FILLER) + png_warning(png_ptr, "PNG_READ_FILLER_SUPPORTED is not defined"); +#endif + +#if defined(PNG_WRITE_PACKSWAP_SUPPORTED) && \ + !defined(PNG_READ_PACKSWAP_SUPPORTED) + if (png_ptr->transformations & PNG_PACKSWAP) + png_warning(png_ptr, "PNG_READ_PACKSWAP_SUPPORTED is not defined"); +#endif + +#if defined(PNG_WRITE_PACK_SUPPORTED) && !defined(PNG_READ_PACK_SUPPORTED) + if (png_ptr->transformations & PNG_PACK) + png_warning(png_ptr, "PNG_READ_PACK_SUPPORTED is not defined"); +#endif + +#if defined(PNG_WRITE_SHIFT_SUPPORTED) && !defined(PNG_READ_SHIFT_SUPPORTED) + if (png_ptr->transformations & PNG_SHIFT) + png_warning(png_ptr, "PNG_READ_SHIFT_SUPPORTED is not defined"); +#endif + +#if defined(PNG_WRITE_BGR_SUPPORTED) && !defined(PNG_READ_BGR_SUPPORTED) + if (png_ptr->transformations & PNG_BGR) + png_warning(png_ptr, "PNG_READ_BGR_SUPPORTED is not defined"); +#endif + +#if defined(PNG_WRITE_SWAP_SUPPORTED) && !defined(PNG_READ_SWAP_SUPPORTED) + if (png_ptr->transformations & PNG_SWAP_BYTES) + png_warning(png_ptr, "PNG_READ_SWAP_SUPPORTED is not defined"); +#endif + } + +#ifdef PNG_READ_INTERLACING_SUPPORTED + /* If interlaced and we do not need a new row, combine row and return. + * Notice that the pixels we have from previous rows have been transformed + * already; we can only combine like with like (transformed or + * untransformed) and, because of the libpng API for interlaced images, this + * means we must transform before de-interlacing. + */ + if (png_ptr->interlaced && (png_ptr->transformations & PNG_INTERLACE)) + { + switch (png_ptr->pass) + { + case 0: + if (png_ptr->row_number & 0x07) + { + if (dsp_row != NULL) + png_combine_row(png_ptr, dsp_row, 1/*display*/); + png_read_finish_row(png_ptr); + return; + } + break; + + case 1: + if ((png_ptr->row_number & 0x07) || png_ptr->width < 5) + { + if (dsp_row != NULL) + png_combine_row(png_ptr, dsp_row, 1/*display*/); + + png_read_finish_row(png_ptr); + return; + } + break; + + case 2: + if ((png_ptr->row_number & 0x07) != 4) + { + if (dsp_row != NULL && (png_ptr->row_number & 4)) + png_combine_row(png_ptr, dsp_row, 1/*display*/); + + png_read_finish_row(png_ptr); + return; + } + break; + + case 3: + if ((png_ptr->row_number & 3) || png_ptr->width < 3) + { + if (dsp_row != NULL) + png_combine_row(png_ptr, dsp_row, 1/*display*/); + + png_read_finish_row(png_ptr); + return; + } + break; + + case 4: + if ((png_ptr->row_number & 3) != 2) + { + if (dsp_row != NULL && (png_ptr->row_number & 2)) + png_combine_row(png_ptr, dsp_row, 1/*display*/); + + png_read_finish_row(png_ptr); + return; + } + break; + + case 5: + if ((png_ptr->row_number & 1) || png_ptr->width < 2) + { + if (dsp_row != NULL) + png_combine_row(png_ptr, dsp_row, 1/*display*/); + + png_read_finish_row(png_ptr); + return; + } + break; + + default: + case 6: + if (!(png_ptr->row_number & 1)) + { + png_read_finish_row(png_ptr); + return; + } + break; + } + } +#endif + + if (!(png_ptr->mode & PNG_HAVE_IDAT)) + png_error(png_ptr, "Invalid attempt to read row data"); + + /* Fill the row with IDAT data: */ + png_read_IDAT_data(png_ptr, png_ptr->row_buf, row_info.rowbytes + 1); + + if (png_ptr->row_buf[0] > PNG_FILTER_VALUE_NONE) + { + if (png_ptr->row_buf[0] < PNG_FILTER_VALUE_LAST) + png_read_filter_row(png_ptr, &row_info, png_ptr->row_buf + 1, + png_ptr->prev_row + 1, png_ptr->row_buf[0]); + else + png_error(png_ptr, "bad adaptive filter value"); + } + + /* libpng 1.5.6: the following line was copying png_ptr->rowbytes before + * 1.5.6, while the buffer really is this big in current versions of libpng + * it may not be in the future, so this was changed just to copy the + * interlaced count: + */ + memcpy(png_ptr->prev_row, png_ptr->row_buf, row_info.rowbytes + 1); + +#ifdef PNG_MNG_FEATURES_SUPPORTED + if ((png_ptr->mng_features_permitted & PNG_FLAG_MNG_FILTER_64) && + (png_ptr->filter_type == PNG_INTRAPIXEL_DIFFERENCING)) + { + /* Intrapixel differencing */ + png_do_read_intrapixel(&row_info, png_ptr->row_buf + 1); + } +#endif + + +#ifdef PNG_READ_TRANSFORMS_SUPPORTED + if (png_ptr->transformations) + png_do_read_transformations(png_ptr, &row_info); +#endif + + /* The transformed pixel depth should match the depth now in row_info. */ + if (png_ptr->transformed_pixel_depth == 0) + { + png_ptr->transformed_pixel_depth = row_info.pixel_depth; + if (row_info.pixel_depth > png_ptr->maximum_pixel_depth) + png_error(png_ptr, "sequential row overflow"); + } + + else if (png_ptr->transformed_pixel_depth != row_info.pixel_depth) + png_error(png_ptr, "internal sequential row size calculation error"); + +#ifdef PNG_READ_INTERLACING_SUPPORTED + /* Blow up interlaced rows to full size */ + if (png_ptr->interlaced && + (png_ptr->transformations & PNG_INTERLACE)) + { + if (png_ptr->pass < 6) + png_do_read_interlace(&row_info, png_ptr->row_buf + 1, png_ptr->pass, + png_ptr->transformations); + + if (dsp_row != NULL) + png_combine_row(png_ptr, dsp_row, 1/*display*/); + + if (row != NULL) + png_combine_row(png_ptr, row, 0/*row*/); + } + + else +#endif + { + if (row != NULL) + png_combine_row(png_ptr, row, -1/*ignored*/); + + if (dsp_row != NULL) + png_combine_row(png_ptr, dsp_row, -1/*ignored*/); + } + png_read_finish_row(png_ptr); + + if (png_ptr->read_row_fn != NULL) + (*(png_ptr->read_row_fn))(png_ptr, png_ptr->row_number, png_ptr->pass); + +} +#endif /* PNG_SEQUENTIAL_READ_SUPPORTED */ + +#ifdef PNG_SEQUENTIAL_READ_SUPPORTED +/* Read one or more rows of image data. If the image is interlaced, + * and png_set_interlace_handling() has been called, the rows need to + * contain the contents of the rows from the previous pass. If the + * image has alpha or transparency, and png_handle_alpha()[*] has been + * called, the rows contents must be initialized to the contents of the + * screen. + * + * "row" holds the actual image, and pixels are placed in it + * as they arrive. If the image is displayed after each pass, it will + * appear to "sparkle" in. "display_row" can be used to display a + * "chunky" progressive image, with finer detail added as it becomes + * available. If you do not want this "chunky" display, you may pass + * NULL for display_row. If you do not want the sparkle display, and + * you have not called png_handle_alpha(), you may pass NULL for rows. + * If you have called png_handle_alpha(), and the image has either an + * alpha channel or a transparency chunk, you must provide a buffer for + * rows. In this case, you do not have to provide a display_row buffer + * also, but you may. If the image is not interlaced, or if you have + * not called png_set_interlace_handling(), the display_row buffer will + * be ignored, so pass NULL to it. + * + * [*] png_handle_alpha() does not exist yet, as of this version of libpng + */ + +void PNGAPI +png_read_rows(png_structrp png_ptr, png_bytepp row, + png_bytepp display_row, png_uint_32 num_rows) +{ + png_uint_32 i; + png_bytepp rp; + png_bytepp dp; + + png_debug(1, "in png_read_rows"); + + if (png_ptr == NULL) + return; + + rp = row; + dp = display_row; + if (rp != NULL && dp != NULL) + for (i = 0; i < num_rows; i++) + { + png_bytep rptr = *rp++; + png_bytep dptr = *dp++; + + png_read_row(png_ptr, rptr, dptr); + } + + else if (rp != NULL) + for (i = 0; i < num_rows; i++) + { + png_bytep rptr = *rp; + png_read_row(png_ptr, rptr, NULL); + rp++; + } + + else if (dp != NULL) + for (i = 0; i < num_rows; i++) + { + png_bytep dptr = *dp; + png_read_row(png_ptr, NULL, dptr); + dp++; + } +} +#endif /* PNG_SEQUENTIAL_READ_SUPPORTED */ + +#ifdef PNG_SEQUENTIAL_READ_SUPPORTED +/* Read the entire image. If the image has an alpha channel or a tRNS + * chunk, and you have called png_handle_alpha()[*], you will need to + * initialize the image to the current image that PNG will be overlaying. + * We set the num_rows again here, in case it was incorrectly set in + * png_read_start_row() by a call to png_read_update_info() or + * png_start_read_image() if png_set_interlace_handling() wasn't called + * prior to either of these functions like it should have been. You can + * only call this function once. If you desire to have an image for + * each pass of a interlaced image, use png_read_rows() instead. + * + * [*] png_handle_alpha() does not exist yet, as of this version of libpng + */ +void PNGAPI +png_read_image(png_structrp png_ptr, png_bytepp image) +{ + png_uint_32 i, image_height; + int pass, j; + png_bytepp rp; + + png_debug(1, "in png_read_image"); + + if (png_ptr == NULL) + return; + +#ifdef PNG_READ_INTERLACING_SUPPORTED + if (!(png_ptr->flags & PNG_FLAG_ROW_INIT)) + { + pass = png_set_interlace_handling(png_ptr); + /* And make sure transforms are initialized. */ + png_start_read_image(png_ptr); + } + else + { + if (png_ptr->interlaced && !(png_ptr->transformations & PNG_INTERLACE)) + { + /* Caller called png_start_read_image or png_read_update_info without + * first turning on the PNG_INTERLACE transform. We can fix this here, + * but the caller should do it! + */ + png_warning(png_ptr, "Interlace handling should be turned on when " + "using png_read_image"); + /* Make sure this is set correctly */ + png_ptr->num_rows = png_ptr->height; + } + + /* Obtain the pass number, which also turns on the PNG_INTERLACE flag in + * the above error case. + */ + pass = png_set_interlace_handling(png_ptr); + } +#else + if (png_ptr->interlaced) + png_error(png_ptr, + "Cannot read interlaced image -- interlace handler disabled"); + + pass = 1; +#endif + + image_height=png_ptr->height; + + for (j = 0; j < pass; j++) + { + rp = image; + for (i = 0; i < image_height; i++) + { + png_read_row(png_ptr, *rp, NULL); + rp++; + } + } +} +#endif /* PNG_SEQUENTIAL_READ_SUPPORTED */ + +#ifdef PNG_SEQUENTIAL_READ_SUPPORTED +/* Read the end of the PNG file. Will not read past the end of the + * file, will verify the end is accurate, and will read any comments + * or time information at the end of the file, if info is not NULL. + */ +void PNGAPI +png_read_end(png_structrp png_ptr, png_inforp info_ptr) +{ +#ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED + int keep; +#endif + + png_debug(1, "in png_read_end"); + + if (png_ptr == NULL) + return; + + /* If png_read_end is called in the middle of reading the rows there may + * still be pending IDAT data and an owned zstream. Deal with this here. + */ +#ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED + if (!png_chunk_unknown_handling(png_ptr, png_IDAT)) +#endif + png_read_finish_IDAT(png_ptr); + +#ifdef PNG_READ_CHECK_FOR_INVALID_INDEX_SUPPORTED + /* Report invalid palette index; added at libng-1.5.10 */ + if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE && + png_ptr->num_palette_max > png_ptr->num_palette) + png_benign_error(png_ptr, "Read palette index exceeding num_palette"); +#endif + + do + { + png_uint_32 length = png_read_chunk_header(png_ptr); + png_uint_32 chunk_name = png_ptr->chunk_name; + + if (chunk_name == png_IHDR) + png_handle_IHDR(png_ptr, info_ptr, length); + + else if (chunk_name == png_IEND) + png_handle_IEND(png_ptr, info_ptr, length); + +#ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED + else if ((keep = png_chunk_unknown_handling(png_ptr, chunk_name)) != 0) + { + if (chunk_name == png_IDAT) + { + if ((length > 0) || (png_ptr->mode & PNG_HAVE_CHUNK_AFTER_IDAT)) + png_benign_error(png_ptr, "Too many IDATs found"); + } + png_handle_unknown(png_ptr, info_ptr, length, keep); + if (chunk_name == png_PLTE) + png_ptr->mode |= PNG_HAVE_PLTE; + } +#endif + + else if (chunk_name == png_IDAT) + { + /* Zero length IDATs are legal after the last IDAT has been + * read, but not after other chunks have been read. + */ + if ((length > 0) || (png_ptr->mode & PNG_HAVE_CHUNK_AFTER_IDAT)) + png_benign_error(png_ptr, "Too many IDATs found"); + + png_crc_finish(png_ptr, length); + } + else if (chunk_name == png_PLTE) + png_handle_PLTE(png_ptr, info_ptr, length); + +#ifdef PNG_READ_bKGD_SUPPORTED + else if (chunk_name == png_bKGD) + png_handle_bKGD(png_ptr, info_ptr, length); +#endif + +#ifdef PNG_READ_cHRM_SUPPORTED + else if (chunk_name == png_cHRM) + png_handle_cHRM(png_ptr, info_ptr, length); +#endif + +#ifdef PNG_READ_gAMA_SUPPORTED + else if (chunk_name == png_gAMA) + png_handle_gAMA(png_ptr, info_ptr, length); +#endif + +#ifdef PNG_READ_hIST_SUPPORTED + else if (chunk_name == png_hIST) + png_handle_hIST(png_ptr, info_ptr, length); +#endif + +#ifdef PNG_READ_oFFs_SUPPORTED + else if (chunk_name == png_oFFs) + png_handle_oFFs(png_ptr, info_ptr, length); +#endif + +#ifdef PNG_READ_pCAL_SUPPORTED + else if (chunk_name == png_pCAL) + png_handle_pCAL(png_ptr, info_ptr, length); +#endif + +#ifdef PNG_READ_sCAL_SUPPORTED + else if (chunk_name == png_sCAL) + png_handle_sCAL(png_ptr, info_ptr, length); +#endif + +#ifdef PNG_READ_pHYs_SUPPORTED + else if (chunk_name == png_pHYs) + png_handle_pHYs(png_ptr, info_ptr, length); +#endif + +#ifdef PNG_READ_sBIT_SUPPORTED + else if (chunk_name == png_sBIT) + png_handle_sBIT(png_ptr, info_ptr, length); +#endif + +#ifdef PNG_READ_sRGB_SUPPORTED + else if (chunk_name == png_sRGB) + png_handle_sRGB(png_ptr, info_ptr, length); +#endif + +#ifdef PNG_READ_iCCP_SUPPORTED + else if (chunk_name == png_iCCP) + png_handle_iCCP(png_ptr, info_ptr, length); +#endif + +#ifdef PNG_READ_sPLT_SUPPORTED + else if (chunk_name == png_sPLT) + png_handle_sPLT(png_ptr, info_ptr, length); +#endif + +#ifdef PNG_READ_tEXt_SUPPORTED + else if (chunk_name == png_tEXt) + png_handle_tEXt(png_ptr, info_ptr, length); +#endif + +#ifdef PNG_READ_tIME_SUPPORTED + else if (chunk_name == png_tIME) + png_handle_tIME(png_ptr, info_ptr, length); +#endif + +#ifdef PNG_READ_tRNS_SUPPORTED + else if (chunk_name == png_tRNS) + png_handle_tRNS(png_ptr, info_ptr, length); +#endif + +#ifdef PNG_READ_zTXt_SUPPORTED + else if (chunk_name == png_zTXt) + png_handle_zTXt(png_ptr, info_ptr, length); +#endif + +#ifdef PNG_READ_iTXt_SUPPORTED + else if (chunk_name == png_iTXt) + png_handle_iTXt(png_ptr, info_ptr, length); +#endif + + else + png_handle_unknown(png_ptr, info_ptr, length, + PNG_HANDLE_CHUNK_AS_DEFAULT); + } while (!(png_ptr->mode & PNG_HAVE_IEND)); +} +#endif /* PNG_SEQUENTIAL_READ_SUPPORTED */ + +/* Free all memory used in the read struct */ +static void +png_read_destroy(png_structrp png_ptr) +{ + png_debug(1, "in png_read_destroy"); + +#ifdef PNG_READ_GAMMA_SUPPORTED + png_destroy_gamma_table(png_ptr); +#endif + + png_free(png_ptr, png_ptr->big_row_buf); + png_free(png_ptr, png_ptr->big_prev_row); + png_free(png_ptr, png_ptr->read_buffer); + +#ifdef PNG_READ_QUANTIZE_SUPPORTED + png_free(png_ptr, png_ptr->palette_lookup); + png_free(png_ptr, png_ptr->quantize_index); +#endif + + if (png_ptr->free_me & PNG_FREE_PLTE) + png_zfree(png_ptr, png_ptr->palette); + png_ptr->free_me &= ~PNG_FREE_PLTE; + +#if defined(PNG_tRNS_SUPPORTED) || \ + defined(PNG_READ_EXPAND_SUPPORTED) || defined(PNG_READ_BACKGROUND_SUPPORTED) + if (png_ptr->free_me & PNG_FREE_TRNS) + png_free(png_ptr, png_ptr->trans_alpha); + png_ptr->free_me &= ~PNG_FREE_TRNS; +#endif + + inflateEnd(&png_ptr->zstream); + +#ifdef PNG_PROGRESSIVE_READ_SUPPORTED + png_free(png_ptr, png_ptr->save_buffer); +#endif + +#if defined(PNG_STORE_UNKNOWN_CHUNKS_SUPPORTED) &&\ + defined(PNG_READ_UNKNOWN_CHUNKS_SUPPORTED) + png_free(png_ptr, png_ptr->unknown_chunk.data); +#endif + +#ifdef PNG_SET_UNKNOWN_CHUNKS_SUPPORTED + png_free(png_ptr, png_ptr->chunk_list); +#endif + + /* NOTE: the 'setjmp' buffer may still be allocated and the memory and error + * callbacks are still set at this point. They are required to complete the + * destruction of the png_struct itself. + */ +} + +/* Free all memory used by the read */ +void PNGAPI +png_destroy_read_struct(png_structpp png_ptr_ptr, png_infopp info_ptr_ptr, + png_infopp end_info_ptr_ptr) +{ + png_structrp png_ptr = NULL; + + png_debug(1, "in png_destroy_read_struct"); + + if (png_ptr_ptr != NULL) + png_ptr = *png_ptr_ptr; + + if (png_ptr == NULL) + return; + + /* libpng 1.6.0: use the API to destroy info structs to ensure consistent + * behavior. Prior to 1.6.0 libpng did extra 'info' destruction in this API. + * The extra was, apparently, unnecessary yet this hides memory leak bugs. + */ + png_destroy_info_struct(png_ptr, end_info_ptr_ptr); + png_destroy_info_struct(png_ptr, info_ptr_ptr); + + *png_ptr_ptr = NULL; + png_read_destroy(png_ptr); + png_destroy_png_struct(png_ptr); +} + +void PNGAPI +png_set_read_status_fn(png_structrp png_ptr, png_read_status_ptr read_row_fn) +{ + if (png_ptr == NULL) + return; + + png_ptr->read_row_fn = read_row_fn; +} + + +#ifdef PNG_SEQUENTIAL_READ_SUPPORTED +#ifdef PNG_INFO_IMAGE_SUPPORTED +void PNGAPI +png_read_png(png_structrp png_ptr, png_inforp info_ptr, + int transforms, + voidp params) +{ + int row; + + if (png_ptr == NULL || info_ptr == NULL) + return; + + /* png_read_info() gives us all of the information from the + * PNG file before the first IDAT (image data chunk). + */ + png_read_info(png_ptr, info_ptr); + if (info_ptr->height > PNG_UINT_32_MAX/(sizeof (png_bytep))) + png_error(png_ptr, "Image is too high to process with png_read_png()"); + + /* -------------- image transformations start here ------------------- */ + +#ifdef PNG_READ_SCALE_16_TO_8_SUPPORTED + /* Tell libpng to strip 16-bit/color files down to 8 bits per color. + */ + if (transforms & PNG_TRANSFORM_SCALE_16) + { + /* Added at libpng-1.5.4. "strip_16" produces the same result that it + * did in earlier versions, while "scale_16" is now more accurate. + */ + png_set_scale_16(png_ptr); + } +#endif + +#ifdef PNG_READ_STRIP_16_TO_8_SUPPORTED + /* If both SCALE and STRIP are required pngrtran will effectively cancel the + * latter by doing SCALE first. This is ok and allows apps not to check for + * which is supported to get the right answer. + */ + if (transforms & PNG_TRANSFORM_STRIP_16) + png_set_strip_16(png_ptr); +#endif + +#ifdef PNG_READ_STRIP_ALPHA_SUPPORTED + /* Strip alpha bytes from the input data without combining with + * the background (not recommended). + */ + if (transforms & PNG_TRANSFORM_STRIP_ALPHA) + png_set_strip_alpha(png_ptr); +#endif + +#if defined(PNG_READ_PACK_SUPPORTED) && !defined(PNG_READ_EXPAND_SUPPORTED) + /* Extract multiple pixels with bit depths of 1, 2, or 4 from a single + * byte into separate bytes (useful for paletted and grayscale images). + */ + if (transforms & PNG_TRANSFORM_PACKING) + png_set_packing(png_ptr); +#endif + +#ifdef PNG_READ_PACKSWAP_SUPPORTED + /* Change the order of packed pixels to least significant bit first + * (not useful if you are using png_set_packing). + */ + if (transforms & PNG_TRANSFORM_PACKSWAP) + png_set_packswap(png_ptr); +#endif + +#ifdef PNG_READ_EXPAND_SUPPORTED + /* Expand paletted colors into true RGB triplets + * Expand grayscale images to full 8 bits from 1, 2, or 4 bits/pixel + * Expand paletted or RGB images with transparency to full alpha + * channels so the data will be available as RGBA quartets. + */ + if (transforms & PNG_TRANSFORM_EXPAND) + if ((png_ptr->bit_depth < 8) || + (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE) || + (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS))) + png_set_expand(png_ptr); +#endif + + /* We don't handle background color or gamma transformation or quantizing. + */ + +#ifdef PNG_READ_INVERT_SUPPORTED + /* Invert monochrome files to have 0 as white and 1 as black + */ + if (transforms & PNG_TRANSFORM_INVERT_MONO) + png_set_invert_mono(png_ptr); +#endif + +#ifdef PNG_READ_SHIFT_SUPPORTED + /* If you want to shift the pixel values from the range [0,255] or + * [0,65535] to the original [0,7] or [0,31], or whatever range the + * colors were originally in: + */ + if ((transforms & PNG_TRANSFORM_SHIFT) + && png_get_valid(png_ptr, info_ptr, PNG_INFO_sBIT)) + { + // 4j change: initialize with null to silence MSVC + png_color_8p sig_bit = NULL; + + png_get_sBIT(png_ptr, info_ptr, &sig_bit); + png_set_shift(png_ptr, sig_bit); + } +#endif + +#ifdef PNG_READ_BGR_SUPPORTED + /* Flip the RGB pixels to BGR (or RGBA to BGRA) */ + if (transforms & PNG_TRANSFORM_BGR) + png_set_bgr(png_ptr); +#endif + +#ifdef PNG_READ_SWAP_ALPHA_SUPPORTED + /* Swap the RGBA or GA data to ARGB or AG (or BGRA to ABGR) */ + if (transforms & PNG_TRANSFORM_SWAP_ALPHA) + png_set_swap_alpha(png_ptr); +#endif + +#ifdef PNG_READ_SWAP_SUPPORTED + /* Swap bytes of 16-bit files to least significant byte first */ + if (transforms & PNG_TRANSFORM_SWAP_ENDIAN) + png_set_swap(png_ptr); +#endif + +/* Added at libpng-1.2.41 */ +#ifdef PNG_READ_INVERT_ALPHA_SUPPORTED + /* Invert the alpha channel from opacity to transparency */ + if (transforms & PNG_TRANSFORM_INVERT_ALPHA) + png_set_invert_alpha(png_ptr); +#endif + +/* Added at libpng-1.2.41 */ +#ifdef PNG_READ_GRAY_TO_RGB_SUPPORTED + /* Expand grayscale image to RGB */ + if (transforms & PNG_TRANSFORM_GRAY_TO_RGB) + png_set_gray_to_rgb(png_ptr); +#endif + +/* Added at libpng-1.5.4 */ +#ifdef PNG_READ_EXPAND_16_SUPPORTED + if (transforms & PNG_TRANSFORM_EXPAND_16) + png_set_expand_16(png_ptr); +#endif + + /* We don't handle adding filler bytes */ + + /* We use png_read_image and rely on that for interlace handling, but we also + * call png_read_update_info therefore must turn on interlace handling now: + */ + (void)png_set_interlace_handling(png_ptr); + + /* Optional call to gamma correct and add the background to the palette + * and update info structure. REQUIRED if you are expecting libpng to + * update the palette for you (i.e., you selected such a transform above). + */ + png_read_update_info(png_ptr, info_ptr); + + /* -------------- image transformations end here ------------------- */ + + png_free_data(png_ptr, info_ptr, PNG_FREE_ROWS, 0); + if (info_ptr->row_pointers == NULL) + { + png_uint_32 iptr; + + info_ptr->row_pointers = (png_bytepp)png_malloc(png_ptr, + info_ptr->height * (sizeof (png_bytep))); + for (iptr=0; iptrheight; iptr++) + info_ptr->row_pointers[iptr] = NULL; + + info_ptr->free_me |= PNG_FREE_ROWS; + + for (row = 0; row < (int)info_ptr->height; row++) + info_ptr->row_pointers[row] = (png_bytep)png_malloc(png_ptr, + png_get_rowbytes(png_ptr, info_ptr)); + } + + png_read_image(png_ptr, info_ptr->row_pointers); + info_ptr->valid |= PNG_INFO_IDAT; + + /* Read rest of file, and get additional chunks in info_ptr - REQUIRED */ + png_read_end(png_ptr, info_ptr); + + PNG_UNUSED(transforms) /* Quiet compiler warnings */ + PNG_UNUSED(params) + +} +#endif /* PNG_INFO_IMAGE_SUPPORTED */ +#endif /* PNG_SEQUENTIAL_READ_SUPPORTED */ + +#ifdef PNG_SIMPLIFIED_READ_SUPPORTED +/* SIMPLIFIED READ + * + * This code currently relies on the sequential reader, though it could easily + * be made to work with the progressive one. + */ +/* Arguments to png_image_finish_read: */ + +/* Encoding of PNG data (used by the color-map code) */ +/* TODO: change these, dang, ANSI-C reserves the 'E' namespace. */ +# define E_NOTSET 0 /* File encoding not yet known */ +# define E_sRGB 1 /* 8-bit encoded to sRGB gamma */ +# define E_LINEAR 2 /* 16-bit linear: not encoded, NOT pre-multiplied! */ +# define E_FILE 3 /* 8-bit encoded to file gamma, not sRGB or linear */ +# define E_LINEAR8 4 /* 8-bit linear: only from a file value */ + +/* Color-map processing: after libpng has run on the PNG image further + * processing may be needed to conver the data to color-map indicies. + */ +#define PNG_CMAP_NONE 0 +#define PNG_CMAP_GA 1 /* Process GA data to a color-map with alpha */ +#define PNG_CMAP_TRANS 2 /* Process GA data to a background index */ +#define PNG_CMAP_RGB 3 /* Process RGB data */ +#define PNG_CMAP_RGB_ALPHA 4 /* Process RGBA data */ + +/* The following document where the background is for each processing case. */ +#define PNG_CMAP_NONE_BACKGROUND 256 +#define PNG_CMAP_GA_BACKGROUND 231 +#define PNG_CMAP_TRANS_BACKGROUND 254 +#define PNG_CMAP_RGB_BACKGROUND 256 +#define PNG_CMAP_RGB_ALPHA_BACKGROUND 216 + +typedef struct +{ + /* Arguments: */ + png_imagep image; + png_voidp buffer; + png_int_32 row_stride; + png_voidp colormap; + png_const_colorp background; + /* Local variables: */ + png_voidp local_row; + png_voidp first_row; + ptrdiff_t row_bytes; /* step between rows */ + int file_encoding; /* E_ values above */ + png_fixed_point gamma_to_linear; /* For E_FILE, reciprocal of gamma */ + int colormap_processing; /* PNG_CMAP_ values above */ +} png_image_read_control; + +/* Do all the *safe* initialization - 'safe' means that png_error won't be + * called, so setting up the jmp_buf is not required. This means that anything + * called from here must *not* call png_malloc - it has to call png_malloc_warn + * instead so that control is returned safely back to this routine. + */ +static int +png_image_read_init(png_imagep image) +{ + if (image->opaque == NULL) + { + png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, image, + png_safe_error, png_safe_warning); + + /* And set the rest of the structure to NULL to ensure that the various + * fields are consistent. + */ + memset(image, 0, (sizeof *image)); + image->version = PNG_IMAGE_VERSION; + + if (png_ptr != NULL) + { + png_infop info_ptr = png_create_info_struct(png_ptr); + + if (info_ptr != NULL) + { + png_controlp control = png_voidcast(png_controlp, + png_malloc_warn(png_ptr, (sizeof *control))); + + if (control != NULL) + { + memset(control, 0, (sizeof *control)); + + control->png_ptr = png_ptr; + control->info_ptr = info_ptr; + control->for_write = 0; + + image->opaque = control; + return 1; + } + + /* Error clean up */ + png_destroy_info_struct(png_ptr, &info_ptr); + } + + png_destroy_read_struct(&png_ptr, NULL, NULL); + } + + return png_image_error(image, "png_image_read: out of memory"); + } + + return png_image_error(image, "png_image_read: opaque pointer not NULL"); +} + +/* Utility to find the base format of a PNG file from a png_struct. */ +static png_uint_32 +png_image_format(png_structrp png_ptr) +{ + png_uint_32 format = 0; + + if (png_ptr->color_type & PNG_COLOR_MASK_COLOR) + format |= PNG_FORMAT_FLAG_COLOR; + + if (png_ptr->color_type & PNG_COLOR_MASK_ALPHA) + format |= PNG_FORMAT_FLAG_ALPHA; + + /* Use png_ptr here, not info_ptr, because by examination png_handle_tRNS + * sets the png_struct fields; that's all we are interested in here. The + * precise interaction with an app call to png_set_tRNS and PNG file reading + * is unclear. + */ + else if (png_ptr->num_trans > 0) + format |= PNG_FORMAT_FLAG_ALPHA; + + if (png_ptr->bit_depth == 16) + format |= PNG_FORMAT_FLAG_LINEAR; + + if (png_ptr->color_type & PNG_COLOR_MASK_PALETTE) + format |= PNG_FORMAT_FLAG_COLORMAP; + + return format; +} + +/* Is the given gamma significantly different from sRGB? The test is the same + * one used in pngrtran.c when deciding whether to do gamma correction. The + * arithmetic optimizes the division by using the fact that the inverse of the + * file sRGB gamma is 2.2 + */ +static int +png_gamma_not_sRGB(png_fixed_point g) +{ + if (g < PNG_FP_1) + { + /* An uninitialized gamma is assumed to be sRGB for the simplified API. */ + if (g == 0) + return 0; + + return png_gamma_significant((g * 11 + 2)/5 /* i.e. *2.2, rounded */); + } + + return 1; +} + +/* Do the main body of a 'png_image_begin_read' function; read the PNG file + * header and fill in all the information. This is executed in a safe context, + * unlike the init routine above. + */ +static int +png_image_read_header(png_voidp argument) +{ + png_imagep image = png_voidcast(png_imagep, argument); + png_structrp png_ptr = image->opaque->png_ptr; + png_inforp info_ptr = image->opaque->info_ptr; + + png_set_benign_errors(png_ptr, 1/*warn*/); + png_read_info(png_ptr, info_ptr); + + /* Do this the fast way; just read directly out of png_struct. */ + image->width = png_ptr->width; + image->height = png_ptr->height; + + { + png_uint_32 format = png_image_format(png_ptr); + + image->format = format; + +#ifdef PNG_COLORSPACE_SUPPORTED + /* Does the colorspace match sRGB? If there is no color endpoint + * (colorant) information assume yes, otherwise require the + * 'ENDPOINTS_MATCHE_sRGB' colorspace flag to have been set. If the + * colorspace has been determined to be invalid ignore it. + */ + if ((format & PNG_FORMAT_FLAG_COLOR) != 0 && ((png_ptr->colorspace.flags + & (PNG_COLORSPACE_HAVE_ENDPOINTS|PNG_COLORSPACE_ENDPOINTS_MATCH_sRGB| + PNG_COLORSPACE_INVALID)) == PNG_COLORSPACE_HAVE_ENDPOINTS)) + image->flags |= PNG_IMAGE_FLAG_COLORSPACE_NOT_sRGB; +#endif + } + + /* We need the maximum number of entries regardless of the format the + * application sets here. + */ + { + png_uint_32 cmap_entries; + + switch (png_ptr->color_type) + { + case PNG_COLOR_TYPE_GRAY: + cmap_entries = 1U << png_ptr->bit_depth; + break; + + case PNG_COLOR_TYPE_PALETTE: + cmap_entries = png_ptr->num_palette; + break; + + default: + cmap_entries = 256; + break; + } + + if (cmap_entries > 256) + cmap_entries = 256; + + image->colormap_entries = cmap_entries; + } + + return 1; +} + +#ifdef PNG_STDIO_SUPPORTED +int PNGAPI +png_image_begin_read_from_stdio(png_imagep image, FILE* file) +{ + if (image != NULL && image->version == PNG_IMAGE_VERSION) + { + if (file != NULL) + { + if (png_image_read_init(image)) + { + /* This is slightly evil, but png_init_io doesn't do anything other + * than this and we haven't changed the standard IO functions so + * this saves a 'safe' function. + */ + image->opaque->png_ptr->io_ptr = file; + return png_safe_execute(image, png_image_read_header, image); + } + } + + else + return png_image_error(image, + "png_image_begin_read_from_stdio: invalid argument"); + } + + else if (image != NULL) + return png_image_error(image, + "png_image_begin_read_from_stdio: incorrect PNG_IMAGE_VERSION"); + + return 0; +} + +int PNGAPI +png_image_begin_read_from_file(png_imagep image, const char *file_name) +{ + if (image != NULL && image->version == PNG_IMAGE_VERSION) + { + if (file_name != NULL) + { + FILE *fp = fopen(file_name, "rb"); + + if (fp != NULL) + { + if (png_image_read_init(image)) + { + image->opaque->png_ptr->io_ptr = fp; + image->opaque->owned_file = 1; + return png_safe_execute(image, png_image_read_header, image); + } + + /* Clean up: just the opened file. */ + (void)fclose(fp); + } + + else + return png_image_error(image, strerror(errno)); + } + + else + return png_image_error(image, + "png_image_begin_read_from_file: invalid argument"); + } + + else if (image != NULL) + return png_image_error(image, + "png_image_begin_read_from_file: incorrect PNG_IMAGE_VERSION"); + + return 0; +} +#endif /* PNG_STDIO_SUPPORTED */ + +static void PNGCBAPI +png_image_memory_read(png_structp png_ptr, png_bytep out, png_size_t need) +{ + if (png_ptr != NULL) + { + png_imagep image = png_voidcast(png_imagep, png_ptr->io_ptr); + if (image != NULL) + { + png_controlp cp = image->opaque; + if (cp != NULL) + { + png_const_bytep memory = cp->memory; + png_size_t size = cp->size; + + if (memory != NULL && size >= need) + { + memcpy(out, memory, need); + cp->memory = memory + need; + cp->size = size - need; + return; + } + + png_error(png_ptr, "read beyond end of data"); + } + } + + png_error(png_ptr, "invalid memory read"); + } +} + +int PNGAPI png_image_begin_read_from_memory(png_imagep image, + png_const_voidp memory, png_size_t size) +{ + if (image != NULL && image->version == PNG_IMAGE_VERSION) + { + if (memory != NULL && size > 0) + { + if (png_image_read_init(image)) + { + /* Now set the IO functions to read from the memory buffer and + * store it into io_ptr. Again do this in-place to avoid calling a + * libpng function that requires error handling. + */ + image->opaque->memory = png_voidcast(png_const_bytep, memory); + image->opaque->size = size; + image->opaque->png_ptr->io_ptr = image; + image->opaque->png_ptr->read_data_fn = png_image_memory_read; + + return png_safe_execute(image, png_image_read_header, image); + } + } + + else + return png_image_error(image, + "png_image_begin_read_from_memory: invalid argument"); + } + + else if (image != NULL) + return png_image_error(image, + "png_image_begin_read_from_memory: incorrect PNG_IMAGE_VERSION"); + + return 0; +} + +/* Utility function to skip chunks that are not used by the simplified image + * read functions and an appropriate macro to call it. + */ +#ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED +static void +png_image_skip_unused_chunks(png_structrp png_ptr) +{ + /* Prepare the reader to ignore all recognized chunks whose data will not + * be used, i.e., all chunks recognized by libpng except for those + * involved in basic image reading: + * + * IHDR, PLTE, IDAT, IEND + * + * Or image data handling: + * + * tRNS, bKGD, gAMA, cHRM, sRGB, iCCP and sBIT. + * + * This provides a small performance improvement and eliminates any + * potential vulnerability to security problems in the unused chunks. + */ + { + static PNG_CONST png_byte chunks_to_process[] = { + 98, 75, 71, 68, '\0', /* bKGD */ + 99, 72, 82, 77, '\0', /* cHRM */ + 103, 65, 77, 65, '\0', /* gAMA */ + 105, 67, 67, 80, '\0', /* iCCP */ + 115, 66, 73, 84, '\0', /* sBIT */ + 115, 82, 71, 66, '\0', /* sRGB */ + }; + + /* Ignore unknown chunks and all other chunks except for the + * IHDR, PLTE, tRNS, IDAT, and IEND chunks. + */ + png_set_keep_unknown_chunks(png_ptr, PNG_HANDLE_CHUNK_NEVER, + NULL, -1); + + /* But do not ignore image data handling chunks */ + png_set_keep_unknown_chunks(png_ptr, PNG_HANDLE_CHUNK_AS_DEFAULT, + chunks_to_process, (sizeof chunks_to_process)/5); + } +} + +# define PNG_SKIP_CHUNKS(p) png_image_skip_unused_chunks(p) +#else +# define PNG_SKIP_CHUNKS(p) ((void)0) +#endif /* PNG_HANDLE_AS_UNKNOWN_SUPPORTED */ + +/* The following macro gives the exact rounded answer for all values in the + * range 0..255 (it actually divides by 51.2, but the rounding still generates + * the correct numbers 0..5 + */ +#define PNG_DIV51(v8) (((v8) * 5 + 130) >> 8) + +/* Utility functions to make particular color-maps */ +static void +set_file_encoding(png_image_read_control *display) +{ + png_fixed_point g = display->image->opaque->png_ptr->colorspace.gamma; + if (png_gamma_significant(g)) + { + if (png_gamma_not_sRGB(g)) + { + display->file_encoding = E_FILE; + display->gamma_to_linear = png_reciprocal(g); + } + + else + display->file_encoding = E_sRGB; + } + + else + display->file_encoding = E_LINEAR8; +} + +static unsigned int +decode_gamma(png_image_read_control *display, png_uint_32 value, int encoding) +{ + if (encoding == E_FILE) /* double check */ + encoding = display->file_encoding; + + if (encoding == E_NOTSET) /* must be the file encoding */ + { + set_file_encoding(display); + encoding = display->file_encoding; + } + + switch (encoding) + { + case E_FILE: + value = png_gamma_16bit_correct(value*257, display->gamma_to_linear); + break; + + case E_sRGB: + value = png_sRGB_table[value]; + break; + + case E_LINEAR: + break; + + case E_LINEAR8: + value *= 257; + break; + + default: + png_error(display->image->opaque->png_ptr, + "unexpected encoding (internal error)"); + break; + } + + return value; +} + +static png_uint_32 +png_colormap_compose(png_image_read_control *display, + png_uint_32 foreground, int foreground_encoding, png_uint_32 alpha, + png_uint_32 background, int encoding) +{ + /* The file value is composed on the background, the background has the given + * encoding and so does the result, the file is encoded with E_FILE and the + * file and alpha are 8-bit values. The (output) encoding will always be + * E_LINEAR or E_sRGB. + */ + png_uint_32 f = decode_gamma(display, foreground, foreground_encoding); + png_uint_32 b = decode_gamma(display, background, encoding); + + /* The alpha is always an 8-bit value (it comes from the palette), the value + * scaled by 255 is what PNG_sRGB_FROM_LINEAR requires. + */ + f = f * alpha + b * (255-alpha); + + if (encoding == E_LINEAR) + { + /* Scale to 65535; divide by 255, approximately (in fact this is extremely + * accurate, it divides by 255.00000005937181414556, with no overflow.) + */ + f *= 257; /* Now scaled by 65535 */ + f += f >> 16; + f = (f+32768) >> 16; + } + + else /* E_sRGB */ + f = PNG_sRGB_FROM_LINEAR(f); + + return f; +} + +/* NOTE: E_LINEAR values to this routine must be 16-bit, but E_FILE values must + * be 8-bit. + */ +static void +png_create_colormap_entry(png_image_read_control *display, + png_uint_32 ip, png_uint_32 red, png_uint_32 green, png_uint_32 blue, + png_uint_32 alpha, int encoding) +{ + png_imagep image = display->image; + const int output_encoding = (image->format & PNG_FORMAT_FLAG_LINEAR) ? + E_LINEAR : E_sRGB; + const int convert_to_Y = (image->format & PNG_FORMAT_FLAG_COLOR) == 0 && + (red != green || green != blue); + + if (ip > 255) + png_error(image->opaque->png_ptr, "color-map index out of range"); + + /* Update the cache with whether the file gamma is significantly different + * from sRGB. + */ + if (encoding == E_FILE) + { + if (display->file_encoding == E_NOTSET) + set_file_encoding(display); + + /* Note that the cached value may be E_FILE too, but if it is then the + * gamma_to_linear member has been set. + */ + encoding = display->file_encoding; + } + + if (encoding == E_FILE) + { + png_fixed_point g = display->gamma_to_linear; + + red = png_gamma_16bit_correct(red*257, g); + green = png_gamma_16bit_correct(green*257, g); + blue = png_gamma_16bit_correct(blue*257, g); + + if (convert_to_Y || output_encoding == E_LINEAR) + { + alpha *= 257; + encoding = E_LINEAR; + } + + else + { + red = PNG_sRGB_FROM_LINEAR(red * 255); + green = PNG_sRGB_FROM_LINEAR(green * 255); + blue = PNG_sRGB_FROM_LINEAR(blue * 255); + encoding = E_sRGB; + } + } + + else if (encoding == E_LINEAR8) + { + /* This encoding occurs quite frequently in test cases because PngSuite + * includes a gAMA 1.0 chunk with most images. + */ + red *= 257; + green *= 257; + blue *= 257; + alpha *= 257; + encoding = E_LINEAR; + } + + else if (encoding == E_sRGB && (convert_to_Y || output_encoding == E_LINEAR)) + { + /* The values are 8-bit sRGB values, but must be converted to 16-bit + * linear. + */ + red = png_sRGB_table[red]; + green = png_sRGB_table[green]; + blue = png_sRGB_table[blue]; + alpha *= 257; + encoding = E_LINEAR; + } + + /* This is set if the color isn't gray but the output is. */ + if (encoding == E_LINEAR) + { + if (convert_to_Y) + { + /* NOTE: these values are copied from png_do_rgb_to_gray */ + png_uint_32 y = (png_uint_32)6968 * red + (png_uint_32)23434 * green + + (png_uint_32)2366 * blue; + + if (output_encoding == E_LINEAR) + y = (y + 16384) >> 15; + + else + { + /* y is scaled by 32768, we need it scaled by 255: */ + y = (y + 128) >> 8; + y *= 255; + y = PNG_sRGB_FROM_LINEAR((y + 64) >> 7); + encoding = E_sRGB; + } + + blue = red = green = y; + } + + else if (output_encoding == E_sRGB) + { + red = PNG_sRGB_FROM_LINEAR(red * 255); + green = PNG_sRGB_FROM_LINEAR(green * 255); + blue = PNG_sRGB_FROM_LINEAR(blue * 255); + alpha = PNG_DIV257(alpha); + encoding = E_sRGB; + } + } + + if (encoding != output_encoding) + png_error(image->opaque->png_ptr, "bad encoding (internal error)"); + + /* Store the value. */ + { +# ifdef PNG_FORMAT_BGR_SUPPORTED + const int afirst = (image->format & PNG_FORMAT_FLAG_AFIRST) != 0 && + (image->format & PNG_FORMAT_FLAG_ALPHA) != 0; +# else +# define afirst 0 +# endif +# ifdef PNG_FORMAT_BGR_SUPPORTED + const int bgr = (image->format & PNG_FORMAT_FLAG_BGR) ? 2 : 0; +# else +# define bgr 0 +# endif + + if (output_encoding == E_LINEAR) + { + png_uint_16p entry = png_voidcast(png_uint_16p, display->colormap); + + entry += ip * PNG_IMAGE_SAMPLE_CHANNELS(image->format); + + /* The linear 16-bit values must be pre-multiplied by the alpha channel + * value, if less than 65535 (this is, effectively, composite on black + * if the alpha channel is removed.) + */ + switch (PNG_IMAGE_SAMPLE_CHANNELS(image->format)) + { + case 4: + entry[afirst ? 0 : 3] = (png_uint_16)alpha; + /* FALL THROUGH */ + + case 3: + if (alpha < 65535) + { + if (alpha > 0) + { + blue = (blue * alpha + 32767U)/65535U; + green = (green * alpha + 32767U)/65535U; + red = (red * alpha + 32767U)/65535U; + } + + else + red = green = blue = 0; + } + entry[afirst + (2 ^ bgr)] = (png_uint_16)blue; + entry[afirst + 1] = (png_uint_16)green; + entry[afirst + bgr] = (png_uint_16)red; + break; + + case 2: + entry[1 ^ afirst] = (png_uint_16)alpha; + /* FALL THROUGH */ + + case 1: + if (alpha < 65535) + { + if (alpha > 0) + green = (green * alpha + 32767U)/65535U; + + else + green = 0; + } + entry[afirst] = (png_uint_16)green; + break; + + default: + break; + } + } + + else /* output encoding is E_sRGB */ + { + png_bytep entry = png_voidcast(png_bytep, display->colormap); + + entry += ip * PNG_IMAGE_SAMPLE_CHANNELS(image->format); + + switch (PNG_IMAGE_SAMPLE_CHANNELS(image->format)) + { + case 4: + entry[afirst ? 0 : 3] = (png_byte)alpha; + case 3: + entry[afirst + (2 ^ bgr)] = (png_byte)blue; + entry[afirst + 1] = (png_byte)green; + entry[afirst + bgr] = (png_byte)red; + break; + + case 2: + entry[1 ^ afirst] = (png_byte)alpha; + case 1: + entry[afirst] = (png_byte)green; + break; + + default: + break; + } + } + +# ifdef afirst +# undef afirst +# endif +# ifdef bgr +# undef bgr +# endif + } +} + +static int +make_gray_file_colormap(png_image_read_control *display) +{ + unsigned int i; + + for (i=0; i<256; ++i) + png_create_colormap_entry(display, i, i, i, i, 255, E_FILE); + + return i; +} + +static int +make_gray_colormap(png_image_read_control *display) +{ + unsigned int i; + + for (i=0; i<256; ++i) + png_create_colormap_entry(display, i, i, i, i, 255, E_sRGB); + + return i; +} +#define PNG_GRAY_COLORMAP_ENTRIES 256 + +static int +make_ga_colormap(png_image_read_control *display) +{ + unsigned int i, a; + + /* Alpha is retained, the output will be a color-map with entries + * selected by six levels of alpha. One transparent entry, 6 gray + * levels for all the intermediate alpha values, leaving 230 entries + * for the opaque grays. The color-map entries are the six values + * [0..5]*51, the GA processing uses PNG_DIV51(value) to find the + * relevant entry. + * + * if (alpha > 229) // opaque + * { + * // The 231 entries are selected to make the math below work: + * base = 0; + * entry = (231 * gray + 128) >> 8; + * } + * else if (alpha < 26) // transparent + * { + * base = 231; + * entry = 0; + * } + * else // partially opaque + * { + * base = 226 + 6 * PNG_DIV51(alpha); + * entry = PNG_DIV51(gray); + * } + */ + i = 0; + while (i < 231) + { + unsigned int gray = (i * 256 + 115) / 231; + png_create_colormap_entry(display, i++, gray, gray, gray, 255, E_sRGB); + } + + /* 255 is used here for the component values for consistency with the code + * that undoes premultiplication in pngwrite.c. + */ + png_create_colormap_entry(display, i++, 255, 255, 255, 0, E_sRGB); + + for (a=1; a<5; ++a) + { + unsigned int g; + + for (g=0; g<6; ++g) + png_create_colormap_entry(display, i++, g*51, g*51, g*51, a*51, + E_sRGB); + } + + return i; +} + +#define PNG_GA_COLORMAP_ENTRIES 256 + +static int +make_rgb_colormap(png_image_read_control *display) +{ + unsigned int i, r; + + /* Build a 6x6x6 opaque RGB cube */ + for (i=r=0; r<6; ++r) + { + unsigned int g; + + for (g=0; g<6; ++g) + { + unsigned int b; + + for (b=0; b<6; ++b) + png_create_colormap_entry(display, i++, r*51, g*51, b*51, 255, + E_sRGB); + } + } + + return i; +} + +#define PNG_RGB_COLORMAP_ENTRIES 216 + +/* Return a palette index to the above palette given three 8-bit sRGB values. */ +#define PNG_RGB_INDEX(r,g,b) \ + ((png_byte)(6 * (6 * PNG_DIV51(r) + PNG_DIV51(g)) + PNG_DIV51(b))) + +static int +png_image_read_colormap(png_voidp argument) +{ + png_image_read_control *display = + png_voidcast(png_image_read_control*, argument); + const png_imagep image = display->image; + + const png_structrp png_ptr = image->opaque->png_ptr; + const png_uint_32 output_format = image->format; + const int output_encoding = (output_format & PNG_FORMAT_FLAG_LINEAR) ? + E_LINEAR : E_sRGB; + + unsigned int cmap_entries; + unsigned int output_processing; /* Output processing option */ + unsigned int data_encoding = E_NOTSET; /* Encoding libpng must produce */ + + /* Background information; the background color and the index of this color + * in the color-map if it exists (else 256). + */ + unsigned int background_index = 256; + png_uint_32 back_r, back_g, back_b; + + /* Flags to accumulate things that need to be done to the input. */ + int expand_tRNS = 0; + + /* Exclude the NYI feature of compositing onto a color-mapped buffer; it is + * very difficult to do, the results look awful, and it is difficult to see + * what possible use it is because the application can't control the + * color-map. + */ + if (((png_ptr->color_type & PNG_COLOR_MASK_ALPHA) != 0 || + png_ptr->num_trans > 0) /* alpha in input */ && + ((output_format & PNG_FORMAT_FLAG_ALPHA) == 0) /* no alpha in output */) + { + if (output_encoding == E_LINEAR) /* compose on black */ + back_b = back_g = back_r = 0; + + else if (display->background == NULL /* no way to remove it */) + png_error(png_ptr, + "a background color must be supplied to remove alpha/transparency"); + + /* Get a copy of the background color (this avoids repeating the checks + * below.) The encoding is 8-bit sRGB or 16-bit linear, depending on the + * output format. + */ + else + { + back_g = display->background->green; + if (output_format & PNG_FORMAT_FLAG_COLOR) + { + back_r = display->background->red; + back_b = display->background->blue; + } + else + back_b = back_r = back_g; + } + } + + else if (output_encoding == E_LINEAR) + back_b = back_r = back_g = 65535; + + else + back_b = back_r = back_g = 255; + + /* Default the input file gamma if required - this is necessary because + * libpng assumes that if no gamma information is present the data is in the + * output format, but the simplified API deduces the gamma from the input + * format. + */ + if ((png_ptr->colorspace.flags & PNG_COLORSPACE_HAVE_GAMMA) == 0) + { + /* Do this directly, not using the png_colorspace functions, to ensure + * that it happens even if the colorspace is invalid (though probably if + * it is the setting will be ignored) Note that the same thing can be + * achieved at the application interface with png_set_gAMA. + */ + if (png_ptr->bit_depth == 16 && + (image->flags & PNG_IMAGE_FLAG_16BIT_sRGB) == 0) + png_ptr->colorspace.gamma = PNG_GAMMA_LINEAR; + + else + png_ptr->colorspace.gamma = PNG_GAMMA_sRGB_INVERSE; + + png_ptr->colorspace.flags |= PNG_COLORSPACE_HAVE_GAMMA; + } + + /* Decide what to do based on the PNG color type of the input data. The + * utility function png_create_colormap_entry deals with most aspects of the + * output transformations; this code works out how to produce bytes of + * color-map entries from the original format. + */ + switch (png_ptr->color_type) + { + case PNG_COLOR_TYPE_GRAY: + if (png_ptr->bit_depth <= 8) + { + /* There at most 256 colors in the output, regardless of + * transparency. + */ + unsigned int step, i, val, trans = 256/*ignore*/, back_alpha = 0; + + cmap_entries = 1U << png_ptr->bit_depth; + if (cmap_entries > image->colormap_entries) + png_error(png_ptr, "gray[8] color-map: too few entries"); + + step = 255 / (cmap_entries - 1); + output_processing = PNG_CMAP_NONE; + + /* If there is a tRNS chunk then this either selects a transparent + * value or, if the output has no alpha, the background color. + */ + if (png_ptr->num_trans > 0) + { + trans = png_ptr->trans_color.gray; + + if ((output_format & PNG_FORMAT_FLAG_ALPHA) == 0) + back_alpha = output_encoding == E_LINEAR ? 65535 : 255; + } + + /* png_create_colormap_entry just takes an RGBA and writes the + * corresponding color-map entry using the format from 'image', + * including the required conversion to sRGB or linear as + * appropriate. The input values are always either sRGB (if the + * gamma correction flag is 0) or 0..255 scaled file encoded values + * (if the function must gamma correct them). + */ + for (i=val=0; ibit_depth < 8) + png_set_packing(png_ptr); + } + + else /* bit depth is 16 */ + { + /* The 16-bit input values can be converted directly to 8-bit gamma + * encoded values; however, if a tRNS chunk is present 257 color-map + * entries are required. This means that the extra entry requires + * special processing; add an alpha channel, sacrifice gray level + * 254 and convert transparent (alpha==0) entries to that. + * + * Use libpng to chop the data to 8 bits. Convert it to sRGB at the + * same time to minimize quality loss. If a tRNS chunk is present + * this means libpng must handle it too; otherwise it is impossible + * to do the exact match on the 16-bit value. + * + * If the output has no alpha channel *and* the background color is + * gray then it is possible to let libpng handle the substitution by + * ensuring that the corresponding gray level matches the background + * color exactly. + */ + data_encoding = E_sRGB; + + if (PNG_GRAY_COLORMAP_ENTRIES > image->colormap_entries) + png_error(png_ptr, "gray[16] color-map: too few entries"); + + cmap_entries = make_gray_colormap(display); + + if (png_ptr->num_trans > 0) + { + unsigned int back_alpha; + + if (output_format & PNG_FORMAT_FLAG_ALPHA) + back_alpha = 0; + + else + { + if (back_r == back_g && back_g == back_b) + { + /* Background is gray; no special processing will be + * required. + */ + png_color_16 c; + png_uint_32 gray = back_g; + + if (output_encoding == E_LINEAR) + { + gray = PNG_sRGB_FROM_LINEAR(gray * 255); + + /* And make sure the corresponding palette entry + * matches. + */ + png_create_colormap_entry(display, gray, back_g, back_g, + back_g, 65535, E_LINEAR); + } + + /* The background passed to libpng, however, must be the + * sRGB value. + */ + c.index = 0; /*unused*/ + c.gray = c.red = c.green = c.blue = (png_uint_16)gray; + + /* NOTE: does this work without expanding tRNS to alpha? + * It should be the color->gray case below apparently + * doesn't. + */ + png_set_background_fixed(png_ptr, &c, + PNG_BACKGROUND_GAMMA_SCREEN, 0/*need_expand*/, + 0/*gamma: not used*/); + + output_processing = PNG_CMAP_NONE; + break; + } + + back_alpha = output_encoding == E_LINEAR ? 65535 : 255; + } + + /* output_processing means that the libpng-processed row will be + * 8-bit GA and it has to be processing to single byte color-map + * values. Entry 254 is replaced by either a completely + * transparent entry or by the background color at full + * precision (and the background color is not a simple gray leve + * in this case.) + */ + expand_tRNS = 1; + output_processing = PNG_CMAP_TRANS; + background_index = 254; + + /* And set (overwrite) color-map entry 254 to the actual + * background color at full precision. + */ + png_create_colormap_entry(display, 254, back_r, back_g, back_b, + back_alpha, output_encoding); + } + + else + output_processing = PNG_CMAP_NONE; + } + break; + + case PNG_COLOR_TYPE_GRAY_ALPHA: + /* 8-bit or 16-bit PNG with two channels - gray and alpha. A minimum + * of 65536 combinations. If, however, the alpha channel is to be + * removed there are only 256 possibilities if the background is gray. + * (Otherwise there is a subset of the 65536 possibilities defined by + * the triangle between black, white and the background color.) + * + * Reduce 16-bit files to 8-bit and sRGB encode the result. No need to + * worry about tRNS matching - tRNS is ignored if there is an alpha + * channel. + */ + data_encoding = E_sRGB; + + if (output_format & PNG_FORMAT_FLAG_ALPHA) + { + if (PNG_GA_COLORMAP_ENTRIES > image->colormap_entries) + png_error(png_ptr, "gray+alpha color-map: too few entries"); + + cmap_entries = make_ga_colormap(display); + + background_index = PNG_CMAP_GA_BACKGROUND; + output_processing = PNG_CMAP_GA; + } + + else /* alpha is removed */ + { + /* Alpha must be removed as the PNG data is processed when the + * background is a color because the G and A channels are + * independent and the vector addition (non-parallel vectors) is a + * 2-D problem. + * + * This can be reduced to the same algorithm as above by making a + * colormap containing gray levels (for the opaque grays), a + * background entry (for a transparent pixel) and a set of four six + * level color values, one set for each intermediate alpha value. + * See the comments in make_ga_colormap for how this works in the + * per-pixel processing. + * + * If the background is gray, however, we only need a 256 entry gray + * level color map. It is sufficient to make the entry generated + * for the background color be exactly the color specified. + */ + if ((output_format & PNG_FORMAT_FLAG_COLOR) == 0 || + (back_r == back_g && back_g == back_b)) + { + /* Background is gray; no special processing will be required. */ + png_color_16 c; + png_uint_32 gray = back_g; + + if (PNG_GRAY_COLORMAP_ENTRIES > image->colormap_entries) + png_error(png_ptr, "gray-alpha color-map: too few entries"); + + cmap_entries = make_gray_colormap(display); + + if (output_encoding == E_LINEAR) + { + gray = PNG_sRGB_FROM_LINEAR(gray * 255); + + /* And make sure the corresponding palette entry matches. */ + png_create_colormap_entry(display, gray, back_g, back_g, + back_g, 65535, E_LINEAR); + } + + /* The background passed to libpng, however, must be the sRGB + * value. + */ + c.index = 0; /*unused*/ + c.gray = c.red = c.green = c.blue = (png_uint_16)gray; + + png_set_background_fixed(png_ptr, &c, + PNG_BACKGROUND_GAMMA_SCREEN, 0/*need_expand*/, + 0/*gamma: not used*/); + + output_processing = PNG_CMAP_NONE; + } + + else + { + png_uint_32 i, a; + + /* This is the same as png_make_ga_colormap, above, except that + * the entries are all opaque. + */ + if (PNG_GA_COLORMAP_ENTRIES > image->colormap_entries) + png_error(png_ptr, "ga-alpha color-map: too few entries"); + + i = 0; + while (i < 231) + { + png_uint_32 gray = (i * 256 + 115) / 231; + png_create_colormap_entry(display, i++, gray, gray, gray, + 255, E_sRGB); + } + + /* NOTE: this preserves the full precision of the application + * background color. + */ + background_index = i; + png_create_colormap_entry(display, i++, back_r, back_g, back_b, + output_encoding == E_LINEAR ? 65535U : 255U, output_encoding); + + /* For non-opaque input composite on the sRGB background - this + * requires inverting the encoding for each component. The input + * is still converted to the sRGB encoding because this is a + * reasonable approximate to the logarithmic curve of human + * visual sensitivity, at least over the narrow range which PNG + * represents. Consequently 'G' is always sRGB encoded, while + * 'A' is linear. We need the linear background colors. + */ + if (output_encoding == E_sRGB) /* else already linear */ + { + /* This may produce a value not exactly matching the + * background, but that's ok because these numbers are only + * used when alpha != 0 + */ + back_r = png_sRGB_table[back_r]; + back_g = png_sRGB_table[back_g]; + back_b = png_sRGB_table[back_b]; + } + + for (a=1; a<5; ++a) + { + unsigned int g; + + /* PNG_sRGB_FROM_LINEAR expects a 16-bit linear value scaled + * by an 8-bit alpha value (0..255). + */ + png_uint_32 alpha = 51 * a; + png_uint_32 back_rx = (255-alpha) * back_r; + png_uint_32 back_gx = (255-alpha) * back_g; + png_uint_32 back_bx = (255-alpha) * back_b; + + for (g=0; g<6; ++g) + { + png_uint_32 gray = png_sRGB_table[g*51] * alpha; + + png_create_colormap_entry(display, i++, + PNG_sRGB_FROM_LINEAR(gray + back_rx), + PNG_sRGB_FROM_LINEAR(gray + back_gx), + PNG_sRGB_FROM_LINEAR(gray + back_bx), 255, E_sRGB); + } + } + + cmap_entries = i; + output_processing = PNG_CMAP_GA; + } + } + break; + + case PNG_COLOR_TYPE_RGB: + case PNG_COLOR_TYPE_RGB_ALPHA: + /* Exclude the case where the output is gray; we can always handle this + * with the cases above. + */ + if ((output_format & PNG_FORMAT_FLAG_COLOR) == 0) + { + /* The color-map will be grayscale, so we may as well convert the + * input RGB values to a simple grayscale and use the grayscale + * code above. + * + * NOTE: calling this apparently damages the recognition of the + * transparent color in background color handling; call + * png_set_tRNS_to_alpha before png_set_background_fixed. + */ + png_set_rgb_to_gray_fixed(png_ptr, PNG_ERROR_ACTION_NONE, -1, + -1); + data_encoding = E_sRGB; + + /* The output will now be one or two 8-bit gray or gray+alpha + * channels. The more complex case arises when the input has alpha. + */ + if ((png_ptr->color_type == PNG_COLOR_TYPE_RGB_ALPHA || + png_ptr->num_trans > 0) && + (output_format & PNG_FORMAT_FLAG_ALPHA) != 0) + { + /* Both input and output have an alpha channel, so no background + * processing is required; just map the GA bytes to the right + * color-map entry. + */ + expand_tRNS = 1; + + if (PNG_GA_COLORMAP_ENTRIES > image->colormap_entries) + png_error(png_ptr, "rgb[ga] color-map: too few entries"); + + cmap_entries = make_ga_colormap(display); + background_index = PNG_CMAP_GA_BACKGROUND; + output_processing = PNG_CMAP_GA; + } + + else + { + /* Either the input or the output has no alpha channel, so there + * will be no non-opaque pixels in the color-map; it will just be + * grayscale. + */ + if (PNG_GRAY_COLORMAP_ENTRIES > image->colormap_entries) + png_error(png_ptr, "rgb[gray] color-map: too few entries"); + + /* Ideally this code would use libpng to do the gamma correction, + * but if an input alpha channel is to be removed we will hit the + * libpng bug in gamma+compose+rgb-to-gray (the double gamma + * correction bug). Fix this by dropping the gamma correction in + * this case and doing it in the palette; this will result in + * duplicate palette entries, but that's better than the + * alternative of double gamma correction. + */ + if ((png_ptr->color_type == PNG_COLOR_TYPE_RGB_ALPHA || + png_ptr->num_trans > 0) && + png_gamma_not_sRGB(png_ptr->colorspace.gamma)) + { + cmap_entries = make_gray_file_colormap(display); + data_encoding = E_FILE; + } + + else + cmap_entries = make_gray_colormap(display); + + /* But if the input has alpha or transparency it must be removed + */ + if (png_ptr->color_type == PNG_COLOR_TYPE_RGB_ALPHA || + png_ptr->num_trans > 0) + { + png_color_16 c; + png_uint_32 gray = back_g; + + /* We need to ensure that the application background exists in + * the colormap and that completely transparent pixels map to + * it. Achieve this simply by ensuring that the entry + * selected for the background really is the background color. + */ + if (data_encoding == E_FILE) /* from the fixup above */ + { + /* The app supplied a gray which is in output_encoding, we + * need to convert it to a value of the input (E_FILE) + * encoding then set this palette entry to the required + * output encoding. + */ + if (output_encoding == E_sRGB) + gray = png_sRGB_table[gray]; /* now E_LINEAR */ + + gray = PNG_DIV257(png_gamma_16bit_correct(gray, + png_ptr->colorspace.gamma)); /* now E_FILE */ + + /* And make sure the corresponding palette entry contains + * exactly the required sRGB value. + */ + png_create_colormap_entry(display, gray, back_g, back_g, + back_g, 0/*unused*/, output_encoding); + } + + else if (output_encoding == E_LINEAR) + { + gray = PNG_sRGB_FROM_LINEAR(gray * 255); + + /* And make sure the corresponding palette entry matches. + */ + png_create_colormap_entry(display, gray, back_g, back_g, + back_g, 0/*unused*/, E_LINEAR); + } + + /* The background passed to libpng, however, must be the + * output (normally sRGB) value. + */ + c.index = 0; /*unused*/ + c.gray = c.red = c.green = c.blue = (png_uint_16)gray; + + /* NOTE: the following is apparently a bug in libpng. Without + * it the transparent color recognition in + * png_set_background_fixed seems to go wrong. + */ + expand_tRNS = 1; + png_set_background_fixed(png_ptr, &c, + PNG_BACKGROUND_GAMMA_SCREEN, 0/*need_expand*/, + 0/*gamma: not used*/); + } + + output_processing = PNG_CMAP_NONE; + } + } + + else /* output is color */ + { + /* We could use png_quantize here so long as there is no transparent + * color or alpha; png_quantize ignores alpha. Easier overall just + * to do it once and using PNG_DIV51 on the 6x6x6 reduced RGB cube. + * Consequently we always want libpng to produce sRGB data. + */ + data_encoding = E_sRGB; + + /* Is there any transparency or alpha? */ + if (png_ptr->color_type == PNG_COLOR_TYPE_RGB_ALPHA || + png_ptr->num_trans > 0) + { + /* Is there alpha in the output too? If so all four channels are + * processed into a special RGB cube with alpha support. + */ + if (output_format & PNG_FORMAT_FLAG_ALPHA) + { + png_uint_32 r; + + if (PNG_RGB_COLORMAP_ENTRIES+1+27 > image->colormap_entries) + png_error(png_ptr, "rgb+alpha color-map: too few entries"); + + cmap_entries = make_rgb_colormap(display); + + /* Add a transparent entry. */ + png_create_colormap_entry(display, cmap_entries, 255, 255, + 255, 0, E_sRGB); + + /* This is stored as the background index for the processing + * algorithm. + */ + background_index = cmap_entries++; + + /* Add 27 r,g,b entries each with alpha 0.5. */ + for (r=0; r<256; r = (r << 1) | 0x7f) + { + png_uint_32 g; + + for (g=0; g<256; g = (g << 1) | 0x7f) + { + png_uint_32 b; + + /* This generates components with the values 0, 127 and + * 255 + */ + for (b=0; b<256; b = (b << 1) | 0x7f) + png_create_colormap_entry(display, cmap_entries++, + r, g, b, 128, E_sRGB); + } + } + + expand_tRNS = 1; + output_processing = PNG_CMAP_RGB_ALPHA; + } + + else + { + /* Alpha/transparency must be removed. The background must + * exist in the color map (achieved by setting adding it after + * the 666 color-map). If the standard processing code will + * pick up this entry automatically that's all that is + * required; libpng can be called to do the background + * processing. + */ + unsigned int sample_size = + PNG_IMAGE_SAMPLE_SIZE(output_format); + png_uint_32 r, g, b; /* sRGB background */ + + if (PNG_RGB_COLORMAP_ENTRIES+1+27 > image->colormap_entries) + png_error(png_ptr, "rgb-alpha color-map: too few entries"); + + cmap_entries = make_rgb_colormap(display); + + png_create_colormap_entry(display, cmap_entries, back_r, + back_g, back_b, 0/*unused*/, output_encoding); + + if (output_encoding == E_LINEAR) + { + r = PNG_sRGB_FROM_LINEAR(back_r * 255); + g = PNG_sRGB_FROM_LINEAR(back_g * 255); + b = PNG_sRGB_FROM_LINEAR(back_b * 255); + } + + else + { + r = back_r; + g = back_g; + b = back_g; + } + + /* Compare the newly-created color-map entry with the one the + * PNG_CMAP_RGB algorithm will use. If the two entries don't + * match, add the new one and set this as the background + * index. + */ + if (memcmp((png_const_bytep)display->colormap + + sample_size * cmap_entries, + (png_const_bytep)display->colormap + + sample_size * PNG_RGB_INDEX(r,g,b), + sample_size) != 0) + { + /* The background color must be added. */ + background_index = cmap_entries++; + + /* Add 27 r,g,b entries each with created by composing with + * the background at alpha 0.5. + */ + for (r=0; r<256; r = (r << 1) | 0x7f) + { + for (g=0; g<256; g = (g << 1) | 0x7f) + { + /* This generates components with the values 0, 127 + * and 255 + */ + for (b=0; b<256; b = (b << 1) | 0x7f) + png_create_colormap_entry(display, cmap_entries++, + png_colormap_compose(display, r, E_sRGB, 128, + back_r, output_encoding), + png_colormap_compose(display, g, E_sRGB, 128, + back_g, output_encoding), + png_colormap_compose(display, b, E_sRGB, 128, + back_b, output_encoding), + 0/*unused*/, output_encoding); + } + } + + expand_tRNS = 1; + output_processing = PNG_CMAP_RGB_ALPHA; + } + + else /* background color is in the standard color-map */ + { + png_color_16 c; + + c.index = 0; /*unused*/ + c.red = (png_uint_16)back_r; + c.gray = c.green = (png_uint_16)back_g; + c.blue = (png_uint_16)back_b; + + png_set_background_fixed(png_ptr, &c, + PNG_BACKGROUND_GAMMA_SCREEN, 0/*need_expand*/, + 0/*gamma: not used*/); + + output_processing = PNG_CMAP_RGB; + } + } + } + + else /* no alpha or transparency in the input */ + { + /* Alpha in the output is irrelevant, simply map the opaque input + * pixels to the 6x6x6 color-map. + */ + if (PNG_RGB_COLORMAP_ENTRIES > image->colormap_entries) + png_error(png_ptr, "rgb color-map: too few entries"); + + cmap_entries = make_rgb_colormap(display); + output_processing = PNG_CMAP_RGB; + } + } + break; + + case PNG_COLOR_TYPE_PALETTE: + /* It's already got a color-map. It may be necessary to eliminate the + * tRNS entries though. + */ + { + unsigned int num_trans = png_ptr->num_trans; + png_const_bytep trans = num_trans > 0 ? png_ptr->trans_alpha : NULL; + png_const_colorp colormap = png_ptr->palette; + const int do_background = trans != NULL && + (output_format & PNG_FORMAT_FLAG_ALPHA) == 0; + unsigned int i; + + /* Just in case: */ + if (trans == NULL) + num_trans = 0; + + output_processing = PNG_CMAP_NONE; + data_encoding = E_FILE; /* Don't change from color-map indicies */ + cmap_entries = png_ptr->num_palette; + if (cmap_entries > 256) + cmap_entries = 256; + + if (cmap_entries > image->colormap_entries) + png_error(png_ptr, "palette color-map: too few entries"); + + for (i=0; i < cmap_entries; ++i) + { + if (do_background && i < num_trans && trans[i] < 255) + { + if (trans[i] == 0) + png_create_colormap_entry(display, i, back_r, back_g, + back_b, 0, output_encoding); + + else + { + /* Must compose the PNG file color in the color-map entry + * on the sRGB color in 'back'. + */ + png_create_colormap_entry(display, i, + png_colormap_compose(display, colormap[i].red, E_FILE, + trans[i], back_r, output_encoding), + png_colormap_compose(display, colormap[i].green, E_FILE, + trans[i], back_g, output_encoding), + png_colormap_compose(display, colormap[i].blue, E_FILE, + trans[i], back_b, output_encoding), + output_encoding == E_LINEAR ? trans[i] * 257U : + trans[i], + output_encoding); + } + } + + else + png_create_colormap_entry(display, i, colormap[i].red, + colormap[i].green, colormap[i].blue, + i < num_trans ? trans[i] : 255U, E_FILE/*8-bit*/); + } + + /* The PNG data may have indicies packed in fewer than 8 bits, it + * must be expanded if so. + */ + if (png_ptr->bit_depth < 8) + png_set_packing(png_ptr); + } + break; + + default: + png_error(png_ptr, "invalid PNG color type"); + /*NOT REACHED*/ + break; + } + + /* Now deal with the output processing */ + if (expand_tRNS && png_ptr->num_trans > 0 && + (png_ptr->color_type & PNG_COLOR_MASK_ALPHA) == 0) + png_set_tRNS_to_alpha(png_ptr); + + switch (data_encoding) + { + default: + png_error(png_ptr, "bad data option (internal error)"); + break; + + case E_sRGB: + /* Change to 8-bit sRGB */ + png_set_alpha_mode_fixed(png_ptr, PNG_ALPHA_PNG, PNG_GAMMA_sRGB); + /* FALL THROUGH */ + + case E_FILE: + if (png_ptr->bit_depth > 8) + png_set_scale_16(png_ptr); + break; + } + + if (cmap_entries > 256 || cmap_entries > image->colormap_entries) + png_error(png_ptr, "color map overflow (BAD internal error)"); + + image->colormap_entries = cmap_entries; + + /* Double check using the recorded background index */ + switch (output_processing) + { + case PNG_CMAP_NONE: + if (background_index != PNG_CMAP_NONE_BACKGROUND) + goto bad_background; + break; + + case PNG_CMAP_GA: + if (background_index != PNG_CMAP_GA_BACKGROUND) + goto bad_background; + break; + + case PNG_CMAP_TRANS: + if (background_index >= cmap_entries || + background_index != PNG_CMAP_TRANS_BACKGROUND) + goto bad_background; + break; + + case PNG_CMAP_RGB: + if (background_index != PNG_CMAP_RGB_BACKGROUND) + goto bad_background; + break; + + case PNG_CMAP_RGB_ALPHA: + if (background_index != PNG_CMAP_RGB_ALPHA_BACKGROUND) + goto bad_background; + break; + + default: + png_error(png_ptr, "bad processing option (internal error)"); + + bad_background: + png_error(png_ptr, "bad background index (internal error)"); + } + + display->colormap_processing = output_processing; + + return 1/*ok*/; +} + +/* The final part of the color-map read called from png_image_finish_read. */ +static int +png_image_read_and_map(png_voidp argument) +{ + png_image_read_control *display = png_voidcast(png_image_read_control*, + argument); + png_imagep image = display->image; + png_structrp png_ptr = image->opaque->png_ptr; + int passes; + + /* Called when the libpng data must be transformed into the color-mapped + * form. There is a local row buffer in display->local and this routine must + * do the interlace handling. + */ + switch (png_ptr->interlaced) + { + case PNG_INTERLACE_NONE: + passes = 1; + break; + + case PNG_INTERLACE_ADAM7: + passes = PNG_INTERLACE_ADAM7_PASSES; + break; + + default: + passes = 0; + png_error(png_ptr, "unknown interlace type"); + } + + { + png_uint_32 height = image->height; + png_uint_32 width = image->width; + int proc = display->colormap_processing; + png_bytep first_row = png_voidcast(png_bytep, display->first_row); + ptrdiff_t step_row = display->row_bytes; + int pass; + + for (pass = 0; pass < passes; ++pass) + { + unsigned int startx, stepx, stepy; + png_uint_32 y; + + if (png_ptr->interlaced == PNG_INTERLACE_ADAM7) + { + /* The row may be empty for a short image: */ + if (PNG_PASS_COLS(width, pass) == 0) + continue; + + startx = PNG_PASS_START_COL(pass); + stepx = PNG_PASS_COL_OFFSET(pass); + y = PNG_PASS_START_ROW(pass); + stepy = PNG_PASS_ROW_OFFSET(pass); + } + + else + { + y = 0; + startx = 0; + stepx = stepy = 1; + } + + for (; ylocal_row); + png_bytep outrow = first_row + y * step_row; + png_const_bytep end_row = outrow + width; + + /* Read read the libpng data into the temporary buffer. */ + png_read_row(png_ptr, inrow, NULL); + + /* Now process the row according to the processing option, note + * that the caller verifies that the format of the libpng output + * data is as required. + */ + outrow += startx; + switch (proc) + { + case PNG_CMAP_GA: + for (; outrow < end_row; outrow += stepx) + { + /* The data is always in the PNG order */ + unsigned int gray = *inrow++; + unsigned int alpha = *inrow++; + unsigned int entry; + + /* NOTE: this code is copied as a comment in + * make_ga_colormap above. Please update the + * comment if you change this code! + */ + if (alpha > 229) /* opaque */ + { + entry = (231 * gray + 128) >> 8; + } + else if (alpha < 26) /* transparent */ + { + entry = 231; + } + else /* partially opaque */ + { + entry = 226 + 6 * PNG_DIV51(alpha) + PNG_DIV51(gray); + } + + *outrow = (png_byte)entry; + } + break; + + case PNG_CMAP_TRANS: + for (; outrow < end_row; outrow += stepx) + { + png_byte gray = *inrow++; + png_byte alpha = *inrow++; + + if (alpha == 0) + *outrow = PNG_CMAP_TRANS_BACKGROUND; + + else if (gray != PNG_CMAP_TRANS_BACKGROUND) + *outrow = gray; + + else + *outrow = (png_byte)(PNG_CMAP_TRANS_BACKGROUND+1); + } + break; + + case PNG_CMAP_RGB: + for (; outrow < end_row; outrow += stepx) + { + *outrow = PNG_RGB_INDEX(inrow[0], inrow[1], inrow[2]); + inrow += 3; + } + break; + + case PNG_CMAP_RGB_ALPHA: + for (; outrow < end_row; outrow += stepx) + { + unsigned int alpha = inrow[3]; + + /* Because the alpha entries only hold alpha==0.5 values + * split the processing at alpha==0.25 (64) and 0.75 + * (196). + */ + + if (alpha >= 196) + *outrow = PNG_RGB_INDEX(inrow[0], inrow[1], + inrow[2]); + + else if (alpha < 64) + *outrow = PNG_CMAP_RGB_ALPHA_BACKGROUND; + + else + { + /* Likewise there are three entries for each of r, g + * and b. We could select the entry by popcount on + * the top two bits on those architectures that + * support it, this is what the code below does, + * crudely. + */ + unsigned int back_i = PNG_CMAP_RGB_ALPHA_BACKGROUND+1; + + /* Here are how the values map: + * + * 0x00 .. 0x3f -> 0 + * 0x40 .. 0xbf -> 1 + * 0xc0 .. 0xff -> 2 + * + * So, as above with the explicit alpha checks, the + * breakpoints are at 64 and 196. + */ + if (inrow[0] & 0x80) back_i += 9; /* red */ + if (inrow[0] & 0x40) back_i += 9; + if (inrow[0] & 0x80) back_i += 3; /* green */ + if (inrow[0] & 0x40) back_i += 3; + if (inrow[0] & 0x80) back_i += 1; /* blue */ + if (inrow[0] & 0x40) back_i += 1; + + *outrow = (png_byte)back_i; + } + + inrow += 4; + } + break; + + default: + break; + } + } + } + } + + return 1; +} + +static int +png_image_read_colormapped(png_voidp argument) +{ + png_image_read_control *display = png_voidcast(png_image_read_control*, + argument); + png_imagep image = display->image; + png_controlp control = image->opaque; + png_structrp png_ptr = control->png_ptr; + png_inforp info_ptr = control->info_ptr; + + int passes = 0; /* As a flag */ + + PNG_SKIP_CHUNKS(png_ptr); + + /* Update the 'info' structure and make sure the result is as required; first + * make sure to turn on the interlace handling if it will be required + * (because it can't be turned on *after* the call to png_read_update_info!) + */ + if (display->colormap_processing == PNG_CMAP_NONE) + passes = png_set_interlace_handling(png_ptr); + + png_read_update_info(png_ptr, info_ptr); + + /* The expected output can be deduced from the colormap_processing option. */ + switch (display->colormap_processing) + { + case PNG_CMAP_NONE: + /* Output must be one channel and one byte per pixel, the output + * encoding can be anything. + */ + if ((info_ptr->color_type == PNG_COLOR_TYPE_PALETTE || + info_ptr->color_type == PNG_COLOR_TYPE_GRAY) && + info_ptr->bit_depth == 8) + break; + + goto bad_output; + + case PNG_CMAP_TRANS: + case PNG_CMAP_GA: + /* Output must be two channels and the 'G' one must be sRGB, the latter + * can be checked with an exact number because it should have been set + * to this number above! + */ + if (info_ptr->color_type == PNG_COLOR_TYPE_GRAY_ALPHA && + info_ptr->bit_depth == 8 && + png_ptr->screen_gamma == PNG_GAMMA_sRGB && + image->colormap_entries == 256) + break; + + goto bad_output; + + case PNG_CMAP_RGB: + /* Output must be 8-bit sRGB encoded RGB */ + if (info_ptr->color_type == PNG_COLOR_TYPE_RGB && + info_ptr->bit_depth == 8 && + png_ptr->screen_gamma == PNG_GAMMA_sRGB && + image->colormap_entries == 216) + break; + + goto bad_output; + + case PNG_CMAP_RGB_ALPHA: + /* Output must be 8-bit sRGB encoded RGBA */ + if (info_ptr->color_type == PNG_COLOR_TYPE_RGB_ALPHA && + info_ptr->bit_depth == 8 && + png_ptr->screen_gamma == PNG_GAMMA_sRGB && + image->colormap_entries == 244 /* 216 + 1 + 27 */) + break; + + /* goto bad_output; */ + /* FALL THROUGH */ + + default: + bad_output: + png_error(png_ptr, "bad color-map processing (internal error)"); + } + + /* Now read the rows. Do this here if it is possible to read directly into + * the output buffer, otherwise allocate a local row buffer of the maximum + * size libpng requires and call the relevant processing routine safely. + */ + { + png_voidp first_row = display->buffer; + ptrdiff_t row_bytes = display->row_stride; + + /* The following expression is designed to work correctly whether it gives + * a signed or an unsigned result. + */ + if (row_bytes < 0) + { + char *ptr = png_voidcast(char*, first_row); + ptr += (image->height-1) * (-row_bytes); + first_row = png_voidcast(png_voidp, ptr); + } + + display->first_row = first_row; + display->row_bytes = row_bytes; + } + + if (passes == 0) + { + int result; + png_voidp row = png_malloc(png_ptr, png_get_rowbytes(png_ptr, info_ptr)); + + display->local_row = row; + result = png_safe_execute(image, png_image_read_and_map, display); + display->local_row = NULL; + png_free(png_ptr, row); + + return result; + } + + else + { + png_alloc_size_t row_bytes = display->row_bytes; + + while (--passes >= 0) + { + png_uint_32 y = image->height; + png_bytep row = png_voidcast(png_bytep, display->first_row); + + while (y-- > 0) + { + png_read_row(png_ptr, row, NULL); + row += row_bytes; + } + } + + return 1; + } +} + +/* Just the row reading part of png_image_read. */ +static int +png_image_read_composite(png_voidp argument) +{ + png_image_read_control *display = png_voidcast(png_image_read_control*, + argument); + png_imagep image = display->image; + png_structrp png_ptr = image->opaque->png_ptr; + int passes; + + switch (png_ptr->interlaced) + { + case PNG_INTERLACE_NONE: + passes = 1; + break; + + case PNG_INTERLACE_ADAM7: + passes = PNG_INTERLACE_ADAM7_PASSES; + break; + + default: + passes = 0; + png_error(png_ptr, "unknown interlace type"); + } + + { + png_uint_32 height = image->height; + png_uint_32 width = image->width; + ptrdiff_t step_row = display->row_bytes; + unsigned int channels = (image->format & PNG_FORMAT_FLAG_COLOR) ? 3 : 1; + int pass; + + for (pass = 0; pass < passes; ++pass) + { + unsigned int startx, stepx, stepy; + png_uint_32 y; + + if (png_ptr->interlaced == PNG_INTERLACE_ADAM7) + { + /* The row may be empty for a short image: */ + if (PNG_PASS_COLS(width, pass) == 0) + continue; + + startx = PNG_PASS_START_COL(pass) * channels; + stepx = PNG_PASS_COL_OFFSET(pass) * channels; + y = PNG_PASS_START_ROW(pass); + stepy = PNG_PASS_ROW_OFFSET(pass); + } + + else + { + y = 0; + startx = 0; + stepx = channels; + stepy = 1; + } + + for (; ylocal_row); + png_bytep outrow; + png_const_bytep end_row; + + /* Read the row, which is packed: */ + png_read_row(png_ptr, inrow, NULL); + + outrow = png_voidcast(png_bytep, display->first_row); + outrow += y * step_row; + end_row = outrow + width * channels; + + /* Now do the composition on each pixel in this row. */ + outrow += startx; + for (; outrow < end_row; outrow += stepx) + { + png_byte alpha = inrow[channels]; + + if (alpha > 0) /* else no change to the output */ + { + unsigned int c; + + for (c=0; cimage; + png_structrp png_ptr = image->opaque->png_ptr; + png_inforp info_ptr = image->opaque->info_ptr; + png_uint_32 height = image->height; + png_uint_32 width = image->width; + int pass, passes; + + /* Double check the convoluted logic below. We expect to get here with + * libpng doing rgb to gray and gamma correction but background processing + * left to the png_image_read_background function. The rows libpng produce + * might be 8 or 16-bit but should always have two channels; gray plus alpha. + */ + if ((png_ptr->transformations & PNG_RGB_TO_GRAY) == 0) + png_error(png_ptr, "lost rgb to gray"); + + if ((png_ptr->transformations & PNG_COMPOSE) != 0) + png_error(png_ptr, "unexpected compose"); + + if (png_get_channels(png_ptr, info_ptr) != 2) + png_error(png_ptr, "lost/gained channels"); + + /* Expect the 8-bit case to always remove the alpha channel */ + if ((image->format & PNG_FORMAT_FLAG_LINEAR) == 0 && + (image->format & PNG_FORMAT_FLAG_ALPHA) != 0) + png_error(png_ptr, "unexpected 8-bit transformation"); + + switch (png_ptr->interlaced) + { + case PNG_INTERLACE_NONE: + passes = 1; + break; + + case PNG_INTERLACE_ADAM7: + passes = PNG_INTERLACE_ADAM7_PASSES; + break; + + default: + passes = 0; + png_error(png_ptr, "unknown interlace type"); + } + + switch (png_get_bit_depth(png_ptr, info_ptr)) + { + default: + png_error(png_ptr, "unexpected bit depth"); + break; + + case 8: + /* 8-bit sRGB gray values with an alpha channel; the alpha channel is + * to be removed by composing on a backgroundi: either the row if + * display->background is NULL or display->background->green if not. + * Unlike the code above ALPHA_OPTIMIZED has *not* been done. + */ + { + png_bytep first_row = png_voidcast(png_bytep, display->first_row); + ptrdiff_t step_row = display->row_bytes; + + for (pass = 0; pass < passes; ++pass) + { + png_bytep row = png_voidcast(png_bytep, + display->first_row); + unsigned int startx, stepx, stepy; + png_uint_32 y; + + if (png_ptr->interlaced == PNG_INTERLACE_ADAM7) + { + /* The row may be empty for a short image: */ + if (PNG_PASS_COLS(width, pass) == 0) + continue; + + startx = PNG_PASS_START_COL(pass); + stepx = PNG_PASS_COL_OFFSET(pass); + y = PNG_PASS_START_ROW(pass); + stepy = PNG_PASS_ROW_OFFSET(pass); + } + + else + { + y = 0; + startx = 0; + stepx = stepy = 1; + } + + if (display->background == NULL) + { + for (; ylocal_row); + png_bytep outrow = first_row + y * step_row; + png_const_bytep end_row = outrow + width; + + /* Read the row, which is packed: */ + png_read_row(png_ptr, inrow, NULL); + + /* Now do the composition on each pixel in this row. */ + outrow += startx; + for (; outrow < end_row; outrow += stepx) + { + png_byte alpha = inrow[1]; + + if (alpha > 0) /* else no change to the output */ + { + png_uint_32 component = inrow[0]; + + if (alpha < 255) /* else just use component */ + { + /* Since PNG_OPTIMIZED_ALPHA was not set it is + * necessary to invert the sRGB transfer + * function and multiply the alpha out. + */ + component = png_sRGB_table[component] * alpha; + component += png_sRGB_table[outrow[0]] * + (255-alpha); + component = PNG_sRGB_FROM_LINEAR(component); + } + + outrow[0] = (png_byte)component; + } + + inrow += 2; /* gray and alpha channel */ + } + } + } + + else /* constant background value */ + { + png_byte background8 = display->background->green; + png_uint_16 background = png_sRGB_table[background8]; + + for (; ylocal_row); + png_bytep outrow = first_row + y * step_row; + png_const_bytep end_row = outrow + width; + + /* Read the row, which is packed: */ + png_read_row(png_ptr, inrow, NULL); + + /* Now do the composition on each pixel in this row. */ + outrow += startx; + for (; outrow < end_row; outrow += stepx) + { + png_byte alpha = inrow[1]; + + if (alpha > 0) /* else use background */ + { + png_uint_32 component = inrow[0]; + + if (alpha < 255) /* else just use component */ + { + component = png_sRGB_table[component] * alpha; + component += background * (255-alpha); + component = PNG_sRGB_FROM_LINEAR(component); + } + + outrow[0] = (png_byte)component; + } + + else + outrow[0] = background8; + + inrow += 2; /* gray and alpha channel */ + } + + row += display->row_bytes; + } + } + } + } + break; + + case 16: + /* 16-bit linear with pre-multiplied alpha; the pre-multiplication must + * still be done and, maybe, the alpha channel removed. This code also + * handles the alpha-first option. + */ + { + png_uint_16p first_row = png_voidcast(png_uint_16p, + display->first_row); + /* The division by two is safe because the caller passed in a + * stride which was multiplied by 2 (below) to get row_bytes. + */ + ptrdiff_t step_row = display->row_bytes / 2; + int preserve_alpha = (image->format & PNG_FORMAT_FLAG_ALPHA) != 0; + unsigned int outchannels = 1+preserve_alpha; + int swap_alpha = 0; + + if (preserve_alpha && (image->format & PNG_FORMAT_FLAG_AFIRST)) + swap_alpha = 1; + + for (pass = 0; pass < passes; ++pass) + { + unsigned int startx, stepx, stepy; + png_uint_32 y; + + /* The 'x' start and step are adjusted to output components here. + */ + if (png_ptr->interlaced == PNG_INTERLACE_ADAM7) + { + /* The row may be empty for a short image: */ + if (PNG_PASS_COLS(width, pass) == 0) + continue; + + startx = PNG_PASS_START_COL(pass) * outchannels; + stepx = PNG_PASS_COL_OFFSET(pass) * outchannels; + y = PNG_PASS_START_ROW(pass); + stepy = PNG_PASS_ROW_OFFSET(pass); + } + + else + { + y = 0; + startx = 0; + stepx = outchannels; + stepy = 1; + } + + for (; ylocal_row), NULL); + inrow = png_voidcast(png_const_uint_16p, display->local_row); + + /* Now do the pre-multiplication on each pixel in this row. + */ + outrow += startx; + for (; outrow < end_row; outrow += stepx) + { + png_uint_32 component = inrow[0]; + png_uint_16 alpha = inrow[1]; + + if (alpha > 0) /* else 0 */ + { + if (alpha < 65535) /* else just use component */ + { + component *= alpha; + component += 32767; + component /= 65535; + } + } + + else + component = 0; + + outrow[swap_alpha] = (png_uint_16)component; + if (preserve_alpha) + outrow[1 ^ swap_alpha] = alpha; + + inrow += 2; /* components and alpha channel */ + } + } + } + } + break; + } + + return 1; +} + +/* The guts of png_image_finish_read as a png_safe_execute callback. */ +static int +png_image_read_direct(png_voidp argument) +{ + png_image_read_control *display = png_voidcast(png_image_read_control*, + argument); + png_imagep image = display->image; + png_structrp png_ptr = image->opaque->png_ptr; + png_inforp info_ptr = image->opaque->info_ptr; + + png_uint_32 format = image->format; + int linear = (format & PNG_FORMAT_FLAG_LINEAR) != 0; + int do_local_compose = 0; + int do_local_background = 0; /* to avoid double gamma correction bug */ + int passes = 0; + + /* Add transforms to ensure the correct output format is produced then check + * that the required implementation support is there. Always expand; always + * need 8 bits minimum, no palette and expanded tRNS. + */ + png_set_expand(png_ptr); + + /* Now check the format to see if it was modified. */ + { + png_uint_32 base_format = png_image_format(png_ptr) & + ~PNG_FORMAT_FLAG_COLORMAP /* removed by png_set_expand */; + png_uint_32 change = format ^ base_format; + png_fixed_point output_gamma; + int mode; /* alpha mode */ + + /* Do this first so that we have a record if rgb to gray is happening. */ + if (change & PNG_FORMAT_FLAG_COLOR) + { + /* gray<->color transformation required. */ + if (format & PNG_FORMAT_FLAG_COLOR) + png_set_gray_to_rgb(png_ptr); + + else + { + /* libpng can't do both rgb to gray and + * background/pre-multiplication if there is also significant gamma + * correction, because both operations require linear colors and + * the code only supports one transform doing the gamma correction. + * Handle this by doing the pre-multiplication or background + * operation in this code, if necessary. + * + * TODO: fix this by rewriting pngrtran.c (!) + * + * For the moment (given that fixing this in pngrtran.c is an + * enormous change) 'do_local_background' is used to indicate that + * the problem exists. + */ + if (base_format & PNG_FORMAT_FLAG_ALPHA) + do_local_background = 1/*maybe*/; + + png_set_rgb_to_gray_fixed(png_ptr, PNG_ERROR_ACTION_NONE, + PNG_RGB_TO_GRAY_DEFAULT, PNG_RGB_TO_GRAY_DEFAULT); + } + + change &= ~PNG_FORMAT_FLAG_COLOR; + } + + /* Set the gamma appropriately, linear for 16-bit input, sRGB otherwise. + */ + { + png_fixed_point input_gamma_default; + + if ((base_format & PNG_FORMAT_FLAG_LINEAR) && + (image->flags & PNG_IMAGE_FLAG_16BIT_sRGB) == 0) + input_gamma_default = PNG_GAMMA_LINEAR; + else + input_gamma_default = PNG_DEFAULT_sRGB; + + /* Call png_set_alpha_mode to set the default for the input gamma; the + * output gamma is set by a second call below. + */ + png_set_alpha_mode_fixed(png_ptr, PNG_ALPHA_PNG, input_gamma_default); + } + + if (linear) + { + /* If there *is* an alpha channel in the input it must be multiplied + * out; use PNG_ALPHA_STANDARD, otherwise just use PNG_ALPHA_PNG. + */ + if (base_format & PNG_FORMAT_FLAG_ALPHA) + mode = PNG_ALPHA_STANDARD; /* associated alpha */ + + else + mode = PNG_ALPHA_PNG; + + output_gamma = PNG_GAMMA_LINEAR; + } + + else + { + mode = PNG_ALPHA_PNG; + output_gamma = PNG_DEFAULT_sRGB; + } + + /* If 'do_local_background' is set check for the presence of gamma + * correction; this is part of the work-round for the libpng bug + * described above. + * + * TODO: fix libpng and remove this. + */ + if (do_local_background) + { + png_fixed_point gtest; + + /* This is 'png_gamma_threshold' from pngrtran.c; the test used for + * gamma correction, the screen gamma hasn't been set on png_struct + * yet; it's set below. png_struct::gamma, however, is set to the + * final value. + */ + if (png_muldiv(>est, output_gamma, png_ptr->colorspace.gamma, + PNG_FP_1) && !png_gamma_significant(gtest)) + do_local_background = 0; + + else if (mode == PNG_ALPHA_STANDARD) + { + do_local_background = 2/*required*/; + mode = PNG_ALPHA_PNG; /* prevent libpng doing it */ + } + + /* else leave as 1 for the checks below */ + } + + /* If the bit-depth changes then handle that here. */ + if (change & PNG_FORMAT_FLAG_LINEAR) + { + if (linear /*16-bit output*/) + png_set_expand_16(png_ptr); + + else /* 8-bit output */ + png_set_scale_16(png_ptr); + + change &= ~PNG_FORMAT_FLAG_LINEAR; + } + + /* Now the background/alpha channel changes. */ + if (change & PNG_FORMAT_FLAG_ALPHA) + { + /* Removing an alpha channel requires composition for the 8-bit + * formats; for the 16-bit it is already done, above, by the + * pre-multiplication and the channel just needs to be stripped. + */ + if (base_format & PNG_FORMAT_FLAG_ALPHA) + { + /* If RGB->gray is happening the alpha channel must be left and the + * operation completed locally. + * + * TODO: fix libpng and remove this. + */ + if (do_local_background) + do_local_background = 2/*required*/; + + /* 16-bit output: just remove the channel */ + else if (linear) /* compose on black (well, pre-multiply) */ + png_set_strip_alpha(png_ptr); + + /* 8-bit output: do an appropriate compose */ + else if (display->background != NULL) + { + png_color_16 c; + + c.index = 0; /*unused*/ + c.red = display->background->red; + c.green = display->background->green; + c.blue = display->background->blue; + c.gray = display->background->green; + + /* This is always an 8-bit sRGB value, using the 'green' channel + * for gray is much better than calculating the luminance here; + * we can get off-by-one errors in that calculation relative to + * the app expectations and that will show up in transparent + * pixels. + */ + png_set_background_fixed(png_ptr, &c, + PNG_BACKGROUND_GAMMA_SCREEN, 0/*need_expand*/, + 0/*gamma: not used*/); + } + + else /* compose on row: implemented below. */ + { + do_local_compose = 1; + /* This leaves the alpha channel in the output, so it has to be + * removed by the code below. Set the encoding to the 'OPTIMIZE' + * one so the code only has to hack on the pixels that require + * composition. + */ + mode = PNG_ALPHA_OPTIMIZED; + } + } + + else /* output needs an alpha channel */ + { + /* This is tricky because it happens before the swap operation has + * been accomplished; however, the swap does *not* swap the added + * alpha channel (weird API), so it must be added in the correct + * place. + */ + png_uint_32 filler; /* opaque filler */ + int where; + + if (linear) + filler = 65535; + + else + filler = 255; + +# ifdef PNG_FORMAT_AFIRST_SUPPORTED + if (format & PNG_FORMAT_FLAG_AFIRST) + { + where = PNG_FILLER_BEFORE; + change &= ~PNG_FORMAT_FLAG_AFIRST; + } + + else +# endif + where = PNG_FILLER_AFTER; + + png_set_add_alpha(png_ptr, filler, where); + } + + /* This stops the (irrelevant) call to swap_alpha below. */ + change &= ~PNG_FORMAT_FLAG_ALPHA; + } + + /* Now set the alpha mode correctly; this is always done, even if there is + * no alpha channel in either the input or the output because it correctly + * sets the output gamma. + */ + png_set_alpha_mode_fixed(png_ptr, mode, output_gamma); + +# ifdef PNG_FORMAT_BGR_SUPPORTED + if (change & PNG_FORMAT_FLAG_BGR) + { + /* Check only the output format; PNG is never BGR; don't do this if + * the output is gray, but fix up the 'format' value in that case. + */ + if (format & PNG_FORMAT_FLAG_COLOR) + png_set_bgr(png_ptr); + + else + format &= ~PNG_FORMAT_FLAG_BGR; + + change &= ~PNG_FORMAT_FLAG_BGR; + } +# endif + +# ifdef PNG_FORMAT_AFIRST_SUPPORTED + if (change & PNG_FORMAT_FLAG_AFIRST) + { + /* Only relevant if there is an alpha channel - it's particularly + * important to handle this correctly because do_local_compose may + * be set above and then libpng will keep the alpha channel for this + * code to remove. + */ + if (format & PNG_FORMAT_FLAG_ALPHA) + { + /* Disable this if doing a local background, + * TODO: remove this when local background is no longer required. + */ + if (do_local_background != 2) + png_set_swap_alpha(png_ptr); + } + + else + format &= ~PNG_FORMAT_FLAG_AFIRST; + + change &= ~PNG_FORMAT_FLAG_AFIRST; + } +# endif + + /* If the *output* is 16-bit then we need to check for a byte-swap on this + * architecture. + */ + if (linear) + { + PNG_CONST png_uint_16 le = 0x0001; + + if (*(png_const_bytep)&le) + png_set_swap(png_ptr); + } + + /* If change is not now 0 some transformation is missing - error out. */ + if (change) + png_error(png_ptr, "png_read_image: unsupported transformation"); + } + + PNG_SKIP_CHUNKS(png_ptr); + + /* Update the 'info' structure and make sure the result is as required; first + * make sure to turn on the interlace handling if it will be required + * (because it can't be turned on *after* the call to png_read_update_info!) + * + * TODO: remove the do_local_background fixup below. + */ + if (!do_local_compose && do_local_background != 2) + passes = png_set_interlace_handling(png_ptr); + + png_read_update_info(png_ptr, info_ptr); + + { + png_uint_32 info_format = 0; + + if (info_ptr->color_type & PNG_COLOR_MASK_COLOR) + info_format |= PNG_FORMAT_FLAG_COLOR; + + if (info_ptr->color_type & PNG_COLOR_MASK_ALPHA) + { + /* do_local_compose removes this channel below. */ + if (!do_local_compose) + { + /* do_local_background does the same if required. */ + if (do_local_background != 2 || + (format & PNG_FORMAT_FLAG_ALPHA) != 0) + info_format |= PNG_FORMAT_FLAG_ALPHA; + } + } + + else if (do_local_compose) /* internal error */ + png_error(png_ptr, "png_image_read: alpha channel lost"); + + if (info_ptr->bit_depth == 16) + info_format |= PNG_FORMAT_FLAG_LINEAR; + +# ifdef PNG_FORMAT_BGR_SUPPORTED + if (png_ptr->transformations & PNG_BGR) + info_format |= PNG_FORMAT_FLAG_BGR; +# endif + +# ifdef PNG_FORMAT_AFIRST_SUPPORTED + if (do_local_background == 2) + { + if (format & PNG_FORMAT_FLAG_AFIRST) + info_format |= PNG_FORMAT_FLAG_AFIRST; + } + + if ((png_ptr->transformations & PNG_SWAP_ALPHA) != 0 || + ((png_ptr->transformations & PNG_ADD_ALPHA) != 0 && + (png_ptr->flags & PNG_FLAG_FILLER_AFTER) == 0)) + { + if (do_local_background == 2) + png_error(png_ptr, "unexpected alpha swap transformation"); + + info_format |= PNG_FORMAT_FLAG_AFIRST; + } +# endif + + /* This is actually an internal error. */ + if (info_format != format) + png_error(png_ptr, "png_read_image: invalid transformations"); + } + + /* Now read the rows. If do_local_compose is set then it is necessary to use + * a local row buffer. The output will be GA, RGBA or BGRA and must be + * converted to G, RGB or BGR as appropriate. The 'local_row' member of the + * display acts as a flag. + */ + { + png_voidp first_row = display->buffer; + ptrdiff_t row_bytes = display->row_stride; + + if (linear) + row_bytes *= 2; + + /* The following expression is designed to work correctly whether it gives + * a signed or an unsigned result. + */ + if (row_bytes < 0) + { + char *ptr = png_voidcast(char*, first_row); + ptr += (image->height-1) * (-row_bytes); + first_row = png_voidcast(png_voidp, ptr); + } + + display->first_row = first_row; + display->row_bytes = row_bytes; + } + + if (do_local_compose) + { + int result; + png_voidp row = png_malloc(png_ptr, png_get_rowbytes(png_ptr, info_ptr)); + + display->local_row = row; + result = png_safe_execute(image, png_image_read_composite, display); + display->local_row = NULL; + png_free(png_ptr, row); + + return result; + } + + else if (do_local_background == 2) + { + int result; + png_voidp row = png_malloc(png_ptr, png_get_rowbytes(png_ptr, info_ptr)); + + display->local_row = row; + result = png_safe_execute(image, png_image_read_background, display); + display->local_row = NULL; + png_free(png_ptr, row); + + return result; + } + + else + { + png_alloc_size_t row_bytes = display->row_bytes; + + while (--passes >= 0) + { + png_uint_32 y = image->height; + png_bytep row = png_voidcast(png_bytep, display->first_row); + + while (y-- > 0) + { + png_read_row(png_ptr, row, NULL); + row += row_bytes; + } + } + + return 1; + } +} + +int PNGAPI +png_image_finish_read(png_imagep image, png_const_colorp background, + void *buffer, png_int_32 row_stride, void *colormap) +{ + if (image != NULL && image->version == PNG_IMAGE_VERSION) + { + png_uint_32 check; + + if (row_stride == 0) + row_stride = PNG_IMAGE_ROW_STRIDE(*image); + + if (row_stride < 0) + check = -row_stride; + + else + check = row_stride; + + if (image->opaque != NULL && buffer != NULL && + check >= PNG_IMAGE_ROW_STRIDE(*image)) + { + if ((image->format & PNG_FORMAT_FLAG_COLORMAP) == 0 || + (image->colormap_entries > 0 && colormap != NULL)) + { + int result; + png_image_read_control display; + + memset(&display, 0, (sizeof display)); + display.image = image; + display.buffer = buffer; + display.row_stride = row_stride; + display.colormap = colormap; + display.background = background; + display.local_row = NULL; + + /* Choose the correct 'end' routine; for the color-map case all the + * setup has already been done. + */ + if (image->format & PNG_FORMAT_FLAG_COLORMAP) + result = + png_safe_execute(image, png_image_read_colormap, &display) && + png_safe_execute(image, png_image_read_colormapped, &display); + + else + result = + png_safe_execute(image, png_image_read_direct, &display); + + png_image_free(image); + return result; + } + + else + return png_image_error(image, + "png_image_finish_read[color-map]: no color-map"); + } + + else + return png_image_error(image, + "png_image_finish_read: invalid argument"); + } + + else if (image != NULL) + return png_image_error(image, + "png_image_finish_read: damaged PNG_IMAGE_VERSION"); + + return 0; +} + +#endif /* PNG_SIMPLIFIED_READ_SUPPORTED */ +#endif /* PNG_READ_SUPPORTED */ diff --git a/Minecraft.Client/Windows64/4JLibs/inc/Render/libpng/pngrio.c b/Minecraft.Client/Windows64/4JLibs/inc/Render/libpng/pngrio.c new file mode 100644 index 000000000..d7864407b --- /dev/null +++ b/Minecraft.Client/Windows64/4JLibs/inc/Render/libpng/pngrio.c @@ -0,0 +1,118 @@ + +/* pngrio.c - functions for data input + * + * Last changed in libpng 1.6.0 [February 14, 2013] + * Copyright (c) 1998-2013 Glenn Randers-Pehrson + * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) + * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) + * + * This code is released under the libpng license. + * For conditions of distribution and use, see the disclaimer + * and license in png.h + * + * This file provides a location for all input. Users who need + * special handling are expected to write a function that has the same + * arguments as this and performs a similar function, but that possibly + * has a different input method. Note that you shouldn't change this + * function, but rather write a replacement function and then make + * libpng use it at run time with png_set_read_fn(...). + */ + +#include "pngpriv.h" + +#ifdef PNG_READ_SUPPORTED + +/* Read the data from whatever input you are using. The default routine + * reads from a file pointer. Note that this routine sometimes gets called + * with very small lengths, so you should implement some kind of simple + * buffering if you are using unbuffered reads. This should never be asked + * to read more then 64K on a 16 bit machine. + */ +void /* PRIVATE */ +png_read_data(png_structrp png_ptr, png_bytep data, png_size_t length) +{ + png_debug1(4, "reading %d bytes", (int)length); + + if (png_ptr->read_data_fn != NULL) + (*(png_ptr->read_data_fn))(png_ptr, data, length); + + else + png_error(png_ptr, "Call to NULL read function"); +} + +#ifdef PNG_STDIO_SUPPORTED +/* This is the function that does the actual reading of data. If you are + * not reading from a standard C stream, you should create a replacement + * read_data function and use it at run time with png_set_read_fn(), rather + * than changing the library. + */ +void PNGCBAPI +png_default_read_data(png_structp png_ptr, png_bytep data, png_size_t length) +{ + png_size_t check; + + if (png_ptr == NULL) + return; + + /* fread() returns 0 on error, so it is OK to store this in a png_size_t + * instead of an int, which is what fread() actually returns. + */ + check = fread(data, 1, length, png_voidcast(png_FILE_p, png_ptr->io_ptr)); + + if (check != length) + png_error(png_ptr, "Read Error"); +} +#endif + +/* This function allows the application to supply a new input function + * for libpng if standard C streams aren't being used. + * + * This function takes as its arguments: + * + * png_ptr - pointer to a png input data structure + * + * io_ptr - pointer to user supplied structure containing info about + * the input functions. May be NULL. + * + * read_data_fn - pointer to a new input function that takes as its + * arguments a pointer to a png_struct, a pointer to + * a location where input data can be stored, and a 32-bit + * unsigned int that is the number of bytes to be read. + * To exit and output any fatal error messages the new write + * function should call png_error(png_ptr, "Error msg"). + * May be NULL, in which case libpng's default function will + * be used. + */ +void PNGAPI +png_set_read_fn(png_structrp png_ptr, png_voidp io_ptr, + png_rw_ptr read_data_fn) +{ + if (png_ptr == NULL) + return; + + png_ptr->io_ptr = io_ptr; + +#ifdef PNG_STDIO_SUPPORTED + if (read_data_fn != NULL) + png_ptr->read_data_fn = read_data_fn; + + else + png_ptr->read_data_fn = png_default_read_data; +#else + png_ptr->read_data_fn = read_data_fn; +#endif + + /* It is an error to write to a read device */ + if (png_ptr->write_data_fn != NULL) + { + png_ptr->write_data_fn = NULL; + png_warning(png_ptr, + "Can't set both read_data_fn and write_data_fn in the" + " same structure"); + } + +#ifdef PNG_WRITE_FLUSH_SUPPORTED + png_ptr->output_flush_fn = NULL; +#endif +} +#endif /* PNG_READ_SUPPORTED */ diff --git a/Minecraft.Client/Windows64/4JLibs/inc/Render/libpng/pngrtran.c b/Minecraft.Client/Windows64/4JLibs/inc/Render/libpng/pngrtran.c new file mode 100644 index 000000000..e378ceba4 --- /dev/null +++ b/Minecraft.Client/Windows64/4JLibs/inc/Render/libpng/pngrtran.c @@ -0,0 +1,5101 @@ + +/* pngrtran.c - transforms the data in a row for PNG readers + * + * Last changed in libpng 1.6.2 [April 25, 2013] + * Copyright (c) 1998-2013 Glenn Randers-Pehrson + * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) + * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) + * + * This code is released under the libpng license. + * For conditions of distribution and use, see the disclaimer + * and license in png.h + * + * This file contains functions optionally called by an application + * in order to tell libpng how to handle data when reading a PNG. + * Transformations that are used in both reading and writing are + * in pngtrans.c. + */ + +#include "pngpriv.h" + +#ifdef PNG_READ_SUPPORTED + +/* Set the action on getting a CRC error for an ancillary or critical chunk. */ +void PNGAPI +png_set_crc_action(png_structrp png_ptr, int crit_action, int ancil_action) +{ + png_debug(1, "in png_set_crc_action"); + + if (png_ptr == NULL) + return; + + /* Tell libpng how we react to CRC errors in critical chunks */ + switch (crit_action) + { + case PNG_CRC_NO_CHANGE: /* Leave setting as is */ + break; + + case PNG_CRC_WARN_USE: /* Warn/use data */ + png_ptr->flags &= ~PNG_FLAG_CRC_CRITICAL_MASK; + png_ptr->flags |= PNG_FLAG_CRC_CRITICAL_USE; + break; + + case PNG_CRC_QUIET_USE: /* Quiet/use data */ + png_ptr->flags &= ~PNG_FLAG_CRC_CRITICAL_MASK; + png_ptr->flags |= PNG_FLAG_CRC_CRITICAL_USE | + PNG_FLAG_CRC_CRITICAL_IGNORE; + break; + + case PNG_CRC_WARN_DISCARD: /* Not a valid action for critical data */ + png_warning(png_ptr, + "Can't discard critical data on CRC error"); + case PNG_CRC_ERROR_QUIT: /* Error/quit */ + + case PNG_CRC_DEFAULT: + default: + png_ptr->flags &= ~PNG_FLAG_CRC_CRITICAL_MASK; + break; + } + + /* Tell libpng how we react to CRC errors in ancillary chunks */ + switch (ancil_action) + { + case PNG_CRC_NO_CHANGE: /* Leave setting as is */ + break; + + case PNG_CRC_WARN_USE: /* Warn/use data */ + png_ptr->flags &= ~PNG_FLAG_CRC_ANCILLARY_MASK; + png_ptr->flags |= PNG_FLAG_CRC_ANCILLARY_USE; + break; + + case PNG_CRC_QUIET_USE: /* Quiet/use data */ + png_ptr->flags &= ~PNG_FLAG_CRC_ANCILLARY_MASK; + png_ptr->flags |= PNG_FLAG_CRC_ANCILLARY_USE | + PNG_FLAG_CRC_ANCILLARY_NOWARN; + break; + + case PNG_CRC_ERROR_QUIT: /* Error/quit */ + png_ptr->flags &= ~PNG_FLAG_CRC_ANCILLARY_MASK; + png_ptr->flags |= PNG_FLAG_CRC_ANCILLARY_NOWARN; + break; + + case PNG_CRC_WARN_DISCARD: /* Warn/discard data */ + + case PNG_CRC_DEFAULT: + default: + png_ptr->flags &= ~PNG_FLAG_CRC_ANCILLARY_MASK; + break; + } +} + +#ifdef PNG_READ_TRANSFORMS_SUPPORTED +/* Is it OK to set a transformation now? Only if png_start_read_image or + * png_read_update_info have not been called. It is not necessary for the IHDR + * to have been read in all cases, the parameter allows for this check too. + */ +static int +png_rtran_ok(png_structrp png_ptr, int need_IHDR) +{ + if (png_ptr != NULL) + { + if (png_ptr->flags & PNG_FLAG_ROW_INIT) + png_app_error(png_ptr, + "invalid after png_start_read_image or png_read_update_info"); + + else if (need_IHDR && (png_ptr->mode & PNG_HAVE_IHDR) == 0) + png_app_error(png_ptr, "invalid before the PNG header has been read"); + + else + { + /* Turn on failure to initialize correctly for all transforms. */ + png_ptr->flags |= PNG_FLAG_DETECT_UNINITIALIZED; + + return 1; /* Ok */ + } + } + + return 0; /* no png_error possible! */ +} +#endif + +#ifdef PNG_READ_BACKGROUND_SUPPORTED +/* Handle alpha and tRNS via a background color */ +void PNGFAPI +png_set_background_fixed(png_structrp png_ptr, + png_const_color_16p background_color, int background_gamma_code, + int need_expand, png_fixed_point background_gamma) +{ + png_debug(1, "in png_set_background_fixed"); + + if (!png_rtran_ok(png_ptr, 0) || background_color == NULL) + return; + + if (background_gamma_code == PNG_BACKGROUND_GAMMA_UNKNOWN) + { + png_warning(png_ptr, "Application must supply a known background gamma"); + return; + } + + png_ptr->transformations |= PNG_COMPOSE | PNG_STRIP_ALPHA; + png_ptr->transformations &= ~PNG_ENCODE_ALPHA; + png_ptr->flags &= ~PNG_FLAG_OPTIMIZE_ALPHA; + + png_ptr->background = *background_color; + png_ptr->background_gamma = background_gamma; + png_ptr->background_gamma_type = (png_byte)(background_gamma_code); + if (need_expand) + png_ptr->transformations |= PNG_BACKGROUND_EXPAND; + else + png_ptr->transformations &= ~PNG_BACKGROUND_EXPAND; +} + +# ifdef PNG_FLOATING_POINT_SUPPORTED +void PNGAPI +png_set_background(png_structrp png_ptr, + png_const_color_16p background_color, int background_gamma_code, + int need_expand, double background_gamma) +{ + png_set_background_fixed(png_ptr, background_color, background_gamma_code, + need_expand, png_fixed(png_ptr, background_gamma, "png_set_background")); +} +# endif /* FLOATING_POINT */ +#endif /* READ_BACKGROUND */ + +/* Scale 16-bit depth files to 8-bit depth. If both of these are set then the + * one that pngrtran does first (scale) happens. This is necessary to allow the + * TRANSFORM and API behavior to be somewhat consistent, and it's simpler. + */ +#ifdef PNG_READ_SCALE_16_TO_8_SUPPORTED +void PNGAPI +png_set_scale_16(png_structrp png_ptr) +{ + png_debug(1, "in png_set_scale_16"); + + if (!png_rtran_ok(png_ptr, 0)) + return; + + png_ptr->transformations |= PNG_SCALE_16_TO_8; +} +#endif + +#ifdef PNG_READ_STRIP_16_TO_8_SUPPORTED +/* Chop 16-bit depth files to 8-bit depth */ +void PNGAPI +png_set_strip_16(png_structrp png_ptr) +{ + png_debug(1, "in png_set_strip_16"); + + if (!png_rtran_ok(png_ptr, 0)) + return; + + png_ptr->transformations |= PNG_16_TO_8; +} +#endif + +#ifdef PNG_READ_STRIP_ALPHA_SUPPORTED +void PNGAPI +png_set_strip_alpha(png_structrp png_ptr) +{ + png_debug(1, "in png_set_strip_alpha"); + + if (!png_rtran_ok(png_ptr, 0)) + return; + + png_ptr->transformations |= PNG_STRIP_ALPHA; +} +#endif + +#if defined(PNG_READ_ALPHA_MODE_SUPPORTED) || defined(PNG_READ_GAMMA_SUPPORTED) +static png_fixed_point +translate_gamma_flags(png_structrp png_ptr, png_fixed_point output_gamma, + int is_screen) +{ + /* Check for flag values. The main reason for having the old Mac value as a + * flag is that it is pretty near impossible to work out what the correct + * value is from Apple documentation - a working Mac system is needed to + * discover the value! + */ + if (output_gamma == PNG_DEFAULT_sRGB || + output_gamma == PNG_FP_1 / PNG_DEFAULT_sRGB) + { + /* If there is no sRGB support this just sets the gamma to the standard + * sRGB value. (This is a side effect of using this function!) + */ +# ifdef PNG_READ_sRGB_SUPPORTED + png_ptr->flags |= PNG_FLAG_ASSUME_sRGB; +# else + PNG_UNUSED(png_ptr) +# endif + if (is_screen) + output_gamma = PNG_GAMMA_sRGB; + else + output_gamma = PNG_GAMMA_sRGB_INVERSE; + } + + else if (output_gamma == PNG_GAMMA_MAC_18 || + output_gamma == PNG_FP_1 / PNG_GAMMA_MAC_18) + { + if (is_screen) + output_gamma = PNG_GAMMA_MAC_OLD; + else + output_gamma = PNG_GAMMA_MAC_INVERSE; + } + + return output_gamma; +} + +# ifdef PNG_FLOATING_POINT_SUPPORTED +static png_fixed_point +convert_gamma_value(png_structrp png_ptr, double output_gamma) +{ + /* The following silently ignores cases where fixed point (times 100,000) + * gamma values are passed to the floating point API. This is safe and it + * means the fixed point constants work just fine with the floating point + * API. The alternative would just lead to undetected errors and spurious + * bug reports. Negative values fail inside the _fixed API unless they + * correspond to the flag values. + */ + if (output_gamma > 0 && output_gamma < 128) + output_gamma *= PNG_FP_1; + + /* This preserves -1 and -2 exactly: */ + output_gamma = floor(output_gamma + .5); + + if (output_gamma > PNG_FP_MAX || output_gamma < PNG_FP_MIN) + png_fixed_error(png_ptr, "gamma value"); + + return (png_fixed_point)output_gamma; +} +# endif +#endif /* READ_ALPHA_MODE || READ_GAMMA */ + +#ifdef PNG_READ_ALPHA_MODE_SUPPORTED +void PNGFAPI +png_set_alpha_mode_fixed(png_structrp png_ptr, int mode, + png_fixed_point output_gamma) +{ + int compose = 0; + png_fixed_point file_gamma; + + png_debug(1, "in png_set_alpha_mode"); + + if (!png_rtran_ok(png_ptr, 0)) + return; + + output_gamma = translate_gamma_flags(png_ptr, output_gamma, 1/*screen*/); + + /* Validate the value to ensure it is in a reasonable range. The value + * is expected to be 1 or greater, but this range test allows for some + * viewing correction values. The intent is to weed out users of this API + * who use the inverse of the gamma value accidentally! Since some of these + * values are reasonable this may have to be changed. + */ + if (output_gamma < 70000 || output_gamma > 300000) + png_error(png_ptr, "output gamma out of expected range"); + + /* The default file gamma is the inverse of the output gamma; the output + * gamma may be changed below so get the file value first: + */ + file_gamma = png_reciprocal(output_gamma); + + /* There are really 8 possibilities here, composed of any combination + * of: + * + * premultiply the color channels + * do not encode non-opaque pixels + * encode the alpha as well as the color channels + * + * The differences disappear if the input/output ('screen') gamma is 1.0, + * because then the encoding is a no-op and there is only the choice of + * premultiplying the color channels or not. + * + * png_set_alpha_mode and png_set_background interact because both use + * png_compose to do the work. Calling both is only useful when + * png_set_alpha_mode is used to set the default mode - PNG_ALPHA_PNG - along + * with a default gamma value. Otherwise PNG_COMPOSE must not be set. + */ + switch (mode) + { + case PNG_ALPHA_PNG: /* default: png standard */ + /* No compose, but it may be set by png_set_background! */ + png_ptr->transformations &= ~PNG_ENCODE_ALPHA; + png_ptr->flags &= ~PNG_FLAG_OPTIMIZE_ALPHA; + break; + + case PNG_ALPHA_ASSOCIATED: /* color channels premultiplied */ + compose = 1; + png_ptr->transformations &= ~PNG_ENCODE_ALPHA; + png_ptr->flags &= ~PNG_FLAG_OPTIMIZE_ALPHA; + /* The output is linear: */ + output_gamma = PNG_FP_1; + break; + + case PNG_ALPHA_OPTIMIZED: /* associated, non-opaque pixels linear */ + compose = 1; + png_ptr->transformations &= ~PNG_ENCODE_ALPHA; + png_ptr->flags |= PNG_FLAG_OPTIMIZE_ALPHA; + /* output_gamma records the encoding of opaque pixels! */ + break; + + case PNG_ALPHA_BROKEN: /* associated, non-linear, alpha encoded */ + compose = 1; + png_ptr->transformations |= PNG_ENCODE_ALPHA; + png_ptr->flags &= ~PNG_FLAG_OPTIMIZE_ALPHA; + break; + + default: + png_error(png_ptr, "invalid alpha mode"); + } + + /* Only set the default gamma if the file gamma has not been set (this has + * the side effect that the gamma in a second call to png_set_alpha_mode will + * be ignored.) + */ + if (png_ptr->colorspace.gamma == 0) + { + png_ptr->colorspace.gamma = file_gamma; + png_ptr->colorspace.flags |= PNG_COLORSPACE_HAVE_GAMMA; + } + + /* But always set the output gamma: */ + png_ptr->screen_gamma = output_gamma; + + /* Finally, if pre-multiplying, set the background fields to achieve the + * desired result. + */ + if (compose) + { + /* And obtain alpha pre-multiplication by composing on black: */ + memset(&png_ptr->background, 0, (sizeof png_ptr->background)); + png_ptr->background_gamma = png_ptr->colorspace.gamma; /* just in case */ + png_ptr->background_gamma_type = PNG_BACKGROUND_GAMMA_FILE; + png_ptr->transformations &= ~PNG_BACKGROUND_EXPAND; + + if (png_ptr->transformations & PNG_COMPOSE) + png_error(png_ptr, + "conflicting calls to set alpha mode and background"); + + png_ptr->transformations |= PNG_COMPOSE; + } +} + +# ifdef PNG_FLOATING_POINT_SUPPORTED +void PNGAPI +png_set_alpha_mode(png_structrp png_ptr, int mode, double output_gamma) +{ + png_set_alpha_mode_fixed(png_ptr, mode, convert_gamma_value(png_ptr, + output_gamma)); +} +# endif +#endif + +#ifdef PNG_READ_QUANTIZE_SUPPORTED +/* Dither file to 8-bit. Supply a palette, the current number + * of elements in the palette, the maximum number of elements + * allowed, and a histogram if possible. If the current number + * of colors is greater then the maximum number, the palette will be + * modified to fit in the maximum number. "full_quantize" indicates + * whether we need a quantizing cube set up for RGB images, or if we + * simply are reducing the number of colors in a paletted image. + */ + +typedef struct png_dsort_struct +{ + struct png_dsort_struct * next; + png_byte left; + png_byte right; +} png_dsort; +typedef png_dsort * png_dsortp; +typedef png_dsort * * png_dsortpp; + +void PNGAPI +png_set_quantize(png_structrp png_ptr, png_colorp palette, + int num_palette, int maximum_colors, png_const_uint_16p histogram, + int full_quantize) +{ + png_debug(1, "in png_set_quantize"); + + if (!png_rtran_ok(png_ptr, 0)) + return; + + png_ptr->transformations |= PNG_QUANTIZE; + + if (!full_quantize) + { + int i; + + png_ptr->quantize_index = (png_bytep)png_malloc(png_ptr, + (png_uint_32)(num_palette * (sizeof (png_byte)))); + for (i = 0; i < num_palette; i++) + png_ptr->quantize_index[i] = (png_byte)i; + } + + if (num_palette > maximum_colors) + { + if (histogram != NULL) + { + /* This is easy enough, just throw out the least used colors. + * Perhaps not the best solution, but good enough. + */ + + int i; + + /* Initialize an array to sort colors */ + png_ptr->quantize_sort = (png_bytep)png_malloc(png_ptr, + (png_uint_32)(num_palette * (sizeof (png_byte)))); + + /* Initialize the quantize_sort array */ + for (i = 0; i < num_palette; i++) + png_ptr->quantize_sort[i] = (png_byte)i; + + /* Find the least used palette entries by starting a + * bubble sort, and running it until we have sorted + * out enough colors. Note that we don't care about + * sorting all the colors, just finding which are + * least used. + */ + + for (i = num_palette - 1; i >= maximum_colors; i--) + { + int done; /* To stop early if the list is pre-sorted */ + int j; + + done = 1; + for (j = 0; j < i; j++) + { + if (histogram[png_ptr->quantize_sort[j]] + < histogram[png_ptr->quantize_sort[j + 1]]) + { + png_byte t; + + t = png_ptr->quantize_sort[j]; + png_ptr->quantize_sort[j] = png_ptr->quantize_sort[j + 1]; + png_ptr->quantize_sort[j + 1] = t; + done = 0; + } + } + + if (done) + break; + } + + /* Swap the palette around, and set up a table, if necessary */ + if (full_quantize) + { + int j = num_palette; + + /* Put all the useful colors within the max, but don't + * move the others. + */ + for (i = 0; i < maximum_colors; i++) + { + if ((int)png_ptr->quantize_sort[i] >= maximum_colors) + { + do + j--; + while ((int)png_ptr->quantize_sort[j] >= maximum_colors); + + palette[i] = palette[j]; + } + } + } + else + { + int j = num_palette; + + /* Move all the used colors inside the max limit, and + * develop a translation table. + */ + for (i = 0; i < maximum_colors; i++) + { + /* Only move the colors we need to */ + if ((int)png_ptr->quantize_sort[i] >= maximum_colors) + { + png_color tmp_color; + + do + j--; + while ((int)png_ptr->quantize_sort[j] >= maximum_colors); + + tmp_color = palette[j]; + palette[j] = palette[i]; + palette[i] = tmp_color; + /* Indicate where the color went */ + png_ptr->quantize_index[j] = (png_byte)i; + png_ptr->quantize_index[i] = (png_byte)j; + } + } + + /* Find closest color for those colors we are not using */ + for (i = 0; i < num_palette; i++) + { + if ((int)png_ptr->quantize_index[i] >= maximum_colors) + { + int min_d, k, min_k, d_index; + + /* Find the closest color to one we threw out */ + d_index = png_ptr->quantize_index[i]; + min_d = PNG_COLOR_DIST(palette[d_index], palette[0]); + for (k = 1, min_k = 0; k < maximum_colors; k++) + { + int d; + + d = PNG_COLOR_DIST(palette[d_index], palette[k]); + + if (d < min_d) + { + min_d = d; + min_k = k; + } + } + /* Point to closest color */ + png_ptr->quantize_index[i] = (png_byte)min_k; + } + } + } + png_free(png_ptr, png_ptr->quantize_sort); + png_ptr->quantize_sort = NULL; + } + else + { + /* This is much harder to do simply (and quickly). Perhaps + * we need to go through a median cut routine, but those + * don't always behave themselves with only a few colors + * as input. So we will just find the closest two colors, + * and throw out one of them (chosen somewhat randomly). + * [We don't understand this at all, so if someone wants to + * work on improving it, be our guest - AED, GRP] + */ + int i; + int max_d; + int num_new_palette; + png_dsortp t; + png_dsortpp hash; + + t = NULL; + + /* Initialize palette index arrays */ + png_ptr->index_to_palette = (png_bytep)png_malloc(png_ptr, + (png_uint_32)(num_palette * (sizeof (png_byte)))); + png_ptr->palette_to_index = (png_bytep)png_malloc(png_ptr, + (png_uint_32)(num_palette * (sizeof (png_byte)))); + + /* Initialize the sort array */ + for (i = 0; i < num_palette; i++) + { + png_ptr->index_to_palette[i] = (png_byte)i; + png_ptr->palette_to_index[i] = (png_byte)i; + } + + hash = (png_dsortpp)png_calloc(png_ptr, (png_uint_32)(769 * + (sizeof (png_dsortp)))); + + num_new_palette = num_palette; + + /* Initial wild guess at how far apart the farthest pixel + * pair we will be eliminating will be. Larger + * numbers mean more areas will be allocated, Smaller + * numbers run the risk of not saving enough data, and + * having to do this all over again. + * + * I have not done extensive checking on this number. + */ + max_d = 96; + + while (num_new_palette > maximum_colors) + { + for (i = 0; i < num_new_palette - 1; i++) + { + int j; + + for (j = i + 1; j < num_new_palette; j++) + { + int d; + + d = PNG_COLOR_DIST(palette[i], palette[j]); + + if (d <= max_d) + { + + t = (png_dsortp)png_malloc_warn(png_ptr, + (png_uint_32)(sizeof (png_dsort))); + + if (t == NULL) + break; + + t->next = hash[d]; + t->left = (png_byte)i; + t->right = (png_byte)j; + hash[d] = t; + } + } + if (t == NULL) + break; + } + + if (t != NULL) + for (i = 0; i <= max_d; i++) + { + if (hash[i] != NULL) + { + png_dsortp p; + + for (p = hash[i]; p; p = p->next) + { + if ((int)png_ptr->index_to_palette[p->left] + < num_new_palette && + (int)png_ptr->index_to_palette[p->right] + < num_new_palette) + { + int j, next_j; + + if (num_new_palette & 0x01) + { + j = p->left; + next_j = p->right; + } + else + { + j = p->right; + next_j = p->left; + } + + num_new_palette--; + palette[png_ptr->index_to_palette[j]] + = palette[num_new_palette]; + if (!full_quantize) + { + int k; + + for (k = 0; k < num_palette; k++) + { + if (png_ptr->quantize_index[k] == + png_ptr->index_to_palette[j]) + png_ptr->quantize_index[k] = + png_ptr->index_to_palette[next_j]; + + if ((int)png_ptr->quantize_index[k] == + num_new_palette) + png_ptr->quantize_index[k] = + png_ptr->index_to_palette[j]; + } + } + + png_ptr->index_to_palette[png_ptr->palette_to_index + [num_new_palette]] = png_ptr->index_to_palette[j]; + + png_ptr->palette_to_index[png_ptr->index_to_palette[j]] + = png_ptr->palette_to_index[num_new_palette]; + + png_ptr->index_to_palette[j] = + (png_byte)num_new_palette; + + png_ptr->palette_to_index[num_new_palette] = + (png_byte)j; + } + if (num_new_palette <= maximum_colors) + break; + } + if (num_new_palette <= maximum_colors) + break; + } + } + + for (i = 0; i < 769; i++) + { + if (hash[i] != NULL) + { + png_dsortp p = hash[i]; + while (p) + { + t = p->next; + png_free(png_ptr, p); + p = t; + } + } + hash[i] = 0; + } + max_d += 96; + } + png_free(png_ptr, hash); + png_free(png_ptr, png_ptr->palette_to_index); + png_free(png_ptr, png_ptr->index_to_palette); + png_ptr->palette_to_index = NULL; + png_ptr->index_to_palette = NULL; + } + num_palette = maximum_colors; + } + if (png_ptr->palette == NULL) + { + png_ptr->palette = palette; + } + png_ptr->num_palette = (png_uint_16)num_palette; + + if (full_quantize) + { + int i; + png_bytep distance; + int total_bits = PNG_QUANTIZE_RED_BITS + PNG_QUANTIZE_GREEN_BITS + + PNG_QUANTIZE_BLUE_BITS; + int num_red = (1 << PNG_QUANTIZE_RED_BITS); + int num_green = (1 << PNG_QUANTIZE_GREEN_BITS); + int num_blue = (1 << PNG_QUANTIZE_BLUE_BITS); + png_size_t num_entries = ((png_size_t)1 << total_bits); + + png_ptr->palette_lookup = (png_bytep)png_calloc(png_ptr, + (png_uint_32)(num_entries * (sizeof (png_byte)))); + + distance = (png_bytep)png_malloc(png_ptr, (png_uint_32)(num_entries * + (sizeof (png_byte)))); + + memset(distance, 0xff, num_entries * (sizeof (png_byte))); + + for (i = 0; i < num_palette; i++) + { + int ir, ig, ib; + int r = (palette[i].red >> (8 - PNG_QUANTIZE_RED_BITS)); + int g = (palette[i].green >> (8 - PNG_QUANTIZE_GREEN_BITS)); + int b = (palette[i].blue >> (8 - PNG_QUANTIZE_BLUE_BITS)); + + for (ir = 0; ir < num_red; ir++) + { + /* int dr = abs(ir - r); */ + int dr = ((ir > r) ? ir - r : r - ir); + int index_r = (ir << (PNG_QUANTIZE_BLUE_BITS + + PNG_QUANTIZE_GREEN_BITS)); + + for (ig = 0; ig < num_green; ig++) + { + /* int dg = abs(ig - g); */ + int dg = ((ig > g) ? ig - g : g - ig); + int dt = dr + dg; + int dm = ((dr > dg) ? dr : dg); + int index_g = index_r | (ig << PNG_QUANTIZE_BLUE_BITS); + + for (ib = 0; ib < num_blue; ib++) + { + int d_index = index_g | ib; + /* int db = abs(ib - b); */ + int db = ((ib > b) ? ib - b : b - ib); + int dmax = ((dm > db) ? dm : db); + int d = dmax + dt + db; + + if (d < (int)distance[d_index]) + { + distance[d_index] = (png_byte)d; + png_ptr->palette_lookup[d_index] = (png_byte)i; + } + } + } + } + } + + png_free(png_ptr, distance); + } +} +#endif /* PNG_READ_QUANTIZE_SUPPORTED */ + +#ifdef PNG_READ_GAMMA_SUPPORTED +void PNGFAPI +png_set_gamma_fixed(png_structrp png_ptr, png_fixed_point scrn_gamma, + png_fixed_point file_gamma) +{ + png_debug(1, "in png_set_gamma_fixed"); + + if (!png_rtran_ok(png_ptr, 0)) + return; + + /* New in libpng-1.5.4 - reserve particular negative values as flags. */ + scrn_gamma = translate_gamma_flags(png_ptr, scrn_gamma, 1/*screen*/); + file_gamma = translate_gamma_flags(png_ptr, file_gamma, 0/*file*/); + + /* Checking the gamma values for being >0 was added in 1.5.4 along with the + * premultiplied alpha support; this actually hides an undocumented feature + * of the previous implementation which allowed gamma processing to be + * disabled in background handling. There is no evidence (so far) that this + * was being used; however, png_set_background itself accepted and must still + * accept '0' for the gamma value it takes, because it isn't always used. + * + * Since this is an API change (albeit a very minor one that removes an + * undocumented API feature) the following checks were only enabled in + * libpng-1.6.0. + */ + if (file_gamma <= 0) + png_error(png_ptr, "invalid file gamma in png_set_gamma"); + + if (scrn_gamma <= 0) + png_error(png_ptr, "invalid screen gamma in png_set_gamma"); + + /* Set the gamma values unconditionally - this overrides the value in the PNG + * file if a gAMA chunk was present. png_set_alpha_mode provides a + * different, easier, way to default the file gamma. + */ + png_ptr->colorspace.gamma = file_gamma; + png_ptr->colorspace.flags |= PNG_COLORSPACE_HAVE_GAMMA; + png_ptr->screen_gamma = scrn_gamma; +} + +# ifdef PNG_FLOATING_POINT_SUPPORTED +void PNGAPI +png_set_gamma(png_structrp png_ptr, double scrn_gamma, double file_gamma) +{ + png_set_gamma_fixed(png_ptr, convert_gamma_value(png_ptr, scrn_gamma), + convert_gamma_value(png_ptr, file_gamma)); +} +# endif /* FLOATING_POINT_SUPPORTED */ +#endif /* READ_GAMMA */ + +#ifdef PNG_READ_EXPAND_SUPPORTED +/* Expand paletted images to RGB, expand grayscale images of + * less than 8-bit depth to 8-bit depth, and expand tRNS chunks + * to alpha channels. + */ +void PNGAPI +png_set_expand(png_structrp png_ptr) +{ + png_debug(1, "in png_set_expand"); + + if (!png_rtran_ok(png_ptr, 0)) + return; + + png_ptr->transformations |= (PNG_EXPAND | PNG_EXPAND_tRNS); +} + +/* GRR 19990627: the following three functions currently are identical + * to png_set_expand(). However, it is entirely reasonable that someone + * might wish to expand an indexed image to RGB but *not* expand a single, + * fully transparent palette entry to a full alpha channel--perhaps instead + * convert tRNS to the grayscale/RGB format (16-bit RGB value), or replace + * the transparent color with a particular RGB value, or drop tRNS entirely. + * IOW, a future version of the library may make the transformations flag + * a bit more fine-grained, with separate bits for each of these three + * functions. + * + * More to the point, these functions make it obvious what libpng will be + * doing, whereas "expand" can (and does) mean any number of things. + * + * GRP 20060307: In libpng-1.2.9, png_set_gray_1_2_4_to_8() was modified + * to expand only the sample depth but not to expand the tRNS to alpha + * and its name was changed to png_set_expand_gray_1_2_4_to_8(). + */ + +/* Expand paletted images to RGB. */ +void PNGAPI +png_set_palette_to_rgb(png_structrp png_ptr) +{ + png_debug(1, "in png_set_palette_to_rgb"); + + if (!png_rtran_ok(png_ptr, 0)) + return; + + png_ptr->transformations |= (PNG_EXPAND | PNG_EXPAND_tRNS); +} + +/* Expand grayscale images of less than 8-bit depth to 8 bits. */ +void PNGAPI +png_set_expand_gray_1_2_4_to_8(png_structrp png_ptr) +{ + png_debug(1, "in png_set_expand_gray_1_2_4_to_8"); + + if (!png_rtran_ok(png_ptr, 0)) + return; + + png_ptr->transformations |= PNG_EXPAND; +} + +/* Expand tRNS chunks to alpha channels. */ +void PNGAPI +png_set_tRNS_to_alpha(png_structrp png_ptr) +{ + png_debug(1, "in png_set_tRNS_to_alpha"); + + if (!png_rtran_ok(png_ptr, 0)) + return; + + png_ptr->transformations |= (PNG_EXPAND | PNG_EXPAND_tRNS); +} +#endif /* defined(PNG_READ_EXPAND_SUPPORTED) */ + +#ifdef PNG_READ_EXPAND_16_SUPPORTED +/* Expand to 16-bit channels, expand the tRNS chunk too (because otherwise + * it may not work correctly.) + */ +void PNGAPI +png_set_expand_16(png_structrp png_ptr) +{ + png_debug(1, "in png_set_expand_16"); + + if (!png_rtran_ok(png_ptr, 0)) + return; + + png_ptr->transformations |= (PNG_EXPAND_16 | PNG_EXPAND | PNG_EXPAND_tRNS); +} +#endif + +#ifdef PNG_READ_GRAY_TO_RGB_SUPPORTED +void PNGAPI +png_set_gray_to_rgb(png_structrp png_ptr) +{ + png_debug(1, "in png_set_gray_to_rgb"); + + if (!png_rtran_ok(png_ptr, 0)) + return; + + /* Because rgb must be 8 bits or more: */ + png_set_expand_gray_1_2_4_to_8(png_ptr); + png_ptr->transformations |= PNG_GRAY_TO_RGB; +} +#endif + +#ifdef PNG_READ_RGB_TO_GRAY_SUPPORTED +void PNGFAPI +png_set_rgb_to_gray_fixed(png_structrp png_ptr, int error_action, + png_fixed_point red, png_fixed_point green) +{ + png_debug(1, "in png_set_rgb_to_gray"); + + /* Need the IHDR here because of the check on color_type below. */ + /* TODO: fix this */ + if (!png_rtran_ok(png_ptr, 1)) + return; + + switch(error_action) + { + case PNG_ERROR_ACTION_NONE: + png_ptr->transformations |= PNG_RGB_TO_GRAY; + break; + + case PNG_ERROR_ACTION_WARN: + png_ptr->transformations |= PNG_RGB_TO_GRAY_WARN; + break; + + case PNG_ERROR_ACTION_ERROR: + png_ptr->transformations |= PNG_RGB_TO_GRAY_ERR; + break; + + default: + png_error(png_ptr, "invalid error action to rgb_to_gray"); + break; + } + + if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE) +#ifdef PNG_READ_EXPAND_SUPPORTED + png_ptr->transformations |= PNG_EXPAND; +#else + { + /* Make this an error in 1.6 because otherwise the application may assume + * that it just worked and get a memory overwrite. + */ + png_error(png_ptr, + "Cannot do RGB_TO_GRAY without EXPAND_SUPPORTED"); + + /* png_ptr->transformations &= ~PNG_RGB_TO_GRAY; */ + } +#endif + { + if (red >= 0 && green >= 0 && red + green <= PNG_FP_1) + { + png_uint_16 red_int, green_int; + + /* NOTE: this calculation does not round, but this behavior is retained + * for consistency, the inaccuracy is very small. The code here always + * overwrites the coefficients, regardless of whether they have been + * defaulted or set already. + */ + red_int = (png_uint_16)(((png_uint_32)red*32768)/100000); + green_int = (png_uint_16)(((png_uint_32)green*32768)/100000); + + png_ptr->rgb_to_gray_red_coeff = red_int; + png_ptr->rgb_to_gray_green_coeff = green_int; + png_ptr->rgb_to_gray_coefficients_set = 1; + } + + else + { + if (red >= 0 && green >= 0) + png_app_warning(png_ptr, + "ignoring out of range rgb_to_gray coefficients"); + + /* Use the defaults, from the cHRM chunk if set, else the historical + * values which are close to the sRGB/HDTV/ITU-Rec 709 values. See + * png_do_rgb_to_gray for more discussion of the values. In this case + * the coefficients are not marked as 'set' and are not overwritten if + * something has already provided a default. + */ + if (png_ptr->rgb_to_gray_red_coeff == 0 && + png_ptr->rgb_to_gray_green_coeff == 0) + { + png_ptr->rgb_to_gray_red_coeff = 6968; + png_ptr->rgb_to_gray_green_coeff = 23434; + /* png_ptr->rgb_to_gray_blue_coeff = 2366; */ + } + } + } +} + +#ifdef PNG_FLOATING_POINT_SUPPORTED +/* Convert a RGB image to a grayscale of the same width. This allows us, + * for example, to convert a 24 bpp RGB image into an 8 bpp grayscale image. + */ + +void PNGAPI +png_set_rgb_to_gray(png_structrp png_ptr, int error_action, double red, + double green) +{ + png_set_rgb_to_gray_fixed(png_ptr, error_action, + png_fixed(png_ptr, red, "rgb to gray red coefficient"), + png_fixed(png_ptr, green, "rgb to gray green coefficient")); +} +#endif /* FLOATING POINT */ + +#endif /* RGB_TO_GRAY */ + +#if defined(PNG_READ_USER_TRANSFORM_SUPPORTED) || \ + defined(PNG_WRITE_USER_TRANSFORM_SUPPORTED) +void PNGAPI +png_set_read_user_transform_fn(png_structrp png_ptr, png_user_transform_ptr + read_user_transform_fn) +{ + png_debug(1, "in png_set_read_user_transform_fn"); + +#ifdef PNG_READ_USER_TRANSFORM_SUPPORTED + png_ptr->transformations |= PNG_USER_TRANSFORM; + png_ptr->read_user_transform_fn = read_user_transform_fn; +#endif +} +#endif + +#ifdef PNG_READ_TRANSFORMS_SUPPORTED +#ifdef PNG_READ_GAMMA_SUPPORTED +/* In the case of gamma transformations only do transformations on images where + * the [file] gamma and screen_gamma are not close reciprocals, otherwise it + * slows things down slightly, and also needlessly introduces small errors. + */ +static int /* PRIVATE */ +png_gamma_threshold(png_fixed_point screen_gamma, png_fixed_point file_gamma) +{ + /* PNG_GAMMA_THRESHOLD is the threshold for performing gamma + * correction as a difference of the overall transform from 1.0 + * + * We want to compare the threshold with s*f - 1, if we get + * overflow here it is because of wacky gamma values so we + * turn on processing anyway. + */ + png_fixed_point gtest; + return !png_muldiv(>est, screen_gamma, file_gamma, PNG_FP_1) || + png_gamma_significant(gtest); +} +#endif + +/* Initialize everything needed for the read. This includes modifying + * the palette. + */ + +/*For the moment 'png_init_palette_transformations' and + * 'png_init_rgb_transformations' only do some flag canceling optimizations. + * The intent is that these two routines should have palette or rgb operations + * extracted from 'png_init_read_transformations'. + */ +static void /* PRIVATE */ +png_init_palette_transformations(png_structrp png_ptr) +{ + /* Called to handle the (input) palette case. In png_do_read_transformations + * the first step is to expand the palette if requested, so this code must + * take care to only make changes that are invariant with respect to the + * palette expansion, or only do them if there is no expansion. + * + * STRIP_ALPHA has already been handled in the caller (by setting num_trans + * to 0.) + */ + int input_has_alpha = 0; + int input_has_transparency = 0; + + if (png_ptr->num_trans > 0) + { + int i; + + /* Ignore if all the entries are opaque (unlikely!) */ + for (i=0; inum_trans; ++i) + if (png_ptr->trans_alpha[i] == 255) + continue; + else if (png_ptr->trans_alpha[i] == 0) + input_has_transparency = 1; + else + input_has_alpha = 1; + } + + /* If no alpha we can optimize. */ + if (!input_has_alpha) + { + /* Any alpha means background and associative alpha processing is + * required, however if the alpha is 0 or 1 throughout OPTIIMIZE_ALPHA + * and ENCODE_ALPHA are irrelevant. + */ + png_ptr->transformations &= ~PNG_ENCODE_ALPHA; + png_ptr->flags &= ~PNG_FLAG_OPTIMIZE_ALPHA; + + if (!input_has_transparency) + png_ptr->transformations &= ~(PNG_COMPOSE | PNG_BACKGROUND_EXPAND); + } + +#if defined(PNG_READ_EXPAND_SUPPORTED) && defined(PNG_READ_BACKGROUND_SUPPORTED) + /* png_set_background handling - deals with the complexity of whether the + * background color is in the file format or the screen format in the case + * where an 'expand' will happen. + */ + + /* The following code cannot be entered in the alpha pre-multiplication case + * because PNG_BACKGROUND_EXPAND is cancelled below. + */ + if ((png_ptr->transformations & PNG_BACKGROUND_EXPAND) && + (png_ptr->transformations & PNG_EXPAND)) + { + { + png_ptr->background.red = + png_ptr->palette[png_ptr->background.index].red; + png_ptr->background.green = + png_ptr->palette[png_ptr->background.index].green; + png_ptr->background.blue = + png_ptr->palette[png_ptr->background.index].blue; + +#ifdef PNG_READ_INVERT_ALPHA_SUPPORTED + if (png_ptr->transformations & PNG_INVERT_ALPHA) + { + if (!(png_ptr->transformations & PNG_EXPAND_tRNS)) + { + /* Invert the alpha channel (in tRNS) unless the pixels are + * going to be expanded, in which case leave it for later + */ + int i, istop = png_ptr->num_trans; + + for (i=0; itrans_alpha[i] = (png_byte)(255 - + png_ptr->trans_alpha[i]); + } + } +#endif /* PNG_READ_INVERT_ALPHA_SUPPORTED */ + } + } /* background expand and (therefore) no alpha association. */ +#endif /* PNG_READ_EXPAND_SUPPORTED && PNG_READ_BACKGROUND_SUPPORTED */ +} + +static void /* PRIVATE */ +png_init_rgb_transformations(png_structrp png_ptr) +{ + /* Added to libpng-1.5.4: check the color type to determine whether there + * is any alpha or transparency in the image and simply cancel the + * background and alpha mode stuff if there isn't. + */ + int input_has_alpha = (png_ptr->color_type & PNG_COLOR_MASK_ALPHA) != 0; + int input_has_transparency = png_ptr->num_trans > 0; + + /* If no alpha we can optimize. */ + if (!input_has_alpha) + { + /* Any alpha means background and associative alpha processing is + * required, however if the alpha is 0 or 1 throughout OPTIIMIZE_ALPHA + * and ENCODE_ALPHA are irrelevant. + */ +# ifdef PNG_READ_ALPHA_MODE_SUPPORTED + png_ptr->transformations &= ~PNG_ENCODE_ALPHA; + png_ptr->flags &= ~PNG_FLAG_OPTIMIZE_ALPHA; +# endif + + if (!input_has_transparency) + png_ptr->transformations &= ~(PNG_COMPOSE | PNG_BACKGROUND_EXPAND); + } + +#if defined(PNG_READ_EXPAND_SUPPORTED) && defined(PNG_READ_BACKGROUND_SUPPORTED) + /* png_set_background handling - deals with the complexity of whether the + * background color is in the file format or the screen format in the case + * where an 'expand' will happen. + */ + + /* The following code cannot be entered in the alpha pre-multiplication case + * because PNG_BACKGROUND_EXPAND is cancelled below. + */ + if ((png_ptr->transformations & PNG_BACKGROUND_EXPAND) && + (png_ptr->transformations & PNG_EXPAND) && + !(png_ptr->color_type & PNG_COLOR_MASK_COLOR)) + /* i.e., GRAY or GRAY_ALPHA */ + { + { + /* Expand background and tRNS chunks */ + int gray = png_ptr->background.gray; + int trans_gray = png_ptr->trans_color.gray; + + switch (png_ptr->bit_depth) + { + case 1: + gray *= 0xff; + trans_gray *= 0xff; + break; + + case 2: + gray *= 0x55; + trans_gray *= 0x55; + break; + + case 4: + gray *= 0x11; + trans_gray *= 0x11; + break; + + default: + + case 8: + /* FALL THROUGH (Already 8 bits) */ + + case 16: + /* Already a full 16 bits */ + break; + } + + png_ptr->background.red = png_ptr->background.green = + png_ptr->background.blue = (png_uint_16)gray; + + if (!(png_ptr->transformations & PNG_EXPAND_tRNS)) + { + png_ptr->trans_color.red = png_ptr->trans_color.green = + png_ptr->trans_color.blue = (png_uint_16)trans_gray; + } + } + } /* background expand and (therefore) no alpha association. */ +#endif /* PNG_READ_EXPAND_SUPPORTED && PNG_READ_BACKGROUND_SUPPORTED */ +} + +void /* PRIVATE */ +png_init_read_transformations(png_structrp png_ptr) +{ + png_debug(1, "in png_init_read_transformations"); + + /* This internal function is called from png_read_start_row in pngrutil.c + * and it is called before the 'rowbytes' calculation is done, so the code + * in here can change or update the transformations flags. + * + * First do updates that do not depend on the details of the PNG image data + * being processed. + */ + +#ifdef PNG_READ_GAMMA_SUPPORTED + /* Prior to 1.5.4 these tests were performed from png_set_gamma, 1.5.4 adds + * png_set_alpha_mode and this is another source for a default file gamma so + * the test needs to be performed later - here. In addition prior to 1.5.4 + * the tests were repeated for the PALETTE color type here - this is no + * longer necessary (and doesn't seem to have been necessary before.) + */ + { + /* The following temporary indicates if overall gamma correction is + * required. + */ + int gamma_correction = 0; + + if (png_ptr->colorspace.gamma != 0) /* has been set */ + { + if (png_ptr->screen_gamma != 0) /* screen set too */ + gamma_correction = png_gamma_threshold(png_ptr->colorspace.gamma, + png_ptr->screen_gamma); + + else + /* Assume the output matches the input; a long time default behavior + * of libpng, although the standard has nothing to say about this. + */ + png_ptr->screen_gamma = png_reciprocal(png_ptr->colorspace.gamma); + } + + else if (png_ptr->screen_gamma != 0) + /* The converse - assume the file matches the screen, note that this + * perhaps undesireable default can (from 1.5.4) be changed by calling + * png_set_alpha_mode (even if the alpha handling mode isn't required + * or isn't changed from the default.) + */ + png_ptr->colorspace.gamma = png_reciprocal(png_ptr->screen_gamma); + + else /* neither are set */ + /* Just in case the following prevents any processing - file and screen + * are both assumed to be linear and there is no way to introduce a + * third gamma value other than png_set_background with 'UNIQUE', and, + * prior to 1.5.4 + */ + png_ptr->screen_gamma = png_ptr->colorspace.gamma = PNG_FP_1; + + /* We have a gamma value now. */ + png_ptr->colorspace.flags |= PNG_COLORSPACE_HAVE_GAMMA; + + /* Now turn the gamma transformation on or off as appropriate. Notice + * that PNG_GAMMA just refers to the file->screen correction. Alpha + * composition may independently cause gamma correction because it needs + * linear data (e.g. if the file has a gAMA chunk but the screen gamma + * hasn't been specified.) In any case this flag may get turned off in + * the code immediately below if the transform can be handled outside the + * row loop. + */ + if (gamma_correction) + png_ptr->transformations |= PNG_GAMMA; + + else + png_ptr->transformations &= ~PNG_GAMMA; + } +#endif + + /* Certain transformations have the effect of preventing other + * transformations that happen afterward in png_do_read_transformations, + * resolve the interdependencies here. From the code of + * png_do_read_transformations the order is: + * + * 1) PNG_EXPAND (including PNG_EXPAND_tRNS) + * 2) PNG_STRIP_ALPHA (if no compose) + * 3) PNG_RGB_TO_GRAY + * 4) PNG_GRAY_TO_RGB iff !PNG_BACKGROUND_IS_GRAY + * 5) PNG_COMPOSE + * 6) PNG_GAMMA + * 7) PNG_STRIP_ALPHA (if compose) + * 8) PNG_ENCODE_ALPHA + * 9) PNG_SCALE_16_TO_8 + * 10) PNG_16_TO_8 + * 11) PNG_QUANTIZE (converts to palette) + * 12) PNG_EXPAND_16 + * 13) PNG_GRAY_TO_RGB iff PNG_BACKGROUND_IS_GRAY + * 14) PNG_INVERT_MONO + * 15) PNG_SHIFT + * 16) PNG_PACK + * 17) PNG_BGR + * 18) PNG_PACKSWAP + * 19) PNG_FILLER (includes PNG_ADD_ALPHA) + * 20) PNG_INVERT_ALPHA + * 21) PNG_SWAP_ALPHA + * 22) PNG_SWAP_BYTES + * 23) PNG_USER_TRANSFORM [must be last] + */ +#ifdef PNG_READ_STRIP_ALPHA_SUPPORTED + if ((png_ptr->transformations & PNG_STRIP_ALPHA) && + !(png_ptr->transformations & PNG_COMPOSE)) + { + /* Stripping the alpha channel happens immediately after the 'expand' + * transformations, before all other transformation, so it cancels out + * the alpha handling. It has the side effect negating the effect of + * PNG_EXPAND_tRNS too: + */ + png_ptr->transformations &= ~(PNG_BACKGROUND_EXPAND | PNG_ENCODE_ALPHA | + PNG_EXPAND_tRNS); + png_ptr->flags &= ~PNG_FLAG_OPTIMIZE_ALPHA; + + /* Kill the tRNS chunk itself too. Prior to 1.5.4 this did not happen + * so transparency information would remain just so long as it wasn't + * expanded. This produces unexpected API changes if the set of things + * that do PNG_EXPAND_tRNS changes (perfectly possible given the + * documentation - which says ask for what you want, accept what you + * get.) This makes the behavior consistent from 1.5.4: + */ + png_ptr->num_trans = 0; + } +#endif /* STRIP_ALPHA supported, no COMPOSE */ + +#ifdef PNG_READ_ALPHA_MODE_SUPPORTED + /* If the screen gamma is about 1.0 then the OPTIMIZE_ALPHA and ENCODE_ALPHA + * settings will have no effect. + */ + if (!png_gamma_significant(png_ptr->screen_gamma)) + { + png_ptr->transformations &= ~PNG_ENCODE_ALPHA; + png_ptr->flags &= ~PNG_FLAG_OPTIMIZE_ALPHA; + } +#endif + +#ifdef PNG_READ_RGB_TO_GRAY_SUPPORTED + /* Make sure the coefficients for the rgb to gray conversion are set + * appropriately. + */ + if (png_ptr->transformations & PNG_RGB_TO_GRAY) + png_colorspace_set_rgb_coefficients(png_ptr); +#endif + +#ifdef PNG_READ_GRAY_TO_RGB_SUPPORTED +#if defined(PNG_READ_EXPAND_SUPPORTED) && defined(PNG_READ_BACKGROUND_SUPPORTED) + /* Detect gray background and attempt to enable optimization for + * gray --> RGB case. + * + * Note: if PNG_BACKGROUND_EXPAND is set and color_type is either RGB or + * RGB_ALPHA (in which case need_expand is superfluous anyway), the + * background color might actually be gray yet not be flagged as such. + * This is not a problem for the current code, which uses + * PNG_BACKGROUND_IS_GRAY only to decide when to do the + * png_do_gray_to_rgb() transformation. + * + * TODO: this code needs to be revised to avoid the complexity and + * interdependencies. The color type of the background should be recorded in + * png_set_background, along with the bit depth, then the code has a record + * of exactly what color space the background is currently in. + */ + if (png_ptr->transformations & PNG_BACKGROUND_EXPAND) + { + /* PNG_BACKGROUND_EXPAND: the background is in the file color space, so if + * the file was grayscale the background value is gray. + */ + if (!(png_ptr->color_type & PNG_COLOR_MASK_COLOR)) + png_ptr->mode |= PNG_BACKGROUND_IS_GRAY; + } + + else if (png_ptr->transformations & PNG_COMPOSE) + { + /* PNG_COMPOSE: png_set_background was called with need_expand false, + * so the color is in the color space of the output or png_set_alpha_mode + * was called and the color is black. Ignore RGB_TO_GRAY because that + * happens before GRAY_TO_RGB. + */ + if (png_ptr->transformations & PNG_GRAY_TO_RGB) + { + if (png_ptr->background.red == png_ptr->background.green && + png_ptr->background.red == png_ptr->background.blue) + { + png_ptr->mode |= PNG_BACKGROUND_IS_GRAY; + png_ptr->background.gray = png_ptr->background.red; + } + } + } +#endif /* PNG_READ_EXPAND_SUPPORTED && PNG_READ_BACKGROUND_SUPPORTED */ +#endif /* PNG_READ_GRAY_TO_RGB_SUPPORTED */ + + /* For indexed PNG data (PNG_COLOR_TYPE_PALETTE) many of the transformations + * can be performed directly on the palette, and some (such as rgb to gray) + * can be optimized inside the palette. This is particularly true of the + * composite (background and alpha) stuff, which can be pretty much all done + * in the palette even if the result is expanded to RGB or gray afterward. + * + * NOTE: this is Not Yet Implemented, the code behaves as in 1.5.1 and + * earlier and the palette stuff is actually handled on the first row. This + * leads to the reported bug that the palette returned by png_get_PLTE is not + * updated. + */ + if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE) + png_init_palette_transformations(png_ptr); + + else + png_init_rgb_transformations(png_ptr); + +#if defined(PNG_READ_BACKGROUND_SUPPORTED) && \ + defined(PNG_READ_EXPAND_16_SUPPORTED) + if ((png_ptr->transformations & PNG_EXPAND_16) && + (png_ptr->transformations & PNG_COMPOSE) && + !(png_ptr->transformations & PNG_BACKGROUND_EXPAND) && + png_ptr->bit_depth != 16) + { + /* TODO: fix this. Because the expand_16 operation is after the compose + * handling the background color must be 8, not 16, bits deep, but the + * application will supply a 16-bit value so reduce it here. + * + * The PNG_BACKGROUND_EXPAND code above does not expand to 16 bits at + * present, so that case is ok (until do_expand_16 is moved.) + * + * NOTE: this discards the low 16 bits of the user supplied background + * color, but until expand_16 works properly there is no choice! + */ +# define CHOP(x) (x)=((png_uint_16)PNG_DIV257(x)) + CHOP(png_ptr->background.red); + CHOP(png_ptr->background.green); + CHOP(png_ptr->background.blue); + CHOP(png_ptr->background.gray); +# undef CHOP + } +#endif /* PNG_READ_BACKGROUND_SUPPORTED && PNG_READ_EXPAND_16_SUPPORTED */ + +#if defined(PNG_READ_BACKGROUND_SUPPORTED) && \ + (defined(PNG_READ_SCALE_16_TO_8_SUPPORTED) || \ + defined(PNG_READ_STRIP_16_TO_8_SUPPORTED)) + if ((png_ptr->transformations & (PNG_16_TO_8|PNG_SCALE_16_TO_8)) && + (png_ptr->transformations & PNG_COMPOSE) && + !(png_ptr->transformations & PNG_BACKGROUND_EXPAND) && + png_ptr->bit_depth == 16) + { + /* On the other hand, if a 16-bit file is to be reduced to 8-bits per + * component this will also happen after PNG_COMPOSE and so the background + * color must be pre-expanded here. + * + * TODO: fix this too. + */ + png_ptr->background.red = (png_uint_16)(png_ptr->background.red * 257); + png_ptr->background.green = + (png_uint_16)(png_ptr->background.green * 257); + png_ptr->background.blue = (png_uint_16)(png_ptr->background.blue * 257); + png_ptr->background.gray = (png_uint_16)(png_ptr->background.gray * 257); + } +#endif + + /* NOTE: below 'PNG_READ_ALPHA_MODE_SUPPORTED' is presumed to also enable the + * background support (see the comments in scripts/pnglibconf.dfa), this + * allows pre-multiplication of the alpha channel to be implemented as + * compositing on black. This is probably sub-optimal and has been done in + * 1.5.4 betas simply to enable external critique and testing (i.e. to + * implement the new API quickly, without lots of internal changes.) + */ + +#ifdef PNG_READ_GAMMA_SUPPORTED +# ifdef PNG_READ_BACKGROUND_SUPPORTED + /* Includes ALPHA_MODE */ + png_ptr->background_1 = png_ptr->background; +# endif + + /* This needs to change - in the palette image case a whole set of tables are + * built when it would be quicker to just calculate the correct value for + * each palette entry directly. Also, the test is too tricky - why check + * PNG_RGB_TO_GRAY if PNG_GAMMA is not set? The answer seems to be that + * PNG_GAMMA is cancelled even if the gamma is known? The test excludes the + * PNG_COMPOSE case, so apparently if there is no *overall* gamma correction + * the gamma tables will not be built even if composition is required on a + * gamma encoded value. + * + * In 1.5.4 this is addressed below by an additional check on the individual + * file gamma - if it is not 1.0 both RGB_TO_GRAY and COMPOSE need the + * tables. + */ + if ((png_ptr->transformations & PNG_GAMMA) + || ((png_ptr->transformations & PNG_RGB_TO_GRAY) + && (png_gamma_significant(png_ptr->colorspace.gamma) || + png_gamma_significant(png_ptr->screen_gamma))) + || ((png_ptr->transformations & PNG_COMPOSE) + && (png_gamma_significant(png_ptr->colorspace.gamma) + || png_gamma_significant(png_ptr->screen_gamma) +# ifdef PNG_READ_BACKGROUND_SUPPORTED + || (png_ptr->background_gamma_type == PNG_BACKGROUND_GAMMA_UNIQUE + && png_gamma_significant(png_ptr->background_gamma)) +# endif + )) || ((png_ptr->transformations & PNG_ENCODE_ALPHA) + && png_gamma_significant(png_ptr->screen_gamma)) + ) + { + png_build_gamma_table(png_ptr, png_ptr->bit_depth); + +#ifdef PNG_READ_BACKGROUND_SUPPORTED + if (png_ptr->transformations & PNG_COMPOSE) + { + /* Issue a warning about this combination: because RGB_TO_GRAY is + * optimized to do the gamma transform if present yet do_background has + * to do the same thing if both options are set a + * double-gamma-correction happens. This is true in all versions of + * libpng to date. + */ + if (png_ptr->transformations & PNG_RGB_TO_GRAY) + png_warning(png_ptr, + "libpng does not support gamma+background+rgb_to_gray"); + + if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE) + { + /* We don't get to here unless there is a tRNS chunk with non-opaque + * entries - see the checking code at the start of this function. + */ + png_color back, back_1; + png_colorp palette = png_ptr->palette; + int num_palette = png_ptr->num_palette; + int i; + if (png_ptr->background_gamma_type == PNG_BACKGROUND_GAMMA_FILE) + { + + back.red = png_ptr->gamma_table[png_ptr->background.red]; + back.green = png_ptr->gamma_table[png_ptr->background.green]; + back.blue = png_ptr->gamma_table[png_ptr->background.blue]; + + back_1.red = png_ptr->gamma_to_1[png_ptr->background.red]; + back_1.green = png_ptr->gamma_to_1[png_ptr->background.green]; + back_1.blue = png_ptr->gamma_to_1[png_ptr->background.blue]; + } + else + { + png_fixed_point g, gs; + + switch (png_ptr->background_gamma_type) + { + case PNG_BACKGROUND_GAMMA_SCREEN: + g = (png_ptr->screen_gamma); + gs = PNG_FP_1; + break; + + case PNG_BACKGROUND_GAMMA_FILE: + g = png_reciprocal(png_ptr->colorspace.gamma); + gs = png_reciprocal2(png_ptr->colorspace.gamma, + png_ptr->screen_gamma); + break; + + case PNG_BACKGROUND_GAMMA_UNIQUE: + g = png_reciprocal(png_ptr->background_gamma); + gs = png_reciprocal2(png_ptr->background_gamma, + png_ptr->screen_gamma); + break; + default: + g = PNG_FP_1; /* back_1 */ + gs = PNG_FP_1; /* back */ + break; + } + + if (png_gamma_significant(gs)) + { + back.red = png_gamma_8bit_correct(png_ptr->background.red, + gs); + back.green = png_gamma_8bit_correct(png_ptr->background.green, + gs); + back.blue = png_gamma_8bit_correct(png_ptr->background.blue, + gs); + } + + else + { + back.red = (png_byte)png_ptr->background.red; + back.green = (png_byte)png_ptr->background.green; + back.blue = (png_byte)png_ptr->background.blue; + } + + if (png_gamma_significant(g)) + { + back_1.red = png_gamma_8bit_correct(png_ptr->background.red, + g); + back_1.green = png_gamma_8bit_correct( + png_ptr->background.green, g); + back_1.blue = png_gamma_8bit_correct(png_ptr->background.blue, + g); + } + + else + { + back_1.red = (png_byte)png_ptr->background.red; + back_1.green = (png_byte)png_ptr->background.green; + back_1.blue = (png_byte)png_ptr->background.blue; + } + } + + for (i = 0; i < num_palette; i++) + { + if (i < (int)png_ptr->num_trans && + png_ptr->trans_alpha[i] != 0xff) + { + if (png_ptr->trans_alpha[i] == 0) + { + palette[i] = back; + } + else /* if (png_ptr->trans_alpha[i] != 0xff) */ + { + png_byte v, w; + + v = png_ptr->gamma_to_1[palette[i].red]; + png_composite(w, v, png_ptr->trans_alpha[i], back_1.red); + palette[i].red = png_ptr->gamma_from_1[w]; + + v = png_ptr->gamma_to_1[palette[i].green]; + png_composite(w, v, png_ptr->trans_alpha[i], back_1.green); + palette[i].green = png_ptr->gamma_from_1[w]; + + v = png_ptr->gamma_to_1[palette[i].blue]; + png_composite(w, v, png_ptr->trans_alpha[i], back_1.blue); + palette[i].blue = png_ptr->gamma_from_1[w]; + } + } + else + { + palette[i].red = png_ptr->gamma_table[palette[i].red]; + palette[i].green = png_ptr->gamma_table[palette[i].green]; + palette[i].blue = png_ptr->gamma_table[palette[i].blue]; + } + } + + /* Prevent the transformations being done again. + * + * NOTE: this is highly dubious; it removes the transformations in + * place. This seems inconsistent with the general treatment of the + * transformations elsewhere. + */ + png_ptr->transformations &= ~(PNG_COMPOSE | PNG_GAMMA); + } /* color_type == PNG_COLOR_TYPE_PALETTE */ + + /* if (png_ptr->background_gamma_type!=PNG_BACKGROUND_GAMMA_UNKNOWN) */ + else /* color_type != PNG_COLOR_TYPE_PALETTE */ + { + int gs_sig, g_sig; + png_fixed_point g = PNG_FP_1; /* Correction to linear */ + png_fixed_point gs = PNG_FP_1; /* Correction to screen */ + + switch (png_ptr->background_gamma_type) + { + case PNG_BACKGROUND_GAMMA_SCREEN: + g = png_ptr->screen_gamma; + /* gs = PNG_FP_1; */ + break; + + case PNG_BACKGROUND_GAMMA_FILE: + g = png_reciprocal(png_ptr->colorspace.gamma); + gs = png_reciprocal2(png_ptr->colorspace.gamma, + png_ptr->screen_gamma); + break; + + case PNG_BACKGROUND_GAMMA_UNIQUE: + g = png_reciprocal(png_ptr->background_gamma); + gs = png_reciprocal2(png_ptr->background_gamma, + png_ptr->screen_gamma); + break; + + default: + png_error(png_ptr, "invalid background gamma type"); + } + + g_sig = png_gamma_significant(g); + gs_sig = png_gamma_significant(gs); + + if (g_sig) + png_ptr->background_1.gray = png_gamma_correct(png_ptr, + png_ptr->background.gray, g); + + if (gs_sig) + png_ptr->background.gray = png_gamma_correct(png_ptr, + png_ptr->background.gray, gs); + + if ((png_ptr->background.red != png_ptr->background.green) || + (png_ptr->background.red != png_ptr->background.blue) || + (png_ptr->background.red != png_ptr->background.gray)) + { + /* RGB or RGBA with color background */ + if (g_sig) + { + png_ptr->background_1.red = png_gamma_correct(png_ptr, + png_ptr->background.red, g); + + png_ptr->background_1.green = png_gamma_correct(png_ptr, + png_ptr->background.green, g); + + png_ptr->background_1.blue = png_gamma_correct(png_ptr, + png_ptr->background.blue, g); + } + + if (gs_sig) + { + png_ptr->background.red = png_gamma_correct(png_ptr, + png_ptr->background.red, gs); + + png_ptr->background.green = png_gamma_correct(png_ptr, + png_ptr->background.green, gs); + + png_ptr->background.blue = png_gamma_correct(png_ptr, + png_ptr->background.blue, gs); + } + } + + else + { + /* GRAY, GRAY ALPHA, RGB, or RGBA with gray background */ + png_ptr->background_1.red = png_ptr->background_1.green + = png_ptr->background_1.blue = png_ptr->background_1.gray; + + png_ptr->background.red = png_ptr->background.green + = png_ptr->background.blue = png_ptr->background.gray; + } + + /* The background is now in screen gamma: */ + png_ptr->background_gamma_type = PNG_BACKGROUND_GAMMA_SCREEN; + } /* color_type != PNG_COLOR_TYPE_PALETTE */ + }/* png_ptr->transformations & PNG_BACKGROUND */ + + else + /* Transformation does not include PNG_BACKGROUND */ +#endif /* PNG_READ_BACKGROUND_SUPPORTED */ + if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE +#ifdef PNG_READ_RGB_TO_GRAY_SUPPORTED + /* RGB_TO_GRAY needs to have non-gamma-corrected values! */ + && ((png_ptr->transformations & PNG_EXPAND) == 0 || + (png_ptr->transformations & PNG_RGB_TO_GRAY) == 0) +#endif + ) + { + png_colorp palette = png_ptr->palette; + int num_palette = png_ptr->num_palette; + int i; + + /* NOTE: there are other transformations that should probably be in + * here too. + */ + for (i = 0; i < num_palette; i++) + { + palette[i].red = png_ptr->gamma_table[palette[i].red]; + palette[i].green = png_ptr->gamma_table[palette[i].green]; + palette[i].blue = png_ptr->gamma_table[palette[i].blue]; + } + + /* Done the gamma correction. */ + png_ptr->transformations &= ~PNG_GAMMA; + } /* color_type == PALETTE && !PNG_BACKGROUND transformation */ + } +#ifdef PNG_READ_BACKGROUND_SUPPORTED + else +#endif +#endif /* PNG_READ_GAMMA_SUPPORTED */ + +#ifdef PNG_READ_BACKGROUND_SUPPORTED + /* No GAMMA transformation (see the hanging else 4 lines above) */ + if ((png_ptr->transformations & PNG_COMPOSE) && + (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE)) + { + int i; + int istop = (int)png_ptr->num_trans; + png_color back; + png_colorp palette = png_ptr->palette; + + back.red = (png_byte)png_ptr->background.red; + back.green = (png_byte)png_ptr->background.green; + back.blue = (png_byte)png_ptr->background.blue; + + for (i = 0; i < istop; i++) + { + if (png_ptr->trans_alpha[i] == 0) + { + palette[i] = back; + } + + else if (png_ptr->trans_alpha[i] != 0xff) + { + /* The png_composite() macro is defined in png.h */ + png_composite(palette[i].red, palette[i].red, + png_ptr->trans_alpha[i], back.red); + + png_composite(palette[i].green, palette[i].green, + png_ptr->trans_alpha[i], back.green); + + png_composite(palette[i].blue, palette[i].blue, + png_ptr->trans_alpha[i], back.blue); + } + } + + png_ptr->transformations &= ~PNG_COMPOSE; + } +#endif /* PNG_READ_BACKGROUND_SUPPORTED */ + +#ifdef PNG_READ_SHIFT_SUPPORTED + if ((png_ptr->transformations & PNG_SHIFT) && + !(png_ptr->transformations & PNG_EXPAND) && + (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE)) + { + int i; + int istop = png_ptr->num_palette; + int shift = 8 - png_ptr->sig_bit.red; + + png_ptr->transformations &= ~PNG_SHIFT; + + /* significant bits can be in the range 1 to 7 for a meaninful result, if + * the number of significant bits is 0 then no shift is done (this is an + * error condition which is silently ignored.) + */ + if (shift > 0 && shift < 8) for (i=0; ipalette[i].red; + + component >>= shift; + png_ptr->palette[i].red = (png_byte)component; + } + + shift = 8 - png_ptr->sig_bit.green; + if (shift > 0 && shift < 8) for (i=0; ipalette[i].green; + + component >>= shift; + png_ptr->palette[i].green = (png_byte)component; + } + + shift = 8 - png_ptr->sig_bit.blue; + if (shift > 0 && shift < 8) for (i=0; ipalette[i].blue; + + component >>= shift; + png_ptr->palette[i].blue = (png_byte)component; + } + } +#endif /* PNG_READ_SHIFT_SUPPORTED */ +} + +/* Modify the info structure to reflect the transformations. The + * info should be updated so a PNG file could be written with it, + * assuming the transformations result in valid PNG data. + */ +void /* PRIVATE */ +png_read_transform_info(png_structrp png_ptr, png_inforp info_ptr) +{ + png_debug(1, "in png_read_transform_info"); + +#ifdef PNG_READ_EXPAND_SUPPORTED + if (png_ptr->transformations & PNG_EXPAND) + { + if (info_ptr->color_type == PNG_COLOR_TYPE_PALETTE) + { + /* This check must match what actually happens in + * png_do_expand_palette; if it ever checks the tRNS chunk to see if + * it is all opaque we must do the same (at present it does not.) + */ + if (png_ptr->num_trans > 0) + info_ptr->color_type = PNG_COLOR_TYPE_RGB_ALPHA; + + else + info_ptr->color_type = PNG_COLOR_TYPE_RGB; + + info_ptr->bit_depth = 8; + info_ptr->num_trans = 0; + } + else + { + if (png_ptr->num_trans) + { + if (png_ptr->transformations & PNG_EXPAND_tRNS) + info_ptr->color_type |= PNG_COLOR_MASK_ALPHA; + } + if (info_ptr->bit_depth < 8) + info_ptr->bit_depth = 8; + + info_ptr->num_trans = 0; + } + } +#endif + +#if defined(PNG_READ_BACKGROUND_SUPPORTED) ||\ + defined(PNG_READ_ALPHA_MODE_SUPPORTED) + /* The following is almost certainly wrong unless the background value is in + * the screen space! + */ + if (png_ptr->transformations & PNG_COMPOSE) + info_ptr->background = png_ptr->background; +#endif + +#ifdef PNG_READ_GAMMA_SUPPORTED + /* The following used to be conditional on PNG_GAMMA (prior to 1.5.4), + * however it seems that the code in png_init_read_transformations, which has + * been called before this from png_read_update_info->png_read_start_row + * sometimes does the gamma transform and cancels the flag. + * + * TODO: this looks wrong; the info_ptr should end up with a gamma equal to + * the screen_gamma value. The following probably results in weirdness if + * the info_ptr is used by the app after the rows have been read. + */ + info_ptr->colorspace.gamma = png_ptr->colorspace.gamma; +#endif + + if (info_ptr->bit_depth == 16) + { +# ifdef PNG_READ_16BIT_SUPPORTED +# ifdef PNG_READ_SCALE_16_TO_8_SUPPORTED + if (png_ptr->transformations & PNG_SCALE_16_TO_8) + info_ptr->bit_depth = 8; +# endif + +# ifdef PNG_READ_STRIP_16_TO_8_SUPPORTED + if (png_ptr->transformations & PNG_16_TO_8) + info_ptr->bit_depth = 8; +# endif + +# else + /* No 16 bit support: force chopping 16-bit input down to 8, in this case + * the app program can chose if both APIs are available by setting the + * correct scaling to use. + */ +# ifdef PNG_READ_STRIP_16_TO_8_SUPPORTED + /* For compatibility with previous versions use the strip method by + * default. This code works because if PNG_SCALE_16_TO_8 is already + * set the code below will do that in preference to the chop. + */ + png_ptr->transformations |= PNG_16_TO_8; + info_ptr->bit_depth = 8; +# else + +# ifdef PNG_READ_SCALE_16_TO_8_SUPPORTED + png_ptr->transformations |= PNG_SCALE_16_TO_8; + info_ptr->bit_depth = 8; +# else + + CONFIGURATION ERROR: you must enable at least one 16 to 8 method +# endif +# endif +#endif /* !READ_16BIT_SUPPORTED */ + } + +#ifdef PNG_READ_GRAY_TO_RGB_SUPPORTED + if (png_ptr->transformations & PNG_GRAY_TO_RGB) + info_ptr->color_type = (png_byte)(info_ptr->color_type | + PNG_COLOR_MASK_COLOR); +#endif + +#ifdef PNG_READ_RGB_TO_GRAY_SUPPORTED + if (png_ptr->transformations & PNG_RGB_TO_GRAY) + info_ptr->color_type = (png_byte)(info_ptr->color_type & + ~PNG_COLOR_MASK_COLOR); +#endif + +#ifdef PNG_READ_QUANTIZE_SUPPORTED + if (png_ptr->transformations & PNG_QUANTIZE) + { + if (((info_ptr->color_type == PNG_COLOR_TYPE_RGB) || + (info_ptr->color_type == PNG_COLOR_TYPE_RGB_ALPHA)) && + png_ptr->palette_lookup && info_ptr->bit_depth == 8) + { + info_ptr->color_type = PNG_COLOR_TYPE_PALETTE; + } + } +#endif + +#ifdef PNG_READ_EXPAND_16_SUPPORTED + if (png_ptr->transformations & PNG_EXPAND_16 && info_ptr->bit_depth == 8 && + info_ptr->color_type != PNG_COLOR_TYPE_PALETTE) + { + info_ptr->bit_depth = 16; + } +#endif + +#ifdef PNG_READ_PACK_SUPPORTED + if ((png_ptr->transformations & PNG_PACK) && (info_ptr->bit_depth < 8)) + info_ptr->bit_depth = 8; +#endif + + if (info_ptr->color_type == PNG_COLOR_TYPE_PALETTE) + info_ptr->channels = 1; + + else if (info_ptr->color_type & PNG_COLOR_MASK_COLOR) + info_ptr->channels = 3; + + else + info_ptr->channels = 1; + +#ifdef PNG_READ_STRIP_ALPHA_SUPPORTED + if (png_ptr->transformations & PNG_STRIP_ALPHA) + { + info_ptr->color_type = (png_byte)(info_ptr->color_type & + ~PNG_COLOR_MASK_ALPHA); + info_ptr->num_trans = 0; + } +#endif + + if (info_ptr->color_type & PNG_COLOR_MASK_ALPHA) + info_ptr->channels++; + +#ifdef PNG_READ_FILLER_SUPPORTED + /* STRIP_ALPHA and FILLER allowed: MASK_ALPHA bit stripped above */ + if ((png_ptr->transformations & PNG_FILLER) && + ((info_ptr->color_type == PNG_COLOR_TYPE_RGB) || + (info_ptr->color_type == PNG_COLOR_TYPE_GRAY))) + { + info_ptr->channels++; + /* If adding a true alpha channel not just filler */ + if (png_ptr->transformations & PNG_ADD_ALPHA) + info_ptr->color_type |= PNG_COLOR_MASK_ALPHA; + } +#endif + +#if defined(PNG_USER_TRANSFORM_PTR_SUPPORTED) && \ +defined(PNG_READ_USER_TRANSFORM_SUPPORTED) + if (png_ptr->transformations & PNG_USER_TRANSFORM) + { + if (info_ptr->bit_depth < png_ptr->user_transform_depth) + info_ptr->bit_depth = png_ptr->user_transform_depth; + + if (info_ptr->channels < png_ptr->user_transform_channels) + info_ptr->channels = png_ptr->user_transform_channels; + } +#endif + + info_ptr->pixel_depth = (png_byte)(info_ptr->channels * + info_ptr->bit_depth); + + info_ptr->rowbytes = PNG_ROWBYTES(info_ptr->pixel_depth, info_ptr->width); + + /* Adding in 1.5.4: cache the above value in png_struct so that we can later + * check in png_rowbytes that the user buffer won't get overwritten. Note + * that the field is not always set - if png_read_update_info isn't called + * the application has to either not do any transforms or get the calculation + * right itself. + */ + png_ptr->info_rowbytes = info_ptr->rowbytes; + +#ifndef PNG_READ_EXPAND_SUPPORTED + if (png_ptr) + return; +#endif +} + +/* Transform the row. The order of transformations is significant, + * and is very touchy. If you add a transformation, take care to + * decide how it fits in with the other transformations here. + */ +void /* PRIVATE */ +png_do_read_transformations(png_structrp png_ptr, png_row_infop row_info) +{ + png_debug(1, "in png_do_read_transformations"); + + if (png_ptr->row_buf == NULL) + { + /* Prior to 1.5.4 this output row/pass where the NULL pointer is, but this + * error is incredibly rare and incredibly easy to debug without this + * information. + */ + png_error(png_ptr, "NULL row buffer"); + } + + /* The following is debugging; prior to 1.5.4 the code was never compiled in; + * in 1.5.4 PNG_FLAG_DETECT_UNINITIALIZED was added and the macro + * PNG_WARN_UNINITIALIZED_ROW removed. In 1.6 the new flag is set only for + * all transformations, however in practice the ROW_INIT always gets done on + * demand, if necessary. + */ + if ((png_ptr->flags & PNG_FLAG_DETECT_UNINITIALIZED) != 0 && + !(png_ptr->flags & PNG_FLAG_ROW_INIT)) + { + /* Application has failed to call either png_read_start_image() or + * png_read_update_info() after setting transforms that expand pixels. + * This check added to libpng-1.2.19 (but not enabled until 1.5.4). + */ + png_error(png_ptr, "Uninitialized row"); + } + +#ifdef PNG_READ_EXPAND_SUPPORTED + if (png_ptr->transformations & PNG_EXPAND) + { + if (row_info->color_type == PNG_COLOR_TYPE_PALETTE) + { + png_do_expand_palette(row_info, png_ptr->row_buf + 1, + png_ptr->palette, png_ptr->trans_alpha, png_ptr->num_trans); + } + + else + { + if (png_ptr->num_trans && + (png_ptr->transformations & PNG_EXPAND_tRNS)) + png_do_expand(row_info, png_ptr->row_buf + 1, + &(png_ptr->trans_color)); + + else + png_do_expand(row_info, png_ptr->row_buf + 1, + NULL); + } + } +#endif + +#ifdef PNG_READ_STRIP_ALPHA_SUPPORTED + if ((png_ptr->transformations & PNG_STRIP_ALPHA) && + !(png_ptr->transformations & PNG_COMPOSE) && + (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA || + row_info->color_type == PNG_COLOR_TYPE_GRAY_ALPHA)) + png_do_strip_channel(row_info, png_ptr->row_buf + 1, + 0 /* at_start == false, because SWAP_ALPHA happens later */); +#endif + +#ifdef PNG_READ_RGB_TO_GRAY_SUPPORTED + if (png_ptr->transformations & PNG_RGB_TO_GRAY) + { + int rgb_error = + png_do_rgb_to_gray(png_ptr, row_info, + png_ptr->row_buf + 1); + + if (rgb_error) + { + png_ptr->rgb_to_gray_status=1; + if ((png_ptr->transformations & PNG_RGB_TO_GRAY) == + PNG_RGB_TO_GRAY_WARN) + png_warning(png_ptr, "png_do_rgb_to_gray found nongray pixel"); + + if ((png_ptr->transformations & PNG_RGB_TO_GRAY) == + PNG_RGB_TO_GRAY_ERR) + png_error(png_ptr, "png_do_rgb_to_gray found nongray pixel"); + } + } +#endif + +/* From Andreas Dilger e-mail to png-implement, 26 March 1998: + * + * In most cases, the "simple transparency" should be done prior to doing + * gray-to-RGB, or you will have to test 3x as many bytes to check if a + * pixel is transparent. You would also need to make sure that the + * transparency information is upgraded to RGB. + * + * To summarize, the current flow is: + * - Gray + simple transparency -> compare 1 or 2 gray bytes and composite + * with background "in place" if transparent, + * convert to RGB if necessary + * - Gray + alpha -> composite with gray background and remove alpha bytes, + * convert to RGB if necessary + * + * To support RGB backgrounds for gray images we need: + * - Gray + simple transparency -> convert to RGB + simple transparency, + * compare 3 or 6 bytes and composite with + * background "in place" if transparent + * (3x compare/pixel compared to doing + * composite with gray bkgrnd) + * - Gray + alpha -> convert to RGB + alpha, composite with background and + * remove alpha bytes (3x float + * operations/pixel compared with composite + * on gray background) + * + * Greg's change will do this. The reason it wasn't done before is for + * performance, as this increases the per-pixel operations. If we would check + * in advance if the background was gray or RGB, and position the gray-to-RGB + * transform appropriately, then it would save a lot of work/time. + */ + +#ifdef PNG_READ_GRAY_TO_RGB_SUPPORTED + /* If gray -> RGB, do so now only if background is non-gray; else do later + * for performance reasons + */ + if ((png_ptr->transformations & PNG_GRAY_TO_RGB) && + !(png_ptr->mode & PNG_BACKGROUND_IS_GRAY)) + png_do_gray_to_rgb(row_info, png_ptr->row_buf + 1); +#endif + +#if defined(PNG_READ_BACKGROUND_SUPPORTED) ||\ + defined(PNG_READ_ALPHA_MODE_SUPPORTED) + if (png_ptr->transformations & PNG_COMPOSE) + png_do_compose(row_info, png_ptr->row_buf + 1, png_ptr); +#endif + +#ifdef PNG_READ_GAMMA_SUPPORTED + if ((png_ptr->transformations & PNG_GAMMA) && +#ifdef PNG_READ_RGB_TO_GRAY_SUPPORTED + /* Because RGB_TO_GRAY does the gamma transform. */ + !(png_ptr->transformations & PNG_RGB_TO_GRAY) && +#endif +#if defined(PNG_READ_BACKGROUND_SUPPORTED) ||\ + defined(PNG_READ_ALPHA_MODE_SUPPORTED) + /* Because PNG_COMPOSE does the gamma transform if there is something to + * do (if there is an alpha channel or transparency.) + */ + !((png_ptr->transformations & PNG_COMPOSE) && + ((png_ptr->num_trans != 0) || + (png_ptr->color_type & PNG_COLOR_MASK_ALPHA))) && +#endif + /* Because png_init_read_transformations transforms the palette, unless + * RGB_TO_GRAY will do the transform. + */ + (png_ptr->color_type != PNG_COLOR_TYPE_PALETTE)) + png_do_gamma(row_info, png_ptr->row_buf + 1, png_ptr); +#endif + +#ifdef PNG_READ_STRIP_ALPHA_SUPPORTED + if ((png_ptr->transformations & PNG_STRIP_ALPHA) && + (png_ptr->transformations & PNG_COMPOSE) && + (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA || + row_info->color_type == PNG_COLOR_TYPE_GRAY_ALPHA)) + png_do_strip_channel(row_info, png_ptr->row_buf + 1, + 0 /* at_start == false, because SWAP_ALPHA happens later */); +#endif + +#ifdef PNG_READ_ALPHA_MODE_SUPPORTED + if ((png_ptr->transformations & PNG_ENCODE_ALPHA) && + (row_info->color_type & PNG_COLOR_MASK_ALPHA)) + png_do_encode_alpha(row_info, png_ptr->row_buf + 1, png_ptr); +#endif + +#ifdef PNG_READ_SCALE_16_TO_8_SUPPORTED + if (png_ptr->transformations & PNG_SCALE_16_TO_8) + png_do_scale_16_to_8(row_info, png_ptr->row_buf + 1); +#endif + +#ifdef PNG_READ_STRIP_16_TO_8_SUPPORTED + /* There is no harm in doing both of these because only one has any effect, + * by putting the 'scale' option first if the app asks for scale (either by + * calling the API or in a TRANSFORM flag) this is what happens. + */ + if (png_ptr->transformations & PNG_16_TO_8) + png_do_chop(row_info, png_ptr->row_buf + 1); +#endif + +#ifdef PNG_READ_QUANTIZE_SUPPORTED + if (png_ptr->transformations & PNG_QUANTIZE) + { + png_do_quantize(row_info, png_ptr->row_buf + 1, + png_ptr->palette_lookup, png_ptr->quantize_index); + + if (row_info->rowbytes == 0) + png_error(png_ptr, "png_do_quantize returned rowbytes=0"); + } +#endif /* PNG_READ_QUANTIZE_SUPPORTED */ + +#ifdef PNG_READ_EXPAND_16_SUPPORTED + /* Do the expansion now, after all the arithmetic has been done. Notice + * that previous transformations can handle the PNG_EXPAND_16 flag if this + * is efficient (particularly true in the case of gamma correction, where + * better accuracy results faster!) + */ + if (png_ptr->transformations & PNG_EXPAND_16) + png_do_expand_16(row_info, png_ptr->row_buf + 1); +#endif + +#ifdef PNG_READ_GRAY_TO_RGB_SUPPORTED + /* NOTE: moved here in 1.5.4 (from much later in this list.) */ + if ((png_ptr->transformations & PNG_GRAY_TO_RGB) && + (png_ptr->mode & PNG_BACKGROUND_IS_GRAY)) + png_do_gray_to_rgb(row_info, png_ptr->row_buf + 1); +#endif + +#ifdef PNG_READ_INVERT_SUPPORTED + if (png_ptr->transformations & PNG_INVERT_MONO) + png_do_invert(row_info, png_ptr->row_buf + 1); +#endif + +#ifdef PNG_READ_SHIFT_SUPPORTED + if (png_ptr->transformations & PNG_SHIFT) + png_do_unshift(row_info, png_ptr->row_buf + 1, + &(png_ptr->shift)); +#endif + +#ifdef PNG_READ_PACK_SUPPORTED + if (png_ptr->transformations & PNG_PACK) + png_do_unpack(row_info, png_ptr->row_buf + 1); +#endif + +#ifdef PNG_READ_CHECK_FOR_INVALID_INDEX_SUPPORTED + /* Added at libpng-1.5.10 */ + if (row_info->color_type == PNG_COLOR_TYPE_PALETTE && + png_ptr->num_palette_max >= 0) + png_do_check_palette_indexes(png_ptr, row_info); +#endif + +#ifdef PNG_READ_BGR_SUPPORTED + if (png_ptr->transformations & PNG_BGR) + png_do_bgr(row_info, png_ptr->row_buf + 1); +#endif + +#ifdef PNG_READ_PACKSWAP_SUPPORTED + if (png_ptr->transformations & PNG_PACKSWAP) + png_do_packswap(row_info, png_ptr->row_buf + 1); +#endif + +#ifdef PNG_READ_FILLER_SUPPORTED + if (png_ptr->transformations & PNG_FILLER) + png_do_read_filler(row_info, png_ptr->row_buf + 1, + (png_uint_32)png_ptr->filler, png_ptr->flags); +#endif + +#ifdef PNG_READ_INVERT_ALPHA_SUPPORTED + if (png_ptr->transformations & PNG_INVERT_ALPHA) + png_do_read_invert_alpha(row_info, png_ptr->row_buf + 1); +#endif + +#ifdef PNG_READ_SWAP_ALPHA_SUPPORTED + if (png_ptr->transformations & PNG_SWAP_ALPHA) + png_do_read_swap_alpha(row_info, png_ptr->row_buf + 1); +#endif + +#ifdef PNG_READ_16BIT_SUPPORTED +#ifdef PNG_READ_SWAP_SUPPORTED + if (png_ptr->transformations & PNG_SWAP_BYTES) + png_do_swap(row_info, png_ptr->row_buf + 1); +#endif +#endif + +#ifdef PNG_READ_USER_TRANSFORM_SUPPORTED + if (png_ptr->transformations & PNG_USER_TRANSFORM) + { + if (png_ptr->read_user_transform_fn != NULL) + (*(png_ptr->read_user_transform_fn)) /* User read transform function */ + (png_ptr, /* png_ptr */ + row_info, /* row_info: */ + /* png_uint_32 width; width of row */ + /* png_size_t rowbytes; number of bytes in row */ + /* png_byte color_type; color type of pixels */ + /* png_byte bit_depth; bit depth of samples */ + /* png_byte channels; number of channels (1-4) */ + /* png_byte pixel_depth; bits per pixel (depth*channels) */ + png_ptr->row_buf + 1); /* start of pixel data for row */ +#ifdef PNG_USER_TRANSFORM_PTR_SUPPORTED + if (png_ptr->user_transform_depth) + row_info->bit_depth = png_ptr->user_transform_depth; + + if (png_ptr->user_transform_channels) + row_info->channels = png_ptr->user_transform_channels; +#endif + row_info->pixel_depth = (png_byte)(row_info->bit_depth * + row_info->channels); + + row_info->rowbytes = PNG_ROWBYTES(row_info->pixel_depth, row_info->width); + } +#endif +} + +#ifdef PNG_READ_PACK_SUPPORTED +/* Unpack pixels of 1, 2, or 4 bits per pixel into 1 byte per pixel, + * without changing the actual values. Thus, if you had a row with + * a bit depth of 1, you would end up with bytes that only contained + * the numbers 0 or 1. If you would rather they contain 0 and 255, use + * png_do_shift() after this. + */ +void /* PRIVATE */ +png_do_unpack(png_row_infop row_info, png_bytep row) +{ + png_debug(1, "in png_do_unpack"); + + if (row_info->bit_depth < 8) + { + png_uint_32 i; + png_uint_32 row_width=row_info->width; + + switch (row_info->bit_depth) + { + case 1: + { + png_bytep sp = row + (png_size_t)((row_width - 1) >> 3); + png_bytep dp = row + (png_size_t)row_width - 1; + png_uint_32 shift = 7 - (int)((row_width + 7) & 0x07); + for (i = 0; i < row_width; i++) + { + *dp = (png_byte)((*sp >> shift) & 0x01); + + if (shift == 7) + { + shift = 0; + sp--; + } + + else + shift++; + + dp--; + } + break; + } + + case 2: + { + + png_bytep sp = row + (png_size_t)((row_width - 1) >> 2); + png_bytep dp = row + (png_size_t)row_width - 1; + png_uint_32 shift = (int)((3 - ((row_width + 3) & 0x03)) << 1); + for (i = 0; i < row_width; i++) + { + *dp = (png_byte)((*sp >> shift) & 0x03); + + if (shift == 6) + { + shift = 0; + sp--; + } + + else + shift += 2; + + dp--; + } + break; + } + + case 4: + { + png_bytep sp = row + (png_size_t)((row_width - 1) >> 1); + png_bytep dp = row + (png_size_t)row_width - 1; + png_uint_32 shift = (int)((1 - ((row_width + 1) & 0x01)) << 2); + for (i = 0; i < row_width; i++) + { + *dp = (png_byte)((*sp >> shift) & 0x0f); + + if (shift == 4) + { + shift = 0; + sp--; + } + + else + shift = 4; + + dp--; + } + break; + } + + default: + break; + } + row_info->bit_depth = 8; + row_info->pixel_depth = (png_byte)(8 * row_info->channels); + row_info->rowbytes = row_width * row_info->channels; + } +} +#endif + +#ifdef PNG_READ_SHIFT_SUPPORTED +/* Reverse the effects of png_do_shift. This routine merely shifts the + * pixels back to their significant bits values. Thus, if you have + * a row of bit depth 8, but only 5 are significant, this will shift + * the values back to 0 through 31. + */ +void /* PRIVATE */ +png_do_unshift(png_row_infop row_info, png_bytep row, + png_const_color_8p sig_bits) +{ + int color_type; + + png_debug(1, "in png_do_unshift"); + + /* The palette case has already been handled in the _init routine. */ + color_type = row_info->color_type; + + if (color_type != PNG_COLOR_TYPE_PALETTE) + { + int shift[4]; + int channels = 0; + int bit_depth = row_info->bit_depth; + + if (color_type & PNG_COLOR_MASK_COLOR) + { + shift[channels++] = bit_depth - sig_bits->red; + shift[channels++] = bit_depth - sig_bits->green; + shift[channels++] = bit_depth - sig_bits->blue; + } + + else + { + shift[channels++] = bit_depth - sig_bits->gray; + } + + if (color_type & PNG_COLOR_MASK_ALPHA) + { + shift[channels++] = bit_depth - sig_bits->alpha; + } + + { + int c, have_shift; + + for (c = have_shift = 0; c < channels; ++c) + { + /* A shift of more than the bit depth is an error condition but it + * gets ignored here. + */ + if (shift[c] <= 0 || shift[c] >= bit_depth) + shift[c] = 0; + + else + have_shift = 1; + } + + if (!have_shift) + return; + } + + switch (bit_depth) + { + default: + /* Must be 1bpp gray: should not be here! */ + /* NOTREACHED */ + break; + + case 2: + /* Must be 2bpp gray */ + /* assert(channels == 1 && shift[0] == 1) */ + { + png_bytep bp = row; + png_bytep bp_end = bp + row_info->rowbytes; + + while (bp < bp_end) + { + int b = (*bp >> 1) & 0x55; + *bp++ = (png_byte)b; + } + break; + } + + case 4: + /* Must be 4bpp gray */ + /* assert(channels == 1) */ + { + png_bytep bp = row; + png_bytep bp_end = bp + row_info->rowbytes; + int gray_shift = shift[0]; + int mask = 0xf >> gray_shift; + + mask |= mask << 4; + + while (bp < bp_end) + { + int b = (*bp >> gray_shift) & mask; + *bp++ = (png_byte)b; + } + break; + } + + case 8: + /* Single byte components, G, GA, RGB, RGBA */ + { + png_bytep bp = row; + png_bytep bp_end = bp + row_info->rowbytes; + int channel = 0; + + while (bp < bp_end) + { + int b = *bp >> shift[channel]; + if (++channel >= channels) + channel = 0; + *bp++ = (png_byte)b; + } + break; + } + +#ifdef PNG_READ_16BIT_SUPPORTED + case 16: + /* Double byte components, G, GA, RGB, RGBA */ + { + png_bytep bp = row; + png_bytep bp_end = bp + row_info->rowbytes; + int channel = 0; + + while (bp < bp_end) + { + int value = (bp[0] << 8) + bp[1]; + + value >>= shift[channel]; + if (++channel >= channels) + channel = 0; + *bp++ = (png_byte)(value >> 8); + *bp++ = (png_byte)(value & 0xff); + } + break; + } +#endif + } + } +} +#endif + +#ifdef PNG_READ_SCALE_16_TO_8_SUPPORTED +/* Scale rows of bit depth 16 down to 8 accurately */ +void /* PRIVATE */ +png_do_scale_16_to_8(png_row_infop row_info, png_bytep row) +{ + png_debug(1, "in png_do_scale_16_to_8"); + + if (row_info->bit_depth == 16) + { + png_bytep sp = row; /* source */ + png_bytep dp = row; /* destination */ + png_bytep ep = sp + row_info->rowbytes; /* end+1 */ + + while (sp < ep) + { + /* The input is an array of 16 bit components, these must be scaled to + * 8 bits each. For a 16 bit value V the required value (from the PNG + * specification) is: + * + * (V * 255) / 65535 + * + * This reduces to round(V / 257), or floor((V + 128.5)/257) + * + * Represent V as the two byte value vhi.vlo. Make a guess that the + * result is the top byte of V, vhi, then the correction to this value + * is: + * + * error = floor(((V-vhi.vhi) + 128.5) / 257) + * = floor(((vlo-vhi) + 128.5) / 257) + * + * This can be approximated using integer arithmetic (and a signed + * shift): + * + * error = (vlo-vhi+128) >> 8; + * + * The approximate differs from the exact answer only when (vlo-vhi) is + * 128; it then gives a correction of +1 when the exact correction is + * 0. This gives 128 errors. The exact answer (correct for all 16 bit + * input values) is: + * + * error = (vlo-vhi+128)*65535 >> 24; + * + * An alternative arithmetic calculation which also gives no errors is: + * + * (V * 255 + 32895) >> 16 + */ + + png_int_32 tmp = *sp++; /* must be signed! */ + tmp += (((int)*sp++ - tmp + 128) * 65535) >> 24; + *dp++ = (png_byte)tmp; + } + + row_info->bit_depth = 8; + row_info->pixel_depth = (png_byte)(8 * row_info->channels); + row_info->rowbytes = row_info->width * row_info->channels; + } +} +#endif + +#ifdef PNG_READ_STRIP_16_TO_8_SUPPORTED +void /* PRIVATE */ +/* Simply discard the low byte. This was the default behavior prior + * to libpng-1.5.4. + */ +png_do_chop(png_row_infop row_info, png_bytep row) +{ + png_debug(1, "in png_do_chop"); + + if (row_info->bit_depth == 16) + { + png_bytep sp = row; /* source */ + png_bytep dp = row; /* destination */ + png_bytep ep = sp + row_info->rowbytes; /* end+1 */ + + while (sp < ep) + { + *dp++ = *sp; + sp += 2; /* skip low byte */ + } + + row_info->bit_depth = 8; + row_info->pixel_depth = (png_byte)(8 * row_info->channels); + row_info->rowbytes = row_info->width * row_info->channels; + } +} +#endif + +#ifdef PNG_READ_SWAP_ALPHA_SUPPORTED +void /* PRIVATE */ +png_do_read_swap_alpha(png_row_infop row_info, png_bytep row) +{ + png_debug(1, "in png_do_read_swap_alpha"); + + { + png_uint_32 row_width = row_info->width; + if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA) + { + /* This converts from RGBA to ARGB */ + if (row_info->bit_depth == 8) + { + png_bytep sp = row + row_info->rowbytes; + png_bytep dp = sp; + png_byte save; + png_uint_32 i; + + for (i = 0; i < row_width; i++) + { + save = *(--sp); + *(--dp) = *(--sp); + *(--dp) = *(--sp); + *(--dp) = *(--sp); + *(--dp) = save; + } + } + +#ifdef PNG_READ_16BIT_SUPPORTED + /* This converts from RRGGBBAA to AARRGGBB */ + else + { + png_bytep sp = row + row_info->rowbytes; + png_bytep dp = sp; + png_byte save[2]; + png_uint_32 i; + + for (i = 0; i < row_width; i++) + { + save[0] = *(--sp); + save[1] = *(--sp); + *(--dp) = *(--sp); + *(--dp) = *(--sp); + *(--dp) = *(--sp); + *(--dp) = *(--sp); + *(--dp) = *(--sp); + *(--dp) = *(--sp); + *(--dp) = save[0]; + *(--dp) = save[1]; + } + } +#endif + } + + else if (row_info->color_type == PNG_COLOR_TYPE_GRAY_ALPHA) + { + /* This converts from GA to AG */ + if (row_info->bit_depth == 8) + { + png_bytep sp = row + row_info->rowbytes; + png_bytep dp = sp; + png_byte save; + png_uint_32 i; + + for (i = 0; i < row_width; i++) + { + save = *(--sp); + *(--dp) = *(--sp); + *(--dp) = save; + } + } + +#ifdef PNG_READ_16BIT_SUPPORTED + /* This converts from GGAA to AAGG */ + else + { + png_bytep sp = row + row_info->rowbytes; + png_bytep dp = sp; + png_byte save[2]; + png_uint_32 i; + + for (i = 0; i < row_width; i++) + { + save[0] = *(--sp); + save[1] = *(--sp); + *(--dp) = *(--sp); + *(--dp) = *(--sp); + *(--dp) = save[0]; + *(--dp) = save[1]; + } + } +#endif + } + } +} +#endif + +#ifdef PNG_READ_INVERT_ALPHA_SUPPORTED +void /* PRIVATE */ +png_do_read_invert_alpha(png_row_infop row_info, png_bytep row) +{ + png_uint_32 row_width; + png_debug(1, "in png_do_read_invert_alpha"); + + row_width = row_info->width; + if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA) + { + if (row_info->bit_depth == 8) + { + /* This inverts the alpha channel in RGBA */ + png_bytep sp = row + row_info->rowbytes; + png_bytep dp = sp; + png_uint_32 i; + + for (i = 0; i < row_width; i++) + { + *(--dp) = (png_byte)(255 - *(--sp)); + +/* This does nothing: + *(--dp) = *(--sp); + *(--dp) = *(--sp); + *(--dp) = *(--sp); + We can replace it with: +*/ + sp-=3; + dp=sp; + } + } + +#ifdef PNG_READ_16BIT_SUPPORTED + /* This inverts the alpha channel in RRGGBBAA */ + else + { + png_bytep sp = row + row_info->rowbytes; + png_bytep dp = sp; + png_uint_32 i; + + for (i = 0; i < row_width; i++) + { + *(--dp) = (png_byte)(255 - *(--sp)); + *(--dp) = (png_byte)(255 - *(--sp)); + +/* This does nothing: + *(--dp) = *(--sp); + *(--dp) = *(--sp); + *(--dp) = *(--sp); + *(--dp) = *(--sp); + *(--dp) = *(--sp); + *(--dp) = *(--sp); + We can replace it with: +*/ + sp-=6; + dp=sp; + } + } +#endif + } + else if (row_info->color_type == PNG_COLOR_TYPE_GRAY_ALPHA) + { + if (row_info->bit_depth == 8) + { + /* This inverts the alpha channel in GA */ + png_bytep sp = row + row_info->rowbytes; + png_bytep dp = sp; + png_uint_32 i; + + for (i = 0; i < row_width; i++) + { + *(--dp) = (png_byte)(255 - *(--sp)); + *(--dp) = *(--sp); + } + } + +#ifdef PNG_READ_16BIT_SUPPORTED + else + { + /* This inverts the alpha channel in GGAA */ + png_bytep sp = row + row_info->rowbytes; + png_bytep dp = sp; + png_uint_32 i; + + for (i = 0; i < row_width; i++) + { + *(--dp) = (png_byte)(255 - *(--sp)); + *(--dp) = (png_byte)(255 - *(--sp)); +/* + *(--dp) = *(--sp); + *(--dp) = *(--sp); +*/ + sp-=2; + dp=sp; + } + } +#endif + } +} +#endif + +#ifdef PNG_READ_FILLER_SUPPORTED +/* Add filler channel if we have RGB color */ +void /* PRIVATE */ +png_do_read_filler(png_row_infop row_info, png_bytep row, + png_uint_32 filler, png_uint_32 flags) +{ + png_uint_32 i; + png_uint_32 row_width = row_info->width; + +#ifdef PNG_READ_16BIT_SUPPORTED + png_byte hi_filler = (png_byte)((filler>>8) & 0xff); +#endif + png_byte lo_filler = (png_byte)(filler & 0xff); + + png_debug(1, "in png_do_read_filler"); + + if ( + row_info->color_type == PNG_COLOR_TYPE_GRAY) + { + if (row_info->bit_depth == 8) + { + if (flags & PNG_FLAG_FILLER_AFTER) + { + /* This changes the data from G to GX */ + png_bytep sp = row + (png_size_t)row_width; + png_bytep dp = sp + (png_size_t)row_width; + for (i = 1; i < row_width; i++) + { + *(--dp) = lo_filler; + *(--dp) = *(--sp); + } + *(--dp) = lo_filler; + row_info->channels = 2; + row_info->pixel_depth = 16; + row_info->rowbytes = row_width * 2; + } + + else + { + /* This changes the data from G to XG */ + png_bytep sp = row + (png_size_t)row_width; + png_bytep dp = sp + (png_size_t)row_width; + for (i = 0; i < row_width; i++) + { + *(--dp) = *(--sp); + *(--dp) = lo_filler; + } + row_info->channels = 2; + row_info->pixel_depth = 16; + row_info->rowbytes = row_width * 2; + } + } + +#ifdef PNG_READ_16BIT_SUPPORTED + else if (row_info->bit_depth == 16) + { + if (flags & PNG_FLAG_FILLER_AFTER) + { + /* This changes the data from GG to GGXX */ + png_bytep sp = row + (png_size_t)row_width * 2; + png_bytep dp = sp + (png_size_t)row_width * 2; + for (i = 1; i < row_width; i++) + { + *(--dp) = hi_filler; + *(--dp) = lo_filler; + *(--dp) = *(--sp); + *(--dp) = *(--sp); + } + *(--dp) = hi_filler; + *(--dp) = lo_filler; + row_info->channels = 2; + row_info->pixel_depth = 32; + row_info->rowbytes = row_width * 4; + } + + else + { + /* This changes the data from GG to XXGG */ + png_bytep sp = row + (png_size_t)row_width * 2; + png_bytep dp = sp + (png_size_t)row_width * 2; + for (i = 0; i < row_width; i++) + { + *(--dp) = *(--sp); + *(--dp) = *(--sp); + *(--dp) = hi_filler; + *(--dp) = lo_filler; + } + row_info->channels = 2; + row_info->pixel_depth = 32; + row_info->rowbytes = row_width * 4; + } + } +#endif + } /* COLOR_TYPE == GRAY */ + else if (row_info->color_type == PNG_COLOR_TYPE_RGB) + { + if (row_info->bit_depth == 8) + { + if (flags & PNG_FLAG_FILLER_AFTER) + { + /* This changes the data from RGB to RGBX */ + png_bytep sp = row + (png_size_t)row_width * 3; + png_bytep dp = sp + (png_size_t)row_width; + for (i = 1; i < row_width; i++) + { + *(--dp) = lo_filler; + *(--dp) = *(--sp); + *(--dp) = *(--sp); + *(--dp) = *(--sp); + } + *(--dp) = lo_filler; + row_info->channels = 4; + row_info->pixel_depth = 32; + row_info->rowbytes = row_width * 4; + } + + else + { + /* This changes the data from RGB to XRGB */ + png_bytep sp = row + (png_size_t)row_width * 3; + png_bytep dp = sp + (png_size_t)row_width; + for (i = 0; i < row_width; i++) + { + *(--dp) = *(--sp); + *(--dp) = *(--sp); + *(--dp) = *(--sp); + *(--dp) = lo_filler; + } + row_info->channels = 4; + row_info->pixel_depth = 32; + row_info->rowbytes = row_width * 4; + } + } + +#ifdef PNG_READ_16BIT_SUPPORTED + else if (row_info->bit_depth == 16) + { + if (flags & PNG_FLAG_FILLER_AFTER) + { + /* This changes the data from RRGGBB to RRGGBBXX */ + png_bytep sp = row + (png_size_t)row_width * 6; + png_bytep dp = sp + (png_size_t)row_width * 2; + for (i = 1; i < row_width; i++) + { + *(--dp) = hi_filler; + *(--dp) = lo_filler; + *(--dp) = *(--sp); + *(--dp) = *(--sp); + *(--dp) = *(--sp); + *(--dp) = *(--sp); + *(--dp) = *(--sp); + *(--dp) = *(--sp); + } + *(--dp) = hi_filler; + *(--dp) = lo_filler; + row_info->channels = 4; + row_info->pixel_depth = 64; + row_info->rowbytes = row_width * 8; + } + + else + { + /* This changes the data from RRGGBB to XXRRGGBB */ + png_bytep sp = row + (png_size_t)row_width * 6; + png_bytep dp = sp + (png_size_t)row_width * 2; + for (i = 0; i < row_width; i++) + { + *(--dp) = *(--sp); + *(--dp) = *(--sp); + *(--dp) = *(--sp); + *(--dp) = *(--sp); + *(--dp) = *(--sp); + *(--dp) = *(--sp); + *(--dp) = hi_filler; + *(--dp) = lo_filler; + } + + row_info->channels = 4; + row_info->pixel_depth = 64; + row_info->rowbytes = row_width * 8; + } + } +#endif + } /* COLOR_TYPE == RGB */ +} +#endif + +#ifdef PNG_READ_GRAY_TO_RGB_SUPPORTED +/* Expand grayscale files to RGB, with or without alpha */ +void /* PRIVATE */ +png_do_gray_to_rgb(png_row_infop row_info, png_bytep row) +{ + png_uint_32 i; + png_uint_32 row_width = row_info->width; + + png_debug(1, "in png_do_gray_to_rgb"); + + if (row_info->bit_depth >= 8 && + !(row_info->color_type & PNG_COLOR_MASK_COLOR)) + { + if (row_info->color_type == PNG_COLOR_TYPE_GRAY) + { + if (row_info->bit_depth == 8) + { + /* This changes G to RGB */ + png_bytep sp = row + (png_size_t)row_width - 1; + png_bytep dp = sp + (png_size_t)row_width * 2; + for (i = 0; i < row_width; i++) + { + *(dp--) = *sp; + *(dp--) = *sp; + *(dp--) = *(sp--); + } + } + + else + { + /* This changes GG to RRGGBB */ + png_bytep sp = row + (png_size_t)row_width * 2 - 1; + png_bytep dp = sp + (png_size_t)row_width * 4; + for (i = 0; i < row_width; i++) + { + *(dp--) = *sp; + *(dp--) = *(sp - 1); + *(dp--) = *sp; + *(dp--) = *(sp - 1); + *(dp--) = *(sp--); + *(dp--) = *(sp--); + } + } + } + + else if (row_info->color_type == PNG_COLOR_TYPE_GRAY_ALPHA) + { + if (row_info->bit_depth == 8) + { + /* This changes GA to RGBA */ + png_bytep sp = row + (png_size_t)row_width * 2 - 1; + png_bytep dp = sp + (png_size_t)row_width * 2; + for (i = 0; i < row_width; i++) + { + *(dp--) = *(sp--); + *(dp--) = *sp; + *(dp--) = *sp; + *(dp--) = *(sp--); + } + } + + else + { + /* This changes GGAA to RRGGBBAA */ + png_bytep sp = row + (png_size_t)row_width * 4 - 1; + png_bytep dp = sp + (png_size_t)row_width * 4; + for (i = 0; i < row_width; i++) + { + *(dp--) = *(sp--); + *(dp--) = *(sp--); + *(dp--) = *sp; + *(dp--) = *(sp - 1); + *(dp--) = *sp; + *(dp--) = *(sp - 1); + *(dp--) = *(sp--); + *(dp--) = *(sp--); + } + } + } + row_info->channels = (png_byte)(row_info->channels + 2); + row_info->color_type |= PNG_COLOR_MASK_COLOR; + row_info->pixel_depth = (png_byte)(row_info->channels * + row_info->bit_depth); + row_info->rowbytes = PNG_ROWBYTES(row_info->pixel_depth, row_width); + } +} +#endif + +#ifdef PNG_READ_RGB_TO_GRAY_SUPPORTED +/* Reduce RGB files to grayscale, with or without alpha + * using the equation given in Poynton's ColorFAQ of 1998-01-04 at + * (THIS LINK IS DEAD June 2008 but + * versions dated 1998 through November 2002 have been archived at + * http://web.archive.org/web/20000816232553/http://www.inforamp.net/ + * ~poynton/notes/colour_and_gamma/ColorFAQ.txt ) + * Charles Poynton poynton at poynton.com + * + * Y = 0.212671 * R + 0.715160 * G + 0.072169 * B + * + * which can be expressed with integers as + * + * Y = (6969 * R + 23434 * G + 2365 * B)/32768 + * + * Poynton's current link (as of January 2003 through July 2011): + * + * has changed the numbers slightly: + * + * Y = 0.2126*R + 0.7152*G + 0.0722*B + * + * which can be expressed with integers as + * + * Y = (6966 * R + 23436 * G + 2366 * B)/32768 + * + * Historically, however, libpng uses numbers derived from the ITU-R Rec 709 + * end point chromaticities and the D65 white point. Depending on the + * precision used for the D65 white point this produces a variety of different + * numbers, however if the four decimal place value used in ITU-R Rec 709 is + * used (0.3127,0.3290) the Y calculation would be: + * + * Y = (6968 * R + 23435 * G + 2366 * B)/32768 + * + * While this is correct the rounding results in an overflow for white, because + * the sum of the rounded coefficients is 32769, not 32768. Consequently + * libpng uses, instead, the closest non-overflowing approximation: + * + * Y = (6968 * R + 23434 * G + 2366 * B)/32768 + * + * Starting with libpng-1.5.5, if the image being converted has a cHRM chunk + * (including an sRGB chunk) then the chromaticities are used to calculate the + * coefficients. See the chunk handling in pngrutil.c for more information. + * + * In all cases the calculation is to be done in a linear colorspace. If no + * gamma information is available to correct the encoding of the original RGB + * values this results in an implicit assumption that the original PNG RGB + * values were linear. + * + * Other integer coefficents can be used via png_set_rgb_to_gray(). Because + * the API takes just red and green coefficients the blue coefficient is + * calculated to make the sum 32768. This will result in different rounding + * to that used above. + */ +int /* PRIVATE */ +png_do_rgb_to_gray(png_structrp png_ptr, png_row_infop row_info, png_bytep row) + +{ + int rgb_error = 0; + + png_debug(1, "in png_do_rgb_to_gray"); + + if (!(row_info->color_type & PNG_COLOR_MASK_PALETTE) && + (row_info->color_type & PNG_COLOR_MASK_COLOR)) + { + PNG_CONST png_uint_32 rc = png_ptr->rgb_to_gray_red_coeff; + PNG_CONST png_uint_32 gc = png_ptr->rgb_to_gray_green_coeff; + PNG_CONST png_uint_32 bc = 32768 - rc - gc; + PNG_CONST png_uint_32 row_width = row_info->width; + PNG_CONST int have_alpha = + (row_info->color_type & PNG_COLOR_MASK_ALPHA) != 0; + + if (row_info->bit_depth == 8) + { +#ifdef PNG_READ_GAMMA_SUPPORTED + /* Notice that gamma to/from 1 are not necessarily inverses (if + * there is an overall gamma correction). Prior to 1.5.5 this code + * checked the linearized values for equality; this doesn't match + * the documentation, the original values must be checked. + */ + if (png_ptr->gamma_from_1 != NULL && png_ptr->gamma_to_1 != NULL) + { + png_bytep sp = row; + png_bytep dp = row; + png_uint_32 i; + + for (i = 0; i < row_width; i++) + { + png_byte red = *(sp++); + png_byte green = *(sp++); + png_byte blue = *(sp++); + + if (red != green || red != blue) + { + red = png_ptr->gamma_to_1[red]; + green = png_ptr->gamma_to_1[green]; + blue = png_ptr->gamma_to_1[blue]; + + rgb_error |= 1; + *(dp++) = png_ptr->gamma_from_1[ + (rc*red + gc*green + bc*blue + 16384)>>15]; + } + + else + { + /* If there is no overall correction the table will not be + * set. + */ + if (png_ptr->gamma_table != NULL) + red = png_ptr->gamma_table[red]; + + *(dp++) = red; + } + + if (have_alpha) + *(dp++) = *(sp++); + } + } + else +#endif + { + png_bytep sp = row; + png_bytep dp = row; + png_uint_32 i; + + for (i = 0; i < row_width; i++) + { + png_byte red = *(sp++); + png_byte green = *(sp++); + png_byte blue = *(sp++); + + if (red != green || red != blue) + { + rgb_error |= 1; + /* NOTE: this is the historical approach which simply + * truncates the results. + */ + *(dp++) = (png_byte)((rc*red + gc*green + bc*blue)>>15); + } + + else + *(dp++) = red; + + if (have_alpha) + *(dp++) = *(sp++); + } + } + } + + else /* RGB bit_depth == 16 */ + { +#ifdef PNG_READ_GAMMA_SUPPORTED + if (png_ptr->gamma_16_to_1 != NULL && png_ptr->gamma_16_from_1 != NULL) + { + png_bytep sp = row; + png_bytep dp = row; + png_uint_32 i; + + for (i = 0; i < row_width; i++) + { + png_uint_16 red, green, blue, w; + + red = (png_uint_16)(((*(sp))<<8) | *(sp + 1)); sp += 2; + green = (png_uint_16)(((*(sp))<<8) | *(sp + 1)); sp += 2; + blue = (png_uint_16)(((*(sp))<<8) | *(sp + 1)); sp += 2; + + if (red == green && red == blue) + { + if (png_ptr->gamma_16_table != NULL) + w = png_ptr->gamma_16_table[(red&0xff) + >> png_ptr->gamma_shift][red>>8]; + + else + w = red; + } + + else + { + png_uint_16 red_1 = png_ptr->gamma_16_to_1[(red&0xff) + >> png_ptr->gamma_shift][red>>8]; + png_uint_16 green_1 = + png_ptr->gamma_16_to_1[(green&0xff) >> + png_ptr->gamma_shift][green>>8]; + png_uint_16 blue_1 = png_ptr->gamma_16_to_1[(blue&0xff) + >> png_ptr->gamma_shift][blue>>8]; + png_uint_16 gray16 = (png_uint_16)((rc*red_1 + gc*green_1 + + bc*blue_1 + 16384)>>15); + w = png_ptr->gamma_16_from_1[(gray16&0xff) >> + png_ptr->gamma_shift][gray16 >> 8]; + rgb_error |= 1; + } + + *(dp++) = (png_byte)((w>>8) & 0xff); + *(dp++) = (png_byte)(w & 0xff); + + if (have_alpha) + { + *(dp++) = *(sp++); + *(dp++) = *(sp++); + } + } + } + else +#endif + { + png_bytep sp = row; + png_bytep dp = row; + png_uint_32 i; + + for (i = 0; i < row_width; i++) + { + png_uint_16 red, green, blue, gray16; + + red = (png_uint_16)(((*(sp))<<8) | *(sp + 1)); sp += 2; + green = (png_uint_16)(((*(sp))<<8) | *(sp + 1)); sp += 2; + blue = (png_uint_16)(((*(sp))<<8) | *(sp + 1)); sp += 2; + + if (red != green || red != blue) + rgb_error |= 1; + + /* From 1.5.5 in the 16 bit case do the accurate conversion even + * in the 'fast' case - this is because this is where the code + * ends up when handling linear 16 bit data. + */ + gray16 = (png_uint_16)((rc*red + gc*green + bc*blue + 16384) >> + 15); + *(dp++) = (png_byte)((gray16>>8) & 0xff); + *(dp++) = (png_byte)(gray16 & 0xff); + + if (have_alpha) + { + *(dp++) = *(sp++); + *(dp++) = *(sp++); + } + } + } + } + + row_info->channels = (png_byte)(row_info->channels - 2); + row_info->color_type = (png_byte)(row_info->color_type & + ~PNG_COLOR_MASK_COLOR); + row_info->pixel_depth = (png_byte)(row_info->channels * + row_info->bit_depth); + row_info->rowbytes = PNG_ROWBYTES(row_info->pixel_depth, row_width); + } + return rgb_error; +} +#endif +#endif /* PNG_READ_TRANSFORMS_SUPPORTED */ + +#ifdef PNG_BUILD_GRAYSCALE_PALETTE_SUPPORTED +/* Build a grayscale palette. Palette is assumed to be 1 << bit_depth + * large of png_color. This lets grayscale images be treated as + * paletted. Most useful for gamma correction and simplification + * of code. This API is not used internally. + */ +void PNGAPI +png_build_grayscale_palette(int bit_depth, png_colorp palette) +{ + int num_palette; + int color_inc; + int i; + int v; + + png_debug(1, "in png_do_build_grayscale_palette"); + + if (palette == NULL) + return; + + switch (bit_depth) + { + case 1: + num_palette = 2; + color_inc = 0xff; + break; + + case 2: + num_palette = 4; + color_inc = 0x55; + break; + + case 4: + num_palette = 16; + color_inc = 0x11; + break; + + case 8: + num_palette = 256; + color_inc = 1; + break; + + default: + num_palette = 0; + color_inc = 0; + break; + } + + for (i = 0, v = 0; i < num_palette; i++, v += color_inc) + { + palette[i].red = (png_byte)v; + palette[i].green = (png_byte)v; + palette[i].blue = (png_byte)v; + } +} +#endif + + +#ifdef PNG_READ_TRANSFORMS_SUPPORTED +#if defined(PNG_READ_BACKGROUND_SUPPORTED) ||\ + defined(PNG_READ_ALPHA_MODE_SUPPORTED) +/* Replace any alpha or transparency with the supplied background color. + * "background" is already in the screen gamma, while "background_1" is + * at a gamma of 1.0. Paletted files have already been taken care of. + */ +void /* PRIVATE */ +png_do_compose(png_row_infop row_info, png_bytep row, png_structrp png_ptr) +{ +#ifdef PNG_READ_GAMMA_SUPPORTED + png_const_bytep gamma_table = png_ptr->gamma_table; + png_const_bytep gamma_from_1 = png_ptr->gamma_from_1; + png_const_bytep gamma_to_1 = png_ptr->gamma_to_1; + png_const_uint_16pp gamma_16 = png_ptr->gamma_16_table; + png_const_uint_16pp gamma_16_from_1 = png_ptr->gamma_16_from_1; + png_const_uint_16pp gamma_16_to_1 = png_ptr->gamma_16_to_1; + int gamma_shift = png_ptr->gamma_shift; + int optimize = (png_ptr->flags & PNG_FLAG_OPTIMIZE_ALPHA) != 0; +#endif + + png_bytep sp; + png_uint_32 i; + png_uint_32 row_width = row_info->width; + int shift; + + png_debug(1, "in png_do_compose"); + + { + switch (row_info->color_type) + { + case PNG_COLOR_TYPE_GRAY: + { + switch (row_info->bit_depth) + { + case 1: + { + sp = row; + shift = 7; + for (i = 0; i < row_width; i++) + { + if ((png_uint_16)((*sp >> shift) & 0x01) + == png_ptr->trans_color.gray) + { + unsigned int tmp = *sp & (0x7f7f >> (7 - shift)); + tmp |= png_ptr->background.gray << shift; + *sp = (png_byte)(tmp & 0xff); + } + + if (!shift) + { + shift = 7; + sp++; + } + + else + shift--; + } + break; + } + + case 2: + { +#ifdef PNG_READ_GAMMA_SUPPORTED + if (gamma_table != NULL) + { + sp = row; + shift = 6; + for (i = 0; i < row_width; i++) + { + if ((png_uint_16)((*sp >> shift) & 0x03) + == png_ptr->trans_color.gray) + { + unsigned int tmp = *sp & (0x3f3f >> (6 - shift)); + tmp |= png_ptr->background.gray << shift; + *sp = (png_byte)(tmp & 0xff); + } + + else + { + unsigned int p = (*sp >> shift) & 0x03; + unsigned int g = (gamma_table [p | (p << 2) | + (p << 4) | (p << 6)] >> 6) & 0x03; + unsigned int tmp = *sp & (0x3f3f >> (6 - shift)); + tmp |= g << shift; + *sp = (png_byte)(tmp & 0xff); + } + + if (!shift) + { + shift = 6; + sp++; + } + + else + shift -= 2; + } + } + + else +#endif + { + sp = row; + shift = 6; + for (i = 0; i < row_width; i++) + { + if ((png_uint_16)((*sp >> shift) & 0x03) + == png_ptr->trans_color.gray) + { + unsigned int tmp = *sp & (0x3f3f >> (6 - shift)); + tmp |= png_ptr->background.gray << shift; + *sp = (png_byte)(tmp & 0xff); + } + + if (!shift) + { + shift = 6; + sp++; + } + + else + shift -= 2; + } + } + break; + } + + case 4: + { +#ifdef PNG_READ_GAMMA_SUPPORTED + if (gamma_table != NULL) + { + sp = row; + shift = 4; + for (i = 0; i < row_width; i++) + { + if ((png_uint_16)((*sp >> shift) & 0x0f) + == png_ptr->trans_color.gray) + { + unsigned int tmp = *sp & (0xf0f >> (4 - shift)); + tmp |= png_ptr->background.gray << shift; + *sp = (png_byte)(tmp & 0xff); + } + + else + { + unsigned int p = (*sp >> shift) & 0x0f; + unsigned int g = (gamma_table[p | (p << 4)] >> 4) & + 0x0f; + unsigned int tmp = *sp & (0xf0f >> (4 - shift)); + tmp |= g << shift; + *sp = (png_byte)(tmp & 0xff); + } + + if (!shift) + { + shift = 4; + sp++; + } + + else + shift -= 4; + } + } + + else +#endif + { + sp = row; + shift = 4; + for (i = 0; i < row_width; i++) + { + if ((png_uint_16)((*sp >> shift) & 0x0f) + == png_ptr->trans_color.gray) + { + unsigned int tmp = *sp & (0xf0f >> (4 - shift)); + tmp |= png_ptr->background.gray << shift; + *sp = (png_byte)(tmp & 0xff); + } + + if (!shift) + { + shift = 4; + sp++; + } + + else + shift -= 4; + } + } + break; + } + + case 8: + { +#ifdef PNG_READ_GAMMA_SUPPORTED + if (gamma_table != NULL) + { + sp = row; + for (i = 0; i < row_width; i++, sp++) + { + if (*sp == png_ptr->trans_color.gray) + *sp = (png_byte)png_ptr->background.gray; + + else + *sp = gamma_table[*sp]; + } + } + else +#endif + { + sp = row; + for (i = 0; i < row_width; i++, sp++) + { + if (*sp == png_ptr->trans_color.gray) + *sp = (png_byte)png_ptr->background.gray; + } + } + break; + } + + case 16: + { +#ifdef PNG_READ_GAMMA_SUPPORTED + if (gamma_16 != NULL) + { + sp = row; + for (i = 0; i < row_width; i++, sp += 2) + { + png_uint_16 v; + + v = (png_uint_16)(((*sp) << 8) + *(sp + 1)); + + if (v == png_ptr->trans_color.gray) + { + /* Background is already in screen gamma */ + *sp = (png_byte)((png_ptr->background.gray >> 8) + & 0xff); + *(sp + 1) = (png_byte)(png_ptr->background.gray + & 0xff); + } + + else + { + v = gamma_16[*(sp + 1) >> gamma_shift][*sp]; + *sp = (png_byte)((v >> 8) & 0xff); + *(sp + 1) = (png_byte)(v & 0xff); + } + } + } + else +#endif + { + sp = row; + for (i = 0; i < row_width; i++, sp += 2) + { + png_uint_16 v; + + v = (png_uint_16)(((*sp) << 8) + *(sp + 1)); + + if (v == png_ptr->trans_color.gray) + { + *sp = (png_byte)((png_ptr->background.gray >> 8) + & 0xff); + *(sp + 1) = (png_byte)(png_ptr->background.gray + & 0xff); + } + } + } + break; + } + + default: + break; + } + break; + } + + case PNG_COLOR_TYPE_RGB: + { + if (row_info->bit_depth == 8) + { +#ifdef PNG_READ_GAMMA_SUPPORTED + if (gamma_table != NULL) + { + sp = row; + for (i = 0; i < row_width; i++, sp += 3) + { + if (*sp == png_ptr->trans_color.red && + *(sp + 1) == png_ptr->trans_color.green && + *(sp + 2) == png_ptr->trans_color.blue) + { + *sp = (png_byte)png_ptr->background.red; + *(sp + 1) = (png_byte)png_ptr->background.green; + *(sp + 2) = (png_byte)png_ptr->background.blue; + } + + else + { + *sp = gamma_table[*sp]; + *(sp + 1) = gamma_table[*(sp + 1)]; + *(sp + 2) = gamma_table[*(sp + 2)]; + } + } + } + else +#endif + { + sp = row; + for (i = 0; i < row_width; i++, sp += 3) + { + if (*sp == png_ptr->trans_color.red && + *(sp + 1) == png_ptr->trans_color.green && + *(sp + 2) == png_ptr->trans_color.blue) + { + *sp = (png_byte)png_ptr->background.red; + *(sp + 1) = (png_byte)png_ptr->background.green; + *(sp + 2) = (png_byte)png_ptr->background.blue; + } + } + } + } + else /* if (row_info->bit_depth == 16) */ + { +#ifdef PNG_READ_GAMMA_SUPPORTED + if (gamma_16 != NULL) + { + sp = row; + for (i = 0; i < row_width; i++, sp += 6) + { + png_uint_16 r = (png_uint_16)(((*sp) << 8) + *(sp + 1)); + + png_uint_16 g = (png_uint_16)(((*(sp + 2)) << 8) + + *(sp + 3)); + + png_uint_16 b = (png_uint_16)(((*(sp + 4)) << 8) + + *(sp + 5)); + + if (r == png_ptr->trans_color.red && + g == png_ptr->trans_color.green && + b == png_ptr->trans_color.blue) + { + /* Background is already in screen gamma */ + *sp = (png_byte)((png_ptr->background.red >> 8) & 0xff); + *(sp + 1) = (png_byte)(png_ptr->background.red & 0xff); + *(sp + 2) = (png_byte)((png_ptr->background.green >> 8) + & 0xff); + *(sp + 3) = (png_byte)(png_ptr->background.green + & 0xff); + *(sp + 4) = (png_byte)((png_ptr->background.blue >> 8) + & 0xff); + *(sp + 5) = (png_byte)(png_ptr->background.blue & 0xff); + } + + else + { + png_uint_16 v = gamma_16[*(sp + 1) >> gamma_shift][*sp]; + *sp = (png_byte)((v >> 8) & 0xff); + *(sp + 1) = (png_byte)(v & 0xff); + + v = gamma_16[*(sp + 3) >> gamma_shift][*(sp + 2)]; + *(sp + 2) = (png_byte)((v >> 8) & 0xff); + *(sp + 3) = (png_byte)(v & 0xff); + + v = gamma_16[*(sp + 5) >> gamma_shift][*(sp + 4)]; + *(sp + 4) = (png_byte)((v >> 8) & 0xff); + *(sp + 5) = (png_byte)(v & 0xff); + } + } + } + + else +#endif + { + sp = row; + for (i = 0; i < row_width; i++, sp += 6) + { + png_uint_16 r = (png_uint_16)(((*sp) << 8) + *(sp + 1)); + + png_uint_16 g = (png_uint_16)(((*(sp + 2)) << 8) + + *(sp + 3)); + + png_uint_16 b = (png_uint_16)(((*(sp + 4)) << 8) + + *(sp + 5)); + + if (r == png_ptr->trans_color.red && + g == png_ptr->trans_color.green && + b == png_ptr->trans_color.blue) + { + *sp = (png_byte)((png_ptr->background.red >> 8) & 0xff); + *(sp + 1) = (png_byte)(png_ptr->background.red & 0xff); + *(sp + 2) = (png_byte)((png_ptr->background.green >> 8) + & 0xff); + *(sp + 3) = (png_byte)(png_ptr->background.green + & 0xff); + *(sp + 4) = (png_byte)((png_ptr->background.blue >> 8) + & 0xff); + *(sp + 5) = (png_byte)(png_ptr->background.blue & 0xff); + } + } + } + } + break; + } + + case PNG_COLOR_TYPE_GRAY_ALPHA: + { + if (row_info->bit_depth == 8) + { +#ifdef PNG_READ_GAMMA_SUPPORTED + if (gamma_to_1 != NULL && gamma_from_1 != NULL && + gamma_table != NULL) + { + sp = row; + for (i = 0; i < row_width; i++, sp += 2) + { + png_uint_16 a = *(sp + 1); + + if (a == 0xff) + *sp = gamma_table[*sp]; + + else if (a == 0) + { + /* Background is already in screen gamma */ + *sp = (png_byte)png_ptr->background.gray; + } + + else + { + png_byte v, w; + + v = gamma_to_1[*sp]; + png_composite(w, v, a, png_ptr->background_1.gray); + if (!optimize) + w = gamma_from_1[w]; + *sp = w; + } + } + } + else +#endif + { + sp = row; + for (i = 0; i < row_width; i++, sp += 2) + { + png_byte a = *(sp + 1); + + if (a == 0) + *sp = (png_byte)png_ptr->background.gray; + + else if (a < 0xff) + png_composite(*sp, *sp, a, png_ptr->background.gray); + } + } + } + else /* if (png_ptr->bit_depth == 16) */ + { +#ifdef PNG_READ_GAMMA_SUPPORTED + if (gamma_16 != NULL && gamma_16_from_1 != NULL && + gamma_16_to_1 != NULL) + { + sp = row; + for (i = 0; i < row_width; i++, sp += 4) + { + png_uint_16 a = (png_uint_16)(((*(sp + 2)) << 8) + + *(sp + 3)); + + if (a == (png_uint_16)0xffff) + { + png_uint_16 v; + + v = gamma_16[*(sp + 1) >> gamma_shift][*sp]; + *sp = (png_byte)((v >> 8) & 0xff); + *(sp + 1) = (png_byte)(v & 0xff); + } + + else if (a == 0) + { + /* Background is already in screen gamma */ + *sp = (png_byte)((png_ptr->background.gray >> 8) + & 0xff); + *(sp + 1) = (png_byte)(png_ptr->background.gray & 0xff); + } + + else + { + png_uint_16 g, v, w; + + g = gamma_16_to_1[*(sp + 1) >> gamma_shift][*sp]; + png_composite_16(v, g, a, png_ptr->background_1.gray); + if (optimize) + w = v; + else + w = gamma_16_from_1[(v&0xff) >> gamma_shift][v >> 8]; + *sp = (png_byte)((w >> 8) & 0xff); + *(sp + 1) = (png_byte)(w & 0xff); + } + } + } + else +#endif + { + sp = row; + for (i = 0; i < row_width; i++, sp += 4) + { + png_uint_16 a = (png_uint_16)(((*(sp + 2)) << 8) + + *(sp + 3)); + + if (a == 0) + { + *sp = (png_byte)((png_ptr->background.gray >> 8) + & 0xff); + *(sp + 1) = (png_byte)(png_ptr->background.gray & 0xff); + } + + else if (a < 0xffff) + { + png_uint_16 g, v; + + g = (png_uint_16)(((*sp) << 8) + *(sp + 1)); + png_composite_16(v, g, a, png_ptr->background.gray); + *sp = (png_byte)((v >> 8) & 0xff); + *(sp + 1) = (png_byte)(v & 0xff); + } + } + } + } + break; + } + + case PNG_COLOR_TYPE_RGB_ALPHA: + { + if (row_info->bit_depth == 8) + { +#ifdef PNG_READ_GAMMA_SUPPORTED + if (gamma_to_1 != NULL && gamma_from_1 != NULL && + gamma_table != NULL) + { + sp = row; + for (i = 0; i < row_width; i++, sp += 4) + { + png_byte a = *(sp + 3); + + if (a == 0xff) + { + *sp = gamma_table[*sp]; + *(sp + 1) = gamma_table[*(sp + 1)]; + *(sp + 2) = gamma_table[*(sp + 2)]; + } + + else if (a == 0) + { + /* Background is already in screen gamma */ + *sp = (png_byte)png_ptr->background.red; + *(sp + 1) = (png_byte)png_ptr->background.green; + *(sp + 2) = (png_byte)png_ptr->background.blue; + } + + else + { + png_byte v, w; + + v = gamma_to_1[*sp]; + png_composite(w, v, a, png_ptr->background_1.red); + if (!optimize) w = gamma_from_1[w]; + *sp = w; + + v = gamma_to_1[*(sp + 1)]; + png_composite(w, v, a, png_ptr->background_1.green); + if (!optimize) w = gamma_from_1[w]; + *(sp + 1) = w; + + v = gamma_to_1[*(sp + 2)]; + png_composite(w, v, a, png_ptr->background_1.blue); + if (!optimize) w = gamma_from_1[w]; + *(sp + 2) = w; + } + } + } + else +#endif + { + sp = row; + for (i = 0; i < row_width; i++, sp += 4) + { + png_byte a = *(sp + 3); + + if (a == 0) + { + *sp = (png_byte)png_ptr->background.red; + *(sp + 1) = (png_byte)png_ptr->background.green; + *(sp + 2) = (png_byte)png_ptr->background.blue; + } + + else if (a < 0xff) + { + png_composite(*sp, *sp, a, png_ptr->background.red); + + png_composite(*(sp + 1), *(sp + 1), a, + png_ptr->background.green); + + png_composite(*(sp + 2), *(sp + 2), a, + png_ptr->background.blue); + } + } + } + } + else /* if (row_info->bit_depth == 16) */ + { +#ifdef PNG_READ_GAMMA_SUPPORTED + if (gamma_16 != NULL && gamma_16_from_1 != NULL && + gamma_16_to_1 != NULL) + { + sp = row; + for (i = 0; i < row_width; i++, sp += 8) + { + png_uint_16 a = (png_uint_16)(((png_uint_16)(*(sp + 6)) + << 8) + (png_uint_16)(*(sp + 7))); + + if (a == (png_uint_16)0xffff) + { + png_uint_16 v; + + v = gamma_16[*(sp + 1) >> gamma_shift][*sp]; + *sp = (png_byte)((v >> 8) & 0xff); + *(sp + 1) = (png_byte)(v & 0xff); + + v = gamma_16[*(sp + 3) >> gamma_shift][*(sp + 2)]; + *(sp + 2) = (png_byte)((v >> 8) & 0xff); + *(sp + 3) = (png_byte)(v & 0xff); + + v = gamma_16[*(sp + 5) >> gamma_shift][*(sp + 4)]; + *(sp + 4) = (png_byte)((v >> 8) & 0xff); + *(sp + 5) = (png_byte)(v & 0xff); + } + + else if (a == 0) + { + /* Background is already in screen gamma */ + *sp = (png_byte)((png_ptr->background.red >> 8) & 0xff); + *(sp + 1) = (png_byte)(png_ptr->background.red & 0xff); + *(sp + 2) = (png_byte)((png_ptr->background.green >> 8) + & 0xff); + *(sp + 3) = (png_byte)(png_ptr->background.green + & 0xff); + *(sp + 4) = (png_byte)((png_ptr->background.blue >> 8) + & 0xff); + *(sp + 5) = (png_byte)(png_ptr->background.blue & 0xff); + } + + else + { + png_uint_16 v, w; + + v = gamma_16_to_1[*(sp + 1) >> gamma_shift][*sp]; + png_composite_16(w, v, a, png_ptr->background_1.red); + if (!optimize) + w = gamma_16_from_1[((w&0xff) >> gamma_shift)][w >> + 8]; + *sp = (png_byte)((w >> 8) & 0xff); + *(sp + 1) = (png_byte)(w & 0xff); + + v = gamma_16_to_1[*(sp + 3) >> gamma_shift][*(sp + 2)]; + png_composite_16(w, v, a, png_ptr->background_1.green); + if (!optimize) + w = gamma_16_from_1[((w&0xff) >> gamma_shift)][w >> + 8]; + + *(sp + 2) = (png_byte)((w >> 8) & 0xff); + *(sp + 3) = (png_byte)(w & 0xff); + + v = gamma_16_to_1[*(sp + 5) >> gamma_shift][*(sp + 4)]; + png_composite_16(w, v, a, png_ptr->background_1.blue); + if (!optimize) + w = gamma_16_from_1[((w&0xff) >> gamma_shift)][w >> + 8]; + + *(sp + 4) = (png_byte)((w >> 8) & 0xff); + *(sp + 5) = (png_byte)(w & 0xff); + } + } + } + + else +#endif + { + sp = row; + for (i = 0; i < row_width; i++, sp += 8) + { + png_uint_16 a = (png_uint_16)(((png_uint_16)(*(sp + 6)) + << 8) + (png_uint_16)(*(sp + 7))); + + if (a == 0) + { + *sp = (png_byte)((png_ptr->background.red >> 8) & 0xff); + *(sp + 1) = (png_byte)(png_ptr->background.red & 0xff); + *(sp + 2) = (png_byte)((png_ptr->background.green >> 8) + & 0xff); + *(sp + 3) = (png_byte)(png_ptr->background.green + & 0xff); + *(sp + 4) = (png_byte)((png_ptr->background.blue >> 8) + & 0xff); + *(sp + 5) = (png_byte)(png_ptr->background.blue & 0xff); + } + + else if (a < 0xffff) + { + png_uint_16 v; + + png_uint_16 r = (png_uint_16)(((*sp) << 8) + *(sp + 1)); + png_uint_16 g = (png_uint_16)(((*(sp + 2)) << 8) + + *(sp + 3)); + png_uint_16 b = (png_uint_16)(((*(sp + 4)) << 8) + + *(sp + 5)); + + png_composite_16(v, r, a, png_ptr->background.red); + *sp = (png_byte)((v >> 8) & 0xff); + *(sp + 1) = (png_byte)(v & 0xff); + + png_composite_16(v, g, a, png_ptr->background.green); + *(sp + 2) = (png_byte)((v >> 8) & 0xff); + *(sp + 3) = (png_byte)(v & 0xff); + + png_composite_16(v, b, a, png_ptr->background.blue); + *(sp + 4) = (png_byte)((v >> 8) & 0xff); + *(sp + 5) = (png_byte)(v & 0xff); + } + } + } + } + break; + } + + default: + break; + } + } +} +#endif /* PNG_READ_BACKGROUND_SUPPORTED || PNG_READ_ALPHA_MODE_SUPPORTED */ + +#ifdef PNG_READ_GAMMA_SUPPORTED +/* Gamma correct the image, avoiding the alpha channel. Make sure + * you do this after you deal with the transparency issue on grayscale + * or RGB images. If your bit depth is 8, use gamma_table, if it + * is 16, use gamma_16_table and gamma_shift. Build these with + * build_gamma_table(). + */ +void /* PRIVATE */ +png_do_gamma(png_row_infop row_info, png_bytep row, png_structrp png_ptr) +{ + png_const_bytep gamma_table = png_ptr->gamma_table; + png_const_uint_16pp gamma_16_table = png_ptr->gamma_16_table; + int gamma_shift = png_ptr->gamma_shift; + + png_bytep sp; + png_uint_32 i; + png_uint_32 row_width=row_info->width; + + png_debug(1, "in png_do_gamma"); + + if (((row_info->bit_depth <= 8 && gamma_table != NULL) || + (row_info->bit_depth == 16 && gamma_16_table != NULL))) + { + switch (row_info->color_type) + { + case PNG_COLOR_TYPE_RGB: + { + if (row_info->bit_depth == 8) + { + sp = row; + for (i = 0; i < row_width; i++) + { + *sp = gamma_table[*sp]; + sp++; + *sp = gamma_table[*sp]; + sp++; + *sp = gamma_table[*sp]; + sp++; + } + } + + else /* if (row_info->bit_depth == 16) */ + { + sp = row; + for (i = 0; i < row_width; i++) + { + png_uint_16 v; + + v = gamma_16_table[*(sp + 1) >> gamma_shift][*sp]; + *sp = (png_byte)((v >> 8) & 0xff); + *(sp + 1) = (png_byte)(v & 0xff); + sp += 2; + + v = gamma_16_table[*(sp + 1) >> gamma_shift][*sp]; + *sp = (png_byte)((v >> 8) & 0xff); + *(sp + 1) = (png_byte)(v & 0xff); + sp += 2; + + v = gamma_16_table[*(sp + 1) >> gamma_shift][*sp]; + *sp = (png_byte)((v >> 8) & 0xff); + *(sp + 1) = (png_byte)(v & 0xff); + sp += 2; + } + } + break; + } + + case PNG_COLOR_TYPE_RGB_ALPHA: + { + if (row_info->bit_depth == 8) + { + sp = row; + for (i = 0; i < row_width; i++) + { + *sp = gamma_table[*sp]; + sp++; + + *sp = gamma_table[*sp]; + sp++; + + *sp = gamma_table[*sp]; + sp++; + + sp++; + } + } + + else /* if (row_info->bit_depth == 16) */ + { + sp = row; + for (i = 0; i < row_width; i++) + { + png_uint_16 v = gamma_16_table[*(sp + 1) >> gamma_shift][*sp]; + *sp = (png_byte)((v >> 8) & 0xff); + *(sp + 1) = (png_byte)(v & 0xff); + sp += 2; + + v = gamma_16_table[*(sp + 1) >> gamma_shift][*sp]; + *sp = (png_byte)((v >> 8) & 0xff); + *(sp + 1) = (png_byte)(v & 0xff); + sp += 2; + + v = gamma_16_table[*(sp + 1) >> gamma_shift][*sp]; + *sp = (png_byte)((v >> 8) & 0xff); + *(sp + 1) = (png_byte)(v & 0xff); + sp += 4; + } + } + break; + } + + case PNG_COLOR_TYPE_GRAY_ALPHA: + { + if (row_info->bit_depth == 8) + { + sp = row; + for (i = 0; i < row_width; i++) + { + *sp = gamma_table[*sp]; + sp += 2; + } + } + + else /* if (row_info->bit_depth == 16) */ + { + sp = row; + for (i = 0; i < row_width; i++) + { + png_uint_16 v = gamma_16_table[*(sp + 1) >> gamma_shift][*sp]; + *sp = (png_byte)((v >> 8) & 0xff); + *(sp + 1) = (png_byte)(v & 0xff); + sp += 4; + } + } + break; + } + + case PNG_COLOR_TYPE_GRAY: + { + if (row_info->bit_depth == 2) + { + sp = row; + for (i = 0; i < row_width; i += 4) + { + int a = *sp & 0xc0; + int b = *sp & 0x30; + int c = *sp & 0x0c; + int d = *sp & 0x03; + + *sp = (png_byte)( + ((((int)gamma_table[a|(a>>2)|(a>>4)|(a>>6)]) ) & 0xc0)| + ((((int)gamma_table[(b<<2)|b|(b>>2)|(b>>4)])>>2) & 0x30)| + ((((int)gamma_table[(c<<4)|(c<<2)|c|(c>>2)])>>4) & 0x0c)| + ((((int)gamma_table[(d<<6)|(d<<4)|(d<<2)|d])>>6) )); + sp++; + } + } + + if (row_info->bit_depth == 4) + { + sp = row; + for (i = 0; i < row_width; i += 2) + { + int msb = *sp & 0xf0; + int lsb = *sp & 0x0f; + + *sp = (png_byte)((((int)gamma_table[msb | (msb >> 4)]) & 0xf0) + | (((int)gamma_table[(lsb << 4) | lsb]) >> 4)); + sp++; + } + } + + else if (row_info->bit_depth == 8) + { + sp = row; + for (i = 0; i < row_width; i++) + { + *sp = gamma_table[*sp]; + sp++; + } + } + + else if (row_info->bit_depth == 16) + { + sp = row; + for (i = 0; i < row_width; i++) + { + png_uint_16 v = gamma_16_table[*(sp + 1) >> gamma_shift][*sp]; + *sp = (png_byte)((v >> 8) & 0xff); + *(sp + 1) = (png_byte)(v & 0xff); + sp += 2; + } + } + break; + } + + default: + break; + } + } +} +#endif + +#ifdef PNG_READ_ALPHA_MODE_SUPPORTED +/* Encode the alpha channel to the output gamma (the input channel is always + * linear.) Called only with color types that have an alpha channel. Needs the + * from_1 tables. + */ +void /* PRIVATE */ +png_do_encode_alpha(png_row_infop row_info, png_bytep row, png_structrp png_ptr) +{ + png_uint_32 row_width = row_info->width; + + png_debug(1, "in png_do_encode_alpha"); + + if (row_info->color_type & PNG_COLOR_MASK_ALPHA) + { + if (row_info->bit_depth == 8) + { + PNG_CONST png_bytep table = png_ptr->gamma_from_1; + + if (table != NULL) + { + PNG_CONST int step = + (row_info->color_type & PNG_COLOR_MASK_COLOR) ? 4 : 2; + + /* The alpha channel is the last component: */ + row += step - 1; + + for (; row_width > 0; --row_width, row += step) + *row = table[*row]; + + return; + } + } + + else if (row_info->bit_depth == 16) + { + PNG_CONST png_uint_16pp table = png_ptr->gamma_16_from_1; + PNG_CONST int gamma_shift = png_ptr->gamma_shift; + + if (table != NULL) + { + PNG_CONST int step = + (row_info->color_type & PNG_COLOR_MASK_COLOR) ? 8 : 4; + + /* The alpha channel is the last component: */ + row += step - 2; + + for (; row_width > 0; --row_width, row += step) + { + png_uint_16 v; + + v = table[*(row + 1) >> gamma_shift][*row]; + *row = (png_byte)((v >> 8) & 0xff); + *(row + 1) = (png_byte)(v & 0xff); + } + + return; + } + } + } + + /* Only get to here if called with a weird row_info; no harm has been done, + * so just issue a warning. + */ + png_warning(png_ptr, "png_do_encode_alpha: unexpected call"); +} +#endif + +#ifdef PNG_READ_EXPAND_SUPPORTED +/* Expands a palette row to an RGB or RGBA row depending + * upon whether you supply trans and num_trans. + */ +void /* PRIVATE */ +png_do_expand_palette(png_row_infop row_info, png_bytep row, + png_const_colorp palette, png_const_bytep trans_alpha, int num_trans) +{ + int shift, value; + png_bytep sp, dp; + png_uint_32 i; + png_uint_32 row_width=row_info->width; + + png_debug(1, "in png_do_expand_palette"); + + if (row_info->color_type == PNG_COLOR_TYPE_PALETTE) + { + if (row_info->bit_depth < 8) + { + switch (row_info->bit_depth) + { + case 1: + { + sp = row + (png_size_t)((row_width - 1) >> 3); + dp = row + (png_size_t)row_width - 1; + shift = 7 - (int)((row_width + 7) & 0x07); + for (i = 0; i < row_width; i++) + { + if ((*sp >> shift) & 0x01) + *dp = 1; + + else + *dp = 0; + + if (shift == 7) + { + shift = 0; + sp--; + } + + else + shift++; + + dp--; + } + break; + } + + case 2: + { + sp = row + (png_size_t)((row_width - 1) >> 2); + dp = row + (png_size_t)row_width - 1; + shift = (int)((3 - ((row_width + 3) & 0x03)) << 1); + for (i = 0; i < row_width; i++) + { + value = (*sp >> shift) & 0x03; + *dp = (png_byte)value; + if (shift == 6) + { + shift = 0; + sp--; + } + + else + shift += 2; + + dp--; + } + break; + } + + case 4: + { + sp = row + (png_size_t)((row_width - 1) >> 1); + dp = row + (png_size_t)row_width - 1; + shift = (int)((row_width & 0x01) << 2); + for (i = 0; i < row_width; i++) + { + value = (*sp >> shift) & 0x0f; + *dp = (png_byte)value; + if (shift == 4) + { + shift = 0; + sp--; + } + + else + shift += 4; + + dp--; + } + break; + } + + default: + break; + } + row_info->bit_depth = 8; + row_info->pixel_depth = 8; + row_info->rowbytes = row_width; + } + + if (row_info->bit_depth == 8) + { + { + if (num_trans > 0) + { + sp = row + (png_size_t)row_width - 1; + dp = row + (png_size_t)(row_width << 2) - 1; + + for (i = 0; i < row_width; i++) + { + if ((int)(*sp) >= num_trans) + *dp-- = 0xff; + + else + *dp-- = trans_alpha[*sp]; + + *dp-- = palette[*sp].blue; + *dp-- = palette[*sp].green; + *dp-- = palette[*sp].red; + sp--; + } + row_info->bit_depth = 8; + row_info->pixel_depth = 32; + row_info->rowbytes = row_width * 4; + row_info->color_type = 6; + row_info->channels = 4; + } + + else + { + sp = row + (png_size_t)row_width - 1; + dp = row + (png_size_t)(row_width * 3) - 1; + + for (i = 0; i < row_width; i++) + { + *dp-- = palette[*sp].blue; + *dp-- = palette[*sp].green; + *dp-- = palette[*sp].red; + sp--; + } + + row_info->bit_depth = 8; + row_info->pixel_depth = 24; + row_info->rowbytes = row_width * 3; + row_info->color_type = 2; + row_info->channels = 3; + } + } + } + } +} + +/* If the bit depth < 8, it is expanded to 8. Also, if the already + * expanded transparency value is supplied, an alpha channel is built. + */ +void /* PRIVATE */ +png_do_expand(png_row_infop row_info, png_bytep row, + png_const_color_16p trans_color) +{ + int shift, value; + png_bytep sp, dp; + png_uint_32 i; + png_uint_32 row_width=row_info->width; + + png_debug(1, "in png_do_expand"); + + { + if (row_info->color_type == PNG_COLOR_TYPE_GRAY) + { + unsigned int gray = trans_color ? trans_color->gray : 0; + + if (row_info->bit_depth < 8) + { + switch (row_info->bit_depth) + { + case 1: + { + gray = (gray & 0x01) * 0xff; + sp = row + (png_size_t)((row_width - 1) >> 3); + dp = row + (png_size_t)row_width - 1; + shift = 7 - (int)((row_width + 7) & 0x07); + for (i = 0; i < row_width; i++) + { + if ((*sp >> shift) & 0x01) + *dp = 0xff; + + else + *dp = 0; + + if (shift == 7) + { + shift = 0; + sp--; + } + + else + shift++; + + dp--; + } + break; + } + + case 2: + { + gray = (gray & 0x03) * 0x55; + sp = row + (png_size_t)((row_width - 1) >> 2); + dp = row + (png_size_t)row_width - 1; + shift = (int)((3 - ((row_width + 3) & 0x03)) << 1); + for (i = 0; i < row_width; i++) + { + value = (*sp >> shift) & 0x03; + *dp = (png_byte)(value | (value << 2) | (value << 4) | + (value << 6)); + if (shift == 6) + { + shift = 0; + sp--; + } + + else + shift += 2; + + dp--; + } + break; + } + + case 4: + { + gray = (gray & 0x0f) * 0x11; + sp = row + (png_size_t)((row_width - 1) >> 1); + dp = row + (png_size_t)row_width - 1; + shift = (int)((1 - ((row_width + 1) & 0x01)) << 2); + for (i = 0; i < row_width; i++) + { + value = (*sp >> shift) & 0x0f; + *dp = (png_byte)(value | (value << 4)); + if (shift == 4) + { + shift = 0; + sp--; + } + + else + shift = 4; + + dp--; + } + break; + } + + default: + break; + } + + row_info->bit_depth = 8; + row_info->pixel_depth = 8; + row_info->rowbytes = row_width; + } + + if (trans_color != NULL) + { + if (row_info->bit_depth == 8) + { + gray = gray & 0xff; + sp = row + (png_size_t)row_width - 1; + dp = row + (png_size_t)(row_width << 1) - 1; + + for (i = 0; i < row_width; i++) + { + if (*sp == gray) + *dp-- = 0; + + else + *dp-- = 0xff; + + *dp-- = *sp--; + } + } + + else if (row_info->bit_depth == 16) + { + unsigned int gray_high = (gray >> 8) & 0xff; + unsigned int gray_low = gray & 0xff; + sp = row + row_info->rowbytes - 1; + dp = row + (row_info->rowbytes << 1) - 1; + for (i = 0; i < row_width; i++) + { + if (*(sp - 1) == gray_high && *(sp) == gray_low) + { + *dp-- = 0; + *dp-- = 0; + } + + else + { + *dp-- = 0xff; + *dp-- = 0xff; + } + + *dp-- = *sp--; + *dp-- = *sp--; + } + } + + row_info->color_type = PNG_COLOR_TYPE_GRAY_ALPHA; + row_info->channels = 2; + row_info->pixel_depth = (png_byte)(row_info->bit_depth << 1); + row_info->rowbytes = PNG_ROWBYTES(row_info->pixel_depth, + row_width); + } + } + else if (row_info->color_type == PNG_COLOR_TYPE_RGB && trans_color) + { + if (row_info->bit_depth == 8) + { + png_byte red = (png_byte)(trans_color->red & 0xff); + png_byte green = (png_byte)(trans_color->green & 0xff); + png_byte blue = (png_byte)(trans_color->blue & 0xff); + sp = row + (png_size_t)row_info->rowbytes - 1; + dp = row + (png_size_t)(row_width << 2) - 1; + for (i = 0; i < row_width; i++) + { + if (*(sp - 2) == red && *(sp - 1) == green && *(sp) == blue) + *dp-- = 0; + + else + *dp-- = 0xff; + + *dp-- = *sp--; + *dp-- = *sp--; + *dp-- = *sp--; + } + } + else if (row_info->bit_depth == 16) + { + png_byte red_high = (png_byte)((trans_color->red >> 8) & 0xff); + png_byte green_high = (png_byte)((trans_color->green >> 8) & 0xff); + png_byte blue_high = (png_byte)((trans_color->blue >> 8) & 0xff); + png_byte red_low = (png_byte)(trans_color->red & 0xff); + png_byte green_low = (png_byte)(trans_color->green & 0xff); + png_byte blue_low = (png_byte)(trans_color->blue & 0xff); + sp = row + row_info->rowbytes - 1; + dp = row + (png_size_t)(row_width << 3) - 1; + for (i = 0; i < row_width; i++) + { + if (*(sp - 5) == red_high && + *(sp - 4) == red_low && + *(sp - 3) == green_high && + *(sp - 2) == green_low && + *(sp - 1) == blue_high && + *(sp ) == blue_low) + { + *dp-- = 0; + *dp-- = 0; + } + + else + { + *dp-- = 0xff; + *dp-- = 0xff; + } + + *dp-- = *sp--; + *dp-- = *sp--; + *dp-- = *sp--; + *dp-- = *sp--; + *dp-- = *sp--; + *dp-- = *sp--; + } + } + row_info->color_type = PNG_COLOR_TYPE_RGB_ALPHA; + row_info->channels = 4; + row_info->pixel_depth = (png_byte)(row_info->bit_depth << 2); + row_info->rowbytes = PNG_ROWBYTES(row_info->pixel_depth, row_width); + } + } +} +#endif + +#ifdef PNG_READ_EXPAND_16_SUPPORTED +/* If the bit depth is 8 and the color type is not a palette type expand the + * whole row to 16 bits. Has no effect otherwise. + */ +void /* PRIVATE */ +png_do_expand_16(png_row_infop row_info, png_bytep row) +{ + if (row_info->bit_depth == 8 && + row_info->color_type != PNG_COLOR_TYPE_PALETTE) + { + /* The row have a sequence of bytes containing [0..255] and we need + * to turn it into another row containing [0..65535], to do this we + * calculate: + * + * (input / 255) * 65535 + * + * Which happens to be exactly input * 257 and this can be achieved + * simply by byte replication in place (copying backwards). + */ + png_byte *sp = row + row_info->rowbytes; /* source, last byte + 1 */ + png_byte *dp = sp + row_info->rowbytes; /* destination, end + 1 */ + while (dp > sp) + dp[-2] = dp[-1] = *--sp, dp -= 2; + + row_info->rowbytes *= 2; + row_info->bit_depth = 16; + row_info->pixel_depth = (png_byte)(row_info->channels * 16); + } +} +#endif + +#ifdef PNG_READ_QUANTIZE_SUPPORTED +void /* PRIVATE */ +png_do_quantize(png_row_infop row_info, png_bytep row, + png_const_bytep palette_lookup, png_const_bytep quantize_lookup) +{ + png_bytep sp, dp; + png_uint_32 i; + png_uint_32 row_width=row_info->width; + + png_debug(1, "in png_do_quantize"); + + if (row_info->bit_depth == 8) + { + if (row_info->color_type == PNG_COLOR_TYPE_RGB && palette_lookup) + { + int r, g, b, p; + sp = row; + dp = row; + for (i = 0; i < row_width; i++) + { + r = *sp++; + g = *sp++; + b = *sp++; + + /* This looks real messy, but the compiler will reduce + * it down to a reasonable formula. For example, with + * 5 bits per color, we get: + * p = (((r >> 3) & 0x1f) << 10) | + * (((g >> 3) & 0x1f) << 5) | + * ((b >> 3) & 0x1f); + */ + p = (((r >> (8 - PNG_QUANTIZE_RED_BITS)) & + ((1 << PNG_QUANTIZE_RED_BITS) - 1)) << + (PNG_QUANTIZE_GREEN_BITS + PNG_QUANTIZE_BLUE_BITS)) | + (((g >> (8 - PNG_QUANTIZE_GREEN_BITS)) & + ((1 << PNG_QUANTIZE_GREEN_BITS) - 1)) << + (PNG_QUANTIZE_BLUE_BITS)) | + ((b >> (8 - PNG_QUANTIZE_BLUE_BITS)) & + ((1 << PNG_QUANTIZE_BLUE_BITS) - 1)); + + *dp++ = palette_lookup[p]; + } + + row_info->color_type = PNG_COLOR_TYPE_PALETTE; + row_info->channels = 1; + row_info->pixel_depth = row_info->bit_depth; + row_info->rowbytes = PNG_ROWBYTES(row_info->pixel_depth, row_width); + } + + else if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA && + palette_lookup != NULL) + { + int r, g, b, p; + sp = row; + dp = row; + for (i = 0; i < row_width; i++) + { + r = *sp++; + g = *sp++; + b = *sp++; + sp++; + + p = (((r >> (8 - PNG_QUANTIZE_RED_BITS)) & + ((1 << PNG_QUANTIZE_RED_BITS) - 1)) << + (PNG_QUANTIZE_GREEN_BITS + PNG_QUANTIZE_BLUE_BITS)) | + (((g >> (8 - PNG_QUANTIZE_GREEN_BITS)) & + ((1 << PNG_QUANTIZE_GREEN_BITS) - 1)) << + (PNG_QUANTIZE_BLUE_BITS)) | + ((b >> (8 - PNG_QUANTIZE_BLUE_BITS)) & + ((1 << PNG_QUANTIZE_BLUE_BITS) - 1)); + + *dp++ = palette_lookup[p]; + } + + row_info->color_type = PNG_COLOR_TYPE_PALETTE; + row_info->channels = 1; + row_info->pixel_depth = row_info->bit_depth; + row_info->rowbytes = PNG_ROWBYTES(row_info->pixel_depth, row_width); + } + + else if (row_info->color_type == PNG_COLOR_TYPE_PALETTE && + quantize_lookup) + { + sp = row; + + for (i = 0; i < row_width; i++, sp++) + { + *sp = quantize_lookup[*sp]; + } + } + } +} +#endif /* PNG_READ_QUANTIZE_SUPPORTED */ +#endif /* PNG_READ_TRANSFORMS_SUPPORTED */ + +#ifdef PNG_MNG_FEATURES_SUPPORTED +/* Undoes intrapixel differencing */ +void /* PRIVATE */ +png_do_read_intrapixel(png_row_infop row_info, png_bytep row) +{ + png_debug(1, "in png_do_read_intrapixel"); + + if ( + (row_info->color_type & PNG_COLOR_MASK_COLOR)) + { + int bytes_per_pixel; + png_uint_32 row_width = row_info->width; + + if (row_info->bit_depth == 8) + { + png_bytep rp; + png_uint_32 i; + + if (row_info->color_type == PNG_COLOR_TYPE_RGB) + bytes_per_pixel = 3; + + else if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA) + bytes_per_pixel = 4; + + else + return; + + for (i = 0, rp = row; i < row_width; i++, rp += bytes_per_pixel) + { + *(rp) = (png_byte)((256 + *rp + *(rp + 1)) & 0xff); + *(rp+2) = (png_byte)((256 + *(rp + 2) + *(rp + 1)) & 0xff); + } + } + else if (row_info->bit_depth == 16) + { + png_bytep rp; + png_uint_32 i; + + if (row_info->color_type == PNG_COLOR_TYPE_RGB) + bytes_per_pixel = 6; + + else if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA) + bytes_per_pixel = 8; + + else + return; + + for (i = 0, rp = row; i < row_width; i++, rp += bytes_per_pixel) + { + png_uint_32 s0 = (*(rp ) << 8) | *(rp + 1); + png_uint_32 s1 = (*(rp + 2) << 8) | *(rp + 3); + png_uint_32 s2 = (*(rp + 4) << 8) | *(rp + 5); + png_uint_32 red = (s0 + s1 + 65536) & 0xffff; + png_uint_32 blue = (s2 + s1 + 65536) & 0xffff; + *(rp ) = (png_byte)((red >> 8) & 0xff); + *(rp + 1) = (png_byte)(red & 0xff); + *(rp + 4) = (png_byte)((blue >> 8) & 0xff); + *(rp + 5) = (png_byte)(blue & 0xff); + } + } + } +} +#endif /* PNG_MNG_FEATURES_SUPPORTED */ +#endif /* PNG_READ_SUPPORTED */ diff --git a/Minecraft.Client/Windows64/4JLibs/inc/Render/libpng/pngrutil.c b/Minecraft.Client/Windows64/4JLibs/inc/Render/libpng/pngrutil.c new file mode 100644 index 000000000..01c3679df --- /dev/null +++ b/Minecraft.Client/Windows64/4JLibs/inc/Render/libpng/pngrutil.c @@ -0,0 +1,4462 @@ + +/* pngrutil.c - utilities to read a PNG file + * + * Last changed in libpng 1.6.2 [April 25, 2013] + * Copyright (c) 1998-2013 Glenn Randers-Pehrson + * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) + * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) + * + * This code is released under the libpng license. + * For conditions of distribution and use, see the disclaimer + * and license in png.h + * + * This file contains routines that are only called from within + * libpng itself during the course of reading an image. + */ + +#include "pngpriv.h" + +#ifdef PNG_READ_SUPPORTED + +#define png_strtod(p,a,b) strtod(a,b) + +png_uint_32 PNGAPI +png_get_uint_31(png_const_structrp png_ptr, png_const_bytep buf) +{ + png_uint_32 uval = png_get_uint_32(buf); + + if (uval > PNG_UINT_31_MAX) + png_error(png_ptr, "PNG unsigned integer out of range"); + + return (uval); +} + +#if defined(PNG_READ_gAMA_SUPPORTED) || defined(PNG_READ_cHRM_SUPPORTED) +/* The following is a variation on the above for use with the fixed + * point values used for gAMA and cHRM. Instead of png_error it + * issues a warning and returns (-1) - an invalid value because both + * gAMA and cHRM use *unsigned* integers for fixed point values. + */ +#define PNG_FIXED_ERROR (-1) + +static png_fixed_point /* PRIVATE */ +png_get_fixed_point(png_structrp png_ptr, png_const_bytep buf) +{ + png_uint_32 uval = png_get_uint_32(buf); + + if (uval <= PNG_UINT_31_MAX) + return (png_fixed_point)uval; /* known to be in range */ + + /* The caller can turn off the warning by passing NULL. */ + if (png_ptr != NULL) + png_warning(png_ptr, "PNG fixed point integer out of range"); + + return PNG_FIXED_ERROR; +} +#endif + +#ifdef PNG_READ_INT_FUNCTIONS_SUPPORTED +/* NOTE: the read macros will obscure these definitions, so that if + * PNG_USE_READ_MACROS is set the library will not use them internally, + * but the APIs will still be available externally. + * + * The parentheses around "PNGAPI function_name" in the following three + * functions are necessary because they allow the macros to co-exist with + * these (unused but exported) functions. + */ + +/* Grab an unsigned 32-bit integer from a buffer in big-endian format. */ +png_uint_32 (PNGAPI +png_get_uint_32)(png_const_bytep buf) +{ + png_uint_32 uval = + ((png_uint_32)(*(buf )) << 24) + + ((png_uint_32)(*(buf + 1)) << 16) + + ((png_uint_32)(*(buf + 2)) << 8) + + ((png_uint_32)(*(buf + 3)) ) ; + + return uval; +} + +/* Grab a signed 32-bit integer from a buffer in big-endian format. The + * data is stored in the PNG file in two's complement format and there + * is no guarantee that a 'png_int_32' is exactly 32 bits, therefore + * the following code does a two's complement to native conversion. + */ +png_int_32 (PNGAPI +png_get_int_32)(png_const_bytep buf) +{ + png_uint_32 uval = png_get_uint_32(buf); + if ((uval & 0x80000000) == 0) /* non-negative */ + return uval; + + uval = (uval ^ 0xffffffff) + 1; /* 2's complement: -x = ~x+1 */ + return -(png_int_32)uval; +} + +/* Grab an unsigned 16-bit integer from a buffer in big-endian format. */ +png_uint_16 (PNGAPI +png_get_uint_16)(png_const_bytep buf) +{ + /* ANSI-C requires an int value to accomodate at least 16 bits so this + * works and allows the compiler not to worry about possible narrowing + * on 32 bit systems. (Pre-ANSI systems did not make integers smaller + * than 16 bits either.) + */ + unsigned int val = + ((unsigned int)(*buf) << 8) + + ((unsigned int)(*(buf + 1))); + + return (png_uint_16)val; +} + +#endif /* PNG_READ_INT_FUNCTIONS_SUPPORTED */ + +/* Read and check the PNG file signature */ +void /* PRIVATE */ +png_read_sig(png_structrp png_ptr, png_inforp info_ptr) +{ + png_size_t num_checked, num_to_check; + + /* Exit if the user application does not expect a signature. */ + if (png_ptr->sig_bytes >= 8) + return; + + num_checked = png_ptr->sig_bytes; + num_to_check = 8 - num_checked; + +#ifdef PNG_IO_STATE_SUPPORTED + png_ptr->io_state = PNG_IO_READING | PNG_IO_SIGNATURE; +#endif + + /* The signature must be serialized in a single I/O call. */ + png_read_data(png_ptr, &(info_ptr->signature[num_checked]), num_to_check); + png_ptr->sig_bytes = 8; + + if (png_sig_cmp(info_ptr->signature, num_checked, num_to_check)) + { + if (num_checked < 4 && + png_sig_cmp(info_ptr->signature, num_checked, num_to_check - 4)) + png_error(png_ptr, "Not a PNG file"); + else + png_error(png_ptr, "PNG file corrupted by ASCII conversion"); + } + if (num_checked < 3) + png_ptr->mode |= PNG_HAVE_PNG_SIGNATURE; +} + +/* Read the chunk header (length + type name). + * Put the type name into png_ptr->chunk_name, and return the length. + */ +png_uint_32 /* PRIVATE */ +png_read_chunk_header(png_structrp png_ptr) +{ + png_byte buf[8]; + png_uint_32 length; + +#ifdef PNG_IO_STATE_SUPPORTED + png_ptr->io_state = PNG_IO_READING | PNG_IO_CHUNK_HDR; +#endif + + /* Read the length and the chunk name. + * This must be performed in a single I/O call. + */ + png_read_data(png_ptr, buf, 8); + length = png_get_uint_31(png_ptr, buf); + + /* Put the chunk name into png_ptr->chunk_name. */ + png_ptr->chunk_name = PNG_CHUNK_FROM_STRING(buf+4); + + png_debug2(0, "Reading %lx chunk, length = %lu", + (unsigned long)png_ptr->chunk_name, (unsigned long)length); + + /* Reset the crc and run it over the chunk name. */ + png_reset_crc(png_ptr); + png_calculate_crc(png_ptr, buf + 4, 4); + + /* Check to see if chunk name is valid. */ + png_check_chunk_name(png_ptr, png_ptr->chunk_name); + +#ifdef PNG_IO_STATE_SUPPORTED + png_ptr->io_state = PNG_IO_READING | PNG_IO_CHUNK_DATA; +#endif + + return length; +} + +/* Read data, and (optionally) run it through the CRC. */ +void /* PRIVATE */ +png_crc_read(png_structrp png_ptr, png_bytep buf, png_uint_32 length) +{ + if (png_ptr == NULL) + return; + + png_read_data(png_ptr, buf, length); + png_calculate_crc(png_ptr, buf, length); +} + +/* Optionally skip data and then check the CRC. Depending on whether we + * are reading an ancillary or critical chunk, and how the program has set + * things up, we may calculate the CRC on the data and print a message. + * Returns '1' if there was a CRC error, '0' otherwise. + */ +int /* PRIVATE */ +png_crc_finish(png_structrp png_ptr, png_uint_32 skip) +{ + /* The size of the local buffer for inflate is a good guess as to a + * reasonable size to use for buffering reads from the application. + */ + while (skip > 0) + { + png_uint_32 len; + png_byte tmpbuf[PNG_INFLATE_BUF_SIZE]; + + len = (sizeof tmpbuf); + if (len > skip) + len = skip; + skip -= len; + + png_crc_read(png_ptr, tmpbuf, len); + } + + if (png_crc_error(png_ptr)) + { + if (PNG_CHUNK_ANCILLARY(png_ptr->chunk_name) ? + !(png_ptr->flags & PNG_FLAG_CRC_ANCILLARY_NOWARN) : + (png_ptr->flags & PNG_FLAG_CRC_CRITICAL_USE)) + { + png_chunk_warning(png_ptr, "CRC error"); + } + + else + { + png_chunk_benign_error(png_ptr, "CRC error"); + return (0); + } + + return (1); + } + + return (0); +} + +/* Compare the CRC stored in the PNG file with that calculated by libpng from + * the data it has read thus far. + */ +int /* PRIVATE */ +png_crc_error(png_structrp png_ptr) +{ + png_byte crc_bytes[4]; + png_uint_32 crc; + int need_crc = 1; + + if (PNG_CHUNK_ANCILLARY(png_ptr->chunk_name)) + { + if ((png_ptr->flags & PNG_FLAG_CRC_ANCILLARY_MASK) == + (PNG_FLAG_CRC_ANCILLARY_USE | PNG_FLAG_CRC_ANCILLARY_NOWARN)) + need_crc = 0; + } + + else /* critical */ + { + if (png_ptr->flags & PNG_FLAG_CRC_CRITICAL_IGNORE) + need_crc = 0; + } + +#ifdef PNG_IO_STATE_SUPPORTED + png_ptr->io_state = PNG_IO_READING | PNG_IO_CHUNK_CRC; +#endif + + /* The chunk CRC must be serialized in a single I/O call. */ + png_read_data(png_ptr, crc_bytes, 4); + + if (need_crc) + { + crc = png_get_uint_32(crc_bytes); + return ((int)(crc != png_ptr->crc)); + } + + else + return (0); +} + +/* Manage the read buffer; this simply reallocates the buffer if it is not small + * enough (or if it is not allocated). The routine returns a pointer to the + * buffer; if an error occurs and 'warn' is set the routine returns NULL, else + * it will call png_error (via png_malloc) on failure. (warn == 2 means + * 'silent'). + */ +static png_bytep +png_read_buffer(png_structrp png_ptr, png_alloc_size_t new_size, int warn) +{ + png_bytep buffer = png_ptr->read_buffer; + + if (buffer != NULL && new_size > png_ptr->read_buffer_size) + { + png_ptr->read_buffer = NULL; + png_ptr->read_buffer = NULL; + png_ptr->read_buffer_size = 0; + png_free(png_ptr, buffer); + buffer = NULL; + } + + if (buffer == NULL) + { + buffer = png_voidcast(png_bytep, png_malloc_base(png_ptr, new_size)); + + if (buffer != NULL) + { + png_ptr->read_buffer = buffer; + png_ptr->read_buffer_size = new_size; + } + + else if (warn < 2) /* else silent */ + { +#ifdef PNG_WARNINGS_SUPPORTED + if (warn) + png_chunk_warning(png_ptr, "insufficient memory to read chunk"); + else +#endif + { +#ifdef PNG_ERROR_TEXT_SUPPORTED + png_chunk_error(png_ptr, "insufficient memory to read chunk"); +#endif + } + } + } + + return buffer; +} + +/* png_inflate_claim: claim the zstream for some nefarious purpose that involves + * decompression. Returns Z_OK on success, else a zlib error code. It checks + * the owner but, in final release builds, just issues a warning if some other + * chunk apparently owns the stream. Prior to release it does a png_error. + */ +static int +png_inflate_claim(png_structrp png_ptr, png_uint_32 owner, int window_bits) +{ + if (png_ptr->zowner != 0) + { + char msg[64]; + + PNG_STRING_FROM_CHUNK(msg, png_ptr->zowner); + /* So the message that results is " using zstream"; this is an + * internal error, but is very useful for debugging. i18n requirements + * are minimal. + */ + (void)png_safecat(msg, (sizeof msg), 4, " using zstream"); +# if PNG_LIBPNG_BUILD_BASE_TYPE >= PNG_LIBPNG_BUILD_RC + png_chunk_warning(png_ptr, msg); + png_ptr->zowner = 0; +# else + png_chunk_error(png_ptr, msg); +# endif + } + + /* Implementation note: unlike 'png_deflate_claim' this internal function + * does not take the size of the data as an argument. Some efficiency could + * be gained by using this when it is known *if* the zlib stream itself does + * not record the number; however, this is an illusion: the original writer + * of the PNG may have selected a lower window size, and we really must + * follow that because, for systems with with limited capabilities, we + * would otherwise reject the application's attempts to use a smaller window + * size (zlib doesn't have an interface to say "this or lower"!). + * + * inflateReset2 was added to zlib 1.2.4; before this the window could not be + * reset, therefore it is necessary to always allocate the maximum window + * size with earlier zlibs just in case later compressed chunks need it. + */ + { + int ret; /* zlib return code */ + + /* Set this for safety, just in case the previous owner left pointers to + * memory allocations. + */ + png_ptr->zstream.next_in = NULL; + png_ptr->zstream.avail_in = 0; + png_ptr->zstream.next_out = NULL; + png_ptr->zstream.avail_out = 0; + + if (png_ptr->flags & PNG_FLAG_ZSTREAM_INITIALIZED) + { +# if ZLIB_VERNUM < 0x1240 + PNG_UNUSED(window_bits) + ret = inflateReset(&png_ptr->zstream); +# else + ret = inflateReset2(&png_ptr->zstream, window_bits); +# endif + } + + else + { +# if ZLIB_VERNUM < 0x1240 + ret = inflateInit(&png_ptr->zstream); +# else + ret = inflateInit2(&png_ptr->zstream, window_bits); +# endif + + if (ret == Z_OK) + png_ptr->flags |= PNG_FLAG_ZSTREAM_INITIALIZED; + } + + if (ret == Z_OK) + png_ptr->zowner = owner; + + else + png_zstream_error(png_ptr, ret); + + return ret; + } +} + +#ifdef PNG_READ_COMPRESSED_TEXT_SUPPORTED +/* png_inflate now returns zlib error codes including Z_OK and Z_STREAM_END to + * allow the caller to do multiple calls if required. If the 'finish' flag is + * set Z_FINISH will be passed to the final inflate() call and Z_STREAM_END must + * be returned or there has been a problem, otherwise Z_SYNC_FLUSH is used and + * Z_OK or Z_STREAM_END will be returned on success. + * + * The input and output sizes are updated to the actual amounts of data consumed + * or written, not the amount available (as in a z_stream). The data pointers + * are not changed, so the next input is (data+input_size) and the next + * available output is (output+output_size). + */ +static int +png_inflate(png_structrp png_ptr, png_uint_32 owner, int finish, + /* INPUT: */ png_const_bytep input, png_uint_32p input_size_ptr, + /* OUTPUT: */ png_bytep output, png_alloc_size_t *output_size_ptr) +{ + if (png_ptr->zowner == owner) /* Else not claimed */ + { + int ret; + png_alloc_size_t avail_out = *output_size_ptr; + png_uint_32 avail_in = *input_size_ptr; + + /* zlib can't necessarily handle more than 65535 bytes at once (i.e. it + * can't even necessarily handle 65536 bytes) because the type uInt is + * "16 bits or more". Consequently it is necessary to chunk the input to + * zlib. This code uses ZLIB_IO_MAX, from pngpriv.h, as the maximum (the + * maximum value that can be stored in a uInt.) It is possible to set + * ZLIB_IO_MAX to a lower value in pngpriv.h and this may sometimes have + * a performance advantage, because it reduces the amount of data accessed + * at each step and that may give the OS more time to page it in. + */ + png_ptr->zstream.next_in = PNGZ_INPUT_CAST(input); + /* avail_in and avail_out are set below from 'size' */ + png_ptr->zstream.avail_in = 0; + png_ptr->zstream.avail_out = 0; + + /* Read directly into the output if it is available (this is set to + * a local buffer below if output is NULL). + */ + if (output != NULL) + png_ptr->zstream.next_out = output; + + do + { + uInt avail; + Byte local_buffer[PNG_INFLATE_BUF_SIZE]; + + /* zlib INPUT BUFFER */ + /* The setting of 'avail_in' used to be outside the loop; by setting it + * inside it is possible to chunk the input to zlib and simply rely on + * zlib to advance the 'next_in' pointer. This allows arbitrary + * amounts of data to be passed through zlib at the unavoidable cost of + * requiring a window save (memcpy of up to 32768 output bytes) + * every ZLIB_IO_MAX input bytes. + */ + avail_in += png_ptr->zstream.avail_in; /* not consumed last time */ + + avail = ZLIB_IO_MAX; + + if (avail_in < avail) + avail = (uInt)avail_in; /* safe: < than ZLIB_IO_MAX */ + + avail_in -= avail; + png_ptr->zstream.avail_in = avail; + + /* zlib OUTPUT BUFFER */ + avail_out += png_ptr->zstream.avail_out; /* not written last time */ + + avail = ZLIB_IO_MAX; /* maximum zlib can process */ + + if (output == NULL) + { + /* Reset the output buffer each time round if output is NULL and + * make available the full buffer, up to 'remaining_space' + */ + png_ptr->zstream.next_out = local_buffer; + if ((sizeof local_buffer) < avail) + avail = (sizeof local_buffer); + } + + if (avail_out < avail) + avail = (uInt)avail_out; /* safe: < ZLIB_IO_MAX */ + + png_ptr->zstream.avail_out = avail; + avail_out -= avail; + + /* zlib inflate call */ + /* In fact 'avail_out' may be 0 at this point, that happens at the end + * of the read when the final LZ end code was not passed at the end of + * the previous chunk of input data. Tell zlib if we have reached the + * end of the output buffer. + */ + ret = inflate(&png_ptr->zstream, avail_out > 0 ? Z_NO_FLUSH : + (finish ? Z_FINISH : Z_SYNC_FLUSH)); + } while (ret == Z_OK); + + /* For safety kill the local buffer pointer now */ + if (output == NULL) + png_ptr->zstream.next_out = NULL; + + /* Claw back the 'size' and 'remaining_space' byte counts. */ + avail_in += png_ptr->zstream.avail_in; + avail_out += png_ptr->zstream.avail_out; + + /* Update the input and output sizes; the updated values are the amount + * consumed or written, effectively the inverse of what zlib uses. + */ + if (avail_out > 0) + *output_size_ptr -= avail_out; + + if (avail_in > 0) + *input_size_ptr -= avail_in; + + /* Ensure png_ptr->zstream.msg is set (even in the success case!) */ + png_zstream_error(png_ptr, ret); + return ret; + } + + else + { + /* This is a bad internal error. The recovery assigns to the zstream msg + * pointer, which is not owned by the caller, but this is safe; it's only + * used on errors! + */ + png_ptr->zstream.msg = PNGZ_MSG_CAST("zstream unclaimed"); + return Z_STREAM_ERROR; + } +} + +/* + * Decompress trailing data in a chunk. The assumption is that read_buffer + * points at an allocated area holding the contents of a chunk with a + * trailing compressed part. What we get back is an allocated area + * holding the original prefix part and an uncompressed version of the + * trailing part (the malloc area passed in is freed). + */ +static int +png_decompress_chunk(png_structrp png_ptr, + png_uint_32 chunklength, png_uint_32 prefix_size, + png_alloc_size_t *newlength /* must be initialized to the maximum! */, + int terminate /*add a '\0' to the end of the uncompressed data*/) +{ + /* TODO: implement different limits for different types of chunk. + * + * The caller supplies *newlength set to the maximum length of the + * uncompressed data, but this routine allocates space for the prefix and + * maybe a '\0' terminator too. We have to assume that 'prefix_size' is + * limited only by the maximum chunk size. + */ + png_alloc_size_t limit = PNG_SIZE_MAX; + +# ifdef PNG_SET_CHUNK_MALLOC_LIMIT_SUPPORTED + if (png_ptr->user_chunk_malloc_max > 0 && + png_ptr->user_chunk_malloc_max < limit) + limit = png_ptr->user_chunk_malloc_max; +# elif PNG_USER_CHUNK_MALLOC_MAX > 0 + if (PNG_USER_CHUNK_MALLOC_MAX < limit) + limit = PNG_USER_CHUNK_MALLOC_MAX; +# endif + + if (limit >= prefix_size + (terminate != 0)) + { + int ret; + + limit -= prefix_size + (terminate != 0); + + if (limit < *newlength) + *newlength = limit; + + /* Now try to claim the stream; the 'warn' setting causes zlib to be told + * to use the maximum window size during inflate; this hides errors in the + * deflate header window bits value which is used if '0' is passed. In + * fact this only has an effect with zlib versions 1.2.4 and later - see + * the comments in png_inflate_claim above. + */ + ret = png_inflate_claim(png_ptr, png_ptr->chunk_name, + png_ptr->flags & PNG_FLAG_BENIGN_ERRORS_WARN ? 15 : 0); + + if (ret == Z_OK) + { + png_uint_32 lzsize = chunklength - prefix_size; + + ret = png_inflate(png_ptr, png_ptr->chunk_name, 1/*finish*/, + /* input: */ png_ptr->read_buffer + prefix_size, &lzsize, + /* output: */ NULL, newlength); + + if (ret == Z_STREAM_END) + { + /* Use 'inflateReset' here, not 'inflateReset2' because this + * preserves the previously decided window size (otherwise it would + * be necessary to store the previous window size.) In practice + * this doesn't matter anyway, because png_inflate will call inflate + * with Z_FINISH in almost all cases, so the window will not be + * maintained. + */ + if (inflateReset(&png_ptr->zstream) == Z_OK) + { + /* Because of the limit checks above we know that the new, + * expanded, size will fit in a size_t (let alone an + * png_alloc_size_t). Use png_malloc_base here to avoid an + * extra OOM message. + */ + png_alloc_size_t new_size = *newlength; + png_alloc_size_t buffer_size = prefix_size + new_size + + (terminate != 0); + png_bytep text = png_voidcast(png_bytep, png_malloc_base(png_ptr, + buffer_size)); + + if (text != NULL) + { + ret = png_inflate(png_ptr, png_ptr->chunk_name, 1/*finish*/, + png_ptr->read_buffer + prefix_size, &lzsize, + text + prefix_size, newlength); + + if (ret == Z_STREAM_END) + { + if (new_size == *newlength) + { + if (terminate) + text[prefix_size + *newlength] = 0; + + if (prefix_size > 0) + memcpy(text, png_ptr->read_buffer, prefix_size); + + { + png_bytep old_ptr = png_ptr->read_buffer; + + png_ptr->read_buffer = text; + png_ptr->read_buffer_size = buffer_size; + text = old_ptr; /* freed below */ + } + } + + else + { + /* The size changed on the second read, there can be no + * guarantee that anything is correct at this point. + * The 'msg' pointer has been set to "unexpected end of + * LZ stream", which is fine, but return an error code + * that the caller won't accept. + */ + ret = PNG_UNEXPECTED_ZLIB_RETURN; + } + } + + else if (ret == Z_OK) + ret = PNG_UNEXPECTED_ZLIB_RETURN; /* for safety */ + + /* Free the text pointer (this is the old read_buffer on + * success) + */ + png_free(png_ptr, text); + + /* This really is very benign, but it's still an error because + * the extra space may otherwise be used as a Trojan Horse. + */ + if (ret == Z_STREAM_END && + chunklength - prefix_size != lzsize) + png_chunk_benign_error(png_ptr, "extra compressed data"); + } + + else + { + /* Out of memory allocating the buffer */ + ret = Z_MEM_ERROR; + png_zstream_error(png_ptr, Z_MEM_ERROR); + } + } + + else + { + /* inflateReset failed, store the error message */ + png_zstream_error(png_ptr, ret); + + if (ret == Z_STREAM_END) + ret = PNG_UNEXPECTED_ZLIB_RETURN; + } + } + + else if (ret == Z_OK) + ret = PNG_UNEXPECTED_ZLIB_RETURN; + + /* Release the claimed stream */ + png_ptr->zowner = 0; + } + + else /* the claim failed */ if (ret == Z_STREAM_END) /* impossible! */ + ret = PNG_UNEXPECTED_ZLIB_RETURN; + + return ret; + } + + else + { + /* Application/configuration limits exceeded */ + png_zstream_error(png_ptr, Z_MEM_ERROR); + return Z_MEM_ERROR; + } +} +#endif /* PNG_READ_COMPRESSED_TEXT_SUPPORTED */ + +#ifdef PNG_READ_iCCP_SUPPORTED +/* Perform a partial read and decompress, producing 'avail_out' bytes and + * reading from the current chunk as required. + */ +static int +png_inflate_read(png_structrp png_ptr, png_bytep read_buffer, uInt read_size, + png_uint_32p chunk_bytes, png_bytep next_out, png_alloc_size_t *out_size, + int finish) +{ + if (png_ptr->zowner == png_ptr->chunk_name) + { + int ret; + + /* next_in and avail_in must have been initialized by the caller. */ + png_ptr->zstream.next_out = next_out; + png_ptr->zstream.avail_out = 0; /* set in the loop */ + + do + { + if (png_ptr->zstream.avail_in == 0) + { + if (read_size > *chunk_bytes) + read_size = (uInt)*chunk_bytes; + *chunk_bytes -= read_size; + + if (read_size > 0) + png_crc_read(png_ptr, read_buffer, read_size); + + png_ptr->zstream.next_in = read_buffer; + png_ptr->zstream.avail_in = read_size; + } + + if (png_ptr->zstream.avail_out == 0) + { + uInt avail = ZLIB_IO_MAX; + if (avail > *out_size) + avail = (uInt)*out_size; + *out_size -= avail; + + png_ptr->zstream.avail_out = avail; + } + + /* Use Z_SYNC_FLUSH when there is no more chunk data to ensure that all + * the available output is produced; this allows reading of truncated + * streams. + */ + ret = inflate(&png_ptr->zstream, + *chunk_bytes > 0 ? Z_NO_FLUSH : (finish ? Z_FINISH : Z_SYNC_FLUSH)); + } + while (ret == Z_OK && (*out_size > 0 || png_ptr->zstream.avail_out > 0)); + + *out_size += png_ptr->zstream.avail_out; + png_ptr->zstream.avail_out = 0; /* Should not be required, but is safe */ + + /* Ensure the error message pointer is always set: */ + png_zstream_error(png_ptr, ret); + return ret; + } + + else + { + png_ptr->zstream.msg = PNGZ_MSG_CAST("zstream unclaimed"); + return Z_STREAM_ERROR; + } +} +#endif + +/* Read and check the IDHR chunk */ +void /* PRIVATE */ +png_handle_IHDR(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) +{ + png_byte buf[13]; + png_uint_32 width, height; + int bit_depth, color_type, compression_type, filter_type; + int interlace_type; + + png_debug(1, "in png_handle_IHDR"); + + if (png_ptr->mode & PNG_HAVE_IHDR) + png_chunk_error(png_ptr, "out of place"); + + /* Check the length */ + if (length != 13) + png_chunk_error(png_ptr, "invalid"); + + png_ptr->mode |= PNG_HAVE_IHDR; + + png_crc_read(png_ptr, buf, 13); + png_crc_finish(png_ptr, 0); + + width = png_get_uint_31(png_ptr, buf); + height = png_get_uint_31(png_ptr, buf + 4); + bit_depth = buf[8]; + color_type = buf[9]; + compression_type = buf[10]; + filter_type = buf[11]; + interlace_type = buf[12]; + + /* Set internal variables */ + png_ptr->width = width; + png_ptr->height = height; + png_ptr->bit_depth = (png_byte)bit_depth; + png_ptr->interlaced = (png_byte)interlace_type; + png_ptr->color_type = (png_byte)color_type; +#ifdef PNG_MNG_FEATURES_SUPPORTED + png_ptr->filter_type = (png_byte)filter_type; +#endif + png_ptr->compression_type = (png_byte)compression_type; + + /* Find number of channels */ + switch (png_ptr->color_type) + { + default: /* invalid, png_set_IHDR calls png_error */ + case PNG_COLOR_TYPE_GRAY: + case PNG_COLOR_TYPE_PALETTE: + png_ptr->channels = 1; + break; + + case PNG_COLOR_TYPE_RGB: + png_ptr->channels = 3; + break; + + case PNG_COLOR_TYPE_GRAY_ALPHA: + png_ptr->channels = 2; + break; + + case PNG_COLOR_TYPE_RGB_ALPHA: + png_ptr->channels = 4; + break; + } + + /* Set up other useful info */ + png_ptr->pixel_depth = (png_byte)(png_ptr->bit_depth * + png_ptr->channels); + png_ptr->rowbytes = PNG_ROWBYTES(png_ptr->pixel_depth, png_ptr->width); + png_debug1(3, "bit_depth = %d", png_ptr->bit_depth); + png_debug1(3, "channels = %d", png_ptr->channels); + png_debug1(3, "rowbytes = %lu", (unsigned long)png_ptr->rowbytes); + png_set_IHDR(png_ptr, info_ptr, width, height, bit_depth, + color_type, interlace_type, compression_type, filter_type); +} + +/* Read and check the palette */ +void /* PRIVATE */ +png_handle_PLTE(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) +{ + png_color palette[PNG_MAX_PALETTE_LENGTH]; + int num, i; +#ifdef PNG_POINTER_INDEXING_SUPPORTED + png_colorp pal_ptr; +#endif + + png_debug(1, "in png_handle_PLTE"); + + if (!(png_ptr->mode & PNG_HAVE_IHDR)) + png_chunk_error(png_ptr, "missing IHDR"); + + /* Moved to before the 'after IDAT' check below because otherwise duplicate + * PLTE chunks are potentially ignored (the spec says there shall not be more + * than one PLTE, the error is not treated as benign, so this check trumps + * the requirement that PLTE appears before IDAT.) + */ + else if (png_ptr->mode & PNG_HAVE_PLTE) + png_chunk_error(png_ptr, "duplicate"); + + else if (png_ptr->mode & PNG_HAVE_IDAT) + { + /* This is benign because the non-benign error happened before, when an + * IDAT was encountered in a color-mapped image with no PLTE. + */ + png_crc_finish(png_ptr, length); + png_chunk_benign_error(png_ptr, "out of place"); + return; + } + + png_ptr->mode |= PNG_HAVE_PLTE; + + if (!(png_ptr->color_type & PNG_COLOR_MASK_COLOR)) + { + png_crc_finish(png_ptr, length); + png_chunk_benign_error(png_ptr, "ignored in grayscale PNG"); + return; + } + +#ifndef PNG_READ_OPT_PLTE_SUPPORTED + if (png_ptr->color_type != PNG_COLOR_TYPE_PALETTE) + { + png_crc_finish(png_ptr, length); + return; + } +#endif + + if (length > 3*PNG_MAX_PALETTE_LENGTH || length % 3) + { + png_crc_finish(png_ptr, length); + + if (png_ptr->color_type != PNG_COLOR_TYPE_PALETTE) + png_chunk_benign_error(png_ptr, "invalid"); + + else + png_chunk_error(png_ptr, "invalid"); + + return; + } + + /* The cast is safe because 'length' is less than 3*PNG_MAX_PALETTE_LENGTH */ + num = (int)length / 3; + +#ifdef PNG_POINTER_INDEXING_SUPPORTED + for (i = 0, pal_ptr = palette; i < num; i++, pal_ptr++) + { + png_byte buf[3]; + + png_crc_read(png_ptr, buf, 3); + pal_ptr->red = buf[0]; + pal_ptr->green = buf[1]; + pal_ptr->blue = buf[2]; + } +#else + for (i = 0; i < num; i++) + { + png_byte buf[3]; + + png_crc_read(png_ptr, buf, 3); + /* Don't depend upon png_color being any order */ + palette[i].red = buf[0]; + palette[i].green = buf[1]; + palette[i].blue = buf[2]; + } +#endif + + /* If we actually need the PLTE chunk (ie for a paletted image), we do + * whatever the normal CRC configuration tells us. However, if we + * have an RGB image, the PLTE can be considered ancillary, so + * we will act as though it is. + */ +#ifndef PNG_READ_OPT_PLTE_SUPPORTED + if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE) +#endif + { + png_crc_finish(png_ptr, 0); + } + +#ifndef PNG_READ_OPT_PLTE_SUPPORTED + else if (png_crc_error(png_ptr)) /* Only if we have a CRC error */ + { + /* If we don't want to use the data from an ancillary chunk, + * we have two options: an error abort, or a warning and we + * ignore the data in this chunk (which should be OK, since + * it's considered ancillary for a RGB or RGBA image). + * + * IMPLEMENTATION NOTE: this is only here because png_crc_finish uses the + * chunk type to determine whether to check the ancillary or the critical + * flags. + */ + if (!(png_ptr->flags & PNG_FLAG_CRC_ANCILLARY_USE)) + { + if (png_ptr->flags & PNG_FLAG_CRC_ANCILLARY_NOWARN) + { + png_chunk_benign_error(png_ptr, "CRC error"); + } + + else + { + png_chunk_warning(png_ptr, "CRC error"); + return; + } + } + + /* Otherwise, we (optionally) emit a warning and use the chunk. */ + else if (!(png_ptr->flags & PNG_FLAG_CRC_ANCILLARY_NOWARN)) + { + png_chunk_warning(png_ptr, "CRC error"); + } + } +#endif + + /* TODO: png_set_PLTE has the side effect of setting png_ptr->palette to its + * own copy of the palette. This has the side effect that when png_start_row + * is called (this happens after any call to png_read_update_info) the + * info_ptr palette gets changed. This is extremely unexpected and + * confusing. + * + * Fix this by not sharing the palette in this way. + */ + png_set_PLTE(png_ptr, info_ptr, palette, num); + + /* The three chunks, bKGD, hIST and tRNS *must* appear after PLTE and before + * IDAT. Prior to 1.6.0 this was not checked; instead the code merely + * checked the apparent validity of a tRNS chunk inserted before PLTE on a + * palette PNG. 1.6.0 attempts to rigorously follow the standard and + * therefore does a benign error if the erroneous condition is detected *and* + * cancels the tRNS if the benign error returns. The alternative is to + * amend the standard since it would be rather hypocritical of the standards + * maintainers to ignore it. + */ +#ifdef PNG_READ_tRNS_SUPPORTED + if (png_ptr->num_trans > 0 || + (info_ptr != NULL && (info_ptr->valid & PNG_INFO_tRNS) != 0)) + { + /* Cancel this because otherwise it would be used if the transforms + * require it. Don't cancel the 'valid' flag because this would prevent + * detection of duplicate chunks. + */ + png_ptr->num_trans = 0; + + if (info_ptr != NULL) + info_ptr->num_trans = 0; + + png_chunk_benign_error(png_ptr, "tRNS must be after"); + } +#endif + +#ifdef PNG_READ_hIST_SUPPORTED + if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_hIST) != 0) + png_chunk_benign_error(png_ptr, "hIST must be after"); +#endif + +#ifdef PNG_READ_bKGD_SUPPORTED + if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_bKGD) != 0) + png_chunk_benign_error(png_ptr, "bKGD must be after"); +#endif +} + +void /* PRIVATE */ +png_handle_IEND(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) +{ + png_debug(1, "in png_handle_IEND"); + + if (!(png_ptr->mode & PNG_HAVE_IHDR) || !(png_ptr->mode & PNG_HAVE_IDAT)) + png_chunk_error(png_ptr, "out of place"); + + png_ptr->mode |= (PNG_AFTER_IDAT | PNG_HAVE_IEND); + + png_crc_finish(png_ptr, length); + + if (length != 0) + png_chunk_benign_error(png_ptr, "invalid"); + + PNG_UNUSED(info_ptr) +} + +#ifdef PNG_READ_gAMA_SUPPORTED +void /* PRIVATE */ +png_handle_gAMA(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) +{ + png_fixed_point igamma; + png_byte buf[4]; + + png_debug(1, "in png_handle_gAMA"); + + if (!(png_ptr->mode & PNG_HAVE_IHDR)) + png_chunk_error(png_ptr, "missing IHDR"); + + else if (png_ptr->mode & (PNG_HAVE_IDAT|PNG_HAVE_PLTE)) + { + png_crc_finish(png_ptr, length); + png_chunk_benign_error(png_ptr, "out of place"); + return; + } + + if (length != 4) + { + png_crc_finish(png_ptr, length); + png_chunk_benign_error(png_ptr, "invalid"); + return; + } + + png_crc_read(png_ptr, buf, 4); + + if (png_crc_finish(png_ptr, 0)) + return; + + igamma = png_get_fixed_point(NULL, buf); + + png_colorspace_set_gamma(png_ptr, &png_ptr->colorspace, igamma); + png_colorspace_sync(png_ptr, info_ptr); +} +#endif + +#ifdef PNG_READ_sBIT_SUPPORTED +void /* PRIVATE */ +png_handle_sBIT(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) +{ + unsigned int truelen; + png_byte buf[4]; + + png_debug(1, "in png_handle_sBIT"); + + buf[0] = buf[1] = buf[2] = buf[3] = 0; + + if (!(png_ptr->mode & PNG_HAVE_IHDR)) + png_chunk_error(png_ptr, "missing IHDR"); + + else if (png_ptr->mode & (PNG_HAVE_IDAT|PNG_HAVE_PLTE)) + { + png_crc_finish(png_ptr, length); + png_chunk_benign_error(png_ptr, "out of place"); + return; + } + + if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_sBIT)) + { + png_crc_finish(png_ptr, length); + png_chunk_benign_error(png_ptr, "duplicate"); + return; + } + + if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE) + truelen = 3; + + else + truelen = png_ptr->channels; + + if (length != truelen || length > 4) + { + png_chunk_benign_error(png_ptr, "invalid"); + png_crc_finish(png_ptr, length); + return; + } + + png_crc_read(png_ptr, buf, truelen); + + if (png_crc_finish(png_ptr, 0)) + return; + + if (png_ptr->color_type & PNG_COLOR_MASK_COLOR) + { + png_ptr->sig_bit.red = buf[0]; + png_ptr->sig_bit.green = buf[1]; + png_ptr->sig_bit.blue = buf[2]; + png_ptr->sig_bit.alpha = buf[3]; + } + + else + { + png_ptr->sig_bit.gray = buf[0]; + png_ptr->sig_bit.red = buf[0]; + png_ptr->sig_bit.green = buf[0]; + png_ptr->sig_bit.blue = buf[0]; + png_ptr->sig_bit.alpha = buf[1]; + } + + png_set_sBIT(png_ptr, info_ptr, &(png_ptr->sig_bit)); +} +#endif + +#ifdef PNG_READ_cHRM_SUPPORTED +void /* PRIVATE */ +png_handle_cHRM(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) +{ + png_byte buf[32]; + png_xy xy; + + png_debug(1, "in png_handle_cHRM"); + + if (!(png_ptr->mode & PNG_HAVE_IHDR)) + png_chunk_error(png_ptr, "missing IHDR"); + + else if (png_ptr->mode & (PNG_HAVE_IDAT|PNG_HAVE_PLTE)) + { + png_crc_finish(png_ptr, length); + png_chunk_benign_error(png_ptr, "out of place"); + return; + } + + if (length != 32) + { + png_crc_finish(png_ptr, length); + png_chunk_benign_error(png_ptr, "invalid"); + return; + } + + png_crc_read(png_ptr, buf, 32); + + if (png_crc_finish(png_ptr, 0)) + return; + + xy.whitex = png_get_fixed_point(NULL, buf); + xy.whitey = png_get_fixed_point(NULL, buf + 4); + xy.redx = png_get_fixed_point(NULL, buf + 8); + xy.redy = png_get_fixed_point(NULL, buf + 12); + xy.greenx = png_get_fixed_point(NULL, buf + 16); + xy.greeny = png_get_fixed_point(NULL, buf + 20); + xy.bluex = png_get_fixed_point(NULL, buf + 24); + xy.bluey = png_get_fixed_point(NULL, buf + 28); + + if (xy.whitex == PNG_FIXED_ERROR || + xy.whitey == PNG_FIXED_ERROR || + xy.redx == PNG_FIXED_ERROR || + xy.redy == PNG_FIXED_ERROR || + xy.greenx == PNG_FIXED_ERROR || + xy.greeny == PNG_FIXED_ERROR || + xy.bluex == PNG_FIXED_ERROR || + xy.bluey == PNG_FIXED_ERROR) + { + png_chunk_benign_error(png_ptr, "invalid values"); + return; + } + + /* If a colorspace error has already been output skip this chunk */ + if (png_ptr->colorspace.flags & PNG_COLORSPACE_INVALID) + return; + + if (png_ptr->colorspace.flags & PNG_COLORSPACE_FROM_cHRM) + { + png_ptr->colorspace.flags |= PNG_COLORSPACE_INVALID; + png_colorspace_sync(png_ptr, info_ptr); + png_chunk_benign_error(png_ptr, "duplicate"); + return; + } + + png_ptr->colorspace.flags |= PNG_COLORSPACE_FROM_cHRM; + (void)png_colorspace_set_chromaticities(png_ptr, &png_ptr->colorspace, &xy, + 1/*prefer cHRM values*/); + png_colorspace_sync(png_ptr, info_ptr); +} +#endif + +#ifdef PNG_READ_sRGB_SUPPORTED +void /* PRIVATE */ +png_handle_sRGB(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) +{ + png_byte intent; + + png_debug(1, "in png_handle_sRGB"); + + if (!(png_ptr->mode & PNG_HAVE_IHDR)) + png_chunk_error(png_ptr, "missing IHDR"); + + else if (png_ptr->mode & (PNG_HAVE_IDAT|PNG_HAVE_PLTE)) + { + png_crc_finish(png_ptr, length); + png_chunk_benign_error(png_ptr, "out of place"); + return; + } + + if (length != 1) + { + png_crc_finish(png_ptr, length); + png_chunk_benign_error(png_ptr, "invalid"); + return; + } + + png_crc_read(png_ptr, &intent, 1); + + if (png_crc_finish(png_ptr, 0)) + return; + + /* If a colorspace error has already been output skip this chunk */ + if (png_ptr->colorspace.flags & PNG_COLORSPACE_INVALID) + return; + + /* Only one sRGB or iCCP chunk is allowed, use the HAVE_INTENT flag to detect + * this. + */ + if (png_ptr->colorspace.flags & PNG_COLORSPACE_HAVE_INTENT) + { + png_ptr->colorspace.flags |= PNG_COLORSPACE_INVALID; + png_colorspace_sync(png_ptr, info_ptr); + png_chunk_benign_error(png_ptr, "too many profiles"); + return; + } + + (void)png_colorspace_set_sRGB(png_ptr, &png_ptr->colorspace, intent); + png_colorspace_sync(png_ptr, info_ptr); +} +#endif /* PNG_READ_sRGB_SUPPORTED */ + +#ifdef PNG_READ_iCCP_SUPPORTED +void /* PRIVATE */ +png_handle_iCCP(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) +/* Note: this does not properly handle profiles that are > 64K under DOS */ +{ + png_const_charp errmsg = NULL; /* error message output, or no error */ + int finished = 0; /* crc checked */ + + png_debug(1, "in png_handle_iCCP"); + + if (!(png_ptr->mode & PNG_HAVE_IHDR)) + png_chunk_error(png_ptr, "missing IHDR"); + + else if (png_ptr->mode & (PNG_HAVE_IDAT|PNG_HAVE_PLTE)) + { + png_crc_finish(png_ptr, length); + png_chunk_benign_error(png_ptr, "out of place"); + return; + } + + /* Consistent with all the above colorspace handling an obviously *invalid* + * chunk is just ignored, so does not invalidate the color space. An + * alternative is to set the 'invalid' flags at the start of this routine + * and only clear them in they were not set before and all the tests pass. + * The minimum 'deflate' stream is assumed to be just the 2 byte header and 4 + * byte checksum. The keyword must be one character and there is a + * terminator (0) byte and the compression method. + */ + if (length < 9) + { + png_crc_finish(png_ptr, length); + png_chunk_benign_error(png_ptr, "too short"); + return; + } + + /* If a colorspace error has already been output skip this chunk */ + if (png_ptr->colorspace.flags & PNG_COLORSPACE_INVALID) + { + png_crc_finish(png_ptr, length); + return; + } + + /* Only one sRGB or iCCP chunk is allowed, use the HAVE_INTENT flag to detect + * this. + */ + if ((png_ptr->colorspace.flags & PNG_COLORSPACE_HAVE_INTENT) == 0) + { + uInt read_length, keyword_length; + char keyword[81]; + + /* Find the keyword; the keyword plus separator and compression method + * bytes can be at most 81 characters long. + */ + read_length = 81; /* maximum */ + if (read_length > length) + read_length = (uInt)length; + + png_crc_read(png_ptr, (png_bytep)keyword, read_length); + length -= read_length; + + keyword_length = 0; + while (keyword_length < 80 && keyword_length < read_length && + keyword[keyword_length] != 0) + ++keyword_length; + + /* TODO: make the keyword checking common */ + if (keyword_length >= 1 && keyword_length <= 79) + { + /* We only understand '0' compression - deflate - so if we get a + * different value we can't safely decode the chunk. + */ + if (keyword_length+1 < read_length && + keyword[keyword_length+1] == PNG_COMPRESSION_TYPE_BASE) + { + read_length -= keyword_length+2; + + if (png_inflate_claim(png_ptr, png_iCCP, + png_ptr->flags & PNG_FLAG_BENIGN_ERRORS_WARN ? 15 : 0) == Z_OK) + { + Byte profile_header[132]; + Byte local_buffer[PNG_INFLATE_BUF_SIZE]; + png_alloc_size_t size = (sizeof profile_header); + + png_ptr->zstream.next_in = (Bytef*)keyword + (keyword_length+2); + png_ptr->zstream.avail_in = read_length; + (void)png_inflate_read(png_ptr, local_buffer, + (sizeof local_buffer), &length, profile_header, &size, + 0/*finish: don't, because the output is too small*/); + + if (size == 0) + { + /* We have the ICC profile header; do the basic header checks. + */ + const png_uint_32 profile_length = + png_get_uint_32(profile_header); + + if (png_icc_check_length(png_ptr, &png_ptr->colorspace, + keyword, profile_length)) + { + /* The length is apparently ok, so we can check the 132 + * byte header. + */ + if (png_icc_check_header(png_ptr, &png_ptr->colorspace, + keyword, profile_length, profile_header, + png_ptr->color_type)) + { + /* Now read the tag table; a variable size buffer is + * needed at this point, allocate one for the whole + * profile. The header check has already validated + * that none of these stuff will overflow. + */ + const png_uint_32 tag_count = png_get_uint_32( + profile_header+128); + png_bytep profile = png_read_buffer(png_ptr, + profile_length, 2/*silent*/); + + if (profile != NULL) + { + memcpy(profile, profile_header, + (sizeof profile_header)); + + size = 12 * tag_count; + + (void)png_inflate_read(png_ptr, local_buffer, + (sizeof local_buffer), &length, + profile + (sizeof profile_header), &size, 0); + + /* Still expect a a buffer error because we expect + * there to be some tag data! + */ + if (size == 0) + { + if (png_icc_check_tag_table(png_ptr, + &png_ptr->colorspace, keyword, profile_length, + profile)) + { + /* The profile has been validated for basic + * security issues, so read the whole thing in. + */ + size = profile_length - (sizeof profile_header) + - 12 * tag_count; + + (void)png_inflate_read(png_ptr, local_buffer, + (sizeof local_buffer), &length, + profile + (sizeof profile_header) + + 12 * tag_count, &size, 1/*finish*/); + + if (length > 0 && !(png_ptr->flags & + PNG_FLAG_BENIGN_ERRORS_WARN)) + errmsg = "extra compressed data"; + + /* But otherwise allow extra data: */ + else if (size == 0) + { + if (length > 0) + { + /* This can be handled completely, so + * keep going. + */ + png_chunk_warning(png_ptr, + "extra compressed data"); + } + + png_crc_finish(png_ptr, length); + finished = 1; + +# ifdef PNG_sRGB_SUPPORTED + /* Check for a match against sRGB */ + png_icc_set_sRGB(png_ptr, + &png_ptr->colorspace, profile, + png_ptr->zstream.adler); +# endif + + /* Steal the profile for info_ptr. */ + if (info_ptr != NULL) + { + png_free_data(png_ptr, info_ptr, + PNG_FREE_ICCP, 0); + + info_ptr->iccp_name = png_voidcast(char*, + png_malloc_base(png_ptr, + keyword_length+1)); + if (info_ptr->iccp_name != NULL) + { + memcpy(info_ptr->iccp_name, keyword, + keyword_length+1); + info_ptr->iccp_proflen = + profile_length; + info_ptr->iccp_profile = profile; + png_ptr->read_buffer = NULL; /*steal*/ + info_ptr->free_me |= PNG_FREE_ICCP; + info_ptr->valid |= PNG_INFO_iCCP; + } + + else + { + png_ptr->colorspace.flags |= + PNG_COLORSPACE_INVALID; + errmsg = "out of memory"; + } + } + + /* else the profile remains in the read + * buffer which gets reused for subsequent + * chunks. + */ + + if (info_ptr != NULL) + png_colorspace_sync(png_ptr, info_ptr); + + if (errmsg == NULL) + { + png_ptr->zowner = 0; + return; + } + } + + else if (size > 0) + errmsg = "truncated"; + + else + errmsg = png_ptr->zstream.msg; + } + + /* else png_icc_check_tag_table output an error */ + } + + else /* profile truncated */ + errmsg = png_ptr->zstream.msg; + } + + else + errmsg = "out of memory"; + } + + /* else png_icc_check_header output an error */ + } + + /* else png_icc_check_length output an error */ + } + + else /* profile truncated */ + errmsg = png_ptr->zstream.msg; + + /* Release the stream */ + png_ptr->zowner = 0; + } + + else /* png_inflate_claim failed */ + errmsg = png_ptr->zstream.msg; + } + + else + errmsg = "bad compression method"; /* or missing */ + } + + else + errmsg = "bad keyword"; + } + + else + errmsg = "too many profiles"; + + /* Failure: the reason is in 'errmsg' */ + if (!finished) + png_crc_finish(png_ptr, length); + + png_ptr->colorspace.flags |= PNG_COLORSPACE_INVALID; + png_colorspace_sync(png_ptr, info_ptr); + if (errmsg != NULL) /* else already output */ + png_chunk_benign_error(png_ptr, errmsg); +} +#endif /* PNG_READ_iCCP_SUPPORTED */ + +#ifdef PNG_READ_sPLT_SUPPORTED +void /* PRIVATE */ +png_handle_sPLT(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) +/* Note: this does not properly handle chunks that are > 64K under DOS */ +{ + png_bytep entry_start, buffer; + png_sPLT_t new_palette; + png_sPLT_entryp pp; + png_uint_32 data_length; + int entry_size, i; + png_uint_32 skip = 0; + png_uint_32 dl; + png_size_t max_dl; + + png_debug(1, "in png_handle_sPLT"); + +#ifdef PNG_USER_LIMITS_SUPPORTED + if (png_ptr->user_chunk_cache_max != 0) + { + if (png_ptr->user_chunk_cache_max == 1) + { + png_crc_finish(png_ptr, length); + return; + } + + if (--png_ptr->user_chunk_cache_max == 1) + { + png_warning(png_ptr, "No space in chunk cache for sPLT"); + png_crc_finish(png_ptr, length); + return; + } + } +#endif + + if (!(png_ptr->mode & PNG_HAVE_IHDR)) + png_chunk_error(png_ptr, "missing IHDR"); + + else if (png_ptr->mode & PNG_HAVE_IDAT) + { + png_crc_finish(png_ptr, length); + png_chunk_benign_error(png_ptr, "out of place"); + return; + } + +#ifdef PNG_MAX_MALLOC_64K + if (length > 65535U) + { + png_crc_finish(png_ptr, length); + png_chunk_benign_error(png_ptr, "too large to fit in memory"); + return; + } +#endif + + buffer = png_read_buffer(png_ptr, length+1, 2/*silent*/); + if (buffer == NULL) + { + png_crc_finish(png_ptr, length); + png_chunk_benign_error(png_ptr, "out of memory"); + return; + } + + + /* WARNING: this may break if size_t is less than 32 bits; it is assumed + * that the PNG_MAX_MALLOC_64K test is enabled in this case, but this is a + * potential breakage point if the types in pngconf.h aren't exactly right. + */ + png_crc_read(png_ptr, buffer, length); + + if (png_crc_finish(png_ptr, skip)) + return; + + buffer[length] = 0; + + for (entry_start = buffer; *entry_start; entry_start++) + /* Empty loop to find end of name */ ; + + ++entry_start; + + /* A sample depth should follow the separator, and we should be on it */ + if (entry_start > buffer + length - 2) + { + png_warning(png_ptr, "malformed sPLT chunk"); + return; + } + + new_palette.depth = *entry_start++; + entry_size = (new_palette.depth == 8 ? 6 : 10); + /* This must fit in a png_uint_32 because it is derived from the original + * chunk data length. + */ + data_length = length - (png_uint_32)(entry_start - buffer); + + /* Integrity-check the data length */ + if (data_length % entry_size) + { + png_warning(png_ptr, "sPLT chunk has bad length"); + return; + } + + dl = (png_int_32)(data_length / entry_size); + max_dl = PNG_SIZE_MAX / (sizeof (png_sPLT_entry)); + + if (dl > max_dl) + { + png_warning(png_ptr, "sPLT chunk too long"); + return; + } + + new_palette.nentries = (png_int_32)(data_length / entry_size); + + new_palette.entries = (png_sPLT_entryp)png_malloc_warn( + png_ptr, new_palette.nentries * (sizeof (png_sPLT_entry))); + + if (new_palette.entries == NULL) + { + png_warning(png_ptr, "sPLT chunk requires too much memory"); + return; + } + +#ifdef PNG_POINTER_INDEXING_SUPPORTED + for (i = 0; i < new_palette.nentries; i++) + { + pp = new_palette.entries + i; + + if (new_palette.depth == 8) + { + pp->red = *entry_start++; + pp->green = *entry_start++; + pp->blue = *entry_start++; + pp->alpha = *entry_start++; + } + + else + { + pp->red = png_get_uint_16(entry_start); entry_start += 2; + pp->green = png_get_uint_16(entry_start); entry_start += 2; + pp->blue = png_get_uint_16(entry_start); entry_start += 2; + pp->alpha = png_get_uint_16(entry_start); entry_start += 2; + } + + pp->frequency = png_get_uint_16(entry_start); entry_start += 2; + } +#else + pp = new_palette.entries; + + for (i = 0; i < new_palette.nentries; i++) + { + + if (new_palette.depth == 8) + { + pp[i].red = *entry_start++; + pp[i].green = *entry_start++; + pp[i].blue = *entry_start++; + pp[i].alpha = *entry_start++; + } + + else + { + pp[i].red = png_get_uint_16(entry_start); entry_start += 2; + pp[i].green = png_get_uint_16(entry_start); entry_start += 2; + pp[i].blue = png_get_uint_16(entry_start); entry_start += 2; + pp[i].alpha = png_get_uint_16(entry_start); entry_start += 2; + } + + pp[i].frequency = png_get_uint_16(entry_start); entry_start += 2; + } +#endif + + /* Discard all chunk data except the name and stash that */ + new_palette.name = (png_charp)buffer; + + png_set_sPLT(png_ptr, info_ptr, &new_palette, 1); + + png_free(png_ptr, new_palette.entries); +} +#endif /* PNG_READ_sPLT_SUPPORTED */ + +#ifdef PNG_READ_tRNS_SUPPORTED +void /* PRIVATE */ +png_handle_tRNS(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) +{ + png_byte readbuf[PNG_MAX_PALETTE_LENGTH]; + + png_debug(1, "in png_handle_tRNS"); + + if (!(png_ptr->mode & PNG_HAVE_IHDR)) + png_chunk_error(png_ptr, "missing IHDR"); + + else if (png_ptr->mode & PNG_HAVE_IDAT) + { + png_crc_finish(png_ptr, length); + png_chunk_benign_error(png_ptr, "out of place"); + return; + } + + else if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_tRNS)) + { + png_crc_finish(png_ptr, length); + png_chunk_benign_error(png_ptr, "duplicate"); + return; + } + + if (png_ptr->color_type == PNG_COLOR_TYPE_GRAY) + { + png_byte buf[2]; + + if (length != 2) + { + png_crc_finish(png_ptr, length); + png_chunk_benign_error(png_ptr, "invalid"); + return; + } + + png_crc_read(png_ptr, buf, 2); + png_ptr->num_trans = 1; + png_ptr->trans_color.gray = png_get_uint_16(buf); + } + + else if (png_ptr->color_type == PNG_COLOR_TYPE_RGB) + { + png_byte buf[6]; + + if (length != 6) + { + png_crc_finish(png_ptr, length); + png_chunk_benign_error(png_ptr, "invalid"); + return; + } + + png_crc_read(png_ptr, buf, length); + png_ptr->num_trans = 1; + png_ptr->trans_color.red = png_get_uint_16(buf); + png_ptr->trans_color.green = png_get_uint_16(buf + 2); + png_ptr->trans_color.blue = png_get_uint_16(buf + 4); + } + + else if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE) + { + if (!(png_ptr->mode & PNG_HAVE_PLTE)) + { + /* TODO: is this actually an error in the ISO spec? */ + png_crc_finish(png_ptr, length); + png_chunk_benign_error(png_ptr, "out of place"); + return; + } + + if (length > png_ptr->num_palette || length > PNG_MAX_PALETTE_LENGTH || + length == 0) + { + png_crc_finish(png_ptr, length); + png_chunk_benign_error(png_ptr, "invalid"); + return; + } + + png_crc_read(png_ptr, readbuf, length); + png_ptr->num_trans = (png_uint_16)length; + } + + else + { + png_crc_finish(png_ptr, length); + png_chunk_benign_error(png_ptr, "invalid with alpha channel"); + return; + } + + if (png_crc_finish(png_ptr, 0)) + { + png_ptr->num_trans = 0; + return; + } + + /* TODO: this is a horrible side effect in the palette case because the + * png_struct ends up with a pointer to the tRNS buffer owned by the + * png_info. Fix this. + */ + png_set_tRNS(png_ptr, info_ptr, readbuf, png_ptr->num_trans, + &(png_ptr->trans_color)); +} +#endif + +#ifdef PNG_READ_bKGD_SUPPORTED +void /* PRIVATE */ +png_handle_bKGD(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) +{ + unsigned int truelen; + png_byte buf[6]; + png_color_16 background; + + png_debug(1, "in png_handle_bKGD"); + + if (!(png_ptr->mode & PNG_HAVE_IHDR)) + png_chunk_error(png_ptr, "missing IHDR"); + + else if ((png_ptr->mode & PNG_HAVE_IDAT) || + (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE && + !(png_ptr->mode & PNG_HAVE_PLTE))) + { + png_crc_finish(png_ptr, length); + png_chunk_benign_error(png_ptr, "out of place"); + return; + } + + else if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_bKGD)) + { + png_crc_finish(png_ptr, length); + png_chunk_benign_error(png_ptr, "duplicate"); + return; + } + + if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE) + truelen = 1; + + else if (png_ptr->color_type & PNG_COLOR_MASK_COLOR) + truelen = 6; + + else + truelen = 2; + + if (length != truelen) + { + png_crc_finish(png_ptr, length); + png_chunk_benign_error(png_ptr, "invalid"); + return; + } + + png_crc_read(png_ptr, buf, truelen); + + if (png_crc_finish(png_ptr, 0)) + return; + + /* We convert the index value into RGB components so that we can allow + * arbitrary RGB values for background when we have transparency, and + * so it is easy to determine the RGB values of the background color + * from the info_ptr struct. + */ + if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE) + { + background.index = buf[0]; + + if (info_ptr && info_ptr->num_palette) + { + if (buf[0] >= info_ptr->num_palette) + { + png_chunk_benign_error(png_ptr, "invalid index"); + return; + } + + background.red = (png_uint_16)png_ptr->palette[buf[0]].red; + background.green = (png_uint_16)png_ptr->palette[buf[0]].green; + background.blue = (png_uint_16)png_ptr->palette[buf[0]].blue; + } + + else + background.red = background.green = background.blue = 0; + + background.gray = 0; + } + + else if (!(png_ptr->color_type & PNG_COLOR_MASK_COLOR)) /* GRAY */ + { + background.index = 0; + background.red = + background.green = + background.blue = + background.gray = png_get_uint_16(buf); + } + + else + { + background.index = 0; + background.red = png_get_uint_16(buf); + background.green = png_get_uint_16(buf + 2); + background.blue = png_get_uint_16(buf + 4); + background.gray = 0; + } + + png_set_bKGD(png_ptr, info_ptr, &background); +} +#endif + +#ifdef PNG_READ_hIST_SUPPORTED +void /* PRIVATE */ +png_handle_hIST(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) +{ + unsigned int num, i; + png_uint_16 readbuf[PNG_MAX_PALETTE_LENGTH]; + + png_debug(1, "in png_handle_hIST"); + + if (!(png_ptr->mode & PNG_HAVE_IHDR)) + png_chunk_error(png_ptr, "missing IHDR"); + + else if ((png_ptr->mode & PNG_HAVE_IDAT) || !(png_ptr->mode & PNG_HAVE_PLTE)) + { + png_crc_finish(png_ptr, length); + png_chunk_benign_error(png_ptr, "out of place"); + return; + } + + else if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_hIST)) + { + png_crc_finish(png_ptr, length); + png_chunk_benign_error(png_ptr, "duplicate"); + return; + } + + num = length / 2 ; + + if (num != png_ptr->num_palette || num > PNG_MAX_PALETTE_LENGTH) + { + png_crc_finish(png_ptr, length); + png_chunk_benign_error(png_ptr, "invalid"); + return; + } + + for (i = 0; i < num; i++) + { + png_byte buf[2]; + + png_crc_read(png_ptr, buf, 2); + readbuf[i] = png_get_uint_16(buf); + } + + if (png_crc_finish(png_ptr, 0)) + return; + + png_set_hIST(png_ptr, info_ptr, readbuf); +} +#endif + +#ifdef PNG_READ_pHYs_SUPPORTED +void /* PRIVATE */ +png_handle_pHYs(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) +{ + png_byte buf[9]; + png_uint_32 res_x, res_y; + int unit_type; + + png_debug(1, "in png_handle_pHYs"); + + if (!(png_ptr->mode & PNG_HAVE_IHDR)) + png_chunk_error(png_ptr, "missing IHDR"); + + else if (png_ptr->mode & PNG_HAVE_IDAT) + { + png_crc_finish(png_ptr, length); + png_chunk_benign_error(png_ptr, "out of place"); + return; + } + + else if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_pHYs)) + { + png_crc_finish(png_ptr, length); + png_chunk_benign_error(png_ptr, "duplicate"); + return; + } + + if (length != 9) + { + png_crc_finish(png_ptr, length); + png_chunk_benign_error(png_ptr, "invalid"); + return; + } + + png_crc_read(png_ptr, buf, 9); + + if (png_crc_finish(png_ptr, 0)) + return; + + res_x = png_get_uint_32(buf); + res_y = png_get_uint_32(buf + 4); + unit_type = buf[8]; + png_set_pHYs(png_ptr, info_ptr, res_x, res_y, unit_type); +} +#endif + +#ifdef PNG_READ_oFFs_SUPPORTED +void /* PRIVATE */ +png_handle_oFFs(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) +{ + png_byte buf[9]; + png_int_32 offset_x, offset_y; + int unit_type; + + png_debug(1, "in png_handle_oFFs"); + + if (!(png_ptr->mode & PNG_HAVE_IHDR)) + png_chunk_error(png_ptr, "missing IHDR"); + + else if (png_ptr->mode & PNG_HAVE_IDAT) + { + png_crc_finish(png_ptr, length); + png_chunk_benign_error(png_ptr, "out of place"); + return; + } + + else if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_oFFs)) + { + png_crc_finish(png_ptr, length); + png_chunk_benign_error(png_ptr, "duplicate"); + return; + } + + if (length != 9) + { + png_crc_finish(png_ptr, length); + png_chunk_benign_error(png_ptr, "invalid"); + return; + } + + png_crc_read(png_ptr, buf, 9); + + if (png_crc_finish(png_ptr, 0)) + return; + + offset_x = png_get_int_32(buf); + offset_y = png_get_int_32(buf + 4); + unit_type = buf[8]; + png_set_oFFs(png_ptr, info_ptr, offset_x, offset_y, unit_type); +} +#endif + +#ifdef PNG_READ_pCAL_SUPPORTED +/* Read the pCAL chunk (described in the PNG Extensions document) */ +void /* PRIVATE */ +png_handle_pCAL(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) +{ + png_int_32 X0, X1; + png_byte type, nparams; + png_bytep buffer, buf, units, endptr; + png_charpp params; + int i; + + png_debug(1, "in png_handle_pCAL"); + + if (!(png_ptr->mode & PNG_HAVE_IHDR)) + png_chunk_error(png_ptr, "missing IHDR"); + + else if (png_ptr->mode & PNG_HAVE_IDAT) + { + png_crc_finish(png_ptr, length); + png_chunk_benign_error(png_ptr, "out of place"); + return; + } + + else if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_pCAL)) + { + png_crc_finish(png_ptr, length); + png_chunk_benign_error(png_ptr, "duplicate"); + return; + } + + png_debug1(2, "Allocating and reading pCAL chunk data (%u bytes)", + length + 1); + + buffer = png_read_buffer(png_ptr, length+1, 2/*silent*/); + + if (buffer == NULL) + { + png_crc_finish(png_ptr, length); + png_chunk_benign_error(png_ptr, "out of memory"); + return; + } + + png_crc_read(png_ptr, buffer, length); + + if (png_crc_finish(png_ptr, 0)) + return; + + buffer[length] = 0; /* Null terminate the last string */ + + png_debug(3, "Finding end of pCAL purpose string"); + for (buf = buffer; *buf; buf++) + /* Empty loop */ ; + + endptr = buffer + length; + + /* We need to have at least 12 bytes after the purpose string + * in order to get the parameter information. + */ + if (endptr <= buf + 12) + { + png_chunk_benign_error(png_ptr, "invalid"); + return; + } + + png_debug(3, "Reading pCAL X0, X1, type, nparams, and units"); + X0 = png_get_int_32((png_bytep)buf+1); + X1 = png_get_int_32((png_bytep)buf+5); + type = buf[9]; + nparams = buf[10]; + units = buf + 11; + + png_debug(3, "Checking pCAL equation type and number of parameters"); + /* Check that we have the right number of parameters for known + * equation types. + */ + if ((type == PNG_EQUATION_LINEAR && nparams != 2) || + (type == PNG_EQUATION_BASE_E && nparams != 3) || + (type == PNG_EQUATION_ARBITRARY && nparams != 3) || + (type == PNG_EQUATION_HYPERBOLIC && nparams != 4)) + { + png_chunk_benign_error(png_ptr, "invalid parameter count"); + return; + } + + else if (type >= PNG_EQUATION_LAST) + { + png_chunk_benign_error(png_ptr, "unrecognized equation type"); + } + + for (buf = units; *buf; buf++) + /* Empty loop to move past the units string. */ ; + + png_debug(3, "Allocating pCAL parameters array"); + + params = png_voidcast(png_charpp, png_malloc_warn(png_ptr, + nparams * (sizeof (png_charp)))); + + if (params == NULL) + { + png_chunk_benign_error(png_ptr, "out of memory"); + return; + } + + /* Get pointers to the start of each parameter string. */ + for (i = 0; i < nparams; i++) + { + buf++; /* Skip the null string terminator from previous parameter. */ + + png_debug1(3, "Reading pCAL parameter %d", i); + + for (params[i] = (png_charp)buf; buf <= endptr && *buf != 0; buf++) + /* Empty loop to move past each parameter string */ ; + + /* Make sure we haven't run out of data yet */ + if (buf > endptr) + { + png_free(png_ptr, params); + png_chunk_benign_error(png_ptr, "invalid data"); + return; + } + } + + png_set_pCAL(png_ptr, info_ptr, (png_charp)buffer, X0, X1, type, nparams, + (png_charp)units, params); + + png_free(png_ptr, params); +} +#endif + +#ifdef PNG_READ_sCAL_SUPPORTED +/* Read the sCAL chunk */ +void /* PRIVATE */ +png_handle_sCAL(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) +{ + png_bytep buffer; + png_size_t i; + int state; + + png_debug(1, "in png_handle_sCAL"); + + if (!(png_ptr->mode & PNG_HAVE_IHDR)) + png_chunk_error(png_ptr, "missing IHDR"); + + else if (png_ptr->mode & PNG_HAVE_IDAT) + { + png_crc_finish(png_ptr, length); + png_chunk_benign_error(png_ptr, "out of place"); + return; + } + + else if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_sCAL)) + { + png_crc_finish(png_ptr, length); + png_chunk_benign_error(png_ptr, "duplicate"); + return; + } + + /* Need unit type, width, \0, height: minimum 4 bytes */ + else if (length < 4) + { + png_crc_finish(png_ptr, length); + png_chunk_benign_error(png_ptr, "invalid"); + return; + } + + png_debug1(2, "Allocating and reading sCAL chunk data (%u bytes)", + length + 1); + + buffer = png_read_buffer(png_ptr, length+1, 2/*silent*/); + + if (buffer == NULL) + { + png_chunk_benign_error(png_ptr, "out of memory"); + png_crc_finish(png_ptr, length); + return; + } + + png_crc_read(png_ptr, buffer, length); + buffer[length] = 0; /* Null terminate the last string */ + + if (png_crc_finish(png_ptr, 0)) + return; + + /* Validate the unit. */ + if (buffer[0] != 1 && buffer[0] != 2) + { + png_chunk_benign_error(png_ptr, "invalid unit"); + return; + } + + /* Validate the ASCII numbers, need two ASCII numbers separated by + * a '\0' and they need to fit exactly in the chunk data. + */ + i = 1; + state = 0; + + if (!png_check_fp_number((png_const_charp)buffer, length, &state, &i) || + i >= length || buffer[i++] != 0) + png_chunk_benign_error(png_ptr, "bad width format"); + + else if (!PNG_FP_IS_POSITIVE(state)) + png_chunk_benign_error(png_ptr, "non-positive width"); + + else + { + png_size_t heighti = i; + + state = 0; + if (!png_check_fp_number((png_const_charp)buffer, length, &state, &i) || + i != length) + png_chunk_benign_error(png_ptr, "bad height format"); + + else if (!PNG_FP_IS_POSITIVE(state)) + png_chunk_benign_error(png_ptr, "non-positive height"); + + else + /* This is the (only) success case. */ + png_set_sCAL_s(png_ptr, info_ptr, buffer[0], + (png_charp)buffer+1, (png_charp)buffer+heighti); + } +} +#endif + +#ifdef PNG_READ_tIME_SUPPORTED +void /* PRIVATE */ +png_handle_tIME(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) +{ + png_byte buf[7]; + png_time mod_time; + + png_debug(1, "in png_handle_tIME"); + + if (!(png_ptr->mode & PNG_HAVE_IHDR)) + png_chunk_error(png_ptr, "missing IHDR"); + + else if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_tIME)) + { + png_crc_finish(png_ptr, length); + png_chunk_benign_error(png_ptr, "duplicate"); + return; + } + + if (png_ptr->mode & PNG_HAVE_IDAT) + png_ptr->mode |= PNG_AFTER_IDAT; + + if (length != 7) + { + png_crc_finish(png_ptr, length); + png_chunk_benign_error(png_ptr, "invalid"); + return; + } + + png_crc_read(png_ptr, buf, 7); + + if (png_crc_finish(png_ptr, 0)) + return; + + mod_time.second = buf[6]; + mod_time.minute = buf[5]; + mod_time.hour = buf[4]; + mod_time.day = buf[3]; + mod_time.month = buf[2]; + mod_time.year = png_get_uint_16(buf); + + png_set_tIME(png_ptr, info_ptr, &mod_time); +} +#endif + +#ifdef PNG_READ_tEXt_SUPPORTED +/* Note: this does not properly handle chunks that are > 64K under DOS */ +void /* PRIVATE */ +png_handle_tEXt(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) +{ + png_text text_info; + png_bytep buffer; + png_charp key; + png_charp text; + png_uint_32 skip = 0; + + png_debug(1, "in png_handle_tEXt"); + +#ifdef PNG_USER_LIMITS_SUPPORTED + if (png_ptr->user_chunk_cache_max != 0) + { + if (png_ptr->user_chunk_cache_max == 1) + { + png_crc_finish(png_ptr, length); + return; + } + + if (--png_ptr->user_chunk_cache_max == 1) + { + png_crc_finish(png_ptr, length); + png_chunk_benign_error(png_ptr, "no space in chunk cache"); + return; + } + } +#endif + + if (!(png_ptr->mode & PNG_HAVE_IHDR)) + png_chunk_error(png_ptr, "missing IHDR"); + + if (png_ptr->mode & PNG_HAVE_IDAT) + png_ptr->mode |= PNG_AFTER_IDAT; + +#ifdef PNG_MAX_MALLOC_64K + if (length > 65535U) + { + png_crc_finish(png_ptr, length); + png_chunk_benign_error(png_ptr, "too large to fit in memory"); + return; + } +#endif + + buffer = png_read_buffer(png_ptr, length+1, 1/*warn*/); + + if (buffer == NULL) + { + png_chunk_benign_error(png_ptr, "out of memory"); + return; + } + + png_crc_read(png_ptr, buffer, length); + + if (png_crc_finish(png_ptr, skip)) + return; + + key = (png_charp)buffer; + key[length] = 0; + + for (text = key; *text; text++) + /* Empty loop to find end of key */ ; + + if (text != key + length) + text++; + + text_info.compression = PNG_TEXT_COMPRESSION_NONE; + text_info.key = key; + text_info.lang = NULL; + text_info.lang_key = NULL; + text_info.itxt_length = 0; + text_info.text = text; + text_info.text_length = strlen(text); + + if (png_set_text_2(png_ptr, info_ptr, &text_info, 1)) + png_warning(png_ptr, "Insufficient memory to process text chunk"); +} +#endif + +#ifdef PNG_READ_zTXt_SUPPORTED +/* Note: this does not correctly handle chunks that are > 64K under DOS */ +void /* PRIVATE */ +png_handle_zTXt(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) +{ + png_const_charp errmsg = NULL; + png_bytep buffer; + png_uint_32 keyword_length; + + png_debug(1, "in png_handle_zTXt"); + +#ifdef PNG_USER_LIMITS_SUPPORTED + if (png_ptr->user_chunk_cache_max != 0) + { + if (png_ptr->user_chunk_cache_max == 1) + { + png_crc_finish(png_ptr, length); + return; + } + + if (--png_ptr->user_chunk_cache_max == 1) + { + png_crc_finish(png_ptr, length); + png_chunk_benign_error(png_ptr, "no space in chunk cache"); + return; + } + } +#endif + + if (!(png_ptr->mode & PNG_HAVE_IHDR)) + png_chunk_error(png_ptr, "missing IHDR"); + + if (png_ptr->mode & PNG_HAVE_IDAT) + png_ptr->mode |= PNG_AFTER_IDAT; + + buffer = png_read_buffer(png_ptr, length, 2/*silent*/); + + if (buffer == NULL) + { + png_crc_finish(png_ptr, length); + png_chunk_benign_error(png_ptr, "out of memory"); + return; + } + + png_crc_read(png_ptr, buffer, length); + + if (png_crc_finish(png_ptr, 0)) + return; + + /* TODO: also check that the keyword contents match the spec! */ + for (keyword_length = 0; + keyword_length < length && buffer[keyword_length] != 0; + ++keyword_length) + /* Empty loop to find end of name */ ; + + if (keyword_length > 79 || keyword_length < 1) + errmsg = "bad keyword"; + + /* zTXt must have some LZ data after the keyword, although it may expand to + * zero bytes; we need a '\0' at the end of the keyword, the compression type + * then the LZ data: + */ + else if (keyword_length + 3 > length) + errmsg = "truncated"; + + else if (buffer[keyword_length+1] != PNG_COMPRESSION_TYPE_BASE) + errmsg = "unknown compression type"; + + else + { + png_alloc_size_t uncompressed_length = PNG_SIZE_MAX; + + /* TODO: at present png_decompress_chunk imposes a single application + * level memory limit, this should be split to different values for iCCP + * and text chunks. + */ + if (png_decompress_chunk(png_ptr, length, keyword_length+2, + &uncompressed_length, 1/*terminate*/) == Z_STREAM_END) + { + png_text text; + + /* It worked; png_ptr->read_buffer now looks like a tEXt chunk except + * for the extra compression type byte and the fact that it isn't + * necessarily '\0' terminated. + */ + buffer = png_ptr->read_buffer; + buffer[uncompressed_length+(keyword_length+2)] = 0; + + text.compression = PNG_TEXT_COMPRESSION_zTXt; + text.key = (png_charp)buffer; + text.text = (png_charp)(buffer + keyword_length+2); + text.text_length = uncompressed_length; + text.itxt_length = 0; + text.lang = NULL; + text.lang_key = NULL; + + if (png_set_text_2(png_ptr, info_ptr, &text, 1)) + errmsg = "insufficient memory"; + } + + else + errmsg = png_ptr->zstream.msg; + } + + if (errmsg != NULL) + png_chunk_benign_error(png_ptr, errmsg); +} +#endif + +#ifdef PNG_READ_iTXt_SUPPORTED +/* Note: this does not correctly handle chunks that are > 64K under DOS */ +void /* PRIVATE */ +png_handle_iTXt(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) +{ + png_const_charp errmsg = NULL; + png_bytep buffer; + png_uint_32 prefix_length; + + png_debug(1, "in png_handle_iTXt"); + +#ifdef PNG_USER_LIMITS_SUPPORTED + if (png_ptr->user_chunk_cache_max != 0) + { + if (png_ptr->user_chunk_cache_max == 1) + { + png_crc_finish(png_ptr, length); + return; + } + + if (--png_ptr->user_chunk_cache_max == 1) + { + png_crc_finish(png_ptr, length); + png_chunk_benign_error(png_ptr, "no space in chunk cache"); + return; + } + } +#endif + + if (!(png_ptr->mode & PNG_HAVE_IHDR)) + png_chunk_error(png_ptr, "missing IHDR"); + + if (png_ptr->mode & PNG_HAVE_IDAT) + png_ptr->mode |= PNG_AFTER_IDAT; + + buffer = png_read_buffer(png_ptr, length+1, 1/*warn*/); + + if (buffer == NULL) + { + png_crc_finish(png_ptr, length); + png_chunk_benign_error(png_ptr, "out of memory"); + return; + } + + png_crc_read(png_ptr, buffer, length); + + if (png_crc_finish(png_ptr, 0)) + return; + + /* First the keyword. */ + for (prefix_length=0; + prefix_length < length && buffer[prefix_length] != 0; + ++prefix_length) + /* Empty loop */ ; + + /* Perform a basic check on the keyword length here. */ + if (prefix_length > 79 || prefix_length < 1) + errmsg = "bad keyword"; + + /* Expect keyword, compression flag, compression type, language, translated + * keyword (both may be empty but are 0 terminated) then the text, which may + * be empty. + */ + else if (prefix_length + 5 > length) + errmsg = "truncated"; + + else if (buffer[prefix_length+1] == 0 || + (buffer[prefix_length+1] == 1 && + buffer[prefix_length+2] == PNG_COMPRESSION_TYPE_BASE)) + { + int compressed = buffer[prefix_length+1] != 0; + png_uint_32 language_offset, translated_keyword_offset; + png_alloc_size_t uncompressed_length = 0; + + /* Now the language tag */ + prefix_length += 3; + language_offset = prefix_length; + + for (; prefix_length < length && buffer[prefix_length] != 0; + ++prefix_length) + /* Empty loop */ ; + + /* WARNING: the length may be invalid here, this is checked below. */ + translated_keyword_offset = ++prefix_length; + + for (; prefix_length < length && buffer[prefix_length] != 0; + ++prefix_length) + /* Empty loop */ ; + + /* prefix_length should now be at the trailing '\0' of the translated + * keyword, but it may already be over the end. None of this arithmetic + * can overflow because chunks are at most 2^31 bytes long, but on 16-bit + * systems the available allocaton may overflow. + */ + ++prefix_length; + + if (!compressed && prefix_length <= length) + uncompressed_length = length - prefix_length; + + else if (compressed && prefix_length < length) + { + uncompressed_length = PNG_SIZE_MAX; + + /* TODO: at present png_decompress_chunk imposes a single application + * level memory limit, this should be split to different values for + * iCCP and text chunks. + */ + if (png_decompress_chunk(png_ptr, length, prefix_length, + &uncompressed_length, 1/*terminate*/) == Z_STREAM_END) + buffer = png_ptr->read_buffer; + + else + errmsg = png_ptr->zstream.msg; + } + + else + errmsg = "truncated"; + + if (errmsg == NULL) + { + png_text text; + + buffer[uncompressed_length+prefix_length] = 0; + + if (compressed) + text.compression = PNG_ITXT_COMPRESSION_NONE; + + else + text.compression = PNG_ITXT_COMPRESSION_zTXt; + + text.key = (png_charp)buffer; + text.lang = (png_charp)buffer + language_offset; + text.lang_key = (png_charp)buffer + translated_keyword_offset; + text.text = (png_charp)buffer + prefix_length; + text.text_length = 0; + text.itxt_length = uncompressed_length; + + if (png_set_text_2(png_ptr, info_ptr, &text, 1)) + errmsg = "insufficient memory"; + } + } + + else + errmsg = "bad compression info"; + + if (errmsg != NULL) + png_chunk_benign_error(png_ptr, errmsg); +} +#endif + +#ifdef PNG_READ_UNKNOWN_CHUNKS_SUPPORTED +/* Utility function for png_handle_unknown; set up png_ptr::unknown_chunk */ +static int +png_cache_unknown_chunk(png_structrp png_ptr, png_uint_32 length) +{ + png_alloc_size_t limit = PNG_SIZE_MAX; + + if (png_ptr->unknown_chunk.data != NULL) + { + png_free(png_ptr, png_ptr->unknown_chunk.data); + png_ptr->unknown_chunk.data = NULL; + } + +# ifdef PNG_SET_CHUNK_MALLOC_LIMIT_SUPPORTED + if (png_ptr->user_chunk_malloc_max > 0 && + png_ptr->user_chunk_malloc_max < limit) + limit = png_ptr->user_chunk_malloc_max; + +# elif PNG_USER_CHUNK_MALLOC_MAX > 0 + if (PNG_USER_CHUNK_MALLOC_MAX < limit) + limit = PNG_USER_CHUNK_MALLOC_MAX; +# endif + + if (length <= limit) + { + PNG_CSTRING_FROM_CHUNK(png_ptr->unknown_chunk.name, png_ptr->chunk_name); + /* The following is safe because of the PNG_SIZE_MAX init above */ + png_ptr->unknown_chunk.size = (png_size_t)length/*SAFE*/; + /* 'mode' is a flag array, only the bottom four bits matter here */ + png_ptr->unknown_chunk.location = (png_byte)png_ptr->mode/*SAFE*/; + + if (length == 0) + png_ptr->unknown_chunk.data = NULL; + + else + { + /* Do a 'warn' here - it is handled below. */ + png_ptr->unknown_chunk.data = png_voidcast(png_bytep, + png_malloc_warn(png_ptr, length)); + } + } + + if (png_ptr->unknown_chunk.data == NULL && length > 0) + { + /* This is benign because we clean up correctly */ + png_crc_finish(png_ptr, length); + png_chunk_benign_error(png_ptr, "unknown chunk exceeds memory limits"); + return 0; + } + + else + { + if (length > 0) + png_crc_read(png_ptr, png_ptr->unknown_chunk.data, length); + png_crc_finish(png_ptr, 0); + return 1; + } +} +#endif /* PNG_READ_UNKNOWN_CHUNKS_SUPPORTED */ + +/* Handle an unknown, or known but disabled, chunk */ +void /* PRIVATE */ +png_handle_unknown(png_structrp png_ptr, png_inforp info_ptr, + png_uint_32 length, int keep) +{ + int handled = 0; /* the chunk was handled */ + + png_debug(1, "in png_handle_unknown"); + +#ifdef PNG_READ_UNKNOWN_CHUNKS_SUPPORTED + /* NOTE: this code is based on the code in libpng-1.4.12 except for fixing + * the bug which meant that setting a non-default behavior for a specific + * chunk would be ignored (the default was always used unless a user + * callback was installed). + * + * 'keep' is the value from the png_chunk_unknown_handling, the setting for + * this specific chunk_name, if PNG_HANDLE_AS_UNKNOWN_SUPPORTED, if not it + * will always be PNG_HANDLE_CHUNK_AS_DEFAULT and it needs to be set here. + * This is just an optimization to avoid multiple calls to the lookup + * function. + */ +# ifndef PNG_HANDLE_AS_UNKNOWN_SUPPORTED +# ifdef PNG_SET_UNKNOWN_CHUNKS_SUPPORTED + keep = png_chunk_unknown_handling(png_ptr, png_ptr->chunk_name); +# endif +# endif + + /* One of the following methods will read the chunk or skip it (at least one + * of these is always defined because this is the only way to switch on + * PNG_READ_UNKNOWN_CHUNKS_SUPPORTED) + */ +# ifdef PNG_READ_USER_CHUNKS_SUPPORTED + /* The user callback takes precedence over the chunk keep value, but the + * keep value is still required to validate a save of a critical chunk. + */ + if (png_ptr->read_user_chunk_fn != NULL) + { + if (png_cache_unknown_chunk(png_ptr, length)) + { + /* Callback to user unknown chunk handler */ + int ret = (*(png_ptr->read_user_chunk_fn))(png_ptr, + &png_ptr->unknown_chunk); + + /* ret is: + * negative: An error occured, png_chunk_error will be called. + * zero: The chunk was not handled, the chunk will be discarded + * unless png_set_keep_unknown_chunks has been used to set + * a 'keep' behavior for this particular chunk, in which + * case that will be used. A critical chunk will cause an + * error at this point unless it is to be saved. + * positive: The chunk was handled, libpng will ignore/discard it. + */ + if (ret < 0) + png_chunk_error(png_ptr, "error in user chunk"); + + else if (ret == 0) + { + /* If the keep value is 'default' or 'never' override it, but + * still error out on critical chunks unless the keep value is + * 'always' While this is weird it is the behavior in 1.4.12. + * A possible improvement would be to obey the value set for the + * chunk, but this would be an API change that would probably + * damage some applications. + * + * The png_app_warning below catches the case that matters, where + * the application has not set specific save or ignore for this + * chunk or global save or ignore. + */ + if (keep < PNG_HANDLE_CHUNK_IF_SAFE) + { +# ifdef PNG_SET_UNKNOWN_CHUNKS_SUPPORTED + if (png_ptr->unknown_default < PNG_HANDLE_CHUNK_IF_SAFE) + { + png_chunk_warning(png_ptr, "Saving unknown chunk:"); + png_app_warning(png_ptr, + "forcing save of an unhandled chunk;" + " please call png_set_keep_unknown_chunks"); + /* with keep = PNG_HANDLE_CHUNK_IF_SAFE */ + } +# endif + keep = PNG_HANDLE_CHUNK_IF_SAFE; + } + } + + else /* chunk was handled */ + { + handled = 1; + /* Critical chunks can be safely discarded at this point. */ + keep = PNG_HANDLE_CHUNK_NEVER; + } + } + + else + keep = PNG_HANDLE_CHUNK_NEVER; /* insufficient memory */ + } + + else + /* Use the SAVE_UNKNOWN_CHUNKS code or skip the chunk */ +# endif /* PNG_READ_USER_CHUNKS_SUPPORTED */ + +# ifdef PNG_SAVE_UNKNOWN_CHUNKS_SUPPORTED + { + /* keep is currently just the per-chunk setting, if there was no + * setting change it to the global default now (not that this may + * still be AS_DEFAULT) then obtain the cache of the chunk if required, + * if not simply skip the chunk. + */ + if (keep == PNG_HANDLE_CHUNK_AS_DEFAULT) + keep = png_ptr->unknown_default; + + if (keep == PNG_HANDLE_CHUNK_ALWAYS || + (keep == PNG_HANDLE_CHUNK_IF_SAFE && + PNG_CHUNK_ANCILLARY(png_ptr->chunk_name))) + { + if (!png_cache_unknown_chunk(png_ptr, length)) + keep = PNG_HANDLE_CHUNK_NEVER; + } + + else + png_crc_finish(png_ptr, length); + } +# else +# ifndef PNG_READ_USER_CHUNKS_SUPPORTED +# error no method to support READ_UNKNOWN_CHUNKS +# endif + + { + /* If here there is no read callback pointer set and no support is + * compiled in to just save the unknown chunks, so simply skip this + * chunk. If 'keep' is something other than AS_DEFAULT or NEVER then + * the app has erroneously asked for unknown chunk saving when there + * is no support. + */ + if (keep > PNG_HANDLE_CHUNK_NEVER) + png_app_error(png_ptr, "no unknown chunk support available"); + + png_crc_finish(png_ptr, length); + } +# endif + +# ifdef PNG_STORE_UNKNOWN_CHUNKS_SUPPORTED + /* Now store the chunk in the chunk list if appropriate, and if the limits + * permit it. + */ + if (keep == PNG_HANDLE_CHUNK_ALWAYS || + (keep == PNG_HANDLE_CHUNK_IF_SAFE && + PNG_CHUNK_ANCILLARY(png_ptr->chunk_name))) + { +# ifdef PNG_USER_LIMITS_SUPPORTED + switch (png_ptr->user_chunk_cache_max) + { + case 2: + png_ptr->user_chunk_cache_max = 1; + png_chunk_benign_error(png_ptr, "no space in chunk cache"); + /* FALL THROUGH */ + case 1: + /* NOTE: prior to 1.6.0 this case resulted in an unknown critical + * chunk being skipped, now there will be a hard error below. + */ + break; + + default: /* not at limit */ + --(png_ptr->user_chunk_cache_max); + /* FALL THROUGH */ + case 0: /* no limit */ +# endif /* PNG_USER_LIMITS_SUPPORTED */ + /* Here when the limit isn't reached or when limits are compiled + * out; store the chunk. + */ + png_set_unknown_chunks(png_ptr, info_ptr, + &png_ptr->unknown_chunk, 1); + handled = 1; +# ifdef PNG_USER_LIMITS_SUPPORTED + break; + } +# endif + } +# else /* no store support! */ + PNG_UNUSED(info_ptr) +# error untested code (reading unknown chunks with no store support) +# endif + + /* Regardless of the error handling below the cached data (if any) can be + * freed now. Notice that the data is not freed if there is a png_error, but + * it will be freed by destroy_read_struct. + */ + if (png_ptr->unknown_chunk.data != NULL) + png_free(png_ptr, png_ptr->unknown_chunk.data); + png_ptr->unknown_chunk.data = NULL; + +#else /* !PNG_READ_UNKNOWN_CHUNKS_SUPPORTED */ + /* There is no support to read an unknown chunk, so just skip it. */ + png_crc_finish(png_ptr, length); + PNG_UNUSED(info_ptr) + PNG_UNUSED(keep) +#endif /* !PNG_READ_UNKNOWN_CHUNKS_SUPPORTED */ + + /* Check for unhandled critical chunks */ + if (!handled && PNG_CHUNK_CRITICAL(png_ptr->chunk_name)) + png_chunk_error(png_ptr, "unhandled critical chunk"); +} + +/* This function is called to verify that a chunk name is valid. + * This function can't have the "critical chunk check" incorporated + * into it, since in the future we will need to be able to call user + * functions to handle unknown critical chunks after we check that + * the chunk name itself is valid. + */ + +/* Bit hacking: the test for an invalid byte in the 4 byte chunk name is: + * + * ((c) < 65 || (c) > 122 || ((c) > 90 && (c) < 97)) + */ + +void /* PRIVATE */ +png_check_chunk_name(png_structrp png_ptr, png_uint_32 chunk_name) +{ + int i; + + png_debug(1, "in png_check_chunk_name"); + + for (i=1; i<=4; ++i) + { + int c = chunk_name & 0xff; + + if (c < 65 || c > 122 || (c > 90 && c < 97)) + png_chunk_error(png_ptr, "invalid chunk type"); + + chunk_name >>= 8; + } +} + +/* Combines the row recently read in with the existing pixels in the row. This + * routine takes care of alpha and transparency if requested. This routine also + * handles the two methods of progressive display of interlaced images, + * depending on the 'display' value; if 'display' is true then the whole row + * (dp) is filled from the start by replicating the available pixels. If + * 'display' is false only those pixels present in the pass are filled in. + */ +void /* PRIVATE */ +png_combine_row(png_const_structrp png_ptr, png_bytep dp, int display) +{ + unsigned int pixel_depth = png_ptr->transformed_pixel_depth; + png_const_bytep sp = png_ptr->row_buf + 1; + png_uint_32 row_width = png_ptr->width; + unsigned int pass = png_ptr->pass; + png_bytep end_ptr = 0; + png_byte end_byte = 0; + unsigned int end_mask; + + png_debug(1, "in png_combine_row"); + + /* Added in 1.5.6: it should not be possible to enter this routine until at + * least one row has been read from the PNG data and transformed. + */ + if (pixel_depth == 0) + png_error(png_ptr, "internal row logic error"); + + /* Added in 1.5.4: the pixel depth should match the information returned by + * any call to png_read_update_info at this point. Do not continue if we got + * this wrong. + */ + if (png_ptr->info_rowbytes != 0 && png_ptr->info_rowbytes != + PNG_ROWBYTES(pixel_depth, row_width)) + png_error(png_ptr, "internal row size calculation error"); + + /* Don't expect this to ever happen: */ + if (row_width == 0) + png_error(png_ptr, "internal row width error"); + + /* Preserve the last byte in cases where only part of it will be overwritten, + * the multiply below may overflow, we don't care because ANSI-C guarantees + * we get the low bits. + */ + end_mask = (pixel_depth * row_width) & 7; + if (end_mask != 0) + { + /* end_ptr == NULL is a flag to say do nothing */ + end_ptr = dp + PNG_ROWBYTES(pixel_depth, row_width) - 1; + end_byte = *end_ptr; +# ifdef PNG_READ_PACKSWAP_SUPPORTED + if (png_ptr->transformations & PNG_PACKSWAP) /* little-endian byte */ + end_mask = 0xff << end_mask; + + else /* big-endian byte */ +# endif + end_mask = 0xff >> end_mask; + /* end_mask is now the bits to *keep* from the destination row */ + } + + /* For non-interlaced images this reduces to a memcpy(). A memcpy() + * will also happen if interlacing isn't supported or if the application + * does not call png_set_interlace_handling(). In the latter cases the + * caller just gets a sequence of the unexpanded rows from each interlace + * pass. + */ +#ifdef PNG_READ_INTERLACING_SUPPORTED + if (png_ptr->interlaced && (png_ptr->transformations & PNG_INTERLACE) && + pass < 6 && (display == 0 || + /* The following copies everything for 'display' on passes 0, 2 and 4. */ + (display == 1 && (pass & 1) != 0))) + { + /* Narrow images may have no bits in a pass; the caller should handle + * this, but this test is cheap: + */ + if (row_width <= PNG_PASS_START_COL(pass)) + return; + + if (pixel_depth < 8) + { + /* For pixel depths up to 4 bpp the 8-pixel mask can be expanded to fit + * into 32 bits, then a single loop over the bytes using the four byte + * values in the 32-bit mask can be used. For the 'display' option the + * expanded mask may also not require any masking within a byte. To + * make this work the PACKSWAP option must be taken into account - it + * simply requires the pixels to be reversed in each byte. + * + * The 'regular' case requires a mask for each of the first 6 passes, + * the 'display' case does a copy for the even passes in the range + * 0..6. This has already been handled in the test above. + * + * The masks are arranged as four bytes with the first byte to use in + * the lowest bits (little-endian) regardless of the order (PACKSWAP or + * not) of the pixels in each byte. + * + * NOTE: the whole of this logic depends on the caller of this function + * only calling it on rows appropriate to the pass. This function only + * understands the 'x' logic; the 'y' logic is handled by the caller. + * + * The following defines allow generation of compile time constant bit + * masks for each pixel depth and each possibility of swapped or not + * swapped bytes. Pass 'p' is in the range 0..6; 'x', a pixel index, + * is in the range 0..7; and the result is 1 if the pixel is to be + * copied in the pass, 0 if not. 'S' is for the sparkle method, 'B' + * for the block method. + * + * With some compilers a compile time expression of the general form: + * + * (shift >= 32) ? (a >> (shift-32)) : (b >> shift) + * + * Produces warnings with values of 'shift' in the range 33 to 63 + * because the right hand side of the ?: expression is evaluated by + * the compiler even though it isn't used. Microsoft Visual C (various + * versions) and the Intel C compiler are known to do this. To avoid + * this the following macros are used in 1.5.6. This is a temporary + * solution to avoid destabilizing the code during the release process. + */ +# if PNG_USE_COMPILE_TIME_MASKS +# define PNG_LSR(x,s) ((x)>>((s) & 0x1f)) +# define PNG_LSL(x,s) ((x)<<((s) & 0x1f)) +# else +# define PNG_LSR(x,s) ((x)>>(s)) +# define PNG_LSL(x,s) ((x)<<(s)) +# endif +# define S_COPY(p,x) (((p)<4 ? PNG_LSR(0x80088822,(3-(p))*8+(7-(x))) :\ + PNG_LSR(0xaa55ff00,(7-(p))*8+(7-(x)))) & 1) +# define B_COPY(p,x) (((p)<4 ? PNG_LSR(0xff0fff33,(3-(p))*8+(7-(x))) :\ + PNG_LSR(0xff55ff00,(7-(p))*8+(7-(x)))) & 1) + + /* Return a mask for pass 'p' pixel 'x' at depth 'd'. The mask is + * little endian - the first pixel is at bit 0 - however the extra + * parameter 's' can be set to cause the mask position to be swapped + * within each byte, to match the PNG format. This is done by XOR of + * the shift with 7, 6 or 4 for bit depths 1, 2 and 4. + */ +# define PIXEL_MASK(p,x,d,s) \ + (PNG_LSL(((PNG_LSL(1U,(d)))-1),(((x)*(d))^((s)?8-(d):0)))) + + /* Hence generate the appropriate 'block' or 'sparkle' pixel copy mask. + */ +# define S_MASKx(p,x,d,s) (S_COPY(p,x)?PIXEL_MASK(p,x,d,s):0) +# define B_MASKx(p,x,d,s) (B_COPY(p,x)?PIXEL_MASK(p,x,d,s):0) + + /* Combine 8 of these to get the full mask. For the 1-bpp and 2-bpp + * cases the result needs replicating, for the 4-bpp case the above + * generates a full 32 bits. + */ +# define MASK_EXPAND(m,d) ((m)*((d)==1?0x01010101:((d)==2?0x00010001:1))) + +# define S_MASK(p,d,s) MASK_EXPAND(S_MASKx(p,0,d,s) + S_MASKx(p,1,d,s) +\ + S_MASKx(p,2,d,s) + S_MASKx(p,3,d,s) + S_MASKx(p,4,d,s) +\ + S_MASKx(p,5,d,s) + S_MASKx(p,6,d,s) + S_MASKx(p,7,d,s), d) + +# define B_MASK(p,d,s) MASK_EXPAND(B_MASKx(p,0,d,s) + B_MASKx(p,1,d,s) +\ + B_MASKx(p,2,d,s) + B_MASKx(p,3,d,s) + B_MASKx(p,4,d,s) +\ + B_MASKx(p,5,d,s) + B_MASKx(p,6,d,s) + B_MASKx(p,7,d,s), d) + +#if PNG_USE_COMPILE_TIME_MASKS + /* Utility macros to construct all the masks for a depth/swap + * combination. The 's' parameter says whether the format is PNG + * (big endian bytes) or not. Only the three odd-numbered passes are + * required for the display/block algorithm. + */ +# define S_MASKS(d,s) { S_MASK(0,d,s), S_MASK(1,d,s), S_MASK(2,d,s),\ + S_MASK(3,d,s), S_MASK(4,d,s), S_MASK(5,d,s) } + +# define B_MASKS(d,s) { B_MASK(1,d,s), S_MASK(3,d,s), S_MASK(5,d,s) } + +# define DEPTH_INDEX(d) ((d)==1?0:((d)==2?1:2)) + + /* Hence the pre-compiled masks indexed by PACKSWAP (or not), depth and + * then pass: + */ + static PNG_CONST png_uint_32 row_mask[2/*PACKSWAP*/][3/*depth*/][6] = + { + /* Little-endian byte masks for PACKSWAP */ + { S_MASKS(1,0), S_MASKS(2,0), S_MASKS(4,0) }, + /* Normal (big-endian byte) masks - PNG format */ + { S_MASKS(1,1), S_MASKS(2,1), S_MASKS(4,1) } + }; + + /* display_mask has only three entries for the odd passes, so index by + * pass>>1. + */ + static PNG_CONST png_uint_32 display_mask[2][3][3] = + { + /* Little-endian byte masks for PACKSWAP */ + { B_MASKS(1,0), B_MASKS(2,0), B_MASKS(4,0) }, + /* Normal (big-endian byte) masks - PNG format */ + { B_MASKS(1,1), B_MASKS(2,1), B_MASKS(4,1) } + }; + +# define MASK(pass,depth,display,png)\ + ((display)?display_mask[png][DEPTH_INDEX(depth)][pass>>1]:\ + row_mask[png][DEPTH_INDEX(depth)][pass]) + +#else /* !PNG_USE_COMPILE_TIME_MASKS */ + /* This is the runtime alternative: it seems unlikely that this will + * ever be either smaller or faster than the compile time approach. + */ +# define MASK(pass,depth,display,png)\ + ((display)?B_MASK(pass,depth,png):S_MASK(pass,depth,png)) +#endif /* !PNG_USE_COMPILE_TIME_MASKS */ + + /* Use the appropriate mask to copy the required bits. In some cases + * the byte mask will be 0 or 0xff, optimize these cases. row_width is + * the number of pixels, but the code copies bytes, so it is necessary + * to special case the end. + */ + png_uint_32 pixels_per_byte = 8 / pixel_depth; + png_uint_32 mask; + +# ifdef PNG_READ_PACKSWAP_SUPPORTED + if (png_ptr->transformations & PNG_PACKSWAP) + mask = MASK(pass, pixel_depth, display, 0); + + else +# endif + mask = MASK(pass, pixel_depth, display, 1); + + for (;;) + { + png_uint_32 m; + + /* It doesn't matter in the following if png_uint_32 has more than + * 32 bits because the high bits always match those in m<<24; it is, + * however, essential to use OR here, not +, because of this. + */ + m = mask; + mask = (m >> 8) | (m << 24); /* rotate right to good compilers */ + m &= 0xff; + + if (m != 0) /* something to copy */ + { + if (m != 0xff) + *dp = (png_byte)((*dp & ~m) | (*sp & m)); + else + *dp = *sp; + } + + /* NOTE: this may overwrite the last byte with garbage if the image + * is not an exact number of bytes wide; libpng has always done + * this. + */ + if (row_width <= pixels_per_byte) + break; /* May need to restore part of the last byte */ + + row_width -= pixels_per_byte; + ++dp; + ++sp; + } + } + + else /* pixel_depth >= 8 */ + { + unsigned int bytes_to_copy, bytes_to_jump; + + /* Validate the depth - it must be a multiple of 8 */ + if (pixel_depth & 7) + png_error(png_ptr, "invalid user transform pixel depth"); + + pixel_depth >>= 3; /* now in bytes */ + row_width *= pixel_depth; + + /* Regardless of pass number the Adam 7 interlace always results in a + * fixed number of pixels to copy then to skip. There may be a + * different number of pixels to skip at the start though. + */ + { + unsigned int offset = PNG_PASS_START_COL(pass) * pixel_depth; + + row_width -= offset; + dp += offset; + sp += offset; + } + + /* Work out the bytes to copy. */ + if (display) + { + /* When doing the 'block' algorithm the pixel in the pass gets + * replicated to adjacent pixels. This is why the even (0,2,4,6) + * passes are skipped above - the entire expanded row is copied. + */ + bytes_to_copy = (1<<((6-pass)>>1)) * pixel_depth; + + /* But don't allow this number to exceed the actual row width. */ + if (bytes_to_copy > row_width) + bytes_to_copy = row_width; + } + + else /* normal row; Adam7 only ever gives us one pixel to copy. */ + bytes_to_copy = pixel_depth; + + /* In Adam7 there is a constant offset between where the pixels go. */ + bytes_to_jump = PNG_PASS_COL_OFFSET(pass) * pixel_depth; + + /* And simply copy these bytes. Some optimization is possible here, + * depending on the value of 'bytes_to_copy'. Special case the low + * byte counts, which we know to be frequent. + * + * Notice that these cases all 'return' rather than 'break' - this + * avoids an unnecessary test on whether to restore the last byte + * below. + */ + switch (bytes_to_copy) + { + case 1: + for (;;) + { + *dp = *sp; + + if (row_width <= bytes_to_jump) + return; + + dp += bytes_to_jump; + sp += bytes_to_jump; + row_width -= bytes_to_jump; + } + + case 2: + /* There is a possibility of a partial copy at the end here; this + * slows the code down somewhat. + */ + do + { + dp[0] = sp[0], dp[1] = sp[1]; + + if (row_width <= bytes_to_jump) + return; + + sp += bytes_to_jump; + dp += bytes_to_jump; + row_width -= bytes_to_jump; + } + while (row_width > 1); + + /* And there can only be one byte left at this point: */ + *dp = *sp; + return; + + case 3: + /* This can only be the RGB case, so each copy is exactly one + * pixel and it is not necessary to check for a partial copy. + */ + for(;;) + { + dp[0] = sp[0], dp[1] = sp[1], dp[2] = sp[2]; + + if (row_width <= bytes_to_jump) + return; + + sp += bytes_to_jump; + dp += bytes_to_jump; + row_width -= bytes_to_jump; + } + + default: +#if PNG_ALIGN_TYPE != PNG_ALIGN_NONE + /* Check for double byte alignment and, if possible, use a + * 16-bit copy. Don't attempt this for narrow images - ones that + * are less than an interlace panel wide. Don't attempt it for + * wide bytes_to_copy either - use the memcpy there. + */ + if (bytes_to_copy < 16 /*else use memcpy*/ && + png_isaligned(dp, png_uint_16) && + png_isaligned(sp, png_uint_16) && + bytes_to_copy % (sizeof (png_uint_16)) == 0 && + bytes_to_jump % (sizeof (png_uint_16)) == 0) + { + /* Everything is aligned for png_uint_16 copies, but try for + * png_uint_32 first. + */ + if (png_isaligned(dp, png_uint_32) && + png_isaligned(sp, png_uint_32) && + bytes_to_copy % (sizeof (png_uint_32)) == 0 && + bytes_to_jump % (sizeof (png_uint_32)) == 0) + { + png_uint_32p dp32 = png_aligncast(png_uint_32p,dp); + png_const_uint_32p sp32 = png_aligncastconst( + png_const_uint_32p, sp); + size_t skip = (bytes_to_jump-bytes_to_copy) / + (sizeof (png_uint_32)); + + do + { + size_t c = bytes_to_copy; + do + { + *dp32++ = *sp32++; + c -= (sizeof (png_uint_32)); + } + while (c > 0); + + if (row_width <= bytes_to_jump) + return; + + dp32 += skip; + sp32 += skip; + row_width -= bytes_to_jump; + } + while (bytes_to_copy <= row_width); + + /* Get to here when the row_width truncates the final copy. + * There will be 1-3 bytes left to copy, so don't try the + * 16-bit loop below. + */ + dp = (png_bytep)dp32; + sp = (png_const_bytep)sp32; + do + *dp++ = *sp++; + while (--row_width > 0); + return; + } + + /* Else do it in 16-bit quantities, but only if the size is + * not too large. + */ + else + { + png_uint_16p dp16 = png_aligncast(png_uint_16p, dp); + png_const_uint_16p sp16 = png_aligncastconst( + png_const_uint_16p, sp); + size_t skip = (bytes_to_jump-bytes_to_copy) / + (sizeof (png_uint_16)); + + do + { + size_t c = bytes_to_copy; + do + { + *dp16++ = *sp16++; + c -= (sizeof (png_uint_16)); + } + while (c > 0); + + if (row_width <= bytes_to_jump) + return; + + dp16 += skip; + sp16 += skip; + row_width -= bytes_to_jump; + } + while (bytes_to_copy <= row_width); + + /* End of row - 1 byte left, bytes_to_copy > row_width: */ + dp = (png_bytep)dp16; + sp = (png_const_bytep)sp16; + do + *dp++ = *sp++; + while (--row_width > 0); + return; + } + } +#endif /* PNG_ALIGN_ code */ + + /* The true default - use a memcpy: */ + for (;;) + { + memcpy(dp, sp, bytes_to_copy); + + if (row_width <= bytes_to_jump) + return; + + sp += bytes_to_jump; + dp += bytes_to_jump; + row_width -= bytes_to_jump; + if (bytes_to_copy > row_width) + bytes_to_copy = row_width; + } + } + + /* NOT REACHED*/ + } /* pixel_depth >= 8 */ + + /* Here if pixel_depth < 8 to check 'end_ptr' below. */ + } + else +#endif + + /* If here then the switch above wasn't used so just memcpy the whole row + * from the temporary row buffer (notice that this overwrites the end of the + * destination row if it is a partial byte.) + */ + memcpy(dp, sp, PNG_ROWBYTES(pixel_depth, row_width)); + + /* Restore the overwritten bits from the last byte if necessary. */ + if (end_ptr != NULL) + *end_ptr = (png_byte)((end_byte & end_mask) | (*end_ptr & ~end_mask)); +} + +#ifdef PNG_READ_INTERLACING_SUPPORTED +void /* PRIVATE */ +png_do_read_interlace(png_row_infop row_info, png_bytep row, int pass, + png_uint_32 transformations /* Because these may affect the byte layout */) +{ + /* Arrays to facilitate easy interlacing - use pass (0 - 6) as index */ + /* Offset to next interlace block */ + static PNG_CONST int png_pass_inc[7] = {8, 8, 4, 4, 2, 2, 1}; + + png_debug(1, "in png_do_read_interlace"); + if (row != NULL && row_info != NULL) + { + png_uint_32 final_width; + + final_width = row_info->width * png_pass_inc[pass]; + + switch (row_info->pixel_depth) + { + case 1: + { + png_bytep sp = row + (png_size_t)((row_info->width - 1) >> 3); + png_bytep dp = row + (png_size_t)((final_width - 1) >> 3); + int sshift, dshift; + int s_start, s_end, s_inc; + int jstop = png_pass_inc[pass]; + png_byte v; + png_uint_32 i; + int j; + +#ifdef PNG_READ_PACKSWAP_SUPPORTED + if (transformations & PNG_PACKSWAP) + { + sshift = (int)((row_info->width + 7) & 0x07); + dshift = (int)((final_width + 7) & 0x07); + s_start = 7; + s_end = 0; + s_inc = -1; + } + + else +#endif + { + sshift = 7 - (int)((row_info->width + 7) & 0x07); + dshift = 7 - (int)((final_width + 7) & 0x07); + s_start = 0; + s_end = 7; + s_inc = 1; + } + + for (i = 0; i < row_info->width; i++) + { + v = (png_byte)((*sp >> sshift) & 0x01); + for (j = 0; j < jstop; j++) + { + unsigned int tmp = *dp & (0x7f7f >> (7 - dshift)); + tmp |= v << dshift; + *dp = (png_byte)(tmp & 0xff); + + if (dshift == s_end) + { + dshift = s_start; + dp--; + } + + else + dshift += s_inc; + } + + if (sshift == s_end) + { + sshift = s_start; + sp--; + } + + else + sshift += s_inc; + } + break; + } + + case 2: + { + png_bytep sp = row + (png_uint_32)((row_info->width - 1) >> 2); + png_bytep dp = row + (png_uint_32)((final_width - 1) >> 2); + int sshift, dshift; + int s_start, s_end, s_inc; + int jstop = png_pass_inc[pass]; + png_uint_32 i; + +#ifdef PNG_READ_PACKSWAP_SUPPORTED + if (transformations & PNG_PACKSWAP) + { + sshift = (int)(((row_info->width + 3) & 0x03) << 1); + dshift = (int)(((final_width + 3) & 0x03) << 1); + s_start = 6; + s_end = 0; + s_inc = -2; + } + + else +#endif + { + sshift = (int)((3 - ((row_info->width + 3) & 0x03)) << 1); + dshift = (int)((3 - ((final_width + 3) & 0x03)) << 1); + s_start = 0; + s_end = 6; + s_inc = 2; + } + + for (i = 0; i < row_info->width; i++) + { + png_byte v; + int j; + + v = (png_byte)((*sp >> sshift) & 0x03); + for (j = 0; j < jstop; j++) + { + unsigned int tmp = *dp & (0x3f3f >> (6 - dshift)); + tmp |= v << dshift; + *dp = (png_byte)(tmp & 0xff); + + if (dshift == s_end) + { + dshift = s_start; + dp--; + } + + else + dshift += s_inc; + } + + if (sshift == s_end) + { + sshift = s_start; + sp--; + } + + else + sshift += s_inc; + } + break; + } + + case 4: + { + png_bytep sp = row + (png_size_t)((row_info->width - 1) >> 1); + png_bytep dp = row + (png_size_t)((final_width - 1) >> 1); + int sshift, dshift; + int s_start, s_end, s_inc; + png_uint_32 i; + int jstop = png_pass_inc[pass]; + +#ifdef PNG_READ_PACKSWAP_SUPPORTED + if (transformations & PNG_PACKSWAP) + { + sshift = (int)(((row_info->width + 1) & 0x01) << 2); + dshift = (int)(((final_width + 1) & 0x01) << 2); + s_start = 4; + s_end = 0; + s_inc = -4; + } + + else +#endif + { + sshift = (int)((1 - ((row_info->width + 1) & 0x01)) << 2); + dshift = (int)((1 - ((final_width + 1) & 0x01)) << 2); + s_start = 0; + s_end = 4; + s_inc = 4; + } + + for (i = 0; i < row_info->width; i++) + { + png_byte v = (png_byte)((*sp >> sshift) & 0x0f); + int j; + + for (j = 0; j < jstop; j++) + { + unsigned int tmp = *dp & (0xf0f >> (4 - dshift)); + tmp |= v << dshift; + *dp = (png_byte)(tmp & 0xff); + + if (dshift == s_end) + { + dshift = s_start; + dp--; + } + + else + dshift += s_inc; + } + + if (sshift == s_end) + { + sshift = s_start; + sp--; + } + + else + sshift += s_inc; + } + break; + } + + default: + { + png_size_t pixel_bytes = (row_info->pixel_depth >> 3); + + png_bytep sp = row + (png_size_t)(row_info->width - 1) + * pixel_bytes; + + png_bytep dp = row + (png_size_t)(final_width - 1) * pixel_bytes; + + int jstop = png_pass_inc[pass]; + png_uint_32 i; + + for (i = 0; i < row_info->width; i++) + { + png_byte v[8]; + int j; + + memcpy(v, sp, pixel_bytes); + + for (j = 0; j < jstop; j++) + { + memcpy(dp, v, pixel_bytes); + dp -= pixel_bytes; + } + + sp -= pixel_bytes; + } + break; + } + } + + row_info->width = final_width; + row_info->rowbytes = PNG_ROWBYTES(row_info->pixel_depth, final_width); + } +#ifndef PNG_READ_PACKSWAP_SUPPORTED + PNG_UNUSED(transformations) /* Silence compiler warning */ +#endif +} +#endif /* PNG_READ_INTERLACING_SUPPORTED */ + +static void +png_read_filter_row_sub(png_row_infop row_info, png_bytep row, + png_const_bytep prev_row) +{ + png_size_t i; + png_size_t istop = row_info->rowbytes; + unsigned int bpp = (row_info->pixel_depth + 7) >> 3; + png_bytep rp = row + bpp; + + PNG_UNUSED(prev_row) + + for (i = bpp; i < istop; i++) + { + *rp = (png_byte)(((int)(*rp) + (int)(*(rp-bpp))) & 0xff); + rp++; + } +} + +static void +png_read_filter_row_up(png_row_infop row_info, png_bytep row, + png_const_bytep prev_row) +{ + png_size_t i; + png_size_t istop = row_info->rowbytes; + png_bytep rp = row; + png_const_bytep pp = prev_row; + + for (i = 0; i < istop; i++) + { + *rp = (png_byte)(((int)(*rp) + (int)(*pp++)) & 0xff); + rp++; + } +} + +static void +png_read_filter_row_avg(png_row_infop row_info, png_bytep row, + png_const_bytep prev_row) +{ + png_size_t i; + png_bytep rp = row; + png_const_bytep pp = prev_row; + unsigned int bpp = (row_info->pixel_depth + 7) >> 3; + png_size_t istop = row_info->rowbytes - bpp; + + for (i = 0; i < bpp; i++) + { + *rp = (png_byte)(((int)(*rp) + + ((int)(*pp++) / 2 )) & 0xff); + + rp++; + } + + for (i = 0; i < istop; i++) + { + *rp = (png_byte)(((int)(*rp) + + (int)(*pp++ + *(rp-bpp)) / 2 ) & 0xff); + + rp++; + } +} + +static void +png_read_filter_row_paeth_1byte_pixel(png_row_infop row_info, png_bytep row, + png_const_bytep prev_row) +{ + png_bytep rp_end = row + row_info->rowbytes; + int a, c; + + /* First pixel/byte */ + c = *prev_row++; + a = *row + c; + *row++ = (png_byte)a; + + /* Remainder */ + while (row < rp_end) + { + int b, pa, pb, pc, p; + + a &= 0xff; /* From previous iteration or start */ + b = *prev_row++; + + p = b - c; + pc = a - c; + +# ifdef PNG_USE_ABS + pa = abs(p); + pb = abs(pc); + pc = abs(p + pc); +# else + pa = p < 0 ? -p : p; + pb = pc < 0 ? -pc : pc; + pc = (p + pc) < 0 ? -(p + pc) : p + pc; +# endif + + /* Find the best predictor, the least of pa, pb, pc favoring the earlier + * ones in the case of a tie. + */ + if (pb < pa) pa = pb, a = b; + if (pc < pa) a = c; + + /* Calculate the current pixel in a, and move the previous row pixel to c + * for the next time round the loop + */ + c = b; + a += *row; + *row++ = (png_byte)a; + } +} + +static void +png_read_filter_row_paeth_multibyte_pixel(png_row_infop row_info, png_bytep row, + png_const_bytep prev_row) +{ + int bpp = (row_info->pixel_depth + 7) >> 3; + png_bytep rp_end = row + bpp; + + /* Process the first pixel in the row completely (this is the same as 'up' + * because there is only one candidate predictor for the first row). + */ + while (row < rp_end) + { + int a = *row + *prev_row++; + *row++ = (png_byte)a; + } + + /* Remainder */ + rp_end += row_info->rowbytes - bpp; + + while (row < rp_end) + { + int a, b, c, pa, pb, pc, p; + + c = *(prev_row - bpp); + a = *(row - bpp); + b = *prev_row++; + + p = b - c; + pc = a - c; + +# ifdef PNG_USE_ABS + pa = abs(p); + pb = abs(pc); + pc = abs(p + pc); +# else + pa = p < 0 ? -p : p; + pb = pc < 0 ? -pc : pc; + pc = (p + pc) < 0 ? -(p + pc) : p + pc; +# endif + + if (pb < pa) pa = pb, a = b; + if (pc < pa) a = c; + + c = b; + a += *row; + *row++ = (png_byte)a; + } +} + +static void +png_init_filter_functions(png_structrp pp) + /* This function is called once for every PNG image to set the + * implementations required to reverse the filtering of PNG rows. Reversing + * the filter is the first transformation performed on the row data. It is + * performed in place, therefore an implementation can be selected based on + * the image pixel format. If the implementation depends on image width then + * take care to ensure that it works correctly if the image is interlaced - + * interlacing causes the actual row width to vary. + */ +{ + unsigned int bpp = (pp->pixel_depth + 7) >> 3; + + pp->read_filter[PNG_FILTER_VALUE_SUB-1] = png_read_filter_row_sub; + pp->read_filter[PNG_FILTER_VALUE_UP-1] = png_read_filter_row_up; + pp->read_filter[PNG_FILTER_VALUE_AVG-1] = png_read_filter_row_avg; + if (bpp == 1) + pp->read_filter[PNG_FILTER_VALUE_PAETH-1] = + png_read_filter_row_paeth_1byte_pixel; + else + pp->read_filter[PNG_FILTER_VALUE_PAETH-1] = + png_read_filter_row_paeth_multibyte_pixel; + +#ifdef PNG_FILTER_OPTIMIZATIONS + /* To use this define PNG_FILTER_OPTIMIZATIONS as the name of a function to + * call to install hardware optimizations for the above functions; simply + * replace whatever elements of the pp->read_filter[] array with a hardware + * specific (or, for that matter, generic) optimization. + * + * To see an example of this examine what configure.ac does when + * --enable-arm-neon is specified on the command line. + */ + PNG_FILTER_OPTIMIZATIONS(pp, bpp); +#endif +} + +void /* PRIVATE */ +png_read_filter_row(png_structrp pp, png_row_infop row_info, png_bytep row, + png_const_bytep prev_row, int filter) +{ + /* OPTIMIZATION: DO NOT MODIFY THIS FUNCTION, instead #define + * PNG_FILTER_OPTIMIZATIONS to a function that overrides the generic + * implementations. See png_init_filter_functions above. + */ + if (pp->read_filter[0] == NULL) + png_init_filter_functions(pp); + if (filter > PNG_FILTER_VALUE_NONE && filter < PNG_FILTER_VALUE_LAST) + pp->read_filter[filter-1](row_info, row, prev_row); +} + +#ifdef PNG_SEQUENTIAL_READ_SUPPORTED +void /* PRIVATE */ +png_read_IDAT_data(png_structrp png_ptr, png_bytep output, + png_alloc_size_t avail_out) +{ + /* Loop reading IDATs and decompressing the result into output[avail_out] */ + png_ptr->zstream.next_out = output; + png_ptr->zstream.avail_out = 0; /* safety: set below */ + + if (output == NULL) + avail_out = 0; + + do + { + int ret; + png_byte tmpbuf[PNG_INFLATE_BUF_SIZE]; + + if (png_ptr->zstream.avail_in == 0) + { + uInt avail_in; + png_bytep buffer; + + while (png_ptr->idat_size == 0) + { + png_crc_finish(png_ptr, 0); + + png_ptr->idat_size = png_read_chunk_header(png_ptr); + /* This is an error even in the 'check' case because the code just + * consumed a non-IDAT header. + */ + if (png_ptr->chunk_name != png_IDAT) + png_error(png_ptr, "Not enough image data"); + } + + avail_in = png_ptr->IDAT_read_size; + + if (avail_in > png_ptr->idat_size) + avail_in = (uInt)png_ptr->idat_size; + + /* A PNG with a gradually increasing IDAT size will defeat this attempt + * to minimize memory usage by causing lots of re-allocs, but + * realistically doing IDAT_read_size re-allocs is not likely to be a + * big problem. + */ + buffer = png_read_buffer(png_ptr, avail_in, 0/*error*/); + + png_crc_read(png_ptr, buffer, avail_in); + png_ptr->idat_size -= avail_in; + + png_ptr->zstream.next_in = buffer; + png_ptr->zstream.avail_in = avail_in; + } + + /* And set up the output side. */ + if (output != NULL) /* standard read */ + { + uInt out = ZLIB_IO_MAX; + + if (out > avail_out) + out = (uInt)avail_out; + + avail_out -= out; + png_ptr->zstream.avail_out = out; + } + + else /* after last row, checking for end */ + { + png_ptr->zstream.next_out = tmpbuf; + png_ptr->zstream.avail_out = (sizeof tmpbuf); + } + + /* Use NO_FLUSH; this gives zlib the maximum opportunity to optimize the + * process. If the LZ stream is truncated the sequential reader will + * terminally damage the stream, above, by reading the chunk header of the + * following chunk (it then exits with png_error). + * + * TODO: deal more elegantly with truncated IDAT lists. + */ + ret = inflate(&png_ptr->zstream, Z_NO_FLUSH); + + /* Take the unconsumed output back. */ + if (output != NULL) + avail_out += png_ptr->zstream.avail_out; + + else /* avail_out counts the extra bytes */ + avail_out += (sizeof tmpbuf) - png_ptr->zstream.avail_out; + + png_ptr->zstream.avail_out = 0; + + if (ret == Z_STREAM_END) + { + /* Do this for safety; we won't read any more into this row. */ + png_ptr->zstream.next_out = NULL; + + png_ptr->mode |= PNG_AFTER_IDAT; + png_ptr->flags |= PNG_FLAG_ZSTREAM_ENDED; + + if (png_ptr->zstream.avail_in > 0 || png_ptr->idat_size > 0) + png_chunk_benign_error(png_ptr, "Extra compressed data"); + break; + } + + if (ret != Z_OK) + { + png_zstream_error(png_ptr, ret); + + if (output != NULL) + png_chunk_error(png_ptr, png_ptr->zstream.msg); + + else /* checking */ + { + png_chunk_benign_error(png_ptr, png_ptr->zstream.msg); + return; + } + } + } while (avail_out > 0); + + if (avail_out > 0) + { + /* The stream ended before the image; this is the same as too few IDATs so + * should be handled the same way. + */ + if (output != NULL) + png_error(png_ptr, "Not enough image data"); + + else /* the deflate stream contained extra data */ + png_chunk_benign_error(png_ptr, "Too much image data"); + } +} + +void /* PRIVATE */ +png_read_finish_IDAT(png_structrp png_ptr) +{ + /* We don't need any more data and the stream should have ended, however the + * LZ end code may actually not have been processed. In this case we must + * read it otherwise stray unread IDAT data or, more likely, an IDAT chunk + * may still remain to be consumed. + */ + if (!(png_ptr->flags & PNG_FLAG_ZSTREAM_ENDED)) + { + /* The NULL causes png_read_IDAT_data to swallow any remaining bytes in + * the compressed stream, but the stream may be damaged too, so even after + * this call we may need to terminate the zstream ownership. + */ + png_read_IDAT_data(png_ptr, NULL, 0); + png_ptr->zstream.next_out = NULL; /* safety */ + + /* Now clear everything out for safety; the following may not have been + * done. + */ + if (!(png_ptr->flags & PNG_FLAG_ZSTREAM_ENDED)) + { + png_ptr->mode |= PNG_AFTER_IDAT; + png_ptr->flags |= PNG_FLAG_ZSTREAM_ENDED; + } + } + + /* If the zstream has not been released do it now *and* terminate the reading + * of the final IDAT chunk. + */ + if (png_ptr->zowner == png_IDAT) + { + /* Always do this; the pointers otherwise point into the read buffer. */ + png_ptr->zstream.next_in = NULL; + png_ptr->zstream.avail_in = 0; + + /* Now we no longer own the zstream. */ + png_ptr->zowner = 0; + + /* The slightly weird semantics of the sequential IDAT reading is that we + * are always in or at the end of an IDAT chunk, so we always need to do a + * crc_finish here. If idat_size is non-zero we also need to read the + * spurious bytes at the end of the chunk now. + */ + (void)png_crc_finish(png_ptr, png_ptr->idat_size); + } +} + +void /* PRIVATE */ +png_read_finish_row(png_structrp png_ptr) +{ +#ifdef PNG_READ_INTERLACING_SUPPORTED + /* Arrays to facilitate easy interlacing - use pass (0 - 6) as index */ + + /* Start of interlace block */ + static PNG_CONST png_byte png_pass_start[7] = {0, 4, 0, 2, 0, 1, 0}; + + /* Offset to next interlace block */ + static PNG_CONST png_byte png_pass_inc[7] = {8, 8, 4, 4, 2, 2, 1}; + + /* Start of interlace block in the y direction */ + static PNG_CONST png_byte png_pass_ystart[7] = {0, 0, 4, 0, 2, 0, 1}; + + /* Offset to next interlace block in the y direction */ + static PNG_CONST png_byte png_pass_yinc[7] = {8, 8, 8, 4, 4, 2, 2}; +#endif /* PNG_READ_INTERLACING_SUPPORTED */ + + png_debug(1, "in png_read_finish_row"); + png_ptr->row_number++; + if (png_ptr->row_number < png_ptr->num_rows) + return; + +#ifdef PNG_READ_INTERLACING_SUPPORTED + if (png_ptr->interlaced) + { + png_ptr->row_number = 0; + + /* TO DO: don't do this if prev_row isn't needed (requires + * read-ahead of the next row's filter byte. + */ + memset(png_ptr->prev_row, 0, png_ptr->rowbytes + 1); + + do + { + png_ptr->pass++; + + if (png_ptr->pass >= 7) + break; + + png_ptr->iwidth = (png_ptr->width + + png_pass_inc[png_ptr->pass] - 1 - + png_pass_start[png_ptr->pass]) / + png_pass_inc[png_ptr->pass]; + + if (!(png_ptr->transformations & PNG_INTERLACE)) + { + png_ptr->num_rows = (png_ptr->height + + png_pass_yinc[png_ptr->pass] - 1 - + png_pass_ystart[png_ptr->pass]) / + png_pass_yinc[png_ptr->pass]; + } + + else /* if (png_ptr->transformations & PNG_INTERLACE) */ + break; /* libpng deinterlacing sees every row */ + + } while (png_ptr->num_rows == 0 || png_ptr->iwidth == 0); + + if (png_ptr->pass < 7) + return; + } +#endif /* PNG_READ_INTERLACING_SUPPORTED */ + + /* Here after at the end of the last row of the last pass. */ + png_read_finish_IDAT(png_ptr); +} +#endif /* PNG_SEQUENTIAL_READ_SUPPORTED */ + +void /* PRIVATE */ +png_read_start_row(png_structrp png_ptr) +{ +#ifdef PNG_READ_INTERLACING_SUPPORTED + /* Arrays to facilitate easy interlacing - use pass (0 - 6) as index */ + + /* Start of interlace block */ + static PNG_CONST png_byte png_pass_start[7] = {0, 4, 0, 2, 0, 1, 0}; + + /* Offset to next interlace block */ + static PNG_CONST png_byte png_pass_inc[7] = {8, 8, 4, 4, 2, 2, 1}; + + /* Start of interlace block in the y direction */ + static PNG_CONST png_byte png_pass_ystart[7] = {0, 0, 4, 0, 2, 0, 1}; + + /* Offset to next interlace block in the y direction */ + static PNG_CONST png_byte png_pass_yinc[7] = {8, 8, 8, 4, 4, 2, 2}; +#endif + + int max_pixel_depth; + png_size_t row_bytes; + + png_debug(1, "in png_read_start_row"); + +#ifdef PNG_READ_TRANSFORMS_SUPPORTED + png_init_read_transformations(png_ptr); +#endif +#ifdef PNG_READ_INTERLACING_SUPPORTED + if (png_ptr->interlaced) + { + if (!(png_ptr->transformations & PNG_INTERLACE)) + png_ptr->num_rows = (png_ptr->height + png_pass_yinc[0] - 1 - + png_pass_ystart[0]) / png_pass_yinc[0]; + + else + png_ptr->num_rows = png_ptr->height; + + png_ptr->iwidth = (png_ptr->width + + png_pass_inc[png_ptr->pass] - 1 - + png_pass_start[png_ptr->pass]) / + png_pass_inc[png_ptr->pass]; + } + + else +#endif /* PNG_READ_INTERLACING_SUPPORTED */ + { + png_ptr->num_rows = png_ptr->height; + png_ptr->iwidth = png_ptr->width; + } + + max_pixel_depth = png_ptr->pixel_depth; + + /* WARNING: * png_read_transform_info (pngrtran.c) performs a simpliar set of + * calculations to calculate the final pixel depth, then + * png_do_read_transforms actually does the transforms. This means that the + * code which effectively calculates this value is actually repeated in three + * separate places. They must all match. Innocent changes to the order of + * transformations can and will break libpng in a way that causes memory + * overwrites. + * + * TODO: fix this. + */ +#ifdef PNG_READ_PACK_SUPPORTED + if ((png_ptr->transformations & PNG_PACK) && png_ptr->bit_depth < 8) + max_pixel_depth = 8; +#endif + +#ifdef PNG_READ_EXPAND_SUPPORTED + if (png_ptr->transformations & PNG_EXPAND) + { + if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE) + { + if (png_ptr->num_trans) + max_pixel_depth = 32; + + else + max_pixel_depth = 24; + } + + else if (png_ptr->color_type == PNG_COLOR_TYPE_GRAY) + { + if (max_pixel_depth < 8) + max_pixel_depth = 8; + + if (png_ptr->num_trans) + max_pixel_depth *= 2; + } + + else if (png_ptr->color_type == PNG_COLOR_TYPE_RGB) + { + if (png_ptr->num_trans) + { + max_pixel_depth *= 4; + max_pixel_depth /= 3; + } + } + } +#endif + +#ifdef PNG_READ_EXPAND_16_SUPPORTED + if (png_ptr->transformations & PNG_EXPAND_16) + { +# ifdef PNG_READ_EXPAND_SUPPORTED + /* In fact it is an error if it isn't supported, but checking is + * the safe way. + */ + if (png_ptr->transformations & PNG_EXPAND) + { + if (png_ptr->bit_depth < 16) + max_pixel_depth *= 2; + } + else +# endif + png_ptr->transformations &= ~PNG_EXPAND_16; + } +#endif + +#ifdef PNG_READ_FILLER_SUPPORTED + if (png_ptr->transformations & (PNG_FILLER)) + { + if (png_ptr->color_type == PNG_COLOR_TYPE_GRAY) + { + if (max_pixel_depth <= 8) + max_pixel_depth = 16; + + else + max_pixel_depth = 32; + } + + else if (png_ptr->color_type == PNG_COLOR_TYPE_RGB || + png_ptr->color_type == PNG_COLOR_TYPE_PALETTE) + { + if (max_pixel_depth <= 32) + max_pixel_depth = 32; + + else + max_pixel_depth = 64; + } + } +#endif + +#ifdef PNG_READ_GRAY_TO_RGB_SUPPORTED + if (png_ptr->transformations & PNG_GRAY_TO_RGB) + { + if ( +#ifdef PNG_READ_EXPAND_SUPPORTED + (png_ptr->num_trans && (png_ptr->transformations & PNG_EXPAND)) || +#endif +#ifdef PNG_READ_FILLER_SUPPORTED + (png_ptr->transformations & (PNG_FILLER)) || +#endif + png_ptr->color_type == PNG_COLOR_TYPE_GRAY_ALPHA) + { + if (max_pixel_depth <= 16) + max_pixel_depth = 32; + + else + max_pixel_depth = 64; + } + + else + { + if (max_pixel_depth <= 8) + { + if (png_ptr->color_type == PNG_COLOR_TYPE_RGB_ALPHA) + max_pixel_depth = 32; + + else + max_pixel_depth = 24; + } + + else if (png_ptr->color_type == PNG_COLOR_TYPE_RGB_ALPHA) + max_pixel_depth = 64; + + else + max_pixel_depth = 48; + } + } +#endif + +#if defined(PNG_READ_USER_TRANSFORM_SUPPORTED) && \ +defined(PNG_USER_TRANSFORM_PTR_SUPPORTED) + if (png_ptr->transformations & PNG_USER_TRANSFORM) + { + int user_pixel_depth = png_ptr->user_transform_depth * + png_ptr->user_transform_channels; + + if (user_pixel_depth > max_pixel_depth) + max_pixel_depth = user_pixel_depth; + } +#endif + + /* This value is stored in png_struct and double checked in the row read + * code. + */ + png_ptr->maximum_pixel_depth = (png_byte)max_pixel_depth; + png_ptr->transformed_pixel_depth = 0; /* calculated on demand */ + + /* Align the width on the next larger 8 pixels. Mainly used + * for interlacing + */ + row_bytes = ((png_ptr->width + 7) & ~((png_uint_32)7)); + /* Calculate the maximum bytes needed, adding a byte and a pixel + * for safety's sake + */ + row_bytes = PNG_ROWBYTES(max_pixel_depth, row_bytes) + + 1 + ((max_pixel_depth + 7) >> 3); + +#ifdef PNG_MAX_MALLOC_64K + if (row_bytes > (png_uint_32)65536L) + png_error(png_ptr, "This image requires a row greater than 64KB"); +#endif + + if (row_bytes + 48 > png_ptr->old_big_row_buf_size) + { + png_free(png_ptr, png_ptr->big_row_buf); + png_free(png_ptr, png_ptr->big_prev_row); + + if (png_ptr->interlaced) + png_ptr->big_row_buf = (png_bytep)png_calloc(png_ptr, + row_bytes + 48); + + else + png_ptr->big_row_buf = (png_bytep)png_malloc(png_ptr, row_bytes + 48); + + png_ptr->big_prev_row = (png_bytep)png_malloc(png_ptr, row_bytes + 48); + +#ifdef PNG_ALIGNED_MEMORY_SUPPORTED + /* Use 16-byte aligned memory for row_buf with at least 16 bytes + * of padding before and after row_buf; treat prev_row similarly. + * NOTE: the alignment is to the start of the pixels, one beyond the start + * of the buffer, because of the filter byte. Prior to libpng 1.5.6 this + * was incorrect; the filter byte was aligned, which had the exact + * opposite effect of that intended. + */ + { + png_bytep temp = png_ptr->big_row_buf + 32; + int extra = (int)((temp - (png_bytep)0) & 0x0f); + png_ptr->row_buf = temp - extra - 1/*filter byte*/; + + temp = png_ptr->big_prev_row + 32; + extra = (int)((temp - (png_bytep)0) & 0x0f); + png_ptr->prev_row = temp - extra - 1/*filter byte*/; + } + +#else + /* Use 31 bytes of padding before and 17 bytes after row_buf. */ + png_ptr->row_buf = png_ptr->big_row_buf + 31; + png_ptr->prev_row = png_ptr->big_prev_row + 31; +#endif + png_ptr->old_big_row_buf_size = row_bytes + 48; + } + +#ifdef PNG_MAX_MALLOC_64K + if (png_ptr->rowbytes > 65535) + png_error(png_ptr, "This image requires a row greater than 64KB"); + +#endif + if (png_ptr->rowbytes > (PNG_SIZE_MAX - 1)) + png_error(png_ptr, "Row has too many bytes to allocate in memory"); + + memset(png_ptr->prev_row, 0, png_ptr->rowbytes + 1); + + png_debug1(3, "width = %u,", png_ptr->width); + png_debug1(3, "height = %u,", png_ptr->height); + png_debug1(3, "iwidth = %u,", png_ptr->iwidth); + png_debug1(3, "num_rows = %u,", png_ptr->num_rows); + png_debug1(3, "rowbytes = %lu,", (unsigned long)png_ptr->rowbytes); + png_debug1(3, "irowbytes = %lu", + (unsigned long)PNG_ROWBYTES(png_ptr->pixel_depth, png_ptr->iwidth) + 1); + + /* The sequential reader needs a buffer for IDAT, but the progressive reader + * does not, so free the read buffer now regardless; the sequential reader + * reallocates it on demand. + */ + if (png_ptr->read_buffer) + { + png_bytep buffer = png_ptr->read_buffer; + + png_ptr->read_buffer_size = 0; + png_ptr->read_buffer = NULL; + png_free(png_ptr, buffer); + } + + /* Finally claim the zstream for the inflate of the IDAT data, use the bits + * value from the stream (note that this will result in a fatal error if the + * IDAT stream has a bogus deflate header window_bits value, but this should + * not be happening any longer!) + */ + if (png_inflate_claim(png_ptr, png_IDAT, 0) != Z_OK) + png_error(png_ptr, png_ptr->zstream.msg); + + png_ptr->flags |= PNG_FLAG_ROW_INIT; +} +#endif /* PNG_READ_SUPPORTED */ diff --git a/Minecraft.Client/Windows64/4JLibs/inc/Render/libpng/pngset.c b/Minecraft.Client/Windows64/4JLibs/inc/Render/libpng/pngset.c new file mode 100644 index 000000000..fcb077913 --- /dev/null +++ b/Minecraft.Client/Windows64/4JLibs/inc/Render/libpng/pngset.c @@ -0,0 +1,1606 @@ + +/* pngset.c - storage of image information into info struct + * + * Last changed in libpng 1.6.2 [April 25, 2013] + * Copyright (c) 1998-2013 Glenn Randers-Pehrson + * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) + * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) + * + * This code is released under the libpng license. + * For conditions of distribution and use, see the disclaimer + * and license in png.h + * + * The functions here are used during reads to store data from the file + * into the info struct, and during writes to store application data + * into the info struct for writing into the file. This abstracts the + * info struct and allows us to change the structure in the future. + */ + +#include "pngpriv.h" + +#if defined(PNG_READ_SUPPORTED) || defined(PNG_WRITE_SUPPORTED) + +#ifdef PNG_bKGD_SUPPORTED +void PNGAPI +png_set_bKGD(png_const_structrp png_ptr, png_inforp info_ptr, + png_const_color_16p background) +{ + png_debug1(1, "in %s storage function", "bKGD"); + + if (png_ptr == NULL || info_ptr == NULL || background == NULL) + return; + + info_ptr->background = *background; + info_ptr->valid |= PNG_INFO_bKGD; +} +#endif + +#ifdef PNG_cHRM_SUPPORTED +void PNGFAPI +png_set_cHRM_fixed(png_const_structrp png_ptr, png_inforp info_ptr, + png_fixed_point white_x, png_fixed_point white_y, png_fixed_point red_x, + png_fixed_point red_y, png_fixed_point green_x, png_fixed_point green_y, + png_fixed_point blue_x, png_fixed_point blue_y) +{ + png_xy xy; + + png_debug1(1, "in %s storage function", "cHRM fixed"); + + if (png_ptr == NULL || info_ptr == NULL) + return; + + xy.redx = red_x; + xy.redy = red_y; + xy.greenx = green_x; + xy.greeny = green_y; + xy.bluex = blue_x; + xy.bluey = blue_y; + xy.whitex = white_x; + xy.whitey = white_y; + + if (png_colorspace_set_chromaticities(png_ptr, &info_ptr->colorspace, &xy, + 2/* override with app values*/)) + info_ptr->colorspace.flags |= PNG_COLORSPACE_FROM_cHRM; + + png_colorspace_sync_info(png_ptr, info_ptr); +} + +void PNGFAPI +png_set_cHRM_XYZ_fixed(png_const_structrp png_ptr, png_inforp info_ptr, + png_fixed_point int_red_X, png_fixed_point int_red_Y, + png_fixed_point int_red_Z, png_fixed_point int_green_X, + png_fixed_point int_green_Y, png_fixed_point int_green_Z, + png_fixed_point int_blue_X, png_fixed_point int_blue_Y, + png_fixed_point int_blue_Z) +{ + png_XYZ XYZ; + + png_debug1(1, "in %s storage function", "cHRM XYZ fixed"); + + if (png_ptr == NULL || info_ptr == NULL) + return; + + XYZ.red_X = int_red_X; + XYZ.red_Y = int_red_Y; + XYZ.red_Z = int_red_Z; + XYZ.green_X = int_green_X; + XYZ.green_Y = int_green_Y; + XYZ.green_Z = int_green_Z; + XYZ.blue_X = int_blue_X; + XYZ.blue_Y = int_blue_Y; + XYZ.blue_Z = int_blue_Z; + + if (png_colorspace_set_endpoints(png_ptr, &info_ptr->colorspace, &XYZ, 2)) + info_ptr->colorspace.flags |= PNG_COLORSPACE_FROM_cHRM; + + png_colorspace_sync_info(png_ptr, info_ptr); +} + +# ifdef PNG_FLOATING_POINT_SUPPORTED +void PNGAPI +png_set_cHRM(png_const_structrp png_ptr, png_inforp info_ptr, + double white_x, double white_y, double red_x, double red_y, + double green_x, double green_y, double blue_x, double blue_y) +{ + png_set_cHRM_fixed(png_ptr, info_ptr, + png_fixed(png_ptr, white_x, "cHRM White X"), + png_fixed(png_ptr, white_y, "cHRM White Y"), + png_fixed(png_ptr, red_x, "cHRM Red X"), + png_fixed(png_ptr, red_y, "cHRM Red Y"), + png_fixed(png_ptr, green_x, "cHRM Green X"), + png_fixed(png_ptr, green_y, "cHRM Green Y"), + png_fixed(png_ptr, blue_x, "cHRM Blue X"), + png_fixed(png_ptr, blue_y, "cHRM Blue Y")); +} + +void PNGAPI +png_set_cHRM_XYZ(png_const_structrp png_ptr, png_inforp info_ptr, double red_X, + double red_Y, double red_Z, double green_X, double green_Y, double green_Z, + double blue_X, double blue_Y, double blue_Z) +{ + png_set_cHRM_XYZ_fixed(png_ptr, info_ptr, + png_fixed(png_ptr, red_X, "cHRM Red X"), + png_fixed(png_ptr, red_Y, "cHRM Red Y"), + png_fixed(png_ptr, red_Z, "cHRM Red Z"), + png_fixed(png_ptr, green_X, "cHRM Red X"), + png_fixed(png_ptr, green_Y, "cHRM Red Y"), + png_fixed(png_ptr, green_Z, "cHRM Red Z"), + png_fixed(png_ptr, blue_X, "cHRM Red X"), + png_fixed(png_ptr, blue_Y, "cHRM Red Y"), + png_fixed(png_ptr, blue_Z, "cHRM Red Z")); +} +# endif /* PNG_FLOATING_POINT_SUPPORTED */ + +#endif /* PNG_cHRM_SUPPORTED */ + +#ifdef PNG_gAMA_SUPPORTED +void PNGFAPI +png_set_gAMA_fixed(png_const_structrp png_ptr, png_inforp info_ptr, + png_fixed_point file_gamma) +{ + png_debug1(1, "in %s storage function", "gAMA"); + + if (png_ptr == NULL || info_ptr == NULL) + return; + + png_colorspace_set_gamma(png_ptr, &info_ptr->colorspace, file_gamma); + png_colorspace_sync_info(png_ptr, info_ptr); +} + +# ifdef PNG_FLOATING_POINT_SUPPORTED +void PNGAPI +png_set_gAMA(png_const_structrp png_ptr, png_inforp info_ptr, double file_gamma) +{ + png_set_gAMA_fixed(png_ptr, info_ptr, png_fixed(png_ptr, file_gamma, + "png_set_gAMA")); +} +# endif +#endif + +#ifdef PNG_hIST_SUPPORTED +void PNGAPI +png_set_hIST(png_const_structrp png_ptr, png_inforp info_ptr, + png_const_uint_16p hist) +{ + int i; + + png_debug1(1, "in %s storage function", "hIST"); + + if (png_ptr == NULL || info_ptr == NULL) + return; + + if (info_ptr->num_palette == 0 || info_ptr->num_palette + > PNG_MAX_PALETTE_LENGTH) + { + png_warning(png_ptr, + "Invalid palette size, hIST allocation skipped"); + + return; + } + + png_free_data(png_ptr, info_ptr, PNG_FREE_HIST, 0); + + /* Changed from info->num_palette to PNG_MAX_PALETTE_LENGTH in + * version 1.2.1 + */ + info_ptr->hist = png_voidcast(png_uint_16p, png_malloc_warn(png_ptr, + PNG_MAX_PALETTE_LENGTH * (sizeof (png_uint_16)))); + + if (info_ptr->hist == NULL) + { + png_warning(png_ptr, "Insufficient memory for hIST chunk data"); + return; + } + + info_ptr->free_me |= PNG_FREE_HIST; + + for (i = 0; i < info_ptr->num_palette; i++) + info_ptr->hist[i] = hist[i]; + + info_ptr->valid |= PNG_INFO_hIST; +} +#endif + +void PNGAPI +png_set_IHDR(png_const_structrp png_ptr, png_inforp info_ptr, + png_uint_32 width, png_uint_32 height, int bit_depth, + int color_type, int interlace_type, int compression_type, + int filter_type) +{ + png_debug1(1, "in %s storage function", "IHDR"); + + if (png_ptr == NULL || info_ptr == NULL) + return; + + info_ptr->width = width; + info_ptr->height = height; + info_ptr->bit_depth = (png_byte)bit_depth; + info_ptr->color_type = (png_byte)color_type; + info_ptr->compression_type = (png_byte)compression_type; + info_ptr->filter_type = (png_byte)filter_type; + info_ptr->interlace_type = (png_byte)interlace_type; + + png_check_IHDR (png_ptr, info_ptr->width, info_ptr->height, + info_ptr->bit_depth, info_ptr->color_type, info_ptr->interlace_type, + info_ptr->compression_type, info_ptr->filter_type); + + if (info_ptr->color_type == PNG_COLOR_TYPE_PALETTE) + info_ptr->channels = 1; + + else if (info_ptr->color_type & PNG_COLOR_MASK_COLOR) + info_ptr->channels = 3; + + else + info_ptr->channels = 1; + + if (info_ptr->color_type & PNG_COLOR_MASK_ALPHA) + info_ptr->channels++; + + info_ptr->pixel_depth = (png_byte)(info_ptr->channels * info_ptr->bit_depth); + + /* Check for potential overflow */ + if (width > + (PNG_UINT_32_MAX >> 3) /* 8-byte RRGGBBAA pixels */ + - 48 /* bigrowbuf hack */ + - 1 /* filter byte */ + - 7*8 /* rounding of width to multiple of 8 pixels */ + - 8) /* extra max_pixel_depth pad */ + info_ptr->rowbytes = 0; + else + info_ptr->rowbytes = PNG_ROWBYTES(info_ptr->pixel_depth, width); +} + +#ifdef PNG_oFFs_SUPPORTED +void PNGAPI +png_set_oFFs(png_const_structrp png_ptr, png_inforp info_ptr, + png_int_32 offset_x, png_int_32 offset_y, int unit_type) +{ + png_debug1(1, "in %s storage function", "oFFs"); + + if (png_ptr == NULL || info_ptr == NULL) + return; + + info_ptr->x_offset = offset_x; + info_ptr->y_offset = offset_y; + info_ptr->offset_unit_type = (png_byte)unit_type; + info_ptr->valid |= PNG_INFO_oFFs; +} +#endif + +#ifdef PNG_pCAL_SUPPORTED +void PNGAPI +png_set_pCAL(png_const_structrp png_ptr, png_inforp info_ptr, + png_const_charp purpose, png_int_32 X0, png_int_32 X1, int type, + int nparams, png_const_charp units, png_charpp params) +{ + png_size_t length; + int i; + + png_debug1(1, "in %s storage function", "pCAL"); + + if (png_ptr == NULL || info_ptr == NULL || purpose == NULL || units == NULL + || (nparams > 0 && params == NULL)) + return; + + length = strlen(purpose) + 1; + png_debug1(3, "allocating purpose for info (%lu bytes)", + (unsigned long)length); + + /* TODO: validate format of calibration name and unit name */ + + /* Check that the type matches the specification. */ + if (type < 0 || type > 3) + png_error(png_ptr, "Invalid pCAL equation type"); + + if (nparams < 0 || nparams > 255) + png_error(png_ptr, "Invalid pCAL parameter count"); + + /* Validate params[nparams] */ + for (i=0; ipcal_purpose = png_voidcast(png_charp, + png_malloc_warn(png_ptr, length)); + + if (info_ptr->pcal_purpose == NULL) + { + png_warning(png_ptr, "Insufficient memory for pCAL purpose"); + return; + } + + memcpy(info_ptr->pcal_purpose, purpose, length); + + png_debug(3, "storing X0, X1, type, and nparams in info"); + info_ptr->pcal_X0 = X0; + info_ptr->pcal_X1 = X1; + info_ptr->pcal_type = (png_byte)type; + info_ptr->pcal_nparams = (png_byte)nparams; + + length = strlen(units) + 1; + png_debug1(3, "allocating units for info (%lu bytes)", + (unsigned long)length); + + info_ptr->pcal_units = png_voidcast(png_charp, + png_malloc_warn(png_ptr, length)); + + if (info_ptr->pcal_units == NULL) + { + png_warning(png_ptr, "Insufficient memory for pCAL units"); + return; + } + + memcpy(info_ptr->pcal_units, units, length); + + info_ptr->pcal_params = png_voidcast(png_charpp, png_malloc_warn(png_ptr, + (png_size_t)((nparams + 1) * (sizeof (png_charp))))); + + if (info_ptr->pcal_params == NULL) + { + png_warning(png_ptr, "Insufficient memory for pCAL params"); + return; + } + + memset(info_ptr->pcal_params, 0, (nparams + 1) * (sizeof (png_charp))); + + for (i = 0; i < nparams; i++) + { + length = strlen(params[i]) + 1; + png_debug2(3, "allocating parameter %d for info (%lu bytes)", i, + (unsigned long)length); + + info_ptr->pcal_params[i] = (png_charp)png_malloc_warn(png_ptr, length); + + if (info_ptr->pcal_params[i] == NULL) + { + png_warning(png_ptr, "Insufficient memory for pCAL parameter"); + return; + } + + memcpy(info_ptr->pcal_params[i], params[i], length); + } + + info_ptr->valid |= PNG_INFO_pCAL; + info_ptr->free_me |= PNG_FREE_PCAL; +} +#endif + +#ifdef PNG_sCAL_SUPPORTED +void PNGAPI +png_set_sCAL_s(png_const_structrp png_ptr, png_inforp info_ptr, + int unit, png_const_charp swidth, png_const_charp sheight) +{ + png_size_t lengthw = 0, lengthh = 0; + + png_debug1(1, "in %s storage function", "sCAL"); + + if (png_ptr == NULL || info_ptr == NULL) + return; + + /* Double check the unit (should never get here with an invalid + * unit unless this is an API call.) + */ + if (unit != 1 && unit != 2) + png_error(png_ptr, "Invalid sCAL unit"); + + if (swidth == NULL || (lengthw = strlen(swidth)) == 0 || + swidth[0] == 45 /* '-' */ || !png_check_fp_string(swidth, lengthw)) + png_error(png_ptr, "Invalid sCAL width"); + + if (sheight == NULL || (lengthh = strlen(sheight)) == 0 || + sheight[0] == 45 /* '-' */ || !png_check_fp_string(sheight, lengthh)) + png_error(png_ptr, "Invalid sCAL height"); + + info_ptr->scal_unit = (png_byte)unit; + + ++lengthw; + + png_debug1(3, "allocating unit for info (%u bytes)", (unsigned int)lengthw); + + info_ptr->scal_s_width = png_voidcast(png_charp, + png_malloc_warn(png_ptr, lengthw)); + + if (info_ptr->scal_s_width == NULL) + { + png_warning(png_ptr, "Memory allocation failed while processing sCAL"); + return; + } + + memcpy(info_ptr->scal_s_width, swidth, lengthw); + + ++lengthh; + + png_debug1(3, "allocating unit for info (%u bytes)", (unsigned int)lengthh); + + info_ptr->scal_s_height = png_voidcast(png_charp, + png_malloc_warn(png_ptr, lengthh)); + + if (info_ptr->scal_s_height == NULL) + { + png_free (png_ptr, info_ptr->scal_s_width); + info_ptr->scal_s_width = NULL; + + png_warning(png_ptr, "Memory allocation failed while processing sCAL"); + return; + } + + memcpy(info_ptr->scal_s_height, sheight, lengthh); + + info_ptr->valid |= PNG_INFO_sCAL; + info_ptr->free_me |= PNG_FREE_SCAL; +} + +# ifdef PNG_FLOATING_POINT_SUPPORTED +void PNGAPI +png_set_sCAL(png_const_structrp png_ptr, png_inforp info_ptr, int unit, + double width, double height) +{ + png_debug1(1, "in %s storage function", "sCAL"); + + /* Check the arguments. */ + if (width <= 0) + png_warning(png_ptr, "Invalid sCAL width ignored"); + + else if (height <= 0) + png_warning(png_ptr, "Invalid sCAL height ignored"); + + else + { + /* Convert 'width' and 'height' to ASCII. */ + char swidth[PNG_sCAL_MAX_DIGITS+1]; + char sheight[PNG_sCAL_MAX_DIGITS+1]; + + png_ascii_from_fp(png_ptr, swidth, (sizeof swidth), width, + PNG_sCAL_PRECISION); + png_ascii_from_fp(png_ptr, sheight, (sizeof sheight), height, + PNG_sCAL_PRECISION); + + png_set_sCAL_s(png_ptr, info_ptr, unit, swidth, sheight); + } +} +# endif + +# ifdef PNG_FIXED_POINT_SUPPORTED +void PNGAPI +png_set_sCAL_fixed(png_const_structrp png_ptr, png_inforp info_ptr, int unit, + png_fixed_point width, png_fixed_point height) +{ + png_debug1(1, "in %s storage function", "sCAL"); + + /* Check the arguments. */ + if (width <= 0) + png_warning(png_ptr, "Invalid sCAL width ignored"); + + else if (height <= 0) + png_warning(png_ptr, "Invalid sCAL height ignored"); + + else + { + /* Convert 'width' and 'height' to ASCII. */ + char swidth[PNG_sCAL_MAX_DIGITS+1]; + char sheight[PNG_sCAL_MAX_DIGITS+1]; + + png_ascii_from_fixed(png_ptr, swidth, (sizeof swidth), width); + png_ascii_from_fixed(png_ptr, sheight, (sizeof sheight), height); + + png_set_sCAL_s(png_ptr, info_ptr, unit, swidth, sheight); + } +} +# endif +#endif + +#ifdef PNG_pHYs_SUPPORTED +void PNGAPI +png_set_pHYs(png_const_structrp png_ptr, png_inforp info_ptr, + png_uint_32 res_x, png_uint_32 res_y, int unit_type) +{ + png_debug1(1, "in %s storage function", "pHYs"); + + if (png_ptr == NULL || info_ptr == NULL) + return; + + info_ptr->x_pixels_per_unit = res_x; + info_ptr->y_pixels_per_unit = res_y; + info_ptr->phys_unit_type = (png_byte)unit_type; + info_ptr->valid |= PNG_INFO_pHYs; +} +#endif + +void PNGAPI +png_set_PLTE(png_structrp png_ptr, png_inforp info_ptr, + png_const_colorp palette, int num_palette) +{ + + png_debug1(1, "in %s storage function", "PLTE"); + + if (png_ptr == NULL || info_ptr == NULL) + return; + + if (num_palette < 0 || num_palette > PNG_MAX_PALETTE_LENGTH) + { + if (info_ptr->color_type == PNG_COLOR_TYPE_PALETTE) + png_error(png_ptr, "Invalid palette length"); + + else + { + png_warning(png_ptr, "Invalid palette length"); + return; + } + } + + if ((num_palette > 0 && palette == NULL) || + (num_palette == 0 +# ifdef PNG_MNG_FEATURES_SUPPORTED + && (png_ptr->mng_features_permitted & PNG_FLAG_MNG_EMPTY_PLTE) == 0 +# endif + )) + { + png_chunk_report(png_ptr, "Invalid palette", PNG_CHUNK_ERROR); + return; + } + + /* It may not actually be necessary to set png_ptr->palette here; + * we do it for backward compatibility with the way the png_handle_tRNS + * function used to do the allocation. + * + * 1.6.0: the above statement appears to be incorrect; something has to set + * the palette inside png_struct on read. + */ + png_free_data(png_ptr, info_ptr, PNG_FREE_PLTE, 0); + + /* Changed in libpng-1.2.1 to allocate PNG_MAX_PALETTE_LENGTH instead + * of num_palette entries, in case of an invalid PNG file that has + * too-large sample values. + */ + png_ptr->palette = png_voidcast(png_colorp, png_calloc(png_ptr, + PNG_MAX_PALETTE_LENGTH * (sizeof (png_color)))); + + if (num_palette > 0) + memcpy(png_ptr->palette, palette, num_palette * (sizeof (png_color))); + info_ptr->palette = png_ptr->palette; + info_ptr->num_palette = png_ptr->num_palette = (png_uint_16)num_palette; + + info_ptr->free_me |= PNG_FREE_PLTE; + + info_ptr->valid |= PNG_INFO_PLTE; +} + +#ifdef PNG_sBIT_SUPPORTED +void PNGAPI +png_set_sBIT(png_const_structrp png_ptr, png_inforp info_ptr, + png_const_color_8p sig_bit) +{ + png_debug1(1, "in %s storage function", "sBIT"); + + if (png_ptr == NULL || info_ptr == NULL || sig_bit == NULL) + return; + + info_ptr->sig_bit = *sig_bit; + info_ptr->valid |= PNG_INFO_sBIT; +} +#endif + +#ifdef PNG_sRGB_SUPPORTED +void PNGAPI +png_set_sRGB(png_const_structrp png_ptr, png_inforp info_ptr, int srgb_intent) +{ + png_debug1(1, "in %s storage function", "sRGB"); + + if (png_ptr == NULL || info_ptr == NULL) + return; + + (void)png_colorspace_set_sRGB(png_ptr, &info_ptr->colorspace, srgb_intent); + png_colorspace_sync_info(png_ptr, info_ptr); +} + +void PNGAPI +png_set_sRGB_gAMA_and_cHRM(png_const_structrp png_ptr, png_inforp info_ptr, + int srgb_intent) +{ + png_debug1(1, "in %s storage function", "sRGB_gAMA_and_cHRM"); + + if (png_ptr == NULL || info_ptr == NULL) + return; + + if (png_colorspace_set_sRGB(png_ptr, &info_ptr->colorspace, srgb_intent)) + { + /* This causes the gAMA and cHRM to be written too */ + info_ptr->colorspace.flags |= + PNG_COLORSPACE_FROM_gAMA|PNG_COLORSPACE_FROM_cHRM; + } + + png_colorspace_sync_info(png_ptr, info_ptr); +} +#endif /* sRGB */ + + +#ifdef PNG_iCCP_SUPPORTED +void PNGAPI +png_set_iCCP(png_const_structrp png_ptr, png_inforp info_ptr, + png_const_charp name, int compression_type, + png_const_bytep profile, png_uint_32 proflen) +{ + png_charp new_iccp_name; + png_bytep new_iccp_profile; + png_size_t length; + + png_debug1(1, "in %s storage function", "iCCP"); + + if (png_ptr == NULL || info_ptr == NULL || name == NULL || profile == NULL) + return; + + if (compression_type != PNG_COMPRESSION_TYPE_BASE) + png_app_error(png_ptr, "Invalid iCCP compression method"); + + /* Set the colorspace first because this validates the profile; do not + * override previously set app cHRM or gAMA here (because likely as not the + * application knows better than libpng what the correct values are.) Pass + * the info_ptr color_type field to png_colorspace_set_ICC because in the + * write case it has not yet been stored in png_ptr. + */ + { + int result = png_colorspace_set_ICC(png_ptr, &info_ptr->colorspace, name, + proflen, profile, info_ptr->color_type); + + png_colorspace_sync_info(png_ptr, info_ptr); + + /* Don't do any of the copying if the profile was bad, or inconsistent. */ + if (!result) + return; + + /* But do write the gAMA and cHRM chunks from the profile. */ + info_ptr->colorspace.flags |= + PNG_COLORSPACE_FROM_gAMA|PNG_COLORSPACE_FROM_cHRM; + } + + length = strlen(name)+1; + new_iccp_name = png_voidcast(png_charp, png_malloc_warn(png_ptr, length)); + + if (new_iccp_name == NULL) + { + png_benign_error(png_ptr, "Insufficient memory to process iCCP chunk"); + return; + } + + memcpy(new_iccp_name, name, length); + new_iccp_profile = png_voidcast(png_bytep, + png_malloc_warn(png_ptr, proflen)); + + if (new_iccp_profile == NULL) + { + png_free(png_ptr, new_iccp_name); + png_benign_error(png_ptr, + "Insufficient memory to process iCCP profile"); + return; + } + + memcpy(new_iccp_profile, profile, proflen); + + png_free_data(png_ptr, info_ptr, PNG_FREE_ICCP, 0); + + info_ptr->iccp_proflen = proflen; + info_ptr->iccp_name = new_iccp_name; + info_ptr->iccp_profile = new_iccp_profile; + info_ptr->free_me |= PNG_FREE_ICCP; + info_ptr->valid |= PNG_INFO_iCCP; +} +#endif + +#ifdef PNG_TEXT_SUPPORTED +void PNGAPI +png_set_text(png_const_structrp png_ptr, png_inforp info_ptr, + png_const_textp text_ptr, int num_text) +{ + int ret; + ret = png_set_text_2(png_ptr, info_ptr, text_ptr, num_text); + + if (ret) + png_error(png_ptr, "Insufficient memory to store text"); +} + +int /* PRIVATE */ +png_set_text_2(png_const_structrp png_ptr, png_inforp info_ptr, + png_const_textp text_ptr, int num_text) +{ + int i; + + png_debug1(1, "in %lx storage function", png_ptr == NULL ? "unexpected" : + (unsigned long)png_ptr->chunk_name); + + if (png_ptr == NULL || info_ptr == NULL || num_text <= 0 || text_ptr == NULL) + return(0); + + /* Make sure we have enough space in the "text" array in info_struct + * to hold all of the incoming text_ptr objects. This compare can't overflow + * because max_text >= num_text (anyway, subtract of two positive integers + * can't overflow in any case.) + */ + if (num_text > info_ptr->max_text - info_ptr->num_text) + { + int old_num_text = info_ptr->num_text; + int max_text; + png_textp new_text = NULL; + + /* Calculate an appropriate max_text, checking for overflow. */ + max_text = old_num_text; + if (num_text <= INT_MAX - max_text) + { + max_text += num_text; + + /* Round up to a multiple of 8 */ + if (max_text < INT_MAX-8) + max_text = (max_text + 8) & ~0x7; + + else + max_text = INT_MAX; + + /* Now allocate a new array and copy the old members in, this does all + * the overflow checks. + */ + new_text = png_voidcast(png_textp,png_realloc_array(png_ptr, + info_ptr->text, old_num_text, max_text-old_num_text, + sizeof *new_text)); + } + + if (new_text == NULL) + { + png_chunk_report(png_ptr, "too many text chunks", + PNG_CHUNK_WRITE_ERROR); + return 1; + } + + png_free(png_ptr, info_ptr->text); + + info_ptr->text = new_text; + info_ptr->free_me |= PNG_FREE_TEXT; + info_ptr->max_text = max_text; + /* num_text is adjusted below as the entries are copied in */ + + png_debug1(3, "allocated %d entries for info_ptr->text", max_text); + } + + for (i = 0; i < num_text; i++) + { + size_t text_length, key_len; + size_t lang_len, lang_key_len; + png_textp textp = &(info_ptr->text[info_ptr->num_text]); + + if (text_ptr[i].key == NULL) + continue; + + if (text_ptr[i].compression < PNG_TEXT_COMPRESSION_NONE || + text_ptr[i].compression >= PNG_TEXT_COMPRESSION_LAST) + { + png_chunk_report(png_ptr, "text compression mode is out of range", + PNG_CHUNK_WRITE_ERROR); + continue; + } + + key_len = strlen(text_ptr[i].key); + + if (text_ptr[i].compression <= 0) + { + lang_len = 0; + lang_key_len = 0; + } + + else +# ifdef PNG_iTXt_SUPPORTED + { + /* Set iTXt data */ + + if (text_ptr[i].lang != NULL) + lang_len = strlen(text_ptr[i].lang); + + else + lang_len = 0; + + if (text_ptr[i].lang_key != NULL) + lang_key_len = strlen(text_ptr[i].lang_key); + + else + lang_key_len = 0; + } +# else /* PNG_iTXt_SUPPORTED */ + { + png_chunk_report(png_ptr, "iTXt chunk not supported", + PNG_CHUNK_WRITE_ERROR); + continue; + } +# endif + + if (text_ptr[i].text == NULL || text_ptr[i].text[0] == '\0') + { + text_length = 0; +# ifdef PNG_iTXt_SUPPORTED + if (text_ptr[i].compression > 0) + textp->compression = PNG_ITXT_COMPRESSION_NONE; + + else +# endif + textp->compression = PNG_TEXT_COMPRESSION_NONE; + } + + else + { + text_length = strlen(text_ptr[i].text); + textp->compression = text_ptr[i].compression; + } + + textp->key = png_voidcast(png_charp,png_malloc_base(png_ptr, + key_len + text_length + lang_len + lang_key_len + 4)); + + if (textp->key == NULL) + { + png_chunk_report(png_ptr, "text chunk: out of memory", + PNG_CHUNK_WRITE_ERROR); + return 1; + } + + png_debug2(2, "Allocated %lu bytes at %p in png_set_text", + (unsigned long)(png_uint_32) + (key_len + lang_len + lang_key_len + text_length + 4), + textp->key); + + memcpy(textp->key, text_ptr[i].key, key_len); + *(textp->key + key_len) = '\0'; + + if (text_ptr[i].compression > 0) + { + textp->lang = textp->key + key_len + 1; + memcpy(textp->lang, text_ptr[i].lang, lang_len); + *(textp->lang + lang_len) = '\0'; + textp->lang_key = textp->lang + lang_len + 1; + memcpy(textp->lang_key, text_ptr[i].lang_key, lang_key_len); + *(textp->lang_key + lang_key_len) = '\0'; + textp->text = textp->lang_key + lang_key_len + 1; + } + + else + { + textp->lang=NULL; + textp->lang_key=NULL; + textp->text = textp->key + key_len + 1; + } + + if (text_length) + memcpy(textp->text, text_ptr[i].text, text_length); + + *(textp->text + text_length) = '\0'; + +# ifdef PNG_iTXt_SUPPORTED + if (textp->compression > 0) + { + textp->text_length = 0; + textp->itxt_length = text_length; + } + + else +# endif + { + textp->text_length = text_length; + textp->itxt_length = 0; + } + + info_ptr->num_text++; + png_debug1(3, "transferred text chunk %d", info_ptr->num_text); + } + + return(0); +} +#endif + +#ifdef PNG_tIME_SUPPORTED +void PNGAPI +png_set_tIME(png_const_structrp png_ptr, png_inforp info_ptr, + png_const_timep mod_time) +{ + png_debug1(1, "in %s storage function", "tIME"); + + if (png_ptr == NULL || info_ptr == NULL || mod_time == NULL || + (png_ptr->mode & PNG_WROTE_tIME)) + return; + + if (mod_time->month == 0 || mod_time->month > 12 || + mod_time->day == 0 || mod_time->day > 31 || + mod_time->hour > 23 || mod_time->minute > 59 || + mod_time->second > 60) + { + png_warning(png_ptr, "Ignoring invalid time value"); + return; + } + + info_ptr->mod_time = *mod_time; + info_ptr->valid |= PNG_INFO_tIME; +} +#endif + +#ifdef PNG_tRNS_SUPPORTED +void PNGAPI +png_set_tRNS(png_structrp png_ptr, png_inforp info_ptr, + png_const_bytep trans_alpha, int num_trans, png_const_color_16p trans_color) +{ + png_debug1(1, "in %s storage function", "tRNS"); + + if (png_ptr == NULL || info_ptr == NULL) + return; + + if (trans_alpha != NULL) + { + /* It may not actually be necessary to set png_ptr->trans_alpha here; + * we do it for backward compatibility with the way the png_handle_tRNS + * function used to do the allocation. + * + * 1.6.0: The above statement is incorrect; png_handle_tRNS effectively + * relies on png_set_tRNS storing the information in png_struct + * (otherwise it won't be there for the code in pngrtran.c). + */ + + png_free_data(png_ptr, info_ptr, PNG_FREE_TRNS, 0); + + /* Changed from num_trans to PNG_MAX_PALETTE_LENGTH in version 1.2.1 */ + png_ptr->trans_alpha = info_ptr->trans_alpha = png_voidcast(png_bytep, + png_malloc(png_ptr, PNG_MAX_PALETTE_LENGTH)); + + if (num_trans > 0 && num_trans <= PNG_MAX_PALETTE_LENGTH) + memcpy(info_ptr->trans_alpha, trans_alpha, (png_size_t)num_trans); + } + + if (trans_color != NULL) + { + int sample_max = (1 << info_ptr->bit_depth); + + if ((info_ptr->color_type == PNG_COLOR_TYPE_GRAY && + trans_color->gray > sample_max) || + (info_ptr->color_type == PNG_COLOR_TYPE_RGB && + (trans_color->red > sample_max || + trans_color->green > sample_max || + trans_color->blue > sample_max))) + png_warning(png_ptr, + "tRNS chunk has out-of-range samples for bit_depth"); + + info_ptr->trans_color = *trans_color; + + if (num_trans == 0) + num_trans = 1; + } + + info_ptr->num_trans = (png_uint_16)num_trans; + + if (num_trans != 0) + { + info_ptr->valid |= PNG_INFO_tRNS; + info_ptr->free_me |= PNG_FREE_TRNS; + } +} +#endif + +#ifdef PNG_sPLT_SUPPORTED +void PNGAPI +png_set_sPLT(png_const_structrp png_ptr, + png_inforp info_ptr, png_const_sPLT_tp entries, int nentries) +/* + * entries - array of png_sPLT_t structures + * to be added to the list of palettes + * in the info structure. + * + * nentries - number of palette structures to be + * added. + */ +{ + png_sPLT_tp np; + + if (png_ptr == NULL || info_ptr == NULL || nentries <= 0 || entries == NULL) + return; + + /* Use the internal realloc function, which checks for all the possible + * overflows. Notice that the parameters are (int) and (size_t) + */ + np = png_voidcast(png_sPLT_tp,png_realloc_array(png_ptr, + info_ptr->splt_palettes, info_ptr->splt_palettes_num, nentries, + sizeof *np)); + + if (np == NULL) + { + /* Out of memory or too many chunks */ + png_chunk_report(png_ptr, "too many sPLT chunks", PNG_CHUNK_WRITE_ERROR); + return; + } + + png_free(png_ptr, info_ptr->splt_palettes); + info_ptr->splt_palettes = np; + info_ptr->free_me |= PNG_FREE_SPLT; + + np += info_ptr->splt_palettes_num; + + do + { + png_size_t length; + + /* Skip invalid input entries */ + if (entries->name == NULL || entries->entries == NULL) + { + /* png_handle_sPLT doesn't do this, so this is an app error */ + png_app_error(png_ptr, "png_set_sPLT: invalid sPLT"); + /* Just skip the invalid entry */ + continue; + } + + np->depth = entries->depth; + + /* In the even of out-of-memory just return - there's no point keeping on + * trying to add sPLT chunks. + */ + length = strlen(entries->name) + 1; + np->name = png_voidcast(png_charp, png_malloc_base(png_ptr, length)); + + if (np->name == NULL) + break; + + memcpy(np->name, entries->name, length); + + /* IMPORTANT: we have memory now that won't get freed if something else + * goes wrong, this code must free it. png_malloc_array produces no + * warnings, use a png_chunk_report (below) if there is an error. + */ + np->entries = png_voidcast(png_sPLT_entryp, png_malloc_array(png_ptr, + entries->nentries, sizeof (png_sPLT_entry))); + + if (np->entries == NULL) + { + png_free(png_ptr, np->name); + break; + } + + np->nentries = entries->nentries; + /* This multiply can't overflow because png_malloc_array has already + * checked it when doing the allocation. + */ + memcpy(np->entries, entries->entries, + entries->nentries * sizeof (png_sPLT_entry)); + + /* Note that 'continue' skips the advance of the out pointer and out + * count, so an invalid entry is not added. + */ + info_ptr->valid |= PNG_INFO_sPLT; + ++(info_ptr->splt_palettes_num); + ++np; + } + while (++entries, --nentries); + + if (nentries > 0) + png_chunk_report(png_ptr, "sPLT out of memory", PNG_CHUNK_WRITE_ERROR); +} +#endif /* PNG_sPLT_SUPPORTED */ + +#ifdef PNG_STORE_UNKNOWN_CHUNKS_SUPPORTED +static png_byte +check_location(png_const_structrp png_ptr, int location) +{ + location &= (PNG_HAVE_IHDR|PNG_HAVE_PLTE|PNG_AFTER_IDAT); + + /* New in 1.6.0; copy the location and check it. This is an API + * change, previously the app had to use the + * png_set_unknown_chunk_location API below for each chunk. + */ + if (location == 0 && !(png_ptr->mode & PNG_IS_READ_STRUCT)) + { + /* Write struct, so unknown chunks come from the app */ + png_app_warning(png_ptr, + "png_set_unknown_chunks now expects a valid location"); + /* Use the old behavior */ + location = (png_byte)(png_ptr->mode & + (PNG_HAVE_IHDR|PNG_HAVE_PLTE|PNG_AFTER_IDAT)); + } + + /* This need not be an internal error - if the app calls + * png_set_unknown_chunks on a read pointer it must get the location right. + */ + if (location == 0) + png_error(png_ptr, "invalid location in png_set_unknown_chunks"); + + /* Now reduce the location to the top-most set bit by removing each least + * significant bit in turn. + */ + while (location != (location & -location)) + location &= ~(location & -location); + + /* The cast is safe because 'location' is a bit mask and only the low four + * bits are significant. + */ + return (png_byte)location; +} + +void PNGAPI +png_set_unknown_chunks(png_const_structrp png_ptr, + png_inforp info_ptr, png_const_unknown_chunkp unknowns, int num_unknowns) +{ + png_unknown_chunkp np; + + if (png_ptr == NULL || info_ptr == NULL || num_unknowns <= 0 || + unknowns == NULL) + return; + + /* Check for the failure cases where support has been disabled at compile + * time. This code is hardly ever compiled - it's here because + * STORE_UNKNOWN_CHUNKS is set by both read and write code (compiling in this + * code) but may be meaningless if the read or write handling of unknown + * chunks is not compiled in. + */ +# if !defined(PNG_READ_UNKNOWN_CHUNKS_SUPPORTED) && \ + defined(PNG_READ_SUPPORTED) + if (png_ptr->mode & PNG_IS_READ_STRUCT) + { + png_app_error(png_ptr, "no unknown chunk support on read"); + return; + } +# endif +# if !defined(PNG_WRITE_UNKNOWN_CHUNKS_SUPPORTED) && \ + defined(PNG_WRITE_SUPPORTED) + if (!(png_ptr->mode & PNG_IS_READ_STRUCT)) + { + png_app_error(png_ptr, "no unknown chunk support on write"); + return; + } +# endif + + /* Prior to 1.6.0 this code used png_malloc_warn; however, this meant that + * unknown critical chunks could be lost with just a warning resulting in + * undefined behavior. Now png_chunk_report is used to provide behavior + * appropriate to read or write. + */ + np = png_voidcast(png_unknown_chunkp, png_realloc_array(png_ptr, + info_ptr->unknown_chunks, info_ptr->unknown_chunks_num, num_unknowns, + sizeof *np)); + + if (np == NULL) + { + png_chunk_report(png_ptr, "too many unknown chunks", + PNG_CHUNK_WRITE_ERROR); + return; + } + + png_free(png_ptr, info_ptr->unknown_chunks); + info_ptr->unknown_chunks = np; /* safe because it is initialized */ + info_ptr->free_me |= PNG_FREE_UNKN; + + np += info_ptr->unknown_chunks_num; + + /* Increment unknown_chunks_num each time round the loop to protect the + * just-allocated chunk data. + */ + for (; num_unknowns > 0; --num_unknowns, ++unknowns) + { + memcpy(np->name, unknowns->name, (sizeof np->name)); + np->name[(sizeof np->name)-1] = '\0'; + np->location = check_location(png_ptr, unknowns->location); + + if (unknowns->size == 0) + { + np->data = NULL; + np->size = 0; + } + + else + { + np->data = png_voidcast(png_bytep, + png_malloc_base(png_ptr, unknowns->size)); + + if (np->data == NULL) + { + png_chunk_report(png_ptr, "unknown chunk: out of memory", + PNG_CHUNK_WRITE_ERROR); + /* But just skip storing the unknown chunk */ + continue; + } + + memcpy(np->data, unknowns->data, unknowns->size); + np->size = unknowns->size; + } + + /* These increments are skipped on out-of-memory for the data - the + * unknown chunk entry gets overwritten if the png_chunk_report returns. + * This is correct in the read case (the chunk is just dropped.) + */ + ++np; + ++(info_ptr->unknown_chunks_num); + } +} + +void PNGAPI +png_set_unknown_chunk_location(png_const_structrp png_ptr, png_inforp info_ptr, + int chunk, int location) +{ + /* This API is pretty pointless in 1.6.0 because the location can be set + * before the call to png_set_unknown_chunks. + * + * TODO: add a png_app_warning in 1.7 + */ + if (png_ptr != NULL && info_ptr != NULL && chunk >= 0 && + chunk < info_ptr->unknown_chunks_num) + { + if ((location & (PNG_HAVE_IHDR|PNG_HAVE_PLTE|PNG_AFTER_IDAT)) == 0) + { + png_app_error(png_ptr, "invalid unknown chunk location"); + /* Fake out the pre 1.6.0 behavior: */ + if ((location & PNG_HAVE_IDAT)) /* undocumented! */ + location = PNG_AFTER_IDAT; + + else + location = PNG_HAVE_IHDR; /* also undocumented */ + } + + info_ptr->unknown_chunks[chunk].location = + check_location(png_ptr, location); + } +} +#endif + + +#ifdef PNG_MNG_FEATURES_SUPPORTED +png_uint_32 PNGAPI +png_permit_mng_features (png_structrp png_ptr, png_uint_32 mng_features) +{ + png_debug(1, "in png_permit_mng_features"); + + if (png_ptr == NULL) + return 0; + + png_ptr->mng_features_permitted = mng_features & PNG_ALL_MNG_FEATURES; + + return png_ptr->mng_features_permitted; +} +#endif + +#ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED +static unsigned int +add_one_chunk(png_bytep list, unsigned int count, png_const_bytep add, int keep) +{ + unsigned int i; + + /* Utility function: update the 'keep' state of a chunk if it is already in + * the list, otherwise add it to the list. + */ + for (i=0; i= PNG_HANDLE_CHUNK_LAST) + { + png_app_error(png_ptr, "png_set_keep_unknown_chunks: invalid keep"); + return; + } + + if (num_chunks_in <= 0) + { + png_ptr->unknown_default = keep; + + /* '0' means just set the flags, so stop here */ + if (num_chunks_in == 0) + return; + } + + if (num_chunks_in < 0) + { + /* Ignore all unknown chunks and all chunks recognized by + * libpng except for IHDR, PLTE, tRNS, IDAT, and IEND + */ + static PNG_CONST png_byte chunks_to_ignore[] = { + 98, 75, 71, 68, '\0', /* bKGD */ + 99, 72, 82, 77, '\0', /* cHRM */ + 103, 65, 77, 65, '\0', /* gAMA */ + 104, 73, 83, 84, '\0', /* hIST */ + 105, 67, 67, 80, '\0', /* iCCP */ + 105, 84, 88, 116, '\0', /* iTXt */ + 111, 70, 70, 115, '\0', /* oFFs */ + 112, 67, 65, 76, '\0', /* pCAL */ + 112, 72, 89, 115, '\0', /* pHYs */ + 115, 66, 73, 84, '\0', /* sBIT */ + 115, 67, 65, 76, '\0', /* sCAL */ + 115, 80, 76, 84, '\0', /* sPLT */ + 115, 84, 69, 82, '\0', /* sTER */ + 115, 82, 71, 66, '\0', /* sRGB */ + 116, 69, 88, 116, '\0', /* tEXt */ + 116, 73, 77, 69, '\0', /* tIME */ + 122, 84, 88, 116, '\0' /* zTXt */ + }; + + chunk_list = chunks_to_ignore; + num_chunks = (sizeof chunks_to_ignore)/5; + } + + else /* num_chunks_in > 0 */ + { + if (chunk_list == NULL) + { + /* Prior to 1.6.0 this was silently ignored, now it is an app_error + * which can be switched off. + */ + png_app_error(png_ptr, "png_set_keep_unknown_chunks: no chunk list"); + return; + } + + num_chunks = num_chunks_in; + } + + old_num_chunks = png_ptr->num_chunk_list; + if (png_ptr->chunk_list == NULL) + old_num_chunks = 0; + + /* Since num_chunks is always restricted to UINT_MAX/5 this can't overflow. + */ + if (num_chunks + old_num_chunks > UINT_MAX/5) + { + png_app_error(png_ptr, "png_set_keep_unknown_chunks: too many chunks"); + return; + } + + /* If these chunks are being reset to the default then no more memory is + * required because add_one_chunk above doesn't extend the list if the 'keep' + * parameter is the default. + */ + if (keep) + { + new_list = png_voidcast(png_bytep, png_malloc(png_ptr, + 5 * (num_chunks + old_num_chunks))); + + if (old_num_chunks > 0) + memcpy(new_list, png_ptr->chunk_list, 5*old_num_chunks); + } + + else if (old_num_chunks > 0) + new_list = png_ptr->chunk_list; + + else + new_list = NULL; + + /* Add the new chunks together with each one's handling code. If the chunk + * already exists the code is updated, otherwise the chunk is added to the + * end. (In libpng 1.6.0 order no longer matters because this code enforces + * the earlier convention that the last setting is the one that is used.) + */ + if (new_list != NULL) + { + png_const_bytep inlist; + png_bytep outlist; + unsigned int i; + + for (i=0; ichunk_list != new_list) + png_free(png_ptr, new_list); + + new_list = NULL; + } + } + + else + num_chunks = 0; + + png_ptr->num_chunk_list = num_chunks; + + if (png_ptr->chunk_list != new_list) + { + if (png_ptr->chunk_list != NULL) + png_free(png_ptr, png_ptr->chunk_list); + + png_ptr->chunk_list = new_list; + } +} +#endif + +#ifdef PNG_READ_USER_CHUNKS_SUPPORTED +void PNGAPI +png_set_read_user_chunk_fn(png_structrp png_ptr, png_voidp user_chunk_ptr, + png_user_chunk_ptr read_user_chunk_fn) +{ + png_debug(1, "in png_set_read_user_chunk_fn"); + + if (png_ptr == NULL) + return; + + png_ptr->read_user_chunk_fn = read_user_chunk_fn; + png_ptr->user_chunk_ptr = user_chunk_ptr; +} +#endif + +#ifdef PNG_INFO_IMAGE_SUPPORTED +void PNGAPI +png_set_rows(png_const_structrp png_ptr, png_inforp info_ptr, + png_bytepp row_pointers) +{ + png_debug1(1, "in %s storage function", "rows"); + + if (png_ptr == NULL || info_ptr == NULL) + return; + + if (info_ptr->row_pointers && (info_ptr->row_pointers != row_pointers)) + png_free_data(png_ptr, info_ptr, PNG_FREE_ROWS, 0); + + info_ptr->row_pointers = row_pointers; + + if (row_pointers) + info_ptr->valid |= PNG_INFO_IDAT; +} +#endif + +void PNGAPI +png_set_compression_buffer_size(png_structrp png_ptr, png_size_t size) +{ + if (png_ptr == NULL) + return; + + if (size == 0 || size > PNG_UINT_31_MAX) + png_error(png_ptr, "invalid compression buffer size"); + +# ifdef PNG_SEQUENTIAL_READ_SUPPORTED + if (png_ptr->mode & PNG_IS_READ_STRUCT) + { + png_ptr->IDAT_read_size = (png_uint_32)size; /* checked above */ + return; + } +# endif + +# ifdef PNG_WRITE_SUPPORTED + if (!(png_ptr->mode & PNG_IS_READ_STRUCT)) + { + if (png_ptr->zowner != 0) + { + png_warning(png_ptr, + "Compression buffer size cannot be changed because it is in use"); + return; + } + + if (size > ZLIB_IO_MAX) + { + png_warning(png_ptr, + "Compression buffer size limited to system maximum"); + size = ZLIB_IO_MAX; /* must fit */ + } + + else if (size < 6) + { + /* Deflate will potentially go into an infinite loop on a SYNC_FLUSH + * if this is permitted. + */ + png_warning(png_ptr, + "Compression buffer size cannot be reduced below 6"); + return; + } + + if (png_ptr->zbuffer_size != size) + { + png_free_buffer_list(png_ptr, &png_ptr->zbuffer_list); + png_ptr->zbuffer_size = (uInt)size; + } + } +# endif +} + +void PNGAPI +png_set_invalid(png_const_structrp png_ptr, png_inforp info_ptr, int mask) +{ + if (png_ptr && info_ptr) + info_ptr->valid &= ~mask; +} + + +#ifdef PNG_SET_USER_LIMITS_SUPPORTED +/* This function was added to libpng 1.2.6 */ +void PNGAPI +png_set_user_limits (png_structrp png_ptr, png_uint_32 user_width_max, + png_uint_32 user_height_max) +{ + /* Images with dimensions larger than these limits will be + * rejected by png_set_IHDR(). To accept any PNG datastream + * regardless of dimensions, set both limits to 0x7ffffffL. + */ + if (png_ptr == NULL) + return; + + png_ptr->user_width_max = user_width_max; + png_ptr->user_height_max = user_height_max; +} + +/* This function was added to libpng 1.4.0 */ +void PNGAPI +png_set_chunk_cache_max (png_structrp png_ptr, png_uint_32 user_chunk_cache_max) +{ + if (png_ptr) + png_ptr->user_chunk_cache_max = user_chunk_cache_max; +} + +/* This function was added to libpng 1.4.1 */ +void PNGAPI +png_set_chunk_malloc_max (png_structrp png_ptr, + png_alloc_size_t user_chunk_malloc_max) +{ + if (png_ptr) + png_ptr->user_chunk_malloc_max = user_chunk_malloc_max; +} +#endif /* ?PNG_SET_USER_LIMITS_SUPPORTED */ + + +#ifdef PNG_BENIGN_ERRORS_SUPPORTED +void PNGAPI +png_set_benign_errors(png_structrp png_ptr, int allowed) +{ + png_debug(1, "in png_set_benign_errors"); + + /* If allowed is 1, png_benign_error() is treated as a warning. + * + * If allowed is 0, png_benign_error() is treated as an error (which + * is the default behavior if png_set_benign_errors() is not called). + */ + + if (allowed) + png_ptr->flags |= PNG_FLAG_BENIGN_ERRORS_WARN | + PNG_FLAG_APP_WARNINGS_WARN | PNG_FLAG_APP_ERRORS_WARN; + + else + png_ptr->flags &= ~(PNG_FLAG_BENIGN_ERRORS_WARN | + PNG_FLAG_APP_WARNINGS_WARN | PNG_FLAG_APP_ERRORS_WARN); +} +#endif /* PNG_BENIGN_ERRORS_SUPPORTED */ + +#ifdef PNG_CHECK_FOR_INVALID_INDEX_SUPPORTED + /* Whether to report invalid palette index; added at libng-1.5.10. + * It is possible for an indexed (color-type==3) PNG file to contain + * pixels with invalid (out-of-range) indexes if the PLTE chunk has + * fewer entries than the image's bit-depth would allow. We recover + * from this gracefully by filling any incomplete palette with zeroes + * (opaque black). By default, when this occurs libpng will issue + * a benign error. This API can be used to override that behavior. + */ +void PNGAPI +png_set_check_for_invalid_index(png_structrp png_ptr, int allowed) +{ + png_debug(1, "in png_set_check_for_invalid_index"); + + if (allowed > 0) + png_ptr->num_palette_max = 0; + + else + png_ptr->num_palette_max = -1; +} +#endif +#endif /* PNG_READ_SUPPORTED || PNG_WRITE_SUPPORTED */ diff --git a/Minecraft.Client/Windows64/4JLibs/inc/Render/libpng/pngstruct.h b/Minecraft.Client/Windows64/4JLibs/inc/Render/libpng/pngstruct.h new file mode 100644 index 000000000..d58c02884 --- /dev/null +++ b/Minecraft.Client/Windows64/4JLibs/inc/Render/libpng/pngstruct.h @@ -0,0 +1,489 @@ + +/* pngstruct.h - header file for PNG reference library + * + * Copyright (c) 1998-2013 Glenn Randers-Pehrson + * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) + * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) + * + * Last changed in libpng 1.6.1 [March 28, 2013] + * + * This code is released under the libpng license. + * For conditions of distribution and use, see the disclaimer + * and license in png.h + */ + +/* The structure that holds the information to read and write PNG files. + * The only people who need to care about what is inside of this are the + * people who will be modifying the library for their own special needs. + * It should NOT be accessed directly by an application. + */ + +#ifndef PNGSTRUCT_H +#define PNGSTRUCT_H +/* zlib.h defines the structure z_stream, an instance of which is included + * in this structure and is required for decompressing the LZ compressed + * data in PNG files. + */ +#ifndef ZLIB_CONST + /* We must ensure that zlib uses 'const' in declarations. */ +# define ZLIB_CONST +#endif +#include "zlib.h" +#ifdef const + /* zlib.h sometimes #defines const to nothing, undo this. */ +# undef const +#endif + +/* zlib.h has mediocre z_const use before 1.2.6, this stuff is for compatibility + * with older builds. + */ +#if ZLIB_VERNUM < 0x1260 +# define PNGZ_MSG_CAST(s) png_constcast(char*,s) +# define PNGZ_INPUT_CAST(b) png_constcast(png_bytep,b) +#else +# define PNGZ_MSG_CAST(s) (s) +# define PNGZ_INPUT_CAST(b) (b) +#endif + +/* zlib.h declares a magic type 'uInt' that limits the amount of data that zlib + * can handle at once. This type need be no larger than 16 bits (so maximum of + * 65535), this define allows us to discover how big it is, but limited by the + * maximuum for png_size_t. The value can be overriden in a library build + * (pngusr.h, or set it in CPPFLAGS) and it works to set it to a considerably + * lower value (e.g. 255 works). A lower value may help memory usage (slightly) + * and may even improve performance on some systems (and degrade it on others.) + */ +#ifndef ZLIB_IO_MAX +# define ZLIB_IO_MAX ((uInt)-1) +#endif + +#ifdef PNG_WRITE_SUPPORTED +/* The type of a compression buffer list used by the write code. */ +typedef struct png_compression_buffer +{ + struct png_compression_buffer *next; + png_byte output[1]; /* actually zbuf_size */ +} png_compression_buffer, *png_compression_bufferp; + +#define PNG_COMPRESSION_BUFFER_SIZE(pp)\ + (offsetof(png_compression_buffer, output) + (pp)->zbuffer_size) +#endif + +/* Colorspace support; structures used in png_struct, png_info and in internal + * functions to hold and communicate information about the color space. + * + * PNG_COLORSPACE_SUPPORTED is only required if the application will perform + * colorspace corrections, otherwise all the colorspace information can be + * skipped and the size of libpng can be reduced (significantly) by compiling + * out the colorspace support. + */ +#ifdef PNG_COLORSPACE_SUPPORTED +/* The chromaticities of the red, green and blue colorants and the chromaticity + * of the corresponding white point (i.e. of rgb(1.0,1.0,1.0)). + */ +typedef struct png_xy +{ + png_fixed_point redx, redy; + png_fixed_point greenx, greeny; + png_fixed_point bluex, bluey; + png_fixed_point whitex, whitey; +} png_xy; + +/* The same data as above but encoded as CIE XYZ values. When this data comes + * from chromaticities the sum of the Y values is assumed to be 1.0 + */ +typedef struct png_XYZ +{ + png_fixed_point red_X, red_Y, red_Z; + png_fixed_point green_X, green_Y, green_Z; + png_fixed_point blue_X, blue_Y, blue_Z; +} png_XYZ; +#endif /* COLORSPACE */ + +#if defined(PNG_COLORSPACE_SUPPORTED) || defined(PNG_GAMMA_SUPPORTED) +/* A colorspace is all the above plus, potentially, profile information, + * however at present libpng does not use the profile internally so it is only + * stored in the png_info struct (if iCCP is supported.) The rendering intent + * is retained here and is checked. + * + * The file gamma encoding information is also stored here and gamma correction + * is done by libpng, whereas color correction must currently be done by the + * application. + */ +typedef struct png_colorspace +{ +#ifdef PNG_GAMMA_SUPPORTED + png_fixed_point gamma; /* File gamma */ +#endif + +#ifdef PNG_COLORSPACE_SUPPORTED + png_xy end_points_xy; /* End points as chromaticities */ + png_XYZ end_points_XYZ; /* End points as CIE XYZ colorant values */ + png_uint_16 rendering_intent; /* Rendering intent of a profile */ +#endif + + /* Flags are always defined to simplify the code. */ + png_uint_16 flags; /* As defined below */ +} png_colorspace, * PNG_RESTRICT png_colorspacerp; + +typedef const png_colorspace * PNG_RESTRICT png_const_colorspacerp; + +/* General flags for the 'flags' field */ +#define PNG_COLORSPACE_HAVE_GAMMA 0x0001 +#define PNG_COLORSPACE_HAVE_ENDPOINTS 0x0002 +#define PNG_COLORSPACE_HAVE_INTENT 0x0004 +#define PNG_COLORSPACE_FROM_gAMA 0x0008 +#define PNG_COLORSPACE_FROM_cHRM 0x0010 +#define PNG_COLORSPACE_FROM_sRGB 0x0020 +#define PNG_COLORSPACE_ENDPOINTS_MATCH_sRGB 0x0040 +#define PNG_COLORSPACE_MATCHES_sRGB 0x0080 /* exact match on profile */ +#define PNG_COLORSPACE_INVALID 0x8000 +#define PNG_COLORSPACE_CANCEL(flags) (0xffff ^ (flags)) +#endif /* COLORSPACE || GAMMA */ + +struct png_struct_def +{ +#ifdef PNG_SETJMP_SUPPORTED + jmp_buf jmp_buf_local; /* New name in 1.6.0 for jmp_buf in png_struct */ + png_longjmp_ptr longjmp_fn;/* setjmp non-local goto function. */ + jmp_buf *jmp_buf_ptr; /* passed to longjmp_fn */ + size_t jmp_buf_size; /* size of the above, if allocated */ +#endif + png_error_ptr error_fn; /* function for printing errors and aborting */ +#ifdef PNG_WARNINGS_SUPPORTED + png_error_ptr warning_fn; /* function for printing warnings */ +#endif + png_voidp error_ptr; /* user supplied struct for error functions */ + png_rw_ptr write_data_fn; /* function for writing output data */ + png_rw_ptr read_data_fn; /* function for reading input data */ + png_voidp io_ptr; /* ptr to application struct for I/O functions */ + +#ifdef PNG_READ_USER_TRANSFORM_SUPPORTED + png_user_transform_ptr read_user_transform_fn; /* user read transform */ +#endif + +#ifdef PNG_WRITE_USER_TRANSFORM_SUPPORTED + png_user_transform_ptr write_user_transform_fn; /* user write transform */ +#endif + +/* These were added in libpng-1.0.2 */ +#ifdef PNG_USER_TRANSFORM_PTR_SUPPORTED +#if defined(PNG_READ_USER_TRANSFORM_SUPPORTED) || \ + defined(PNG_WRITE_USER_TRANSFORM_SUPPORTED) + png_voidp user_transform_ptr; /* user supplied struct for user transform */ + png_byte user_transform_depth; /* bit depth of user transformed pixels */ + png_byte user_transform_channels; /* channels in user transformed pixels */ +#endif +#endif + + png_uint_32 mode; /* tells us where we are in the PNG file */ + png_uint_32 flags; /* flags indicating various things to libpng */ + png_uint_32 transformations; /* which transformations to perform */ + + png_uint_32 zowner; /* ID (chunk type) of zstream owner, 0 if none */ + z_stream zstream; /* decompression structure */ + +#ifdef PNG_WRITE_SUPPORTED + png_compression_bufferp zbuffer_list; /* Created on demand during write */ + uInt zbuffer_size; /* size of the actual buffer */ + + int zlib_level; /* holds zlib compression level */ + int zlib_method; /* holds zlib compression method */ + int zlib_window_bits; /* holds zlib compression window bits */ + int zlib_mem_level; /* holds zlib compression memory level */ + int zlib_strategy; /* holds zlib compression strategy */ +#endif +/* Added at libpng 1.5.4 */ +#ifdef PNG_WRITE_CUSTOMIZE_ZTXT_COMPRESSION_SUPPORTED + int zlib_text_level; /* holds zlib compression level */ + int zlib_text_method; /* holds zlib compression method */ + int zlib_text_window_bits; /* holds zlib compression window bits */ + int zlib_text_mem_level; /* holds zlib compression memory level */ + int zlib_text_strategy; /* holds zlib compression strategy */ +#endif +/* End of material added at libpng 1.5.4 */ +/* Added at libpng 1.6.0 */ +#ifdef PNG_WRITE_SUPPORTED + int zlib_set_level; /* Actual values set into the zstream on write */ + int zlib_set_method; + int zlib_set_window_bits; + int zlib_set_mem_level; + int zlib_set_strategy; +#endif + + png_uint_32 width; /* width of image in pixels */ + png_uint_32 height; /* height of image in pixels */ + png_uint_32 num_rows; /* number of rows in current pass */ + png_uint_32 usr_width; /* width of row at start of write */ + png_size_t rowbytes; /* size of row in bytes */ + png_uint_32 iwidth; /* width of current interlaced row in pixels */ + png_uint_32 row_number; /* current row in interlace pass */ + png_uint_32 chunk_name; /* PNG_CHUNK() id of current chunk */ + png_bytep prev_row; /* buffer to save previous (unfiltered) row. + * This is a pointer into big_prev_row + */ + png_bytep row_buf; /* buffer to save current (unfiltered) row. + * This is a pointer into big_row_buf + */ +#ifdef PNG_WRITE_SUPPORTED + png_bytep sub_row; /* buffer to save "sub" row when filtering */ + png_bytep up_row; /* buffer to save "up" row when filtering */ + png_bytep avg_row; /* buffer to save "avg" row when filtering */ + png_bytep paeth_row; /* buffer to save "Paeth" row when filtering */ +#endif + png_size_t info_rowbytes; /* Added in 1.5.4: cache of updated row bytes */ + + png_uint_32 idat_size; /* current IDAT size for read */ + png_uint_32 crc; /* current chunk CRC value */ + png_colorp palette; /* palette from the input file */ + png_uint_16 num_palette; /* number of color entries in palette */ + +/* Added at libpng-1.5.10 */ +#ifdef PNG_CHECK_FOR_INVALID_INDEX_SUPPORTED + int num_palette_max; /* maximum palette index found in IDAT */ +#endif + + png_uint_16 num_trans; /* number of transparency values */ + png_byte compression; /* file compression type (always 0) */ + png_byte filter; /* file filter type (always 0) */ + png_byte interlaced; /* PNG_INTERLACE_NONE, PNG_INTERLACE_ADAM7 */ + png_byte pass; /* current interlace pass (0 - 6) */ + png_byte do_filter; /* row filter flags (see PNG_FILTER_ below ) */ + png_byte color_type; /* color type of file */ + png_byte bit_depth; /* bit depth of file */ + png_byte usr_bit_depth; /* bit depth of users row: write only */ + png_byte pixel_depth; /* number of bits per pixel */ + png_byte channels; /* number of channels in file */ +#ifdef PNG_WRITE_SUPPORTED + png_byte usr_channels; /* channels at start of write: write only */ +#endif + png_byte sig_bytes; /* magic bytes read/written from start of file */ + png_byte maximum_pixel_depth; + /* pixel depth used for the row buffers */ + png_byte transformed_pixel_depth; + /* pixel depth after read/write transforms */ +#if defined(PNG_READ_FILLER_SUPPORTED) || defined(PNG_WRITE_FILLER_SUPPORTED) + png_uint_16 filler; /* filler bytes for pixel expansion */ +#endif + +#if defined(PNG_bKGD_SUPPORTED) || defined(PNG_READ_BACKGROUND_SUPPORTED) ||\ + defined(PNG_READ_ALPHA_MODE_SUPPORTED) + png_byte background_gamma_type; + png_fixed_point background_gamma; + png_color_16 background; /* background color in screen gamma space */ +#ifdef PNG_READ_GAMMA_SUPPORTED + png_color_16 background_1; /* background normalized to gamma 1.0 */ +#endif +#endif /* PNG_bKGD_SUPPORTED */ + +#ifdef PNG_WRITE_FLUSH_SUPPORTED + png_flush_ptr output_flush_fn; /* Function for flushing output */ + png_uint_32 flush_dist; /* how many rows apart to flush, 0 - no flush */ + png_uint_32 flush_rows; /* number of rows written since last flush */ +#endif + +#ifdef PNG_READ_GAMMA_SUPPORTED + int gamma_shift; /* number of "insignificant" bits in 16-bit gamma */ + png_fixed_point screen_gamma; /* screen gamma value (display_exponent) */ + + png_bytep gamma_table; /* gamma table for 8-bit depth files */ + png_uint_16pp gamma_16_table; /* gamma table for 16-bit depth files */ +#if defined(PNG_READ_BACKGROUND_SUPPORTED) || \ + defined(PNG_READ_ALPHA_MODE_SUPPORTED) || \ + defined(PNG_READ_RGB_TO_GRAY_SUPPORTED) + png_bytep gamma_from_1; /* converts from 1.0 to screen */ + png_bytep gamma_to_1; /* converts from file to 1.0 */ + png_uint_16pp gamma_16_from_1; /* converts from 1.0 to screen */ + png_uint_16pp gamma_16_to_1; /* converts from file to 1.0 */ +#endif /* READ_BACKGROUND || READ_ALPHA_MODE || RGB_TO_GRAY */ +#endif + +#if defined(PNG_READ_GAMMA_SUPPORTED) || defined(PNG_sBIT_SUPPORTED) + png_color_8 sig_bit; /* significant bits in each available channel */ +#endif + +#if defined(PNG_READ_SHIFT_SUPPORTED) || defined(PNG_WRITE_SHIFT_SUPPORTED) + png_color_8 shift; /* shift for significant bit tranformation */ +#endif + +#if defined(PNG_tRNS_SUPPORTED) || defined(PNG_READ_BACKGROUND_SUPPORTED) \ + || defined(PNG_READ_EXPAND_SUPPORTED) || defined(PNG_READ_BACKGROUND_SUPPORTED) + png_bytep trans_alpha; /* alpha values for paletted files */ + png_color_16 trans_color; /* transparent color for non-paletted files */ +#endif + + png_read_status_ptr read_row_fn; /* called after each row is decoded */ + png_write_status_ptr write_row_fn; /* called after each row is encoded */ +#ifdef PNG_PROGRESSIVE_READ_SUPPORTED + png_progressive_info_ptr info_fn; /* called after header data fully read */ + png_progressive_row_ptr row_fn; /* called after a prog. row is decoded */ + png_progressive_end_ptr end_fn; /* called after image is complete */ + png_bytep save_buffer_ptr; /* current location in save_buffer */ + png_bytep save_buffer; /* buffer for previously read data */ + png_bytep current_buffer_ptr; /* current location in current_buffer */ + png_bytep current_buffer; /* buffer for recently used data */ + png_uint_32 push_length; /* size of current input chunk */ + png_uint_32 skip_length; /* bytes to skip in input data */ + png_size_t save_buffer_size; /* amount of data now in save_buffer */ + png_size_t save_buffer_max; /* total size of save_buffer */ + png_size_t buffer_size; /* total amount of available input data */ + png_size_t current_buffer_size; /* amount of data now in current_buffer */ + int process_mode; /* what push library is currently doing */ + int cur_palette; /* current push library palette index */ + +#endif /* PNG_PROGRESSIVE_READ_SUPPORTED */ + +#if defined(__TURBOC__) && !defined(_Windows) && !defined(__FLAT__) +/* For the Borland special 64K segment handler */ + png_bytepp offset_table_ptr; + png_bytep offset_table; + png_uint_16 offset_table_number; + png_uint_16 offset_table_count; + png_uint_16 offset_table_count_free; +#endif + +#ifdef PNG_READ_QUANTIZE_SUPPORTED + png_bytep palette_lookup; /* lookup table for quantizing */ + png_bytep quantize_index; /* index translation for palette files */ +#endif + +#ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED + png_byte heuristic_method; /* heuristic for row filter selection */ + png_byte num_prev_filters; /* number of weights for previous rows */ + png_bytep prev_filters; /* filter type(s) of previous row(s) */ + png_uint_16p filter_weights; /* weight(s) for previous line(s) */ + png_uint_16p inv_filter_weights; /* 1/weight(s) for previous line(s) */ + png_uint_16p filter_costs; /* relative filter calculation cost */ + png_uint_16p inv_filter_costs; /* 1/relative filter calculation cost */ +#endif + + /* Options */ +#ifdef PNG_SET_OPTION_SUPPORTED + png_byte options; /* On/off state (up to 4 options) */ +#endif + +#if PNG_LIBPNG_VER < 10700 +/* To do: remove this from libpng-1.7 */ +#ifdef PNG_TIME_RFC1123_SUPPORTED + char time_buffer[29]; /* String to hold RFC 1123 time text */ +#endif +#endif + +/* New members added in libpng-1.0.6 */ + + png_uint_32 free_me; /* flags items libpng is responsible for freeing */ + +#ifdef PNG_USER_CHUNKS_SUPPORTED + png_voidp user_chunk_ptr; +#ifdef PNG_READ_USER_CHUNKS_SUPPORTED + png_user_chunk_ptr read_user_chunk_fn; /* user read chunk handler */ +#endif +#endif + +#ifdef PNG_SET_UNKNOWN_CHUNKS_SUPPORTED + int unknown_default; /* As PNG_HANDLE_* */ + unsigned int num_chunk_list; /* Number of entries in the list */ + png_bytep chunk_list; /* List of png_byte[5]; the textual chunk name + * followed by a PNG_HANDLE_* byte */ +#endif + +/* New members added in libpng-1.0.3 */ +#ifdef PNG_READ_RGB_TO_GRAY_SUPPORTED + png_byte rgb_to_gray_status; + /* Added in libpng 1.5.5 to record setting of coefficients: */ + png_byte rgb_to_gray_coefficients_set; + /* These were changed from png_byte in libpng-1.0.6 */ + png_uint_16 rgb_to_gray_red_coeff; + png_uint_16 rgb_to_gray_green_coeff; + /* deleted in 1.5.5: rgb_to_gray_blue_coeff; */ +#endif + +/* New member added in libpng-1.0.4 (renamed in 1.0.9) */ +#if defined(PNG_MNG_FEATURES_SUPPORTED) +/* Changed from png_byte to png_uint_32 at version 1.2.0 */ + png_uint_32 mng_features_permitted; +#endif + +/* New member added in libpng-1.0.9, ifdef'ed out in 1.0.12, enabled in 1.2.0 */ +#ifdef PNG_MNG_FEATURES_SUPPORTED + png_byte filter_type; +#endif + +/* New members added in libpng-1.2.0 */ + +/* New members added in libpng-1.0.2 but first enabled by default in 1.2.0 */ +#ifdef PNG_USER_MEM_SUPPORTED + png_voidp mem_ptr; /* user supplied struct for mem functions */ + png_malloc_ptr malloc_fn; /* function for allocating memory */ + png_free_ptr free_fn; /* function for freeing memory */ +#endif + +/* New member added in libpng-1.0.13 and 1.2.0 */ + png_bytep big_row_buf; /* buffer to save current (unfiltered) row */ + +#ifdef PNG_READ_QUANTIZE_SUPPORTED +/* The following three members were added at version 1.0.14 and 1.2.4 */ + png_bytep quantize_sort; /* working sort array */ + png_bytep index_to_palette; /* where the original index currently is + in the palette */ + png_bytep palette_to_index; /* which original index points to this + palette color */ +#endif + +/* New members added in libpng-1.0.16 and 1.2.6 */ + png_byte compression_type; + +#ifdef PNG_USER_LIMITS_SUPPORTED + png_uint_32 user_width_max; + png_uint_32 user_height_max; + + /* Added in libpng-1.4.0: Total number of sPLT, text, and unknown + * chunks that can be stored (0 means unlimited). + */ + png_uint_32 user_chunk_cache_max; + + /* Total memory that a zTXt, sPLT, iTXt, iCCP, or unknown chunk + * can occupy when decompressed. 0 means unlimited. + */ + png_alloc_size_t user_chunk_malloc_max; +#endif + +/* New member added in libpng-1.0.25 and 1.2.17 */ +#ifdef PNG_READ_UNKNOWN_CHUNKS_SUPPORTED + /* Temporary storage for unknown chunk that the library doesn't recognize, + * used while reading the chunk. + */ + png_unknown_chunk unknown_chunk; +#endif + +/* New member added in libpng-1.2.26 */ + png_size_t old_big_row_buf_size; + +#ifdef PNG_READ_SUPPORTED +/* New member added in libpng-1.2.30 */ + png_bytep read_buffer; /* buffer for reading chunk data */ + png_alloc_size_t read_buffer_size; /* current size of the buffer */ +#endif +#ifdef PNG_SEQUENTIAL_READ_SUPPORTED + uInt IDAT_read_size; /* limit on read buffer size for IDAT */ +#endif + +#ifdef PNG_IO_STATE_SUPPORTED +/* New member added in libpng-1.4.0 */ + png_uint_32 io_state; +#endif + +/* New member added in libpng-1.5.6 */ + png_bytep big_prev_row; + +/* New member added in libpng-1.5.7 */ + void (*read_filter[PNG_FILTER_VALUE_LAST-1])(png_row_infop row_info, + png_bytep row, png_const_bytep prev_row); + +#ifdef PNG_READ_SUPPORTED +#if defined(PNG_COLORSPACE_SUPPORTED) || defined(PNG_GAMMA_SUPPORTED) + png_colorspace colorspace; +#endif +#endif +}; +#endif /* PNGSTRUCT_H */ diff --git a/Minecraft.Client/Windows64/4JLibs/inc/Render/libpng/pngtest.c b/Minecraft.Client/Windows64/4JLibs/inc/Render/libpng/pngtest.c new file mode 100644 index 000000000..144f2db35 --- /dev/null +++ b/Minecraft.Client/Windows64/4JLibs/inc/Render/libpng/pngtest.c @@ -0,0 +1,1971 @@ + +/* pngtest.c - a simple test program to test libpng + * + * Last changed in libpng 1.6.2 [April 25, 2013] + * Copyright (c) 1998-2013 Glenn Randers-Pehrson + * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) + * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) + * + * This code is released under the libpng license. + * For conditions of distribution and use, see the disclaimer + * and license in png.h + * + * This program reads in a PNG image, writes it out again, and then + * compares the two files. If the files are identical, this shows that + * the basic chunk handling, filtering, and (de)compression code is working + * properly. It does not currently test all of the transforms, although + * it probably should. + * + * The program will report "FAIL" in certain legitimate cases: + * 1) when the compression level or filter selection method is changed. + * 2) when the maximum IDAT size (PNG_ZBUF_SIZE in pngconf.h) is not 8192. + * 3) unknown unsafe-to-copy ancillary chunks or unknown critical chunks + * exist in the input file. + * 4) others not listed here... + * In these cases, it is best to check with another tool such as "pngcheck" + * to see what the differences between the two files are. + * + * If a filename is given on the command-line, then this file is used + * for the input, rather than the default "pngtest.png". This allows + * testing a wide variety of files easily. You can also test a number + * of files at once by typing "pngtest -m file1.png file2.png ..." + */ + +#define _POSIX_SOURCE 1 + +#include +#include +#include + +/* Defined so I can write to a file on gui/windowing platforms */ +/* #define STDERR stderr */ +#define STDERR stdout /* For DOS */ + +#include "png.h" + +/* Known chunks that exist in pngtest.png must be supported or pngtest will fail + * simply as a result of re-ordering them. This may be fixed in 1.7 + */ +#if defined PNG_READ_SUPPORTED && /* else nothing can be done */\ + defined PNG_READ_bKGD_SUPPORTED &&\ + defined PNG_READ_cHRM_SUPPORTED &&\ + defined PNG_READ_gAMA_SUPPORTED &&\ + defined PNG_READ_oFFs_SUPPORTED &&\ + defined PNG_READ_pCAL_SUPPORTED &&\ + defined PNG_READ_pHYs_SUPPORTED &&\ + defined PNG_READ_sBIT_SUPPORTED &&\ + defined PNG_READ_sCAL_SUPPORTED &&\ + defined PNG_READ_sRGB_SUPPORTED &&\ + defined PNG_READ_tEXt_SUPPORTED &&\ + defined PNG_READ_tIME_SUPPORTED &&\ + defined PNG_READ_zTXt_SUPPORTED + +#include "zlib.h" +/* Copied from pngpriv.h but only used in error messages below. */ +#ifndef PNG_ZBUF_SIZE +# define PNG_ZBUF_SIZE 8192 +#endif +#define FCLOSE(file) fclose(file) + +#ifndef PNG_STDIO_SUPPORTED +typedef FILE * png_FILE_p; +#endif + +/* Makes pngtest verbose so we can find problems. */ +#ifndef PNG_DEBUG +# define PNG_DEBUG 0 +#endif + +#if PNG_DEBUG > 1 +# define pngtest_debug(m) ((void)fprintf(stderr, m "\n")) +# define pngtest_debug1(m,p1) ((void)fprintf(stderr, m "\n", p1)) +# define pngtest_debug2(m,p1,p2) ((void)fprintf(stderr, m "\n", p1, p2)) +#else +# define pngtest_debug(m) ((void)0) +# define pngtest_debug1(m,p1) ((void)0) +# define pngtest_debug2(m,p1,p2) ((void)0) +#endif + +#if !PNG_DEBUG +# define SINGLE_ROWBUF_ALLOC /* Makes buffer overruns easier to nail */ +#endif + +/* Turn on CPU timing +#define PNGTEST_TIMING +*/ + +#ifndef PNG_FLOATING_POINT_SUPPORTED +#undef PNGTEST_TIMING +#endif + +#ifdef PNGTEST_TIMING +static float t_start, t_stop, t_decode, t_encode, t_misc; +#include +#endif + +#ifdef PNG_TIME_RFC1123_SUPPORTED +#define PNG_tIME_STRING_LENGTH 29 +static int tIME_chunk_present = 0; +static char tIME_string[PNG_tIME_STRING_LENGTH] = "tIME chunk is not present"; +#endif + +static int verbose = 0; +static int strict = 0; +static int relaxed = 0; +static int unsupported_chunks = 0; /* chunk unsupported by libpng in input */ +static int error_count = 0; /* count calls to png_error */ +static int warning_count = 0; /* count calls to png_warning */ + +#ifdef __TURBOC__ +#include +#endif + +/* Define png_jmpbuf() in case we are using a pre-1.0.6 version of libpng */ +#ifndef png_jmpbuf +# define png_jmpbuf(png_ptr) png_ptr->jmpbuf +#endif + +/* Defines for unknown chunk handling if required. */ +#ifndef PNG_HANDLE_CHUNK_ALWAYS +# define PNG_HANDLE_CHUNK_ALWAYS 3 +#endif +#ifndef PNG_HANDLE_CHUNK_IF_SAFE +# define PNG_HANDLE_CHUNK_IF_SAFE 2 +#endif + +/* Utility to save typing/errors, the argument must be a name */ +#define MEMZERO(var) ((void)memset(&var, 0, sizeof var)) + +/* Example of using row callbacks to make a simple progress meter */ +static int status_pass = 1; +static int status_dots_requested = 0; +static int status_dots = 1; + +static void PNGCBAPI +read_row_callback(png_structp png_ptr, png_uint_32 row_number, int pass) +{ + if (png_ptr == NULL || row_number > PNG_UINT_31_MAX) + return; + + if (status_pass != pass) + { + fprintf(stdout, "\n Pass %d: ", pass); + status_pass = pass; + status_dots = 31; + } + + status_dots--; + + if (status_dots == 0) + { + fprintf(stdout, "\n "); + status_dots=30; + } + + fprintf(stdout, "r"); +} + +#ifdef PNG_WRITE_SUPPORTED +static void PNGCBAPI +write_row_callback(png_structp png_ptr, png_uint_32 row_number, int pass) +{ + if (png_ptr == NULL || row_number > PNG_UINT_31_MAX || pass > 7) + return; + + fprintf(stdout, "w"); +} +#endif + + +#ifdef PNG_READ_USER_TRANSFORM_SUPPORTED +/* Example of using user transform callback (we don't transform anything, + * but merely examine the row filters. We set this to 256 rather than + * 5 in case illegal filter values are present.) + */ +static png_uint_32 filters_used[256]; +static void PNGCBAPI +count_filters(png_structp png_ptr, png_row_infop row_info, png_bytep data) +{ + if (png_ptr != NULL && row_info != NULL) + ++filters_used[*(data - 1)]; +} +#endif + +#ifdef PNG_WRITE_USER_TRANSFORM_SUPPORTED +/* Example of using user transform callback (we don't transform anything, + * but merely count the zero samples) + */ + +static png_uint_32 zero_samples; + +static void PNGCBAPI +count_zero_samples(png_structp png_ptr, png_row_infop row_info, png_bytep data) +{ + png_bytep dp = data; + if (png_ptr == NULL) + return; + + /* Contents of row_info: + * png_uint_32 width width of row + * png_uint_32 rowbytes number of bytes in row + * png_byte color_type color type of pixels + * png_byte bit_depth bit depth of samples + * png_byte channels number of channels (1-4) + * png_byte pixel_depth bits per pixel (depth*channels) + */ + + /* Counts the number of zero samples (or zero pixels if color_type is 3 */ + + if (row_info->color_type == 0 || row_info->color_type == 3) + { + int pos = 0; + png_uint_32 n, nstop; + + for (n = 0, nstop=row_info->width; nbit_depth == 1) + { + if (((*dp << pos++ ) & 0x80) == 0) + zero_samples++; + + if (pos == 8) + { + pos = 0; + dp++; + } + } + + if (row_info->bit_depth == 2) + { + if (((*dp << (pos+=2)) & 0xc0) == 0) + zero_samples++; + + if (pos == 8) + { + pos = 0; + dp++; + } + } + + if (row_info->bit_depth == 4) + { + if (((*dp << (pos+=4)) & 0xf0) == 0) + zero_samples++; + + if (pos == 8) + { + pos = 0; + dp++; + } + } + + if (row_info->bit_depth == 8) + if (*dp++ == 0) + zero_samples++; + + if (row_info->bit_depth == 16) + { + if ((*dp | *(dp+1)) == 0) + zero_samples++; + dp+=2; + } + } + } + else /* Other color types */ + { + png_uint_32 n, nstop; + int channel; + int color_channels = row_info->channels; + if (row_info->color_type > 3)color_channels--; + + for (n = 0, nstop=row_info->width; nbit_depth == 8) + if (*dp++ == 0) + zero_samples++; + + if (row_info->bit_depth == 16) + { + if ((*dp | *(dp+1)) == 0) + zero_samples++; + + dp+=2; + } + } + if (row_info->color_type > 3) + { + dp++; + if (row_info->bit_depth == 16) + dp++; + } + } + } +} +#endif /* PNG_WRITE_USER_TRANSFORM_SUPPORTED */ + +#ifndef PNG_STDIO_SUPPORTED +/* START of code to validate stdio-free compilation */ +/* These copies of the default read/write functions come from pngrio.c and + * pngwio.c. They allow "don't include stdio" testing of the library. + * This is the function that does the actual reading of data. If you are + * not reading from a standard C stream, you should create a replacement + * read_data function and use it at run time with png_set_read_fn(), rather + * than changing the library. + */ + +#ifdef PNG_IO_STATE_SUPPORTED +void +pngtest_check_io_state(png_structp png_ptr, png_size_t data_length, + png_uint_32 io_op); +void +pngtest_check_io_state(png_structp png_ptr, png_size_t data_length, + png_uint_32 io_op) +{ + png_uint_32 io_state = png_get_io_state(png_ptr); + int err = 0; + + /* Check if the current operation (reading / writing) is as expected. */ + if ((io_state & PNG_IO_MASK_OP) != io_op) + png_error(png_ptr, "Incorrect operation in I/O state"); + + /* Check if the buffer size specific to the current location + * (file signature / header / data / crc) is as expected. + */ + switch (io_state & PNG_IO_MASK_LOC) + { + case PNG_IO_SIGNATURE: + if (data_length > 8) + err = 1; + break; + case PNG_IO_CHUNK_HDR: + if (data_length != 8) + err = 1; + break; + case PNG_IO_CHUNK_DATA: + break; /* no restrictions here */ + case PNG_IO_CHUNK_CRC: + if (data_length != 4) + err = 1; + break; + default: + err = 1; /* uninitialized */ + } + if (err) + png_error(png_ptr, "Bad I/O state or buffer size"); +} +#endif + +static void PNGCBAPI +pngtest_read_data(png_structp png_ptr, png_bytep data, png_size_t length) +{ + png_size_t check = 0; + png_voidp io_ptr; + + /* fread() returns 0 on error, so it is OK to store this in a png_size_t + * instead of an int, which is what fread() actually returns. + */ + io_ptr = png_get_io_ptr(png_ptr); + if (io_ptr != NULL) + { + check = fread(data, 1, length, (png_FILE_p)io_ptr); + } + + if (check != length) + { + png_error(png_ptr, "Read Error"); + } + +#ifdef PNG_IO_STATE_SUPPORTED + pngtest_check_io_state(png_ptr, length, PNG_IO_READING); +#endif +} + +#ifdef PNG_WRITE_FLUSH_SUPPORTED +static void PNGCBAPI +pngtest_flush(png_structp png_ptr) +{ + /* Do nothing; fflush() is said to be just a waste of energy. */ + PNG_UNUSED(png_ptr) /* Stifle compiler warning */ +} +#endif + +/* This is the function that does the actual writing of data. If you are + * not writing to a standard C stream, you should create a replacement + * write_data function and use it at run time with png_set_write_fn(), rather + * than changing the library. + */ +static void PNGCBAPI +pngtest_write_data(png_structp png_ptr, png_bytep data, png_size_t length) +{ + png_size_t check; + + check = fwrite(data, 1, length, (png_FILE_p)png_get_io_ptr(png_ptr)); + + if (check != length) + { + png_error(png_ptr, "Write Error"); + } + +#ifdef PNG_IO_STATE_SUPPORTED + pngtest_check_io_state(png_ptr, length, PNG_IO_WRITING); +#endif +} +#endif /* !PNG_STDIO_SUPPORTED */ + +/* This function is called when there is a warning, but the library thinks + * it can continue anyway. Replacement functions don't have to do anything + * here if you don't want to. In the default configuration, png_ptr is + * not used, but it is passed in case it may be useful. + */ +typedef struct +{ + PNG_CONST char *file_name; +} pngtest_error_parameters; + +static void PNGCBAPI +pngtest_warning(png_structp png_ptr, png_const_charp message) +{ + PNG_CONST char *name = "UNKNOWN (ERROR!)"; + pngtest_error_parameters *test = + (pngtest_error_parameters*)png_get_error_ptr(png_ptr); + + ++warning_count; + + if (test != NULL && test->file_name != NULL) + name = test->file_name; + + fprintf(STDERR, "%s: libpng warning: %s\n", name, message); +} + +/* This is the default error handling function. Note that replacements for + * this function MUST NOT RETURN, or the program will likely crash. This + * function is used by default, or if the program supplies NULL for the + * error function pointer in png_set_error_fn(). + */ +static void PNGCBAPI +pngtest_error(png_structp png_ptr, png_const_charp message) +{ + ++error_count; + + pngtest_warning(png_ptr, message); + /* We can return because png_error calls the default handler, which is + * actually OK in this case. + */ +} + +/* END of code to validate stdio-free compilation */ + +/* START of code to validate memory allocation and deallocation */ +#if defined(PNG_USER_MEM_SUPPORTED) && PNG_DEBUG + +/* Allocate memory. For reasonable files, size should never exceed + * 64K. However, zlib may allocate more then 64K if you don't tell + * it not to. See zconf.h and png.h for more information. zlib does + * need to allocate exactly 64K, so whatever you call here must + * have the ability to do that. + * + * This piece of code can be compiled to validate max 64K allocations + * by setting MAXSEG_64K in zlib zconf.h *or* PNG_MAX_MALLOC_64K. + */ +typedef struct memory_information +{ + png_alloc_size_t size; + png_voidp pointer; + struct memory_information *next; +} memory_information; +typedef memory_information *memory_infop; + +static memory_infop pinformation = NULL; +static int current_allocation = 0; +static int maximum_allocation = 0; +static int total_allocation = 0; +static int num_allocations = 0; + +png_voidp PNGCBAPI png_debug_malloc PNGARG((png_structp png_ptr, + png_alloc_size_t size)); +void PNGCBAPI png_debug_free PNGARG((png_structp png_ptr, png_voidp ptr)); + +png_voidp +PNGCBAPI png_debug_malloc(png_structp png_ptr, png_alloc_size_t size) +{ + + /* png_malloc has already tested for NULL; png_create_struct calls + * png_debug_malloc directly, with png_ptr == NULL which is OK + */ + + if (size == 0) + return (NULL); + + /* This calls the library allocator twice, once to get the requested + buffer and once to get a new free list entry. */ + { + /* Disable malloc_fn and free_fn */ + memory_infop pinfo; + png_set_mem_fn(png_ptr, NULL, NULL, NULL); + pinfo = (memory_infop)png_malloc(png_ptr, + (sizeof *pinfo)); + pinfo->size = size; + current_allocation += size; + total_allocation += size; + num_allocations ++; + + if (current_allocation > maximum_allocation) + maximum_allocation = current_allocation; + + pinfo->pointer = png_malloc(png_ptr, size); + /* Restore malloc_fn and free_fn */ + + png_set_mem_fn(png_ptr, + NULL, png_debug_malloc, png_debug_free); + + if (size != 0 && pinfo->pointer == NULL) + { + current_allocation -= size; + total_allocation -= size; + png_error(png_ptr, + "out of memory in pngtest->png_debug_malloc"); + } + + pinfo->next = pinformation; + pinformation = pinfo; + /* Make sure the caller isn't assuming zeroed memory. */ + memset(pinfo->pointer, 0xdd, pinfo->size); + + if (verbose) + printf("png_malloc %lu bytes at %p\n", (unsigned long)size, + pinfo->pointer); + + return (png_voidp)(pinfo->pointer); + } +} + +/* Free a pointer. It is removed from the list at the same time. */ +void PNGCBAPI +png_debug_free(png_structp png_ptr, png_voidp ptr) +{ + if (png_ptr == NULL) + fprintf(STDERR, "NULL pointer to png_debug_free.\n"); + + if (ptr == 0) + { +#if 0 /* This happens all the time. */ + fprintf(STDERR, "WARNING: freeing NULL pointer\n"); +#endif + return; + } + + /* Unlink the element from the list. */ + { + memory_infop *ppinfo = &pinformation; + + for (;;) + { + memory_infop pinfo = *ppinfo; + + if (pinfo->pointer == ptr) + { + *ppinfo = pinfo->next; + current_allocation -= pinfo->size; + if (current_allocation < 0) + fprintf(STDERR, "Duplicate free of memory\n"); + /* We must free the list element too, but first kill + the memory that is to be freed. */ + memset(ptr, 0x55, pinfo->size); + png_free_default(png_ptr, pinfo); + pinfo = NULL; + break; + } + + if (pinfo->next == NULL) + { + fprintf(STDERR, "Pointer %x not found\n", (unsigned int)ptr); + break; + } + + ppinfo = &pinfo->next; + } + } + + /* Finally free the data. */ + if (verbose) + printf("Freeing %p\n", ptr); + + png_free_default(png_ptr, ptr); + ptr = NULL; +} +#endif /* PNG_USER_MEM_SUPPORTED && PNG_DEBUG */ +/* END of code to test memory allocation/deallocation */ + + +#ifdef PNG_READ_USER_CHUNKS_SUPPORTED +/* Demonstration of user chunk support of the sTER and vpAg chunks */ + +/* (sTER is a public chunk not yet known by libpng. vpAg is a private +chunk used in ImageMagick to store "virtual page" size). */ + +static struct user_chunk_data +{ + png_const_infop info_ptr; + png_uint_32 vpAg_width, vpAg_height; + png_byte vpAg_units; + png_byte sTER_mode; + int location[2]; +} +user_chunk_data; + +/* Used for location and order; zero means nothing. */ +#define have_sTER 0x01 +#define have_vpAg 0x02 +#define before_PLTE 0x10 +#define before_IDAT 0x20 +#define after_IDAT 0x40 + +static void +init_callback_info(png_const_infop info_ptr) +{ + MEMZERO(user_chunk_data); + user_chunk_data.info_ptr = info_ptr; +} + +static int +set_location(png_structp png_ptr, struct user_chunk_data *data, int what) +{ + int location; + + if ((data->location[0] & what) || (data->location[1] & what)) + return 0; /* already have one of these */ + + /* Find where we are (the code below zeros info_ptr to indicate that the + * chunks before the first IDAT have been read.) + */ + if (data->info_ptr == NULL) /* after IDAT */ + location = what | after_IDAT; + + else if (png_get_valid(png_ptr, data->info_ptr, PNG_INFO_PLTE)) + location = what | before_IDAT; + + else + location = what | before_PLTE; + + if (data->location[0] == 0) + data->location[0] = location; + + else + data->location[1] = location; + + return 1; /* handled */ +} + +static int PNGCBAPI read_user_chunk_callback(png_struct *png_ptr, + png_unknown_chunkp chunk) +{ + struct user_chunk_data *my_user_chunk_data = + (struct user_chunk_data*)png_get_user_chunk_ptr(png_ptr); + + if (my_user_chunk_data == NULL) + png_error(png_ptr, "lost user chunk pointer"); + + /* Return one of the following: + * return (-n); chunk had an error + * return (0); did not recognize + * return (n); success + * + * The unknown chunk structure contains the chunk data: + * png_byte name[5]; + * png_byte *data; + * png_size_t size; + * + * Note that libpng has already taken care of the CRC handling. + */ + + if (chunk->name[0] == 115 && chunk->name[1] == 84 && /* s T */ + chunk->name[2] == 69 && chunk->name[3] == 82) /* E R */ + { + /* Found sTER chunk */ + if (chunk->size != 1) + return (-1); /* Error return */ + + if (chunk->data[0] != 0 && chunk->data[0] != 1) + return (-1); /* Invalid mode */ + + if (set_location(png_ptr, my_user_chunk_data, have_sTER)) + { + my_user_chunk_data->sTER_mode=chunk->data[0]; + return (1); + } + + else + return (0); /* duplicate sTER - give it to libpng */ + } + + if (chunk->name[0] != 118 || chunk->name[1] != 112 || /* v p */ + chunk->name[2] != 65 || chunk->name[3] != 103) /* A g */ + return (0); /* Did not recognize */ + + /* Found ImageMagick vpAg chunk */ + + if (chunk->size != 9) + return (-1); /* Error return */ + + if (!set_location(png_ptr, my_user_chunk_data, have_vpAg)) + return (0); /* duplicate vpAg */ + + my_user_chunk_data->vpAg_width = png_get_uint_31(png_ptr, chunk->data); + my_user_chunk_data->vpAg_height = png_get_uint_31(png_ptr, chunk->data + 4); + my_user_chunk_data->vpAg_units = chunk->data[8]; + + return (1); +} + +#ifdef PNG_WRITE_SUPPORTED +static void +write_sTER_chunk(png_structp write_ptr) +{ + png_byte png_sTER[5] = {115, 84, 69, 82, '\0'}; + + if (verbose) + fprintf(STDERR, "\n stereo mode = %d\n", user_chunk_data.sTER_mode); + + png_write_chunk(write_ptr, png_sTER, &user_chunk_data.sTER_mode, 1); +} + +static void +write_vpAg_chunk(png_structp write_ptr) +{ + png_byte png_vpAg[5] = {118, 112, 65, 103, '\0'}; + + png_byte vpag_chunk_data[9]; + + if (verbose) + fprintf(STDERR, " vpAg = %lu x %lu, units = %d\n", + (unsigned long)user_chunk_data.vpAg_width, + (unsigned long)user_chunk_data.vpAg_height, + user_chunk_data.vpAg_units); + + png_save_uint_32(vpag_chunk_data, user_chunk_data.vpAg_width); + png_save_uint_32(vpag_chunk_data + 4, user_chunk_data.vpAg_height); + vpag_chunk_data[8] = user_chunk_data.vpAg_units; + png_write_chunk(write_ptr, png_vpAg, vpag_chunk_data, 9); +} + +static void +write_chunks(png_structp write_ptr, int location) +{ + int i; + + /* Notice that this preserves the original chunk order, however chunks + * intercepted by the callback will be written *after* chunks passed to + * libpng. This will actually reverse a pair of sTER chunks or a pair of + * vpAg chunks, resulting in an error later. This is not worth worrying + * about - the chunks should not be duplicated! + */ + for (i=0; i<2; ++i) + { + if (user_chunk_data.location[i] == (location | have_sTER)) + write_sTER_chunk(write_ptr); + + else if (user_chunk_data.location[i] == (location | have_vpAg)) + write_vpAg_chunk(write_ptr); + } +} +#endif /* PNG_WRITE_SUPPORTED */ +#else /* !PNG_READ_USER_CHUNKS_SUPPORTED */ +# define write_chunks(pp,loc) ((void)0) +#endif +/* END of code to demonstrate user chunk support */ + +/* START of code to check that libpng has the required text support; this only + * checks for the write support because if read support is missing the chunk + * will simply not be reported back to pngtest. + */ +#ifdef PNG_TEXT_SUPPORTED +static void +pngtest_check_text_support(png_const_structp png_ptr, png_textp text_ptr, + int num_text) +{ + while (num_text > 0) + { + switch (text_ptr[--num_text].compression) + { + case PNG_TEXT_COMPRESSION_NONE: + break; + + case PNG_TEXT_COMPRESSION_zTXt: +# ifndef PNG_WRITE_zTXt_SUPPORTED + ++unsupported_chunks; +# endif + break; + + case PNG_ITXT_COMPRESSION_NONE: + case PNG_ITXT_COMPRESSION_zTXt: +# ifndef PNG_WRITE_iTXt_SUPPORTED + ++unsupported_chunks; +# endif + break; + + default: + /* This is an error */ + png_error(png_ptr, "invalid text chunk compression field"); + break; + } + } +} +#endif +/* END of code to check that libpng has the required text support */ + +/* Test one file */ +static int +test_one_file(PNG_CONST char *inname, PNG_CONST char *outname) +{ + static png_FILE_p fpin; + static png_FILE_p fpout; /* "static" prevents setjmp corruption */ + pngtest_error_parameters error_parameters; + png_structp read_ptr; + png_infop read_info_ptr, end_info_ptr; +#ifdef PNG_WRITE_SUPPORTED + png_structp write_ptr; + png_infop write_info_ptr; + png_infop write_end_info_ptr; +#else + png_structp write_ptr = NULL; + png_infop write_info_ptr = NULL; + png_infop write_end_info_ptr = NULL; +#endif + png_bytep row_buf; + png_uint_32 y; + png_uint_32 width, height; + int num_pass, pass; + int bit_depth, color_type; + + row_buf = NULL; + error_parameters.file_name = inname; + + if ((fpin = fopen(inname, "rb")) == NULL) + { + fprintf(STDERR, "Could not find input file %s\n", inname); + return (1); + } + + if ((fpout = fopen(outname, "wb")) == NULL) + { + fprintf(STDERR, "Could not open output file %s\n", outname); + FCLOSE(fpin); + return (1); + } + + pngtest_debug("Allocating read and write structures"); +#if defined(PNG_USER_MEM_SUPPORTED) && PNG_DEBUG + read_ptr = + png_create_read_struct_2(PNG_LIBPNG_VER_STRING, NULL, + NULL, NULL, NULL, png_debug_malloc, png_debug_free); +#else + read_ptr = + png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); +#endif + png_set_error_fn(read_ptr, &error_parameters, pngtest_error, + pngtest_warning); + +#ifdef PNG_WRITE_SUPPORTED +#if defined(PNG_USER_MEM_SUPPORTED) && PNG_DEBUG + write_ptr = + png_create_write_struct_2(PNG_LIBPNG_VER_STRING, NULL, + NULL, NULL, NULL, png_debug_malloc, png_debug_free); +#else + write_ptr = + png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); +#endif + png_set_error_fn(write_ptr, &error_parameters, pngtest_error, + pngtest_warning); +#endif + pngtest_debug("Allocating read_info, write_info and end_info structures"); + read_info_ptr = png_create_info_struct(read_ptr); + end_info_ptr = png_create_info_struct(read_ptr); +#ifdef PNG_WRITE_SUPPORTED + write_info_ptr = png_create_info_struct(write_ptr); + write_end_info_ptr = png_create_info_struct(write_ptr); +#endif + +#ifdef PNG_READ_USER_CHUNKS_SUPPORTED + init_callback_info(read_info_ptr); + png_set_read_user_chunk_fn(read_ptr, &user_chunk_data, + read_user_chunk_callback); +#endif + +#ifdef PNG_SETJMP_SUPPORTED + pngtest_debug("Setting jmpbuf for read struct"); + if (setjmp(png_jmpbuf(read_ptr))) + { + fprintf(STDERR, "%s -> %s: libpng read error\n", inname, outname); + png_free(read_ptr, row_buf); + row_buf = NULL; + png_destroy_read_struct(&read_ptr, &read_info_ptr, &end_info_ptr); +#ifdef PNG_WRITE_SUPPORTED + png_destroy_info_struct(write_ptr, &write_end_info_ptr); + png_destroy_write_struct(&write_ptr, &write_info_ptr); +#endif + FCLOSE(fpin); + FCLOSE(fpout); + return (1); + } + +#ifdef PNG_WRITE_SUPPORTED + pngtest_debug("Setting jmpbuf for write struct"); + + if (setjmp(png_jmpbuf(write_ptr))) + { + fprintf(STDERR, "%s -> %s: libpng write error\n", inname, outname); + png_destroy_read_struct(&read_ptr, &read_info_ptr, &end_info_ptr); + png_destroy_info_struct(write_ptr, &write_end_info_ptr); +#ifdef PNG_WRITE_SUPPORTED + png_destroy_write_struct(&write_ptr, &write_info_ptr); +#endif + FCLOSE(fpin); + FCLOSE(fpout); + return (1); + } +#endif +#endif + + if (strict) + { + /* Treat png_benign_error() as errors on read */ + png_set_benign_errors(read_ptr, 0); + +#ifdef PNG_WRITE_SUPPORTED + /* Treat them as errors on write */ + png_set_benign_errors(write_ptr, 0); +#endif + + /* if strict is not set, then app warnings and errors are treated as + * warnings in release builds, but not in unstable builds; this can be + * changed with '--relaxed'. + */ + } + + else if (relaxed) + { + /* Allow application (pngtest) errors and warnings to pass */ + png_set_benign_errors(read_ptr, 1); + +#ifdef PNG_WRITE_SUPPORTED + png_set_benign_errors(write_ptr, 1); +#endif + } + + pngtest_debug("Initializing input and output streams"); +#ifdef PNG_STDIO_SUPPORTED + png_init_io(read_ptr, fpin); +# ifdef PNG_WRITE_SUPPORTED + png_init_io(write_ptr, fpout); +# endif +#else + png_set_read_fn(read_ptr, (png_voidp)fpin, pngtest_read_data); +# ifdef PNG_WRITE_SUPPORTED + png_set_write_fn(write_ptr, (png_voidp)fpout, pngtest_write_data, +# ifdef PNG_WRITE_FLUSH_SUPPORTED + pngtest_flush); +# else + NULL); +# endif +# endif +#endif + + if (status_dots_requested == 1) + { +#ifdef PNG_WRITE_SUPPORTED + png_set_write_status_fn(write_ptr, write_row_callback); +#endif + png_set_read_status_fn(read_ptr, read_row_callback); + } + + else + { +#ifdef PNG_WRITE_SUPPORTED + png_set_write_status_fn(write_ptr, NULL); +#endif + png_set_read_status_fn(read_ptr, NULL); + } + +#ifdef PNG_READ_USER_TRANSFORM_SUPPORTED + { + int i; + + for (i = 0; i<256; i++) + filters_used[i] = 0; + + png_set_read_user_transform_fn(read_ptr, count_filters); + } +#endif +#ifdef PNG_WRITE_USER_TRANSFORM_SUPPORTED + zero_samples = 0; + png_set_write_user_transform_fn(write_ptr, count_zero_samples); +#endif + +#ifdef PNG_SET_UNKNOWN_CHUNKS_SUPPORTED + /* Preserve all the unknown chunks, if possible. If this is disabled then, + * even if the png_{get,set}_unknown_chunks stuff is enabled, we can't use + * libpng to *save* the unknown chunks on read (because we can't switch the + * save option on!) + * + * Notice that if SET_UNKNOWN_CHUNKS is *not* supported read will discard all + * unknown chunks and write will write them all. + */ +#ifdef PNG_SAVE_UNKNOWN_CHUNKS_SUPPORTED + png_set_keep_unknown_chunks(read_ptr, PNG_HANDLE_CHUNK_ALWAYS, + NULL, 0); +#endif +#ifdef PNG_WRITE_UNKNOWN_CHUNKS_SUPPORTED + png_set_keep_unknown_chunks(write_ptr, PNG_HANDLE_CHUNK_ALWAYS, + NULL, 0); +#endif +#endif + + pngtest_debug("Reading info struct"); + png_read_info(read_ptr, read_info_ptr); + +#ifdef PNG_READ_USER_CHUNKS_SUPPORTED + /* This is a bit of a hack; there is no obvious way in the callback function + * to determine that the chunks before the first IDAT have been read, so + * remove the info_ptr (which is only used to determine position relative to + * PLTE) here to indicate that we are after the IDAT. + */ + user_chunk_data.info_ptr = NULL; +#endif + + pngtest_debug("Transferring info struct"); + { + int interlace_type, compression_type, filter_type; + + if (png_get_IHDR(read_ptr, read_info_ptr, &width, &height, &bit_depth, + &color_type, &interlace_type, &compression_type, &filter_type)) + { + png_set_IHDR(write_ptr, write_info_ptr, width, height, bit_depth, +#ifdef PNG_WRITE_INTERLACING_SUPPORTED + color_type, interlace_type, compression_type, filter_type); +#else + color_type, PNG_INTERLACE_NONE, compression_type, filter_type); +#endif + } + } +#ifdef PNG_FIXED_POINT_SUPPORTED +#ifdef PNG_cHRM_SUPPORTED + { + png_fixed_point white_x, white_y, red_x, red_y, green_x, green_y, blue_x, + blue_y; + + if (png_get_cHRM_fixed(read_ptr, read_info_ptr, &white_x, &white_y, + &red_x, &red_y, &green_x, &green_y, &blue_x, &blue_y)) + { + png_set_cHRM_fixed(write_ptr, write_info_ptr, white_x, white_y, red_x, + red_y, green_x, green_y, blue_x, blue_y); + } + } +#endif +#ifdef PNG_gAMA_SUPPORTED + { + png_fixed_point gamma; + + if (png_get_gAMA_fixed(read_ptr, read_info_ptr, &gamma)) + png_set_gAMA_fixed(write_ptr, write_info_ptr, gamma); + } +#endif +#else /* Use floating point versions */ +#ifdef PNG_FLOATING_POINT_SUPPORTED +#ifdef PNG_cHRM_SUPPORTED + { + double white_x, white_y, red_x, red_y, green_x, green_y, blue_x, + blue_y; + + if (png_get_cHRM(read_ptr, read_info_ptr, &white_x, &white_y, &red_x, + &red_y, &green_x, &green_y, &blue_x, &blue_y)) + { + png_set_cHRM(write_ptr, write_info_ptr, white_x, white_y, red_x, + red_y, green_x, green_y, blue_x, blue_y); + } + } +#endif +#ifdef PNG_gAMA_SUPPORTED + { + double gamma; + + if (png_get_gAMA(read_ptr, read_info_ptr, &gamma)) + png_set_gAMA(write_ptr, write_info_ptr, gamma); + } +#endif +#endif /* Floating point */ +#endif /* Fixed point */ +#ifdef PNG_iCCP_SUPPORTED + { + png_charp name; + png_bytep profile; + png_uint_32 proflen; + int compression_type; + + if (png_get_iCCP(read_ptr, read_info_ptr, &name, &compression_type, + &profile, &proflen)) + { + png_set_iCCP(write_ptr, write_info_ptr, name, compression_type, + profile, proflen); + } + } +#endif +#ifdef PNG_sRGB_SUPPORTED + { + int intent; + + if (png_get_sRGB(read_ptr, read_info_ptr, &intent)) + png_set_sRGB(write_ptr, write_info_ptr, intent); + } +#endif + { + png_colorp palette; + int num_palette; + + if (png_get_PLTE(read_ptr, read_info_ptr, &palette, &num_palette)) + png_set_PLTE(write_ptr, write_info_ptr, palette, num_palette); + } +#ifdef PNG_bKGD_SUPPORTED + { + png_color_16p background; + + if (png_get_bKGD(read_ptr, read_info_ptr, &background)) + { + png_set_bKGD(write_ptr, write_info_ptr, background); + } + } +#endif +#ifdef PNG_hIST_SUPPORTED + { + png_uint_16p hist; + + if (png_get_hIST(read_ptr, read_info_ptr, &hist)) + png_set_hIST(write_ptr, write_info_ptr, hist); + } +#endif +#ifdef PNG_oFFs_SUPPORTED + { + png_int_32 offset_x, offset_y; + int unit_type; + + if (png_get_oFFs(read_ptr, read_info_ptr, &offset_x, &offset_y, + &unit_type)) + { + png_set_oFFs(write_ptr, write_info_ptr, offset_x, offset_y, unit_type); + } + } +#endif +#ifdef PNG_pCAL_SUPPORTED + { + png_charp purpose, units; + png_charpp params; + png_int_32 X0, X1; + int type, nparams; + + if (png_get_pCAL(read_ptr, read_info_ptr, &purpose, &X0, &X1, &type, + &nparams, &units, ¶ms)) + { + png_set_pCAL(write_ptr, write_info_ptr, purpose, X0, X1, type, + nparams, units, params); + } + } +#endif +#ifdef PNG_pHYs_SUPPORTED + { + png_uint_32 res_x, res_y; + int unit_type; + + if (png_get_pHYs(read_ptr, read_info_ptr, &res_x, &res_y, &unit_type)) + png_set_pHYs(write_ptr, write_info_ptr, res_x, res_y, unit_type); + } +#endif +#ifdef PNG_sBIT_SUPPORTED + { + png_color_8p sig_bit; + + if (png_get_sBIT(read_ptr, read_info_ptr, &sig_bit)) + png_set_sBIT(write_ptr, write_info_ptr, sig_bit); + } +#endif +#ifdef PNG_sCAL_SUPPORTED +#if defined(PNG_FLOATING_POINT_SUPPORTED) && \ + defined(PNG_FLOATING_ARITHMETIC_SUPPORTED) + { + int unit; + double scal_width, scal_height; + + if (png_get_sCAL(read_ptr, read_info_ptr, &unit, &scal_width, + &scal_height)) + { + png_set_sCAL(write_ptr, write_info_ptr, unit, scal_width, scal_height); + } + } +#else +#ifdef PNG_FIXED_POINT_SUPPORTED + { + int unit; + png_charp scal_width, scal_height; + + if (png_get_sCAL_s(read_ptr, read_info_ptr, &unit, &scal_width, + &scal_height)) + { + png_set_sCAL_s(write_ptr, write_info_ptr, unit, scal_width, + scal_height); + } + } +#endif +#endif +#endif +#ifdef PNG_TEXT_SUPPORTED + { + png_textp text_ptr; + int num_text; + + if (png_get_text(read_ptr, read_info_ptr, &text_ptr, &num_text) > 0) + { + pngtest_debug1("Handling %d iTXt/tEXt/zTXt chunks", num_text); + + pngtest_check_text_support(read_ptr, text_ptr, num_text); + + if (verbose) + { + int i; + + printf("\n"); + for (i=0; igray > sample_max) || + (color_type == PNG_COLOR_TYPE_RGB && + ((int)trans_color->red > sample_max || + (int)trans_color->green > sample_max || + (int)trans_color->blue > sample_max)))) + png_set_tRNS(write_ptr, write_info_ptr, trans_alpha, num_trans, + trans_color); + } + } +#endif +#ifdef PNG_WRITE_UNKNOWN_CHUNKS_SUPPORTED + { + png_unknown_chunkp unknowns; + int num_unknowns = png_get_unknown_chunks(read_ptr, read_info_ptr, + &unknowns); + + if (num_unknowns) + { + png_set_unknown_chunks(write_ptr, write_info_ptr, unknowns, + num_unknowns); +#if PNG_LIBPNG_VER < 10600 + /* Copy the locations from the read_info_ptr. The automatically + * generated locations in write_end_info_ptr are wrong prior to 1.6.0 + * because they are reset from the write pointer (removed in 1.6.0). + */ + { + int i; + for (i = 0; i < num_unknowns; i++) + png_set_unknown_chunk_location(write_ptr, write_info_ptr, i, + unknowns[i].location); + } +#endif + } + } +#endif + +#ifdef PNG_WRITE_SUPPORTED + pngtest_debug("Writing info struct"); + + /* Write the info in two steps so that if we write the 'unknown' chunks here + * they go to the correct place. + */ + png_write_info_before_PLTE(write_ptr, write_info_ptr); + + write_chunks(write_ptr, before_PLTE); /* before PLTE */ + + png_write_info(write_ptr, write_info_ptr); + + write_chunks(write_ptr, before_IDAT); /* after PLTE */ +#endif + +#ifdef SINGLE_ROWBUF_ALLOC + pngtest_debug("Allocating row buffer..."); + row_buf = (png_bytep)png_malloc(read_ptr, + png_get_rowbytes(read_ptr, read_info_ptr)); + + pngtest_debug1("\t0x%08lx", (unsigned long)row_buf); +#endif /* SINGLE_ROWBUF_ALLOC */ + pngtest_debug("Writing row data"); + +#if defined(PNG_READ_INTERLACING_SUPPORTED) || \ + defined(PNG_WRITE_INTERLACING_SUPPORTED) + num_pass = png_set_interlace_handling(read_ptr); +# ifdef PNG_WRITE_SUPPORTED + png_set_interlace_handling(write_ptr); +# endif +#else + num_pass = 1; +#endif + +#ifdef PNGTEST_TIMING + t_stop = (float)clock(); + t_misc += (t_stop - t_start); + t_start = t_stop; +#endif + for (pass = 0; pass < num_pass; pass++) + { + pngtest_debug1("Writing row data for pass %d", pass); + for (y = 0; y < height; y++) + { +#ifndef SINGLE_ROWBUF_ALLOC + pngtest_debug2("Allocating row buffer (pass %d, y = %u)...", pass, y); + row_buf = (png_bytep)png_malloc(read_ptr, + png_get_rowbytes(read_ptr, read_info_ptr)); + + pngtest_debug2("\t0x%08lx (%u bytes)", (unsigned long)row_buf, + png_get_rowbytes(read_ptr, read_info_ptr)); + +#endif /* !SINGLE_ROWBUF_ALLOC */ + png_read_rows(read_ptr, (png_bytepp)&row_buf, NULL, 1); + +#ifdef PNG_WRITE_SUPPORTED +#ifdef PNGTEST_TIMING + t_stop = (float)clock(); + t_decode += (t_stop - t_start); + t_start = t_stop; +#endif + png_write_rows(write_ptr, (png_bytepp)&row_buf, 1); +#ifdef PNGTEST_TIMING + t_stop = (float)clock(); + t_encode += (t_stop - t_start); + t_start = t_stop; +#endif +#endif /* PNG_WRITE_SUPPORTED */ + +#ifndef SINGLE_ROWBUF_ALLOC + pngtest_debug2("Freeing row buffer (pass %d, y = %u)", pass, y); + png_free(read_ptr, row_buf); + row_buf = NULL; +#endif /* !SINGLE_ROWBUF_ALLOC */ + } + } + +#ifdef PNG_READ_UNKNOWN_CHUNKS_SUPPORTED + png_free_data(read_ptr, read_info_ptr, PNG_FREE_UNKN, -1); +#endif +#ifdef PNG_WRITE_UNKNOWN_CHUNKS_SUPPORTED + png_free_data(write_ptr, write_info_ptr, PNG_FREE_UNKN, -1); +#endif + + pngtest_debug("Reading and writing end_info data"); + + png_read_end(read_ptr, end_info_ptr); +#ifdef PNG_TEXT_SUPPORTED + { + png_textp text_ptr; + int num_text; + + if (png_get_text(read_ptr, end_info_ptr, &text_ptr, &num_text) > 0) + { + pngtest_debug1("Handling %d iTXt/tEXt/zTXt chunks", num_text); + + pngtest_check_text_support(read_ptr, text_ptr, num_text); + + if (verbose) + { + int i; + + printf("\n"); + for (i=0; i 0) + { + /* We don't really expect to get here because of the setjmp handling + * above, but this is safe. + */ + fprintf(STDERR, "\n %s: %d libpng errors found (%d warnings)", + inname, error_count, warning_count); + + if (strict != 0) + return (1); + } + +# ifdef PNG_WRITE_SUPPORTED + /* If there we no write support nothing was written! */ + else if (unsupported_chunks > 0) + { + fprintf(STDERR, "\n %s: unsupported chunks (%d)%s", + inname, unsupported_chunks, strict ? ": IGNORED --strict!" : ""); + } +# endif + + else if (warning_count > 0) + { + fprintf(STDERR, "\n %s: %d libpng warnings found", + inname, warning_count); + + if (strict != 0) + return (1); + } + + pngtest_debug("Opening files for comparison"); + if ((fpin = fopen(inname, "rb")) == NULL) + { + fprintf(STDERR, "Could not find file %s\n", inname); + return (1); + } + + if ((fpout = fopen(outname, "rb")) == NULL) + { + fprintf(STDERR, "Could not find file %s\n", outname); + FCLOSE(fpin); + return (1); + } + +#ifdef PNG_WRITE_SUPPORTED /* else nothing was written */ + { + int wrote_question = 0; + + for (;;) + { + png_size_t num_in, num_out; + char inbuf[256], outbuf[256]; + + + num_in = fread(inbuf, 1, sizeof inbuf, fpin); + num_out = fread(outbuf, 1, sizeof outbuf, fpout); + + if (num_in != num_out) + { + fprintf(STDERR, "\nFiles %s and %s are of a different size\n", + inname, outname); + + if (wrote_question == 0 && unsupported_chunks == 0) + { + fprintf(STDERR, + " Was %s written with the same maximum IDAT chunk size (%d bytes),", + inname, PNG_ZBUF_SIZE); + fprintf(STDERR, + "\n filtering heuristic (libpng default), compression"); + fprintf(STDERR, + " level (zlib default),\n and zlib version (%s)?\n\n", + ZLIB_VERSION); + wrote_question = 1; + } + + FCLOSE(fpin); + FCLOSE(fpout); + + if (strict != 0 && unsupported_chunks == 0) + return (1); + + else + return (0); + } + + if (!num_in) + break; + + if (memcmp(inbuf, outbuf, num_in)) + { + fprintf(STDERR, "\nFiles %s and %s are different\n", inname, + outname); + + if (wrote_question == 0 && unsupported_chunks == 0) + { + fprintf(STDERR, + " Was %s written with the same maximum IDAT chunk size (%d bytes),", + inname, PNG_ZBUF_SIZE); + fprintf(STDERR, + "\n filtering heuristic (libpng default), compression"); + fprintf(STDERR, + " level (zlib default),\n and zlib version (%s)?\n\n", + ZLIB_VERSION); + wrote_question = 1; + } + + FCLOSE(fpin); + FCLOSE(fpout); + + /* NOTE: the unsupported_chunks escape is permitted here because + * unsupported text chunk compression will result in the compression + * mode being changed (to NONE) yet, in the test case, the result + * can be exactly the same size! + */ + if (strict != 0 && unsupported_chunks == 0) + return (1); + + else + return (0); + } + } + } +#endif /* PNG_WRITE_SUPPORTED */ + + FCLOSE(fpin); + FCLOSE(fpout); + + return (0); +} + +/* Input and output filenames */ +#ifdef RISCOS +static PNG_CONST char *inname = "pngtest/png"; +static PNG_CONST char *outname = "pngout/png"; +#else +static PNG_CONST char *inname = "pngtest.png"; +static PNG_CONST char *outname = "pngout.png"; +#endif + +int +main(int argc, char *argv[]) +{ + int multiple = 0; + int ierror = 0; + + fprintf(STDERR, "\n Testing libpng version %s\n", PNG_LIBPNG_VER_STRING); + fprintf(STDERR, " with zlib version %s\n", ZLIB_VERSION); + fprintf(STDERR, "%s", png_get_copyright(NULL)); + /* Show the version of libpng used in building the library */ + fprintf(STDERR, " library (%lu):%s", + (unsigned long)png_access_version_number(), + png_get_header_version(NULL)); + + /* Show the version of libpng used in building the application */ + fprintf(STDERR, " pngtest (%lu):%s", (unsigned long)PNG_LIBPNG_VER, + PNG_HEADER_VERSION_STRING); + + /* Do some consistency checking on the memory allocation settings, I'm + * not sure this matters, but it is nice to know, the first of these + * tests should be impossible because of the way the macros are set + * in pngconf.h + */ +#if defined(MAXSEG_64K) && !defined(PNG_MAX_MALLOC_64K) + fprintf(STDERR, " NOTE: Zlib compiled for max 64k, libpng not\n"); +#endif + /* I think the following can happen. */ +#if !defined(MAXSEG_64K) && defined(PNG_MAX_MALLOC_64K) + fprintf(STDERR, " NOTE: libpng compiled for max 64k, zlib not\n"); +#endif + + if (strcmp(png_libpng_ver, PNG_LIBPNG_VER_STRING)) + { + fprintf(STDERR, + "Warning: versions are different between png.h and png.c\n"); + fprintf(STDERR, " png.h version: %s\n", PNG_LIBPNG_VER_STRING); + fprintf(STDERR, " png.c version: %s\n\n", png_libpng_ver); + ++ierror; + } + + if (argc > 1) + { + if (strcmp(argv[1], "-m") == 0) + { + multiple = 1; + status_dots_requested = 0; + } + + else if (strcmp(argv[1], "-mv") == 0 || + strcmp(argv[1], "-vm") == 0 ) + { + multiple = 1; + verbose = 1; + status_dots_requested = 1; + } + + else if (strcmp(argv[1], "-v") == 0) + { + verbose = 1; + status_dots_requested = 1; + inname = argv[2]; + } + + else if (strcmp(argv[1], "--strict") == 0) + { + status_dots_requested = 0; + verbose = 1; + inname = argv[2]; + strict++; + relaxed = 0; + } + + else if (strcmp(argv[1], "--relaxed") == 0) + { + status_dots_requested = 0; + verbose = 1; + inname = argv[2]; + strict = 0; + relaxed++; + } + + else + { + inname = argv[1]; + status_dots_requested = 0; + } + } + + if (!multiple && argc == 3 + verbose) + outname = argv[2 + verbose]; + + if ((!multiple && argc > 3 + verbose) || (multiple && argc < 2)) + { + fprintf(STDERR, + "usage: %s [infile.png] [outfile.png]\n\t%s -m {infile.png}\n", + argv[0], argv[0]); + fprintf(STDERR, + " reads/writes one PNG file (without -m) or multiple files (-m)\n"); + fprintf(STDERR, + " with -m %s is used as a temporary file\n", outname); + exit(1); + } + + if (multiple) + { + int i; +#if defined(PNG_USER_MEM_SUPPORTED) && PNG_DEBUG + int allocation_now = current_allocation; +#endif + for (i=2; isize, + (unsigned int)pinfo->pointer); + pinfo = pinfo->next; + } + } +#endif + } +#if defined(PNG_USER_MEM_SUPPORTED) && PNG_DEBUG + fprintf(STDERR, " Current memory allocation: %10d bytes\n", + current_allocation); + fprintf(STDERR, " Maximum memory allocation: %10d bytes\n", + maximum_allocation); + fprintf(STDERR, " Total memory allocation: %10d bytes\n", + total_allocation); + fprintf(STDERR, " Number of allocations: %10d\n", + num_allocations); +#endif + } + + else + { + int i; + for (i = 0; i<3; ++i) + { + int kerror; +#if defined(PNG_USER_MEM_SUPPORTED) && PNG_DEBUG + int allocation_now = current_allocation; +#endif + if (i == 1) + status_dots_requested = 1; + + else if (verbose == 0) + status_dots_requested = 0; + + if (i == 0 || verbose == 1 || ierror != 0) + fprintf(STDERR, "\n Testing %s:", inname); + + kerror = test_one_file(inname, outname); + + if (kerror == 0) + { + if (verbose == 1 || i == 2) + { +#ifdef PNG_READ_USER_TRANSFORM_SUPPORTED + int k; +#endif +#ifdef PNG_WRITE_USER_TRANSFORM_SUPPORTED + fprintf(STDERR, "\n PASS (%lu zero samples)\n", + (unsigned long)zero_samples); +#else + fprintf(STDERR, " PASS\n"); +#endif +#ifdef PNG_READ_USER_TRANSFORM_SUPPORTED + for (k = 0; k<256; k++) + if (filters_used[k]) + fprintf(STDERR, " Filter %d was used %lu times\n", + k, (unsigned long)filters_used[k]); +#endif +#ifdef PNG_TIME_RFC1123_SUPPORTED + if (tIME_chunk_present != 0) + fprintf(STDERR, " tIME = %s\n", tIME_string); +#endif /* PNG_TIME_RFC1123_SUPPORTED */ + } + } + + else + { + if (verbose == 0 && i != 2) + fprintf(STDERR, "\n Testing %s:", inname); + + fprintf(STDERR, " FAIL\n"); + ierror += kerror; + } +#if defined(PNG_USER_MEM_SUPPORTED) && PNG_DEBUG + if (allocation_now != current_allocation) + fprintf(STDERR, "MEMORY ERROR: %d bytes lost\n", + current_allocation - allocation_now); + + if (current_allocation != 0) + { + memory_infop pinfo = pinformation; + + fprintf(STDERR, "MEMORY ERROR: %d bytes still allocated\n", + current_allocation); + + while (pinfo != NULL) + { + fprintf(STDERR, " %lu bytes at %x\n", + (unsigned long)pinfo->size, (unsigned int)pinfo->pointer); + pinfo = pinfo->next; + } + } +#endif + } +#if defined(PNG_USER_MEM_SUPPORTED) && PNG_DEBUG + fprintf(STDERR, " Current memory allocation: %10d bytes\n", + current_allocation); + fprintf(STDERR, " Maximum memory allocation: %10d bytes\n", + maximum_allocation); + fprintf(STDERR, " Total memory allocation: %10d bytes\n", + total_allocation); + fprintf(STDERR, " Number of allocations: %10d\n", + num_allocations); +#endif + } + +#ifdef PNGTEST_TIMING + t_stop = (float)clock(); + t_misc += (t_stop - t_start); + t_start = t_stop; + fprintf(STDERR, " CPU time used = %.3f seconds", + (t_misc+t_decode+t_encode)/(float)CLOCKS_PER_SEC); + fprintf(STDERR, " (decoding %.3f,\n", + t_decode/(float)CLOCKS_PER_SEC); + fprintf(STDERR, " encoding %.3f ,", + t_encode/(float)CLOCKS_PER_SEC); + fprintf(STDERR, " other %.3f seconds)\n\n", + t_misc/(float)CLOCKS_PER_SEC); +#endif + + if (ierror == 0) + fprintf(STDERR, " libpng passes test\n"); + + else + fprintf(STDERR, " libpng FAILS test\n"); + + return (int)(ierror != 0); +} +#else +int +main(void) +{ + fprintf(STDERR, + " test ignored because libpng was not built with read support\n"); + /* And skip this test */ + return 77; +} +#endif + +/* Generate a compiler error if there is an old png.h in the search path. */ +typedef png_libpng_version_1_6_2 Your_png_h_is_not_version_1_6_2; diff --git a/Minecraft.Client/Windows64/4JLibs/inc/Render/libpng/pngtrans.c b/Minecraft.Client/Windows64/4JLibs/inc/Render/libpng/pngtrans.c new file mode 100644 index 000000000..8f8bc5d9e --- /dev/null +++ b/Minecraft.Client/Windows64/4JLibs/inc/Render/libpng/pngtrans.c @@ -0,0 +1,841 @@ + +/* pngtrans.c - transforms the data in a row (used by both readers and writers) + * + * Last changed in libpng 1.6.2 [April 25, 2013] + * Copyright (c) 1998-2013 Glenn Randers-Pehrson + * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) + * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) + * + * This code is released under the libpng license. + * For conditions of distribution and use, see the disclaimer + * and license in png.h + */ + +#include "pngpriv.h" + +#if defined(PNG_READ_SUPPORTED) || defined(PNG_WRITE_SUPPORTED) + +#if defined(PNG_READ_BGR_SUPPORTED) || defined(PNG_WRITE_BGR_SUPPORTED) +/* Turn on BGR-to-RGB mapping */ +void PNGAPI +png_set_bgr(png_structrp png_ptr) +{ + png_debug(1, "in png_set_bgr"); + + if (png_ptr == NULL) + return; + + png_ptr->transformations |= PNG_BGR; +} +#endif + +#if defined(PNG_READ_SWAP_SUPPORTED) || defined(PNG_WRITE_SWAP_SUPPORTED) +/* Turn on 16 bit byte swapping */ +void PNGAPI +png_set_swap(png_structrp png_ptr) +{ + png_debug(1, "in png_set_swap"); + + if (png_ptr == NULL) + return; + + if (png_ptr->bit_depth == 16) + png_ptr->transformations |= PNG_SWAP_BYTES; +} +#endif + +#if defined(PNG_READ_PACK_SUPPORTED) || defined(PNG_WRITE_PACK_SUPPORTED) +/* Turn on pixel packing */ +void PNGAPI +png_set_packing(png_structrp png_ptr) +{ + png_debug(1, "in png_set_packing"); + + if (png_ptr == NULL) + return; + + if (png_ptr->bit_depth < 8) + { + png_ptr->transformations |= PNG_PACK; + png_ptr->usr_bit_depth = 8; + } +} +#endif + +#if defined(PNG_READ_PACKSWAP_SUPPORTED)||defined(PNG_WRITE_PACKSWAP_SUPPORTED) +/* Turn on packed pixel swapping */ +void PNGAPI +png_set_packswap(png_structrp png_ptr) +{ + png_debug(1, "in png_set_packswap"); + + if (png_ptr == NULL) + return; + + if (png_ptr->bit_depth < 8) + png_ptr->transformations |= PNG_PACKSWAP; +} +#endif + +#if defined(PNG_READ_SHIFT_SUPPORTED) || defined(PNG_WRITE_SHIFT_SUPPORTED) +void PNGAPI +png_set_shift(png_structrp png_ptr, png_const_color_8p true_bits) +{ + png_debug(1, "in png_set_shift"); + + if (png_ptr == NULL) + return; + + png_ptr->transformations |= PNG_SHIFT; + png_ptr->shift = *true_bits; +} +#endif + +#if defined(PNG_READ_INTERLACING_SUPPORTED) || \ + defined(PNG_WRITE_INTERLACING_SUPPORTED) +int PNGAPI +png_set_interlace_handling(png_structrp png_ptr) +{ + png_debug(1, "in png_set_interlace handling"); + + if (png_ptr && png_ptr->interlaced) + { + png_ptr->transformations |= PNG_INTERLACE; + return (7); + } + + return (1); +} +#endif + +#if defined(PNG_READ_FILLER_SUPPORTED) || defined(PNG_WRITE_FILLER_SUPPORTED) +/* Add a filler byte on read, or remove a filler or alpha byte on write. + * The filler type has changed in v0.95 to allow future 2-byte fillers + * for 48-bit input data, as well as to avoid problems with some compilers + * that don't like bytes as parameters. + */ +void PNGAPI +png_set_filler(png_structrp png_ptr, png_uint_32 filler, int filler_loc) +{ + png_debug(1, "in png_set_filler"); + + if (png_ptr == NULL) + return; + + /* In libpng 1.6 it is possible to determine whether this is a read or write + * operation and therefore to do more checking here for a valid call. + */ + if (png_ptr->mode & PNG_IS_READ_STRUCT) + { +# ifdef PNG_READ_FILLER_SUPPORTED + /* On read png_set_filler is always valid, regardless of the base PNG + * format, because other transformations can give a format where the + * filler code can execute (basically an 8 or 16-bit component RGB or G + * format.) + * + * NOTE: usr_channels is not used by the read code! (This has led to + * confusion in the past.) The filler is only used in the read code. + */ + png_ptr->filler = (png_uint_16)filler; +# else + png_app_error(png_ptr, "png_set_filler not supported on read"); + PNG_UNUSED(filler) /* not used in the write case */ + return; +# endif + } + + else /* write */ + { +# ifdef PNG_WRITE_FILLER_SUPPORTED + /* On write the usr_channels parameter must be set correctly at the + * start to record the number of channels in the app-supplied data. + */ + switch (png_ptr->color_type) + { + case PNG_COLOR_TYPE_RGB: + png_ptr->usr_channels = 4; + break; + + case PNG_COLOR_TYPE_GRAY: + if (png_ptr->bit_depth >= 8) + { + png_ptr->usr_channels = 2; + break; + } + + else + { + /* There simply isn't any code in libpng to strip out bits + * from bytes when the components are less than a byte in + * size! + */ + png_app_error(png_ptr, + "png_set_filler is invalid for low bit depth gray output"); + return; + } + + default: + png_app_error(png_ptr, + "png_set_filler: inappropriate color type"); + return; + } +# else + png_app_error(png_ptr, "png_set_filler not supported on write"); + return; +# endif + } + + /* Here on success - libpng supports the operation, set the transformation + * and the flag to say where the filler channel is. + */ + png_ptr->transformations |= PNG_FILLER; + + if (filler_loc == PNG_FILLER_AFTER) + png_ptr->flags |= PNG_FLAG_FILLER_AFTER; + + else + png_ptr->flags &= ~PNG_FLAG_FILLER_AFTER; +} + +/* Added to libpng-1.2.7 */ +void PNGAPI +png_set_add_alpha(png_structrp png_ptr, png_uint_32 filler, int filler_loc) +{ + png_debug(1, "in png_set_add_alpha"); + + if (png_ptr == NULL) + return; + + png_set_filler(png_ptr, filler, filler_loc); + /* The above may fail to do anything. */ + if (png_ptr->transformations & PNG_FILLER) + png_ptr->transformations |= PNG_ADD_ALPHA; +} + +#endif + +#if defined(PNG_READ_SWAP_ALPHA_SUPPORTED) || \ + defined(PNG_WRITE_SWAP_ALPHA_SUPPORTED) +void PNGAPI +png_set_swap_alpha(png_structrp png_ptr) +{ + png_debug(1, "in png_set_swap_alpha"); + + if (png_ptr == NULL) + return; + + png_ptr->transformations |= PNG_SWAP_ALPHA; +} +#endif + +#if defined(PNG_READ_INVERT_ALPHA_SUPPORTED) || \ + defined(PNG_WRITE_INVERT_ALPHA_SUPPORTED) +void PNGAPI +png_set_invert_alpha(png_structrp png_ptr) +{ + png_debug(1, "in png_set_invert_alpha"); + + if (png_ptr == NULL) + return; + + png_ptr->transformations |= PNG_INVERT_ALPHA; +} +#endif + +#if defined(PNG_READ_INVERT_SUPPORTED) || defined(PNG_WRITE_INVERT_SUPPORTED) +void PNGAPI +png_set_invert_mono(png_structrp png_ptr) +{ + png_debug(1, "in png_set_invert_mono"); + + if (png_ptr == NULL) + return; + + png_ptr->transformations |= PNG_INVERT_MONO; +} + +/* Invert monochrome grayscale data */ +void /* PRIVATE */ +png_do_invert(png_row_infop row_info, png_bytep row) +{ + png_debug(1, "in png_do_invert"); + + /* This test removed from libpng version 1.0.13 and 1.2.0: + * if (row_info->bit_depth == 1 && + */ + if (row_info->color_type == PNG_COLOR_TYPE_GRAY) + { + png_bytep rp = row; + png_size_t i; + png_size_t istop = row_info->rowbytes; + + for (i = 0; i < istop; i++) + { + *rp = (png_byte)(~(*rp)); + rp++; + } + } + + else if (row_info->color_type == PNG_COLOR_TYPE_GRAY_ALPHA && + row_info->bit_depth == 8) + { + png_bytep rp = row; + png_size_t i; + png_size_t istop = row_info->rowbytes; + + for (i = 0; i < istop; i += 2) + { + *rp = (png_byte)(~(*rp)); + rp += 2; + } + } + +#ifdef PNG_16BIT_SUPPORTED + else if (row_info->color_type == PNG_COLOR_TYPE_GRAY_ALPHA && + row_info->bit_depth == 16) + { + png_bytep rp = row; + png_size_t i; + png_size_t istop = row_info->rowbytes; + + for (i = 0; i < istop; i += 4) + { + *rp = (png_byte)(~(*rp)); + *(rp + 1) = (png_byte)(~(*(rp + 1))); + rp += 4; + } + } +#endif +} +#endif + +#ifdef PNG_16BIT_SUPPORTED +#if defined(PNG_READ_SWAP_SUPPORTED) || defined(PNG_WRITE_SWAP_SUPPORTED) +/* Swaps byte order on 16 bit depth images */ +void /* PRIVATE */ +png_do_swap(png_row_infop row_info, png_bytep row) +{ + png_debug(1, "in png_do_swap"); + + if (row_info->bit_depth == 16) + { + png_bytep rp = row; + png_uint_32 i; + png_uint_32 istop= row_info->width * row_info->channels; + + for (i = 0; i < istop; i++, rp += 2) + { + png_byte t = *rp; + *rp = *(rp + 1); + *(rp + 1) = t; + } + } +} +#endif +#endif + +#if defined(PNG_READ_PACKSWAP_SUPPORTED)||defined(PNG_WRITE_PACKSWAP_SUPPORTED) +static PNG_CONST png_byte onebppswaptable[256] = { + 0x00, 0x80, 0x40, 0xC0, 0x20, 0xA0, 0x60, 0xE0, + 0x10, 0x90, 0x50, 0xD0, 0x30, 0xB0, 0x70, 0xF0, + 0x08, 0x88, 0x48, 0xC8, 0x28, 0xA8, 0x68, 0xE8, + 0x18, 0x98, 0x58, 0xD8, 0x38, 0xB8, 0x78, 0xF8, + 0x04, 0x84, 0x44, 0xC4, 0x24, 0xA4, 0x64, 0xE4, + 0x14, 0x94, 0x54, 0xD4, 0x34, 0xB4, 0x74, 0xF4, + 0x0C, 0x8C, 0x4C, 0xCC, 0x2C, 0xAC, 0x6C, 0xEC, + 0x1C, 0x9C, 0x5C, 0xDC, 0x3C, 0xBC, 0x7C, 0xFC, + 0x02, 0x82, 0x42, 0xC2, 0x22, 0xA2, 0x62, 0xE2, + 0x12, 0x92, 0x52, 0xD2, 0x32, 0xB2, 0x72, 0xF2, + 0x0A, 0x8A, 0x4A, 0xCA, 0x2A, 0xAA, 0x6A, 0xEA, + 0x1A, 0x9A, 0x5A, 0xDA, 0x3A, 0xBA, 0x7A, 0xFA, + 0x06, 0x86, 0x46, 0xC6, 0x26, 0xA6, 0x66, 0xE6, + 0x16, 0x96, 0x56, 0xD6, 0x36, 0xB6, 0x76, 0xF6, + 0x0E, 0x8E, 0x4E, 0xCE, 0x2E, 0xAE, 0x6E, 0xEE, + 0x1E, 0x9E, 0x5E, 0xDE, 0x3E, 0xBE, 0x7E, 0xFE, + 0x01, 0x81, 0x41, 0xC1, 0x21, 0xA1, 0x61, 0xE1, + 0x11, 0x91, 0x51, 0xD1, 0x31, 0xB1, 0x71, 0xF1, + 0x09, 0x89, 0x49, 0xC9, 0x29, 0xA9, 0x69, 0xE9, + 0x19, 0x99, 0x59, 0xD9, 0x39, 0xB9, 0x79, 0xF9, + 0x05, 0x85, 0x45, 0xC5, 0x25, 0xA5, 0x65, 0xE5, + 0x15, 0x95, 0x55, 0xD5, 0x35, 0xB5, 0x75, 0xF5, + 0x0D, 0x8D, 0x4D, 0xCD, 0x2D, 0xAD, 0x6D, 0xED, + 0x1D, 0x9D, 0x5D, 0xDD, 0x3D, 0xBD, 0x7D, 0xFD, + 0x03, 0x83, 0x43, 0xC3, 0x23, 0xA3, 0x63, 0xE3, + 0x13, 0x93, 0x53, 0xD3, 0x33, 0xB3, 0x73, 0xF3, + 0x0B, 0x8B, 0x4B, 0xCB, 0x2B, 0xAB, 0x6B, 0xEB, + 0x1B, 0x9B, 0x5B, 0xDB, 0x3B, 0xBB, 0x7B, 0xFB, + 0x07, 0x87, 0x47, 0xC7, 0x27, 0xA7, 0x67, 0xE7, + 0x17, 0x97, 0x57, 0xD7, 0x37, 0xB7, 0x77, 0xF7, + 0x0F, 0x8F, 0x4F, 0xCF, 0x2F, 0xAF, 0x6F, 0xEF, + 0x1F, 0x9F, 0x5F, 0xDF, 0x3F, 0xBF, 0x7F, 0xFF +}; + +static PNG_CONST png_byte twobppswaptable[256] = { + 0x00, 0x40, 0x80, 0xC0, 0x10, 0x50, 0x90, 0xD0, + 0x20, 0x60, 0xA0, 0xE0, 0x30, 0x70, 0xB0, 0xF0, + 0x04, 0x44, 0x84, 0xC4, 0x14, 0x54, 0x94, 0xD4, + 0x24, 0x64, 0xA4, 0xE4, 0x34, 0x74, 0xB4, 0xF4, + 0x08, 0x48, 0x88, 0xC8, 0x18, 0x58, 0x98, 0xD8, + 0x28, 0x68, 0xA8, 0xE8, 0x38, 0x78, 0xB8, 0xF8, + 0x0C, 0x4C, 0x8C, 0xCC, 0x1C, 0x5C, 0x9C, 0xDC, + 0x2C, 0x6C, 0xAC, 0xEC, 0x3C, 0x7C, 0xBC, 0xFC, + 0x01, 0x41, 0x81, 0xC1, 0x11, 0x51, 0x91, 0xD1, + 0x21, 0x61, 0xA1, 0xE1, 0x31, 0x71, 0xB1, 0xF1, + 0x05, 0x45, 0x85, 0xC5, 0x15, 0x55, 0x95, 0xD5, + 0x25, 0x65, 0xA5, 0xE5, 0x35, 0x75, 0xB5, 0xF5, + 0x09, 0x49, 0x89, 0xC9, 0x19, 0x59, 0x99, 0xD9, + 0x29, 0x69, 0xA9, 0xE9, 0x39, 0x79, 0xB9, 0xF9, + 0x0D, 0x4D, 0x8D, 0xCD, 0x1D, 0x5D, 0x9D, 0xDD, + 0x2D, 0x6D, 0xAD, 0xED, 0x3D, 0x7D, 0xBD, 0xFD, + 0x02, 0x42, 0x82, 0xC2, 0x12, 0x52, 0x92, 0xD2, + 0x22, 0x62, 0xA2, 0xE2, 0x32, 0x72, 0xB2, 0xF2, + 0x06, 0x46, 0x86, 0xC6, 0x16, 0x56, 0x96, 0xD6, + 0x26, 0x66, 0xA6, 0xE6, 0x36, 0x76, 0xB6, 0xF6, + 0x0A, 0x4A, 0x8A, 0xCA, 0x1A, 0x5A, 0x9A, 0xDA, + 0x2A, 0x6A, 0xAA, 0xEA, 0x3A, 0x7A, 0xBA, 0xFA, + 0x0E, 0x4E, 0x8E, 0xCE, 0x1E, 0x5E, 0x9E, 0xDE, + 0x2E, 0x6E, 0xAE, 0xEE, 0x3E, 0x7E, 0xBE, 0xFE, + 0x03, 0x43, 0x83, 0xC3, 0x13, 0x53, 0x93, 0xD3, + 0x23, 0x63, 0xA3, 0xE3, 0x33, 0x73, 0xB3, 0xF3, + 0x07, 0x47, 0x87, 0xC7, 0x17, 0x57, 0x97, 0xD7, + 0x27, 0x67, 0xA7, 0xE7, 0x37, 0x77, 0xB7, 0xF7, + 0x0B, 0x4B, 0x8B, 0xCB, 0x1B, 0x5B, 0x9B, 0xDB, + 0x2B, 0x6B, 0xAB, 0xEB, 0x3B, 0x7B, 0xBB, 0xFB, + 0x0F, 0x4F, 0x8F, 0xCF, 0x1F, 0x5F, 0x9F, 0xDF, + 0x2F, 0x6F, 0xAF, 0xEF, 0x3F, 0x7F, 0xBF, 0xFF +}; + +static PNG_CONST png_byte fourbppswaptable[256] = { + 0x00, 0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, + 0x80, 0x90, 0xA0, 0xB0, 0xC0, 0xD0, 0xE0, 0xF0, + 0x01, 0x11, 0x21, 0x31, 0x41, 0x51, 0x61, 0x71, + 0x81, 0x91, 0xA1, 0xB1, 0xC1, 0xD1, 0xE1, 0xF1, + 0x02, 0x12, 0x22, 0x32, 0x42, 0x52, 0x62, 0x72, + 0x82, 0x92, 0xA2, 0xB2, 0xC2, 0xD2, 0xE2, 0xF2, + 0x03, 0x13, 0x23, 0x33, 0x43, 0x53, 0x63, 0x73, + 0x83, 0x93, 0xA3, 0xB3, 0xC3, 0xD3, 0xE3, 0xF3, + 0x04, 0x14, 0x24, 0x34, 0x44, 0x54, 0x64, 0x74, + 0x84, 0x94, 0xA4, 0xB4, 0xC4, 0xD4, 0xE4, 0xF4, + 0x05, 0x15, 0x25, 0x35, 0x45, 0x55, 0x65, 0x75, + 0x85, 0x95, 0xA5, 0xB5, 0xC5, 0xD5, 0xE5, 0xF5, + 0x06, 0x16, 0x26, 0x36, 0x46, 0x56, 0x66, 0x76, + 0x86, 0x96, 0xA6, 0xB6, 0xC6, 0xD6, 0xE6, 0xF6, + 0x07, 0x17, 0x27, 0x37, 0x47, 0x57, 0x67, 0x77, + 0x87, 0x97, 0xA7, 0xB7, 0xC7, 0xD7, 0xE7, 0xF7, + 0x08, 0x18, 0x28, 0x38, 0x48, 0x58, 0x68, 0x78, + 0x88, 0x98, 0xA8, 0xB8, 0xC8, 0xD8, 0xE8, 0xF8, + 0x09, 0x19, 0x29, 0x39, 0x49, 0x59, 0x69, 0x79, + 0x89, 0x99, 0xA9, 0xB9, 0xC9, 0xD9, 0xE9, 0xF9, + 0x0A, 0x1A, 0x2A, 0x3A, 0x4A, 0x5A, 0x6A, 0x7A, + 0x8A, 0x9A, 0xAA, 0xBA, 0xCA, 0xDA, 0xEA, 0xFA, + 0x0B, 0x1B, 0x2B, 0x3B, 0x4B, 0x5B, 0x6B, 0x7B, + 0x8B, 0x9B, 0xAB, 0xBB, 0xCB, 0xDB, 0xEB, 0xFB, + 0x0C, 0x1C, 0x2C, 0x3C, 0x4C, 0x5C, 0x6C, 0x7C, + 0x8C, 0x9C, 0xAC, 0xBC, 0xCC, 0xDC, 0xEC, 0xFC, + 0x0D, 0x1D, 0x2D, 0x3D, 0x4D, 0x5D, 0x6D, 0x7D, + 0x8D, 0x9D, 0xAD, 0xBD, 0xCD, 0xDD, 0xED, 0xFD, + 0x0E, 0x1E, 0x2E, 0x3E, 0x4E, 0x5E, 0x6E, 0x7E, + 0x8E, 0x9E, 0xAE, 0xBE, 0xCE, 0xDE, 0xEE, 0xFE, + 0x0F, 0x1F, 0x2F, 0x3F, 0x4F, 0x5F, 0x6F, 0x7F, + 0x8F, 0x9F, 0xAF, 0xBF, 0xCF, 0xDF, 0xEF, 0xFF +}; + +/* Swaps pixel packing order within bytes */ +void /* PRIVATE */ +png_do_packswap(png_row_infop row_info, png_bytep row) +{ + png_debug(1, "in png_do_packswap"); + + if (row_info->bit_depth < 8) + { + png_bytep rp; + png_const_bytep end, table; + + end = row + row_info->rowbytes; + + if (row_info->bit_depth == 1) + table = onebppswaptable; + + else if (row_info->bit_depth == 2) + table = twobppswaptable; + + else if (row_info->bit_depth == 4) + table = fourbppswaptable; + + else + return; + + for (rp = row; rp < end; rp++) + *rp = table[*rp]; + } +} +#endif /* PNG_READ_PACKSWAP_SUPPORTED or PNG_WRITE_PACKSWAP_SUPPORTED */ + +#if defined(PNG_WRITE_FILLER_SUPPORTED) || \ + defined(PNG_READ_STRIP_ALPHA_SUPPORTED) +/* Remove a channel - this used to be 'png_do_strip_filler' but it used a + * somewhat weird combination of flags to determine what to do. All the calls + * to png_do_strip_filler are changed in 1.5.2 to call this instead with the + * correct arguments. + * + * The routine isn't general - the channel must be the channel at the start or + * end (not in the middle) of each pixel. + */ +void /* PRIVATE */ +png_do_strip_channel(png_row_infop row_info, png_bytep row, int at_start) +{ + png_bytep sp = row; /* source pointer */ + png_bytep dp = row; /* destination pointer */ + png_bytep ep = row + row_info->rowbytes; /* One beyond end of row */ + + /* At the start sp will point to the first byte to copy and dp to where + * it is copied to. ep always points just beyond the end of the row, so + * the loop simply copies (channels-1) channels until sp reaches ep. + * + * at_start: 0 -- convert AG, XG, ARGB, XRGB, AAGG, XXGG, etc. + * nonzero -- convert GA, GX, RGBA, RGBX, GGAA, RRGGBBXX, etc. + */ + + /* GA, GX, XG cases */ + if (row_info->channels == 2) + { + if (row_info->bit_depth == 8) + { + if (at_start) /* Skip initial filler */ + ++sp; + else /* Skip initial channel and, for sp, the filler */ + sp += 2, ++dp; + + /* For a 1 pixel wide image there is nothing to do */ + while (sp < ep) + *dp++ = *sp, sp += 2; + + row_info->pixel_depth = 8; + } + + else if (row_info->bit_depth == 16) + { + if (at_start) /* Skip initial filler */ + sp += 2; + else /* Skip initial channel and, for sp, the filler */ + sp += 4, dp += 2; + + while (sp < ep) + *dp++ = *sp++, *dp++ = *sp, sp += 3; + + row_info->pixel_depth = 16; + } + + else + return; /* bad bit depth */ + + row_info->channels = 1; + + /* Finally fix the color type if it records an alpha channel */ + if (row_info->color_type == PNG_COLOR_TYPE_GRAY_ALPHA) + row_info->color_type = PNG_COLOR_TYPE_GRAY; + } + + /* RGBA, RGBX, XRGB cases */ + else if (row_info->channels == 4) + { + if (row_info->bit_depth == 8) + { + if (at_start) /* Skip initial filler */ + ++sp; + else /* Skip initial channels and, for sp, the filler */ + sp += 4, dp += 3; + + /* Note that the loop adds 3 to dp and 4 to sp each time. */ + while (sp < ep) + *dp++ = *sp++, *dp++ = *sp++, *dp++ = *sp, sp += 2; + + row_info->pixel_depth = 24; + } + + else if (row_info->bit_depth == 16) + { + if (at_start) /* Skip initial filler */ + sp += 2; + else /* Skip initial channels and, for sp, the filler */ + sp += 8, dp += 6; + + while (sp < ep) + { + /* Copy 6 bytes, skip 2 */ + *dp++ = *sp++, *dp++ = *sp++; + *dp++ = *sp++, *dp++ = *sp++; + *dp++ = *sp++, *dp++ = *sp, sp += 3; + } + + row_info->pixel_depth = 48; + } + + else + return; /* bad bit depth */ + + row_info->channels = 3; + + /* Finally fix the color type if it records an alpha channel */ + if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA) + row_info->color_type = PNG_COLOR_TYPE_RGB; + } + + else + return; /* The filler channel has gone already */ + + /* Fix the rowbytes value. */ + row_info->rowbytes = dp-row; +} +#endif + +#if defined(PNG_READ_BGR_SUPPORTED) || defined(PNG_WRITE_BGR_SUPPORTED) +/* Swaps red and blue bytes within a pixel */ +void /* PRIVATE */ +png_do_bgr(png_row_infop row_info, png_bytep row) +{ + png_debug(1, "in png_do_bgr"); + + if ((row_info->color_type & PNG_COLOR_MASK_COLOR)) + { + png_uint_32 row_width = row_info->width; + if (row_info->bit_depth == 8) + { + if (row_info->color_type == PNG_COLOR_TYPE_RGB) + { + png_bytep rp; + png_uint_32 i; + + for (i = 0, rp = row; i < row_width; i++, rp += 3) + { + png_byte save = *rp; + *rp = *(rp + 2); + *(rp + 2) = save; + } + } + + else if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA) + { + png_bytep rp; + png_uint_32 i; + + for (i = 0, rp = row; i < row_width; i++, rp += 4) + { + png_byte save = *rp; + *rp = *(rp + 2); + *(rp + 2) = save; + } + } + } + +#ifdef PNG_16BIT_SUPPORTED + else if (row_info->bit_depth == 16) + { + if (row_info->color_type == PNG_COLOR_TYPE_RGB) + { + png_bytep rp; + png_uint_32 i; + + for (i = 0, rp = row; i < row_width; i++, rp += 6) + { + png_byte save = *rp; + *rp = *(rp + 4); + *(rp + 4) = save; + save = *(rp + 1); + *(rp + 1) = *(rp + 5); + *(rp + 5) = save; + } + } + + else if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA) + { + png_bytep rp; + png_uint_32 i; + + for (i = 0, rp = row; i < row_width; i++, rp += 8) + { + png_byte save = *rp; + *rp = *(rp + 4); + *(rp + 4) = save; + save = *(rp + 1); + *(rp + 1) = *(rp + 5); + *(rp + 5) = save; + } + } + } +#endif + } +} +#endif /* PNG_READ_BGR_SUPPORTED or PNG_WRITE_BGR_SUPPORTED */ + +#if defined(PNG_READ_CHECK_FOR_INVALID_INDEX_SUPPORTED) || \ + defined(PNG_WRITE_CHECK_FOR_INVALID_INDEX_SUPPORTED) +/* Added at libpng-1.5.10 */ +void /* PRIVATE */ +png_do_check_palette_indexes(png_structrp png_ptr, png_row_infop row_info) +{ + if (png_ptr->num_palette < (1 << row_info->bit_depth) && + png_ptr->num_palette > 0) /* num_palette can be 0 in MNG files */ + { + /* Calculations moved outside switch in an attempt to stop different + * compiler warnings. 'padding' is in *bits* within the last byte, it is + * an 'int' because pixel_depth becomes an 'int' in the expression below, + * and this calculation is used because it avoids warnings that other + * forms produced on either GCC or MSVC. + */ + int padding = (-row_info->pixel_depth * row_info->width) & 7; + png_bytep rp = png_ptr->row_buf + row_info->rowbytes; + + switch (row_info->bit_depth) + { + case 1: + { + /* in this case, all bytes must be 0 so we don't need + * to unpack the pixels except for the rightmost one. + */ + for (; rp > png_ptr->row_buf; rp--) + { + if (*rp >> padding != 0) + png_ptr->num_palette_max = 1; + padding = 0; + } + + break; + } + + case 2: + { + for (; rp > png_ptr->row_buf; rp--) + { + int i = ((*rp >> padding) & 0x03); + + if (i > png_ptr->num_palette_max) + png_ptr->num_palette_max = i; + + i = (((*rp >> padding) >> 2) & 0x03); + + if (i > png_ptr->num_palette_max) + png_ptr->num_palette_max = i; + + i = (((*rp >> padding) >> 4) & 0x03); + + if (i > png_ptr->num_palette_max) + png_ptr->num_palette_max = i; + + i = (((*rp >> padding) >> 6) & 0x03); + + if (i > png_ptr->num_palette_max) + png_ptr->num_palette_max = i; + + padding = 0; + } + + break; + } + + case 4: + { + for (; rp > png_ptr->row_buf; rp--) + { + int i = ((*rp >> padding) & 0x0f); + + if (i > png_ptr->num_palette_max) + png_ptr->num_palette_max = i; + + i = (((*rp >> padding) >> 4) & 0x0f); + + if (i > png_ptr->num_palette_max) + png_ptr->num_palette_max = i; + + padding = 0; + } + + break; + } + + case 8: + { + for (; rp > png_ptr->row_buf; rp--) + { + if (*rp > png_ptr->num_palette_max) + png_ptr->num_palette_max = (int) *rp; + } + + break; + } + + default: + break; + } + } +} +#endif /* PNG_CHECK_FOR_INVALID_INDEX_SUPPORTED */ + +#if defined(PNG_READ_USER_TRANSFORM_SUPPORTED) || \ + defined(PNG_WRITE_USER_TRANSFORM_SUPPORTED) +#ifdef PNG_USER_TRANSFORM_PTR_SUPPORTED +void PNGAPI +png_set_user_transform_info(png_structrp png_ptr, png_voidp + user_transform_ptr, int user_transform_depth, int user_transform_channels) +{ + png_debug(1, "in png_set_user_transform_info"); + + if (png_ptr == NULL) + return; + +#ifdef PNG_READ_USER_TRANSFORM_SUPPORTED + if ((png_ptr->mode & PNG_IS_READ_STRUCT) != 0 && + (png_ptr->flags & PNG_FLAG_ROW_INIT) != 0) + { + png_app_error(png_ptr, + "info change after png_start_read_image or png_read_update_info"); + return; + } +#endif + + png_ptr->user_transform_ptr = user_transform_ptr; + png_ptr->user_transform_depth = (png_byte)user_transform_depth; + png_ptr->user_transform_channels = (png_byte)user_transform_channels; +} +#endif + +/* This function returns a pointer to the user_transform_ptr associated with + * the user transform functions. The application should free any memory + * associated with this pointer before png_write_destroy and png_read_destroy + * are called. + */ +#ifdef PNG_USER_TRANSFORM_PTR_SUPPORTED +png_voidp PNGAPI +png_get_user_transform_ptr(png_const_structrp png_ptr) +{ + if (png_ptr == NULL) + return (NULL); + + return png_ptr->user_transform_ptr; +} +#endif + +#ifdef PNG_USER_TRANSFORM_INFO_SUPPORTED +png_uint_32 PNGAPI +png_get_current_row_number(png_const_structrp png_ptr) +{ + /* See the comments in png.h - this is the sub-image row when reading and + * interlaced image. + */ + if (png_ptr != NULL) + return png_ptr->row_number; + + return PNG_UINT_32_MAX; /* help the app not to fail silently */ +} + +png_byte PNGAPI +png_get_current_pass_number(png_const_structrp png_ptr) +{ + if (png_ptr != NULL) + return png_ptr->pass; + return 8; /* invalid */ +} +#endif /* PNG_USER_TRANSFORM_INFO_SUPPORTED */ +#endif /* PNG_READ_USER_TRANSFORM_SUPPORTED || + PNG_WRITE_USER_TRANSFORM_SUPPORTED */ +#endif /* PNG_READ_SUPPORTED || PNG_WRITE_SUPPORTED */ diff --git a/Minecraft.Client/Windows64/4JLibs/inc/Render/libpng/pngwio.c b/Minecraft.Client/Windows64/4JLibs/inc/Render/libpng/pngwio.c new file mode 100644 index 000000000..e3289dfe4 --- /dev/null +++ b/Minecraft.Client/Windows64/4JLibs/inc/Render/libpng/pngwio.c @@ -0,0 +1,164 @@ + +/* pngwio.c - functions for data output + * + * Last changed in libpng 1.6.0 [February 14, 2013] + * Copyright (c) 1998-2013 Glenn Randers-Pehrson + * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) + * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) + * + * This code is released under the libpng license. + * For conditions of distribution and use, see the disclaimer + * and license in png.h + * + * This file provides a location for all output. Users who need + * special handling are expected to write functions that have the same + * arguments as these and perform similar functions, but that possibly + * use different output methods. Note that you shouldn't change these + * functions, but rather write replacement functions and then change + * them at run time with png_set_write_fn(...). + */ + +#include "pngpriv.h" + +#ifdef PNG_WRITE_SUPPORTED + +/* Write the data to whatever output you are using. The default routine + * writes to a file pointer. Note that this routine sometimes gets called + * with very small lengths, so you should implement some kind of simple + * buffering if you are using unbuffered writes. This should never be asked + * to write more than 64K on a 16 bit machine. + */ + +void /* PRIVATE */ +png_write_data(png_structrp png_ptr, png_const_bytep data, png_size_t length) +{ + /* NOTE: write_data_fn must not change the buffer! */ + if (png_ptr->write_data_fn != NULL ) + (*(png_ptr->write_data_fn))(png_ptr, png_constcast(png_bytep,data), + length); + + else + png_error(png_ptr, "Call to NULL write function"); +} + +#ifdef PNG_STDIO_SUPPORTED +/* This is the function that does the actual writing of data. If you are + * not writing to a standard C stream, you should create a replacement + * write_data function and use it at run time with png_set_write_fn(), rather + * than changing the library. + */ +void PNGCBAPI +png_default_write_data(png_structp png_ptr, png_bytep data, png_size_t length) +{ + png_size_t check; + + if (png_ptr == NULL) + return; + + check = fwrite(data, 1, length, (png_FILE_p)(png_ptr->io_ptr)); + + if (check != length) + png_error(png_ptr, "Write Error"); +} +#endif + +/* This function is called to output any data pending writing (normally + * to disk). After png_flush is called, there should be no data pending + * writing in any buffers. + */ +#ifdef PNG_WRITE_FLUSH_SUPPORTED +void /* PRIVATE */ +png_flush(png_structrp png_ptr) +{ + if (png_ptr->output_flush_fn != NULL) + (*(png_ptr->output_flush_fn))(png_ptr); +} + +# ifdef PNG_STDIO_SUPPORTED +void PNGCBAPI +png_default_flush(png_structp png_ptr) +{ + png_FILE_p io_ptr; + + if (png_ptr == NULL) + return; + + io_ptr = png_voidcast(png_FILE_p, (png_ptr->io_ptr)); + fflush(io_ptr); +} +# endif +#endif + +/* This function allows the application to supply new output functions for + * libpng if standard C streams aren't being used. + * + * This function takes as its arguments: + * png_ptr - pointer to a png output data structure + * io_ptr - pointer to user supplied structure containing info about + * the output functions. May be NULL. + * write_data_fn - pointer to a new output function that takes as its + * arguments a pointer to a png_struct, a pointer to + * data to be written, and a 32-bit unsigned int that is + * the number of bytes to be written. The new write + * function should call png_error(png_ptr, "Error msg") + * to exit and output any fatal error messages. May be + * NULL, in which case libpng's default function will + * be used. + * flush_data_fn - pointer to a new flush function that takes as its + * arguments a pointer to a png_struct. After a call to + * the flush function, there should be no data in any buffers + * or pending transmission. If the output method doesn't do + * any buffering of output, a function prototype must still be + * supplied although it doesn't have to do anything. If + * PNG_WRITE_FLUSH_SUPPORTED is not defined at libpng compile + * time, output_flush_fn will be ignored, although it must be + * supplied for compatibility. May be NULL, in which case + * libpng's default function will be used, if + * PNG_WRITE_FLUSH_SUPPORTED is defined. This is not + * a good idea if io_ptr does not point to a standard + * *FILE structure. + */ +void PNGAPI +png_set_write_fn(png_structrp png_ptr, png_voidp io_ptr, + png_rw_ptr write_data_fn, png_flush_ptr output_flush_fn) +{ + if (png_ptr == NULL) + return; + + png_ptr->io_ptr = io_ptr; + +#ifdef PNG_STDIO_SUPPORTED + if (write_data_fn != NULL) + png_ptr->write_data_fn = write_data_fn; + + else + png_ptr->write_data_fn = png_default_write_data; +#else + png_ptr->write_data_fn = write_data_fn; +#endif + +#ifdef PNG_WRITE_FLUSH_SUPPORTED +# ifdef PNG_STDIO_SUPPORTED + + if (output_flush_fn != NULL) + png_ptr->output_flush_fn = output_flush_fn; + + else + png_ptr->output_flush_fn = png_default_flush; + +# else + png_ptr->output_flush_fn = output_flush_fn; +# endif +#endif /* PNG_WRITE_FLUSH_SUPPORTED */ + + /* It is an error to read while writing a png file */ + if (png_ptr->read_data_fn != NULL) + { + png_ptr->read_data_fn = NULL; + + png_warning(png_ptr, + "Can't set both read_data_fn and write_data_fn in the" + " same structure"); + } +} +#endif /* PNG_WRITE_SUPPORTED */ diff --git a/Minecraft.Client/Windows64/4JLibs/inc/Render/libpng/pngwrite.c b/Minecraft.Client/Windows64/4JLibs/inc/Render/libpng/pngwrite.c new file mode 100644 index 000000000..53aaa8ce3 --- /dev/null +++ b/Minecraft.Client/Windows64/4JLibs/inc/Render/libpng/pngwrite.c @@ -0,0 +1,2341 @@ + +/* pngwrite.c - general routines to write a PNG file + * + * Last changed in libpng 1.6.2 [April 25, 2013] + * Copyright (c) 1998-2013 Glenn Randers-Pehrson + * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) + * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) + * + * This code is released under the libpng license. + * For conditions of distribution and use, see the disclaimer + * and license in png.h + */ + +#include "pngpriv.h" +#if defined(PNG_SIMPLIFIED_WRITE_SUPPORTED) && defined(PNG_STDIO_SUPPORTED) +# include +#endif + +#ifdef PNG_WRITE_SUPPORTED + +#ifdef PNG_WRITE_UNKNOWN_CHUNKS_SUPPORTED +/* Write out all the unknown chunks for the current given location */ +static void +write_unknown_chunks(png_structrp png_ptr, png_const_inforp info_ptr, + unsigned int where) +{ + if (info_ptr->unknown_chunks_num) + { + png_const_unknown_chunkp up; + + png_debug(5, "writing extra chunks"); + + for (up = info_ptr->unknown_chunks; + up < info_ptr->unknown_chunks + info_ptr->unknown_chunks_num; + ++up) + if (up->location & where) + { + /* If per-chunk unknown chunk handling is enabled use it, otherwise + * just write the chunks the application has set. + */ +#ifdef PNG_SET_UNKNOWN_CHUNKS_SUPPORTED + int keep = png_handle_as_unknown(png_ptr, up->name); + + /* NOTE: this code is radically different from the read side in the + * matter of handling an ancillary unknown chunk. In the read side + * the default behavior is to discard it, in the code below the default + * behavior is to write it. Critical chunks are, however, only + * written if explicitly listed or if the default is set to write all + * unknown chunks. + * + * The default handling is also slightly weird - it is not possible to + * stop the writing of all unsafe-to-copy chunks! + * + * TODO: REVIEW: this would seem to be a bug. + */ + if (keep != PNG_HANDLE_CHUNK_NEVER && + ((up->name[3] & 0x20) /* safe-to-copy overrides everything */ || + keep == PNG_HANDLE_CHUNK_ALWAYS || + (keep == PNG_HANDLE_CHUNK_AS_DEFAULT && + png_ptr->unknown_default == PNG_HANDLE_CHUNK_ALWAYS))) +#endif + { + /* TODO: review, what is wrong with a zero length unknown chunk? */ + if (up->size == 0) + png_warning(png_ptr, "Writing zero-length unknown chunk"); + + png_write_chunk(png_ptr, up->name, up->data, up->size); + } + } + } +} +#endif /* PNG_WRITE_UNKNOWN_CHUNKS_SUPPORTED */ + +/* Writes all the PNG information. This is the suggested way to use the + * library. If you have a new chunk to add, make a function to write it, + * and put it in the correct location here. If you want the chunk written + * after the image data, put it in png_write_end(). I strongly encourage + * you to supply a PNG_INFO_ flag, and check info_ptr->valid before writing + * the chunk, as that will keep the code from breaking if you want to just + * write a plain PNG file. If you have long comments, I suggest writing + * them in png_write_end(), and compressing them. + */ +void PNGAPI +png_write_info_before_PLTE(png_structrp png_ptr, png_const_inforp info_ptr) +{ + png_debug(1, "in png_write_info_before_PLTE"); + + if (png_ptr == NULL || info_ptr == NULL) + return; + + if (!(png_ptr->mode & PNG_WROTE_INFO_BEFORE_PLTE)) + { + /* Write PNG signature */ + png_write_sig(png_ptr); + +#ifdef PNG_MNG_FEATURES_SUPPORTED + if ((png_ptr->mode&PNG_HAVE_PNG_SIGNATURE) && \ + (png_ptr->mng_features_permitted)) + { + png_warning(png_ptr, "MNG features are not allowed in a PNG datastream"); + png_ptr->mng_features_permitted = 0; + } +#endif + + /* Write IHDR information. */ + png_write_IHDR(png_ptr, info_ptr->width, info_ptr->height, + info_ptr->bit_depth, info_ptr->color_type, info_ptr->compression_type, + info_ptr->filter_type, +#ifdef PNG_WRITE_INTERLACING_SUPPORTED + info_ptr->interlace_type +#else + 0 +#endif + ); + + /* The rest of these check to see if the valid field has the appropriate + * flag set, and if it does, writes the chunk. + * + * 1.6.0: COLORSPACE support controls the writing of these chunks too, and + * the chunks will be written if the WRITE routine is there and information + * is available in the COLORSPACE. (See png_colorspace_sync_info in png.c + * for where the valid flags get set.) + * + * Under certain circumstances the colorspace can be invalidated without + * syncing the info_struct 'valid' flags; this happens if libpng detects and + * error and calls png_error while the color space is being set, yet the + * application continues writing the PNG. So check the 'invalid' flag here + * too. + */ +#ifdef PNG_GAMMA_SUPPORTED +# ifdef PNG_WRITE_gAMA_SUPPORTED + if (!(info_ptr->colorspace.flags & PNG_COLORSPACE_INVALID) && + (info_ptr->colorspace.flags & PNG_COLORSPACE_FROM_gAMA) && + (info_ptr->valid & PNG_INFO_gAMA)) + png_write_gAMA_fixed(png_ptr, info_ptr->colorspace.gamma); +# endif +#endif + +#ifdef PNG_COLORSPACE_SUPPORTED + /* Write only one of sRGB or an ICC profile. If a profile was supplied + * and it matches one of the known sRGB ones issue a warning. + */ +# ifdef PNG_WRITE_iCCP_SUPPORTED + if (!(info_ptr->colorspace.flags & PNG_COLORSPACE_INVALID) && + (info_ptr->valid & PNG_INFO_iCCP)) + { +# ifdef PNG_WRITE_sRGB_SUPPORTED + if (info_ptr->valid & PNG_INFO_sRGB) + png_app_warning(png_ptr, + "profile matches sRGB but writing iCCP instead"); +# endif + + png_write_iCCP(png_ptr, info_ptr->iccp_name, + info_ptr->iccp_profile); + } +# ifdef PNG_WRITE_sRGB_SUPPORTED + else +# endif +# endif + +# ifdef PNG_WRITE_sRGB_SUPPORTED + if (!(info_ptr->colorspace.flags & PNG_COLORSPACE_INVALID) && + (info_ptr->valid & PNG_INFO_sRGB)) + png_write_sRGB(png_ptr, info_ptr->colorspace.rendering_intent); +# endif /* WRITE_sRGB */ +#endif /* COLORSPACE */ + +#ifdef PNG_WRITE_sBIT_SUPPORTED + if (info_ptr->valid & PNG_INFO_sBIT) + png_write_sBIT(png_ptr, &(info_ptr->sig_bit), info_ptr->color_type); +#endif + +#ifdef PNG_COLORSPACE_SUPPORTED +# ifdef PNG_WRITE_cHRM_SUPPORTED + if (!(info_ptr->colorspace.flags & PNG_COLORSPACE_INVALID) && + (info_ptr->colorspace.flags & PNG_COLORSPACE_FROM_cHRM) && + (info_ptr->valid & PNG_INFO_cHRM)) + png_write_cHRM_fixed(png_ptr, &info_ptr->colorspace.end_points_xy); +# endif +#endif + +#ifdef PNG_WRITE_UNKNOWN_CHUNKS_SUPPORTED + write_unknown_chunks(png_ptr, info_ptr, PNG_HAVE_IHDR); +#endif + + png_ptr->mode |= PNG_WROTE_INFO_BEFORE_PLTE; + } +} + +void PNGAPI +png_write_info(png_structrp png_ptr, png_const_inforp info_ptr) +{ +#if defined(PNG_WRITE_TEXT_SUPPORTED) || defined(PNG_WRITE_sPLT_SUPPORTED) + int i; +#endif + + png_debug(1, "in png_write_info"); + + if (png_ptr == NULL || info_ptr == NULL) + return; + + png_write_info_before_PLTE(png_ptr, info_ptr); + + if (info_ptr->valid & PNG_INFO_PLTE) + png_write_PLTE(png_ptr, info_ptr->palette, + (png_uint_32)info_ptr->num_palette); + + else if (info_ptr->color_type == PNG_COLOR_TYPE_PALETTE) + png_error(png_ptr, "Valid palette required for paletted images"); + +#ifdef PNG_WRITE_tRNS_SUPPORTED + if (info_ptr->valid & PNG_INFO_tRNS) + { +#ifdef PNG_WRITE_INVERT_ALPHA_SUPPORTED + /* Invert the alpha channel (in tRNS) */ + if ((png_ptr->transformations & PNG_INVERT_ALPHA) && + info_ptr->color_type == PNG_COLOR_TYPE_PALETTE) + { + int j; + for (j = 0; j<(int)info_ptr->num_trans; j++) + info_ptr->trans_alpha[j] = + (png_byte)(255 - info_ptr->trans_alpha[j]); + } +#endif + png_write_tRNS(png_ptr, info_ptr->trans_alpha, &(info_ptr->trans_color), + info_ptr->num_trans, info_ptr->color_type); + } +#endif +#ifdef PNG_WRITE_bKGD_SUPPORTED + if (info_ptr->valid & PNG_INFO_bKGD) + png_write_bKGD(png_ptr, &(info_ptr->background), info_ptr->color_type); +#endif + +#ifdef PNG_WRITE_hIST_SUPPORTED + if (info_ptr->valid & PNG_INFO_hIST) + png_write_hIST(png_ptr, info_ptr->hist, info_ptr->num_palette); +#endif + +#ifdef PNG_WRITE_oFFs_SUPPORTED + if (info_ptr->valid & PNG_INFO_oFFs) + png_write_oFFs(png_ptr, info_ptr->x_offset, info_ptr->y_offset, + info_ptr->offset_unit_type); +#endif + +#ifdef PNG_WRITE_pCAL_SUPPORTED + if (info_ptr->valid & PNG_INFO_pCAL) + png_write_pCAL(png_ptr, info_ptr->pcal_purpose, info_ptr->pcal_X0, + info_ptr->pcal_X1, info_ptr->pcal_type, info_ptr->pcal_nparams, + info_ptr->pcal_units, info_ptr->pcal_params); +#endif + +#ifdef PNG_WRITE_sCAL_SUPPORTED + if (info_ptr->valid & PNG_INFO_sCAL) + png_write_sCAL_s(png_ptr, (int)info_ptr->scal_unit, + info_ptr->scal_s_width, info_ptr->scal_s_height); +#endif /* sCAL */ + +#ifdef PNG_WRITE_pHYs_SUPPORTED + if (info_ptr->valid & PNG_INFO_pHYs) + png_write_pHYs(png_ptr, info_ptr->x_pixels_per_unit, + info_ptr->y_pixels_per_unit, info_ptr->phys_unit_type); +#endif /* pHYs */ + +#ifdef PNG_WRITE_tIME_SUPPORTED + if (info_ptr->valid & PNG_INFO_tIME) + { + png_write_tIME(png_ptr, &(info_ptr->mod_time)); + png_ptr->mode |= PNG_WROTE_tIME; + } +#endif /* tIME */ + +#ifdef PNG_WRITE_sPLT_SUPPORTED + if (info_ptr->valid & PNG_INFO_sPLT) + for (i = 0; i < (int)info_ptr->splt_palettes_num; i++) + png_write_sPLT(png_ptr, info_ptr->splt_palettes + i); +#endif /* sPLT */ + +#ifdef PNG_WRITE_TEXT_SUPPORTED + /* Check to see if we need to write text chunks */ + for (i = 0; i < info_ptr->num_text; i++) + { + png_debug2(2, "Writing header text chunk %d, type %d", i, + info_ptr->text[i].compression); + /* An internationalized chunk? */ + if (info_ptr->text[i].compression > 0) + { +#ifdef PNG_WRITE_iTXt_SUPPORTED + /* Write international chunk */ + png_write_iTXt(png_ptr, + info_ptr->text[i].compression, + info_ptr->text[i].key, + info_ptr->text[i].lang, + info_ptr->text[i].lang_key, + info_ptr->text[i].text); +#else + png_warning(png_ptr, "Unable to write international text"); +#endif + /* Mark this chunk as written */ + info_ptr->text[i].compression = PNG_TEXT_COMPRESSION_NONE_WR; + } + + /* If we want a compressed text chunk */ + else if (info_ptr->text[i].compression == PNG_TEXT_COMPRESSION_zTXt) + { +#ifdef PNG_WRITE_zTXt_SUPPORTED + /* Write compressed chunk */ + png_write_zTXt(png_ptr, info_ptr->text[i].key, + info_ptr->text[i].text, 0, + info_ptr->text[i].compression); +#else + png_warning(png_ptr, "Unable to write compressed text"); +#endif + /* Mark this chunk as written */ + info_ptr->text[i].compression = PNG_TEXT_COMPRESSION_zTXt_WR; + } + + else if (info_ptr->text[i].compression == PNG_TEXT_COMPRESSION_NONE) + { +#ifdef PNG_WRITE_tEXt_SUPPORTED + /* Write uncompressed chunk */ + png_write_tEXt(png_ptr, info_ptr->text[i].key, + info_ptr->text[i].text, + 0); + /* Mark this chunk as written */ + info_ptr->text[i].compression = PNG_TEXT_COMPRESSION_NONE_WR; +#else + /* Can't get here */ + png_warning(png_ptr, "Unable to write uncompressed text"); +#endif + } + } +#endif /* tEXt */ + +#ifdef PNG_WRITE_UNKNOWN_CHUNKS_SUPPORTED + write_unknown_chunks(png_ptr, info_ptr, PNG_HAVE_PLTE); +#endif +} + +/* Writes the end of the PNG file. If you don't want to write comments or + * time information, you can pass NULL for info. If you already wrote these + * in png_write_info(), do not write them again here. If you have long + * comments, I suggest writing them here, and compressing them. + */ +void PNGAPI +png_write_end(png_structrp png_ptr, png_inforp info_ptr) +{ + png_debug(1, "in png_write_end"); + + if (png_ptr == NULL) + return; + + if (!(png_ptr->mode & PNG_HAVE_IDAT)) + png_error(png_ptr, "No IDATs written into file"); + +#ifdef PNG_WRITE_CHECK_FOR_INVALID_INDEX_SUPPORTED + if (png_ptr->num_palette_max > png_ptr->num_palette) + png_benign_error(png_ptr, "Wrote palette index exceeding num_palette"); +#endif + + /* See if user wants us to write information chunks */ + if (info_ptr != NULL) + { +#ifdef PNG_WRITE_TEXT_SUPPORTED + int i; /* local index variable */ +#endif +#ifdef PNG_WRITE_tIME_SUPPORTED + /* Check to see if user has supplied a time chunk */ + if ((info_ptr->valid & PNG_INFO_tIME) && + !(png_ptr->mode & PNG_WROTE_tIME)) + png_write_tIME(png_ptr, &(info_ptr->mod_time)); + +#endif +#ifdef PNG_WRITE_TEXT_SUPPORTED + /* Loop through comment chunks */ + for (i = 0; i < info_ptr->num_text; i++) + { + png_debug2(2, "Writing trailer text chunk %d, type %d", i, + info_ptr->text[i].compression); + /* An internationalized chunk? */ + if (info_ptr->text[i].compression > 0) + { +#ifdef PNG_WRITE_iTXt_SUPPORTED + /* Write international chunk */ + png_write_iTXt(png_ptr, + info_ptr->text[i].compression, + info_ptr->text[i].key, + info_ptr->text[i].lang, + info_ptr->text[i].lang_key, + info_ptr->text[i].text); +#else + png_warning(png_ptr, "Unable to write international text"); +#endif + /* Mark this chunk as written */ + info_ptr->text[i].compression = PNG_TEXT_COMPRESSION_NONE_WR; + } + + else if (info_ptr->text[i].compression >= PNG_TEXT_COMPRESSION_zTXt) + { +#ifdef PNG_WRITE_zTXt_SUPPORTED + /* Write compressed chunk */ + png_write_zTXt(png_ptr, info_ptr->text[i].key, + info_ptr->text[i].text, 0, + info_ptr->text[i].compression); +#else + png_warning(png_ptr, "Unable to write compressed text"); +#endif + /* Mark this chunk as written */ + info_ptr->text[i].compression = PNG_TEXT_COMPRESSION_zTXt_WR; + } + + else if (info_ptr->text[i].compression == PNG_TEXT_COMPRESSION_NONE) + { +#ifdef PNG_WRITE_tEXt_SUPPORTED + /* Write uncompressed chunk */ + png_write_tEXt(png_ptr, info_ptr->text[i].key, + info_ptr->text[i].text, 0); +#else + png_warning(png_ptr, "Unable to write uncompressed text"); +#endif + + /* Mark this chunk as written */ + info_ptr->text[i].compression = PNG_TEXT_COMPRESSION_NONE_WR; + } + } +#endif +#ifdef PNG_WRITE_UNKNOWN_CHUNKS_SUPPORTED + write_unknown_chunks(png_ptr, info_ptr, PNG_AFTER_IDAT); +#endif + } + + png_ptr->mode |= PNG_AFTER_IDAT; + + /* Write end of PNG file */ + png_write_IEND(png_ptr); + /* This flush, added in libpng-1.0.8, removed from libpng-1.0.9beta03, + * and restored again in libpng-1.2.30, may cause some applications that + * do not set png_ptr->output_flush_fn to crash. If your application + * experiences a problem, please try building libpng with + * PNG_WRITE_FLUSH_AFTER_IEND_SUPPORTED defined, and report the event to + * png-mng-implement at lists.sf.net . + */ +#ifdef PNG_WRITE_FLUSH_SUPPORTED +# ifdef PNG_WRITE_FLUSH_AFTER_IEND_SUPPORTED + png_flush(png_ptr); +# endif +#endif +} + +#ifdef PNG_CONVERT_tIME_SUPPORTED +void PNGAPI +png_convert_from_struct_tm(png_timep ptime, PNG_CONST struct tm * ttime) +{ + png_debug(1, "in png_convert_from_struct_tm"); + + ptime->year = (png_uint_16)(1900 + ttime->tm_year); + ptime->month = (png_byte)(ttime->tm_mon + 1); + ptime->day = (png_byte)ttime->tm_mday; + ptime->hour = (png_byte)ttime->tm_hour; + ptime->minute = (png_byte)ttime->tm_min; + ptime->second = (png_byte)ttime->tm_sec; +} + +void PNGAPI +png_convert_from_time_t(png_timep ptime, time_t ttime) +{ + struct tm *tbuf; + + png_debug(1, "in png_convert_from_time_t"); + + tbuf = gmtime(&ttime); + png_convert_from_struct_tm(ptime, tbuf); +} +#endif + +/* Initialize png_ptr structure, and allocate any memory needed */ +PNG_FUNCTION(png_structp,PNGAPI +png_create_write_struct,(png_const_charp user_png_ver, png_voidp error_ptr, + png_error_ptr error_fn, png_error_ptr warn_fn),PNG_ALLOCATED) +{ +#ifndef PNG_USER_MEM_SUPPORTED + png_structrp png_ptr = png_create_png_struct(user_png_ver, error_ptr, + error_fn, warn_fn, NULL, NULL, NULL); +#else + return png_create_write_struct_2(user_png_ver, error_ptr, error_fn, + warn_fn, NULL, NULL, NULL); +} + +/* Alternate initialize png_ptr structure, and allocate any memory needed */ +PNG_FUNCTION(png_structp,PNGAPI +png_create_write_struct_2,(png_const_charp user_png_ver, png_voidp error_ptr, + png_error_ptr error_fn, png_error_ptr warn_fn, png_voidp mem_ptr, + png_malloc_ptr malloc_fn, png_free_ptr free_fn),PNG_ALLOCATED) +{ + png_structrp png_ptr = png_create_png_struct(user_png_ver, error_ptr, + error_fn, warn_fn, mem_ptr, malloc_fn, free_fn); +#endif /* PNG_USER_MEM_SUPPORTED */ + + /* Set the zlib control values to defaults; they can be overridden by the + * application after the struct has been created. + */ + png_ptr->zbuffer_size = PNG_ZBUF_SIZE; + + /* The 'zlib_strategy' setting is irrelevant because png_default_claim in + * pngwutil.c defaults it according to whether or not filters will be used, + * and ignores this setting. + */ + png_ptr->zlib_strategy = PNG_Z_DEFAULT_STRATEGY; + png_ptr->zlib_level = PNG_Z_DEFAULT_COMPRESSION; + png_ptr->zlib_mem_level = 8; + png_ptr->zlib_window_bits = 15; + png_ptr->zlib_method = 8; + +#ifdef PNG_WRITE_COMPRESSED_TEXT_SUPPORTED + png_ptr->zlib_text_strategy = PNG_TEXT_Z_DEFAULT_STRATEGY; + png_ptr->zlib_text_level = PNG_TEXT_Z_DEFAULT_COMPRESSION; + png_ptr->zlib_text_mem_level = 8; + png_ptr->zlib_text_window_bits = 15; + png_ptr->zlib_text_method = 8; +#endif /* PNG_WRITE_COMPRESSED_TEXT_SUPPORTED */ + + /* This is a highly dubious configuration option; by default it is off, but + * it may be appropriate for private builds that are testing extensions not + * conformant to the current specification, or of applications that must not + * fail to write at all costs! + */ +# ifdef PNG_BENIGN_WRITE_ERRORS_SUPPORTED + png_ptr->flags |= PNG_FLAG_BENIGN_ERRORS_WARN; + /* In stable builds only warn if an application error can be completely + * handled. + */ +# endif + + /* App warnings are warnings in release (or release candidate) builds but + * are errors during development. + */ +# if PNG_LIBPNG_BUILD_BASE_TYPE >= PNG_LIBPNG_BUILD_RC + png_ptr->flags |= PNG_FLAG_APP_WARNINGS_WARN; +# endif + + if (png_ptr != NULL) + { + /* TODO: delay this, it can be done in png_init_io() (if the app doesn't + * do it itself) avoiding setting the default function if it is not + * required. + */ + png_set_write_fn(png_ptr, NULL, NULL, NULL); + } + + return png_ptr; +} + + +/* Write a few rows of image data. If the image is interlaced, + * either you will have to write the 7 sub images, or, if you + * have called png_set_interlace_handling(), you will have to + * "write" the image seven times. + */ +void PNGAPI +png_write_rows(png_structrp png_ptr, png_bytepp row, + png_uint_32 num_rows) +{ + png_uint_32 i; /* row counter */ + png_bytepp rp; /* row pointer */ + + png_debug(1, "in png_write_rows"); + + if (png_ptr == NULL) + return; + + /* Loop through the rows */ + for (i = 0, rp = row; i < num_rows; i++, rp++) + { + png_write_row(png_ptr, *rp); + } +} + +/* Write the image. You only need to call this function once, even + * if you are writing an interlaced image. + */ +void PNGAPI +png_write_image(png_structrp png_ptr, png_bytepp image) +{ + png_uint_32 i; /* row index */ + int pass, num_pass; /* pass variables */ + png_bytepp rp; /* points to current row */ + + if (png_ptr == NULL) + return; + + png_debug(1, "in png_write_image"); + +#ifdef PNG_WRITE_INTERLACING_SUPPORTED + /* Initialize interlace handling. If image is not interlaced, + * this will set pass to 1 + */ + num_pass = png_set_interlace_handling(png_ptr); +#else + num_pass = 1; +#endif + /* Loop through passes */ + for (pass = 0; pass < num_pass; pass++) + { + /* Loop through image */ + for (i = 0, rp = image; i < png_ptr->height; i++, rp++) + { + png_write_row(png_ptr, *rp); + } + } +} + +/* Called by user to write a row of image data */ +void PNGAPI +png_write_row(png_structrp png_ptr, png_const_bytep row) +{ + /* 1.5.6: moved from png_struct to be a local structure: */ + png_row_info row_info; + + if (png_ptr == NULL) + return; + + png_debug2(1, "in png_write_row (row %u, pass %d)", + png_ptr->row_number, png_ptr->pass); + + /* Initialize transformations and other stuff if first time */ + if (png_ptr->row_number == 0 && png_ptr->pass == 0) + { + /* Make sure we wrote the header info */ + if (!(png_ptr->mode & PNG_WROTE_INFO_BEFORE_PLTE)) + png_error(png_ptr, + "png_write_info was never called before png_write_row"); + + /* Check for transforms that have been set but were defined out */ +#if !defined(PNG_WRITE_INVERT_SUPPORTED) && defined(PNG_READ_INVERT_SUPPORTED) + if (png_ptr->transformations & PNG_INVERT_MONO) + png_warning(png_ptr, "PNG_WRITE_INVERT_SUPPORTED is not defined"); +#endif + +#if !defined(PNG_WRITE_FILLER_SUPPORTED) && defined(PNG_READ_FILLER_SUPPORTED) + if (png_ptr->transformations & PNG_FILLER) + png_warning(png_ptr, "PNG_WRITE_FILLER_SUPPORTED is not defined"); +#endif +#if !defined(PNG_WRITE_PACKSWAP_SUPPORTED) && \ + defined(PNG_READ_PACKSWAP_SUPPORTED) + if (png_ptr->transformations & PNG_PACKSWAP) + png_warning(png_ptr, + "PNG_WRITE_PACKSWAP_SUPPORTED is not defined"); +#endif + +#if !defined(PNG_WRITE_PACK_SUPPORTED) && defined(PNG_READ_PACK_SUPPORTED) + if (png_ptr->transformations & PNG_PACK) + png_warning(png_ptr, "PNG_WRITE_PACK_SUPPORTED is not defined"); +#endif + +#if !defined(PNG_WRITE_SHIFT_SUPPORTED) && defined(PNG_READ_SHIFT_SUPPORTED) + if (png_ptr->transformations & PNG_SHIFT) + png_warning(png_ptr, "PNG_WRITE_SHIFT_SUPPORTED is not defined"); +#endif + +#if !defined(PNG_WRITE_BGR_SUPPORTED) && defined(PNG_READ_BGR_SUPPORTED) + if (png_ptr->transformations & PNG_BGR) + png_warning(png_ptr, "PNG_WRITE_BGR_SUPPORTED is not defined"); +#endif + +#if !defined(PNG_WRITE_SWAP_SUPPORTED) && defined(PNG_READ_SWAP_SUPPORTED) + if (png_ptr->transformations & PNG_SWAP_BYTES) + png_warning(png_ptr, "PNG_WRITE_SWAP_SUPPORTED is not defined"); +#endif + + png_write_start_row(png_ptr); + } + +#ifdef PNG_WRITE_INTERLACING_SUPPORTED + /* If interlaced and not interested in row, return */ + if (png_ptr->interlaced && (png_ptr->transformations & PNG_INTERLACE)) + { + switch (png_ptr->pass) + { + case 0: + if (png_ptr->row_number & 0x07) + { + png_write_finish_row(png_ptr); + return; + } + break; + + case 1: + if ((png_ptr->row_number & 0x07) || png_ptr->width < 5) + { + png_write_finish_row(png_ptr); + return; + } + break; + + case 2: + if ((png_ptr->row_number & 0x07) != 4) + { + png_write_finish_row(png_ptr); + return; + } + break; + + case 3: + if ((png_ptr->row_number & 0x03) || png_ptr->width < 3) + { + png_write_finish_row(png_ptr); + return; + } + break; + + case 4: + if ((png_ptr->row_number & 0x03) != 2) + { + png_write_finish_row(png_ptr); + return; + } + break; + + case 5: + if ((png_ptr->row_number & 0x01) || png_ptr->width < 2) + { + png_write_finish_row(png_ptr); + return; + } + break; + + case 6: + if (!(png_ptr->row_number & 0x01)) + { + png_write_finish_row(png_ptr); + return; + } + break; + + default: /* error: ignore it */ + break; + } + } +#endif + + /* Set up row info for transformations */ + row_info.color_type = png_ptr->color_type; + row_info.width = png_ptr->usr_width; + row_info.channels = png_ptr->usr_channels; + row_info.bit_depth = png_ptr->usr_bit_depth; + row_info.pixel_depth = (png_byte)(row_info.bit_depth * row_info.channels); + row_info.rowbytes = PNG_ROWBYTES(row_info.pixel_depth, row_info.width); + + png_debug1(3, "row_info->color_type = %d", row_info.color_type); + png_debug1(3, "row_info->width = %u", row_info.width); + png_debug1(3, "row_info->channels = %d", row_info.channels); + png_debug1(3, "row_info->bit_depth = %d", row_info.bit_depth); + png_debug1(3, "row_info->pixel_depth = %d", row_info.pixel_depth); + png_debug1(3, "row_info->rowbytes = %lu", (unsigned long)row_info.rowbytes); + + /* Copy user's row into buffer, leaving room for filter byte. */ + memcpy(png_ptr->row_buf + 1, row, row_info.rowbytes); + +#ifdef PNG_WRITE_INTERLACING_SUPPORTED + /* Handle interlacing */ + if (png_ptr->interlaced && png_ptr->pass < 6 && + (png_ptr->transformations & PNG_INTERLACE)) + { + png_do_write_interlace(&row_info, png_ptr->row_buf + 1, png_ptr->pass); + /* This should always get caught above, but still ... */ + if (!(row_info.width)) + { + png_write_finish_row(png_ptr); + return; + } + } +#endif + +#ifdef PNG_WRITE_TRANSFORMS_SUPPORTED + /* Handle other transformations */ + if (png_ptr->transformations) + png_do_write_transformations(png_ptr, &row_info); +#endif + + /* At this point the row_info pixel depth must match the 'transformed' depth, + * which is also the output depth. + */ + if (row_info.pixel_depth != png_ptr->pixel_depth || + row_info.pixel_depth != png_ptr->transformed_pixel_depth) + png_error(png_ptr, "internal write transform logic error"); + +#ifdef PNG_MNG_FEATURES_SUPPORTED + /* Write filter_method 64 (intrapixel differencing) only if + * 1. Libpng was compiled with PNG_MNG_FEATURES_SUPPORTED and + * 2. Libpng did not write a PNG signature (this filter_method is only + * used in PNG datastreams that are embedded in MNG datastreams) and + * 3. The application called png_permit_mng_features with a mask that + * included PNG_FLAG_MNG_FILTER_64 and + * 4. The filter_method is 64 and + * 5. The color_type is RGB or RGBA + */ + if ((png_ptr->mng_features_permitted & PNG_FLAG_MNG_FILTER_64) && + (png_ptr->filter_type == PNG_INTRAPIXEL_DIFFERENCING)) + { + /* Intrapixel differencing */ + png_do_write_intrapixel(&row_info, png_ptr->row_buf + 1); + } +#endif + +/* Added at libpng-1.5.10 */ +#ifdef PNG_WRITE_CHECK_FOR_INVALID_INDEX_SUPPORTED + /* Check for out-of-range palette index */ + if (row_info.color_type == PNG_COLOR_TYPE_PALETTE && + png_ptr->num_palette_max >= 0) + png_do_check_palette_indexes(png_ptr, &row_info); +#endif + + /* Find a filter if necessary, filter the row and write it out. */ + png_write_find_filter(png_ptr, &row_info); + + if (png_ptr->write_row_fn != NULL) + (*(png_ptr->write_row_fn))(png_ptr, png_ptr->row_number, png_ptr->pass); +} + +#ifdef PNG_WRITE_FLUSH_SUPPORTED +/* Set the automatic flush interval or 0 to turn flushing off */ +void PNGAPI +png_set_flush(png_structrp png_ptr, int nrows) +{ + png_debug(1, "in png_set_flush"); + + if (png_ptr == NULL) + return; + + png_ptr->flush_dist = (nrows < 0 ? 0 : nrows); +} + +/* Flush the current output buffers now */ +void PNGAPI +png_write_flush(png_structrp png_ptr) +{ + png_debug(1, "in png_write_flush"); + + if (png_ptr == NULL) + return; + + /* We have already written out all of the data */ + if (png_ptr->row_number >= png_ptr->num_rows) + return; + + png_compress_IDAT(png_ptr, NULL, 0, Z_SYNC_FLUSH); + png_ptr->flush_rows = 0; + png_flush(png_ptr); +} +#endif /* PNG_WRITE_FLUSH_SUPPORTED */ + +#ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED +static void png_reset_filter_heuristics(png_structrp png_ptr);/* forward decl */ +#endif + +/* Free any memory used in png_ptr struct without freeing the struct itself. */ +static void +png_write_destroy(png_structrp png_ptr) +{ + png_debug(1, "in png_write_destroy"); + + /* Free any memory zlib uses */ + if (png_ptr->flags & PNG_FLAG_ZSTREAM_INITIALIZED) + deflateEnd(&png_ptr->zstream); + + /* Free our memory. png_free checks NULL for us. */ + png_free_buffer_list(png_ptr, &png_ptr->zbuffer_list); + png_free(png_ptr, png_ptr->row_buf); +#ifdef PNG_WRITE_FILTER_SUPPORTED + png_free(png_ptr, png_ptr->prev_row); + png_free(png_ptr, png_ptr->sub_row); + png_free(png_ptr, png_ptr->up_row); + png_free(png_ptr, png_ptr->avg_row); + png_free(png_ptr, png_ptr->paeth_row); +#endif + +#ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED + /* Use this to save a little code space, it doesn't free the filter_costs */ + png_reset_filter_heuristics(png_ptr); + png_free(png_ptr, png_ptr->filter_costs); + png_free(png_ptr, png_ptr->inv_filter_costs); +#endif + +#ifdef PNG_SET_UNKNOWN_CHUNKS_SUPPORTED + png_free(png_ptr, png_ptr->chunk_list); +#endif + + /* The error handling and memory handling information is left intact at this + * point: the jmp_buf may still have to be freed. See png_destroy_png_struct + * for how this happens. + */ +} + +/* Free all memory used by the write. + * In libpng 1.6.0 this API changed quietly to no longer accept a NULL value for + * *png_ptr_ptr. Prior to 1.6.0 it would accept such a value and it would free + * the passed in info_structs but it would quietly fail to free any of the data + * inside them. In 1.6.0 it quietly does nothing (it has to be quiet because it + * has no png_ptr.) + */ +void PNGAPI +png_destroy_write_struct(png_structpp png_ptr_ptr, png_infopp info_ptr_ptr) +{ + png_debug(1, "in png_destroy_write_struct"); + + if (png_ptr_ptr != NULL) + { + png_structrp png_ptr = *png_ptr_ptr; + + if (png_ptr != NULL) /* added in libpng 1.6.0 */ + { + png_destroy_info_struct(png_ptr, info_ptr_ptr); + + *png_ptr_ptr = NULL; + png_write_destroy(png_ptr); + png_destroy_png_struct(png_ptr); + } + } +} + +/* Allow the application to select one or more row filters to use. */ +void PNGAPI +png_set_filter(png_structrp png_ptr, int method, int filters) +{ + png_debug(1, "in png_set_filter"); + + if (png_ptr == NULL) + return; + +#ifdef PNG_MNG_FEATURES_SUPPORTED + if ((png_ptr->mng_features_permitted & PNG_FLAG_MNG_FILTER_64) && + (method == PNG_INTRAPIXEL_DIFFERENCING)) + method = PNG_FILTER_TYPE_BASE; + +#endif + if (method == PNG_FILTER_TYPE_BASE) + { + switch (filters & (PNG_ALL_FILTERS | 0x07)) + { +#ifdef PNG_WRITE_FILTER_SUPPORTED + case 5: + case 6: + case 7: png_app_error(png_ptr, "Unknown row filter for method 0"); + /* FALL THROUGH */ +#endif /* PNG_WRITE_FILTER_SUPPORTED */ + case PNG_FILTER_VALUE_NONE: + png_ptr->do_filter = PNG_FILTER_NONE; break; + +#ifdef PNG_WRITE_FILTER_SUPPORTED + case PNG_FILTER_VALUE_SUB: + png_ptr->do_filter = PNG_FILTER_SUB; break; + + case PNG_FILTER_VALUE_UP: + png_ptr->do_filter = PNG_FILTER_UP; break; + + case PNG_FILTER_VALUE_AVG: + png_ptr->do_filter = PNG_FILTER_AVG; break; + + case PNG_FILTER_VALUE_PAETH: + png_ptr->do_filter = PNG_FILTER_PAETH; break; + + default: + png_ptr->do_filter = (png_byte)filters; break; +#else + default: + png_app_error(png_ptr, "Unknown row filter for method 0"); +#endif /* PNG_WRITE_FILTER_SUPPORTED */ + } + + /* If we have allocated the row_buf, this means we have already started + * with the image and we should have allocated all of the filter buffers + * that have been selected. If prev_row isn't already allocated, then + * it is too late to start using the filters that need it, since we + * will be missing the data in the previous row. If an application + * wants to start and stop using particular filters during compression, + * it should start out with all of the filters, and then add and + * remove them after the start of compression. + */ + if (png_ptr->row_buf != NULL) + { +#ifdef PNG_WRITE_FILTER_SUPPORTED + if ((png_ptr->do_filter & PNG_FILTER_SUB) && png_ptr->sub_row == NULL) + { + png_ptr->sub_row = (png_bytep)png_malloc(png_ptr, + (png_ptr->rowbytes + 1)); + png_ptr->sub_row[0] = PNG_FILTER_VALUE_SUB; + } + + if ((png_ptr->do_filter & PNG_FILTER_UP) && png_ptr->up_row == NULL) + { + if (png_ptr->prev_row == NULL) + { + png_warning(png_ptr, "Can't add Up filter after starting"); + png_ptr->do_filter = (png_byte)(png_ptr->do_filter & + ~PNG_FILTER_UP); + } + + else + { + png_ptr->up_row = (png_bytep)png_malloc(png_ptr, + (png_ptr->rowbytes + 1)); + png_ptr->up_row[0] = PNG_FILTER_VALUE_UP; + } + } + + if ((png_ptr->do_filter & PNG_FILTER_AVG) && png_ptr->avg_row == NULL) + { + if (png_ptr->prev_row == NULL) + { + png_warning(png_ptr, "Can't add Average filter after starting"); + png_ptr->do_filter = (png_byte)(png_ptr->do_filter & + ~PNG_FILTER_AVG); + } + + else + { + png_ptr->avg_row = (png_bytep)png_malloc(png_ptr, + (png_ptr->rowbytes + 1)); + png_ptr->avg_row[0] = PNG_FILTER_VALUE_AVG; + } + } + + if ((png_ptr->do_filter & PNG_FILTER_PAETH) && + png_ptr->paeth_row == NULL) + { + if (png_ptr->prev_row == NULL) + { + png_warning(png_ptr, "Can't add Paeth filter after starting"); + png_ptr->do_filter &= (png_byte)(~PNG_FILTER_PAETH); + } + + else + { + png_ptr->paeth_row = (png_bytep)png_malloc(png_ptr, + (png_ptr->rowbytes + 1)); + png_ptr->paeth_row[0] = PNG_FILTER_VALUE_PAETH; + } + } + + if (png_ptr->do_filter == PNG_NO_FILTERS) +#endif /* PNG_WRITE_FILTER_SUPPORTED */ + png_ptr->do_filter = PNG_FILTER_NONE; + } + } + else + png_error(png_ptr, "Unknown custom filter method"); +} + +/* This allows us to influence the way in which libpng chooses the "best" + * filter for the current scanline. While the "minimum-sum-of-absolute- + * differences metric is relatively fast and effective, there is some + * question as to whether it can be improved upon by trying to keep the + * filtered data going to zlib more consistent, hopefully resulting in + * better compression. + */ +#ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED /* GRR 970116 */ +/* Convenience reset API. */ +static void +png_reset_filter_heuristics(png_structrp png_ptr) +{ + /* Clear out any old values in the 'weights' - this must be done because if + * the app calls set_filter_heuristics multiple times with different + * 'num_weights' values we would otherwise potentially have wrong sized + * arrays. + */ + png_ptr->num_prev_filters = 0; + png_ptr->heuristic_method = PNG_FILTER_HEURISTIC_UNWEIGHTED; + if (png_ptr->prev_filters != NULL) + { + png_bytep old = png_ptr->prev_filters; + png_ptr->prev_filters = NULL; + png_free(png_ptr, old); + } + if (png_ptr->filter_weights != NULL) + { + png_uint_16p old = png_ptr->filter_weights; + png_ptr->filter_weights = NULL; + png_free(png_ptr, old); + } + + if (png_ptr->inv_filter_weights != NULL) + { + png_uint_16p old = png_ptr->inv_filter_weights; + png_ptr->inv_filter_weights = NULL; + png_free(png_ptr, old); + } + + /* Leave the filter_costs - this array is fixed size. */ +} + +static int +png_init_filter_heuristics(png_structrp png_ptr, int heuristic_method, + int num_weights) +{ + if (png_ptr == NULL) + return 0; + + /* Clear out the arrays */ + png_reset_filter_heuristics(png_ptr); + + /* Check arguments; the 'reset' function makes the correct settings for the + * unweighted case, but we must handle the weight case by initializing the + * arrays for the caller. + */ + if (heuristic_method == PNG_FILTER_HEURISTIC_WEIGHTED) + { + int i; + + if (num_weights > 0) + { + png_ptr->prev_filters = (png_bytep)png_malloc(png_ptr, + (png_uint_32)((sizeof (png_byte)) * num_weights)); + + /* To make sure that the weighting starts out fairly */ + for (i = 0; i < num_weights; i++) + { + png_ptr->prev_filters[i] = 255; + } + + png_ptr->filter_weights = (png_uint_16p)png_malloc(png_ptr, + (png_uint_32)((sizeof (png_uint_16)) * num_weights)); + + png_ptr->inv_filter_weights = (png_uint_16p)png_malloc(png_ptr, + (png_uint_32)((sizeof (png_uint_16)) * num_weights)); + + for (i = 0; i < num_weights; i++) + { + png_ptr->inv_filter_weights[i] = + png_ptr->filter_weights[i] = PNG_WEIGHT_FACTOR; + } + + /* Safe to set this now */ + png_ptr->num_prev_filters = (png_byte)num_weights; + } + + /* If, in the future, there are other filter methods, this would + * need to be based on png_ptr->filter. + */ + if (png_ptr->filter_costs == NULL) + { + png_ptr->filter_costs = (png_uint_16p)png_malloc(png_ptr, + (png_uint_32)((sizeof (png_uint_16)) * PNG_FILTER_VALUE_LAST)); + + png_ptr->inv_filter_costs = (png_uint_16p)png_malloc(png_ptr, + (png_uint_32)((sizeof (png_uint_16)) * PNG_FILTER_VALUE_LAST)); + } + + for (i = 0; i < PNG_FILTER_VALUE_LAST; i++) + { + png_ptr->inv_filter_costs[i] = + png_ptr->filter_costs[i] = PNG_COST_FACTOR; + } + + /* All the arrays are inited, safe to set this: */ + png_ptr->heuristic_method = PNG_FILTER_HEURISTIC_WEIGHTED; + + /* Return the 'ok' code. */ + return 1; + } + else if (heuristic_method == PNG_FILTER_HEURISTIC_DEFAULT || + heuristic_method == PNG_FILTER_HEURISTIC_UNWEIGHTED) + { + return 1; + } + else + { + png_warning(png_ptr, "Unknown filter heuristic method"); + return 0; + } +} + +/* Provide floating and fixed point APIs */ +#ifdef PNG_FLOATING_POINT_SUPPORTED +void PNGAPI +png_set_filter_heuristics(png_structrp png_ptr, int heuristic_method, + int num_weights, png_const_doublep filter_weights, + png_const_doublep filter_costs) +{ + png_debug(1, "in png_set_filter_heuristics"); + + /* The internal API allocates all the arrays and ensures that the elements of + * those arrays are set to the default value. + */ + if (!png_init_filter_heuristics(png_ptr, heuristic_method, num_weights)) + return; + + /* If using the weighted method copy in the weights. */ + if (heuristic_method == PNG_FILTER_HEURISTIC_WEIGHTED) + { + int i; + for (i = 0; i < num_weights; i++) + { + if (filter_weights[i] <= 0.0) + { + png_ptr->inv_filter_weights[i] = + png_ptr->filter_weights[i] = PNG_WEIGHT_FACTOR; + } + + else + { + png_ptr->inv_filter_weights[i] = + (png_uint_16)(PNG_WEIGHT_FACTOR*filter_weights[i]+.5); + + png_ptr->filter_weights[i] = + (png_uint_16)(PNG_WEIGHT_FACTOR/filter_weights[i]+.5); + } + } + + /* Here is where we set the relative costs of the different filters. We + * should take the desired compression level into account when setting + * the costs, so that Paeth, for instance, has a high relative cost at low + * compression levels, while it has a lower relative cost at higher + * compression settings. The filter types are in order of increasing + * relative cost, so it would be possible to do this with an algorithm. + */ + for (i = 0; i < PNG_FILTER_VALUE_LAST; i++) if (filter_costs[i] >= 1.0) + { + png_ptr->inv_filter_costs[i] = + (png_uint_16)(PNG_COST_FACTOR / filter_costs[i] + .5); + + png_ptr->filter_costs[i] = + (png_uint_16)(PNG_COST_FACTOR * filter_costs[i] + .5); + } + } +} +#endif /* FLOATING_POINT */ + +#ifdef PNG_FIXED_POINT_SUPPORTED +void PNGAPI +png_set_filter_heuristics_fixed(png_structrp png_ptr, int heuristic_method, + int num_weights, png_const_fixed_point_p filter_weights, + png_const_fixed_point_p filter_costs) +{ + png_debug(1, "in png_set_filter_heuristics_fixed"); + + /* The internal API allocates all the arrays and ensures that the elements of + * those arrays are set to the default value. + */ + if (!png_init_filter_heuristics(png_ptr, heuristic_method, num_weights)) + return; + + /* If using the weighted method copy in the weights. */ + if (heuristic_method == PNG_FILTER_HEURISTIC_WEIGHTED) + { + int i; + for (i = 0; i < num_weights; i++) + { + if (filter_weights[i] <= 0) + { + png_ptr->inv_filter_weights[i] = + png_ptr->filter_weights[i] = PNG_WEIGHT_FACTOR; + } + + else + { + png_ptr->inv_filter_weights[i] = (png_uint_16) + ((PNG_WEIGHT_FACTOR*filter_weights[i]+PNG_FP_HALF)/PNG_FP_1); + + png_ptr->filter_weights[i] = (png_uint_16)((PNG_WEIGHT_FACTOR* + PNG_FP_1+(filter_weights[i]/2))/filter_weights[i]); + } + } + + /* Here is where we set the relative costs of the different filters. We + * should take the desired compression level into account when setting + * the costs, so that Paeth, for instance, has a high relative cost at low + * compression levels, while it has a lower relative cost at higher + * compression settings. The filter types are in order of increasing + * relative cost, so it would be possible to do this with an algorithm. + */ + for (i = 0; i < PNG_FILTER_VALUE_LAST; i++) + if (filter_costs[i] >= PNG_FP_1) + { + png_uint_32 tmp; + + /* Use a 32 bit unsigned temporary here because otherwise the + * intermediate value will be a 32 bit *signed* integer (ANSI rules) + * and this will get the wrong answer on division. + */ + tmp = PNG_COST_FACTOR*PNG_FP_1 + (filter_costs[i]/2); + tmp /= filter_costs[i]; + + png_ptr->inv_filter_costs[i] = (png_uint_16)tmp; + + tmp = PNG_COST_FACTOR * filter_costs[i] + PNG_FP_HALF; + tmp /= PNG_FP_1; + + png_ptr->filter_costs[i] = (png_uint_16)tmp; + } + } +} +#endif /* FIXED_POINT */ +#endif /* PNG_WRITE_WEIGHTED_FILTER_SUPPORTED */ + +void PNGAPI +png_set_compression_level(png_structrp png_ptr, int level) +{ + png_debug(1, "in png_set_compression_level"); + + if (png_ptr == NULL) + return; + + png_ptr->zlib_level = level; +} + +void PNGAPI +png_set_compression_mem_level(png_structrp png_ptr, int mem_level) +{ + png_debug(1, "in png_set_compression_mem_level"); + + if (png_ptr == NULL) + return; + + png_ptr->zlib_mem_level = mem_level; +} + +void PNGAPI +png_set_compression_strategy(png_structrp png_ptr, int strategy) +{ + png_debug(1, "in png_set_compression_strategy"); + + if (png_ptr == NULL) + return; + + /* The flag setting here prevents the libpng dynamic selection of strategy. + */ + png_ptr->flags |= PNG_FLAG_ZLIB_CUSTOM_STRATEGY; + png_ptr->zlib_strategy = strategy; +} + +/* If PNG_WRITE_OPTIMIZE_CMF_SUPPORTED is defined, libpng will use a + * smaller value of window_bits if it can do so safely. + */ +void PNGAPI +png_set_compression_window_bits(png_structrp png_ptr, int window_bits) +{ + if (png_ptr == NULL) + return; + + /* Prior to 1.6.0 this would warn but then set the window_bits value, this + * meant that negative window bits values could be selected which would cause + * libpng to write a non-standard PNG file with raw deflate or gzip + * compressed IDAT or ancillary chunks. Such files can be read and there is + * no warning on read, so this seems like a very bad idea. + */ + if (window_bits > 15) + { + png_warning(png_ptr, "Only compression windows <= 32k supported by PNG"); + window_bits = 15; + } + + else if (window_bits < 8) + { + png_warning(png_ptr, "Only compression windows >= 256 supported by PNG"); + window_bits = 8; + } + + png_ptr->zlib_window_bits = window_bits; +} + +void PNGAPI +png_set_compression_method(png_structrp png_ptr, int method) +{ + png_debug(1, "in png_set_compression_method"); + + if (png_ptr == NULL) + return; + + /* This would produce an invalid PNG file if it worked, but it doesn't and + * deflate will fault it, so it is harmless to just warn here. + */ + if (method != 8) + png_warning(png_ptr, "Only compression method 8 is supported by PNG"); + + png_ptr->zlib_method = method; +} + +/* The following were added to libpng-1.5.4 */ +#ifdef PNG_WRITE_CUSTOMIZE_ZTXT_COMPRESSION_SUPPORTED +void PNGAPI +png_set_text_compression_level(png_structrp png_ptr, int level) +{ + png_debug(1, "in png_set_text_compression_level"); + + if (png_ptr == NULL) + return; + + png_ptr->zlib_text_level = level; +} + +void PNGAPI +png_set_text_compression_mem_level(png_structrp png_ptr, int mem_level) +{ + png_debug(1, "in png_set_text_compression_mem_level"); + + if (png_ptr == NULL) + return; + + png_ptr->zlib_text_mem_level = mem_level; +} + +void PNGAPI +png_set_text_compression_strategy(png_structrp png_ptr, int strategy) +{ + png_debug(1, "in png_set_text_compression_strategy"); + + if (png_ptr == NULL) + return; + + png_ptr->zlib_text_strategy = strategy; +} + +/* If PNG_WRITE_OPTIMIZE_CMF_SUPPORTED is defined, libpng will use a + * smaller value of window_bits if it can do so safely. + */ +void PNGAPI +png_set_text_compression_window_bits(png_structrp png_ptr, int window_bits) +{ + if (png_ptr == NULL) + return; + + if (window_bits > 15) + { + png_warning(png_ptr, "Only compression windows <= 32k supported by PNG"); + window_bits = 15; + } + + else if (window_bits < 8) + { + png_warning(png_ptr, "Only compression windows >= 256 supported by PNG"); + window_bits = 8; + } + + png_ptr->zlib_text_window_bits = window_bits; +} + +void PNGAPI +png_set_text_compression_method(png_structrp png_ptr, int method) +{ + png_debug(1, "in png_set_text_compression_method"); + + if (png_ptr == NULL) + return; + + if (method != 8) + png_warning(png_ptr, "Only compression method 8 is supported by PNG"); + + png_ptr->zlib_text_method = method; +} +#endif /* PNG_WRITE_CUSTOMIZE_ZTXT_COMPRESSION_SUPPORTED */ +/* end of API added to libpng-1.5.4 */ + +void PNGAPI +png_set_write_status_fn(png_structrp png_ptr, png_write_status_ptr write_row_fn) +{ + if (png_ptr == NULL) + return; + + png_ptr->write_row_fn = write_row_fn; +} + +#ifdef PNG_WRITE_USER_TRANSFORM_SUPPORTED +void PNGAPI +png_set_write_user_transform_fn(png_structrp png_ptr, png_user_transform_ptr + write_user_transform_fn) +{ + png_debug(1, "in png_set_write_user_transform_fn"); + + if (png_ptr == NULL) + return; + + png_ptr->transformations |= PNG_USER_TRANSFORM; + png_ptr->write_user_transform_fn = write_user_transform_fn; +} +#endif + + +#ifdef PNG_INFO_IMAGE_SUPPORTED +void PNGAPI +png_write_png(png_structrp png_ptr, png_inforp info_ptr, + int transforms, voidp params) +{ + if (png_ptr == NULL || info_ptr == NULL) + return; + + /* Write the file header information. */ + png_write_info(png_ptr, info_ptr); + + /* ------ these transformations don't touch the info structure ------- */ + +#ifdef PNG_WRITE_INVERT_SUPPORTED + /* Invert monochrome pixels */ + if (transforms & PNG_TRANSFORM_INVERT_MONO) + png_set_invert_mono(png_ptr); +#endif + +#ifdef PNG_WRITE_SHIFT_SUPPORTED + /* Shift the pixels up to a legal bit depth and fill in + * as appropriate to correctly scale the image. + */ + if ((transforms & PNG_TRANSFORM_SHIFT) + && (info_ptr->valid & PNG_INFO_sBIT)) + png_set_shift(png_ptr, &info_ptr->sig_bit); +#endif + +#ifdef PNG_WRITE_PACK_SUPPORTED + /* Pack pixels into bytes */ + if (transforms & PNG_TRANSFORM_PACKING) + png_set_packing(png_ptr); +#endif + +#ifdef PNG_WRITE_SWAP_ALPHA_SUPPORTED + /* Swap location of alpha bytes from ARGB to RGBA */ + if (transforms & PNG_TRANSFORM_SWAP_ALPHA) + png_set_swap_alpha(png_ptr); +#endif + +#ifdef PNG_WRITE_FILLER_SUPPORTED + /* Pack XRGB/RGBX/ARGB/RGBA into RGB (4 channels -> 3 channels) */ + if (transforms & PNG_TRANSFORM_STRIP_FILLER_AFTER) + png_set_filler(png_ptr, 0, PNG_FILLER_AFTER); + + else if (transforms & PNG_TRANSFORM_STRIP_FILLER_BEFORE) + png_set_filler(png_ptr, 0, PNG_FILLER_BEFORE); +#endif + +#ifdef PNG_WRITE_BGR_SUPPORTED + /* Flip BGR pixels to RGB */ + if (transforms & PNG_TRANSFORM_BGR) + png_set_bgr(png_ptr); +#endif + +#ifdef PNG_WRITE_SWAP_SUPPORTED + /* Swap bytes of 16-bit files to most significant byte first */ + if (transforms & PNG_TRANSFORM_SWAP_ENDIAN) + png_set_swap(png_ptr); +#endif + +#ifdef PNG_WRITE_PACKSWAP_SUPPORTED + /* Swap bits of 1, 2, 4 bit packed pixel formats */ + if (transforms & PNG_TRANSFORM_PACKSWAP) + png_set_packswap(png_ptr); +#endif + +#ifdef PNG_WRITE_INVERT_ALPHA_SUPPORTED + /* Invert the alpha channel from opacity to transparency */ + if (transforms & PNG_TRANSFORM_INVERT_ALPHA) + png_set_invert_alpha(png_ptr); +#endif + + /* ----------------------- end of transformations ------------------- */ + + /* Write the bits */ + if (info_ptr->valid & PNG_INFO_IDAT) + png_write_image(png_ptr, info_ptr->row_pointers); + + /* It is REQUIRED to call this to finish writing the rest of the file */ + png_write_end(png_ptr, info_ptr); + + PNG_UNUSED(transforms) /* Quiet compiler warnings */ + PNG_UNUSED(params) +} +#endif + + +#ifdef PNG_SIMPLIFIED_WRITE_SUPPORTED +#ifdef PNG_STDIO_SUPPORTED /* currently required for png_image_write_* */ +/* Initialize the write structure - general purpose utility. */ +static int +// 4j studios: callback support +png_image_write_init(png_imagep image, /*begin 4j change*/ png_rw_ptr write_fn, png_flush_ptr flush_fn /*end 4j change*/) +{ + png_structp png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, image, + png_safe_error, png_safe_warning); + + if (png_ptr != NULL) + { + png_infop info_ptr = png_create_info_struct(png_ptr); + + /*begin 4j change*/ + if (write_fn != NULL) + png_set_write_fn(png_ptr, NULL, write_fn, flush_fn); + /*end 4j change*/ + + if (info_ptr != NULL) + { + png_controlp control = png_voidcast(png_controlp, + png_malloc_warn(png_ptr, (sizeof *control))); + + if (control != NULL) + { + memset(control, 0, (sizeof *control)); + + control->png_ptr = png_ptr; + control->info_ptr = info_ptr; + control->for_write = 1; + + image->opaque = control; + return 1; + } + + /* Error clean up */ + png_destroy_info_struct(png_ptr, &info_ptr); + } + + png_destroy_write_struct(&png_ptr, NULL); + } + + return png_image_error(image, "png_image_write_: out of memory"); +} + +/* Arguments to png_image_write_main: */ +typedef struct +{ + /* Arguments: */ + png_imagep image; + png_const_voidp buffer; + png_int_32 row_stride; + png_const_voidp colormap; + int convert_to_8bit; + /* Local variables: */ + png_const_voidp first_row; + ptrdiff_t row_bytes; + png_voidp local_row; +} png_image_write_control; + +/* Write png_uint_16 input to a 16-bit PNG; the png_ptr has already been set to + * do any necessary byte swapping. The component order is defined by the + * png_image format value. + */ +static int +png_write_image_16bit(png_voidp argument) +{ + png_image_write_control *display = png_voidcast(png_image_write_control*, + argument); + png_imagep image = display->image; + png_structrp png_ptr = image->opaque->png_ptr; + + png_const_uint_16p input_row = png_voidcast(png_const_uint_16p, + display->first_row); + png_uint_16p output_row = png_voidcast(png_uint_16p, display->local_row); + png_uint_16p row_end; + const int channels = (image->format & PNG_FORMAT_FLAG_COLOR) ? 3 : 1; + int aindex = 0; + png_uint_32 y = image->height; + + if (image->format & PNG_FORMAT_FLAG_ALPHA) + { + if (image->format & PNG_FORMAT_FLAG_AFIRST) + { + aindex = -1; + ++input_row; /* To point to the first component */ + ++output_row; + } + + else + aindex = channels; + } + + else + png_error(png_ptr, "png_write_image: internal call error"); + + /* Work out the output row end and count over this, note that the increment + * above to 'row' means that row_end can actually be beyond the end of the + * row; this is correct. + */ + row_end = output_row + image->width * (channels+1); + + while (y-- > 0) + { + png_const_uint_16p in_ptr = input_row; + png_uint_16p out_ptr = output_row; + + while (out_ptr < row_end) + { + const png_uint_16 alpha = in_ptr[aindex]; + png_uint_32 reciprocal = 0; + int c; + + out_ptr[aindex] = alpha; + + /* Calculate a reciprocal. The correct calculation is simply + * component/alpha*65535 << 15. (I.e. 15 bits of precision); this + * allows correct rounding by adding .5 before the shift. 'reciprocal' + * is only initialized when required. + */ + if (alpha > 0 && alpha < 65535) + reciprocal = ((0xffff<<15)+(alpha>>1))/alpha; + + c = channels; + do /* always at least one channel */ + { + png_uint_16 component = *in_ptr++; + + /* The following gives 65535 for an alpha of 0, which is fine, + * otherwise if 0/0 is represented as some other value there is more + * likely to be a discontinuity which will probably damage + * compression when moving from a fully transparent area to a + * nearly transparent one. (The assumption here is that opaque + * areas tend not to be 0 intensity.) + */ + if (component >= alpha) + component = 65535; + + /* component 0 && alpha < 65535) + { + png_uint_32 calc = component * reciprocal; + calc += 16384; /* round to nearest */ + component = (png_uint_16)(calc >> 15); + } + + *out_ptr++ = component; + } + while (--c > 0); + + /* Skip to next component (skip the intervening alpha channel) */ + ++in_ptr; + ++out_ptr; + } + + png_write_row(png_ptr, png_voidcast(png_const_bytep, display->local_row)); + input_row += display->row_bytes/(sizeof (png_uint_16)); + } + + return 1; +} + +/* Given 16-bit input (1 to 4 channels) write 8-bit output. If an alpha channel + * is present it must be removed from the components, the components are then + * written in sRGB encoding. No components are added or removed. + * + * Calculate an alpha reciprocal to reverse pre-multiplication. As above the + * calculation can be done to 15 bits of accuracy; however, the output needs to + * be scaled in the range 0..255*65535, so include that scaling here. + */ +#define UNP_RECIPROCAL(alpha) ((((0xffff*0xff)<<7)+(alpha>>1))/alpha) + +static png_byte +png_unpremultiply(png_uint_32 component, png_uint_32 alpha, + png_uint_32 reciprocal/*from the above macro*/) +{ + /* The following gives 1.0 for an alpha of 0, which is fine, otherwise if 0/0 + * is represented as some other value there is more likely to be a + * discontinuity which will probably damage compression when moving from a + * fully transparent area to a nearly transparent one. (The assumption here + * is that opaque areas tend not to be 0 intensity.) + * + * There is a rounding problem here; if alpha is less than 128 it will end up + * as 0 when scaled to 8 bits. To avoid introducing spurious colors into the + * output change for this too. + */ + if (component >= alpha || alpha < 128) + return 255; + + /* component 0) + { + /* The test is that alpha/257 (rounded) is less than 255, the first value + * that becomes 255 is 65407. + * NOTE: this must agree with the PNG_DIV257 macro (which must, therefore, + * be exact!) [Could also test reciprocal != 0] + */ + if (alpha < 65407) + { + component *= reciprocal; + component += 64; /* round to nearest */ + component >>= 7; + } + + else + component *= 255; + + /* Convert the component to sRGB. */ + return (png_byte)PNG_sRGB_FROM_LINEAR(component); + } + + else + return 0; +} + +static int +png_write_image_8bit(png_voidp argument) +{ + png_image_write_control *display = png_voidcast(png_image_write_control*, + argument); + png_imagep image = display->image; + png_structrp png_ptr = image->opaque->png_ptr; + + png_const_uint_16p input_row = png_voidcast(png_const_uint_16p, + display->first_row); + png_bytep output_row = png_voidcast(png_bytep, display->local_row); + png_uint_32 y = image->height; + const int channels = (image->format & PNG_FORMAT_FLAG_COLOR) ? 3 : 1; + + if (image->format & PNG_FORMAT_FLAG_ALPHA) + { + png_bytep row_end; + int aindex; + + if (image->format & PNG_FORMAT_FLAG_AFIRST) + { + aindex = -1; + ++input_row; /* To point to the first component */ + ++output_row; + } + + else + aindex = channels; + + /* Use row_end in place of a loop counter: */ + row_end = output_row + image->width * (channels+1); + + while (y-- > 0) + { + png_const_uint_16p in_ptr = input_row; + png_bytep out_ptr = output_row; + + while (out_ptr < row_end) + { + png_uint_16 alpha = in_ptr[aindex]; + png_byte alphabyte = (png_byte)PNG_DIV257(alpha); + png_uint_32 reciprocal = 0; + int c; + + /* Scale and write the alpha channel. */ + out_ptr[aindex] = alphabyte; + + if (alphabyte > 0 && alphabyte < 255) + reciprocal = UNP_RECIPROCAL(alpha); + + c = channels; + do /* always at least one channel */ + *out_ptr++ = png_unpremultiply(*in_ptr++, alpha, reciprocal); + while (--c > 0); + + /* Skip to next component (skip the intervening alpha channel) */ + ++in_ptr; + ++out_ptr; + } /* while out_ptr < row_end */ + + png_write_row(png_ptr, png_voidcast(png_const_bytep, + display->local_row)); + input_row += display->row_bytes/(sizeof (png_uint_16)); + } /* while y */ + } + + else + { + /* No alpha channel, so the row_end really is the end of the row and it + * is sufficient to loop over the components one by one. + */ + png_bytep row_end = output_row + image->width * channels; + + while (y-- > 0) + { + png_const_uint_16p in_ptr = input_row; + png_bytep out_ptr = output_row; + + while (out_ptr < row_end) + { + png_uint_32 component = *in_ptr++; + + component *= 255; + *out_ptr++ = (png_byte)PNG_sRGB_FROM_LINEAR(component); + } + + png_write_row(png_ptr, output_row); + input_row += display->row_bytes/(sizeof (png_uint_16)); + } + } + + return 1; +} + +static void +png_image_set_PLTE(png_image_write_control *display) +{ + const png_imagep image = display->image; + const void *cmap = display->colormap; + const int entries = image->colormap_entries > 256 ? 256 : + (int)image->colormap_entries; + + /* NOTE: the caller must check for cmap != NULL and entries != 0 */ + const png_uint_32 format = image->format; + const int channels = PNG_IMAGE_SAMPLE_CHANNELS(format); + +# ifdef PNG_FORMAT_BGR_SUPPORTED + const int afirst = (format & PNG_FORMAT_FLAG_AFIRST) != 0 && + (format & PNG_FORMAT_FLAG_ALPHA) != 0; +# else +# define afirst 0 +# endif + +# ifdef PNG_FORMAT_BGR_SUPPORTED + const int bgr = (format & PNG_FORMAT_FLAG_BGR) ? 2 : 0; +# else +# define bgr 0 +# endif + + int i, num_trans; + png_color palette[256]; + png_byte tRNS[256]; + + memset(tRNS, 255, (sizeof tRNS)); + memset(palette, 0, (sizeof palette)); + + for (i=num_trans=0; i= 3) /* RGB */ + { + palette[i].blue = (png_byte)PNG_sRGB_FROM_LINEAR(255 * + entry[(2 ^ bgr)]); + palette[i].green = (png_byte)PNG_sRGB_FROM_LINEAR(255 * + entry[1]); + palette[i].red = (png_byte)PNG_sRGB_FROM_LINEAR(255 * + entry[bgr]); + } + + else /* Gray */ + palette[i].blue = palette[i].red = palette[i].green = + (png_byte)PNG_sRGB_FROM_LINEAR(255 * *entry); + } + + else /* alpha */ + { + png_uint_16 alpha = entry[afirst ? 0 : channels-1]; + png_byte alphabyte = (png_byte)PNG_DIV257(alpha); + png_uint_32 reciprocal = 0; + + /* Calculate a reciprocal, as in the png_write_image_8bit code above + * this is designed to produce a value scaled to 255*65535 when + * divided by 128 (i.e. asr 7). + */ + if (alphabyte > 0 && alphabyte < 255) + reciprocal = (((0xffff*0xff)<<7)+(alpha>>1))/alpha; + + tRNS[i] = alphabyte; + if (alphabyte < 255) + num_trans = i+1; + + if (channels >= 3) /* RGB */ + { + palette[i].blue = png_unpremultiply(entry[afirst + (2 ^ bgr)], + alpha, reciprocal); + palette[i].green = png_unpremultiply(entry[afirst + 1], alpha, + reciprocal); + palette[i].red = png_unpremultiply(entry[afirst + bgr], alpha, + reciprocal); + } + + else /* gray */ + palette[i].blue = palette[i].red = palette[i].green = + png_unpremultiply(entry[afirst], alpha, reciprocal); + } + } + + else /* Color-map has sRGB values */ + { + png_const_bytep entry = png_voidcast(png_const_bytep, cmap); + + entry += i * channels; + + switch (channels) + { + case 4: + tRNS[i] = entry[afirst ? 0 : 3]; + if (tRNS[i] < 255) + num_trans = i+1; + /* FALL THROUGH */ + case 3: + palette[i].blue = entry[afirst + (2 ^ bgr)]; + palette[i].green = entry[afirst + 1]; + palette[i].red = entry[afirst + bgr]; + break; + + case 2: + tRNS[i] = entry[1 ^ afirst]; + if (tRNS[i] < 255) + num_trans = i+1; + /* FALL THROUGH */ + case 1: + palette[i].blue = palette[i].red = palette[i].green = + entry[afirst]; + break; + + default: + break; + } + } + } + +# ifdef afirst +# undef afirst +# endif +# ifdef bgr +# undef bgr +# endif + + png_set_PLTE(image->opaque->png_ptr, image->opaque->info_ptr, palette, + entries); + + if (num_trans > 0) + png_set_tRNS(image->opaque->png_ptr, image->opaque->info_ptr, tRNS, + num_trans, NULL); + + image->colormap_entries = entries; +} + +static int +png_image_write_main(png_voidp argument) +{ + png_image_write_control *display = png_voidcast(png_image_write_control*, + argument); + png_imagep image = display->image; + png_structrp png_ptr = image->opaque->png_ptr; + png_inforp info_ptr = image->opaque->info_ptr; + png_uint_32 format = image->format; + + int colormap = (format & PNG_FORMAT_FLAG_COLORMAP) != 0; + int linear = !colormap && (format & PNG_FORMAT_FLAG_LINEAR) != 0; /* input */ + int alpha = !colormap && (format & PNG_FORMAT_FLAG_ALPHA) != 0; + int write_16bit = linear && !colormap && !display->convert_to_8bit; + +# ifdef PNG_BENIGN_ERRORS_SUPPORTED + /* Make sure we error out on any bad situation */ + png_set_benign_errors(png_ptr, 0/*error*/); +# endif + + /* Default the 'row_stride' parameter if required. */ + if (display->row_stride == 0) + display->row_stride = PNG_IMAGE_ROW_STRIDE(*image); + + /* Set the required transforms then write the rows in the correct order. */ + if (format & PNG_FORMAT_FLAG_COLORMAP) + { + if (display->colormap != NULL && image->colormap_entries > 0) + { + png_uint_32 entries = image->colormap_entries; + + png_set_IHDR(png_ptr, info_ptr, image->width, image->height, + entries > 16 ? 8 : (entries > 4 ? 4 : (entries > 2 ? 2 : 1)), + PNG_COLOR_TYPE_PALETTE, PNG_INTERLACE_NONE, + PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE); + + png_image_set_PLTE(display); + } + + else + png_error(image->opaque->png_ptr, + "no color-map for color-mapped image"); + } + + else + png_set_IHDR(png_ptr, info_ptr, image->width, image->height, + write_16bit ? 16 : 8, + ((format & PNG_FORMAT_FLAG_COLOR) ? PNG_COLOR_MASK_COLOR : 0) + + ((format & PNG_FORMAT_FLAG_ALPHA) ? PNG_COLOR_MASK_ALPHA : 0), + PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE); + + /* Counter-intuitively the data transformations must be called *after* + * png_write_info, not before as in the read code, but the 'set' functions + * must still be called before. Just set the color space information, never + * write an interlaced image. + */ + + if (write_16bit) + { + /* The gamma here is 1.0 (linear) and the cHRM chunk matches sRGB. */ + png_set_gAMA_fixed(png_ptr, info_ptr, PNG_GAMMA_LINEAR); + + if (!(image->flags & PNG_IMAGE_FLAG_COLORSPACE_NOT_sRGB)) + png_set_cHRM_fixed(png_ptr, info_ptr, + /* color x y */ + /* white */ 31270, 32900, + /* red */ 64000, 33000, + /* green */ 30000, 60000, + /* blue */ 15000, 6000 + ); + } + + else if (!(image->flags & PNG_IMAGE_FLAG_COLORSPACE_NOT_sRGB)) + png_set_sRGB(png_ptr, info_ptr, PNG_sRGB_INTENT_PERCEPTUAL); + + /* Else writing an 8-bit file and the *colors* aren't sRGB, but the 8-bit + * space must still be gamma encoded. + */ + else + png_set_gAMA_fixed(png_ptr, info_ptr, PNG_GAMMA_sRGB_INVERSE); + + /* Write the file header. */ + png_write_info(png_ptr, info_ptr); + + /* Now set up the data transformations (*after* the header is written), + * remove the handled transformations from the 'format' flags for checking. + * + * First check for a little endian system if writing 16 bit files. + */ + if (write_16bit) + { + PNG_CONST png_uint_16 le = 0x0001; + + if (*(png_const_bytep)&le) + png_set_swap(png_ptr); + } + +# ifdef PNG_SIMPLIFIED_WRITE_BGR_SUPPORTED + if (format & PNG_FORMAT_FLAG_BGR) + { + if (!colormap && (format & PNG_FORMAT_FLAG_COLOR) != 0) + png_set_bgr(png_ptr); + format &= ~PNG_FORMAT_FLAG_BGR; + } +# endif + +# ifdef PNG_SIMPLIFIED_WRITE_AFIRST_SUPPORTED + if (format & PNG_FORMAT_FLAG_AFIRST) + { + if (!colormap && (format & PNG_FORMAT_FLAG_ALPHA) != 0) + png_set_swap_alpha(png_ptr); + format &= ~PNG_FORMAT_FLAG_AFIRST; + } +# endif + + /* If there are 16 or fewer color-map entries we wrote a lower bit depth + * above, but the application data is still byte packed. + */ + if (colormap && image->colormap_entries <= 16) + png_set_packing(png_ptr); + + /* That should have handled all (both) the transforms. */ + if ((format & ~(png_uint_32)(PNG_FORMAT_FLAG_COLOR | PNG_FORMAT_FLAG_LINEAR | + PNG_FORMAT_FLAG_ALPHA | PNG_FORMAT_FLAG_COLORMAP)) != 0) + png_error(png_ptr, "png_write_image: unsupported transformation"); + + { + png_const_bytep row = png_voidcast(png_const_bytep, display->buffer); + ptrdiff_t row_bytes = display->row_stride; + + if (linear) + row_bytes *= (sizeof (png_uint_16)); + + if (row_bytes < 0) + row += (image->height-1) * (-row_bytes); + + display->first_row = row; + display->row_bytes = row_bytes; + } + + /* Apply 'fast' options if the flag is set. */ + if ((image->flags & PNG_IMAGE_FLAG_FAST) != 0) + { + png_set_filter(png_ptr, PNG_FILTER_TYPE_BASE, PNG_NO_FILTERS); + /* NOTE: determined by experiment using pngstest, this reflects some + * balance between the time to write the image once and the time to read + * it about 50 times. The speed-up in pngstest was about 10-20% of the + * total (user) time on a heavily loaded system. + */ + png_set_compression_level(png_ptr, 3); + } + + /* Check for the cases that currently require a pre-transform on the row + * before it is written. This only applies when the input is 16-bit and + * either there is an alpha channel or it is converted to 8-bit. + */ + if ((linear && alpha) || (!colormap && display->convert_to_8bit)) + { + png_bytep row = png_voidcast(png_bytep, png_malloc(png_ptr, + png_get_rowbytes(png_ptr, info_ptr))); + int result; + + display->local_row = row; + if (write_16bit) + result = png_safe_execute(image, png_write_image_16bit, display); + else + result = png_safe_execute(image, png_write_image_8bit, display); + display->local_row = NULL; + + png_free(png_ptr, row); + + /* Skip the 'write_end' on error: */ + if (!result) + return 0; + } + + /* Otherwise this is the case where the input is in a format currently + * supported by the rest of the libpng write code; call it directly. + */ + else + { + png_const_bytep row = png_voidcast(png_const_bytep, display->first_row); + ptrdiff_t row_bytes = display->row_bytes; + png_uint_32 y = image->height; + + while (y-- > 0) + { + png_write_row(png_ptr, row); + row += row_bytes; + } + } + + png_write_end(png_ptr, info_ptr); + return 1; +} + +int PNGAPI +// 4j studios: callback support +png_image_write_to_stdio(png_imagep image, FILE *file, int convert_to_8bit, + const void *buffer, png_int_32 row_stride, const void *colormap, + /*begin 4j change*/ png_rw_ptr write_fn, png_flush_ptr flush_fn /*end 4j change*/) +{ + /* Write the image to the given (FILE*). */ + if (image != NULL && image->version == PNG_IMAGE_VERSION) + { + // begin 4j change: allow null file, callback support + //if (file != NULL) + //{ + //if (png_image_write_init(image)) + if (png_image_write_init(image, write_fn, flush_fn)) + { + png_image_write_control display; + int result; + + /* This is slightly evil, but png_init_io doesn't do anything other + * than this and we haven't changed the standard IO functions so + * this saves a 'safe' function. + */ + image->opaque->png_ptr->io_ptr = file; + + memset(&display, 0, (sizeof display)); + display.image = image; + display.buffer = buffer; + display.row_stride = row_stride; + display.colormap = colormap; + display.convert_to_8bit = convert_to_8bit; + + result = png_safe_execute(image, png_image_write_main, &display); + png_image_free(image); + return result; + } + + else + return 0; + //} + + //else + //return png_image_error(image, + //"png_image_write_to_stdio: invalid argument"); + } + + else if (image != NULL) + return png_image_error(image, + "png_image_write_to_stdio: incorrect PNG_IMAGE_VERSION"); + + else + return 0; +} + +int PNGAPI +png_image_write_to_file(png_imagep image, const char *file_name, + int convert_to_8bit, const void *buffer, png_int_32 row_stride, + const void *colormap) +{ + /* Write the image to the named file. */ + if (image != NULL && image->version == PNG_IMAGE_VERSION) + { + if (file_name != NULL) + { + FILE *fp = fopen(file_name, "wb"); + + if (fp != NULL) + { + if (png_image_write_to_stdio(image, fp, convert_to_8bit, buffer, + row_stride, colormap /*begin 4j change*/, NULL, NULL /*end 4j change*/)) + { + int error; /* from fflush/fclose */ + + /* Make sure the file is flushed correctly. */ + if (fflush(fp) == 0 && ferror(fp) == 0) + { + if (fclose(fp) == 0) + return 1; + + error = errno; /* from fclose */ + } + + else + { + error = errno; /* from fflush or ferror */ + (void)fclose(fp); + } + + (void)remove(file_name); + /* The image has already been cleaned up; this is just used to + * set the error (because the original write succeeded). + */ + return png_image_error(image, strerror(error)); + } + + else + { + /* Clean up: just the opened file. */ + (void)fclose(fp); + (void)remove(file_name); + return 0; + } + } + + else + return png_image_error(image, strerror(errno)); + } + + else + return png_image_error(image, + "png_image_write_to_file: invalid argument"); + } + + else if (image != NULL) + return png_image_error(image, + "png_image_write_to_file: incorrect PNG_IMAGE_VERSION"); + + else + return 0; +} +#endif /* PNG_STDIO_SUPPORTED */ +#endif /* SIMPLIFIED_WRITE */ +#endif /* PNG_WRITE_SUPPORTED */ diff --git a/Minecraft.Client/Windows64/4JLibs/inc/Render/libpng/pngwtran.c b/Minecraft.Client/Windows64/4JLibs/inc/Render/libpng/pngwtran.c new file mode 100644 index 000000000..98703f8c8 --- /dev/null +++ b/Minecraft.Client/Windows64/4JLibs/inc/Render/libpng/pngwtran.c @@ -0,0 +1,637 @@ + +/* pngwtran.c - transforms the data in a row for PNG writers + * + * Last changed in libpng 1.6.0 [February 14, 2013] + * Copyright (c) 1998-2013 Glenn Randers-Pehrson + * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) + * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) + * + * This code is released under the libpng license. + * For conditions of distribution and use, see the disclaimer + * and license in png.h + */ + +#include "pngpriv.h" + +#ifdef PNG_WRITE_SUPPORTED + +#ifdef PNG_WRITE_TRANSFORMS_SUPPORTED +/* Transform the data according to the user's wishes. The order of + * transformations is significant. + */ +void /* PRIVATE */ +png_do_write_transformations(png_structrp png_ptr, png_row_infop row_info) +{ + png_debug(1, "in png_do_write_transformations"); + + if (png_ptr == NULL) + return; + +#ifdef PNG_WRITE_USER_TRANSFORM_SUPPORTED + if (png_ptr->transformations & PNG_USER_TRANSFORM) + if (png_ptr->write_user_transform_fn != NULL) + (*(png_ptr->write_user_transform_fn)) /* User write transform + function */ + (png_ptr, /* png_ptr */ + row_info, /* row_info: */ + /* png_uint_32 width; width of row */ + /* png_size_t rowbytes; number of bytes in row */ + /* png_byte color_type; color type of pixels */ + /* png_byte bit_depth; bit depth of samples */ + /* png_byte channels; number of channels (1-4) */ + /* png_byte pixel_depth; bits per pixel (depth*channels) */ + png_ptr->row_buf + 1); /* start of pixel data for row */ +#endif + +#ifdef PNG_WRITE_FILLER_SUPPORTED + if (png_ptr->transformations & PNG_FILLER) + png_do_strip_channel(row_info, png_ptr->row_buf + 1, + !(png_ptr->flags & PNG_FLAG_FILLER_AFTER)); +#endif + +#ifdef PNG_WRITE_PACKSWAP_SUPPORTED + if (png_ptr->transformations & PNG_PACKSWAP) + png_do_packswap(row_info, png_ptr->row_buf + 1); +#endif + +#ifdef PNG_WRITE_PACK_SUPPORTED + if (png_ptr->transformations & PNG_PACK) + png_do_pack(row_info, png_ptr->row_buf + 1, + (png_uint_32)png_ptr->bit_depth); +#endif + +#ifdef PNG_WRITE_SWAP_SUPPORTED + if (png_ptr->transformations & PNG_SWAP_BYTES) + png_do_swap(row_info, png_ptr->row_buf + 1); +#endif + +#ifdef PNG_WRITE_SHIFT_SUPPORTED + if (png_ptr->transformations & PNG_SHIFT) + png_do_shift(row_info, png_ptr->row_buf + 1, + &(png_ptr->shift)); +#endif + +#ifdef PNG_WRITE_SWAP_ALPHA_SUPPORTED + if (png_ptr->transformations & PNG_SWAP_ALPHA) + png_do_write_swap_alpha(row_info, png_ptr->row_buf + 1); +#endif + +#ifdef PNG_WRITE_INVERT_ALPHA_SUPPORTED + if (png_ptr->transformations & PNG_INVERT_ALPHA) + png_do_write_invert_alpha(row_info, png_ptr->row_buf + 1); +#endif + +#ifdef PNG_WRITE_BGR_SUPPORTED + if (png_ptr->transformations & PNG_BGR) + png_do_bgr(row_info, png_ptr->row_buf + 1); +#endif + +#ifdef PNG_WRITE_INVERT_SUPPORTED + if (png_ptr->transformations & PNG_INVERT_MONO) + png_do_invert(row_info, png_ptr->row_buf + 1); +#endif +} + +#ifdef PNG_WRITE_PACK_SUPPORTED +/* Pack pixels into bytes. Pass the true bit depth in bit_depth. The + * row_info bit depth should be 8 (one pixel per byte). The channels + * should be 1 (this only happens on grayscale and paletted images). + */ +void /* PRIVATE */ +png_do_pack(png_row_infop row_info, png_bytep row, png_uint_32 bit_depth) +{ + png_debug(1, "in png_do_pack"); + + if (row_info->bit_depth == 8 && + row_info->channels == 1) + { + switch ((int)bit_depth) + { + case 1: + { + png_bytep sp, dp; + int mask, v; + png_uint_32 i; + png_uint_32 row_width = row_info->width; + + sp = row; + dp = row; + mask = 0x80; + v = 0; + + for (i = 0; i < row_width; i++) + { + if (*sp != 0) + v |= mask; + + sp++; + + if (mask > 1) + mask >>= 1; + + else + { + mask = 0x80; + *dp = (png_byte)v; + dp++; + v = 0; + } + } + + if (mask != 0x80) + *dp = (png_byte)v; + + break; + } + + case 2: + { + png_bytep sp, dp; + int shift, v; + png_uint_32 i; + png_uint_32 row_width = row_info->width; + + sp = row; + dp = row; + shift = 6; + v = 0; + + for (i = 0; i < row_width; i++) + { + png_byte value; + + value = (png_byte)(*sp & 0x03); + v |= (value << shift); + + if (shift == 0) + { + shift = 6; + *dp = (png_byte)v; + dp++; + v = 0; + } + + else + shift -= 2; + + sp++; + } + + if (shift != 6) + *dp = (png_byte)v; + + break; + } + + case 4: + { + png_bytep sp, dp; + int shift, v; + png_uint_32 i; + png_uint_32 row_width = row_info->width; + + sp = row; + dp = row; + shift = 4; + v = 0; + + for (i = 0; i < row_width; i++) + { + png_byte value; + + value = (png_byte)(*sp & 0x0f); + v |= (value << shift); + + if (shift == 0) + { + shift = 4; + *dp = (png_byte)v; + dp++; + v = 0; + } + + else + shift -= 4; + + sp++; + } + + if (shift != 4) + *dp = (png_byte)v; + + break; + } + + default: + break; + } + + row_info->bit_depth = (png_byte)bit_depth; + row_info->pixel_depth = (png_byte)(bit_depth * row_info->channels); + row_info->rowbytes = PNG_ROWBYTES(row_info->pixel_depth, + row_info->width); + } +} +#endif + +#ifdef PNG_WRITE_SHIFT_SUPPORTED +/* Shift pixel values to take advantage of whole range. Pass the + * true number of bits in bit_depth. The row should be packed + * according to row_info->bit_depth. Thus, if you had a row of + * bit depth 4, but the pixels only had values from 0 to 7, you + * would pass 3 as bit_depth, and this routine would translate the + * data to 0 to 15. + */ +void /* PRIVATE */ +png_do_shift(png_row_infop row_info, png_bytep row, + png_const_color_8p bit_depth) +{ + png_debug(1, "in png_do_shift"); + + if (row_info->color_type != PNG_COLOR_TYPE_PALETTE) + { + int shift_start[4], shift_dec[4]; + int channels = 0; + + if (row_info->color_type & PNG_COLOR_MASK_COLOR) + { + shift_start[channels] = row_info->bit_depth - bit_depth->red; + shift_dec[channels] = bit_depth->red; + channels++; + + shift_start[channels] = row_info->bit_depth - bit_depth->green; + shift_dec[channels] = bit_depth->green; + channels++; + + shift_start[channels] = row_info->bit_depth - bit_depth->blue; + shift_dec[channels] = bit_depth->blue; + channels++; + } + + else + { + shift_start[channels] = row_info->bit_depth - bit_depth->gray; + shift_dec[channels] = bit_depth->gray; + channels++; + } + + if (row_info->color_type & PNG_COLOR_MASK_ALPHA) + { + shift_start[channels] = row_info->bit_depth - bit_depth->alpha; + shift_dec[channels] = bit_depth->alpha; + channels++; + } + + /* With low row depths, could only be grayscale, so one channel */ + if (row_info->bit_depth < 8) + { + png_bytep bp = row; + png_size_t i; + unsigned int mask; + png_size_t row_bytes = row_info->rowbytes; + + if (bit_depth->gray == 1 && row_info->bit_depth == 2) + mask = 0x55; + + else if (row_info->bit_depth == 4 && bit_depth->gray == 3) + mask = 0x11; + + else + mask = 0xff; + + for (i = 0; i < row_bytes; i++, bp++) + { + int j; + unsigned int v, out; + + v = *bp; + out = 0; + + for (j = shift_start[0]; j > -shift_dec[0]; j -= shift_dec[0]) + { + if (j > 0) + out |= v << j; + + else + out |= (v >> (-j)) & mask; + } + + *bp = (png_byte)(out & 0xff); + } + } + + else if (row_info->bit_depth == 8) + { + png_bytep bp = row; + png_uint_32 i; + png_uint_32 istop = channels * row_info->width; + + for (i = 0; i < istop; i++, bp++) + { + + const unsigned int c = i%channels; + int j; + unsigned int v, out; + + v = *bp; + out = 0; + + for (j = shift_start[c]; j > -shift_dec[c]; j -= shift_dec[c]) + { + if (j > 0) + out |= v << j; + + else + out |= v >> (-j); + } + + *bp = (png_byte)(out & 0xff); + } + } + + else + { + png_bytep bp; + png_uint_32 i; + png_uint_32 istop = channels * row_info->width; + + for (bp = row, i = 0; i < istop; i++) + { + const unsigned int c = i%channels; + int j; + unsigned int value, v; + + v = png_get_uint_16(bp); + value = 0; + + for (j = shift_start[c]; j > -shift_dec[c]; j -= shift_dec[c]) + { + if (j > 0) + value |= v << j; + + else + value |= v >> (-j); + } + *bp++ = (png_byte)((value >> 8) & 0xff); + *bp++ = (png_byte)(value & 0xff); + } + } + } +} +#endif + +#ifdef PNG_WRITE_SWAP_ALPHA_SUPPORTED +void /* PRIVATE */ +png_do_write_swap_alpha(png_row_infop row_info, png_bytep row) +{ + png_debug(1, "in png_do_write_swap_alpha"); + + { + if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA) + { + if (row_info->bit_depth == 8) + { + /* This converts from ARGB to RGBA */ + png_bytep sp, dp; + png_uint_32 i; + png_uint_32 row_width = row_info->width; + + for (i = 0, sp = dp = row; i < row_width; i++) + { + png_byte save = *(sp++); + *(dp++) = *(sp++); + *(dp++) = *(sp++); + *(dp++) = *(sp++); + *(dp++) = save; + } + } + +#ifdef PNG_WRITE_16BIT_SUPPORTED + else + { + /* This converts from AARRGGBB to RRGGBBAA */ + png_bytep sp, dp; + png_uint_32 i; + png_uint_32 row_width = row_info->width; + + for (i = 0, sp = dp = row; i < row_width; i++) + { + png_byte save[2]; + save[0] = *(sp++); + save[1] = *(sp++); + *(dp++) = *(sp++); + *(dp++) = *(sp++); + *(dp++) = *(sp++); + *(dp++) = *(sp++); + *(dp++) = *(sp++); + *(dp++) = *(sp++); + *(dp++) = save[0]; + *(dp++) = save[1]; + } + } +#endif /* PNG_WRITE_16BIT_SUPPORTED */ + } + + else if (row_info->color_type == PNG_COLOR_TYPE_GRAY_ALPHA) + { + if (row_info->bit_depth == 8) + { + /* This converts from AG to GA */ + png_bytep sp, dp; + png_uint_32 i; + png_uint_32 row_width = row_info->width; + + for (i = 0, sp = dp = row; i < row_width; i++) + { + png_byte save = *(sp++); + *(dp++) = *(sp++); + *(dp++) = save; + } + } + +#ifdef PNG_WRITE_16BIT_SUPPORTED + else + { + /* This converts from AAGG to GGAA */ + png_bytep sp, dp; + png_uint_32 i; + png_uint_32 row_width = row_info->width; + + for (i = 0, sp = dp = row; i < row_width; i++) + { + png_byte save[2]; + save[0] = *(sp++); + save[1] = *(sp++); + *(dp++) = *(sp++); + *(dp++) = *(sp++); + *(dp++) = save[0]; + *(dp++) = save[1]; + } + } +#endif /* PNG_WRITE_16BIT_SUPPORTED */ + } + } +} +#endif + +#ifdef PNG_WRITE_INVERT_ALPHA_SUPPORTED +void /* PRIVATE */ +png_do_write_invert_alpha(png_row_infop row_info, png_bytep row) +{ + png_debug(1, "in png_do_write_invert_alpha"); + + { + if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA) + { + if (row_info->bit_depth == 8) + { + /* This inverts the alpha channel in RGBA */ + png_bytep sp, dp; + png_uint_32 i; + png_uint_32 row_width = row_info->width; + + for (i = 0, sp = dp = row; i < row_width; i++) + { + /* Does nothing + *(dp++) = *(sp++); + *(dp++) = *(sp++); + *(dp++) = *(sp++); + */ + sp+=3; dp = sp; + *(dp++) = (png_byte)(255 - *(sp++)); + } + } + +#ifdef PNG_WRITE_16BIT_SUPPORTED + else + { + /* This inverts the alpha channel in RRGGBBAA */ + png_bytep sp, dp; + png_uint_32 i; + png_uint_32 row_width = row_info->width; + + for (i = 0, sp = dp = row; i < row_width; i++) + { + /* Does nothing + *(dp++) = *(sp++); + *(dp++) = *(sp++); + *(dp++) = *(sp++); + *(dp++) = *(sp++); + *(dp++) = *(sp++); + *(dp++) = *(sp++); + */ + sp+=6; dp = sp; + *(dp++) = (png_byte)(255 - *(sp++)); + *(dp++) = (png_byte)(255 - *(sp++)); + } + } +#endif /* PNG_WRITE_16BIT_SUPPORTED */ + } + + else if (row_info->color_type == PNG_COLOR_TYPE_GRAY_ALPHA) + { + if (row_info->bit_depth == 8) + { + /* This inverts the alpha channel in GA */ + png_bytep sp, dp; + png_uint_32 i; + png_uint_32 row_width = row_info->width; + + for (i = 0, sp = dp = row; i < row_width; i++) + { + *(dp++) = *(sp++); + *(dp++) = (png_byte)(255 - *(sp++)); + } + } + +#ifdef PNG_WRITE_16BIT_SUPPORTED + else + { + /* This inverts the alpha channel in GGAA */ + png_bytep sp, dp; + png_uint_32 i; + png_uint_32 row_width = row_info->width; + + for (i = 0, sp = dp = row; i < row_width; i++) + { + /* Does nothing + *(dp++) = *(sp++); + *(dp++) = *(sp++); + */ + sp+=2; dp = sp; + *(dp++) = (png_byte)(255 - *(sp++)); + *(dp++) = (png_byte)(255 - *(sp++)); + } + } +#endif /* PNG_WRITE_16BIT_SUPPORTED */ + } + } +} +#endif +#endif /* PNG_WRITE_TRANSFORMS_SUPPORTED */ + +#ifdef PNG_MNG_FEATURES_SUPPORTED +/* Undoes intrapixel differencing */ +void /* PRIVATE */ +png_do_write_intrapixel(png_row_infop row_info, png_bytep row) +{ + png_debug(1, "in png_do_write_intrapixel"); + + if ((row_info->color_type & PNG_COLOR_MASK_COLOR)) + { + int bytes_per_pixel; + png_uint_32 row_width = row_info->width; + if (row_info->bit_depth == 8) + { + png_bytep rp; + png_uint_32 i; + + if (row_info->color_type == PNG_COLOR_TYPE_RGB) + bytes_per_pixel = 3; + + else if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA) + bytes_per_pixel = 4; + + else + return; + + for (i = 0, rp = row; i < row_width; i++, rp += bytes_per_pixel) + { + *(rp) = (png_byte)((*rp - *(rp + 1)) & 0xff); + *(rp + 2) = (png_byte)((*(rp + 2) - *(rp + 1)) & 0xff); + } + } + +#ifdef PNG_WRITE_16BIT_SUPPORTED + else if (row_info->bit_depth == 16) + { + png_bytep rp; + png_uint_32 i; + + if (row_info->color_type == PNG_COLOR_TYPE_RGB) + bytes_per_pixel = 6; + + else if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA) + bytes_per_pixel = 8; + + else + return; + + for (i = 0, rp = row; i < row_width; i++, rp += bytes_per_pixel) + { + png_uint_32 s0 = (*(rp ) << 8) | *(rp + 1); + png_uint_32 s1 = (*(rp + 2) << 8) | *(rp + 3); + png_uint_32 s2 = (*(rp + 4) << 8) | *(rp + 5); + png_uint_32 red = (png_uint_32)((s0 - s1) & 0xffffL); + png_uint_32 blue = (png_uint_32)((s2 - s1) & 0xffffL); + *(rp ) = (png_byte)((red >> 8) & 0xff); + *(rp + 1) = (png_byte)(red & 0xff); + *(rp + 4) = (png_byte)((blue >> 8) & 0xff); + *(rp + 5) = (png_byte)(blue & 0xff); + } + } +#endif /* PNG_WRITE_16BIT_SUPPORTED */ + } +} +#endif /* PNG_MNG_FEATURES_SUPPORTED */ +#endif /* PNG_WRITE_SUPPORTED */ diff --git a/Minecraft.Client/Windows64/4JLibs/inc/Render/libpng/pngwutil.c b/Minecraft.Client/Windows64/4JLibs/inc/Render/libpng/pngwutil.c new file mode 100644 index 000000000..49e6a2d21 --- /dev/null +++ b/Minecraft.Client/Windows64/4JLibs/inc/Render/libpng/pngwutil.c @@ -0,0 +1,3023 @@ + +/* pngwutil.c - utilities to write a PNG file + * + * Last changed in libpng 1.6.2 [April 25, 2013] + * Copyright (c) 1998-2013 Glenn Randers-Pehrson + * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) + * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) + * + * This code is released under the libpng license. + * For conditions of distribution and use, see the disclaimer + * and license in png.h + */ + +#include "pngpriv.h" + +#ifdef PNG_WRITE_SUPPORTED + +#ifdef PNG_WRITE_INT_FUNCTIONS_SUPPORTED +/* Place a 32-bit number into a buffer in PNG byte order. We work + * with unsigned numbers for convenience, although one supported + * ancillary chunk uses signed (two's complement) numbers. + */ +void PNGAPI +png_save_uint_32(png_bytep buf, png_uint_32 i) +{ + buf[0] = (png_byte)((i >> 24) & 0xff); + buf[1] = (png_byte)((i >> 16) & 0xff); + buf[2] = (png_byte)((i >> 8) & 0xff); + buf[3] = (png_byte)(i & 0xff); +} + +/* Place a 16-bit number into a buffer in PNG byte order. + * The parameter is declared unsigned int, not png_uint_16, + * just to avoid potential problems on pre-ANSI C compilers. + */ +void PNGAPI +png_save_uint_16(png_bytep buf, unsigned int i) +{ + buf[0] = (png_byte)((i >> 8) & 0xff); + buf[1] = (png_byte)(i & 0xff); +} +#endif + +/* Simple function to write the signature. If we have already written + * the magic bytes of the signature, or more likely, the PNG stream is + * being embedded into another stream and doesn't need its own signature, + * we should call png_set_sig_bytes() to tell libpng how many of the + * bytes have already been written. + */ +void PNGAPI +png_write_sig(png_structrp png_ptr) +{ + png_byte png_signature[8] = {137, 80, 78, 71, 13, 10, 26, 10}; + +#ifdef PNG_IO_STATE_SUPPORTED + /* Inform the I/O callback that the signature is being written */ + png_ptr->io_state = PNG_IO_WRITING | PNG_IO_SIGNATURE; +#endif + + /* Write the rest of the 8 byte signature */ + png_write_data(png_ptr, &png_signature[png_ptr->sig_bytes], + (png_size_t)(8 - png_ptr->sig_bytes)); + + if (png_ptr->sig_bytes < 3) + png_ptr->mode |= PNG_HAVE_PNG_SIGNATURE; +} + +/* Write the start of a PNG chunk. The type is the chunk type. + * The total_length is the sum of the lengths of all the data you will be + * passing in png_write_chunk_data(). + */ +static void +png_write_chunk_header(png_structrp png_ptr, png_uint_32 chunk_name, + png_uint_32 length) +{ + png_byte buf[8]; + +#if defined(PNG_DEBUG) && (PNG_DEBUG > 0) + PNG_CSTRING_FROM_CHUNK(buf, chunk_name); + png_debug2(0, "Writing %s chunk, length = %lu", buf, (unsigned long)length); +#endif + + if (png_ptr == NULL) + return; + +#ifdef PNG_IO_STATE_SUPPORTED + /* Inform the I/O callback that the chunk header is being written. + * PNG_IO_CHUNK_HDR requires a single I/O call. + */ + png_ptr->io_state = PNG_IO_WRITING | PNG_IO_CHUNK_HDR; +#endif + + /* Write the length and the chunk name */ + png_save_uint_32(buf, length); + png_save_uint_32(buf + 4, chunk_name); + png_write_data(png_ptr, buf, 8); + + /* Put the chunk name into png_ptr->chunk_name */ + png_ptr->chunk_name = chunk_name; + + /* Reset the crc and run it over the chunk name */ + png_reset_crc(png_ptr); + + png_calculate_crc(png_ptr, buf + 4, 4); + +#ifdef PNG_IO_STATE_SUPPORTED + /* Inform the I/O callback that chunk data will (possibly) be written. + * PNG_IO_CHUNK_DATA does NOT require a specific number of I/O calls. + */ + png_ptr->io_state = PNG_IO_WRITING | PNG_IO_CHUNK_DATA; +#endif +} + +void PNGAPI +png_write_chunk_start(png_structrp png_ptr, png_const_bytep chunk_string, + png_uint_32 length) +{ + png_write_chunk_header(png_ptr, PNG_CHUNK_FROM_STRING(chunk_string), length); +} + +/* Write the data of a PNG chunk started with png_write_chunk_header(). + * Note that multiple calls to this function are allowed, and that the + * sum of the lengths from these calls *must* add up to the total_length + * given to png_write_chunk_header(). + */ +void PNGAPI +png_write_chunk_data(png_structrp png_ptr, png_const_bytep data, + png_size_t length) +{ + /* Write the data, and run the CRC over it */ + if (png_ptr == NULL) + return; + + if (data != NULL && length > 0) + { + png_write_data(png_ptr, data, length); + + /* Update the CRC after writing the data, + * in case that the user I/O routine alters it. + */ + png_calculate_crc(png_ptr, data, length); + } +} + +/* Finish a chunk started with png_write_chunk_header(). */ +void PNGAPI +png_write_chunk_end(png_structrp png_ptr) +{ + png_byte buf[4]; + + if (png_ptr == NULL) return; + +#ifdef PNG_IO_STATE_SUPPORTED + /* Inform the I/O callback that the chunk CRC is being written. + * PNG_IO_CHUNK_CRC requires a single I/O function call. + */ + png_ptr->io_state = PNG_IO_WRITING | PNG_IO_CHUNK_CRC; +#endif + + /* Write the crc in a single operation */ + png_save_uint_32(buf, png_ptr->crc); + + png_write_data(png_ptr, buf, (png_size_t)4); +} + +/* Write a PNG chunk all at once. The type is an array of ASCII characters + * representing the chunk name. The array must be at least 4 bytes in + * length, and does not need to be null terminated. To be safe, pass the + * pre-defined chunk names here, and if you need a new one, define it + * where the others are defined. The length is the length of the data. + * All the data must be present. If that is not possible, use the + * png_write_chunk_start(), png_write_chunk_data(), and png_write_chunk_end() + * functions instead. + */ +static void +png_write_complete_chunk(png_structrp png_ptr, png_uint_32 chunk_name, + png_const_bytep data, png_size_t length) +{ + if (png_ptr == NULL) + return; + + /* On 64 bit architectures 'length' may not fit in a png_uint_32. */ + if (length > PNG_UINT_31_MAX) + png_error(png_ptr, "length exceeds PNG maxima"); + + png_write_chunk_header(png_ptr, chunk_name, (png_uint_32)length); + png_write_chunk_data(png_ptr, data, length); + png_write_chunk_end(png_ptr); +} + +/* This is the API that calls the internal function above. */ +void PNGAPI +png_write_chunk(png_structrp png_ptr, png_const_bytep chunk_string, + png_const_bytep data, png_size_t length) +{ + png_write_complete_chunk(png_ptr, PNG_CHUNK_FROM_STRING(chunk_string), data, + length); +} + +/* This is used below to find the size of an image to pass to png_deflate_claim, + * so it only needs to be accurate if the size is less than 16384 bytes (the + * point at which a lower LZ window size can be used.) + */ +static png_alloc_size_t +png_image_size(png_structrp png_ptr) +{ + /* Only return sizes up to the maximum of a png_uint_32, do this by limiting + * the width and height used to 15 bits. + */ + png_uint_32 h = png_ptr->height; + + if (png_ptr->rowbytes < 32768 && h < 32768) + { + if (png_ptr->interlaced) + { + /* Interlacing makes the image larger because of the replication of + * both the filter byte and the padding to a byte boundary. + */ + png_uint_32 w = png_ptr->width; + unsigned int pd = png_ptr->pixel_depth; + png_alloc_size_t cb_base; + int pass; + + for (cb_base=0, pass=0; pass<=6; ++pass) + { + png_uint_32 pw = PNG_PASS_COLS(w, pass); + + if (pw > 0) + cb_base += (PNG_ROWBYTES(pd, pw)+1) * PNG_PASS_ROWS(h, pass); + } + + return cb_base; + } + + else + return (png_ptr->rowbytes+1) * h; + } + + else + return 0xffffffffU; +} + +#ifdef PNG_WRITE_OPTIMIZE_CMF_SUPPORTED + /* This is the code to hack the first two bytes of the deflate stream (the + * deflate header) to correct the windowBits value to match the actual data + * size. Note that the second argument is the *uncompressed* size but the + * first argument is the *compressed* data (and it must be deflate + * compressed.) + */ +static void +optimize_cmf(png_bytep data, png_alloc_size_t data_size) +{ + /* Optimize the CMF field in the zlib stream. The resultant zlib stream is + * still compliant to the stream specification. + */ + if (data_size <= 16384) /* else windowBits must be 15 */ + { + unsigned int z_cmf = data[0]; /* zlib compression method and flags */ + + if ((z_cmf & 0x0f) == 8 && (z_cmf & 0xf0) <= 0x70) + { + unsigned int z_cinfo; + unsigned int half_z_window_size; + + z_cinfo = z_cmf >> 4; + half_z_window_size = 1U << (z_cinfo + 7); + + if (data_size <= half_z_window_size) /* else no change */ + { + unsigned int tmp; + + do + { + half_z_window_size >>= 1; + --z_cinfo; + } + while (z_cinfo > 0 && data_size <= half_z_window_size); + + z_cmf = (z_cmf & 0x0f) | (z_cinfo << 4); + + data[0] = (png_byte)z_cmf; + tmp = data[1] & 0xe0; + tmp += 0x1f - ((z_cmf << 8) + tmp) % 0x1f; + data[1] = (png_byte)tmp; + } + } + } +} +#else +# define optimize_cmf(dp,dl) ((void)0) +#endif /* PNG_WRITE_OPTIMIZE_CMF_SUPPORTED */ + +/* Initialize the compressor for the appropriate type of compression. */ +static int +png_deflate_claim(png_structrp png_ptr, png_uint_32 owner, + png_alloc_size_t data_size) +{ + if (png_ptr->zowner != 0) + { + char msg[64]; + + PNG_STRING_FROM_CHUNK(msg, owner); + msg[4] = ':'; + msg[5] = ' '; + PNG_STRING_FROM_CHUNK(msg+6, png_ptr->zowner); + /* So the message that results is " using zstream"; this is an + * internal error, but is very useful for debugging. i18n requirements + * are minimal. + */ + (void)png_safecat(msg, (sizeof msg), 10, " using zstream"); +# if PNG_LIBPNG_BUILD_BASE_TYPE >= PNG_LIBPNG_BUILD_RC + png_warning(png_ptr, msg); + + /* Attempt sane error recovery */ + if (png_ptr->zowner == png_IDAT) /* don't steal from IDAT */ + { + png_ptr->zstream.msg = PNGZ_MSG_CAST("in use by IDAT"); + return Z_STREAM_ERROR; + } + + png_ptr->zowner = 0; +# else + png_error(png_ptr, msg); +# endif + } + + { + int level = png_ptr->zlib_level; + int method = png_ptr->zlib_method; + int windowBits = png_ptr->zlib_window_bits; + int memLevel = png_ptr->zlib_mem_level; + int strategy; /* set below */ + int ret; /* zlib return code */ + + if (owner == png_IDAT) + { + if (png_ptr->flags & PNG_FLAG_ZLIB_CUSTOM_STRATEGY) + strategy = png_ptr->zlib_strategy; + + else if (png_ptr->do_filter != PNG_FILTER_NONE) + strategy = PNG_Z_DEFAULT_STRATEGY; + + else + strategy = PNG_Z_DEFAULT_NOFILTER_STRATEGY; + } + + else + { +# ifdef PNG_WRITE_CUSTOMIZE_ZTXT_COMPRESSION_SUPPORTED + level = png_ptr->zlib_text_level; + method = png_ptr->zlib_text_method; + windowBits = png_ptr->zlib_text_window_bits; + memLevel = png_ptr->zlib_text_mem_level; + strategy = png_ptr->zlib_text_strategy; +# else + /* If customization is not supported the values all come from the + * IDAT values except for the strategy, which is fixed to the + * default. (This is the pre-1.6.0 behavior too, although it was + * implemented in a very different way.) + */ + strategy = Z_DEFAULT_STRATEGY; +# endif + } + + /* Adjust 'windowBits' down if larger than 'data_size'; to stop this + * happening just pass 32768 as the data_size parameter. Notice that zlib + * requires an extra 262 bytes in the window in addition to the data to be + * able to see the whole of the data, so if data_size+262 takes us to the + * next windowBits size we need to fix up the value later. (Because even + * though deflate needs the extra window, inflate does not!) + */ + if (data_size <= 16384) + { + /* IMPLEMENTATION NOTE: this 'half_window_size' stuff is only here to + * work round a Microsoft Visual C misbehavior which, contrary to C-90, + * widens the result of the following shift to 64-bits if (and, + * apparently, only if) it is used in a test. + */ + unsigned int half_window_size = 1U << (windowBits-1); + + while (data_size + 262 <= half_window_size) + { + half_window_size >>= 1; + --windowBits; + } + } + + /* Check against the previous initialized values, if any. */ + if ((png_ptr->flags & PNG_FLAG_ZSTREAM_INITIALIZED) && + (png_ptr->zlib_set_level != level || + png_ptr->zlib_set_method != method || + png_ptr->zlib_set_window_bits != windowBits || + png_ptr->zlib_set_mem_level != memLevel || + png_ptr->zlib_set_strategy != strategy)) + { + if (deflateEnd(&png_ptr->zstream) != Z_OK) + png_warning(png_ptr, "deflateEnd failed (ignored)"); + + png_ptr->flags &= ~PNG_FLAG_ZSTREAM_INITIALIZED; + } + + /* For safety clear out the input and output pointers (currently zlib + * doesn't use them on Init, but it might in the future). + */ + png_ptr->zstream.next_in = NULL; + png_ptr->zstream.avail_in = 0; + png_ptr->zstream.next_out = NULL; + png_ptr->zstream.avail_out = 0; + + /* Now initialize if required, setting the new parameters, otherwise just + * to a simple reset to the previous parameters. + */ + if (png_ptr->flags & PNG_FLAG_ZSTREAM_INITIALIZED) + ret = deflateReset(&png_ptr->zstream); + + else + { + ret = deflateInit2(&png_ptr->zstream, level, method, windowBits, + memLevel, strategy); + + if (ret == Z_OK) + png_ptr->flags |= PNG_FLAG_ZSTREAM_INITIALIZED; + } + + /* The return code is from either deflateReset or deflateInit2; they have + * pretty much the same set of error codes. + */ + if (ret == Z_OK) + png_ptr->zowner = owner; + + else + png_zstream_error(png_ptr, ret); + + return ret; + } +} + +/* Clean up (or trim) a linked list of compression buffers. */ +void /* PRIVATE */ +png_free_buffer_list(png_structrp png_ptr, png_compression_bufferp *listp) +{ + png_compression_bufferp list = *listp; + + if (list != NULL) + { + *listp = NULL; + + do + { + png_compression_bufferp next = list->next; + + png_free(png_ptr, list); + list = next; + } + while (list != NULL); + } +} + +#ifdef PNG_WRITE_COMPRESSED_TEXT_SUPPORTED +/* This pair of functions encapsulates the operation of (a) compressing a + * text string, and (b) issuing it later as a series of chunk data writes. + * The compression_state structure is shared context for these functions + * set up by the caller to allow access to the relevant local variables. + * + * compression_buffer (new in 1.6.0) is just a linked list of zbuffer_size + * temporary buffers. From 1.6.0 it is retained in png_struct so that it will + * be correctly freed in the event of a write error (previous implementations + * just leaked memory.) + */ +typedef struct +{ + png_const_bytep input; /* The uncompressed input data */ + png_alloc_size_t input_len; /* Its length */ + png_uint_32 output_len; /* Final compressed length */ + png_byte output[1024]; /* First block of output */ +} compression_state; + +static void +png_text_compress_init(compression_state *comp, png_const_bytep input, + png_alloc_size_t input_len) +{ + comp->input = input; + comp->input_len = input_len; + comp->output_len = 0; +} + +/* Compress the data in the compression state input */ +static int +png_text_compress(png_structrp png_ptr, png_uint_32 chunk_name, + compression_state *comp, png_uint_32 prefix_len) +{ + int ret; + + /* To find the length of the output it is necessary to first compress the + * input, the result is buffered rather than using the two-pass algorithm + * that is used on the inflate side; deflate is assumed to be slower and a + * PNG writer is assumed to have more memory available than a PNG reader. + * + * IMPLEMENTATION NOTE: the zlib API deflateBound() can be used to find an + * upper limit on the output size, but it is always bigger than the input + * size so it is likely to be more efficient to use this linked-list + * approach. + */ + ret = png_deflate_claim(png_ptr, chunk_name, comp->input_len); + + if (ret != Z_OK) + return ret; + + /* Set up the compression buffers, we need a loop here to avoid overflowing a + * uInt. Use ZLIB_IO_MAX to limit the input. The output is always limited + * by the output buffer size, so there is no need to check that. Since this + * is ANSI-C we know that an 'int', hence a uInt, is always at least 16 bits + * in size. + */ + { + png_compression_bufferp *end = &png_ptr->zbuffer_list; + png_alloc_size_t input_len = comp->input_len; /* may be zero! */ + png_uint_32 output_len; + + /* zlib updates these for us: */ + png_ptr->zstream.next_in = PNGZ_INPUT_CAST(comp->input); + png_ptr->zstream.avail_in = 0; /* Set below */ + png_ptr->zstream.next_out = comp->output; + png_ptr->zstream.avail_out = (sizeof comp->output); + + output_len = png_ptr->zstream.avail_out; + + do + { + uInt avail_in = ZLIB_IO_MAX; + + if (avail_in > input_len) + avail_in = (uInt)input_len; + + input_len -= avail_in; + + png_ptr->zstream.avail_in = avail_in; + + if (png_ptr->zstream.avail_out == 0) + { + png_compression_buffer *next; + + /* Chunk data is limited to 2^31 bytes in length, so the prefix + * length must be counted here. + */ + if (output_len + prefix_len > PNG_UINT_31_MAX) + { + ret = Z_MEM_ERROR; + break; + } + + /* Need a new (malloc'ed) buffer, but there may be one present + * already. + */ + next = *end; + if (next == NULL) + { + next = png_voidcast(png_compression_bufferp, png_malloc_base + (png_ptr, PNG_COMPRESSION_BUFFER_SIZE(png_ptr))); + + if (next == NULL) + { + ret = Z_MEM_ERROR; + break; + } + + /* Link in this buffer (so that it will be freed later) */ + next->next = NULL; + *end = next; + } + + png_ptr->zstream.next_out = next->output; + png_ptr->zstream.avail_out = png_ptr->zbuffer_size; + output_len += png_ptr->zstream.avail_out; + + /* Move 'end' to the next buffer pointer. */ + end = &next->next; + } + + /* Compress the data */ + ret = deflate(&png_ptr->zstream, + input_len > 0 ? Z_NO_FLUSH : Z_FINISH); + + /* Claw back input data that was not consumed (because avail_in is + * reset above every time round the loop). + */ + input_len += png_ptr->zstream.avail_in; + png_ptr->zstream.avail_in = 0; /* safety */ + } + while (ret == Z_OK); + + /* There may be some space left in the last output buffer, this needs to + * be subtracted from output_len. + */ + output_len -= png_ptr->zstream.avail_out; + png_ptr->zstream.avail_out = 0; /* safety */ + comp->output_len = output_len; + + /* Now double check the output length, put in a custom message if it is + * too long. Otherwise ensure the z_stream::msg pointer is set to + * something. + */ + if (output_len + prefix_len >= PNG_UINT_31_MAX) + { + png_ptr->zstream.msg = PNGZ_MSG_CAST("compressed data too long"); + ret = Z_MEM_ERROR; + } + + else + png_zstream_error(png_ptr, ret); + + /* Reset zlib for another zTXt/iTXt or image data */ + png_ptr->zowner = 0; + + /* The only success case is Z_STREAM_END, input_len must be 0, if not this + * is an internal error. + */ + if (ret == Z_STREAM_END && input_len == 0) + { + /* Fix up the deflate header, if required */ + optimize_cmf(comp->output, comp->input_len); + + /* But Z_OK is returned, not Z_STREAM_END; this allows the claim + * function above to return Z_STREAM_END on an error (though it never + * does in the current versions of zlib.) + */ + return Z_OK; + } + + else + return ret; + } +} + +/* Ship the compressed text out via chunk writes */ +static void +png_write_compressed_data_out(png_structrp png_ptr, compression_state *comp) +{ + png_uint_32 output_len = comp->output_len; + png_const_bytep output = comp->output; + png_uint_32 avail = (sizeof comp->output); + png_compression_buffer *next = png_ptr->zbuffer_list; + + for (;;) + { + if (avail > output_len) + avail = output_len; + + png_write_chunk_data(png_ptr, output, avail); + + output_len -= avail; + + if (output_len == 0 || next == NULL) + break; + + avail = png_ptr->zbuffer_size; + output = next->output; + next = next->next; + } + + /* This is an internal error; 'next' must have been NULL! */ + if (output_len > 0) + png_error(png_ptr, "error writing ancillary chunked compressed data"); +} +#endif /* PNG_WRITE_COMPRESSED_TEXT_SUPPORTED */ + +#if defined(PNG_WRITE_TEXT_SUPPORTED) || defined(PNG_WRITE_pCAL_SUPPORTED) || \ + defined(PNG_WRITE_iCCP_SUPPORTED) || defined(PNG_WRITE_sPLT_SUPPORTED) +/* Check that the tEXt or zTXt keyword is valid per PNG 1.0 specification, + * and if invalid, correct the keyword rather than discarding the entire + * chunk. The PNG 1.0 specification requires keywords 1-79 characters in + * length, forbids leading or trailing whitespace, multiple internal spaces, + * and the non-break space (0x80) from ISO 8859-1. Returns keyword length. + * + * The 'new_key' buffer must be 80 characters in size (for the keyword plus a + * trailing '\0'). If this routine returns 0 then there was no keyword, or a + * valid one could not be generated, and the caller must png_error. + */ +static png_uint_32 +png_check_keyword(png_structrp png_ptr, png_const_charp key, png_bytep new_key) +{ + png_const_charp orig_key = key; + png_uint_32 key_len = 0; + int bad_character = 0; + int space = 1; + + png_debug(1, "in png_check_keyword"); + + if (key == NULL) + { + *new_key = 0; + return 0; + } + + while (*key && key_len < 79) + { + png_byte ch = (png_byte)(0xff & *key++); + + if ((ch > 32 && ch <= 126) || (ch >= 161 /*&& ch <= 255*/)) + *new_key++ = ch, ++key_len, space = 0; + + else if (!space) + { + /* A space or an invalid character when one wasn't seen immediately + * before; output just a space. + */ + *new_key++ = 32, ++key_len, space = 1; + + /* If the character was not a space then it is invalid. */ + if (ch != 32) + bad_character = ch; + } + + else if (!bad_character) + bad_character = ch; /* just skip it, record the first error */ + } + + if (key_len > 0 && space) /* trailing space */ + { + --key_len, --new_key; + if (!bad_character) + bad_character = 32; + } + + /* Terminate the keyword */ + *new_key = 0; + + if (key_len == 0) + return 0; + + /* Try to only output one warning per keyword: */ + if (*key) /* keyword too long */ + png_warning(png_ptr, "keyword truncated"); + + else if (bad_character) + { + PNG_WARNING_PARAMETERS(p) + + png_warning_parameter(p, 1, orig_key); + png_warning_parameter_signed(p, 2, PNG_NUMBER_FORMAT_02x, bad_character); + + png_formatted_warning(png_ptr, p, "keyword \"@1\": bad character '0x@2'"); + } + + return key_len; +} +#endif + +/* Write the IHDR chunk, and update the png_struct with the necessary + * information. Note that the rest of this code depends upon this + * information being correct. + */ +void /* PRIVATE */ +png_write_IHDR(png_structrp png_ptr, png_uint_32 width, png_uint_32 height, + int bit_depth, int color_type, int compression_type, int filter_type, + int interlace_type) +{ + png_byte buf[13]; /* Buffer to store the IHDR info */ + + png_debug(1, "in png_write_IHDR"); + + /* Check that we have valid input data from the application info */ + switch (color_type) + { + case PNG_COLOR_TYPE_GRAY: + switch (bit_depth) + { + case 1: + case 2: + case 4: + case 8: +#ifdef PNG_WRITE_16BIT_SUPPORTED + case 16: +#endif + png_ptr->channels = 1; break; + + default: + png_error(png_ptr, + "Invalid bit depth for grayscale image"); + } + break; + + case PNG_COLOR_TYPE_RGB: +#ifdef PNG_WRITE_16BIT_SUPPORTED + if (bit_depth != 8 && bit_depth != 16) +#else + if (bit_depth != 8) +#endif + png_error(png_ptr, "Invalid bit depth for RGB image"); + + png_ptr->channels = 3; + break; + + case PNG_COLOR_TYPE_PALETTE: + switch (bit_depth) + { + case 1: + case 2: + case 4: + case 8: + png_ptr->channels = 1; + break; + + default: + png_error(png_ptr, "Invalid bit depth for paletted image"); + } + break; + + case PNG_COLOR_TYPE_GRAY_ALPHA: + if (bit_depth != 8 && bit_depth != 16) + png_error(png_ptr, "Invalid bit depth for grayscale+alpha image"); + + png_ptr->channels = 2; + break; + + case PNG_COLOR_TYPE_RGB_ALPHA: +#ifdef PNG_WRITE_16BIT_SUPPORTED + if (bit_depth != 8 && bit_depth != 16) +#else + if (bit_depth != 8) +#endif + png_error(png_ptr, "Invalid bit depth for RGBA image"); + + png_ptr->channels = 4; + break; + + default: + png_error(png_ptr, "Invalid image color type specified"); + } + + if (compression_type != PNG_COMPRESSION_TYPE_BASE) + { + png_warning(png_ptr, "Invalid compression type specified"); + compression_type = PNG_COMPRESSION_TYPE_BASE; + } + + /* Write filter_method 64 (intrapixel differencing) only if + * 1. Libpng was compiled with PNG_MNG_FEATURES_SUPPORTED and + * 2. Libpng did not write a PNG signature (this filter_method is only + * used in PNG datastreams that are embedded in MNG datastreams) and + * 3. The application called png_permit_mng_features with a mask that + * included PNG_FLAG_MNG_FILTER_64 and + * 4. The filter_method is 64 and + * 5. The color_type is RGB or RGBA + */ + if ( +#ifdef PNG_MNG_FEATURES_SUPPORTED + !((png_ptr->mng_features_permitted & PNG_FLAG_MNG_FILTER_64) && + ((png_ptr->mode&PNG_HAVE_PNG_SIGNATURE) == 0) && + (color_type == PNG_COLOR_TYPE_RGB || + color_type == PNG_COLOR_TYPE_RGB_ALPHA) && + (filter_type == PNG_INTRAPIXEL_DIFFERENCING)) && +#endif + filter_type != PNG_FILTER_TYPE_BASE) + { + png_warning(png_ptr, "Invalid filter type specified"); + filter_type = PNG_FILTER_TYPE_BASE; + } + +#ifdef PNG_WRITE_INTERLACING_SUPPORTED + if (interlace_type != PNG_INTERLACE_NONE && + interlace_type != PNG_INTERLACE_ADAM7) + { + png_warning(png_ptr, "Invalid interlace type specified"); + interlace_type = PNG_INTERLACE_ADAM7; + } +#else + interlace_type=PNG_INTERLACE_NONE; +#endif + + /* Save the relevent information */ + png_ptr->bit_depth = (png_byte)bit_depth; + png_ptr->color_type = (png_byte)color_type; + png_ptr->interlaced = (png_byte)interlace_type; +#ifdef PNG_MNG_FEATURES_SUPPORTED + png_ptr->filter_type = (png_byte)filter_type; +#endif + png_ptr->compression_type = (png_byte)compression_type; + png_ptr->width = width; + png_ptr->height = height; + + png_ptr->pixel_depth = (png_byte)(bit_depth * png_ptr->channels); + png_ptr->rowbytes = PNG_ROWBYTES(png_ptr->pixel_depth, width); + /* Set the usr info, so any transformations can modify it */ + png_ptr->usr_width = png_ptr->width; + png_ptr->usr_bit_depth = png_ptr->bit_depth; + png_ptr->usr_channels = png_ptr->channels; + + /* Pack the header information into the buffer */ + png_save_uint_32(buf, width); + png_save_uint_32(buf + 4, height); + buf[8] = (png_byte)bit_depth; + buf[9] = (png_byte)color_type; + buf[10] = (png_byte)compression_type; + buf[11] = (png_byte)filter_type; + buf[12] = (png_byte)interlace_type; + + /* Write the chunk */ + png_write_complete_chunk(png_ptr, png_IHDR, buf, (png_size_t)13); + + if (!(png_ptr->do_filter)) + { + if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE || + png_ptr->bit_depth < 8) + png_ptr->do_filter = PNG_FILTER_NONE; + + else + png_ptr->do_filter = PNG_ALL_FILTERS; + } + + png_ptr->mode = PNG_HAVE_IHDR; /* not READY_FOR_ZTXT */ +} + +/* Write the palette. We are careful not to trust png_color to be in the + * correct order for PNG, so people can redefine it to any convenient + * structure. + */ +void /* PRIVATE */ +png_write_PLTE(png_structrp png_ptr, png_const_colorp palette, + png_uint_32 num_pal) +{ + png_uint_32 i; + png_const_colorp pal_ptr; + png_byte buf[3]; + + png_debug(1, "in png_write_PLTE"); + + if (( +#ifdef PNG_MNG_FEATURES_SUPPORTED + !(png_ptr->mng_features_permitted & PNG_FLAG_MNG_EMPTY_PLTE) && +#endif + num_pal == 0) || num_pal > 256) + { + if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE) + { + png_error(png_ptr, "Invalid number of colors in palette"); + } + + else + { + png_warning(png_ptr, "Invalid number of colors in palette"); + return; + } + } + + if (!(png_ptr->color_type&PNG_COLOR_MASK_COLOR)) + { + png_warning(png_ptr, + "Ignoring request to write a PLTE chunk in grayscale PNG"); + + return; + } + + png_ptr->num_palette = (png_uint_16)num_pal; + png_debug1(3, "num_palette = %d", png_ptr->num_palette); + + png_write_chunk_header(png_ptr, png_PLTE, (png_uint_32)(num_pal * 3)); +#ifdef PNG_POINTER_INDEXING_SUPPORTED + + for (i = 0, pal_ptr = palette; i < num_pal; i++, pal_ptr++) + { + buf[0] = pal_ptr->red; + buf[1] = pal_ptr->green; + buf[2] = pal_ptr->blue; + png_write_chunk_data(png_ptr, buf, (png_size_t)3); + } + +#else + /* This is a little slower but some buggy compilers need to do this + * instead + */ + pal_ptr=palette; + + for (i = 0; i < num_pal; i++) + { + buf[0] = pal_ptr[i].red; + buf[1] = pal_ptr[i].green; + buf[2] = pal_ptr[i].blue; + png_write_chunk_data(png_ptr, buf, (png_size_t)3); + } + +#endif + png_write_chunk_end(png_ptr); + png_ptr->mode |= PNG_HAVE_PLTE; +} + +/* This is similar to png_text_compress, above, except that it does not require + * all of the data at once and, instead of buffering the compressed result, + * writes it as IDAT chunks. Unlike png_text_compress it *can* png_error out + * because it calls the write interface. As a result it does its own error + * reporting and does not return an error code. In the event of error it will + * just call png_error. The input data length may exceed 32-bits. The 'flush' + * parameter is exactly the same as that to deflate, with the following + * meanings: + * + * Z_NO_FLUSH: normal incremental output of compressed data + * Z_SYNC_FLUSH: do a SYNC_FLUSH, used by png_write_flush + * Z_FINISH: this is the end of the input, do a Z_FINISH and clean up + * + * The routine manages the acquire and release of the png_ptr->zstream by + * checking and (at the end) clearing png_ptr->zowner, it does some sanity + * checks on the 'mode' flags while doing this. + */ +void /* PRIVATE */ +png_compress_IDAT(png_structrp png_ptr, png_const_bytep input, + png_alloc_size_t input_len, int flush) +{ + if (png_ptr->zowner != png_IDAT) + { + /* First time. Ensure we have a temporary buffer for compression and + * trim the buffer list if it has more than one entry to free memory. + * If 'WRITE_COMPRESSED_TEXT' is not set the list will never have been + * created at this point, but the check here is quick and safe. + */ + if (png_ptr->zbuffer_list == NULL) + { + png_ptr->zbuffer_list = png_voidcast(png_compression_bufferp, + png_malloc(png_ptr, PNG_COMPRESSION_BUFFER_SIZE(png_ptr))); + png_ptr->zbuffer_list->next = NULL; + } + + else + png_free_buffer_list(png_ptr, &png_ptr->zbuffer_list->next); + + /* It is a terminal error if we can't claim the zstream. */ + if (png_deflate_claim(png_ptr, png_IDAT, png_image_size(png_ptr)) != Z_OK) + png_error(png_ptr, png_ptr->zstream.msg); + + /* The output state is maintained in png_ptr->zstream, so it must be + * initialized here after the claim. + */ + png_ptr->zstream.next_out = png_ptr->zbuffer_list->output; + png_ptr->zstream.avail_out = png_ptr->zbuffer_size; + } + + /* Now loop reading and writing until all the input is consumed or an error + * terminates the operation. The _out values are maintained across calls to + * this function, but the input must be reset each time. + */ + png_ptr->zstream.next_in = PNGZ_INPUT_CAST(input); + png_ptr->zstream.avail_in = 0; /* set below */ + for (;;) + { + int ret; + + /* INPUT: from the row data */ + uInt avail = ZLIB_IO_MAX; + + if (avail > input_len) + avail = (uInt)input_len; /* safe because of the check */ + + png_ptr->zstream.avail_in = avail; + input_len -= avail; + + ret = deflate(&png_ptr->zstream, input_len > 0 ? Z_NO_FLUSH : flush); + + /* Include as-yet unconsumed input */ + input_len += png_ptr->zstream.avail_in; + png_ptr->zstream.avail_in = 0; + + /* OUTPUT: write complete IDAT chunks when avail_out drops to zero, note + * that these two zstream fields are preserved across the calls, therefore + * there is no need to set these up on entry to the loop. + */ + if (png_ptr->zstream.avail_out == 0) + { + png_bytep data = png_ptr->zbuffer_list->output; + uInt size = png_ptr->zbuffer_size; + + /* Write an IDAT containing the data then reset the buffer. The + * first IDAT may need deflate header optimization. + */ +# ifdef PNG_WRITE_OPTIMIZE_CMF_SUPPORTED + if (!(png_ptr->mode & PNG_HAVE_IDAT) && + png_ptr->compression_type == PNG_COMPRESSION_TYPE_BASE) + optimize_cmf(data, png_image_size(png_ptr)); +# endif + + png_write_complete_chunk(png_ptr, png_IDAT, data, size); + png_ptr->mode |= PNG_HAVE_IDAT; + + png_ptr->zstream.next_out = data; + png_ptr->zstream.avail_out = size; + + /* For SYNC_FLUSH or FINISH it is essential to keep calling zlib with + * the same flush parameter until it has finished output, for NO_FLUSH + * it doesn't matter. + */ + if (ret == Z_OK && flush != Z_NO_FLUSH) + continue; + } + + /* The order of these checks doesn't matter much; it just effect which + * possible error might be detected if multiple things go wrong at once. + */ + if (ret == Z_OK) /* most likely return code! */ + { + /* If all the input has been consumed then just return. If Z_FINISH + * was used as the flush parameter something has gone wrong if we get + * here. + */ + if (input_len == 0) + { + if (flush == Z_FINISH) + png_error(png_ptr, "Z_OK on Z_FINISH with output space"); + + return; + } + } + + else if (ret == Z_STREAM_END && flush == Z_FINISH) + { + /* This is the end of the IDAT data; any pending output must be + * flushed. For small PNG files we may still be at the beginning. + */ + png_bytep data = png_ptr->zbuffer_list->output; + uInt size = png_ptr->zbuffer_size - png_ptr->zstream.avail_out; + +# ifdef PNG_WRITE_OPTIMIZE_CMF_SUPPORTED + if (!(png_ptr->mode & PNG_HAVE_IDAT) && + png_ptr->compression_type == PNG_COMPRESSION_TYPE_BASE) + optimize_cmf(data, png_image_size(png_ptr)); +# endif + + png_write_complete_chunk(png_ptr, png_IDAT, data, size); + png_ptr->zstream.avail_out = 0; + png_ptr->zstream.next_out = NULL; + png_ptr->mode |= PNG_HAVE_IDAT | PNG_AFTER_IDAT; + + png_ptr->zowner = 0; /* Release the stream */ + return; + } + + else + { + /* This is an error condition. */ + png_zstream_error(png_ptr, ret); + png_error(png_ptr, png_ptr->zstream.msg); + } + } +} + +/* Write an IEND chunk */ +void /* PRIVATE */ +png_write_IEND(png_structrp png_ptr) +{ + png_debug(1, "in png_write_IEND"); + + png_write_complete_chunk(png_ptr, png_IEND, NULL, (png_size_t)0); + png_ptr->mode |= PNG_HAVE_IEND; +} + +#ifdef PNG_WRITE_gAMA_SUPPORTED +/* Write a gAMA chunk */ +void /* PRIVATE */ +png_write_gAMA_fixed(png_structrp png_ptr, png_fixed_point file_gamma) +{ + png_byte buf[4]; + + png_debug(1, "in png_write_gAMA"); + + /* file_gamma is saved in 1/100,000ths */ + png_save_uint_32(buf, (png_uint_32)file_gamma); + png_write_complete_chunk(png_ptr, png_gAMA, buf, (png_size_t)4); +} +#endif + +#ifdef PNG_WRITE_sRGB_SUPPORTED +/* Write a sRGB chunk */ +void /* PRIVATE */ +png_write_sRGB(png_structrp png_ptr, int srgb_intent) +{ + png_byte buf[1]; + + png_debug(1, "in png_write_sRGB"); + + if (srgb_intent >= PNG_sRGB_INTENT_LAST) + png_warning(png_ptr, + "Invalid sRGB rendering intent specified"); + + buf[0]=(png_byte)srgb_intent; + png_write_complete_chunk(png_ptr, png_sRGB, buf, (png_size_t)1); +} +#endif + +#ifdef PNG_WRITE_iCCP_SUPPORTED +/* Write an iCCP chunk */ +void /* PRIVATE */ +png_write_iCCP(png_structrp png_ptr, png_const_charp name, + png_const_bytep profile) +{ + png_uint_32 name_len; + png_uint_32 profile_len; + png_byte new_name[81]; /* 1 byte for the compression byte */ + compression_state comp; + + png_debug(1, "in png_write_iCCP"); + + /* These are all internal problems: the profile should have been checked + * before when it was stored. + */ + if (profile == NULL) + png_error(png_ptr, "No profile for iCCP chunk"); /* internal error */ + + profile_len = png_get_uint_32(profile); + + if (profile_len < 132) + png_error(png_ptr, "ICC profile too short"); + + if (profile_len & 0x03) + png_error(png_ptr, "ICC profile length invalid (not a multiple of 4)"); + + { + png_uint_32 embedded_profile_len = png_get_uint_32(profile); + + if (profile_len != embedded_profile_len) + png_error(png_ptr, "Profile length does not match profile"); + } + + name_len = png_check_keyword(png_ptr, name, new_name); + + if (name_len == 0) + png_error(png_ptr, "iCCP: invalid keyword"); + + new_name[++name_len] = PNG_COMPRESSION_TYPE_BASE; + + /* Make sure we include the NULL after the name and the compression type */ + ++name_len; + + png_text_compress_init(&comp, profile, profile_len); + + /* Allow for keyword terminator and compression byte */ + if (png_text_compress(png_ptr, png_iCCP, &comp, name_len) != Z_OK) + png_error(png_ptr, png_ptr->zstream.msg); + + png_write_chunk_header(png_ptr, png_iCCP, name_len + comp.output_len); + + png_write_chunk_data(png_ptr, new_name, name_len); + + png_write_compressed_data_out(png_ptr, &comp); + + png_write_chunk_end(png_ptr); +} +#endif + +#ifdef PNG_WRITE_sPLT_SUPPORTED +/* Write a sPLT chunk */ +void /* PRIVATE */ +png_write_sPLT(png_structrp png_ptr, png_const_sPLT_tp spalette) +{ + png_uint_32 name_len; + png_byte new_name[80]; + png_byte entrybuf[10]; + png_size_t entry_size = (spalette->depth == 8 ? 6 : 10); + png_size_t palette_size = entry_size * spalette->nentries; + png_sPLT_entryp ep; +#ifndef PNG_POINTER_INDEXING_SUPPORTED + int i; +#endif + + png_debug(1, "in png_write_sPLT"); + + name_len = png_check_keyword(png_ptr, spalette->name, new_name); + + if (name_len == 0) + png_error(png_ptr, "sPLT: invalid keyword"); + + /* Make sure we include the NULL after the name */ + png_write_chunk_header(png_ptr, png_sPLT, + (png_uint_32)(name_len + 2 + palette_size)); + + png_write_chunk_data(png_ptr, (png_bytep)new_name, + (png_size_t)(name_len + 1)); + + png_write_chunk_data(png_ptr, &spalette->depth, (png_size_t)1); + + /* Loop through each palette entry, writing appropriately */ +#ifdef PNG_POINTER_INDEXING_SUPPORTED + for (ep = spalette->entries; epentries + spalette->nentries; ep++) + { + if (spalette->depth == 8) + { + entrybuf[0] = (png_byte)ep->red; + entrybuf[1] = (png_byte)ep->green; + entrybuf[2] = (png_byte)ep->blue; + entrybuf[3] = (png_byte)ep->alpha; + png_save_uint_16(entrybuf + 4, ep->frequency); + } + + else + { + png_save_uint_16(entrybuf + 0, ep->red); + png_save_uint_16(entrybuf + 2, ep->green); + png_save_uint_16(entrybuf + 4, ep->blue); + png_save_uint_16(entrybuf + 6, ep->alpha); + png_save_uint_16(entrybuf + 8, ep->frequency); + } + + png_write_chunk_data(png_ptr, entrybuf, entry_size); + } +#else + ep=spalette->entries; + for (i = 0; i>spalette->nentries; i++) + { + if (spalette->depth == 8) + { + entrybuf[0] = (png_byte)ep[i].red; + entrybuf[1] = (png_byte)ep[i].green; + entrybuf[2] = (png_byte)ep[i].blue; + entrybuf[3] = (png_byte)ep[i].alpha; + png_save_uint_16(entrybuf + 4, ep[i].frequency); + } + + else + { + png_save_uint_16(entrybuf + 0, ep[i].red); + png_save_uint_16(entrybuf + 2, ep[i].green); + png_save_uint_16(entrybuf + 4, ep[i].blue); + png_save_uint_16(entrybuf + 6, ep[i].alpha); + png_save_uint_16(entrybuf + 8, ep[i].frequency); + } + + png_write_chunk_data(png_ptr, entrybuf, entry_size); + } +#endif + + png_write_chunk_end(png_ptr); +} +#endif + +#ifdef PNG_WRITE_sBIT_SUPPORTED +/* Write the sBIT chunk */ +void /* PRIVATE */ +png_write_sBIT(png_structrp png_ptr, png_const_color_8p sbit, int color_type) +{ + png_byte buf[4]; + png_size_t size; + + png_debug(1, "in png_write_sBIT"); + + /* Make sure we don't depend upon the order of PNG_COLOR_8 */ + if (color_type & PNG_COLOR_MASK_COLOR) + { + png_byte maxbits; + + maxbits = (png_byte)(color_type==PNG_COLOR_TYPE_PALETTE ? 8 : + png_ptr->usr_bit_depth); + + if (sbit->red == 0 || sbit->red > maxbits || + sbit->green == 0 || sbit->green > maxbits || + sbit->blue == 0 || sbit->blue > maxbits) + { + png_warning(png_ptr, "Invalid sBIT depth specified"); + return; + } + + buf[0] = sbit->red; + buf[1] = sbit->green; + buf[2] = sbit->blue; + size = 3; + } + + else + { + if (sbit->gray == 0 || sbit->gray > png_ptr->usr_bit_depth) + { + png_warning(png_ptr, "Invalid sBIT depth specified"); + return; + } + + buf[0] = sbit->gray; + size = 1; + } + + if (color_type & PNG_COLOR_MASK_ALPHA) + { + if (sbit->alpha == 0 || sbit->alpha > png_ptr->usr_bit_depth) + { + png_warning(png_ptr, "Invalid sBIT depth specified"); + return; + } + + buf[size++] = sbit->alpha; + } + + png_write_complete_chunk(png_ptr, png_sBIT, buf, size); +} +#endif + +#ifdef PNG_WRITE_cHRM_SUPPORTED +/* Write the cHRM chunk */ +void /* PRIVATE */ +png_write_cHRM_fixed(png_structrp png_ptr, const png_xy *xy) +{ + png_byte buf[32]; + + png_debug(1, "in png_write_cHRM"); + + /* Each value is saved in 1/100,000ths */ + png_save_int_32(buf, xy->whitex); + png_save_int_32(buf + 4, xy->whitey); + + png_save_int_32(buf + 8, xy->redx); + png_save_int_32(buf + 12, xy->redy); + + png_save_int_32(buf + 16, xy->greenx); + png_save_int_32(buf + 20, xy->greeny); + + png_save_int_32(buf + 24, xy->bluex); + png_save_int_32(buf + 28, xy->bluey); + + png_write_complete_chunk(png_ptr, png_cHRM, buf, 32); +} +#endif + +#ifdef PNG_WRITE_tRNS_SUPPORTED +/* Write the tRNS chunk */ +void /* PRIVATE */ +png_write_tRNS(png_structrp png_ptr, png_const_bytep trans_alpha, + png_const_color_16p tran, int num_trans, int color_type) +{ + png_byte buf[6]; + + png_debug(1, "in png_write_tRNS"); + + if (color_type == PNG_COLOR_TYPE_PALETTE) + { + if (num_trans <= 0 || num_trans > (int)png_ptr->num_palette) + { + png_app_warning(png_ptr, + "Invalid number of transparent colors specified"); + return; + } + + /* Write the chunk out as it is */ + png_write_complete_chunk(png_ptr, png_tRNS, trans_alpha, + (png_size_t)num_trans); + } + + else if (color_type == PNG_COLOR_TYPE_GRAY) + { + /* One 16 bit value */ + if (tran->gray >= (1 << png_ptr->bit_depth)) + { + png_app_warning(png_ptr, + "Ignoring attempt to write tRNS chunk out-of-range for bit_depth"); + + return; + } + + png_save_uint_16(buf, tran->gray); + png_write_complete_chunk(png_ptr, png_tRNS, buf, (png_size_t)2); + } + + else if (color_type == PNG_COLOR_TYPE_RGB) + { + /* Three 16 bit values */ + png_save_uint_16(buf, tran->red); + png_save_uint_16(buf + 2, tran->green); + png_save_uint_16(buf + 4, tran->blue); +#ifdef PNG_WRITE_16BIT_SUPPORTED + if (png_ptr->bit_depth == 8 && (buf[0] | buf[2] | buf[4])) +#else + if (buf[0] | buf[2] | buf[4]) +#endif + { + png_app_warning(png_ptr, + "Ignoring attempt to write 16-bit tRNS chunk when bit_depth is 8"); + return; + } + + png_write_complete_chunk(png_ptr, png_tRNS, buf, (png_size_t)6); + } + + else + { + png_app_warning(png_ptr, "Can't write tRNS with an alpha channel"); + } +} +#endif + +#ifdef PNG_WRITE_bKGD_SUPPORTED +/* Write the background chunk */ +void /* PRIVATE */ +png_write_bKGD(png_structrp png_ptr, png_const_color_16p back, int color_type) +{ + png_byte buf[6]; + + png_debug(1, "in png_write_bKGD"); + + if (color_type == PNG_COLOR_TYPE_PALETTE) + { + if ( +#ifdef PNG_MNG_FEATURES_SUPPORTED + (png_ptr->num_palette || + (!(png_ptr->mng_features_permitted & PNG_FLAG_MNG_EMPTY_PLTE))) && +#endif + back->index >= png_ptr->num_palette) + { + png_warning(png_ptr, "Invalid background palette index"); + return; + } + + buf[0] = back->index; + png_write_complete_chunk(png_ptr, png_bKGD, buf, (png_size_t)1); + } + + else if (color_type & PNG_COLOR_MASK_COLOR) + { + png_save_uint_16(buf, back->red); + png_save_uint_16(buf + 2, back->green); + png_save_uint_16(buf + 4, back->blue); +#ifdef PNG_WRITE_16BIT_SUPPORTED + if (png_ptr->bit_depth == 8 && (buf[0] | buf[2] | buf[4])) +#else + if (buf[0] | buf[2] | buf[4]) +#endif + { + png_warning(png_ptr, + "Ignoring attempt to write 16-bit bKGD chunk when bit_depth is 8"); + + return; + } + + png_write_complete_chunk(png_ptr, png_bKGD, buf, (png_size_t)6); + } + + else + { + if (back->gray >= (1 << png_ptr->bit_depth)) + { + png_warning(png_ptr, + "Ignoring attempt to write bKGD chunk out-of-range for bit_depth"); + + return; + } + + png_save_uint_16(buf, back->gray); + png_write_complete_chunk(png_ptr, png_bKGD, buf, (png_size_t)2); + } +} +#endif + +#ifdef PNG_WRITE_hIST_SUPPORTED +/* Write the histogram */ +void /* PRIVATE */ +png_write_hIST(png_structrp png_ptr, png_const_uint_16p hist, int num_hist) +{ + int i; + png_byte buf[3]; + + png_debug(1, "in png_write_hIST"); + + if (num_hist > (int)png_ptr->num_palette) + { + png_debug2(3, "num_hist = %d, num_palette = %d", num_hist, + png_ptr->num_palette); + + png_warning(png_ptr, "Invalid number of histogram entries specified"); + return; + } + + png_write_chunk_header(png_ptr, png_hIST, (png_uint_32)(num_hist * 2)); + + for (i = 0; i < num_hist; i++) + { + png_save_uint_16(buf, hist[i]); + png_write_chunk_data(png_ptr, buf, (png_size_t)2); + } + + png_write_chunk_end(png_ptr); +} +#endif + +#ifdef PNG_WRITE_tEXt_SUPPORTED +/* Write a tEXt chunk */ +void /* PRIVATE */ +png_write_tEXt(png_structrp png_ptr, png_const_charp key, png_const_charp text, + png_size_t text_len) +{ + png_uint_32 key_len; + png_byte new_key[80]; + + png_debug(1, "in png_write_tEXt"); + + key_len = png_check_keyword(png_ptr, key, new_key); + + if (key_len == 0) + png_error(png_ptr, "tEXt: invalid keyword"); + + if (text == NULL || *text == '\0') + text_len = 0; + + else + text_len = strlen(text); + + if (text_len > PNG_UINT_31_MAX - (key_len+1)) + png_error(png_ptr, "tEXt: text too long"); + + /* Make sure we include the 0 after the key */ + png_write_chunk_header(png_ptr, png_tEXt, + (png_uint_32)/*checked above*/(key_len + text_len + 1)); + /* + * We leave it to the application to meet PNG-1.0 requirements on the + * contents of the text. PNG-1.0 through PNG-1.2 discourage the use of + * any non-Latin-1 characters except for NEWLINE. ISO PNG will forbid them. + * The NUL character is forbidden by PNG-1.0 through PNG-1.2 and ISO PNG. + */ + png_write_chunk_data(png_ptr, new_key, key_len + 1); + + if (text_len) + png_write_chunk_data(png_ptr, (png_const_bytep)text, text_len); + + png_write_chunk_end(png_ptr); +} +#endif + +#ifdef PNG_WRITE_zTXt_SUPPORTED +/* Write a compressed text chunk */ +void /* PRIVATE */ +png_write_zTXt(png_structrp png_ptr, png_const_charp key, png_const_charp text, + png_size_t text_len, int compression) +{ + png_uint_32 key_len; + png_byte new_key[81]; + compression_state comp; + + png_debug(1, "in png_write_zTXt"); + PNG_UNUSED(text_len) /* Always use strlen */ + + if (compression == PNG_TEXT_COMPRESSION_NONE) + { + png_write_tEXt(png_ptr, key, text, 0); + return; + } + + if (compression != PNG_TEXT_COMPRESSION_zTXt) + png_error(png_ptr, "zTXt: invalid compression type"); + + key_len = png_check_keyword(png_ptr, key, new_key); + + if (key_len == 0) + png_error(png_ptr, "zTXt: invalid keyword"); + + /* Add the compression method and 1 for the keyword separator. */ + new_key[++key_len] = PNG_COMPRESSION_TYPE_BASE; + ++key_len; + + /* Compute the compressed data; do it now for the length */ + png_text_compress_init(&comp, (png_const_bytep)text, + text == NULL ? 0 : strlen(text)); + + if (png_text_compress(png_ptr, png_zTXt, &comp, key_len) != Z_OK) + png_error(png_ptr, png_ptr->zstream.msg); + + /* Write start of chunk */ + png_write_chunk_header(png_ptr, png_zTXt, key_len + comp.output_len); + + /* Write key */ + png_write_chunk_data(png_ptr, new_key, key_len); + + /* Write the compressed data */ + png_write_compressed_data_out(png_ptr, &comp); + + /* Close the chunk */ + png_write_chunk_end(png_ptr); +} +#endif + +#ifdef PNG_WRITE_iTXt_SUPPORTED +/* Write an iTXt chunk */ +void /* PRIVATE */ +png_write_iTXt(png_structrp png_ptr, int compression, png_const_charp key, + png_const_charp lang, png_const_charp lang_key, png_const_charp text) +{ + png_uint_32 key_len, prefix_len; + png_size_t lang_len, lang_key_len; + png_byte new_key[82]; + compression_state comp; + + png_debug(1, "in png_write_iTXt"); + + key_len = png_check_keyword(png_ptr, key, new_key); + + if (key_len == 0) + png_error(png_ptr, "iTXt: invalid keyword"); + + /* Set the compression flag */ + switch (compression) + { + case PNG_ITXT_COMPRESSION_NONE: + case PNG_TEXT_COMPRESSION_NONE: + compression = new_key[++key_len] = 0; /* no compression */ + break; + + case PNG_TEXT_COMPRESSION_zTXt: + case PNG_ITXT_COMPRESSION_zTXt: + compression = new_key[++key_len] = 1; /* compressed */ + break; + + default: + png_error(png_ptr, "iTXt: invalid compression"); + } + + new_key[++key_len] = PNG_COMPRESSION_TYPE_BASE; + ++key_len; /* for the keywod separator */ + + /* We leave it to the application to meet PNG-1.0 requirements on the + * contents of the text. PNG-1.0 through PNG-1.2 discourage the use of + * any non-Latin-1 characters except for NEWLINE. ISO PNG, however, + * specifies that the text is UTF-8 and this really doesn't require any + * checking. + * + * The NUL character is forbidden by PNG-1.0 through PNG-1.2 and ISO PNG. + * + * TODO: validate the language tag correctly (see the spec.) + */ + if (lang == NULL) lang = ""; /* empty language is valid */ + lang_len = strlen(lang)+1; + if (lang_key == NULL) lang_key = ""; /* may be empty */ + lang_key_len = strlen(lang_key)+1; + if (text == NULL) text = ""; /* may be empty */ + + prefix_len = key_len; + if (lang_len > PNG_UINT_31_MAX-prefix_len) + prefix_len = PNG_UINT_31_MAX; + else + prefix_len = (png_uint_32)(prefix_len + lang_len); + + if (lang_key_len > PNG_UINT_31_MAX-prefix_len) + prefix_len = PNG_UINT_31_MAX; + else + prefix_len = (png_uint_32)(prefix_len + lang_key_len); + + png_text_compress_init(&comp, (png_const_bytep)text, strlen(text)); + + if (compression) + { + if (png_text_compress(png_ptr, png_iTXt, &comp, prefix_len) != Z_OK) + png_error(png_ptr, png_ptr->zstream.msg); + } + + else + { + if (comp.input_len > PNG_UINT_31_MAX-prefix_len) + png_error(png_ptr, "iTXt: uncompressed text too long"); + + /* So the string will fit in a chunk: */ + comp.output_len = (png_uint_32)/*SAFE*/comp.input_len; + } + + png_write_chunk_header(png_ptr, png_iTXt, comp.output_len + prefix_len); + + png_write_chunk_data(png_ptr, new_key, key_len); + + png_write_chunk_data(png_ptr, (png_const_bytep)lang, lang_len); + + png_write_chunk_data(png_ptr, (png_const_bytep)lang_key, lang_key_len); + + if (compression) + png_write_compressed_data_out(png_ptr, &comp); + + else + png_write_chunk_data(png_ptr, (png_const_bytep)text, comp.input_len); + + png_write_chunk_end(png_ptr); +} +#endif + +#ifdef PNG_WRITE_oFFs_SUPPORTED +/* Write the oFFs chunk */ +void /* PRIVATE */ +png_write_oFFs(png_structrp png_ptr, png_int_32 x_offset, png_int_32 y_offset, + int unit_type) +{ + png_byte buf[9]; + + png_debug(1, "in png_write_oFFs"); + + if (unit_type >= PNG_OFFSET_LAST) + png_warning(png_ptr, "Unrecognized unit type for oFFs chunk"); + + png_save_int_32(buf, x_offset); + png_save_int_32(buf + 4, y_offset); + buf[8] = (png_byte)unit_type; + + png_write_complete_chunk(png_ptr, png_oFFs, buf, (png_size_t)9); +} +#endif +#ifdef PNG_WRITE_pCAL_SUPPORTED +/* Write the pCAL chunk (described in the PNG extensions document) */ +void /* PRIVATE */ +png_write_pCAL(png_structrp png_ptr, png_charp purpose, png_int_32 X0, + png_int_32 X1, int type, int nparams, png_const_charp units, + png_charpp params) +{ + png_uint_32 purpose_len; + png_size_t units_len, total_len; + png_size_tp params_len; + png_byte buf[10]; + png_byte new_purpose[80]; + int i; + + png_debug1(1, "in png_write_pCAL (%d parameters)", nparams); + + if (type >= PNG_EQUATION_LAST) + png_error(png_ptr, "Unrecognized equation type for pCAL chunk"); + + purpose_len = png_check_keyword(png_ptr, purpose, new_purpose); + + if (purpose_len == 0) + png_error(png_ptr, "pCAL: invalid keyword"); + + ++purpose_len; /* terminator */ + + png_debug1(3, "pCAL purpose length = %d", (int)purpose_len); + units_len = strlen(units) + (nparams == 0 ? 0 : 1); + png_debug1(3, "pCAL units length = %d", (int)units_len); + total_len = purpose_len + units_len + 10; + + params_len = (png_size_tp)png_malloc(png_ptr, + (png_alloc_size_t)(nparams * (sizeof (png_size_t)))); + + /* Find the length of each parameter, making sure we don't count the + * null terminator for the last parameter. + */ + for (i = 0; i < nparams; i++) + { + params_len[i] = strlen(params[i]) + (i == nparams - 1 ? 0 : 1); + png_debug2(3, "pCAL parameter %d length = %lu", i, + (unsigned long)params_len[i]); + total_len += params_len[i]; + } + + png_debug1(3, "pCAL total length = %d", (int)total_len); + png_write_chunk_header(png_ptr, png_pCAL, (png_uint_32)total_len); + png_write_chunk_data(png_ptr, new_purpose, purpose_len); + png_save_int_32(buf, X0); + png_save_int_32(buf + 4, X1); + buf[8] = (png_byte)type; + buf[9] = (png_byte)nparams; + png_write_chunk_data(png_ptr, buf, (png_size_t)10); + png_write_chunk_data(png_ptr, (png_const_bytep)units, (png_size_t)units_len); + + for (i = 0; i < nparams; i++) + { + png_write_chunk_data(png_ptr, (png_const_bytep)params[i], params_len[i]); + } + + png_free(png_ptr, params_len); + png_write_chunk_end(png_ptr); +} +#endif + +#ifdef PNG_WRITE_sCAL_SUPPORTED +/* Write the sCAL chunk */ +void /* PRIVATE */ +png_write_sCAL_s(png_structrp png_ptr, int unit, png_const_charp width, + png_const_charp height) +{ + png_byte buf[64]; + png_size_t wlen, hlen, total_len; + + png_debug(1, "in png_write_sCAL_s"); + + wlen = strlen(width); + hlen = strlen(height); + total_len = wlen + hlen + 2; + + if (total_len > 64) + { + png_warning(png_ptr, "Can't write sCAL (buffer too small)"); + return; + } + + buf[0] = (png_byte)unit; + memcpy(buf + 1, width, wlen + 1); /* Append the '\0' here */ + memcpy(buf + wlen + 2, height, hlen); /* Do NOT append the '\0' here */ + + png_debug1(3, "sCAL total length = %u", (unsigned int)total_len); + png_write_complete_chunk(png_ptr, png_sCAL, buf, total_len); +} +#endif + +#ifdef PNG_WRITE_pHYs_SUPPORTED +/* Write the pHYs chunk */ +void /* PRIVATE */ +png_write_pHYs(png_structrp png_ptr, png_uint_32 x_pixels_per_unit, + png_uint_32 y_pixels_per_unit, + int unit_type) +{ + png_byte buf[9]; + + png_debug(1, "in png_write_pHYs"); + + if (unit_type >= PNG_RESOLUTION_LAST) + png_warning(png_ptr, "Unrecognized unit type for pHYs chunk"); + + png_save_uint_32(buf, x_pixels_per_unit); + png_save_uint_32(buf + 4, y_pixels_per_unit); + buf[8] = (png_byte)unit_type; + + png_write_complete_chunk(png_ptr, png_pHYs, buf, (png_size_t)9); +} +#endif + +#ifdef PNG_WRITE_tIME_SUPPORTED +/* Write the tIME chunk. Use either png_convert_from_struct_tm() + * or png_convert_from_time_t(), or fill in the structure yourself. + */ +void /* PRIVATE */ +png_write_tIME(png_structrp png_ptr, png_const_timep mod_time) +{ + png_byte buf[7]; + + png_debug(1, "in png_write_tIME"); + + if (mod_time->month > 12 || mod_time->month < 1 || + mod_time->day > 31 || mod_time->day < 1 || + mod_time->hour > 23 || mod_time->second > 60) + { + png_warning(png_ptr, "Invalid time specified for tIME chunk"); + return; + } + + png_save_uint_16(buf, mod_time->year); + buf[2] = mod_time->month; + buf[3] = mod_time->day; + buf[4] = mod_time->hour; + buf[5] = mod_time->minute; + buf[6] = mod_time->second; + + png_write_complete_chunk(png_ptr, png_tIME, buf, (png_size_t)7); +} +#endif + +/* Initializes the row writing capability of libpng */ +void /* PRIVATE */ +png_write_start_row(png_structrp png_ptr) +{ +#ifdef PNG_WRITE_INTERLACING_SUPPORTED + /* Arrays to facilitate easy interlacing - use pass (0 - 6) as index */ + + /* Start of interlace block */ + static PNG_CONST png_byte png_pass_start[7] = {0, 4, 0, 2, 0, 1, 0}; + + /* Offset to next interlace block */ + static PNG_CONST png_byte png_pass_inc[7] = {8, 8, 4, 4, 2, 2, 1}; + + /* Start of interlace block in the y direction */ + static PNG_CONST png_byte png_pass_ystart[7] = {0, 0, 4, 0, 2, 0, 1}; + + /* Offset to next interlace block in the y direction */ + static PNG_CONST png_byte png_pass_yinc[7] = {8, 8, 8, 4, 4, 2, 2}; +#endif + + png_alloc_size_t buf_size; + int usr_pixel_depth; + + png_debug(1, "in png_write_start_row"); + + usr_pixel_depth = png_ptr->usr_channels * png_ptr->usr_bit_depth; + buf_size = PNG_ROWBYTES(usr_pixel_depth, png_ptr->width) + 1; + + /* 1.5.6: added to allow checking in the row write code. */ + png_ptr->transformed_pixel_depth = png_ptr->pixel_depth; + png_ptr->maximum_pixel_depth = (png_byte)usr_pixel_depth; + + /* Set up row buffer */ + png_ptr->row_buf = (png_bytep)png_malloc(png_ptr, buf_size); + + png_ptr->row_buf[0] = PNG_FILTER_VALUE_NONE; + +#ifdef PNG_WRITE_FILTER_SUPPORTED + /* Set up filtering buffer, if using this filter */ + if (png_ptr->do_filter & PNG_FILTER_SUB) + { + png_ptr->sub_row = (png_bytep)png_malloc(png_ptr, png_ptr->rowbytes + 1); + + png_ptr->sub_row[0] = PNG_FILTER_VALUE_SUB; + } + + /* We only need to keep the previous row if we are using one of these. */ + if (png_ptr->do_filter & (PNG_FILTER_AVG | PNG_FILTER_UP | PNG_FILTER_PAETH)) + { + /* Set up previous row buffer */ + png_ptr->prev_row = (png_bytep)png_calloc(png_ptr, buf_size); + + if (png_ptr->do_filter & PNG_FILTER_UP) + { + png_ptr->up_row = (png_bytep)png_malloc(png_ptr, + png_ptr->rowbytes + 1); + + png_ptr->up_row[0] = PNG_FILTER_VALUE_UP; + } + + if (png_ptr->do_filter & PNG_FILTER_AVG) + { + png_ptr->avg_row = (png_bytep)png_malloc(png_ptr, + png_ptr->rowbytes + 1); + + png_ptr->avg_row[0] = PNG_FILTER_VALUE_AVG; + } + + if (png_ptr->do_filter & PNG_FILTER_PAETH) + { + png_ptr->paeth_row = (png_bytep)png_malloc(png_ptr, + png_ptr->rowbytes + 1); + + png_ptr->paeth_row[0] = PNG_FILTER_VALUE_PAETH; + } + } +#endif /* PNG_WRITE_FILTER_SUPPORTED */ + +#ifdef PNG_WRITE_INTERLACING_SUPPORTED + /* If interlaced, we need to set up width and height of pass */ + if (png_ptr->interlaced) + { + if (!(png_ptr->transformations & PNG_INTERLACE)) + { + png_ptr->num_rows = (png_ptr->height + png_pass_yinc[0] - 1 - + png_pass_ystart[0]) / png_pass_yinc[0]; + + png_ptr->usr_width = (png_ptr->width + png_pass_inc[0] - 1 - + png_pass_start[0]) / png_pass_inc[0]; + } + + else + { + png_ptr->num_rows = png_ptr->height; + png_ptr->usr_width = png_ptr->width; + } + } + + else +#endif + { + png_ptr->num_rows = png_ptr->height; + png_ptr->usr_width = png_ptr->width; + } +} + +/* Internal use only. Called when finished processing a row of data. */ +void /* PRIVATE */ +png_write_finish_row(png_structrp png_ptr) +{ +#ifdef PNG_WRITE_INTERLACING_SUPPORTED + /* Arrays to facilitate easy interlacing - use pass (0 - 6) as index */ + + /* Start of interlace block */ + static PNG_CONST png_byte png_pass_start[7] = {0, 4, 0, 2, 0, 1, 0}; + + /* Offset to next interlace block */ + static PNG_CONST png_byte png_pass_inc[7] = {8, 8, 4, 4, 2, 2, 1}; + + /* Start of interlace block in the y direction */ + static PNG_CONST png_byte png_pass_ystart[7] = {0, 0, 4, 0, 2, 0, 1}; + + /* Offset to next interlace block in the y direction */ + static PNG_CONST png_byte png_pass_yinc[7] = {8, 8, 8, 4, 4, 2, 2}; +#endif + + png_debug(1, "in png_write_finish_row"); + + /* Next row */ + png_ptr->row_number++; + + /* See if we are done */ + if (png_ptr->row_number < png_ptr->num_rows) + return; + +#ifdef PNG_WRITE_INTERLACING_SUPPORTED + /* If interlaced, go to next pass */ + if (png_ptr->interlaced) + { + png_ptr->row_number = 0; + if (png_ptr->transformations & PNG_INTERLACE) + { + png_ptr->pass++; + } + + else + { + /* Loop until we find a non-zero width or height pass */ + do + { + png_ptr->pass++; + + if (png_ptr->pass >= 7) + break; + + png_ptr->usr_width = (png_ptr->width + + png_pass_inc[png_ptr->pass] - 1 - + png_pass_start[png_ptr->pass]) / + png_pass_inc[png_ptr->pass]; + + png_ptr->num_rows = (png_ptr->height + + png_pass_yinc[png_ptr->pass] - 1 - + png_pass_ystart[png_ptr->pass]) / + png_pass_yinc[png_ptr->pass]; + + if (png_ptr->transformations & PNG_INTERLACE) + break; + + } while (png_ptr->usr_width == 0 || png_ptr->num_rows == 0); + + } + + /* Reset the row above the image for the next pass */ + if (png_ptr->pass < 7) + { + if (png_ptr->prev_row != NULL) + memset(png_ptr->prev_row, 0, + (png_size_t)(PNG_ROWBYTES(png_ptr->usr_channels* + png_ptr->usr_bit_depth, png_ptr->width)) + 1); + + return; + } + } +#endif + + /* If we get here, we've just written the last row, so we need + to flush the compressor */ + png_compress_IDAT(png_ptr, NULL, 0, Z_FINISH); +} + +#ifdef PNG_WRITE_INTERLACING_SUPPORTED +/* Pick out the correct pixels for the interlace pass. + * The basic idea here is to go through the row with a source + * pointer and a destination pointer (sp and dp), and copy the + * correct pixels for the pass. As the row gets compacted, + * sp will always be >= dp, so we should never overwrite anything. + * See the default: case for the easiest code to understand. + */ +void /* PRIVATE */ +png_do_write_interlace(png_row_infop row_info, png_bytep row, int pass) +{ + /* Arrays to facilitate easy interlacing - use pass (0 - 6) as index */ + + /* Start of interlace block */ + static PNG_CONST png_byte png_pass_start[7] = {0, 4, 0, 2, 0, 1, 0}; + + /* Offset to next interlace block */ + static PNG_CONST png_byte png_pass_inc[7] = {8, 8, 4, 4, 2, 2, 1}; + + png_debug(1, "in png_do_write_interlace"); + + /* We don't have to do anything on the last pass (6) */ + if (pass < 6) + { + /* Each pixel depth is handled separately */ + switch (row_info->pixel_depth) + { + case 1: + { + png_bytep sp; + png_bytep dp; + int shift; + int d; + int value; + png_uint_32 i; + png_uint_32 row_width = row_info->width; + + dp = row; + d = 0; + shift = 7; + + for (i = png_pass_start[pass]; i < row_width; + i += png_pass_inc[pass]) + { + sp = row + (png_size_t)(i >> 3); + value = (int)(*sp >> (7 - (int)(i & 0x07))) & 0x01; + d |= (value << shift); + + if (shift == 0) + { + shift = 7; + *dp++ = (png_byte)d; + d = 0; + } + + else + shift--; + + } + if (shift != 7) + *dp = (png_byte)d; + + break; + } + + case 2: + { + png_bytep sp; + png_bytep dp; + int shift; + int d; + int value; + png_uint_32 i; + png_uint_32 row_width = row_info->width; + + dp = row; + shift = 6; + d = 0; + + for (i = png_pass_start[pass]; i < row_width; + i += png_pass_inc[pass]) + { + sp = row + (png_size_t)(i >> 2); + value = (*sp >> ((3 - (int)(i & 0x03)) << 1)) & 0x03; + d |= (value << shift); + + if (shift == 0) + { + shift = 6; + *dp++ = (png_byte)d; + d = 0; + } + + else + shift -= 2; + } + if (shift != 6) + *dp = (png_byte)d; + + break; + } + + case 4: + { + png_bytep sp; + png_bytep dp; + int shift; + int d; + int value; + png_uint_32 i; + png_uint_32 row_width = row_info->width; + + dp = row; + shift = 4; + d = 0; + for (i = png_pass_start[pass]; i < row_width; + i += png_pass_inc[pass]) + { + sp = row + (png_size_t)(i >> 1); + value = (*sp >> ((1 - (int)(i & 0x01)) << 2)) & 0x0f; + d |= (value << shift); + + if (shift == 0) + { + shift = 4; + *dp++ = (png_byte)d; + d = 0; + } + + else + shift -= 4; + } + if (shift != 4) + *dp = (png_byte)d; + + break; + } + + default: + { + png_bytep sp; + png_bytep dp; + png_uint_32 i; + png_uint_32 row_width = row_info->width; + png_size_t pixel_bytes; + + /* Start at the beginning */ + dp = row; + + /* Find out how many bytes each pixel takes up */ + pixel_bytes = (row_info->pixel_depth >> 3); + + /* Loop through the row, only looking at the pixels that matter */ + for (i = png_pass_start[pass]; i < row_width; + i += png_pass_inc[pass]) + { + /* Find out where the original pixel is */ + sp = row + (png_size_t)i * pixel_bytes; + + /* Move the pixel */ + if (dp != sp) + memcpy(dp, sp, pixel_bytes); + + /* Next pixel */ + dp += pixel_bytes; + } + break; + } + } + /* Set new row width */ + row_info->width = (row_info->width + + png_pass_inc[pass] - 1 - + png_pass_start[pass]) / + png_pass_inc[pass]; + + row_info->rowbytes = PNG_ROWBYTES(row_info->pixel_depth, + row_info->width); + } +} +#endif + +/* This filters the row, chooses which filter to use, if it has not already + * been specified by the application, and then writes the row out with the + * chosen filter. + */ +static void png_write_filtered_row(png_structrp png_ptr, png_bytep filtered_row, + png_size_t row_bytes); + +#define PNG_MAXSUM (((png_uint_32)(-1)) >> 1) +#define PNG_HISHIFT 10 +#define PNG_LOMASK ((png_uint_32)0xffffL) +#define PNG_HIMASK ((png_uint_32)(~PNG_LOMASK >> PNG_HISHIFT)) +void /* PRIVATE */ +png_write_find_filter(png_structrp png_ptr, png_row_infop row_info) +{ + png_bytep best_row; +#ifdef PNG_WRITE_FILTER_SUPPORTED + png_bytep prev_row, row_buf; + png_uint_32 mins, bpp; + png_byte filter_to_do = png_ptr->do_filter; + png_size_t row_bytes = row_info->rowbytes; +#ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED + int num_p_filters = png_ptr->num_prev_filters; +#endif + + png_debug(1, "in png_write_find_filter"); + +#ifndef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED + if (png_ptr->row_number == 0 && filter_to_do == PNG_ALL_FILTERS) + { + /* These will never be selected so we need not test them. */ + filter_to_do &= ~(PNG_FILTER_UP | PNG_FILTER_PAETH); + } +#endif + + /* Find out how many bytes offset each pixel is */ + bpp = (row_info->pixel_depth + 7) >> 3; + + prev_row = png_ptr->prev_row; +#endif + best_row = png_ptr->row_buf; +#ifdef PNG_WRITE_FILTER_SUPPORTED + row_buf = best_row; + mins = PNG_MAXSUM; + + /* The prediction method we use is to find which method provides the + * smallest value when summing the absolute values of the distances + * from zero, using anything >= 128 as negative numbers. This is known + * as the "minimum sum of absolute differences" heuristic. Other + * heuristics are the "weighted minimum sum of absolute differences" + * (experimental and can in theory improve compression), and the "zlib + * predictive" method (not implemented yet), which does test compressions + * of lines using different filter methods, and then chooses the + * (series of) filter(s) that give minimum compressed data size (VERY + * computationally expensive). + * + * GRR 980525: consider also + * + * (1) minimum sum of absolute differences from running average (i.e., + * keep running sum of non-absolute differences & count of bytes) + * [track dispersion, too? restart average if dispersion too large?] + * + * (1b) minimum sum of absolute differences from sliding average, probably + * with window size <= deflate window (usually 32K) + * + * (2) minimum sum of squared differences from zero or running average + * (i.e., ~ root-mean-square approach) + */ + + + /* We don't need to test the 'no filter' case if this is the only filter + * that has been chosen, as it doesn't actually do anything to the data. + */ + if ((filter_to_do & PNG_FILTER_NONE) && filter_to_do != PNG_FILTER_NONE) + { + png_bytep rp; + png_uint_32 sum = 0; + png_size_t i; + int v; + + for (i = 0, rp = row_buf + 1; i < row_bytes; i++, rp++) + { + v = *rp; + sum += (v < 128) ? v : 256 - v; + } + +#ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED + if (png_ptr->heuristic_method == PNG_FILTER_HEURISTIC_WEIGHTED) + { + png_uint_32 sumhi, sumlo; + int j; + sumlo = sum & PNG_LOMASK; + sumhi = (sum >> PNG_HISHIFT) & PNG_HIMASK; /* Gives us some footroom */ + + /* Reduce the sum if we match any of the previous rows */ + for (j = 0; j < num_p_filters; j++) + { + if (png_ptr->prev_filters[j] == PNG_FILTER_VALUE_NONE) + { + sumlo = (sumlo * png_ptr->filter_weights[j]) >> + PNG_WEIGHT_SHIFT; + + sumhi = (sumhi * png_ptr->filter_weights[j]) >> + PNG_WEIGHT_SHIFT; + } + } + + /* Factor in the cost of this filter (this is here for completeness, + * but it makes no sense to have a "cost" for the NONE filter, as + * it has the minimum possible computational cost - none). + */ + sumlo = (sumlo * png_ptr->filter_costs[PNG_FILTER_VALUE_NONE]) >> + PNG_COST_SHIFT; + + sumhi = (sumhi * png_ptr->filter_costs[PNG_FILTER_VALUE_NONE]) >> + PNG_COST_SHIFT; + + if (sumhi > PNG_HIMASK) + sum = PNG_MAXSUM; + + else + sum = (sumhi << PNG_HISHIFT) + sumlo; + } +#endif + mins = sum; + } + + /* Sub filter */ + if (filter_to_do == PNG_FILTER_SUB) + /* It's the only filter so no testing is needed */ + { + png_bytep rp, lp, dp; + png_size_t i; + + for (i = 0, rp = row_buf + 1, dp = png_ptr->sub_row + 1; i < bpp; + i++, rp++, dp++) + { + *dp = *rp; + } + + for (lp = row_buf + 1; i < row_bytes; + i++, rp++, lp++, dp++) + { + *dp = (png_byte)(((int)*rp - (int)*lp) & 0xff); + } + + best_row = png_ptr->sub_row; + } + + else if (filter_to_do & PNG_FILTER_SUB) + { + png_bytep rp, dp, lp; + png_uint_32 sum = 0, lmins = mins; + png_size_t i; + int v; + +#ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED + /* We temporarily increase the "minimum sum" by the factor we + * would reduce the sum of this filter, so that we can do the + * early exit comparison without scaling the sum each time. + */ + if (png_ptr->heuristic_method == PNG_FILTER_HEURISTIC_WEIGHTED) + { + int j; + png_uint_32 lmhi, lmlo; + lmlo = lmins & PNG_LOMASK; + lmhi = (lmins >> PNG_HISHIFT) & PNG_HIMASK; + + for (j = 0; j < num_p_filters; j++) + { + if (png_ptr->prev_filters[j] == PNG_FILTER_VALUE_SUB) + { + lmlo = (lmlo * png_ptr->inv_filter_weights[j]) >> + PNG_WEIGHT_SHIFT; + + lmhi = (lmhi * png_ptr->inv_filter_weights[j]) >> + PNG_WEIGHT_SHIFT; + } + } + + lmlo = (lmlo * png_ptr->inv_filter_costs[PNG_FILTER_VALUE_SUB]) >> + PNG_COST_SHIFT; + + lmhi = (lmhi * png_ptr->inv_filter_costs[PNG_FILTER_VALUE_SUB]) >> + PNG_COST_SHIFT; + + if (lmhi > PNG_HIMASK) + lmins = PNG_MAXSUM; + + else + lmins = (lmhi << PNG_HISHIFT) + lmlo; + } +#endif + + for (i = 0, rp = row_buf + 1, dp = png_ptr->sub_row + 1; i < bpp; + i++, rp++, dp++) + { + v = *dp = *rp; + + sum += (v < 128) ? v : 256 - v; + } + + for (lp = row_buf + 1; i < row_bytes; + i++, rp++, lp++, dp++) + { + v = *dp = (png_byte)(((int)*rp - (int)*lp) & 0xff); + + sum += (v < 128) ? v : 256 - v; + + if (sum > lmins) /* We are already worse, don't continue. */ + break; + } + +#ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED + if (png_ptr->heuristic_method == PNG_FILTER_HEURISTIC_WEIGHTED) + { + int j; + png_uint_32 sumhi, sumlo; + sumlo = sum & PNG_LOMASK; + sumhi = (sum >> PNG_HISHIFT) & PNG_HIMASK; + + for (j = 0; j < num_p_filters; j++) + { + if (png_ptr->prev_filters[j] == PNG_FILTER_VALUE_SUB) + { + sumlo = (sumlo * png_ptr->inv_filter_weights[j]) >> + PNG_WEIGHT_SHIFT; + + sumhi = (sumhi * png_ptr->inv_filter_weights[j]) >> + PNG_WEIGHT_SHIFT; + } + } + + sumlo = (sumlo * png_ptr->inv_filter_costs[PNG_FILTER_VALUE_SUB]) >> + PNG_COST_SHIFT; + + sumhi = (sumhi * png_ptr->inv_filter_costs[PNG_FILTER_VALUE_SUB]) >> + PNG_COST_SHIFT; + + if (sumhi > PNG_HIMASK) + sum = PNG_MAXSUM; + + else + sum = (sumhi << PNG_HISHIFT) + sumlo; + } +#endif + + if (sum < mins) + { + mins = sum; + best_row = png_ptr->sub_row; + } + } + + /* Up filter */ + if (filter_to_do == PNG_FILTER_UP) + { + png_bytep rp, dp, pp; + png_size_t i; + + for (i = 0, rp = row_buf + 1, dp = png_ptr->up_row + 1, + pp = prev_row + 1; i < row_bytes; + i++, rp++, pp++, dp++) + { + *dp = (png_byte)(((int)*rp - (int)*pp) & 0xff); + } + + best_row = png_ptr->up_row; + } + + else if (filter_to_do & PNG_FILTER_UP) + { + png_bytep rp, dp, pp; + png_uint_32 sum = 0, lmins = mins; + png_size_t i; + int v; + + +#ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED + if (png_ptr->heuristic_method == PNG_FILTER_HEURISTIC_WEIGHTED) + { + int j; + png_uint_32 lmhi, lmlo; + lmlo = lmins & PNG_LOMASK; + lmhi = (lmins >> PNG_HISHIFT) & PNG_HIMASK; + + for (j = 0; j < num_p_filters; j++) + { + if (png_ptr->prev_filters[j] == PNG_FILTER_VALUE_UP) + { + lmlo = (lmlo * png_ptr->inv_filter_weights[j]) >> + PNG_WEIGHT_SHIFT; + + lmhi = (lmhi * png_ptr->inv_filter_weights[j]) >> + PNG_WEIGHT_SHIFT; + } + } + + lmlo = (lmlo * png_ptr->inv_filter_costs[PNG_FILTER_VALUE_UP]) >> + PNG_COST_SHIFT; + + lmhi = (lmhi * png_ptr->inv_filter_costs[PNG_FILTER_VALUE_UP]) >> + PNG_COST_SHIFT; + + if (lmhi > PNG_HIMASK) + lmins = PNG_MAXSUM; + + else + lmins = (lmhi << PNG_HISHIFT) + lmlo; + } +#endif + + for (i = 0, rp = row_buf + 1, dp = png_ptr->up_row + 1, + pp = prev_row + 1; i < row_bytes; i++) + { + v = *dp++ = (png_byte)(((int)*rp++ - (int)*pp++) & 0xff); + + sum += (v < 128) ? v : 256 - v; + + if (sum > lmins) /* We are already worse, don't continue. */ + break; + } + +#ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED + if (png_ptr->heuristic_method == PNG_FILTER_HEURISTIC_WEIGHTED) + { + int j; + png_uint_32 sumhi, sumlo; + sumlo = sum & PNG_LOMASK; + sumhi = (sum >> PNG_HISHIFT) & PNG_HIMASK; + + for (j = 0; j < num_p_filters; j++) + { + if (png_ptr->prev_filters[j] == PNG_FILTER_VALUE_UP) + { + sumlo = (sumlo * png_ptr->filter_weights[j]) >> + PNG_WEIGHT_SHIFT; + + sumhi = (sumhi * png_ptr->filter_weights[j]) >> + PNG_WEIGHT_SHIFT; + } + } + + sumlo = (sumlo * png_ptr->filter_costs[PNG_FILTER_VALUE_UP]) >> + PNG_COST_SHIFT; + + sumhi = (sumhi * png_ptr->filter_costs[PNG_FILTER_VALUE_UP]) >> + PNG_COST_SHIFT; + + if (sumhi > PNG_HIMASK) + sum = PNG_MAXSUM; + + else + sum = (sumhi << PNG_HISHIFT) + sumlo; + } +#endif + + if (sum < mins) + { + mins = sum; + best_row = png_ptr->up_row; + } + } + + /* Avg filter */ + if (filter_to_do == PNG_FILTER_AVG) + { + png_bytep rp, dp, pp, lp; + png_uint_32 i; + + for (i = 0, rp = row_buf + 1, dp = png_ptr->avg_row + 1, + pp = prev_row + 1; i < bpp; i++) + { + *dp++ = (png_byte)(((int)*rp++ - ((int)*pp++ / 2)) & 0xff); + } + + for (lp = row_buf + 1; i < row_bytes; i++) + { + *dp++ = (png_byte)(((int)*rp++ - (((int)*pp++ + (int)*lp++) / 2)) + & 0xff); + } + best_row = png_ptr->avg_row; + } + + else if (filter_to_do & PNG_FILTER_AVG) + { + png_bytep rp, dp, pp, lp; + png_uint_32 sum = 0, lmins = mins; + png_size_t i; + int v; + +#ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED + if (png_ptr->heuristic_method == PNG_FILTER_HEURISTIC_WEIGHTED) + { + int j; + png_uint_32 lmhi, lmlo; + lmlo = lmins & PNG_LOMASK; + lmhi = (lmins >> PNG_HISHIFT) & PNG_HIMASK; + + for (j = 0; j < num_p_filters; j++) + { + if (png_ptr->prev_filters[j] == PNG_FILTER_VALUE_AVG) + { + lmlo = (lmlo * png_ptr->inv_filter_weights[j]) >> + PNG_WEIGHT_SHIFT; + + lmhi = (lmhi * png_ptr->inv_filter_weights[j]) >> + PNG_WEIGHT_SHIFT; + } + } + + lmlo = (lmlo * png_ptr->inv_filter_costs[PNG_FILTER_VALUE_AVG]) >> + PNG_COST_SHIFT; + + lmhi = (lmhi * png_ptr->inv_filter_costs[PNG_FILTER_VALUE_AVG]) >> + PNG_COST_SHIFT; + + if (lmhi > PNG_HIMASK) + lmins = PNG_MAXSUM; + + else + lmins = (lmhi << PNG_HISHIFT) + lmlo; + } +#endif + + for (i = 0, rp = row_buf + 1, dp = png_ptr->avg_row + 1, + pp = prev_row + 1; i < bpp; i++) + { + v = *dp++ = (png_byte)(((int)*rp++ - ((int)*pp++ / 2)) & 0xff); + + sum += (v < 128) ? v : 256 - v; + } + + for (lp = row_buf + 1; i < row_bytes; i++) + { + v = *dp++ = + (png_byte)(((int)*rp++ - (((int)*pp++ + (int)*lp++) / 2)) & 0xff); + + sum += (v < 128) ? v : 256 - v; + + if (sum > lmins) /* We are already worse, don't continue. */ + break; + } + +#ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED + if (png_ptr->heuristic_method == PNG_FILTER_HEURISTIC_WEIGHTED) + { + int j; + png_uint_32 sumhi, sumlo; + sumlo = sum & PNG_LOMASK; + sumhi = (sum >> PNG_HISHIFT) & PNG_HIMASK; + + for (j = 0; j < num_p_filters; j++) + { + if (png_ptr->prev_filters[j] == PNG_FILTER_VALUE_NONE) + { + sumlo = (sumlo * png_ptr->filter_weights[j]) >> + PNG_WEIGHT_SHIFT; + + sumhi = (sumhi * png_ptr->filter_weights[j]) >> + PNG_WEIGHT_SHIFT; + } + } + + sumlo = (sumlo * png_ptr->filter_costs[PNG_FILTER_VALUE_AVG]) >> + PNG_COST_SHIFT; + + sumhi = (sumhi * png_ptr->filter_costs[PNG_FILTER_VALUE_AVG]) >> + PNG_COST_SHIFT; + + if (sumhi > PNG_HIMASK) + sum = PNG_MAXSUM; + + else + sum = (sumhi << PNG_HISHIFT) + sumlo; + } +#endif + + if (sum < mins) + { + mins = sum; + best_row = png_ptr->avg_row; + } + } + + /* Paeth filter */ + if (filter_to_do == PNG_FILTER_PAETH) + { + png_bytep rp, dp, pp, cp, lp; + png_size_t i; + + for (i = 0, rp = row_buf + 1, dp = png_ptr->paeth_row + 1, + pp = prev_row + 1; i < bpp; i++) + { + *dp++ = (png_byte)(((int)*rp++ - (int)*pp++) & 0xff); + } + + for (lp = row_buf + 1, cp = prev_row + 1; i < row_bytes; i++) + { + int a, b, c, pa, pb, pc, p; + + b = *pp++; + c = *cp++; + a = *lp++; + + p = b - c; + pc = a - c; + +#ifdef PNG_USE_ABS + pa = abs(p); + pb = abs(pc); + pc = abs(p + pc); +#else + pa = p < 0 ? -p : p; + pb = pc < 0 ? -pc : pc; + pc = (p + pc) < 0 ? -(p + pc) : p + pc; +#endif + + p = (pa <= pb && pa <=pc) ? a : (pb <= pc) ? b : c; + + *dp++ = (png_byte)(((int)*rp++ - p) & 0xff); + } + best_row = png_ptr->paeth_row; + } + + else if (filter_to_do & PNG_FILTER_PAETH) + { + png_bytep rp, dp, pp, cp, lp; + png_uint_32 sum = 0, lmins = mins; + png_size_t i; + int v; + +#ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED + if (png_ptr->heuristic_method == PNG_FILTER_HEURISTIC_WEIGHTED) + { + int j; + png_uint_32 lmhi, lmlo; + lmlo = lmins & PNG_LOMASK; + lmhi = (lmins >> PNG_HISHIFT) & PNG_HIMASK; + + for (j = 0; j < num_p_filters; j++) + { + if (png_ptr->prev_filters[j] == PNG_FILTER_VALUE_PAETH) + { + lmlo = (lmlo * png_ptr->inv_filter_weights[j]) >> + PNG_WEIGHT_SHIFT; + + lmhi = (lmhi * png_ptr->inv_filter_weights[j]) >> + PNG_WEIGHT_SHIFT; + } + } + + lmlo = (lmlo * png_ptr->inv_filter_costs[PNG_FILTER_VALUE_PAETH]) >> + PNG_COST_SHIFT; + + lmhi = (lmhi * png_ptr->inv_filter_costs[PNG_FILTER_VALUE_PAETH]) >> + PNG_COST_SHIFT; + + if (lmhi > PNG_HIMASK) + lmins = PNG_MAXSUM; + + else + lmins = (lmhi << PNG_HISHIFT) + lmlo; + } +#endif + + for (i = 0, rp = row_buf + 1, dp = png_ptr->paeth_row + 1, + pp = prev_row + 1; i < bpp; i++) + { + v = *dp++ = (png_byte)(((int)*rp++ - (int)*pp++) & 0xff); + + sum += (v < 128) ? v : 256 - v; + } + + for (lp = row_buf + 1, cp = prev_row + 1; i < row_bytes; i++) + { + int a, b, c, pa, pb, pc, p; + + b = *pp++; + c = *cp++; + a = *lp++; + +#ifndef PNG_SLOW_PAETH + p = b - c; + pc = a - c; +#ifdef PNG_USE_ABS + pa = abs(p); + pb = abs(pc); + pc = abs(p + pc); +#else + pa = p < 0 ? -p : p; + pb = pc < 0 ? -pc : pc; + pc = (p + pc) < 0 ? -(p + pc) : p + pc; +#endif + p = (pa <= pb && pa <=pc) ? a : (pb <= pc) ? b : c; +#else /* PNG_SLOW_PAETH */ + p = a + b - c; + pa = abs(p - a); + pb = abs(p - b); + pc = abs(p - c); + + if (pa <= pb && pa <= pc) + p = a; + + else if (pb <= pc) + p = b; + + else + p = c; +#endif /* PNG_SLOW_PAETH */ + + v = *dp++ = (png_byte)(((int)*rp++ - p) & 0xff); + + sum += (v < 128) ? v : 256 - v; + + if (sum > lmins) /* We are already worse, don't continue. */ + break; + } + +#ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED + if (png_ptr->heuristic_method == PNG_FILTER_HEURISTIC_WEIGHTED) + { + int j; + png_uint_32 sumhi, sumlo; + sumlo = sum & PNG_LOMASK; + sumhi = (sum >> PNG_HISHIFT) & PNG_HIMASK; + + for (j = 0; j < num_p_filters; j++) + { + if (png_ptr->prev_filters[j] == PNG_FILTER_VALUE_PAETH) + { + sumlo = (sumlo * png_ptr->filter_weights[j]) >> + PNG_WEIGHT_SHIFT; + + sumhi = (sumhi * png_ptr->filter_weights[j]) >> + PNG_WEIGHT_SHIFT; + } + } + + sumlo = (sumlo * png_ptr->filter_costs[PNG_FILTER_VALUE_PAETH]) >> + PNG_COST_SHIFT; + + sumhi = (sumhi * png_ptr->filter_costs[PNG_FILTER_VALUE_PAETH]) >> + PNG_COST_SHIFT; + + if (sumhi > PNG_HIMASK) + sum = PNG_MAXSUM; + + else + sum = (sumhi << PNG_HISHIFT) + sumlo; + } +#endif + + if (sum < mins) + { + best_row = png_ptr->paeth_row; + } + } +#endif /* PNG_WRITE_FILTER_SUPPORTED */ + + /* Do the actual writing of the filtered row data from the chosen filter. */ + png_write_filtered_row(png_ptr, best_row, row_info->rowbytes+1); + +#ifdef PNG_WRITE_FILTER_SUPPORTED +#ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED + /* Save the type of filter we picked this time for future calculations */ + if (png_ptr->num_prev_filters > 0) + { + int j; + + for (j = 1; j < num_p_filters; j++) + { + png_ptr->prev_filters[j] = png_ptr->prev_filters[j - 1]; + } + + png_ptr->prev_filters[j] = best_row[0]; + } +#endif +#endif /* PNG_WRITE_FILTER_SUPPORTED */ +} + + +/* Do the actual writing of a previously filtered row. */ +static void +png_write_filtered_row(png_structrp png_ptr, png_bytep filtered_row, + png_size_t full_row_length/*includes filter byte*/) +{ + png_debug(1, "in png_write_filtered_row"); + + png_debug1(2, "filter = %d", filtered_row[0]); + + png_compress_IDAT(png_ptr, filtered_row, full_row_length, Z_NO_FLUSH); + + /* Swap the current and previous rows */ + if (png_ptr->prev_row != NULL) + { + png_bytep tptr; + + tptr = png_ptr->prev_row; + png_ptr->prev_row = png_ptr->row_buf; + png_ptr->row_buf = tptr; + } + + /* Finish row - updates counters and flushes zlib if last row */ + png_write_finish_row(png_ptr); + +#ifdef PNG_WRITE_FLUSH_SUPPORTED + png_ptr->flush_rows++; + + if (png_ptr->flush_dist > 0 && + png_ptr->flush_rows >= png_ptr->flush_dist) + { + png_write_flush(png_ptr); + } +#endif +} +#endif /* PNG_WRITE_SUPPORTED */ diff --git a/Minecraft.Client/Windows64/4JLibs/inc/Render/microprofile/microprofile.config.h b/Minecraft.Client/Windows64/4JLibs/inc/Render/microprofile/microprofile.config.h new file mode 100644 index 000000000..7c66cfda3 --- /dev/null +++ b/Minecraft.Client/Windows64/4JLibs/inc/Render/microprofile/microprofile.config.h @@ -0,0 +1,4 @@ + +#define MICROPROFILE_ENABLED 1 +#define MICROPROFILE_GPU_TIMERS_D3D11 1 +#define MICROPROFILE_GPU_TIMERS_D3D12 1 diff --git a/Minecraft.Client/Windows64/4JLibs/inc/Render/microprofile/microprofile.cpp b/Minecraft.Client/Windows64/4JLibs/inc/Render/microprofile/microprofile.cpp new file mode 100644 index 000000000..c4af76751 --- /dev/null +++ b/Minecraft.Client/Windows64/4JLibs/inc/Render/microprofile/microprofile.cpp @@ -0,0 +1,16191 @@ +#include "../Profiler.h" + +#ifdef ENABLE_PROFILING + +#define MICROPROFILE_IMPL +#include "microprofile.h" +#if MICROPROFILE_ENABLED + +#define BREAK_SKIP() __builtin_trap() + +#ifdef _WIN32 +#if !defined(WIN32_LEAN_AND_MEAN) +#define WIN32_LEAN_AND_MEAN +#endif +#include +#endif + +#ifdef _WIN32 +#define MICROPROFILE_MAX_PATH MAX_PATH +#else +#define MICROPROFILE_MAX_PATH 1024 +#endif + +#include +#include +#include +#include +#include +#include +#include + +#if defined(MICROPROFILE_SYSTEM_STB) +#include +#else +#define STB_SPRINTF_IMPLEMENTATION +#include "stb/stb_sprintf.h" +#endif + +#if defined(_WIN32) && _MSC_VER == 1700 +#define PRIx64 "llx" +#define PRIu64 "llu" +#define PRId64 "lld" +#else +#include +#endif + +#define MICROPROFILE_MAX_COUNTERS 512 +#define MICROPROFILE_MAX_COUNTER_NAME_CHARS (MICROPROFILE_MAX_COUNTERS * 16) +#define MICROPROFILE_MAX_GROUP_INTS (MICROPROFILE_MAX_GROUPS / 32) +#define MICROPROFILE_MAX_CATEGORIES 16 +#define MICROPROFILE_MAX_GRAPHS 5 +#define MICROPROFILE_GRAPH_HISTORY 128 +#define MICROPROFILE_BUFFER_SIZE ((MICROPROFILE_PER_THREAD_BUFFER_SIZE) / sizeof(MicroProfileLogEntry)) +#define MICROPROFILE_GPU_BUFFER_SIZE ((MICROPROFILE_PER_THREAD_GPU_BUFFER_SIZE) / sizeof(MicroProfileLogEntry)) +#define MICROPROFILE_MAX_CONTEXT_SWITCH_THREADS 256 +#define MICROPROFILE_WEBSOCKET_BUFFER_SIZE (64 << 10) +#define MICROPROFILE_INVALID_TICK ((uint64_t) - 1) +#define MICROPROFILE_DROPPED_TICK ((uint64_t) - 2) +#define MICROPROFILE_INVALID_FRAME ((uint32_t) - 1) +#define MICROPROFILE_GROUP_MASK_ALL 0xffffffff +#define MICROPROFILE_MAX_PATCH_ERRORS 32 +#define MICROPROFILE_MAX_MODULE_EXEC_REGIONS 16 + +#define MP_LOG_TICK_MASK 0x0000ffffffffffff +#define MP_LOG_INDEX_MASK 0x3fff000000000000 +#define MP_LOG_BEGIN_MASK 0xc000000000000000 +#define MP_LOG_CSTR_MASK 0xe000000000000000 +#define MP_LOG_CSTR_BIT 0x2000000000000000 +#define MP_LOG_PAYLOAD_PTR_MASK (~(MP_LOG_BEGIN_MASK | MP_LOG_CSTR_BIT)) + +#define MP_LOG_ENTER_LEAVE_MASK 0x8000000000000000 + +#define MP_LOG_LEAVE 0x0 +#define MP_LOG_ENTER 0x1 +#define MP_LOG_EXTENDED 0x2 +#define MP_LOG_EXTENDED_NO_DATA 0x3 + +#ifndef MICROPROFILE_SETTINGS_FILE +#define MICROPROFILE_SETTINGS_FILE "mppresets.cfg" +#endif +#ifndef MICROPROFILE_SETTINGS_FILE_BUILTIN +#define MICROPROFILE_SETTINGS_FILE_BUILTIN "mppresets.builtin.cfg" +#endif +#ifndef MICROPROFILE_SETTINGS_FILE_TEMP +#define MICROPROFILE_SETTINGS_FILE_TEMP ".tmp" +#endif + +// #define MP_LOG_EXTRA_DATA 0x3 + +static_assert(0 == (MICROPROFILE_MAX_GROUPS % 32), "MICROPROFILE_MAX_GROUPS must be divisible by 32"); + +enum EMicroProfileTokenExtended +{ + ETOKEN_GPU_CPU_TIMESTAMP = 0x3fff, + ETOKEN_GPU_CPU_SOURCE_THREAD = 0x3ffe, + ETOKEN_META_MARKER = 0x3ffd, + ETOKEN_CUSTOM_NAME = 0x3ffc, + ETOKEN_CUSTOM_COLOR = 0x3ffb, + ETOKEN_CUSTOM_ID = 0x3ffa, + ETOKEN_CSTR_PTR = 0x2000, // note, matches MP_LOG_CSTR_BIT + ETOKEN_MAX = 0x2000, +}; + +enum +{ + MICROPROFILE_WEBSOCKET_DIRTY_MENU, + MICROPROFILE_WEBSOCKET_DIRTY_ENABLED, +}; + +#ifndef MICROPROFILE_ALLOC // redefine all if overriding +#define MICROPROFILE_ALLOC(nSize, nAlign) MicroProfileAllocAligned(nSize, nAlign); +#define MICROPROFILE_REALLOC(p, s) realloc(p, s) +#define MICROPROFILE_FREE(p) MicroProfileFreeAligned(p) +#define MICROPROFILE_FREE_NON_ALIGNED(p) free(p) +#endif + +#define MP_ALLOC(nSize, nAlign) MicroProfileAllocInternal(nSize, nAlign) +#define MP_REALLOC(p, s) MicroProfileReallocInternal(p, s) +#define MP_FREE(p) MicroProfileFreeInternal(p) +#define MP_ALLOC_OBJECT(T) (T*)MP_ALLOC(sizeof(T), alignof(T)) +#define MP_ALLOC_OBJECT_ARRAY(T, Count) (T*)MP_ALLOC(sizeof(T) * Count, alignof(T)) + +#ifndef MICROPROFILE_DEBUG +#define MICROPROFILE_DEBUG 0 +#endif + +typedef uint64_t MicroProfileLogEntry; + +void MicroProfileSleep(uint32_t nMs); +template +T MicroProfileMin(T a, T b); +template +T MicroProfileMax(T a, T b); +template +T MicroProfileClamp(T a, T min_, T max_); +int64_t MicroProfileMsToTick(float fMs, int64_t nTicksPerSecond); +float MicroProfileTickToMsMultiplier(int64_t nTicksPerSecond); +uint32_t MicroProfileLogGetType(MicroProfileLogEntry Index); +uint64_t MicroProfileLogGetTimerIndex(MicroProfileLogEntry Index); +MicroProfileLogEntry MicroProfileMakeLogIndex(uint64_t nBegin, MicroProfileToken nToken, int64_t nTick); +int64_t MicroProfileLogTickDifference(MicroProfileLogEntry Start, MicroProfileLogEntry End); +int64_t MicroProfileLogSetTick(MicroProfileLogEntry e, int64_t nTick); +uint16_t MicroProfileGetTimerIndex(MicroProfileToken t); +uint32_t MicroProfileGetGroupMask(MicroProfileToken t); +MicroProfileToken MicroProfileMakeToken(uint64_t nGroupMask, uint32_t nGroupIndex, uint16_t nTimer); +bool MicroProfileAnyGroupActive(); +void MicroProfileWriteFile(void* Handle, size_t nSize, const char* pData); + +// defer implementation +#define CONCAT_INTERNAL(x, y) x##y +#define CONCAT(x, y) CONCAT_INTERNAL(x, y) +void IntentionallyNotDefinedFunction__(); // DO NOT DEFINE THIS +template +struct MicroProfileExitScope +{ + T lambda; + MicroProfileExitScope(T lambda) + : lambda(lambda) + { + } + ~MicroProfileExitScope() + { + lambda(); + } + + MicroProfileExitScope(const MicroProfileExitScope& rhs) + : lambda(rhs.lambda) + { + IntentionallyNotDefinedFunction__(); // this is here to ensure the compiler does not create duplicate copies + } + + private: + MicroProfileExitScope& operator=(const MicroProfileExitScope&); +}; + +class MicroProfileExitScopeHelp +{ + public: + template + MicroProfileExitScope operator+(T t) + { + return t; + } +}; +#define defer const auto& CONCAT(defer__, __LINE__) = MicroProfileExitScopeHelp() + [&]() + +////////////////////////////////////////////////////////////////////////// +// platform IMPL +void* MicroProfileAllocInternal(size_t nSize, size_t nAlign); +void MicroProfileFreeInternal(void* pPtr); +void* MicroProfileReallocInternal(void* pPtr, size_t nSize); + +void* MicroProfileAllocAligned(size_t nSize, size_t nAlign); +void MicroProfileFreeAligned(void* pMem); + +#if defined(__APPLE__) +#include +#include +#include +#include +#include +#include + +#if TARGET_OS_IPHONE +#define MICROPROFILE_IOS +#endif + +#define MP_TICK() mach_absolute_time() +inline int64_t MicroProfileTicksPerSecondCpu_() +{ + static int64_t nTicksPerSecond = 0; + if(nTicksPerSecond == 0) + { + mach_timebase_info_data_t sTimebaseInfo; + mach_timebase_info(&sTimebaseInfo); + nTicksPerSecond = 1000000000ll * sTimebaseInfo.denom / sTimebaseInfo.numer; + } + return nTicksPerSecond; +} + +int64_t MicroProfileTicksPerSecondCpu() +{ + return MicroProfileTicksPerSecondCpu_(); +} +#define MicroProfileTicksPerSecondCpu MicroProfileTicksPerSecondCpu_ + +inline uint64_t MicroProfileGetCurrentThreadId() +{ + uint64_t tid; + pthread_threadid_np(pthread_self(), &tid); + return tid; +} + +#include + +#define MP_BREAK() __builtin_trap() +#define MP_THREAD_LOCAL __thread +#define MP_STRCASECMP strcasecmp +#define MP_GETCURRENTTHREADID() MicroProfileGetCurrentThreadId() +#define MP_STRCASESTR strcasestr +#define MP_THREAD_LOCAL __thread +#define MP_NOINLINE __attribute__((noinline)) + +void* MicroProfileAllocAligned(size_t nSize, size_t nAlign) +{ + void* p; + int result = posix_memalign(&p, nAlign, nSize); + if(result != 0) + { + return nullptr; + } + return p; +} + +void MicroProfileFreeAligned(void* pMem) +{ + free(pMem); +} + +#elif defined(_WIN32) +#include +#include +#include +int64_t MicroProfileGetTick(); +#define MP_TICK() MicroProfileGetTick() +#define MP_BREAK() __debugbreak() +#define MP_THREAD_LOCAL __declspec(thread) +#define MP_STRCASECMP _stricmp +#define MP_GETCURRENTTHREADID() GetCurrentThreadId() +#define MP_STRCASESTR StrStrI +#define MP_THREAD_LOCAL __declspec(thread) +#define MP_NOINLINE __declspec(noinline) + +#ifndef MICROPROFILE_WIN32_TRAP_ALLOCATOR +#define MICROPROFILE_WIN32_TRAP_ALLOCATOR 0 +#endif + +#if MICROPROFILE_WIN32_TRAP_ALLOCATOR +// minimal trap allocator +#define PAGE_SIZE (4096) +void* MicroProfileAllocAligned(size_t nSize, size_t nAlign) +{ + (void)nAlign; + size_t nAlignedSize = (nSize + PAGE_SIZE - 1) & (~(PAGE_SIZE - 1)); + size_t nDelta = nAlignedSize - nSize; + size_t nFullSize = nAlignedSize + 2 * PAGE_SIZE; + + void* ptr = VirtualAlloc(0, nFullSize, MEM_RESERVE, PAGE_READWRITE); + intptr_t intptr = (intptr_t)ptr; + + void* pResult = VirtualAlloc((void*)(intptr + PAGE_SIZE), nAlignedSize, MEM_COMMIT, PAGE_READWRITE); + memset(pResult, 0xf0, nAlignedSize); + + intptr_t page = (intptr_t)pResult; + //((char*)page)[-1] = 0x70; //trap test + page += nDelta; + pResult = (void*)page; + memset(pResult, 0xfe, nSize); + //((char*)page)[nSize] = 0x70; //trap test + return (void*)page; +} + +void MicroProfileFreeAligned(void* pMem) +{ + intptr_t intptr = (intptr_t)pMem; + intptr = (intptr & (~(PAGE_SIZE - 1))) - PAGE_SIZE; + VirtualFree(pMem, 0, MEM_RELEASE); +} +#else +void* MicroProfileAllocAligned(size_t nSize, size_t nAlign) +{ + return _aligned_malloc(nSize, nAlign); +} + +void MicroProfileFreeAligned(void* pMem) +{ + _aligned_free(pMem); +} +#endif + +#else + +#ifndef MICROPROFILE_CUSTOM_PLATFORM +#include +#include +#include +#include +#include + +inline int64_t MicroProfileTicksPerSecondCpu_() +{ + return 1000000000ll; +} + +int64_t MicroProfileTicksPerSecondCpu() +{ + return MicroProfileTicksPerSecondCpu_(); +} +#define MicroProfileTicksPerSecondCpu MicroProfileTicksPerSecondCpu_ + +inline int64_t MicroProfileGetTick() +{ + timespec ts; + clock_gettime(CLOCK_REALTIME, &ts); + return 1000000000ll * ts.tv_sec + ts.tv_nsec; +} +#define MP_TICK() MicroProfileGetTick() +#define MP_BREAK() __builtin_trap() +#define MP_THREAD_LOCAL __thread +#define MP_STRCASECMP strcasecmp +#define MP_GETCURRENTTHREADID() (uint64_t) pthread_self() +#define MP_STRCASESTR strcasestr +#define MP_THREAD_LOCAL __thread +#define MP_NOINLINE __attribute__((noinline)) + +void* MicroProfileAllocAligned(size_t nSize, size_t nAlign) +{ +#if defined(__linux__) + void* p; + int result = posix_memalign(&p, nAlign, nSize); + if(result != 0) + { + return nullptr; + } + return p; +#else + return memalign(nAlign, nSize); +#endif +} +void MicroProfileFreeAligned(void* pMem) +{ + free(pMem); +} +#endif + +#endif + +#ifdef MICROPROFILE_PS4 +#define MICROPROFILE_PS4_DECL +#include "microprofile_ps4.h" +#endif + +#ifdef MICROPROFILE_XBOXONE +#define MICROPROFILE_XBOXONE_DECL +#include "microprofile_xboxone.h" +#else +#ifdef _WIN32 +#include +#endif +#endif + +#ifdef _WIN32 +typedef uint32_t MicroProfileThreadIdType; +#else +#ifdef MICROPROFILE_THREADID_SIZE_4BYTE +typedef uint32_t MicroProfileThreadIdType; +#elif MICROPROFILE_THREADID_SIZE_8BYTE +typedef uint64_t MicroProfileThreadIdType; +#else +typedef uint64_t MicroProfileThreadIdType; +#endif +#endif + +#define MP_ASSERT(a) \ + do \ + { \ + if(!(a)) \ + { \ + MP_BREAK(); \ + } \ + } while(0) + +#ifdef _WIN32 +#include +typedef UINT_PTR MpSocket; +#else +typedef int MpSocket; +#endif + +#ifndef _WIN32 +typedef pthread_t MicroProfileThread; +#elif defined(_WIN32) +#if _MSC_VER == 1900 +typedef void* HANDLE; +#endif + +typedef HANDLE MicroProfileThread; +#else +typedef std::thread* MicroProfileThread; +#endif + +#if MICROPROFILE_DYNAMIC_INSTRUMENT +struct MicroProfileSymbolDesc; + +#define MICROPROFILE_SUSPEND_MAX (4 << 10) +struct MicroProfileSuspendState +{ + uint32_t SuspendCounter = 0; + uint32_t NumSuspended = 0; +#ifdef _WIN32 + HANDLE Suspended[MICROPROFILE_SUSPEND_MAX]; + intptr_t SuspendedIP[MICROPROFILE_SUSPEND_MAX]; +#endif +}; + +void MicroProfileSymbolQueryFunctions(MpSocket Connection, const char* pFilter); +bool MicroProfileInstrumentFunction(void* pFunction, const char* pModuleName, const char* pFunctionName, uint32_t nColor); +bool MicroProfileSymbolInitialize(bool bStartLoad, const char* pModuleName = 0); +MicroProfileSymbolDesc* MicroProfileSymbolFindFuction(void* pAddress); +void MicroProfileInstrumentFunctionsCalled(void* pFunction, const char* pModuleName, const char* pFunctionName, int nMinBytes, int nMaxCalls); +void MicroProfileSymbolQuerySendResult(MpSocket Connection); +void MicroProfileSymbolSendFunctionNames(MpSocket Connection); +void MicroProfileSymbolSendErrors(MpSocket Connection); +const char* MicroProfileSymbolModuleGetString(uint32_t nIndex); +void MicroProfileInstrumentWithoutSymbols(const char** pModules, const char** pSymbols, uint32_t nNumSymbols); +void MicroProfileSymbolUpdateModuleList(); +bool MicroProfileSymInit(); +void MicroProfileSymCleanup(); +#endif + +struct MicroProfileFunctionQuery; + +// hash table functions & declarations +struct MicroProfileHashTable; +struct MicroProfileHashTableIterator; +typedef bool (*MicroProfileHashCompareFunction)(uint64_t l, uint64_t r); +typedef uint64_t (*MicroProfileHashFunction)(uint64_t p); +uint64_t MicroProfileHashTableHashString(uint64_t pString); +bool MicroProfileHashTableCompareString(uint64_t L, uint64_t R); +uint64_t MicroProfileHashTableHashPtr(uint64_t pString); +bool MicroProfileHashTableComparePtr(uint64_t L, uint64_t R); +void MicroProfileHashTableInit(MicroProfileHashTable* pTable, uint32_t nInitialSize, uint32_t nSearchLimit, MicroProfileHashCompareFunction CompareFunc, MicroProfileHashFunction HashFunc); +void MicroProfileHashTableDestroy(MicroProfileHashTable* pTable); +uint64_t MicroProfileHashTableHash(MicroProfileHashTable* pTable, uint64_t K); +bool MicroProfileHashTableSet(MicroProfileHashTable* pTable, uint64_t Key, uintptr_t Value); +MicroProfileHashTableIterator MicroProfileGetHashTableIteratorBegin(MicroProfileHashTable* HashTable); +MicroProfileHashTableIterator MicroProfileGetHashTableIteratorEnd(MicroProfileHashTable* HashTable); + +template +struct MicroProfileArray +{ + T* Data = nullptr; + uint32_t Size = 0; + uint32_t Capacity = 0; + T& operator[](const uint32_t Index); + const T& operator[](const uint32_t Index) const; + T* begin(); + T* end(); +}; + +template +void MicroProfileArrayInit(MicroProfileArray& Array, uint32_t InitialCapacity); +template +void MicroProfileArrayDestroy(MicroProfileArray& Array, uint32_t InitialCapacity); +template +void MicroProfileArrayClear(MicroProfileArray& Array); +template +void MicroProfileArrayPushBack(MicroProfileArray& Array, const T& v); + +struct MicroProfileTimer +{ + uint64_t nTicks; + uint32_t nCount; +}; + +struct MicroProfileCategory +{ + char pName[MICROPROFILE_NAME_MAX_LEN]; + uint32_t nGroupMask[MICROPROFILE_MAX_GROUP_INTS]; +}; + +struct MicroProfileGroupInfo +{ + char pName[MICROPROFILE_NAME_MAX_LEN]; + uint32_t nNameLen; + uint32_t nGroupIndex; + uint32_t nNumTimers; + uint32_t nMaxTimerNameLen; + uint32_t nColor; + uint32_t nCategory; + MicroProfileTokenType Type; + int nWSNext; +}; + +struct MicroProfileTimerInfo +{ + MicroProfileToken nToken; + uint32_t nTimerIndex; + uint32_t nGroupIndex; + char pName[MICROPROFILE_NAME_MAX_LEN]; + char pNameExt[MICROPROFILE_NAME_MAX_LEN]; + uint32_t nNameLen; + uint32_t nColor; + int nWSNext; + bool bGraph; + MicroProfileTokenType Type; + uint32_t Flags; +}; + +struct MicroProfileCounterInfo +{ + int nParent; + int nSibling; + int nFirstChild; + uint16_t nNameLen; + uint8_t nLevel; + const char* pName; + uint32_t nFlags; + int64_t nLimit; + double dLimit; + int nWSNext; + MicroProfileCounterFormat eFormat; + std::atomic ExternalAtomic; +}; + +struct MicroProfileCounterHistory +{ + uint32_t nPut; + uint64_t nHistory[MICROPROFILE_GRAPH_HISTORY]; +}; + +struct MicroProfileCounterSource +{ + void* pSource; + uint32_t nSourceSize; +}; + +struct MicroProfileGraphState +{ + int64_t nHistory[MICROPROFILE_GRAPH_HISTORY]; + MicroProfileToken nToken; + int32_t nKey; +}; + +struct MicroProfileContextSwitch +{ + MicroProfileThreadIdType nThreadOut; + MicroProfileThreadIdType nThreadIn; + int64_t nCpu : 8; + int64_t nTicks : 56; +}; + +struct MicroProfileFrameState +{ + uint64_t nFrameStartCpu; + uint64_t nFrameStartGpu; + uint64_t nFrameId; + uint32_t nGpuPending; + uint32_t nLogStart[MICROPROFILE_MAX_THREADS]; + uint32_t nLogStartTimeline; + uint32_t nTimelineFrameMax; + int32_t nHistoryTimeline; +}; + +// All frame counter data stored. Used to store the time for all counters/groups for every frame. +// Must be enabled with MicroProfileEnableFrameCounterExtraData() +// Will allocate sizeof(MicroProfileFrameExtraCounterData) * MICROPROFILE_MAX_FRAME_HISTORY bytes +struct MicroProfileFrameExtraCounterData +{ + uint16_t NumTimers; + uint16_t NumGroups; + uint64_t Timers[MICROPROFILE_MAX_TIMERS]; + uint64_t Groups[MICROPROFILE_MAX_GROUPS]; +}; + +struct MicroProfileCsvConfig +{ + enum CsvConfigState + { + INACTIVE = 0, + CONFIG, + ACTIVE, + }; + CsvConfigState State; + uint32_t NumTimers; + uint32_t NumGroups; + uint32_t NumCounters; + uint32_t MaxTimers; + uint32_t MaxGroups; + uint32_t MaxCounters; + uint32_t TotalElements; + uint16_t* TimerIndices; + uint16_t* GroupIndices; + uint16_t* CounterIndices; + uint64_t* FrameData; + const char** pTimerNames; + const char** pGroupNames; + const char** pCounterNames; + uint32_t Flags; +}; + +#ifdef _WIN32 +#pragma warning(push) +#pragma warning(disable : 4200) // zero-sized struct +#pragma warning(disable : 4201) // nameless struct/union +#pragma warning(disable : 4244) // possible loss of data +#pragma warning(disable : 4100) // unreferenced formal parameter +#pragma warning(disable : 4091) +#pragma warning(disable : 4189) // local variable is initialized but not referenced. (for defer local variables) +#pragma warning(disable : 4456) +#pragma warning(disable : 4702) +#endif + +struct MicroProfileStringBlock +{ + enum + { + DEFAULT_SIZE = 8192, + }; + MicroProfileStringBlock* pNext; + uint32_t nUsed; + uint32_t nSize; + char Memory[]; +}; + +struct MicroProfileHashTableEntry +{ + uint64_t Key; + uint64_t Hash; + uintptr_t Value; +}; + +struct MicroProfileHashTable +{ + MicroProfileHashTableEntry* pEntries; + uint32_t nUsed; + uint32_t nAllocated; + uint32_t nSearchLimit; + uint32_t nLim; + MicroProfileHashCompareFunction CompareFunc; + MicroProfileHashFunction HashFunc; +}; + +struct MicroProfileHashTableIterator +{ + MicroProfileHashTableIterator(uint32_t nIndex, MicroProfileHashTable* pTable) + : nIndex(nIndex) + , pTable(pTable) + { + } + MicroProfileHashTableIterator(const MicroProfileHashTableIterator& other) + : nIndex(other.nIndex) + , pTable(other.pTable) + { + } + + uint32_t nIndex; + MicroProfileHashTable* pTable; + + void AssertValid() + { + MP_ASSERT(nIndex < pTable->nAllocated); + } + + MicroProfileHashTableEntry& operator*() + { + AssertValid(); + return pTable->pEntries[nIndex]; + } + MicroProfileHashTableEntry* operator->() + { + AssertValid(); + return &pTable->pEntries[nIndex]; + } + bool operator==(const MicroProfileHashTableIterator& rhs) + { + return nIndex == rhs.nIndex && pTable == rhs.pTable; + } + bool operator!=(const MicroProfileHashTableIterator& rhs) + { + return nIndex != rhs.nIndex || pTable != rhs.pTable; + } + + void SkipInvalid() + { + while(nIndex < pTable->nAllocated && pTable->pEntries[nIndex].Hash == 0) + nIndex++; + } + MicroProfileHashTableIterator operator++() + { + AssertValid(); + nIndex++; + SkipInvalid(); + return *this; + } + MicroProfileHashTableIterator operator++(int) + { + MicroProfileHashTableIterator tmp = *this; + ++(*this); + return tmp; + } +}; + +struct MicroProfileStrings +{ + MicroProfileHashTable HashTable; + MicroProfileStringBlock* pFirst; + MicroProfileStringBlock* pLast; +}; + +struct MicroProfileThreadLog +{ + + std::atomic nPut; + std::atomic nGet; + + MicroProfileLogEntry Log[MICROPROFILE_BUFFER_SIZE]; + + uint32_t nStackPut; + uint32_t nStackScope; +#ifdef MICROPROFILE_VERIFY_BALANCED + uint64_t VerifyStack[MICROPROFILE_STACK_MAX]; +#endif + MicroProfileScopeStateC ScopeState[MICROPROFILE_STACK_MAX]; + + uint32_t nActive; + uint32_t nGpu; + MicroProfileThreadIdType nThreadId; + uint32_t nLogIndex; + uint32_t nCustomId; + uint32_t nIdleFrames; + + MicroProfileLogEntry nStackLogEntry[MICROPROFILE_STACK_MAX]; + uint64_t nChildTickStack[MICROPROFILE_STACK_MAX + 1]; + int32_t nStackPos; + + uint8_t nGroupStackPos[MICROPROFILE_MAX_GROUPS]; + uint64_t nGroupTicks[MICROPROFILE_MAX_GROUPS]; + uint64_t nAggregateGroupTicks[MICROPROFILE_MAX_GROUPS]; + enum + { + THREAD_MAX_LEN = 64, + }; + char ThreadName[64]; + int nFreeListNext; +}; + +struct MicroProfileWebSocketBuffer +{ + char* pBufferAllocation; + char* pBuffer; + uint32_t nBufferSize; + uint32_t nPut; + MpSocket Socket; + + char SendBuffer[MICROPROFILE_WEBSOCKET_BUFFER_SIZE]; + std::atomic nSendPut; + std::atomic nSendGet; +}; + +typedef void (*MicroProfileHookFunc)(int x); + +struct MicroProfilePatchError +{ + unsigned char Code[32]; + char Message[256]; + int AlreadyInstrumented; + int nCodeSize; +}; + +// linear, per-frame per-thread gpu log +struct MicroProfileThreadLogGpu +{ + MicroProfileLogEntry Log[MICROPROFILE_GPU_BUFFER_SIZE]; + uint32_t nPut; + uint32_t nStart; + uint32_t nId; + void* pContext; + uint32_t nAllocated; + + uint32_t nStackScope; + MicroProfileScopeStateC ScopeState[MICROPROFILE_STACK_MAX]; +}; + +#if MICROPROFILE_GPU_TIMERS +static MicroProfileGpuInsertTimeStamp_CB MicroProfileGpuInsertTimeStamp_Callback = 0; +static MicroProfileGpuGetTimeStamp_CB MicroProfileGpuGetTimeStamp_Callback = 0; +static MicroProfileTicksPerSecondGpu_CB MicroProfileTicksPerSecondGpu_Callback = 0; +static MicroProfileGetGpuTickReference_CB MicroProfileGetGpuTickReference_Callback = 0; +static MicroProfileGpuFlip_CB MicroProfileGpuFlip_Callback = 0; +static MicroProfileGpuShutdown_CB MicroProfileGpuShutdown_Callback = 0; + +uint32_t MicroProfileGpuInsertTimeStamp(void* pContext) +{ + return MicroProfileGpuInsertTimeStamp_Callback ? MicroProfileGpuInsertTimeStamp_Callback(pContext) : 0; +} +uint64_t MicroProfileGpuGetTimeStamp(uint32_t nKey) +{ + return MicroProfileGpuGetTimeStamp_Callback ? MicroProfileGpuGetTimeStamp_Callback(nKey) : 1; +} +uint64_t MicroProfileTicksPerSecondGpu() +{ + return MicroProfileTicksPerSecondGpu_Callback ? MicroProfileTicksPerSecondGpu_Callback() : 1; +} +int MicroProfileGetGpuTickReference(int64_t* pOutCPU, int64_t* pOutGpu) +{ + return MicroProfileGetGpuTickReference_Callback ? MicroProfileGetGpuTickReference_Callback(pOutCPU, pOutGpu) : 0; +} +uint32_t MicroProfileGpuFlip(void* p) +{ + return MicroProfileGpuFlip_Callback ? MicroProfileGpuFlip_Callback(p) : 0; +} +void MicroProfileGpuShutdown() +{ + if(MicroProfileGpuShutdown_Callback) + MicroProfileGpuShutdown_Callback(); +} + +#endif + +#if MICROPROFILE_GPU_TIMERS_D3D11 +//:'######:::'########::'##::::'##::::'########:::'#######::'########:::::'##::::::'##::: +//'##... ##:: ##.... ##: ##:::: ##:::: ##.... ##:'##.... ##: ##.... ##::'####::::'####::: +// ##:::..::: ##:::: ##: ##:::: ##:::: ##:::: ##:..::::: ##: ##:::: ##::.. ##::::.. ##::: +// ##::'####: ########:: ##:::: ##:::: ##:::: ##::'#######:: ##:::: ##:::: ##:::::: ##::: +// ##::: ##:: ##.....::: ##:::: ##:::: ##:::: ##::...... ##: ##:::: ##:::: ##:::::: ##::: +// ##::: ##:: ##:::::::: ##:::: ##:::: ##:::: ##:'##:::: ##: ##:::: ##:::: ##:::::: ##::: +//. ######::: ##::::::::. #######::::: ########::. #######:: ########:::'######::'######: +//:......::::..::::::::::.......::::::........::::.......:::........::::......:::......:: + +struct MicroProfileD3D11Frame +{ + uint32_t m_nQueryStart; + uint32_t m_nQueryCountMax; + std::atomic m_nQueryCount; + uint32_t m_nRateQueryStarted; + void* m_pRateQuery; +}; + +struct MicroProfileGpuTimerStateD3D11 : public MicroProfileGpuTimerState +{ + uint32_t bInitialized; + void* m_pDevice; + void* m_pImmediateContext; + void* m_pQueries[MICROPROFILE_D3D11_MAX_QUERIES]; + int64_t m_nQueryResults[MICROPROFILE_D3D11_MAX_QUERIES]; + + uint32_t m_nQueryPut; + uint32_t m_nQueryGet; + uint32_t m_nQueryFrame; + int64_t m_nQueryFrequency; + void* pSyncQuery; + + MicroProfileD3D11Frame m_QueryFrames[MICROPROFILE_GPU_FRAME_DELAY]; +}; + +uint32_t MicroProfileGpuInsertTimeStampD3D11(void* pContext_); +uint64_t MicroProfileGpuGetTimeStampD3D11(uint32_t nIndex); +bool MicroProfileGpuGetDataD3D11(void* pQuery, void* pData, uint32_t nDataSize); +uint64_t MicroProfileTicksPerSecondGpuD3D11(); +uint32_t MicroProfileGpuFlipD3D11(void* pDeviceContext_); +void MicroProfileGpuInitD3D11(void* pDevice_, void* pImmediateContext); +void MicroProfileGpuShutdownD3D11(); +int MicroProfileGetGpuTickReferenceD3D11(int64_t* pOutCPU, int64_t* pOutGpu); +MicroProfileGpuTimerStateD3D11* MicroProfileGetGpuTimerStateD3D11(); +#endif + +#if MICROPROFILE_GPU_TIMERS_D3D12 +//:'######:::'########::'##::::'##::::'########:::'#######::'########:::::'##::::'#######:: +//'##... ##:: ##.... ##: ##:::: ##:::: ##.... ##:'##.... ##: ##.... ##::'####:::'##.... ##: +// ##:::..::: ##:::: ##: ##:::: ##:::: ##:::: ##:..::::: ##: ##:::: ##::.. ##:::..::::: ##: +// ##::'####: ########:: ##:::: ##:::: ##:::: ##::'#######:: ##:::: ##:::: ##::::'#######:: +// ##::: ##:: ##.....::: ##:::: ##:::: ##:::: ##::...... ##: ##:::: ##:::: ##:::'##:::::::: +// ##::: ##:: ##:::::::: ##:::: ##:::: ##:::: ##:'##:::: ##: ##:::: ##:::: ##::: ##:::::::: +//. ######::: ##::::::::. #######::::: ########::. #######:: ########:::'######: #########: +//:......::::..::::::::::.......::::::........::::.......:::........::::......::.........:: + +#include + +#ifndef MICROPROFILE_D3D12_MAX_QUERIES +#define MICROPROFILE_D3D12_MAX_QUERIES (32 << 10) +#endif + +#define MICROPROFILE_D3D_MAX_NODE_COUNT 4 +#define MICROPROFILE_D3D_INTERNAL_DELAY 8 + +#define MP_NODE_MASK_ALL(n) ((1u << (n)) - 1u) +#define MP_NODE_MASK_ONE(n) (1u << (n)) + +struct MicroProfileGpuTimerStateD3D12; + +int MicroProfileGetGpuTickReferenceD3D12(int64_t* pOutCPU, int64_t* pOutGpu); +uint32_t MicroProfileGpuInsertTimeStampD3D12(void* pContext); +uint64_t MicroProfileGpuGetTimeStampD3D12(uint32_t nIndex); +uint64_t MicroProfileTicksPerSecondGpuD3D12(); +uint32_t MicroProfileGpuFlipD3D12(void* pContext); +void MicroProfileGpuInitD3D12(void* pDevice_, uint32_t nNodeCount, void** pCommandQueues_, void** pCommandQueuesCopy_); +void MicroProfileGpuShutdownD3D12(); +void MicroProfileSetCurrentNodeD3D12(uint32_t nNode); +int MicroProfileGetGpuTickReferenceD3D12(int64_t* pOutCPU, int64_t* pOutGpu); +MicroProfileGpuTimerStateD3D12* MicroProfileGetGpuTimerStateD3D12(); + +struct MicroProfileFrameD3D12 +{ + uint32_t nTimeStampBegin; + uint32_t nTimeStampCount; + uint32_t nTimeStampBeginCopyQueue; + uint32_t nTimeStampCountCopyQueue; + uint32_t nNode; + ID3D12GraphicsCommandList* pCommandList[MICROPROFILE_D3D_MAX_NODE_COUNT]; + ID3D12GraphicsCommandList* pCommandListCopy[MICROPROFILE_D3D_MAX_NODE_COUNT]; + ID3D12CommandAllocator* pCommandAllocator; + ID3D12CommandAllocator* pCommandAllocatorCopy; +}; + +struct MicroProfileGpuTimerStateD3D12 : public MicroProfileGpuTimerState +{ + ID3D12Device* pDevice; + uint32_t nNodeCount; + uint32_t nCurrentNode; + + uint64_t nFrame; + uint64_t nPendingFrame; + + uint32_t nFrameStartTimeStamps; + uint32_t nFrameStartCopyQueueTimeStamps; + std::atomic nFrameCountTimeStamps; + std::atomic nFrameCountCopyQueueTimeStamps; + + int64_t nFrequency; + ID3D12Resource* pBuffer; + ID3D12Resource* pBufferCopy; + + struct + { + ID3D12CommandQueue* pCommandQueue; + ID3D12CommandQueue* pCommandQueueCopy; + ID3D12QueryHeap* pHeap; + ID3D12QueryHeap* pCopyQueueHeap; + ID3D12Fence* pFence; + ID3D12Fence* pFenceCopy; + } NodeState[MICROPROFILE_D3D_MAX_NODE_COUNT]; + + uint16_t nQueryFrames[MICROPROFILE_D3D12_MAX_QUERIES]; + int64_t nResults[MICROPROFILE_D3D12_MAX_QUERIES]; + uint16_t nQueryFramesCopy[MICROPROFILE_D3D12_MAX_QUERIES]; + int64_t nResultsCopy[MICROPROFILE_D3D12_MAX_QUERIES]; + + MicroProfileFrameD3D12 Frames[MICROPROFILE_D3D_INTERNAL_DELAY]; +}; +#endif + +#if MICROPROFILE_GPU_TIMERS_GL +//:'######:::'########::'##::::'##:::::'######:::'##::::::: +//'##... ##:: ##.... ##: ##:::: ##::::'##... ##:: ##::::::: +// ##:::..::: ##:::: ##: ##:::: ##:::: ##:::..::: ##::::::: +// ##::'####: ########:: ##:::: ##:::: ##::'####: ##::::::: +// ##::: ##:: ##.....::: ##:::: ##:::: ##::: ##:: ##::::::: +// ##::: ##:: ##:::::::: ##:::: ##:::: ##::: ##:: ##::::::: +//. ######::: ##::::::::. #######:::::. ######::: ########: +//:......::::..::::::::::.......:::::::......::::........:: +struct MicroProfileGpuTimerStateGL : public MicroProfileGpuTimerState +{ + uint32_t GLTimers[MICROPROFILE_GL_MAX_QUERIES]; + uint32_t GLTimerPos; +}; + +MicroProfileGpuTimerStateGL* MicroProfileGetGpuTimerStateGL(); +uint32_t MicroProfileGpuInsertTimeStampGL(void* pContext); +uint64_t MicroProfileGpuGetTimeStampGL(uint32_t nKey); +uint64_t MicroProfileTicksPerSecondGpuGL(); +int MicroProfileGetGpuTickReferenceGL(int64_t* pOutCpu, int64_t* pOutGpu); +uint32_t MicroProfileGpuFlipGL(void* pContext); +void MicroProfileGpuShutdownGL(); +#endif + +#if MICROPROFILE_GPU_TIMERS_VULKAN + +//:'######:::'########::'##::::'##::::'##::::'##:'##::::'##:'##:::::::'##:::'##::::'###::::'##::: ##: +//'##... ##:: ##.... ##: ##:::: ##:::: ##:::: ##: ##:::: ##: ##::::::: ##::'##::::'## ##::: ###:: ##: +// ##:::..::: ##:::: ##: ##:::: ##:::: ##:::: ##: ##:::: ##: ##::::::: ##:'##::::'##:. ##:: ####: ##: +// ##::'####: ########:: ##:::: ##:::: ##:::: ##: ##:::: ##: ##::::::: #####::::'##:::. ##: ## ## ##: +// ##::: ##:: ##.....::: ##:::: ##::::. ##:: ##:: ##:::: ##: ##::::::: ##. ##::: #########: ##. ####: +// ##::: ##:: ##:::::::: ##:::: ##:::::. ## ##::: ##:::: ##: ##::::::: ##:. ##:: ##.... ##: ##:. ###: +//. ######::: ##::::::::. #######:::::::. ###::::. #######:: ########: ##::. ##: ##:::: ##: ##::. ##: +//:......::::..::::::::::.......:::::::::...::::::.......:::........::..::::..::..:::::..::..::::..:: + +struct MicroProfileGpuTimerStateVulkan; +MicroProfileGpuTimerStateVulkan* MicroProfileGetGpuTimerStateVulkan(); +uint32_t MicroProfileGpuInsertTimeStampVulkan(void* pContext); +uint64_t MicroProfileGpuGetTimeStampVulkan(uint32_t nKey); +uint64_t MicroProfileTicksPerSecondGpuVulkan(); +int MicroProfileGetGpuTickReferenceVulkan(int64_t* pOutCpu, int64_t* pOutGpu); +uint32_t MicroProfileGpuFlipVulkan(void* pContext); +void MicroProfileGpuShutdownVulkan(); +#endif + +struct MicroProfileSymbolState +{ + std::atomic nModuleLoadsFinished; + std::atomic nModuleLoadsRequested; + std::atomic nSymbolsLoaded; +}; + +struct MicroProfileSymbolModuleRegion +{ + intptr_t nBegin; + intptr_t nEnd; +}; +struct MicroProfileSymbolModule +{ + uint64_t nModuleBase; + uint32_t nMatchOffset; + uint32_t nStringOffset; + const char* pBaseString; + const char* pTrimmedString; + MicroProfileSymbolModuleRegion Regions[MICROPROFILE_MAX_MODULE_EXEC_REGIONS]; + int nNumExecutableRegions; + + bool bDownloading; + intptr_t nProgress; + intptr_t nProgressTarget; + struct MicroProfileSymbolBlock* pSymbolBlock; + MicroProfileHashTable AddressToSymbol; + + int64_t nSymbols; + std::atomic nSymbolsLoaded; + std::atomic nModuleLoadRequested; + std::atomic nModuleLoadFinished; +}; + +struct MicroProfileInstrumentMemoryRegion +{ + intptr_t Start; + intptr_t Size; + uint32_t Protect; +}; + +struct MicroProfile +{ + uint32_t nTotalTimers; + uint32_t nGroupCount; + uint32_t nCategoryCount; + uint32_t nAggregateClear; + uint32_t nAggregateFlip; + uint32_t nAggregateFlipCount; + uint32_t nAggregateFrames; + + uint64_t nFlipStartTick; + uint64_t nAggregateFlipTick; + + uint32_t nDisplay; + uint32_t nBars; + uint32_t nActiveGroups[MICROPROFILE_MAX_GROUP_INTS]; + bool AnyActive; + uint32_t nFrozen; + uint32_t nWasFrozen; + uint32_t nPlatformMarkersEnabled; + + uint32_t nForceEnable; + + uint32_t nForceGroups[MICROPROFILE_MAX_GROUP_INTS]; + uint32_t nActiveGroupsWanted[MICROPROFILE_MAX_GROUP_INTS]; + uint32_t nGroupMask[MICROPROFILE_MAX_GROUP_INTS]; + + uint32_t nStartEnabled; + uint32_t nAllThreadsWanted; + + uint32_t nOverflow; + + uint32_t nMaxGroupSize; + uint32_t nDumpFileNextFrame; + uint32_t nDumpFileCountDown; + uint32_t nDumpSpikeMask; + uint32_t nAutoClearFrames; + + float fDumpCpuSpike; + float fDumpGpuSpike; + char HtmlDumpPath[512]; + char CsvDumpPath[512]; + uint32_t DumpFrameCount; + + int64_t nPauseTicks; + std::atomic nContextSwitchStalledTick; + int64_t nContextSwitchLastPushed; + int64_t nContextSwitchLastIndexPushed; + + float fReferenceTime; + float fRcpReferenceTime; + + MicroProfileCategory CategoryInfo[MICROPROFILE_MAX_CATEGORIES]; + MicroProfileGroupInfo GroupInfo[MICROPROFILE_MAX_GROUPS]; + MicroProfileTimerInfo TimerInfo[MICROPROFILE_MAX_TIMERS]; + uint32_t TimerToGroup[MICROPROFILE_MAX_TIMERS]; + + MicroProfileTimer AccumTimers[MICROPROFILE_MAX_TIMERS]; + uint64_t AccumMaxTimers[MICROPROFILE_MAX_TIMERS]; + uint64_t AccumMinTimers[MICROPROFILE_MAX_TIMERS]; + uint64_t AccumTimersExclusive[MICROPROFILE_MAX_TIMERS]; + uint64_t AccumMaxTimersExclusive[MICROPROFILE_MAX_TIMERS]; + + MicroProfileTimer Frame[MICROPROFILE_MAX_TIMERS]; + uint64_t FrameExclusive[MICROPROFILE_MAX_TIMERS]; + + MicroProfileTimer Aggregate[MICROPROFILE_MAX_TIMERS]; + uint64_t AggregateMax[MICROPROFILE_MAX_TIMERS]; + uint64_t AggregateMin[MICROPROFILE_MAX_TIMERS]; + uint64_t AggregateExclusive[MICROPROFILE_MAX_TIMERS]; + uint64_t AggregateMaxExclusive[MICROPROFILE_MAX_TIMERS]; + + uint32_t FrameGroupThreadValid[MICROPROFILE_MAX_THREADS / 32 + 1]; + struct GroupTime + { + uint64_t nTicks; + uint64_t nTicksExclusive; + uint32_t nCount; + }; + + GroupTime FrameGroupThread[MICROPROFILE_MAX_THREADS][MICROPROFILE_MAX_GROUPS]; + GroupTime FrameGroup[MICROPROFILE_MAX_GROUPS]; + uint64_t AccumGroup[MICROPROFILE_MAX_GROUPS]; + uint64_t AccumGroupMax[MICROPROFILE_MAX_GROUPS]; + + uint64_t AggregateGroup[MICROPROFILE_MAX_GROUPS]; + uint64_t AggregateGroupMax[MICROPROFILE_MAX_GROUPS]; + + MicroProfileGraphState Graph[MICROPROFILE_MAX_GRAPHS]; + uint32_t nGraphPut; + + uint32_t nThreadActive[MICROPROFILE_MAX_THREADS]; + MicroProfileThreadLog* Pool[MICROPROFILE_MAX_THREADS]; + MicroProfileThreadLogGpu* PoolGpu[MICROPROFILE_MAX_THREADS]; + + MicroProfileThreadLog TimelineLog; + uint32_t TimelineTokenFrameEnter[MICROPROFILE_TIMELINE_MAX_TOKENS]; + uint32_t TimelineTokenFrameLeave[MICROPROFILE_TIMELINE_MAX_TOKENS]; + uint32_t TimelineToken[MICROPROFILE_TIMELINE_MAX_TOKENS]; + const char* TimelineTokenStaticString[MICROPROFILE_TIMELINE_MAX_TOKENS]; + + uint32_t nTimelineFrameMax; + MicroProfileFrameExtraCounterData* FrameExtraCounterData; + MicroProfileCsvConfig CsvConfig; + const char* pSettings; + const char* pSettingsReadOnly; + const char* pSettingsTemp; + + uint32_t nNumLogs; + uint32_t nNumLogsGpu; + uint32_t nMemUsage; + int nFreeListHead; + + uint32_t nFrameCurrent; + uint32_t nFrameCurrentIndex; + uint32_t nFramePut; + uint32_t nFrameNext; + uint64_t nFramePutIndex; + + MicroProfileFrameState Frames[MICROPROFILE_MAX_FRAME_HISTORY]; + + uint64_t nFlipTicks; + uint64_t nFlipAggregate; + uint64_t nFlipMax; + uint64_t nFlipAggregateDisplay; + uint64_t nFlipMaxDisplay; + + MicroProfileThread ContextSwitchThread; + bool bContextSwitchRunning; + bool bContextSwitchStop; + bool bContextSwitchAllThreads; + bool bContextSwitchNoBars; + uint32_t nContextSwitchUsage; + uint32_t nContextSwitchLastPut; + + int64_t nContextSwitchHoverTickIn; + int64_t nContextSwitchHoverTickOut; + uint32_t nContextSwitchHoverThread; + uint32_t nContextSwitchHoverThreadBefore; + uint32_t nContextSwitchHoverThreadAfter; + uint8_t nContextSwitchHoverCpu; + uint8_t nContextSwitchHoverCpuNext; + + uint32_t CoreCount; + uint8_t CoreEfficiencyClass[MICROPROFILE_MAX_CPU_CORES]; + + uint32_t nContextSwitchPut; + MicroProfileContextSwitch ContextSwitch[MICROPROFILE_CONTEXT_SWITCH_BUFFER_SIZE]; + + MpSocket ListenerSocket; + uint32_t nWebServerPort; + + char WebServerBuffer[MICROPROFILE_WEBSERVER_SOCKET_BUFFER_SIZE]; + uint32_t WebServerPut; + + uint64_t nWebServerDataSent; + + int WebSocketTimers; + int WebSocketCounters; + int WebSocketGroups; + uint32_t nWebSocketDirty; + MpSocket WebSockets[1]; + int64_t WebSocketFrameLast[1]; + uint32_t nNumWebSockets; + uint32_t nSocketFail; // for error propagation. + + MicroProfileThread WebSocketSendThread; + bool WebSocketThreadRunning; + bool WebSocketThreadJoined; + + uint32_t WSCategoriesSent; + uint32_t WSGroupsSent; + uint32_t WSTimersSent; + uint32_t WSCountersSent; + MicroProfileWebSocketBuffer WSBuf; + char* pJsonSettings; + const char* pJsonSettingsName; + bool bJsonSettingsReadOnly; + uint32_t nJsonSettingsPending; + uint32_t nJsonSettingsBufferSize; + uint32_t nWSWasConnected; + uint32_t nMicroProfileShutdown; + uint32_t nWSViewMode; + + char CounterNames[MICROPROFILE_MAX_COUNTER_NAME_CHARS]; + MicroProfileCounterInfo CounterInfo[MICROPROFILE_MAX_COUNTERS]; + MicroProfileCounterSource CounterSource[MICROPROFILE_MAX_COUNTERS]; + uint32_t nNumCounters; + uint32_t nCounterNamePos; + std::atomic Counters[MICROPROFILE_MAX_COUNTERS]; + std::atomic* CountersDouble; +#if MICROPROFILE_COUNTER_HISTORY // uses 1kb per allocated counter. 512kb for default counter count + uint32_t nCounterHistoryPut; + int64_t nCounterHistory[MICROPROFILE_GRAPH_HISTORY][MICROPROFILE_MAX_COUNTERS]; // flipped to make swapping cheap, drawing more expensive. + int64_t nCounterMax[MICROPROFILE_MAX_COUNTERS]; + int64_t nCounterMin[MICROPROFILE_MAX_COUNTERS]; + double* dCounterHistory; + double* dCounterMax; + double* dCounterMin; +#endif + + MicroProfileThread AutoFlipThread; + std::atomic nAutoFlipDelay; + std::atomic nAutoFlipStop; + + MicroProfileStrings Strings; + MicroProfileToken CounterToken_MicroProfile; + MicroProfileToken CounterToken_StringBlock; + MicroProfileToken CounterToken_StringBlock_Count; + MicroProfileToken CounterToken_StringBlock_Waste; + MicroProfileToken CounterToken_StringBlock_Strings; + MicroProfileToken CounterToken_StringBlock_Memory; + + MicroProfileToken CounterToken_Alloc; + MicroProfileToken CounterToken_Alloc_Memory; + MicroProfileToken CounterToken_Alloc_Count; + +#if MICROPROFILE_DYNAMIC_INSTRUMENT + uint32_t DynamicTokenIndex; + MicroProfileToken DynamicTokens[MICROPROFILE_MAX_DYNAMIC_TOKENS]; + void* FunctionsInstrumented[MICROPROFILE_MAX_DYNAMIC_TOKENS]; + const char* FunctionsInstrumentedName[MICROPROFILE_MAX_DYNAMIC_TOKENS]; + const char* FunctionsInstrumentedModuleNames[MICROPROFILE_MAX_DYNAMIC_TOKENS]; + // const char* FunctionsInstrumentedUnmangled[MICROPROFILE_MAX_DYNAMIC_TOKENS]; + uint32_t WSFunctionsInstrumentedSent; + MicroProfileSymbolState SymbolState; + + MicroProfileSymbolModule SymbolModules[MICROPROFILE_INSTRUMENT_MAX_MODULES]; + char SymbolModuleNameBuffer[MICROPROFILE_INSTRUMENT_MAX_MODULE_CHARS]; + int SymbolModuleNameOffset; + int SymbolNumModules; + int WSSymbolModulesSent; + std::atomic nSymbolsDirty; + + MicroProfileFunctionQuery* pPendingQuery; + MicroProfileFunctionQuery* pFinishedQuery; + MicroProfileFunctionQuery* pQueryFreeList; + uint32_t nQueryProcessed; + uint32_t nNumQueryFree; + uint32_t nNumQueryAllocated; + + int SymbolThreadRunning; + int SymbolThreadFinished; + MicroProfileThread SymbolThread; + int nNumPatchErrors; + MicroProfilePatchError PatchErrors[MICROPROFILE_MAX_PATCH_ERRORS]; + int nNumPatchErrorFunctions; + const char* PatchErrorFunctionNames[MICROPROFILE_MAX_PATCH_ERRORS]; + MicroProfileSuspendState SuspendState; + MicroProfileArray MemoryRegions; +#endif + + int GpuQueue; + MicroProfileThreadLogGpu* pGpuGlobal; + MicroProfileGpuTimerState* pGPU; +}; + +inline uint32_t MicroProfileLogGetType(MicroProfileLogEntry Index) +{ + return ((MP_LOG_BEGIN_MASK & Index) >> 62) & 0x3; +} + +inline uint64_t MicroProfileLogGetTimerIndex(MicroProfileLogEntry Index) +{ + return (0x3fff & (Index >> 48)); +} +uint32_t MicroProfileLogGetDataSize(MicroProfileLogEntry Index) +{ + if(MicroProfileLogGetType(Index) == MP_LOG_EXTENDED) + return 0xffff & (Index >> 32); + else + return 0; +} + +inline EMicroProfileTokenExtended MicroProfileLogGetExtendedToken(MicroProfileLogEntry Index) +{ + return (EMicroProfileTokenExtended)(0x3fff & (Index >> 48)); +} + +inline uint32_t MicroProfileLogGetExtendedDataSize(MicroProfileLogEntry Index) +{ + return (uint32_t)(0xffff & (Index >> 32)); +} + +inline uint32_t MicroProfileLogGetExtendedPayload(MicroProfileLogEntry Index) +{ + return (uint32_t)(0xffffffff & Index); +} + +inline uint64_t MicroProfileLogGetExtendedPayloadNoData(MicroProfileLogEntry Index) +{ + return (uint64_t)(MP_LOG_TICK_MASK & Index); +} + +inline void* MicroProfileLogGetExtendedPayloadNoDataPtr(MicroProfileLogEntry Index) +{ + return (void*)(MP_LOG_PAYLOAD_PTR_MASK & Index); +} + +MicroProfileLogEntry MicroProfileMakeLogIndex(uint64_t nBegin, MicroProfileToken nToken, int64_t nTick); +MicroProfileLogEntry MicroProfileMakeLogExtended(EMicroProfileTokenExtended eTokenExt, uint32_t nDataSizeQWords, uint32_t nPayload); +MicroProfileLogEntry MicroProfileMakeLogExtendedNoData(EMicroProfileTokenExtended eTokenExt, uint64_t nTick); + +inline MicroProfileLogEntry MicroProfileMakeLogIndex(uint64_t nBegin, MicroProfileToken nToken, int64_t nTick) +{ + MicroProfileLogEntry Entry = (nBegin << 62) | ((0x3fff & nToken) << 48) | (MP_LOG_TICK_MASK & nTick); + uint32_t t = MicroProfileLogGetType(Entry); + uint64_t nTimerIndex = MicroProfileLogGetTimerIndex(Entry); + MP_ASSERT(t == nBegin); + MP_ASSERT(nTimerIndex == (nToken & 0x3fff)); + return Entry; +} + +// extended data, with the option to store 0xfffe * 8 bytes after +inline MicroProfileLogEntry MicroProfileMakeLogExtended(EMicroProfileTokenExtended eTokenExt, uint32_t nDataSizeQWords, uint32_t nPayload) +{ + MP_ASSERT(nDataSizeQWords < 0xffff); + MicroProfileLogEntry Entry = (((uint64_t)MP_LOG_EXTENDED) << 62) | ((0x3fff & (uint64_t)eTokenExt) << 48) | ((0xffff & (uint64_t)nDataSizeQWords) << 32) | nPayload; + + MP_ASSERT(MicroProfileLogGetExtendedToken(Entry) == eTokenExt); + MP_ASSERT(MicroProfileLogGetExtendedDataSize(Entry) == nDataSizeQWords); + MP_ASSERT(MicroProfileLogGetExtendedPayload(Entry) == nPayload); + + return Entry; +} +// extended with no data, but instead 48 bits payload +inline MicroProfileLogEntry MicroProfileMakeLogExtendedNoData(EMicroProfileTokenExtended eTokenExt, uint64_t nPayload) +{ + MicroProfileLogEntry Entry = (((uint64_t)MP_LOG_EXTENDED_NO_DATA) << 62) | ((0x3fff & (uint64_t)eTokenExt) << 48) | (MP_LOG_TICK_MASK & nPayload); + + MP_ASSERT(MicroProfileLogGetExtendedToken(Entry) == eTokenExt); + MP_ASSERT(MicroProfileLogGetExtendedPayloadNoData(Entry) == nPayload); + + return Entry; +} + +// extended with no data, but instead 61 bits payload. used to store a pointer. +inline MicroProfileLogEntry MicroProfileMakeLogExtendedNoDataPtr(uint64_t nPayload) +{ + uint64_t hest = ETOKEN_CSTR_PTR; + MicroProfileLogEntry Entry = (((uint64_t)MP_LOG_EXTENDED_NO_DATA) << 62) | (hest << 48) | (MP_LOG_PAYLOAD_PTR_MASK & nPayload); + uint64_t v0 = (MP_LOG_PAYLOAD_PTR_MASK & nPayload); + uint64_t v1 = (uint64_t)MicroProfileLogGetExtendedPayloadNoDataPtr(Entry); + + MP_ASSERT(v0 == v1); + return Entry; +} + +inline uint32_t MicroProfileGetQWordSize(uint32_t nDataSize) +{ + uint32_t nSize = (nDataSize + 7) / 8; + MP_ASSERT(nSize < 0xffff); // won't pack... + return nSize; +} + +namespace +{ +struct MicroProfilePayloadPack +{ + union + { + struct + { +#if MICROPROFILE_BIG_ENDIAN /// NOT implemented. + char h; + char message[7]; +#else + char message[7]; + char h; +#endif + }; + uint64_t LogEntry; + }; +}; +}; // namespace + +inline int64_t MicroProfileLogTickDifference(MicroProfileLogEntry Start, MicroProfileLogEntry End) +{ + int64_t nStart = Start; + int64_t nEnd = End; + int64_t nDifference = ((nEnd << 16) - (nStart << 16)); + return nDifference >> 16; +} +inline int64_t MicroProfileLogTickMax(MicroProfileLogEntry A, MicroProfileLogEntry B) +{ + int64_t Diff = MicroProfileLogTickDifference(A, B); + if(Diff < 0) + { + return A; + } + else + { + return B; + } +} + +inline int64_t MicroProfileLogTickMin(MicroProfileLogEntry A, MicroProfileLogEntry B) +{ + int64_t Diff = MicroProfileLogTickDifference(A, B); + if(Diff < 0) + { + return B; + } + else + { + return A; + } +} +inline int64_t MicroProfileLogTickClamp(uint64_t T, uint64_t min, uint64_t max) +{ + return MicroProfileLogTickMin(MicroProfileLogTickMax(T, min), max); +} + +inline int64_t MicroProfileLogGetTick(MicroProfileLogEntry e) +{ + return MP_LOG_TICK_MASK & e; +} + +inline int64_t MicroProfileLogSetTick(MicroProfileLogEntry e, int64_t nTick) +{ + return (MP_LOG_TICK_MASK & nTick) | (e & ~MP_LOG_TICK_MASK); +} + +inline uint16_t MicroProfileGetTimerIndex(MicroProfileToken t) +{ + return (t & 0xffff); +} +inline uint32_t MicroProfileGetGroupMask(MicroProfileToken t) +{ + return (uint32_t)((t >> 16) & MICROPROFILE_GROUP_MASK_ALL); +} +inline uint32_t MicroProfileGetGroupMaskIndex(MicroProfileToken t) +{ + return (uint32_t)(t >> 48); +} + +inline MicroProfileToken MicroProfileMakeToken(uint32_t nGroupMask, uint16_t nGroupIndex, uint16_t nTimer) +{ + uint64_t token = ((uint64_t)nGroupIndex << 48llu) | ((uint64_t)nGroupMask << 16llu) | nTimer; + if(0 != (token & MP_LOG_CSTR_MASK)) + { + MP_BREAK(); // should never happen + } + return token; +} + +template +T MicroProfileMin(T a, T b) +{ + return a < b ? a : b; +} + +template +T MicroProfileMax(T a, T b) +{ + return a > b ? a : b; +} +template +T MicroProfileClamp(T a, T min_, T max_) +{ + return MicroProfileMin(max_, MicroProfileMax(min_, a)); +} + +inline int64_t MicroProfileMsToTick(float fMs, int64_t nTicksPerSecond) +{ + return (int64_t)(fMs * 0.001f * nTicksPerSecond); +} + +inline float MicroProfileTickToMsMultiplier(int64_t nTicksPerSecond) +{ + return 1000.f / (nTicksPerSecond ? nTicksPerSecond : 1); +} +float MicroProfileTickToMsMultiplierCpu() +{ + return MicroProfileTickToMsMultiplier(MicroProfileTicksPerSecondCpu()); +} + +float MicroProfileTickToMsMultiplierGpu() +{ + return MicroProfileTickToMsMultiplier(MicroProfileTicksPerSecondGpu()); +} +uint16_t MicroProfileGetGroupIndex(MicroProfileToken t) +{ + return (uint16_t)MicroProfileGet()->TimerToGroup[MicroProfileGetTimerIndex(t)]; +} + +uint64_t MicroProfileTick() +{ + return MP_TICK(); +} + +#ifdef _WIN32 +#include +#define fopen microprofile_fopen_helper + +FILE* microprofile_fopen_helper(const char* filename, const char* mode) +{ + FILE* F = 0; + if(0 == fopen_s(&F, filename, mode)) + { + return F; + } + return 0; +} + +int64_t MicroProfileTicksPerSecondCpu() +{ + static int64_t nTicksPerSecond = 0; + if(nTicksPerSecond == 0) + { + QueryPerformanceFrequency((LARGE_INTEGER*)&nTicksPerSecond); + } + return nTicksPerSecond; +} +int64_t MicroProfileGetTick() +{ + int64_t ticks; + QueryPerformanceCounter((LARGE_INTEGER*)&ticks); + return ticks; +} + +#endif + +#if 1 + +typedef void* (*MicroProfileThreadFunc)(void*); + +#ifndef _WIN32 +typedef pthread_t MicroProfileThread; +void MicroProfileThreadStart(MicroProfileThread* pThread, MicroProfileThreadFunc Func) +{ + pthread_attr_t Attr; + int r = pthread_attr_init(&Attr); + MP_ASSERT(r == 0); + pthread_create(pThread, &Attr, Func, 0); +} +void MicroProfileThreadJoin(MicroProfileThread* pThread) +{ + int r = pthread_join(*pThread, 0); + MP_ASSERT(r == 0); +} +#elif defined(_WIN32) +typedef HANDLE MicroProfileThread; +DWORD __stdcall ThreadTrampoline(void* pFunc) +{ + MicroProfileThreadFunc F = (MicroProfileThreadFunc)pFunc; + return (uint32_t)(uintptr_t)F(0); +} + +void MicroProfileThreadStart(MicroProfileThread* pThread, MicroProfileThreadFunc Func) +{ + *pThread = CreateThread(0, 0, ThreadTrampoline, Func, 0, 0); +} +void MicroProfileThreadJoin(MicroProfileThread* pThread) +{ + WaitForSingleObject(*pThread, INFINITE); + CloseHandle(*pThread); +} +#else +#include +typedef std::thread* MicroProfileThread; +inline void MicroProfileThreadStart(MicroProfileThread* pThread, MicroProfileThreadFunc Func) +{ + *pThread = MP_ALLOC_OBJECT(std::thread); + new(*pThread) std::thread(Func, nullptr); +} +inline void MicroProfileThreadJoin(MicroProfileThread* pThread) +{ + (*pThread)->join(); + (*pThread)->~thread(); + MP_FREE(*pThread); + *pThread = 0; +} +#endif +#endif + +#if MICROPROFILE_WEBSERVER + +#ifdef _WIN32 +#define MP_INVALID_SOCKET(f) (f == INVALID_SOCKET) +#else +#include +#include +#include +#define MP_INVALID_SOCKET(f) (f < 0) +#endif + +void MicroProfileWebServerStart(); +void MicroProfileWebServerStop(); +void MicroProfileWebServerJoin(); +bool MicroProfileWebServerUpdate(); +void MicroProfileDumpToFile(); + +#else + +#define MicroProfileWebServerStart() \ + do \ + { \ + } while(0) +#define MicroProfileWebServerStop() \ + do \ + { \ + } while(0) +#define MicroProfileWebServerJoin() \ + do \ + { \ + } while(0) +#define MicroProfileWebServerUpdate() false +#define MicroProfileDumpToFile() \ + do \ + { \ + } while(0) +#endif + +#include +#include +#include +#include + +#if MICROPROFILE_DEBUG +#ifdef _WIN32 +void uprintf(const char* fmt, ...) +{ + va_list args; + va_start(args, fmt); + char buffer[1024]; + stbsp_vsnprintf(buffer, sizeof(buffer) - 1, fmt, args); + OutputDebugStringA(buffer); + va_end(args); +} +#else +#define uprintf(...) printf(__VA_ARGS__) +#endif +#else +#define uprintf(...) \ + do \ + { \ + sizeof(__VA_ARGS__); \ + } while(0) +#endif + +#define S g_MicroProfile + +MicroProfile g_MicroProfile; +#ifdef MICROPROFILE_IOS +// iOS doesn't support __thread +static pthread_key_t g_MicroProfileThreadLogKey; +static pthread_once_t g_MicroProfileThreadLogKeyOnce = PTHREAD_ONCE_INIT; + +static void MicroProfileCreateThreadLogKey() +{ + pthread_key_create(&g_MicroProfileThreadLogKey, NULL); +} +#else +MP_THREAD_LOCAL MicroProfileThreadLog* g_MicroProfileThreadLogThreadLocal = 0; +#endif +static bool g_bUseLock = false; /// This is used because windows does not support using mutexes under dll init(which is where global initialization is handled) + +MICROPROFILE_DEFINE(g_MicroProfileFlip, "MicroProfile", "MicroProfileFlip", MP_GREEN4); +MICROPROFILE_DEFINE(g_MicroProfileThreadLoop, "MicroProfile", "ThreadLoop", MP_GREEN4); +MICROPROFILE_DEFINE(g_MicroProfileClear, "MicroProfile", "Clear", MP_GREEN4); +MICROPROFILE_DEFINE(g_MicroProfileAccumulate, "MicroProfile", "Accumulate", MP_GREEN4); +MICROPROFILE_DEFINE(g_MicroProfileContextSwitchSearch, "MicroProfile", "ContextSwitchSearch", MP_GREEN4); +MICROPROFILE_DEFINE(g_MicroProfileGpuSubmit, "MicroProfile", "MicroProfileGpuSubmit", MP_HOTPINK2); +MICROPROFILE_DEFINE(g_MicroProfileSendLoop, "MicroProfile", "MicroProfileSocketSendLoop", MP_GREEN4); +MICROPROFILE_DEFINE_LOCAL_ATOMIC_COUNTER(g_MicroProfileBytesPerFlip, "microprofile/bytesperflip"); + +// void MicroProfileHashTableInit(MicroProfileHashTable* pTable, uint32_t nInitialSize, MicroProfileHashCompareFunction CompareFunc, MicroProfileHashFunction HashFunc); +void MicroProfileHashTableDestroy(MicroProfileHashTable* pTable); +uint64_t MicroProfileHashTableHash(MicroProfileHashTable* pTable, uint64_t K); +void MicroProfileHashTableGrow(MicroProfileHashTable* pTable); + +bool MicroProfileHashTableSet(MicroProfileHashTable* pTable, uint64_t Key, uintptr_t Value, uint64_t H, bool bAllowGrow); +bool MicroProfileHashTableGet(MicroProfileHashTable* pTable, uint64_t Key, uintptr_t* pValue); +bool MicroProfileHashTableRemove(MicroProfileHashTable* pTable, uint64_t Key); + +bool MicroProfileHashTableSetString(MicroProfileHashTable* pTable, const char* pKey, const char* pValue); +bool MicroProfileHashTableGetString(MicroProfileHashTable* pTable, const char* pKey, const char** pValue); +bool MicroProfileHashTableRemoveString(MicroProfileHashTable* pTable, const char* pKey); + +bool MicroProfileHashTableSetPtr(MicroProfileHashTable* pTable, const void* pKey, void* pValue); +template +bool MicroProfileHashTableGetPtr(MicroProfileHashTable* pTable, const void* pKey, T** pValue = nullptr); +bool MicroProfileHashTableRemovePtr(MicroProfileHashTable* pTable, const void* pKey); + +enum +{ + ESTRINGINTERN_LOWERCASE = 1, + ESTRINGINTERN_FORCEFORWARDSLASH = 0x2, +}; +const char* MicroProfileStringIntern(const char* pStr); +const char* MicroProfileStringInternLower(const char* pStr); +const char* MicroProfileStringInternSlash(const char* pStr); +const char* MicroProfileStringIntern(const char* pStr, uint32_t nLen, uint32_t nInternalFlags = 0); + +void MicroProfileStringsInit(MicroProfileStrings* pStrings); +void MicroProfileStringsDestroy(MicroProfileStrings* pStrings); + +MicroProfileToken MicroProfileCounterTokenInit(int nParent, uint32_t nFlags); +void MicroProfileCounterTokenInitName(MicroProfileToken nToken, const char* pName); +void MicroProfileCounterConfigToken(MicroProfileToken, uint32_t eFormat, int64_t nLimit, uint32_t nFlags); +uint16_t MicroProfileFindGroup(const char* pGroup); + +inline std::recursive_mutex& MicroProfileMutex() +{ + static std::recursive_mutex Mutex; + return Mutex; +} +std::recursive_mutex& MicroProfileGetMutex() +{ + return MicroProfileMutex(); +} + +inline std::recursive_mutex& MicroProfileTimelineMutex() +{ + static std::recursive_mutex Mutex; + return Mutex; +} +MICROPROFILE_API MicroProfile* MicroProfileGet() +{ + return &g_MicroProfile; +} + +MicroProfileThreadLog* MicroProfileCreateThreadLog(const char* pName); +MicroProfileThreadLogGpu* MicroProfileThreadLogGpuAllocInternal(); +void* MicroProfileSocketSenderThread(void*); + +void MicroProfileInit() +{ + static bool bOnce = true; + if(!bOnce) + { + return; + } + + std::recursive_mutex& mutex = MicroProfileMutex(); + bool bUseLock = g_bUseLock; + if(bUseLock) + mutex.lock(); + if(bOnce) + { + bOnce = false; + memset(&S, 0, sizeof(S)); + + MicroProfileStringsInit(&S.Strings); + + // these strings are used for counter names inside the string + S.CounterToken_MicroProfile = MicroProfileCounterTokenInit(-1, 0); + S.CounterToken_StringBlock = MicroProfileCounterTokenInit(S.CounterToken_MicroProfile, 0); + S.CounterToken_StringBlock_Count = MicroProfileCounterTokenInit(S.CounterToken_StringBlock, 0); + S.CounterToken_StringBlock_Waste = MicroProfileCounterTokenInit(S.CounterToken_StringBlock, 0); + S.CounterToken_StringBlock_Strings = MicroProfileCounterTokenInit(S.CounterToken_StringBlock, 0); + S.CounterToken_StringBlock_Memory = MicroProfileCounterTokenInit(S.CounterToken_StringBlock, 0); + + S.CounterToken_Alloc = MicroProfileCounterTokenInit(S.CounterToken_MicroProfile, 0); + S.CounterToken_Alloc_Memory = MicroProfileCounterTokenInit(S.CounterToken_Alloc, 0); + S.CounterToken_Alloc_Count = MicroProfileCounterTokenInit(S.CounterToken_Alloc, 0); + + MicroProfileCounterTokenInitName(S.CounterToken_MicroProfile, "microprofile"); + MicroProfileCounterTokenInitName(S.CounterToken_StringBlock, "stringblock"); + MicroProfileCounterTokenInitName(S.CounterToken_StringBlock_Count, "count"); + MicroProfileCounterTokenInitName(S.CounterToken_StringBlock_Waste, "waste"); + MicroProfileCounterTokenInitName(S.CounterToken_StringBlock_Strings, "strings"); + MicroProfileCounterTokenInitName(S.CounterToken_StringBlock_Memory, "memory"); + + MicroProfileCounterTokenInitName(S.CounterToken_Alloc, "alloc"); + MicroProfileCounterTokenInitName(S.CounterToken_Alloc_Memory, "memory"); + MicroProfileCounterTokenInitName(S.CounterToken_Alloc_Count, "count"); + + S.nMemUsage += sizeof(S); + for(int i = 0; i < MICROPROFILE_MAX_GROUPS; ++i) + { + S.GroupInfo[i].pName[0] = '\0'; + } + for(int i = 0; i < MICROPROFILE_MAX_CATEGORIES; ++i) + { + S.CategoryInfo[i].pName[0] = '\0'; + memset(S.CategoryInfo[i].nGroupMask, 0, sizeof(S.CategoryInfo[i].nGroupMask)); + } + memcpy(&S.CategoryInfo[0].pName[0], "default", sizeof("default")); + S.nCategoryCount = 1; + for(int i = 0; i < MICROPROFILE_MAX_TIMERS; ++i) + { + S.TimerInfo[i].pName[0] = '\0'; + } + S.nGroupCount = 0; + S.nFlipStartTick = MP_TICK(); + S.nContextSwitchStalledTick = MP_TICK(); + S.nAggregateFlipTick = MP_TICK(); + memset(S.nActiveGroups, 0, sizeof(S.nActiveGroups)); + S.nFrozen = 0; + S.nWasFrozen = 0; + memset(S.nForceGroups, 0, sizeof(S.nForceGroups)); + memset(S.nActiveGroupsWanted, 0, sizeof(S.nActiveGroupsWanted)); + S.nStartEnabled = 0; + S.nAllThreadsWanted = 1; + S.nAggregateFlip = 0; + S.nTotalTimers = 0; + for(uint32_t i = 0; i < MICROPROFILE_MAX_GRAPHS; ++i) + { + S.Graph[i].nToken = MICROPROFILE_INVALID_TOKEN; + } + S.fReferenceTime = 33.33f; + S.fRcpReferenceTime = 1.f / S.fReferenceTime; + S.nFreeListHead = -1; + int64_t nTick = MP_TICK(); + for(int i = 0; i < MICROPROFILE_MAX_FRAME_HISTORY; ++i) + { + S.Frames[i].nFrameStartCpu = nTick; + S.Frames[i].nFrameStartGpu = MICROPROFILE_INVALID_TICK; + } + S.nWebServerPort = MICROPROFILE_WEBSERVER_PORT; // Use defined value as default port + S.nWebServerDataSent = (uint64_t)-1; + S.WebSocketTimers = -1; + S.WebSocketCounters = -1; + S.WebSocketGroups = -1; + S.nSocketFail = 0; + + S.DumpFrameCount = MICROPROFILE_WEBSERVER_DEFAULT_FRAMES; + +#if MICROPROFILE_COUNTER_HISTORY + S.nCounterHistoryPut = 0; + for(uint32_t i = 0; i < MICROPROFILE_MAX_COUNTERS; ++i) + { + S.nCounterMin[i] = 0x7fffffffffffffff; + S.nCounterMax[i] = 0x8000000000000000; + } +#endif + S.GpuQueue = MICROPROFILE_GPU_INIT_QUEUE("GPU"); + S.pGpuGlobal = MicroProfileThreadLogGpuAllocInternal(); + MicroProfileGpuBegin(0, S.pGpuGlobal); + + S.pJsonSettings = 0; + S.pJsonSettingsName = nullptr; + S.nJsonSettingsPending = 0; + S.nJsonSettingsBufferSize = 0; + S.nWSWasConnected = 0; + + for(uint32_t i = 0; i < MICROPROFILE_TIMELINE_MAX_TOKENS; ++i) + { + S.TimelineTokenFrameEnter[i] = MICROPROFILE_INVALID_FRAME; + S.TimelineTokenFrameLeave[i] = MICROPROFILE_INVALID_FRAME; + S.TimelineTokenStaticString[i] = nullptr; + S.TimelineToken[i] = 0; + } + memset(&S.AccumMinTimers[0], 0xFF, sizeof(S.AccumMinTimers)); + S.CountersDouble = (std::atomic*)&S.Counters; +#if MICROPROFILE_COUNTER_HISTORY + S.dCounterHistory = (double*)S.nCounterHistory; + S.dCounterMax = (double*)S.nCounterMax; + S.dCounterMin = (double*)S.nCounterMin; +#endif + } + MicroProfileUpdateSettingsPath(); + +#if MICROPROFILE_FRAME_EXTRA_DATA + S.FrameExtraCounterData = (MicroProfileFrameExtraCounterData*)1; +#endif + MicroProfileCounterConfigToken(S.CounterToken_Alloc_Memory, MICROPROFILE_COUNTER_FORMAT_BYTES, 0, MICROPROFILE_COUNTER_FLAG_DETAILED); + MICROPROFILE_COUNTER_CONFIG("MicroProfile/ThreadLog/Memory", MICROPROFILE_COUNTER_FORMAT_BYTES, 0, MICROPROFILE_COUNTER_FLAG_DETAILED); + + if(bUseLock) + { + mutex.unlock(); + } +} +void MicroProfileUpdateSettingsPath() +{ + if(S.pSettings) + { + MicroProfileFreeInternal((void*)S.pSettings); + S.pSettings = nullptr; + } + if(S.pSettingsReadOnly) + { + MicroProfileFreeInternal((void*)S.pSettingsReadOnly); + S.pSettingsReadOnly = nullptr; + } + if(S.pSettingsTemp) + { + MicroProfileFreeInternal((void*)S.pSettingsTemp); + S.pSettingsTemp = nullptr; + } + auto DupeString = [](const char* BasePath, const char* File) -> const char* + { + size_t BaseLen = strlen(BasePath); + bool TrailingSlash = BaseLen > 1 && (BasePath[BaseLen - 1] == '\\' || BasePath[BaseLen - 1] == '/'); + size_t Len = BaseLen + strlen(File) + 2; + char* Data = (char*)MicroProfileAllocInternal(Len + 1, 1); +#ifdef _WIN32 + char Slash = '\\'; +#else + char Slash = '/'; +#endif + if(TrailingSlash) + snprintf(Data, Len, "%s%s", BasePath, File); + else + snprintf(Data, Len, "%s%c%s", BasePath, Slash, File); + + return Data; + }; + const char* pBaseSettingsPath = MICROPROFILE_GET_SETTINGS_FILE_PATH; + S.pSettings = DupeString(pBaseSettingsPath, MICROPROFILE_SETTINGS_FILE); + S.pSettingsReadOnly = DupeString(pBaseSettingsPath, MICROPROFILE_SETTINGS_FILE_BUILTIN); + S.pSettingsTemp = DupeString(pBaseSettingsPath, MICROPROFILE_SETTINGS_FILE MICROPROFILE_SETTINGS_FILE_TEMP); +} + +void MicroProfileJoinContextSwitchTrace(); + +void MicroProfileShutdown() +{ + { + std::lock_guard Lock(MicroProfileMutex()); + S.nMicroProfileShutdown = 1; + MicroProfileStopContextSwitchTrace(); + } + MicroProfileWebServerJoin(); + MicroProfileJoinContextSwitchTrace(); + { + + std::lock_guard Lock(MicroProfileMutex()); + if(S.pJsonSettings) + { + MP_FREE(S.pJsonSettings); + S.pJsonSettings = 0; + S.pJsonSettingsName = 0; + S.nJsonSettingsBufferSize = 0; + } + if(S.pGPU) + { + MicroProfileGpuShutdownPlatform(); + } + MicroProfileHashTableDestroy(&S.Strings.HashTable); + MicroProfileStringsDestroy(&S.Strings); + MICROPROFILE_FREE_NON_ALIGNED(S.WSBuf.pBufferAllocation); + + MicroProfileFreeGpuQueue(S.GpuQueue); + MicroProfileThreadLogGpuFree(S.pGpuGlobal); + + for(uint32_t i = 0; i < S.nNumLogs; ++i) + { +#if MICROPROFILE_ASSERT_LOG_FREED + MP_ASSERT(S.Pool[i]->nActive != 1); +#endif + MP_FREE(S.Pool[i]); + } + + for(uint32_t i = 0; i < S.nNumLogsGpu; ++i) + { +#if MICROPROFILE_ASSERT_LOG_FREED + MP_ASSERT(!S.PoolGpu[i]->nAllocated); +#endif + MP_FREE(S.PoolGpu[i]); + } + MicroProfileFreeInternal((void*)S.pSettings); + S.pSettings = nullptr; + MicroProfileFreeInternal((void*)S.pSettingsReadOnly); + S.pSettingsReadOnly = nullptr; + MicroProfileFreeInternal((void*)S.pSettingsTemp); + S.pSettingsTemp = nullptr; + } +} + +static void* MicroProfileAutoFlipThread(void*) +{ + MicroProfileOnThreadCreate("AutoFlipThread"); + while(0 == S.nAutoFlipStop.load()) + { + MICROPROFILE_SCOPEI("MICROPROFILE", "AutoFlipThread", 0); + MicroProfileSleep(S.nAutoFlipDelay); + MicroProfileFlip(0); + } + MicroProfileOnThreadExit(); + return 0; +} + +void MicroProfileStartAutoFlip(uint32_t nMsDelay) +{ + S.nAutoFlipDelay = nMsDelay; + S.nAutoFlipStop.store(0); + MicroProfileThreadStart(&S.AutoFlipThread, MicroProfileAutoFlipThread); +} +void MicroProfileStopAutoFlip() +{ + S.nAutoFlipStop.store(1); + MicroProfileThreadJoin(&S.AutoFlipThread); +} + +void MicroProfileEnableFrameExtraCounterData() +{ + // should not be called at the same time as MicroProfileFlip. + if(!S.FrameExtraCounterData) + { + S.FrameExtraCounterData = (MicroProfileFrameExtraCounterData*)1; + } +} + +void MicroProfileCsvConfigEnd() +{ + MP_ASSERT(S.CsvConfig.State == MicroProfileCsvConfig::CONFIG); + S.CsvConfig.State = MicroProfileCsvConfig::ACTIVE; +} +void MicroProfileCsvConfigBegin(uint32_t MaxTimers, uint32_t MaxGroups, uint32_t MaxCounters, uint32_t Flags) +{ + MP_ASSERT(S.CsvConfig.State == MicroProfileCsvConfig::INACTIVE); // right now, only support being configured once. + uint32_t TotalElements = MaxTimers + MaxGroups + MaxCounters; + uint32_t BaseSize = (sizeof(MicroProfileCsvConfig) + 7) & 7; + uint32_t TimerIndexSize = sizeof(uint16_t) * MaxTimers; + uint32_t GroupIndexSize = sizeof(uint16_t) * MaxGroups; + uint32_t CounterIndexSize = sizeof(uint16_t) * MaxCounters; + uint32_t FrameBlockSize = TotalElements * sizeof(uint64_t); + uint32_t FrameDataSize = FrameBlockSize * MICROPROFILE_MAX_FRAME_HISTORY; + S.CsvConfig.NumTimers = 0; + S.CsvConfig.NumGroups = 0; + S.CsvConfig.NumCounters = 0; + S.CsvConfig.MaxTimers = MaxTimers; + S.CsvConfig.MaxGroups = MaxGroups; + S.CsvConfig.MaxCounters = MaxCounters; + S.CsvConfig.TotalElements = TotalElements; + S.CsvConfig.TimerIndices = (uint16_t*)MicroProfileAllocInternal(TimerIndexSize, alignof(uint16_t)); + S.CsvConfig.pTimerNames = (const char**)MicroProfileAllocInternal(MaxTimers * sizeof(const char*), alignof(const char*)); + memset(S.CsvConfig.pTimerNames, 0, MaxTimers * sizeof(const char*)); + for(uint32_t i = 0; i < MaxTimers; ++i) + S.CsvConfig.TimerIndices[i] = UINT16_MAX; + S.CsvConfig.pGroupNames = (const char**)MicroProfileAllocInternal(MaxGroups * sizeof(const char*), alignof(const char*)); + memset(S.CsvConfig.pGroupNames, 0, MaxGroups * sizeof(const char*)); + S.CsvConfig.GroupIndices = (uint16_t*)MicroProfileAllocInternal(GroupIndexSize, alignof(uint16_t)); + for(uint32_t i = 0; i < MaxGroups; ++i) + S.CsvConfig.GroupIndices[i] = UINT16_MAX; + S.CsvConfig.pCounterNames = (const char**)MicroProfileAllocInternal(MaxCounters * sizeof(const char*), alignof(const char*)); + memset(S.CsvConfig.pCounterNames, 0, MaxCounters * sizeof(const char*)); + S.CsvConfig.CounterIndices = (uint16_t*)MicroProfileAllocInternal(CounterIndexSize, alignof(uint16_t)); + for(uint32_t i = 0; i < MaxCounters; ++i) + S.CsvConfig.CounterIndices[i] = UINT16_MAX; + S.CsvConfig.FrameData = (uint64_t*)MicroProfileAllocInternal(FrameDataSize, alignof(uint64_t)); + memset(S.CsvConfig.FrameData, 0, FrameDataSize); + S.CsvConfig.State = MicroProfileCsvConfig::CONFIG; + S.CsvConfig.Flags = Flags; +} +void MicroProfileCsvConfigAddTimer(const char* Group, const char* Timer, const char* Name, MicroProfileTokenType Type) +{ + MP_ASSERT(S.CsvConfig.State == MicroProfileCsvConfig::CONFIG); + if(S.CsvConfig.State == MicroProfileCsvConfig::CONFIG && S.CsvConfig.NumTimers < S.CsvConfig.MaxTimers) + { + MicroProfileToken ret = MicroProfileGetToken(Group, Timer, MP_AUTO, Type, MICROPROFILE_TIMER_FLAG_PLACEHOLDER); + if(ret != MICROPROFILE_INVALID_TOKEN) + { + MP_ASSERT(S.CsvConfig.NumTimers < S.CsvConfig.MaxTimers); + uint16_t TimerIndex = MicroProfileGetTimerIndex(ret); + for(uint32_t i = 0; i < S.CsvConfig.NumTimers; ++i) + { + if(S.CsvConfig.TimerIndices[i] == TimerIndex) + return; + } + S.CsvConfig.pTimerNames[S.CsvConfig.NumTimers] = Name; + S.CsvConfig.TimerIndices[S.CsvConfig.NumTimers++] = TimerIndex; + } + } +} +void MicroProfileCsvConfigAddGroup(const char* Group, const char* Name) +{ + MP_ASSERT(S.CsvConfig.State == MicroProfileCsvConfig::CONFIG); + if(S.CsvConfig.State == MicroProfileCsvConfig::CONFIG && S.CsvConfig.NumGroups < S.CsvConfig.MaxGroups) + { + uint16_t Index = MicroProfileFindGroup(Group); + MP_ASSERT(UINT16_MAX != Index); + if(UINT16_MAX != Index) + { + MP_ASSERT(S.CsvConfig.NumGroups < S.CsvConfig.MaxGroups); + for(uint32_t i = 0; i < S.CsvConfig.NumGroups; ++i) + { + if(S.CsvConfig.GroupIndices[i] == Index) + return; + } + S.CsvConfig.pGroupNames[S.CsvConfig.NumGroups] = Name; + S.CsvConfig.GroupIndices[S.CsvConfig.NumGroups++] = Index; + } + } +} +void MicroProfileCsvConfigAddCounter(const char* CounterName, const char* Name) +{ + MP_ASSERT(S.CsvConfig.State == MicroProfileCsvConfig::CONFIG); + if(S.CsvConfig.State == MicroProfileCsvConfig::CONFIG && S.CsvConfig.NumCounters < S.CsvConfig.MaxCounters) + { + MicroProfileToken Token = MicroProfileGetCounterToken(CounterName, 0); + if(MICROPROFILE_INVALID_TOKEN != Token) + { + MP_ASSERT(Token < UINT16_MAX); + MP_ASSERT(S.CsvConfig.NumCounters < S.CsvConfig.MaxCounters); + for(uint32_t i = 0; i < S.CsvConfig.NumCounters; ++i) + { + if(S.CsvConfig.CounterIndices[i] == (uint16_t)Token) + return; + } + S.CsvConfig.pCounterNames[S.CsvConfig.NumCounters] = Name; + S.CsvConfig.CounterIndices[S.CsvConfig.NumCounters++] = (uint16_t)Token; + } + } +} + +#ifdef MICROPROFILE_IOS +inline MicroProfileThreadLog* MicroProfileGetThreadLog() +{ + pthread_once(&g_MicroProfileThreadLogKeyOnce, MicroProfileCreateThreadLogKey); + return (MicroProfileThreadLog*)pthread_getspecific(g_MicroProfileThreadLogKey); +} + +inline void MicroProfileSetThreadLog(MicroProfileThreadLog* pLog) +{ + pthread_once(&g_MicroProfileThreadLogKeyOnce, MicroProfileCreateThreadLogKey); + pthread_setspecific(g_MicroProfileThreadLogKey, pLog); +} +#else +MicroProfileThreadLog* MicroProfileGetThreadLog() +{ + return g_MicroProfileThreadLogThreadLocal; +} +void MicroProfileSetThreadLog(MicroProfileThreadLog* pLog) +{ + g_MicroProfileThreadLogThreadLocal = pLog; +} +#endif + +MicroProfileThreadLog* MicroProfileGetThreadLog2() +{ + MicroProfileThreadLog* pLog = MicroProfileGetThreadLog(); + if(!pLog) + { + MicroProfileInitThreadLog(); + pLog = MicroProfileGetThreadLog(); + } + return pLog; +} + +struct MicroProfileScopeLock +{ + bool bUseLock; + int nUnlock; + std::recursive_mutex& m; + MicroProfileScopeLock(std::recursive_mutex& m) + : bUseLock(g_bUseLock) + , nUnlock(0) + , m(m) + { + if(bUseLock) + m.lock(); + } + ~MicroProfileScopeLock() + { + MP_ASSERT(nUnlock == 0); + if(bUseLock) + m.unlock(); + } + void Unlock() + { + MP_ASSERT(bUseLock); + m.unlock(); + nUnlock++; + } + void Lock() + { + m.lock(); + nUnlock--; + } +}; + +void MicroProfileLogReset(MicroProfileThreadLog* pLog); +void MicroProfileLogClearInternal(MicroProfileThreadLog* pLog); + +MicroProfileThreadLog* MicroProfileCreateThreadLog(const char* pName) +{ + MicroProfileScopeLock L(MicroProfileMutex()); + + if(S.nNumLogs == MICROPROFILE_MAX_THREADS && S.nFreeListHead == -1) + { + uprintf("recycling thread logs\n"); + // reuse the oldest. + MicroProfileThreadLog* pOldest = 0; + uint32_t nIdleFrames = 0; + for(uint32_t i = 0; i < MICROPROFILE_MAX_THREADS; ++i) + { + MicroProfileThreadLog* pLog = S.Pool[i]; + uprintf("tlactive %p, %d. idle:%d\n", pLog, pLog->nActive, pLog->nIdleFrames); + if(pLog->nActive == 2) + { + if(pLog->nIdleFrames >= nIdleFrames) + { + nIdleFrames = pLog->nIdleFrames; + pOldest = pLog; + } + } + } + MP_ASSERT(pOldest); + MicroProfileLogReset(pOldest); + } + + MicroProfileThreadLog* pLog = 0; + if(S.nFreeListHead != -1) + { + pLog = S.Pool[S.nFreeListHead]; + MP_ASSERT(pLog->nPut.load() == 0); + MP_ASSERT(pLog->nGet.load() == 0); + S.nFreeListHead = S.Pool[S.nFreeListHead]->nFreeListNext; + } + else + { + MICROPROFILE_COUNTER_ADD("MicroProfile/ThreadLog/Allocated", 1); + MICROPROFILE_COUNTER_ADD("MicroProfile/ThreadLog/Memory", sizeof(MicroProfileThreadLog)); + pLog = MP_ALLOC_OBJECT(MicroProfileThreadLog); + MicroProfileLogClearInternal(pLog); + S.nMemUsage += sizeof(MicroProfileThreadLog); + pLog->nLogIndex = S.nNumLogs; + MP_ASSERT(S.nNumLogs < MICROPROFILE_MAX_THREADS); + S.Pool[S.nNumLogs++] = pLog; + } + int len = 0; + if(pName) + { + len = (int)strlen(pName); + int maxlen = sizeof(pLog->ThreadName) - 1; + len = len < maxlen ? len : maxlen; + memcpy(&pLog->ThreadName[0], pName, len); + } + else + { + len = snprintf(&pLog->ThreadName[0], sizeof(pLog->ThreadName) - 1, "TID:[%" PRId64 "]", (int64_t)MP_GETCURRENTTHREADID()); + } + pLog->ThreadName[len] = '\0'; + pLog->nThreadId = MP_GETCURRENTTHREADID(); + pLog->nFreeListNext = -1; + pLog->nActive = 1; + return pLog; +} + +void MicroProfileOnThreadCreate(const char* pThreadName) +{ + char Buffer[64]; + g_bUseLock = true; + MicroProfileInit(); + MP_ASSERT(MicroProfileGetThreadLog() == 0); + MicroProfileThreadLog* pLog = MicroProfileCreateThreadLog(pThreadName ? pThreadName : MicroProfileGetThreadName(Buffer)); + (void)Buffer; + MP_ASSERT(pLog); + MicroProfileSetThreadLog(pLog); +} + +void MicroProfileThreadLogGpuReset(MicroProfileThreadLogGpu* pLog) +{ + MP_ASSERT(pLog->nAllocated); + pLog->pContext = (void*)-1; + pLog->nStart = (uint32_t)-1; + pLog->nPut = 0; + pLog->nStackScope = 0; +} + +MicroProfileThreadLogGpu* MicroProfileThreadLogGpuAllocInternal() +{ + MicroProfileThreadLogGpu* pLog = 0; + for(uint32_t i = 0; i < S.nNumLogsGpu; ++i) + { + MicroProfileThreadLogGpu* pNextLog = S.PoolGpu[i]; + if(pNextLog && !pNextLog->nAllocated) + { + pLog = pNextLog; + break; + } + } + if(!pLog) + { + pLog = MP_ALLOC_OBJECT(MicroProfileThreadLogGpu); + int nLogIndex = S.nNumLogsGpu++; + MP_ASSERT(nLogIndex < MICROPROFILE_MAX_THREADS); + pLog->nId = nLogIndex; + S.PoolGpu[nLogIndex] = pLog; + } + pLog->nAllocated = 1; + MicroProfileThreadLogGpuReset(pLog); + return pLog; +} + +MicroProfileThreadLogGpu* MicroProfileThreadLogGpuAlloc() +{ + std::lock_guard Lock(MicroProfileMutex()); + return MicroProfileThreadLogGpuAllocInternal(); +} + +void MicroProfileThreadLogGpuFree(MicroProfileThreadLogGpu* pLog) +{ + MP_ASSERT(pLog->nAllocated); + pLog->nAllocated = 0; +} + +int MicroProfileGetGpuQueue(const char* pQueueName) +{ + for(uint32_t i = 0; i < MICROPROFILE_MAX_THREADS; i++) + { + MicroProfileThreadLog* pLog = S.Pool[i]; + if(pLog && pLog->nGpu && pLog->nActive && 0 == MP_STRCASECMP(pQueueName, pLog->ThreadName)) + { + return i; + } + } + MP_ASSERT(0); // call MicroProfileInitGpuQueue + return 0; +} + +MicroProfileThreadLog* MicroProfileGetGpuQueueLog(const char* pQueueName) +{ + for(uint32_t i = 0; i < MICROPROFILE_MAX_THREADS; i++) + { + MicroProfileThreadLog* pLog = S.Pool[i]; + if(pLog && pLog->nGpu && pLog->nActive && 0 == MP_STRCASECMP(pQueueName, pLog->ThreadName)) + { + return pLog; + } + } + MP_ASSERT(0); // call MicroProfileInitGpuQueue + return 0; +} + +int MicroProfileInitGpuQueue(const char* pQueueName) +{ + for(uint32_t i = 0; i < MICROPROFILE_MAX_THREADS; ++i) + { + MicroProfileThreadLog* pLog = S.Pool[i]; + if(pLog && 0 == MP_STRCASECMP(pQueueName, pLog->ThreadName)) + { + + MP_ASSERT(0); // call MicroProfileInitGpuQueue only once per CommandQueue. name must not clash with threadname + } + } + MicroProfileThreadLog* pLog = MicroProfileCreateThreadLog(pQueueName); + pLog->nGpu = 1; + pLog->nThreadId = 0; + for(uint32_t i = 0; i < MICROPROFILE_MAX_THREADS; ++i) + { + if(S.Pool[i] == pLog) + { + return i; + } + } + MP_BREAK(); + return 0; +} + +void MicroProfileFreeGpuQueue(int nQueue) +{ + MicroProfileThreadLog* pLog = S.Pool[nQueue]; + if(pLog) + { + MP_ASSERT(pLog->nActive == 1); + pLog->nActive = 2; + } +} + +MicroProfileThreadLogGpu* MicroProfileGetGlobalGpuThreadLog() +{ + return S.pGpuGlobal; +} + +MICROPROFILE_API int MicroProfileGetGlobalGpuQueue() +{ + return S.GpuQueue; +} +void MicroProfileLogClearInternal(MicroProfileThreadLog* pLog) +{ + // can't clear atomics.. + void* pStart = (void*)&pLog->Log[0]; + void* pEnd = (void*)(pLog + 1); + memset(pStart, 0, (uintptr_t)pEnd - (uintptr_t)pStart); + pLog->nPut.store(0); + pLog->nGet.store(0); +} +void MicroProfileLogReset(MicroProfileThreadLog* pLog) +{ + std::lock_guard Lock(MicroProfileMutex()); + + int32_t nLogIndex = -1; + for(int i = 0; i < MICROPROFILE_MAX_THREADS; ++i) + { + if(pLog == S.Pool[i]) + { + nLogIndex = i; + break; + } + } + MP_ASSERT(nLogIndex < MICROPROFILE_MAX_THREADS && nLogIndex > 0); + MicroProfileLogClearInternal(pLog); + pLog->nFreeListNext = S.nFreeListHead; + S.nFreeListHead = nLogIndex; + for(int i = 0; i < MICROPROFILE_MAX_FRAME_HISTORY; ++i) + { + S.Frames[i].nLogStart[nLogIndex] = 0; + } +} + +void MicroProfileOnThreadExit() +{ + MicroProfileThreadLog* pLog = MicroProfileGetThreadLog(); + if(pLog) + { + MP_ASSERT(pLog->nActive == 1); + pLog->nActive = 2; + } +} + +void MicroProfileInitThreadLog() +{ + MicroProfileOnThreadCreate(nullptr); +} + +MicroProfileToken MicroProfileFindTokenInternal(const char* pGroup, const char* pName) +{ + MicroProfileInit(); + MicroProfileScopeLock L(MicroProfileMutex()); + for(uint32_t i = 0; i < S.nTotalTimers; ++i) + { + if(!MP_STRCASECMP(pName, S.TimerInfo[i].pName) && !MP_STRCASECMP(pGroup, S.GroupInfo[S.TimerToGroup[i]].pName)) + { + return S.TimerInfo[i].nToken; + } + } + return MICROPROFILE_INVALID_TOKEN; +} +MicroProfileToken MicroProfileFindToken(const char* pGroup, const char* pName) +{ + return MicroProfileGetToken(pGroup, pName, MP_AUTO, MicroProfileTokenTypeCpu, MICROPROFILE_TIMER_FLAG_PLACEHOLDER); +} + +uint16_t MicroProfileFindGroup(const char* pGroup) +{ + for(uint32_t i = 0; i < S.nGroupCount; ++i) + { + if(!MP_STRCASECMP(pGroup, S.GroupInfo[i].pName)) + { + return i; + } + } + return UINT16_MAX; +} + +uint16_t MicroProfileGetGroup(const char* pGroup, MicroProfileTokenType Type) +{ + for(uint32_t i = 0; i < S.nGroupCount; ++i) + { + if(!MP_STRCASECMP(pGroup, S.GroupInfo[i].pName)) + { + return i; + } + } + uint16_t nGroupIndex = 0xffff; + uint32_t nLen = (uint32_t)strlen(pGroup); + if(nLen > MICROPROFILE_NAME_MAX_LEN - 1) + nLen = MICROPROFILE_NAME_MAX_LEN - 1; + memcpy(&S.GroupInfo[S.nGroupCount].pName[0], pGroup, nLen); + S.GroupInfo[S.nGroupCount].pName[nLen] = '\0'; + S.GroupInfo[S.nGroupCount].nNameLen = nLen; + S.GroupInfo[S.nGroupCount].nNumTimers = 0; + S.GroupInfo[S.nGroupCount].nGroupIndex = S.nGroupCount; + S.GroupInfo[S.nGroupCount].Type = Type; + S.GroupInfo[S.nGroupCount].nMaxTimerNameLen = 0; + S.GroupInfo[S.nGroupCount].nColor = 0x42; + S.GroupInfo[S.nGroupCount].nCategory = 0; + S.GroupInfo[S.nGroupCount].nWSNext = -2; + + uint32_t nIndex = S.nGroupCount / 32; + uint32_t nBit = S.nGroupCount % 32; + { + S.CategoryInfo[0].nGroupMask[nIndex] |= (1 << nBit); + } + if(S.nStartEnabled) + { + S.nActiveGroupsWanted[nIndex] |= (1ll << nBit); + S.nActiveGroups[nIndex] |= (1ll << nBit); + S.AnyActive = true; + } + nGroupIndex = S.nGroupCount++; + S.nGroupMask[nIndex] |= (1 << nBit); + MP_ASSERT(S.nGroupCount < MICROPROFILE_MAX_GROUPS); + return nGroupIndex; +} + +void MicroProfileRegisterGroup(const char* pGroup, const char* pCategory, uint32_t nColor) +{ + MicroProfileScopeLock L(MicroProfileMutex()); + + int nCategoryIndex = -1; + for(uint32_t i = 0; i < S.nCategoryCount; ++i) + { + if(!MP_STRCASECMP(pCategory, S.CategoryInfo[i].pName)) + { + nCategoryIndex = (int)i; + break; + } + } + if(-1 == nCategoryIndex && S.nCategoryCount < MICROPROFILE_MAX_CATEGORIES) + { + MP_ASSERT(S.CategoryInfo[S.nCategoryCount].pName[0] == '\0'); + nCategoryIndex = (int)S.nCategoryCount++; + uint32_t nLen = (uint32_t)strlen(pCategory); + if(nLen > MICROPROFILE_NAME_MAX_LEN - 1) + nLen = MICROPROFILE_NAME_MAX_LEN - 1; + memcpy(&S.CategoryInfo[nCategoryIndex].pName[0], pCategory, nLen); + S.CategoryInfo[nCategoryIndex].pName[nLen] = '\0'; + } + uint16_t nGroup = MicroProfileGetGroup(pGroup, 0 != MP_STRCASECMP(pGroup, "gpu") ? MicroProfileTokenTypeCpu : MicroProfileTokenTypeGpu); + S.GroupInfo[nGroup].nColor = nColor; + if(nCategoryIndex >= 0) + { + uint32_t nIndex = nGroup / 32; + uint32_t nBit = nGroup % 32; + nBit = (1 << nBit); + uint32_t nOldCategory = S.GroupInfo[nGroup].nCategory; + S.CategoryInfo[nOldCategory].nGroupMask[nIndex] &= ~nBit; + S.CategoryInfo[nCategoryIndex].nGroupMask[nIndex] |= nBit; + S.GroupInfo[nGroup].nCategory = nCategoryIndex; + } +} + +MicroProfileToken MicroProfileGetToken(const char* pGroup, const char* pName, uint32_t nColor, MicroProfileTokenType Type, uint32_t Flags) +{ + MicroProfileInit(); + MicroProfileScopeLock L(MicroProfileMutex()); + MicroProfileToken ret = MicroProfileFindTokenInternal(pGroup, pName); + if(ret != MICROPROFILE_INVALID_TOKEN) + { + int idx = MicroProfileGetTimerIndex(ret); + if(S.TimerInfo[idx].Flags & MICROPROFILE_TIMER_FLAG_PLACEHOLDER) + { + S.TimerInfo[idx].nColor = nColor & 0xffffff; + S.TimerInfo[idx].Flags = Flags; + S.TimerInfo[idx].Type = Type; + } + MP_ASSERT(S.TimerInfo[idx].Flags == Flags || (Flags & MICROPROFILE_TIMER_FLAG_PLACEHOLDER)); + return ret; + } + uint16_t nGroupIndex = MicroProfileGetGroup(pGroup, Type); + uint16_t nTimerIndex = (uint16_t)(S.nTotalTimers++); + MP_ASSERT(nTimerIndex < MICROPROFILE_MAX_TIMERS); + + uint32_t nBitIndex = nGroupIndex / 32; + uint32_t nBit = nGroupIndex % 32; + uint32_t nGroupMask = 1ll << nBit; + MicroProfileToken nToken = MicroProfileMakeToken(nGroupMask, (uint16_t)nBitIndex, nTimerIndex); + S.GroupInfo[nGroupIndex].nNumTimers++; + S.GroupInfo[nGroupIndex].nMaxTimerNameLen = MicroProfileMax(S.GroupInfo[nGroupIndex].nMaxTimerNameLen, (uint32_t)strlen(pName)); + MP_ASSERT(S.GroupInfo[nGroupIndex].Type == Type); // dont mix cpu & gpu timers in the same group + S.nMaxGroupSize = MicroProfileMax(S.nMaxGroupSize, S.GroupInfo[nGroupIndex].nNumTimers); + S.TimerInfo[nTimerIndex].nToken = nToken; + uint32_t nLen = (uint32_t)strlen(pName); + if(nLen > MICROPROFILE_NAME_MAX_LEN - 1) + nLen = MICROPROFILE_NAME_MAX_LEN - 1; + memcpy(&S.TimerInfo[nTimerIndex].pName, pName, nLen); + snprintf(&S.TimerInfo[nTimerIndex].pNameExt[0], sizeof(S.TimerInfo[nTimerIndex].pNameExt) - 1, "%s %s", S.GroupInfo[nGroupIndex].pName, pName); + S.TimerInfo[nTimerIndex].pName[nLen] = '\0'; + S.TimerInfo[nTimerIndex].nNameLen = nLen; + S.TimerInfo[nTimerIndex].nColor = nColor & 0xffffff; + S.TimerInfo[nTimerIndex].nGroupIndex = nGroupIndex; + S.TimerInfo[nTimerIndex].nTimerIndex = nTimerIndex; + S.TimerInfo[nTimerIndex].nWSNext = -2; + S.TimerInfo[nTimerIndex].Type = Type; + S.TimerInfo[nTimerIndex].Flags = Flags; + // printf("*** TOKEN %08d %s\\%s .. flags %08x\n", nTimerIndex, pGroup, pName, Flags); + S.TimerToGroup[nTimerIndex] = nGroupIndex; + return nToken; +} + +void MicroProfileGetTokenC(MicroProfileToken* pToken, const char* pGroup, const char* pName, uint32_t nColor, MicroProfileTokenType Type, uint32_t flags) +{ + if(*pToken == MICROPROFILE_INVALID_TOKEN) + { + MicroProfileInit(); + MicroProfileScopeLock L(MicroProfileMutex()); + if(*pToken == MICROPROFILE_INVALID_TOKEN) + { + *pToken = MicroProfileGetToken(pGroup, pName, nColor, Type, flags); + } + } +} + +const char* MicroProfileNextName(const char* pName, char* pNameOut, uint32_t* nSubNameLen) +{ + int nMaxLen = MICROPROFILE_NAME_MAX_LEN - 1; + const char* pRet = 0; + bool bDone = false; + uint32_t nChars = 0; + for(int i = 0; i < nMaxLen && !bDone; ++i) + { + char c = *pName++; + switch(c) + { + case 0: + bDone = true; + break; + case '\\': + case '/': + if(nChars) + { + bDone = true; + pRet = pName; + } + break; + default: + nChars++; + *pNameOut++ = c; + } + } + *nSubNameLen = nChars; + *pNameOut = '\0'; + return pRet; +} + +const char* MicroProfileCounterFullName(int nCounter) +{ + static char Buffer[1024]; + int nNodes[32]; + int nIndex = 0; + do + { + nNodes[nIndex++] = nCounter; + nCounter = S.CounterInfo[nCounter].nParent; + } while(nCounter >= 0); + int nOffset = 0; + while(nIndex >= 0 && nOffset < (int)sizeof(Buffer) - 2) + { + uint32_t nLen = S.CounterInfo[nNodes[nIndex]].nNameLen + nOffset; // < sizeof(Buffer)-1 + nLen = MicroProfileMin((uint32_t)(sizeof(Buffer) - 2 - nOffset), nLen); + memcpy(&Buffer[nOffset], S.CounterInfo[nNodes[nIndex]].pName, nLen); + + nOffset += S.CounterInfo[nNodes[nIndex]].nNameLen + 1; + if(nIndex) + { + Buffer[nOffset++] = '/'; + } + nIndex--; + } + return &Buffer[0]; +} + +MicroProfileToken MicroProfileCounterTokenInit(int nParent, uint32_t nFlags) +{ + MP_ASSERT(0 == (nFlags & (~MICROPROFILE_COUNTER_FLAG_TYPE_MASK))); + MicroProfileToken nResult = S.nNumCounters++; + S.CounterInfo[nResult].nParent = nParent; + S.CounterInfo[nResult].nSibling = -1; + S.CounterInfo[nResult].nFirstChild = -1; + S.CounterInfo[nResult].nFlags = nFlags; + S.CounterInfo[nResult].eFormat = MICROPROFILE_COUNTER_FORMAT_DEFAULT; + S.CounterInfo[nResult].nLimit = 0; + S.CounterInfo[nResult].ExternalAtomic = 0; + S.CounterSource[nResult].pSource = 0; + S.CounterSource[nResult].nSourceSize = 0; + S.CounterInfo[nResult].nNameLen = 0; + S.CounterInfo[nResult].pName = nullptr; + S.CounterInfo[nResult].nWSNext = -2; + if(nParent >= 0) + { + MP_ASSERT(nParent < (int)S.nNumCounters); + S.CounterInfo[nResult].nSibling = S.CounterInfo[nParent].nFirstChild; + S.CounterInfo[nResult].nLevel = S.CounterInfo[nParent].nLevel + 1; + S.CounterInfo[nParent].nFirstChild = nResult; + } + else + { + S.CounterInfo[nResult].nLevel = 0; + } + return nResult; +} +void MicroProfileCounterTokenInitName(MicroProfileToken nToken, const char* pName) +{ + MP_ASSERT(0 == S.CounterInfo[nToken].pName); + S.CounterInfo[nToken].nNameLen = (uint16_t)strlen(pName); + S.CounterInfo[nToken].pName = MicroProfileStringInternLower(pName); +} + +MicroProfileToken MicroProfileGetCounterTokenByParent(int nParent, const char* pName, uint32_t nFlags) +{ + for(uint32_t i = 0; i < S.nNumCounters; ++i) + { + if(nParent == S.CounterInfo[i].nParent && S.CounterInfo[i].pName == pName) + { + return i; + } + } + if(0 != (MICROPROFILE_COUNTER_FLAG_TOKEN_DONT_CREATE & nFlags)) + return MICROPROFILE_INVALID_TOKEN; + MicroProfileToken nResult = MicroProfileCounterTokenInit(nParent, nFlags); + MicroProfileCounterTokenInitName(nResult, pName); + return nResult; +} + +// by passing in last token/parent, and a non-changing static string, +// we can quickly return in case the parent is the same as before. +// Note that this doesn't support paths, but instead must be called once per level in the tree +// String must be preinterned. +MicroProfileToken MicroProfileCounterTokenTree(MicroProfileToken* LastToken, MicroProfileToken CurrentParent, const char* pString) +{ + MicroProfileToken Token = *LastToken; + if(Token != MICROPROFILE_INVALID_TOKEN) + { + if(S.CounterInfo[Token].pName == pString && S.CounterInfo[Token].nParent == CurrentParent) + { + return Token; + } + } + MicroProfileInit(); + MicroProfileScopeLock L(MicroProfileMutex()); + Token = MicroProfileGetCounterTokenByParent(CurrentParent, pString, 0); + *LastToken = Token; + return Token; +} + +const char* MicroProfileCounterString(const char* pString) +{ + MicroProfileInit(); + MicroProfileScopeLock L(MicroProfileMutex()); + return MicroProfileStringInternLower(pString); +} + +// Same as above, but works with non-static strings. always takes a lock, and does a search, so expect this to be not cheap +MicroProfileToken MicroProfileCounterTokenTreeDynamic(MicroProfileToken* LastToken, MicroProfileToken Parent, const char* pString) +{ + (void)LastToken; + MicroProfileInit(); + MicroProfileScopeLock L(MicroProfileMutex()); + const char* pSubNameLower = MicroProfileStringInternLower(pString); + return MicroProfileGetCounterTokenByParent(Parent, pSubNameLower, 0); +} + +MicroProfileToken MicroProfileGetCounterToken(const char* pName, uint32_t CounterFlag) +{ + MicroProfileInit(); + MicroProfileScopeLock L(MicroProfileMutex()); + char SubName[MICROPROFILE_NAME_MAX_LEN]; + MicroProfileToken nResult = MICROPROFILE_INVALID_TOKEN; + do + { + uint32_t nLen = 0; + pName = MicroProfileNextName(pName, &SubName[0], &nLen); + if(0 == nLen) + { + break; + } + const char* pSubNameLower = MicroProfileStringInternLower(SubName); + nResult = MicroProfileGetCounterTokenByParent(nResult, pSubNameLower, 0); + if(MICROPROFILE_INVALID_TOKEN == nResult) + return nResult; + + } while(pName != 0); + S.CounterInfo[nResult].nFlags |= MICROPROFILE_COUNTER_FLAG_LEAF; + +#if MICROPROFILE_COUNTER_HISTORY + if(CounterFlag & MICROPROFILE_COUNTER_FLAG_DOUBLE) + { + S.CounterInfo[nResult].nFlags |= MICROPROFILE_COUNTER_FLAG_DOUBLE; + S.dCounterMax[nResult] = -DBL_MAX; + S.dCounterMin[nResult] = DBL_MAX; + } +#endif + + MP_ASSERT((int)nResult >= 0); + return nResult; +} + +MicroProfileToken MicroProfileGetChildCounterToken(MicroProfileToken Parent, const char* pName) +{ + MP_ASSERT(NULL == strpbrk(pName, "\\/")); // delimiters not supported when manually building the tree. + return MicroProfileCounterTokenTreeDynamic(nullptr, Parent, pName); +} + +inline void MicroProfileLogPut(MicroProfileLogEntry LE, MicroProfileThreadLog* pLog) +{ + MP_ASSERT(pLog != 0); // this assert is hit if MicroProfileOnCreateThread is not called + MP_ASSERT(pLog->nActive == 1); // Dont put after calling thread exit + uint32_t nPut = pLog->nPut.load(std::memory_order_relaxed); + uint32_t nNextPos = (nPut + 1) % MICROPROFILE_BUFFER_SIZE; + uint32_t nGet = pLog->nGet.load(std::memory_order_relaxed); + uint32_t nDistance = (nGet - nNextPos) % MICROPROFILE_BUFFER_SIZE; + MP_ASSERT(nDistance < MICROPROFILE_BUFFER_SIZE); + uint32_t nStackPut = pLog->nStackPut; + if(nDistance < nStackPut + 2) + { + S.nOverflow = 100; + } + else + { + pLog->Log[nPut] = LE; + pLog->nPut.store(nNextPos, std::memory_order_release); + } +} + +inline uint64_t MicroProfileLogPutEnter(MicroProfileToken nToken_, uint64_t nTick, MicroProfileThreadLog* pLog) +{ + MP_ASSERT(pLog != 0); // this assert is hit if MicroProfileOnCreateThread is not called + MP_ASSERT(pLog->nActive == 1); // Dont put after calling thread exit + uint32_t nStackPut = pLog->nStackPut; + if(nStackPut < MICROPROFILE_STACK_MAX) + { + uint64_t LE = MicroProfileMakeLogIndex(MP_LOG_ENTER, nToken_, nTick); + uint32_t nPut = pLog->nPut.load(std::memory_order_relaxed); + uint32_t nNextPos = (nPut + 1) % MICROPROFILE_BUFFER_SIZE; + uint32_t nGet = pLog->nGet.load(std::memory_order_acquire); + uint32_t nDistance = (nGet - nNextPos) % MICROPROFILE_BUFFER_SIZE; + MP_ASSERT(nDistance < MICROPROFILE_BUFFER_SIZE); + if(nDistance < nStackPut + 4) // 2 for ring buffer, 2 for the actual entries + { + S.nOverflow = 100; + return MICROPROFILE_INVALID_TICK; + } + else + { +#ifdef MICROPROFILE_VERIFY_BALANCED + pLog->VerifyStack[nStackPut] = LE; +#endif + pLog->nStackPut = nStackPut + 1; + pLog->Log[nPut] = LE; + pLog->nPut.store(nNextPos, std::memory_order_release); + return nTick; + } + } + else + { + S.nOverflow = 100; + pLog->nStackPut = nStackPut + 1; + return MICROPROFILE_DROPPED_TICK; + } +} + +inline uint64_t MicroProfileLogPutEnterCStr(const char* pStr, uint64_t nTick, MicroProfileThreadLog* pLog) +{ + MP_ASSERT(pLog != 0); // this assert is hit if MicroProfileOnCreateThread is not called + MP_ASSERT(pLog->nActive == 1); // Dont put after calling thread exit + uint32_t nStackPut = pLog->nStackPut; + if(nStackPut < MICROPROFILE_STACK_MAX) + { + uint64_t LE = MicroProfileMakeLogIndex(MP_LOG_ENTER, ETOKEN_CSTR_PTR, nTick); + uint64_t LEStr = MicroProfileMakeLogExtendedNoDataPtr((uint64_t)pStr); + + MP_ASSERT(ETOKEN_CSTR_PTR == MicroProfileLogGetTimerIndex(LE)); + + uint32_t nPut = pLog->nPut.load(std::memory_order_relaxed); + uint32_t nNextPos = (nPut + 2) % MICROPROFILE_BUFFER_SIZE; + uint32_t nGet = pLog->nGet.load(std::memory_order_acquire); + uint32_t nDistance = (nGet - nNextPos) % MICROPROFILE_BUFFER_SIZE; + MP_ASSERT(nDistance < MICROPROFILE_BUFFER_SIZE); + if(nDistance < nStackPut + 6) // 2 for ring buffer, 4 for the actual entries + { + S.nOverflow = 100; + return MICROPROFILE_INVALID_TICK; + } + else + { + pLog->nStackPut = nStackPut + 1; + pLog->Log[nPut + 0] = LE; + pLog->Log[(nPut + 1) % MICROPROFILE_BUFFER_SIZE] = LEStr; + pLog->nPut.store(nNextPos, std::memory_order_release); + return nTick; + } + } + else + { + S.nOverflow = 100; + pLog->nStackPut = nStackPut + 1; + return MICROPROFILE_DROPPED_TICK; + } +} +inline void MicroProfileLogPutLeaveCStr(const char* pStr, uint64_t nTick, MicroProfileThreadLog* pLog) +{ + MP_ASSERT(pLog != 0); // this assert is hit if MicroProfileOnCreateThread is not called + MP_ASSERT(pLog->nActive); + MP_ASSERT(pLog->nStackPut != 0); + uint32_t nStackPut = --(pLog->nStackPut); + MP_ASSERT(nStackPut < 0xf0000000); + if(nStackPut < MICROPROFILE_STACK_MAX) + { + uint64_t LE = MicroProfileMakeLogIndex(MP_LOG_LEAVE, ETOKEN_CSTR_PTR, nTick); + uint64_t LEStr = MicroProfileMakeLogExtendedNoDataPtr((uint64_t)pStr); + MP_ASSERT(ETOKEN_CSTR_PTR == MicroProfileLogGetTimerIndex(LE)); + + uint32_t nPos = pLog->nPut.load(std::memory_order_relaxed); + uint32_t nNextPos = (nPos + 2) % MICROPROFILE_BUFFER_SIZE; + + uint32_t nGet = pLog->nGet.load(std::memory_order_acquire); + MP_ASSERT(nStackPut < MICROPROFILE_STACK_MAX); + MP_ASSERT(nNextPos != nGet); // should never happen + pLog->Log[nPos + 0] = LE; + pLog->Log[(nPos + 1) % MICROPROFILE_BUFFER_SIZE] = LEStr; + + pLog->nPut.store(nNextPos, std::memory_order_release); + } +} + +inline void MicroProfileLogPutLeave(MicroProfileToken nToken_, uint64_t nTick, MicroProfileThreadLog* pLog) +{ + MP_ASSERT(pLog != 0); // this assert is hit if MicroProfileOnCreateThread is not called + MP_ASSERT(pLog->nActive); + MP_ASSERT(pLog->nStackPut != 0); + uint32_t nStackPut = --(pLog->nStackPut); + if(nStackPut < MICROPROFILE_STACK_MAX) + { + uint64_t LE = MicroProfileMakeLogIndex(MP_LOG_LEAVE, nToken_, nTick); + uint32_t nPos = pLog->nPut.load(std::memory_order_relaxed); + uint32_t nNextPos = (nPos + 1) % MICROPROFILE_BUFFER_SIZE; + + uint32_t nGet = pLog->nGet.load(std::memory_order_acquire); + MP_ASSERT(nStackPut < MICROPROFILE_STACK_MAX); + MP_ASSERT(nNextPos != nGet); // should never happen + +#ifdef MICROPROFILE_VERIFY_BALANCED + // verify what we pop is what we push. + uint64_t Pushed = pLog->VerifyStack[nStackPut]; + uint64_t TimerPopped = MicroProfileLogGetTimerIndex(LE); + uint64_t TimerOnStack = MicroProfileLogGetTimerIndex(Pushed); + if(TimerPopped != TimerOnStack) + { + uprintf("Push/Pop Mismatch %s vs %s\n", S.TimerInfo[TimerPopped].pName, S.TimerInfo[TimerOnStack].pName); + MP_ASSERT(0); + } +#endif + + pLog->Log[nPos] = LE; + pLog->nPut.store(nNextPos, std::memory_order_release); + } +} + +inline void MicroProfileLogPut(MicroProfileToken nToken_, uint64_t nTick, uint64_t nBegin, MicroProfileThreadLog* pLog) +{ + MicroProfileLogPut(MicroProfileMakeLogIndex(nBegin, nToken_, nTick), pLog); +} + +inline void MicroProfileLogPutGpu(MicroProfileLogEntry LE, MicroProfileThreadLogGpu* pLog) +{ + uint32_t nPos = pLog->nPut; + if(nPos < MICROPROFILE_GPU_BUFFER_SIZE) + { + pLog->Log[nPos] = LE; + pLog->nPut = nPos + 1; + } +} + +inline void MicroProfileLogPutGpuTimer(MicroProfileToken nToken_, uint64_t nTick, uint64_t nBegin, MicroProfileThreadLogGpu* pLog) +{ + MicroProfileLogPutGpu(MicroProfileMakeLogIndex(nBegin, nToken_, nTick), pLog); +} + +inline void MicroProfileLogPutGpuExtended(EMicroProfileTokenExtended eTokenExt, uint32_t nDataSizeQWords, uint32_t nPayload, MicroProfileThreadLogGpu* pLog) +{ + MicroProfileLogEntry LE = MicroProfileMakeLogExtended(eTokenExt, nDataSizeQWords, nPayload); + MicroProfileLogPutGpu(LE, pLog); +} + +inline void MicroProfileLogPutGpuExtendedNoData(EMicroProfileTokenExtended eTokenExt, uint64_t nPayload, MicroProfileThreadLogGpu* pLog) +{ + MicroProfileLogEntry LE = MicroProfileMakeLogExtendedNoData(eTokenExt, nPayload); + MicroProfileLogPutGpu(LE, pLog); +} + +uint32_t MicroProfileGroupTokenActive(MicroProfileToken nToken_) +{ + uint32_t nMask = MicroProfileGetGroupMask(nToken_); + uint32_t nIndex = MicroProfileGetGroupMaskIndex(nToken_); + return 0 != (S.nActiveGroups[nIndex] & nMask); +} + +uint64_t MicroProfileEnterInternal(MicroProfileToken nToken_) +{ + if(MicroProfileGroupTokenActive(nToken_)) + { + uint64_t nTick = MP_TICK(); + if(MICROPROFILE_PLATFORM_MARKERS_ENABLED) + { + uint32_t idx = MicroProfileGetTimerIndex(nToken_); + MicroProfileTimerInfo& TI = S.TimerInfo[idx]; + MICROPROFILE_PLATFORM_MARKER_BEGIN(TI.nColor, TI.pNameExt); + return nTick; + } + else + { + return MicroProfileLogPutEnter(nToken_, nTick, MicroProfileGetThreadLog2()); + } + } + return MICROPROFILE_INVALID_TICK; +} + +uint64_t MicroProfileEnterInternalCStr(const char* pStr) +{ + if(S.AnyActive) + { + uint64_t nTick = MP_TICK(); + if(MICROPROFILE_PLATFORM_MARKERS_ENABLED) + { + MICROPROFILE_PLATFORM_MARKER_BEGIN(0, pStr); + return nTick; + } + else + { + return MicroProfileLogPutEnterCStr(pStr, nTick, MicroProfileGetThreadLog2()); + } + } + return MICROPROFILE_INVALID_TICK; +} + +void MicroProfileTimelineLeave(uint32_t id) +{ + if(!id) + return; + std::lock_guard Lock(MicroProfileTimelineMutex()); + MicroProfileThreadLog* pLog = &S.TimelineLog; + uint32_t nPut = pLog->nPut.load(std::memory_order_relaxed); + uint32_t nNextPos = (nPut + 1) % MICROPROFILE_BUFFER_SIZE; + uint32_t nGet = pLog->nGet.load(std::memory_order_acquire); + uint32_t nDistance = (nGet - nNextPos) % MICROPROFILE_BUFFER_SIZE; + + { + uint32_t nFrameStart = S.TimelineTokenFrameEnter[id % MICROPROFILE_TIMELINE_MAX_TOKENS]; + uint32_t nFrameCurrent = S.nFrameCurrent; + if(nFrameCurrent < nFrameStart) + nFrameCurrent += MICROPROFILE_MAX_FRAME_HISTORY; + uint32_t nFrameDistance = (nFrameCurrent - nFrameStart) % MICROPROFILE_MAX_FRAME_HISTORY; + + S.TimelineTokenFrameEnter[id % MICROPROFILE_TIMELINE_MAX_TOKENS] = MICROPROFILE_INVALID_FRAME; + S.TimelineTokenFrameLeave[id % MICROPROFILE_TIMELINE_MAX_TOKENS] = nFrameCurrent; + + S.TimelineToken[id % MICROPROFILE_TIMELINE_MAX_TOKENS] = 0; + S.nTimelineFrameMax = MicroProfileMax(S.nTimelineFrameMax, nFrameDistance); + } + + if(nDistance < 2 + 4) + { + S.nOverflow = 100; + } + else + { + uint64_t LEEnter = MicroProfileMakeLogIndex(MP_LOG_LEAVE, ETOKEN_CUSTOM_NAME, MP_TICK()); + uint64_t LEId = MicroProfileMakeLogExtended(ETOKEN_CUSTOM_ID, 0, id); + + pLog->Log[nPut++] = LEEnter; + nPut %= MICROPROFILE_BUFFER_SIZE; + pLog->Log[nPut++] = LEId; + nPut %= MICROPROFILE_BUFFER_SIZE; + pLog->nPut.store(nPut); + } +} + +void MicroProfileTimelineEnterStatic(uint32_t nColor, const char* pStr) +{ + if(!S.AnyActive) + return; + uint32_t nToken = MicroProfileTimelineEnterInternal(nColor, pStr, (uint32_t)strlen(pStr), true); + (void)nToken; +} +void MicroProfileTimelineLeaveStatic(const char* pStr) +{ + if(!S.AnyActive) + return; + + for(uint32_t i = 0; i < MICROPROFILE_TIMELINE_MAX_TOKENS; ++i) + { + if(S.TimelineTokenStaticString[i] && 0 == MP_STRCASECMP(pStr, S.TimelineTokenStaticString[i])) + { + MicroProfileTimelineLeave(S.TimelineToken[i]); + } + } +} + +uint32_t MicroProfileTimelineEnterInternal(uint32_t nColor, const char* pStr, uint32_t nStrLen, int bIsStaticString) +{ + if(!S.AnyActive) + return 0; + std::lock_guard Lock(MicroProfileTimelineMutex()); + MicroProfileThreadLog* pLog = &S.TimelineLog; + MP_ASSERT(pStr[nStrLen] == '\0'); + nStrLen += 1; + uint32_t nStringQwords = MicroProfileGetQWordSize(nStrLen); + uint32_t nNumMessages = nStringQwords; + + uint32_t nPut = pLog->nPut.load(std::memory_order_relaxed); + uint32_t nNextPos = (nPut + 1) % MICROPROFILE_BUFFER_SIZE; + uint32_t nGet = pLog->nGet.load(std::memory_order_acquire); + uint32_t nDistance = (nGet - nNextPos) % MICROPROFILE_BUFFER_SIZE; + + if(nDistance < nNumMessages + 7) + { + S.nOverflow = 100; + return 0; + } + else + { + + uint32_t token = pLog->nCustomId; + uint32_t nFrameLeave = S.TimelineTokenFrameLeave[token % MICROPROFILE_TIMELINE_MAX_TOKENS]; + uint32_t nFrameEnter = S.TimelineTokenFrameEnter[token % MICROPROFILE_TIMELINE_MAX_TOKENS]; + uint32_t nCounter = 0; + uint32_t nFrameCurrent = S.nFrameCurrent; + { + + /// dont reuse tokens until their leave command has been dead for the maximum amount of frames we can generate a capture for. + while(token == 0 || nFrameEnter != MICROPROFILE_INVALID_FRAME || (nFrameCurrent - nFrameLeave < MICROPROFILE_MAX_FRAME_HISTORY + 3 && nFrameLeave != MICROPROFILE_INVALID_FRAME)) + { + token = (uint32_t)pLog->nCustomId++; + nFrameLeave = S.TimelineTokenFrameLeave[token % MICROPROFILE_TIMELINE_MAX_TOKENS]; + nFrameEnter = S.TimelineTokenFrameEnter[token % MICROPROFILE_TIMELINE_MAX_TOKENS]; + if(++nCounter == MICROPROFILE_TIMELINE_MAX_TOKENS) + { + // MP_BREAK(); + return 0; + } + } + S.TimelineTokenFrameEnter[token % MICROPROFILE_TIMELINE_MAX_TOKENS] = S.nFrameCurrent; + } + if(bIsStaticString) + { + S.TimelineTokenStaticString[token % MICROPROFILE_TIMELINE_MAX_TOKENS] = pStr; + } + else + { + S.TimelineTokenStaticString[token % MICROPROFILE_TIMELINE_MAX_TOKENS] = nullptr; + } + S.TimelineToken[token % MICROPROFILE_TIMELINE_MAX_TOKENS] = token; + + uint64_t LEEnter = MicroProfileMakeLogIndex(MP_LOG_ENTER, ETOKEN_CUSTOM_NAME, MP_TICK()); + uint64_t LEColor = MicroProfileMakeLogExtended(ETOKEN_CUSTOM_COLOR, 0, nColor); + uint64_t LEId = MicroProfileMakeLogExtended(ETOKEN_CUSTOM_ID, nStringQwords, token); + + pLog->Log[nPut++] = LEEnter; + nPut %= MICROPROFILE_BUFFER_SIZE; + pLog->Log[nPut++] = LEColor; + nPut %= MICROPROFILE_BUFFER_SIZE; + pLog->Log[nPut++] = LEId; + nPut %= MICROPROFILE_BUFFER_SIZE; + + // copy if we dont wrap + if(nPut + nStringQwords <= MICROPROFILE_BUFFER_SIZE) + { + memcpy(&pLog->Log[nPut], pStr, nStrLen + 1); + nPut += nStringQwords; + } + else + { + int nCharsLeft = (int)nStrLen; + while(nCharsLeft > 0) + { + int nCount = MicroProfileMin(nCharsLeft, 8); + memcpy(&pLog->Log[nPut++], pStr, nCount); + // uint64_t LEPayload = MicroProfileMakeLogPayload(pStr, nCount); + // pLog->Log[nPut++] = LEPayload; nPut %= MICROPROFILE_BUFFER_SIZE; + pStr += nCount; + nCharsLeft -= nCount; + } + } + pLog->nPut.store(nPut); + return token; + } +} + +uint32_t MicroProfileTimelineEnter(uint32_t nColor, const char* pStr) +{ + return MicroProfileTimelineEnterInternal(nColor, pStr, (uint32_t)strlen(pStr), false); +} + +uint32_t MicroProfileTimelineEnterf(uint32_t nColor, const char* pStr, ...) +{ + if(!S.AnyActive) + return 0; + char buffer[MICROPROFILE_MAX_STRING + 1]; + va_list args; + va_start(args, pStr); +#ifdef _WIN32 + size_t size = vsprintf_s(buffer, pStr, args); +#else + size_t size = vsnprintf(buffer, sizeof(buffer) - 1, pStr, args); +#endif + va_end(args); + MP_ASSERT(size < sizeof(buffer)); + buffer[size] = '\0'; + return MicroProfileTimelineEnterInternal(nColor, buffer, (uint32_t)size, false); +} + +void MicroProfileLocalCounterAdd(int64_t* pCounter, int64_t nCount) +{ + *pCounter += nCount; +} +int64_t MicroProfileLocalCounterSet(int64_t* pCounter, int64_t nCount) +{ + int64_t r = *pCounter; + *pCounter = nCount; + return r; +} + +void MicroProfileLocalCounterAddAtomic(MicroProfileToken nToken, int64_t nCount) +{ + std::atomic* pCounter = &S.CounterInfo[nToken].ExternalAtomic; + pCounter->fetch_add(nCount); +} +int64_t MicroProfileLocalCounterSetAtomic(MicroProfileToken nToken, int64_t nCount) +{ + + std::atomic* pCounter = &S.CounterInfo[nToken].ExternalAtomic; + return pCounter->exchange(nCount); +} + +void MicroProfileCounterAdd(MicroProfileToken nToken, int64_t nCount) +{ + MP_ASSERT(nToken < S.nNumCounters); + S.Counters[nToken].fetch_add(nCount); +} +void MicroProfileCounterSet(MicroProfileToken nToken, int64_t nCount) +{ + MP_ASSERT(nToken < S.nNumCounters); + S.Counters[nToken].store(nCount); +} +int64_t MicroProfileCounterGet(MicroProfileToken nToken) +{ + MP_ASSERT(nToken < S.nNumCounters); + return S.Counters[nToken].load(); +} + +void MicroProfileCounterSetDouble(MicroProfileToken nToken, double nCount) +{ + MP_ASSERT(nToken < S.nNumCounters); + MP_ASSERT((S.CounterInfo[nToken].nFlags & MICROPROFILE_COUNTER_FLAG_DOUBLE) == MICROPROFILE_COUNTER_FLAG_DOUBLE); + S.CountersDouble[nToken].store(nCount); +} +double MicroProfileCounterGetDouble(MicroProfileToken nToken) +{ + MP_ASSERT(nToken < S.nNumCounters); + MP_ASSERT((S.CounterInfo[nToken].nFlags & MICROPROFILE_COUNTER_FLAG_DOUBLE) == MICROPROFILE_COUNTER_FLAG_DOUBLE); + return S.CountersDouble[nToken].load(); +} +void MicroProfileCounterSetLimit(MicroProfileToken nToken, int64_t nCount) +{ + MP_ASSERT(nToken < S.nNumCounters); + S.CounterInfo[nToken].nLimit = nCount; +} + +void MicroProfileCounterSetLimitDouble(MicroProfileToken nToken, double dCount) +{ + MP_ASSERT(nToken < S.nNumCounters); + MP_ASSERT((S.CounterInfo[nToken].nFlags & MICROPROFILE_COUNTER_FLAG_DOUBLE) == MICROPROFILE_COUNTER_FLAG_DOUBLE); + S.CounterInfo[nToken].dLimit = dCount; +} + +void MicroProfileCounterConfigToken(MicroProfileToken nToken, uint32_t eFormat, int64_t nLimit, uint32_t nFlags) +{ + S.CounterInfo[nToken].eFormat = (MicroProfileCounterFormat)eFormat; + S.CounterInfo[nToken].nLimit = nLimit; + S.CounterInfo[nToken].nFlags |= (nFlags & ~MICROPROFILE_COUNTER_FLAG_INTERNAL_MASK); +} + +void MicroProfileCounterConfig(const char* pName, uint32_t eFormat, int64_t nLimit, uint32_t nFlags) +{ + MicroProfileToken nToken = MicroProfileGetCounterToken(pName, 0); + MicroProfileCounterConfigToken(nToken, eFormat, nLimit, nFlags); +} + +void MicroProfileCounterSetPtr(const char* pCounterName, void* pSource, uint32_t nSize) +{ + MicroProfileToken nToken = MicroProfileGetCounterToken(pCounterName, 0); + S.CounterSource[nToken].pSource = pSource; + S.CounterSource[nToken].nSourceSize = nSize; +} + +inline void MicroProfileFetchCounter(uint32_t i) +{ + MP_ASSERT(0 == S.CounterSource[i].nSourceSize || (S.CounterInfo[i].nFlags & MICROPROFILE_COUNTER_FLAG_DOUBLE) == MICROPROFILE_COUNTER_FLAG_DOUBLE); + switch(S.CounterSource[i].nSourceSize) + { + case sizeof(int32_t): + S.Counters[i] = *(int32_t*)S.CounterSource[i].pSource; + break; + case sizeof(int64_t): + S.Counters[i] = *(int64_t*)S.CounterSource[i].pSource; + break; + default: + break; + } +} +void MicroProfileCounterFetchCounters() +{ + for(uint32_t i = 0; i < S.nNumCounters; ++i) + { + MicroProfileFetchCounter(i); + } +} + +void MicroProfileLeaveInternal(MicroProfileToken nToken_, uint64_t nTickStart) +{ + if(MICROPROFILE_INVALID_TICK != nTickStart) + { + if(MICROPROFILE_PLATFORM_MARKERS_ENABLED) + { + MICROPROFILE_PLATFORM_MARKER_END(); + } + else + { + uint64_t nTick = MP_TICK(); + MicroProfileThreadLog* pLog = MicroProfileGetThreadLog2(); + MicroProfileLogPutLeave(nToken_, nTick, pLog); + } + } +} + +void MicroProfileLeaveInternalCStr(const char* pStr, uint64_t nTickStart) +{ + if(MICROPROFILE_INVALID_TICK != nTickStart) + { + if(MICROPROFILE_PLATFORM_MARKERS_ENABLED) + { + MICROPROFILE_PLATFORM_MARKER_END(); + } + else + { + uint64_t nTick = MP_TICK(); + MicroProfileThreadLog* pLog = MicroProfileGetThreadLog2(); + MicroProfileLogPutLeaveCStr(pStr, nTick, pLog); + } + } +} + +void MicroProfileEnter(MicroProfileToken nToken) +{ + MicroProfileThreadLog* pLog = MicroProfileGetThreadLog2(); + MP_ASSERT(pLog->nStackScope < MICROPROFILE_STACK_MAX); // if youre hitting this assert you probably have mismatched _ENTER/_LEAVE markers + uint32_t nStackPos = pLog->nStackScope++; + if(nStackPos < MICROPROFILE_STACK_MAX) + { + MicroProfileScopeStateC* pScopeState = &pLog->ScopeState[nStackPos]; + pScopeState->Token = nToken; + pScopeState->nTick = MicroProfileEnterInternal(nToken); + } + else + { + S.nOverflow = 100; + } +} +void MicroProfileLeave() +{ + MicroProfileThreadLog* pLog = MicroProfileGetThreadLog2(); + MP_ASSERT(pLog->nStackScope > 0); // if youre hitting this assert you probably have mismatched _ENTER/_LEAVE markers + uint32_t nStackPos = --pLog->nStackScope; + if(nStackPos < MICROPROFILE_STACK_MAX) + { + MicroProfileScopeStateC* pScopeState = &pLog->ScopeState[nStackPos]; + MicroProfileLeaveInternal(pScopeState->Token, pScopeState->nTick); + } + else + { + S.nOverflow = 100; + } +} + +void MicroProfileEnterGpu(MicroProfileToken nToken, MicroProfileThreadLogGpu* pLog) +{ + // MP_ASSERT(pLog->nStackScope < MICROPROFILE_STACK_MAX); // if youre hitting this assert you probably have mismatched _ENTER/_LEAVE markers + uint32_t nStackPos = pLog->nStackScope++; + if(nStackPos < MICROPROFILE_STACK_MAX) + { + MicroProfileScopeStateC* pScopeState = &pLog->ScopeState[nStackPos]; + pScopeState->Token = nToken; + pScopeState->nTick = MicroProfileGpuEnterInternal(pLog, nToken); + } + else + { + S.nOverflow = 100; + } +} +void MicroProfileLeaveGpu(MicroProfileThreadLogGpu* pLog) +{ + uint32_t nStackPos = --pLog->nStackScope; + if(nStackPos < MICROPROFILE_STACK_MAX) + { + MicroProfileScopeStateC* pScopeState = &pLog->ScopeState[nStackPos]; + MicroProfileGpuLeaveInternal(pLog, pScopeState->Token, pScopeState->nTick); + } +} + +void MicroProfileGpuBegin(void* pContext, MicroProfileThreadLogGpu* pLog) +{ + MP_ASSERT(pLog->pContext == (void*)-1); // dont call begin without calling end + MP_ASSERT(pLog->nStart == (uint32_t)-1); + MP_ASSERT(pContext != (void*)-1); + + pLog->pContext = pContext; + pLog->nStart = pLog->nPut; + MicroProfileLogPutGpu(0, pLog); +} + +void MicroProfileGpuSetContext(void* pContext, MicroProfileThreadLogGpu* pLog) +{ + MP_ASSERT(pLog->pContext != (void*)-1); // dont call begin without calling end + MP_ASSERT(pLog->nStart != (uint32_t)-1); + pLog->pContext = pContext; +} + +uint64_t MicroProfileGpuEnd(MicroProfileThreadLogGpu* pLog) +{ + uint64_t nStart = pLog->nStart; + uint32_t nEnd = pLog->nPut; + uint64_t nId = pLog->nId; + if(nStart < MICROPROFILE_GPU_BUFFER_SIZE) + { + pLog->Log[nStart] = nEnd - nStart - 1; + } + pLog->pContext = (void*)-1; + pLog->nStart = (uint32_t)-1; + return nStart | (nId << 32); +} + +void MicroProfileGpuSubmit(int nQueue, uint64_t nWork) +{ + MP_ASSERT(nQueue >= 0 && nQueue < MICROPROFILE_MAX_THREADS); + MICROPROFILE_SCOPE(g_MicroProfileGpuSubmit); + uint32_t nStart = (uint32_t)nWork; + uint32_t nThreadLog = uint32_t(nWork >> 32); + + MicroProfileThreadLog* pQueueLog = S.Pool[nQueue]; + MP_ASSERT(nQueue < MICROPROFILE_MAX_THREADS); + MicroProfileThreadLogGpu* pGpuLog = S.PoolGpu[nThreadLog]; + MP_ASSERT(pGpuLog); + + int64_t nCount = 0; + if(nStart < MICROPROFILE_GPU_BUFFER_SIZE) + { + nCount = pGpuLog->Log[nStart]; + } + MP_ASSERT(nCount < (int64_t)MICROPROFILE_GPU_BUFFER_SIZE); + nStart++; + for(int32_t i = 0; i < nCount; ++i) + { + MP_ASSERT(nStart < MICROPROFILE_GPU_BUFFER_SIZE); + MicroProfileLogEntry LE = pGpuLog->Log[nStart++]; + MicroProfileLogPut(LE, pQueueLog); + } +} + +uint64_t MicroProfileGpuEnterInternal(MicroProfileThreadLogGpu* pGpuLog, MicroProfileToken nToken_) +{ + if(MicroProfileGroupTokenActive(nToken_)) + { + if(!MicroProfileGetThreadLog()) + { + MicroProfileInitThreadLog(); + } + + MP_ASSERT(pGpuLog->pContext != (void*)-1); // must be called between GpuBegin/GpuEnd + uint64_t nTimer = MicroProfileGpuInsertTimeStamp(pGpuLog->pContext); + MicroProfileLogPutGpuTimer(nToken_, nTimer, MP_LOG_ENTER, pGpuLog); + MicroProfileThreadLog* pLog = MicroProfileGetThreadLog(); + + MicroProfileLogPutGpuExtendedNoData(ETOKEN_GPU_CPU_TIMESTAMP, MP_TICK(), pGpuLog); + MicroProfileLogPutGpuExtendedNoData(ETOKEN_GPU_CPU_SOURCE_THREAD, pLog->nLogIndex, pGpuLog); + // MP_ASSERT(pGpuLog->pContext != (void*)-1); // must be called between GpuBegin/GpuEnd + // uint64_t nTimer = MicroProfileGpuInsertTimeStamp(pGpuLog->pContext); + // MicroProfileLogPutGpu(nToken_, nTimer, MP_LOG_ENTER, pGpuLog); + // MicroProfileThreadLog* pLog = MicroProfileGetThreadLog(); + // MicroProfileLogPutGpu(ETOKEN_GPU_CPU_TIMESTAMP, MP_TICK(), MP_LOG_EXTRA_DATA, pGpuLog); + // MicroProfileLogPutGpu(ETOKEN_GPU_CPU_SOURCE_THREAD, pLog->nLogIndex, MP_LOG_EXTRA_DATA, pGpuLog); + + return 1; + } + return 0; +} + +uint64_t MicroProfileGpuEnterInternalCStr(MicroProfileThreadLogGpu* pGpuLog, const char* pStr) +{ + MP_BREAK(); // not implemented + return 0; + // if(S.AnyGpuActive) + // { + // if(!MicroProfileGetThreadLog()) + // { + // MicroProfileInitThreadLog(); + // } + + // MP_ASSERT(pGpuLog->pContext != (void*)-1); // must be called between GpuBegin/GpuEnd + // uint64_t nTimer = MicroProfileGpuInsertTimeStamp(pGpuLog->pContext); + // MicroProfileLogPutGpuTimer(nToken_, nTimer, MP_LOG_ENTER, pGpuLog); + // MicroProfileThreadLog* pLog = MicroProfileGetThreadLog(); + + // MicroProfileLogPutGpuExtendedNoData(ETOKEN_GPU_CPU_TIMESTAMP, MP_TICK(), pGpuLog); + // MicroProfileLogPutGpuExtendedNoData(ETOKEN_GPU_CPU_SOURCE_THREAD, pLog->nLogIndex, pGpuLog); + // // MP_ASSERT(pGpuLog->pContext != (void*)-1); // must be called between GpuBegin/GpuEnd + // // uint64_t nTimer = MicroProfileGpuInsertTimeStamp(pGpuLog->pContext); + // // MicroProfileLogPutGpu(nToken_, nTimer, MP_LOG_ENTER, pGpuLog); + // // MicroProfileThreadLog* pLog = MicroProfileGetThreadLog(); + // // MicroProfileLogPutGpu(ETOKEN_GPU_CPU_TIMESTAMP, MP_TICK(), MP_LOG_EXTRA_DATA, pGpuLog); + // // MicroProfileLogPutGpu(ETOKEN_GPU_CPU_SOURCE_THREAD, pLog->nLogIndex, MP_LOG_EXTRA_DATA, pGpuLog); + + // return 1; + // } + // return 0; +} + +void MicroProfileGpuLeaveInternal(MicroProfileThreadLogGpu* pGpuLog, MicroProfileToken nToken_, uint64_t nTickStart) +{ + if(nTickStart) + { + if(!MicroProfileGetThreadLog()) + { + MicroProfileInitThreadLog(); + } + + MP_ASSERT(pGpuLog->pContext != (void*)-1); // must be called between GpuBegin/GpuEnd + uint64_t nTimer = MicroProfileGpuInsertTimeStamp(pGpuLog->pContext); + MicroProfileLogPutGpuTimer(nToken_, nTimer, MP_LOG_LEAVE, pGpuLog); + MicroProfileThreadLog* pLog = MicroProfileGetThreadLog(); + MicroProfileLogPutGpuExtendedNoData(ETOKEN_GPU_CPU_TIMESTAMP, MP_TICK(), pGpuLog); + MicroProfileLogPutGpuExtendedNoData(ETOKEN_GPU_CPU_SOURCE_THREAD, pLog->nLogIndex, pGpuLog); + } +} + +void MicroProfileGpuLeaveInternalCStr(MicroProfileThreadLogGpu* pGpuLog, uint64_t nTickStart) +{ + MP_BREAK(); // not implemented + return; + // if(nTickStart) + // { + // if(!MicroProfileGetThreadLog()) + // { + // MicroProfileInitThreadLog(); + // } + + // MP_ASSERT(pGpuLog->pContext != (void*)-1); // must be called between GpuBegin/GpuEnd + // uint64_t nTimer = MicroProfileGpuInsertTimeStamp(pGpuLog->pContext); + // MicroProfileLogPutGpuTimer(nToken_, nTimer, MP_LOG_LEAVE, pGpuLog); + // MicroProfileThreadLog* pLog = MicroProfileGetThreadLog(); + // MicroProfileLogPutGpuExtendedNoData(ETOKEN_GPU_CPU_TIMESTAMP, MP_TICK(), pGpuLog); + // MicroProfileLogPutGpuExtendedNoData(ETOKEN_GPU_CPU_SOURCE_THREAD, pLog->nLogIndex, pGpuLog); + // } +} + +void MicroProfileContextSwitchPut(MicroProfileContextSwitch* pContextSwitch) +{ + if(0 == S.nPauseTicks || (S.nPauseTicks - pContextSwitch->nTicks) > 0) + { + uint32_t nPut = S.nContextSwitchPut; + S.ContextSwitch[nPut] = *pContextSwitch; + S.nContextSwitchPut = (S.nContextSwitchPut + 1) % MICROPROFILE_CONTEXT_SWITCH_BUFFER_SIZE; + // if(S.nContextSwitchPut < nPut) + //{ + // float fMsDelay = MicroProfileTickToMsMultiplierCpu() * ((int64_t)S.nFlipStartTick - pContextSwitch->nTicks); + // uprintf("context switch wrap .. %7.3fms\n", fMsDelay); + // } + // if(S.nContextSwitchPut % 1024 == 0) + //{ + // float fMsDelay = MicroProfileTickToMsMultiplierCpu() * ((int64_t)S.nFlipStartTick - pContextSwitch->nTicks); + // uprintf("cswitch tick %x ... %7.3fms\n", S.nContextSwitchPut, fMsDelay); + // } + S.nContextSwitchLastPushed = pContextSwitch->nTicks; + } + else + { + S.nContextSwitchStalledTick = MP_TICK(); + } +} + +void MicroProfileGetRange(uint32_t nPut, uint32_t nGet, uint32_t nRange[2][2]) +{ + if(nPut > nGet) + { + nRange[0][0] = nGet; + nRange[0][1] = nPut; + nRange[1][0] = nRange[1][1] = 0; + } + else if(nPut != nGet) + { + MP_ASSERT(nGet != MICROPROFILE_BUFFER_SIZE); + uint32_t nCountEnd = MICROPROFILE_BUFFER_SIZE - nGet; + nRange[0][0] = nGet; + nRange[0][1] = nGet + nCountEnd; + nRange[1][0] = 0; + nRange[1][1] = nPut; + } +} + +void MicroProfileToggleFrozen() +{ + S.nFrozen = !S.nFrozen; +} + +int MicroProfileIsFrozen() +{ + return S.nFrozen != 0 ? 1 : 0; +} +int MicroProfileEnabled() +{ + return MicroProfileAnyGroupActive(); +} +void* MicroProfileAllocInternal(size_t nSize, size_t nAlign) +{ + nAlign = MicroProfileMax(4 * sizeof(uint32_t), nAlign); + nSize += nAlign; + intptr_t nPtr = (intptr_t)MICROPROFILE_ALLOC(nSize, nAlign); + nPtr += nAlign; + uint32_t* pVal = (uint32_t*)nPtr; + MP_ASSERT(nSize < 0xffffffff); + MP_ASSERT(nAlign < 0xffffffff); + pVal[-1] = (uint32_t)nSize; + pVal[-2] = (uint32_t)nAlign; + pVal[-3] = (uint32_t)0x28586813; + MicroProfileCounterAdd(S.CounterToken_Alloc_Memory, nSize); + MicroProfileCounterAdd(S.CounterToken_Alloc_Count, 1); + return (void*)nPtr; +} +void MicroProfileFreeInternal(void* pPtr) +{ + intptr_t p = (intptr_t)pPtr; + uint32_t* p4 = (uint32_t*)pPtr; + uint32_t nSize = p4[-1]; + uint32_t nAlign = p4[-2]; + uint32_t nMagic = p4[-3]; + MP_ASSERT(nMagic == 0x28586813); + MICROPROFILE_FREE((void*)(p - nAlign)); + MicroProfileCounterAdd(S.CounterToken_Alloc_Memory, -(int)nSize); + MicroProfileCounterAdd(S.CounterToken_Alloc_Count, -1); +} +void* MicroProfileReallocInternal(void* pPtr, size_t nSize) +{ + intptr_t p = (intptr_t)pPtr; + uint32_t nAlignBase; + + if(p) + { + uint32_t* p4 = (uint32_t*)pPtr; + uint32_t nSizeBase = p4[-1]; + nAlignBase = p4[-2]; + uint32_t nMagicBase = p4[-3]; + MP_ASSERT(nMagicBase == 0x28586813); + + MicroProfileCounterAdd(S.CounterToken_Alloc_Memory, nSize - nSizeBase); + } + else + { + nAlignBase = 4 * sizeof(uint32_t); + MicroProfileCounterAdd(S.CounterToken_Alloc_Memory, nSize + nAlignBase); + MicroProfileCounterAdd(S.CounterToken_Alloc_Count, 1); + } + + nSize += nAlignBase; + MP_ASSERT(nAlignBase >= 4 * sizeof(uint32_t)); + if(p) + { + p = (intptr_t)MICROPROFILE_REALLOC((void*)(p - nAlignBase), nSize); + } + else + { + p = (intptr_t)MICROPROFILE_REALLOC((void*)(p), nSize); + } + p += nAlignBase; + uint32_t* pVal = (uint32_t*)p; + MP_ASSERT(nSize < 0xffffffff); + MP_ASSERT(nAlignBase < 0xffffffff); + pVal[-1] = (uint32_t)nSize; + pVal[-2] = (uint32_t)nAlignBase; + pVal[-3] = (uint32_t)0x28586813; + return (void*)p; +} + +static void MicroProfileFlipEnabled() +{ + if(S.nFrozen) + { + memset(S.nActiveGroups, 0, sizeof(S.nActiveGroups)); + S.AnyActive = false; + } + else + { + bool AnyActive = false; + for(uint32_t i = 0; i < MICROPROFILE_MAX_GROUP_INTS; ++i) + { + uint32_t nNew = S.nActiveGroupsWanted[i]; + nNew |= S.nForceGroups[i]; + if(nNew) + AnyActive = true; + if(S.nActiveGroups[i] != nNew) + { + S.nActiveGroups[i] = nNew; + } + } + S.AnyActive = AnyActive; + } +} + +void MicroProfileFlip(void* pContext, uint32_t FlipFlag) +{ + MicroProfileFlip_CB(pContext, nullptr, FlipFlag); +} + +#define MICROPROFILE_TICK_VALIDATE_FRAME_TIME 0 + +void MicroProfileFlip_CB(void* pContext, MicroProfileOnFreeze FreezeCB, uint32_t FlipFlag) +{ + MICROPROFILE_COUNTER_LOCAL_UPDATE_SET_ATOMIC(g_MicroProfileBytesPerFlip); +#if 0 + //verify LogEntry wraps correctly + MicroProfileLogEntry c = MP_LOG_TICK_MASK-5000; + for(int i = 0; i < 10000; ++i, c += 1) + { + MicroProfileLogEntry l2 = (c+2500) & MP_LOG_TICK_MASK; + MP_ASSERT(2500 == MicroProfileLogTickDifference(c, l2)); + } +#endif + MICROPROFILE_SCOPE(g_MicroProfileFlip); + std::lock_guard Lock(MicroProfileMutex()); + + if(S.nDumpFileNextFrame) + { + if(0 == S.nDumpFileCountDown) + { + MicroProfileDumpToFile(); + S.nDumpFileNextFrame = 0; + S.nAutoClearFrames = MICROPROFILE_GPU_FRAME_DELAY + 3; // hide spike from dumping webpage + } + else + { + S.nDumpFileCountDown--; + } + } +#if MICROPROFILE_WEBSERVER + if(MICROPROFILE_FLIP_FLAG_START_WEBSERVER == (MICROPROFILE_FLIP_FLAG_START_WEBSERVER & FlipFlag) && S.nWebServerDataSent == (uint64_t)-1) + { + MicroProfileWebServerStart(); + S.nWebServerDataSent = 0; + if(!S.WebSocketThreadRunning) + { + S.WebSocketThreadRunning = 1; + MicroProfileThreadStart(&S.WebSocketSendThread, MicroProfileSocketSenderThread); + } + } +#endif + + int nLoop = 0; + do + { +#if MICROPROFILE_WEBSERVER + if(MicroProfileWebServerUpdate()) + { + S.nAutoClearFrames = MICROPROFILE_GPU_FRAME_DELAY + 3; // hide spike from dumping webpage + } +#endif + if(nLoop++) + { + MicroProfileSleep(100); + if((nLoop % 10) == 0) + { + uprintf("microprofile frozen %d\n", nLoop); + } + } + } while(S.nFrozen); + + uint32_t nAggregateClear = S.nAggregateClear || S.nAutoClearFrames, nAggregateFlip = 0; + + if(S.nAutoClearFrames) + { + nAggregateClear = 1; + nAggregateFlip = 1; + S.nAutoClearFrames -= 1; + } + + bool nRunning = MicroProfileAnyGroupActive(); + if(nRunning) + { + S.nFlipStartTick = MP_TICK(); + int64_t nGpuWork = MicroProfileGpuEnd(S.pGpuGlobal); + MicroProfileGpuSubmit(S.GpuQueue, nGpuWork); + MicroProfileThreadLogGpuReset(S.pGpuGlobal); + for(uint32_t i = 0; i < MICROPROFILE_MAX_THREADS; ++i) + { + if(S.PoolGpu[i]) + { + S.PoolGpu[i]->nPut = 0; + } + } + + MicroProfileGpuBegin(pContext, S.pGpuGlobal); + + uint32_t nGpuTimeStamp = MicroProfileGpuFlip(pContext); + + uint64_t nFrameIdx = S.nFramePutIndex++; + S.nFramePut = (S.nFramePut + 1) % MICROPROFILE_MAX_FRAME_HISTORY; + S.Frames[S.nFramePut].nFrameId = nFrameIdx; + MP_ASSERT((S.nFramePutIndex % MICROPROFILE_MAX_FRAME_HISTORY) == S.nFramePut); + S.nFrameCurrent = (S.nFramePut + MICROPROFILE_MAX_FRAME_HISTORY - MICROPROFILE_GPU_FRAME_DELAY - 1) % MICROPROFILE_MAX_FRAME_HISTORY; + S.nFrameCurrentIndex++; + uint32_t nFrameNext = (S.nFrameCurrent + 1) % MICROPROFILE_MAX_FRAME_HISTORY; + S.nFrameNext = nFrameNext; + + uint32_t nContextSwitchPut = S.nContextSwitchPut; + if(S.nContextSwitchLastPut < nContextSwitchPut) + { + S.nContextSwitchUsage = (nContextSwitchPut - S.nContextSwitchLastPut); + } + else + { + S.nContextSwitchUsage = MICROPROFILE_CONTEXT_SWITCH_BUFFER_SIZE - S.nContextSwitchLastPut + nContextSwitchPut; + } + S.nContextSwitchLastPut = nContextSwitchPut; + + MicroProfileFrameState* pFramePut = &S.Frames[S.nFramePut]; + MicroProfileFrameState* pFrameCurrent = &S.Frames[S.nFrameCurrent]; + MicroProfileFrameState* pFrameNext = &S.Frames[nFrameNext]; + const int64_t nTickStartFrame = pFrameCurrent->nFrameStartCpu; + const int64_t nTickEndFrame = pFrameNext->nFrameStartCpu; + + pFrameCurrent->nGpuPending = 0; + pFramePut->nGpuPending = 1; + + pFramePut->nFrameStartCpu = MP_TICK(); + + pFramePut->nFrameStartGpu = nGpuTimeStamp; + { + const float fDumpTimeThreshold = 1000.f * 60 * 60 * 24 * 365.f; // if time above this, then we're handling uninitialized counters + int nDumpNextFrame = 0; + float fTimeGpu = 0.f; + if(pFrameNext->nFrameStartGpu != MICROPROFILE_INVALID_TICK) + { + + uint64_t nTickCurrent = pFrameCurrent->nFrameStartGpu; + uint64_t nTickNext = pFrameNext->nFrameStartGpu = MicroProfileGpuGetTimeStamp((uint32_t)pFrameNext->nFrameStartGpu); + nTickCurrent = MicroProfileLogTickMin(nTickCurrent, nTickNext); + float fTime = 1000.f * (nTickNext - nTickCurrent) / (MicroProfileTicksPerSecondGpu()); + fTime = fTimeGpu; + if(S.fDumpGpuSpike > 0.f && fTime > S.fDumpGpuSpike && fTime < fDumpTimeThreshold) + { + nDumpNextFrame = 1; + } + } + float fTimeCpu = 1000.f * (pFrameNext->nFrameStartCpu - pFrameCurrent->nFrameStartCpu) / MicroProfileTicksPerSecondCpu(); + if(S.fDumpCpuSpike > 0.f && fTimeCpu > S.fDumpCpuSpike && fTimeCpu < fDumpTimeThreshold) + { + nDumpNextFrame = 1; + } + if(nDumpNextFrame) + { + S.nDumpFileNextFrame = S.nDumpSpikeMask; + S.nDumpSpikeMask = 0; + S.nDumpFileCountDown = 5; + } + } + + const uint64_t nTickEndFrameGpu_ = pFrameNext->nFrameStartGpu; + const uint64_t nTickStartFrameGpu_ = pFrameCurrent->nFrameStartGpu; + const bool bGpuFrameInvalid = nTickEndFrameGpu_ == MICROPROFILE_INVALID_TICK || nTickStartFrameGpu_ == MICROPROFILE_INVALID_TICK; + const uint64_t nTickEndFrameGpu = bGpuFrameInvalid ? 1 : nTickEndFrameGpu_; + const uint64_t nTickStartFrameGpu = bGpuFrameInvalid ? 2 : nTickStartFrameGpu_; + + MicroProfileFrameExtraCounterData* ExtraData = S.FrameExtraCounterData; + bool UsingExtraData = false; + if(ExtraData) + { + if((intptr_t)ExtraData == 1) + { + size_t Bytes = sizeof(MicroProfileFrameExtraCounterData) * MICROPROFILE_MAX_FRAME_HISTORY; + printf(" allocating %d bytes %f\n", (int)Bytes, Bytes / (1024.0 * 1024.0)); + ExtraData = S.FrameExtraCounterData = (MicroProfileFrameExtraCounterData*)MicroProfileAllocInternal(Bytes, alignof(uint64_t)); + memset(ExtraData, 0, Bytes); + } + ExtraData = ExtraData + S.nFrameCurrent; + UsingExtraData = true; + } +#define MP_ASSERT_LE_WRAP(l, g) MP_ASSERT(uint64_t(g - l) < 0x8000000000000000) + + { + MP_ASSERT_LE_WRAP(nTickStartFrame, nTickEndFrame); + uint64_t nTick = nTickEndFrame - nTickStartFrame; + S.nFlipTicks = nTick; + S.nFlipAggregate += nTick; + S.nFlipMax = MicroProfileMax(S.nFlipMax, nTick); + } + + uint32_t* pTimerToGroup = &S.TimerToGroup[0]; + for(uint32_t i = 0; i < MICROPROFILE_MAX_THREADS; ++i) + { + MicroProfileThreadLog* pLog = S.Pool[i]; + if(!pLog) + { + pFramePut->nLogStart[i] = 0; + } + else + { + uint32_t nPut = pLog->nPut.load(std::memory_order_acquire); + pFramePut->nLogStart[i] = nPut; + if(!pLog->nGpu) + { + uint32_t nStart = pFrameCurrent->nLogStart[i]; + while(nStart != nPut) + { + int64_t LE = pLog->Log[nStart]; + int64_t nDifference = MicroProfileLogTickDifference(LE, nTickEndFrame); + uint32_t Ext = MicroProfileLogGetType(LE); + if(nDifference > 0 || 0 != (0x2 & Ext)) + { + nStart = (nStart + 1) % MICROPROFILE_BUFFER_SIZE; + } + else + { + break; + } + } + pFrameNext->nLogStart[i] = nStart; + } + } + } + { + pFramePut->nLogStartTimeline = S.TimelineLog.nPut.load(std::memory_order_acquire); + + uint32_t nFrameCurrent = S.nFrameCurrent; + uint32_t nTimelineFrameDeltaMax = S.nTimelineFrameMax; + for(uint32_t i = 0; i != MICROPROFILE_TIMELINE_MAX_TOKENS; ++i) + { + uint32_t nFrameStart = S.TimelineTokenFrameEnter[i]; + if(nFrameStart != MICROPROFILE_INVALID_FRAME) + { + uint32_t nCur = nFrameCurrent; + if(nCur < nFrameStart) + nCur += MICROPROFILE_MAX_FRAME_HISTORY; + if(nCur >= nFrameStart) + { + uint32_t D = nCur - nFrameStart; + nTimelineFrameDeltaMax = MicroProfileMax(nTimelineFrameDeltaMax, D); + } + } + } + pFramePut->nTimelineFrameMax = nTimelineFrameDeltaMax; + S.nTimelineFrameMax = 0; + } + { + for(uint32_t i = 0; i < MICROPROFILE_MAX_THREADS; ++i) + { + MicroProfileThreadLog* pLog = S.Pool[i]; + if(!pLog) + continue; + if(pLog->nGpu) + { + uint32_t nPut = pFrameNext->nLogStart[i]; + uint32_t nGet = pFrameCurrent->nLogStart[i]; + uint32_t nRange[2][2] = { + { 0, 0 }, + { 0, 0 }, + }; + MicroProfileGetRange(nPut, nGet, nRange); + for(uint32_t j = 0; j < 2; ++j) + { + uint32_t nStart = nRange[j][0]; + uint32_t nEnd = nRange[j][1]; + for(uint32_t k = nStart; k < nEnd; ++k) + { + MicroProfileLogEntry L = pLog->Log[k]; + if(MicroProfileLogGetType(L) < MP_LOG_EXTENDED) + { + pLog->Log[k] = MicroProfileLogSetTick(L, MicroProfileGpuGetTimeStamp((uint32_t)MicroProfileLogGetTick(L))); + } + k += MicroProfileLogGetDataSize(L); + } + } + } + } + } + + { + MicroProfile::GroupTime* pFrameGroup = &S.FrameGroup[0]; + { + MICROPROFILE_SCOPE(g_MicroProfileClear); + for(uint32_t i = 0; i < S.nTotalTimers; ++i) + { + S.Frame[i].nTicks = 0; + S.Frame[i].nCount = 0; + S.FrameExclusive[i] = 0; + } + for(uint32_t i = 0; i < MICROPROFILE_MAX_GROUPS; ++i) + { + pFrameGroup[i].nTicks = 0; + pFrameGroup[i].nTicksExclusive = 0; + pFrameGroup[i].nCount = 0; + } + } + { + MICROPROFILE_SCOPE(g_MicroProfileThreadLoop); + memset(S.FrameGroupThreadValid, 0, sizeof(S.FrameGroupThreadValid)); + + for(uint32_t idx_thread = 0; idx_thread < MICROPROFILE_MAX_THREADS; ++idx_thread) + { + MicroProfileThreadLog* pLog = S.Pool[idx_thread]; + if(!pLog) + continue; + bool bGpu = pLog->nGpu != 0; + int64_t nTickStartLog = bGpu ? nTickStartFrameGpu : nTickStartFrame; + int64_t nTickEndLog = bGpu ? nTickEndFrameGpu : nTickEndFrame; + + float fToMs = bGpu ? MicroProfileTickToMsMultiplierGpu() : MicroProfileTickToMsMultiplierCpu(); + float fFrameTime = fToMs * (nTickEndLog - nTickStartLog); + + MicroProfile::GroupTime* pFrameGroupThread = &S.FrameGroupThread[idx_thread][0]; + + uint32_t nPut = pFrameNext->nLogStart[idx_thread]; + uint32_t nGet = pFrameCurrent->nLogStart[idx_thread]; + uint32_t nRange[2][2] = { + { 0, 0 }, + { 0, 0 }, + }; + MicroProfileGetRange(nPut, nGet, nRange); + if(nPut != nGet) + { + S.FrameGroupThreadValid[idx_thread / 32] |= 1 << (idx_thread % 32); + memset(pFrameGroupThread, 0, sizeof(S.FrameGroupThread[idx_thread])); + } + + uint64_t* pStackLog = &pLog->nStackLogEntry[0]; + uint64_t* pChildTickStack = &pLog->nChildTickStack[1]; + int32_t nStackPos = pLog->nStackPos; + uint8_t TimerStackPos[MICROPROFILE_MAX_TIMERS]; + uint8_t GroupStackPos[MICROPROFILE_MAX_GROUPS]; + memset(TimerStackPos, 0, sizeof(TimerStackPos)); + memset(GroupStackPos, 0, sizeof(GroupStackPos)); + + // restore group and timer stack pos. + for(int32_t i = 0; i < nStackPos; ++i) + { + uint64_t nTimer = MicroProfileLogGetTimerIndex(pStackLog[i]); + uint32_t nGroup = pTimerToGroup[nTimer]; + MP_ASSERT(nTimer < MICROPROFILE_MAX_TIMERS); + MP_ASSERT(nGroup < MICROPROFILE_MAX_GROUPS); + TimerStackPos[nTimer]++; + GroupStackPos[nGroup]++; + } + + for(uint32_t j = 0; j < 2; ++j) + { + uint32_t nStart = nRange[j][0]; + uint32_t nEnd = nRange[j][1]; + for(uint32_t k = nStart; k < nEnd; ++k) + { + MicroProfileLogEntry LE = pLog->Log[k]; + uint32_t nType = MicroProfileLogGetType(LE); + + switch(nType) + { + case MP_LOG_ENTER: + { + uint64_t nTimer = MicroProfileLogGetTimerIndex(LE); + if(nTimer != ETOKEN_CSTR_PTR) + { + MP_ASSERT(nTimer < S.nTotalTimers); + uint32_t nGroup = pTimerToGroup[nTimer]; + MP_ASSERT(nStackPos < MICROPROFILE_STACK_MAX); + MP_ASSERT(nGroup < MICROPROFILE_MAX_GROUPS); + + // When we aggretate the total time, we have to count if the timers & groups are layered, to avoid summing them twice when calculating the total time. + // Averages become nonsense regardless. + TimerStackPos[nTimer]++; + GroupStackPos[nGroup]++; + + pStackLog[nStackPos] = LE; + + pChildTickStack[nStackPos] = 0; + nStackPos++; + } + break; + } + case MP_LOG_LEAVE: + { + uint64_t nTimer = MicroProfileLogGetTimerIndex(LE); + if(nTimer != ETOKEN_CSTR_PTR) + { + MP_ASSERT(nTimer < S.nTotalTimers); + uint32_t nGroup = pTimerToGroup[nTimer]; + MP_ASSERT(nGroup < MICROPROFILE_MAX_GROUPS); + MP_ASSERT(nStackPos); + uint64_t nTicks; + bool bGroupRoot = 0 == GroupStackPos[nGroup] || 0 == --GroupStackPos[nGroup]; + bool bTimerRoot = 0 == TimerStackPos[nTimer] || 0 == --TimerStackPos[nTimer]; + { + nStackPos--; + MicroProfileLogEntry LEStack = pStackLog[nStackPos]; + MP_ASSERT(MicroProfileLogGetTimerIndex(LEStack) == nTimer); // unbalanced timers are not supported + uint64_t nTickStart = MicroProfileLogTickClamp(LEStack, nTickStartLog, nTickEndLog); + uint64_t nClamped = MicroProfileLogTickClamp(LE, nTickStartLog, nTickEndLog); + nTicks = MicroProfileLogTickDifference(nTickStart, nClamped); + MP_ASSERT(nTicks < 0x8000000000000000); + + uint64_t nChildTicks = pChildTickStack[nStackPos]; + + MP_ASSERT(nStackPos < MICROPROFILE_STACK_MAX); + if(nStackPos) + { + pChildTickStack[nStackPos - 1] += nTicks; + } + MP_ASSERT(nTicks >= nChildTicks); + uint64_t nTicksExclusive = (nTicks - nChildTicks); + S.FrameExclusive[nTimer] += nTicksExclusive; + pFrameGroupThread[nGroup].nTicksExclusive += nTicksExclusive; + if(bTimerRoot) // dont count this if its below another instance of the same timer. + { + S.Frame[nTimer].nTicks += nTicks; + S.Frame[nTimer].nCount += 1; + MP_ASSERT(nGroup < MICROPROFILE_MAX_GROUPS); + if(bGroupRoot) + { + pFrameGroupThread[nGroup].nTicks += nTicks; + pFrameGroupThread[nGroup].nCount += 1; + } + } + } + } + break; + } + case MP_LOG_EXTENDED: + { + k += MicroProfileLogGetDataSize(LE); + break; + } + case MP_LOG_EXTENDED_NO_DATA: + break; + } + } + } + + for(int32_t i = nStackPos - 1; i >= 0; --i) + { + + MicroProfileLogEntry LE = pStackLog[i]; + uint64_t nTickStart = MicroProfileLogTickClamp(LE, nTickStartLog, nTickEndLog); + uint64_t nTicks = MicroProfileLogTickDifference(nTickStart, nTickEndLog); + int64_t nChildTicks = pChildTickStack[i]; + pChildTickStack[i] = 0; // consume.. + + MP_ASSERT(i < MICROPROFILE_STACK_MAX && i >= 0); + if(i) + { + pChildTickStack[i - 1] += nTicks; + } + MP_ASSERT(nTicks >= (uint64_t)nChildTicks); + + uint32_t nTimer = (uint32_t)MicroProfileLogGetTimerIndex(LE); + uint32_t nGroup = pTimerToGroup[nTimer]; + + bool bGroupRoot = 0 == GroupStackPos[nGroup] || 0 == --GroupStackPos[nGroup]; + bool bTimerRoot = 0 == TimerStackPos[nTimer] || 0 == --TimerStackPos[nTimer]; + + uint64_t nTicksExclusive = (nTicks - nChildTicks); + S.FrameExclusive[nTimer] += nTicksExclusive; + pFrameGroupThread[nGroup].nTicksExclusive += nTicksExclusive; + if(bTimerRoot) + { + S.Frame[nTimer].nTicks += nTicks; + S.Frame[nTimer].nCount += 1; + + MP_ASSERT(nGroup < MICROPROFILE_MAX_GROUPS); + if(bGroupRoot) + { + pFrameGroupThread[nGroup].nTicks += nTicks; + pFrameGroupThread[nGroup].nCount += 1; + } + } + } +#ifdef MP_ASSERT + for(uint8_t& g : GroupStackPos) + { + MP_ASSERT(g == 0); + } + for(uint8_t& g : TimerStackPos) + { + MP_ASSERT(g == 0); + } +#endif + + pLog->nStackPos = nStackPos; + for(uint32_t j = 0; j < MICROPROFILE_MAX_GROUPS; ++j) + { + pLog->nGroupTicks[j] += pFrameGroupThread[j].nTicks; + + if((S.FrameGroupThreadValid[idx_thread / 32] & (1 << (idx_thread % 32))) != 0) + { + pFrameGroup[j].nTicks += pFrameGroupThread[j].nTicks; + pFrameGroup[j].nTicksExclusive += pFrameGroupThread[j].nTicksExclusive; + pFrameGroup[j].nCount += pFrameGroupThread[j].nCount; + } + } + + if(pLog->nPut == pLog->nGet && pLog->nActive == 2) + { + pLog->nIdleFrames++; + } + else + { + pLog->nIdleFrames = 0; + } + if(pLog->nActive == 2 && pLog->nIdleFrames > MICROPROFILE_THREAD_LOG_FRAMES_REUSE) + { + MicroProfileLogReset(pLog); + } + } + } + { + MICROPROFILE_SCOPE(g_MicroProfileAccumulate); + uint64_t* ExtraPut = nullptr; + if(UsingExtraData) + { + ExtraPut = &ExtraData->Timers[0]; + ExtraData->NumTimers = S.nTotalTimers; + } + + for(uint32_t i = 0; i < S.nTotalTimers; ++i) + { + S.AccumTimers[i].nTicks += S.Frame[i].nTicks; + S.AccumTimers[i].nCount += S.Frame[i].nCount; + S.AccumMaxTimers[i] = MicroProfileMax(S.AccumMaxTimers[i], S.Frame[i].nTicks); + S.AccumMinTimers[i] = MicroProfileMin(S.AccumMinTimers[i], S.Frame[i].nTicks); + S.AccumTimersExclusive[i] += S.FrameExclusive[i]; + S.AccumMaxTimersExclusive[i] = MicroProfileMax(S.AccumMaxTimersExclusive[i], S.FrameExclusive[i]); + if(ExtraPut) + *ExtraPut++ = S.Frame[i].nTicks; + } + ExtraPut = nullptr; + if(UsingExtraData) + { + ExtraPut = &ExtraData->Groups[0]; + ExtraData->NumGroups = S.nGroupCount; + } + + for(uint32_t i = 0; i < MICROPROFILE_MAX_GROUPS; ++i) + { + S.AccumGroup[i] += pFrameGroup[i].nTicks; + S.AccumGroupMax[i] = MicroProfileMax(S.AccumGroupMax[i], pFrameGroup[i].nTicks); + if(ExtraPut) + *ExtraPut++ = pFrameGroup[i].nTicks; + } +#if MICROPROFILE_IMGUI + void MicroProfileImguiGather(); + MicroProfileImguiGather(); +#endif + if(S.CsvConfig.State == MicroProfileCsvConfig::ACTIVE) + { + uint32_t FrameIndex = S.nFrameCurrent % MICROPROFILE_MAX_FRAME_HISTORY; + uint64_t* FrameData = S.CsvConfig.FrameData + S.CsvConfig.TotalElements * FrameIndex; + { + uint16_t* TimerIndices = S.CsvConfig.TimerIndices; + for(uint32_t i = 0; i < S.CsvConfig.NumTimers; ++i) + { + uint16_t Index = TimerIndices[i]; + if(Index != UINT16_MAX) + { + *FrameData = S.Frame[Index].nTicks; + } + else + { + *FrameData = 0; + } + FrameData++; + } + } + { + uint16_t* GroupIndices = S.CsvConfig.GroupIndices; + for(uint32_t i = 0; i < S.CsvConfig.NumGroups; ++i) + { + uint16_t Index = GroupIndices[i]; + if(Index != UINT16_MAX) + { + *FrameData = pFrameGroup[Index].nTicks; + } + else + { + *FrameData = 0; + } + FrameData++; + } + } + { + uint16_t* CounterIndices = S.CsvConfig.CounterIndices; + for(uint32_t i = 0; i < S.CsvConfig.NumCounters; ++i) + { + uint16_t Index = CounterIndices[i]; + if(Index != UINT16_MAX) + { + if(S.CounterInfo[Index].nFlags & MICROPROFILE_COUNTER_FLAG_DOUBLE) + { + double d = S.CountersDouble[Index].load(); + memcpy(FrameData, &d, sizeof(d)); + } + else + { + *FrameData = S.Counters[Index].load(); + } + } + else + { + *FrameData = 0; + } + FrameData++; + } + } + } + } + for(uint32_t i = 0; i < MICROPROFILE_MAX_GRAPHS; ++i) + { + if(S.Graph[i].nToken != MICROPROFILE_INVALID_TOKEN) + { + MicroProfileToken nToken = S.Graph[i].nToken; + S.Graph[i].nHistory[S.nGraphPut] = S.Frame[MicroProfileGetTimerIndex(nToken)].nTicks; + } + } + S.nGraphPut = (S.nGraphPut + 1) % MICROPROFILE_GRAPH_HISTORY; + } + + if(S.nAggregateFlip <= ++S.nAggregateFlipCount) + { + nAggregateFlip = 1; + if(S.nAggregateFlip) // if 0 accumulate indefinitely + { + nAggregateClear = 1; + } + } + + for(uint32_t i = 0; i < MICROPROFILE_MAX_THREADS; ++i) + { + MicroProfileThreadLog* pLog = S.Pool[i]; + uint32_t nNewGet = pFrameNext->nLogStart[i]; + + if(pLog && nNewGet != (uint32_t)-1) + { + pLog->nGet.store(nNewGet); + } + } + if(pFrameNext->nLogStartTimeline != (uint32_t)-1) + { + S.TimelineLog.nGet.store(pFrameNext->nLogStartTimeline); + } + } + if(nAggregateFlip) + { + memcpy(&S.Aggregate[0], &S.AccumTimers[0], sizeof(S.Aggregate[0]) * S.nTotalTimers); + memcpy(&S.AggregateMax[0], &S.AccumMaxTimers[0], sizeof(S.AggregateMax[0]) * S.nTotalTimers); + memcpy(&S.AggregateMin[0], &S.AccumMinTimers[0], sizeof(S.AggregateMin[0]) * S.nTotalTimers); + memcpy(&S.AggregateExclusive[0], &S.AccumTimersExclusive[0], sizeof(S.AggregateExclusive[0]) * S.nTotalTimers); + memcpy(&S.AggregateMaxExclusive[0], &S.AccumMaxTimersExclusive[0], sizeof(S.AggregateMaxExclusive[0]) * S.nTotalTimers); + + memcpy(&S.AggregateGroup[0], &S.AccumGroup[0], sizeof(S.AggregateGroup)); + memcpy(&S.AggregateGroupMax[0], &S.AccumGroupMax[0], sizeof(S.AggregateGroup)); + + for(uint32_t i = 0; i < MICROPROFILE_MAX_THREADS; ++i) + { + MicroProfileThreadLog* pLog = S.Pool[i]; + if(!pLog) + continue; + + memcpy(&pLog->nAggregateGroupTicks[0], &pLog->nGroupTicks[0], sizeof(pLog->nAggregateGroupTicks)); + + if(nAggregateClear) + { + memset(&pLog->nGroupTicks[0], 0, sizeof(pLog->nGroupTicks)); + } + } + + S.nAggregateFrames = S.nAggregateFlipCount; + S.nFlipAggregateDisplay = S.nFlipAggregate; + S.nFlipMaxDisplay = S.nFlipMax; + if(nAggregateClear) + { + memset(&S.AccumTimers[0], 0, sizeof(S.Aggregate[0]) * S.nTotalTimers); + memset(&S.AccumMaxTimers[0], 0, sizeof(S.AccumMaxTimers[0]) * S.nTotalTimers); + memset(&S.AccumMinTimers[0], 0xFF, sizeof(S.AccumMinTimers[0]) * S.nTotalTimers); + memset(&S.AccumTimersExclusive[0], 0, sizeof(S.AggregateExclusive[0]) * S.nTotalTimers); + memset(&S.AccumMaxTimersExclusive[0], 0, sizeof(S.AccumMaxTimersExclusive[0]) * S.nTotalTimers); + memset(&S.AccumGroup[0], 0, sizeof(S.AggregateGroup)); + memset(&S.AccumGroupMax[0], 0, sizeof(S.AggregateGroup)); + + S.nAggregateFlipCount = 0; + S.nFlipAggregate = 0; + S.nFlipMax = 0; + + S.nAggregateFlipTick = MP_TICK(); + } + +#if MICROPROFILE_COUNTER_HISTORY + int64_t* pDest = &S.nCounterHistory[S.nCounterHistoryPut][0]; + S.nCounterHistoryPut = (S.nCounterHistoryPut + 1) % MICROPROFILE_GRAPH_HISTORY; + for(uint32_t i = 0; i < S.nNumCounters; ++i) + { + if(0 != (S.CounterInfo[i].nFlags & MICROPROFILE_COUNTER_FLAG_DETAILED)) + { + MicroProfileFetchCounter(i); + bool IsDouble = (S.CounterInfo[i].nFlags & MICROPROFILE_COUNTER_FLAG_DOUBLE) != 0; + if(IsDouble) + { + double dValue = S.CountersDouble[i].load(std::memory_order_relaxed); + memcpy(&pDest[i], &dValue, sizeof(dValue)); + S.dCounterMin[i] = MicroProfileMin(S.dCounterMin[i], dValue); + S.dCounterMax[i] = MicroProfileMax(S.dCounterMax[i], dValue); + } + else + { + uint64_t nValue = S.Counters[i].load(std::memory_order_relaxed); + pDest[i] = nValue; + S.nCounterMin[i] = MicroProfileMin(S.nCounterMin[i], (int64_t)nValue); + S.nCounterMax[i] = MicroProfileMax(S.nCounterMax[i], (int64_t)nValue); + } + } + } +#endif + } + S.nAggregateClear = 0; + + MicroProfileFlipEnabled(); +} + +void MicroProfileSetEnableAllGroups(int bEnable) +{ + if(bEnable) + { + for(uint32_t i = 0; i < MICROPROFILE_MAX_GROUP_INTS; ++i) + { + S.nActiveGroupsWanted[i] = S.nGroupMask[i]; + } + S.nStartEnabled = 1; + MicroProfileFlipEnabled(); + } + else + { + for(uint32_t i = 0; i < MICROPROFILE_MAX_GROUP_INTS; ++i) + { + S.nActiveGroupsWanted[i] = 0; + } + S.nStartEnabled = 0; + MicroProfileFlipEnabled(); + } +} +void MicroProfileEnableCategory(const char* pCategory, int bEnabled) +{ + int nCategoryIndex = -1; + for(uint32_t i = 0; i < S.nCategoryCount; ++i) + { + if(!MP_STRCASECMP(pCategory, S.CategoryInfo[i].pName)) + { + nCategoryIndex = (int)i; + break; + } + } + if(nCategoryIndex >= 0) + { + if(bEnabled) + { + for(uint32_t i = 0; i < MICROPROFILE_MAX_GROUP_INTS; ++i) + { + S.nActiveGroupsWanted[i] |= S.CategoryInfo[nCategoryIndex].nGroupMask[i]; + } + } + else + { + for(uint32_t i = 0; i < MICROPROFILE_MAX_GROUP_INTS; ++i) + { + S.nActiveGroupsWanted[i] &= ~S.CategoryInfo[nCategoryIndex].nGroupMask[i]; + } + } + } +} + +void MicroProfileEnableCategory(const char* pCategory) +{ + MicroProfileEnableCategory(pCategory, true); +} +void MicroProfileDisableCategory(const char* pCategory) +{ + MicroProfileEnableCategory(pCategory, false); +} + +int MicroProfileGetEnableAllGroups() +{ + return 0 == memcmp(S.nGroupMask, S.nActiveGroupsWanted, sizeof(S.nGroupMask)); +} + +void MicroProfileSetForceMetaCounters(int bForce) +{ +} + +int MicroProfileGetForceMetaCounters() +{ + + return 0; +} + +void MicroProfileEnableMetaCounter(const char* pMeta) +{ +} + +void MicroProfileDisableMetaCounter(const char* pMeta) +{ +} + +void MicroProfileSetAggregateFrames(int nFrames) +{ + S.nAggregateFlip = (uint32_t)nFrames; + if(0 == nFrames) + { + S.nAggregateClear = 1; + } +} + +int MicroProfileGetAggregateFrames() +{ + return S.nAggregateFlip; +} + +int MicroProfileGetCurrentAggregateFrames() +{ + return int(S.nAggregateFlip ? S.nAggregateFlip : S.nAggregateFlipCount); +} + +void MicroProfileForceEnableGroup(const char* pGroup, MicroProfileTokenType Type) +{ + MicroProfileInit(); + std::lock_guard Lock(MicroProfileMutex()); + uint16_t nGroup = MicroProfileGetGroup(pGroup, Type); + uint32_t nIndex = nGroup / 32; + uint32_t nBit = nGroup % 32; + S.nForceGroups[nIndex] |= (1ll << nBit); +} + +void MicroProfileForceDisableGroup(const char* pGroup, MicroProfileTokenType Type) +{ + MicroProfileInit(); + std::lock_guard Lock(MicroProfileMutex()); + uint16_t nGroup = MicroProfileGetGroup(pGroup, Type); + uint32_t nIndex = nGroup / 32; + uint32_t nBit = nGroup % 32; + + S.nForceGroups[nIndex] &= ~(1ll << nBit); +} + +struct MicroProfileTimerValues +{ + float TimeMs; + float AverageMs; + float MaxMs; + float MinMs; + float CallAverageMs; + float ExclusiveMs; + float AverageExclusiveMs; + float MaxExclusiveMs; + float TotalMs; + uint32_t nCount; +}; + +void MicroProfileCalcTimers(int nTimer, MicroProfileTimerValues& Out) +{ + const uint32_t nGroupId = S.TimerInfo[nTimer].nGroupIndex; + const float fToMs = MicroProfileTickToMsMultiplier(S.GroupInfo[nGroupId].Type == MicroProfileTokenTypeGpu ? MicroProfileTicksPerSecondGpu() : MicroProfileTicksPerSecondCpu()); + uint32_t nAggregateFrames = S.nAggregateFrames ? S.nAggregateFrames : 1; + uint32_t nAggregateCount = S.Aggregate[nTimer].nCount ? S.Aggregate[nTimer].nCount : 1; + Out.nCount = S.Aggregate[nTimer].nCount; + + float fToPrc = S.fRcpReferenceTime; + float fMs = fToMs * (S.Frame[nTimer].nTicks); + + float fAverageMs = fToMs * (S.Aggregate[nTimer].nTicks / nAggregateFrames); + float fMaxMs = fToMs * (S.AggregateMax[nTimer]); + float fMinMs = fToMs * (S.AggregateMin[nTimer] != uint64_t(-1) ? S.AggregateMin[nTimer] : 0); + float fCallAverageMs = fToMs * (S.Aggregate[nTimer].nTicks / nAggregateCount); + float fMsExclusive = fToMs * (S.FrameExclusive[nTimer]); + float fAverageMsExclusive = fToMs * (S.AggregateExclusive[nTimer] / nAggregateFrames); + float fMaxMsExclusive = fToMs * (S.AggregateMaxExclusive[nTimer]); + float fTotalMs = fToMs * S.Aggregate[nTimer].nTicks; + + Out.TimeMs = fMs; + Out.AverageMs = fAverageMs; + Out.MaxMs = fMaxMs; + Out.MinMs = fMinMs; + Out.CallAverageMs = fCallAverageMs; + Out.ExclusiveMs = fMsExclusive; + Out.AverageExclusiveMs = fAverageMsExclusive; + Out.MaxExclusiveMs = fMaxMsExclusive; + Out.TotalMs = fTotalMs; +} + +void MicroProfileCalcAllTimers( + float* pTimers, float* pAverage, float* pMax, float* pMin, float* pCallAverage, float* pExclusive, float* pAverageExclusive, float* pMaxExclusive, float* pTotal, uint32_t nSize) +{ + for(uint32_t i = 0; i < S.nTotalTimers && i < nSize; ++i) + { + const uint32_t nGroupId = S.TimerInfo[i].nGroupIndex; + const float fToMs = MicroProfileTickToMsMultiplier(S.GroupInfo[nGroupId].Type == MicroProfileTokenTypeGpu ? MicroProfileTicksPerSecondGpu() : MicroProfileTicksPerSecondCpu()); + uint32_t nTimer = i; + uint32_t nIdx = i * 2; + uint32_t nAggregateFrames = S.nAggregateFrames ? S.nAggregateFrames : 1; + uint32_t nAggregateCount = S.Aggregate[nTimer].nCount ? S.Aggregate[nTimer].nCount : 1; + float fToPrc = S.fRcpReferenceTime; + float fMs = fToMs * (S.Frame[nTimer].nTicks); + float fPrc = MicroProfileMin(fMs * fToPrc, 1.f); + float fAverageMs = fToMs * (S.Aggregate[nTimer].nTicks / nAggregateFrames); + float fAveragePrc = MicroProfileMin(fAverageMs * fToPrc, 1.f); + float fMaxMs = fToMs * (S.AggregateMax[nTimer]); + float fMaxPrc = MicroProfileMin(fMaxMs * fToPrc, 1.f); + float fMinMs = fToMs * (S.AggregateMin[nTimer] != uint64_t(-1) ? S.AggregateMin[nTimer] : 0); + float fMinPrc = MicroProfileMin(fMinMs * fToPrc, 1.f); + float fCallAverageMs = fToMs * (S.Aggregate[nTimer].nTicks / nAggregateCount); + float fCallAveragePrc = MicroProfileMin(fCallAverageMs * fToPrc, 1.f); + float fMsExclusive = fToMs * (S.FrameExclusive[nTimer]); + float fPrcExclusive = MicroProfileMin(fMsExclusive * fToPrc, 1.f); + float fAverageMsExclusive = fToMs * (S.AggregateExclusive[nTimer] / nAggregateFrames); + float fAveragePrcExclusive = MicroProfileMin(fAverageMsExclusive * fToPrc, 1.f); + float fMaxMsExclusive = fToMs * (S.AggregateMaxExclusive[nTimer]); + float fMaxPrcExclusive = MicroProfileMin(fMaxMsExclusive * fToPrc, 1.f); + float fTotalMs = fToMs * S.Aggregate[nTimer].nTicks; + pTimers[nIdx] = fMs; + pTimers[nIdx + 1] = fPrc; + pAverage[nIdx] = fAverageMs; + pAverage[nIdx + 1] = fAveragePrc; + pMax[nIdx] = fMaxMs; + pMax[nIdx + 1] = fMaxPrc; + pMin[nIdx] = fMinMs; + pMin[nIdx + 1] = fMinPrc; + pCallAverage[nIdx] = fCallAverageMs; + pCallAverage[nIdx + 1] = fCallAveragePrc; + pExclusive[nIdx] = fMsExclusive; + pExclusive[nIdx + 1] = fPrcExclusive; + pAverageExclusive[nIdx] = fAverageMsExclusive; + pAverageExclusive[nIdx + 1] = fAveragePrcExclusive; + pMaxExclusive[nIdx] = fMaxMsExclusive; + pMaxExclusive[nIdx + 1] = fMaxPrcExclusive; + pTotal[nIdx] = fTotalMs; + pTotal[nIdx + 1] = 0.f; + } +} + +float MicroProfileGetTime(const char* pGroup, const char* pName) +{ + MicroProfileToken nToken = MicroProfileFindTokenInternal(pGroup, pName); + if(nToken == MICROPROFILE_INVALID_TOKEN) + { + return 0.f; + } + uint32_t nTimerIndex = MicroProfileGetTimerIndex(nToken); + uint32_t nGroupIndex = MicroProfileGetGroupIndex(nToken); + float fToMs = MicroProfileTickToMsMultiplier(S.GroupInfo[nGroupIndex].Type == MicroProfileTokenTypeGpu ? MicroProfileTicksPerSecondGpu() : MicroProfileTicksPerSecondCpu()); + return S.Frame[nTimerIndex].nTicks * fToMs; +} + +int MicroProfilePlatformMarkersGetEnabled() +{ + return S.nPlatformMarkersEnabled != 0 ? 1 : 0; +} +void MicroProfilePlatformMarkersSetEnabled(int bEnabled) +{ + S.nPlatformMarkersEnabled = bEnabled ? 1 : 0; +} + +#define MICROPROFILE_CONTEXT_SWITCH_SEARCH_DEBUG MICROPROFILE_DEBUG + +void MicroProfileContextSwitchSearch(uint32_t* pContextSwitchStart, uint32_t* pContextSwitchEnd, uint64_t nBaseTicksCpu, uint64_t nBaseTicksEndCpu) +{ + MICROPROFILE_SCOPE(g_MicroProfileContextSwitchSearch); + uint32_t nContextSwitchPut = S.nContextSwitchPut; + uint64_t nContextSwitchStart, nContextSwitchEnd; + nContextSwitchStart = nContextSwitchEnd = (nContextSwitchPut + MICROPROFILE_CONTEXT_SWITCH_BUFFER_SIZE - 1) % MICROPROFILE_CONTEXT_SWITCH_BUFFER_SIZE; + int64_t nSearchEnd = nBaseTicksEndCpu + MicroProfileMsToTick(30.f, MicroProfileTicksPerSecondCpu()); + int64_t nSearchBegin = nBaseTicksCpu - MicroProfileMsToTick(30.f, MicroProfileTicksPerSecondCpu()); + +#if MICROPROFILE_CONTEXT_SWITCH_SEARCH_DEBUG + int64_t lp = S.nContextSwitchLastPushed; + uprintf("cswitch-search\n"); + uprintf("Begin %" PRId64 " End %" PRId64 " Last %" PRId64 "\n", nSearchBegin, nSearchEnd, lp); + + float fToMs = MicroProfileTickToMsMultiplierCpu(); + uprintf("E %6.2fms\n", fToMs * (nSearchEnd - nSearchBegin)); + uprintf("LAST %6.2fms\n", fToMs * (lp - nSearchBegin)); +#endif + + int64_t nMax = INT64_MIN; + int64_t nMin = INT64_MAX; + for(uint32_t i = 0; i < MICROPROFILE_CONTEXT_SWITCH_BUFFER_SIZE; ++i) + { + uint32_t nIndex = (nContextSwitchPut + MICROPROFILE_CONTEXT_SWITCH_BUFFER_SIZE - (i + 1)) % MICROPROFILE_CONTEXT_SWITCH_BUFFER_SIZE; + MicroProfileContextSwitch& CS = S.ContextSwitch[nIndex]; + if(nMax < CS.nTicks) + nMax = CS.nTicks; + if(nMin > CS.nTicks && CS.nTicks != 0) + nMin = CS.nTicks; + if(CS.nTicks > nSearchEnd) + { + nContextSwitchEnd = nIndex; + } + if(CS.nTicks > nSearchBegin) + { + nContextSwitchStart = nIndex; + } + } + *pContextSwitchStart = nContextSwitchStart; + *pContextSwitchEnd = nContextSwitchEnd; + +#if MICROPROFILE_CONTEXT_SWITCH_SEARCH_DEBUG + { + uprintf("contextswitch start %" PRId64 " %" PRId64 "\n", nContextSwitchStart, nContextSwitchEnd); + + MicroProfileContextSwitch& CS0 = S.ContextSwitch[0]; + int64_t nMax = CS0.nTicks; + int64_t nMin = CS0.nTicks; + int64_t nBegin = 0; + int64_t nEnd = 0; + int nRanges = 0; + for(uint32_t i = 0; i < MICROPROFILE_CONTEXT_SWITCH_BUFFER_SIZE; i += 1024) + { + int64_t MinTick = INT64_MAX; + int64_t MaxTick = INT64_MIN; + for(int j = 0; j < 1024; ++j) + { + MicroProfileContextSwitch& CS = S.ContextSwitch[i + j]; + int64_t nTicks = CS.nTicks; + MinTick = MicroProfileMin(nTicks, MinTick); + MaxTick = MicroProfileMax(nTicks, MaxTick); + } + + uprintf("XX range [%5" PRIx64 ":%5" PRIx64 "] :: [%6.2f:%6.2f] [%p :: %p] .. ref %p\n", + i, + i + 1024, + fToMs * (MinTick - nSearchBegin), + fToMs * (MaxTick - nSearchBegin), + (void*)MinTick, + (void*)MaxTick, + (void*)nSearchBegin + + ); + } + uprintf("\n\n"); + + for(uint32_t i = 0; i < MICROPROFILE_CONTEXT_SWITCH_BUFFER_SIZE; ++i) + { + MicroProfileContextSwitch& CS = S.ContextSwitch[i]; + int64_t nTicks = CS.nTicks; + float fMs = (nTicks - nMax) * fToMs; + if(fMs < 0 || fMs > 50) + { + // dump range here + uprintf("range [%5" PRId64 ":%5" PRId64 "] :: [%6.2f:%6.2f] [%p :: %p] .. ref %p\n", + nBegin, + nEnd, + fToMs * (nMin - nSearchBegin), + fToMs * (nMax - nSearchBegin), + (void*)nMin, + (void*)nMax, + (void*)nSearchBegin + + ); + + nEnd = nBegin = i; + nMax = nMin = CS.nTicks; + nRanges++; + } + else + { + nEnd = i; + nMax = MicroProfileMax(nTicks, nMax); + } + } + } + + lp = S.nContextSwitchLastPushed; + uprintf("E %6.2fms\n", fToMs * (nSearchEnd - nSearchBegin)); + uprintf("LP2 %6.2fms\n", fToMs * (lp - nSearchBegin)); +#endif +} + +int MicroProfileFormatCounter(int eFormat, int64_t nCounter, char* pOut, uint32_t nBufferSize) +{ + if(!nCounter) + { + pOut[0] = '0'; + pOut[1] = '\0'; + return 1; + } + int nLen = 0; + char* pBase = pOut; + char* pTmp = pOut; + char* pEnd = pOut + nBufferSize; + int nNegative = 0; + if(nCounter < 0) + { + nCounter = -nCounter; + nNegative = 1; + if(nCounter < 0) // handle INT_MIN + { + nCounter = -(nCounter + 1); + } + } + + switch(eFormat) + { + case MICROPROFILE_COUNTER_FORMAT_DEFAULT: + { + int nSeperate = 0; + while(nCounter) + { + if(nSeperate) + { + *pTmp++ = '.'; + } + nSeperate = 1; + for(uint32_t i = 0; nCounter && i < 3; ++i) + { + int nDigit = nCounter % 10; + nCounter /= 10; + *pTmp++ = '0' + nDigit; + } + } + if(nNegative) + { + *pTmp++ = '-'; + } + nLen = pTmp - pOut; + --pTmp; + MP_ASSERT(pTmp <= pEnd); + while(pTmp > pOut) // reverse string + { + char c = *pTmp; + *pTmp = *pOut; + *pOut = c; + pTmp--; + pOut++; + } + } + break; + case MICROPROFILE_COUNTER_FORMAT_BYTES: + { + const char* pExt[] = { "b", "kb", "mb", "gb", "tb", "pb", "eb", "zb", "yb" }; + size_t nNumExt = sizeof(pExt) / sizeof(pExt[0]); + int64_t nShift = 0; + int64_t nDivisor = 1; + int64_t nCountShifted = nCounter >> 10; + while(nCountShifted) + { + nDivisor <<= 10; + nCountShifted >>= 10; + nShift++; + } + MP_ASSERT(nShift < (int64_t)nNumExt); + if(nShift) + { + nLen = snprintf(pOut, nBufferSize - 1, "%c%3.2f%s", nNegative ? '-' : ' ', (double)nCounter / nDivisor, pExt[nShift]); + } + else + { + nLen = snprintf(pOut, nBufferSize - 1, "%c%" PRId64 "%s", nNegative ? '-' : ' ', nCounter, pExt[nShift]); + } + } + break; + } + pBase[nLen] = '\0'; + + return nLen; +} + +int MicroProfileFormatCounterDouble(int eFormat, double dCounter, char* pOut, uint32_t nBufferSize) +{ + int nLen = 0; + switch(eFormat) + { + case MICROPROFILE_COUNTER_FORMAT_DEFAULT: + { + nLen = stbsp_snprintf(pOut, nBufferSize - 1, "%f", dCounter); + } + break; + case MICROPROFILE_COUNTER_FORMAT_BYTES: + { + const char* pExt[] = { "b", "kb", "mb", "gb", "tb", "pb", "eb", "zb", "yb" }; + double scale = 1.f; + int offset = 0; + int end = sizeof(pExt) / sizeof(pExt[0]); + double d = dCounter; + while(d / scale > 1024.f && offset + 1 < end) + { + scale *= 1024.f; + offset += 1; + } + nLen = stbsp_snprintf(pOut, nBufferSize - 1, "%.3f%s", d / scale, pExt[offset]); + } + break; + } + pOut[nLen] = '\0'; + + return nLen; +} + +bool MicroProfileAnyGroupActive() +{ + for(uint32_t i = 0; i < MICROPROFILE_MAX_GROUP_INTS; ++i) + { + if(S.nActiveGroups[i] != 0) + return true; + } + return false; +} +bool MicroProfileGroupActive(uint32_t nGroupIndex) +{ + MP_ASSERT(nGroupIndex < MICROPROFILE_MAX_GROUPS); + uint32_t nIndex = nGroupIndex / 32; + uint32_t nBit = nGroupIndex % 32; + return ((S.nActiveGroups[nIndex] >> nBit) & 1) == 1; +} + +void MicroProfileToggleGroup(uint32_t nGroup) +{ + if(nGroup < S.nGroupCount) + { + uint32_t nIndex = nGroup / 32; + uint32_t nBit = nGroup % 32; + S.nActiveGroupsWanted[nIndex] ^= (1ll << nBit); + S.nWebSocketDirty |= MICROPROFILE_WEBSOCKET_DIRTY_ENABLED; + } +} +void MicroProfileGroupSetEnabled(uint32_t nGroup) +{ + if(nGroup < S.nGroupCount) + { + uint32_t nIndex = nGroup / 32; + uint32_t nBit = nGroup % 32; + S.nActiveGroupsWanted[nIndex] |= (1ll << nBit); + S.nWebSocketDirty |= MICROPROFILE_WEBSOCKET_DIRTY_ENABLED; + } +} +bool MicroProfileGroupEnabled(uint32_t nGroup) +{ + if(nGroup < S.nGroupCount) + { + uint32_t nIndex = nGroup / 32; + uint32_t nBit = nGroup % 32; + return 0 != (S.nActiveGroupsWanted[nIndex] & (1ll << nBit)); + } + return false; +} +bool MicroProfileCategoryEnabled(uint32_t nCategory) +{ + if(nCategory < S.nCategoryCount) + { + for(uint32_t i = 0; i < MICROPROFILE_MAX_GROUP_INTS; ++i) + { + if(S.CategoryInfo[nCategory].nGroupMask[i] != (S.CategoryInfo[nCategory].nGroupMask[i] & S.nActiveGroupsWanted[i])) + { + return false; + } + } + return true; + } + return false; +} + +bool MicroProfileCategoryDisabled(uint32_t nCategory) +{ + if(nCategory < S.nCategoryCount) + { + for(uint32_t i = 0; i < MICROPROFILE_MAX_GROUP_INTS; ++i) + { + uint32_t ActiveMask = S.nActiveGroupsWanted[i]; + uint32_t CategoryMask = S.CategoryInfo[nCategory].nGroupMask[i]; + + if(0 != (ActiveMask & CategoryMask)) + { + return false; + } + } + return true; + } + return false; +} + +void MicroProfileToggleCategory(uint32_t nCategory) +{ + if(nCategory < S.nCategoryCount) + { + bool bAllSet = true; + for(uint32_t i = 0; i < MICROPROFILE_MAX_GROUP_INTS; ++i) + { + bAllSet = bAllSet && S.CategoryInfo[nCategory].nGroupMask[i] == (S.CategoryInfo[nCategory].nGroupMask[i] & S.nActiveGroupsWanted[i]); + } + for(uint32_t i = 0; i < MICROPROFILE_MAX_GROUP_INTS; ++i) + { + if(bAllSet) + { + S.nActiveGroupsWanted[i] &= ~S.CategoryInfo[nCategory].nGroupMask[i]; + } + else + { + S.nActiveGroupsWanted[i] |= S.CategoryInfo[nCategory].nGroupMask[i]; + } + } + S.nWebSocketDirty |= MICROPROFILE_WEBSOCKET_DIRTY_ENABLED; + } +} + +void MicroProfileSleep(uint32_t nMs) +{ +#ifdef _WIN32 + Sleep(nMs); +#else + usleep(nMs * 1000); +#endif +} + +#if MICROPROFILE_WEBSERVER + +#define MICROPROFILE_EMBED_HTML + +extern const char* g_MicroProfileHtml_begin[]; +extern size_t g_MicroProfileHtml_begin_sizes[]; +extern size_t g_MicroProfileHtml_begin_count; +extern const char* g_MicroProfileHtml_end[]; +extern size_t g_MicroProfileHtml_end_sizes[]; +extern size_t g_MicroProfileHtml_end_count; + +extern const char* g_MicroProfileHtmlLive_begin[]; +extern size_t g_MicroProfileHtmlLive_begin_sizes[]; +extern size_t g_MicroProfileHtmlLive_begin_count; +extern const char* g_MicroProfileHtmlLive_end[]; +extern size_t g_MicroProfileHtmlLive_end_sizes[]; +extern size_t g_MicroProfileHtmlLive_end_count; + +extern const uint32_t uprof_16[]; +extern const uint32_t uprof_16_len; +extern const uint32_t uprof_32[]; +extern const uint32_t uprof_32_len; +extern const uint32_t uprof_192[]; +extern const uint32_t uprof_192_len; +extern const uint32_t uprof_512[]; +extern const uint32_t uprof_512_len; + +typedef void (*MicroProfileWriteCallback)(void* Handle, size_t size, const char* pData); + +uint32_t MicroProfileWebServerPort() +{ + return S.nWebServerPort; +} + +void MicroProfileSetWebServerPort(uint32_t nPort) +{ + if(S.nWebServerPort != nPort) + { + MicroProfileWebServerJoin(); + MicroProfileWebServerStop(); + S.nWebServerPort = nPort; + S.nWebServerDataSent = (uint64_t)-1; // Will cause the web server and its thread to be restarted next time MicroProfileFlip() is called. + } +} + +void MicroProfileDumpFileImmediately(const char* pHtml, const char* pCsv, void* pGpuContext, uint32_t FrameCount) +{ + for(uint32_t i = 0; i < 2; ++i) + { + MicroProfileFlip(pGpuContext); + } + for(uint32_t i = 0; i < MICROPROFILE_GPU_FRAME_DELAY + 1; ++i) + { + MicroProfileFlip(pGpuContext); + } + + uint32_t nDumpMask = 0; + if(pHtml) + { + + size_t nLen = strlen(pHtml); + if(nLen > sizeof(S.HtmlDumpPath) - 1) + { + return; + } + const size_t ExtSize = sizeof(".html") - 1; + if(nLen > ExtSize && 0 == memcmp(".html", pHtml + nLen - ExtSize, ExtSize)) + nLen -= ExtSize; + memcpy(S.HtmlDumpPath, pHtml, nLen); + S.HtmlDumpPath[nLen] = '\0'; + + nDumpMask |= 1; + } + if(pCsv) + { + size_t nLen = strlen(pCsv); + if(nLen > sizeof(S.CsvDumpPath) - 1) + { + return; + } + const size_t ExtSize = sizeof(".csv") - 1; + if(nLen > ExtSize && 0 == memcmp(".csv", pCsv + nLen - ExtSize, ExtSize)) + nLen -= ExtSize; + memcpy(S.CsvDumpPath, pCsv, nLen + 1); + S.CsvDumpPath[nLen] = '\0'; + + nDumpMask |= 2; + } + std::lock_guard Lock(MicroProfileMutex()); + S.nDumpFileNextFrame = nDumpMask; + S.nDumpSpikeMask = 0; + S.nDumpFileCountDown = 0; + S.DumpFrameCount = FrameCount; + + MicroProfileDumpToFile(); +} +void MicroProfileDumpFile(const char* pHtml, const char* pCsv, float fCpuSpike, float fGpuSpike, uint32_t FrameCount) +{ + S.fDumpCpuSpike = fCpuSpike; + S.fDumpGpuSpike = fGpuSpike; + S.DumpFrameCount = FrameCount; + uint32_t nDumpMask = 0; + if(pHtml) + { + size_t nLen = strlen(pHtml); + if(nLen > sizeof(S.HtmlDumpPath) - 1) + { + return; + } + const size_t ExtSize = sizeof(".html") - 1; + if(nLen > ExtSize && 0 == memcmp(".html", pHtml + nLen - ExtSize, ExtSize)) + nLen -= ExtSize; + memcpy(S.HtmlDumpPath, pHtml, nLen); + S.HtmlDumpPath[nLen] = '\0'; + + nDumpMask |= 1; + } + if(pCsv) + { + size_t nLen = strlen(pCsv); + if(nLen > sizeof(S.CsvDumpPath) - 1) + { + return; + } + const size_t ExtSize = sizeof(".csv") - 1; + if(nLen > ExtSize && 0 == memcmp(".csv", pCsv + nLen - ExtSize, ExtSize)) + nLen -= ExtSize; + memcpy(S.CsvDumpPath, pCsv, nLen); + S.CsvDumpPath[nLen] = '\0'; + + nDumpMask |= 2; + } + if(fCpuSpike > 0.f || fGpuSpike > 0.f) + { + S.nDumpFileNextFrame = 0; + S.nDumpSpikeMask = nDumpMask; + } + else + { + std::lock_guard Lock(MicroProfileMutex()); + S.nDumpFileNextFrame = nDumpMask; + S.nDumpSpikeMask = 0; + S.nDumpFileCountDown = 0; + + MicroProfileDumpToFile(); + } +} + +struct MicroProfilePrintfArgs +{ + MicroProfileWriteCallback CB; + void* Handle; +}; + +char* MicroProfilePrintfCallback(const char* buf, void* user, int len) +{ + MicroProfilePrintfArgs* A = (MicroProfilePrintfArgs*)user; + (A->CB)(A->Handle, len, buf); + return const_cast(buf); +}; + +void MicroProfilePrintf(MicroProfileWriteCallback CB, void* Handle, const char* pFmt, ...) +{ + va_list args; + va_start(args, pFmt); + MicroProfilePrintfArgs A; + A.CB = CB; + A.Handle = Handle; + char Buffer[STB_SPRINTF_MIN]; + int size = stbsp_vsprintfcb(MicroProfilePrintfCallback, (void*)&A, Buffer, pFmt, args); + (void)size; + va_end(args); +} + +void MicroProfileGetFramesToDump(uint64_t nStartFrameId, uint32_t nMaxFrames, uint32_t& nFirstFrame, uint32_t& nLastFrame, uint32_t& nNumFrames) +{ + nFirstFrame = (uint32_t)-1; + nNumFrames = 0; + + if(nStartFrameId != (uint64_t)-1) + { + // search for the frane + for(uint32_t i = 0; i < MICROPROFILE_MAX_FRAME_HISTORY; ++i) + { + if(S.Frames[i].nFrameId == nStartFrameId) + { + nFirstFrame = i; + break; + } + } + if(nFirstFrame != (uint32_t)-1) + { + nLastFrame = S.nFrameCurrent; + uint32_t nDistance = (MICROPROFILE_MAX_FRAME_HISTORY + nFirstFrame - nLastFrame) % MICROPROFILE_MAX_FRAME_HISTORY; + nNumFrames = MicroProfileMin(nDistance, (uint32_t)nMaxFrames); + } + } + + if(nNumFrames == 0) + { + nNumFrames = (MICROPROFILE_MAX_FRAME_HISTORY - MICROPROFILE_GPU_FRAME_DELAY - 3); // leave a few to not overwrite + nNumFrames = MicroProfileMin(nNumFrames, (uint32_t)nMaxFrames); + nFirstFrame = (S.nFrameCurrent + MICROPROFILE_MAX_FRAME_HISTORY - nNumFrames) % MICROPROFILE_MAX_FRAME_HISTORY; + } + + nLastFrame = (nFirstFrame + nNumFrames) % MICROPROFILE_MAX_FRAME_HISTORY; +} + +#define printf(...) MicroProfilePrintf(CB, Handle, __VA_ARGS__) + +void MicroProfileDumpCsvWithConfig(MicroProfileWriteCallback CB, void* Handle, uint32_t nFirstFrame, uint32_t nLastFrame, uint32_t nNumFrames) +{ + uint32_t NumTimers = S.CsvConfig.NumTimers; + uint32_t NumGroups = S.CsvConfig.NumGroups; + uint32_t NumCounters = S.CsvConfig.NumCounters; + uint16_t* TimerIndices = S.CsvConfig.TimerIndices; + uint16_t* GroupIndices = S.CsvConfig.GroupIndices; + uint64_t* FrameData = S.CsvConfig.FrameData; + uint16_t* CounterIndices = S.CsvConfig.CounterIndices; + uint32_t TotalElements = S.CsvConfig.TotalElements; + uint32_t Offset = 0; + bool UseFrameTime = 0 != (MICROPROFILE_CSV_FLAG_FRAME_TIME & S.CsvConfig.Flags); + const char** pTimerNames = S.CsvConfig.pTimerNames; + const char** pGroupNames = S.CsvConfig.pGroupNames; + const char** pCounterNames = S.CsvConfig.pCounterNames; + if(UseFrameTime) + printf("Time"); + else + printf("FrameNumber"); + for(uint32_t i = 0; i < NumTimers; ++i, ++Offset) + printf(", %s", pTimerNames[i] ? pTimerNames[i] : S.TimerInfo[TimerIndices[i]].pName); + + for(uint32_t i = 0; i < NumGroups; ++i, ++Offset) + printf(", %s", pGroupNames[i] ? pGroupNames[i] : S.GroupInfo[GroupIndices[i]].pName); + for(uint32_t i = 0; i < NumCounters; ++i, ++Offset) + printf(", %s", pCounterNames[i] ? pCounterNames[i] : S.CounterInfo[CounterIndices[i]].pName); + printf("\n"); + + float* fToMsTimer = (float*)alloca(sizeof(float) * NumTimers); + float* fToMsGroup = (float*)alloca(sizeof(float) * NumGroups); + float fToMsCPU = MicroProfileTickToMsMultiplier(MicroProfileTicksPerSecondCpu()); + float fToMsGPU = MicroProfileTickToMsMultiplier(MicroProfileTicksPerSecondGpu()); + + for(uint32_t i = 0; i < NumTimers; ++i) + fToMsTimer[i] = S.TimerInfo[TimerIndices[i]].Type == MicroProfileTokenTypeGpu ? fToMsGPU : fToMsCPU; + for(uint32_t i = 0; i < NumGroups; ++i) + fToMsGroup[i] = S.GroupInfo[GroupIndices[i]].Type == MicroProfileTokenTypeGpu ? fToMsGPU : fToMsCPU; + + uint64_t TickStart = S.Frames[nFirstFrame % MICROPROFILE_MAX_FRAME_HISTORY].nFrameStartCpu; + for(uint32_t i = 0; i < nNumFrames; ++i) + { + uint32_t FrameIndex = ((nFirstFrame + i) % MICROPROFILE_MAX_FRAME_HISTORY); + uint64_t TickFrame = S.Frames[FrameIndex].nFrameStartCpu; + uint64_t* Data = FrameData + TotalElements * FrameIndex; + if(UseFrameTime) + printf("%f", (TickFrame - TickStart) * fToMsCPU); + else + printf("%d", i); + Offset = 0; + for(uint32_t j = 0; j < NumTimers; ++j) + printf(", %f", Data[Offset++] * fToMsTimer[j]); + for(uint32_t j = 0; j < NumGroups; ++j) + printf(", %f", Data[Offset++] * fToMsGroup[j]); + for(uint32_t j = 0; j < NumCounters; ++j) + { + if(S.CounterInfo[CounterIndices[j]].nFlags & MICROPROFILE_COUNTER_FLAG_DOUBLE) + { + printf(", %f", ((double*)Data)[Offset++]); + } + else + { + printf(", %lld", Data[Offset++]); + } + } + printf("\n"); + } +} +void MicroProfileDumpCsvTimerFrames(MicroProfileWriteCallback CB, void* Handle, uint32_t nFirstFrame, uint32_t nLastFrame, uint32_t nNumFrames) +{ + MP_ASSERT(S.FrameExtraCounterData); + uint32_t TotalTimers = S.nTotalTimers; + float* fToMs = (float*)alloca(sizeof(float) * TotalTimers); + float fToMsCPU = MicroProfileTickToMsMultiplier(MicroProfileTicksPerSecondCpu()); + float fToMsGPU = MicroProfileTickToMsMultiplier(MicroProfileTicksPerSecondGpu()); + + for(uint32_t i = 0; i < TotalTimers; ++i) + fToMs[i] = S.TimerInfo[i].Type == MicroProfileTokenTypeGpu ? fToMsGPU : fToMsCPU; + + for(uint32_t i = 0; i < TotalTimers; ++i) + { + printf(i == 0 ? "FrameNumber, \"%s\"" : ",\"%s\"", S.TimerInfo[i].pName); + } + printf("\n"); + + for(uint32_t i = 0; i < nNumFrames; ++i) + { + // printf("%d", i) MicroProfileFrame& F = S.Frames[(i + nFirstFrame) % MICROPROFILE_MAX_FRAME_HISTORY]; + MicroProfileFrameExtraCounterData* Data = S.FrameExtraCounterData; + uint32_t NumTimers = 0; + uint32_t j; + printf("%d", i); + Data += ((i + nFirstFrame) % MICROPROFILE_MAX_FRAME_HISTORY); + NumTimers = MicroProfileMin(TotalTimers, (uint32_t)Data->NumTimers); + for(j = 0; j < NumTimers; ++j) + { + printf(",%f", Data->Timers[j] * fToMs[j]); + } + for(; j < TotalTimers; ++j) + printf(",0"); + printf("\n"); + } +} + +void MicroProfileDumpCsvGroupFrames(MicroProfileWriteCallback CB, void* Handle, uint32_t nFirstFrame, uint32_t nLastFrame, uint32_t nNumFrames) +{ + MP_ASSERT(S.FrameExtraCounterData); + float fToMsCPU = MicroProfileTickToMsMultiplier(MicroProfileTicksPerSecondCpu()); + float fToMsGPU = MicroProfileTickToMsMultiplier(MicroProfileTicksPerSecondGpu()); + + uint32_t nGroupCount = S.nGroupCount; + + float* fToMs = (float*)alloca(sizeof(float) * nGroupCount); + for(uint32_t i = 0; i < nGroupCount; ++i) + fToMs[i] = S.GroupInfo[i].Type == MicroProfileTokenTypeGpu ? fToMsGPU : fToMsCPU; + + for(uint32_t i = 0; i < nGroupCount; ++i) + { + printf(i == 0 ? "FrameNumber, \"%s\"" : ",\"%s\"", S.GroupInfo[i].pName); + } + + printf("\n"); + for(uint32_t i = 0; i < nNumFrames; ++i) + { + MicroProfileFrameExtraCounterData* Data = S.FrameExtraCounterData; + uint32_t NumGroups = 0; + uint32_t j; + printf("%d", i); + Data += ((i + nFirstFrame) % MICROPROFILE_MAX_FRAME_HISTORY); + NumGroups = MicroProfileMin(nGroupCount, (uint32_t)Data->NumGroups); + for(j = 0; j < NumGroups; ++j) + { + printf(",%f", Data->Groups[j] * fToMs[j]); + } + for(; j < nGroupCount; ++j) + printf(",0"); + printf("\n"); + } +} + +void MicroProfileDumpCsv(uint32_t nDumpFrameCount) +{ + uint32_t nNumFrames, nFirstFrame, nLastFrame; + MicroProfileGetFramesToDump((uint64_t)-1, nDumpFrameCount, nFirstFrame, nLastFrame, nNumFrames); + + char Path[MICROPROFILE_MAX_PATH]; + int Length; + if(S.FrameExtraCounterData) + { + Length = snprintf(Path, sizeof(S.CsvDumpPath), "%s_timer_frames.csv", S.CsvDumpPath); + if(Length > 0 && Length < MICROPROFILE_MAX_PATH) + { + FILE* F = fopen(Path, "w"); + if(F) + { + MicroProfileDumpCsvTimerFrames(MicroProfileWriteFile, F, nFirstFrame, nLastFrame, nNumFrames); + fclose(F); + } + } + Length = snprintf(Path, sizeof(S.CsvDumpPath), "%s_group_frames.csv", S.CsvDumpPath); + if(Length > 0 && Length < MICROPROFILE_MAX_PATH) + { + FILE* F = fopen(Path, "w"); + if(F) + { + MicroProfileDumpCsvGroupFrames(MicroProfileWriteFile, F, nFirstFrame, nLastFrame, nNumFrames); + fclose(F); + } + } + } + if(S.CsvConfig.State == MicroProfileCsvConfig::ACTIVE) + { + Length = snprintf(Path, sizeof(S.CsvDumpPath), "%s_custom.csv", S.CsvDumpPath); + if(Length > 0 && Length < MICROPROFILE_MAX_PATH) + { + FILE* F = fopen(Path, "w"); + if(F) + { + MicroProfileDumpCsvWithConfig(MicroProfileWriteFile, F, nFirstFrame, nLastFrame, nNumFrames); + fclose(F); + } + } + } +} + +void MicroProfileDumpCsvLegacy(MicroProfileWriteCallback CB, void* Handle) +{ + uint32_t nAggregateFrames = S.nAggregateFrames ? S.nAggregateFrames : 1; + float fToMsCPU = MicroProfileTickToMsMultiplier(MicroProfileTicksPerSecondCpu()); + float fToMsGPU = MicroProfileTickToMsMultiplier(MicroProfileTicksPerSecondGpu()); + + printf("frames,%d\n", nAggregateFrames); + printf("group,name,average,max,callaverage\n"); + + uint32_t nNumTimers = S.nTotalTimers; + uint32_t nBlockSize = 2 * nNumTimers; + float* pTimers = (float*)alloca(nBlockSize * 9 * sizeof(float)); + float* pAverage = pTimers + nBlockSize; + float* pMax = pTimers + 2 * nBlockSize; + float* pMin = pTimers + 3 * nBlockSize; + float* pCallAverage = pTimers + 4 * nBlockSize; + float* pTimersExclusive = pTimers + 5 * nBlockSize; + float* pAverageExclusive = pTimers + 6 * nBlockSize; + float* pMaxExclusive = pTimers + 7 * nBlockSize; + float* pTotal = pTimers + 8 * nBlockSize; + + MicroProfileCalcAllTimers(pTimers, pAverage, pMax, pMin, pCallAverage, pTimersExclusive, pAverageExclusive, pMaxExclusive, pTotal, nNumTimers); + + for(uint32_t i = 0; i < S.nTotalTimers; ++i) + { + uint32_t nIdx = i * 2; + printf("\"%s\",\"%s\",%f,%f,%f\n", S.TimerInfo[i].pName, S.GroupInfo[S.TimerInfo[i].nGroupIndex].pName, pAverage[nIdx], pMax[nIdx], pCallAverage[nIdx]); + } + + printf("\n\n"); + + printf("group,average,max,total\n"); + for(uint32_t j = 0; j < MICROPROFILE_MAX_GROUPS; ++j) + { + const char* pGroupName = S.GroupInfo[j].pName; + float fToMs = S.GroupInfo[j].Type == MicroProfileTokenTypeGpu ? fToMsGPU : fToMsCPU; + if(pGroupName[0] != '\0') + { + printf("\"%s\",%.3f,%.3f,%.3f\n", pGroupName, fToMs * S.AggregateGroup[j] / nAggregateFrames, fToMs * S.AggregateGroup[j] / nAggregateFrames, fToMs * S.AggregateGroup[j]); + } + } + + printf("\n\n"); + printf("group,thread,average,total\n"); + for(uint32_t j = 0; j < MICROPROFILE_MAX_GROUPS; ++j) + { + for(uint32_t i = 0; i < S.nNumLogs; ++i) + { + if(S.Pool[i]) + { + const char* pThreadName = &S.Pool[i]->ThreadName[0]; + // MicroProfilePrintf(CB, Handle, "var ThreadGroupTime%d = [", i); + float fToMs = S.Pool[i]->nGpu ? fToMsGPU : fToMsCPU; + { + uint64_t nTicks = S.Pool[i]->nAggregateGroupTicks[j]; + float fTime = nTicks / nAggregateFrames * fToMs; + float fTimeTotal = nTicks * fToMs; + if(fTimeTotal > 0.01f) + { + const char* pGroupName = S.GroupInfo[j].pName; + printf("\"%s\",\"%s\",%.3f,%.3f\n", pGroupName, pThreadName, fTime, fTimeTotal); + } + } + } + } + } + + printf("\n\n"); + printf("frametimecpu\n"); + + const uint32_t nCount = MICROPROFILE_MAX_FRAME_HISTORY - MICROPROFILE_GPU_FRAME_DELAY - 3; + const uint32_t nStart = S.nFrameCurrent; + for(uint32_t i = nCount; i > 0; i--) + { + uint32_t nFrame = (nStart + MICROPROFILE_MAX_FRAME_HISTORY - i) % MICROPROFILE_MAX_FRAME_HISTORY; + uint32_t nFrameNext = (nStart + MICROPROFILE_MAX_FRAME_HISTORY - i + 1) % MICROPROFILE_MAX_FRAME_HISTORY; + uint64_t nTicks = S.Frames[nFrameNext].nFrameStartCpu - S.Frames[nFrame].nFrameStartCpu; + printf("%f,", nTicks * fToMsCPU); + } + printf("\n"); + + printf("\n\n"); + printf("frametimegpu\n"); + + for(uint32_t i = nCount; i > 0; i--) + { + uint32_t nFrame = (nStart + MICROPROFILE_MAX_FRAME_HISTORY - i) % MICROPROFILE_MAX_FRAME_HISTORY; + uint32_t nFrameNext = (nStart + MICROPROFILE_MAX_FRAME_HISTORY - i + 1) % MICROPROFILE_MAX_FRAME_HISTORY; + uint64_t nTicks = S.Frames[nFrameNext].nFrameStartGpu - S.Frames[nFrame].nFrameStartGpu; + printf("%f,", nTicks * fToMsGPU); + } + printf("\n\n"); +} +#undef printf + +void MicroProfileDumpCsvLegacy() +{ + char Path[MICROPROFILE_MAX_PATH]; + int Length = snprintf(Path, sizeof(S.CsvDumpPath), "%s.csv", S.CsvDumpPath); + if(Length > 0 && Length < MICROPROFILE_MAX_PATH) + { + FILE* F = fopen(Path, "w"); + if(F) + { + MicroProfileDumpCsvLegacy(MicroProfileWriteFile, F); + fclose(F); + } + } +} + +void MicroProfileDumpHtmlLive(MicroProfileWriteCallback CB, void* Handle) +{ + for(size_t i = 0; i < g_MicroProfileHtmlLive_begin_count; ++i) + { + CB(Handle, g_MicroProfileHtmlLive_begin_sizes[i] - 1, g_MicroProfileHtmlLive_begin[i]); + } + for(size_t i = 0; i < g_MicroProfileHtmlLive_end_count; ++i) + { + CB(Handle, g_MicroProfileHtmlLive_end_sizes[i] - 1, g_MicroProfileHtmlLive_end[i]); + } +} +void MicroProfileGetCoreInformation() +{ +#ifdef _WIN32 + unsigned long BufferSize; + HANDLE Process = GetCurrentProcess(); + GetSystemCpuSetInformation(nullptr, 0, &BufferSize, Process, 0); + char* Buffer = (char*)alloca(BufferSize); + if(!GetSystemCpuSetInformation((PSYSTEM_CPU_SET_INFORMATION)Buffer, BufferSize, &BufferSize, Process, 0)) + { + return; + } + for(ULONG Size = 0; Size < BufferSize;) + { + PSYSTEM_CPU_SET_INFORMATION CpuSet = reinterpret_cast(Buffer); + if(CpuSet->Type == CPU_SET_INFORMATION_TYPE::CpuSetInformation) + { + if(CpuSet->CpuSet.CoreIndex < MICROPROFILE_MAX_CPU_CORES) + { + S.CoreEfficiencyClass[CpuSet->CpuSet.LogicalProcessorIndex] = CpuSet->CpuSet.EfficiencyClass; + } + } + Buffer += CpuSet->Size; + Size += CpuSet->Size; + } +#endif +} + +void MicroProfileDumpHtml(MicroProfileWriteCallback CB, void* Handle, uint64_t nMaxFrames, const char* pHost, uint64_t nStartFrameId = (uint64_t)-1) +{ + // Stall pushing of timers + uint64_t nActiveGroup[MICROPROFILE_MAX_GROUP_INTS]; + memcpy(nActiveGroup, S.nActiveGroups, sizeof(S.nActiveGroups)); + memset(S.nActiveGroups, 0, sizeof(S.nActiveGroups)); + bool AnyActive = S.AnyActive; + S.AnyActive = false; + + S.nPauseTicks = MP_TICK(); + + MicroProfileGetCoreInformation(); + + if(S.bContextSwitchRunning) + { + auto StallForContextSwitchThread = []() + { + int64_t nPauseTicks = S.nPauseTicks; + int64_t nContextSwitchStalledTick = S.nContextSwitchStalledTick; + return (nPauseTicks - nContextSwitchStalledTick) > 0; + }; + int SleepMs = 1; + while(S.bContextSwitchRunning && !S.bContextSwitchStop && StallForContextSwitchThread()) + { + MicroProfileSleep(SleepMs); + SleepMs = SleepMs * 2 / 3; + SleepMs = MicroProfileMin(128, SleepMs); + } + int64_t TicksAfterStall = MP_TICK(); + uprintf("Stalled %7.2fms for context switch data\n", MicroProfileTickToMsMultiplierCpu() * (TicksAfterStall - S.nPauseTicks)); + } + + MicroProfileHashTable StringsHashTable; + MicroProfileHashTableInit(&StringsHashTable, 50, 25, MicroProfileHashTableCompareString, MicroProfileHashTableHashString); + + defer + { + MicroProfileHashTableDestroy(&StringsHashTable); + }; + + MicroProfileCounterFetchCounters(); + for(size_t i = 0; i < g_MicroProfileHtml_begin_count; ++i) + { + CB(Handle, g_MicroProfileHtml_begin_sizes[i] - 1, g_MicroProfileHtml_begin[i]); + } + // dump info + uint64_t nTicks = MP_TICK(); + + float fToMsCPU = MicroProfileTickToMsMultiplier(MicroProfileTicksPerSecondCpu()); + float fToMsGPU = MicroProfileTickToMsMultiplier(MicroProfileTicksPerSecondGpu()); + float fAggregateMs = fToMsCPU * (nTicks - S.nAggregateFlipTick); + + uint32_t nNumFrames = 0; + uint32_t nFirstFrame = (uint32_t)-1; + if(nStartFrameId != (uint64_t)-1) + { + // search for the frane + for(uint32_t i = 0; i < MICROPROFILE_MAX_FRAME_HISTORY; ++i) + { + if(S.Frames[i].nFrameId == nStartFrameId) + { + nFirstFrame = i; + break; + } + } + if(nFirstFrame != (uint32_t)-1) + { + uint32_t nLastFrame = S.nFrameCurrent; + uint32_t nDistance = (MICROPROFILE_MAX_FRAME_HISTORY + nFirstFrame - nLastFrame) % MICROPROFILE_MAX_FRAME_HISTORY; + nNumFrames = MicroProfileMin(nDistance, (uint32_t)nMaxFrames); + } + } + + if(nNumFrames == 0) + { + nNumFrames = (MICROPROFILE_MAX_FRAME_HISTORY - MICROPROFILE_GPU_FRAME_DELAY - 3); // leave a few to not overwrite + nNumFrames = MicroProfileMin(nNumFrames, (uint32_t)nMaxFrames); + nFirstFrame = (S.nFrameCurrent + MICROPROFILE_MAX_FRAME_HISTORY - nNumFrames) % MICROPROFILE_MAX_FRAME_HISTORY; + } + + uint32_t nLastFrame = (nFirstFrame + nNumFrames) % MICROPROFILE_MAX_FRAME_HISTORY; + MP_ASSERT(nFirstFrame < MICROPROFILE_MAX_FRAME_HISTORY); + MP_ASSERT(nLastFrame < MICROPROFILE_MAX_FRAME_HISTORY); + + MicroProfilePrintf(CB, Handle, "S.DumpHost = '%s';\n", pHost ? pHost : ""); + time_t CaptureTime; + time(&CaptureTime); + MicroProfilePrintf(CB, Handle, "S.DumpUtcCaptureTime = %ld;\n", CaptureTime); + MicroProfilePrintf(CB, Handle, "S.AggregateInfo = {'Frames':%d, 'Time':%f};\n", S.nAggregateFrames, fAggregateMs); + + // categories + MicroProfilePrintf(CB, Handle, "S.CategoryInfo = Array(%d);\n", S.nCategoryCount); + for(uint32_t i = 0; i < S.nCategoryCount; ++i) + { + MicroProfilePrintf(CB, Handle, "S.CategoryInfo[%d] = \"%s\";\n", i, S.CategoryInfo[i].pName); + } + + // groups + MicroProfilePrintf(CB, Handle, "S.GroupInfo = Array(%d);\n\n", S.nGroupCount + 1); + uint32_t nAggregateFrames = S.nAggregateFrames ? S.nAggregateFrames : 1; + float fRcpAggregateFrames = 1.f / nAggregateFrames; + (void)fRcpAggregateFrames; + char ColorString[32]; + for(uint32_t i = 0; i < S.nGroupCount; ++i) + { + MP_ASSERT(i == S.GroupInfo[i].nGroupIndex); + float fToMs = S.GroupInfo[i].Type == MicroProfileTokenTypeCpu ? fToMsCPU : fToMsGPU; + const char* pColorStr = ""; + if(S.GroupInfo[i].nColor != 0x42) + { + stbsp_snprintf(ColorString, + sizeof(ColorString) - 1, + "#%02x%02x%02x", + MICROPROFILE_UNPACK_RED(S.GroupInfo[i].nColor) & 0xff, + MICROPROFILE_UNPACK_GREEN(S.GroupInfo[i].nColor) & 0xff, + MICROPROFILE_UNPACK_BLUE(S.GroupInfo[i].nColor) & 0xff); + pColorStr = &ColorString[0]; + } + MicroProfilePrintf(CB, + Handle, + "S.GroupInfo[%d] = MakeGroup(%d, \"%s\", %d, %d, %d, %f, %f, %f, '%s');\n", + S.GroupInfo[i].nGroupIndex, + S.GroupInfo[i].nGroupIndex, + S.GroupInfo[i].pName, + S.GroupInfo[i].nCategory, + S.GroupInfo[i].nNumTimers, + S.GroupInfo[i].Type == MicroProfileTokenTypeGpu ? 1 : 0, + fToMs * S.AggregateGroup[i], + fToMs * S.AggregateGroup[i] / nAggregateFrames, + fToMs * S.AggregateGroupMax[i], + pColorStr); + } + uint32_t nUncategorized = S.nGroupCount; + + MicroProfilePrintf(CB, + Handle, + "S.GroupInfo[%d] = MakeGroup(%d, \"%s\", %d, %d, %d, %f, %f, %f, 'grey');\n", + nUncategorized, + nUncategorized, + "Uncategorized", + -1, + 1, + // S.GroupInfo[i].Type == MicroProfileTokenTypeGpu ? 1 : + 0, + 0, + 0, + 0); + + // timers + + uint32_t nNumTimers = S.nTotalTimers; + uint32_t nBlockSize = 2 * nNumTimers; + float* pTimers = (float*)alloca(nBlockSize * 9 * sizeof(float)); + float* pAverage = pTimers + nBlockSize; + float* pMax = pTimers + 2 * nBlockSize; + float* pMin = pTimers + 3 * nBlockSize; + float* pCallAverage = pTimers + 4 * nBlockSize; + float* pTimersExclusive = pTimers + 5 * nBlockSize; + float* pAverageExclusive = pTimers + 6 * nBlockSize; + float* pMaxExclusive = pTimers + 7 * nBlockSize; + float* pTotal = pTimers + 8 * nBlockSize; + + MicroProfileCalcAllTimers(pTimers, pAverage, pMax, pMin, pCallAverage, pTimersExclusive, pAverageExclusive, pMaxExclusive, pTotal, nNumTimers); + + MicroProfilePrintf(CB, Handle, "\nS.TimerInfo = Array(%d);\n\n", S.nTotalTimers); + for(uint32_t i = 0; i < S.nTotalTimers; ++i) + { + uint32_t nIdx = i * 2; + MP_ASSERT(i == S.TimerInfo[i].nTimerIndex); + MicroProfilePrintf(CB, Handle, "S.Meta%d = [];\n", i); + MicroProfilePrintf(CB, Handle, "S.MetaAvg%d = [];\n", i); + MicroProfilePrintf(CB, Handle, "S.MetaMax%d = [];\n", i); + + uint32_t nColor = S.TimerInfo[i].nColor; + uint32_t nColorDark = (nColor >> 1) & ~0x80808080; + MicroProfilePrintf(CB, + Handle, + "S.TimerInfo[%d] = MakeTimer(%d, \"%s\", %d, '#%02x%02x%02x','#%02x%02x%02x', %f, %f, %f, %f, %f, %f, %d, %f, S.Meta%d, S.MetaAvg%d, S.MetaMax%d, %d);\n", + S.TimerInfo[i].nTimerIndex, + S.TimerInfo[i].nTimerIndex, + S.TimerInfo[i].pName, + S.TimerInfo[i].nGroupIndex, + MICROPROFILE_UNPACK_RED(nColor) & 0xff, + MICROPROFILE_UNPACK_GREEN(nColor) & 0xff, + MICROPROFILE_UNPACK_BLUE(nColor) & 0xff, + MICROPROFILE_UNPACK_RED(nColorDark) & 0xff, + MICROPROFILE_UNPACK_GREEN(nColorDark) & 0xff, + MICROPROFILE_UNPACK_BLUE(nColorDark) & 0xff, + pAverage[nIdx], + pMax[nIdx], + pMin[nIdx], + pAverageExclusive[nIdx], + pMaxExclusive[nIdx], + pCallAverage[nIdx], + S.Aggregate[i].nCount, + pTotal[nIdx], + i, + i, + i, + S.TimerInfo[i].Flags); + } + + uint32_t nTotalTimersExt = S.nTotalTimers; + { + for(uint32_t j = 0; j < S.nNumLogs; ++j) + { + MicroProfileThreadLog* pLog = S.Pool[j]; + uint32_t nLogStart = S.Frames[nFirstFrame].nLogStart[j]; + uint32_t nLogEnd = S.Frames[nLastFrame].nLogStart[j]; + uint64_t nLogType; + if(nLogStart != nLogEnd) + { + for(uint32_t k = nLogStart; k != nLogEnd; k = (k + 1) % MICROPROFILE_BUFFER_SIZE) + { + uint64_t v = pLog->Log[k]; + nLogType = MicroProfileLogGetType(v); + uint32_t tidx = MicroProfileLogGetTimerIndex(v); + if((nLogType == MP_LOG_ENTER || nLogType == MP_LOG_LEAVE) && tidx == ETOKEN_CSTR_PTR) + { + MP_ASSERT(k + 1 != nLogEnd); + uint64_t v1 = pLog->Log[(k + 1) % MICROPROFILE_BUFFER_SIZE]; + const char* pString = (const char*)MicroProfileLogGetExtendedPayloadNoDataPtr(v1); + uintptr_t value; + if(!MicroProfileHashTableGet(&StringsHashTable, (uint64_t)pString, &value)) + { + uintptr_t nTimerIndex = nTotalTimersExt++; + MicroProfileHashTableSet(&StringsHashTable, (uint64_t)pString, nTimerIndex); + MicroProfilePrintf( + CB, Handle, "S.TimerInfo.push(MakeTimer(%d, \"%s\", %d, '#000000','#000000', 0, 0, 0, 0, 0, 0, 0, 0, null, null, null, 0));\n", nTimerIndex, pString, nUncategorized); + } + } + } + } + } + } + + MicroProfilePrintf(CB, Handle, "\nS.ThreadNames = ["); + for(uint32_t i = 0; i < S.nNumLogs; ++i) + { + if(S.Pool[i]) + { + MicroProfilePrintf(CB, Handle, "'%s',", S.Pool[i]->ThreadName); + } + else + { + MicroProfilePrintf(CB, Handle, "'Thread %d',", i); + } + } + MicroProfilePrintf(CB, Handle, "];\n\n"); + MicroProfilePrintf(CB, Handle, "\nS.ISGPU = ["); + for(uint32_t i = 0; i < S.nNumLogs; ++i) + { + MicroProfilePrintf(CB, Handle, "%d,", (S.Pool[i] && S.Pool[i]->nGpu) ? 1 : 0); + } + MicroProfilePrintf(CB, Handle, "];\n\n"); + + for(uint32_t i = 0; i < S.nNumLogs; ++i) + { + if(S.Pool[i]) + { + MicroProfilePrintf(CB, Handle, "S.ThreadGroupTime%d = [", i); + float fToMs = S.Pool[i]->nGpu ? fToMsGPU : fToMsCPU; + for(uint32_t j = 0; j < MICROPROFILE_MAX_GROUPS; ++j) + { + MicroProfilePrintf(CB, Handle, "%f,", S.Pool[i]->nAggregateGroupTicks[j] / nAggregateFrames * fToMs); + } + MicroProfilePrintf(CB, Handle, "];\n"); + } + } + MicroProfilePrintf(CB, Handle, "\nS.ThreadGroupTimeArray = ["); + for(uint32_t i = 0; i < S.nNumLogs; ++i) + { + if(S.Pool[i]) + { + MicroProfilePrintf(CB, Handle, "S.ThreadGroupTime%d,", i); + } + } + MicroProfilePrintf(CB, Handle, "];\n"); + + for(uint32_t i = 0; i < S.nNumLogs; ++i) + { + if(S.Pool[i]) + { + MicroProfilePrintf(CB, Handle, "S.ThreadGroupTimeTotal%d = [", i); + float fToMs = S.Pool[i]->nGpu ? fToMsGPU : fToMsCPU; + for(uint32_t j = 0; j < MICROPROFILE_MAX_GROUPS; ++j) + { + MicroProfilePrintf(CB, Handle, "%f,", S.Pool[i]->nAggregateGroupTicks[j] * fToMs); + } + MicroProfilePrintf(CB, Handle, "];\n"); + } + } + MicroProfilePrintf(CB, Handle, "\nS.ThreadGroupTimeTotalArray = ["); + for(uint32_t i = 0; i < S.nNumLogs; ++i) + { + if(S.Pool[i]) + { + MicroProfilePrintf(CB, Handle, "S.ThreadGroupTimeTotal%d,", i); + } + } + MicroProfilePrintf(CB, Handle, "];"); + + MicroProfilePrintf(CB, Handle, "\nS.ThreadIds = ["); + for(uint32_t i = 0; i < S.nNumLogs; ++i) + { + if(S.Pool[i]) + { + MicroProfileThreadIdType ThreadId = S.Pool[i]->nThreadId; + if(!ThreadId) + { + ThreadId = (MicroProfileThreadIdType)-1; + } + MicroProfilePrintf(CB, Handle, "%" PRIu64 ",", (uint64_t)ThreadId); + } + else + { + MicroProfilePrintf(CB, Handle, "-1,"); + } + } + MicroProfilePrintf(CB, Handle, "];\n\n"); + + for(int i = 0; i < (int)S.nNumCounters; ++i) + { + bool IsDouble = (S.CounterInfo[i].nFlags & MICROPROFILE_COUNTER_FLAG_DOUBLE) != 0; + if(0 != (S.CounterInfo[i].nFlags & MICROPROFILE_COUNTER_FLAG_DETAILED) && !IsDouble) + { + int64_t nCounterMax = S.nCounterMax[i]; + int64_t nCounterMin = S.nCounterMin[i]; + uint32_t nBaseIndex = S.nCounterHistoryPut; + MicroProfilePrintf(CB, Handle, "\nS.CounterHistoryArray%d =[", i); + for(uint32_t j = 0; j < MICROPROFILE_GRAPH_HISTORY; ++j) + { + uint32_t nHistoryIndex = (nBaseIndex + j) % MICROPROFILE_GRAPH_HISTORY; + int64_t nValue = MicroProfileClamp(S.nCounterHistory[nHistoryIndex][i], nCounterMin, nCounterMax); + MicroProfilePrintf(CB, Handle, "%lld,", nValue); + } + MicroProfilePrintf(CB, Handle, "];\n"); + + int64_t nCounterHeightBase = nCounterMax; + int64_t nCounterOffset = 0; + if(nCounterMin < 0) + { + nCounterHeightBase = nCounterMax - nCounterMin; + nCounterOffset = -nCounterMin; + } + double fRcp = nCounterHeightBase ? (1.0 / nCounterHeightBase) : 0; + + MicroProfilePrintf(CB, Handle, "\nS.CounterHistoryArrayPrc%d =[", i); + for(uint32_t j = 0; j < MICROPROFILE_GRAPH_HISTORY; ++j) + { + uint32_t nHistoryIndex = (nBaseIndex + j) % MICROPROFILE_GRAPH_HISTORY; + int64_t nValue = MicroProfileClamp(S.nCounterHistory[nHistoryIndex][i], nCounterMin, nCounterMax); + float fPrc = (nValue + nCounterOffset) * fRcp; + MicroProfilePrintf(CB, Handle, "%f,", fPrc); + } + MicroProfilePrintf(CB, Handle, "];\n"); + MicroProfilePrintf(CB, Handle, "S.CounterHistory%d = MakeCounterHistory(%d, S.CounterHistoryArray%d, S.CounterHistoryArrayPrc%d)\n", i, i, i, i); + } + else + { + MicroProfilePrintf(CB, Handle, "S.CounterHistory%d;\n", i); + } + } + + MicroProfilePrintf(CB, Handle, "\nS.CounterInfo = ["); + + for(int i = 0; i < (int)S.nNumCounters; ++i) + { + bool IsDouble = (S.CounterInfo[i].nFlags & MICROPROFILE_COUNTER_FLAG_DOUBLE) != 0; + float fCounterPrc = 0.f; + float fBoxPrc = 1.f; + double dCounter, dLimit, dMax, dMin; + char Formatted[64]; + char FormattedLimit[64]; + + if(!IsDouble) + { + uint64_t nCounter = S.Counters[i].load(); + uint64_t nLimit = S.CounterInfo[i].nLimit; + fCounterPrc = 0.f; + if(nLimit) + { + fCounterPrc = (float)nCounter / nLimit; + if(fCounterPrc > 1.f) + { + fBoxPrc = 1.f / fCounterPrc; + fCounterPrc = 1.f; + } + } + MicroProfileFormatCounter(S.CounterInfo[i].eFormat, nCounter, Formatted, sizeof(Formatted) - 1); + MicroProfileFormatCounter(S.CounterInfo[i].eFormat, S.CounterInfo[i].nLimit, FormattedLimit, sizeof(FormattedLimit) - 1); + + dCounter = (double)nCounter; + dMin = (double)S.nCounterMin[i]; + dMax = (double)S.nCounterMax[i]; + dLimit = (double)nLimit; + } + else + { + dCounter = S.CountersDouble[i].load(); + dLimit = S.CounterInfo[i].dLimit; + fCounterPrc = 0.f; + if(dLimit > 0.f) + { + fCounterPrc = (float)(dCounter / dLimit); + if(fCounterPrc > 1.f) + { + fBoxPrc = 1.f / fCounterPrc; + fCounterPrc = 1.f; + } + } + MicroProfileFormatCounterDouble(S.CounterInfo[i].eFormat, dCounter, Formatted, sizeof(Formatted) - 1); + MicroProfileFormatCounterDouble(S.CounterInfo[i].eFormat, S.CounterInfo[i].dLimit, FormattedLimit, sizeof(FormattedLimit) - 1); + dMin = (double)S.dCounterMin[i]; + dMax = (double)S.dCounterMax[i]; + } + MicroProfilePrintf(CB, + Handle, + "MakeCounter(%d, %d, %d, %d, %d, '%s', %f, %f, %f, '%s', %f, '%s', %f, %f, %d, S.CounterHistory%d),", + i, + S.CounterInfo[i].nParent, + S.CounterInfo[i].nSibling, + S.CounterInfo[i].nFirstChild, + S.CounterInfo[i].nLevel, + S.CounterInfo[i].pName, + dCounter, + dMin, + dMax, + Formatted, + dLimit, + FormattedLimit, + fCounterPrc, + fBoxPrc, + S.CounterInfo[i].eFormat == MICROPROFILE_COUNTER_FORMAT_BYTES ? 1 : 0, + i); + } + MicroProfilePrintf(CB, Handle, "];\n\n"); + + const int64_t nTickStart = S.Frames[nFirstFrame].nFrameStartCpu; + const int64_t nTickEnd = S.Frames[nLastFrame].nFrameStartCpu; + int64_t nTickStartGpu = S.Frames[nFirstFrame].nFrameStartGpu; + + int64_t nTickReferenceCpu, nTickReferenceGpu; + int64_t nTicksPerSecondCpu = MicroProfileTicksPerSecondCpu(); + int64_t nTicksPerSecondGpu = MicroProfileTicksPerSecondGpu(); + int nTickReference = 0; + if(MicroProfileGetGpuTickReference(&nTickReferenceCpu, &nTickReferenceGpu)) + { + nTickStartGpu = (nTickStart - nTickReferenceCpu) * nTicksPerSecondGpu / nTicksPerSecondCpu + nTickReferenceGpu; + nTickReference = 1; + } + + uprintf("dumping %d frames\n", nNumFrames); + uprintf("dumping frame %d to %d\n", nFirstFrame, nLastFrame); + + uint32_t* nTimerCounter = (uint32_t*)alloca(sizeof(uint32_t) * S.nTotalTimers); + memset(nTimerCounter, 0, sizeof(uint32_t) * S.nTotalTimers); + + { + MicroProfilePrintf(CB, Handle, " //Timeline begin\n"); + MicroProfileThreadLog* pLog = &S.TimelineLog; + uint32_t nFrameIndexFirst = (nFirstFrame) % MICROPROFILE_MAX_FRAME_HISTORY; + uint32_t nFrameIndexLast = (nFirstFrame + nNumFrames) % MICROPROFILE_MAX_FRAME_HISTORY; + { + // find the frame that has an active marker the furtest distance from the selected range + int nDelta = 0; + int nOffset = 0; + for(uint32_t i = nFrameIndexFirst; i != nFrameIndexLast; i = (i + 1) % MICROPROFILE_MAX_FRAME_HISTORY) + { + int D = (int)S.Frames[i].nTimelineFrameMax - nOffset; + nDelta = MicroProfileMax(D, nDelta); + nOffset++; + } + nFrameIndexFirst = (nFirstFrame - nDelta) % MICROPROFILE_MAX_FRAME_HISTORY; + } + + uint32_t nLogStart = S.Frames[nFrameIndexFirst].nLogStartTimeline; + uint32_t nLogEnd = S.Frames[nFrameIndexLast].nLogStartTimeline; + float fToMs = MicroProfileTickToMsMultiplier(nTicksPerSecondCpu); + +#define pp(...) MicroProfilePrintf(CB, Handle, __VA_ARGS__) + + if(nLogStart != nLogEnd) + { + uint32_t nLogType; + float fTime; + int f = 0; + + pp("S.TimelineColorArray=["); + for(uint32_t k = nLogStart; k != nLogEnd; k = (k + 1) % MICROPROFILE_BUFFER_SIZE) + { + uint64_t v = pLog->Log[k]; + uint64_t nIndex = MicroProfileLogGetTimerIndex(v); + uint64_t nTick = MicroProfileLogGetTick(v); + (void)nTick; + nLogType = MicroProfileLogGetType(v); + switch(nLogType) + { + case MP_LOG_ENTER: + break; + case MP_LOG_LEAVE: + pp("%c'%s'", f++ ? ',' : ' ', "#ff8080"); + break; + + case MP_LOG_EXTENDED: + case MP_LOG_EXTENDED_NO_DATA: + uint32_t payload = MicroProfileLogGetExtendedPayload(v); + if(nIndex == ETOKEN_CUSTOM_COLOR) + { + uint32_t nColor = payload; + pp("%c'#%02x%02x%02x'", f++ ? ',' : ' ', MICROPROFILE_UNPACK_RED(nColor) & 0xff, MICROPROFILE_UNPACK_GREEN(nColor) & 0xff, MICROPROFILE_UNPACK_BLUE(nColor) & 0xff); + } + k += MicroProfileLogGetDataSize(v); + break; + } + } + pp("];\n"); + + f = 0; + pp("S.TimelineIdArray=["); + for(uint32_t k = nLogStart; k != nLogEnd; k = (k + 1) % MICROPROFILE_BUFFER_SIZE) + { + uint64_t v = pLog->Log[k]; + uint64_t nIndex = MicroProfileLogGetTimerIndex(v); + uint64_t nTick = MicroProfileLogGetTick(v); + (void)nTick; + nLogType = MicroProfileLogGetType(v); + switch(nLogType) + { + case MP_LOG_ENTER: + case MP_LOG_LEAVE: + case MP_LOG_EXTENDED_NO_DATA: + + break; + case MP_LOG_EXTENDED: + if(nIndex == ETOKEN_CUSTOM_ID) + { + pp("%c%d", f++ ? ',' : ' ', (uint32_t)nTick); + } + k += MicroProfileLogGetDataSize(v); + break; + } + } + pp("];\n"); + + f = 0; + + pp("S.TimelineArray=["); + for(uint32_t k = nLogStart; k != nLogEnd; k = (k + 1) % MICROPROFILE_BUFFER_SIZE) + { + uint64_t v = pLog->Log[k]; + nLogType = MicroProfileLogGetType(v); + switch(nLogType) + { + case MP_LOG_ENTER: + case MP_LOG_LEAVE: + fTime = MicroProfileLogTickDifference(nTickStart, v) * fToMs; + pp("%c%f", f++ ? ',' : ' ', fTime); + break; + case MP_LOG_EXTENDED: + k += MicroProfileLogGetDataSize(v); + break; + case MP_LOG_EXTENDED_NO_DATA: + break; + } + } + pp("];\n"); + pp("S.TimelineNames=["); + f = 0; + char String[MICROPROFILE_MAX_STRING + 1]; + for(uint32_t k = nLogStart; k != nLogEnd;) + { + uint64_t v = pLog->Log[k]; + nLogType = MicroProfileLogGetType(v); + uint64_t nIndex = MicroProfileLogGetTimerIndex(v); + uint64_t nTick = MicroProfileLogGetTick(v); + (void)nTick; + switch(nLogType) + { + case MP_LOG_ENTER: + case MP_LOG_LEAVE: + if(nIndex == ETOKEN_CUSTOM_NAME && nLogType == MP_LOG_LEAVE) + { + // pp(f++ ? ",''" : "''"); + } + k = (k + 1) % MICROPROFILE_BUFFER_SIZE; + break; + case MP_LOG_EXTENDED_NO_DATA: + k = (k + 1) % MICROPROFILE_BUFFER_SIZE; + break; + case MP_LOG_EXTENDED: + uint32_t nSize = MicroProfileLogGetDataSize(v); + + if(nIndex == ETOKEN_CUSTOM_ID) + { + char* pSource = (char*)&pLog->Log[(k + 1) % MICROPROFILE_BUFFER_SIZE]; + const char* pOut = nullptr; + if(nSize == 0) + { + pOut = ""; + } + else if(k + nSize <= MICROPROFILE_BUFFER_SIZE) + { + pOut = pSource; + } + else + { + pOut = &String[0]; + char* pDest = &String[0]; + MP_ASSERT(nSize * 8 < sizeof(MICROPROFILE_MAX_STRING) + 1); + uint32_t Index = (k + 1) % MICROPROFILE_BUFFER_SIZE; + for(uint32_t l = 0; l < nSize; ++l) + { + memcpy(pDest, (char*)pLog->Log[Index], sizeof(uint64_t)); + Index = (Index + 1) % MICROPROFILE_BUFFER_SIZE; + } + } + if(f++) + { + pp(",'%s'", pOut); + } + else + { + pp("'%s'", pOut); + } + } + k = (k + 1 + nSize) % MICROPROFILE_BUFFER_SIZE; + break; + } + } + pp("];\n"); + } + MicroProfilePrintf(CB, Handle, " //Timeline end\n"); + } + + MicroProfilePrintf(CB, Handle, "S.Frames = Array(%d);\n", nNumFrames); + for(uint32_t i = 0; i < nNumFrames; ++i) + { + uint32_t nFrameIndex = (nFirstFrame + i) % MICROPROFILE_MAX_FRAME_HISTORY; + uint32_t nFrameIndexNext = (nFrameIndex + 1) % MICROPROFILE_MAX_FRAME_HISTORY; + + for(uint32_t j = 0; j < S.nNumLogs; ++j) + { + MicroProfileThreadLog* pLog = S.Pool[j]; + int64_t nStartTickBase = pLog->nGpu ? nTickStartGpu : nTickStart; + uint32_t nLogStart = S.Frames[nFrameIndex].nLogStart[j]; + uint32_t nLogEnd = S.Frames[nFrameIndexNext].nLogStart[j]; + uint32_t nLogType; + float fToMs; + uint64_t nStartTick; + float fToMsCpu = MicroProfileTickToMsMultiplier(nTicksPerSecondCpu); + float fToMsBase = MicroProfileTickToMsMultiplier(pLog->nGpu ? nTicksPerSecondGpu : nTicksPerSecondCpu); + MicroProfilePrintf(CB, Handle, "S.ts_%d_%d = [", i, j); + if(nLogStart != nLogEnd) + { + int f = 0; + for(uint32_t k = nLogStart; k != nLogEnd; k = (k + 1) % MICROPROFILE_BUFFER_SIZE) + { + float fTime; + MicroProfileLogEntry v = pLog->Log[k]; + nLogType = MicroProfileLogGetType(v); + fToMs = fToMsBase; + nStartTick = nStartTickBase; + switch(nLogType) + { + case MP_LOG_EXTENDED: + { + fTime = 0.f; + k += MicroProfileLogGetDataSize(v); + break; + } + case MP_LOG_EXTENDED_NO_DATA: + { + uint32_t nTimerIndex = (uint32_t)MicroProfileLogGetTimerIndex(v); + if(nTimerIndex == ETOKEN_GPU_CPU_TIMESTAMP) + { + fToMs = fToMsCpu; + nStartTick = nTickStart; + fTime = MicroProfileLogTickDifference(nStartTick, v) * fToMs; + } + else + { + fTime = 0.f; + } + break; + } + default: + fTime = MicroProfileLogTickDifference(nStartTick, pLog->Log[k]) * fToMs; + } + MicroProfilePrintf(CB, Handle, f++ ? ",%f" : "%f", fTime); + } + } + MicroProfilePrintf(CB, Handle, "];\n"); + + MicroProfilePrintf(CB, Handle, "S.tt_%d_%d = [", i, j); + if(nLogStart != nLogEnd) + { + uint32_t k = nLogStart; + MicroProfilePrintf(CB, Handle, "%d", MicroProfileLogGetType(pLog->Log[k])); + for(k = (k + 1) % MICROPROFILE_BUFFER_SIZE; k != nLogEnd; k = (k + 1) % MICROPROFILE_BUFFER_SIZE) + { + uint64_t v = pLog->Log[k]; + uint32_t nLogType2 = MicroProfileLogGetType(v); + + if(nLogType2 > MP_LOG_ENTER) + nLogType2 |= (MicroProfileLogGetExtendedToken(v)) + << 2; // pack extended token here.. this way all code can check agains ENTER/LEAVE, and only the ext code needs to care about the top bits. + MicroProfilePrintf(CB, Handle, ",%d", nLogType2); + if(nLogType2 == MP_LOG_EXTENDED) + k += MicroProfileLogGetDataSize(v); + } + } + MicroProfilePrintf(CB, Handle, "];\n"); + + MicroProfilePrintf(CB, Handle, "S.ti_%d_%d = [", i, j); + if(nLogStart != nLogEnd) + { + for(uint32_t k = nLogStart; k != nLogEnd; k = (k + 1) % MICROPROFILE_BUFFER_SIZE) + { + uint64_t v = pLog->Log[k]; + nLogType = MicroProfileLogGetType(v); + const char* pFormat = k == nLogStart ? "%d" : ",%d"; + if(nLogType == MP_LOG_ENTER || nLogType == MP_LOG_LEAVE) + { + uint32_t nTimerIndex = (uint32_t)MicroProfileLogGetTimerIndex(pLog->Log[k]); + if(ETOKEN_CSTR_PTR == nTimerIndex) + { + MP_ASSERT(k + 1 != nLogEnd); + uint64_t v1 = pLog->Log[(k + 1) % MICROPROFILE_BUFFER_SIZE]; + + const char* pString = (const char*)MicroProfileLogGetExtendedPayloadNoDataPtr(v1); + uintptr_t value; + if(!MicroProfileHashTableGet(&StringsHashTable, (uint64_t)pString, &value)) + { + MP_BREAK(); // should be covered earlier. + } + MicroProfilePrintf(CB, Handle, pFormat, value); + } + else + { + if(nTimerIndex < S.nTotalTimers) + { + nTimerCounter[nTimerIndex]++; + } + MicroProfilePrintf(CB, Handle, pFormat, nTimerIndex); + } + } + else + { + uint64_t ExtendedToken = MicroProfileLogGetExtendedToken(v); + uint64_t PayloadNoData = MicroProfileLogGetExtendedPayloadNoData(v); + switch(ExtendedToken) + { + case ETOKEN_GPU_CPU_SOURCE_THREAD: + MicroProfilePrintf(CB, Handle, pFormat, PayloadNoData); + break; + default: + MicroProfilePrintf(CB, Handle, pFormat, -1); + } + + if(nLogType == MP_LOG_EXTENDED) + k += MicroProfileLogGetDataSize(v); + } + } + } + MicroProfilePrintf(CB, Handle, "];\n"); + } + + MicroProfilePrintf(CB, Handle, "S.ts%d = [", i); + for(uint32_t j = 0; j < S.nNumLogs; ++j) + { + MicroProfilePrintf(CB, Handle, "S.ts_%d_%d,", i, j); + } + MicroProfilePrintf(CB, Handle, "];\n"); + MicroProfilePrintf(CB, Handle, "S.tt%d = [", i); + for(uint32_t j = 0; j < S.nNumLogs; ++j) + { + MicroProfilePrintf(CB, Handle, "S.tt_%d_%d,", i, j); + } + MicroProfilePrintf(CB, Handle, "];\n"); + + MicroProfilePrintf(CB, Handle, "S.ti%d = [", i); + for(uint32_t j = 0; j < S.nNumLogs; ++j) + { + MicroProfilePrintf(CB, Handle, "S.ti_%d_%d,", i, j); + } + MicroProfilePrintf(CB, Handle, "];\n"); + + int64_t nFrameStart = S.Frames[nFrameIndex].nFrameStartCpu; + int64_t nFrameEnd = S.Frames[nFrameIndexNext].nFrameStartCpu; + + float fToMs = MicroProfileTickToMsMultiplier(nTicksPerSecondCpu); + float fFrameMs = MicroProfileLogTickDifference(nTickStart, nFrameStart) * fToMs; + float fFrameEndMs = MicroProfileLogTickDifference(nTickStart, nFrameEnd) * fToMs; + float fFrameGpuMs = 0; + float fFrameGpuEndMs = 0; + if(nTickReference) + { + fFrameGpuMs = MicroProfileLogTickDifference(nTickStartGpu, S.Frames[nFrameIndex].nFrameStartGpu) * fToMsGPU; + fFrameGpuEndMs = MicroProfileLogTickDifference(nTickStartGpu, S.Frames[nFrameIndexNext].nFrameStartGpu) * fToMsGPU; + } + MicroProfilePrintf(CB, Handle, "S.Frames[%d] = MakeFrame(%d, %f, %f, %f, %f, S.ts%d, S.tt%d, S.ti%d);\n", i, 0, fFrameMs, fFrameEndMs, fFrameGpuMs, fFrameGpuEndMs, i, i, i); + } + + uint32_t nContextSwitchStart = 0; + uint32_t nContextSwitchEnd = 0; + MicroProfileContextSwitchSearch(&nContextSwitchStart, &nContextSwitchEnd, nTickStart, nTickEnd); + + uprintf("CONTEXT SWITCH SEARCH .... %d %d %d .... %lld, %lld\n", nContextSwitchStart, nContextSwitchEnd, nContextSwitchEnd - nContextSwitchStart, nTickStart, nTickEnd); + + uint32_t nWrittenBefore = S.nWebServerDataSent; + MicroProfilePrintf(CB, Handle, "S.CSwitchThreadInOutCpu = ["); + for(uint32_t j = nContextSwitchStart; j != nContextSwitchEnd; j = (j + 1) % MICROPROFILE_CONTEXT_SWITCH_BUFFER_SIZE) + { + MicroProfileContextSwitch CS = S.ContextSwitch[j]; + int nCpu = CS.nCpu; + MicroProfilePrintf(CB, Handle, "%d,%d,%d,", CS.nThreadIn, CS.nThreadOut, nCpu); + } + MicroProfilePrintf(CB, Handle, "];\n"); + MicroProfilePrintf(CB, Handle, "S.CSwitchTime = ["); + float fToMsCpu = MicroProfileTickToMsMultiplier(MicroProfileTicksPerSecondCpu()); + for(uint32_t j = nContextSwitchStart; j != nContextSwitchEnd; j = (j + 1) % MICROPROFILE_CONTEXT_SWITCH_BUFFER_SIZE) + { + MicroProfileContextSwitch CS = S.ContextSwitch[j]; + float fTime = MicroProfileLogTickDifference(nTickStart, CS.nTicks) * fToMsCpu; + MicroProfilePrintf(CB, Handle, "%f,", fTime); + } + MicroProfilePrintf(CB, Handle, "];\n"); + + MicroProfilePrintf(CB, Handle, "S.CSwitchThreads = {"); + + MicroProfileThreadInfo* pThreadInfo = nullptr; + uint32_t nNumThreads = MicroProfileGetThreadInfoArray(&pThreadInfo); + for(uint32_t i = 0; i < nNumThreads; ++i) + { + const char* p1 = pThreadInfo[i].pThreadModule ? pThreadInfo[i].pThreadModule : "?"; + const char* p2 = pThreadInfo[i].pProcessModule ? pThreadInfo[i].pProcessModule : "?"; + + MicroProfilePrintf(CB, + Handle, + "%" PRId64 ":{\'tid\':%" PRId64 ",\'pid\':%" PRId64 ",\'t\':\'%s\',\'p\':\'%s\'},", + (uint64_t)pThreadInfo[i].tid, + (uint64_t)pThreadInfo[i].tid, + (uint64_t)pThreadInfo[i].pid, + p1, + p2); + } + + MicroProfilePrintf(CB, Handle, "};\n"); + MicroProfilePrintf(CB, Handle, "S.CoreEfficiencyClass = ["); + for(uint32_t i = 0; i < MICROPROFILE_MAX_CPU_CORES; ++i) + { + MicroProfilePrintf(CB, Handle, "%d,", S.CoreEfficiencyClass[i]); + } + MicroProfilePrintf(CB, Handle, "];\n"); + + { + MicroProfilePrintf(CB, Handle, "//String Table\n"); + MicroProfilePrintf(CB, Handle, "S.StringTable = {}\n"); + // dump string table + MicroProfileHashTableIterator beg = MicroProfileGetHashTableIteratorBegin(&StringsHashTable); + MicroProfileHashTableIterator end = MicroProfileGetHashTableIteratorEnd(&StringsHashTable); + while(beg != end) + { + uint64_t Key = beg->Key; + uint64_t Value = beg->Value; + MicroProfilePrintf(CB, Handle, "S.StringTable[%d] = '%s';\n", Value, (const char*)Key); + beg++; + } + } + + uint32_t nWrittenAfter = S.nWebServerDataSent; + + MicroProfilePrintf(CB, Handle, "//CSwitch Size %d\n", nWrittenAfter - nWrittenBefore); + + for(size_t i = 0; i < g_MicroProfileHtml_end_count; ++i) + { + CB(Handle, g_MicroProfileHtml_end_sizes[i] - 1, g_MicroProfileHtml_end[i]); + } + + uint32_t* nGroupCounter = (uint32_t*)alloca(sizeof(uint32_t) * S.nGroupCount); + + memset(nGroupCounter, 0, sizeof(uint32_t) * S.nGroupCount); + for(uint32_t i = 0; i < S.nTotalTimers; ++i) + { + uint32_t nGroupIndex = S.TimerInfo[i].nGroupIndex; + nGroupCounter[nGroupIndex] += nTimerCounter[i]; + } + + uint32_t* nGroupCounterSort = (uint32_t*)alloca(sizeof(uint32_t) * S.nGroupCount); + uint32_t* nTimerCounterSort = (uint32_t*)alloca(sizeof(uint32_t) * S.nTotalTimers); + for(uint32_t i = 0; i < S.nGroupCount; ++i) + { + nGroupCounterSort[i] = i; + } + for(uint32_t i = 0; i < S.nTotalTimers; ++i) + { + nTimerCounterSort[i] = i; + } + std::sort(nGroupCounterSort, nGroupCounterSort + S.nGroupCount, [nGroupCounter](const uint32_t l, const uint32_t r) { return nGroupCounter[l] > nGroupCounter[r]; }); + + std::sort(nTimerCounterSort, nTimerCounterSort + S.nTotalTimers, [nTimerCounter](const uint32_t l, const uint32_t r) { return nTimerCounter[l] > nTimerCounter[r]; }); + + MicroProfilePrintf(CB, Handle, "\n\n"); + + memcpy(S.nActiveGroups, nActiveGroup, sizeof(S.nActiveGroups)); + S.AnyActive = AnyActive; +#if MICROPROFILE_DEBUG + int64_t nTicksEnd = MP_TICK(); + float fMs = fToMsCpu * (nTicksEnd - S.nPauseTicks); + uprintf("html dump took %6.2fms\n", fMs); +#endif + +#undef pp + + S.nPauseTicks = 0; +} + +void MicroProfileWriteFile(void* Handle, size_t nSize, const char* pData) +{ + fwrite(pData, nSize, 1, (FILE*)Handle); +} + +void MicroProfileDumpToFile() +{ + std::lock_guard Lock(MicroProfileMutex()); + if(S.nDumpFileNextFrame & 1) + { + char Path[MICROPROFILE_MAX_PATH]; + int Length = snprintf(Path, sizeof(S.HtmlDumpPath), "%s.html", S.HtmlDumpPath); + if(Length > 0 && Length < MICROPROFILE_MAX_PATH) + { + FILE* F = fopen(Path, "w"); + if(F) + { + MicroProfileDumpHtml(MicroProfileWriteFile, F, S.DumpFrameCount, S.HtmlDumpPath); + fclose(F); + } + } + } + if(S.nDumpFileNextFrame & 2) + { +#if MICROPROFILE_LEGACY_CSV + MicroProfileDumpCsvLegacy(); +#else + MicroProfileDumpCsv(S.DumpFrameCount); +#endif + } +} + +void MicroProfileFlushSocket(MpSocket Socket) +{ + send(Socket, &S.WebServerBuffer[0], S.WebServerPut, 0); + S.WebServerPut = 0; +} + +void MicroProfileWriteSocket(void* Handle, size_t nSize, const char* pData) +{ + S.nWebServerDataSent += nSize; + MpSocket Socket = *(MpSocket*)Handle; + if(nSize > MICROPROFILE_WEBSERVER_SOCKET_BUFFER_SIZE / 2) + { + MicroProfileFlushSocket(Socket); + send(Socket, pData, (int)nSize, 0); + } + else + { + memcpy(&S.WebServerBuffer[S.WebServerPut], pData, nSize); + S.WebServerPut += (uint32_t)nSize; + if(S.WebServerPut > MICROPROFILE_WEBSERVER_SOCKET_BUFFER_SIZE / 2) + { + MicroProfileFlushSocket(Socket); + } + } +} + +#if MICROPROFILE_MINIZ +#ifndef MICROPROFILE_COMPRESS_BUFFER_SIZE +#define MICROPROFILE_COMPRESS_BUFFER_SIZE (256 << 10) +#endif + +#define MICROPROFILE_COMPRESS_CHUNK (MICROPROFILE_COMPRESS_BUFFER_SIZE / 2) +struct MicroProfileCompressedSocketState +{ + unsigned char DeflateOut[MICROPROFILE_COMPRESS_CHUNK]; + unsigned char DeflateIn[MICROPROFILE_COMPRESS_CHUNK]; + mz_stream Stream; + MpSocket Socket; + uint32_t nSize; + uint32_t nCompressedSize; + uint32_t nFlushes; + uint32_t nMemmoveBytes; +}; + +void MicroProfileCompressedSocketFlush(MicroProfileCompressedSocketState* pState) +{ + mz_stream& Stream = pState->Stream; + unsigned char* pSendStart = &pState->DeflateOut[0]; + unsigned char* pSendEnd = &pState->DeflateOut[MICROPROFILE_COMPRESS_CHUNK - Stream.avail_out]; + if(pSendStart != pSendEnd) + { + send(pState->Socket, (const char*)pSendStart, pSendEnd - pSendStart, 0); + pState->nCompressedSize += pSendEnd - pSendStart; + } + Stream.next_out = &pState->DeflateOut[0]; + Stream.avail_out = MICROPROFILE_COMPRESS_CHUNK; +} +void MicroProfileCompressedSocketStart(MicroProfileCompressedSocketState* pState, MpSocket Socket) +{ + mz_stream& Stream = pState->Stream; + memset(&Stream, 0, sizeof(Stream)); + Stream.next_out = &pState->DeflateOut[0]; + Stream.avail_out = MICROPROFILE_COMPRESS_CHUNK; + Stream.next_in = &pState->DeflateIn[0]; + Stream.avail_in = 0; + mz_deflateInit(&Stream, Z_DEFAULT_COMPRESSION); + pState->Socket = Socket; + pState->nSize = 0; + pState->nCompressedSize = 0; + pState->nFlushes = 0; + pState->nMemmoveBytes = 0; +} +void MicroProfileCompressedSocketFinish(MicroProfileCompressedSocketState* pState) +{ + mz_stream& Stream = pState->Stream; + MicroProfileCompressedSocketFlush(pState); + int r = mz_deflate(&Stream, MZ_FINISH); + MP_ASSERT(r == MZ_STREAM_END); + MicroProfileCompressedSocketFlush(pState); + r = mz_deflateEnd(&Stream); + MP_ASSERT(r == MZ_OK); +} + +void MicroProfileCompressedWriteSocket(void* Handle, size_t nSize, const char* pData) +{ + MicroProfileCompressedSocketState* pState = (MicroProfileCompressedSocketState*)Handle; + mz_stream& Stream = pState->Stream; + const unsigned char* pDeflateInEnd = Stream.next_in + Stream.avail_in; + const unsigned char* pDeflateInStart = &pState->DeflateIn[0]; + const unsigned char* pDeflateInRealEnd = &pState->DeflateIn[MICROPROFILE_COMPRESS_CHUNK]; + pState->nSize += (uint32_t)nSize; + if((ptrdiff_t)nSize <= pDeflateInRealEnd - pDeflateInEnd) + { + memcpy((void*)pDeflateInEnd, pData, nSize); + Stream.avail_in += (uint32_t)nSize; + MP_ASSERT(Stream.next_in + Stream.avail_in <= pDeflateInRealEnd); + return; + } + int Flush = 0; + while(nSize) + { + pDeflateInEnd = Stream.next_in + Stream.avail_in; + if(Flush) + { + pState->nFlushes++; + MicroProfileCompressedSocketFlush(pState); + pDeflateInRealEnd = &pState->DeflateIn[MICROPROFILE_COMPRESS_CHUNK]; + if(pDeflateInEnd == pDeflateInRealEnd) + { + if(Stream.avail_in) + { + MP_ASSERT(pDeflateInStart != Stream.next_in); + memmove((void*)pDeflateInStart, Stream.next_in, Stream.avail_in); + pState->nMemmoveBytes += Stream.avail_in; + } + Stream.next_in = pDeflateInStart; + pDeflateInEnd = Stream.next_in + Stream.avail_in; + } + } + size_t nSpace = pDeflateInRealEnd - pDeflateInEnd; + size_t nBytes = MicroProfileMin(nSpace, nSize); + MP_ASSERT(nBytes + pDeflateInEnd <= pDeflateInRealEnd); + memcpy((void*)pDeflateInEnd, pData, nBytes); + Stream.avail_in += (uint32_t)nBytes; + nSize -= nBytes; + pData += nBytes; + int r = mz_deflate(&Stream, MZ_NO_FLUSH); + Flush = r == MZ_BUF_ERROR || nBytes == 0 || Stream.avail_out == 0 ? 1 : 0; + MP_ASSERT(r == MZ_BUF_ERROR || r == MZ_OK); + if(r == MZ_BUF_ERROR) + { + r = mz_deflate(&Stream, MZ_SYNC_FLUSH); + } + } +} +#endif + +#ifndef MicroProfileSetNonBlocking // fcntl doesnt work on a some unix like platforms.. +void MicroProfileSetNonBlocking(MpSocket Socket, int NonBlocking) +{ +#ifdef _WIN32 + u_long nonBlocking = NonBlocking ? 1 : 0; + ioctlsocket(Socket, FIONBIO, &nonBlocking); +#else + int Options = fcntl(Socket, F_GETFL); + if(NonBlocking) + { + fcntl(Socket, F_SETFL, Options | O_NONBLOCK); + } + else + { + fcntl(Socket, F_SETFL, Options & (~O_NONBLOCK)); + } +#endif +} +#endif + +void MicroProfileWebServerStart() +{ +#ifdef _WIN32 + WSADATA wsa; + if(WSAStartup(MAKEWORD(2, 2), &wsa)) + { + S.ListenerSocket = (MpSocket)-1; + return; + } +#endif + + S.ListenerSocket = socket(PF_INET, SOCK_STREAM, 6); + MP_ASSERT(!MP_INVALID_SOCKET(S.ListenerSocket)); + MicroProfileSetNonBlocking(S.ListenerSocket, 1); + + { + int r = 0; + int on = 1; +#if defined(_WIN32) + r = setsockopt(S.ListenerSocket, SOL_SOCKET, SO_REUSEADDR, (const char*)&on, sizeof(on)); +#else + r = setsockopt(S.ListenerSocket, SOL_SOCKET, SO_REUSEADDR, (void*)&on, sizeof(on)); +#endif + (void)r; + } + + int nStartPort = S.nWebServerPort; + struct sockaddr_in Addr; + Addr.sin_family = AF_INET; + Addr.sin_addr.s_addr = INADDR_ANY; + for(int i = 0; i < 20; ++i) + { + Addr.sin_port = htons(nStartPort + i); + if(0 == bind(S.ListenerSocket, (sockaddr*)&Addr, sizeof(Addr))) + { + S.nWebServerPort = (uint32_t)(nStartPort + i); + break; + } + } + listen(S.ListenerSocket, 8); +} + +void MicroProfileWebServerJoin() +{ + if(S.WebSocketThreadRunning) + { + MicroProfileThreadJoin(&S.WebSocketSendThread); + } + S.WebSocketThreadJoined = 1; +} + +void MicroProfileWebServerStop() +{ + MP_ASSERT(S.WebSocketThreadJoined); +#ifdef _WIN32 + closesocket(S.ListenerSocket); + WSACleanup(); +#else + close(S.ListenerSocket); +#endif +} +enum MicroProfileGetCommand +{ + EMICROPROFILE_GET_COMMAND_DUMP, + EMICROPROFILE_GET_COMMAND_DUMP_RANGE, + EMICROPROFILE_GET_COMMAND_LIVE, + EMICROPROFILE_GET_COMMAND_FAVICON, + EMICROPROFILE_GET_COMMAND_SERVICE_WORKER, + EMICROPROFILE_GET_COMMAND_UNKNOWN, +}; +struct MicroProfileParseGetResult +{ + uint64_t nFrames; + uint64_t nFrameStart; +}; +MicroProfileGetCommand MicroProfileParseGet(const char* pGet, MicroProfileParseGetResult* pResult) +{ + if(0 == strlen(pGet)) + { + return EMICROPROFILE_GET_COMMAND_LIVE; + } + if(0 == strcmp(pGet, "favicon.ico")) + { + return EMICROPROFILE_GET_COMMAND_FAVICON; + } + if(0 == strcmp(pGet, "favicon.png")) + { + return EMICROPROFILE_GET_COMMAND_FAVICON; + } + if(0 == strcmp(pGet, "service-worker.js")) + { + return EMICROPROFILE_GET_COMMAND_SERVICE_WORKER; + } + const char* pStart = pGet; + if(*pStart == 'b' || *pStart == 'p') + { + S.nWSWasConnected = 1; // do not load default when url has one specified. + return EMICROPROFILE_GET_COMMAND_LIVE; + } + if(*pStart == 'r') // range + { + // very very manual parsing + if('/' != *++pStart) + return EMICROPROFILE_GET_COMMAND_UNKNOWN; + ++pStart; + + char* pEnd = nullptr; + uint64_t nFrameStart = strtoll(pStart, &pEnd, 10); + if(pEnd == pStart || *pEnd != '/' || *pEnd == '\0') + { + return EMICROPROFILE_GET_COMMAND_UNKNOWN; + } + pStart = pEnd + 1; + + uint64_t nFrameEnd = strtoll(pStart, &pEnd, 10); + + if(pEnd == pStart || nFrameEnd <= nFrameStart) + { + return EMICROPROFILE_GET_COMMAND_UNKNOWN; + } + pResult->nFrames = nFrameEnd - nFrameStart; + pResult->nFrameStart = nFrameStart; + return EMICROPROFILE_GET_COMMAND_DUMP_RANGE; + } + while(*pGet != '\0') + { + if(*pGet < '0' || *pGet > '9') + return EMICROPROFILE_GET_COMMAND_UNKNOWN; + pGet++; + } + int nFrames = atoi(pStart); + pResult->nFrameStart = (uint64_t)-1; + if(nFrames) + { + pResult->nFrames = nFrames; + } + else + { + pResult->nFrames = MICROPROFILE_WEBSERVER_DEFAULT_FRAMES; + } + return EMICROPROFILE_GET_COMMAND_DUMP; +} + +void MicroProfileBase64Encode(char* pOut, const uint8_t* pIn, uint32_t nLen) +{ + static const char* CODES = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="; + //..straight from wikipedia. + int b; + char* o = pOut; + for(uint32_t i = 0; i < nLen; i += 3) + { + b = (pIn[i] & 0xfc) >> 2; + *o++ = CODES[b]; + b = (pIn[i] & 0x3) << 4; + if(i + 1 < nLen) + { + b |= (pIn[i + 1] & 0xF0) >> 4; + *o++ = CODES[b]; + b = (pIn[i + 1] & 0x0F) << 2; + if(i + 2 < nLen) + { + b |= (pIn[i + 2] & 0xC0) >> 6; + *o++ = CODES[b]; + b = pIn[i + 2] & 0x3F; + *o++ = CODES[b]; + } + else + { + *o++ = CODES[b]; + *o++ = '='; + } + } + else + { + *o++ = CODES[b]; + *o++ = '='; + *o++ = '='; + } + } +} + +// begin: SHA-1 in C +// ftp://ftp.funet.fi/pub/crypt/hash/sha/sha1.c +// SHA-1 in C +// By Steve Reid +// 100% Public Domain + +typedef struct +{ + uint32_t state[5]; + uint32_t count[2]; + unsigned char buffer[64]; +} MicroProfile_SHA1_CTX; +#include +#ifndef _WIN32 +#include +#endif + +static void MicroProfile_SHA1_Transform(uint32_t[5], const unsigned char[64]); + +#define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits)))) + +#define blk0(i) (block->l[i] = htonl(block->l[i])) +#define blk(i) (block->l[i & 15] = rol(block->l[(i + 13) & 15] ^ block->l[(i + 8) & 15] ^ block->l[(i + 2) & 15] ^ block->l[i & 15], 1)) + +#define R0(v, w, x, y, z, i) \ + z += ((w & (x ^ y)) ^ y) + blk0(i) + 0x5A827999 + rol(v, 5); \ + w = rol(w, 30); +#define R1(v, w, x, y, z, i) \ + z += ((w & (x ^ y)) ^ y) + blk(i) + 0x5A827999 + rol(v, 5); \ + w = rol(w, 30); +#define R2(v, w, x, y, z, i) \ + z += (w ^ x ^ y) + blk(i) + 0x6ED9EBA1 + rol(v, 5); \ + w = rol(w, 30); +#define R3(v, w, x, y, z, i) \ + z += (((w | x) & y) | (w & x)) + blk(i) + 0x8F1BBCDC + rol(v, 5); \ + w = rol(w, 30); +#define R4(v, w, x, y, z, i) \ + z += (w ^ x ^ y) + blk(i) + 0xCA62C1D6 + rol(v, 5); \ + w = rol(w, 30); + +// Hash a single 512-bit block. This is the core of the algorithm. + +static void MicroProfile_SHA1_Transform(uint32_t state[5], const unsigned char buffer[64]) +{ + uint32_t a, b, c, d, e; + typedef union + { + unsigned char c[64]; + uint32_t l[16]; + } CHAR64LONG16; + CHAR64LONG16* block; + + block = (CHAR64LONG16*)buffer; + // Copy context->state[] to working vars + a = state[0]; + b = state[1]; + c = state[2]; + d = state[3]; + e = state[4]; + // 4 rounds of 20 operations each. Loop unrolled. + R0(a, b, c, d, e, 0); + R0(e, a, b, c, d, 1); + R0(d, e, a, b, c, 2); + R0(c, d, e, a, b, 3); + R0(b, c, d, e, a, 4); + R0(a, b, c, d, e, 5); + R0(e, a, b, c, d, 6); + R0(d, e, a, b, c, 7); + R0(c, d, e, a, b, 8); + R0(b, c, d, e, a, 9); + R0(a, b, c, d, e, 10); + R0(e, a, b, c, d, 11); + R0(d, e, a, b, c, 12); + R0(c, d, e, a, b, 13); + R0(b, c, d, e, a, 14); + R0(a, b, c, d, e, 15); + R1(e, a, b, c, d, 16); + R1(d, e, a, b, c, 17); + R1(c, d, e, a, b, 18); + R1(b, c, d, e, a, 19); + R2(a, b, c, d, e, 20); + R2(e, a, b, c, d, 21); + R2(d, e, a, b, c, 22); + R2(c, d, e, a, b, 23); + R2(b, c, d, e, a, 24); + R2(a, b, c, d, e, 25); + R2(e, a, b, c, d, 26); + R2(d, e, a, b, c, 27); + R2(c, d, e, a, b, 28); + R2(b, c, d, e, a, 29); + R2(a, b, c, d, e, 30); + R2(e, a, b, c, d, 31); + R2(d, e, a, b, c, 32); + R2(c, d, e, a, b, 33); + R2(b, c, d, e, a, 34); + R2(a, b, c, d, e, 35); + R2(e, a, b, c, d, 36); + R2(d, e, a, b, c, 37); + R2(c, d, e, a, b, 38); + R2(b, c, d, e, a, 39); + R3(a, b, c, d, e, 40); + R3(e, a, b, c, d, 41); + R3(d, e, a, b, c, 42); + R3(c, d, e, a, b, 43); + R3(b, c, d, e, a, 44); + R3(a, b, c, d, e, 45); + R3(e, a, b, c, d, 46); + R3(d, e, a, b, c, 47); + R3(c, d, e, a, b, 48); + R3(b, c, d, e, a, 49); + R3(a, b, c, d, e, 50); + R3(e, a, b, c, d, 51); + R3(d, e, a, b, c, 52); + R3(c, d, e, a, b, 53); + R3(b, c, d, e, a, 54); + R3(a, b, c, d, e, 55); + R3(e, a, b, c, d, 56); + R3(d, e, a, b, c, 57); + R3(c, d, e, a, b, 58); + R3(b, c, d, e, a, 59); + R4(a, b, c, d, e, 60); + R4(e, a, b, c, d, 61); + R4(d, e, a, b, c, 62); + R4(c, d, e, a, b, 63); + R4(b, c, d, e, a, 64); + R4(a, b, c, d, e, 65); + R4(e, a, b, c, d, 66); + R4(d, e, a, b, c, 67); + R4(c, d, e, a, b, 68); + R4(b, c, d, e, a, 69); + R4(a, b, c, d, e, 70); + R4(e, a, b, c, d, 71); + R4(d, e, a, b, c, 72); + R4(c, d, e, a, b, 73); + R4(b, c, d, e, a, 74); + R4(a, b, c, d, e, 75); + R4(e, a, b, c, d, 76); + R4(d, e, a, b, c, 77); + R4(c, d, e, a, b, 78); + R4(b, c, d, e, a, 79); + // Add the working vars back into context.state[] + state[0] += a; + state[1] += b; + state[2] += c; + state[3] += d; + state[4] += e; + // Wipe variables + a = b = c = d = e = 0; +} + +void MicroProfile_SHA1_Init(MicroProfile_SHA1_CTX* context) +{ + // SHA1 initialization constants + context->state[0] = 0x67452301; + context->state[1] = 0xEFCDAB89; + context->state[2] = 0x98BADCFE; + context->state[3] = 0x10325476; + context->state[4] = 0xC3D2E1F0; + context->count[0] = context->count[1] = 0; +} + +// Run your data through this. + +void MicroProfile_SHA1_Update(MicroProfile_SHA1_CTX* context, const unsigned char* data, unsigned int len) +{ + unsigned int i, j; + + j = (context->count[0] >> 3) & 63; + if((context->count[0] += len << 3) < (len << 3)) + context->count[1]++; + context->count[1] += (len >> 29); + i = 64 - j; + while(len >= i) + { + memcpy(&context->buffer[j], data, i); + MicroProfile_SHA1_Transform(context->state, context->buffer); + data += i; + len -= i; + i = 64; + j = 0; + } + + memcpy(&context->buffer[j], data, len); +} + +// Add padding and return the message digest. + +void MicroProfile_SHA1_Final(unsigned char digest[20], MicroProfile_SHA1_CTX* context) +{ + uint32_t i, j; + unsigned char finalcount[8]; + + for(i = 0; i < 8; i++) + { + finalcount[i] = (unsigned char)((context->count[(i >= 4 ? 0 : 1)] >> ((3 - (i & 3)) * 8)) & 255); // Endian independent + } + MicroProfile_SHA1_Update(context, (unsigned char*)"\200", 1); + while((context->count[0] & 504) != 448) + { + MicroProfile_SHA1_Update(context, (unsigned char*)"\0", 1); + } + MicroProfile_SHA1_Update(context, finalcount, 8); // Should cause a SHA1Transform() + for(i = 0; i < 20; i++) + { + digest[i] = (unsigned char)((context->state[i >> 2] >> ((3 - (i & 3)) * 8)) & 255); + } + // Wipe variables + i = j = 0; + memset(context->buffer, 0, 64); + memset(context->state, 0, 20); + memset(context->count, 0, 8); + memset(&finalcount, 0, 8); +} + +#undef rol +#undef blk0 +#undef blk +#undef R0 +#undef R1 +#undef R2 +#undef R3 +#undef R4 + +// end: SHA-1 in C + +void MicroProfileWebSocketSendState(MpSocket C); +void MicroProfileWebSocketSendEnabled(MpSocket C); +void MicroProfileWSPrintStart(MpSocket C); +void MicroProfileWSPrintf(const char* pFmt, ...); +void MicroProfileWSPrintEnd(); +void MicroProfileWSFlush(); +bool MicroProfileWebSocketReceive(MpSocket C); + +enum +{ + TYPE_NONE = 0, + TYPE_TIMER = 1, + TYPE_GROUP = 2, + TYPE_CATEGORY = 3, + TYPE_SETTING = 4, + TYPE_COUNTER = 5, +}; + +enum +{ + SETTING_FORCE_ENABLE = 0, + SETTING_CONTEXT_SWITCH_TRACE = 1, + SETTING_PLATFORM_MARKERS = 2, +}; + +enum +{ + MSG_TIMER_TREE = 1, + MSG_ENABLED = 2, + MSG_FRAME = 3, + MSG_LOADSETTINGS = 4, + MSG_PRESETS = 5, + MSG_CURRENTSETTINGS = 6, + MSG_COUNTERS = 7, + MSG_FUNCTION_RESULTS = 8, + MSG_INACTIVE_FRAME = 9, + MSG_FUNCTION_NAMES = 10, + MSG_INSTRUMENT_ERROR = 11, + MSG_QUERY_INDEX = 12, + // MSG_MODULE_NAME = 12, +}; + +enum +{ + VIEW_GRAPH_SPLIT = 0, + VIEW_GRAPH_PERCENTILE = 1, + VIEW_GRAPH_THREAD_GROUP = 2, + VIEW_BAR = 3, + VIEW_BAR_ALL = 4, + VIEW_BAR_SINGLE = 5, + VIEW_COUNTERS = 6, + VIEW_SIZE = 7, +}; + +void MicroProfileSocketDumpState() +{ + fd_set Read, Write, Error; + FD_ZERO(&Read); + FD_ZERO(&Write); + FD_ZERO(&Error); + MpSocket LastSocket = 1; + for(uint32_t i = 0; i < S.nNumWebSockets; ++i) + { + LastSocket = MicroProfileMax(LastSocket, S.WebSockets[i] + 1); + FD_SET(S.WebSockets[i], &Read); + FD_SET(S.WebSockets[i], &Write); + FD_SET(S.WebSockets[i], &Error); + } + timeval tv; + tv.tv_sec = 0; + tv.tv_usec = 0; + + if(-1 == select(LastSocket, &Read, &Write, &Error, &tv)) + { + MP_ASSERT(0); + } + for(uint32_t i = 0; i < S.nNumWebSockets; i++) + { + MpSocket s = S.WebSockets[i]; + uprintf("%" PRId64 " ", (uint64_t)s); + + if(FD_ISSET(s, &Error)) + { + uprintf("e"); + } + else + { + uprintf("_"); + } + if(FD_ISSET(s, &Read)) + { + uprintf("r"); + } + else + { + uprintf(" "); + } + if(FD_ISSET(s, &Write)) + { + uprintf("w"); + } + else + { + uprintf(" "); + } + } + uprintf("\n"); + for(uint32_t i = 1; i < S.nNumWebSockets; i++) + { + MpSocket s = S.WebSockets[i]; + int error_code; + socklen_t error_code_size = sizeof(error_code); + int r = getsockopt(s, SOL_SOCKET, SO_ERROR, (char*)&error_code, &error_code_size); + MP_ASSERT(r >= 0); + if(error_code != 0) + { +#ifdef _WIN32 + char buffer[1024]; + strerror_s(buffer, sizeof(buffer) - 1, error_code); + fprintf(stderr, "socket error: %d %s\n", (int)s, buffer); +#else + fprintf(stderr, "socket error: %d %s\n", (int)s, strerror(error_code)); +#endif + MP_ASSERT(0); + } + } +} + +bool MicroProfileSocketSend2(MpSocket Connection, const void* pMessage, int nLen); +void* MicroProfileSocketSenderThread(void*) +{ + MicroProfileOnThreadCreate("MicroProfileSocketSenderThread"); + while(!S.nMicroProfileShutdown) + { + if(S.nSocketFail) + { + MicroProfileSleep(100); + continue; + } + + uint32_t nEnd = MICROPROFILE_WEBSOCKET_BUFFER_SIZE; + uint32_t nGet = S.WSBuf.nSendGet.load(); + uint32_t nPut = S.WSBuf.nSendPut.load(); + uint32_t nSendStart = 0; + uint32_t nSendAmount = 0; + if(nGet > nPut) + { + nSendStart = nGet; + nSendAmount = nEnd - nGet; + } + else if(nGet < nPut) + { + nSendStart = nGet; + nSendAmount = nPut - nGet; + } + + if(nSendAmount) + { + MICROPROFILE_SCOPE(g_MicroProfileSendLoop); + MICROPROFILE_COUNTER_LOCAL_ADD_ATOMIC(g_MicroProfileBytesPerFlip, nSendAmount); + if(!MicroProfileSocketSend2(S.WebSockets[0], &S.WSBuf.SendBuffer[nSendStart], nSendAmount)) + { + S.nSocketFail = 1; + } + else + { + S.WSBuf.nSendGet.store((nGet + nSendAmount) % MICROPROFILE_WEBSOCKET_BUFFER_SIZE); + } + } + else + { + MicroProfileSleep(20); + } + } + MicroProfileOnThreadExit(); + return 0; +} + +void MicroProfileSocketSend(MpSocket Connection, const void* pMessage, int nLen) +{ + if(S.nSocketFail || nLen <= 0) + { + return; + } + MICROPROFILE_SCOPEI("MicroProfile", "MicroProfileSocketSend", MP_GREEN4); + while(nLen != 0) + { + MP_ASSERT(nLen > 0); + uint32_t nEnd = MICROPROFILE_WEBSOCKET_BUFFER_SIZE; + uint32_t nGet = S.WSBuf.nSendGet.load(); + uint32_t nPut = S.WSBuf.nSendPut.load(); + uint32_t nAmount = 0; + if(nPut < nGet) + { + nAmount = nGet - nPut - 1; + } + else + { + if(nGet == 0) + { + nAmount = nEnd - nPut - 1; + } + else + { + nAmount = nEnd - nPut; + } + } + MP_ASSERT((int)nAmount >= 0); + nAmount = MicroProfileMin(nLen, (int)nAmount); + if(nAmount) + { + memcpy(&S.WSBuf.SendBuffer[nPut], pMessage, nAmount); + pMessage = (void*)((char*)pMessage + nAmount); + nLen -= nAmount; + S.WSBuf.nSendPut.store((nPut + nAmount) % MICROPROFILE_WEBSOCKET_BUFFER_SIZE); + } + else + { + if(S.nSocketFail) + { + return; + } + MicroProfileSleep(20); + } + } +} + +bool MicroProfileSocketSend2(MpSocket Connection, const void* pMessage, int nLen) +{ + if(S.nSocketFail || nLen <= 0) + { + return false; + } + // MICROPROFILE_SCOPEI("MicroProfile", "MicroProfileSocketSend2", 0); +#ifndef _WIN32 + int error_code; + socklen_t error_code_size = sizeof(error_code); + getsockopt(Connection, SOL_SOCKET, SO_ERROR, &error_code, &error_code_size); + if(error_code != 0) + { + return false; + } +#endif + + int s = 0; + while(nLen) + { + s = send(Connection, (const char*)pMessage, nLen, 0); + if(s < 0) + { + const int error = errno; + if(error == EAGAIN || error == EWOULDBLOCK) + { + MicroProfileSleep(20); + continue; + } + break; + } + + nLen -= s; + pMessage = (const char*)pMessage + s; + } +#ifdef _WIN32 + if(s == SOCKET_ERROR) + { + return false; + } +#endif + if(s < 0) + { + return false; + } + return true; +} + +uint32_t MicroProfileWebSocketIdPack(uint32_t type, uint32_t element) +{ + MP_ASSERT(type < 255); + MP_ASSERT(element < 0xffffff); + return type << 24 | element; +} +void MicroProfileWebSocketIdUnpack(uint32_t nPacked, uint32_t& type, uint32_t& element) +{ + type = (nPacked >> 24) & 0xff; + element = nPacked & 0xffffff; +} + +struct MicroProfileWebSocketHeader0 +{ + union + { + struct + { + uint8_t opcode : 4; + uint8_t RSV3 : 1; + uint8_t RSV2 : 1; + uint8_t RSV1 : 1; + uint8_t FIN : 1; + }; + uint8_t v; + }; +}; + +struct MicroProfileWebSocketHeader1 +{ + union + { + struct + { + uint8_t payload : 7; + uint8_t MASK : 1; + }; + uint8_t v; + }; +}; + +bool MicroProfileWebSocketSend(MpSocket Connection, const char* pMessage, uint64_t nLen) +{ + MicroProfileWebSocketHeader0 h0; + MicroProfileWebSocketHeader1 h1; + h0.v = 0; + h1.v = 0; + h0.opcode = 1; + h0.FIN = 1; + uint32_t nExtraSizeBytes = 0; + uint8_t nExtraSize[8]; + if(nLen > 125) + { + if(nLen > 0xffff) + { + nExtraSizeBytes = 8; + h1.payload = 127; + } + else + { + h1.payload = 126; + nExtraSizeBytes = 2; + } + uint64_t nCount = nLen; + for(uint32_t i = 0; i < nExtraSizeBytes; ++i) + { + nExtraSize[nExtraSizeBytes - i - 1] = nCount & 0xff; + nCount >>= 8; + } + + uint32_t nSize = 0; + for(uint32_t i = 0; i < nExtraSizeBytes; i++) + { + nSize <<= 8; + nSize += nExtraSize[i]; + } + MP_ASSERT(nSize == nLen); // verify + } + else + { + h1.payload = nLen; + } + MP_ASSERT(pMessage == S.WSBuf.pBuffer); // space for header is preallocated here + MP_ASSERT(pMessage == S.WSBuf.pBufferAllocation + 20); // space for header is preallocated here + MP_ASSERT(nExtraSizeBytes < 18); + char* pTmp = (char*)(pMessage - nExtraSizeBytes - 2); + memcpy(pTmp + 2, &nExtraSize[0], nExtraSizeBytes); + pTmp[1] = *(char*)&h1; + pTmp[0] = *(char*)&h0; +// MicroProfileSocketSend(Connection, pTmp, nExtraSizeBytes + 2 + nLen); +#if 1 + MicroProfileSocketSend(Connection, &h0, 1); + MicroProfileSocketSend(Connection, &h1, 1); + if(nExtraSizeBytes) + { + MicroProfileSocketSend(Connection, &nExtraSize[0], nExtraSizeBytes); + } + MicroProfileSocketSend(Connection, pMessage, nLen); +#endif + return true; +} + +void MicroProfileWebSocketClearTimers() +{ + while(S.WebSocketTimers > -1) + { + int nNext = S.TimerInfo[S.WebSocketTimers].nWSNext; + S.TimerInfo[S.WebSocketTimers].nWSNext = -2; + S.WebSocketTimers = nNext; + } + MP_ASSERT(S.WebSocketTimers == -1); + while(S.WebSocketCounters > -1) + { + int nNext = S.CounterInfo[S.WebSocketCounters].nWSNext; + S.CounterInfo[S.WebSocketCounters].nWSNext = -2; + S.WebSocketCounters = nNext; + } + MP_ASSERT(S.WebSocketCounters == -1); + + while(S.WebSocketGroups > -1) + { + int nNext = S.GroupInfo[S.WebSocketGroups].nWSNext; + S.GroupInfo[S.WebSocketGroups].nWSNext = -2; + S.WebSocketGroups = nNext; + } + MP_ASSERT(S.WebSocketGroups == -1); + S.nWebSocketDirty |= MICROPROFILE_WEBSOCKET_DIRTY_ENABLED; +} +void MicroProfileWebSocketToggleTimer(uint32_t nTimer) +{ + if(nTimer < S.nTotalTimers) + { + auto& TI = S.TimerInfo[nTimer]; + int* pPrev = &S.WebSocketTimers; + while(*pPrev > -1 && *pPrev != (int)nTimer) + { + MP_ASSERT(*pPrev < (int)S.nTotalTimers && *pPrev >= 0); + pPrev = &S.TimerInfo[*pPrev].nWSNext; + } + if(TI.nWSNext >= -1) + { + MP_ASSERT(*pPrev == (int)nTimer); + *pPrev = TI.nWSNext; + TI.nWSNext = -2; + } + else + { + MP_ASSERT(*pPrev == -1); + TI.nWSNext = -1; + *pPrev = (int)nTimer; + } + S.nWebSocketDirty |= MICROPROFILE_WEBSOCKET_DIRTY_ENABLED; + } +} + +void MicroProfileWebSocketToggleCounter(uint32_t nCounter) +{ + if(nCounter < S.nNumCounters) + { + auto& TI = S.CounterInfo[nCounter]; + int* pPrev = &S.WebSocketCounters; + while(*pPrev > -1 && *pPrev != (int)nCounter) + { + MP_ASSERT(*pPrev < (int)S.nNumCounters && *pPrev >= 0); + pPrev = &S.CounterInfo[*pPrev].nWSNext; + } + if(TI.nWSNext >= -1) + { + MP_ASSERT(*pPrev == (int)nCounter); + *pPrev = TI.nWSNext; + TI.nWSNext = -2; + } + else + { + MP_ASSERT(*pPrev == -1); + TI.nWSNext = -1; + *pPrev = (int)nCounter; + } + S.nWebSocketDirty |= MICROPROFILE_WEBSOCKET_DIRTY_ENABLED; + } +} + +void MicroProfileWebSocketToggleGroup(uint32_t nGroup) +{ + if(nGroup < S.nGroupCount) + { + auto& TI = S.GroupInfo[nGroup]; + int* pPrev = &S.WebSocketGroups; + while(*pPrev > -1 && *pPrev != (int)nGroup) + { + MP_ASSERT(*pPrev < (int)S.nGroupCount && *pPrev >= 0); + pPrev = &S.GroupInfo[*pPrev].nWSNext; + } + if(TI.nWSNext >= -1) + { + MP_ASSERT(*pPrev == (int)nGroup); + *pPrev = TI.nWSNext; + TI.nWSNext = -2; + } + else + { + MP_ASSERT(*pPrev == -1); + TI.nWSNext = -1; + *pPrev = (int)nGroup; + } + S.nWebSocketDirty |= MICROPROFILE_WEBSOCKET_DIRTY_ENABLED; + } +} + +bool MicroProfileWebSocketTimerEnabled(uint32_t nTimer) +{ + if(nTimer < S.nTotalTimers) + { + return S.TimerInfo[nTimer].nWSNext > -2; + } + return false; +} + +bool MicroProfileWebSocketCounterEnabled(uint32_t nCounter) +{ + if(nCounter < S.nNumCounters) + { + return S.CounterInfo[nCounter].nWSNext > -2; + } + return false; +} +void MicroProfileWebSocketCommand(uint32_t nCommand) +{ + uint32_t nType, nElement; + MicroProfileWebSocketIdUnpack(nCommand, nType, nElement); + switch(nType) + { + case TYPE_NONE: + break; + case TYPE_SETTING: + switch(nElement) + { + case SETTING_FORCE_ENABLE: + MicroProfileSetEnableAllGroups(!MicroProfileGetEnableAllGroups()); + break; + case SETTING_CONTEXT_SWITCH_TRACE: + if(!S.bContextSwitchRunning) + { + MicroProfileStartContextSwitchTrace(); + } + else + { + MicroProfileStopContextSwitchTrace(); + } + break; + case SETTING_PLATFORM_MARKERS: + MicroProfilePlatformMarkersSetEnabled(!MicroProfilePlatformMarkersGetEnabled()); + break; + } + S.nWebSocketDirty |= MICROPROFILE_WEBSOCKET_DIRTY_ENABLED; + break; + case TYPE_TIMER: + MicroProfileWebSocketToggleTimer(nElement); + break; + case TYPE_GROUP: + MicroProfileToggleGroup(nElement); + break; + case TYPE_CATEGORY: + MicroProfileToggleCategory(nElement); + break; + case TYPE_COUNTER: + MicroProfileWebSocketToggleCounter(nElement); + break; + default: + uprintf("unknown type %d\n", nType); + } +} +#define MICROPROFILE_PRESET_HEADER_MAGIC2 0x28586813 +#define MICROPROFILE_PRESET_HEADER_VERSION2 0x00000200 + +struct MicroProfileSettingsFileHeader +{ + uint32_t nMagic; + uint32_t nVersion; + uint32_t nNumHeaders; + uint32_t nHeadersOffset; + uint32_t nMaxJsonSize; + uint32_t nMaxNameSize; +}; +struct MicroProfileSettingsHeader +{ + uint32_t nJsonOffset; + uint32_t nJsonSize; + uint32_t nNameOffset; + uint32_t nNameSize; +}; + +template +void MicroProfileParseSettings(const char* pFileName, T CB) +{ + std::lock_guard Lock(MicroProfileGetMutex()); + + FILE* F = fopen(pFileName, "rb"); + if(!F) + { + return; + } + long nFileSize = 0; + fseek(F, 0, SEEK_END); + nFileSize = ftell(F); + char* pFile = 0; + char* pAlloc = 0; + if(nFileSize > (32 << 10)) + { + pFile = pAlloc = (char*)MP_ALLOC(nFileSize + 1, 1); + } + else + { + pFile = (char*)alloca(nFileSize + 1); + } + fseek(F, 0, SEEK_SET); + if(1 != fread(pFile, nFileSize, 1, F)) + { + uprintf("failed to read settings file\n"); + fclose(F); + return; + } + fclose(F); + pFile[nFileSize] = '\0'; + + char* pPos = pFile; + char* pEnd = pFile + nFileSize; + + while(pPos != pEnd) + { + const char* pName = 0; + int nNameLen = 0; + const char* pJson = 0; + int nJsonLen = 0; + int Failed = 0; + + auto SkipWhite = [&](char* pPos, const char* pEnd) + { + while(pPos != pEnd) + { + if(isspace(*pPos)) + { + pPos++; + } + else if('#' == *pPos) + { + while(pPos != pEnd && *pPos != '\n') + { + ++pPos; + } + } + else + { + break; + } + } + return pPos; + }; + + auto ParseName = [&](char* pPos, char* pEnd, const char** ppName, int* pLen) + { + pPos = SkipWhite(pPos, pEnd); + int nLen = 0; + *ppName = pPos; + + while(pPos != pEnd && (isalpha(*pPos) || isdigit(*pPos) || *pPos == '_')) + { + nLen++; + pPos++; + } + *pLen = nLen; + if(pPos == pEnd || !isspace(*pPos)) + { + Failed = 1; + return pEnd; + } + *pPos++ = '\0'; + return pPos; + }; + + auto ParseJson = [&](char* pPos, char* pEnd, const char** pJson, int* pLen) -> char* + { + pPos = SkipWhite(pPos, pEnd); + if(*pPos != '{' || pPos == pEnd) + { + Failed = 1; + return pPos; + } + *pJson = pPos++; + int nLen = 1; + int nDepth = 1; + while(pPos != pEnd && nDepth != 0) + { + nLen++; + char nChar = *pPos++; + if(nChar == '{') + { + nDepth++; + } + else if(nChar == '}') + { + nDepth--; + } + } + if(pPos == pEnd || !isspace(*pPos)) + { + Failed = 1; + return pEnd; + } + *pLen = nLen; + *pPos++ = '\0'; + return pPos; + }; + + pPos = ParseName(pPos, pEnd, &pName, &nNameLen); + pPos = ParseJson(pPos, pEnd, &pJson, &nJsonLen); + if(Failed) + { + break; + } + if(!CB(pName, nNameLen, pJson, nJsonLen)) + { + break; + } + } + if(pAlloc) + MP_FREE(pAlloc); +} + +bool MicroProfileSavePresets(const char* pSettingsName, const char* pJsonSettings) +{ + std::lock_guard Lock(MicroProfileGetMutex()); + + FILE* F = fopen(S.pSettingsTemp, "w"); + if(!F) + { + return false; + } + + bool bWritten = false; + + MicroProfileParseSettings(S.pSettings, + [&](const char* pName, uint32_t nNameSize, const char* pJson, uint32_t nJsonSize) -> bool + { + fwrite(pName, nNameSize, 1, F); + fputc(' ', F); + if(0 != MP_STRCASECMP(pSettingsName, pName)) + { + fwrite(pJson, nJsonSize, 1, F); + } + else + { + bWritten = true; + fwrite(pJsonSettings, strlen(pJsonSettings), 1, F); + } + fputc('\n', F); + return true; + }); + if(!bWritten) + { + fwrite(pSettingsName, strlen(pSettingsName), 1, F); + fputc(' ', F); + fwrite(pJsonSettings, strlen(pJsonSettings), 1, F); + fputc('\n', F); + } + fflush(F); + fclose(F); +#ifdef MICROPROFILE_MOVE_FILE + MICROPROFILE_MOVE_FILE(S.pSettingsTemp, S.pSettings); +#elif defined(_WIN32) + MoveFileExA(S.pSettingsTemp, S.pSettings, MOVEFILE_REPLACE_EXISTING); +#else + rename(S.pSettingsTemp, S.pSettings); +#endif + return false; +} + +void MicroProfileWriteJsonString(const char* pJson, uint32_t nJsonLen) +{ + char* pCur = (char*)pJson; + char* pEnd = pCur + nJsonLen; + MicroProfileWSPrintf("\"", pCur); + while(pCur != pEnd) + { + char* pTag = strchr(pCur, '\"'); + if(pTag) + { + *pTag = '\0'; + MicroProfileWSPrintf("%s\\\"", pCur); + *pTag = '\"'; + pCur = pTag + 1; + } + else + { + MicroProfileWSPrintf("%s\"", pCur); + pCur = pEnd; + } + } +}; + +void MicroProfileWebSocketSendPresets(MpSocket Connection) +{ + std::lock_guard Lock(MicroProfileGetMutex()); + uprintf("sending presets ... \n"); + MicroProfileWSPrintStart(Connection); + MicroProfileWSPrintf("{\"k\":\"%d\",\"v\":{", MSG_PRESETS); + MicroProfileWSPrintf("\"p\":{\"Default\":\"{}\""); + + MicroProfileParseSettings(S.pSettings, + [](const char* pName, uint32_t nNameLen, const char* pJson, uint32_t nJsonLen) + { + MicroProfileWSPrintf(",\"%s\":", pName); + MicroProfileWriteJsonString(pJson, nJsonLen); + + return true; + }); + MicroProfileWSPrintf("},\"r\":{"); + bool bFirst = true; + MicroProfileParseSettings(S.pSettingsReadOnly, + [&bFirst](const char* pName, uint32_t nNameLen, const char* pJson, uint32_t nJsonLen) + { + MicroProfileWSPrintf("%c\"%s\":", bFirst ? ' ' : ',', pName); + MicroProfileWriteJsonString(pJson, nJsonLen); + + bFirst = false; + return true; + }); + MicroProfileWSPrintf("}}}"); + MicroProfileWSFlush(); + MicroProfileWSPrintEnd(); +} + +#define LOAD_PRESET_DEFAULT 0x1 +#define LOAD_PRESET_READONLY 0x2 + +void MicroProfileLoadPresets(const char* pSettingsName, uint32_t nLoadPresetType) +{ + std::lock_guard Lock(MicroProfileGetMutex()); + const char* pPresetFiles[] = { S.pSettings, S.pSettingsReadOnly }; + for(uint32_t i = 0; i < 2; ++i) + { + if(nLoadPresetType & (1u << i)) + { + const char* pPresetFile = pPresetFiles[i]; + bool bReadOnly = (1u << i) == LOAD_PRESET_READONLY; + bool bSuccess = false; + MicroProfileParseSettings(pPresetFile, + [&bSuccess, bReadOnly, pSettingsName](const char* pName, uint32_t l0, const char* pJson, uint32_t l1) + { + if(0 == MP_STRCASECMP(pName, pSettingsName)) + { + uint32_t nLen = (uint32_t)strlen(pJson) + 1; + if(nLen > S.nJsonSettingsBufferSize) + { + if(S.pJsonSettings) + S.pJsonSettings = nullptr; + S.pJsonSettings = (char*)MP_ALLOC(nLen, 1); + S.nJsonSettingsBufferSize = nLen; + } + S.pJsonSettingsName = pSettingsName; + memcpy(S.pJsonSettings, pJson, nLen); + S.nJsonSettingsPending = 1; + S.bJsonSettingsReadOnly = bReadOnly ? 1 : 0; + bSuccess = true; + return false; + } + return true; + }); + if(bSuccess) + return; + } + } +} + +bool MicroProfileWebSocketReceive(MpSocket Connection) +{ + + // 0 1 2 3 + // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + // +-+-+-+-+-------+-+-------------+-------------------------------+ + // |F|R|R|R| opcode|M| Payload len | Extended payload length | + // |I|S|S|S| (4) |A| (7) | (16/64) | + // |N|V|V|V| |S| | (if payload len==126/127) | + // | |1|2|3| |K| | | + // +-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - + + int r; + uint64_t nSize; + uint64_t nSizeBytes = 0; + uint8_t Mask[4]; + static unsigned char* Bytes = 0; + static uint64_t BytesAllocated = 0; + MicroProfileWebSocketHeader0 h0; + MicroProfileWebSocketHeader1 h1; + static_assert(sizeof(h0) == 1, ""); + static_assert(sizeof(h1) == 1, ""); + r = recv(Connection, (char*)&h0, 1, 0); + if(1 != r) + goto fail; + r = recv(Connection, (char*)&h1, 1, 0); + if(1 != r) + goto fail; + + if(h0.v == 0x88) + { + goto fail; + } + + if(h0.RSV1 != 0 || h0.RSV2 != 0 || h0.RSV3 != 0) + goto fail; + + nSize = h1.payload; + nSizeBytes = 0; + switch(nSize) + { + case 126: + nSizeBytes = 2; + break; + case 127: + nSizeBytes = 8; + break; + default: + break; + } + if(nSizeBytes) + { + nSize = 0; + uint64_t MessageLength = 0; + + uint8_t BytesMessage[8]; + r = recv(Connection, (char*)&BytesMessage[0], nSizeBytes, 0); + if((int)nSizeBytes != r) + goto fail; + for(uint32_t i = 0; i < nSizeBytes; i++) + { + nSize <<= 8; + nSize += BytesMessage[i]; + } + + for(uint32_t i = 0; i < nSizeBytes; i++) + MessageLength |= BytesMessage[i] << ((nSizeBytes - 1 - i) * 8); + MP_ASSERT(MessageLength == nSize); + } + + if(h1.MASK) + { + recv(Connection, (char*)&Mask[0], 4, 0); + } + + MICROPROFILE_COUNTER_LOCAL_ADD_ATOMIC(g_MicroProfileBytesPerFlip, nSize); + if(nSize + 1 > BytesAllocated) + { + Bytes = (unsigned char*)MP_REALLOC(Bytes, nSize + 1); + BytesAllocated = nSize + 1; + } + recv(Connection, (char*)Bytes, nSize, 0); + for(uint32_t i = 0; i < nSize; ++i) + Bytes[i] ^= Mask[i & 3]; + + Bytes[nSize] = '\0'; + switch(Bytes[0]) + { + case 'a': + { + S.nAggregateFlip = strtoll((const char*)&Bytes[1], nullptr, 10); + } + break; + case 's': + { + char* pJson = strchr((char*)Bytes, ','); + if(pJson && *pJson != '\0') + { + *pJson = '\0'; + MicroProfileSavePresets((const char*)Bytes + 1, (const char*)pJson + 1); + } + break; + } + + case 'l': + { + MicroProfileLoadPresets((const char*)Bytes + 1, LOAD_PRESET_DEFAULT); + break; + } + case 'm': + { + MicroProfileLoadPresets((const char*)Bytes + 1, LOAD_PRESET_READONLY); + break; + } + case 'd': + { + MicroProfileWebSocketClearTimers(); + memset(&S.nActiveGroupsWanted, 0, sizeof(S.nActiveGroupsWanted)); + S.nWebSocketDirty |= MICROPROFILE_WEBSOCKET_DIRTY_ENABLED; + break; + } + case 'c': + { + char* pStr = (char*)Bytes + 1; + char* pEnd = pStr + nSize - 1; + uint32_t Message = strtol(pStr, &pEnd, 10); + MicroProfileWebSocketCommand(Message); + } + break; + case 'f': + MicroProfileToggleFrozen(); + break; + case 'v': + S.nWSViewMode = (int)Bytes[1] - '0'; + break; + case 'r': + uprintf("got clear message\n"); + S.nAggregateClear = 1; + break; + case 'x': + MicroProfileWebSocketClearTimers(); + break; +#if MICROPROFILE_DYNAMIC_INSTRUMENT + case 'D': // instrumentation without loading queryable symbols. + { + uprintf("got INSTRUMENT Message: %s\n", (const char*)&Bytes[0]); + char* pGet = (char*)&Bytes[1]; + uint32_t nNumArguments = 0; +#ifdef _WIN32 + r = sscanf_s(pGet, "%d", &nNumArguments); +#else + r = sscanf(pGet, "%d", &nNumArguments); +#endif + if(r != 1) + { + uprintf("failed to parse..\n"); + break; + } + while(' ' == *pGet || (*pGet >= '0' && *pGet <= '9')) + { + pGet++; + } + if(nNumArguments > 200) + nNumArguments = 200; + uint32_t nParsedArguments = 0; + const char* pModule = 0; + const char* pSymbol = 0; + const char** pModules = (const char**)(alloca(sizeof(const char*) * nNumArguments)); + const char** pSymbols = (const char**)(alloca(sizeof(const char*) * nNumArguments)); + auto Next = [&pGet]() -> const char* + { + if(!pGet) + return 0; + const char* pRet = pGet; + pGet = (char*)strchr(pRet, '!'); + if(!pGet) + { + return 0; + } + *pGet++ = '\0'; + return (const char*)pRet; + }; + do + { + pModule = Next(); + pSymbol = Next(); + if(pModule && pSymbol) + { + pModules[nParsedArguments] = pModule; + pSymbols[nParsedArguments] = pSymbol; + uprintf("found symbol %s ::: %s \n", pModule, pSymbol); + nParsedArguments++; + if(nParsedArguments == nNumArguments) + { + break; + } + } + } while(pGet); + + MicroProfileInstrumentWithoutSymbols(pModules, pSymbols, nParsedArguments); + + break; + } + case 'I': + case 'i': + { + uprintf("got Message: %s\n", (const char*)&Bytes[0]); + void* p = 0; + uint32_t nColor = 0x0; + int nMinBytes = 0; + int nMaxCalls = 0; + int nCharsRead = 0; +#ifdef _WIN32 + r = sscanf_s((const char*)&Bytes[1], "%p %x %d %d%n", &p, &nColor, &nMinBytes, &nMaxCalls, &nCharsRead); +#else + r = sscanf((const char*)&Bytes[1], "%p %x %d %d%n", &p, &nColor, &nMinBytes, &nMaxCalls, &nCharsRead); +#endif + if(r == 4) + { + const char* pModule = (const char*)&Bytes[1]; + // int nNumChars = stbsp_snprintf(0, 0, "%p %x", p, nColor); + pModule += nCharsRead; + while(*pModule != ' ' && *pModule != '\0') + ++pModule; + + if(*pModule == '\0') + break; + + pModule++; + const char* pName = pModule; + while(*pName != '!' && *pName != '\0') + { + pName++; + } + if(*pName == '!') + { + // name and module seperately + *(char*)pName = '\0'; + pName++; + } + else + { + // name only + pName = pModule; + pModule = ""; + } + + uprintf("scanning for ptr %p %x mod:'%s' name'%s'\n", p, nColor, pModule, pName); + if(Bytes[0] == 'I') + { + MicroProfileInstrumentFunctionsCalled(p, pModule, pName, nMinBytes, nMaxCalls); + } + else + { + MicroProfileInstrumentFunction(p, pModule, pName, nColor); + } + } + } + break; + case 'S': + uprintf("loading symbols...\n"); + MicroProfileSymbolInitialize(true); + break; + case 'q': + MicroProfileSymbolQueryFunctions(Connection, 1 + (const char*)Bytes); + break; + case 'L': + uprintf("LOAD MODULE: '%s'\n", 1 + (const char*)Bytes); + MicroProfileSymbolInitialize(true, 1 + (const char*)Bytes); + break; +#else + case 'D': + case 'I': + case 'i': + case 'S': + case 'q': + case 'L': + break; +#endif + default: + uprintf("got unknown message size %lld: '%s'\n", (long long)nSize, Bytes); + } + return true; + +fail: + return false; +} +void MicroProfileWebSocketSendPresets(MpSocket Connection); + +void MicroProfileWebSocketHandshake(MpSocket Connection, char* pWebSocketKey) +{ + // reset web socket buffer + S.WSBuf.nSendPut.store(0); + S.WSBuf.nSendGet.store(0); + S.nSocketFail = 0; + + const char* pGUID = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; + const char* pHandShake = "HTTP/1.1 101 Switching Protocols\r\n" + "Upgrade: websocket\r\n" + "Connection: Upgrade\r\n" + "Sec-WebSocket-Accept: "; + + char EncodeBuffer[512]; + int nLen = stbsp_snprintf(EncodeBuffer, sizeof(EncodeBuffer) - 1, "%s%s", pWebSocketKey, pGUID); + // uprintf("encode buffer is '%s' %d, %d\n", EncodeBuffer, nLen, (int)strlen(EncodeBuffer)); + + uint8_t sha[20]; + MicroProfile_SHA1_CTX ctx; + MicroProfile_SHA1_Init(&ctx); + MicroProfile_SHA1_Update(&ctx, (unsigned char*)EncodeBuffer, nLen); + MicroProfile_SHA1_Final((unsigned char*)&sha[0], &ctx); + char HashOut[(2 + sizeof(sha) / 3) * 4]; + memset(&HashOut[0], 0, sizeof(HashOut)); + MicroProfileBase64Encode(&HashOut[0], &sha[0], sizeof(sha)); + + char Reply[11024]; + nLen = stbsp_snprintf(Reply, sizeof(Reply) - 1, "%s%s\r\n\r\n", pHandShake, HashOut); + ; + MP_ASSERT(nLen >= 0); + MicroProfileSocketSend(Connection, Reply, nLen); + S.WebSockets[S.nNumWebSockets++] = Connection; + + S.WSCategoriesSent = 0; + S.WSGroupsSent = 0; + S.WSTimersSent = 0; + S.WSCountersSent = 0; + S.nJsonSettingsPending = 0; +#if MICROPROFILE_DYNAMIC_INSTRUMENT + S.WSFunctionsInstrumentedSent = 0; + S.WSSymbolModulesSent = 0; + { + uint64_t t0 = MP_TICK(); + MicroProfileSymbolUpdateModuleList(); + uint64_t t1 = MP_TICK(); + float fTime = float(MicroProfileTickToMsMultiplierCpu()) * (t1 - t0); + (void)fTime; + uprintf("update module list time %6.2fms\n", fTime); + } +#endif + + MicroProfileWebSocketSendState(Connection); + MicroProfileWebSocketSendPresets(Connection); + if(!S.nWSWasConnected) + { + S.nWSWasConnected = 1; + MicroProfileLoadPresets("Default", LOAD_PRESET_DEFAULT | LOAD_PRESET_READONLY); + } + else + { +#if MICROPROFILE_DYNAMIC_INSTRUMENT + MicroProfileWSPrintStart(Connection); + MicroProfileWSPrintf("{\"k\":\"%d\",\"qp\":%d}", MSG_QUERY_INDEX, S.nQueryProcessed); + MicroProfileWSFlush(); + MicroProfileWSPrintEnd(); +#endif + if(S.pJsonSettings) + { + MicroProfileWSPrintStart(Connection); + MicroProfileWSPrintf( + "{\"k\":\"%d\",\"ro\":%d,\"name\":\"%s\",\"v\":%s}", MSG_CURRENTSETTINGS, S.bJsonSettingsReadOnly ? 1 : 0, S.pJsonSettingsName ? S.pJsonSettingsName : "", S.pJsonSettings); + MicroProfileWSFlush(); + MicroProfileWSPrintEnd(); + } + } +} + +void MicroProfileWebSocketSendCounters() +{ + MICROPROFILE_SCOPEI("MicroProfile", "MicroProfileWebSocketSendCounters", MP_GREEN4); + if(S.nWSViewMode == VIEW_COUNTERS) + { + MicroProfileWSPrintf("{\"k\":\"%d\",\"v\":[", MSG_COUNTERS); + for(uint32_t i = 0; i < S.nNumCounters; ++i) + { + bool IsDouble = (S.CounterInfo[i].nFlags & MICROPROFILE_COUNTER_FLAG_DOUBLE) != 0; + if(IsDouble) + { + double dCounter = S.CountersDouble[i].load(); + MicroProfileWSPrintf("%c%f", i == 0 ? ' ' : ',', dCounter); + } + else + { + uint64_t nCounter = S.Counters[i].load(); + MicroProfileWSPrintf("%c%lld", i == 0 ? ' ' : ',', nCounter); + } + } + MicroProfileWSPrintf("]}"); + MicroProfileWSFlush(); + } +} + +#if MICROPROFILE_DYNAMIC_INSTRUMENT +void MicroProfileSymbolSendModuleState() +{ + if(S.WSSymbolModulesSent != S.SymbolNumModules || S.nSymbolsDirty.load()) // todo: tag when modulestate is updated. + { + S.nSymbolsDirty.exchange(0); + MicroProfileWSPrintf(",\"M\":["); + bool bFirst = true; + for(int i = 0; i < S.SymbolNumModules; ++i) + { + MicroProfileSymbolModule& M = S.SymbolModules[i]; + const char* pModuleName = (const char*)M.pBaseString; + uint64_t nAddrBegin = M.Regions[0].nBegin; + // intptr_t nProgress = M.nProgress; + intptr_t nProgressTarget = M.nProgressTarget; + nProgressTarget = MicroProfileMax(intptr_t(1), M.nProgressTarget); + // nProgress = MicroProfileMin(nProgressTarget, M.nProgress); + float fLoadPrc = M.nProgress / float(nProgressTarget); + uint64_t nNumSymbols = M.nSymbolsLoaded; +#define FMT "{\"n\":\"%s\",\"a\":\"%llx\",\"s\":\"%lld\", \"p\":%f, \"d\":%d}" + MicroProfileWSPrintf(bFirst ? FMT : ("," FMT), pModuleName, nAddrBegin, nNumSymbols, fLoadPrc, M.bDownloading ? 1 : 0); +#undef FMT + bFirst = false; + } + MicroProfileWSPrintf("]"); + S.WSSymbolModulesSent = S.SymbolNumModules; + } +} +#endif + +void MicroProfileWebSocketSendFrame(MpSocket Connection) +{ + if(S.nFrameCurrent != S.WebSocketFrameLast[0] || S.nFrozen) + { + MicroProfileWebSocketSendState(Connection); + MICROPROFILE_SCOPEI("MicroProfile", "MicroProfileWebSocketSendFrame", MP_GREEN4); + MicroProfileWSPrintStart(Connection); + float fTickToMsCpu = MicroProfileTickToMsMultiplier(MicroProfileTicksPerSecondCpu()); + float fTickToMsGpu = MicroProfileTickToMsMultiplier(MicroProfileTicksPerSecondGpu()); + MicroProfileFrameState* pFrameCurrent = &S.Frames[S.nFrameCurrent]; + MicroProfileFrameState* pFrameNext = &S.Frames[S.nFrameNext]; + + uint64_t nFrameTicks = pFrameNext->nFrameStartCpu - pFrameCurrent->nFrameStartCpu; + uint64_t nFrame = pFrameCurrent->nFrameId; + double fTime = nFrameTicks * fTickToMsCpu; + MicroProfileWSPrintf("{\"k\":\"%d\",\"v\":{\"t\":%f,\"f\":%lld,\"a\":%d,\"fr\":%d,\"m\":%d", MSG_FRAME, fTime, nFrame, MicroProfileGetCurrentAggregateFrames(), S.nFrozen, S.nWSViewMode); +#if MICROPROFILE_DYNAMIC_INSTRUMENT + MicroProfileWSPrintf(",\"s\":{\"n\":%d,\"f\":%d,\"r\":%d,\"l\":%d,\"q\":%d}", + S.SymbolNumModules, + S.SymbolState.nModuleLoadsFinished.load(), + S.SymbolState.nModuleLoadsRequested.load(), + S.SymbolState.nSymbolsLoaded.load(), + S.pPendingQuery ? 1 : 0); + MicroProfileSymbolSendModuleState(); +#endif + + auto WriteTickArray = [fTickToMsCpu, fTickToMsGpu](MicroProfile::GroupTime* pFrameGroup) + { + MicroProfileWSPrintf("["); + int f = 0; + for(uint32_t i = 0; i < MICROPROFILE_MAX_GROUPS; ++i) + { + uint64_t nTicksExcl = pFrameGroup[i].nTicksExclusive; + if(nTicksExcl) + { + uint64_t nTicks = pFrameGroup[i].nTicks; + float fCount = (float)pFrameGroup[i].nCount; + float fToMs = S.GroupInfo[i].Type == MicroProfileTokenTypeCpu ? fTickToMsCpu : fTickToMsGpu; + + MicroProfileWSPrintf("%c[%f,%f,%f]", f ? ',' : ' ', nTicks * fToMs, nTicksExcl * fToMs, fCount); + f = 1; + } + } + MicroProfileWSPrintf("]"); + }; + auto WriteIndexArray = [](MicroProfile::GroupTime* pFrameGroup) + { + MicroProfileWSPrintf("["); + int f = 0; + for(uint32_t i = 0; i < MICROPROFILE_MAX_GROUPS; ++i) + { + uint64_t nTicksExcl = pFrameGroup[i].nTicksExclusive; + if(nTicksExcl) + { + uint32_t id = MicroProfileWebSocketIdPack(TYPE_GROUP, i); + MicroProfileWSPrintf("%c%d", f ? ',' : ' ', id); + f = 1; + } + } + MicroProfileWSPrintf("]"); + }; + + MicroProfileWSPrintf(",\"g\":"); + WriteTickArray(S.FrameGroup); + MicroProfileWSPrintf(",\"gi\":"); + WriteIndexArray(S.FrameGroup); + if(S.nWSViewMode == VIEW_GRAPH_THREAD_GROUP) + { + MicroProfileWSPrintf(",\"gt\":["); + int f = 0; + for(uint32_t i = 0; i < MICROPROFILE_MAX_THREADS; ++i) + { + if(0 != (S.FrameGroupThreadValid[i / 32] & (1 << (i % 32)))) + { + if(!f) + MicroProfileWSPrintf("{"); + else + MicroProfileWSPrintf(",{"); + MicroProfileThreadLog* pLog = S.Pool[i]; + MicroProfileWSPrintf("\"i\":%d,\"n\":\"%s\",\"g\":", i, pLog->ThreadName); + WriteTickArray(&S.FrameGroupThread[i][0]); + MicroProfileWSPrintf(",\"gi\":"); + WriteIndexArray(&S.FrameGroupThread[i][0]); + MicroProfileWSPrintf("}"); + f = 1; + } + } + MicroProfileWSPrintf("]"); + } + + if(S.nFrameCurrent != S.WebSocketFrameLast[0]) + { + MicroProfileWSPrintf(",\"x\":{\"t\":{"); + int nTimer = S.WebSocketTimers; + // uprintf("T : "); + while(nTimer >= 0) + { + MicroProfileTimerInfo& TI = S.TimerInfo[nTimer]; + float fTickToMs = TI.Type == MicroProfileTokenTypeGpu ? fTickToMsGpu : fTickToMsCpu; + uint32_t id = MicroProfileWebSocketIdPack(TYPE_TIMER, nTimer); + fTime = fTickToMs * S.Frame[nTimer].nTicks; + float fCount = (float)S.Frame[nTimer].nCount; + float fTimeExcl = fTickToMs * S.FrameExclusive[nTimer]; + // uprintf("%4.2f, ", fTimeExcl); + if(!MicroProfileGroupActive(TI.nGroupIndex)) + { + fTime = fCount = fTimeExcl = 0.f; + } + nTimer = TI.nWSNext; + MicroProfileWSPrintf("\"%d\":[%f,%f,%f]%c", id, fTime, fTimeExcl, fCount, nTimer == -1 ? ' ' : ','); + } + MicroProfileWSPrintf("}, \"c\":{"); + int nCounter = S.WebSocketCounters; + while(nCounter >= 0) + { + MicroProfileCounterInfo& CI = S.CounterInfo[nCounter]; + bool IsDouble = (CI.nFlags & MICROPROFILE_COUNTER_FLAG_DOUBLE) != 0; + uint32_t id = MicroProfileWebSocketIdPack(TYPE_COUNTER, nCounter); + int nCounterNext = CI.nWSNext; + if(IsDouble) + { + double value = S.CountersDouble[nCounter].load(); + MicroProfileWSPrintf("\"%d\":%f%c", id, value, nCounterNext < 0 ? ' ' : ','); + } + else + { + uint64_t value = S.Counters[nCounter].load(); + MicroProfileWSPrintf("\"%d\":%lld%c", id, value, nCounterNext < 0 ? ' ' : ','); + } + nCounter = nCounterNext; + } + MicroProfileWSPrintf("}, \"g\":{"); + // uprintf("\n"); + MicroProfileWSPrintf("}}"); + } + MicroProfileWSPrintf("}}"); + MicroProfileWSFlush(); + MicroProfileWebSocketSendCounters(); + MicroProfileWSPrintEnd(); + S.WebSocketFrameLast[0] = S.nFrameCurrent; + } + else + { + MicroProfileWSPrintStart(Connection); + MicroProfileWSPrintf("{\"k\":\"%d\",\"v\":{\"fr\":%d,\"m\":%d", MSG_INACTIVE_FRAME, S.nFrozen, S.nWSViewMode); +#if MICROPROFILE_DYNAMIC_INSTRUMENT + MicroProfileWSPrintf(",\"s\":{\"n\":%d,\"f\":%d,\"r\":%d,\"l\":%d,\"q\":%d}", + S.SymbolNumModules, + S.SymbolState.nModuleLoadsFinished.load(), + S.SymbolState.nModuleLoadsRequested.load(), + S.SymbolState.nSymbolsLoaded.load(), + S.pPendingQuery ? 1 : 0); +#endif + MicroProfileWSPrintf("}}"); + MicroProfileWSFlush(); + MicroProfileWebSocketSendCounters(); + MicroProfileWSPrintEnd(); + } +#if MICROPROFILE_DYNAMIC_INSTRUMENT + MicroProfileSymbolQuerySendResult(Connection); + MicroProfileSymbolSendFunctionNames(Connection); + MicroProfileSymbolSendErrors(Connection); +#endif +} + +void MicroProfileWebSocketFrame() +{ + if(!S.nNumWebSockets) + { + return; + } + MICROPROFILE_SCOPEI("MicroProfile", "Websocket-update", MP_GREEN4); + fd_set Read, Write, Error; + FD_ZERO(&Read); + FD_ZERO(&Write); + FD_ZERO(&Error); + MpSocket LastSocket = 1; + for(uint32_t i = 0; i < S.nNumWebSockets; ++i) + { + LastSocket = MicroProfileMax(LastSocket, S.WebSockets[i] + 1); + FD_SET(S.WebSockets[i], &Read); + FD_SET(S.WebSockets[i], &Write); + FD_SET(S.WebSockets[i], &Error); + } + timeval tv; + tv.tv_sec = 0; + tv.tv_usec = 0; + + if(-1 == select(LastSocket, &Read, &Write, &Error, &tv)) + { + MP_ASSERT(0); + } + for(uint32_t i = 0; i < S.nNumWebSockets;) + { + MpSocket s = S.WebSockets[i]; + bool bConnected = true; + if(FD_ISSET(s, &Error)) + { + MP_ASSERT(0); // todo, remove & fix. + } + if(FD_ISSET(s, &Read)) + { + bConnected = MicroProfileWebSocketReceive(s); + } + if(FD_ISSET(s, &Write)) + { + if(S.nJsonSettingsPending) + { + MicroProfileWSPrintStart(s); + MicroProfileWSPrintf( + "{\"k\":\"%d\",\"ro\":%d,\"name\":\"%s\",\"v\":%s}", MSG_LOADSETTINGS, S.bJsonSettingsReadOnly ? 1 : 0, S.pJsonSettingsName ? S.pJsonSettingsName : "", S.pJsonSettings); + MicroProfileWSFlush(); + MicroProfileWSPrintEnd(); + S.nJsonSettingsPending = 0; + } + if(S.nWebSocketDirty) + { + MicroProfileFlipEnabled(); + MicroProfileWebSocketSendEnabled(s); + S.nWebSocketDirty = 0; + } + MicroProfileWebSocketSendFrame(s); + } + if(S.nSocketFail) + { + bConnected = false; + } + S.nSocketFail = 0; + + if(!bConnected) + { + uprintf("removing socket %" PRId64 "\n", (uint64_t)s); + +#ifndef _WIN32 + shutdown(S.WebSockets[i], SHUT_WR); +#else + shutdown(S.WebSockets[i], 1); +#endif + char tmp[128]; + int r = 1; + while(r > 0) + { + r = recv(S.WebSockets[i], tmp, sizeof(tmp), 0); + } +#ifdef _WIN32 + closesocket(S.WebSockets[i]); +#else + close(S.WebSockets[i]); +#endif + + --S.nNumWebSockets; + S.WebSockets[i] = S.WebSockets[S.nNumWebSockets]; + uprintf("done removing\n"); + } + else + { + ++i; + } + } + if(S.nWasFrozen) + { + S.nWasFrozen--; + } +} + +void MicroProfileWSPrintStart(MpSocket C) +{ + MP_ASSERT(S.WSBuf.Socket == 0); + MP_ASSERT(S.WSBuf.nPut == 0); + S.WSBuf.Socket = C; +} + +void MicroProfileResizeWSBuf(uint32_t nMinSize = 0) +{ + uint32_t nNewSize = MicroProfileMax(S.WSBuf.nPut + 2 * (nMinSize + 2 + 20), MicroProfileMax(S.WSBuf.nBufferSize * 3 / 2, (uint32_t)MICROPROFILE_WEBSOCKET_BUFFER_SIZE)); + S.WSBuf.pBufferAllocation = (char*)MICROPROFILE_REALLOC(S.WSBuf.pBufferAllocation, nNewSize); + S.WSBuf.pBuffer = S.WSBuf.pBufferAllocation + 20; + S.WSBuf.nBufferSize = nNewSize - 20; +} + +char* MicroProfileWSPrintfCallback(const char* buf, void* user, int len) +{ + MP_ASSERT(S.WSBuf.nPut == buf - S.WSBuf.pBuffer); + S.WSBuf.nPut += len; + if(S.WSBuf.nPut + STB_SPRINTF_MIN + 2 >= S.WSBuf.nBufferSize) // + { + MicroProfileResizeWSBuf(S.WSBuf.nPut + STB_SPRINTF_MIN); + } + return S.WSBuf.pBuffer + S.WSBuf.nPut; +} + +void MicroProfileWSPrintf(const char* pFmt, ...) +{ + if(!S.WSBuf.nBufferSize) + { + MicroProfileResizeWSBuf(STB_SPRINTF_MIN * 2); + } + va_list args; + va_start(args, pFmt); + MP_ASSERT(S.WSBuf.nPut + STB_SPRINTF_MIN < S.WSBuf.nBufferSize); + stbsp_vsprintfcb(MicroProfileWSPrintfCallback, 0, S.WSBuf.pBuffer + S.WSBuf.nPut, pFmt, args); + va_end(args); +} + +void MicroProfileWSPrintEnd() +{ + MP_ASSERT(S.WSBuf.nPut == 0); + S.WSBuf.Socket = 0; +} + +void MicroProfileWSFlush() +{ + MP_ASSERT(S.WSBuf.Socket != 0); + MP_ASSERT(S.WSBuf.nPut != 0); + MicroProfileWebSocketSend(S.WSBuf.Socket, &S.WSBuf.pBuffer[0], S.WSBuf.nPut); + S.WSBuf.nPut = 0; +} +void MicroProfileWebSocketSendEnabledMessage(uint32_t id, int bEnabled) +{ + MicroProfileWSPrintf("{\"k\":\"%d\",\"v\":{\"id\":%d,\"e\":%d}}", MSG_ENABLED, id, bEnabled ? 1 : 0); + MicroProfileWSFlush(); +} +void MicroProfileWebSocketSendEnabled(MpSocket C) +{ + MICROPROFILE_SCOPEI("MicroProfile", "Websocket-SendEnabled", MP_GREEN4); + MicroProfileWSPrintStart(C); + for(uint32_t i = 0; i < S.nCategoryCount; ++i) + { + MicroProfileWebSocketSendEnabledMessage(MicroProfileWebSocketIdPack(TYPE_CATEGORY, i), MicroProfileCategoryEnabled(i)); + } + + for(uint32_t i = 0; i < S.nGroupCount; ++i) + { + MicroProfileWebSocketSendEnabledMessage(MicroProfileWebSocketIdPack(TYPE_GROUP, i), MicroProfileGroupEnabled(i)); + } + for(uint32_t i = 0; i < S.nTotalTimers; ++i) + { + MicroProfileWebSocketSendEnabledMessage(MicroProfileWebSocketIdPack(TYPE_TIMER, i), MicroProfileWebSocketTimerEnabled(i)); + } + for(uint32_t i = 0; i < S.nNumCounters; ++i) + { + MicroProfileWebSocketSendEnabledMessage(MicroProfileWebSocketIdPack(TYPE_COUNTER, i), MicroProfileWebSocketCounterEnabled(i)); + } + MicroProfileWebSocketSendEnabledMessage(MicroProfileWebSocketIdPack(TYPE_SETTING, SETTING_FORCE_ENABLE), MicroProfileGetEnableAllGroups()); + MicroProfileWebSocketSendEnabledMessage(MicroProfileWebSocketIdPack(TYPE_SETTING, SETTING_CONTEXT_SWITCH_TRACE), S.bContextSwitchRunning); + MicroProfileWebSocketSendEnabledMessage(MicroProfileWebSocketIdPack(TYPE_SETTING, SETTING_PLATFORM_MARKERS), MicroProfilePlatformMarkersGetEnabled()); + + MicroProfileWSPrintEnd(); +} +void MicroProfileWebSocketSendEntry(uint32_t id, uint32_t parent, const char* pName, int nEnabled, uint32_t nColor, uint32_t nType) +{ + MicroProfileWSPrintf("{\"k\":\"%d\",\"v\":{\"id\":%d,\"pid\":%d,", MSG_TIMER_TREE, id, parent); + MicroProfileWSPrintf("\"name\":\"%s\",", pName); + MicroProfileWSPrintf("\"e\":%d,", nEnabled); + MicroProfileWSPrintf("\"type\":%d,", nType); + if(nColor == 0x42) + { + MicroProfileWSPrintf("\"color\":\"\""); + } + else + { + MicroProfileWSPrintf("\"color\":\"#%02x%02x%02x\"", MICROPROFILE_UNPACK_RED(nColor) & 0xff, MICROPROFILE_UNPACK_GREEN(nColor) & 0xff, MICROPROFILE_UNPACK_BLUE(nColor) & 0xff); + } + + MicroProfileWSPrintf("}}"); + MicroProfileWSFlush(); +} + +void MicroProfileWebSocketSendCounterEntry(uint32_t id, uint32_t parent, const char* pName, int nEnabled, int64_t nLimit, int nFormat) +{ + MicroProfileWSPrintf("{\"k\":\"%d\",\"v\":{\"id\":%d,\"pid\":%d,", MSG_TIMER_TREE, id, parent); + MicroProfileWSPrintf("\"name\":\"%s\",", pName); + MicroProfileWSPrintf("\"e\":%d,", nEnabled); + MicroProfileWSPrintf("\"limit\":%lld,", nLimit); + MicroProfileWSPrintf("\"format\":%d", nFormat); + MicroProfileWSPrintf("}}"); + MicroProfileWSFlush(); +} + +void MicroProfileWebSocketSendState(MpSocket C) +{ + if(S.WSCategoriesSent != S.nCategoryCount || S.WSGroupsSent != S.nGroupCount || S.WSTimersSent != S.nTotalTimers || S.WSCountersSent != S.nNumCounters) + { + MicroProfileWSPrintStart(C); + uint32_t root = MicroProfileWebSocketIdPack(TYPE_SETTING, SETTING_FORCE_ENABLE); + MicroProfileWebSocketSendEntry(root, 0, "All", MicroProfileGetEnableAllGroups(), (uint32_t)-1, 0); + for(uint32_t i = S.WSCategoriesSent; i < S.nCategoryCount; ++i) + { + + MicroProfileCategory& CI = S.CategoryInfo[i]; + uint32_t id = MicroProfileWebSocketIdPack(TYPE_CATEGORY, i); + uint32_t parent = root; + MicroProfileWebSocketSendEntry(id, parent, CI.pName, MicroProfileCategoryEnabled(i), 0xffffffff, 0); + } + + for(uint32_t i = S.WSGroupsSent; i < S.nGroupCount; ++i) + { + MicroProfileGroupInfo& GI = S.GroupInfo[i]; + uint32_t id = MicroProfileWebSocketIdPack(TYPE_GROUP, i); + uint32_t parent = MicroProfileWebSocketIdPack(TYPE_CATEGORY, GI.nCategory); + MicroProfileWebSocketSendEntry(id, parent, GI.pName, MicroProfileGroupEnabled(i), GI.nColor, GI.Type); + } + + for(uint32_t i = S.WSTimersSent; i < S.nTotalTimers; ++i) + { + MicroProfileTimerInfo& TI = S.TimerInfo[i]; + uint32_t id = MicroProfileWebSocketIdPack(TYPE_TIMER, i); + uint32_t parent = MicroProfileWebSocketIdPack(TYPE_GROUP, TI.nGroupIndex); + MicroProfileWebSocketSendEntry(id, parent, TI.pName, MicroProfileWebSocketTimerEnabled(i), TI.nColor, TI.Type); + } + + for(uint32_t i = S.WSCountersSent; i < S.nNumCounters; ++i) + { + MicroProfileCounterInfo& CI = S.CounterInfo[i]; + uint32_t id = MicroProfileWebSocketIdPack(TYPE_COUNTER, i); + uint32_t parent = CI.nParent == -1 ? 0u : MicroProfileWebSocketIdPack(TYPE_COUNTER, CI.nParent); + MicroProfileWebSocketSendCounterEntry(id, parent, CI.pName, MicroProfileWebSocketCounterEnabled(i), CI.nLimit, CI.eFormat); + } +#if MICROPROFILE_CONTEXT_SWITCH_TRACE + MicroProfileWebSocketSendEntry(MicroProfileWebSocketIdPack(TYPE_SETTING, SETTING_CONTEXT_SWITCH_TRACE), 0, "Context Switch Trace", S.bContextSwitchRunning, (uint32_t)-1, 0); +#endif +#if MICROPROFILE_PLATFORM_MARKERS + MicroProfileWebSocketSendEntry(MicroProfileWebSocketIdPack(TYPE_SETTING, SETTING_PLATFORM_MARKERS), 0, "Platform Markers", S.bContextSwitchRunning, (uint32_t)-1); +#endif + MicroProfileWSPrintEnd(); + + S.WSCategoriesSent = S.nCategoryCount; + S.WSGroupsSent = S.nGroupCount; + S.WSTimersSent = S.nTotalTimers; + S.WSCountersSent = S.nNumCounters; + } +} + +bool MicroProfileWebServerUpdate() +{ + MICROPROFILE_SCOPEI("MicroProfile", "Webserver-update", MP_GREEN4); + MpSocket Connection = accept(S.ListenerSocket, 0, 0); + bool bServed = false; + MicroProfileWebSocketFrame(); + if(!MP_INVALID_SOCKET(Connection)) + { + std::lock_guard Lock(MicroProfileMutex()); + char Req[8192]; + int nReceived = recv(Connection, Req, sizeof(Req) - 1, 0); + if(nReceived > 0) + { + Req[nReceived] = '\0'; + uprintf("req received\n%s", Req); + +#define MICROPROFILE_HTML_PNG_HEADER "HTTP/1.0 200 OK\r\nContent-Type: image/png\r\n\r\n" +#define MICROPROFILE_HTML_JS_HEADER "HTTP/1.0 200 OK\r\nContent-Type: text/javascript\r\n\r\n" +#if MICROPROFILE_MINIZ + // Expires: Tue, 01 Jan 2199 16:00:00 GMT\r\n +#define MICROPROFILE_HTML_HEADER "HTTP/1.0 200 OK\r\nContent-Type: text/html\r\nContent-Encoding: deflate\r\n\r\n" +#else +#define MICROPROFILE_HTML_HEADER "HTTP/1.0 200 OK\r\nContent-Type: text/html\r\n\r\n" +#endif + + char* pHttp = strstr(Req, "HTTP/"); + + char* pGet = strstr(Req, "GET /"); + char* pHost = strstr(Req, "Host: "); + char* pWebSocketKey = strstr(Req, "Sec-WebSocket-Key: "); + auto Terminate = [](char* pString) + { + char* pEnd = pString; + while(*pEnd != '\0') + { + if(*pEnd == '\r' || *pEnd == '\n' || *pEnd == ' ') + { + *pEnd = '\0'; + return; + } + pEnd++; + } + }; + + if(pWebSocketKey) + { + if(S.nNumWebSockets) // only allow 1 + { + return false; + } + pWebSocketKey += sizeof("Sec-WebSocket-Key: ") - 1; + Terminate(pWebSocketKey); + MicroProfileWebSocketHandshake(Connection, pWebSocketKey); + return false; + } + + if(pHost) + { + pHost += sizeof("Host: ") - 1; + Terminate(pHost); + } + + if(pHttp && pGet) + { + *pHttp = '\0'; + pGet += sizeof("GET /") - 1; + Terminate(pGet); + MicroProfileParseGetResult R; + auto P = MicroProfileParseGet(pGet, &R); + switch(P) + { + case EMICROPROFILE_GET_COMMAND_SERVICE_WORKER: + { + MicroProfileSetNonBlocking(Connection, 1); + uint64_t nTickStart = MP_TICK(); + send(Connection, MICROPROFILE_HTML_JS_HEADER, sizeof(MICROPROFILE_HTML_JS_HEADER) - 1, 0); + const char* JsCode = "self.addEventListener(\"fetch\", () => {}); \r\n\r\n"; + send(Connection, JsCode, (int)strlen(JsCode), 0); + break; + } + case EMICROPROFILE_GET_COMMAND_FAVICON: + { + MicroProfileSetNonBlocking(Connection, 1); + uint64_t nTickStart = MP_TICK(); + send(Connection, MICROPROFILE_HTML_PNG_HEADER, sizeof(MICROPROFILE_HTML_PNG_HEADER) - 1, 0); + extern const uint32_t uprof_512[]; + extern const uint32_t uprof_512_len; + const char* pFile = (const char*)&uprof_512[0]; + uint32_t nFileSize = uprof_512_len; + send(Connection, pFile, nFileSize, 0); + } + break; + case EMICROPROFILE_GET_COMMAND_LIVE: + { + MicroProfileSetNonBlocking(Connection, 0); + uint64_t nTickStart = MP_TICK(); + send(Connection, MICROPROFILE_HTML_HEADER, sizeof(MICROPROFILE_HTML_HEADER) - 1, 0); + uint64_t nDataStart = S.nWebServerDataSent; + S.WebServerPut = 0; +#if 0 == MICROPROFILE_MINIZ + MicroProfileDumpHtmlLive(MicroProfileWriteSocket, &Connection); + uint64_t nDataEnd = S.nWebServerDataSent; + uint64_t nTickEnd = MP_TICK(); + uint64_t nDiff = (nTickEnd - nTickStart); + float fMs = MicroProfileTickToMsMultiplier(MicroProfileTicksPerSecondCpu()) * nDiff; + int nKb = ((nDataEnd-nDataStart)>>10) + 1; + int nCompressedKb = nKb; + MicroProfilePrintf(MicroProfileWriteSocket, &Connection, "\n\n\n",nKb, fMs); + MicroProfileFlushSocket(Connection); +#else + MicroProfileCompressedSocketState CompressState; + MicroProfileCompressedSocketStart(&CompressState, Connection); + MicroProfileDumpHtmlLive(MicroProfileCompressedWriteSocket, &CompressState); + S.nWebServerDataSent += CompressState.nSize; + uint64_t nDataEnd = S.nWebServerDataSent; + uint64_t nTickEnd = MP_TICK(); + uint64_t nDiff = (nTickEnd - nTickStart); + float fMs = MicroProfileTickToMsMultiplier(MicroProfileTicksPerSecondCpu()) * nDiff; + int nKb = ((nDataEnd - nDataStart) >> 10) + 1; + int nCompressedKb = ((CompressState.nCompressedSize) >> 10) + 1; + MicroProfilePrintf(MicroProfileCompressedWriteSocket, &CompressState, "\n\n\n", nKb, nCompressedKb, fMs); + MicroProfileCompressedSocketFinish(&CompressState); + MicroProfileFlushSocket(Connection); +#endif + + uprintf("\n\n\n", nKb, nCompressedKb, fMs); + (void)nCompressedKb; + } + break; + case EMICROPROFILE_GET_COMMAND_DUMP_RANGE: + case EMICROPROFILE_GET_COMMAND_DUMP: + { + { + MicroProfileSetNonBlocking(Connection, 0); + uint64_t nTickStart = MP_TICK(); + send(Connection, MICROPROFILE_HTML_HEADER, sizeof(MICROPROFILE_HTML_HEADER) - 1, 0); + uint64_t nDataStart = S.nWebServerDataSent; + S.WebServerPut = 0; +#if 0 == MICROPROFILE_MINIZ + MicroProfileDumpHtml(MicroProfileWriteSocket, &Connection, R.nFrames, pHost, R.nFrameStart); + uint64_t nDataEnd = S.nWebServerDataSent; + uint64_t nTickEnd = MP_TICK(); + uint64_t nDiff = (nTickEnd - nTickStart); + float fMs = MicroProfileTickToMsMultiplier(MicroProfileTicksPerSecondCpu()) * nDiff; + int nKb = ((nDataEnd-nDataStart)>>10) + 1; + int nCompressedKb = nKb; + MicroProfilePrintf(MicroProfileWriteSocket, &Connection, "\n\n\n",nKb, fMs); + MicroProfileFlushSocket(Connection); +#else + MicroProfileCompressedSocketState CompressState; + MicroProfileCompressedSocketStart(&CompressState, Connection); + + MicroProfileDumpHtml(MicroProfileCompressedWriteSocket, &CompressState, R.nFrames, pHost, R.nFrameStart); + + S.nWebServerDataSent += CompressState.nSize; + uint64_t nDataEnd = S.nWebServerDataSent; + uint64_t nTickEnd = MP_TICK(); + uint64_t nDiff = (nTickEnd - nTickStart); + float fMs = MicroProfileTickToMsMultiplier(MicroProfileTicksPerSecondCpu()) * nDiff; + int nKb = ((nDataEnd - nDataStart) >> 10) + 1; + int nCompressedKb = ((CompressState.nCompressedSize) >> 10) + 1; + MicroProfilePrintf(MicroProfileCompressedWriteSocket, &CompressState, "\n\n\n", nKb, nCompressedKb, fMs); + MicroProfileCompressedSocketFinish(&CompressState); + MicroProfileFlushSocket(Connection); +#endif + + uprintf("\n\n\n", nKb, nCompressedKb, fMs); + (void)nCompressedKb; + } + } + break; + case EMICROPROFILE_GET_COMMAND_UNKNOWN: + { + uprintf("unknown get command %s\n", pGet); + } + break; + } + } + } +#ifdef _WIN32 + closesocket(Connection); +#else + close(Connection); +#endif + } + return bServed; +} +#endif + +#if MICROPROFILE_CONTEXT_SWITCH_TRACE +// functions that need to be implemented per platform. +void* MicroProfileTraceThread(void* unused); +int MicroProfileIsLocalThread(uint32_t nThreadId); + +void MicroProfileStartContextSwitchTrace() +{ + if(!S.bContextSwitchRunning && !S.nMicroProfileShutdown) + { + S.bContextSwitchRunning = true; + S.bContextSwitchStop = false; + MicroProfileThreadStart(&S.ContextSwitchThread, MicroProfileTraceThread); + } +} + +void MicroProfileJoinContextSwitchTrace() +{ + if(S.bContextSwitchStop) + { + MicroProfileThreadJoin(&S.ContextSwitchThread); + } +} + +void MicroProfileStopContextSwitchTrace() +{ + if(S.bContextSwitchRunning) + { + S.bContextSwitchStop = true; + } +} + +#ifdef _WIN32 +#define INITGUID +#include +#include +#include + +static GUID g_MicroProfileThreadClassGuid = { 0x3d6fa8d1, 0xfe05, 0x11d0, 0x9d, 0xda, 0x00, 0xc0, 0x4f, 0xd7, 0xba, 0x7c }; + +struct MicroProfileSCSwitch +{ + uint32_t NewThreadId; + uint32_t OldThreadId; + int8_t NewThreadPriority; + int8_t OldThreadPriority; + uint8_t PreviousCState; + int8_t SpareByte; + int8_t OldThreadWaitReason; + int8_t OldThreadWaitMode; + int8_t OldThreadState; + int8_t OldThreadWaitIdealProcessor; + uint32_t NewThreadWaitTime; + uint32_t Reserved; +}; + +VOID WINAPI MicroProfileContextSwitchCallback(PEVENT_TRACE pEvent) +{ + if(pEvent->Header.Guid == g_MicroProfileThreadClassGuid) + { + if(pEvent->Header.Class.Type == 36) + { + MicroProfileSCSwitch* pCSwitch = (MicroProfileSCSwitch*)pEvent->MofData; + if((pCSwitch->NewThreadId != 0) || (pCSwitch->OldThreadId != 0)) + { + MicroProfileContextSwitch Switch; + Switch.nThreadOut = pCSwitch->OldThreadId; + Switch.nThreadIn = pCSwitch->NewThreadId; + Switch.nCpu = pEvent->BufferContext.ProcessorNumber; + Switch.nTicks = pEvent->Header.TimeStamp.QuadPart; + MicroProfileContextSwitchPut(&Switch); + } + } + } +} + +ULONG WINAPI MicroProfileBufferCallback(PEVENT_TRACE_LOGFILEA Buffer) +{ + return (S.bContextSwitchStop || !S.bContextSwitchRunning) ? FALSE : TRUE; +} + +struct MicroProfileKernelTraceProperties : public EVENT_TRACE_PROPERTIES +{ + char dummy[sizeof(KERNEL_LOGGER_NAME)]; +}; + +void MicroProfileContextSwitchShutdownTrace() +{ + TRACEHANDLE SessionHandle = 0; + MicroProfileKernelTraceProperties sessionProperties; + + ZeroMemory(&sessionProperties, sizeof(sessionProperties)); + sessionProperties.Wnode.BufferSize = sizeof(sessionProperties); + sessionProperties.Wnode.Flags = WNODE_FLAG_TRACED_GUID; + sessionProperties.Wnode.ClientContext = 1; // QPC clock resolution + sessionProperties.Wnode.Guid = SystemTraceControlGuid; + sessionProperties.BufferSize = 1; + sessionProperties.NumberOfBuffers = 128; + sessionProperties.EnableFlags = EVENT_TRACE_FLAG_CSWITCH; + sessionProperties.LogFileMode = EVENT_TRACE_REAL_TIME_MODE; + sessionProperties.MaximumFileSize = 0; + sessionProperties.LoggerNameOffset = sizeof(EVENT_TRACE_PROPERTIES); + sessionProperties.LogFileNameOffset = 0; + + EVENT_TRACE_LOGFILEA log; + ZeroMemory(&log, sizeof(log)); + log.LoggerName = (LPSTR)KERNEL_LOGGER_NAMEA; + log.ProcessTraceMode = 0; + TRACEHANDLE hLog = OpenTraceA(&log); + if(hLog) + { + ControlTrace(SessionHandle, KERNEL_LOGGER_NAME, &sessionProperties, EVENT_TRACE_CONTROL_STOP); + } + CloseTrace(hLog); +} + +typedef VOID(WINAPI* EventCallback)(PEVENT_TRACE); +typedef ULONG(WINAPI* BufferCallback)(PEVENT_TRACE_LOGFILEA); +bool MicroProfileStartWin32Trace(EventCallback EvtCb, BufferCallback BufferCB) +{ + MicroProfileContextSwitchShutdownTrace(); + ULONG status = ERROR_SUCCESS; + TRACEHANDLE SessionHandle = 0; + MicroProfileKernelTraceProperties sessionProperties; + + ZeroMemory(&sessionProperties, sizeof(sessionProperties)); + sessionProperties.Wnode.BufferSize = sizeof(sessionProperties); + sessionProperties.Wnode.Flags = WNODE_FLAG_TRACED_GUID; + sessionProperties.Wnode.ClientContext = 1; // QPC clock resolution + sessionProperties.Wnode.Guid = SystemTraceControlGuid; + sessionProperties.BufferSize = 1; + sessionProperties.NumberOfBuffers = 128; + sessionProperties.EnableFlags = EVENT_TRACE_FLAG_CSWITCH | EVENT_TRACE_FLAG_PROCESS; + sessionProperties.LogFileMode = EVENT_TRACE_REAL_TIME_MODE; + sessionProperties.MaximumFileSize = 0; + sessionProperties.LoggerNameOffset = sizeof(EVENT_TRACE_PROPERTIES); + sessionProperties.LogFileNameOffset = 0; + + StopTrace(NULL, KERNEL_LOGGER_NAME, &sessionProperties); + status = StartTrace((PTRACEHANDLE)&SessionHandle, KERNEL_LOGGER_NAME, &sessionProperties); + + if(ERROR_SUCCESS != status) + { + return false; + } + + EVENT_TRACE_LOGFILEA log; + ZeroMemory(&log, sizeof(log)); + + log.LoggerName = (LPSTR)KERNEL_LOGGER_NAME; + log.ProcessTraceMode = PROCESS_TRACE_MODE_REAL_TIME | PROCESS_TRACE_MODE_RAW_TIMESTAMP; + log.EventCallback = EvtCb; + log.BufferCallback = BufferCB; + + TRACEHANDLE hLog = OpenTraceA(&log); + ProcessTrace(&hLog, 1, 0, 0); + CloseTrace(hLog); + MicroProfileContextSwitchShutdownTrace(); + return true; +} + +#include +#include +#include +#define ThreadQuerySetWin32StartAddress 9 +typedef LONG NTSTATUS; +typedef NTSTATUS(WINAPI* pNtQIT)(HANDLE, LONG, PVOID, ULONG, PULONG); +#define STATUS_SUCCESS ((NTSTATUS)0x000 00000L) +#define ThreadQuerySetWin32StartAddress 9 +#undef Process32First +#undef Process32Next +#undef PROCESSENTRY32 +#undef Module32First +#undef Module32Next +#undef MODULEENTRY32 + +struct MicroProfileWin32ContextSwitchShared +{ + std::atomic nPut; + std::atomic nGet; + std::atomic nQuit; + std::atomic nTickTrace; + std::atomic nTickProgram; + enum + { + BUFFER_SIZE = (2 << 20) / sizeof(MicroProfileContextSwitch), + }; + MicroProfileContextSwitch Buffer[BUFFER_SIZE]; +}; + +struct MicroProfileWin32ThreadInfo +{ + struct Process + { + uint32_t pid; + uint32_t nNumModules; + uint32_t nModuleStart; + const char* pProcessModule; + }; + struct Module + { + int64_t nBase; + int64_t nEnd; + const char* pName; + }; + enum + { + MAX_PROCESSES = 5 * 1024, + MAX_THREADS = 20 * 1024, + MAX_MODULES = 20 * 1024, + MAX_STRINGS = 16 * 1024, + MAX_CHARS = 128 * 1024, + }; + uint32_t nNumProcesses; + uint32_t nNumThreads; + uint32_t nStringOffset; + uint32_t nNumStrings; + uint32_t nNumModules; + Process P[MAX_PROCESSES]; + Module M[MAX_MODULES]; + MicroProfileThreadInfo T[MAX_THREADS]; + const char* pStrings[MAX_STRINGS]; + char StringData[MAX_CHARS]; +}; + +static MicroProfileWin32ThreadInfo g_ThreadInfo; + +const char* MicroProfileWin32ThreadInfoAddString(const char* pString) +{ + size_t nLen = strlen(pString); + uint32_t nHash = *(uint32_t*)pString; + nHash ^= (nHash >> 16); + enum + { + MAX_SEARCH = 256, + }; + for(uint32_t i = 0; i < MAX_SEARCH; ++i) + { + uint32_t idx = (i + nHash) % MicroProfileWin32ThreadInfo::MAX_STRINGS; + if(0 == g_ThreadInfo.pStrings[idx]) + { + g_ThreadInfo.pStrings[idx] = &g_ThreadInfo.StringData[g_ThreadInfo.nStringOffset]; + memcpy(&g_ThreadInfo.StringData[g_ThreadInfo.nStringOffset], pString, nLen + 1); + g_ThreadInfo.nStringOffset += (uint32_t)(nLen + 1); + return g_ThreadInfo.pStrings[idx]; + } + if(0 == strcmp(g_ThreadInfo.pStrings[idx], pString)) + { + return g_ThreadInfo.pStrings[idx]; + } + } + return "internal hash table fail: should never happen"; +} +void MicroProfileWin32ExtractModules(MicroProfileWin32ThreadInfo::Process& P) +{ + HANDLE hModuleSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, P.pid); + MODULEENTRY32 me; + if(Module32First(hModuleSnapshot, &me)) + { + do + { + if(g_ThreadInfo.nNumModules < MicroProfileWin32ThreadInfo::MAX_MODULES) + { + auto& M = g_ThreadInfo.M[g_ThreadInfo.nNumModules++]; + P.nNumModules++; + intptr_t nBase = (intptr_t)me.modBaseAddr; + intptr_t nEnd = nBase + me.modBaseSize; + M.nBase = nBase; + M.nEnd = nEnd; + M.pName = MicroProfileWin32ThreadInfoAddString(&me.szModule[0]); + } + } while(Module32Next(hModuleSnapshot, &me)); + } + if(hModuleSnapshot) + CloseHandle(hModuleSnapshot); +} +void MicroProfileWin32InitThreadInfo2() +{ + memset(&g_ThreadInfo, 0, sizeof(g_ThreadInfo)); +#if MICROPROFILE_DEBUG + float fToMsCpu = MicroProfileTickToMsMultiplier(MicroProfileTicksPerSecondCpu()); +#endif + + HANDLE hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPALL, 0); + PROCESSENTRY32 pe32; + THREADENTRY32 te32; + te32.dwSize = sizeof(THREADENTRY32); + pe32.dwSize = sizeof(PROCESSENTRY32); + { +#if MICROPROFILE_DEBUG + int64_t nTickStart = MP_TICK(); +#endif + if(Process32First(hSnap, &pe32)) + { + do + { + + MicroProfileWin32ThreadInfo::Process P; + P.pid = pe32.th32ProcessID; + P.pProcessModule = MicroProfileWin32ThreadInfoAddString(pe32.szExeFile); + g_ThreadInfo.P[g_ThreadInfo.nNumProcesses++] = P; + } while(Process32Next(hSnap, &pe32) && g_ThreadInfo.nNumProcesses < MicroProfileWin32ThreadInfo::MAX_PROCESSES); + } +#if MICROPROFILE_DEBUG + int64_t nTicksEnd = MP_TICK(); + float fMs = fToMsCpu * (nTicksEnd - nTickStart); + uprintf("Process iteration %6.2fms processes %d\n", fMs, g_ThreadInfo.nNumProcesses); +#endif + } + { +#if MICROPROFILE_DEBUG + int64_t nTickStart = MP_TICK(); +#endif + for(uint32_t i = 0; i < g_ThreadInfo.nNumProcesses; ++i) + { + g_ThreadInfo.P[i].nModuleStart = g_ThreadInfo.nNumModules; + g_ThreadInfo.P[i].nNumModules = 0; + MicroProfileWin32ExtractModules(g_ThreadInfo.P[i]); + } +#if MICROPROFILE_DEBUG + int64_t nTicksEnd = MP_TICK(); + float fMs = fToMsCpu * (nTicksEnd - nTickStart); + uprintf("Module iteration %6.2fms NumModules %d\n", fMs, g_ThreadInfo.nNumModules); +#endif + } + + pNtQIT NtQueryInformationThread = (pNtQIT)GetProcAddress(GetModuleHandleA("ntdll.dll"), "NtQueryInformationThread"); + intptr_t dwStartAddress; + ULONG olen; + uint32_t nThreadsTested = 0; + uint32_t nThreadsSucceeded = 0; + + if(Thread32First(hSnap, &te32)) + { +#if MICROPROFILE_DEBUG + int64_t nTickStart = MP_TICK(); +#endif + do + { + nThreadsTested++; + const char* pModule = "?"; + HANDLE hThread = OpenThread(THREAD_QUERY_INFORMATION, FALSE, te32.th32ThreadID); + if(hThread) + { + + NTSTATUS ntStatus = NtQueryInformationThread(hThread, (THREADINFOCLASS)ThreadQuerySetWin32StartAddress, &dwStartAddress, sizeof(dwStartAddress), &olen); + if(0 == ntStatus) + { + uint32_t nProcessIndex = (uint32_t)-1; + for(uint32_t i = 0; i < g_ThreadInfo.nNumProcesses; ++i) + { + if(g_ThreadInfo.P[i].pid == te32.th32OwnerProcessID) + { + nProcessIndex = i; + break; + } + } + if(nProcessIndex != (uint32_t)-1) + { + uint32_t nModuleStart = g_ThreadInfo.P[nProcessIndex].nModuleStart; + uint32_t nNumModules = g_ThreadInfo.P[nProcessIndex].nNumModules; + for(uint32_t i = 0; i < nNumModules; ++i) + { + auto& M = g_ThreadInfo.M[nModuleStart + i]; + if(M.nBase <= dwStartAddress && M.nEnd >= dwStartAddress) + { + pModule = M.pName; + } + } + } + } + } + if(hThread) + CloseHandle(hThread); + { + MicroProfileThreadInfo T; + T.pid = te32.th32OwnerProcessID; + T.tid = te32.th32ThreadID; + const char* pProcess = "unknown"; + for(uint32_t i = 0; i < g_ThreadInfo.nNumProcesses; ++i) + { + if(g_ThreadInfo.P[i].pid == T.pid) + { + pProcess = g_ThreadInfo.P[i].pProcessModule; + break; + } + } + T.pProcessModule = pProcess; + T.pThreadModule = MicroProfileWin32ThreadInfoAddString(pModule); + T.nIsLocal = GetCurrentProcessId() == T.pid ? 1 : 0; + nThreadsSucceeded++; + g_ThreadInfo.T[g_ThreadInfo.nNumThreads++] = T; + } + + } while(Thread32Next(hSnap, &te32) && g_ThreadInfo.nNumThreads < MicroProfileWin32ThreadInfo::MAX_THREADS); + +#if MICROPROFILE_DEBUG + int64_t nTickEnd = MP_TICK(); + float fMs = fToMsCpu * (nTickEnd - nTickStart); + uprintf("Thread iteration %6.2fms Threads %d\n", fMs, g_ThreadInfo.nNumThreads); +#endif + } +} + +void MicroProfileWin32UpdateThreadInfo() +{ + static int nWasRunning = 1; + static int nOnce = 0; + int nRunning = MicroProfileAnyGroupActive() ? 1 : 0; + + if((0 == nRunning && 1 == nWasRunning) || nOnce == 0) + { + nOnce = 1; + MicroProfileWin32InitThreadInfo2(); + } + nWasRunning = nRunning; +} + +const char* MicroProfileThreadNameFromId(MicroProfileThreadIdType nThreadId) +{ + MicroProfileWin32UpdateThreadInfo(); + static char result[1024]; + for(uint32_t i = 0; i < g_ThreadInfo.nNumThreads; ++i) + { + if(g_ThreadInfo.T[i].tid == nThreadId) + { + sprintf_s(result, "p:%s t:%s", g_ThreadInfo.T[i].pProcessModule, g_ThreadInfo.T[i].pThreadModule); + return result; + } + } + sprintf_s(result, "?"); + return result; +} + +#define MICROPROFILE_FILEMAPPING "microprofile-shared" +#ifdef MICROPROFILE_WIN32_COLLECTOR +#define MICROPROFILE_WIN32_CSWITCH_TIMEOUT 15 // seconds to wait before collector exits +static MicroProfileWin32ContextSwitchShared* g_pShared = 0; +VOID WINAPI MicroProfileContextSwitchCallbackCollector(PEVENT_TRACE pEvent) +{ + static int64_t nPackets = 0; + static int64_t nSkips = 0; + if(pEvent->Header.Guid == g_MicroProfileThreadClassGuid) + { + if(pEvent->Header.Class.Type == 36) + { + MicroProfileSCSwitch* pCSwitch = (MicroProfileSCSwitch*)pEvent->MofData; + if((pCSwitch->NewThreadId != 0) || (pCSwitch->OldThreadId != 0)) + { + MicroProfileContextSwitch Switch; + Switch.nThreadOut = pCSwitch->OldThreadId; + Switch.nThreadIn = pCSwitch->NewThreadId; + Switch.nCpu = pEvent->BufferContext.ProcessorNumber; + Switch.nTicks = pEvent->Header.TimeStamp.QuadPart; + int64_t nPut = g_pShared->nPut.load(std::memory_order_relaxed); + int64_t nGet = g_pShared->nGet.load(std::memory_order_relaxed); + nPackets++; + if(nPut - nGet < MicroProfileWin32ContextSwitchShared::BUFFER_SIZE) + { + g_pShared->Buffer[nPut % MicroProfileWin32ContextSwitchShared::BUFFER_SIZE] = Switch; + g_pShared->nPut.store(nPut + 1, std::memory_order_release); + nSkips = 0; + } + else + { + nSkips++; + } + } + } + } + if(0 == (nPackets % (4 << 10))) + { + int64_t nTickTrace = MP_TICK(); + g_pShared->nTickTrace.store(nTickTrace); + int64_t nTickProgram = g_pShared->nTickProgram.load(); + float fTickToMs = MicroProfileTickToMsMultiplier(MicroProfileTicksPerSecondCpu()); + float fTime = fabs(fTickToMs * (nTickTrace - nTickProgram)); + printf("\rRead %" PRId64 " CSwitch Packets, Skips %" PRId64 " Time difference %6.3fms ", nPackets, nSkips, fTime); + fflush(stdout); + if(fTime > MICROPROFILE_WIN32_CSWITCH_TIMEOUT * 1000) + { + g_pShared->nQuit.store(1); + } + } +} + +ULONG WINAPI MicroProfileBufferCallbackCollector(PEVENT_TRACE_LOGFILEA Buffer) +{ + return (g_pShared->nQuit.load()) ? FALSE : TRUE; +} + +int main(int argc, char* argv[]) +{ + if(argc != 2) + { + return 1; + } + printf("using file '%s'\n", argv[1]); + HANDLE hMemory = OpenFileMappingA(FILE_MAP_ALL_ACCESS, FALSE, argv[1]); + if(hMemory == NULL) + { + return 1; + } + g_pShared = (MicroProfileWin32ContextSwitchShared*)MapViewOfFile(hMemory, FILE_MAP_ALL_ACCESS, 0, 0, sizeof(MicroProfileWin32ContextSwitchShared)); + + if(g_pShared != NULL) + { + MicroProfileStartWin32Trace(MicroProfileContextSwitchCallbackCollector, MicroProfileBufferCallbackCollector); + UnmapViewOfFile(g_pShared); + } + + CloseHandle(hMemory); + return 0; +} +#endif +#include +void* MicroProfileTraceThread(void* unused) +{ + MicroProfileOnThreadCreate("ContextSwitchThread"); + MicroProfileContextSwitchShutdownTrace(); + if(!MicroProfileStartWin32Trace(MicroProfileContextSwitchCallback, MicroProfileBufferCallback)) + { + MicroProfileContextSwitchShutdownTrace(); + // not running as admin. try and start other process. + MicroProfileWin32ContextSwitchShared* pShared = 0; + char Filename[512]; + time_t t = time(NULL); + _snprintf_s(Filename, sizeof(Filename), "%s_%d", MICROPROFILE_FILEMAPPING, (int)t); + + HANDLE hMemory = CreateFileMappingA(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, sizeof(MicroProfileWin32ContextSwitchShared), Filename); + if(hMemory != NULL) + { + pShared = (MicroProfileWin32ContextSwitchShared*)MapViewOfFile(hMemory, FILE_MAP_ALL_ACCESS, 0, 0, sizeof(MicroProfileWin32ContextSwitchShared)); + if(pShared != NULL) + { +#ifdef _M_IX86 +#define CSWITCH_EXE "microprofile-win32-cswitch_x86.exe" +#else +#define CSWITCH_EXE "microprofile-win32-cswitch_x64.exe" +#endif + pShared->nTickProgram.store(MP_TICK()); + pShared->nTickTrace.store(MP_TICK()); + HINSTANCE Instance = ShellExecuteA(NULL, "runas", CSWITCH_EXE, Filename, "", SW_SHOWMINNOACTIVE); + int64_t nInstance = (int64_t)Instance; + if(nInstance >= 32) + { + int64_t nPut, nGet; + while(!S.bContextSwitchStop) + { + nPut = pShared->nPut.load(std::memory_order_acquire); + nGet = pShared->nGet.load(std::memory_order_relaxed); + if(nPut == nGet) + { + Sleep(20); + } + else + { + for(int64_t i = nGet; i != nPut; i++) + { + MicroProfileContextSwitchPut(&pShared->Buffer[i % MicroProfileWin32ContextSwitchShared::BUFFER_SIZE]); + } + pShared->nGet.store(nPut, std::memory_order_release); + pShared->nTickProgram.store(MP_TICK()); + } + } + pShared->nQuit.store(1); + } + } + UnmapViewOfFile(pShared); + } + CloseHandle(hMemory); + } + S.bContextSwitchRunning = false; + MicroProfileOnThreadExit(); + return 0; +} + +MicroProfileThreadInfo MicroProfileGetThreadInfo(MicroProfileThreadIdType nThreadId) +{ + MicroProfileWin32UpdateThreadInfo(); + + for(uint32_t i = 0; i < g_ThreadInfo.nNumThreads; ++i) + { + if(g_ThreadInfo.T[i].tid == nThreadId) + { + return g_ThreadInfo.T[i]; + } + } + MicroProfileThreadInfo TI((uint32_t)nThreadId, 0, 0); + return TI; +} +uint32_t MicroProfileGetThreadInfoArray(MicroProfileThreadInfo** pThreadArray) +{ + MicroProfileWin32InitThreadInfo2(); + *pThreadArray = &g_ThreadInfo.T[0]; + return g_ThreadInfo.nNumThreads; +} + +#elif defined(__APPLE__) +#include +void* MicroProfileTraceThread(void* unused) +{ + FILE* pFile = fopen("mypipe", "r"); + if(!pFile) + { + uprintf("CONTEXT SWITCH FAILED TO OPEN FILE: make sure to run dtrace script\n"); + S.bContextSwitchRunning = false; + return 0; + } + uprintf("STARTING TRACE THREAD\n"); + char* pLine = 0; + size_t cap = 0; + size_t len = 0; + struct timeval tv; + + gettimeofday(&tv, NULL); + + uint64_t nsSinceEpoch = ((uint64_t)(tv.tv_sec) * 1000000 + (uint64_t)(tv.tv_usec)) * 1000; + uint64_t nTickEpoch = MP_TICK(); + uint32_t nLastThread[MICROPROFILE_MAX_CONTEXT_SWITCH_THREADS] = { 0 }; + mach_timebase_info_data_t sTimebaseInfo; + mach_timebase_info(&sTimebaseInfo); + S.bContextSwitchRunning = true; + + uint64_t nProcessed = 0; + uint64_t nProcessedLast = 0; + while((len = getline(&pLine, &cap, pFile)) > 0 && !S.bContextSwitchStop) + { + nProcessed += len; + if(nProcessed - nProcessedLast > 10 << 10) + { + nProcessedLast = nProcessed; + uprintf("processed %llukb %llukb\n", (nProcessed - nProcessedLast) >> 10, nProcessed >> 10); + } + + char* pX = strchr(pLine, 'X'); + if(pX) + { + int cpu = atoi(pX + 1); + char* pX2 = strchr(pX + 1, 'X'); + char* pX3 = strchr(pX2 + 1, 'X'); + int thread = atoi(pX2 + 1); + char* lala; + int64_t timestamp = strtoll(pX3 + 1, &lala, 10); + MicroProfileContextSwitch Switch; + + // convert to ticks. + uint64_t nDeltaNsSinceEpoch = timestamp - nsSinceEpoch; + uint64_t nDeltaTickSinceEpoch = sTimebaseInfo.numer * nDeltaNsSinceEpoch / sTimebaseInfo.denom; + uint64_t nTicks = nDeltaTickSinceEpoch + nTickEpoch; + if(cpu < MICROPROFILE_MAX_CONTEXT_SWITCH_THREADS) + { + Switch.nThreadOut = nLastThread[cpu]; + Switch.nThreadIn = thread; + nLastThread[cpu] = thread; + Switch.nCpu = cpu; + Switch.nTicks = nTicks; + MicroProfileContextSwitchPut(&Switch); + } + } + } + uprintf("EXITING TRACE THREAD\n"); + S.bContextSwitchRunning = false; + return 0; +} + +MicroProfileThreadInfo MicroProfileGetThreadInfo(MicroProfileThreadIdType nThreadId) +{ + MicroProfileThreadInfo TI((uint32_t)nThreadId, 0, 0); + return TI; +} +uint32_t MicroProfileGetThreadInfoArray(MicroProfileThreadInfo** pThreadArray) +{ + *pThreadArray = 0; + return 0; +} + +#endif +#else + +MicroProfileThreadInfo MicroProfileGetThreadInfo(MicroProfileThreadIdType nThreadId) +{ + MicroProfileThreadInfo TI((uint32_t)nThreadId, 0, 0); + return TI; +} +uint32_t MicroProfileGetThreadInfoArray(MicroProfileThreadInfo** pThreadArray) +{ + *pThreadArray = 0; + return 0; +} +void MicroProfileStopContextSwitchTrace() +{ +} +void MicroProfileJoinContextSwitchTrace() +{ +} +void MicroProfileStartContextSwitchTrace() +{ +} + +#endif + +#if MICROPROFILE_GPU_TIMERS +void MicroProfileGpuShutdownPlatform() +{ + if(S.pGPU) + { + MicroProfileGpuShutdown(); + MP_FREE(S.pGPU); + S.pGPU = nullptr; + MicroProfileGpuInsertTimeStamp_Callback = nullptr; + MicroProfileGpuGetTimeStamp_Callback = nullptr; + MicroProfileTicksPerSecondGpu_Callback = nullptr; + MicroProfileGetGpuTickReference_Callback = nullptr; + MicroProfileGpuFlip_Callback = nullptr; + MicroProfileGpuShutdown_Callback = nullptr; + } +} + +void MicroProfileGpuInitPlatform(MicroProfileGpuTimerStateType eType, + MicroProfileGpuTimerState* pGPU, + MicroProfileGpuInsertTimeStamp_CB InsertTimeStamp, + MicroProfileGpuGetTimeStamp_CB GetTimeStamp, + MicroProfileTicksPerSecondGpu_CB TicksPerSecond, + MicroProfileGetGpuTickReference_CB GetTickReference, + MicroProfileGpuFlip_CB Flip, + MicroProfileGpuShutdown_CB Shutdown) +{ + + MP_ASSERT(S.pGPU == nullptr); + pGPU->Type = eType; + S.pGPU = pGPU; + + MicroProfileGpuInsertTimeStamp_Callback = InsertTimeStamp; + MicroProfileGpuGetTimeStamp_Callback = GetTimeStamp; + MicroProfileTicksPerSecondGpu_Callback = TicksPerSecond; + MicroProfileGetGpuTickReference_Callback = GetTickReference; + MicroProfileGpuFlip_Callback = Flip; + MicroProfileGpuShutdown_Callback = Shutdown; +} +#endif + +#if MICROPROFILE_GPU_TIMERS_D3D11 +//:'######:::'########::'##::::'##::::'########:::'#######::'########:::::'##::::::'##::: +//'##... ##:: ##.... ##: ##:::: ##:::: ##.... ##:'##.... ##: ##.... ##::'####::::'####::: +// ##:::..::: ##:::: ##: ##:::: ##:::: ##:::: ##:..::::: ##: ##:::: ##::.. ##::::.. ##::: +// ##::'####: ########:: ##:::: ##:::: ##:::: ##::'#######:: ##:::: ##:::: ##:::::: ##::: +// ##::: ##:: ##.....::: ##:::: ##:::: ##:::: ##::...... ##: ##:::: ##:::: ##:::::: ##::: +// ##::: ##:: ##:::::::: ##:::: ##:::: ##:::: ##:'##:::: ##: ##:::: ##:::: ##:::::: ##::: +//. ######::: ##::::::::. #######::::: ########::. #######:: ########:::'######::'######: +//:......::::..::::::::::.......::::::........::::.......:::........::::......:::......:: +uint32_t MicroProfileGpuInsertTimeStampD3D11(void* pContext_) +{ + MicroProfileGpuTimerStateD3D11* pGPU = MicroProfileGetGpuTimerStateD3D11(); + if(!pGPU) + return 0; + MicroProfileD3D11Frame& Frame = pGPU->m_QueryFrames[pGPU->m_nQueryFrame]; + uint32_t nStart = Frame.m_nQueryStart; + if(Frame.m_nRateQueryStarted) + { + uint32_t nIndex = (uint32_t)-1; + do + { + nIndex = Frame.m_nQueryCount.load(); + if(nIndex + 1 >= Frame.m_nQueryCountMax) + { + return (uint32_t)-1; + } + } while(!Frame.m_nQueryCount.compare_exchange_weak(nIndex, nIndex + 1)); + nIndex += nStart; + uint32_t nQueryIndex = nIndex % MICROPROFILE_D3D11_MAX_QUERIES; + + ID3D11Query* pQuery = (ID3D11Query*)pGPU->m_pQueries[nQueryIndex]; + ID3D11DeviceContext* pContext = (ID3D11DeviceContext*)pContext_; + pContext->End(pQuery); + return nQueryIndex; + } + return (uint32_t)-1; +} + +uint64_t MicroProfileGpuGetTimeStampD3D11(uint32_t nIndex) +{ + if(nIndex == (uint32_t)-1) + { + return (uint64_t)-1; + } + MicroProfileGpuTimerStateD3D11* pGPU = MicroProfileGetGpuTimerStateD3D11(); + if(!pGPU) + return 0; + + int64_t nResult = pGPU->m_nQueryResults[nIndex]; + MP_ASSERT(nResult != -1); + return nResult; +} + +bool MicroProfileGpuGetDataD3D11(void* pQuery, void* pData, uint32_t nDataSize) +{ + MicroProfileGpuTimerStateD3D11* pGPU = MicroProfileGetGpuTimerStateD3D11(); + if(!pGPU) + return false; + + HRESULT hr; + do + { + hr = ((ID3D11DeviceContext*)pGPU->m_pImmediateContext)->GetData((ID3D11Query*)pQuery, pData, nDataSize, 0); + } while(hr == S_FALSE); + switch(hr) + { + case DXGI_ERROR_DEVICE_REMOVED: + case DXGI_ERROR_INVALID_CALL: + case E_INVALIDARG: + MP_BREAK(); + return false; + } + return true; +} + +uint64_t MicroProfileTicksPerSecondGpuD3D11() +{ + MicroProfileGpuTimerStateD3D11* pGPU = MicroProfileGetGpuTimerStateD3D11(); + if(!pGPU) + return 1; + + return pGPU->m_nQueryFrequency; +} + +uint32_t MicroProfileGpuFlipD3D11(void* pDeviceContext_) +{ + if(!pDeviceContext_) + { + return (uint32_t)-1; + } + MicroProfileGpuTimerStateD3D11* pGPU = MicroProfileGetGpuTimerStateD3D11(); + if(!pGPU) + return 0; + + ID3D11DeviceContext* pDeviceContext = (ID3D11DeviceContext*)pDeviceContext_; + uint32_t nFrameTimeStamp = MicroProfileGpuInsertTimeStamp(pDeviceContext); + MicroProfileD3D11Frame& CurrentFrame = pGPU->m_QueryFrames[pGPU->m_nQueryFrame]; + ID3D11DeviceContext* pImmediateContext = (ID3D11DeviceContext*)pGPU->m_pImmediateContext; + if(CurrentFrame.m_nRateQueryStarted) + { + pImmediateContext->End((ID3D11Query*)CurrentFrame.m_pRateQuery); + } + uint32_t nNextFrame = (pGPU->m_nQueryFrame + 1) % MICROPROFILE_GPU_FRAME_DELAY; + pGPU->m_nQueryPut = (CurrentFrame.m_nQueryStart + CurrentFrame.m_nQueryCount) % MICROPROFILE_D3D11_MAX_QUERIES; + MicroProfileD3D11Frame& OldFrame = pGPU->m_QueryFrames[nNextFrame]; + if(OldFrame.m_nRateQueryStarted) + { + struct RateQueryResult + { + uint64_t nFrequency; + BOOL bDisjoint; + }; + RateQueryResult Result; + if(MicroProfileGpuGetDataD3D11(OldFrame.m_pRateQuery, &Result, sizeof(Result))) + { + if(pGPU->m_nQueryFrequency != (int64_t)Result.nFrequency) + { + if(pGPU->m_nQueryFrequency) + { + OutputDebugStringA("Query freq changing"); + } + pGPU->m_nQueryFrequency = Result.nFrequency; + } + uint32_t nStart = OldFrame.m_nQueryStart; + uint32_t nCount = OldFrame.m_nQueryCount; + for(uint32_t i = 0; i < nCount; ++i) + { + uint32_t nIndex = (i + nStart) % MICROPROFILE_D3D11_MAX_QUERIES; + + if(!MicroProfileGpuGetDataD3D11(pGPU->m_pQueries[nIndex], &pGPU->m_nQueryResults[nIndex], sizeof(uint64_t))) + { + pGPU->m_nQueryResults[nIndex] = -1; + } + } + } + else + { + uint32_t nStart = OldFrame.m_nQueryStart; + uint32_t nCount = OldFrame.m_nQueryCount; + + for(uint32_t i = 0; i < nCount; ++i) + { + uint32_t nIndex = (i + nStart) % MICROPROFILE_D3D11_MAX_QUERIES; + pGPU->m_nQueryResults[nIndex] = -1; + } + } + pGPU->m_nQueryGet = (OldFrame.m_nQueryStart + OldFrame.m_nQueryCount) % MICROPROFILE_D3D11_MAX_QUERIES; + } + + pGPU->m_nQueryFrame = nNextFrame; + MicroProfileD3D11Frame& NextFrame = pGPU->m_QueryFrames[nNextFrame]; + pImmediateContext->Begin((ID3D11Query*)NextFrame.m_pRateQuery); + NextFrame.m_nQueryStart = pGPU->m_nQueryPut; + NextFrame.m_nQueryCount = 0; + if(pGPU->m_nQueryPut >= pGPU->m_nQueryGet) + { + NextFrame.m_nQueryCountMax = (MICROPROFILE_D3D11_MAX_QUERIES - pGPU->m_nQueryPut) + pGPU->m_nQueryGet; + } + else + { + NextFrame.m_nQueryCountMax = pGPU->m_nQueryGet - pGPU->m_nQueryPut - 1; + } + if(NextFrame.m_nQueryCountMax) + NextFrame.m_nQueryCountMax -= 1; + NextFrame.m_nRateQueryStarted = 1; + return nFrameTimeStamp; +} + +void MicroProfileGpuInitD3D11(void* pDevice_, void* pImmediateContext) +{ + ID3D11Device* pDevice = (ID3D11Device*)pDevice_; + + MicroProfileGpuTimerStateD3D11* pGPU = MP_ALLOC_OBJECT(MicroProfileGpuTimerStateD3D11); + + MicroProfileGpuInitPlatform(MicroProfileGpuTimerStateType_D3D11, + pGPU, + MicroProfileGpuInsertTimeStampD3D11, + MicroProfileGpuGetTimeStampD3D11, + MicroProfileTicksPerSecondGpuD3D11, + MicroProfileGetGpuTickReferenceD3D11, + MicroProfileGpuFlipD3D11, + MicroProfileGpuShutdownD3D11); + + pGPU->m_pImmediateContext = pImmediateContext; + + D3D11_QUERY_DESC Desc; + Desc.MiscFlags = 0; + Desc.Query = D3D11_QUERY_TIMESTAMP; + for(uint32_t i = 0; i < MICROPROFILE_D3D11_MAX_QUERIES; ++i) + { + HRESULT hr = pDevice->CreateQuery(&Desc, (ID3D11Query**)&pGPU->m_pQueries[i]); + MP_ASSERT(hr == S_OK); + pGPU->m_nQueryResults[i] = -1; + } + HRESULT hr = pDevice->CreateQuery(&Desc, (ID3D11Query**)&pGPU->pSyncQuery); + MP_ASSERT(hr == S_OK); + + pGPU->m_nQueryPut = 0; + pGPU->m_nQueryGet = 0; + pGPU->m_nQueryFrame = 0; + pGPU->m_nQueryFrequency = 0; + Desc.Query = D3D11_QUERY_TIMESTAMP_DISJOINT; + for(uint32_t i = 0; i < MICROPROFILE_GPU_FRAME_DELAY; ++i) + { + pGPU->m_QueryFrames[i].m_nQueryStart = 0; + pGPU->m_QueryFrames[i].m_nQueryCount = 0; + pGPU->m_QueryFrames[i].m_nRateQueryStarted = 0; + hr = pDevice->CreateQuery(&Desc, (ID3D11Query**)&pGPU->m_QueryFrames[i].m_pRateQuery); + MP_ASSERT(hr == S_OK); + } +} + +void MicroProfileGpuShutdownD3D11() +{ + MicroProfileGpuTimerStateD3D11* pGPU = MicroProfileGetGpuTimerStateD3D11(); + if(!pGPU) + return; + + for(uint32_t i = 0; i < MICROPROFILE_D3D11_MAX_QUERIES; ++i) + { + if(pGPU->m_pQueries[i]) + { + ID3D11Query* pQuery = (ID3D11Query*)pGPU->m_pQueries[i]; + pQuery->Release(); + pGPU->m_pQueries[i] = 0; + } + } + for(uint32_t i = 0; i < MICROPROFILE_GPU_FRAME_DELAY; ++i) + { + if(pGPU->m_QueryFrames[i].m_pRateQuery) + { + ID3D11Query* pQuery = (ID3D11Query*)pGPU->m_QueryFrames[i].m_pRateQuery; + pQuery->Release(); + pGPU->m_QueryFrames[i].m_pRateQuery = 0; + } + } + if(pGPU->pSyncQuery) + { + ID3D11Query* pSyncQuery = (ID3D11Query*)pGPU->pSyncQuery; + pSyncQuery->Release(); + pGPU->pSyncQuery = 0; + } +} + +int MicroProfileGetGpuTickReferenceD3D11(int64_t* pOutCPU, int64_t* pOutGpu) +{ + MicroProfileGpuTimerStateD3D11* pGPU = MicroProfileGetGpuTimerStateD3D11(); + if(!pGPU) + return 0; + { + MicroProfileD3D11Frame& Frame = pGPU->m_QueryFrames[pGPU->m_nQueryFrame]; + if(Frame.m_nRateQueryStarted) + { + ID3D11Query* pSyncQuery = (ID3D11Query*)pGPU->pSyncQuery; + ID3D11DeviceContext* pImmediateContext = (ID3D11DeviceContext*)pGPU->m_pImmediateContext; + pImmediateContext->End(pSyncQuery); + + HRESULT hr; + do + { + hr = pImmediateContext->GetData(pSyncQuery, pOutGpu, sizeof(*pOutGpu), 0); + } while(hr == S_FALSE); + *pOutCPU = MP_TICK(); + switch(hr) + { + case DXGI_ERROR_DEVICE_REMOVED: + case DXGI_ERROR_INVALID_CALL: + case E_INVALIDARG: + MP_BREAK(); + return false; + } + MP_ASSERT(hr == S_OK); + return 1; + } + } + return 0; +} +MicroProfileGpuTimerStateD3D11* MicroProfileGetGpuTimerStateD3D11() +{ + if(S.pGPU && S.pGPU->Type == MicroProfileGpuTimerStateType_D3D11) + return (MicroProfileGpuTimerStateD3D11*)S.pGPU; + return nullptr; +} + +#endif + +#if MICROPROFILE_GPU_TIMERS_D3D12 +//:'######:::'########::'##::::'##::::'########:::'#######::'########:::::'##::::'#######:: +//'##... ##:: ##.... ##: ##:::: ##:::: ##.... ##:'##.... ##: ##.... ##::'####:::'##.... ##: +// ##:::..::: ##:::: ##: ##:::: ##:::: ##:::: ##:..::::: ##: ##:::: ##::.. ##:::..::::: ##: +// ##::'####: ########:: ##:::: ##:::: ##:::: ##::'#######:: ##:::: ##:::: ##::::'#######:: +// ##::: ##:: ##.....::: ##:::: ##:::: ##:::: ##::...... ##: ##:::: ##:::: ##:::'##:::::::: +// ##::: ##:: ##:::::::: ##:::: ##:::: ##:::: ##:'##:::: ##: ##:::: ##:::: ##::: ##:::::::: +//. ######::: ##::::::::. #######::::: ########::. #######:: ########:::'######: #########: +//:......::::..::::::::::.......::::::........::::.......:::........::::......::.........:: +#include +uint32_t MicroProfileGpuInsertTimeStampD3D12(void* pContext) +{ + MicroProfileGpuTimerStateD3D12* pGPU = MicroProfileGetGpuTimerStateD3D12(); + if(!pGPU || !pContext) + return 0; + + ID3D12GraphicsCommandList* pCommandList = (ID3D12GraphicsCommandList*)pContext; + + bool IsCopy = D3D12_COMMAND_LIST_TYPE_COPY == pCommandList->GetType(); + uint32_t nNode = pGPU->nCurrentNode; + uint32_t nFrame = pGPU->nFrame; + + ID3D12QueryHeap* pHeap = IsCopy ? pGPU->NodeState[nNode].pCopyQueueHeap : pGPU->NodeState[nNode].pHeap; + + uint32_t nQueryIndex = IsCopy ? ((pGPU->nFrameCountCopyQueueTimeStamps.fetch_add(1) + pGPU->nFrameStartCopyQueueTimeStamps) % MICROPROFILE_D3D12_MAX_QUERIES) + : ((pGPU->nFrameCountTimeStamps.fetch_add(1) + pGPU->nFrameStartTimeStamps) % MICROPROFILE_D3D12_MAX_QUERIES); + + pCommandList->EndQuery(pHeap, D3D12_QUERY_TYPE_TIMESTAMP, nQueryIndex); + MP_ASSERT(nQueryIndex <= 0xffff); + uint32_t res = (IsCopy ? 0x80000000 : 0) | ((nFrame << 16) & 0x7fff0000) | (nQueryIndex); + return res; +} + +void MicroProfileGpuFetchRange(uint32_t nBegin, int32_t nCount, uint64_t nFrame, int64_t nTimestampOffset) +{ + MicroProfileGpuTimerStateD3D12* pGPU = MicroProfileGetGpuTimerStateD3D12(); + if(!pGPU || nCount <= 0) + return; + void* pData = 0; + // uprintf("fetch [%d-%d]\n", nBegin, nBegin + nCount); + D3D12_RANGE Range = { sizeof(uint64_t) * nBegin, sizeof(uint64_t) * (nBegin + nCount) }; + pGPU->pBuffer->Map(0, &Range, &pData); + memcpy(&pGPU->nResults[nBegin], nBegin + (uint64_t*)pData, nCount * sizeof(uint64_t)); + for(int i = 0; i < nCount; ++i) + { + pGPU->nQueryFrames[i + nBegin] = nFrame; + pGPU->nResults[i + nBegin] -= nTimestampOffset; + } + pGPU->pBuffer->Unmap(0, 0); +} +void MicroProfileGpuFetchRangeCopy(uint32_t nBegin, int32_t nCount, uint64_t nFrame, int64_t nTimestampOffset) +{ + MicroProfileGpuTimerStateD3D12* pGPU = MicroProfileGetGpuTimerStateD3D12(); + if(!pGPU || nCount <= 0) + return; + void* pData = 0; + D3D12_RANGE Range = { sizeof(uint64_t) * nBegin, sizeof(uint64_t) * (nBegin + nCount) }; + pGPU->pBufferCopy->Map(0, &Range, &pData); + memcpy(&pGPU->nResultsCopy[nBegin], nBegin + (uint64_t*)pData, nCount * sizeof(uint64_t)); + for(int i = 0; i < nCount; ++i) + { + pGPU->nQueryFramesCopy[i + nBegin] = nFrame; + pGPU->nResultsCopy[i + nBegin] -= nTimestampOffset; + } + pGPU->pBufferCopy->Unmap(0, 0); +} +void MicroProfileGpuWaitFenceD3D12(uint32_t nNode, uint64_t nFence) +{ + MicroProfileGpuTimerStateD3D12* pGPU = MicroProfileGetGpuTimerStateD3D12(); + if(!pGPU) + return; + + auto GetFence = [&]() -> uint64_t + { + uint64_t f0 = pGPU->NodeState[nNode].pFence->GetCompletedValue(); + uint64_t f1 = pGPU->NodeState[nNode].pFenceCopy->GetCompletedValue(); + return MicroProfileMin(f0, f1); + }; + uint64_t nCompletedFrame = GetFence(); + // while(nCompletedFrame < nPending) + // while(0 < nPending - nCompletedFrame) + while(0 < (int64_t)(nFence - nCompletedFrame)) + { + MICROPROFILE_SCOPEI("Microprofile", "gpu-wait", MP_GREEN4); + Sleep(20); // todo: use event. + nCompletedFrame = GetFence(); + if((uint64_t)-1 == nCompletedFrame) // likely device removed. + return; + } +} + +void MicroProfileGpuFetchResultsD3D12(uint64_t nFrame) +{ + MicroProfileGpuTimerStateD3D12* pGPU = MicroProfileGetGpuTimerStateD3D12(); + if(!pGPU) + return; + uint64_t nPending = pGPU->nPendingFrame; + + // while(nPending <= nFrame) + // while(0 <= nFrame - nPending) + while(0 <= (int64_t)(nFrame - nPending)) + { + uint32_t nInternal = nPending % MICROPROFILE_D3D_INTERNAL_DELAY; + uint32_t nNode = pGPU->Frames[nInternal].nNode; + MicroProfileGpuWaitFenceD3D12(nNode, nPending); + int64_t nTimestampOffset = 0; + if(nNode != 0) + { + // Adjust timestamp queries from GPU x to be in GPU 0's frame of reference + HRESULT hr; + int64_t nCPU0, nGPU0; + hr = pGPU->NodeState[0].pCommandQueue->GetClockCalibration((uint64_t*)&nGPU0, (uint64_t*)&nCPU0); + MP_ASSERT(hr == S_OK); + int64_t nCPUx, nGPUx; + hr = pGPU->NodeState[nNode].pCommandQueue->GetClockCalibration((uint64_t*)&nGPUx, (uint64_t*)&nCPUx); + MP_ASSERT(hr == S_OK); + int64_t nFreqCPU = MicroProfileTicksPerSecondCpu(); + int64_t nElapsedCPU = nCPUx - nCPU0; + int64_t nElapsedGPU = pGPU->nFrequency * nElapsedCPU / nFreqCPU; + nTimestampOffset = nGPUx - nGPU0 - nElapsedGPU; + } + + { + uint32_t nTimeStampBegin = pGPU->Frames[nInternal].nTimeStampBegin; + uint32_t nTimeStampCount = pGPU->Frames[nInternal].nTimeStampCount; + MicroProfileGpuFetchRange( + nTimeStampBegin, (nTimeStampBegin + nTimeStampCount) > MICROPROFILE_D3D12_MAX_QUERIES ? MICROPROFILE_D3D12_MAX_QUERIES - nTimeStampBegin : nTimeStampCount, nPending, nTimestampOffset); + MicroProfileGpuFetchRange(0, (nTimeStampBegin + nTimeStampCount) - MICROPROFILE_D3D12_MAX_QUERIES, nPending, nTimestampOffset); + } + { + uint32_t nTimeStampBegin = pGPU->Frames[nInternal].nTimeStampBeginCopyQueue; + uint32_t nTimeStampCount = pGPU->Frames[nInternal].nTimeStampCountCopyQueue; + MicroProfileGpuFetchRangeCopy( + nTimeStampBegin, (nTimeStampBegin + nTimeStampCount) > MICROPROFILE_D3D12_MAX_QUERIES ? MICROPROFILE_D3D12_MAX_QUERIES - nTimeStampBegin : nTimeStampCount, nPending, nTimestampOffset); + MicroProfileGpuFetchRangeCopy(0, (nTimeStampBegin + nTimeStampCount) - MICROPROFILE_D3D12_MAX_QUERIES, nPending, nTimestampOffset); + } + nPending = ++pGPU->nPendingFrame; + MP_ASSERT(pGPU->nFrame > nPending); + } +} + +uint64_t MicroProfileGpuGetTimeStampD3D12(uint32_t nIndex) +{ + MicroProfileGpuTimerStateD3D12* pGPU = MicroProfileGetGpuTimerStateD3D12(); + if(!pGPU) + return 0; + + uint32_t nFrame = nIndex >> 16; + bool IsCopy = (nFrame & 0x8000) != 0; + nFrame &= 0x7fff; + uint32_t nQueryIndex = nIndex & 0xffff; + uint32_t lala = IsCopy ? pGPU->nQueryFramesCopy[nQueryIndex] : pGPU->nQueryFrames[nQueryIndex]; + // uprintf("read TS [%d <- %lld]\n", nQueryIndex, pGPU->nResults[nQueryIndex]); + MP_ASSERT(nIndex == 0 || (0x7fff & lala) == nFrame); + uint64_t r = IsCopy ? pGPU->nResultsCopy[nQueryIndex] : pGPU->nResults[nQueryIndex]; + if(r == 0x7fffffffffffffff) + { + MP_BREAK(); + } + return r; +} + +uint64_t MicroProfileTicksPerSecondGpuD3D12() +{ + MicroProfileGpuTimerStateD3D12* pGPU = MicroProfileGetGpuTimerStateD3D12(); + if(!pGPU) + return 1; + return pGPU->nFrequency; +} + +uint32_t MicroProfileGpuFlipD3D12(void* pContext) +{ + MicroProfileGpuTimerStateD3D12* pGPU = MicroProfileGetGpuTimerStateD3D12(); + if(!pGPU) + return 0; + uint32_t nNode = pGPU->nCurrentNode; + uint32_t nFrameIndex = pGPU->nFrame % MICROPROFILE_D3D_INTERNAL_DELAY; + uint32_t nCount = 0, nStart = 0; + uint32_t nCountCopyQueue = 0, nStartCopyQueue = 0; + + ID3D12CommandAllocator* pCommandAllocator = pGPU->Frames[nFrameIndex].pCommandAllocator; + ID3D12CommandAllocator* pCommandAllocatorCopy = pGPU->Frames[nFrameIndex].pCommandAllocatorCopy; + pCommandAllocator->Reset(); + pCommandAllocatorCopy->Reset(); + ID3D12GraphicsCommandList* pCommandList = pGPU->Frames[nFrameIndex].pCommandList[nNode]; + + pCommandList->Reset(pCommandAllocator, nullptr); + + ID3D12GraphicsCommandList* pCommandListCopy = nullptr; + + uint32_t nFrameTimeStamp = MicroProfileGpuInsertTimeStamp(pCommandList); + + { + nCount = pGPU->nFrameCountTimeStamps.exchange(0); + nStart = pGPU->nFrameStartTimeStamps; + pGPU->nFrameStartTimeStamps = (pGPU->nFrameStartTimeStamps + nCount) % MICROPROFILE_D3D12_MAX_QUERIES; + uint32_t nEnd = MicroProfileMin(nStart + nCount, (uint32_t)MICROPROFILE_D3D12_MAX_QUERIES); + MP_ASSERT(nStart != nEnd); + uint32_t nSize = nEnd - nStart; + pCommandList->ResolveQueryData(pGPU->NodeState[nNode].pHeap, D3D12_QUERY_TYPE_TIMESTAMP, nStart, nEnd - nStart, pGPU->pBuffer, nStart * sizeof(int64_t)); + if(nStart + nCount > MICROPROFILE_D3D12_MAX_QUERIES) + { + pCommandList->ResolveQueryData(pGPU->NodeState[nNode].pHeap, D3D12_QUERY_TYPE_TIMESTAMP, 0, nEnd + nStart - MICROPROFILE_D3D12_MAX_QUERIES, pGPU->pBuffer, 0); + } + pCommandList->Close(); + } + + { + pCommandListCopy = pGPU->Frames[nFrameIndex].pCommandListCopy[nNode]; + pCommandListCopy->Reset(pCommandAllocatorCopy, nullptr); + + nCountCopyQueue = pGPU->nFrameCountCopyQueueTimeStamps.exchange(0); + nStartCopyQueue = pGPU->nFrameStartCopyQueueTimeStamps; + pGPU->nFrameStartCopyQueueTimeStamps = (nStartCopyQueue + nCountCopyQueue) % MICROPROFILE_D3D12_MAX_QUERIES; + uint32_t nEnd = MicroProfileMin(nStartCopyQueue + nCountCopyQueue, (uint32_t)MICROPROFILE_D3D12_MAX_QUERIES); + if(nStartCopyQueue != nEnd) + { + uint32_t nSize = nEnd - nStartCopyQueue; + pCommandListCopy->ResolveQueryData( + pGPU->NodeState[nNode].pCopyQueueHeap, D3D12_QUERY_TYPE_TIMESTAMP, nStartCopyQueue, nEnd - nStartCopyQueue, pGPU->pBufferCopy, nStartCopyQueue * sizeof(int64_t)); + if(nStartCopyQueue + nCountCopyQueue > MICROPROFILE_D3D12_MAX_QUERIES) + { + pCommandListCopy->ResolveQueryData(pGPU->NodeState[nNode].pCopyQueueHeap, D3D12_QUERY_TYPE_TIMESTAMP, 0, nEnd + nStartCopyQueue - MICROPROFILE_D3D12_MAX_QUERIES, pGPU->pBufferCopy, 0); + } + } + pCommandListCopy->Close(); + } + + if(pCommandList) + { + ID3D12CommandList* pList = pCommandList; + pGPU->NodeState[nNode].pCommandQueue->ExecuteCommandLists(1, &pList); + } + if(pCommandListCopy) + { + ID3D12CommandList* pList = pCommandListCopy; + pGPU->NodeState[nNode].pCommandQueueCopy->ExecuteCommandLists(1, &pList); + } + pGPU->NodeState[nNode].pCommandQueue->Signal(pGPU->NodeState[nNode].pFence, pGPU->nFrame); + pGPU->NodeState[nNode].pCommandQueueCopy->Signal(pGPU->NodeState[nNode].pFenceCopy, pGPU->nFrame); + pGPU->Frames[nFrameIndex].nTimeStampBegin = nStart; + pGPU->Frames[nFrameIndex].nTimeStampCount = nCount; + pGPU->Frames[nFrameIndex].nTimeStampBeginCopyQueue = nStartCopyQueue; + pGPU->Frames[nFrameIndex].nTimeStampCountCopyQueue = nCountCopyQueue; + + pGPU->Frames[nFrameIndex].nNode = nNode; + + pGPU->nFrame++; + // fetch from earlier frames + + MicroProfileGpuFetchResultsD3D12(pGPU->nFrame - MICROPROFILE_GPU_FRAME_DELAY); + return nFrameTimeStamp; +} + +void MicroProfileGpuInitD3D12(void* pDevice_, uint32_t nNodeCount, void** pCommandQueues_, void** pCommandQueuesCopy_) +{ + MicroProfileGpuTimerStateD3D12* pGPU = MP_ALLOC_OBJECT(MicroProfileGpuTimerStateD3D12); + memset(pGPU, 0, sizeof(MicroProfileGpuTimerStateD3D12)); + + MicroProfileGpuInitPlatform(MicroProfileGpuTimerStateType_D3D12, + pGPU, + MicroProfileGpuInsertTimeStampD3D12, + MicroProfileGpuGetTimeStampD3D12, + MicroProfileTicksPerSecondGpuD3D12, + MicroProfileGetGpuTickReferenceD3D12, + MicroProfileGpuFlipD3D12, + MicroProfileGpuShutdownD3D12); + + ID3D12Device* pDevice = (ID3D12Device*)pDevice_; + + pGPU->pDevice = pDevice; + pGPU->nNodeCount = nNodeCount; + MP_ASSERT(pGPU->nNodeCount <= MICROPROFILE_D3D_MAX_NODE_COUNT); + + for(uint32_t nNode = 0; nNode < pGPU->nNodeCount; ++nNode) + { + pGPU->NodeState[nNode].pCommandQueue = (ID3D12CommandQueue*)pCommandQueues_[nNode]; + pGPU->NodeState[nNode].pCommandQueueCopy = (ID3D12CommandQueue*)pCommandQueuesCopy_[nNode]; + if(nNode == 0) + { + pGPU->NodeState[nNode].pCommandQueue->GetTimestampFrequency((uint64_t*)&(pGPU->nFrequency)); + MP_ASSERT(pGPU->nFrequency); + } + else + { + // Don't support GPUs with different timer frequencies for now + int64_t nFrequency; + pGPU->NodeState[nNode].pCommandQueue->GetTimestampFrequency((uint64_t*)&nFrequency); + MP_ASSERT(nFrequency == pGPU->nFrequency); + } + + D3D12_QUERY_HEAP_DESC QHDesc; + QHDesc.Count = MICROPROFILE_D3D12_MAX_QUERIES; + QHDesc.Type = D3D12_QUERY_HEAP_TYPE_TIMESTAMP; + QHDesc.NodeMask = MP_NODE_MASK_ONE(nNode); + HRESULT hr = pDevice->CreateQueryHeap(&QHDesc, IID_PPV_ARGS(&pGPU->NodeState[nNode].pHeap)); + MP_ASSERT(hr == S_OK); + QHDesc.Type = D3D12_QUERY_HEAP_TYPE_COPY_QUEUE_TIMESTAMP; + hr = pDevice->CreateQueryHeap(&QHDesc, IID_PPV_ARGS(&pGPU->NodeState[nNode].pCopyQueueHeap)); + MP_ASSERT(hr == S_OK); + + pDevice->CreateFence(pGPU->nPendingFrame, D3D12_FENCE_FLAG_NONE, IID_PPV_ARGS(&pGPU->NodeState[nNode].pFence)); + pDevice->CreateFence(pGPU->nPendingFrame, D3D12_FENCE_FLAG_NONE, IID_PPV_ARGS(&pGPU->NodeState[nNode].pFenceCopy)); + } + + HRESULT hr; + D3D12_HEAP_PROPERTIES HeapProperties; + HeapProperties.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN; + HeapProperties.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN; + HeapProperties.CreationNodeMask = 0; + HeapProperties.VisibleNodeMask = MP_NODE_MASK_ALL(pGPU->nNodeCount); + HeapProperties.Type = D3D12_HEAP_TYPE_READBACK; + + const size_t nResourceSize = MICROPROFILE_D3D12_MAX_QUERIES * 8; + + D3D12_RESOURCE_DESC ResourceDesc; + ResourceDesc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER; + ResourceDesc.Alignment = 0; + ResourceDesc.Width = nResourceSize; + ResourceDesc.Height = 1; + ResourceDesc.DepthOrArraySize = 1; + ResourceDesc.MipLevels = 1; + ResourceDesc.Format = DXGI_FORMAT_UNKNOWN; + ResourceDesc.SampleDesc.Count = 1; + ResourceDesc.SampleDesc.Quality = 0; + ResourceDesc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR; + ResourceDesc.Flags = D3D12_RESOURCE_FLAG_NONE; + + hr = pDevice->CreateCommittedResource(&HeapProperties, D3D12_HEAP_FLAG_NONE, &ResourceDesc, D3D12_RESOURCE_STATE_COPY_DEST, nullptr, IID_PPV_ARGS(&pGPU->pBuffer)); + MP_ASSERT(hr == S_OK); + hr = pDevice->CreateCommittedResource(&HeapProperties, D3D12_HEAP_FLAG_NONE, &ResourceDesc, D3D12_RESOURCE_STATE_COPY_DEST, nullptr, IID_PPV_ARGS(&pGPU->pBufferCopy)); + MP_ASSERT(hr == S_OK); + + pGPU->nFrame = 0; + pGPU->nPendingFrame = 0; + + for(MicroProfileFrameD3D12& Frame : pGPU->Frames) + { + hr = pDevice->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT, IID_PPV_ARGS(&Frame.pCommandAllocator)); + MP_ASSERT(hr == S_OK); + hr = pDevice->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_COPY, IID_PPV_ARGS(&Frame.pCommandAllocatorCopy)); + MP_ASSERT(hr == S_OK); + for(uint32_t nNode = 0; nNode < pGPU->nNodeCount; ++nNode) + { + hr = pDevice->CreateCommandList(MP_NODE_MASK_ONE(nNode), D3D12_COMMAND_LIST_TYPE_DIRECT, Frame.pCommandAllocator, nullptr, IID_PPV_ARGS(&Frame.pCommandList[nNode])); + MP_ASSERT(hr == S_OK); + hr = Frame.pCommandList[nNode]->Close(); + MP_ASSERT(hr == S_OK); + hr = pDevice->CreateCommandList(MP_NODE_MASK_ONE(nNode), D3D12_COMMAND_LIST_TYPE_COPY, Frame.pCommandAllocatorCopy, nullptr, IID_PPV_ARGS(&Frame.pCommandListCopy[nNode])); + MP_ASSERT(hr == S_OK); + hr = Frame.pCommandListCopy[nNode]->Close(); + MP_ASSERT(hr == S_OK); + } + } +} + +void MicroProfileGpuShutdownD3D12() +{ + MicroProfileGpuTimerStateD3D12* pGPU = MicroProfileGetGpuTimerStateD3D12(); + if(!pGPU) + return; + for(uint32_t nNode = 0; nNode < pGPU->nNodeCount; ++nNode) + { + MicroProfileGpuWaitFenceD3D12(nNode, pGPU->nFrame - 1); + } + for(uint32_t nNode = 0; nNode < pGPU->nNodeCount; ++nNode) + { + pGPU->NodeState[nNode].pHeap->Release(); + pGPU->NodeState[nNode].pCopyQueueHeap->Release(); + pGPU->NodeState[nNode].pFence->Release(); + pGPU->NodeState[nNode].pFenceCopy->Release(); + } + pGPU->pBuffer->Release(); + pGPU->pBufferCopy->Release(); + for(MicroProfileFrameD3D12& Frame : pGPU->Frames) + { + Frame.pCommandAllocator->Release(); + Frame.pCommandAllocatorCopy->Release(); + for(uint32_t nNode = 0; nNode < pGPU->nNodeCount; ++nNode) + { + Frame.pCommandList[nNode]->Release(); + Frame.pCommandListCopy[nNode]->Release(); + } + } +} +void MicroProfileSetCurrentNodeD3D12(uint32_t nNode) +{ + MicroProfileGpuTimerStateD3D12* pGPU = MicroProfileGetGpuTimerStateD3D12(); + pGPU->nCurrentNode = nNode; +} + +int MicroProfileGetGpuTickReferenceD3D12(int64_t* pOutCPU, int64_t* pOutGpu) +{ + MicroProfileGpuTimerStateD3D12* pGPU = MicroProfileGetGpuTimerStateD3D12(); + if(!pGPU) + { + *pOutCPU = 1; + *pOutGpu = 1; + return 1; + } + + HRESULT hr = pGPU->NodeState[0].pCommandQueue->GetClockCalibration((uint64_t*)pOutGpu, (uint64_t*)pOutCPU); + MP_ASSERT(hr == S_OK); + return 1; +} + +MicroProfileGpuTimerStateD3D12* MicroProfileGetGpuTimerStateD3D12() +{ + if(S.pGPU && S.pGPU->Type == MicroProfileGpuTimerStateType_D3D12) + return (MicroProfileGpuTimerStateD3D12*)S.pGPU; + return nullptr; +} + +#endif + +#if MICROPROFILE_GPU_TIMERS_VULKAN + +//:'######:::'########::'##::::'##::::'##::::'##:'##::::'##:'##:::::::'##:::'##::::'###::::'##::: ##: +//'##... ##:: ##.... ##: ##:::: ##:::: ##:::: ##: ##:::: ##: ##::::::: ##::'##::::'## ##::: ###:: ##: +// ##:::..::: ##:::: ##: ##:::: ##:::: ##:::: ##: ##:::: ##: ##::::::: ##:'##::::'##:. ##:: ####: ##: +// ##::'####: ########:: ##:::: ##:::: ##:::: ##: ##:::: ##: ##::::::: #####::::'##:::. ##: ## ## ##: +// ##::: ##:: ##.....::: ##:::: ##::::. ##:: ##:: ##:::: ##: ##::::::: ##. ##::: #########: ##. ####: +// ##::: ##:: ##:::::::: ##:::: ##:::::. ## ##::: ##:::: ##: ##::::::: ##:. ##:: ##.... ##: ##:. ###: +//. ######::: ##::::::::. #######:::::::. ###::::. #######:: ########: ##::. ##: ##:::: ##: ##::. ##: +//:......::::..::::::::::.......:::::::::...::::::.......:::........::..::::..::..:::::..::..::::..:: + +#ifndef MICROPROFILE_VULKAN_MAX_QUERIES +#define MICROPROFILE_VULKAN_MAX_QUERIES (32 << 10) +#endif + +#define MICROPROFILE_VULKAN_MAX_NODE_COUNT 4 +#define MICROPROFILE_VULKAN_INTERNAL_DELAY 8 + +#include +struct MicroProfileGpuFrameVulkan +{ + uint32_t nBegin; + uint32_t nCount; + uint32_t nNode; + VkCommandBuffer CommandBuffer[MICROPROFILE_VULKAN_MAX_NODE_COUNT]; + VkFence Fences[MICROPROFILE_VULKAN_MAX_NODE_COUNT]; +}; +struct MicroProfileGpuTimerStateVulkan : public MicroProfileGpuTimerState +{ + VkDevice Devices[MICROPROFILE_VULKAN_MAX_NODE_COUNT]; + VkPhysicalDevice PhysicalDevices[MICROPROFILE_VULKAN_MAX_NODE_COUNT]; + VkQueue Queues[MICROPROFILE_VULKAN_MAX_NODE_COUNT]; + VkQueryPool QueryPool[MICROPROFILE_VULKAN_MAX_NODE_COUNT]; + VkCommandPool CommandPool[MICROPROFILE_VULKAN_MAX_NODE_COUNT]; + + uint32_t nNodeCount; + uint32_t nCurrentNode; + uint64_t nFrame; + uint64_t nPendingFrame; + uint32_t nFrameStart; + std::atomic nFrameCount; + int64_t nFrequency; + + uint16_t nQueryFrames[MICROPROFILE_VULKAN_MAX_QUERIES]; + int64_t nResults[MICROPROFILE_VULKAN_MAX_QUERIES]; + + MicroProfileGpuFrameVulkan Frames[MICROPROFILE_VULKAN_INTERNAL_DELAY]; +}; + +MicroProfileGpuTimerStateVulkan* MicroProfileGetGpuTimerStateVulkan() +{ + if(S.pGPU && S.pGPU->Type == MicroProfileGpuTimerStateType_Vulkan) + return (MicroProfileGpuTimerStateVulkan*)S.pGPU; + return nullptr; +} + +uint32_t MicroProfileGpuInsertTimeStampVulkan(void* pContext) +{ + MicroProfileGpuTimerStateVulkan* pGPU = MicroProfileGetGpuTimerStateVulkan(); + if(!pGPU) + return 0; + VkCommandBuffer CB = (VkCommandBuffer)pContext; + uint32_t nNode = pGPU->nCurrentNode; + uint32_t nFrame = pGPU->nFrame; + uint32_t nQueryIndex = (pGPU->nFrameCount.fetch_add(1) + pGPU->nFrameStart) % MICROPROFILE_VULKAN_MAX_QUERIES; + vkCmdWriteTimestamp(CB, VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT, pGPU->QueryPool[nNode], nQueryIndex); + MP_ASSERT(nQueryIndex <= 0xffff); + // uprintf("insert timestamp %d :: %d ... ctx %p\n", nQueryIndex, nFrame, pContext); + return ((nFrame << 16) & 0xffff0000) | (nQueryIndex); +} + +void MicroProfileGpuFetchRangeVulkan(VkCommandBuffer CommandBuffer, uint32_t nNode, uint32_t nBegin, int32_t nCount, uint64_t nFrame, int64_t nTimestampOffset) +{ + if(nCount <= 0) + return; + MicroProfileGpuTimerStateVulkan* pGPU = MicroProfileGetGpuTimerStateVulkan(); + if(!pGPU) + return; + + vkGetQueryPoolResults(pGPU->Devices[nNode], pGPU->QueryPool[nNode], nBegin, nCount, 8 * nCount, &pGPU->nResults[nBegin], 8, VK_QUERY_RESULT_64_BIT | VK_QUERY_RESULT_PARTIAL_BIT); + vkCmdResetQueryPool(CommandBuffer, pGPU->QueryPool[nNode], nBegin, nCount); + for(int i = 0; i < nCount; ++i) + { + pGPU->nQueryFrames[i + nBegin] = nFrame; + } +} +void MicroProfileGpuWaitFenceVulkan(uint32_t nNode, uint64_t nFrame) +{ + MicroProfileGpuTimerStateVulkan* pGPU = MicroProfileGetGpuTimerStateVulkan(); + if(!pGPU) + return; + + int r; + int c = 0; + do + { + MICROPROFILE_SCOPEI("Microprofile", "gpu-wait", MP_GREEN4); + r = vkWaitForFences(pGPU->Devices[nNode], 1, &pGPU->Frames[nFrame].Fences[nNode], 1, 1000 * 30); +#if 0 + if(c++ > 1000 && (c%100) == 0) + { + uprintf("waiting really long time for fence\n"); + OutputDebugString("waiting really long time for fence\n"); + } +#endif + } while(r != VK_SUCCESS); +} + +void MicroProfileGpuFetchResultsVulkan(VkCommandBuffer Buffer, uint64_t nFrame) +{ + MicroProfileGpuTimerStateVulkan* pGPU = MicroProfileGetGpuTimerStateVulkan(); + if(!pGPU) + return; + + uint64_t nPending = pGPU->nPendingFrame; + // while(nPending <= nFrame) + // while(0 <= nFrame - nPending) + while(0 <= (int64_t)(nFrame - nPending)) + { + uint32_t nInternal = nPending % MICROPROFILE_VULKAN_INTERNAL_DELAY; + uint32_t nNode = pGPU->Frames[nInternal].nNode; + MicroProfileGpuWaitFenceVulkan(nNode, nInternal); + int64_t nTimestampOffset = 0; + + if(nNode != 0) + { + MP_ASSERT(0 && "NOT IMPLEMENTED"); + // note: timestamp adjustment not implemented. + } + + uint32_t nBegin = pGPU->Frames[nInternal].nBegin; + uint32_t nCount = pGPU->Frames[nInternal].nCount; + MicroProfileGpuFetchRangeVulkan(Buffer, nNode, nBegin, (nBegin + nCount) > MICROPROFILE_VULKAN_MAX_QUERIES ? MICROPROFILE_VULKAN_MAX_QUERIES - nBegin : nCount, nPending, nTimestampOffset); + MicroProfileGpuFetchRangeVulkan(Buffer, nNode, 0, (nBegin + nCount) - MICROPROFILE_VULKAN_MAX_QUERIES, nPending, nTimestampOffset); + + nPending = ++pGPU->nPendingFrame; + MP_ASSERT(pGPU->nFrame > nPending); + } +} + +uint64_t MicroProfileGpuGetTimeStampVulkan(uint32_t nIndex) +{ + if(nIndex == (uint32_t)-1) + { + return 0; + } + + MicroProfileGpuTimerStateVulkan* pGPU = MicroProfileGetGpuTimerStateVulkan(); + if(!pGPU) + return 0; + + uint32_t nFrame = nIndex >> 16; + uint32_t nQueryIndex = nIndex & 0xffff; + uint32_t lala = pGPU->nQueryFrames[nQueryIndex]; + MP_ASSERT((0xffff & lala) == nFrame); + // uprintf("read TS [%d <- %lld]\n", nQueryIndex, pGPU->nResults[nQueryIndex]); + return pGPU->nResults[nQueryIndex]; +} + +uint64_t MicroProfileTicksPerSecondGpuVulkan() +{ + MicroProfileGpuTimerStateVulkan* pGPU = MicroProfileGetGpuTimerStateVulkan(); + if(!pGPU) + return 1; + return pGPU->nFrequency; +} + +uint32_t MicroProfileGpuFlipVulkan(void* pContext) +{ + MicroProfileGpuTimerStateVulkan* pGPU = MicroProfileGetGpuTimerStateVulkan(); + if(!pGPU) + return 0; + + uint32_t nNode = pGPU->nCurrentNode; + uint32_t nFrameIndex = pGPU->nFrame % MICROPROFILE_VULKAN_INTERNAL_DELAY; + uint32_t nCount = 0, nStart = 0; + + VkCommandBuffer CommandBuffer = pGPU->Frames[nFrameIndex].CommandBuffer[nNode]; + auto& F = pGPU->Frames[nFrameIndex]; + VkFence Fence = F.Fences[nNode]; + VkDevice Device = pGPU->Devices[nNode]; + VkQueue Queue = pGPU->Queues[nNode]; + + vkWaitForFences(Device, 1, &Fence, 1, (uint64_t)-1); + uint32_t nFrameTimeStamp = MicroProfileGpuInsertTimeStamp(pContext); + vkResetCommandBuffer(F.CommandBuffer[nNode], VK_COMMAND_BUFFER_RESET_RELEASE_RESOURCES_BIT); + + VkCommandBufferBeginInfo CBI; + CBI.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; + CBI.pNext = 0; + CBI.pInheritanceInfo = 0; + CBI.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT; + vkBeginCommandBuffer(F.CommandBuffer[nNode], &CBI); + vkResetFences(Device, 1, &Fence); + + nCount = pGPU->nFrameCount.exchange(0); + nStart = pGPU->nFrameStart; + pGPU->nFrameStart = (pGPU->nFrameStart + nCount) % MICROPROFILE_VULKAN_MAX_QUERIES; + uint32_t nEnd = MicroProfileMin(nStart + nCount, (uint32_t)MICROPROFILE_VULKAN_MAX_QUERIES); + MP_ASSERT(nStart != nEnd); + uint32_t nSize = nEnd - nStart; + + pGPU->Frames[nFrameIndex].nBegin = nStart; + pGPU->Frames[nFrameIndex].nCount = nCount; + pGPU->Frames[nFrameIndex].nNode = nNode; + pGPU->nFrame++; + ////fetch from earlier frames + MicroProfileGpuFetchResultsVulkan(CommandBuffer, pGPU->nFrame - MICROPROFILE_GPU_FRAME_DELAY); + + vkEndCommandBuffer(F.CommandBuffer[nNode]); + VkSubmitInfo SubmitInfo = {}; + SubmitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; + SubmitInfo.pNext = nullptr; + SubmitInfo.waitSemaphoreCount = 0; + SubmitInfo.pWaitSemaphores = nullptr; + SubmitInfo.commandBufferCount = 1; + SubmitInfo.pCommandBuffers = &CommandBuffer; + SubmitInfo.signalSemaphoreCount = 0; + SubmitInfo.pSignalSemaphores = nullptr; + vkQueueSubmit(Queue, 1, &SubmitInfo, Fence); + return nFrameTimeStamp; +} + +void MicroProfileGpuInitVulkan(VkDevice* pDevices, VkPhysicalDevice* pPhysicalDevices, VkQueue* pQueues, uint32_t* QueueFamily, uint32_t nNodeCount) +{ + MicroProfileGpuTimerStateVulkan* pGPU = MP_ALLOC_OBJECT(MicroProfileGpuTimerStateVulkan); + memset(pGPU, 0, sizeof(MicroProfileGpuTimerStateVulkan)); + MicroProfileGpuInitPlatform(MicroProfileGpuTimerStateType_Vulkan, + pGPU, + MicroProfileGpuInsertTimeStampVulkan, + MicroProfileGpuGetTimeStampVulkan, + MicroProfileTicksPerSecondGpuVulkan, + MicroProfileGetGpuTickReferenceVulkan, + MicroProfileGpuFlipVulkan, + MicroProfileGpuShutdownVulkan); + + pGPU->nNodeCount = nNodeCount; + + VkQueryPoolCreateInfo Q; + Q.sType = VK_STRUCTURE_TYPE_QUERY_POOL_CREATE_INFO; + Q.pNext = 0; + Q.flags = 0; + Q.queryType = VK_QUERY_TYPE_TIMESTAMP; + Q.queryCount = MICROPROFILE_VULKAN_MAX_QUERIES + 1; + + VkCommandPoolCreateInfo CreateInfo; + CreateInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO; + CreateInfo.pNext = 0; + CreateInfo.flags = VK_COMMAND_POOL_CREATE_TRANSIENT_BIT | VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT; + + VkResult r; + for(uint32_t i = 0; i < nNodeCount; ++i) + { + pGPU->Devices[i] = pDevices[i]; + pGPU->PhysicalDevices[i] = pPhysicalDevices[i]; + pGPU->Queues[i] = pQueues[i]; + r = vkCreateQueryPool(pGPU->Devices[i], &Q, 0, &pGPU->QueryPool[i]); + MP_ASSERT(r == VK_SUCCESS); + + CreateInfo.queueFamilyIndex = QueueFamily[i]; + r = vkCreateCommandPool(pGPU->Devices[i], &CreateInfo, 0, &pGPU->CommandPool[i]); + MP_ASSERT(r == VK_SUCCESS); + + for(uint32_t j = 0; j < MICROPROFILE_VULKAN_INTERNAL_DELAY; ++j) + { + auto& F = pGPU->Frames[j]; + VkCommandBufferAllocateInfo AllocInfo; + AllocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; + AllocInfo.pNext = 0; + AllocInfo.commandBufferCount = 1; + AllocInfo.commandPool = pGPU->CommandPool[i]; + AllocInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; + r = vkAllocateCommandBuffers(pGPU->Devices[i], &AllocInfo, &F.CommandBuffer[i]); + MP_ASSERT(r == VK_SUCCESS); + + VkFenceCreateInfo FCI; + FCI.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO; + FCI.pNext = 0; + FCI.flags = j == 0 ? 0 : VK_FENCE_CREATE_SIGNALED_BIT; + r = vkCreateFence(pGPU->Devices[i], &FCI, 0, &F.Fences[i]); + MP_ASSERT(r == VK_SUCCESS); + if(j == 0) + { + VkCommandBufferBeginInfo CBI; + CBI.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; + CBI.pNext = 0; + CBI.pInheritanceInfo = 0; + CBI.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT; + vkBeginCommandBuffer(F.CommandBuffer[i], &CBI); + vkCmdResetQueryPool(F.CommandBuffer[i], pGPU->QueryPool[i], 0, MICROPROFILE_VULKAN_MAX_QUERIES + 1); + + vkEndCommandBuffer(F.CommandBuffer[i]); + VkSubmitInfo SubmitInfo = {}; + SubmitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; + SubmitInfo.pNext = nullptr; + SubmitInfo.waitSemaphoreCount = 0; + SubmitInfo.pWaitSemaphores = nullptr; + SubmitInfo.commandBufferCount = 1; + SubmitInfo.pCommandBuffers = &F.CommandBuffer[i]; + SubmitInfo.signalSemaphoreCount = 0; + SubmitInfo.pSignalSemaphores = nullptr; + vkQueueSubmit(pQueues[i], 1, &SubmitInfo, F.Fences[i]); + vkWaitForFences(pGPU->Devices[i], 1, &F.Fences[i], 1, (uint64_t)-1); + vkResetCommandBuffer(F.CommandBuffer[i], VK_COMMAND_BUFFER_RESET_RELEASE_RESOURCES_BIT); + } + } + } + + VkPhysicalDeviceProperties Properties; + vkGetPhysicalDeviceProperties(pPhysicalDevices[0], &Properties); + pGPU->nFrequency = 1000000000ll / Properties.limits.timestampPeriod; +} + +void MicroProfileGpuShutdownVulkan() +{ + // this is clearly leaking .. +} +void MicroProfileSetCurrentNodeVulkan(uint32_t nNode) +{ + + MicroProfileGpuTimerStateVulkan* pGPU = MicroProfileGetGpuTimerStateVulkan(); + if(!pGPU) + return; + pGPU->nCurrentNode = nNode; +} + +int MicroProfileGetGpuTickReferenceVulkan(int64_t* pOutCPU, int64_t* pOutGpu) +{ + MicroProfileGpuTimerStateVulkan* pGPU = MicroProfileGetGpuTimerStateVulkan(); + if(!pGPU) + return 0; + + auto& F = pGPU->Frames[pGPU->nFrame % MICROPROFILE_VULKAN_INTERNAL_DELAY]; + uint32_t nGpu = pGPU->nCurrentNode; + + VkCommandBufferBeginInfo CBI; + CBI.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; + CBI.pNext = 0; + CBI.pInheritanceInfo = 0; + CBI.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT; + VkCommandBuffer CB = F.CommandBuffer[nGpu]; + VkDevice Device = pGPU->Devices[nGpu]; + VkFence Fence = F.Fences[nGpu]; + + vkWaitForFences(Device, 1, &Fence, 1, (uint64_t)-1); + vkResetFences(Device, 1, &Fence); + vkResetCommandBuffer(CB, VK_COMMAND_BUFFER_RESET_RELEASE_RESOURCES_BIT); + vkBeginCommandBuffer(CB, &CBI); + vkCmdResetQueryPool(CB, pGPU->QueryPool[nGpu], MICROPROFILE_VULKAN_MAX_QUERIES, 1); + vkCmdWriteTimestamp(CB, VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT, pGPU->QueryPool[nGpu], MICROPROFILE_VULKAN_MAX_QUERIES); + vkEndCommandBuffer(CB); + VkSubmitInfo SubmitInfo = {}; + SubmitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; + SubmitInfo.pNext = nullptr; + SubmitInfo.waitSemaphoreCount = 0; + SubmitInfo.pWaitSemaphores = nullptr; + SubmitInfo.commandBufferCount = 1; + SubmitInfo.pCommandBuffers = &CB; + SubmitInfo.signalSemaphoreCount = 0; + SubmitInfo.pSignalSemaphores = nullptr; + vkQueueSubmit(pGPU->Queues[nGpu], 1, &SubmitInfo, Fence); + vkWaitForFences(Device, 1, &Fence, 1, (uint64_t)-1); + *pOutGpu = 0; + vkGetQueryPoolResults(Device, pGPU->QueryPool[nGpu], MICROPROFILE_VULKAN_MAX_QUERIES, 1, 8, pOutGpu, 8, VK_QUERY_RESULT_64_BIT); + *pOutCPU = MP_TICK(); + return 1; +} +#endif + +#if MICROPROFILE_GPU_TIMERS_GL +//:'######:::'########::'##::::'##:::::'######:::'##::::::: +//'##... ##:: ##.... ##: ##:::: ##::::'##... ##:: ##::::::: +// ##:::..::: ##:::: ##: ##:::: ##:::: ##:::..::: ##::::::: +// ##::'####: ########:: ##:::: ##:::: ##::'####: ##::::::: +// ##::: ##:: ##.....::: ##:::: ##:::: ##::: ##:: ##::::::: +// ##::: ##:: ##:::::::: ##:::: ##:::: ##::: ##:: ##::::::: +//. ######::: ##::::::::. #######:::::. ######::: ########: +//:......::::..::::::::::.......:::::::......::::........:: + +void MicroProfileGpuInitGL() +{ + MicroProfileGpuTimerStateGL* pGPU = MP_ALLOC_OBJECT(MicroProfileGpuTimerStateGL); + memset(pGPU, 0, sizeof(MicroProfileGpuTimerStateGL)); + MicroProfileGpuInitPlatform(MicroProfileGpuTimerStateType_GL, + pGPU, + MicroProfileGpuInsertTimeStampGL, + MicroProfileGpuGetTimeStampGL, + MicroProfileTicksPerSecondGpuGL, + MicroProfileGetGpuTickReferenceGL, + MicroProfileGpuFlipGL, + MicroProfileGpuShutdownGL); + + pGPU->GLTimerPos = 0; + glGenQueries(MICROPROFILE_GL_MAX_QUERIES, &pGPU->GLTimers[0]); +} + +uint32_t MicroProfileGpuInsertTimeStampGL(void* pContext) +{ + MicroProfileGpuTimerStateGL* pGPU = MicroProfileGetGpuTimerStateGL(); + if(!pGPU) + return 0; + + uint32_t nIndex = (pGPU->GLTimerPos + 1) % MICROPROFILE_GL_MAX_QUERIES; + glQueryCounter(pGPU->GLTimers[nIndex], GL_TIMESTAMP); + pGPU->GLTimerPos = nIndex; + return nIndex; +} +uint64_t MicroProfileGpuGetTimeStampGL(uint32_t nKey) +{ + MicroProfileGpuTimerStateGL* pGPU = MicroProfileGetGpuTimerStateGL(); + if(!pGPU) + return 0; + + uint64_t result; + glGetQueryObjectui64v(pGPU->GLTimers[nKey], GL_QUERY_RESULT, &result); + return result; +} + +uint64_t MicroProfileTicksPerSecondGpuGL() +{ + return 1000000000ll; +} + +int MicroProfileGetGpuTickReferenceGL(int64_t* pOutCpu, int64_t* pOutGpu) +{ + MicroProfileGpuTimerStateGL* pGPU = MicroProfileGetGpuTimerStateGL(); + if(!pGPU) + return 0; + + int64_t nGpuTimeStamp; + glGetInteger64v(GL_TIMESTAMP, &nGpuTimeStamp); + if(nGpuTimeStamp) + { + *pOutCpu = MP_TICK(); + *pOutGpu = nGpuTimeStamp; +#if 0 // debug test if timestamp diverges + static int64_t nTicksPerSecondCpu = MicroProfileTicksPerSecondCpu(); + static int64_t nTicksPerSecondGpu = MicroProfileTicksPerSecondGpu(); + static int64_t nGpuStart = 0; + static int64_t nCpuStart = 0; + if(!nCpuStart) + { + nCpuStart = *pOutCpu; + nGpuStart = *pOutGpu; + } + static int nCountDown = 100; + if(0 == nCountDown--) + { + int64_t nCurCpu = *pOutCpu; + int64_t nCurGpu = *pOutGpu; + double fDistanceCpu = (nCurCpu - nCpuStart) / (double)nTicksPerSecondCpu; + double fDistanceGpu = (nCurGpu - nGpuStart) / (double)nTicksPerSecondGpu; + + char buf[254]; + snprintf(buf, sizeof(buf)-1,"Distance %f %f diff %f\n", fDistanceCpu, fDistanceGpu, fDistanceCpu-fDistanceGpu); + OutputDebugString(buf); + nCountDown = 100; + } +#endif + return 1; + } + return 0; +} +uint32_t MicroProfileGpuFlipGL(void* pContext) +{ + return MicroProfileGpuInsertTimeStampGL(pContext); +} + +void MicroProfileGpuShutdownGL() +{ + MicroProfileGpuTimerStateGL* pGPU = MicroProfileGetGpuTimerStateGL(); + if(!pGPU) + return; + + glDeleteQueries(MICROPROFILE_GL_MAX_QUERIES, &pGPU->GLTimers[0]); +} + +MicroProfileGpuTimerStateGL* MicroProfileGetGpuTimerStateGL() +{ + if(S.pGPU && S.pGPU->Type == MicroProfileGpuTimerStateType_GL) + return (MicroProfileGpuTimerStateGL*)S.pGPU; + return nullptr; +} +#endif + +uint32_t MicroProfileStringHash(const char* pString) // note matching: code in javascript: microprofilelive.html: function StringHash(s) +{ + uint32_t h = 0xfeedba3e; + char c; + while(0 != (c = *pString++)) + { + h = c + ((h << 5) - h); + } + return h; +} + +const char* MicroProfileStrDup(const char* pStr) +{ + size_t len = strlen(pStr) + 1; + char* pOut = (char*)MP_ALLOC(len, 8); + memcpy(pOut, pStr, len); + return pOut; +} + +uint32_t MicroProfileColorFromString(const char* pString) // note matching code/constants in javascript: microprofilelive.html: function StringToColor(s) +{ + // var h = StringHash(s); + // var cidx = h % 360; + // return "hsl(" + cidx + ",50%, 70%)"; //note: matching code constants in microprofile.cpp: MicroProfileColorFromString + + float h = MicroProfileStringHash(pString) % 360; + float s = 0.5f; + float l = 0.7f; + // from https://www.rapidtables.com/convert/color/hsl-to-rgb.html + float c = (1 - fabsf(2 * l - 1)) * s; + float x = c * (1 - fabsf(fmodf(h / 60, 2.f) - 1)); + float m = l - c / 2.f; + float r = 0.f, g = 0.f, b = 0.f; + if(h < 60) + { + r = c; + g = x; + } + else if(h < 120.f) + { + r = x; + g = c; + } + else if(h < 180.f) + { + g = c; + b = x; + } + else if(h < 240.f) + { + g = x; + b = c; + } + else if(h < 300.f) + { + r = x; + b = c; + } + else + { + r = c; + b = x; + } + r += m; + g += m; + b += m; + + r *= 255.f; + g *= 255.f; + b *= 255.f; + + uint32_t R = MicroProfileMin(0xffu, (uint32_t)r); + uint32_t G = MicroProfileMin(0xffu, (uint32_t)g); + uint32_t B = MicroProfileMin(0xffu, (uint32_t)b); + + return (R << 16) | (G << 8) | B; +} + +#if MICROPROFILE_DYNAMIC_INSTRUMENT +// '##::::'##::'#######:::'#######::'##:::'##:::::'######::'##::::'##::::'###::::'########::'########:'########:: +// ##:::: ##:'##.... ##:'##.... ##: ##::'##:::::'##... ##: ##:::: ##:::'## ##::: ##.... ##: ##.....:: ##.... ##: +// ##:::: ##: ##:::: ##: ##:::: ##: ##:'##:::::: ##:::..:: ##:::: ##::'##:. ##:: ##:::: ##: ##::::::: ##:::: ##: +// #########: ##:::: ##: ##:::: ##: #####:::::::. ######:: #########:'##:::. ##: ########:: ######::: ##:::: ##: +// ##.... ##: ##:::: ##: ##:::: ##: ##. ##:::::::..... ##: ##.... ##: #########: ##.. ##::: ##...:::: ##:::: ##: +// ##:::: ##: ##:::: ##: ##:::: ##: ##:. ##:::::'##::: ##: ##:::: ##: ##.... ##: ##::. ##:: ##::::::: ##:::: ##: +// ##:::: ##:. #######::. #######:: ##::. ##::::. ######:: ##:::: ##: ##:::: ##: ##:::. ##: ########: ########:: +// ..:::::..:::.......::::.......:::..::::..::::::......:::..:::::..::..:::::..::..:::::..::........::........::: + +#include +#include + +#if MICROPROFILE_BREAK_ON_PATCH_FAIL +#define BREAK_ON_PATCH_FAIL() MP_BREAK() +#else +#define BREAK_ON_PATCH_FAIL() \ + do \ + { \ + } while(0) +#endif + +void* MicroProfileX64FollowJump(void* pSrc); +bool MicroProfileCopyInstructionBytes(char* pDest, + void* pSrc, + const int nLimit, + const int nMaxSize, + char* pTrunk, + intptr_t nTrunkSize, + uint32_t nUsableJumpRegs, + int* nBytesDest, + int* nBytesSrc, + uint32_t* pRegsWritten, + uint32_t* nRetSafe); +bool MicroProfilePatchFunction(void* f, int Argument, MicroProfileHookFunc enter, MicroProfileHookFunc leave, MicroProfilePatchError* pError); +template +void MicroProfileIterateSymbols(Callback CB, uint32_t* nModules, uint32_t nNumModules); + +bool MicroProfileDemangleName(const char* pName, char* OutName, uint32_t Size); +bool MicroProfilePatchBeginSuspend(); +void MicroProfilePatchEndSuspend(); +bool MicroProfilePatchHasSuspendedThread(intptr_t Begin, intptr_t End); + +#if 1 +#define STRING_MATCH_SIZE 64 +typedef uint64_t uint_string_match; +#else +#define STRING_MATCH_SIZE 32 +typedef uint32_t uint_string_match; +#endif + +struct MicroProfileStringMatchMask +{ + uint_string_match nMask; + uint_string_match M[64]; +}; + +struct MicroProfileSymbolDesc +{ + const char* pName; + const char* pShortName; + intptr_t nAddress; + intptr_t nAddressEnd; + uint_string_match nMask; + int nIgnoreSymbol; + uint32_t nModule; +}; + +struct MicroProfileSymbolBlock +{ + MicroProfileSymbolBlock* pNext; + uint32_t nNumSymbols; + uint32_t nNumChars; + uint_string_match nMask; + MicroProfileStringMatchMask MatchMask; + enum + { + ESIZE = 4 << 10, + }; + union + { + MicroProfileSymbolDesc Symbols[ESIZE / sizeof(MicroProfileSymbolDesc)]; + char Chars[ESIZE]; + }; +}; + +typedef void (*MicroProfileOnSymbolCallback)(const char* pSymbolName, intptr_t nAddress); + +MP_THREAD_LOCAL uintptr_t g_MicroProfile_TLS[17] = { 16 }; + +extern "C" MP_NOINLINE uintptr_t MicroProfile_Patch_TLS_PUSH(uintptr_t t) +{ + uintptr_t* pTLS = &g_MicroProfile_TLS[0]; + + uintptr_t Limit = (uint32_t)pTLS[0]; + uintptr_t Pos = (uint32_t)(pTLS[0] >> 32); + if(Pos == Limit) + { + return 0; + } + else + { + pTLS[0] = (Limit) | ((Pos + 1) << 32); + } + pTLS[Pos + 1] = t; + return 1; +} +extern "C" MP_NOINLINE uintptr_t MicroProfile_Patch_TLS_POP() +{ + uintptr_t* pTLS = &g_MicroProfile_TLS[0]; + uintptr_t Limit = (uint32_t)pTLS[0]; + uintptr_t Pos = (uint32_t)(pTLS[0] >> 32); + if(Pos == 0) + { + MP_BREAK(); // this should never happen + return 0; + } + else + { + pTLS[0] = (Limit) | ((Pos - 1) << 32); + } + uintptr_t t = pTLS[Pos]; + return t; +} + +char* MicroProfileInsertRegisterJump(char* pCode, intptr_t pDest, int reg) +{ + MP_ASSERT(reg >= R_RAX && reg <= R_R15); + int large = reg >= R_R8 ? 1 : 0; + int offset = large ? (reg - R_R8) : (reg - R_RAX); + unsigned char* uc = (unsigned char*)pCode; + *uc++ = large ? 0x49 : 0x48; + *uc++ = 0xb8 + offset; + memcpy(uc, &pDest, 8); + uc += 8; + if(large) + *uc++ = 0x41; + *uc++ = 0xff; + *uc++ = 0xe0 + offset; + return (char*)uc; + // 164: 48 b8 08 07 06 05 04 03 02 01 movabsq $72623859790382856, %rax + // 16e: 48 b9 08 07 06 05 04 03 02 01 movabsq $72623859790382856, %rcx + // 178: 48 ba 08 07 06 05 04 03 02 01 movabsq $72623859790382856, %rdx + // 182: 48 bb 08 07 06 05 04 03 02 01 movabsq $72623859790382856, %rbx + // 18c: 48 bc 08 07 06 05 04 03 02 01 movabsq $72623859790382856, %rsp + // 196: 48 bd 08 07 06 05 04 03 02 01 movabsq $72623859790382856, %rbp + // 1a0: 48 be 08 07 06 05 04 03 02 01 movabsq $72623859790382856, %rsi + // 1aa: 48 bf 08 07 06 05 04 03 02 01 movabsq $72623859790382856, %rdi + // 1b4: 49 b8 08 07 06 05 04 03 02 01 movabsq $72623859790382856, %r8 + // 1be: 49 b9 08 07 06 05 04 03 02 01 movabsq $72623859790382856, %r9 + // 1c8: 49 ba 08 07 06 05 04 03 02 01 movabsq $72623859790382856, %r10 + // 1d2: 49 bb 08 07 06 05 04 03 02 01 movabsq $72623859790382856, %r11 + // 1dc: 49 bc 08 07 06 05 04 03 02 01 movabsq $72623859790382856, %r12 + // 1e6: 49 bd 08 07 06 05 04 03 02 01 movabsq $72623859790382856, %r13 + // 1f0: 49 be 08 07 06 05 04 03 02 01 movabsq $72623859790382856, %r14 + // 1fa: 49 bf 08 07 06 05 04 03 02 01 movabsq $72623859790382856, %r15 + // 204: ff e0 jmpq *%rax + // 206: ff e1 jmpq *%rcx + // 208: ff e2 jmpq *%rdx + // 20a: ff e3 jmpq *%rbx + // 20c: ff e4 jmpq *%rsp + // 20e: ff e5 jmpq *%rbp + // 210: ff e6 jmpq *%rsi + // 212: ff e7 jmpq *%rdi + // 214: 41 ff e0 jmpq *%r8 + // 217: 41 ff e1 jmpq *%r9 + // 21a: 41 ff e2 jmpq *%r10 + // 21d: 41 ff e3 jmpq *%r11 + // 220: 41 ff e4 jmpq *%r12 + // 223: 41 ff e5 jmpq *%r13 + // 226: 41 ff e6 jmpq *%r14 + // 229: 41 ff e7 jmpq *%r15 +} + +char* MicroProfileInsertRelativeJump(char* pCode, intptr_t pDest) +{ + intptr_t src = intptr_t(pCode) + 5; + intptr_t off = pDest - src; + MP_ASSERT(off > intptr_t(0xffffffff80000000) && off <= 0x7fffffff); + int32_t i32off = (int32_t)off; + unsigned char* uc = (unsigned char*)pCode; + unsigned char* c = (unsigned char*)&i32off; + *uc++ = 0xe9; + memcpy(uc, c, 4); + uc += 4; + return (char*)uc; +} + +char* MicroProfileInsertRetJump(char* pCode, intptr_t pDest) +{ + uint32_t lower = (uint32_t)pDest; + uint32_t upper = (uint32_t)(pDest >> 32); + unsigned char* uc = (unsigned char*)pCode; + *uc++ = 0x68; + memcpy(uc, &lower, 4); + uc += 4; + *uc++ = 0xc7; + *uc++ = 0x44; + *uc++ = 0x24; + *uc++ = 0x04; + memcpy(uc, &upper, 4); + uc += 4; + *uc++ = 0xc3; + return (char*)uc; +} + +uint8_t* MicroProfileInsertMov(uint8_t* p, uint8_t* pend, int r, intptr_t value) +{ + int Large = r >= R_R8 ? 1 : 0; + int RegIndex = Large ? (r - R_R8) : (r - R_RAX); + *p++ = Large ? 0x49 : 0x48; + *p++ = 0xb8 + RegIndex; // + (reg - (large?(R_R8-R_RAX):0)); + intptr_t* pAddress = (intptr_t*)p; + pAddress[0] = value; + p = (uint8_t*)(pAddress + 1); + MP_ASSERT(p < pend); + return p; +} + +uint8_t* MicroProfileInsertCall(uint8_t* p, uint8_t* pend, int r) +{ + int Large = r >= R_R8 ? 1 : 0; + int RegIndex = Large ? (r - R_R8) : (r - R_RAX); + if(Large) + { + *p++ = 0x41; + } + *p++ = 0xff; + *p++ = 0xd0 + RegIndex; + MP_ASSERT(p < pend); + return p; +} + +bool MicroProfileStringMatch(const char* pSymbol, uint32_t nStartOffset, const char** pPatterns, uint32_t* nPatternLength, uint32_t nNumPatterns) +{ + MP_ASSERT(nStartOffset <= nNumPatterns); + const char* p = pSymbol; + for(uint32_t i = nStartOffset; i < nNumPatterns; ++i) + { + p = MP_STRCASESTR(p, pPatterns[i]); + if(p) + { + p += nPatternLength[i]; + } + else + { + return false; + } + } + return true; +} + +int MicroProfileStringMatchOffset(const char* pSymbol, const char** pPatterns, uint32_t* nPatternLength, uint32_t nNumPatterns) +{ + int nOffset = 0; + const char* p = pSymbol; + for(uint32_t i = 0; i < nNumPatterns; ++i) + { + p = MP_STRCASESTR(p, pPatterns[i]); + if(p) + { + p += nPatternLength[i]; + nOffset++; + } + else + { + break; + } + } + return nOffset; +} + +void* MicroProfileX64FollowJump(void* pSrc) +{ + for(uint32_t i = 0; i < S.DynamicTokenIndex; ++i) + if(S.FunctionsInstrumented[i] == pSrc) + return pSrc; // if already instrumented, do not follow the jump inserted by itself. + + // uprintf("deref possible trampoline for %p\n", pSrc); + _DecodeType dt = Decode64Bits; + _DInst Instructions[1]; + unsigned int nCount = 0; + + _CodeInfo ci; + ci.code = (uint8_t*)pSrc; + ci.codeLen = 15; + ci.codeOffset = 0; + ci.dt = dt; + ci.features = DF_NONE; + int r = distorm_decompose(&ci, Instructions, 1, &nCount); + if(!r || nCount != 1) + { + return pSrc; // fail, just return + } + + auto& I = Instructions[0]; + if(I.opcode == I_JMP) + { + if(I.ops[0].type == O_PC) + { + if(I.ops[0].size == 0x20) + { + intptr_t p = (intptr_t)pSrc; + p += I.size; + p += I.imm.sdword; + return (void*)p; + } + } + else if(I.ops[0].type == O_SMEM) + { + if(I.ops[0].index == R_RIP) + { + intptr_t p = (intptr_t)pSrc; + p += I.size; + p += I.disp; + void* pHest = *(void**)p; + return pHest; + } + } + uprintf("failed to interpret I_JMP %p %d %d\n", pSrc, I.ops[0].size, I.ops[0].type); + return pSrc; + MP_BREAK(); + } + return pSrc; +} + +bool MicroProfileCopyInstructionBytes(char* pDest, + void* pSrc, + const int nLimit, + const int nMaxSize, + char* pTrunk, + intptr_t nTrunkSize, + const uint32_t nUsableJumpRegs, + int* pBytesDest, + int* pBytesSrc, + uint32_t* pRegsWritten, + uint32_t* pRetSafe) +{ + + _DecodeType dt = Decode64Bits; + _DInst Instructions[128]; + int rip[128] = { 0 }; + uint32_t nRegsWrittenInstr[128] = { 0 }; + int offsets[129] = { 0 }; + unsigned int nCount = 0; + + _CodeInfo ci; + ci.code = (uint8_t*)pSrc; + ci.codeLen = nLimit + 15; + ci.codeOffset = 0; + ci.dt = dt; + ci.features = DF_NONE; + int r = distorm_decompose(&ci, Instructions, 128, &nCount); + if(r != DECRES_SUCCESS) + { + BREAK_ON_PATCH_FAIL(); + return false; + } + int offset = 0; + unsigned int i = 0; + unsigned nInstructions = 0; + int64_t nTrunkUsage = 0; + offsets[0] = 0; + uint32_t nRegsWritten = 0; + + auto Align16 = [](intptr_t p) { return (p + 15) & (~15); }; + + { + + intptr_t iTrunk = (intptr_t)pTrunk; + intptr_t iTrunkEnd = iTrunk + nTrunkSize; + intptr_t iTrunkAligned = (iTrunk + 15) & ~15; + nTrunkSize = iTrunkEnd - iTrunkAligned; + pTrunk = (char*)iTrunkAligned; + } + const uint8_t* pTrunkEnd = (uint8_t*)(pTrunk + nTrunkSize); + + auto RegToBit = [](int r) -> uint32_t + { + if(r >= R_RAX && r <= R_R15) + { + return (1u << (r - R_RAX)); + } + else if(r >= R_EAX && r <= R_R15D) + { + return (1u << (r - R_EAX)); + } + else if(r >= R_AX && r <= R_R15W) + { + return (1u << (r - R_AX)); + } + else if(r >= R_AL && r <= R_R15B) + { + return (1u << (r - R_AL)); + } + return 0; // might hit on registers like RIP + MP_BREAK(); + }; +#ifdef _WIN32 + const uint32_t nUsableRegisters = RegToBit(R_RAX) | RegToBit(R_R10) | RegToBit(R_R11); +#else + const uint32_t nUsableRegisters = RegToBit(R_RAX) | RegToBit(R_R10) | RegToBit(R_R11); +#endif + + int nBytesToMove = 0; + for(i = 0; i < nCount; ++i) + { + nBytesToMove += Instructions[i].size; + if(nBytesToMove >= nLimit) + break; + } + *pBytesSrc = nBytesToMove; + + uint32_t nRspMask = RegToBit(R_RSP); + *pRetSafe = 1; + + for(i = 0; i < nCount; ++i) + { + rip[i] = 0; + auto& I = Instructions[i]; + // bool bHasRipReference = false; + if(I.opcode == I_LEA) + { + } + if(I.opcode == I_CALL) + { + auto& O = I.ops[0]; + if(O.type != O_PC || O.size != 0x20) + { + uprintf("unknown call encountered. cannot move\n"); + BREAK_ON_PATCH_FAIL(); + return false; + } + if((nRegsWritten & nUsableRegisters) == nUsableRegisters) + { + uprintf("call encountered, but register all regs was written to. TODO: push regs?\n"); + BREAK_ON_PATCH_FAIL(); + return false; + } + // return value might be used past return so preserve registers. +#ifdef _WIN32 + nRegsWritten |= RegToBit(R_RAX); +#else + nRegsWritten |= RegToBit(R_RAX) | RegToBit(R_RDX); +#endif + } + + switch(I.ops[0].type) + { + case O_REG: + { + uint32_t reg = I.ops[0].index; + nRegsWritten |= RegToBit(reg); + auto& O2 = I.ops[1]; + switch(O2.type) + { + case O_REG: + case O_MEM: + case O_SMEM: + { + // if register is RSP 'contaminated', it prevents us from using that to do retjmps + uint32_t nMask = RegToBit(O2.index); + if(nRspMask & nMask) + { + nRspMask |= RegToBit(reg); + } + } + default: + break; + } + break; + } + case O_MEM: + case O_SMEM: + { + uint32_t reg = I.ops[0].index; + if(nRspMask & RegToBit(reg)) + { + uprintf("found contaminated reg at +%lld\n", (long long)I.addr); + *pRetSafe = 0; + } + break; + } + } + nRegsWrittenInstr[i] = nRegsWritten; + for(int j = 0; j < 4; ++j) + { + auto& O = I.ops[j]; + + switch(O.type) + { + case O_REG: + case O_SMEM: + case O_MEM: + { + if(O.index == R_RIP) + { + if(j != 1) + { + uprintf("found non base reference of rip. fail\n"); + BREAK_ON_PATCH_FAIL(); + return false; + } + if(I.dispSize != 0x20 && I.dispSize != 0x10) + { + uprintf("found offset size != 32 && != 16 bit. not implemented\n"); + BREAK_ON_PATCH_FAIL(); + return false; + } + rip[i] = 1; + nTrunkUsage += Align16(O.size / 8); + if(nTrunkUsage > nTrunkSize) + { + uprintf("overuse of trunk %lld\n", (long long)nTrunkUsage); + BREAK_ON_PATCH_FAIL(); + return false; + } + } + break; + } + } + } + if(rip[i]) + { + if(I.ops[0].type != O_REG) + { + uprintf("arg 0 should be O_REG, fail\n"); + BREAK_ON_PATCH_FAIL(); + return false; + } + if(I.ops[1].type != O_SMEM) + { + uprintf("arg 1 should be O_SMEM, fail was %d\n", O_SMEM); + BREAK_ON_PATCH_FAIL(); + return false; + } + } + int fc = META_GET_FC(Instructions[i].meta); + switch(fc) + { + case FC_CALL: + { + break; + } + case FC_RET: + case FC_SYS: + case FC_UNC_BRANCH: + case FC_CND_BRANCH: + uprintf("found branch inst %d :: %d\n", fc, offset); + BREAK_ON_PATCH_FAIL(); + return false; + } + offset += Instructions[i].size; + offsets[i + 1] = offset; + if(offset >= nLimit) + { + nInstructions = i + 1; + break; + } + } + if(nTrunkUsage > nTrunkSize) + { + uprintf("function using too much trunk space\n"); + BREAK_ON_PATCH_FAIL(); + return false; + } + if(offset < nLimit) + { + uprintf("function only had %d bytes of %d\n", offset, nLimit); + BREAK_ON_PATCH_FAIL(); + return false; + } + + if(0 == *pRetSafe && 0 == (nUsableJumpRegs & ~nRegsWritten)) + { + // if ret jump is unsafe all of the usable jump regs are taken, fail. + uprintf("cannot patch function without breaking code]\n"); + BREAK_ON_PATCH_FAIL(); + MP_BREAK(); + return false; + } + + // MP_BREAK(); + *pRegsWritten = nRegsWritten; + uint8_t* d = (uint8_t*)pDest; + uint8_t* dend = d + nMaxSize; + const uint8_t* s = (const uint8_t*)pSrc; + + nTrunkUsage = 0; + + for(i = 0; i < nInstructions; ++i) + { + auto& I = Instructions[i]; + unsigned size = Instructions[i].size; + if(I.opcode == I_CALL) + { + // find reg + uint32_t nRegsWritten = nRegsWrittenInstr[i]; + uint32_t nUsable = nUsableRegisters & ~nRegsWritten; + MP_ASSERT(nUsable); + int r = R_RAX; + while(0 == (1 & nUsable)) + { + nUsable >>= 1; + r++; + } + + intptr_t p = offsets[i + 1]; + p += (intptr_t)pSrc; + p += I.imm.sdword; + d = MicroProfileInsertMov(d, dend, r, p); + d = MicroProfileInsertCall(d, dend, r); + s += size; + } + else if(rip[i]) + { + if(I.opcode == I_LEA) + { + if(I.ops[0].type != O_REG) + { + MP_BREAK(); + } + if(I.ops[1].index != R_RIP) + { + MP_BREAK(); + } + int reg = I.ops[0].index - R_RAX; + int large = I.ops[0].index >= R_R8 ? 1 : 0; + *d++ = large ? 0x49 : 0x48; + *d++ = 0xb8 + (reg - (large ? (R_R8 - R_RAX) : 0)); + // calculate the offset + int64_t offset = offsets[i + 1] + I.disp; + intptr_t base = (intptr_t)pSrc; + + intptr_t sum = base + offset; + intptr_t* pAddress = (intptr_t*)d; + pAddress[0] = sum; + s += size; + d += 10; + d = (uint8_t*)(pAddress + 1); + } + else + { + if(15 & (intptr_t)pTrunk) + { + MP_BREAK(); + } + intptr_t t = (intptr_t)pTrunk; + t = (t + 15) & ~15; + pTrunk = (char*)t; + auto& O = I.ops[1]; + uint32_t Op1Size = O.size / 8; + + memcpy(d, s, size); + int32_t DispOriginal = (int32_t)I.disp; + const uint8_t* pOriginal = (s + size) + DispOriginal; + + intptr_t DispNew = ((uint8_t*)pTrunk - (d + size)); + if(!((intptr_t)pTrunk + Op1Size <= (intptr_t)pTrunkEnd)) + { + MP_BREAK(); + } + memcpy(pTrunk, pOriginal, Op1Size); + pTrunk += Align16(Op1Size); + if(I.dispSize == 32) + { + int32_t off = (int32_t)DispNew; + if(DispNew > 0x7fffffff || DispNew < 0) + { + MP_BREAK(); + } + memcpy(d + size - 4, &off, 4); + } + else if(I.dispSize == 16) + { + int16_t off = (int16_t)DispNew; + if(DispNew > 0x7fff || DispNew < 0) + { + MP_BREAK(); + } + memcpy(d + size - 2, &off, 2); + } + + d += size; + s += size; + } + } + else + { + memcpy(d, s, size); + d += size; + s += size; + } + } + + *pBytesDest = (int)(d - (uint8_t*)pDest); + + return true; +} + +extern "C" void MicroProfileInterceptEnter(int a) +{ + MicroProfileToken T = S.DynamicTokens[a]; + MicroProfileThreadLog* pLog = MicroProfileGetThreadLog2(); + MP_ASSERT(pLog->nStackScope < MICROPROFILE_STACK_MAX); // if youre hitting this assert your instrumenting a deeply nested function + MicroProfileScopeStateC* pScopeState = &pLog->ScopeState[pLog->nStackScope++]; + pScopeState->Token = T; + if(T) + { + pScopeState->nTick = MicroProfileEnterInternal(T); + } + else + { + pScopeState->nTick = MICROPROFILE_INVALID_TICK; + } +} +extern "C" void MicroProfileInterceptLeave(int a) +{ + MicroProfileThreadLog* pLog = MicroProfileGetThreadLog2(); + MP_ASSERT(pLog->nStackScope > 0); // if youre hitting this assert you probably have mismatched _ENTER/_LEAVE markers + MicroProfileScopeStateC* pScopeState = &pLog->ScopeState[--pLog->nStackScope]; + MicroProfileLeaveInternal(pScopeState->Token, pScopeState->nTick); +} + +bool MicroProfileInstrumentFromAddressOnly(void* pFunction) +{ + MicroProfileSymbolDesc* pDesc = MicroProfileSymbolFindFuction(pFunction); + if(pDesc) + { + uprintf("Found function %p :: %s %s\n", (void*)pDesc->nAddress, pDesc->pName, pDesc->pShortName); + uint32_t nColor = MicroProfileColorFromString(pDesc->pName); + + return MicroProfileInstrumentFunction(pFunction, MicroProfileSymbolModuleGetString(pDesc->nModule), pDesc->pName, nColor); + } + else + { + uprintf("No Function Found %p\n", pFunction); + return false; + } +} + +template +void MicroProfileInstrumentScanForFunctionCalls(CB Callback, void* pFunction, size_t nFunctionSize) +{ + pFunction = MicroProfileX64FollowJump(pFunction); + const intptr_t nCodeLen = nFunctionSize; + const uint32_t nMaxInstructions = 15; + intptr_t nOffset = 0; + _DecodeType dt = Decode64Bits; + _DInst Instructions[15]; + _CodeInfo ci; + do + { + ci.code = nOffset + (uint8_t*)pFunction; + ci.codeLen = nCodeLen - nOffset; + ci.codeOffset = 0; + ci.dt = dt; + ci.features = DF_RETURN_FC_ONLY; + uint32_t nCount = 0; + uint32_t nOffsetNext = 0; + + int r = distorm_decompose(&ci, Instructions, nMaxInstructions, &nCount); + // uprintf("decomposed %d\n", nCount); + if(r != DECRES_SUCCESS && r != DECRES_MEMORYERR) + { + BREAK_ON_PATCH_FAIL(); + return; + } + if(nCount == 0) + { + // no instructions left + break; + } + // uprintf("instructions decoded %d %p ::\n", nCount, pFunction); + for(int i = 0; i < (int)nCount; ++i) + { + // rip[i] = 0; + auto& I = Instructions[i]; + // bool bHasRipReference = false; + if(I.addr < nOffsetNext) + { + MP_BREAK(); + } + nOffsetNext = I.addr + I.size; + if(I.opcode == I_CALL) + { + auto& O = I.ops[0]; + if(O.type != O_PC || O.size != 0x20) + { + uprintf("non immediate call encountered. cannot follow\n"); + BREAK_ON_PATCH_FAIL(); + continue; + } + intptr_t pDst = nOffset + (intptr_t)pFunction; + pDst += I.addr; + pDst += I.size; + pDst += I.imm.sdword; + + void* fFun1 = MicroProfileX64FollowJump((void*)pDst); + Callback(fFun1); + } + } + nOffset += nOffsetNext; + } while(nOffset < nCodeLen); +} + +void MicroProfileInstrumentFunctionsCalled(void* pFunction, const char* pModuleName, const char* pFunctionName, int nMinBytes, int nMaxCalls) +{ + pFunction = MicroProfileX64FollowJump(pFunction); + + MicroProfileSymbolDesc* pDesc = MicroProfileSymbolFindFuction(pFunction); + if(pDesc) + { + uprintf("instrumenting child functions %p %p :: %s :: %s\n", (void*)pDesc->nAddress, (void*)pDesc->nAddressEnd, pDesc->pName, pDesc->pShortName); + int a = 0; + (void)a; + } + else + { + uprintf("could not find symbol info\n"); + return; + } + + const intptr_t nCodeLen = (intptr_t)pDesc->nAddressEnd - (intptr_t)pDesc->nAddress; + + MicroProfilePatchBeginSuspend(); + int NumFunctionsInstrumented = 0; + auto Callback = [&NumFunctionsInstrumented, nMinBytes, nMaxCalls](void* pFunc) + { + MicroProfileSymbolDesc* pDesc = MicroProfileSymbolFindFuction(pFunc); + if(!pDesc) + return; + const char* pName = pDesc ? pDesc->pName : "??"; + intptr_t Size = pDesc->nAddressEnd - pDesc->nAddress; + if(nMinBytes == 0 || Size >= nMinBytes) + { + if(0 == nMaxCalls || NumFunctionsInstrumented < nMaxCalls) + { + uprintf("** func Instrumented, count %d, size %d %s\n", NumFunctionsInstrumented, Size, pName); + if(MicroProfileInstrumentFromAddressOnly(pFunc)) + { + ++NumFunctionsInstrumented; + } + } + else + { + uprintf("** func Skipped, count %d>=%d :: %s\n", NumFunctionsInstrumented, nMaxCalls, pName); + } + } + else + { + uprintf("** func Skipped, Size %d<%d :: %s\n", Size, nMinBytes, pName); + } + }; + MicroProfileInstrumentScanForFunctionCalls(Callback, pFunction, nCodeLen); + + MicroProfilePatchEndSuspend(); +} + +bool MicroProfileInstrumentFunction(void* pFunction, const char* pModuleName, const char* pFunctionName, uint32_t nColor) +{ + MicroProfilePatchBeginSuspend(); + struct ScopeExit + { + ~ScopeExit() + { + MicroProfilePatchEndSuspend(); + } + } dummy; + + MicroProfilePatchError Err; + if(S.DynamicTokenIndex == MICROPROFILE_MAX_DYNAMIC_TOKENS) + { + uprintf("instrument failing, out of dynamic tokens %d\n", S.DynamicTokenIndex); + return false; + } + for(uint32_t i = 0; i < S.DynamicTokenIndex; ++i) + { + if(S.FunctionsInstrumented[i] == pFunction) + { + uprintf("function %p already instrumented\n", pFunction); + return false; + } + } + if(MicroProfilePatchFunction(pFunction, S.DynamicTokenIndex, MicroProfileInterceptEnter, MicroProfileInterceptLeave, &Err)) + { + MicroProfileToken Tok = S.DynamicTokens[S.DynamicTokenIndex] = MicroProfileGetToken("PATCHED", pFunctionName, nColor, MicroProfileTokenTypeCpu, 0); + S.FunctionsInstrumented[S.DynamicTokenIndex] = pFunction; + S.FunctionsInstrumentedName[S.DynamicTokenIndex] = MicroProfileStringIntern(pFunctionName); + S.FunctionsInstrumentedModuleNames[S.DynamicTokenIndex] = MicroProfileStringIntern(pModuleName); + S.DynamicTokenIndex++; + + uint16_t nGroup = MicroProfileGetGroupIndex(Tok); + if(!MicroProfileGroupActive(nGroup)) + { + MicroProfileGroupSetEnabled(nGroup); + } +#if MICROPROFILE_WEBSERVER + MicroProfileWebSocketToggleTimer(MicroProfileGetTimerIndex(Tok)); +#endif + + return false; + } + else + { + bool bFound = false; + for(int i = 0; i < S.nNumPatchErrors; ++i) + { + if(Err.nCodeSize == S.PatchErrors[i].nCodeSize && 0 == memcmp(Err.Code, S.PatchErrors[i].Code, Err.nCodeSize)) + { + bFound = true; + break; + } + } + if(!bFound && S.nNumPatchErrors < MICROPROFILE_MAX_PATCH_ERRORS) + { + memcpy(&S.PatchErrors[S.nNumPatchErrors++], &Err, sizeof(Err)); + } + bFound = false; + for(int i = 0; i < S.nNumPatchErrorFunctions; ++i) + { + if(0 == strcmp(pFunctionName, S.PatchErrorFunctionNames[i])) + { + bFound = true; + } + } + if(!bFound && S.nNumPatchErrorFunctions < MICROPROFILE_MAX_PATCH_ERRORS) + { + S.PatchErrorFunctionNames[S.nNumPatchErrorFunctions++] = pFunctionName; + } + uprintf("interception fail!!\n"); + return false; + } +} + +void MicroProfileInstrumentPreInit(); +void MicroProfileSymbolInitializeInternal(); +void MicroProfileSymbolFreeDataInternal(); +void MicroProfileSymbolKickThread(); +void MicroProfileQueryJoinThread(); + +bool MicroProfileSymbolInitialize(bool bStartLoad, const char* pModuleName) +{ + if(!bStartLoad) + return S.SymbolState.nModuleLoadsFinished.load() != 0; + // int nRequests = 0; + { + MicroProfileScopeLock L(MicroProfileMutex()); + for(int i = 0; i < S.SymbolNumModules; ++i) + { + if(0 == pModuleName || 0 == strcmp(pModuleName, (const char*)S.SymbolModules[i].pBaseString)) + { + if(0 == S.SymbolModules[i].nModuleLoadRequested.exchange(1)) + { + S.SymbolState.nModuleLoadsRequested.fetch_add(1); + } + } + } + } + + // todo: unload modules + MicroProfileSymbolKickThread(); + return S.SymbolState.nModuleLoadsRequested.load() == S.SymbolState.nModuleLoadsFinished.load(); + + // if(S.SymbolState.nState == MICROPROFILE_SYMBOLSTATE_DEFAULT) + // { + // if(!bStartLoad) + // return false; + // { + // MicroProfileScopeLock L(MicroProfileMutex()); + // S.SymbolState.nState.store(MICROPROFILE_SYMBOLSTATE_LOADING); + // S.SymbolState.nSymbolsLoaded.store(0); + // } + // MicroProfileSymbolKickThread(); + // return false; + // } + // if(nRequests) + // { + // } + // if(S.SymbolState.nState.load() == MICROPROFILE_SYMBOLSTATE_DONE) + // { + // MicroProfileQueryJoinThread(); + // } + // if(S.SymbolState.nState == MICROPROFILE_SYMBOLSTATE_DONE && bStartLoad) + // { + // MicroProfileSymbolFreeDataInternal(); + // { + // MicroProfileScopeLock L(MicroProfileMutex()); + // S.SymbolState.nState.store(MICROPROFILE_SYMBOLSTATE_LOADING); + // S.SymbolState.nSymbolsLoaded.store(0); + // } + // MicroProfileSymbolKickThread(); + // return false; + + // } + // else + // { + // return S.SymbolState.nState == MICROPROFILE_SYMBOLSTATE_DONE; + // } +} + +void MicroProfileSymbolFreeDataInternal() +{ + { + uprintf("todod;....\n"); + MP_BREAK(); + // MP_ASSERT(S.SymbolState.nState == MICROPROFILE_SYMBOLSTATE_DONE); + + S.nNumPatchErrorFunctions = 0; + memset(S.PatchErrorFunctionNames, 0, sizeof(S.PatchErrorFunctionNames)); + + for(int i = 0; i < S.SymbolNumModules; ++i) + { + + while(S.SymbolModules[i].pSymbolBlock) + { + MicroProfileSymbolBlock* pBlock = S.SymbolModules[i].pSymbolBlock; + S.SymbolModules[i].pSymbolBlock = pBlock->pNext; + MP_FREE(pBlock); + MICROPROFILE_COUNTER_SUB("/MicroProfile/Symbols/Allocs", 1); + MICROPROFILE_COUNTER_SUB("/MicroProfile/Symbols/Memory", sizeof(MicroProfileSymbolBlock)); + } + } + memset(&S.SymbolModules[0], 0, sizeof(S.SymbolModules)); + memset(&S.SymbolModuleNameBuffer[0], 0, sizeof(S.SymbolModuleNameBuffer)); + S.SymbolModuleNameOffset = 0; + S.SymbolNumModules = 0; + } +} +#if STRING_MATCH_SIZE == 64 +int MicroProfileCharacterMaskCharIndex(char c) +{ + if(c >= 'A' && c <= 'Z') + c = 'a' + (c - 'A'); + // abcdefghijklmnopqrstuvwxyz + if(c >= 'a' && c <= 'z') + { + int b = c - 'a'; + return b; + } + if(c >= '0' && c <= '9') + { + int b = c - '0'; + return b + 26; + } + switch(c) + { + case ':': + return 37; + case ';': + return 38; + case '\\': + return 39; + case '\'': + return 40; + case '\"': + return 41; + case '/': + return 42; + case '{': + return 43; + case '}': + return 44; + case '(': + return 45; + case ')': + return 46; + case '[': + return 47; + case ']': + return 48; + case '<': + return 49; + case '>': + return 50; + case '.': + return 51; + case ',': + return 52; // special characters + case ' ': + return -1; // special characters + } + return 63; +} + +uint64_t MicroProfileCharacterMaskChar(char c) +{ + uint64_t nMask = 1; + int nIndex = MicroProfileCharacterMaskCharIndex(c); + if(nIndex == -1) + return 0; + return nMask << nIndex; +} + +#else +uint32_t MicroProfileCharacterMaskChar(char c) +{ + if(c >= 'A' && c <= 'Z') + c = 'a' + (c - 'A'); + // abcdefghijklmnopqrstuvwxyz + if(c >= 'a' && c <= 'z') + { + int b = c - 'a'; + b = MicroProfileMin(20, b); // squish the last together + // static int once = 0; + // if(0 == once) + //{ + // for(int i = 20; i < 28; ++i) + // { + // uprintf("char %d is %c\n", i, (char)('a' + i)); + // } + // once = 1; + //} + uint32_t v = 1; + return v << b; + } + if(c >= '0' && c <= '9') + { + int b = c - '0'; + b += 21; + if(b < 21 || b > 30) + MP_BREAK(); + return 1 << b; + } + switch(c) + { + case ':': + case ';': + case '\\': + case '\'': + case '\"': + case '/': + case '{': + case '}': + case '(': + case ')': + case '[': + case ']': + return 1u << 31; // special characters + case ' ': + return 0; + } + return 0; +} +int MicroProfileCharacterMaskCharIndex(char c) +{ + if(c >= 'A' && c <= 'Z') + c = 'a' + (c - 'A'); + // abcdefghijklmnopqrstuvwxyz + if(c >= 'a' && c <= 'z') + { + int b = c - 'a'; + b = MicroProfileMin(20, b); // squish the last together + static int once = 0; + if(0 == once) + { + for(int i = 20; i < 28; ++i) + { + uprintf("char %d is %c\n", i, (char)('a' + i)); + } + once = 1; + } + return b; + } + if(c >= '0' && c <= '9') + { + int b = c - '0'; + b += 21; + if(b < 21 || b > 30) + MP_BREAK(); + return b; + } + switch(c) + { + case ':': + case ';': + case '\\': + case '\'': + case '\"': + case '/': + case '{': + case '}': + case '(': + case ')': + case '[': + case ']': + return 31; // special characters + case ' ': + return -1; + } + return 1; +} +#endif + +uint_string_match MicroProfileCharacterMaskString(const char* pStr) +{ + uint_string_match nMask = 0; + char c = 0; + while(0 != (c = *pStr++)) + { + nMask |= MicroProfileCharacterMaskChar(c); + } + return nMask; +} + +void MicroProfileCharacterMaskString2(const char* pStr, MicroProfileStringMatchMask& M) +{ + uint_string_match nMask = 0; + char c = 0; + int nLast = -1; + while(0 != (c = *pStr++)) + { + nMask |= MicroProfileCharacterMaskChar(c); + int nIndex = MicroProfileCharacterMaskCharIndex(c); + if(nIndex >= 0 && nLast >= 0) + { + MP_ASSERT(nIndex < STRING_MATCH_SIZE); + M.M[nLast] |= 1llu << nIndex; + } + nLast = nIndex; + } + M.nMask |= nMask; +} + +bool MicroProfileCharacterMatch(const MicroProfileStringMatchMask& Block, const MicroProfileStringMatchMask& String) +{ + if(String.nMask != (Block.nMask & String.nMask)) + return false; + for(uint32_t i = 0; i < STRING_MATCH_SIZE; ++i) + { + if(String.M[i] != (Block.M[i] & String.M[i])) + return false; + } + return true; +} + +uint32_t MicroProfileSymbolGetModule(const char* pString, intptr_t nBaseAddr) +{ + + for(int i = 0; i < S.SymbolNumModules; ++i) + { + auto& M = S.SymbolModules[i]; + for(int j = 0; j < M.nNumExecutableRegions; ++j) + { + if(M.Regions[j].nBegin <= nBaseAddr && nBaseAddr < M.Regions[j].nEnd) + return i; + } + } + MP_BREAK(); // should never happen. + return 0; +} + +void MicroProfileSymbolMergeExecutableRegions() +{ + for(int i = 0; i < S.SymbolNumModules; ++i) + { + auto& M = S.SymbolModules[i]; + if(M.nNumExecutableRegions > 1) + { + std::sort(&M.Regions[0], &M.Regions[M.nNumExecutableRegions], [](const MicroProfileSymbolModuleRegion& l, const MicroProfileSymbolModuleRegion& r) { return l.nBegin < r.nBegin; }); + + int p = 0; + int g = 1; + while(g < M.nNumExecutableRegions) + { + if(M.Regions[p].nEnd == M.Regions[g].nBegin) + { + M.Regions[p].nEnd = M.Regions[g].nEnd; + g++; + } + else + { + ++p; + if(p != g) + M.Regions[p] = M.Regions[g]; + g++; + } + } + M.nNumExecutableRegions = p + 1; + } + } + for(int i = 0; i < S.SymbolNumModules; ++i) + { + auto& M = S.SymbolModules[i]; + uprintf("region %s %s\n", M.pTrimmedString, M.pBaseString); + for(int j = 0; j < M.nNumExecutableRegions; ++j) + uprintf("\t[%p-%p]\n", (void*)M.Regions[j].nBegin, (void*)M.Regions[j].nEnd); + } +} + +uint32_t MicroProfileSymbolInitModule(const char* pString_, intptr_t nAddrBegin, intptr_t nAddrEnd) +{ + const char* pString = MicroProfileStringInternSlash(pString_); + for(int i = 0; i < S.SymbolNumModules; ++i) + { + auto& M = S.SymbolModules[i]; + for(int j = 0; j < M.nNumExecutableRegions; ++j) + { + if(M.Regions[j].nBegin <= nAddrBegin && nAddrEnd < M.Regions[j].nEnd) + { + MP_ASSERT(pString == M.pBaseString); + return i; + } + } + } + + for(int i = 0; i < S.SymbolNumModules; ++i) + { + auto& M = S.SymbolModules[i]; + if(M.pBaseString == pString) + { + MP_ASSERT((intptr_t)pString != -2); + for(int j = 0; j < M.nNumExecutableRegions; ++j) + if(nAddrBegin == M.Regions[j].nBegin) + return i; + + if(M.nNumExecutableRegions == MICROPROFILE_MAX_MODULE_EXEC_REGIONS) + { + return (uint32_t)-1; + } + M.Regions[M.nNumExecutableRegions].nBegin = nAddrBegin; + M.Regions[M.nNumExecutableRegions].nEnd = nAddrEnd; + // uprintf("added module region %d %p %p %s \n", M.nNumExecutableRegions, (void*)nAddrBegin, (void*)nAddrEnd, pString); + M.nNumExecutableRegions++; + return i; + } + } + + MP_ASSERT((intptr_t)pString != -2); + // trim untill last path char + const char* pTrimmedString = pString; + + const char* pWork = pTrimmedString; + bool bLastSeperator = false; + while(*pWork != '\0') + { + if(bLastSeperator) + pTrimmedString = pWork; + bLastSeperator = *pWork == '\\' || *pWork == '/'; + + pWork++; + } + int nLen = (int)strlen(pTrimmedString) + 1; + // uprintf("STRING '%s' :: trimmedstring %s . len %d\n", pString, pTrimmedString, nLen); + + const char* pTrimmedIntern = MicroProfileStringIntern(pTrimmedString); + if(S.SymbolModuleNameOffset + nLen > MICROPROFILE_INSTRUMENT_MAX_MODULE_CHARS) + return 0; + memcpy(S.SymbolModuleNameOffset + &S.SymbolModuleNameBuffer[0], pTrimmedString, nLen); + + MP_ASSERT(S.SymbolNumModules < MICROPROFILE_INSTRUMENT_MAX_MODULES); + S.SymbolModules[S.SymbolNumModules].nModuleBase = nAddrBegin; + S.SymbolModules[S.SymbolNumModules].nMatchOffset = 0; + S.SymbolModules[S.SymbolNumModules].nStringOffset = S.SymbolModuleNameOffset; + S.SymbolModules[S.SymbolNumModules].pBaseString = (const char*)pString; + S.SymbolModules[S.SymbolNumModules].pTrimmedString = pTrimmedIntern; + S.SymbolModules[S.SymbolNumModules].Regions[0].nBegin = nAddrBegin; + S.SymbolModules[S.SymbolNumModules].Regions[0].nEnd = nAddrEnd; + S.SymbolModules[S.SymbolNumModules].nNumExecutableRegions = 1; + S.SymbolModules[S.SymbolNumModules].bDownloading = false; + S.SymbolModules[S.SymbolNumModules].nProgress = 0; + S.SymbolModules[S.SymbolNumModules].nProgressTarget = 0; + + S.SymbolModuleNameOffset += nLen; + return S.SymbolNumModules++; +} + +const char* MicroProfileSymbolModuleGetString(uint32_t nIndex) +{ + MP_ASSERT(S.SymbolNumModules > (int)nIndex); + return S.SymbolModules[nIndex].nStringOffset + &S.SymbolModuleNameBuffer[0]; +} + +bool MicroProfileSymbolIgnoreSymbol(const char* pName) +{ + if(strstr(pName, "MicroProfile")) + { +#if MICROPROFILE_INSTRUMENT_MICROPROFILE == 0 + return true; +#else + if(strstr(pName, "Log") || strstr(pName, "Scope") || strstr(pName, "Tick") || strstr(pName, "Enter") || strstr(pName, "Leave") || strstr(pName, "Thread") || strstr(pName, "Thread") || + strstr(pName, "Mutex")) // just for debugging: skip these so we can play around with the sample projects + { + return true; + } +#endif + } +#ifdef _WIN32 + if(pName[0] == '_' && pName[1] == '_') + return true; + if(strstr(pName, "__security_check_cookie") || strstr(pName, "_RTC_CheckStackVars") || strstr(pName, "__chkstk") || strstr(pName, "std::_Atomic") || strstr(pName, "_Init_thread_header") || + strstr(pName, "_Init_thread_footer")) + { + return true; + } +#endif + return false; +} + +void MicroProfileSymbolInitializeInternal() +{ + uprintf("Starting load...\n"); + MICROPROFILE_SCOPEI("MicroProfile", "MicroProfileSymbolInitialize", MP_CYAN); + + auto AllocBlock = []() -> MicroProfileSymbolBlock* + { + MicroProfileSymbolBlock* pBlock = MP_ALLOC_OBJECT(MicroProfileSymbolBlock); + MICROPROFILE_COUNTER_ADD("/MicroProfile/Symbols/Allocs", 1); + MICROPROFILE_COUNTER_ADD("/MicroProfile/Symbols/Memory", sizeof(MicroProfileSymbolBlock)); + MICROPROFILE_COUNTER_CONFIG_ONCE("/MicroProfile/Symbols/Memory", MICROPROFILE_COUNTER_FORMAT_BYTES, 0, 0); + memset(pBlock, 0, sizeof(MicroProfileSymbolBlock)); + return pBlock; + }; + + auto SymbolCallback = [&](const char* pName, const char* pShortName, intptr_t nAddress, intptr_t nAddressEnd, uint32_t nModuleId) + { + MICROPROFILE_SCOPEI("microprofile", "SymbolCallback", MP_AUTO); + uint32_t nModule = nModuleId; + if(MicroProfileHashTableGetPtr(&S.SymbolModules[nModule].AddressToSymbol, (void*)nAddress)) + { + return; + } + char Demangled[1024]; + if(MicroProfileDemangleName(pName, Demangled, sizeof(Demangled))) + { + pName = &Demangled[0]; + pShortName = &Demangled[0]; + } + intptr_t delta = nAddressEnd - nAddress; + S.SymbolModules[nModule].nProgress = MicroProfileMax(delta, S.SymbolModules[nModule].nProgress); + S.nSymbolsDirty++; + + int nIgnoreSymbol = MicroProfileSymbolIgnoreSymbol(pName) ? 1 : 0; + + MicroProfileSymbolBlock* pActiveBlock = S.SymbolModules[nModule].pSymbolBlock; + if(!pActiveBlock) + { + pActiveBlock = AllocBlock(); + pActiveBlock->pNext = S.SymbolModules[nModule].pSymbolBlock; + S.SymbolModules[nModule].pSymbolBlock = pActiveBlock; + } + + if(pName == pShortName) + { + pShortName = 0; + } + uint32_t nLen = (uint32_t)strlen(pName) + 1; + + if(nLen > MICROPROFILE_INSTRUMENT_SYMBOLNAME_MAXLEN) + nLen = MICROPROFILE_INSTRUMENT_SYMBOLNAME_MAXLEN; + uint32_t nLenShort = (uint32_t)(pShortName ? 1 + strlen(pShortName) : 0); + if(nLenShort && nLenShort > MICROPROFILE_INSTRUMENT_SYMBOLNAME_MAXLEN) + nLenShort = MICROPROFILE_INSTRUMENT_SYMBOLNAME_MAXLEN; + uint32_t S0 = sizeof(MicroProfileSymbolDesc) * pActiveBlock->nNumSymbols; + uint32_t S1 = pActiveBlock->nNumChars; + uint32_t S3 = nLenShort + nLen + sizeof(MicroProfileSymbolDesc) + 64; + if(S0 + S1 + S3 >= MicroProfileSymbolBlock::ESIZE) + { + MicroProfileSymbolBlock* pNewBlock = AllocBlock(); + MP_ASSERT(pActiveBlock == S.SymbolModules[nModule].pSymbolBlock); + pNewBlock->pNext = pActiveBlock; + S.SymbolModules[nModule].pSymbolBlock = pNewBlock; + pActiveBlock = pNewBlock; + } + S0 = sizeof(MicroProfileSymbolDesc) * pActiveBlock->nNumSymbols; + S1 = pActiveBlock->nNumChars; + S3 = nLenShort + nLen + sizeof(MicroProfileSymbolDesc); + MP_ASSERT(S0 + S1 + S3 < MicroProfileSymbolBlock::ESIZE); + pActiveBlock->nNumChars += nLen; + char* pStr = &pActiveBlock->Chars[MicroProfileSymbolBlock::ESIZE - pActiveBlock->nNumChars - 1]; + memcpy(pStr, pName, nLen); + pStr[nLen - 1] = '\0'; + MicroProfileSymbolDesc& E = pActiveBlock->Symbols[pActiveBlock->nNumSymbols++]; + MicroProfileHashTableSetPtr(&S.SymbolModules[nModule].AddressToSymbol, (void*)nAddress, &E); + + E.pName = pStr; + E.nAddress = nAddress; + E.nAddressEnd = nAddressEnd; + E.nIgnoreSymbol = nIgnoreSymbol; + E.nModule = nModule; + if(pShortName && strlen(pShortName)) + { + pActiveBlock->nNumChars += nLenShort; + char* pStrShort = &pActiveBlock->Chars[MicroProfileSymbolBlock::ESIZE - pActiveBlock->nNumChars - 1]; + memcpy(pStrShort, pShortName, nLenShort); + pStrShort[nLenShort - 1] = '\0'; + E.pShortName = pStrShort; + } + else + { + E.pShortName = E.pName; + } +#define SYMDBG 0 +#if SYMDBG + uprintf("Got symbol %lld %lld %f .. %llx %llx %llx %s\n", + S.SymbolModules[nModule].nProgress, + S.SymbolModules[nModule].nProgressTarget, + S.SymbolModules[nModule].nProgressTarget ? float(S.SymbolModules[nModule].nProgress) / float(S.SymbolModules[nModule].nProgressTarget) : 0.f, + (int64_t)E.nAddress, + (int64_t)S.SymbolModules[nModule].nAddrBegin, + (int64_t)S.SymbolModules[nModule].nAddrEnd, + E.pName); + if(E.nAddress < (int64_t)S.SymbolModules[nModule].nAddrBegin || E.nAddress > (int64_t)S.SymbolModules[nModule].nAddrEnd) + { + MP_BREAK(); + } +#endif + E.nMask = MicroProfileCharacterMaskString(E.pShortName); + MicroProfileCharacterMaskString2(E.pShortName, pActiveBlock->MatchMask); + + pActiveBlock->nMask |= E.nMask; + MICROPROFILE_COUNTER_ADD("/MicroProfile/Symbols/Count", 1); + if(nIgnoreSymbol) + { + MICROPROFILE_COUNTER_ADD("/MicroProfile/Symbols/Ignored", 1); + } +#if SYMDBG + MicroProfileSleep(10); +#endif +#undef SYMDBG + + S.SymbolModules[nModule].nSymbolsLoaded.fetch_add(1); + S.nSymbolsDirty.exchange(1); + S.SymbolState.nSymbolsLoaded.fetch_add(1); + MP_ASSERT((intptr_t)E.pShortName >= (intptr_t)&E); // assert pointer arithmetic is correct. + }; + do + { + uint32_t nModuleLoad[MICROPROFILE_INSTRUMENT_MAX_MODULES]; + uint32_t nNumModulesRequested = 0; + for(int i = 0; i < S.SymbolNumModules; ++i) + { + if(S.SymbolModules[i].nModuleLoadRequested.load() != 0 && S.SymbolModules[i].nModuleLoadFinished.load() == 0) + { + nModuleLoad[nNumModulesRequested] = i; + S.SymbolModules[i].nProgress = 0; + MicroProfileHashTableInit(&S.SymbolModules[i].AddressToSymbol, 256, 64, MicroProfileHashTableComparePtr, MicroProfileHashTableHashPtr); + nNumModulesRequested++; + } + } + if(0 == nNumModulesRequested) + { + break; + } + MicroProfileIterateSymbols(SymbolCallback, nModuleLoad, nNumModulesRequested); + S.SymbolState.nModuleLoadsFinished.fetch_add(nNumModulesRequested); + for(uint32_t i = 0; i < nNumModulesRequested; ++i) + { + if(S.SymbolModules[nModuleLoad[i]].nModuleLoadRequested.load() == S.SymbolModules[nModuleLoad[i]].nModuleLoadFinished.load()) + { + S.SymbolModules[nModuleLoad[i]].nProgress = S.SymbolModules[nModuleLoad[i]].nProgressTarget; + S.nSymbolsDirty.exchange(1); + } + } + } while(1); +} + +MicroProfileSymbolDesc* MicroProfileSymbolFindFuction(void* pAddress) +{ + for(int i = 0; i < S.SymbolNumModules; ++i) + { + MicroProfileSymbolDesc* pDesc = nullptr; + if(MicroProfileHashTableGetPtr(&S.SymbolModules[i].AddressToSymbol, pAddress, &pDesc)) + { + if(0 == pDesc->nIgnoreSymbol) + return pDesc; + else + return nullptr; + } + } + return nullptr; +} + +#define MICROPROFILE_MAX_FILTER 32 +#define MICROPROFILE_MAX_QUERY_RESULTS 32 +#define MICROPROFILE_MAX_FILTER_STRING 1024 + +struct MicroProfileFunctionQuery +{ + MicroProfileFunctionQuery* pNext; + uint32_t nState; + const char* pFilterStrings[MICROPROFILE_MAX_FILTER]; + uint32_t nPatternLength[MICROPROFILE_MAX_FILTER]; + int nMaxFilter; + + uint32_t nModuleFilterMatch[MICROPROFILE_INSTRUMENT_MAX_MODULES]; // prematch the modules, so it can be skipped during search + uint32_t nMask[MICROPROFILE_MAX_FILTER]; // masks for subpatterns skipped + MicroProfileStringMatchMask MatchMask[MICROPROFILE_MAX_FILTER]; // masks for subpatterns skipped + + // results + MicroProfileSymbolDesc* Results[MICROPROFILE_MAX_QUERY_RESULTS]; + uint32_t nNumResults; + char FilterString[MICROPROFILE_MAX_FILTER_STRING]; + + uint32_t QueryId; +}; + +MicroProfileFunctionQuery* MicroProfileAllocFunctionQuery() +{ + MicroProfileScopeLock L(MicroProfileMutex()); + MicroProfileFunctionQuery* pQ = nullptr; + S.nNumQueryAllocated++; + if(S.pQueryFreeList != 0) + { + pQ = S.pQueryFreeList; + S.pQueryFreeList = pQ->pNext; + S.nNumQueryFree--; + } + else + { + pQ = MP_ALLOC_OBJECT(MicroProfileFunctionQuery); + MICROPROFILE_COUNTER_ADD("MicroProfile/Symbols/FunctionQuery", 1); + MICROPROFILE_COUNTER_ADD("MicroProfile/Symbols/FunctionQueryMem", sizeof(MicroProfileFunctionQuery)); + S.nNumQueryAllocated++; + } + memset(pQ, 0, sizeof(MicroProfileFunctionQuery)); + return pQ; +} +void MicroProfileFreeFunctionQuery(MicroProfileFunctionQuery* pQ) +{ + pQ->pNext = S.pQueryFreeList; + S.pQueryFreeList = pQ; +} + +void MicroProfileProcessQuery(MicroProfileFunctionQuery* pQuery) +{ + MicroProfileFunctionQuery& Q = *pQuery; + + int nBlocksTested = 0, nSymbolsTested = 0, nStringsTested = 0, nStringsTested0 = 0; + int nBlocks = 0; + // (void)nBlocksTested; + // (void)nSymbolsTested; + // (void)nStringsTested; + // (void)nStringsTested0; + // (void)nBlocks; + + int64_t t = MP_TICK(); + int64_t tt = 0; + + for(int i = 0; i < S.SymbolNumModules; ++i) + { + int nModule = i; + uint32_t nModuleMatchOffset = Q.nModuleFilterMatch[nModule]; + MicroProfileSymbolBlock* pSymbols = S.SymbolModules[nModule].pSymbolBlock; + + uint32_t nMaskQ = Q.nMask[nModuleMatchOffset]; + MicroProfileStringMatchMask& MatchMaskQ = Q.MatchMask[nModuleMatchOffset]; + { + while(pSymbols && 0 == S.pPendingQuery && Q.nNumResults < MICROPROFILE_MAX_QUERY_RESULTS) + { + + MICROPROFILE_SCOPEI("MicroProfile", "SymbolQueryLoop", MP_YELLOW); + nBlocks++; + if(MicroProfileCharacterMatch(pSymbols->MatchMask, MatchMaskQ)) + { + nBlocksTested++; + for(uint32_t i = 0; i < pSymbols->nNumSymbols && 0 == S.pPendingQuery && Q.nNumResults < MICROPROFILE_MAX_QUERY_RESULTS; ++i) + { + MicroProfileSymbolDesc& E = pSymbols->Symbols[i]; + if(0 == E.nIgnoreSymbol) + { + nSymbolsTested++; + if(nMaskQ == (nMaskQ & E.nMask)) + { + nStringsTested++; + MP_ASSERT((int)E.nModule < S.SymbolNumModules); + if(MicroProfileStringMatch(E.pShortName, nModuleMatchOffset, &Q.pFilterStrings[0], Q.nPatternLength, Q.nMaxFilter)) + { + if(Q.nNumResults < MICROPROFILE_MAX_QUERY_RESULTS) + { + + Q.Results[Q.nNumResults++] = &E; + if(Q.nNumResults == MICROPROFILE_MAX_QUERY_RESULTS) + tt = MP_TICK(); + } + } + if(Q.nNumResults < MICROPROFILE_MAX_QUERY_RESULTS) + nStringsTested0++; + } + } + } + } + pSymbols = pSymbols->pNext; + } + } + } + int64_t tend = MP_TICK(); + float ToMS = MicroProfileTickToMsMultiplierCpu(); + float TIME = (tend - t) * ToMS; + float TIME0 = (tt - t) * ToMS; + uprintf(" %6.3fms [%6.3f]: %5d/%5d blocks tested. %5d symbols %5d/%5d string compares\n", TIME, TIME0, nBlocksTested, nBlocks, nSymbolsTested, nStringsTested, nStringsTested0); +} + +void* MicroProfileQueryThread(void* p) +{ + MicroProfileOnThreadCreate("MicroProfileSymbolThread"); + { + while(1) + { + MicroProfileSleep(100); // todo:: use an event instead + MicroProfileScopeLock L(MicroProfileMutex()); + if(S.pPendingQuery != nullptr) + { + MICROPROFILE_SCOPEI("MicroProfile", "SymbolQuery", MP_WHEAT); + MicroProfileFunctionQuery* pQuery = S.pPendingQuery; + + MP_ASSERT(pQuery->QueryId > S.nQueryProcessed); + S.pPendingQuery = 0; + L.Unlock(); + + // uprintf("processing query %d\n", pQuery->QueryId); + MicroProfileProcessQuery(pQuery); + + L.Lock(); + S.nQueryProcessed = MicroProfileMax(pQuery->QueryId, S.nQueryProcessed); + + pQuery->pNext = S.pFinishedQuery; + S.pFinishedQuery = pQuery; + } + if(S.SymbolState.nModuleLoadsRequested.load() != S.SymbolState.nModuleLoadsFinished.load()) + { + L.Unlock(); + MicroProfileSymbolInitializeInternal(); + L.Lock(); + } + } + + S.SymbolThreadFinished = 1; + } + MicroProfileOnThreadExit(); + return 0; +} + +void MicroProfileQueryJoinThread() +{ + if(S.SymbolThreadFinished) + { + MicroProfileThreadJoin(&S.SymbolThread); + S.SymbolThreadFinished = 0; + S.SymbolThreadRunning = 0; + } +} +void MicroProfileSymbolKickThread() +{ + // MicroProfileQueryJoinThread(); + if(S.SymbolThreadRunning == 0) + { + S.SymbolThreadRunning = 1; + MicroProfileThreadStart(&S.SymbolThread, MicroProfileQueryThread); + } +} +#if MICROPROFILE_WEBSERVER +void MicroProfileSymbolSendFunctionNames(MpSocket Connection) +{ + if(S.WSFunctionsInstrumentedSent < S.DynamicTokenIndex) + { + MicroProfileWSPrintStart(Connection); + MicroProfileWSPrintf("{\"k\":\"%d\",\"v\":[", MSG_FUNCTION_NAMES); + bool bFirst = true; + for(uint32_t i = S.WSFunctionsInstrumentedSent; i < S.DynamicTokenIndex; ++i) + { + const char* pString = S.FunctionsInstrumentedName[i]; + const char* pModuleString = S.FunctionsInstrumentedModuleNames[i]; + MicroProfileWSPrintf(bFirst ? "[\"%s\",\"%s\",\"%s\"]" : ",[\"%s\",\"%s\",\"%s\"]", pString, pModuleString, "unused"); + bFirst = false; + } + MicroProfileWSPrintf("]}"); + MicroProfileWSFlush(); + MicroProfileWSPrintEnd(); + + S.WSFunctionsInstrumentedSent = S.DynamicTokenIndex; + } +} + +void MicroProfileSymbolSendErrors(MpSocket Connection) +{ + if(S.nNumPatchErrors) + { + MicroProfileWSPrintStart(Connection); + MicroProfileWSPrintf("{\"k\":\"%d\",\"v\":{\"version\":\"%d.%d\",\"data\":[", MSG_INSTRUMENT_ERROR, MICROPROFILE_MAJOR_VERSION, MICROPROFILE_MINOR_VERSION); + bool bFirst = true; + for(int i = 0; i < S.nNumPatchErrors; ++i) + { + MicroProfilePatchError& E = S.PatchErrors[i]; + (void)E; + if(!bFirst) + MicroProfileWSPrintf(","); + MicroProfileWSPrintf("{\"code\":\""); + for(int i = 0; i < E.nCodeSize; ++i) + MicroProfileWSPrintf("%02x", E.Code[i] & 0xff); + MicroProfileWSPrintf("\",\"message\":\"%s\",\"already\":%d}", &E.Message[0], E.AlreadyInstrumented); + bFirst = false; + } + + MicroProfileWSPrintf("],\"functions\":["); + bFirst = true; + for(int i = 0; i < S.nNumPatchErrorFunctions; ++i) + { + if(!bFirst) + MicroProfileWSPrintf(","); + + MicroProfileWSPrintf("\"%s\"", S.PatchErrorFunctionNames[i]); + + bFirst = false; + } + + MicroProfileWSPrintf("]}}"); + + MicroProfileWSFlush(); + MicroProfileWSPrintEnd(); + + S.nNumPatchErrors = 0; + S.nNumPatchErrorFunctions = 0; + } +} + +void MicroProfileSymbolQuerySendResult(MpSocket Connection) +{ + MICROPROFILE_SCOPEI("MicroProfile", "MicroProfileSymbolQuerySendResult", MP_PINK2); + MicroProfileFunctionQuery* pQuery = 0; + { + MicroProfileScopeLock L(MicroProfileMutex()); + + uint32_t nBest = 0; + + while(S.pFinishedQuery != nullptr) + { + if(!pQuery) + { + pQuery = S.pFinishedQuery; + nBest = pQuery->QueryId; + S.pFinishedQuery = pQuery->pNext; + } + else + { + MicroProfileFunctionQuery* pQ = S.pFinishedQuery; + S.pFinishedQuery = pQ->pNext; + if(pQ->QueryId > nBest) + { + MicroProfileFreeFunctionQuery(pQuery); + nBest = pQ->QueryId; + pQuery = pQ; + } + else + { + MicroProfileFreeFunctionQuery(pQ); + } + } + } + } + + if(pQuery) + { + uprintf("Sending result for query %d\n", pQuery->QueryId); + MicroProfileWSPrintStart(Connection); + MicroProfileWSPrintf("{\"k\":\"%d\",\"q\":%d,\"v\":[", MSG_FUNCTION_RESULTS, pQuery->QueryId); + bool bFirst = true; + for(uint32_t i = 0; i < pQuery->nNumResults; ++i) + { + MicroProfileSymbolDesc& E = *pQuery->Results[i]; + if(bFirst) + { + MicroProfileWSPrintf("{\"a\":\"%p\",\"n\":\"%s\",\"sn\":\"%s\",\"m\":\"%s\"}", E.nAddress, E.pName, E.pShortName, MicroProfileSymbolModuleGetString(E.nModule)); + bFirst = false; + } + else + { + MicroProfileWSPrintf(",{\"a\":\"%p\",\"n\":\"%s\",\"sn\":\"%s\",\"m\":\"%s\"}", E.nAddress, E.pName, E.pShortName, MicroProfileSymbolModuleGetString(E.nModule)); + } + } + MicroProfileWSPrintf("]}"); + MicroProfileWSFlush(); + MicroProfileWSPrintEnd(); + + MicroProfileScopeLock L(MicroProfileMutex()); + MicroProfileFreeFunctionQuery(pQuery); + } +} +#endif + +void MicroProfileSymbolQueryFunctions(MpSocket Connection, const char* pFilter) +{ + MICROPROFILE_SCOPEI("MicroProfile", "MicroProfileSymbolQueryFunctions", MP_WHEAT); + + if(!MicroProfileSymbolInitialize(false)) + { + return; + } + { + int QueryId = atoi(pFilter); + pFilter = strchr(pFilter, 'x'); + pFilter++; + MicroProfileScopeLock L(MicroProfileMutex()); + if(0 == S.pPendingQuery || S.pPendingQuery->QueryId < (uint32_t)QueryId) + { + MicroProfileFunctionQuery* pQuery = S.pPendingQuery; + if(!pQuery) + { + S.pPendingQuery = pQuery = MicroProfileAllocFunctionQuery(); + } + MP_ASSERT(pQuery->pNext == 0); + memset(pQuery, 0, sizeof(*pQuery)); + + MicroProfileFunctionQuery& Q = *pQuery; + Q.QueryId = QueryId; + + uint32_t nLen = (uint32_t)strlen(pFilter) + 1; + if(nLen >= MICROPROFILE_MAX_FILTER_STRING) + nLen = MICROPROFILE_MAX_FILTER_STRING - 1; + + memcpy(Q.FilterString, pFilter, nLen); + Q.FilterString[nLen] = '\0'; + + char* pBuffer = Q.FilterString; + bool bStartString = true; + for(uint32_t i = 0; i < nLen; ++i) + { + char c = pBuffer[i]; + if(c == '\0') + { + break; + } + if(isspace(c) || c == '*') + { + pBuffer[i] = '\0'; + bStartString = true; + } + else + { + if(bStartString) + { + if(Q.nMaxFilter < MICROPROFILE_MAX_FILTER) + { + const char* pstr = &pBuffer[i]; + Q.nMask[Q.nMaxFilter] = MicroProfileCharacterMaskString(pstr); + MicroProfileCharacterMaskString2(pstr, Q.MatchMask[Q.nMaxFilter]); + Q.pFilterStrings[Q.nMaxFilter++] = &pBuffer[i]; + } + } + bStartString = false; + } + } + memset(Q.nModuleFilterMatch, 0xff, sizeof(Q.nModuleFilterMatch)); + for(int i = 0; i < S.SymbolNumModules; ++i) + { + Q.nModuleFilterMatch[i] = MicroProfileStringMatchOffset(MicroProfileSymbolModuleGetString(i), Q.pFilterStrings, Q.nPatternLength, Q.nMaxFilter); + } + +#if 0 + uprintf("query %d::",QueryId); + for(int i = 0; i < Q.nMaxFilter; ++i) + { + Q.nPatternLength[i] = (uint32_t)strlen(Q.pFilterStrings[i]); + uprintf("'%s' ", Q.pFilterStrings[i]); + } + uprintf("\n"); +#endif + } + } + MicroProfileSymbolKickThread(); +} + +#if defined(_WIN32) +// '##::::'##::'#######:::'#######::'##:::'##::::'##:::::'##:'####:'##::: ##::'#######:::'#######:: +// ##:::: ##:'##.... ##:'##.... ##: ##::'##::::: ##:'##: ##:. ##:: ###:: ##:'##.... ##:'##.... ##: +// ##:::: ##: ##:::: ##: ##:::: ##: ##:'##:::::: ##: ##: ##:: ##:: ####: ##:..::::: ##:..::::: ##: +// #########: ##:::: ##: ##:::: ##: #####::::::: ##: ##: ##:: ##:: ## ## ##::'#######:::'#######:: +// ##.... ##: ##:::: ##: ##:::: ##: ##. ##:::::: ##: ##: ##:: ##:: ##. ####::...... ##:'##:::::::: +// ##:::: ##: ##:::: ##: ##:::: ##: ##:. ##::::: ##: ##: ##:: ##:: ##:. ###:'##:::: ##: ##:::::::: +// ##:::: ##:. #######::. #######:: ##::. ##::::. ###. ###::'####: ##::. ##:. #######:: #########: +// ..:::::..:::.......::::.......:::..::::..::::::...::...:::....::..::::..:::.......:::.........:: + +#ifdef _WIN32 +static void* MicroProfileAllocExecutableMemory(void* pBase, size_t s); +static void* MicroProfileAllocExecutableMemoryFar(size_t s); +static void MicroProfileMakeMemoryExecutable(void* p, size_t s); +static void MicroProfileMakeWriteable(void* p_, size_t size, DWORD* oldFlags); +static void MicroProfileRestore(void* p_, size_t size, DWORD* oldFlags); + +extern "C" void microprofile_tramp_enter_patch(); +extern "C" void microprofile_tramp_enter(); +extern "C" void microprofile_tramp_code_begin(); +extern "C" void microprofile_tramp_code_end(); +extern "C" void microprofile_tramp_intercept0(); +extern "C" void microprofile_tramp_end(); +extern "C" void microprofile_tramp_exit(); +extern "C" void microprofile_tramp_leave(); +extern "C" void microprofile_tramp_trunk(); +extern "C" void microprofile_tramp_call_patch_pop(); +extern "C" void microprofile_tramp_call_patch_push(); + +bool MicroProfilePatchFunction(void* f, int Argument, MicroProfileHookFunc enter, MicroProfileHookFunc leave, MicroProfilePatchError* pError) +{ + char* pOriginal = (char*)f; + + f = MicroProfileX64FollowJump(f); + if(MicroProfilePatchHasSuspendedThread((intptr_t)f, (intptr_t)f + 32)) + { + uprintf("failed to patch, thread running in patch position"); + return false; + } + intptr_t t_enter = (intptr_t)microprofile_tramp_enter; + intptr_t t_enter_patch_offset = (intptr_t)microprofile_tramp_enter_patch - t_enter; + intptr_t t_code_begin_offset = (intptr_t)microprofile_tramp_code_begin - t_enter; + intptr_t t_code_end_offset = (intptr_t)microprofile_tramp_code_end - t_enter; + intptr_t t_code_intercept0_offset = (intptr_t)microprofile_tramp_intercept0 - t_enter; + intptr_t t_code_exit_offset = (intptr_t)microprofile_tramp_exit - t_enter; + intptr_t t_code_leave_offset = (intptr_t)microprofile_tramp_leave - t_enter; + + intptr_t t_code_call_patch_push_offset = (intptr_t)microprofile_tramp_call_patch_push - t_enter; + intptr_t t_code_call_patch_pop_offset = (intptr_t)microprofile_tramp_call_patch_pop - t_enter; + intptr_t codemaxsize = t_code_end_offset - t_code_begin_offset; + intptr_t t_end_offset = (intptr_t)microprofile_tramp_end - t_enter; + intptr_t t_trunk_offset = (intptr_t)microprofile_tramp_trunk - t_enter; + int t_trunk_size = (int)((intptr_t)microprofile_tramp_end - (intptr_t)microprofile_tramp_trunk); + + char* ptramp = (char*)MicroProfileAllocExecutableMemory(f, t_end_offset); + if(!ptramp) + ptramp = (char*)MicroProfileAllocExecutableMemoryFar(t_end_offset); + + intptr_t offset = ((intptr_t)f + 6 - (intptr_t)ptramp); + + uint32_t nBytesToCopy = 14; + if(offset < 0x80000000 && offset > -0x7fffffff) + { + /// offset is small enough to insert a relative jump + nBytesToCopy = 5; + } + + memcpy(ptramp, (void*)t_enter, t_end_offset); + + int nInstructionBytesDest = 0; + char* pInstructionMoveDest = ptramp + t_code_begin_offset; + char* pTrunk = ptramp + t_trunk_offset; + + int nInstructionBytesSrc = 0; + uint32_t nRegsWritten = 0; + uint32_t nRetSafe = 1; + uint32_t nUsableJumpRegs = (1 << R_RAX) | (1 << R_R10) | (1 << R_R11); + static_assert(R_RAX == 0, "R_RAX must be 0"); + if(!MicroProfileCopyInstructionBytes( + pInstructionMoveDest, f, nBytesToCopy, (int)codemaxsize, pTrunk, t_trunk_size, nUsableJumpRegs, &nInstructionBytesDest, &nInstructionBytesSrc, &nRegsWritten, &nRetSafe)) + { + if(pError) + { + const char* pCode = (const char*)f; + memset(pError->Code, 0, sizeof(pError->Code)); + memcpy(pError->Code, pCode, nInstructionBytesSrc); + int off = stbsp_snprintf(pError->Message, sizeof(pError->Message), "Failed to move %d code bytes ", nInstructionBytesSrc); + pError->nCodeSize = nInstructionBytesSrc; + for(int i = 0; i < nInstructionBytesSrc; ++i) + { + off += stbsp_snprintf(off + pError->Message, sizeof(pError->Message) - off, "%02x ", 0xff & pCode[i]); + } + uprintf("%s\n", pError->Message); + } + return false; + } + + intptr_t phome = nInstructionBytesSrc + (intptr_t)f; + uint32_t reg = nUsableJumpRegs & ~nRegsWritten; + if(0 == reg) + { + if(0 == nRetSafe) + MP_BREAK(); // should be caught earlier + MicroProfileInsertRetJump(pInstructionMoveDest + nInstructionBytesDest, phome); + } + else + { + int r = R_RAX; + while((reg & 1) == 0) + { + reg >>= 1; + r++; + } + MicroProfileInsertRegisterJump(pInstructionMoveDest + nInstructionBytesDest, phome, r); + } + + // PATCH 1 TRAMP EXIT + intptr_t microprofile_tramp_exit = (intptr_t)ptramp + t_code_exit_offset; + memcpy(ptramp + t_enter_patch_offset + 2, (void*)µprofile_tramp_exit, 8); + + char* pintercept = t_code_intercept0_offset + ptramp; + + // PATCH 1.5 Argument + memcpy(pintercept - 4, (void*)&Argument, 4); + + // PATCH 2 INTERCEPT0 + intptr_t addr = (intptr_t)enter; //&intercept0; + memcpy(pintercept + 2, (void*)&addr, 8); + + // PATHC 2.5 argument + memcpy(ptramp + t_code_exit_offset + 3, (void*)&Argument, 4); + + intptr_t microprofile_tramp_leave = (intptr_t)ptramp + t_code_leave_offset; + // PATCH 3 INTERCEPT1 + intptr_t addr1 = (intptr_t)leave; //&intercept1; + memcpy((char*)microprofile_tramp_leave + 2, (void*)&addr1, 8); + + intptr_t patch_push_addr = (intptr_t)(&MicroProfile_Patch_TLS_PUSH); + intptr_t patch_pop_addr = (intptr_t)(&MicroProfile_Patch_TLS_POP); + memcpy((char*)ptramp + t_code_call_patch_push_offset + 2, &patch_push_addr, 8); + memcpy((char*)ptramp + t_code_call_patch_pop_offset + 2, &patch_pop_addr, 8); + MicroProfileMakeMemoryExecutable(ptramp, t_end_offset); + + { + // PATCH 4 DEST FUNC + + DWORD OldFlags[2] = { 0 }; + MicroProfileMakeWriteable(f, nInstructionBytesSrc, OldFlags); + char* pp = (char*)f; + char* ppend = pp + nInstructionBytesSrc; + + if(nInstructionBytesSrc < 14) + { + pp = MicroProfileInsertRelativeJump((char*)pp, (intptr_t)ptramp); + } + else + { + pp = MicroProfileInsertRegisterJump((char*)pp, (intptr_t)ptramp, R_RAX); + } + + while(pp != ppend) + { + char c = (unsigned char)0x90; + MP_ASSERT((unsigned char)c == (unsigned char)0x90); + *pp++ = (unsigned char)0x90; + } + MicroProfileRestore(f, nInstructionBytesSrc, OldFlags); + } + return true; +} + +static void MicroProfileMakeWriteable(void* p_, size_t s, DWORD* oldFlags) +{ + static uint64_t nPageSize = 4 << 10; + + intptr_t aligned = (intptr_t)p_; + aligned = (aligned & (~(nPageSize - 1))); + intptr_t aligned_end = (intptr_t)p_; + aligned_end += s; + aligned_end = (aligned_end + nPageSize - 1) & (~(nPageSize - 1)); + uint32_t nNumPages = (uint32_t)((aligned_end - aligned) / nPageSize); + MP_ASSERT(nNumPages >= 1 && nNumPages <= 2); + for(uint32_t i = 0; i < nNumPages; ++i) + { + if(!VirtualProtect((void*)(aligned + nPageSize * i), nPageSize, PAGE_EXECUTE_READWRITE, oldFlags + i)) + { + MP_BREAK(); + } + } + //*(unsigned char*)p_ = 0x90; +} + +static void MicroProfileRestore(void* p_, size_t s, DWORD* oldFlags) +{ + static uint64_t nPageSize = 4 << 10; + + intptr_t aligned = (intptr_t)p_; + aligned = (aligned & (~(nPageSize - 1))); + intptr_t aligned_end = (intptr_t)p_; + aligned_end += s; + aligned_end = (aligned_end + nPageSize - 1) & (~(nPageSize - 1)); + uint32_t nNumPages = (uint32_t)((aligned_end - aligned) / nPageSize); + DWORD Dummy; + for(uint32_t i = 0; i < nNumPages; ++i) + { + if(!VirtualProtect((void*)(aligned + nPageSize * i), nPageSize, oldFlags[i], &Dummy)) + { + MP_BREAK(); + } + } +} + +void* MicroProfileAllocExecutableMemoryUp(intptr_t nBase, size_t s, uint32_t RegionIndex) +{ + SYSTEM_INFO si; + GetSystemInfo(&si); + size_t Granularity = si.dwAllocationGranularity << 1; + nBase = (nBase / Granularity) * Granularity; + intptr_t nEnd = nBase + 0x80000000; + + for(uint32_t i = RegionIndex; i < S.MemoryRegions.Size; i++) + { + // try and allocate 2x before + nBase = S.MemoryRegions[i].Start + S.MemoryRegions[i].Size + Granularity; + nBase = (nBase / Granularity) * Granularity; + + if(nBase >= nEnd) + break; + void* pMemory = VirtualAlloc((void*)nBase, s, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE); + if(pMemory) + { + return pMemory; + } + } + return nullptr; +} + +static void MicroProfileUpdateMemoryRegions() +{ + MicroProfileArrayClear(S.MemoryRegions); + SYSTEM_INFO si; + GetSystemInfo(&si); + + BYTE* Addr = (BYTE*)si.lpMinimumApplicationAddress; + BYTE* MaxAddr = (BYTE*)si.lpMaximumApplicationAddress; + // uprintf("updating memory regions\n"); + uint32_t idx = 0; + (void)idx; + while(Addr < MaxAddr) + { + MEMORY_BASIC_INFORMATION mbi; + SIZE_T Result = VirtualQuery(Addr, &mbi, sizeof(mbi)); + if(Result == 0) + break; + MicroProfileInstrumentMemoryRegion region; + region.Start = (intptr_t)mbi.BaseAddress; + region.Size = (intptr_t)mbi.RegionSize; + MicroProfileArrayPushBack(S.MemoryRegions, region); + // uprintf("Memory Region %d: %p(%p) %p .. State=%08x Protect=%08x Type=%08x\n", idx++, mbi.BaseAddress, mbi.AllocationBase, (intptr_t)mbi.BaseAddress + mbi.RegionSize, mbi.State, mbi.Protect, + // mbi.Type); + Addr = (BYTE*)mbi.BaseAddress + mbi.RegionSize; + } + uprintf("Iterated %d regions\n", S.MemoryRegions.Size); +} + +static void* MicroProfileAllocExecutableMemoryDown(intptr_t nBase, size_t s, uint32_t RegionIndex) +{ + SYSTEM_INFO si; + GetSystemInfo(&si); + size_t Granularity = si.dwAllocationGranularity << 1; + intptr_t nEnd = nBase - 0x80000000; + + for(int32_t i = RegionIndex; i >= 0; i--) + { + // try and allocate 2x before + nBase = S.MemoryRegions[i].Start - Granularity; + nBase = (nBase / Granularity) * Granularity; + if(nBase < nEnd) + break; + void* pMemory = VirtualAlloc((void*)nBase, s, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE); + if(pMemory) + { + return pMemory; + } + } + return nullptr; +} + +static void* MicroProfileAllocExecutableMemory(void* pBase, size_t s) +{ + uint32_t RegionIndex = 0; + for(uint32_t i = 0; i < S.MemoryRegions.Size; ++i) + { + auto& R = S.MemoryRegions[i]; + if(R.Start <= (intptr_t)pBase && (intptr_t)pBase < R.Start + R.Size) + { + RegionIndex = i; + break; + } + } + + s = (s + 4095) & ~(4095); + intptr_t nBase = (intptr_t)pBase; + void* pResult = 0; + if(0 == pResult && nBase > 0x40000000) + { + pResult = MicroProfileAllocExecutableMemoryDown(nBase - 0x40000000, s, RegionIndex); + if(0 == pResult) + { + pResult = MicroProfileAllocExecutableMemoryUp(nBase - 0x40000000, s, RegionIndex); + } + } + if(0 == pResult && nBase < 0xffffffff40000000) + { + pResult = MicroProfileAllocExecutableMemoryUp(nBase + 0x40000000, s, RegionIndex); + if(0 == pResult) + { + pResult = MicroProfileAllocExecutableMemoryUp(nBase + 0x40000000, s, RegionIndex); + } + } + return pResult; +} +static void* MicroProfileAllocExecutableMemoryFar(size_t s) +{ + static uint64_t nPageSize = 4 << 10; + s = (s + (nPageSize - 1)) & (~(nPageSize - 1)); + + void* pMem = VirtualAlloc(0, s, MEM_COMMIT, PAGE_READWRITE); + MP_ASSERT(pMem); + + // uprintf("Allocating %zu %p\n", s, pMem); + return pMem; +} +static void MicroProfileMakeMemoryExecutable(void* p, size_t s) +{ + static uint64_t nPageSize = 4 << 10; + s = (s + (nPageSize - 1)) & (~(nPageSize - 1)); + DWORD Unused; + if(!VirtualProtect(p, s, PAGE_EXECUTE_READ, &Unused)) + { + MP_BREAK(); + } +} +#endif + +int MicroProfileTrimFunctionName(const char* pStr, char* pOutBegin, char* pOutEnd) +{ + const char* pStart = pOutBegin; + int l = (int)strlen(pStr) - 1; + int sz = 0; + pOutEnd--; + if(l < 1024 && pOutBegin != pOutEnd) + { + const char* p = pStr; + const char* pEnd = pStr + l + 1; + int in = 0; + while(p != pEnd && pOutBegin != pOutEnd) + { + char c = *p++; + if(c == '(' || c == '<') + { + in++; + } + else if(c == ')' || c == '>') + { + in--; + continue; + } + + if(in == 0) + { + *pOutBegin++ = c; + sz++; + } + } + + *pOutBegin++ = '\0'; + } + return sz; +} + +int MicroProfileFindFunctionName(const char* pStr, const char** ppStart) +{ + int l = (int)strlen(pStr) - 1; + if(l < 1024) + { + char b[1024] = { 0 }; + char* put = &b[0]; + + const char* p = pStr; + const char* pEnd = pStr + l + 1; + int in = 0; + while(p != pEnd) + { + char c = *p++; + if(c == '(' || c == '<') + { + in++; + } + else if(c == ')' || c == '>') + { + in--; + continue; + } + + if(in == 0) + { + *put++ = c; + } + } + + *put++ = '\0'; + uprintf("trimmed %s\n", b); + } + + // int nFirstParen = l; + int nNumParen = 0; + int c = 0; + + while(l >= 0 && pStr[l] != ')' && c++ < sizeof(" const") - 1) + { + l--; + } + if(pStr[l] == ')') + { + do + { + if(pStr[l] == ')') + { + nNumParen++; + } + else if(pStr[l] == '(') + { + nNumParen--; + } + l--; + } while(nNumParen > 0 && l >= 0); + } + else + { + *ppStart = pStr; + return 0; + } + while(l >= 0 && isspace(pStr[l])) + { + --l; + } + int nLast = l; + while(l >= 0 && !isspace(pStr[l])) + { + l--; + } + int nFirst = l; + if(nFirst == nLast) + return 0; + int nCount = nLast - nFirst + 1; + *ppStart = pStr + nFirst; + return nCount; +} + +#include +#include +#include +#include +struct MicroProfileQueryContext +{ + + const char* pFilterStrings[MICROPROFILE_MAX_FILTER]; + uint32_t nPatternLength[MICROPROFILE_MAX_FILTER]; + int nMaxFilter = 0; + char TempBuffer[128]; + uint32_t size = 0; + bool bFirst = false; +}; + +BOOL CALLBACK MicroProfileEnumModules(_In_ PCTSTR ModuleName, _In_ DWORD64 BaseOfDll, _In_opt_ PVOID UserContext) +{ + + MODULEINFO MI; + GetModuleInformation(GetCurrentProcess(), (HMODULE)BaseOfDll, &MI, sizeof(MI)); + MEMORY_BASIC_INFORMATION B; + int r = VirtualQuery((LPCVOID)BaseOfDll, (MEMORY_BASIC_INFORMATION*)&B, sizeof(B)); + char buffer[1024]; + int r1 = GetLastError(); + if(r == 0) + { + stbsp_snprintf(buffer, sizeof(buffer) - 1, "Error %d\n", r1); + OutputDebugString(buffer); + MP_BREAK(); + } + MicroProfileSymbolInitModule(ModuleName, BaseOfDll, BaseOfDll + MI.SizeOfImage); + return true; +} + +namespace +{ +struct QueryCallbackBase // fucking c++, this is a pain in the ass +{ + virtual void CB(const char* pName, const char* pShortName, intptr_t addr, intptr_t addrend, uint32_t nModuleId) = 0; +}; +template +struct QueryCallbackImpl : public QueryCallbackBase +{ + T t; + QueryCallbackImpl(T t) + : t(t) + { + } + virtual void CB(const char* pName, const char* pShortName, intptr_t addr, intptr_t addrend, uint32_t nModuleId) + { + t(pName, pShortName, addr, addrend, nModuleId); + } +}; +} // namespace + +static uint32_t nLastModuleIdWin32 = (uint32_t)-1; +static intptr_t nLastModuleBaseWin32 = (intptr_t)-1; + +BOOL MicroProfileQueryContextEnumSymbols(_In_ PSYMBOL_INFO pSymInfo, _In_ ULONG SymbolSize, _In_opt_ PVOID UserContext) +{ + uint32_t nModuleId = nLastModuleIdWin32; + if(nLastModuleBaseWin32 != (intptr_t)pSymInfo->ModBase) + { + nLastModuleIdWin32 = nModuleId = MicroProfileSymbolGetModule((const char*)(intptr_t)-2, pSymInfo->ModBase); + nLastModuleBaseWin32 = (intptr_t)pSymInfo->ModBase; + } + + if(pSymInfo->Tag == 5 || pSymInfo->Tag == 10) + { + + char FunctionName[1024]; + int ret = 0; + int l = MicroProfileTrimFunctionName(pSymInfo->Name, &FunctionName[0], &FunctionName[1024]); + QueryCallbackBase* pCB = (QueryCallbackBase*)UserContext; + + pCB->CB(pSymInfo->Name, l ? &FunctionName[0] : 0, (intptr_t)pSymInfo->Address, pSymInfo->Size + (intptr_t)pSymInfo->Address, nModuleId); + } + return TRUE; +}; + +bool MicroProfileDemangleName(const char* pName, char* OutName, uint32_t Size) +{ + MICROPROFILE_SCOPEI("microprofile", "SymbolDemangle", MP_AUTO); + if(UnDecorateSymbolName(pName, OutName, Size, UNDNAME_NAME_ONLY)) + { + return true; + } + return false; +} + +bool MicroProfileExtractPdbInfo(HMODULE hMod, GUID& guid, DWORD& age, char pdbName[MAX_PATH]) +{ + struct CV_INFO_PDB70 + { + DWORD CvSignature; // "RSDS" + GUID Signature; // GUID + DWORD Age; // Age + char PdbFileName[1]; // Null-terminated string + }; + + BYTE* base = (BYTE*)hMod; + IMAGE_DOS_HEADER* dos = (IMAGE_DOS_HEADER*)base; + if(dos->e_magic != IMAGE_DOS_SIGNATURE) + return false; + IMAGE_NT_HEADERS* nt = (IMAGE_NT_HEADERS*)(base + dos->e_lfanew); + if(nt->Signature != IMAGE_NT_SIGNATURE) + return false; + auto& dd = nt->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_DEBUG]; + if(!dd.VirtualAddress || !dd.Size) + return false; + IMAGE_DEBUG_DIRECTORY* debugDir = (IMAGE_DEBUG_DIRECTORY*)(base + dd.VirtualAddress); + int count = dd.Size / sizeof(IMAGE_DEBUG_DIRECTORY); + for(int i = 0; i < count; i++) + { + if(debugDir[i].Type == IMAGE_DEBUG_TYPE_CODEVIEW) + { + auto cv = (CV_INFO_PDB70*)(base + debugDir[i].AddressOfRawData); + if(cv->CvSignature != 'SDSR') + continue; // "RSDS" + guid = cv->Signature; + age = cv->Age; + strcpy_s(pdbName, MAX_PATH, cv->PdbFileName); + return true; + } + } + return false; +} + +bool MicroProfileDownloadPDB(HMODULE Module, HANDLE Process, char outPath[MAX_PATH]) +{ + GUID guid; + DWORD age; + char pdbName[MAX_PATH]; + if(!MicroProfileExtractPdbInfo(Module, guid, age, pdbName)) + { + uprintf("Failed to download pdb\n"); + MP_BREAK(); + return false; + } + uprintf("pdb name %s age %d\n", pdbName, age); + + FILE* f = fopen(pdbName, "r"); + if(f) + { + fclose(f); + strcpy_s(outPath, MAX_PATH, pdbName); + return true; + } + char localPath[MAX_PATH] = {}; + BOOL ok = SymFindFileInPath(Process, + NULL, + pdbName, + (PVOID)&guid, // GUID + age, // Age + 0, // FileSize (not used for PDBs) + SSRVOPT_GUIDPTR, // we're passing GUID pointer + outPath, + NULL, + NULL); + return ok != 0; +} + +#include "PDB.h" +#include "PDB_DBIStream.h" +#include "PDB_IPIStream.h" +#include "PDB_InfoStream.h" +#include "PDB_NamesStream.h" +#include "PDB_RawFile.h" +#include "PDB_TPIStream.h" + +template +void MicroProfileLoadRawPDB(Callback CB, const char* Filename, uint64_t Base, uint32_t nModuleId) +{ + auto OnSymbol = [CB, Base, nModuleId](const char* Sym, uint32_t Offset, uint32_t Size) + { + char FunctionName[1024]; + int ret = 0; + int l = MicroProfileTrimFunctionName(Sym, &FunctionName[0], &FunctionName[1024]); + const char* fname = l ? &FunctionName[0] : nullptr; + CB(Sym, fname, (intptr_t)Offset + Base, (intptr_t)Offset + Base + Size, nModuleId); + }; + + void* File = CreateFileA(Filename, GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_READONLY, nullptr); + + if(File == INVALID_HANDLE_VALUE) + { + MP_BREAK(); + } + + void* FileMapping = CreateFileMappingA(File, nullptr, PAGE_READONLY, 0, 0, nullptr); + + if(FileMapping == nullptr) + { + CloseHandle(File); + MP_BREAK(); + } + + void* BaseAddress = MapViewOfFile(FileMapping, FILE_MAP_READ, 0, 0, 0); + + if(BaseAddress == nullptr) + { + CloseHandle(FileMapping); + CloseHandle(File); + } + + BY_HANDLE_FILE_INFORMATION FileInformation; + const bool GetInformationResult = GetFileInformationByHandle(File, &FileInformation); + if(!GetInformationResult) + { + UnmapViewOfFile(BaseAddress); + CloseHandle(FileMapping); + CloseHandle(File); + + MP_BREAK(); + } + + const size_t FileSizeHighBytes = static_cast(FileInformation.nFileSizeHigh) << 32; + const size_t FileSizeLowBytes = FileInformation.nFileSizeLow; + const size_t FileSize = FileSizeHighBytes | FileSizeLowBytes; + + const PDB::RawFile RawPdbFile = PDB::CreateRawFile(BaseAddress); + if(PDB::HasValidDBIStream(RawPdbFile) != PDB::ErrorCode::Success) + { + MP_BREAK(); + } + const PDB::InfoStream InfoStream(RawPdbFile); + if(InfoStream.UsesDebugFastLink()) + { + MP_BREAK(); + } + + // const PDB::Header* h = InfoStream.GetHeader(); + // uprintf("Version %u, signature %u, age %u, GUID %08x-%04x-%04x-%02x%02x%02x%02x%02x%02x%02x%02x\n", + // static_cast(h->version), h->signature, h->age, + // h->guid.Data1, h->guid.Data2, h->guid.Data3, + // h->guid.Data4[0], h->guid.Data4[1], h->guid.Data4[2], h->guid.Data4[3], h->guid.Data4[4], h->guid.Data4[5], h->guid.Data4[6], h->guid.Data4[7]); + + const PDB::DBIStream DbiStream = PDB::CreateDBIStream(RawPdbFile); + if(PDB::ErrorCode::Success != DbiStream.HasValidSymbolRecordStream(RawPdbFile)) + { + MP_BREAK(); + } + + if(PDB::ErrorCode::Success != DbiStream.HasValidPublicSymbolStream(RawPdbFile)) + { + MP_BREAK(); + } + + if(PDB::ErrorCode::Success != DbiStream.HasValidGlobalSymbolStream(RawPdbFile)) + { + MP_BREAK(); + } + + if(PDB::ErrorCode::Success != DbiStream.HasValidSectionContributionStream(RawPdbFile)) + { + MP_BREAK(); + } + + if(PDB::ErrorCode::Success != DbiStream.HasValidImageSectionStream(RawPdbFile)) + { + MP_BREAK(); + } + + const PDB::ImageSectionStream ImageSectionStream = DbiStream.CreateImageSectionStream(RawPdbFile); + const PDB::ModuleInfoStream ModuleInfoStream = DbiStream.CreateModuleInfoStream(RawPdbFile); + const PDB::CoalescedMSFStream SymbolRecordStream = DbiStream.CreateSymbolRecordStream(RawPdbFile); + + const PDB::ArrayView modules = ModuleInfoStream.GetModules(); + + for(const PDB::ModuleInfoStream::Module& module : modules) + { + if(!module.HasSymbolStream()) + { + continue; + } + + const PDB::ModuleSymbolStream moduleSymbolStream = module.CreateSymbolStream(RawPdbFile); + moduleSymbolStream.ForEachSymbol( + [&ImageSectionStream, &OnSymbol](const PDB::CodeView::DBI::Record* record) + { + // only grab function symbols from the module streams + const char* name = nullptr; + uint32_t rva = 0u; + uint32_t size = 0u; + if(record->header.kind == PDB::CodeView::DBI::SymbolRecordKind::S_FRAMEPROC) + { + // functionSymbols[functionSymbols.size() - 1].frameProc = record; + return; + } + else if(record->header.kind == PDB::CodeView::DBI::SymbolRecordKind::S_THUNK32) + { + if(record->data.S_THUNK32.thunk == PDB::CodeView::DBI::ThunkOrdinal::TrampolineIncremental) + { + // we have never seen incremental linking thunks stored inside a S_THUNK32 symbol, but better safe than sorry + name = "ILT"; + rva = ImageSectionStream.ConvertSectionOffsetToRVA(record->data.S_THUNK32.section, record->data.S_THUNK32.offset); + size = 5u; + } + } + else if(record->header.kind == PDB::CodeView::DBI::SymbolRecordKind::S_TRAMPOLINE) + { + // incremental linking thunks are stored in the linker module + name = "ILT"; + rva = ImageSectionStream.ConvertSectionOffsetToRVA(record->data.S_TRAMPOLINE.thunkSection, record->data.S_TRAMPOLINE.thunkOffset); + size = 5u; + } + else if(record->header.kind == PDB::CodeView::DBI::SymbolRecordKind::S_LPROC32) + { + name = record->data.S_LPROC32.name; + rva = ImageSectionStream.ConvertSectionOffsetToRVA(record->data.S_LPROC32.section, record->data.S_LPROC32.offset); + size = record->data.S_LPROC32.codeSize; + } + else if(record->header.kind == PDB::CodeView::DBI::SymbolRecordKind::S_GPROC32) + { + name = record->data.S_GPROC32.name; + rva = ImageSectionStream.ConvertSectionOffsetToRVA(record->data.S_GPROC32.section, record->data.S_GPROC32.offset); + size = record->data.S_GPROC32.codeSize; + } + else if(record->header.kind == PDB::CodeView::DBI::SymbolRecordKind::S_LPROC32_ID) + { + name = record->data.S_LPROC32_ID.name; + rva = ImageSectionStream.ConvertSectionOffsetToRVA(record->data.S_LPROC32_ID.section, record->data.S_LPROC32_ID.offset); + size = record->data.S_LPROC32_ID.codeSize; + } + else if(record->header.kind == PDB::CodeView::DBI::SymbolRecordKind::S_GPROC32_ID) + { + name = record->data.S_GPROC32_ID.name; + rva = ImageSectionStream.ConvertSectionOffsetToRVA(record->data.S_GPROC32_ID.section, record->data.S_GPROC32_ID.offset); + size = record->data.S_GPROC32_ID.codeSize; + } + + if(rva == 0u) + { + return; + } + // uprintf("func %p / %d .. %s \n", rva, size, name); + OnSymbol(name, rva, size); + }); + } + const PDB::PublicSymbolStream PublicSymbolStream = DbiStream.CreatePublicSymbolStream(RawPdbFile); + { + const PDB::ArrayView HashRecords = PublicSymbolStream.GetRecords(); + const size_t Count = HashRecords.GetLength(); + + for(const PDB::HashRecord& HashRecord : HashRecords) + { + const PDB::CodeView::DBI::Record* Record = PublicSymbolStream.GetRecord(SymbolRecordStream, HashRecord); + if(Record->header.kind != PDB::CodeView::DBI::SymbolRecordKind::S_PUB32) + { + continue; + } + + if((PDB_AS_UNDERLYING(Record->data.S_PUB32.flags) & PDB_AS_UNDERLYING(PDB::CodeView::DBI::PublicSymbolFlags::Function)) == 0u) + { + continue; + } + + const uint32_t rva = ImageSectionStream.ConvertSectionOffsetToRVA(Record->data.S_PUB32.section, Record->data.S_PUB32.offset); + if(rva == 0u) + { + continue; + } + OnSymbol(Record->data.S_PUB32.name, rva, 0); + } + } + UnmapViewOfFile(BaseAddress); + CloseHandle(FileMapping); + CloseHandle(File); +} + +bool MicroProfilePatchHasSuspendedThread(intptr_t Begin, intptr_t End) +{ + MicroProfileSuspendState& State = S.SuspendState; + for(uint32_t i = 0; i < State.NumSuspended; ++i) + { + intptr_t ip = State.SuspendedIP[i]; + if(Begin <= ip && ip <= End) + return true; + } + return false; +} + +bool MicroProfilePatchBeginSuspend() +{ + MicroProfileSuspendState& State = S.SuspendState; + + if(State.SuspendCounter++ > 0) + return true; + MicroProfileUpdateMemoryRegions(); + + MicroProfileMutex().lock(); + MP_ASSERT(State.NumSuspended == 0); + + DWORD ProcessId = GetCurrentProcessId(); + DWORD ThreadId = GetCurrentThreadId(); + + HANDLE hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0); + if(hSnap == INVALID_HANDLE_VALUE) + { + return false; + } + THREADENTRY32 te{}; + te.dwSize = sizeof(te); + State.NumSuspended = 0; + + if(Thread32First(hSnap, &te)) + { + do + { + if(te.th32OwnerProcessID != ProcessId) + continue; + if(te.th32ThreadID == ThreadId) + continue; + HANDLE hThread = OpenThread(THREAD_SUSPEND_RESUME | THREAD_GET_CONTEXT | THREAD_QUERY_INFORMATION, FALSE, te.th32ThreadID); + if(!hThread) + { + continue; + } + DWORD PrevCount = SuspendThread(hThread); + if(PrevCount == (DWORD)-1) + { + CloseHandle(hThread); + continue; + } + + CONTEXT ctx{}; + ctx.ContextFlags = CONTEXT_CONTROL | CONTEXT_INTEGER; // Rip + registers + if(GetThreadContext(hThread, &ctx)) + { + State.SuspendedIP[State.NumSuspended] = (intptr_t)ctx.Rip; + } + if(State.NumSuspended < MICROPROFILE_SUSPEND_MAX) + { + State.Suspended[State.NumSuspended++] = hThread; + } + } while(Thread32Next(hSnap, &te)); + } + else + { + uprintf("Thread32First failed %08x\n", GetLastError()); + CloseHandle(hSnap); + return false; + } + CloseHandle(hSnap); + return State.NumSuspended > 0; +} + +void MicroProfilePatchEndSuspend() +{ + MicroProfileSuspendState& State = S.SuspendState; + if(0 == --State.SuspendCounter) + { + + for(uint32_t i = 0; i < State.NumSuspended; ++i) + { + ResumeThread(State.Suspended[i]); + CloseHandle(State.Suspended[i]); + } + State.NumSuspended = 0; + MicroProfileMutex().unlock(); + } +} + +template +void MicroProfileIterateSymbols(Callback CB, uint32_t* nModules, uint32_t nNumModules) +{ + MICROPROFILE_SCOPEI("MicroProfile", "MicroProfileIterateSymbols", MP_PINK3); + QueryCallbackImpl Context(CB); + if(MicroProfileSymInit()) + { + // uprintf("symbols loaded!\n"); + // API_VERSION* pv = ImagehlpApiVersion(); + // uprintf("VERSION %d.%d.%d\n", pv->MajorVersion, pv->MinorVersion, pv->Revision); + + nLastModuleBaseWin32 = -1; + if(SymEnumerateModules64(GetCurrentProcess(), (PSYM_ENUMMODULES_CALLBACK64)MicroProfileEnumModules, NULL)) + { + } + QueryCallbackBase* pBase = &Context; + if(nNumModules) + { + HANDLE hProcess = GetCurrentProcess(); + char buffer[sizeof(SYMBOL_INFO) + MAX_SYM_NAME * sizeof(TCHAR)]; + PSYMBOL_INFO pSymbol = (PSYMBOL_INFO)buffer; + uint64_t t0 = MP_TICK(); + for(uint32_t i = 0; i < nNumModules; ++i) + { + uint32_t nModule = nModules[i]; + int64_t nBytes = 0; + MEMORY_BASIC_INFORMATION B; + + for(int j = 0; j < S.SymbolModules[nModule].nNumExecutableRegions; ++j) + { + intptr_t b = S.SymbolModules[nModule].Regions[j].nBegin; + intptr_t e = S.SymbolModules[nModule].Regions[j].nEnd; + while(b < e) + { + int r = VirtualQuery((LPCVOID)b, &B, sizeof(B)); + if(!r) + break; + switch(B.Protect) + { + case PAGE_EXECUTE: + case PAGE_EXECUTE_READ: + case PAGE_EXECUTE_READWRITE: + case PAGE_EXECUTE_WRITECOPY: + nBytes += B.RegionSize; + // uprintf("RANGE %p, %p .. %5.2fkb %08x, %08x\n", B.BaseAddress, (void*)(intptr_t(B.BaseAddress) + B.RegionSize), B.RegionSize / 1024.f, B.State, B.Protect); + } + b = intptr_t(B.BaseAddress) + B.RegionSize; + } + } + S.SymbolModules[nModule].nProgressTarget = nBytes; + + char pdbPath[MAX_PATH]; + HMODULE Module = (HMODULE)S.SymbolModules[nModule].nModuleBase; + S.nSymbolsDirty++; + S.SymbolModules[nModule].bDownloading = true; + if(MicroProfileDownloadPDB(Module, hProcess, pdbPath)) + { + S.SymbolModules[nModule].bDownloading = false; + S.nSymbolsDirty++; + MicroProfileLoadRawPDB(CB, pdbPath, S.SymbolModules[nModule].nModuleBase, nModule); + } + S.SymbolModules[nModule].bDownloading = false; + S.nSymbolsDirty++; + S.SymbolModules[nModule].nProgress = S.SymbolModules[nModule].nProgressTarget; + S.SymbolModules[nModule].nModuleLoadFinished.exchange(1); + } + + uint64_t t1 = MP_TICK(); + float fTime = float(MicroProfileTickToMsMultiplierCpu()) * (t1 - t0); + uprintf("load symbol time %6.2fms\n", fTime); + } + MicroProfileSymCleanup(); + } +} + +static int MicroProfileWin32SymInitCount = 0; +static int MicroProfileWin32SymInitSuccess = 0; + +bool MicroProfileSymInit() +{ + if(0 == MicroProfileWin32SymInitCount++) + { + auto h = GetCurrentProcess(); + SymCleanup(h); + SymSetOptions(SYMOPT_DEFERRED_LOADS); + if(SymInitialize(h, 0, FALSE)) + { + + MicroProfileWin32SymInitSuccess = 1; + char Path[MAX_PATH]; + bool PathValid = SymGetSearchPath(h, Path, MAX_PATH) > 0; + if(PathValid) + { + PathValid = strlen(Path) > 3; + } + if(!PathValid) + { + SymSetSearchPath(h, "srv*C:\\symbols*https://msdl.microsoft.com/download/symbols"); + } + } + else + { + MicroProfileWin32SymInitSuccess = 0; + } + } + return MicroProfileWin32SymInitSuccess != 0; +} +void MicroProfileSymCleanup() +{ + if(0 == --MicroProfileWin32SymInitCount) + { + MicroProfileWin32SymInitSuccess = 0; + SymCleanup(GetCurrentProcess()); + } +} + +static void* g_pFunctionFoundHack = 0; +static const char* g_pFunctionpNameFound = 0; +static char g_Demangled[512]; + +BOOL MicroProfileQueryContextEnumSymbols1(_In_ PSYMBOL_INFO pSymInfo, _In_ ULONG SymbolSize, _In_opt_ PVOID UserContext) +{ + if(pSymInfo->Tag == 5 || pSymInfo->Tag == 10) + { + char str[200]; + stbsp_snprintf(str, sizeof(str) - 1, "%s : %p\n", pSymInfo->Name, (void*)pSymInfo->Address); + OutputDebugStringA(str); + g_pFunctionpNameFound = pSymInfo->Name; + g_pFunctionFoundHack = (void*)pSymInfo->Address; + return FALSE; + } + return TRUE; +}; + +const char* MicroProfileDemangleSymbol(const char* pSymbol) +{ + return pSymbol; // todo: for some reasons all symbols im seaing right now are already undecorated? +} + +void MicroProfileInstrumentWithoutSymbols(const char** pModules, const char** pSymbols, uint32_t nNumSymbols) +{ + char SymString[512]; + const char* pStr = 0; + if(MicroProfileSymInit()) + { + HANDLE h = GetCurrentProcess(); + for(uint32_t i = 0; i < nNumSymbols; ++i) + { + int nCount = stbsp_snprintf(SymString, sizeof(SymString) - 1, "%s!%s", pModules[i], pSymbols[i]); + if(nCount <= sizeof(SymString) - 1) + { + g_pFunctionFoundHack = 0; + if(SymEnumSymbols(h, 0, SymString, MicroProfileQueryContextEnumSymbols1, 0)) + { + if(g_pFunctionFoundHack) + { + uint32_t nColor = MicroProfileColorFromString(pSymbols[i]); + const char* pDemangled = pSymbols[i]; // MicroProfileDemangleSymbol(pSymbols[i]); + MicroProfileInstrumentFunction(g_pFunctionFoundHack, pModules[i], pDemangled, nColor); + } + } + } + } + MicroProfileSymCleanup(); + } +} + +void MicroProfileSymbolEnumModules() +{ + HMODULE modules[1024]; + DWORD needed; + HANDLE h = GetCurrentProcess(); + + if(EnumProcessModules(h, modules, sizeof(modules), &needed)) + { + int count = needed / sizeof(HMODULE); + for(int i = 0; i < count; i++) + { + char moduleName[MAX_PATH]; + if(GetModuleFileNameEx(h, modules[i], moduleName, MAX_PATH)) + { + MODULEINFO mi = {}; + if(GetModuleInformation(h, modules[i], &mi, sizeof(mi))) + { + MicroProfileEnumModules(moduleName, (DWORD64)mi.lpBaseOfDll, 0); + } + } + } + } +} + +void MicroProfileSymbolUpdateModuleList() +{ + MICROPROFILE_SCOPEI("MicroProfile", "MicroProfileSymbolUpdateModuleList", MP_PINK3); + // QueryCallbackImpl Context(CB); + if(MicroProfileSymInit()) + { + uprintf("symbols loaded!\n"); + API_VERSION* pv = ImagehlpApiVersion(); + uprintf("VERSION %d.%d.%d\n", pv->MajorVersion, pv->MinorVersion, pv->Revision); + + nLastModuleBaseWin32 = -1; + MicroProfileSymbolEnumModules(); + MicroProfileSymCleanup(); + } +} + +#endif + +#if defined(__APPLE__) && defined(__MACH__) +// '##::::'##::'#######:::'#######::'##:::'##:::::'#######:::'######::'##::::'##: +// ##:::: ##:'##.... ##:'##.... ##: ##::'##:::::'##.... ##:'##... ##:. ##::'##:: +// ##:::: ##: ##:::: ##: ##:::: ##: ##:'##:::::: ##:::: ##: ##:::..:::. ##'##::: +// #########: ##:::: ##: ##:::: ##: #####::::::: ##:::: ##:. ######::::. ###:::: +// ##.... ##: ##:::: ##: ##:::: ##: ##. ##:::::: ##:::: ##::..... ##::: ## ##::: +// ##:::: ##: ##:::: ##: ##:::: ##: ##:. ##::::: ##:::: ##:'##::: ##:: ##:. ##:: +// ##:::: ##:. #######::. #######:: ##::. ##::::. #######::. ######:: ##:::. ##: +// ..:::::..:::.......::::.......:::..::::..::::::.......::::......:::..:::::..:: + +#include +#include +#include +#include +#include +#include +#include +#include + +static void* MicroProfileAllocExecutableMemory(void* f, size_t s); +static void MicroProfileMakeWriteable(void* p_); + +extern "C" void microprofile_tramp_enter_patch(); +extern "C" void microprofile_tramp_enter(); +extern "C" void microprofile_tramp_code_begin(); +extern "C" void microprofile_tramp_code_end(); +extern "C" void microprofile_tramp_intercept0(); +extern "C" void microprofile_tramp_end(); +extern "C" void microprofile_tramp_exit(); +extern "C" void microprofile_tramp_leave(); +extern "C" void microprofile_tramp_trunk(); +extern "C" void microprofile_tramp_call_patch_pop(); +extern "C" void microprofile_tramp_call_patch_push(); + +bool MicroProfilePatchFunction(void* f, int Argument, MicroProfileHookFunc enter, MicroProfileHookFunc leave, MicroProfilePatchError* pError) __attribute__((optnone)) +{ + if(pError) + { + memcpy(&pError->Code[0], f, 12); + } + + intptr_t t_enter = (intptr_t)microprofile_tramp_enter; + intptr_t t_enter_patch_offset = (intptr_t)microprofile_tramp_enter_patch - t_enter; + intptr_t t_code_begin_offset = (intptr_t)microprofile_tramp_code_begin - t_enter; + intptr_t t_code_end_offset = (intptr_t)microprofile_tramp_code_end - t_enter; + intptr_t t_code_intercept0_offset = (intptr_t)microprofile_tramp_intercept0 - t_enter; + intptr_t t_code_exit_offset = (intptr_t)microprofile_tramp_exit - t_enter; + intptr_t t_code_leave_offset = (intptr_t)microprofile_tramp_leave - t_enter; + + intptr_t t_code_call_patch_push_offset = (intptr_t)microprofile_tramp_call_patch_push - t_enter; + intptr_t t_code_call_patch_pop_offset = (intptr_t)microprofile_tramp_call_patch_pop - t_enter; + intptr_t codemaxsize = t_code_end_offset - t_code_begin_offset; + intptr_t t_end_offset = (intptr_t)microprofile_tramp_end - t_enter; + intptr_t t_trunk_offset = (intptr_t)microprofile_tramp_trunk - t_enter; + intptr_t t_trunk_size = (intptr_t)microprofile_tramp_end - (intptr_t)microprofile_tramp_trunk; + + char* ptramp = (char*)MicroProfileAllocExecutableMemory(f, t_end_offset); + + intptr_t offset = ((intptr_t)f + 6 - (intptr_t)ptramp); + + uint32_t nBytesToCopy = 14; + if(offset < 0x80000000 && offset > -0x7fffffff) + { + /// offset is small enough to insert a relative jump + nBytesToCopy = 5; + } + + memcpy(ptramp, (void*)t_enter, t_end_offset); + + int nInstructionBytesDest = 0; + char* pInstructionMoveDest = ptramp + t_code_begin_offset; + char* pTrunk = ptramp + t_trunk_offset; + + int nInstructionBytesSrc = 0; + + uint32_t nRegsWritten = 0; + uint32_t nRetSafe = 0; + uint32_t nUsableJumpRegs = (1 << R_RAX) | (1 << R_R10) | (1 << R_R11); // scratch && !parameter register + if(!MicroProfileCopyInstructionBytes( + pInstructionMoveDest, f, nBytesToCopy, codemaxsize, pTrunk, t_trunk_size, nUsableJumpRegs, &nInstructionBytesDest, &nInstructionBytesSrc, &nRegsWritten, &nRetSafe)) + { + if(pError) + { + const char* pCode = (const char*)f; + memset(pError->Code, 0, sizeof(pError->Code)); + memcpy(pError->Code, pCode, nInstructionBytesSrc); + int off = stbsp_snprintf(pError->Message, sizeof(pError->Message), "Failed to move %d code bytes ", nInstructionBytesSrc); + pError->nCodeSize = nInstructionBytesSrc; + for(int i = 0; i < nInstructionBytesSrc; ++i) + { + off += stbsp_snprintf(off + pError->Message, sizeof(pError->Message) - off, "%02x ", 0xff & pCode[i]); + } + uprintf("%s\n", pError->Message); + } + return false; + } + intptr_t phome = nInstructionBytesSrc + (intptr_t)f; + uint32_t reg = nUsableJumpRegs & ~nRegsWritten; + static_assert(R_RAX == 0, "R_RAX must be 0"); + if(0 == reg) + { + if(nRetSafe == 0) + { + MP_BREAK(); // shout fail earlier + } + MicroProfileInsertRetJump(pInstructionMoveDest + nInstructionBytesDest, phome); + } + else + { + int r = R_RAX; + while((reg & 1) == 0) + { + reg >>= 1; + r++; + } + MicroProfileInsertRegisterJump(pInstructionMoveDest + nInstructionBytesDest, phome, r); + } + + // PATCH 1 TRAMP EXIT + intptr_t microprofile_tramp_exit = (intptr_t)ptramp + t_code_exit_offset; + memcpy(ptramp + t_enter_patch_offset + 2, (void*)µprofile_tramp_exit, 8); + + char* pintercept = t_code_intercept0_offset + ptramp; + + // PATCH 1.5 Argument + memcpy(pintercept - 4, (void*)&Argument, 4); + + // PATCH 2 INTERCEPT0 + intptr_t addr = (intptr_t)enter; //&intercept0; + memcpy(pintercept + 2, (void*)&addr, 8); + + // PATHC 2.5 argument + memcpy(ptramp + t_code_exit_offset + 3, (void*)&Argument, 4); + + intptr_t microprofile_tramp_leave = (intptr_t)ptramp + t_code_leave_offset; + // PATCH 3 INTERCEPT1 + intptr_t addr1 = (intptr_t)leave; //&intercept1; + memcpy((char*)microprofile_tramp_leave + 2, (void*)&addr1, 8); + + intptr_t patch_push_addr = (intptr_t)(&MicroProfile_Patch_TLS_PUSH); + intptr_t patch_pop_addr = (intptr_t)(&MicroProfile_Patch_TLS_POP); + memcpy((char*)ptramp + t_code_call_patch_push_offset + 2, &patch_push_addr, 8); + memcpy((char*)ptramp + t_code_call_patch_pop_offset + 2, &patch_pop_addr, 8); + + { + // PATCH 4 DEST FUNC + + MicroProfileMakeWriteable(f); + char* pp = (char*)f; + char* ppend = pp + nInstructionBytesSrc; + if(nInstructionBytesSrc < 14) + { + uprintf("inserting 5b jump\n"); + pp = MicroProfileInsertRelativeJump((char*)pp, (intptr_t)ptramp); + } + else + { + uprintf("inserting 14b jump\n"); + pp = MicroProfileInsertRegisterJump(pp, (intptr_t)ptramp, R_RAX); + } + while(pp != ppend) + { + *pp++ = 0x90; + } + } + return true; +} + +static void MicroProfileMakeWriteable(void* p_) +{ +#ifdef _PATCH_TEST + // for testing.. + static const uint32_t WritableSize = 16; + static uint32_t WritableCount = 0; + static intptr_t WritableStart[WritableSize] = { 0 }; + static intptr_t WritableEnd[WritableSize] = { 0 }; + for(uint32_t i = 0; i < WritableCount; ++i) + { + intptr_t x = (intptr_t)p_; + if(x >= WritableStart[i] && x < WritableEnd[i]) + { + return; + } + } + +#endif + + intptr_t p = (intptr_t)p_; + // uprintf("MicroProfilemakewriteable %lx\n", p); + mach_port_name_t task = mach_task_self(); + vm_map_offset_t vmoffset = 0; + mach_vm_size_t vmsize = 0; + uint32_t nd; + kern_return_t kr; + vm_region_submap_info_64 vbr; + mach_msg_type_number_t vbrcount = sizeof(vbr) / 4; + + while(KERN_SUCCESS == (kr = mach_vm_region_recurse(task, &vmoffset, &vmsize, &nd, (vm_region_recurse_info_t)&vbr, &vbrcount))) + { + if(p >= (intptr_t)vmoffset && p <= intptr_t(vmoffset + vmsize)) + { + if(0 == (vbr.protection & VM_PROT_WRITE)) + { + // uprintf("region match .. enabling write\n"); + int x = mprotect((void*)vmoffset, vmsize, PROT_WRITE | PROT_READ | PROT_EXEC); + if(x) + { + // uprintf("mprotect failed ... err %d:: %d %s\n", errno, x, strerror(errno)); + } + else + { + uprintf("region is [%llx,%llx] .. %08llx %d", vmoffset, vmoffset + vmsize, vmsize, vbr.is_submap); + uprintf("prot: %c%c%c %c%c%c\n", + vbr.protection & VM_PROT_READ ? 'r' : '-', + vbr.protection & VM_PROT_WRITE ? 'w' : '-', + vbr.protection & VM_PROT_EXECUTE ? 'x' : '-', + + vbr.max_protection & VM_PROT_READ ? 'r' : '-', + vbr.max_protection & VM_PROT_WRITE ? 'w' : '-', + vbr.max_protection & VM_PROT_EXECUTE ? 'x' : '-'); + continue; + } + } + else + { +#ifdef _PATCH_TEST + if(WritableCount < WritableSize) + { + WritableStart[WritableCount] = vmoffset; + WritableEnd[WritableCount] = vmoffset + vmsize; + WritableCount++; + } + +#endif + } + } + + vmoffset += vmsize; + vbrcount = sizeof(vbr) / 4; + } +} + +int MicroProfileTrimFunctionName(const char* pStr, char* pOutBegin, char* pOutEnd) +{ + int l = strlen(pStr) - 1; + int sz = 0; + pOutEnd--; + if(l < pOutEnd - pOutBegin && pOutBegin != pOutEnd) + { + const char* p = pStr; + const char* pEnd = pStr + l + 1; + int in = 0; + while(p != pEnd && pOutBegin != pOutEnd) + { + char c = *p++; + if(c == '(' || c == '<') + { + in++; + } + else if(c == ')' || c == '>') + { + in--; + continue; + } + + if(in == 0) + { + *pOutBegin++ = c; + sz++; + } + } + + *pOutBegin++ = '\0'; + } + return sz; +} + +int MicroProfileFindFunctionName(const char* pStr, const char** ppStart) +{ + int l = strlen(pStr) - 1; + if(l < 1024) + { + char b[1024] = { 0 }; + char* put = &b[0]; + + const char* p = pStr; + const char* pEnd = pStr + l + 1; + int in = 0; + while(p != pEnd) + { + char c = *p++; + if(c == '(' || c == '<') + { + in++; + } + else if(c == ')' || c == '>') + { + in--; + continue; + } + + if(in == 0) + { + *put++ = c; + } + } + + *put++ = '\0'; + uprintf("trimmed %s\n", b); + } + + // int nFirstParen = l; + int nNumParen = 0; + int c = 0; + + while(l >= 0 && pStr[l] != ')' && c++ < (int)(sizeof(" const") - 1)) + { + l--; + } + if(pStr[l] == ')') + { + do + { + if(pStr[l] == ')') + { + nNumParen++; + } + else if(pStr[l] == '(') + { + nNumParen--; + } + l--; + } while(nNumParen > 0 && l >= 0); + } + else + { + *ppStart = pStr; + return 0; + } + while(l >= 0 && isspace(pStr[l])) + { + --l; + } + int nLast = l; + while(l >= 0 && !isspace(pStr[l])) + { + l--; + } + int nFirst = l; + if(nFirst == nLast) + return 0; + int nCount = nLast - nFirst + 1; + *ppStart = pStr + nFirst; + return nCount; +} + +const char* MicroProfileDemangleSymbol(const char* pSymbol) +{ + static unsigned long size = 128; + static char* pTempBuffer = (char*)malloc(size); // needs to be malloc because demangle function might realloc it. + unsigned long len = size; + int ret = 0; + char* pBuffer = pTempBuffer; + pBuffer = abi::__cxa_demangle(pSymbol, pTempBuffer, &len, &ret); + if(ret == 0) + { + if(pBuffer != pTempBuffer) + { + pTempBuffer = pBuffer; + if(len < size) + __builtin_trap(); + size = len; + } + return pTempBuffer; + } + else + { + return pSymbol; + } +} + +template +void MicroProfileIterateSymbols(Callback CB, uint32_t* nModules, uint32_t nNumModules) +{ + MICROPROFILE_SCOPEI("MicroProfile", "MicroProfileIterateSymbols", MP_PINK3); + char FunctionName[1024]; + (void)FunctionName; + mach_port_name_t task = mach_task_self(); + vm_map_offset_t vmoffset = 0; + mach_vm_size_t vmsize = 0; + uint32_t nd; + kern_return_t kr; + vm_region_submap_info_64 vbr; + mach_msg_type_number_t vbrcount = sizeof(vbr) / 4; + + intptr_t nCurrentModule = -1; + uint32_t nCurrentModuleId = -1; + + auto OnFunction = [&](void* addr, void* addrend, const char* pSymbol, const char* pModuleName, void* pModuleAddr) -> bool + { + const char* pStr = MicroProfileDemangleSymbol(pSymbol); + ; + int l = MicroProfileTrimFunctionName(pStr, &FunctionName[0], &FunctionName[1024]); + if(nCurrentModule != (intptr_t)pModuleAddr) + { + nCurrentModule = (intptr_t)pModuleAddr; + nCurrentModuleId = MicroProfileSymbolGetModule(pModuleName, nCurrentModule); + } + + CB(l ? &FunctionName[0] : pStr, l ? &FunctionName[0] : 0, (intptr_t)addr, (intptr_t)addrend, nCurrentModuleId); + return true; + }; + vm_offset_t addr_prev = 0; + + while(KERN_SUCCESS == (kr = mach_vm_region_recurse(task, &vmoffset, &vmsize, &nd, (vm_region_recurse_info_t)&vbr, &vbrcount))) + { + { + addr_prev = vmoffset + vmsize; + if(0 != (vbr.protection & VM_PROT_EXECUTE)) + { + bool bProcessModule = true; + int nModule = -1; + if(nNumModules) + { + bProcessModule = false; + for(uint32_t i = 0; i < nNumModules; ++i) + { + intptr_t nBase = S.SymbolModules[nModules[i]].Regions[0].nBegin; + if((intptr_t)vmoffset == nBase) + { + bProcessModule = true; + nModule = nModules[i]; + break; + } + } + } + if(bProcessModule) + { + S.SymbolModules[nModule].nProgressTarget = S.SymbolModules[nModule].Regions[0].nEnd - S.SymbolModules[nModule].Regions[0].nBegin; + dl_info di; + int r = 0; + r = dladdr((void*)vmoffset, &di); + if(r) + { + OnFunction(di.dli_saddr, (void*)addr_prev, di.dli_sname, di.dli_fname, di.dli_fbase); + } + intptr_t addr = vmoffset + vmsize - 1; + while(1) + { + r = dladdr((void*)(addr), &di); + if(r) + { + if(!di.dli_sname) + { + break; + } + OnFunction(di.dli_saddr, (void*)addr_prev, di.dli_sname, di.dli_fname, di.dli_fbase); + } + else + { + break; + } + addr_prev = (vm_offset_t)di.dli_saddr; + addr = (intptr_t)di.dli_saddr - 1; + if(di.dli_saddr < (void*)vmoffset) + { + break; + } + } + for(int i = 0; i < S.SymbolNumModules; ++i) + { + if(S.SymbolModules[i].Regions[0].nBegin == (intptr_t)vmoffset) + { + S.SymbolModules[i].nModuleLoadFinished.store(1); + } + } + } + } + } + vmoffset += vmsize; + vbrcount = sizeof(vbr) / 4; + } +} + +void MicroProfileSymbolUpdateModuleList() +{ + char FunctionName[1024]; + (void)FunctionName; + mach_port_name_t task = mach_task_self(); + vm_map_offset_t vmoffset = 0; + mach_vm_size_t vmsize = 0; + uint32_t nd; + kern_return_t kr; + vm_region_submap_info_64 vbr; + mach_msg_type_number_t vbrcount = sizeof(vbr) / 4; + + while(KERN_SUCCESS == (kr = mach_vm_region_recurse(task, &vmoffset, &vmsize, &nd, (vm_region_recurse_info_t)&vbr, &vbrcount))) + { + { + if(0 != (vbr.protection & VM_PROT_EXECUTE)) + { + dl_info di; + int r = 0; + r = dladdr((void*)vmoffset, &di); + if(r) + { + uprintf("[0x%p-0x%p] (0x%p) %s %s\n", (void*)vmoffset, (void*)addr_prev, di.dli_fbase, di.dli_fname, di.dli_sname); + MicroProfileSymbolInitModule(di.dli_fname, (intptr_t)vmoffset, (intptr_t)vmoffset + vmsize); + } + } + } + vmoffset += vmsize; + vbrcount = sizeof(vbr) / 4; + } +} + +static void* MicroProfileAllocExecutableMemory(void* f, size_t s) +{ + static uint64_t nPageSize = 0; + if(!nPageSize) + { + nPageSize = getpagesize(); + } + s = (s + (nPageSize - 1)) & (~(nPageSize - 1)); + + void* pMem = mmap((void*)f, s, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_ANON | MAP_PRIVATE, 0, 0); + + // uprintf("Allocating %zu %p\n", s, pMem); + return pMem; +} + +bool MicroProfileDemangleName(const char* pName, char* OutName, uint32_t Size) +{ + // demangle not implemented + strcpy(OutName, pName); + return true; +} + +bool MicroProfilePatchBeginSuspend() +{ + // Not implemented + return true; +} + +void MicroProfilePatchEndSuspend() +{ + // Not implemented +} + +void MicroProfileInstrumentWithoutSymbols(const char** pModules, const char** pSymbols, uint32_t nNumSymbols) +{ + void* M = dlopen(0, 0); + for(uint32_t i = 0; i < nNumSymbols; ++i) + { + // uprintf("trying to find symbol %s\n", pSym); + void* s = dlsym(M, pSymbols[i]); + uprintf("sym returned %p\n", s); + if(s) + { + uint32_t nColor = MicroProfileColorFromString(pSymbols[i]); + const char* pDemangled = MicroProfileDemangleSymbol(pSymbols[i]); + MicroProfileInstrumentFunction(s, pModules[i], pDemangled, nColor); + } + } + dlclose(M); +} +#endif + +#if defined(__unix__) && defined(__x86_64__) +// '##::::'##::'#######:::'#######::'##:::'##::::'##:::::::'####:'##::: ##:'##::::'##:'##::::'##: +// ##:::: ##:'##.... ##:'##.... ##: ##::'##::::: ##:::::::. ##:: ###:: ##: ##:::: ##:. ##::'##:: +// ##:::: ##: ##:::: ##: ##:::: ##: ##:'##:::::: ##:::::::: ##:: ####: ##: ##:::: ##::. ##'##::: +// #########: ##:::: ##: ##:::: ##: #####::::::: ##:::::::: ##:: ## ## ##: ##:::: ##:::. ###:::: +// ##.... ##: ##:::: ##: ##:::: ##: ##. ##:::::: ##:::::::: ##:: ##. ####: ##:::: ##::: ## ##::: +// ##:::: ##: ##:::: ##: ##:::: ##: ##:. ##::::: ##:::::::: ##:: ##:. ###: ##:::: ##:: ##:. ##:: +// ##:::: ##:. #######::. #######:: ##::. ##:::: ########:'####: ##::. ##:. #######:: ##:::. ##: +// ..:::::..:::.......::::.......:::..::::..:::::........::....::..::::..:::.......:::..:::::..:: + +#include +#include +#include +#include +#include +#include + +static void* MicroProfileAllocExecutableMemory(void* f, size_t s); +static void MicroProfileMakeWriteable(void* p_); + +extern "C" void microprofile_tramp_enter_patch() asm("_microprofile_tramp_enter_patch"); +extern "C" void microprofile_tramp_enter() asm("_microprofile_tramp_enter"); +extern "C" void microprofile_tramp_code_begin() asm("_microprofile_tramp_code_begin"); +extern "C" void microprofile_tramp_code_end() asm("_microprofile_tramp_code_end"); +extern "C" void microprofile_tramp_intercept0() asm("_microprofile_tramp_intercept0"); +extern "C" void microprofile_tramp_end() asm("_microprofile_tramp_end"); +extern "C" void microprofile_tramp_exit() asm("_microprofile_tramp_exit"); +extern "C" void microprofile_tramp_leave() asm("_microprofile_tramp_leave"); +extern "C" void microprofile_tramp_trunk() asm("_microprofile_tramp_trunk"); +extern "C" void microprofile_tramp_call_patch_pop() asm("_microprofile_tramp_call_patch_pop"); +extern "C" void microprofile_tramp_call_patch_push() asm("_microprofile_tramp_call_patch_push"); + +bool MicroProfilePatchFunction(void* f, int Argument, MicroProfileHookFunc enter, MicroProfileHookFunc leave, MicroProfilePatchError* pError) +{ + if(pError) + { + memcpy(&pError->Code[0], f, 12); + } + + intptr_t t_enter = (intptr_t)microprofile_tramp_enter; + intptr_t t_enter_patch_offset = (intptr_t)microprofile_tramp_enter_patch - t_enter; + intptr_t t_code_begin_offset = (intptr_t)microprofile_tramp_code_begin - t_enter; + intptr_t t_code_end_offset = (intptr_t)microprofile_tramp_code_end - t_enter; + intptr_t t_code_intercept0_offset = (intptr_t)microprofile_tramp_intercept0 - t_enter; + intptr_t t_code_exit_offset = (intptr_t)microprofile_tramp_exit - t_enter; + intptr_t t_code_leave_offset = (intptr_t)microprofile_tramp_leave - t_enter; + + intptr_t t_code_call_patch_push_offset = (intptr_t)microprofile_tramp_call_patch_push - t_enter; + intptr_t t_code_call_patch_pop_offset = (intptr_t)microprofile_tramp_call_patch_pop - t_enter; + intptr_t codemaxsize = t_code_end_offset - t_code_begin_offset; + intptr_t t_end_offset = (intptr_t)microprofile_tramp_end - t_enter; + intptr_t t_trunk_offset = (intptr_t)microprofile_tramp_trunk - t_enter; + intptr_t t_trunk_size = (intptr_t)microprofile_tramp_end - (intptr_t)microprofile_tramp_trunk; + + char* ptramp = (char*)MicroProfileAllocExecutableMemory(f, t_end_offset); + + intptr_t offset = ((intptr_t)f + 6 - (intptr_t)ptramp); + + uint32_t nBytesToCopy = 14; + if(offset < 0x80000000 && offset > -0x7fffffff) + { + /// offset is small enough to insert a relative jump + nBytesToCopy = 5; + } + + memcpy(ptramp, (void*)t_enter, t_end_offset); + + int nInstructionBytesDest = 0; + char* pInstructionMoveDest = ptramp + t_code_begin_offset; + char* pTrunk = ptramp + t_trunk_offset; + + int nInstructionBytesSrc = 0; + + uint32_t nRegsWritten = 0; + uint32_t nRetSafe = 0; + uint32_t nUsableJumpRegs = (1 << R_RAX) | (1 << R_R10) | (1 << R_R11); // scratch && !parameter register + if(!MicroProfileCopyInstructionBytes( + pInstructionMoveDest, f, nBytesToCopy, codemaxsize, pTrunk, t_trunk_size, nUsableJumpRegs, &nInstructionBytesDest, &nInstructionBytesSrc, &nRegsWritten, &nRetSafe)) + { + if(pError) + { + const char* pCode = (const char*)f; + memset(pError->Code, 0, sizeof(pError->Code)); + memcpy(pError->Code, pCode, nInstructionBytesSrc); + int off = stbsp_snprintf(pError->Message, sizeof(pError->Message), "Failed to move %d code bytes ", nInstructionBytesSrc); + pError->nCodeSize = nInstructionBytesSrc; + for(int i = 0; i < nInstructionBytesSrc; ++i) + { + off += stbsp_snprintf(off + pError->Message, sizeof(pError->Message) - off, "%02x ", 0xff & pCode[i]); + } + uprintf("%s\n", pError->Message); + } + return false; + } + intptr_t phome = nInstructionBytesSrc + (intptr_t)f; + uint32_t reg = nUsableJumpRegs & ~nRegsWritten; + static_assert(R_RAX == 0, "R_RAX must be 0"); + if(0 == reg) + { + if(nRetSafe == 0) + { + MP_BREAK(); // shout fail earlier + } + MicroProfileInsertRetJump(pInstructionMoveDest + nInstructionBytesDest, phome); + } + else + { + int r = R_RAX; + while((reg & 1) == 0) + { + reg >>= 1; + r++; + } + MicroProfileInsertRegisterJump(pInstructionMoveDest + nInstructionBytesDest, phome, r); + } + + // PATCH 1 TRAMP EXIT + intptr_t microprofile_tramp_exit = (intptr_t)ptramp + t_code_exit_offset; + memcpy(ptramp + t_enter_patch_offset + 2, (void*)µprofile_tramp_exit, 8); + + char* pintercept = t_code_intercept0_offset + ptramp; + + // PATCH 1.5 Argument + memcpy(pintercept - 4, (void*)&Argument, 4); + + // PATCH 2 INTERCEPT0 + intptr_t addr = (intptr_t)enter; //&intercept0; + memcpy(pintercept + 2, (void*)&addr, 8); + + // PATHC 2.5 argument + memcpy(ptramp + t_code_exit_offset + 3, (void*)&Argument, 4); + + intptr_t microprofile_tramp_leave = (intptr_t)ptramp + t_code_leave_offset; + // PATCH 3 INTERCEPT1 + intptr_t addr1 = (intptr_t)leave; //&intercept1; + memcpy((char*)microprofile_tramp_leave + 2, (void*)&addr1, 8); + + intptr_t patch_push_addr = (intptr_t)(&MicroProfile_Patch_TLS_PUSH); + intptr_t patch_pop_addr = (intptr_t)(&MicroProfile_Patch_TLS_POP); + memcpy((char*)ptramp + t_code_call_patch_push_offset + 2, &patch_push_addr, 8); + memcpy((char*)ptramp + t_code_call_patch_pop_offset + 2, &patch_pop_addr, 8); + + { + // PATCH 4 DEST FUNC + + MicroProfileMakeWriteable(f); + char* pp = (char*)f; + char* ppend = pp + nInstructionBytesSrc; + + if(nInstructionBytesSrc < 14) + { + uprintf("inserting 5b jump\n"); + pp = MicroProfileInsertRelativeJump((char*)pp, (intptr_t)ptramp); + } + else + { + uprintf("inserting 14b jump\n"); + pp = MicroProfileInsertRegisterJump(pp, (intptr_t)ptramp, R_RAX); + } + while(pp != ppend) + { + *pp++ = 0x90; + } + } + return true; +} + +static void MicroProfileMakeWriteable(void* p_) +{ + intptr_t nPageSize = (intptr_t)getpagesize(); + intptr_t p = ((intptr_t)p_) & ~(nPageSize - 1); + intptr_t e = nPageSize + ((14 + (intptr_t)p_) & ~(nPageSize - 1)); + size_t s = e - p; + mprotect((void*)p, s, PROT_READ | PROT_WRITE | PROT_EXEC); +} + +int MicroProfileTrimFunctionName(const char* pStr, char* pOutBegin, char* pOutEnd) +{ + int l = strlen(pStr) - 1; + int sz = 0; + pOutEnd--; + if(l < pOutEnd - pOutBegin && pOutBegin != pOutEnd) + { + const char* p = pStr; + const char* pEnd = pStr + l + 1; + int in = 0; + while(p != pEnd && pOutBegin != pOutEnd) + { + char c = *p++; + if(c == '(' || c == '<') + { + in++; + } + else if(c == ')' || c == '>') + { + in--; + continue; + } + + if(in == 0) + { + *pOutBegin++ = c; + sz++; + } + } + + *pOutBegin++ = '\0'; + } + return sz; +} + +int MicroProfileFindFunctionName(const char* pStr, const char** ppStart) +{ + int l = strlen(pStr) - 1; + if(l < 1024) + { + char b[1024] = { 0 }; + char* put = &b[0]; + + const char* p = pStr; + const char* pEnd = pStr + l + 1; + int in = 0; + while(p != pEnd) + { + char c = *p++; + if(c == '(' || c == '<') + { + in++; + } + else if(c == ')' || c == '>') + { + in--; + continue; + } + + if(in == 0) + { + *put++ = c; + } + } + + *put++ = '\0'; + uprintf("trimmed %s\n", b); + } + + // int nFirstParen = l; + int nNumParen = 0; + int c = 0; + + while(l >= 0 && pStr[l] != ')' && c++ < (int)(sizeof(" const") - 1)) + { + l--; + } + if(pStr[l] == ')') + { + do + { + if(pStr[l] == ')') + { + nNumParen++; + } + else if(pStr[l] == '(') + { + nNumParen--; + } + l--; + } while(nNumParen > 0 && l >= 0); + } + else + { + *ppStart = pStr; + return 0; + } + while(l >= 0 && isspace(pStr[l])) + { + --l; + } + int nLast = l; + while(l >= 0 && !isspace(pStr[l])) + { + l--; + } + int nFirst = l; + if(nFirst == nLast) + return 0; + int nCount = nLast - nFirst + 1; + *ppStart = pStr + nFirst; + return nCount; +} + +const char* MicroProfileDemangleSymbol(const char* pSymbol) +{ + static unsigned long size = 128; + static char* pTempBuffer = (char*)malloc(size); // needs to be malloc because demangle function might realloc it. + unsigned long len = size; + int ret = 0; + char* pBuffer = pTempBuffer; + pBuffer = abi::__cxa_demangle(pSymbol, pTempBuffer, &len, &ret); + if(ret == 0) + { + if(pBuffer != pTempBuffer) + { + pTempBuffer = pBuffer; + if(len < size) + __builtin_trap(); + size = len; + } + return pTempBuffer; + } + else + { + return pSymbol; + } +} + +template +void MicroProfileIterateSymbols(Callback CB, uint32_t* nModules, uint32_t nNumModules) +{ + MICROPROFILE_SCOPEI("MicroProfile", "MicroProfileIterateSymbols", MP_PINK3); + char FunctionName[1024]; + + intptr_t nCurrentModule = -1; + uint32_t nCurrentModuleId = -1; + + auto OnFunction = [&](void* addr, void* addrend, const char* pSymbol, const char* pModuleName, void* pModuleAddr) -> bool + { + const char* pStr = MicroProfileDemangleSymbol(pSymbol); + ; + int l = MicroProfileTrimFunctionName(pStr, &FunctionName[0], &FunctionName[1024]); + MP_ASSERT(nCurrentModule == (intptr_t)pModuleAddr); + CB(l ? &FunctionName[0] : pStr, l ? &FunctionName[0] : 0, (intptr_t)addr, (intptr_t)addrend, nCurrentModuleId); + return true; + }; + + for(int i = 0; i < S.SymbolNumModules; ++i) + { + auto& M = S.SymbolModules[i]; + if(0 != nNumModules) + { + bool bProcess = false; + for(uint32_t j = 0; j < nNumModules; ++j) + { + if(nModules[j] == (uint32_t)i) + { + bProcess = true; + break; + } + } + if(!bProcess) + continue; + } + nCurrentModuleId = i; + Dl_info di; + int r = 0; + r = dladdr((void*)(M.Regions[0].nBegin), &di); + if(r) + { + nCurrentModule = (intptr_t)di.dli_fbase; + M.nProgressTarget = 0; + for(int j = 0; j < M.nNumExecutableRegions; ++j) + { + M.nProgressTarget += M.Regions[j].nEnd - M.Regions[j].nBegin; + } + for(int j = 0; j < M.nNumExecutableRegions; ++j) + { + const intptr_t nBegin = M.Regions[j].nBegin; + const intptr_t nEnd = M.Regions[j].nEnd; + int r = 0; + intptr_t nAddr = (nEnd - 8) & ~7; + intptr_t nAddrPrev = nEnd; + while(1) + { + r = dladdr((void*)(nAddr), &di); + if(r && di.dli_sname) + { + OnFunction(di.dli_saddr, (void*)nAddrPrev, di.dli_sname, di.dli_fname, di.dli_fbase); + nAddrPrev = (intptr_t)di.dli_saddr; + nAddr = (intptr_t)di.dli_saddr - 1; + } + else + { + nAddr = (nAddr - 7) & ~7; // pretty ineffecient, but it seems linux just returns 0 when there is no symbols, making this the only option I can come up with? + } + if(nAddr < nBegin) + { + break; + } + } + } + M.nProgress = M.nProgressTarget; + M.nModuleLoadFinished.store(1); + } + } +} + +void MicroProfileSymbolUpdateModuleList() +{ + // So, this was the only way I could find to do this.. + // Is this seriously how they want this to be done? + FILE* F = fopen("/proc/self/maps", "r"); + char* line = 0; + size_t len; + ssize_t read; + Dl_info di; + while((read = getline(&line, &len, F)) != -1) + { + void* pBase = 0; + void* pEnd = 0; + char c, r, w, x, p; + + if(8 == sscanf(line, "%p%c%p%c%c%c%c%c", &pBase, &c, &pEnd, &c, &r, &w, &x, &p)) + { + if('x' == x) + { + int r = 0; + r = dladdr(pBase, &di); + if(r) + { + if('[' != di.dli_fname[0]) + { + MicroProfileSymbolInitModule(di.dli_fname, (intptr_t)pBase, (intptr_t)pEnd); + } + } + } + } + } + fclose(F); + MicroProfileSymbolMergeExecutableRegions(); +} + +static void* MicroProfileAllocExecutableMemory(void* f, size_t s) +{ + static uint64_t nPageSize = 0; + if(!nPageSize) + { + nPageSize = getpagesize(); + } + s = (s + (nPageSize - 1)) & (~(nPageSize - 1)); + + void* pMem = mmap(f, s, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_ANON | MAP_PRIVATE, 0, 0); + return pMem; +} + +bool MicroProfileDemangleName(const char* pName, char* OutName, uint32_t Size) +{ + // demangle not implemented + strcpy(OutName, pName); + return true; +} + +bool MicroProfilePatchBeginSuspend() +{ + // Not implemented + return true; +} + +void MicroProfilePatchEndSuspend() +{ + // Not implemented +} + +// not yet tested. +void MicroProfileInstrumentWithoutSymbols(const char** pModules, const char** pSymbols, uint32_t nNumSymbols) +{ + void* M = dlopen(0, 0); + for(uint32_t i = 0; i < nNumSymbols; ++i) + { + // uprintf("trying to find symbol %s\n", pSym); + void* s = dlsym(M, pSymbols[i]); + uprintf("sym returned %p\n", s); + if(s) + { + uint32_t nColor = MicroProfileColorFromString(pSymbols[i]); + const char* pDemangled = MicroProfileDemangleSymbol(pSymbols[i]); + MicroProfileInstrumentFunction(s, pModules[i], pDemangled, nColor); + } + } + dlclose(M); +} + +#endif + +#endif + +void MicroProfileHashTableInit(MicroProfileHashTable* pTable, uint32_t nInitialSize, uint32_t nSearchLimit, MicroProfileHashCompareFunction CompareFunc, MicroProfileHashFunction HashFunc) +{ + pTable->nAllocated = nInitialSize; + pTable->nUsed = 0; + uint32_t nSize = nInitialSize * sizeof(MicroProfileHashTableEntry); + pTable->pEntries = (MicroProfileHashTableEntry*)MICROPROFILE_ALLOC(nSize, 8); + pTable->CompareFunc = CompareFunc; + pTable->HashFunc = HashFunc; + pTable->nSearchLimit = nSearchLimit; + pTable->nLim = pTable->nAllocated / 5; + if(pTable->nLim > pTable->nSearchLimit) + pTable->nLim = pTable->nSearchLimit; + memset(pTable->pEntries, 0, nSize); +} +void MicroProfileHashTableDestroy(MicroProfileHashTable* pTable) +{ + MICROPROFILE_FREE(pTable->pEntries); +} + +uint64_t MicroProfileHashTableHash(MicroProfileHashTable* pTable, uint64_t K) +{ + uint64_t H = pTable->HashFunc ? (*pTable->HashFunc)(K) : K; + return H == 0 ? 1 : H; +} + +void MicroProfileHashTableGrow(MicroProfileHashTable* pTable) +{ + uint32_t nAllocated = pTable->nAllocated; + uint32_t nNewSize = nAllocated * 2; + uprintf("GROW %d -> %d\n", nAllocated, nNewSize); + + MicroProfileHashTable New; + MicroProfileHashTableInit(&New, nNewSize, pTable->nSearchLimit, pTable->CompareFunc, pTable->HashFunc); + for(uint32_t i = 0; i < nAllocated; ++i) + { + MicroProfileHashTableEntry& E = pTable->pEntries[i]; + if(E.Hash != 0) + { + MicroProfileHashTableSet(&New, E.Key, E.Value, E.Hash, false); + } + } + MicroProfileHashTableDestroy(pTable); + *pTable = New; +} + +bool MicroProfileHashTableSet(MicroProfileHashTable* pTable, uint64_t Key, uintptr_t Value) +{ + uint64_t H = MicroProfileHashTableHash(pTable, Key); + return MicroProfileHashTableSet(pTable, Key, Value, H, true); +} + +MicroProfileHashTableIterator MicroProfileGetHashTableIteratorBegin(MicroProfileHashTable* HashTable) +{ + return MicroProfileHashTableIterator(0, HashTable); +} + +MicroProfileHashTableIterator MicroProfileGetHashTableIteratorEnd(MicroProfileHashTable* HashTable) +{ + return MicroProfileHashTableIterator(HashTable->nAllocated, HashTable); +} + +bool MicroProfileHashTableSet(MicroProfileHashTable* pTable, uint64_t Key, uintptr_t Value, uint64_t H, bool bAllowGrow) +{ + if(H == 0) + MP_BREAK(); // not supported. + MicroProfileHashCompareFunction Cmp = pTable->CompareFunc; + while(1) + { + const uint32_t nLim = pTable->nLim; + uint32_t B = H % pTable->nAllocated; + MicroProfileHashTableEntry* pEntries = pTable->pEntries; + + for(uint32_t i = 0; i < pTable->nAllocated; ++i) + { + uint32_t Idx = (B + i) % pTable->nAllocated; + if(pEntries[Idx].Hash == 0) + { + pEntries[Idx].Hash = H; + pEntries[Idx].Key = Key; + pEntries[Idx].Value = Value; + return true; + } + else if(pEntries[Idx].Hash == H && (Cmp ? (Cmp)(Key, pEntries[Idx].Key) : Key == pEntries[Idx].Key)) + { + pEntries[Idx].Value = Value; + return true; + } + else if(i > nLim) + { + break; + } + } + if(bAllowGrow) + { + MicroProfileHashTableGrow(pTable); + } + else + { + MP_BREAK(); + } + } + MP_BREAK(); +} + +bool MicroProfileHashTableGet(MicroProfileHashTable* pTable, uint64_t Key, uintptr_t* pValue) +{ + uint64_t H = MicroProfileHashTableHash(pTable, Key); + uint32_t B = H % pTable->nAllocated; + MicroProfileHashTableEntry* pEntries = pTable->pEntries; + MicroProfileHashCompareFunction Cmp = pTable->CompareFunc; + for(uint32_t i = 0; i < pTable->nAllocated; ++i) + { + uint32_t Idx = (B + i) % pTable->nAllocated; + if(pEntries[Idx].Hash == 0) + { + return false; + } + else if(pEntries[Idx].Hash == H && (Cmp ? (Cmp)(Key, pEntries[Idx].Key) : Key == pEntries[Idx].Key)) + { + *pValue = pEntries[Idx].Value; + return true; + } + } + return false; +} + +bool MicroProfileHashTableRemove(MicroProfileHashTable* pTable, uint64_t Key) +{ + + uint64_t H = MicroProfileHashTableHash(pTable, Key); + uint32_t B = H % pTable->nAllocated; + MicroProfileHashTableEntry* pEntries = pTable->pEntries; + MicroProfileHashCompareFunction Cmp = pTable->CompareFunc; + uint32_t nBase = (uint32_t)-1; + uint32_t nAllocated = pTable->nAllocated; + for(uint32_t i = 0; i < nAllocated; ++i) + { + uint32_t Idx = (B + i) % nAllocated; + if(pEntries[Idx].Hash == 0) + { + return false; + } + else if(pEntries[Idx].Hash == H && (Cmp ? (Cmp)(Key, pEntries[Idx].Key) : Key == pEntries[Idx].Key)) + { + nBase = Idx; + break; + } + } + pEntries[nBase].Hash = 0; + pEntries[nBase].Key = 0; + pEntries[nBase].Value = 0; + nBase++; + for(uint32_t i = 0; i < nAllocated; ++i) + { + uint32_t Idx = (nBase + i) % nAllocated; + if(pEntries[Idx].Hash == 0) + { + break; + } + else + { + MicroProfileHashTableEntry E = pEntries[Idx]; + pEntries[Idx] = {}; + MicroProfileHashTableSet(pTable, E.Key, E.Value, E.Hash, false); + } + } + return true; +} +uint64_t MicroProfileHashTableHashString(uint64_t pString) +{ + return MicroProfileStringHash((const char*)pString); +} + +bool MicroProfileHashTableCompareString(uint64_t L, uint64_t R) +{ + return 0 == strcmp((const char*)L, (const char*)R); +} +uint64_t MicroProfileHashTableHashPtr(uint64_t x) +{ + x ^= x >> 33; + x *= 0xff51afd7ed558ccdULL; + x ^= x >> 33; + x *= 0xc4ceb9fe1a85ec53ULL; + x ^= x >> 33; + return x; +} +bool MicroProfileHashTableComparePtr(uint64_t L, uint64_t R) +{ + return L == R; +} + +bool MicroProfileHashTableSetString(MicroProfileHashTable* pTable, const char* pKey, const char* pValue) +{ + return MicroProfileHashTableSet(pTable, (uint64_t)pKey, (uintptr_t)pValue); +} + +bool MicroProfileHashTableGetString(MicroProfileHashTable* pTable, const char* pKey, const char** pValue) +{ + return MicroProfileHashTableGet(pTable, (uint64_t)pKey, (uintptr_t*)pValue); +} + +bool MicroProfileHashTableRemoveString(MicroProfileHashTable* pTable, const char* pKey) +{ + return MicroProfileHashTableRemove(pTable, (uint64_t)pKey); +} + +bool MicroProfileHashTableSetPtr(MicroProfileHashTable* pTable, const void* pKey, void* pValue) +{ + return MicroProfileHashTableSet(pTable, (uint64_t)pKey, (uintptr_t)pValue); +} + +template +bool MicroProfileHashTableGetPtr(MicroProfileHashTable* pTable, const void* pKey, T** pValue) +{ + uintptr_t Dummy; + uintptr_t* Arg = pValue ? (uintptr_t*)pValue : &Dummy; + return MicroProfileHashTableGet(pTable, (uint64_t)pKey, Arg); +} + +bool MicroProfileHashTableRemovePtr(MicroProfileHashTable* pTable, const char* pKey) +{ + return MicroProfileHashTableRemove(pTable, (uint64_t)pKey); +} + +template +T& MicroProfileArray::operator[](const uint32_t Index) +{ + return Data[Index]; +} + +template +const T& MicroProfileArray::operator[](const uint32_t Index) const +{ + MP_ASSERT(Index < Size); + return Data[Index]; +} +template +T* MicroProfileArray::begin() +{ + return Data; +} +template +T* MicroProfileArray::end() +{ + return Data + Size; +} + +template +void MicroProfileArrayInit(MicroProfileArray& Array, uint32_t InitialCapacity) +{ + MP_ASSERT(Array.Data == nullptr); + MP_ASSERT(Array.Size == 0); + MP_ASSERT(Array.Capacity == 0); + Array.Capacity = InitialCapacity; + Array.Data = MP_ALLOC_OBJECT_ARRAY(T, InitialCapacity); + Array.Size = 0; +} +template +void MicroProfileArrayDestroy(MicroProfileArray& Array, uint32_t InitialCapacity) +{ + if(Array.Data) + MP_FREE(Array.Data); + memset(Array, 0, sizeof(*Array)); +} +template +void MicroProfileArrayClear(MicroProfileArray& Array) +{ + Array.Size = 0; +} + +template +void MicroProfileArrayPushBack(MicroProfileArray& Array, const T& v) +{ + uint32_t& Size = Array.Size; + uint32_t& Capacity = Array.Capacity; + if(Size >= Capacity) + { + uint32_t NewCapacity = (MicroProfileMax(1u, Capacity) + 1) * 3 / 2; + T* NewData = MP_ALLOC_OBJECT_ARRAY(T, NewCapacity); + memcpy(NewData, Array.Data, Size * sizeof(T)); + if(Array.Data) + { + MP_FREE(Array.Data); + } + Array.Data = NewData; + Capacity = NewCapacity; + } + Array.Data[Size++] = v; +} + +void MicroProfileStringBlockFree(MicroProfileStringBlock* pBlock) +{ + MicroProfileCounterAdd(S.CounterToken_StringBlock_Count, -1); + MicroProfileCounterAdd(S.CounterToken_StringBlock_Memory, -(int64_t)(pBlock->nSize + sizeof(MicroProfileStringBlock))); + + MP_FREE(pBlock); +} +MicroProfileStringBlock* MicroProfileStringBlockAlloc(uint32_t nSize) +{ + nSize = MicroProfileMax(nSize, (uint32_t)(MicroProfileStringBlock::DEFAULT_SIZE - sizeof(MicroProfileStringBlock))); + nSize += sizeof(MicroProfileStringBlock); + MicroProfileCounterAdd(S.CounterToken_StringBlock_Count, 1); + MicroProfileCounterAdd(S.CounterToken_StringBlock_Memory, nSize); + // uprintf("alloc string block %d sizeof strings is %d\n", nSize, (int)sizeof(MicroProfileStringBlock)); + MicroProfileStringBlock* pBlock = (MicroProfileStringBlock*)MP_ALLOC(nSize, 8); + pBlock->pNext = 0; + pBlock->nSize = nSize - sizeof(MicroProfileStringBlock); + pBlock->nUsed = 0; + return pBlock; +} + +void MicroProfileStringsInit(MicroProfileStrings* pStrings) +{ + MicroProfileHashTableInit(&pStrings->HashTable, 1, 25, MicroProfileHashTableCompareString, MicroProfileHashTableHashString); + pStrings->pFirst = 0; + pStrings->pLast = 0; +} +void MicroProfileStringsDestroy(MicroProfileStrings* pStrings) +{ + MicroProfileStringBlock* pBlock = pStrings->pFirst; + while(pBlock) + { + MicroProfileStringBlock* pNext = pBlock->pNext; + MicroProfileStringBlockFree(pBlock); + pBlock = pNext; + } + MicroProfileCounterSet(S.CounterToken_StringBlock_Waste, 0); + MicroProfileCounterSet(S.CounterToken_StringBlock_Strings, 0); + + memset(pStrings, 0, sizeof(*pStrings)); +} + +const char* MicroProfileStringIntern(const char* pStr) +{ + return MicroProfileStringIntern(pStr, (uint32_t)strlen(pStr), 0); +} + +const char* MicroProfileStringInternLower(const char* pStr) +{ + return MicroProfileStringIntern(pStr, (uint32_t)strlen(pStr), ESTRINGINTERN_LOWERCASE); +} +const char* MicroProfileStringInternSlash(const char* pStr) +{ + return MicroProfileStringIntern(pStr, (uint32_t)strlen(pStr), ESTRINGINTERN_FORCEFORWARDSLASH); +} + +const char* MicroProfileStringIntern(const char* pStr_, uint32_t nLen, uint32_t nFlags) +{ + MicroProfileStrings* pStrings = &S.Strings; + const char* pStr = pStr_; + char* pLowerCaseStr = (char*)alloca(nLen + 1); + if(0 != (nFlags & (ESTRINGINTERN_FORCEFORWARDSLASH | ESTRINGINTERN_LOWERCASE))) + { + for(uint32_t i = 0; i < nLen; ++i) + { + char c = pStr[i]; + if(nFlags & ESTRINGINTERN_LOWERCASE) + { + c = tolower(c); + } + if(nFlags & ESTRINGINTERN_FORCEFORWARDSLASH) + { + if(c == '\\') + c = '/'; + } + pLowerCaseStr[i] = c; + } + pLowerCaseStr[nLen] = '\0'; + pStr = pLowerCaseStr; + } + const char* pRet; + if(MicroProfileHashTableGetString(&pStrings->HashTable, pStr, &pRet)) + { + if(0 != strcmp(pStr, pRet)) + { + MP_BREAK(); + } + return pRet; + } + else + { + if(pStr[nLen] != '\0') + MP_BREAK(); // string should be 0 terminated. + nLen += 1; + MicroProfileStringBlock* pBlock = pStrings->pLast; + if(0 == pBlock || pBlock->nUsed + nLen > pBlock->nSize) + { + MicroProfileStringBlock* pNewBlock = MicroProfileStringBlockAlloc(nLen); + if(pBlock) + { + pBlock->pNext = pNewBlock; + pStrings->pLast = pNewBlock; + MicroProfileCounterAdd(S.CounterToken_StringBlock_Waste, pBlock->nSize - pBlock->nUsed); + } + else + { + pStrings->pLast = pStrings->pFirst = pNewBlock; + } + pBlock = pNewBlock; + } + MicroProfileCounterAdd(S.CounterToken_StringBlock_Strings, 1); + char* pDest = &pBlock->Memory[pBlock->nUsed]; + pBlock->nUsed += nLen; + MP_ASSERT(pBlock->nUsed <= pBlock->nSize); + memcpy(pDest, pStr, nLen); + MicroProfileHashTableSetString(&pStrings->HashTable, pDest, pDest); + +#if 0 + void DumpTableStr(MicroProfileHashTable* pTable); + DumpTableStr(&pStrings->HashTable); +#endif + + return pDest; + } +} + +void DumpTable(MicroProfileHashTable* pTable) +{ + for(uint32_t i = 0; i < pTable->nAllocated; ++i) + { + if(pTable->pEntries[i].Hash != 0) + { + uprintf("[%05d,%05" PRIu64 "] ::::%" PRIx64 ", %p .. hash %" PRIx64 "\n", + i, + pTable->pEntries[i].Hash % pTable->nAllocated, + pTable->pEntries[i].Key, + (void*)pTable->pEntries[i].Value, + pTable->pEntries[i].Hash); + } + } +}; +void DumpTableStr(MicroProfileHashTable* pTable) +{ + int c = 0; + (void)c; + for(uint32_t i = 0; i < pTable->nAllocated; ++i) + { + if(pTable->pEntries[i].Hash != 0) + { + uprintf("%03d [%05d,%05" PRIu64 "] ::::%s, %s .. hash %" PRIx64 "\n", + c++, + i, + pTable->pEntries[i].Hash % pTable->nAllocated, + (const char*)pTable->pEntries[i].Key, + (const char*)pTable->pEntries[i].Value, + pTable->pEntries[i].Hash); + } + } + uprintf("FillPrc %f\n", 100.f * c / (float)pTable->nAllocated); +}; + +static const char* txt[] = { "gaudy", "chilly", "obtain", "suspend", "jelly", "peel", "nauseating", "complain", "cave", "practise", "sail", "close", + "drawer", "mature", "impossible", "exist", "sister", "poke", "ancient", "paddle", "ask", "shallow", "outrageous", "healthy", + "reading", "obey", "water", "elbow", "abnormal", "trap", "wholesale", "lovely", "stupid", "comparison", "swim", "brash", + "towering", "accept", "invention", "plantation", "spooky", "tiger", "knot", "literate", "awake", "itch", "medical", "ticket", + "tawdry", "correct", "mine", "accidental", "dinner", "produce", "protective", "red", "dreary", "toe", "drain", "zesty", + "inform", "boundless", "ghost", "attend", "rely", "fill", "liquid", "pump", "continue", "spark", "church", "fortunate", + "truthful", "conscious", "possible", "motion", "evanescent", "branch", "skirt", "number", "meek", "hour", "form", "work", + "car", "post", "talk", "fear", "tightfisted", "dress", "perform", "fry", "courageous", "dysfunctional", "page", "one", + "annoy", "abrasive", "dependent", "payment" }; + +void MicroProfileStringInternTest() +{ + MicroProfileStringsInit(&S.Strings); + uint32_t nCount = sizeof(txt) / sizeof(txt[0]); + const char* pStrings[100]; + const char* pStrings2[100]; + + DumpTableStr(&S.Strings.HashTable); + for(uint32_t i = 0; i < nCount; ++i) + { + pStrings[i] = MicroProfileStringIntern(txt[i]); + pStrings2[i] = MicroProfileStrDup(txt[i]); + } + + for(uint32_t i = 0; i < nCount; ++i) + { + const char* pStr = MicroProfileStringIntern(pStrings2[i]); + if(pStr != pStrings[i]) + { + MP_BREAK(); + } + } + DumpTableStr(&S.Strings.HashTable); + + MicroProfileStringsDestroy(&S.Strings); +} + +void MicroProfileHashTableTest() +{ + MicroProfileStringInternTest(); + + MicroProfileHashTable T; + MicroProfileHashTable* pTable = &T; + MicroProfileHashTableInit(pTable, 1, 100, 0, 0); + +#define NUM_ITEMS 100 + + uint64_t Keys[NUM_ITEMS]; + uint64_t Values[NUM_ITEMS]; + memset(Keys, 0xff, sizeof(Keys)); + memset(Values, 0xff, sizeof(Values)); + + static int l = 0; + auto RR = [&]() -> uint64_t + { + if(l++ % 4 < 2) + { + return l; + } + uint64_t l2 = rand(); + uint64_t u = rand(); + return l2 | (u << 32); + }; + auto RRUnique = [&]() + { + bool bFound = false; + uint64_t V = 0; + do + { + V = RR(); + for(uint32_t i = 0; i != NUM_ITEMS; ++i) + { + if(V == Keys[i]) + { + bFound = true; + } + } + if(!bFound) + { + return V; + } + } while(bFound); + MP_BREAK(); + return (uint64_t)0; + }; + + Keys[0] = 0; + Values[0] = 42; + for(uint32_t i = 1; i < NUM_ITEMS; ++i) + { + Keys[i] = RRUnique(); + Values[i] = RR(); + } + + for(uint32_t i = 0; i < NUM_ITEMS; ++i) + { + MicroProfileHashTableSet(pTable, Keys[i], Values[i]); + } + + for(uint32_t i = 0; i < NUM_ITEMS; ++i) + { + uintptr_t V; + if(MicroProfileHashTableGet(pTable, Keys[i], &V)) + { + if(V != Values[i]) + { + MP_BREAK(); + } + } + else + { + MP_BREAK(); + } + uint64_t nonkey = RRUnique(); + if(MicroProfileHashTableGet(pTable, nonkey, &V)) + { + MP_BREAK(); + } + } + + DumpTable(pTable); + if(!MicroProfileHashTableRemove(pTable, 0)) + { + MP_BREAK(); + } + uprintf("removed\n"); + DumpTable(pTable); + uintptr_t v; + if(MicroProfileHashTableGet(pTable, 0, &v)) + { + MP_BREAK(); + } + if(MicroProfileHashTableGet(pTable, 1, &v)) + { + if(v != 2) + MP_BREAK(); + } + + MicroProfileHashTableDestroy(pTable); + + MicroProfileHashTable Strings; + MicroProfileHashTableInit(&Strings, 1, 25, MicroProfileHashTableCompareString, MicroProfileHashTableHashString); + uint32_t nCount = sizeof(txt) / sizeof(txt[0]); + for(uint32_t i = 0; i < nCount; i += 2) + { + MicroProfileHashTableSetString(&Strings, txt[i], txt[i + 1]); + } + DumpTableStr(&Strings); + + for(uint32_t i = 0; i < nCount; i += 2) + { + const char* pKey = txt[i]; + const char* pValue = txt[i + 1]; + const char* pRes = 0; + if(MicroProfileHashTableGetString(&Strings, pKey, &pRes)) + { + if(pRes != pValue) + { + MP_BREAK(); + } + } + else + { + MP_BREAK(); + } + } + uint32_t nRem = nCount / 2; + for(uint32_t i = 0; i < nRem; i += 2) + { + const char* pKey = txt[i]; + const char* pValue = txt[i + 1]; + + if(!MicroProfileHashTableRemoveString(&Strings, pKey)) + { + MP_BREAK(); + } + if(MicroProfileHashTableRemoveString(&Strings, pValue)) + { + MP_BREAK(); + } + } + for(uint32_t i = 0; i < nRem; i += 2) + { + const char* pKey = txt[i]; + if(MicroProfileHashTableRemoveString(&Strings, pKey)) + { + MP_BREAK(); + } + } + + for(uint32_t i = 0; i < nCount; i += 2) + { + const char* pKey = txt[i]; + const char* pValue = txt[i + 1]; + const char* V; + if(MicroProfileHashTableGetString(&Strings, pKey, &V)) + { + if(i < nRem) + { + MP_BREAK(); + } + else + { + if(V != pValue) + MP_BREAK(); + } + } + else + { + if(i >= nRem) + MP_BREAK(); + } + } + + DumpTableStr(&Strings); + MicroProfileHashTableDestroy(&Strings); +} + +uint32_t MicroProfileGetColor(uint32_t TimerIndex) +{ + MicroProfileTimerInfo& TI = S.TimerInfo[TimerIndex]; + if(TI.nColor == MP_AUTO) + { + return MicroProfileColorFromString(TI.pName); + } + else + { + return TI.nColor; + } +} + +#if MICROPROFILE_IMGUI +#include "imgui.h" +#ifndef MICROPROFILE_IMGUI_MAX_GRAPHS +#define MICROPROFILE_IMGUI_MAX_GRAPHS 64 +#endif + +#define MICROPROFILE_IMGUI_GRAPH_SIZE 256 + +struct MicroProfileImguiTimerState +{ + int TimerIndex = -1; + uint64_t FrameFetched = (uint64_t)-1; + uint32_t nColor = 0; + float fValues[MICROPROFILE_IMGUI_GRAPH_SIZE]; +}; + +struct MicroProfileImguiState +{ + MicroProfileImguiTimerState Timers[MICROPROFILE_IMGUI_MAX_GRAPHS]; + uint32_t NumTimers = 0; + uint32_t GraphPut; +}; + +static MicroProfileImguiState ImguiState; + +void MicroProfileImguiGather() +{ + MICROPROFILE_SCOPEI("MicroProfile", "ImguiGather", MP_AUTO); + uint32_t Put = ImguiState.GraphPut; + for(uint32_t i = 0; i < ImguiState.NumTimers; ++i) + { + MicroProfileImguiTimerState* pGraphInfo = &ImguiState.Timers[i]; + uint64_t Ticks = S.Frame[pGraphInfo->TimerIndex].nTicks; + float fToMs = S.TimerInfo[pGraphInfo->TimerIndex].Type == MicroProfileTokenTypeGpu ? MicroProfileTickToMsMultiplierGpu() : MicroProfileTickToMsMultiplierCpu(); + pGraphInfo->fValues[Put] = fToMs * Ticks; + } + ImguiState.GraphPut = (ImguiState.GraphPut + 1) % MICROPROFILE_IMGUI_GRAPH_SIZE; +} + +uint32_t MicroProfileImGuiColor(uint32_t Color) +{ + uint32_t A = 0xff; + uint32_t R = 0xff & (Color >> 16); + uint32_t G = 0xff & (Color >> 8); + uint32_t B = 0xff & (Color); + + return (A << IM_COL32_A_SHIFT) | (R << IM_COL32_R_SHIFT) | (G << IM_COL32_G_SHIFT) | (B << IM_COL32_B_SHIFT); +} + +void MicroProfileImguiControls() +{ + using namespace ImGui; + uint32_t IdCounter = 42; + { + PushID(IdCounter++); + int Aggr = MicroProfileGetAggregateFrames(); + Text("Aggregate Frames %7d", MicroProfileGetCurrentAggregateFrames()); + SameLine(); + if(RadioButton("Inf", Aggr == 0)) + MicroProfileSetAggregateFrames(0); + int AggrFrameOptions[] = { + 30, + 60, + 100, + 1000, + }; + for(int i = 0; i < sizeof(AggrFrameOptions) / sizeof(AggrFrameOptions[0]); ++i) + { + int v = AggrFrameOptions[i]; + char Buffer[32]; + stbsp_snprintf(Buffer, sizeof(Buffer) - 1, "%d", v); + SameLine(); + if(RadioButton(Buffer, Aggr == v)) + MicroProfileSetAggregateFrames(v); + } + + if(Aggr == 0) + { + if(Button("Clear Inf Aggregate")) + S.nAggregateClear = 1; + } + + PopID(); + } + Separator(); + { + PushID(IdCounter++); + Text("Categories"); + if(BeginTable("CategoryTable", 3, 0)) + { + TableSetupColumn("Name", ImGuiTableColumnFlags_WidthStretch); + TableSetupColumn("On", ImGuiTableColumnFlags_WidthFixed, 70); + TableSetupColumn("Off", ImGuiTableColumnFlags_WidthFixed, 70); + for(uint32_t i = 0; i < S.nCategoryCount; ++i) + { + PushID(i); + TableNextRow(); + TableSetColumnIndex(0); + Text(S.CategoryInfo[i].pName); + bool bEnabled = MicroProfileCategoryEnabled(i); + bool bDisabled = MicroProfileCategoryDisabled(i); + TableSetColumnIndex(1); + if(RadioButton("On", bEnabled)) + MicroProfileEnableCategory(S.CategoryInfo[i].pName); + TableSetColumnIndex(2); + if(RadioButton("Off", bDisabled)) + MicroProfileDisableCategory(S.CategoryInfo[i].pName); + + PopID(); + } + EndTable(); + } + PopID(); + } + Separator(); + { + PushID(IdCounter++); + Text("Groups"); + if(BeginTable("GroupTable", 3, 0)) + { + TableSetupColumn("Name", ImGuiTableColumnFlags_WidthStretch); + TableSetupColumn("On", ImGuiTableColumnFlags_WidthFixed, 70); + TableSetupColumn("Off", ImGuiTableColumnFlags_WidthFixed, 70); + + for(uint32_t i = 0; i < S.nGroupCount; ++i) + { + TableNextRow(); + PushID(i); + const char* pName = S.GroupInfo[i].pName; + bool bEnabled = MicroProfileGroupEnabled(i); + TableSetColumnIndex(0); + Text(pName); + TableSetColumnIndex(1); + if(RadioButton("On", bEnabled)) + MicroProfileToggleGroup(i); + TableSetColumnIndex(2); + if(RadioButton("Off", !bEnabled)) + MicroProfileToggleGroup(i); + PopID(); + } + EndTable(); + } + PopID(); + } +} + +MicroProfileImguiTimerState* MicroProfileImguiGetTimerState(int TimerIndex) +{ + MicroProfileImguiTimerState* ptr = nullptr; + for(uint32_t i = 0; i < ImguiState.NumTimers; ++i) + if(ImguiState.Timers[i].TimerIndex == TimerIndex) + return &ImguiState.Timers[i]; + + if(ImguiState.NumTimers < MICROPROFILE_IMGUI_MAX_GRAPHS) + { + MicroProfileImguiTimerState* pState = &ImguiState.Timers[ImguiState.NumTimers++]; + pState->TimerIndex = TimerIndex; + pState->nColor = MicroProfileGetColor(TimerIndex); + memset(&pState->fValues[0], 0, sizeof(pState->fValues)); + return pState; + } + + return nullptr; +} + +void MicroProfileImguiTable(const MicroProfileImguiWindowDesc& Window, const MicroProfileImguiEntryDesc* Entries, uint32_t NumEntries) +{ + using namespace ImGui; + const uint32_t NumColumns = 6; + ImGuiIO& io = ImGui::GetIO(); + + float Padding = GetStyle().CellPadding.x * 2; + float GroupWidth = CalcTextSize("Group").x; + float NameWidth = CalcTextSize("Name").x; + float BaseWidth = CalcTextSize("100000.00").x; + float Height = CalcTextSize("G").y + Padding; + + for(uint32_t i = 0; i < NumEntries; ++i) + { + uint32_t TimerIndex = MicroProfileGetTimerIndex(Entries[i].GraphTimer); + const MicroProfileTimerInfo& TI = S.TimerInfo[TimerIndex]; + const MicroProfileGroupInfo& GI = S.GroupInfo[TI.nGroupIndex]; + GroupWidth = MicroProfileMax(GroupWidth, CalcTextSize(GI.pName).x); + NameWidth = MicroProfileMax(NameWidth, CalcTextSize(TI.pName).x); + } + + float TableWidth = GroupWidth + NameWidth + BaseWidth * 4 + NumColumns * Padding + (NumColumns - 1) * GetStyle().ItemSpacing.x; + float TableHeight = Height * (NumEntries + 1); + + ImVec2 TablePos = ImVec2(0.f, 0.f); + if(Window.Align == MICROPROFILE_IMGUI_ALIGN_TOP_RIGHT || Window.Align == MICROPROFILE_IMGUI_ALIGN_BOTTOM_RIGHT) + TablePos.x = io.DisplaySize.x - TableWidth; + + if(Window.Align == MICROPROFILE_IMGUI_ALIGN_BOTTOM_LEFT || Window.Align == MICROPROFILE_IMGUI_ALIGN_BOTTOM_RIGHT) + TablePos.y = io.DisplaySize.y - TableHeight; + TablePos.x += Window.OffsetX; + TablePos.y += Window.OffsetY; + + SetCursorScreenPos(TablePos); + + if(BeginTable("MicroProfileImguiTable", NumColumns, ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg | ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_NoHostExtendX)) + { + TableSetupColumn("Group", ImGuiTableColumnFlags_WidthFixed, GroupWidth); + TableSetupColumn("Name", ImGuiTableColumnFlags_WidthFixed, NameWidth); + + TableSetupColumn("Max", ImGuiTableColumnFlags_WidthFixed, BaseWidth); + TableSetupColumn("Min", ImGuiTableColumnFlags_WidthFixed, BaseWidth); + TableSetupColumn("Avg", ImGuiTableColumnFlags_WidthFixed, BaseWidth); + TableSetupColumn("Time", ImGuiTableColumnFlags_WidthFixed, BaseWidth); + + TableHeadersRow(); + + for(uint32_t i = 0; i < NumEntries; ++i) + { + uint32_t TimerIndex = MicroProfileGetTimerIndex(Entries[i].GraphTimer); + + MicroProfileTimerValues Values; + MicroProfileCalcTimers(TimerIndex, Values); + const MicroProfileTimerInfo& TI = S.TimerInfo[TimerIndex]; + const MicroProfileGroupInfo& GI = S.GroupInfo[TI.nGroupIndex]; + TableNextRow(); + ImU32 RowBGColor = GetColorU32((i % 2) ? ImVec4(0.1f, 0.1f, 0.1f, 0.85f) : ImVec4(0.2f, 0.2f, 0.2f, 0.85f)); + TableSetBgColor(ImGuiTableBgTarget_RowBg1, RowBGColor); + PushID(i); + float fMax = 0.f, fMin = 0.f, fAvg = 0.f, fTime = 0.f; + + auto RightAlignedFloat = [](float f) + { + float CellWidth = GetContentRegionAvail().x; + char Buffer[32]; + stbsp_snprintf(Buffer, sizeof(Buffer) - 1, "%.2f", f); + ImVec2 TextSize = CalcTextSize(Buffer); + SetCursorPosX(GetCursorPosX() + (CellWidth - TextSize.x)); + TextUnformatted(Buffer); + }; + + TableSetColumnIndex(0); + Text(GI.pName); + TableSetColumnIndex(1); + Text(TI.pName); + TableSetColumnIndex(2); + RightAlignedFloat(Values.MaxMs); + TableSetColumnIndex(3); + RightAlignedFloat(Values.MinMs); + TableSetColumnIndex(4); + RightAlignedFloat(Values.AverageMs); + TableSetColumnIndex(5); + RightAlignedFloat(Values.TimeMs); + PopID(); + } + EndTable(); + } +} + +void MicroProfileImguiGraphs(const MicroProfileImguiWindowDesc& Window, const MicroProfileImguiEntryDesc* Entries, uint32_t NumEntries) +{ + using namespace ImGui; + ImGuiIO& io = ImGui::GetIO(); + uint32_t Width = Window.GraphWidth; + uint32_t Height = (Window.GraphHeight + GetStyle().ItemSpacing.y) * NumEntries; + + ImVec2 Pos = ImVec2(0.f, 0.f); + if(Window.Align == MICROPROFILE_IMGUI_ALIGN_TOP_RIGHT || Window.Align == MICROPROFILE_IMGUI_ALIGN_BOTTOM_RIGHT) + Pos.x = io.DisplaySize.x - Width; + + if(Window.Align == MICROPROFILE_IMGUI_ALIGN_BOTTOM_LEFT || Window.Align == MICROPROFILE_IMGUI_ALIGN_BOTTOM_RIGHT) + Pos.y = io.DisplaySize.y - Height; + + Pos.x += Window.OffsetX; + Pos.y += Window.OffsetY; + for(uint32_t i = 0; i < NumEntries; ++i) + { + SetCursorScreenPos(Pos); + uint32_t TimerIndex = MicroProfileGetTimerIndex(Entries[i].GraphTimer); + float GraphMax = Entries[i].GraphMax; + const MicroProfileTimerInfo& TI = S.TimerInfo[TimerIndex]; + + MicroProfileImguiTimerState* TimerState = MicroProfileImguiGetTimerState(TimerIndex); + + PushID(i << 16 | TimerIndex); + if(TimerState->nColor == 0) + TimerState->nColor = MicroProfileGetColor(TimerIndex); + ImVec4 FrameBg = GetStyleColorVec4(ImGuiCol_FrameBg); + FrameBg.x = 0.15f; + FrameBg.y = 0.15f; + FrameBg.z = 0.15f; + FrameBg.w = 0.8f; + + PushStyleColor(ImGuiCol_PlotLines, MicroProfileImGuiColor(TimerState->nColor)); + PushStyleColor(ImGuiCol_FrameBg, FrameBg); + uint32_t Start = (ImguiState.GraphPut) % MICROPROFILE_IMGUI_GRAPH_SIZE; + uint32_t Last = (ImguiState.GraphPut + MICROPROFILE_IMGUI_GRAPH_SIZE - 1) % MICROPROFILE_IMGUI_GRAPH_SIZE; + PlotLines("", &TimerState->fValues[0], MICROPROFILE_IMGUI_GRAPH_SIZE, Start, nullptr, 0.f, GraphMax, ImVec2(Window.GraphWidth, Window.GraphHeight)); + + char TimeStr[32]; + stbsp_snprintf(TimeStr, sizeof(TimeStr) - 1, "%.3fms", TimerState->fValues[Last]); + ImVec2 PlotMin = GetItemRectMin(); + ImVec2 PlotMax = GetItemRectMax(); + ImVec2 NameSize = CalcTextSize(TI.pName); + ImVec2 NamePos = ImVec2(PlotMin.x + 1, PlotMax.y - NameSize.y - 1); + ImVec2 TimeSize = CalcTextSize(TimeStr); + ImVec2 TimePos = ImVec2(PlotMax.x - TimeSize.x - 1, PlotMax.y - TimeSize.y - 1); + GetWindowDrawList()->AddText(NamePos, GetColorU32(ImGuiCol_Text), TI.pName); + GetWindowDrawList()->AddText(TimePos, GetColorU32(ImGuiCol_Text), TimeStr); + + PopStyleColor(); + PopStyleColor(); + PopID(); + Pos.y += Window.GraphHeight + GetStyle().ItemSpacing.y; + } +} + +#endif + +#undef uprintf + +#undef S +#ifdef _WIN32 +#pragma warning(pop) +#undef microprofile_fopen_helper +#endif + +#ifdef MICROPROFILE_PS4 +#define MICROPROFILE_PS4_IMPL +#include "microprofile_ps4.h" +#endif +#ifdef MICROPROFILE_XBOXONE +#define MICROPROFILE_XBOXONE_IMPL +#include "microprofile_xboxone.h" +#endif + +#endif // #if MICROPROFILE_ENABLED + +#include "microprofile_html.h" +#include "microprofile_icons.h" + +#endif \ No newline at end of file diff --git a/Minecraft.Client/Windows64/4JLibs/inc/Render/microprofile/microprofile.h b/Minecraft.Client/Windows64/4JLibs/inc/Render/microprofile/microprofile.h new file mode 100644 index 000000000..1f1dfa031 --- /dev/null +++ b/Minecraft.Client/Windows64/4JLibs/inc/Render/microprofile/microprofile.h @@ -0,0 +1,2058 @@ +#pragma once +// MIT License +// +// Copyright (c) 2023 Jonas Meyer +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// *********************************************************************** + +#ifndef MICROPROFILE_USE_CONFIG +#define MICROPROFILE_USE_CONFIG 1 +#endif + +#if MICROPROFILE_USE_CONFIG +#include "microprofile.config.h" +#endif + +#ifndef MICROPROFILE_ENABLED +#define MICROPROFILE_ENABLED 1 +#endif + +#ifndef MICROPROFILE_DYNAMIC_INSTRUMENT +#if(defined(__APPLE__) && defined(__MACH__)) || (defined(_WIN32) && defined(_M_X64)) || (defined(__unix__) && defined(__x86_64__)) +#ifdef MICROPROFILE_DYNAMIC_INSTRUMENT_ENABLE +#define MICROPROFILE_DYNAMIC_INSTRUMENT 1 +#endif +#endif +#endif + +#ifndef MICROPROFILE_DYNAMIC_INSTRUMENT +#define MICROPROFILE_DYNAMIC_INSTRUMENT 0 +#endif + +#ifndef MICROPROFILE_GPU_TIMERS_D3D11 +#define MICROPROFILE_GPU_TIMERS_D3D11 0 +#endif + +#ifndef MICROPROFILE_GPU_TIMERS_GL +#define MICROPROFILE_GPU_TIMERS_GL 0 +#endif + +#ifndef MICROPROFILE_GPU_TIMERS_D3D12 +#define MICROPROFILE_GPU_TIMERS_D3D12 0 +#endif + +#ifndef MICROPROFILE_BIG_ENDIAN +#define MICROPROFILE_BIG_ENDIAN 0 +#endif + +#ifndef MICROPROFILE_GPU_TIMERS_VULKAN +#define MICROPROFILE_GPU_TIMERS_VULKAN 0 +#endif + +#ifndef MICROPROFILE_LEGACY_CSV // set this if you rely on the legacy csv dump format. (single file with multiple csv sections) +#define MICROPROFILE_LEGACY_CSV 0 +#endif + +#ifndef MICROPROFILE_FRAME_EXTRA_DATA // Set this to always allocate frame extra data. This is useful if you're doing profiling on platform where there is plenty of memory. +#define MICROPROFILE_FRAME_EXTRA_DATA 0 +#endif + +#ifndef MICROPROFILE_IMGUI +#define MICROPROFILE_IMGUI 0 +#endif + +#ifndef MICROPROFILE_VERIFY_BALANCED +#define MICROPROFILE_VERIFY_BALANCED 0 +#endif + +#ifndef MICROPROFILE_ONCE +#define MICROPROFILE_ONCE + +#include + +typedef uint64_t MicroProfileToken; +typedef uint16_t MicroProfileGroupId; +typedef uint32_t MicroProfileTimelineToken; + +#if 0 == MICROPROFILE_ENABLED + +#define MICROPROFILE_DECLARE(var) +#define MICROPROFILE_DEFINE(var, group, name, color) +#define MICROPROFILE_REGISTER_GROUP(group, color, category) +#define MICROPROFILE_DECLARE_GPU(var) +#define MICROPROFILE_DEFINE_GPU(var, name, color) +#define MICROPROFILE_SCOPE(var) \ + do \ + { \ + } while(0) +#define MICROPROFILE_SCOPEI(group, name, color) \ + do \ + { \ + } while(0) +#define MICROPROFILE_SCOPE_CSTR(cstr) \ + do \ + { \ + } while(0) +#define MICROPROFILE_SCOPE_TOKEN(token) \ + do \ + { \ + } while(0) +#define MICROPROFILE_SCOPEGPU_TOKEN(token) +#define MICROPROFILE_SCOPEGPU(var) \ + do \ + { \ + } while(0) +#define MICROPROFILE_SCOPEGPUI(name, color) \ + do \ + { \ + } while(0) +#define MICROPROFILE_SCOPEGPU_CSTR(cstr) \ + do \ + { \ + } while(0) +#define MICROPROFILE_SCOPEGPU_TOKEN_L(Log, token) \ + do \ + { \ + } while(0) +#define MICROPROFILE_SCOPEGPU_L(Log, var) \ + do \ + { \ + } while(0) +#define MICROPROFILE_SCOPEGPUI_L(Log, name, color) \ + do \ + { \ + } while(0) +#define MICROPROFILE_SCOPEGPU_CSTR_L(Log, cstr) \ + do \ + { \ + } while(0) +#define MICROPROFILE_SECTIONGPU_TOKEN(token) \ + do \ + { \ + } while(0) +#define MICROPROFILE_SECTIONGPU(var) \ + do \ + { \ + } while(0) +#define MICROPROFILE_SECTIONGPUI(name, color) \ + do \ + { \ + } while(0) +#define MICROPROFILE_SECTIONGPU_TOKEN_L(Log, token) \ + do \ + { \ + } while(0) +#define MICROPROFILE_SECTIONGPU_L(Log, var) \ + do \ + { \ + } while(0) +#define MICROPROFILE_SECTIONGPUI_L(Log, name, color) \ + do \ + { \ + } while(0) + +#define MICROPROFILE_CONDITIONAL_SCOPEI(condition, group, name, color) \ + do \ + { \ + } while(0) +#define MICROPROFILE_ENTER(var) \ + do \ + { \ + } while(0) +#define MICROPROFILE_ENTER_TOKEN(var) \ + do \ + { \ + } while(0) +#define MICROPROFILE_ENTERI(group, name, color) \ + do \ + { \ + } while(0) +#define MICROPROFILE_LEAVE() \ + do \ + { \ + } while(0) + +#define MICROPROFILE_GPU_ENTER(var) \ + do \ + { \ + } while(0) +#define MICROPROFILE_GPU_ENTER_TOKEN(token) \ + do \ + { \ + } while(0) +#define MICROPROFILE_GPU_ENTERI(group, name, color) \ + do \ + { \ + } while(0) +#define MICROPROFILE_GPU_LEAVE() \ + do \ + { \ + } while(0) +#define MICROPROFILE_GPU_ENTER_L(Log, var) \ + do \ + { \ + } while(0) +#define MICROPROFILE_GPU_ENTER_TOKEN_L(Log, token) \ + do \ + { \ + } while(0) +#define MICROPROFILE_GPU_ENTERI_L(Log, name, color) \ + do \ + { \ + } while(0) +#define MICROPROFILE_GPU_LEAVE_L(Log) \ + do \ + { \ + } while(0) +#define MICROPROFILE_GPU_INIT_QUEUE(QueueName) \ + do \ + { \ + } while(0) +#define MICROPROFILE_GPU_GET_QUEUE(QueueName) \ + do \ + { \ + } while(0) +#define MICROPROFILE_GPU_BEGIN(pContext, pLog) \ + do \ + { \ + } while(0) +#define MICROPROFILE_GPU_SET_CONTEXT(pContext, pLog) \ + do \ + { \ + } while(0) +#define MICROPROFILE_GPU_END(pLog) 0 +#define MICROPROFILE_GPU_SUBMIT(Queue, Work) \ + do \ + { \ + } while(0) + +#define MICROPROFILE_TIMELINE_TOKEN(token) \ + do \ + { \ + } while(0) +#define MICROPROFILE_TIMELINE_ENTER(token, color, name) \ + do \ + { \ + } while(0) +#define MICROPROFILE_TIMELINE_ENTERF(token, color, fmt, ...) \ + do \ + { \ + } while(0) +#define MICROPROFILE_TIMELINE_LEAVE(token) \ + do \ + { \ + } while(0) +#define MICROPROFILE_TIMELINE_ENTER_STATIC(color, name) \ + do \ + { \ + } while(0) +#define MICROPROFILE_TIMELINE_LEAVE_STATIC(name) \ + do \ + { \ + } while(0) +#define MICROPROFILE_TIMELINE_SCOPE(...) \ + do \ + { \ + } while(0) +#define MICROPROFILE_THREADLOGGPURESET(a) \ + do \ + { \ + } while(0) +#define MICROPROFILE_META_CPU(name, count) +#define MICROPROFILE_META_GPU(name, count) +#define MICROPROFILE_FORCEENABLECPUGROUP(s) \ + do \ + { \ + } while(0) +#define MICROPROFILE_FORCEDISABLECPUGROUP(s) \ + do \ + { \ + } while(0) +#define MICROPROFILE_FORCEENABLEGPUGROUP(s) \ + do \ + { \ + } while(0) +#define MICROPROFILE_FORCEDISABLEGPUGROUP(s) \ + do \ + { \ + } while(0) +#define MICROPROFILE_COUNTER_ADD(name, count) \ + do \ + { \ + } while(0) +#define MICROPROFILE_COUNTER_SUB(name, count) \ + do \ + { \ + } while(0) +#define MICROPROFILE_COUNTER_SET(name, count) \ + do \ + { \ + } while(0) +#define MICROPROFILE_COUNTER_SET_DOUBLE(name, count) \ + do \ + { \ + } while(0) +#define MICROPROFILE_COUNTER_SET_INT32_PTR(name, ptr) \ + do \ + { \ + } while(0) +#define MICROPROFILE_COUNTER_SET_INT64_PTR(name, ptr) \ + do \ + { \ + } while(0) +#define MICROPROFILE_COUNTER_CLEAR_PTR(name) \ + do \ + { \ + } while(0) +#define MICROPROFILE_COUNTER_SET_LIMIT(name, count) \ + do \ + { \ + } while(0) +#define MICROPROFILE_CONDITIONAL(expr) +#define MICROPROFILE_COUNTER_CONFIG(name, type, limit, flags) +#define MICROPROFILE_COUNTER_CONFIG_ONCE(name, type, limit, flags) +#define MICROPROFILE_DECLARE_LOCAL_COUNTER(var) +#define MICROPROFILE_DEFINE_LOCAL_COUNTER(var, name) +#define MICROPROFILE_DECLARE_LOCAL_ATOMIC_COUNTER(var) +#define MICROPROFILE_DEFINE_LOCAL_ATOMIC_COUNTER(var, name) +#define MICROPROFILE_COUNTER_LOCAL_ADD(var, count) \ + do \ + { \ + } while(0) +#define MICROPROFILE_COUNTER_LOCAL_SUB(var, count) \ + do \ + { \ + } while(0) +#define MICROPROFILE_COUNTER_LOCAL_SET(var, count) \ + do \ + { \ + } while(0) +#define MICROPROFILE_COUNTER_LOCAL_UPDATE_ADD(var) \ + do \ + { \ + } while(0) +#define MICROPROFILE_COUNTER_LOCAL_UPDATE_SET(var) \ + do \ + { \ + } while(0) +#define MICROPROFILE_COUNTER_LOCAL_ADD_ATOMIC(var, count) \ + do \ + { \ + } while(0) +#define MICROPROFILE_COUNTER_LOCAL_SUB_ATOMIC(var, count) \ + do \ + { \ + } while(0) +#define MICROPROFILE_COUNTER_LOCAL_SET_ATOMIC(var, count) \ + do \ + { \ + } while(0) +#define MICROPROFILE_COUNTER_LOCAL_UPDATE_ADD_ATOMIC(var) \ + do \ + { \ + } while(0) +#define MICROPROFILE_COUNTER_LOCAL_UPDATE_SET_ATOMIC(var) \ + do \ + { \ + } while(0) + +#define MICROPROFILE_COUNTER_GET_TOKEN(name, f) 0 +#define MICROPROFILE_COUNTER_TOKEN_ADD(v, count) do{}while(0) +#define MICROPROFILE_COUNTER_TOKEN_SUB(v, count) do{}while(0) +#define MICROPROFILE_COUNTER_TOKEN_SET(v, count) do{}while(0) + + +#define MicroProfileStartAutoFlip(nHz) \ + do \ + { \ + } while(0) +#define MicroProfileStopAutoFlip() \ + do \ + { \ + } while(0) +#define MicroProfileEnableFrameExtraCounterData() \ + do \ + { \ + } while(0) +#define MicroProfileGetTime(group, name) 0.f +#define MicroProfileOnThreadCreate(foo) \ + do \ + { \ + } while(0) +#define MicroProfileOnThreadExit() \ + do \ + { \ + } while(0) +#define MicroProfileFlip(...) \ + do \ + { \ + } while(0) +#define MicroProfileFlip_CB(pContext, FreezeCB, Flag) \ + do \ + { \ + } while(0) +#define MicroProfileSetAggregateFrames(a) \ + do \ + { \ + } while(0) +#define MicroProfileGetAggregateFrames() 0 +#define MicroProfileGetCurrentAggregateFrames() 0 +#define MicroProfileTogglePause() \ + do \ + { \ + } while(0) +#define MicroProfileToggleAllGroups() \ + do \ + { \ + } while(0) +#define MicroProfileDumpTimers() \ + do \ + { \ + } while(0) +#define MicroProfileShutdown() \ + do \ + { \ + } while(0) +#define MicroProfileSetForceEnable(a) \ + do \ + { \ + } while(0) +#define MicroProfileGetForceEnable() false +#define MicroProfileSetEnableAllGroups(b) \ + do \ + { \ + } while(0) +#define MicroProfileEnableCategory(a) \ + do \ + { \ + } while(0) +#define MicroProfileDisableCategory(a) \ + do \ + { \ + } while(0) +#define MicroProfileGetEnableAllGroups() false +#define MicroProfileSetForceMetaCounters(a) +#define MicroProfileGetForceMetaCounters() 0 +#define MicroProfileEnableMetaCounter(c) \ + do \ + { \ + } while(0) +#define MicroProfileDisableMetaCounter(c) \ + do \ + { \ + } while(0) +#define MicroProfileDumpFile(...) \ + do \ + { \ + } while(0) +#define MicroProfileDumpFileImmediately(...) \ + do \ + { \ + } while(0) +#define MicroProfileStartContextSwitchTrace() \ + do \ + { \ + } while(0) +#define MicroProfileGpuInsertTimeStamp(a) 1 +#define MicroProfileGpuGetTimeStamp(a) 0 +#define MicroProfileTicksPerSecondGpu() 1 +#define MicroProfileGetGpuTickReference(a, b) 0 +#define MicroProfileGpuInitD3D12(pDevice, nNodeCount, pCommandQueue, pCopyQueue) \ + do \ + { \ + } while(0) +#define MicroProfileGpuInitD3D11(pDevice, pDeviceContext) \ + do \ + { \ + } while(0) +#define MicroProfileGpuShutdown() \ + do \ + { \ + } while(0) +#define MicroProfileGpuShutdownPlatform() \ + do \ + { \ + } while(0) +#define MicroProfileGpuInitGL() \ + do \ + { \ + } while(0) +#define MicroProfileSetCurrentNodeD3D12(nNode) \ + do \ + { \ + } while(0) +#define MicroProfilePlatformMarkersGetEnabled() 0 +#define MicroProfilePlatformMarkersSetEnabled(bEnabled) \ + do \ + { \ + } while(0) +#define MicroProfileTickToMsMultiplierCpu() 1.f +#define MicroProfileTickToMsMultiplierGpu() 0.f +#define MicroProfileTicksPerSecondCpu() 1 +#define MicroProfileTick() 0 +#define MicroProfileEnabled() 0 + +#define MicroProfileCounterString nullptr +#define MicroProfileCounterTokenTree 0 +#define MicroProfileCounterTokenTreeDynamic 0 + +#define MicroProfileCsvConfigEnd() +#define MicroProfileCsvConfigBegin(...) +#define MicroProfileCsvConfigAddTimer(...) +#define MicroProfileCsvConfigAddGroup(...) +#define MicroProfileCsvConfigAddCounter(...) +#define MicroProfileUpdateSettingsPath(...) + +#define MicroProfileImguiGraphs(...) \ + do \ + { \ + } while(0) +#define MicroProfileImguiTable(...) \ + do \ + { \ + } while(0) +#define MicroProfileImguiControls(...) \ + do \ + { \ + } while(0) + +#else + +#include + +#ifdef MICROPROFILE_EXPORT +#include "microprofile.export.h" +#else +#ifndef MICROPROFILE_API +#define MICROPROFILE_API +#endif +#endif + +#ifdef MICROPROFILE_PS4 +#include "microprofile_ps4.h" +#endif +#ifdef MICROPROFILE_XBOXONE +#include "microprofile_xboxone.h" +#endif + +#ifdef _WIN32 +typedef uint32_t MicroProfileThreadIdType; +#else +#ifdef MICROPROFILE_THREADID_SIZE_4BYTE +typedef uint32_t MicroProfileThreadIdType; +#elif MICROPROFILE_THREADID_SIZE_8BYTE +typedef uint64_t MicroProfileThreadIdType; +#else +typedef uint64_t MicroProfileThreadIdType; +#endif +#endif + +typedef void (*MicroProfileOnFreeze)(int nFrozen); + +#define MICROPROFILE_TOKEN_PASTE0(a, b) a##b +#define MICROPROFILE_TOKEN_PASTE(a, b) MICROPROFILE_TOKEN_PASTE0(a, b) + +#define MICROPROFILE_DECLARE(var) extern MicroProfileToken g_mp_##var +#define MICROPROFILE_DECLARE_GPU(var) extern MicroProfileToken g_mpGPU_##var + +#define MICROPROFILE_DECLARE_SECTION(var) extern MicroProfileToken g_mp_##var +#define MICROPROFILE_DECLARE_SECTION_GPU(var) extern MicroProfileToken g_mpGPU_##var + +#define MICROPROFILE_DEFINE(var, group, name, color) MicroProfileToken g_mp_##var = MicroProfileGetToken(group, name, color, MicroProfileTokenTypeCpu, 0) +#define MICROPROFILE_DEFINE_GPU(var, name, color) MicroProfileToken g_mpGPU_##var = MicroProfileGetToken("GPU", name, color, MicroProfileTokenTypeGpu, 0) + +#define MICROPROFILE_DEFINE_SECTION(var, name, color) MicroProfileToken g_mp_##var = MicroProfileGetToken("__SECTION", name, color, MicroProfileTokenTypeCpu, MICROPROFILE_TIMER_FLAG_SECTION) +#define MICROPROFILE_DEFINE_SECTION_GPU(var, name, color) MicroProfileToken g_mpGPU_##var = MicroProfileGetToken("GPU", name, color, MicroProfileTokenTypeGpu, MICROPROFILE_TIMER_FLAG_SECTION) + +#define MICROPROFILE_REGISTER_GROUP(group, category, color) MicroProfileRegisterGroup(group, category, color) + +#define MICROPROFILE_SCOPE(var) MicroProfileScopeHandler MICROPROFILE_TOKEN_PASTE(foo, __LINE__)(g_mp_##var) +#define MICROPROFILE_SCOPE_TOKEN(token) MicroProfileScopeHandler MICROPROFILE_TOKEN_PASTE(foo, __LINE__)(token) +#define MICROPROFILE_SCOPEI(group, name, color) \ + static MicroProfileToken MICROPROFILE_TOKEN_PASTE(g_mp, __LINE__) = MicroProfileGetToken(group, name, color, MicroProfileTokenTypeCpu, 0); \ + MicroProfileScopeHandler MICROPROFILE_TOKEN_PASTE(foo, __LINE__)(MICROPROFILE_TOKEN_PASTE(g_mp, __LINE__)) +#define MICROPROFILE_SCOPE_CSTR(CStr) MicroProfileScopeHandlerCStr MICROPROFILE_TOKEN_PASTE(foo, __LINE__)(CStr) + +#define MICROPROFILE_SECTION(var) MICROPROFILE_SCOPE(var) +#define MICROPROFILE_SECTIONI(name, color) \ + static MicroProfileToken MICROPROFILE_TOKEN_PASTE(g_mp, __LINE__) = MicroProfileGetToken("__SECTION", name, color, MicroProfileTokenTypeCpu, MICROPROFILE_TIMER_FLAG_SECTION); \ + MicroProfileScopeHandler MICROPROFILE_TOKEN_PASTE(foo, __LINE__)(MICROPROFILE_TOKEN_PASTE(g_mp, __LINE__)) + +#define MICROPROFILE_SCOPEGPU_TOKEN(token) MicroProfileScopeGpuHandler MICROPROFILE_TOKEN_PASTE(fooGPU, __LINE__)(token, MicroProfileGetGlobalGpuThreadLog()) +#define MICROPROFILE_SCOPEGPU(var) MicroProfileScopeGpuHandler MICROPROFILE_TOKEN_PASTE(fooGPU, __LINE__)(g_mpGPU_##var, MicroProfileGetGlobalGpuThreadLog()) +#define MICROPROFILE_SCOPEGPUI(name, color) \ + static MicroProfileToken MICROPROFILE_TOKEN_PASTE(g_mpGPU, __LINE__) = MicroProfileGetToken("GPU", name, color, MicroProfileTokenTypeGpu, 0); \ + MicroProfileScopeGpuHandler MICROPROFILE_TOKEN_PASTE(fooGPU, __LINE__)(MICROPROFILE_TOKEN_PASTE(g_mpGPU, __LINE__), MicroProfileGetGlobalGpuThreadLog()) +#define MICROPROFILE_SCOPEGPU_CSTR(CStr) MicroProfileScopeGpuHandlerCStr MICROPROFILE_TOKEN_PASTE(foo, __LINE__)(CStr, MicroProfileGetGlobalGpuThreadLog()) + +#define MICROPROFILE_SCOPEGPU_TOKEN_L(Log, token) MicroProfileScopeGpuHandler MICROPROFILE_TOKEN_PASTE(fooGPU, __LINE__)(token, Log) +#define MICROPROFILE_SCOPEGPU_L(Log, var) MicroProfileScopeGpuHandler MICROPROFILE_TOKEN_PASTE(fooGPU, __LINE__)(g_mpGPU_##var, Log) +#define MICROPROFILE_SCOPEGPUI_L(Log, name, color) \ + static MicroProfileToken MICROPROFILE_TOKEN_PASTE(g_mpGPU, __LINE__) = MicroProfileGetToken("GPU", name, color, MicroProfileTokenTypeGpu, 0); \ + MicroProfileScopeGpuHandler MICROPROFILE_TOKEN_PASTE(fooGPU, __LINE__)(MICROPROFILE_TOKEN_PASTE(g_mpGPU, __LINE__), Log) +#define MICROPROFILE_SCOPEGPU_CSTR_L(Log, CStr) MicroProfileScopeGpuHandler MICROPROFILE_TOKEN_PASTE(foo, __LINE__)(CStr, Log) + +#define MICROPROFILE_SECTIONGPU_TOKEN(token) MICROPROFILE_SCOPEGPU_TOKEN(token) +#define MICROPROFILE_SECTIONGPU(var) MICROPROFILE_SCOPEGPU(var) +#define MICROPROFILE_SECTIONGPUI(name, color) \ + static MicroProfileToken MICROPROFILE_TOKEN_PASTE(g_mpGPU, __LINE__) = MicroProfileGetToken("__SECTIONGPU", name, color, MicroProfileTokenTypeGpu, MICROPROFILE_TIMER_FLAG_SECTION); \ + MicroProfileScopeGpuHandler MICROPROFILE_TOKEN_PASTE(fooGPU, __LINE__)(MICROPROFILE_TOKEN_PASTE(g_mpGPU, __LINE__)) + +#define MICROPROFILE_SECTIONGPU_TOKEN_L(Log, token) MICROPROFILE_SCOPEGPU_TOKEN_L(token, Log) +#define MICROPROFILE_SECTIONGPU_L(Log, var) MICROPROFILE_SCOPEGPU_L(var, Log) +#define MICROPROFILE_SECTIONGPUI_L(Log, name, color) \ + static MicroProfileToken MICROPROFILE_TOKEN_PASTE(g_mpGPU, __LINE__) = MicroProfileGetToken("__SECTIONGPU", name, color, MicroProfileTokenTypeGpu, MICROPROFILE_TIMER_FLAG_SECTION); \ + MicroProfileScopeGpuHandler MICROPROFILE_TOKEN_PASTE(fooGPU, __LINE__)(MICROPROFILE_TOKEN_PASTE(g_mpGPU, __LINE__), Log) + +#define MICROPROFILE_CONDITIONAL_SCOPEI(condition, group, name, color) \ + static MicroProfileToken MICROPROFILE_TOKEN_PASTE(g_mp, __LINE__) = MicroProfileGetToken(group, name, color, MicroProfileTokenTypeCpu, 0); \ + MicroProfileConditionalScopeHandler MICROPROFILE_TOKEN_PASTE(foo, __LINE__)(MICROPROFILE_TOKEN_PASTE(g_mp, __LINE__), condition) +#define MICROPROFILE_ENTER(var) MicroProfileEnter(g_mp_##var) +#define MICROPROFILE_ENTER_TOKEN(token) MicroProfileEnter(token) +#define MICROPROFILE_ENTERI(group, name, color) \ + static MicroProfileToken MICROPROFILE_TOKEN_PASTE(g_mp, __LINE__) = MICROPROFILE_INVALID_TOKEN; \ + if(MICROPROFILE_INVALID_TOKEN == MICROPROFILE_TOKEN_PASTE(g_mp, __LINE__)) \ + { \ + MicroProfileGetTokenC(&MICROPROFILE_TOKEN_PASTE(g_mp, __LINE__), group, name, color, MicroProfileTokenTypeCpu, 0); \ + } \ + MicroProfileEnter(MICROPROFILE_TOKEN_PASTE(g_mp, __LINE__)) +#define MICROPROFILE_LEAVE() MicroProfileLeave() + +#define MICROPROFILE_GPU_ENTER(var) MicroProfileEnterGpu(g_mpGPU_##var, MicroProfileGetGlobalGpuThreadLog()) +#define MICROPROFILE_GPU_ENTER_TOKEN(token) MicroProfileEnterGpu(token, MicroProfileGetGlobalGpuThreadLog()) +#define MICROPROFILE_GPU_ENTERI(group, name, color) \ + static MicroProfileToken MICROPROFILE_TOKEN_PASTE(g_mpGPU, __LINE__) = MICROPROFILE_INVALID_TOKEN; \ + if(MICROPROFILE_INVALID_TOKEN == MICROPROFILE_TOKEN_PASTE(g_mpGPU, __LINE__)) \ + { \ + MicroProfileGetTokenC(&MICROPROFILE_TOKEN_PASTE(g_mpGPU, __LINE__), group, name, color, MicroProfileTokenTypeGpu, 0); \ + } \ + MicroProfileEnterGpu(MICROPROFILE_TOKEN_PASTE(g_mpGPU, __LINE__), MicroProfileGetGlobalGpuThreadLog()) +#define MICROPROFILE_GPU_LEAVE() MicroProfileLeaveGpu(MicroProfileGetGlobalGpuThreadLog()) +#define MICROPROFILE_GPU_ENTER_L(Log, var) MicroProfileEnterGpu(g_mpGPU_##var, Log) +#define MICROPROFILE_GPU_ENTER_TOKEN_L(Log, token) MicroProfileEnterGpu(token, Log) +#define MICROPROFILE_GPU_ENTERI_L(Log, name, color) \ + static MicroProfileToken MICROPROFILE_TOKEN_PASTE(g_mpGPU, __LINE__) = MICROPROFILE_INVALID_TOKEN; \ + if(MICROPROFILE_INVALID_TOKEN == MICROPROFILE_TOKEN_PASTE(g_mpGPU, __LINE__)) \ + { \ + MicroProfileGetTokenC(&MICROPROFILE_TOKEN_PASTE(g_mpGPU, __LINE__), group, name, color, MicroProfileTokenTypeGpu, 0); \ + } \ + MicroProfileEnterGpu(MICROPROFILE_TOKEN_PASTE(g_mpGPU, __LINE__), Log) +#define MICROPROFILE_GPU_LEAVE_L(Log) MicroProfileLeaveGpu(Log) +#define MICROPROFILE_GPU_INIT_QUEUE(QueueName) MicroProfileInitGpuQueue(QueueName) +#define MICROPROFILE_GPU_FREE_QUEUE(QueueName) MicroProfileFreeGpuQueue(QueueName) +#define MICROPROFILE_GPU_GET_QUEUE(QueueName) MicroProfileGetGpuQueue(QueueName) +#define MICROPROFILE_GPU_BEGIN(pContext, pLog) MicroProfileGpuBegin(pContext, pLog) +#define MICROPROFILE_GPU_SET_CONTEXT(pContext, pLog) MicroProfileGpuSetContext(pContext, pLog) +#define MICROPROFILE_GPU_END(pLog) MicroProfileGpuEnd(pLog) +#define MICROPROFILE_GPU_SUBMIT(Queue, Work) MicroProfileGpuSubmit(Queue, Work) +#define MICROPROFILE_TIMELINE_TOKEN(token) MicroProfileTimelineToken token = 0 +#define MICROPROFILE_TIMELINE_ENTER(token, color, name) token = MicroProfileTimelineEnter(color, name) +#define MICROPROFILE_TIMELINE_ENTERF(token, color, fmt, ...) token = MicroProfileTimelineEnterf(color, fmt, ##__VA_ARGS__) +#define MICROPROFILE_TIMELINE_LEAVE(token) \ + do \ + { \ + if(token) \ + { \ + MicroProfileTimelineLeave(token); \ + } \ + } while(0) +// use only with static string literals +#define MICROPROFILE_TIMELINE_ENTER_STATIC(color, name) MicroProfileTimelineEnterStatic(color, name) +// use only with static string literals +#define MICROPROFILE_TIMELINE_LEAVE_STATIC(name) MicroProfileTimelineLeaveStatic(name) +#define MICROPROFILE_TIMELINE_SCOPE(color, fmt, ...) \ + MicroProfileTimelineToken MICROPROFILE_TOKEN_PASTE(__timeline__, __LINE__) = MicroProfileTimelineEnterf(color, fmt, ##__VA_ARGS__); \ + MicroProfileScopeTimelineExitHandler MICROPROFILE_TOKEN_PASTE(__timeline__0, __LINE__)(MICROPROFILE_TOKEN_PASTE(__timeline__, __LINE__)) + +#define MICROPROFILE_THREADLOGGPURESET(a) MicroProfileThreadLogGpuReset(a) +#define MICROPROFILE_META_CPU(name, count) \ + do \ + { \ + } while(0) // static MicroProfileToken MICROPROFILE_TOKEN_PASTE(g_mp_meta,__LINE__) = MicroProfileGetMetaToken(name); MicroProfileMetaUpdate(MICROPROFILE_TOKEN_PASTE(g_mp_meta,__LINE__), count, + // MicroProfileTokenTypeCpu) +#define MICROPROFILE_COUNTER_ADD(name, count) \ + static MicroProfileToken MICROPROFILE_TOKEN_PASTE(g_mp_counter, __LINE__) = MicroProfileGetCounterToken(name, 0); \ + MicroProfileCounterAdd(MICROPROFILE_TOKEN_PASTE(g_mp_counter, __LINE__), count) +#define MICROPROFILE_COUNTER_SUB(name, count) \ + static MicroProfileToken MICROPROFILE_TOKEN_PASTE(g_mp_counter, __LINE__) = MicroProfileGetCounterToken(name, 0); \ + MicroProfileCounterAdd(MICROPROFILE_TOKEN_PASTE(g_mp_counter, __LINE__), -(int64_t)count) +#define MICROPROFILE_COUNTER_SET(name, count) \ + static MicroProfileToken MICROPROFILE_TOKEN_PASTE(g_mp_counter_set, __LINE__) = MicroProfileGetCounterToken(name, 0); \ + MicroProfileCounterSet(MICROPROFILE_TOKEN_PASTE(g_mp_counter_set, __LINE__), count) +#define MICROPROFILE_COUNTER_SET_DOUBLE(name, count) \ + static MicroProfileToken MICROPROFILE_TOKEN_PASTE(g_mp_counter_set, __LINE__) = MicroProfileGetCounterToken(name, MICROPROFILE_COUNTER_FLAG_DOUBLE); \ + MicroProfileCounterSetDouble(MICROPROFILE_TOKEN_PASTE(g_mp_counter_set, __LINE__), count) +#define MICROPROFILE_COUNTER_SET_INT32_PTR(name, ptr) MicroProfileCounterSetPtr(name, ptr, sizeof(int32_t)) +#define MICROPROFILE_COUNTER_SET_INT64_PTR(name, ptr) MicroProfileCounterSetPtr(name, ptr, sizeof(int64_t)) +#define MICROPROFILE_COUNTER_CLEAR_PTR(name) MicroProfileCounterSetPtr(name, 0, 0) +#define MICROPROFILE_COUNTER_SET_LIMIT(name, count) \ + static MicroProfileToken MICROPROFILE_TOKEN_PASTE(g_mp_counter, __LINE__) = MicroProfileGetCounterToken(name, 0); \ + MicroProfileCounterSetLimit(MICROPROFILE_TOKEN_PASTE(g_mp_counter, __LINE__), count) +#define MICROPROFILE_COUNTER_CONFIG(name, type, limit, flags) MicroProfileCounterConfig(name, type, limit, flags) +#define MICROPROFILE_COUNTER_CONFIG_ONCE(name, type, limit, flags) \ + do \ + { \ + static bool MICROPROFILE_TOKEN_PASTE(g_mponce, __LINE__) = false; \ + if(!MICROPROFILE_TOKEN_PASTE(g_mponce, __LINE__)) \ + { \ + MICROPROFILE_TOKEN_PASTE(g_mponce, __LINE__) = true; \ + MicroProfileCounterConfig(name, type, limit, flags); \ + } \ + } while(0) +#define MICROPROFILE_DECLARE_LOCAL_COUNTER(var) \ + extern int64_t g_mp_local_counter##var; \ + extern MicroProfileToken g_mp_counter_token##var; +#define MICROPROFILE_DEFINE_LOCAL_COUNTER(var, name) \ + int64_t g_mp_local_counter##var = 0; \ + MicroProfileToken g_mp_counter_token##var = MicroProfileGetCounterToken(name, 0) +#define MICROPROFILE_DECLARE_LOCAL_ATOMIC_COUNTER(var) extern MicroProfileToken g_mp_counter_token##var; +#define MICROPROFILE_DEFINE_LOCAL_ATOMIC_COUNTER(var, name) MicroProfileToken g_mp_counter_token##var = MicroProfileGetCounterToken(name, 0) +#define MICROPROFILE_COUNTER_LOCAL_ADD(var, count) MicroProfileLocalCounterAdd(&g_mp_local_counter##var, (count)) +#define MICROPROFILE_COUNTER_LOCAL_SUB(var, count) MicroProfileLocalCounterAdd(&g_mp_local_counter##var, -(int64_t)(count)) +#define MICROPROFILE_COUNTER_LOCAL_SET(var, count) MicroProfileLocalCounterSet(&g_mp_local_counter##var, count) +#define MICROPROFILE_COUNTER_LOCAL_UPDATE_ADD(var) MicroProfileCounterAdd(g_mp_counter_token##var, MicroProfileLocalCounterSet(&g_mp_local_counter##var, 0)) +#define MICROPROFILE_COUNTER_LOCAL_UPDATE_SET(var) MicroProfileCounterSet(g_mp_counter_token##var, MicroProfileLocalCounterSet(&g_mp_local_counter##var, 0)) +#define MICROPROFILE_COUNTER_LOCAL_ADD_ATOMIC(var, count) MicroProfileLocalCounterAddAtomic(g_mp_counter_token##var, (count)) +#define MICROPROFILE_COUNTER_LOCAL_SUB_ATOMIC(var, count) MicroProfileLocalCounterAddAtomic(g_mp_counter_token##var, -(int64_t)(count)) +#define MICROPROFILE_COUNTER_LOCAL_SET_ATOMIC(var, count) MicroProfileLocalCounterSetAtomic(g_mp_counter_token##var, count) +#define MICROPROFILE_COUNTER_LOCAL_UPDATE_ADD_ATOMIC(var) MicroProfileCounterAdd(g_mp_counter_token##var, MicroProfileLocalCounterSetAtomic(g_mp_counter_token##var, 0)) +#define MICROPROFILE_COUNTER_LOCAL_UPDATE_SET_ATOMIC(var) MicroProfileCounterSet(g_mp_counter_token##var, MicroProfileLocalCounterSetAtomic(g_mp_counter_token##var, 0)) +#define MICROPROFILE_COUNTER_GET_TOKEN(name, f) MicroProfileGetCounterToken(name, f) +#define MICROPROFILE_COUNTER_TOKEN_ADD(v, count) MicroProfileCounterAdd(v, count) +#define MICROPROFILE_COUNTER_TOKEN_SUB(v, count) MicroProfileCounterAdd(v, -(int64_t)count) +#define MICROPROFILE_COUNTER_TOKEN_SET(v, count) MicroProfileCounterSet(v, count) +#define MICROPROFILE_FORCEENABLECPUGROUP(s) MicroProfileForceEnableGroup(s, MicroProfileTokenTypeCpu) +#define MICROPROFILE_FORCEDISABLECPUGROUP(s) MicroProfileForceDisableGroup(s, MicroProfileTokenTypeCpu) +#define MICROPROFILE_FORCEENABLEGPUGROUP(s) MicroProfileForceEnableGroup(s, MicroProfileTokenTypeGpu) +#define MICROPROFILE_FORCEDISABLEGPUGROUP(s) MicroProfileForceDisableGroup(s, MicroProfileTokenTypeGpu) +#define MICROPROFILE_CONDITIONAL(expr) expr + +#ifndef MICROPROFILE_PLATFORM_MARKERS +#define MICROPROFILE_PLATFORM_MARKERS 0 +#endif + +#if MICROPROFILE_PLATFORM_MARKERS +#define MICROPROFILE_PLATFORM_MARKERS_ENABLED S.nPlatformMarkersEnabled +#define MICROPROFILE_PLATFORM_MARKER_BEGIN(color, marker) MicroProfilePlatformMarkerBegin(color, marker) +#define MICROPROFILE_PLATFORM_MARKER_END() MicroProfilePlatformMarkerEnd() +#else +#define MICROPROFILE_PLATFORM_MARKER_BEGIN(color, marker) \ + do \ + { \ + (void)color; \ + (void)marker; \ + } while(0) +#define MICROPROFILE_PLATFORM_MARKER_END() \ + do \ + { \ + } while(0) +#define MICROPROFILE_PLATFORM_MARKERS_ENABLED 0 +#endif + +#define MICROPROFILE_MAJOR_VERSION 4 +#define MICROPROFILE_MINOR_VERSION 0 + +#ifndef MICROPROFILE_USE_THREAD_NAME_CALLBACK +#define MICROPROFILE_USE_THREAD_NAME_CALLBACK 0 +#endif + +#ifndef MICROPROFILE_PER_THREAD_BUFFER_SIZE +#define MICROPROFILE_PER_THREAD_BUFFER_SIZE (2048 << 10) +#endif + +#ifndef MICROPROFILE_PER_THREAD_GPU_BUFFER_SIZE +#define MICROPROFILE_PER_THREAD_GPU_BUFFER_SIZE (128 << 10) +#endif + +#ifndef MICROPROFILE_MAX_FRAME_HISTORY +#define MICROPROFILE_MAX_FRAME_HISTORY 512 +#endif + +#ifndef MICROPROFILE_PRINTF +#define MICROPROFILE_PRINTF printf +#endif + +// #ifndef MICROPROFILE_META_MAX +// #define MICROPROFILE_META_MAX 8 +// #endif + +#ifndef MICROPROFILE_WEBSERVER_PORT +#define MICROPROFILE_WEBSERVER_PORT 1338 +#endif + +#ifndef MICROPROFILE_WEBSERVER +#define MICROPROFILE_WEBSERVER 1 +#endif + +#ifndef MICROPROFILE_WEBSERVER_AUTO_START +#define MICROPROFILE_WEBSERVER_AUTO_START 1 // when set to 1, the webserver will start on first call to MicroProfileFlip +#endif + +#ifndef MICROPROFILE_WEBSERVER_DEFAULT_FRAMES +#define MICROPROFILE_WEBSERVER_DEFAULT_FRAMES 30 +#endif + +#ifndef MICROPROFILE_WEBSERVER_SOCKET_BUFFER_SIZE +#define MICROPROFILE_WEBSERVER_SOCKET_BUFFER_SIZE (16 << 10) +#endif + +#ifndef MICROPROFILE_GPU_TIMERS +#if defined(MICROPROFILE_GPU_TIMERS_VULKAN) || defined(MICROPROFILE_GPU_TIMERS_D3D12) || defined(MICROPROFILE_GPU_TIMERS_D3D11) || defined(MICROPROFILE_GPU_TIMERS_GL) +#define MICROPROFILE_GPU_TIMERS 1 +#else +#define MICROPROFILE_GPU_TIMERS 0 +#endif +#endif + +#ifndef MICROPROFILE_GPU_FRAME_DELAY +#define MICROPROFILE_GPU_FRAME_DELAY 5 // must be > 0 +#endif + +#ifndef MICROPROFILE_NAME_MAX_LEN +#define MICROPROFILE_NAME_MAX_LEN 64 +#endif + +#ifndef MICROPROFILE_MAX_TIMERS +#define MICROPROFILE_MAX_TIMERS 1024 +#endif + +#ifndef MICROPROFILE_MAX_THREADS +#define MICROPROFILE_MAX_THREADS 128 +#endif + +#ifndef MICROPROFILE_UNPACK_RED +#define MICROPROFILE_UNPACK_RED(c) (0xff & ((c) >> 16)) +#endif + +#ifndef MICROPROFILE_UNPACK_GREEN +#define MICROPROFILE_UNPACK_GREEN(c) (0xff & ((c) >> 8)) +#endif + +#ifndef MICROPROFILE_UNPACK_BLUE +#define MICROPROFILE_UNPACK_BLUE(c) (0xff & ((c))) +#endif + +#ifndef MICROPROFILE_DEFAULT_PRESET +#define MICROPROFILE_DEFAULT_PRESET "Default" +#endif + +#ifndef MICROPROFILE_TIMELINE_MAX_ENTRIES +#define MICROPROFILE_TIMELINE_MAX_ENTRIES (4 << 10) +#endif + +#ifndef MICROPROFILE_MAX_STRING +#define MICROPROFILE_MAX_STRING 128 +#endif + +#ifndef MICROPROFILE_TIMELINE_MAX_TOKENS +#define MICROPROFILE_TIMELINE_MAX_TOKENS 64 +#endif + +#ifndef MICROPROFILE_THREAD_LOG_FRAMES_REUSE +#define MICROPROFILE_THREAD_LOG_FRAMES_REUSE 200 +#endif + +#ifndef MICROPROFILE_CONTEXT_SWITCH_TRACE +#if defined(_WIN32) +#define MICROPROFILE_CONTEXT_SWITCH_TRACE 1 +#elif defined(__APPLE__) +#define MICROPROFILE_CONTEXT_SWITCH_TRACE 0 // disabled until dtrace script is working. +#else +#define MICROPROFILE_CONTEXT_SWITCH_TRACE 0 +#endif +#endif + +#if MICROPROFILE_CONTEXT_SWITCH_TRACE +#define MICROPROFILE_CONTEXT_SWITCH_BUFFER_SIZE (128 * 1024) // 2mb with 16 byte entry size +#else +#define MICROPROFILE_CONTEXT_SWITCH_BUFFER_SIZE (1) +#endif + +#ifndef MICROPROFILE_MINIZ +#define MICROPROFILE_MINIZ 0 +#endif + +#ifndef MICROPROFILE_COUNTER_HISTORY +#define MICROPROFILE_COUNTER_HISTORY 1 +#endif + +#ifndef MICROPROFILE_MAX_GROUPS +#define MICROPROFILE_MAX_GROUPS 128 // must be multiple of 32 +#endif + +#ifndef MICROPROFILE_STACK_MAX +#define MICROPROFILE_STACK_MAX 64 +#endif + +#ifndef MICROPROFILE_BREAK_ON_PATCH_FAIL +#define MICROPROFILE_BREAK_ON_PATCH_FAIL 0 +#endif + +#ifndef MICROPROFILE_INSTRUMENT_MICROPROFILE +#define MICROPROFILE_INSTRUMENT_MICROPROFILE 0 +#endif + +#ifndef MICROPROFILE_MAX_DYNAMIC_TOKENS +#define MICROPROFILE_MAX_DYNAMIC_TOKENS 2048 +#endif + +#ifndef MICROPROFILE_INSTRUMENT_SYMBOLNAME_MAXLEN +#define MICROPROFILE_INSTRUMENT_SYMBOLNAME_MAXLEN 128 +#endif + +#ifndef MICROPROFILE_INSTRUMENT_MAX_MODULES +#define MICROPROFILE_INSTRUMENT_MAX_MODULES 256 +#endif + +#ifndef MICROPROFILE_INSTRUMENT_MAX_MODULE_CHARS +#define MICROPROFILE_INSTRUMENT_MAX_MODULE_CHARS (8 << 10) +#endif + +#ifndef MICROPROFILE_ASSERT_LOG_FREED +#define MICROPROFILE_ASSERT_LOG_FREED 0 +#endif + +#define MICROPROFILE_FLIP_FLAG_START_WEBSERVER 0x1 + +#if MICROPROFILE_WEBSERVER_AUTO_START +#define MICROPROFILE_FLIP_FLAG_DEFAULT (MICROPROFILE_FLIP_FLAG_START_WEBSERVER) +#else +#define MICROPROFILE_FLIP_FLAG_DEFAULT (0) +#endif + +#ifndef MICROPROFILE_GET_SETTINGS_FILE_PATH +#define MICROPROFILE_GET_SETTINGS_FILE_PATH "" +#endif + +#ifndef MICROPROFILE_MAX_CPU_CORES +#define MICROPROFILE_MAX_CPU_CORES 256 +#endif + +typedef enum MicroProfileTokenType_t +{ + MicroProfileTokenTypeCpu, + MicroProfileTokenTypeGpu, +} MicroProfileTokenType; + +struct MicroProfile; +struct MicroProfileThreadLogGpu; +struct MicroProfileScopeStateC; + +#ifdef __cplusplus +#define IF_CPP(exp) exp +#else +#define IF_CPP(exp) +#endif + +#ifdef __cplusplus +extern "C" +{ +#endif + +#define MICROPROFILE_INVALID_TOKEN ((uint64_t) - 1) +#define MICROPROFILE_TIMER_FLAG_SECTION 0x1 +#define MICROPROFILE_TIMER_FLAG_PLACEHOLDER 0x1000 +#define MICROPROFILE_CSV_FLAG_FRAME_NUMBERS 0x0 +#define MICROPROFILE_CSV_FLAG_FRAME_TIME 0x1 + + MICROPROFILE_API void MicroProfileInit(); + MICROPROFILE_API void MicroProfileShutdown(); + MICROPROFILE_API void MicroProfileStartAutoFlip(uint32_t nHz); + MICROPROFILE_API void MicroProfileStopAutoFlip(); + MICROPROFILE_API void MicroProfileEnableFrameExtraCounterData(); // enable per frame counter storage. uses more memory + MICROPROFILE_API MicroProfileToken MicroProfileFindToken(const char* sGroup, const char* sName); + MICROPROFILE_API MicroProfileToken MicroProfileGetToken(const char* sGroup, const char* sName, uint32_t nColor, MicroProfileTokenType Token, uint32_t flags); + MICROPROFILE_API void MicroProfileGetTokenC(MicroProfileToken* pToken, const char* sGroup, const char* sName, uint32_t nColor, MicroProfileTokenType Token, uint32_t flags); + MICROPROFILE_API MicroProfileToken MicroProfileGetCounterToken(const char* pName, uint32_t CounterTokenFlag); + MICROPROFILE_API MicroProfileToken MicroProfileGetChildCounterToken(MicroProfileToken Parent, const char* pName); // manually create the child/parent tree + MICROPROFILE_API void MicroProfileCounterAdd(MicroProfileToken nToken, int64_t nCount); + MICROPROFILE_API void MicroProfileCounterSet(MicroProfileToken nToken, int64_t nCount); + MICROPROFILE_API int64_t MicroProfileCounterGet(MicroProfileToken nToken); + MICROPROFILE_API void MicroProfileCounterSetDouble(MicroProfileToken nToken, double dCount); + MICROPROFILE_API double MicroProfileCounterGetDouble(MicroProfileToken nToken); + MICROPROFILE_API void MicroProfileCounterSetLimit(MicroProfileToken nToken, int64_t nCount); + MICROPROFILE_API void MicroProfileCounterSetLimitDouble(MicroProfileToken nToken, double nCount); + MICROPROFILE_API void MicroProfileCounterConfig(const char* pCounterName, uint32_t nFormat, int64_t nLimit, uint32_t nFlags); + MICROPROFILE_API void MicroProfileCounterConfigToken(MicroProfileToken nToken, uint32_t nFormat, int64_t nLimit, uint32_t nFlags); + MICROPROFILE_API void MicroProfileCounterSetPtr(const char* pCounterName, void* pValue, uint32_t nSize); + MICROPROFILE_API void MicroProfileCounterFetchCounters(); + MICROPROFILE_API void MicroProfileLocalCounterAdd(int64_t* pCounter, int64_t nCount); + MICROPROFILE_API int64_t MicroProfileLocalCounterSet(int64_t* pCounter, int64_t nCount); + MICROPROFILE_API uint64_t MicroProfileEnterInternal(MicroProfileToken nToken); + MICROPROFILE_API void MicroProfileLeaveInternal(MicroProfileToken nToken, uint64_t nTick); + MICROPROFILE_API uint64_t MicroProfileEnterInternalCStr(const char* pStr); + + MICROPROFILE_API void MicroProfileLeaveInternalCStr(const char* pStr, uint64_t nTickStart); + + MICROPROFILE_API void MicroProfileEnter(MicroProfileToken nToken); + MICROPROFILE_API void MicroProfileLeave(); + + MICROPROFILE_API void MicroProfileEnterSection(const char* pName, uint32_t nColor); + MICROPROFILE_API void MicroProfileLeaveSection(); + + MICROPROFILE_API void MicroProfileEnterGpu(MicroProfileToken nToken, struct MicroProfileThreadLogGpu* pLog); + MICROPROFILE_API void MicroProfileLeaveGpu(struct MicroProfileThreadLogGpu* pLog); + MICROPROFILE_API MicroProfileTimelineToken MicroProfileTimelineEnterInternal(uint32_t nColor, const char* pStr, uint32_t nStrLen, int bIsStaticString); + MICROPROFILE_API MicroProfileTimelineToken MicroProfileTimelineEnter(uint32_t nColor, const char* pStr); + MICROPROFILE_API MicroProfileTimelineToken MicroProfileTimelineEnterf(uint32_t nColor, const char* pStr, ...); + MICROPROFILE_API void MicroProfileTimelineLeave(MicroProfileTimelineToken id); + MICROPROFILE_API void MicroProfileTimelineEnterStatic(uint32_t nColor, const char* pStr); + MICROPROFILE_API void MicroProfileTimelineLeaveStatic(const char* pStr); + + MICROPROFILE_API uint64_t MicroProfileGpuEnterInternal(struct MicroProfileThreadLogGpu* pLog, MicroProfileToken nToken); + MICROPROFILE_API void MicroProfileGpuLeaveInternal(struct MicroProfileThreadLogGpu* pLog, MicroProfileToken nToken, uint64_t nTick); + MICROPROFILE_API uint64_t MicroProfileGpuEnterInternalCStr(struct MicroProfileThreadLogGpu* pGpuLog, const char* pStr); + MICROPROFILE_API void MicroProfileGpuLeaveInternalCStr(struct MicroProfileThreadLogGpu* pGpuLog, uint64_t nTickStart); + + MICROPROFILE_API void MicroProfileGpuBegin(void* pContext, struct MicroProfileThreadLogGpu* pLog); + MICROPROFILE_API void MicroProfileGpuSetContext(void* pContext, struct MicroProfileThreadLogGpu* pLog); + MICROPROFILE_API uint64_t MicroProfileGpuEnd(struct MicroProfileThreadLogGpu* pLog); + MICROPROFILE_API struct MicroProfileThreadLogGpu* MicroProfileThreadLogGpuAlloc(); + MICROPROFILE_API void MicroProfileThreadLogGpuFree(struct MicroProfileThreadLogGpu* pLog); + MICROPROFILE_API void MicroProfileThreadLogGpuReset(struct MicroProfileThreadLogGpu* pLog); + MICROPROFILE_API void MicroProfileGpuSubmit(int nQueue, uint64_t nWork); + MICROPROFILE_API int MicroProfileInitGpuQueue(const char* pQueueName); + MICROPROFILE_API void MicroProfileFreeGpuQueue(int nQueue); + MICROPROFILE_API int MicroProfileGetGpuQueue(const char* pQueueName); + + MICROPROFILE_API void MicroProfileFlip(void* pGpuContext, uint32_t FlipFlag IF_CPP(= MICROPROFILE_FLIP_FLAG_DEFAULT)); //! call once per frame. + MICROPROFILE_API void MicroProfileFlip_CB(void* pGpuContext, MicroProfileOnFreeze FreezeCB, uint32_t FlipFlag IF_CPP(= MICROPROFILE_FLIP_FLAG_DEFAULT)); //! call once per frame. + MICROPROFILE_API void MicroProfileToggleFrozen(); + MICROPROFILE_API int MicroProfileIsFrozen(); + MICROPROFILE_API int MicroProfileEnabled(); + MICROPROFILE_API void MicroProfileForceEnableGroup(const char* pGroup, MicroProfileTokenType Type); + MICROPROFILE_API void MicroProfileForceDisableGroup(const char* pGroup, MicroProfileTokenType Type); + MICROPROFILE_API float MicroProfileGetTime(const char* pGroup, const char* pName); + MICROPROFILE_API int MicroProfilePlatformMarkersGetEnabled(); // enable platform markers. disables microprofile markers + MICROPROFILE_API void MicroProfilePlatformMarkersSetEnabled(int bEnabled); // enable platform markers. disables microprofile markers + MICROPROFILE_API void MicroProfileContextSwitchSearch(uint32_t* pContextSwitchStart, uint32_t* pContextSwitchEnd, uint64_t nBaseTicksCpu, uint64_t nBaseTicksEndCpu); + MICROPROFILE_API void MicroProfileOnThreadCreate(const char* pThreadName); // should be called from newly created threads + MICROPROFILE_API void MicroProfileOnThreadExit(); // call on exit to reuse log + MICROPROFILE_API void MicroProfileInitThreadLog(); + MICROPROFILE_API void MicroProfileSetEnableAllGroups(int bEnable); + MICROPROFILE_API void MicroProfileEnableCategory(const char* pCategory); + MICROPROFILE_API void MicroProfileDisableCategory(const char* pCategory); + MICROPROFILE_API int MicroProfileGetEnableAllGroups(); + MICROPROFILE_API void MicroProfileSetForceMetaCounters(int bEnable); + MICROPROFILE_API int MicroProfileGetForceMetaCounters(); + MICROPROFILE_API void MicroProfileEnableMetaCounter(const char* pMet); + MICROPROFILE_API void MicroProfileDisableMetaCounter(const char* pMet); + MICROPROFILE_API void MicroProfileSetAggregateFrames(int frames); + MICROPROFILE_API int MicroProfileGetAggregateFrames(); + MICROPROFILE_API int MicroProfileGetCurrentAggregateFrames(); + MICROPROFILE_API struct MicroProfile* MicroProfileGet(); + MICROPROFILE_API void MicroProfileGetRange(uint32_t nPut, uint32_t nGet, uint32_t nRange[2][2]); + MICROPROFILE_API void MicroProfileStartContextSwitchTrace(); + MICROPROFILE_API void MicroProfileStopContextSwitchTrace(); + MICROPROFILE_API int MicroProfileIsLocalThread(uint32_t nThreadId); + MICROPROFILE_API int MicroProfileFormatCounter(int eFormat, int64_t nCounter, char* pOut, uint32_t nBufferSize); + MICROPROFILE_API struct MicroProfileThreadLogGpu* MicroProfileGetGlobalGpuThreadLog(); + MICROPROFILE_API int MicroProfileGetGlobalGpuQueue(); + MICROPROFILE_API void MicroProfileRegisterGroup(const char* pGroup, const char* pCategory, uint32_t nColor); +#if MICROPROFILE_PLATFORM_MARKERS + MICROPROFILE_API void MicroProfilePlatformMarkerBegin(uint32_t nColor, const char* pMarker); // not implemented by microprofile. + MICROPROFILE_API void MicroProfilePlatformMarkerEnd(); // not implemented by microprofile. +#endif + + MICROPROFILE_API float MicroProfileTickToMsMultiplierCpu(); + MICROPROFILE_API float MicroProfileTickToMsMultiplierGpu(); + MICROPROFILE_API int64_t MicroProfileTicksPerSecondCpu(); + MICROPROFILE_API uint64_t MicroProfileTick(); + + MICROPROFILE_API void MicroProfileLocalCounterAddAtomic(MicroProfileToken Token, int64_t nCount); + MICROPROFILE_API int64_t MicroProfileLocalCounterSetAtomic(MicroProfileToken, int64_t nCount); + + MICROPROFILE_API const char* MicroProfileCounterString(const char* String); + MICROPROFILE_API MicroProfileToken MicroProfileCounterTokenTree(MicroProfileToken* LastToken, MicroProfileToken CurrentParent, const char* pString); + MICROPROFILE_API MicroProfileToken MicroProfileCounterTokenTreeDynamic(MicroProfileToken* LastToken, MicroProfileToken Parent, const char* pString); + + MICROPROFILE_API void MicroProfileCsvConfigEnd(); + MICROPROFILE_API void MicroProfileCsvConfigBegin(uint32_t MaxTimers, uint32_t MaxGroups, uint32_t MaxCounters, uint32_t Flags); + MICROPROFILE_API void MicroProfileCsvConfigAddTimer(const char* Group, const char* Timer, const char* DisplayName IF_CPP(= nullptr), MicroProfileTokenType Type IF_CPP(= MicroProfileTokenTypeCpu)); + MICROPROFILE_API void MicroProfileCsvConfigAddGroup(const char* Group, const char* DisplayName IF_CPP(= nullptr)); + MICROPROFILE_API void MicroProfileCsvConfigAddCounter(const char* CounterName, const char* DisplayName IF_CPP(= nullptr)); + + MICROPROFILE_API void MicroProfileUpdateSettingsPath(); + +#if MICROPROFILE_IMGUI + // Basic support for controlling MicroProfile from ImGui, and for rendering graphs & tables + enum MicroProfileImguiAlign; + struct MicroProfileImguiWindowDesc; + struct MicroProfileImguiEntryDesc; + MICROPROFILE_API void MicroProfileImguiGraphs(const MicroProfileImguiWindowDesc& Window, const MicroProfileImguiEntryDesc* Entries, uint32_t NumEntries); + MICROPROFILE_API void MicroProfileImguiTable(const MicroProfileImguiWindowDesc& Window, const MicroProfileImguiEntryDesc* Entries, uint32_t NumEntries); + MICROPROFILE_API void MicroProfileImguiControls(); +#endif + +#ifdef __cplusplus +} +#endif + +#ifdef __cplusplus +struct MicroProfileThreadInfo +{ + // 3 first members are used to sort. dont reorder + uint32_t nIsLocal; + MicroProfileThreadIdType pid; + MicroProfileThreadIdType tid; + // 3 first members are used to sort. dont reorder + + const char* pThreadModule; + const char* pProcessModule; + MicroProfileThreadInfo() + : nIsLocal(0) + , pid(0) + , tid(0) + , pThreadModule("?") + , pProcessModule("?") + { + } + MicroProfileThreadInfo(uint32_t ThreadId, uint32_t ProcessId, uint32_t nIsLocal) + : nIsLocal(nIsLocal) + , pid(ProcessId) + , tid(ThreadId) + , pThreadModule("?") + , pProcessModule("?") + { + } + ~MicroProfileThreadInfo() + { + } +}; +MICROPROFILE_API MicroProfileThreadInfo MicroProfileGetThreadInfo(MicroProfileThreadIdType nThreadId); +MICROPROFILE_API uint32_t MicroProfileGetThreadInfoArray(MicroProfileThreadInfo** pThreadArray); +#endif + +#ifdef __cplusplus +extern "C" +{ +#endif + +#if MICROPROFILE_GPU_TIMERS_D3D12 + MICROPROFILE_API void MicroProfileGpuInitD3D12(void* pDevice, uint32_t nNodeCount, void** pCommandQueues, void** pCopyQueues); + MICROPROFILE_API void MicroProfileGpuShutdownD3D12(); + MICROPROFILE_API void MicroProfileSetCurrentNodeD3D12(uint32_t nNode); +#endif + +#if MICROPROFILE_GPU_TIMERS_VULKAN +#include + void MicroProfileGpuInitVulkan(VkDevice* pDevices, VkPhysicalDevice* pPhysicalDevices, VkQueue* pQueues, uint32_t* QueueFamily, uint32_t nNodeCount); + MICROPROFILE_API void MicroProfileGpuShutdownVulkan(); + MICROPROFILE_API void MicroProfileSetCurrentNodeVulkan(uint32_t nNode); +#endif + + MICROPROFILE_API void MicroProfileDumpFile(const char* pHtml, const char* pCsv, float fCpuSpike, float fGpuSpike, uint32_t FrameCount IF_CPP(= MICROPROFILE_WEBSERVER_DEFAULT_FRAMES)); + MICROPROFILE_API void MicroProfileDumpFileImmediately(const char* pHtml, const char* pCsv, void* pGpuContext, uint32_t FrameCount IF_CPP(= MICROPROFILE_WEBSERVER_DEFAULT_FRAMES)); + +#if MICROPROFILE_ENABLED && MICROPROFILE_WEBSERVER + MICROPROFILE_API uint32_t MicroProfileWebServerPort(); + MICROPROFILE_API void MicroProfileSetWebServerPort(uint32_t nPort); +#else +#define MicroProfileWebServerPort() ((uint32_t) - 1) +#define MicroProfileSetWebServerPort(a) \ + do \ + { \ + } while(0) +#endif + + typedef enum MicroProfileGpuTimerStateType_ + { + MicroProfileGpuTimerStateType_Invalid = 0, + MicroProfileGpuTimerStateType_D3D11, + MicroProfileGpuTimerStateType_D3D12, + MicroProfileGpuTimerStateType_Vulkan, + MicroProfileGpuTimerStateType_GL, + MicroProfileGpuTimerStateType_Custom + } MicroProfileGpuTimerStateType; + + typedef struct MicroProfileGpuTimerState_ + { + MicroProfileGpuTimerStateType Type; + } MicroProfileGpuTimerState; + +#if MICROPROFILE_GPU_TIMERS + typedef uint32_t(*MicroProfileGpuInsertTimeStamp_CB)(void* pContext); + typedef uint64_t(*MicroProfileGpuGetTimeStamp_CB)(uint32_t nKey); + typedef uint64_t(*MicroProfileTicksPerSecondGpu_CB)(); + typedef int (*MicroProfileGetGpuTickReference_CB)(int64_t* pOutCPU, int64_t* pOutGpu); + typedef uint32_t(*MicroProfileGpuFlip_CB)(void*); + typedef void (*MicroProfileGpuShutdown_CB)(); + + MICROPROFILE_API void MicroProfileGpuShutdownPlatform(); + + MICROPROFILE_API void MicroProfileGpuInitPlatform(MicroProfileGpuTimerStateType eType, + MicroProfileGpuTimerState* pGpuState, + MicroProfileGpuInsertTimeStamp_CB InsertTimeStamp, + MicroProfileGpuGetTimeStamp_CB GetTimeStamp, + MicroProfileTicksPerSecondGpu_CB TicksPerSecond, + MicroProfileGetGpuTickReference_CB GetTickReference, + MicroProfileGpuFlip_CB Flip, + MicroProfileGpuShutdown_CB Shutdown); +#else +#define MicroProfileGpuShutdownPlatform() \ + do \ + { \ + } while(0) +#define MicroProfileGpuInitPlatform(...) \ + do \ + { \ + } while(0) +#define MicroProfileGpuInsertTimeStamp(a) 1 +#define MicroProfileGpuGetTimeStamp(a) 0 +#define MicroProfileTicksPerSecondGpu() 1 +#define MicroProfileGetGpuTickReference(a, b) 0 +#define MicroProfileGpuFlip(a) 0 +#define MicroProfileGpuShutdown() \ + do \ + { \ + } while(0) + +#endif + +#if MICROPROFILE_GPU_TIMERS_D3D11 +#define MICROPROFILE_D3D11_MAX_QUERIES (32 << 10) + MICROPROFILE_API void MicroProfileGpuInitD3D11(void* pDevice, void* pDeviceContext); + MICROPROFILE_API void MicroProfileGpuShutdownD3D11(); +#endif + +#if MICROPROFILE_GPU_TIMERS_GL +#define MICROPROFILE_GL_MAX_QUERIES (8 << 10) + MICROPROFILE_API void MicroProfileGpuInitGL(); +#endif + +#if MICROPROFILE_USE_THREAD_NAME_CALLBACK + MICROPROFILE_API const char* MicroProfileGetThreadName(char* pzName); +#else +#define MicroProfileGetThreadName(a) 0 +#endif + +#ifdef __cplusplus +} +#endif + +struct MicroProfileScopeStateC +{ + MicroProfileToken Token; + int64_t nTick; +}; + +#ifdef __cplusplus +struct MicroProfileScopeHandler +{ + MicroProfileToken nToken; + uint64_t nTick; + MicroProfileScopeHandler(MicroProfileToken Token) + : nToken(Token) + { + nTick = MicroProfileEnterInternal(nToken); + } + ~MicroProfileScopeHandler() + { + MicroProfileLeaveInternal(nToken, nTick); + } +}; + +struct MicroProfileScopeHandlerCStr +{ + uint64_t nTick; + const char* pStr; + MicroProfileScopeHandlerCStr(const char* pStr) + : pStr(pStr) + { + nTick = MicroProfileEnterInternalCStr(pStr); + } + ~MicroProfileScopeHandlerCStr() + { + MicroProfileLeaveInternalCStr(pStr, nTick); + } +}; + +struct MicroProfileScopeGpuHandler +{ + MicroProfileToken nToken; + MicroProfileThreadLogGpu* pLog; + uint64_t nTick; + MicroProfileScopeGpuHandler(MicroProfileToken Token, MicroProfileThreadLogGpu* pLog) + : nToken(Token) + , pLog(pLog) + { + nTick = MicroProfileGpuEnterInternal(pLog, nToken); + } + ~MicroProfileScopeGpuHandler() + { + MicroProfileGpuLeaveInternal(pLog, nToken, nTick); + } +}; + +struct MicroProfileScopeGpuHandlerCStr +{ + MicroProfileThreadLogGpu* pLog; + uint64_t nTick; + MicroProfileScopeGpuHandlerCStr(const char* pStr, MicroProfileThreadLogGpu* pLog) + : pLog(pLog) + { + nTick = MicroProfileGpuEnterInternalCStr(pLog, pStr); + } + ~MicroProfileScopeGpuHandlerCStr() + { + MicroProfileGpuLeaveInternalCStr(pLog, nTick); + } +}; + +struct MicroProfileConditionalScopeHandler +{ + MicroProfileToken nToken; + uint64_t nTick; + MicroProfileConditionalScopeHandler(MicroProfileToken token, bool condition) + : nToken(token) + { + nTick = condition ? MicroProfileEnterInternal(token) : MICROPROFILE_INVALID_TOKEN; + } + ~MicroProfileConditionalScopeHandler() + { + if (nTick != MICROPROFILE_INVALID_TOKEN) + { + MicroProfileLeaveInternal(nToken, nTick); + } + } +}; +struct MicroProfileScopeTimelineExitHandler +{ + MicroProfileTimelineToken nToken; + MicroProfileScopeTimelineExitHandler(MicroProfileTimelineToken Token) + : nToken(Token) + { + } + ~MicroProfileScopeTimelineExitHandler() + { + MicroProfileTimelineLeave(nToken); + } +}; + +#if MICROPROFILE_IMGUI +enum MicroProfileImguiAlign +{ + MICROPROFILE_IMGUI_ALIGN_TOP_LEFT = 0, + MICROPROFILE_IMGUI_ALIGN_TOP_RIGHT = 1, + MICROPROFILE_IMGUI_ALIGN_BOTTOM_LEFT = 2, + MICROPROFILE_IMGUI_ALIGN_BOTTOM_RIGHT = 3, +}; + +struct MicroProfileImguiWindowDesc +{ + uint32_t Align = MICROPROFILE_IMGUI_ALIGN_TOP_LEFT; + int GraphWidth = 300; + int GraphHeight = 80; + int OffsetX = 0; + int OffsetY = 0; +}; +struct MicroProfileImguiEntryDesc +{ + MicroProfileToken GraphTimer; + float GraphMax = -1; +}; +#endif + +#endif //__cplusplus +#endif // enabled + +enum MicroProfileCounterFormat +{ + MICROPROFILE_COUNTER_FORMAT_DEFAULT = 0, + MICROPROFILE_COUNTER_FORMAT_BYTES = 1, +}; + +enum MicroProfileCounterFlags +{ + MICROPROFILE_COUNTER_FLAG_NONE = 0, + MICROPROFILE_COUNTER_FLAG_DETAILED = 0x1, + MICROPROFILE_COUNTER_FLAG_DETAILED_GRAPH = 0x2, + // internal: + MICROPROFILE_COUNTER_FLAG_INTERNAL_MASK = ~0x3, + MICROPROFILE_COUNTER_FLAG_HAS_LIMIT = 0x4, + MICROPROFILE_COUNTER_FLAG_CLOSED = 0x8, + MICROPROFILE_COUNTER_FLAG_MANUAL_SWAP = 0x10, + MICROPROFILE_COUNTER_FLAG_LEAF = 0x20, + MICROPROFILE_COUNTER_FLAG_DOUBLE = 0x40, + MICROPROFILE_COUNTER_FLAG_TYPE_MASK = MICROPROFILE_COUNTER_FLAG_DOUBLE, + MICROPROFILE_COUNTER_FLAG_TOKEN_DONT_CREATE = 0x80000000, +}; + +#endif // once + +#define MP_AUTO 0 +// from http://fugal.net/vim/rgbtxt.html +#define MP_SNOW 0xfffafa +#define MP_GHOSTWHITE 0xf8f8ff +#define MP_WHITESMOKE 0xf5f5f5 +#define MP_GAINSBORO 0xdcdcdc +#define MP_FLORALWHITE 0xfffaf0 +#define MP_OLDLACE 0xfdf5e6 +#define MP_LINEN 0xfaf0e6 +#define MP_ANTIQUEWHITE 0xfaebd7 +#define MP_PAPAYAWHIP 0xffefd5 +#define MP_BLANCHEDALMOND 0xffebcd +#define MP_BISQUE 0xffe4c4 +#define MP_PEACHPUFF 0xffdab9 +#define MP_NAVAJOWHITE 0xffdead +#define MP_MOCCASIN 0xffe4b5 +#define MP_CORNSILK 0xfff8dc +#define MP_IVORY 0xfffff0 +#define MP_LEMONCHIFFON 0xfffacd +#define MP_SEASHELL 0xfff5ee +#define MP_HONEYDEW 0xf0fff0 +#define MP_MINTCREAM 0xf5fffa +#define MP_AZURE 0xf0ffff +#define MP_ALICEBLUE 0xf0f8ff +#define MP_LAVENDER 0xe6e6fa +#define MP_LAVENDERBLUSH 0xfff0f5 +#define MP_MISTYROSE 0xffe4e1 +#define MP_WHITE 0xffffff +#define MP_BLACK 0x010101 +#define MP_DARKSLATEGRAY 0x2f4f4f +#define MP_DARKSLATEGREY 0x2f4f4f +#define MP_DIMGRAY 0x696969 +#define MP_DIMGREY 0x696969 +#define MP_SLATEGRAY 0x708090 +#define MP_SLATEGREY 0x708090 +#define MP_LIGHTSLATEGRAY 0x778899 +#define MP_LIGHTSLATEGREY 0x778899 +#define MP_GRAY 0xbebebe +#define MP_GREY 0xbebebe +#define MP_LIGHTGREY 0xd3d3d3 +#define MP_LIGHTGRAY 0xd3d3d3 +#define MP_MIDNIGHTBLUE 0x191970 +#define MP_NAVY 0x000080 +#define MP_NAVYBLUE 0x000080 +#define MP_CORNFLOWERBLUE 0x6495ed +#define MP_DARKSLATEBLUE 0x483d8b +#define MP_SLATEBLUE 0x6a5acd +#define MP_MEDIUMSLATEBLUE 0x7b68ee +#define MP_LIGHTSLATEBLUE 0x8470ff +#define MP_MEDIUMBLUE 0x0000cd +#define MP_ROYALBLUE 0x4169e1 +#define MP_BLUE 0x0000ff +#define MP_DODGERBLUE 0x1e90ff +#define MP_DEEPSKYBLUE 0x00bfff +#define MP_SKYBLUE 0x87ceeb +#define MP_LIGHTSKYBLUE 0x87cefa +#define MP_STEELBLUE 0x4682b4 +#define MP_LIGHTSTEELBLUE 0xb0c4de +#define MP_LIGHTBLUE 0xadd8e6 +#define MP_POWDERBLUE 0xb0e0e6 +#define MP_PALETURQUOISE 0xafeeee +#define MP_DARKTURQUOISE 0x00ced1 +#define MP_MEDIUMTURQUOISE 0x48d1cc +#define MP_TURQUOISE 0x40e0d0 +#define MP_CYAN 0x00ffff +#define MP_LIGHTCYAN 0xe0ffff +#define MP_CADETBLUE 0x5f9ea0 +#define MP_MEDIUMAQUAMARINE 0x66cdaa +#define MP_AQUAMARINE 0x7fffd4 +#define MP_DARKGREEN 0x006400 +#define MP_DARKOLIVEGREEN 0x556b2f +#define MP_DARKSEAGREEN 0x8fbc8f +#define MP_SEAGREEN 0x2e8b57 +#define MP_MEDIUMSEAGREEN 0x3cb371 +#define MP_LIGHTSEAGREEN 0x20b2aa +#define MP_PALEGREEN 0x98fb98 +#define MP_SPRINGGREEN 0x00ff7f +#define MP_LAWNGREEN 0x7cfc00 +#define MP_GREEN 0x00ff00 +#define MP_CHARTREUSE 0x7fff00 +#define MP_MEDIUMSPRINGGREEN 0x00fa9a +#define MP_GREENYELLOW 0xadff2f +#define MP_LIMEGREEN 0x32cd32 +#define MP_YELLOWGREEN 0x9acd32 +#define MP_FORESTGREEN 0x228b22 +#define MP_OLIVEDRAB 0x6b8e23 +#define MP_DARKKHAKI 0xbdb76b +#define MP_KHAKI 0xf0e68c +#define MP_PALEGOLDENROD 0xeee8aa +#define MP_LIGHTGOLDENRODYELLOW 0xfafad2 +#define MP_LIGHTYELLOW 0xffffe0 +#define MP_YELLOW 0xffff00 +#define MP_GOLD 0xffd700 +#define MP_LIGHTGOLDENROD 0xeedd82 +#define MP_GOLDENROD 0xdaa520 +#define MP_DARKGOLDENROD 0xb8860b +#define MP_ROSYBROWN 0xbc8f8f +#define MP_INDIANRED 0xcd5c5c +#define MP_SADDLEBROWN 0x8b4513 +#define MP_SIENNA 0xa0522d +#define MP_PERU 0xcd853f +#define MP_BURLYWOOD 0xdeb887 +#define MP_BEIGE 0xf5f5dc +#define MP_WHEAT 0xf5deb3 +#define MP_SANDYBROWN 0xf4a460 +#define MP_TAN 0xd2b48c +#define MP_CHOCOLATE 0xd2691e +#define MP_FIREBRICK 0xb22222 +#define MP_BROWN 0xa52a2a +#define MP_DARKSALMON 0xe9967a +#define MP_SALMON 0xfa8072 +#define MP_LIGHTSALMON 0xffa07a +#define MP_ORANGE 0xffa500 +#define MP_DARKORANGE 0xff8c00 +#define MP_CORAL 0xff7f50 +#define MP_LIGHTCORAL 0xf08080 +#define MP_TOMATO 0xff6347 +#define MP_ORANGERED 0xff4500 +#define MP_RED 0xff0000 +#define MP_HOTPINK 0xff69b4 +#define MP_DEEPPINK 0xff1493 +#define MP_PINK 0xffc0cb +#define MP_LIGHTPINK 0xffb6c1 +#define MP_PALEVIOLETRED 0xdb7093 +#define MP_MAROON 0xb03060 +#define MP_MEDIUMVIOLETRED 0xc71585 +#define MP_VIOLETRED 0xd02090 +#define MP_MAGENTA 0xff00ff +#define MP_VIOLET 0xee82ee +#define MP_PLUM 0xdda0dd +#define MP_ORCHID 0xda70d6 +#define MP_MEDIUMORCHID 0xba55d3 +#define MP_DARKORCHID 0x9932cc +#define MP_DARKVIOLET 0x9400d3 +#define MP_BLUEVIOLET 0x8a2be2 +#define MP_PURPLE 0xa020f0 +#define MP_MEDIUMPURPLE 0x9370db +#define MP_THISTLE 0xd8bfd8 +#define MP_SNOW1 0xfffafa +#define MP_SNOW2 0xeee9e9 +#define MP_SNOW3 0xcdc9c9 +#define MP_SNOW4 0x8b8989 +#define MP_SEASHELL1 0xfff5ee +#define MP_SEASHELL2 0xeee5de +#define MP_SEASHELL3 0xcdc5bf +#define MP_SEASHELL4 0x8b8682 +#define MP_ANTIQUEWHITE1 0xffefdb +#define MP_ANTIQUEWHITE2 0xeedfcc +#define MP_ANTIQUEWHITE3 0xcdc0b0 +#define MP_ANTIQUEWHITE4 0x8b8378 +#define MP_BISQUE1 0xffe4c4 +#define MP_BISQUE2 0xeed5b7 +#define MP_BISQUE3 0xcdb79e +#define MP_BISQUE4 0x8b7d6b +#define MP_PEACHPUFF1 0xffdab9 +#define MP_PEACHPUFF2 0xeecbad +#define MP_PEACHPUFF3 0xcdaf95 +#define MP_PEACHPUFF4 0x8b7765 +#define MP_NAVAJOWHITE1 0xffdead +#define MP_NAVAJOWHITE2 0xeecfa1 +#define MP_NAVAJOWHITE3 0xcdb38b +#define MP_NAVAJOWHITE4 0x8b795e +#define MP_LEMONCHIFFON1 0xfffacd +#define MP_LEMONCHIFFON2 0xeee9bf +#define MP_LEMONCHIFFON3 0xcdc9a5 +#define MP_LEMONCHIFFON4 0x8b8970 +#define MP_CORNSILK1 0xfff8dc +#define MP_CORNSILK2 0xeee8cd +#define MP_CORNSILK3 0xcdc8b1 +#define MP_CORNSILK4 0x8b8878 +#define MP_IVORY1 0xfffff0 +#define MP_IVORY2 0xeeeee0 +#define MP_IVORY3 0xcdcdc1 +#define MP_IVORY4 0x8b8b83 +#define MP_HONEYDEW1 0xf0fff0 +#define MP_HONEYDEW2 0xe0eee0 +#define MP_HONEYDEW3 0xc1cdc1 +#define MP_HONEYDEW4 0x838b83 +#define MP_LAVENDERBLUSH1 0xfff0f5 +#define MP_LAVENDERBLUSH2 0xeee0e5 +#define MP_LAVENDERBLUSH3 0xcdc1c5 +#define MP_LAVENDERBLUSH4 0x8b8386 +#define MP_MISTYROSE1 0xffe4e1 +#define MP_MISTYROSE2 0xeed5d2 +#define MP_MISTYROSE3 0xcdb7b5 +#define MP_MISTYROSE4 0x8b7d7b +#define MP_AZURE1 0xf0ffff +#define MP_AZURE2 0xe0eeee +#define MP_AZURE3 0xc1cdcd +#define MP_AZURE4 0x838b8b +#define MP_SLATEBLUE1 0x836fff +#define MP_SLATEBLUE2 0x7a67ee +#define MP_SLATEBLUE3 0x6959cd +#define MP_SLATEBLUE4 0x473c8b +#define MP_ROYALBLUE1 0x4876ff +#define MP_ROYALBLUE2 0x436eee +#define MP_ROYALBLUE3 0x3a5fcd +#define MP_ROYALBLUE4 0x27408b +#define MP_BLUE1 0x0000ff +#define MP_BLUE2 0x0000ee +#define MP_BLUE3 0x0000cd +#define MP_BLUE4 0x00008b +#define MP_DODGERBLUE1 0x1e90ff +#define MP_DODGERBLUE2 0x1c86ee +#define MP_DODGERBLUE3 0x1874cd +#define MP_DODGERBLUE4 0x104e8b +#define MP_STEELBLUE1 0x63b8ff +#define MP_STEELBLUE2 0x5cacee +#define MP_STEELBLUE3 0x4f94cd +#define MP_STEELBLUE4 0x36648b +#define MP_DEEPSKYBLUE1 0x00bfff +#define MP_DEEPSKYBLUE2 0x00b2ee +#define MP_DEEPSKYBLUE3 0x009acd +#define MP_DEEPSKYBLUE4 0x00688b +#define MP_SKYBLUE1 0x87ceff +#define MP_SKYBLUE2 0x7ec0ee +#define MP_SKYBLUE3 0x6ca6cd +#define MP_SKYBLUE4 0x4a708b +#define MP_LIGHTSKYBLUE1 0xb0e2ff +#define MP_LIGHTSKYBLUE2 0xa4d3ee +#define MP_LIGHTSKYBLUE3 0x8db6cd +#define MP_LIGHTSKYBLUE4 0x607b8b +#define MP_SLATEGRAY1 0xc6e2ff +#define MP_SLATEGRAY2 0xb9d3ee +#define MP_SLATEGRAY3 0x9fb6cd +#define MP_SLATEGRAY4 0x6c7b8b +#define MP_LIGHTSTEELBLUE1 0xcae1ff +#define MP_LIGHTSTEELBLUE2 0xbcd2ee +#define MP_LIGHTSTEELBLUE3 0xa2b5cd +#define MP_LIGHTSTEELBLUE4 0x6e7b8b +#define MP_LIGHTBLUE1 0xbfefff +#define MP_LIGHTBLUE2 0xb2dfee +#define MP_LIGHTBLUE3 0x9ac0cd +#define MP_LIGHTBLUE4 0x68838b +#define MP_LIGHTCYAN1 0xe0ffff +#define MP_LIGHTCYAN2 0xd1eeee +#define MP_LIGHTCYAN3 0xb4cdcd +#define MP_LIGHTCYAN4 0x7a8b8b +#define MP_PALETURQUOISE1 0xbbffff +#define MP_PALETURQUOISE2 0xaeeeee +#define MP_PALETURQUOISE3 0x96cdcd +#define MP_PALETURQUOISE4 0x668b8b +#define MP_CADETBLUE1 0x98f5ff +#define MP_CADETBLUE2 0x8ee5ee +#define MP_CADETBLUE3 0x7ac5cd +#define MP_CADETBLUE4 0x53868b +#define MP_TURQUOISE1 0x00f5ff +#define MP_TURQUOISE2 0x00e5ee +#define MP_TURQUOISE3 0x00c5cd +#define MP_TURQUOISE4 0x00868b +#define MP_CYAN1 0x00ffff +#define MP_CYAN2 0x00eeee +#define MP_CYAN3 0x00cdcd +#define MP_CYAN4 0x008b8b +#define MP_DARKSLATEGRAY1 0x97ffff +#define MP_DARKSLATEGRAY2 0x8deeee +#define MP_DARKSLATEGRAY3 0x79cdcd +#define MP_DARKSLATEGRAY4 0x528b8b +#define MP_AQUAMARINE1 0x7fffd4 +#define MP_AQUAMARINE2 0x76eec6 +#define MP_AQUAMARINE3 0x66cdaa +#define MP_AQUAMARINE4 0x458b74 +#define MP_DARKSEAGREEN1 0xc1ffc1 +#define MP_DARKSEAGREEN2 0xb4eeb4 +#define MP_DARKSEAGREEN3 0x9bcd9b +#define MP_DARKSEAGREEN4 0x698b69 +#define MP_SEAGREEN1 0x54ff9f +#define MP_SEAGREEN2 0x4eee94 +#define MP_SEAGREEN3 0x43cd80 +#define MP_SEAGREEN4 0x2e8b57 +#define MP_PALEGREEN1 0x9aff9a +#define MP_PALEGREEN2 0x90ee90 +#define MP_PALEGREEN3 0x7ccd7c +#define MP_PALEGREEN4 0x548b54 +#define MP_SPRINGGREEN1 0x00ff7f +#define MP_SPRINGGREEN2 0x00ee76 +#define MP_SPRINGGREEN3 0x00cd66 +#define MP_SPRINGGREEN4 0x008b45 +#define MP_GREEN1 0x00ff00 +#define MP_GREEN2 0x00ee00 +#define MP_GREEN3 0x00cd00 +#define MP_GREEN4 0x008b00 +#define MP_CHARTREUSE1 0x7fff00 +#define MP_CHARTREUSE2 0x76ee00 +#define MP_CHARTREUSE3 0x66cd00 +#define MP_CHARTREUSE4 0x458b00 +#define MP_OLIVEDRAB1 0xc0ff3e +#define MP_OLIVEDRAB2 0xb3ee3a +#define MP_OLIVEDRAB3 0x9acd32 +#define MP_OLIVEDRAB4 0x698b22 +#define MP_DARKOLIVEGREEN1 0xcaff70 +#define MP_DARKOLIVEGREEN2 0xbcee68 +#define MP_DARKOLIVEGREEN3 0xa2cd5a +#define MP_DARKOLIVEGREEN4 0x6e8b3d +#define MP_KHAKI1 0xfff68f +#define MP_KHAKI2 0xeee685 +#define MP_KHAKI3 0xcdc673 +#define MP_KHAKI4 0x8b864e +#define MP_LIGHTGOLDENROD1 0xffec8b +#define MP_LIGHTGOLDENROD2 0xeedc82 +#define MP_LIGHTGOLDENROD3 0xcdbe70 +#define MP_LIGHTGOLDENROD4 0x8b814c +#define MP_LIGHTYELLOW1 0xffffe0 +#define MP_LIGHTYELLOW2 0xeeeed1 +#define MP_LIGHTYELLOW3 0xcdcdb4 +#define MP_LIGHTYELLOW4 0x8b8b7a +#define MP_YELLOW1 0xffff00 +#define MP_YELLOW2 0xeeee00 +#define MP_YELLOW3 0xcdcd00 +#define MP_YELLOW4 0x8b8b00 +#define MP_GOLD1 0xffd700 +#define MP_GOLD2 0xeec900 +#define MP_GOLD3 0xcdad00 +#define MP_GOLD4 0x8b7500 +#define MP_GOLDENROD1 0xffc125 +#define MP_GOLDENROD2 0xeeb422 +#define MP_GOLDENROD3 0xcd9b1d +#define MP_GOLDENROD4 0x8b6914 +#define MP_DARKGOLDENROD1 0xffb90f +#define MP_DARKGOLDENROD2 0xeead0e +#define MP_DARKGOLDENROD3 0xcd950c +#define MP_DARKGOLDENROD4 0x8b6508 +#define MP_ROSYBROWN1 0xffc1c1 +#define MP_ROSYBROWN2 0xeeb4b4 +#define MP_ROSYBROWN3 0xcd9b9b +#define MP_ROSYBROWN4 0x8b6969 +#define MP_INDIANRED1 0xff6a6a +#define MP_INDIANRED2 0xee6363 +#define MP_INDIANRED3 0xcd5555 +#define MP_INDIANRED4 0x8b3a3a +#define MP_SIENNA1 0xff8247 +#define MP_SIENNA2 0xee7942 +#define MP_SIENNA3 0xcd6839 +#define MP_SIENNA4 0x8b4726 +#define MP_BURLYWOOD1 0xffd39b +#define MP_BURLYWOOD2 0xeec591 +#define MP_BURLYWOOD3 0xcdaa7d +#define MP_BURLYWOOD4 0x8b7355 +#define MP_WHEAT1 0xffe7ba +#define MP_WHEAT2 0xeed8ae +#define MP_WHEAT3 0xcdba96 +#define MP_WHEAT4 0x8b7e66 +#define MP_TAN1 0xffa54f +#define MP_TAN2 0xee9a49 +#define MP_TAN3 0xcd853f +#define MP_TAN4 0x8b5a2b +#define MP_CHOCOLATE1 0xff7f24 +#define MP_CHOCOLATE2 0xee7621 +#define MP_CHOCOLATE3 0xcd661d +#define MP_CHOCOLATE4 0x8b4513 +#define MP_FIREBRICK1 0xff3030 +#define MP_FIREBRICK2 0xee2c2c +#define MP_FIREBRICK3 0xcd2626 +#define MP_FIREBRICK4 0x8b1a1a +#define MP_BROWN1 0xff4040 +#define MP_BROWN2 0xee3b3b +#define MP_BROWN3 0xcd3333 +#define MP_BROWN4 0x8b2323 +#define MP_SALMON1 0xff8c69 +#define MP_SALMON2 0xee8262 +#define MP_SALMON3 0xcd7054 +#define MP_SALMON4 0x8b4c39 +#define MP_LIGHTSALMON1 0xffa07a +#define MP_LIGHTSALMON2 0xee9572 +#define MP_LIGHTSALMON3 0xcd8162 +#define MP_LIGHTSALMON4 0x8b5742 +#define MP_ORANGE1 0xffa500 +#define MP_ORANGE2 0xee9a00 +#define MP_ORANGE3 0xcd8500 +#define MP_ORANGE4 0x8b5a00 +#define MP_DARKORANGE1 0xff7f00 +#define MP_DARKORANGE2 0xee7600 +#define MP_DARKORANGE3 0xcd6600 +#define MP_DARKORANGE4 0x8b4500 +#define MP_CORAL1 0xff7256 +#define MP_CORAL2 0xee6a50 +#define MP_CORAL3 0xcd5b45 +#define MP_CORAL4 0x8b3e2f +#define MP_TOMATO1 0xff6347 +#define MP_TOMATO2 0xee5c42 +#define MP_TOMATO3 0xcd4f39 +#define MP_TOMATO4 0x8b3626 +#define MP_ORANGERED1 0xff4500 +#define MP_ORANGERED2 0xee4000 +#define MP_ORANGERED3 0xcd3700 +#define MP_ORANGERED4 0x8b2500 +#define MP_RED1 0xff0000 +#define MP_RED2 0xee0000 +#define MP_RED3 0xcd0000 +#define MP_RED4 0x8b0000 +#define MP_DEEPPINK1 0xff1493 +#define MP_DEEPPINK2 0xee1289 +#define MP_DEEPPINK3 0xcd1076 +#define MP_DEEPPINK4 0x8b0a50 +#define MP_HOTPINK1 0xff6eb4 +#define MP_HOTPINK2 0xee6aa7 +#define MP_HOTPINK3 0xcd6090 +#define MP_HOTPINK4 0x8b3a62 +#define MP_PINK1 0xffb5c5 +#define MP_PINK2 0xeea9b8 +#define MP_PINK3 0xcd919e +#define MP_PINK4 0x8b636c +#define MP_LIGHTPINK1 0xffaeb9 +#define MP_LIGHTPINK2 0xeea2ad +#define MP_LIGHTPINK3 0xcd8c95 +#define MP_LIGHTPINK4 0x8b5f65 +#define MP_PALEVIOLETRED1 0xff82ab +#define MP_PALEVIOLETRED2 0xee799f +#define MP_PALEVIOLETRED3 0xcd6889 +#define MP_PALEVIOLETRED4 0x8b475d +#define MP_MAROON1 0xff34b3 +#define MP_MAROON2 0xee30a7 +#define MP_MAROON3 0xcd2990 +#define MP_MAROON4 0x8b1c62 +#define MP_VIOLETRED1 0xff3e96 +#define MP_VIOLETRED2 0xee3a8c +#define MP_VIOLETRED3 0xcd3278 +#define MP_VIOLETRED4 0x8b2252 +#define MP_MAGENTA1 0xff00ff +#define MP_MAGENTA2 0xee00ee +#define MP_MAGENTA3 0xcd00cd +#define MP_MAGENTA4 0x8b008b +#define MP_ORCHID1 0xff83fa +#define MP_ORCHID2 0xee7ae9 +#define MP_ORCHID3 0xcd69c9 +#define MP_ORCHID4 0x8b4789 +#define MP_PLUM1 0xffbbff +#define MP_PLUM2 0xeeaeee +#define MP_PLUM3 0xcd96cd +#define MP_PLUM4 0x8b668b +#define MP_MEDIUMORCHID1 0xe066ff +#define MP_MEDIUMORCHID2 0xd15fee +#define MP_MEDIUMORCHID3 0xb452cd +#define MP_MEDIUMORCHID4 0x7a378b +#define MP_DARKORCHID1 0xbf3eff +#define MP_DARKORCHID2 0xb23aee +#define MP_DARKORCHID3 0x9a32cd +#define MP_DARKORCHID4 0x68228b +#define MP_PURPLE1 0x9b30ff +#define MP_PURPLE2 0x912cee +#define MP_PURPLE3 0x7d26cd +#define MP_PURPLE4 0x551a8b +#define MP_MEDIUMPURPLE1 0xab82ff +#define MP_MEDIUMPURPLE2 0x9f79ee +#define MP_MEDIUMPURPLE3 0x8968cd +#define MP_MEDIUMPURPLE4 0x5d478b +#define MP_THISTLE1 0xffe1ff +#define MP_THISTLE2 0xeed2ee +#define MP_THISTLE3 0xcdb5cd +#define MP_THISTLE4 0x8b7b8b +#define MP_GRAY0 0x000000 +#define MP_GREY0 0x000000 +#define MP_GRAY1 0x030303 +#define MP_GREY1 0x030303 +#define MP_GRAY2 0x050505 +#define MP_GREY2 0x050505 +#define MP_GRAY3 0x080808 +#define MP_GREY3 0x080808 +#define MP_GRAY4 0x0a0a0a +#define MP_GREY4 0x0a0a0a +#define MP_GRAY5 0x0d0d0d +#define MP_GREY5 0x0d0d0d +#define MP_GRAY6 0x0f0f0f +#define MP_GREY6 0x0f0f0f +#define MP_GRAY7 0x121212 +#define MP_GREY7 0x121212 +#define MP_GRAY8 0x141414 +#define MP_GREY8 0x141414 +#define MP_GRAY9 0x171717 +#define MP_GREY9 0x171717 +#define MP_GRAY10 0x1a1a1a +#define MP_GREY10 0x1a1a1a +#define MP_GRAY11 0x1c1c1c +#define MP_GREY11 0x1c1c1c +#define MP_GRAY12 0x1f1f1f +#define MP_GREY12 0x1f1f1f +#define MP_GRAY13 0x212121 +#define MP_GREY13 0x212121 +#define MP_GRAY14 0x242424 +#define MP_GREY14 0x242424 +#define MP_GRAY15 0x262626 +#define MP_GREY15 0x262626 +#define MP_GRAY16 0x292929 +#define MP_GREY16 0x292929 +#define MP_GRAY17 0x2b2b2b +#define MP_GREY17 0x2b2b2b +#define MP_GRAY18 0x2e2e2e +#define MP_GREY18 0x2e2e2e +#define MP_GRAY19 0x303030 +#define MP_GREY19 0x303030 +#define MP_GRAY20 0x333333 +#define MP_GREY20 0x333333 +#define MP_GRAY21 0x363636 +#define MP_GREY21 0x363636 +#define MP_GRAY22 0x383838 +#define MP_GREY22 0x383838 +#define MP_GRAY23 0x3b3b3b +#define MP_GREY23 0x3b3b3b +#define MP_GRAY24 0x3d3d3d +#define MP_GREY24 0x3d3d3d +#define MP_GRAY25 0x404040 +#define MP_GREY25 0x404040 +#define MP_GRAY26 0x424242 +#define MP_GREY26 0x424242 +#define MP_GRAY27 0x454545 +#define MP_GREY27 0x454545 +#define MP_GRAY28 0x474747 +#define MP_GREY28 0x474747 +#define MP_GRAY29 0x4a4a4a +#define MP_GREY29 0x4a4a4a +#define MP_GRAY30 0x4d4d4d +#define MP_GREY30 0x4d4d4d +#define MP_GRAY31 0x4f4f4f +#define MP_GREY31 0x4f4f4f +#define MP_GRAY32 0x525252 +#define MP_GREY32 0x525252 +#define MP_GRAY33 0x545454 +#define MP_GREY33 0x545454 +#define MP_GRAY34 0x575757 +#define MP_GREY34 0x575757 +#define MP_GRAY35 0x595959 +#define MP_GREY35 0x595959 +#define MP_GRAY36 0x5c5c5c +#define MP_GREY36 0x5c5c5c +#define MP_GRAY37 0x5e5e5e +#define MP_GREY37 0x5e5e5e +#define MP_GRAY38 0x616161 +#define MP_GREY38 0x616161 +#define MP_GRAY39 0x636363 +#define MP_GREY39 0x636363 +#define MP_GRAY40 0x666666 +#define MP_GREY40 0x666666 +#define MP_GRAY41 0x696969 +#define MP_GREY41 0x696969 +#define MP_GRAY42 0x6b6b6b +#define MP_GREY42 0x6b6b6b +#define MP_GRAY43 0x6e6e6e +#define MP_GREY43 0x6e6e6e +#define MP_GRAY44 0x707070 +#define MP_GREY44 0x707070 +#define MP_GRAY45 0x737373 +#define MP_GREY45 0x737373 +#define MP_GRAY46 0x757575 +#define MP_GREY46 0x757575 +#define MP_GRAY47 0x787878 +#define MP_GREY47 0x787878 +#define MP_GRAY48 0x7a7a7a +#define MP_GREY48 0x7a7a7a +#define MP_GRAY49 0x7d7d7d +#define MP_GREY49 0x7d7d7d +#define MP_GRAY50 0x7f7f7f +#define MP_GREY50 0x7f7f7f +#define MP_GRAY51 0x828282 +#define MP_GREY51 0x828282 +#define MP_GRAY52 0x858585 +#define MP_GREY52 0x858585 +#define MP_GRAY53 0x878787 +#define MP_GREY53 0x878787 +#define MP_GRAY54 0x8a8a8a +#define MP_GREY54 0x8a8a8a +#define MP_GRAY55 0x8c8c8c +#define MP_GREY55 0x8c8c8c +#define MP_GRAY56 0x8f8f8f +#define MP_GREY56 0x8f8f8f +#define MP_GRAY57 0x919191 +#define MP_GREY57 0x919191 +#define MP_GRAY58 0x949494 +#define MP_GREY58 0x949494 +#define MP_GRAY59 0x969696 +#define MP_GREY59 0x969696 +#define MP_GRAY60 0x999999 +#define MP_GREY60 0x999999 +#define MP_GRAY61 0x9c9c9c +#define MP_GREY61 0x9c9c9c +#define MP_GRAY62 0x9e9e9e +#define MP_GREY62 0x9e9e9e +#define MP_GRAY63 0xa1a1a1 +#define MP_GREY63 0xa1a1a1 +#define MP_GRAY64 0xa3a3a3 +#define MP_GREY64 0xa3a3a3 +#define MP_GRAY65 0xa6a6a6 +#define MP_GREY65 0xa6a6a6 +#define MP_GRAY66 0xa8a8a8 +#define MP_GREY66 0xa8a8a8 +#define MP_GRAY67 0xababab +#define MP_GREY67 0xababab +#define MP_GRAY68 0xadadad +#define MP_GREY68 0xadadad +#define MP_GRAY69 0xb0b0b0 +#define MP_GREY69 0xb0b0b0 +#define MP_GRAY70 0xb3b3b3 +#define MP_GREY70 0xb3b3b3 +#define MP_GRAY71 0xb5b5b5 +#define MP_GREY71 0xb5b5b5 +#define MP_GRAY72 0xb8b8b8 +#define MP_GREY72 0xb8b8b8 +#define MP_GRAY73 0xbababa +#define MP_GREY73 0xbababa +#define MP_GRAY74 0xbdbdbd +#define MP_GREY74 0xbdbdbd +#define MP_GRAY75 0xbfbfbf +#define MP_GREY75 0xbfbfbf +#define MP_GRAY76 0xc2c2c2 +#define MP_GREY76 0xc2c2c2 +#define MP_GRAY77 0xc4c4c4 +#define MP_GREY77 0xc4c4c4 +#define MP_GRAY78 0xc7c7c7 +#define MP_GREY78 0xc7c7c7 +#define MP_GRAY79 0xc9c9c9 +#define MP_GREY79 0xc9c9c9 +#define MP_GRAY80 0xcccccc +#define MP_GREY80 0xcccccc +#define MP_GRAY81 0xcfcfcf +#define MP_GREY81 0xcfcfcf +#define MP_GRAY82 0xd1d1d1 +#define MP_GREY82 0xd1d1d1 +#define MP_GRAY83 0xd4d4d4 +#define MP_GREY83 0xd4d4d4 +#define MP_GRAY84 0xd6d6d6 +#define MP_GREY84 0xd6d6d6 +#define MP_GRAY85 0xd9d9d9 +#define MP_GREY85 0xd9d9d9 +#define MP_GRAY86 0xdbdbdb +#define MP_GREY86 0xdbdbdb +#define MP_GRAY87 0xdedede +#define MP_GREY87 0xdedede +#define MP_GRAY88 0xe0e0e0 +#define MP_GREY88 0xe0e0e0 +#define MP_GRAY89 0xe3e3e3 +#define MP_GREY89 0xe3e3e3 +#define MP_GRAY90 0xe5e5e5 +#define MP_GREY90 0xe5e5e5 +#define MP_GRAY91 0xe8e8e8 +#define MP_GREY91 0xe8e8e8 +#define MP_GRAY92 0xebebeb +#define MP_GREY92 0xebebeb +#define MP_GRAY93 0xededed +#define MP_GREY93 0xededed +#define MP_GRAY94 0xf0f0f0 +#define MP_GREY94 0xf0f0f0 +#define MP_GRAY95 0xf2f2f2 +#define MP_GREY95 0xf2f2f2 +#define MP_GRAY96 0xf5f5f5 +#define MP_GREY96 0xf5f5f5 +#define MP_GRAY97 0xf7f7f7 +#define MP_GREY97 0xf7f7f7 +#define MP_GRAY98 0xfafafa +#define MP_GREY98 0xfafafa +#define MP_GRAY99 0xfcfcfc +#define MP_GREY99 0xfcfcfc +#define MP_GRAY100 0xffffff +#define MP_GREY100 0xffffff +#define MP_DARKGREY 0xa9a9a9 +#define MP_DARKGRAY 0xa9a9a9 +#define MP_DARKBLUE 0x00008b +#define MP_DARKCYAN 0x008b8b +#define MP_DARKMAGENTA 0x8b008b +#define MP_DARKRED 0x8b0000 +#define MP_LIGHTGREEN 0x90ee90 diff --git a/Minecraft.Client/Windows64/4JLibs/inc/Render/microprofile/microprofile_html.h b/Minecraft.Client/Windows64/4JLibs/inc/Render/microprofile/microprofile_html.h new file mode 100644 index 000000000..49a172773 --- /dev/null +++ b/Minecraft.Client/Windows64/4JLibs/inc/Render/microprofile/microprofile_html.h @@ -0,0 +1,17160 @@ +///start file generated from microprofile.html +#ifdef MICROPROFILE_EMBED_HTML +const char g_MicroProfileHtml_begin_0[] = +"\n" +"\n" +"\n" +"MicroProfile Capture\n" +"\n" +"\n" +" \n" +"\n" +"
drop .html file to compare
\n" +"
\n" +"
\n" +"
Group
\n" +"
Timer/Thread
\n" +"
\n" +"
Filter
\n" +"
Filter
\n" +"\n" +"\n" +"\n" +"
\n" +"
\n" +"History View:
\n" +"Click + Drag: Pan View
\n" +"Right Click + Drag : Zoom on region
\n" +"Click Frame : Center on frame
\n" +"
\n" +"Main View:
\n" +"Ctrl + Mouse up/down: Zoom
\n" +"Mousewheel : Zoom
\n" +"Right Click + Drag: Select region
\n" +"Ctrl + Shift + Drag: Select region
\n" +"Space: Zoom to Selection
\n" +"Ctrl + Drag: Pan
\n" +"Click + Drag: Pan
\n" +"hold alt: Show tooltip from timer view in detailed, detailed in timer
\n" +"x : Toggle View
\n" +"\\ : Switch color mode
\n" +"c : toggle collapse mode
\n" +"h : hightlight core types(context switch only)
\n" +"tab : go to filter view
\n" +"
\n" +"Detailed View:
\n" +"Tab: Go To Worst Instance
\n" +"Left/Right Arror: Next/Prev Instance
\n" +"Enter: Search for timer in view
\n" +"
\n" +"Timer Views:
\n" +"Tab: go to filtering
\n" +"Esc: Exit & Clear filter\n" +"
\n" +"\n" +"\n" +"\n" +"\n" +"\n" +"
CloseClose, Never Show
\n" +"
\n" +"\n" +"\n" +""; + +const size_t g_MicroProfileHtml_end_6_size = sizeof(g_MicroProfileHtml_end_6); +const char* g_MicroProfileHtml_end[] = { +&g_MicroProfileHtml_end_0[0], +&g_MicroProfileHtml_end_1[0], +&g_MicroProfileHtml_end_2[0], +&g_MicroProfileHtml_end_3[0], +&g_MicroProfileHtml_end_4[0], +&g_MicroProfileHtml_end_5[0], +&g_MicroProfileHtml_end_6[0], +}; +size_t g_MicroProfileHtml_end_sizes[] = { +sizeof(g_MicroProfileHtml_end_0), +sizeof(g_MicroProfileHtml_end_1), +sizeof(g_MicroProfileHtml_end_2), +sizeof(g_MicroProfileHtml_end_3), +sizeof(g_MicroProfileHtml_end_4), +sizeof(g_MicroProfileHtml_end_5), +sizeof(g_MicroProfileHtml_end_6), +}; +size_t g_MicroProfileHtml_end_count = 7; +#endif //MICROPROFILE_EMBED_HTML + +///end file generated from microprofile.html +///start file generated from microprofilelive.html +#ifdef MICROPROFILE_EMBED_HTML +const char g_MicroProfileHtmlLive_begin_0[] = +"\n" +"\n" +"\n" +"MicroProfile Live\n" +"\n" +"\n" +"\n" +"\n" +"\n" +"
drop .csv files to view CSV data
\n" +"
Filter
\n" +"
\n" +"History View:
\n" +"Right Click + Drag : Select Region
\n" +"Click + Drag: Move Selection
\n" +"Click Frame : Center on frame
\n" +"
\n" +"Main View:
\n" +"space: Freeze capturing
\n" +"x : Toggle View
\n" +"/ : Rotate connection port % 3
\n" +"Ctrl + Drag: Pan
\n" +"Click + Drag: Pan
\n" +"Enter: Capture selection/Next N Frames\n" +"
\n" +"\n" +"\n" +"\n" +"\n" +"
Close
\n" +"
\n" +"
\n" +"
\n" +"
\n" +"
\n" +"
\n" +" \n" +" \n" +"
\n" +"
\n" +"
\n" +"\n" +"\n" +"\n" +"\n" +"\n" +""; + +const size_t g_MicroProfileHtmlLive_begin_5_size = sizeof(g_MicroProfileHtmlLive_begin_5); +const char* g_MicroProfileHtmlLive_begin[] = { +&g_MicroProfileHtmlLive_begin_0[0], +&g_MicroProfileHtmlLive_begin_1[0], +&g_MicroProfileHtmlLive_begin_2[0], +&g_MicroProfileHtmlLive_begin_3[0], +&g_MicroProfileHtmlLive_begin_4[0], +&g_MicroProfileHtmlLive_begin_5[0], +}; +size_t g_MicroProfileHtmlLive_begin_sizes[] = { +sizeof(g_MicroProfileHtmlLive_begin_0), +sizeof(g_MicroProfileHtmlLive_begin_1), +sizeof(g_MicroProfileHtmlLive_begin_2), +sizeof(g_MicroProfileHtmlLive_begin_3), +sizeof(g_MicroProfileHtmlLive_begin_4), +sizeof(g_MicroProfileHtmlLive_begin_5), +}; +size_t g_MicroProfileHtmlLive_begin_count = 6; +const char* g_MicroProfileHtmlLive_end[] = { +""}; +size_t g_MicroProfileHtmlLive_end_sizes[] = { +0}; +size_t g_MicroProfileHtmlLive_end_count = 0; +#endif //MICROPROFILE_EMBED_HTML + +///end file generated from microprofilelive.html diff --git a/Minecraft.Client/Windows64/4JLibs/inc/Render/microprofile/microprofile_icons.h b/Minecraft.Client/Windows64/4JLibs/inc/Render/microprofile/microprofile_icons.h new file mode 100644 index 000000000..73c53cea8 --- /dev/null +++ b/Minecraft.Client/Windows64/4JLibs/inc/Render/microprofile/microprofile_icons.h @@ -0,0 +1,333 @@ +#ifdef MICROPROFILE_EMBED_HTML +#include + +const uint32_t uprof_16[] = { + 0x474e5089, 0x0a1a0a0d, 0x0d000000, 0x52444849, 0x10000000, 0x10000000, 0x00000308, 0x0f2d2800, 0x00000053, 0x47527301, 0xc9d90142, 0x00007f2c, 0x48700900, 0x00007359, 0x0000130b, 0x0001130b, + 0x00189c9a, 0x50ff0000, 0x0045544c, 0x3d190000, 0x366b3818, 0x296ad96f, 0x6b382757, 0x214b2336, 0x37245226, 0x6b383569, 0x183e1936, 0x3569d76e, 0x411b336e, 0x2652281a, 0x19183d19, 0x6b38183d, + 0x46924936, 0x20275729, 0x57291e49, 0x4fa45327, 0x38193e1a, 0x7d40366b, 0x5cbd603e, 0x2665ce6a, 0x6b382456, 0x366b3836, 0x4557b05b, 0x4f244286, 0x2e673122, 0x38275929, 0xcc68366b, 0x27572963, + 0x3c265628, 0xb65c3a75, 0x27572958, 0x19183d19, 0x7439183d, 0x23532636, 0x6b316933, 0x512566d1, 0x366b3823, 0x66366b38, 0x6b3862c9, 0x376d3936, 0x29408043, 0x5d2a2757, 0x27572927, 0x2f275729, + 0x3d192d62, 0x53ac5718, 0x2f4a964d, 0x3d192d62, 0x438d4718, 0x521a411b, 0x6b384f9f, 0x5ec36336, 0x1938793a, 0x6a37183d, 0x3b773e35, 0x385bb95f, 0x3d19366b, 0x183d1918, 0x5947954b, 0x9a4f55b0, + 0x2a5e2d4c, 0x2b366b38, 0x67252959, 0x00002581, 0x52745500, 0xff00534e, 0xf6ffffff, 0x33ffffff, 0xffffffff, 0x2d1ff3ff, 0x23fff1ff, 0xffa97fff, 0xc9ffffff, 0xffffff89, 0xff30ffff, 0xffa2ff25, + 0xffae5bbd, 0x5cffff8b, 0x81ffc34c, 0xffb2ff67, 0x27a17a3e, 0x38ffffff, 0x57ff89ff, 0xff62ffff, 0x2337ffff, 0xffffffb3, 0x6bffccff, 0x00656578, 0x49c30000, 0x78544144, 0x458f3d9c, 0x0c40c316, + 0xa8753d43, 0x9999940d, 0xe1999999, 0x4ce967fe, 0x2f3ff692, 0x190b4b2c, 0x05897180, 0xfc2b3102, 0x3a2dbeb0, 0xb7a1a751, 0x6f84c5bc, 0xc1d53ee9, 0xc7ea53f8, 0x32cf12e7, 0xf06d6a51, 0xa570fa2e, + 0x0c806d49, 0x2ab19ecb, 0xb4115114, 0xecba0663, 0xd08fc610, 0xa0a2f390, 0x0890a0f5, 0x4d98129e, 0x461a182b, 0x4c1ae922, 0x0bba8b5e, 0x1b0edf65, 0xaeaa9b29, 0xdc71cc61, 0x859c6503, 0x39084930, + 0x3a1e8e40, 0x4210f798, 0x40299087, 0xd6614b89, 0xb46e0c46, 0xb8add787, 0x3bb6a6f6, 0x3b91d558, 0x4bec0669, 0x40ef19e3, 0x38ae9954, 0x9ae6fffe, 0xaaf97f7e, 0xa5be5faf, 0x2b110712, 0x00001d18, + 0x45490000, 0x42ae444e, 0x00008260, +}; + +const uint32_t uprof_16_len = 650; + +const uint32_t uprof_32[] = { + 0x474e5089, 0x0a1a0a0d, 0x0d000000, 0x52444849, 0x20000000, 0x20000000, 0x00000308, 0x8aa44400, 0x000000c6, 0x47527301, 0xc9d90142, 0x00007f2c, 0x48700900, 0x00007359, 0x0000130b, 0x0001130b, + 0x00189c9a, 0x50b90100, 0x0045544c, 0x3d190000, 0x366b3818, 0x296ad96f, 0x4b232757, 0x224d2421, 0x34193f1a, 0x6b383264, 0x366b3836, 0x3a255727, 0x5729386e, 0x2b662e27, 0x3e2a5f2c, 0x3d193b76, + 0x285e2b18, 0x19366b38, 0x6b38183d, 0x67d46c36, 0x1f225224, 0x6b381d49, 0x285c2a36, 0x2255ae59, 0x3d192050, 0x2f673218, 0x38366b38, 0x3d19366b, 0x69d76e18, 0x2d1d461e, 0x713b2a63, 0x214c2339, + 0x3366d16b, 0x56283164, 0x40834326, 0x192c602d, 0x7137183d, 0x5dc16235, 0x38468d49, 0x5729366b, 0x366b3827, 0x383b7c3e, 0xd06a366b, 0x4c9f5065, 0x3057b55b, 0x83412d63, 0x2757293e, 0x6637763a, + 0x6b3862c8, 0x27572936, 0x19366b38, 0x6c34183d, 0x3f864331, 0x693e7b40, 0x743c64cd, 0x366b383a, 0x29245126, 0xc4642757, 0x366b3860, 0x38469049, 0x4b213574, 0x366b3820, 0x3c3f7e41, 0xbb603978, + 0x2757295c, 0x2552ab56, 0xb75c2351, 0x2e643158, 0x29183d19, 0x3d192757, 0x2c612f18, 0x1c183d19, 0x57291b41, 0x366b3827, 0x56275729, 0x864552a7, 0x183d1943, 0x29366b38, 0x57292757, 0x183d1927, + 0x5a366b38, 0x3d1956b0, 0x366b3818, 0x29275729, 0x4e232757, 0x366b3822, 0x21275729, 0x3d19204e, 0x183d1918, 0x19366b38, 0x5729183d, 0x27572927, 0x20469649, 0x612c1e48, 0x30613229, 0x535ab85e, + 0x6b3850a2, 0x366b3836, 0x291b421c, 0x7f3d2757, 0x4488473a, 0x19183d19, 0x3d19183d, 0x366b3818, 0x38356a37, 0x944d366b, 0x27572949, 0x4c3d823f, 0x9d514998, 0x2554274e, 0x3850a653, 0x6b38366b, + 0x2a5c2c36, 0x40e8e81c, 0x93000000, 0x534e5274, 0xffffff00, 0xffffffff, 0xff26fdff, 0xfffffcff, 0xd5ff19ff, 0xffff1127, 0xffffddff, 0x3aff0cff, 0xffff3709, 0xffffffff, 0xffffffff, 0xffffffcd, + 0xff8b0643, 0xffffffa7, 0xff84ffff, 0xce2b1eff, 0xffffffc1, 0xffc8ffff, 0xff9eff14, 0xff38ffff, 0xffc7ffff, 0x04ffffff, 0x802371b6, 0x8e01d6ff, 0xc0b424ff, 0x33301ded, 0x6d99de10, 0xade3f703, + 0xf028f9ff, 0xffc30e8d, 0xffffff2a, 0x19aefbff, 0xd3ffffbe, 0xff492f94, 0xff22ffe9, 0xffffffff, 0x2cffd806, 0x002e00bf, 0x49ff0100, 0x78544144, 0x8993759c, 0x18511257, 0x016f1fc5, 0x14150505, + 0x40410104, 0x2b525c91, 0x4d295cb7, 0x36972d41, 0xd17db4b5, 0xf6cb2f68, 0x2feb353d, 0x1ad9beee, 0xce67ba06, 0xbf7ce77c, 0x337df73b, 0x66210def, 0x51a33a1d, 0x8ff921e7, 0xeef5dd6e, 0x3bd76852, + 0x77065733, 0x8d26a69f, 0xe6e0b17e, 0xac5a4abe, 0x7e66f537, 0x512d37f2, 0xb7c4d3a3, 0xdfa5394b, 0x57b3470c, 0xd4d554ed, 0x26a24a9c, 0x8664ab51, 0xf68ccabe, 0xd8c66bc8, 0x21a50eec, 0x343689d4, + 0xd51e54fa, 0xd00b3be7, 0x67a1da91, 0x0af62350, 0xc315ba74, 0x56d17d35, 0x6d10b480, 0xec1ac671, 0xd7463a42, 0xc07a9dc1, 0x10a7783c, 0x84350862, 0x8bf21d2a, 0xfb01e8f5, 0xc7007b00, 0x754208e5, + 0x7c60c0f4, 0x430057c1, 0x2251cbe8, 0x1a38aec2, 0x5f2c7861, 0x80595e50, 0x6036e83f, 0x2b708f04, 0x936f5f19, 0x9df5da7a, 0xece92576, 0x48b0f824, 0x11c8754d, 0x6a60e9dc, 0xcf084ea9, 0xdf31b20b, + 0xa161fedb, 0x2ae5727d, 0xe127b2c0, 0xf73c2397, 0x77090227, 0x15d2fdc0, 0x33c69723, 0xdefe8591, 0x900cbe01, 0x3f1a6bad, 0x5fc27a86, 0xd936adb0, 0x1896811c, 0x0d35ca79, 0x72b8a933, 0x9b0e9d67, + 0x5c8447aa, 0x333f7957, 0x6dc6bdeb, 0xa99e4261, 0x95b40986, 0xcb2e4cf4, 0x7f8d79e2, 0xaa0e6bc4, 0x9cc04586, 0xd74053cb, 0x03d2e4b1, 0xa370c32e, 0x1dc8315a, 0x3e86fa32, 0x20bacdcb, 0xb5d30679, + 0x4c7cac3a, 0x1911cdd9, 0xc5808031, 0x7a988092, 0xf0c0ae20, 0x569f860b, 0x513b49fe, 0xbe71cf47, 0x5b060c12, 0x5848fc2c, 0x6bd3943f, 0x0c954f9c, 0xdff6a770, 0xc6469eb1, 0x79e5aac1, 0x832ac713, + 0xb45d3cd5, 0x293093c4, 0xce03c8d6, 0xbcde06f3, 0xfcefe659, 0x96ddef71, 0x2ec70f84, 0x4560cafa, 0x7cfadff7, 0x5a8be7b5, 0x0e16017f, 0xe8187143, 0x00000636, 0x45490000, 0x42ae444e, 0x00008260, +}; + +const uint32_t uprof_32_len = 1214; + +const uint32_t uprof_192[] = { + 0x474e5089, 0x0a1a0a0d, 0x0d000000, 0x52444849, 0xc0000000, 0xc0000000, 0x00000308, 0x9c026500, 0x00000035, 0x47527301, 0xc9d90142, 0x00007f2c, 0x48700900, 0x00007359, 0x0000130b, 0x0001130b, + 0x00189c9a, 0x50c20100, 0x0045544c, 0x3d190000, 0x366b3818, 0x296ad96f, 0x4b232757, 0x224d2421, 0x1a69d66e, 0x421c193f, 0x2556271b, 0x253e9443, 0x592b2351, 0x3e844129, 0x3e60c565, 0x753b3a8a, + 0x3c8e4039, 0x60295e2b, 0x54265bbd, 0x2b5c2d24, 0x49357f39, 0xcd69468f, 0x5ab85e64, 0x3c62c967, 0x88443885, 0x3f804141, 0x3567d36c, 0xc1633276, 0x2b622d5e, 0x2f366f38, 0x6b382c69, 0x51a75536, + 0x513b7b3e, 0x6b384da0, 0x66d06a36, 0x4f2e6030, 0x6a334c99, 0x49954c31, 0x30347a37, 0x5c292e65, 0x1d471e26, 0x3350a354, 0x8c463071, 0x55af5943, 0x4e366b38, 0x6b384a9c, 0x57b45b36, 0x382e6e31, + 0x6b37366b, 0x27572935, 0x20366b38, 0x3d191f4b, 0x366b3818, 0x19366b38, 0x5729183d, 0x53ab5627, 0x38366b38, 0x3d19366b, 0x366b3818, 0x38183d19, 0x3d19366b, 0x366b3818, 0x29366b38, 0x57292757, + 0x366b3827, 0x29275729, 0x3e192757, 0x183d1918, 0x38183d19, 0x3d19366b, 0x183d1918, 0x19366b38, 0x5729183d, 0x366b3827, 0x29275729, 0x57292757, 0x366b3827, 0x38183d19, 0x6b38366b, 0x46944a36, + 0x38366b38, 0x6b38366b, 0x27572936, 0x29183d19, 0x57292757, 0x27572927, 0x19366b38, 0x5729183d, 0x366b3827, 0x19183d19, 0x3d19183d, 0x183d1918, 0x19183d19, 0x6b38183d, 0x27572936, 0x29366b38, + 0x3d192757, 0x183d1918, 0x19366b38, 0x6b38183d, 0x183d1936, 0x29275729, 0x3d192757, 0x366b3818, 0x29275729, 0x3d192757, 0x27572918, 0x45366b38, 0x57294098, 0x2d633027, 0x6d448c47, 0xa05068d6, + 0x62c8664d, 0x2868d56d, 0xd56d2656, 0xe6ab1468, 0x000000dc, 0x4e527496, 0xffff0053, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xfffcffff, + 0xff02ffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xfff6ffee, 0x03ff1dff, 0x0e03ffd6, 0xff0cfb40, 0xcf231507, 0xb61b2312, 0x85fd2495, 0x0b4ed0e7, 0x70cbaca5, 0xc7f79fde, 0xe7f03dc4, 0xff7853f3, + 0x864ca429, 0xa7dc17ec, 0x7173e5bf, 0x7e96bdb1, 0xb363d536, 0x5d89312e, 0x3e37645c, 0x8c2c9d49, 0x5bdb5366, 0x0593ff6c, 0x78b998d4, 0x67d57f01, 0x007fccad, 0x49831000, 0x78544144, 0xf95bed9c, + 0x1f47135f, 0x3b9b26ce, 0x1721b721, 0x80920490, 0x84120102, 0x4a9c8080, 0x0aada811, 0xa282d56a, 0xb5bc55a8, 0xd5eb155a, 0xe8f7db6a, 0x33befffb, 0xbb3639bb, 0x4863ddf3, 0xf1f3f636, 0x7491a1f9, + 0xbe679f74, 0x31677ce7, 0x2be2be18, 0xe819a2be, 0xb9313d9d, 0x7f7f7cf2, 0x7f6e6e6e, 0xe4cafaff, 0x56ded989, 0xa58b5253, 0x6b4fb995, 0x1c2fa777, 0x5631ef1d, 0xf47e33d0, 0xdaeaf4c2, 0x2fc9b9b3, + 0xcaef465a, 0xd31bd3d5, 0x8c24da35, 0xdb5c6f4f, 0x53356c9d, 0xf7f91002, 0xc9e4b697, 0xad6c4557, 0xb2fdde9e, 0xdafd716c, 0xee2ab8f2, 0xf4bd1c35, 0xb0d10be9, 0x4176bbb4, 0xa60af923, 0xb52fefdf, + 0xf3ef7d98, 0xeeb4976b, 0xee1d9d7d, 0xbd0ed4b6, 0x6107ab73, 0xb1b0d65f, 0x93fa22df, 0x475d68d7, 0x27fbe58c, 0xbbdf6774, 0xf451ba72, 0xefa67a11, 0x4f5a9ceb, 0x38da3fbc, 0xaeb61cfa, 0x93cf18e9, + 0xfaee0fef, 0x67a70904, 0xfb3fa1f5, 0xf409ba69, 0xaf967a11, 0x7f6121ea, 0x39f439b9, 0x37af7709, 0x86e4fe9b, 0x5a8176ba, 0x46a6fbc2, 0x06abd2f3, 0xcb001ea6, 0x6cabcdbb, 0xddcdabb3, 0x6b3d1e7e, + 0xde848a4d, 0xd03a5b57, 0xdbb0b8c7, 0x6bb3fe0c, 0x649dc8cd, 0xbc6fdf8c, 0x989a5611, 0x6d58087c, 0x5b6782b4, 0x6547f2ba, 0xb191ae6d, 0x3f5f74bc, 0x782aa9f7, 0x4b0538d6, 0x47d3ba6b, 0x28dd39e8, + 0xb52e2705, 0x8dc23f82, 0x2bd974c6, 0x346fe2ad, 0xc142346e, 0x6eb526f5, 0xf07cb0aa, 0xab7cf464, 0x8291fc85, 0xfb777683, 0xe47e6fcd, 0xcd60e971, 0xf356f3dd, 0x0fa71a37, 0x5b2b0712, 0x708fa6ad, + 0xbf9e8569, 0x0403edbe, 0xf662b6ea, 0x97a9ab6e, 0xd1574ab1, 0xfe14be96, 0xcd1ae346, 0x74e962c4, 0x30d769ab, 0x06c29f7e, 0x7d38f4cf, 0x9cf8c5a9, 0x8dbbfe66, 0x0c1d5c9e, 0xb416b547, 0x002fad13, + 0x56d558f5, 0xb556960e, 0x99edf3fd, 0x48c8c8cc, 0x823b972e, 0xd0303031, 0x23e8a1ce, 0x2f10ef2f, 0x77d7e1df, 0x4d623cc8, 0xd35c1853, 0x3498dfc6, 0x9c391111, 0x211f2e27, 0xb00d372f, 0x8c47c0c7, + 0x7fc0d2bf, 0xff1b4b6e, 0xd0991f6c, 0x4fd2cba8, 0x04041f5a, 0x586ecd64, 0x5df551c1, 0x0ed692d1, 0xb420007a, 0x485b3663, 0x9af40036, 0x053966dc, 0x8356d437, 0xf91af7de, 0x4201471b, 0x70523369, + 0x03bcb258, 0xcb431e58, 0x745e32cd, 0x6903ad4d, 0x2103c13e, 0x64947780, 0x1960c803, 0x06c65cbc, 0x3adaea5b, 0xda442ded, 0x04a3b32a, 0x0101441b, 0x21fd7e59, 0x555dc150, 0x10b7b402, 0x88d08009, + 0x07c0a899, 0xbb9f4084, 0xc1b61ebc, 0x27fc1555, 0x286097b5, 0x5477962b, 0x810a00ac, 0xdeb2a5a4, 0x59693d0d, 0xdef51339, 0x0a1fccd0, 0x6643ce81, 0x0a2382a2, 0x045ec306, 0xa2b4ea3d, 0x3ded3980, + 0x6953b65c, 0x01029147, 0xb5672825, 0x368396d5, 0xc714aa38, 0x0c087b5a, 0x66320428, 0x4836aa21, 0xadcbc87b, 0x0a1566fd, 0x7808286e, 0xcd043145, 0x758a0201, 0xd16eaa02, 0xe82c1801, 0xa1172e20, + 0xed2dbe47, 0x416299a5, 0x5ebfb159, 0xa1536540, 0x2f231810, 0x1ebe4317, 0xf7f052ef, 0x3c84c8c5, 0x72a3116e, 0x02012f82, 0x89cb243a, 0xdb3d938f, 0x218cd12c, 0xcb19408a, 0xf88cc458, 0x9af4054d, + 0x64e59252, 0x02321b05, 0x07f052ae, 0x424240ab, 0x7c746b88, 0x6108f2c0, 0xc68bd139, 0xb011c39f, 0x6d402341, 0x8e94f008, 0xa3e58115, 0xa216d584, 0xa444e00d, 0x00c38bbd, 0x8bab031a, 0x2eccc024, + 0x88da0202, 0xc60a49cb, 0x9525d3c1, 0xa100b201, 0xd9909120, 0x41015405, 0x22f456d9, 0xa59eada8, 0xbd40441a, 0x962b38b1, 0x80aa0291, 0xa2306c87, 0x26032d17, 0x1fc34678, 0x32698acc, 0x33862821, + 0x2f64e5e4, 0xb3a53722, 0xbe315754, 0x84509908, 0x0c81d325, 0xa059ca46, 0x74b0f202, 0xd7146a9f, 0xa0815401, 0x00700ab4, 0xe625660c, 0x2f521e2c, 0x10fa22da, 0xda142cdd, 0x9bb01cd1, 0x024b1940, + 0xf06c0306, 0x041e61a9, 0xc692c6f6, 0xb3940780, 0x8290bd00, 0x5d808269, 0x069022aa, 0x3cd6330e, 0x2018c999, 0xaa055c80, 0x0f517b10, 0x774664e1, 0x9401df1b, 0x5c6f5131, 0x0fb65405, 0x1c100bcb, + 0x933c7827, 0xe69ba00a, 0xcb8dedaa, 0x4f210170, 0xa844846a, 0xa486c1b7, 0xe2a15ea9, 0xfa0435af, 0x46a4e581, 0x2f7a8448, 0x3e888e92, 0x1a0009d1, 0x02101303, 0x10a72750, 0xbcea3c02, 0xedea3c48, + 0x02217ba5, 0xe47dabaa, 0xa601602a, 0x242350ba, 0x6b06d082, 0xe8270122, 0xf3a0642e, 0xc0cc897c, 0x127c840d, 0xfac48468, 0x45960a05, 0x8a8e78c5, 0x080331bf, 0x38332768, 0x1b872c06, 0x5e6a1121, + 0x6bb61e24, 0x01e0d1bd, 0x119a484e, 0x366a1c02, 0x14743510, 0x3c2338f3, 0x55018ad2, 0xc8cc3800, 0xd4224235, 0x41f57c46, 0x834af9a0, 0x40a3b407, 0x83003808, 0x21bd57d5, 0x885ee9e9, 0x3ae5828a, + 0x62e9cf01, 0xefcf3a18, 0x03ed9f90, 0xd55d58ca, 0xf2c378b3, 0xd17b2846, 0x7b295d78, 0xadab7745, 0xa317e830, 0x8772cd5d, 0x939f2e33, 0x37545e2a, 0xa56ba3a2, 0x1f4cc3a1, 0xd95004e0, 0xe9bf200e, + 0xf6ea2cae, 0x26f243fb, 0x5d517b28, 0xe71d5314, 0x4b80ed8d, 0x042d8094, 0xc8be00aa, 0x1657f419, 0x439dbb85, 0x7ab8020a, 0x508ffd90, 0x418bdd00, 0xf92f75e0, 0x396029b9, 0x677102ae, 0x3fc87459, + 0x628f97e3, 0x4745ef33, 0x201425fe, 0xe10ee18a, 0x24b67995, 0x944c5de2, 0xf75d00ff, 0x858fc8fa, 0x72f0666e, 0xbbdb657f, 0x84f0b6e3, 0x45ec0092, 0x59ff7fc9, 0xa22b3fe3, 0x3e721418, 0x30bd1262, + 0x9ff28910, 0x980aa069, 0x1e5d5324, 0xabe00840, 0x5e338b31, 0x278517e0, 0x652eae98, 0xcefe9601, 0x0135dbe9, 0x9d3a5ed5, 0x3d1472c9, 0xa6a3be02, 0x121857fa, 0x48af1891, 0x0a57baf6, 0xc61c7007, + 0x3bd67fc7, 0x3989e711, 0x7d774c43, 0x2311751e, 0x71fa552a, 0x0d09d09d, 0xd3b00698, 0x4db52358, 0xedac7b4f, 0x09d9e6f5, 0x21a6cb16, 0x202c9163, 0x5205532f, 0xc51f0761, 0x199db881, 0x3dfdd6f2, + 0x4840bfaf, 0xe5b0854b, 0x65404d45, 0xf3ed9d2a, 0x7b47d064, 0xf4db5929, 0x4474a7a2, 0x33a3bfc8, 0x0d951035, 0xd330e001, 0x55c72c06, 0xcc3130a0, 0x3760f147, 0xc2bcb8c3, 0x53f9773d, 0x54694e5d, + 0x676c87c4, 0x97ee026a, 0x77048dc9, 0x81d1b730, 0x91e71ad1, 0x508cb1d1, 0xf5946640, 0x708da675, 0x4bace815, 0xcaee8122, 0x89b800d1, 0x1fcec4ea, 0x01fdd94f, 0x618bad8e, 0x1de5bd86, 0x61523ee7, + 0x171dd31b, 0x87790ff3, 0x01359799, 0x3a2397e5, 0x27ace962, 0xb2c74c6d, 0x0e28b563, 0x4309b4ee, 0xb349e7da, 0x99096287, 0x394ca810, 0x4f51b827, 0x579873f8, 0x17e60160, 0xb66b2741, 0x970c43f8, + 0x1b4e027b, 0x7cbcd8c3, 0x15700a46, 0x4664db0d, 0x5e52db52, 0xd25e731b, 0xd847468b, 0x7a8b27bc, 0xd56dc3cc, 0xa6774e28, 0x1a4149d3, 0x2cdd45ca, 0x8728cb9b, 0x4e33eb80, 0xd0e71626, 0xadef17a0, + 0x63281fb9, 0x3982aec5, 0x52661ce4, 0x57516883, 0xc8308317, 0xd78b6595, 0x7e2d3e89, 0x9b06fe07, 0x09a81529, 0xd71f0bc0, 0x0c56ac04, 0x84c1b2c0, 0xd36318b1, 0x5f70939c, 0x48f95326, 0x53c026b6, + 0xe7d65964, 0xf9e7c1f1, 0xe1372772, 0x600e738c, 0xcab817b7, 0x7cde1d04, 0xd0863fc8, 0xb7cf63c6, 0xa65b31c4, 0x80d1e5c0, 0x5b8c2bb3, 0x4df461a3, 0x2bdd8fc0, 0xe7996d8e, 0xc037adf2, 0x04d4bf30, + 0xa477af70, 0x5b883b93, 0x1b98c466, 0x157df365, 0x963923ce, 0xba42d6b5, 0x8b479623, 0xf0097ddc, 0x722a6330, 0xbbf8b6e6, 0x53c72bb1, 0x3843f1e8, 0x5bc3a140, 0xe734e42a, 0x77630cbd, 0xaf01cc3a, + 0x70080b1d, 0x089e1d01, 0x94e65df9, 0x8c337f1f, 0xdc5767fe, 0xebd29d8f, 0xcec3600c, 0x5123cc30, 0x3b103127, 0x07b5b884, 0xeb04d18d, 0xd9d4a24e, 0x9a22cb1e, 0xa9eb2c1d, 0x1628d1e5, 0x198bb2c9, + 0x632a0263, 0x6e2ece63, 0xeee1ecff, 0xb571fef2, 0x73dbba15, 0x41857220, 0x329bcab4, 0xb44fe177, 0x2075e0be, 0x78b6c3e6, 0xc854ecaa, 0x99ce0287, 0xd632f202, 0x98b7e13f, 0xf4a46103, 0x5fc04d62, + 0xd8933a38, 0xc46cd839, 0x8647e9e7, 0x7db221c8, 0x13b674d1, 0xe71478ed, 0x2e4523a4, 0x0273f687, 0x1434c912, 0x20d2a602, 0x0b88284e, 0x2f6e8df2, 0xb8a1f8f7, 0x772d0f25, 0x30a9bcd8, 0x5c26540a, + 0xc3031ce0, 0xc2bb0e1e, 0xb4970605, 0x8df710f8, 0x609f1e23, 0xb98b4e42, 0xcdec30ab, 0x1826bb9b, 0xfeff80a1, 0xa853812f, 0xb7eb531b, 0x88027bb6, 0xebe8c961, 0xdbda46cc, 0x50add863, 0x38ac26cc, + 0x8e517394, 0xe8758e3f, 0x6a34c362, 0x905b889f, 0x272e3347, 0x3dd841ef, 0xd450d002, 0x050a6294, 0x9a2d086f, 0x63b0bb08, 0x1e217ff0, 0xc6185a77, 0x87f874e2, 0x9138a379, 0x29bdd977, 0x0cc5d397, + 0x2c7e1df9, 0xc0555ceb, 0x8a457f9f, 0x472cbebe, 0x968b102d, 0x8f31c7d0, 0xcee2332f, 0xe8238246, 0x3ab85407, 0x3b58c53c, 0x5e3ea1ca, 0x8398bb00, 0xb514c25c, 0xd405e3d9, 0x64763193, 0xccf1fe6d, + 0x7aaa17a0, 0xf0e31f26, 0xbc86cb72, 0xb2b65d13, 0xf00b5400, 0x61bcdb1e, 0x14abf96f, 0x4f0818d6, 0xc3c69360, 0x009d7b4e, 0x9e63f7c3, 0xa78062d2, 0x431cea56, 0x99d43dff, 0xa3b22088, 0x30ef2cd6, + 0x58f745c1, 0xb78478d4, 0xe40afe12, 0x90588a6f, 0x29d8bdad, 0x32e1fd06, 0x1418c09f, 0x6d9ca631, 0xf31fb3f8, 0x03faf199, 0x5cc3154c, 0x073b7738, 0x4eec0119, 0x1d77fe3f, 0x0533ce4e, 0x3bbdf031, + 0x532fea67, 0xe07531db, 0x83d44f0f, 0xc531655d, 0x324ccd65, 0xb2c7f6cf, 0x22f29dc9, 0x00a069e4, 0x42250253, 0xe38c00ec, 0xe4fcaa73, 0x11b55002, 0x338c033a, 0xfc4979ce, 0xc22330f3, 0xbaeee7f0, + 0x49bcc39f, 0x0122130b, 0x85775997, 0xe3ebeb3f, 0x4d6a38fe, 0x31b655b0, 0x8247762e, 0xe0e50e5c, 0xc3f3de86, 0x3d8c4294, 0x9162e364, 0xde80e467, 0x1a50e167, 0xa699f30e, 0x8fe62e2b, 0xfdd50bbe, + 0xfc121968, 0x3fa34820, 0x12a435b3, 0xc58c9b9c, 0xdcafd552, 0xf9412e51, 0xf57739ac, 0x700aa82b, 0xc18f2a94, 0xec73606e, 0x5845adc8, 0xab7176ec, 0x00a90e5d, 0x5dfa4dbb, 0xf6428205, 0xef209596, + 0x87c0fc13, 0x35573b87, 0xfa0fdb13, 0x4edf4634, 0x7627cf80, 0xbb24b647, 0x18a44392, 0x10c26982, 0xa53712b4, 0x75001402, 0x63583572, 0x26444f1e, 0x821a4452, 0x0156bca5, 0x9eee6682, 0x5a600166, + 0x467586a7, 0x043d7299, 0xcd47f4ed, 0x1691a002, 0xf2b5163d, 0x9a070467, 0xf7f95a2b, 0x129169c3, 0x633294d6, 0x04d49e11, 0xd0156bac, 0xd5b9daa6, 0xcad76822, 0x4d64fb7f, 0x015682c0, 0x0a9d89b2, + 0x7806cd09, 0x804d4dfa, 0x028d3be1, 0x1686cec0, 0x500d4211, 0x02b383c0, 0xf1b59701, 0x76e72b77, 0x14c9c88d, 0x4282002c, 0xd1dc306c, 0x68dd2026, 0xcd7ea6fe, 0xc06585fc, 0x9b5bf860, 0xf3469d80, + 0x8037698f, 0x0c1a8b30, 0x85ab7fbf, 0x55e1bbbf, 0x7f3b5da0, 0xc050a4e6, 0x7c514d0b, 0x1e25fa18, 0xa3c58005, 0xff9da5cf, 0x80a171fc, 0x08b5efa9, 0xe5e7ee80, 0xa087cc91, 0x30a195e4, 0x0ea1a6c0, + 0xba18a29a, 0x758392eb, 0x019c750c, 0xa83114e0, 0x478257cc, 0xb53402f5, 0xabbbd073, 0x9df20832, 0x06d10cc2, 0x1c7dcd49, 0xd4618a23, 0xe288690d, 0x14070a43, 0x95fd4a29, 0x236f632c, 0x2af68fc4, + 0x4eb730cc, 0xfdb80bd5, 0x3e017aa3, 0xc81837e8, 0xd8b9da41, 0xdaeb3825, 0x04891902, 0x851855ed, 0x017af1c0, 0x316a1a53, 0x797c0554, 0xed3c6982, 0xab96d392, 0x4a2ebf58, 0x021c5fc1, 0xf507c306, + 0x6ea9a002, 0x16f9b188, 0x26b1d605, 0xfc965973, 0x98a14b78, 0x6bce568b, 0x2f011a05, 0xd5d602c5, 0x12300107, 0x1106e3ea, 0x973160ec, 0x76e4cc59, 0x1981d2da, 0x1b6afab9, 0x74f9b176, 0x4dada3b6, + 0x81514c32, 0x02c5275b, 0xe5a93f0c, 0x4b394d0f, 0x8b491b34, 0x0225c773, 0x002b50fe, 0xc9a2ebba, 0x7890d425, 0xebf0c190, 0x80052a0f, 0xf91d7e83, 0x0a929fcf, 0x4dd95058, 0x473f6f40, 0x2d11aa68, + 0x55fca536, 0x54d0d4b7, 0xd2192a94, 0x804935e2, 0x850c62ca, 0xfc8e98c0, 0x935865c5, 0xf9e7410f, 0x1e028eac, 0x53a3f890, 0x6efe1a8d, 0x06b625a0, 0x30db9f94, 0x580b219c, 0x410fd350, 0x8eaa2d23, + 0xdf9b1292, 0x3aa80590, 0xbdd74014, 0x6a056314, 0x1bf8079c, 0x0130a8ae, 0x875696f4, 0x21b7fc7e, 0x7ea61803, 0x9d001656, 0xc70f350a, 0xe21171e9, 0x62b1aa8f, 0x43437526, 0x8c8e9a51, 0x3ed2db0e, + 0x8ac616ae, 0x3ce802a7, 0xbf8dd49f, 0xc29576f0, 0x5d6fe004, 0xfb7fc6e9, 0xa5009c8d, 0x05540af1, 0x09ba5d4c, 0xa46d28b8, 0xdf28f885, 0x9181e693, 0x64c52756, 0x32f30e8a, 0x4099c011, 0x2181113e, + 0x827af9a0, 0x8011b216, 0x41d2df21, 0xd19a79e7, 0x6dc8151a, 0xde099d5c, 0xce8009ca, 0xc69dbaf3, 0x97f12b1e, 0xa0a5109f, 0x78c51691, 0x8a9b0a9f, 0x1bf84006, 0xf42ca276, 0xc57a788b, 0xd686d030, + 0x638e0163, 0x517b7ae8, 0xce0618a7, 0x015c6201, 0x96f42953, 0x04fdcd16, 0x5214f605, 0xe0530568, 0x0dd013ab, 0x011f5a45, 0x512d1b60, 0xe863d400, 0xfa4d3957, 0xb3c5c084, 0x4e2bd4ca, 0x8271be44, + 0x5d11330e, 0x25ca330e, 0x7df20040, 0x46055407, 0xaf3fe0f5, 0x9d55032a, 0x4cba7488, 0xa6d02141, 0xe45f8087, 0x6e31389b, 0x87bd6613, 0x11e800da, 0x73c10f02, 0xf9d20024, 0xcb5ba1d8, 0x96b6c126, + 0x6a41e000, 0xce7a1dac, 0x25b8853f, 0x63218a7b, 0xa5a926ea, 0x3d38eb30, 0xbcfe61a9, 0x2b2800b6, 0x9614b620, 0x18d02856, 0x9b9015c0, 0x81fe90a2, 0x518eb840, 0x45627587, 0x03c15dd6, 0x86a809ad, + 0xac8ae4db, 0xd43f286f, 0x6c94809e, 0x80206ba0, 0x78755c93, 0xb4bcbe41, 0x9b46e6e8, 0xd8685b2a, 0xd22f6406, 0x9d54102b, 0x90141521, 0x5d0e8dc7, 0x1b9aa186, 0x740a2d08, 0xe403e508, 0x91a0211f, + 0xfcd9a5af, 0xc13fa91f, 0x76202828, 0x6aeb2c15, 0x536c206a, 0x82800ff5, 0x7e73e47b, 0xbdec1533, 0x847f1c6e, 0xd464e48f, 0xef7b06bc, 0x23e9235d, 0x22d303ac, 0xcfe85373, 0xf84c6b9f, 0x07f0b10a, + 0x8cd8e679, 0xbe7dbcaa, 0xc42be151, 0x3764afd4, 0xc15c3572, 0xd9944f6f, 0x2de16150, 0x5c1b9631, 0x371fe795, 0x0aaa7dde, 0x46b6277e, 0x9bcaad0d, 0x7d9a1a67, 0x10ee1224, 0xaab0d08d, 0x2fedccdc, + 0x1d3105e3, 0xe6102635, 0xf7b41dcf, 0x0f858354, 0x034243a4, 0xe6fcda6a, 0x1f246699, 0x847b8492, 0x50e07a8c, 0x13ad7bde, 0xf7db8c7d, 0x40f52908, 0x29b4fca1, 0xd4c61fa9, 0xc49fcdfa, 0xf6439e1d, + 0xcc395745, 0xf07f3d3f, 0x2c668b83, 0x54e4907c, 0x7cde50ca, 0x57eeefa9, 0xade57531, 0x42281828, 0xb351ef79, 0x092aaeea, 0xae5dee2f, 0x431a0c2b, 0xf3c3d6f9, 0xb75a9b27, 0xd14068d4, 0x4fca1940, + 0x356a9f1f, 0x3fd632fb, 0x2565a9dc, 0x6fe9ed75, 0x297f5ee7, 0xad4c39ec, 0xf979bc7f, 0x29556fbb, 0xf87bdf98, 0x8a2f99cb, 0x0b53197d, 0xf9ddee2f, 0x50d907e9, 0xee7a7ede, 0xe516bbf5, 0x5a98951c, + 0xdfc3f5ff, 0xfe6c1e8f, 0xdd5db7b6, 0xa7bdedf2, 0x7a3c1f0f, 0xcb4df2fc, 0x8e355623, 0xf2753d9f, 0x4f27bbe4, 0xf379bcde, 0x93bbdde4, 0xfb39bea7, 0x15fea18f, 0xc2cff15f, 0x69e401ff, 0x6f1037bd, + 0x000063a5, 0x45490000, 0x42ae444e, 0x00008260, +}; + +const uint32_t uprof_192_len = 4942; + +const uint32_t uprof_512[] = { + 0x474e5089, 0x0a1a0a0d, 0x0d000000, 0x52444849, 0x00020000, 0x00020000, 0x00000308, 0x24a6c300, 0x000000c8, 0x47527301, 0xc9d90142, 0x00007f2c, 0x48700900, 0x00007359, 0x0000130b, 0x0001130b, + 0x00189c9a, 0x50c20100, 0x0045544c, 0x3d190000, 0x366b3818, 0x296ad96f, 0x4b232757, 0x40994521, 0x2a224e24, 0x9644285a, 0x1b431c3f, 0x26193f1a, 0xbf612451, 0x68d56d5d, 0x42265628, 0xd76e3e93, + 0x48954b69, 0x2567d26c, 0x5f2b2354, 0x42894529, 0x2d468f49, 0x3d192b62, 0x5bbb5f18, 0x68366b38, 0xcf6a64cc, 0x346a3765, 0x5b2b5d2d, 0xab5657b2, 0x2e683053, 0x672e6e31, 0x873d62c9, 0x38703b38, + 0x65316834, 0xa25260c5, 0x3a763c4f, 0x3f2f6231, 0x70343a8b, 0x59b75d31, 0x3e50a654, 0x672e3b7a, 0x3378362b, 0x38327535, 0xaf59366b, 0x3f814255, 0x38275729, 0x8040347c, 0x4b994e3d, 0x381d481f, + 0x833b366b, 0x5ec16237, 0x44357338, 0x7f3a4285, 0x27572936, 0x21366b38, 0x6b381f4b, 0x183d1936, 0x4e366b38, 0x57294a9e, 0x366b3827, 0x384d9d50, 0x8e40366b, 0x366b383c, 0x38366b38, 0x6b38366b, + 0x366b3836, 0x19183d19, 0x3d19183d, 0x366b3818, 0x38366b38, 0x3d19366b, 0x438e4618, 0x38275729, 0x6b38366b, 0x366b3836, 0x29183d19, 0x57292757, 0x183d1927, 0x38366b38, 0x3d19366b, 0x27572918, + 0x19183d19, 0x5729183d, 0x366b3827, 0x38366b38, 0x5729366b, 0x183d1927, 0x29275729, 0x3d192757, 0x27572918, 0x29275729, 0x6b382757, 0x183d1936, 0x29275729, 0x3d192757, 0x366b3818, 0x38366b38, + 0x6b38366b, 0x366b3836, 0x29275729, 0x3d192757, 0x183d1918, 0x38183d19, 0x3d19366b, 0x366b3818, 0x19183d19, 0x5729183d, 0x366b3827, 0x29183d19, 0x3d192757, 0x27572918, 0x6e183d19, 0xd96f69d6, + 0x6ad96f6a, 0x6f52a756, 0xd96f6ad9, 0xf9a3176a, 0x00000094, 0x4e527496, 0xffff0053, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xff04ffff, 0xfffffffc, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xfffff6ff, 0xfffffffa, 0xffff02ff, 0x04ffffff, 0xfb0cff06, 0x2910ff17, 0x20ff95ff, 0x3489d5e9, 0xef2917f1, 0xffb1c0ce, 0xaf3cdcf1, 0x5163e83c, 0x324762a8, + 0x4426c00f, 0x58734b9f, 0x1b73c53f, 0x655bd14b, 0xb9dd94dc, 0x70e3c652, 0x8b217cb9, 0x69807832, 0x80a1e597, 0x86a3adc8, 0xac5507d2, 0x127dd934, 0x00bc7d27, 0x49422f00, 0x78544144, 0xf99ded9c, + 0x9a46d47f, 0xddba6987, 0x8f7df03e, 0xc19f17b1, 0x303e3076, 0x83180d8c, 0xcb9a7301, 0x662f1009, 0xe1308136, 0x92c0d848, 0x3930201d, 0x99612048, 0x6efff9ec, 0x492ae955, 0x6f4952a5, 0x5ddd5549, + 0x19f32fcf, 0x7a356da7, 0xdf7d555f, 0x8763b4ab, 0x183060c1, 0xc183060c, 0x0c183060, 0x60c18306, 0x060c1830, 0xf271d543, 0xd7f3a5f4, 0x6f6f6dae, 0x5f6c203f, 0xd2fe76bb, 0xbfaa9399, 0x4e432195, + 0xf67eda5e, 0xdbc3eafa, 0x795c6f37, 0x27717d75, 0x5fd7dd93, 0xf371badd, 0x6f8b9df6, 0x7f383eee, 0x3637f55a, 0xf6f4e148, 0x6e77abdd, 0x9bbcb95e, 0xd70e736d, 0xfb7f7d6f, 0x9747ebe2, 0x27feaa8e, + 0x7a715218, 0x2f8bd7fb, 0x6c4f5c6e, 0xb78b10f7, 0xf37c3e6e, 0x7fea8ce0, 0xa398418f, 0xbc5eefe7, 0x23f92af9, 0x8dfae7cf, 0x8db66f3b, 0xa39cd04c, 0xb9df5ed7, 0xcbc3b111, 0xdbadf760, 0xea303fbf, + 0x3cce4f40, 0xeda5987b, 0x72b82f29, 0xa379f5e7, 0xd2e2b40c, 0xe56fb7af, 0xcb8f7b61, 0x66bf7e2f, 0xce0b409c, 0x55fb7afc, 0xb877c6be, 0x8138d37e, 0x7cbf4e62, 0xf18d4578, 0xfedf2e1d, 0x8a0804d5, + 0x66e6f4b8, 0x2c7e7b2e, 0x9b6e2e57, 0xb66f2085, 0xf47e2a2f, 0xfdbe5cfd, 0xb90704cc, 0x9dd1f471, 0x1e2daacb, 0xafc5f166, 0x407207c6, 0x2c5beb4f, 0x903e34de, 0x6a1ed731, 0xc58b7d6b, 0xba4ccf17, + 0x5d2e2b30, 0x4fd839cd, 0x0f3bf5c7, 0x6aa9df54, 0xdfafe4e4, 0x67ef4ecb, 0x26abadc7, 0xf6cb9436, 0xbd75091d, 0xd37dd93c, 0x38d2056c, 0x6a86faf9, 0xf2e1a67b, 0xefaa4bc5, 0xfe70555c, 0xfc3d8561, + 0xbc5fbb1e, 0x18221934, 0xd0565f47, 0xd6e2cfce, 0xa618131b, 0x8a9bf4e7, 0xae7cfd90, 0x413b343f, 0xac5cce3a, 0xcfdfb5d8, 0xaadb17ee, 0x720565ef, 0x951e8efe, 0xff7c391e, 0xacfdf552, 0xaeddae30, + 0xcc3fadec, 0x0f466795, 0xa2fdb30a, 0xb98fccda, 0x80b8d775, 0x5537db10, 0x8c2b2a9b, 0xad70080b, 0x077e9f2a, 0x9c3102e3, 0x3fbdbaaf, 0x072315c8, 0xa77339a3, 0xb98fcdda, 0x36428261, 0x5356afa7, + 0xc5e3c7e0, 0x5af7d579, 0xd6be8e43, 0x2643d5bc, 0x729a1fbb, 0x5a478071, 0xcf6678f5, 0xb54ddff5, 0x0bfce298, 0x9fc906d5, 0x3a0d465b, 0x2b58be9c, 0x9b9fbf9b, 0xee100a46, 0x52ec9a2a, 0x5345e2cf, + 0xbe71de2b, 0x227ef36a, 0x7df5aabc, 0x84cd64e0, 0xa8bc517e, 0x07816ee9, 0x01f0d055, 0xcd4df165, 0x3f4f8766, 0x5fc485ac, 0x546a373c, 0x59acbe0c, 0xf16417f1, 0x05260d6a, 0x7d51df4e, 0xcae275db, + 0xc8f6d535, 0x9435479b, 0x776115f8, 0x27022d6d, 0xa21feecd, 0xb6c09496, 0x160cff11, 0x3848d75f, 0x79aaf470, 0xacdcd9fc, 0xa5e11a89, 0x67df55ef, 0x48fcb97d, 0xe7b275b5, 0x7b4c9559, 0x2ac5c464, + 0x1f2782d7, 0xbac3beaa, 0x53aaa373, 0xac6be7c3, 0x8b0d23ed, 0x295b54cf, 0xe59aee3b, 0x5587845f, 0xbd1c0dba, 0x56cefaa8, 0x0db9d50a, 0x63c8a99c, 0xb970d43e, 0x7a998f0a, 0x09bf7260, 0xb57bfdd8, + 0xd75e8525, 0x5247e37a, 0xb755576e, 0xfd1351d8, 0x154ae625, 0x26338109, 0xcb8297f9, 0x76423355, 0xa47e74de, 0x6aaef162, 0x91e1c9cb, 0x72d3fe89, 0xe0a41ab5, 0x4bfb536b, 0x4148154f, 0x7101fd93, + 0xc42bc2a3, 0x1fe74d27, 0x1d155b90, 0xf2369c0c, 0x0ae5cc1f, 0x633d17ee, 0x58127f8a, 0x112ed8ac, 0x61c9fe13, 0x88168577, 0xbfec99af, 0xb6ab5e2c, 0x981e1a65, 0xbf1e5ff0, 0x7266b6ab, 0x8fea9a5e, + 0x5a562e4c, 0xb199e84a, 0x561e5cbf, 0x35dc0796, 0x9dcd97f6, 0xbb146a4a, 0xf2bfd26b, 0xf80f20a9, 0x0b3fd8d5, 0x078a576e, 0x233fe798, 0x8c03c42a, 0x4ee333fd, 0xa3412825, 0x02a433ff, 0xff130162, + 0x1ef68a67, 0x3fd8c8f0, 0xb6aa2e5b, 0xc6079f30, 0x557359fe, 0x5c798db5, 0xecfff933, 0x7432b8d1, 0xf2ffd4de, 0xe166db40, 0x9ffea633, 0xed35bb0b, 0xfa6d3910, 0x5162727f, 0x93b12ecb, 0x372fffa6, + 0xa478ebae, 0xcdffe99c, 0x75fa5791, 0x7d555f8b, 0x1b8b6a4f, 0x988c4dba, 0xdcce6002, 0x68252bd6, 0xa3b94012, 0xbce84255, 0x8fe50049, 0xd380e946, 0x055fece6, 0xfce6d6ec, 0xfd35a380, 0x5c7527de, + 0xd06c65d7, 0xe22b000c, 0x80a11e8a, 0x865200e9, 0x35c0a116, 0xe3a80013, 0x9beb6a8d, 0x9462900a, 0x008c85d7, 0x21728b54, 0x8ac03268, 0x55b6a1b9, 0x8011a5fc, 0x24da51aa, 0x0064c978, 0xf8f0a3d5, + 0x0f4aa388, 0x9d9b9f00, 0xba9a9a9d, 0xe88c3880, 0x43ececec, 0xf6f6f56c, 0x3a1c471e, 0x31196874, 0x8d373737, 0xfb2108f8, 0xeaf853f2, 0xc9d5de53, 0x1fabbde5, 0xd99eaeb2, 0xd5163de5, 0xcda80735, + 0x26285d00, 0xd5d115a6, 0x188435d5, 0x7d5d841c, 0x3bd5f93a, 0x97dbabac, 0x5dce58ff, 0x1f30a9d8, 0x00ad48f4, 0x04dc9fd8, 0x25f55d34, 0x613bd5c8, 0xd40e2f57, 0x11bac835, 0x19c064aa, 0x83c03dc5, + 0xd48c0b30, 0xae01b5d5, 0x80740c3e, 0x1e893abe, 0x038952f0, 0xcc00ce28, 0xbc702d01, 0x3fc0790e, 0x38fcbea5, 0x75abf2f0, 0x49403d12, 0xac8de036, 0xc3929f6f, 0xadec0b40, 0x0a407883, 0x54a7e009, + 0x6e03d327, 0x38145fe6, 0x0004baaf, 0x9a3b835c, 0x9aea05b1, 0x54007a53, 0xab90b402, 0xdc80f3cb, 0x2a8e85c7, 0xae00021f, 0x0596c3c1, 0x41c07ad2, 0xda7403a0, 0xe923df97, 0x0b1ee46e, 0x43014f2a, + 0x3aade0d7, 0x024c0790, 0x275abf2f, 0xe4ae03d9, 0x56de129b, 0xae86029e, 0x107533c1, 0x03a8f80f, 0x9d52f978, 0xb5c80f6c, 0xfaa4f449, 0x18684026, 0xf10756d6, 0xd5e03e80, 0x9757798f, 0xf3bb01e5, + 0xa0d3179c, 0x06b80c09, 0xa81650b7, 0xf49bde6b, 0x700950ea, 0x07be4eb8, 0x136fcadc, 0x8001bf50, 0xda4ee0d7, 0x49f57502, 0x012a003d, 0xf032eba6, 0x140edc80, 0x000360d6, 0x75abc1af, 0x4b000f10, + 0x0cba8680, 0x4db7203c, 0xb0047940, 0x0706be13, 0x03d0902c, 0x1f57817a, 0x925d5c0f, 0x0914e407, 0x0a05d168, 0x1643835c, 0x2301e648, 0x10b80740, 0x0f1cbcba, 0x5447a7c8, 0x4380b787, 0x05b0e0d7, + 0xd413eaea, 0x1d018f67, 0x797c3a60, 0x4b901e29, 0x8cb5e83a, 0xba050a6f, 0x2d85cf06, 0x5a03c490, 0xc3df9781, 0x0f14bcb8, 0x7026a1c8, 0x302e8b46, 0xcfdc1ae8, 0x07b880b0, 0x3d5e071c, 0xf34901bc, + 0xc3fbec80, 0x9af8cb6e, 0xf06b8060, 0x04d33d50, 0x301e8275, 0x2980740b, 0x7865d5f6, 0x8facf5c0, 0xe120790e, 0xb8004a4b, 0x2db08f06, 0x2d01ea20, 0x6d600740, 0x203cb240, 0xa3a0eee3, 0x7600a41a, + 0x43e0d7c2, 0xe6200e51, 0xcbc05d01, 0x2c8024b3, 0x1938c80f, 0x1bfc3bf0, 0x06ba00ca, 0x803a468f, 0x010e0d88, 0xba083eaf, 0x67df1c7a, 0x50f503a9, 0xb835e080, 0x04da39b5, 0x02983602, 0x0484403a, + 0x5901e590, 0x8e5d40ea, 0xc1ae0382, 0x080b1cc3, 0xe80060d8, 0x40125100, 0xce5c0792, 0x9afc4bae, 0x011022fa, 0x72f706ba, 0x06d6201d, 0x33cbc043, 0x1e895063, 0x48515d90, 0xa140b397, 0xd70cf06b, + 0x58367102, 0x239d5e06, 0xc078e401, 0xc23d5662, 0x4cdfa0cf, 0x9835d016, 0xdc080127, 0x1314cd5e, 0xe3663e49, 0x80f60eae, 0x5fd8d93b, 0x27701093, 0x881e0d78, 0x1d5d40b3, 0x3808686f, 0x8e5c5491, + 0x03d83abd, 0x550509b2, 0x40b8020f, 0x58cee0d7, 0x203d700b, 0x8040095b, 0x12660dce, 0xe6d69e0a, 0x35d0106d, 0x02de3578, 0x4e800f1c, 0xa1a54524, 0x16407aa5, 0xd0e8c727, 0x5d016206, 0x06f14b83, + 0x1a1e8ba8, 0xc5244e82, 0x1e19004a, 0x12fe4570, 0x680e4ba0, 0x742a7827, 0x9102c852, 0x04bd901e, 0xf201e190, 0x683545c7, 0xae81b604, 0x1d0567c1, 0x8003c220, 0x58fc57ee, 0x0f30eaf0, 0x3bbb2578, + 0x7d0e92ec, 0x35d01660, 0x138ed198, 0x00606b01, 0x2424913a, 0x80782401, 0x1fa4a3ec, 0x548dbe82, 0x64706ba0, 0x03c84e2d, 0x5917eea0, 0x01ec9689, 0x1ac21392, 0xa9d0002d, 0x5c44b8d0, 0x2740078c, + 0xd12ba292, 0xee403d92, 0x02d1a012, 0xc5835d00, 0x07a47854, 0xe0860f40, 0x7ae5a254, 0x2a9965c0, 0x0005a740, 0xa82a153a, 0x003c23c2, 0xc25d7eea, 0x01ec0012, 0x8d009732, 0x1af00016, 0xac11e89c, + 0x420e0e0b, 0x04812fdd, 0xc4807a80, 0x468ce025, 0x2474000b, 0x5823e124, 0x81191817, 0x9125244e, 0x900f5000, 0x93e804b7, 0x848f8003, 0x016a4c1c, 0xa74007bf, 0x3fae9342, 0x96900f10, 0x4fad380b, + 0x153e1012, 0x120cd31a, 0xf75001e3, 0xfebc4d6b, 0x56403c40, 0x2a9f503a, 0x1af84ec0, 0x038537bc, 0x75001e78, 0x0feb14bf, 0x552403c0, 0x536a4e04, 0xd700c406, 0x66d88ee0, 0xd807b610, 0xdfba8046, + 0x4f5754f1, 0xa40f01ed, 0x7d368038, 0x1ae81800, 0x05953fbc, 0x4a800f5c, 0x4feb5480, 0x29b80f7a, 0x34fafec3, 0x80b66102, 0xe94be816, 0x80079600, 0xf5ba4025, 0xc807bd27, 0xa6d20d68, 0x9d009013, + 0x066a150a, 0x74023489, 0x04953928, 0x43c07880, 0x4ba00e09, 0x7405b02b, 0x19ac5f32, 0x5008ca24, 0x82134bf7, 0xbd392458, 0x7c376280, 0x649af456, 0x4e01020b, 0xbf8b1e85, 0x800f3cd5, 0x094a5fba, + 0xf800f000, 0x0cba2199, 0xa7405903, 0xdfc70f42, 0x80119e69, 0x025a97ee, 0xeb80f7a4, 0x69f0aad0, 0xf0c0b29d, 0x66f150a9, 0xc0331610, 0x0097a7ab, 0x3a003de9, 0x38c9a829, 0x327403b0, 0x0833413f, + 0x28256803, 0xef4804bd, 0x0e803701, 0x080c55a0, 0x9f650a9e, 0xb40c22cd, 0x01200917, 0x2c007bd2, 0xdd247c12, 0x53a02084, 0x6cc1bca1, 0x07ee8001, 0x3da90092, 0xc8647600, 0x8446f90b, 0x4d0a9d00, + 0x60120cd8, 0xfceaf02f, 0x3cacf4a9, 0x03221160, 0xd8046ab5, 0x150a9f09, 0xa366c96d, 0xc12910a2, 0x016d2003, 0x7701d55a, 0x92f2c782, 0x80f88337, 0x254122f6, 0xb80f4a40, 0xcd007002, 0x2a742024, + 0x563f6934, 0x0848e000, 0xf4a4025c, 0x5032f480, 0x5ce13393, 0xe9a153a0, 0x1f0eb18a, 0x412eaf03, 0x901e5802, 0xf4fb6cf4, 0xe3d112ef, 0xb50f4850, 0x10b41861, 0x10024329, 0xea407a92, 0x5966a06c, + 0x5e58f040, 0x071066ca, 0x4c48bd25, 0xda407a92, 0xb35ff161, 0xa06a6331, 0x9b69a153, 0x90f46c41, 0x1e848400, 0x03a53b70, 0x6c377e28, 0xde58e808, 0xe59dfc56, 0x757807b0, 0x90801259, 0x165203cc, + 0x7268cf85, 0x540b9c28, 0x0e1dc402, 0x0cda07c6, 0x10024d22, 0xba407992, 0xbd568054, 0x58e81880, 0x7dfc5b5e, 0x066d00e5, 0x08012791, 0xfbe03c89, 0xc3a00e54, 0xa068419b, 0xf12d7963, 0xd0079577, + 0x004a64fd, 0x480f2242, 0x25e07273, 0x680076dd, 0x70e45631, 0x801a68b0, 0x012a9757, 0x203c4908, 0x75a5bbcd, 0xa783034b, 0xa0fd0142, 0xa78f8c88, 0x0f02434d, 0xa015d348, 0x96000edb, 0xc537c100, + 0x8bda0325, 0x10025924, 0xf2407812, 0xca5d49b7, 0x80a994d0, 0x9b03e4f6, 0x21238041, 0x410025d2, 0xef9203dc, 0x82f59a01, 0x60f2c7c1, 0xe02520cd, 0x004be5d5, 0x2407b082, 0xdbb403df, 0x5899a001, + 0xb1fd29ee, 0x19910b40, 0xb0820048, 0x038d2407, 0x8102cbb4, 0x8e04f2c7, 0xa3a0e5e8, 0x7a882004, 0x905cd240, 0xa487af16, 0xf1004a81, 0x0098f9f3, 0x91b24247, 0x0f410400, 0x01d09178, 0x84066b34, + 0x9ac2854f, 0x3ff2da41, 0x31184497, 0xac9dd80f, 0xa7527c26, 0xd0084143, 0xc3e26c4c, 0x0b407c79, 0x9456b091, 0x203cc418, 0xb3584d59, 0xf09df076, 0x87e4d0a9, 0xdb40d0f3, 0x00933391, 0x480f2104, + 0xd3ae1ed6, 0x2a1670b9, 0xeaeaf2c7, 0x9d4f7cb2, 0xe03c4404, 0xa323a27a, 0x8506eb34, 0xe2c2854f, 0xfba066f0, 0xac012965, 0x01e0282f, 0xb5437a49, 0xa782034b, 0xf8716142, 0x99eaf02a, 0x8082004a, + 0x06492407, 0x000ad76a, 0x35850a9e, 0x5e059c83, 0x40095b3d, 0x2480f788, 0x17690607, 0xd7df8204, 0xbbba0ddc, 0xaca489c0, 0xd6210025, 0x82041203, 0x020bb547, 0x8158c5a1, 0x8071c537, 0xb9910c1e, + 0x5eb02004, 0xfc43e16f, 0x5cedb8d0, 0x68049a8c, 0x066b0618, 0x10025fd1, 0x7c403ce2, 0x5ebb4440, 0x2854f010, 0xda0419ac, 0x41cb0c8e, 0x01e31388, 0xba8243e2, 0x5fc383f5, 0x066b08d6, 0x0323b601, + 0x10135267, 0xdda201e3, 0x0049fae1, 0xf4563168, 0xea6c714d, 0x38140a81, 0xbe049a93, 0x83668807, 0xd00093f5, 0x37e150a9, 0x9e8131c5, 0x432b050d, 0x01eb8324, 0xbb4022a2, 0x48f02066, 0x0419ac08, + 0x979c8eda, 0x3cf02004, 0x482db440, 0x7d00093f, 0x70ec2359, 0x00918ab0, 0x08079e04, 0x8ead008a, 0x8b40c787, 0x14de89b1, 0x40a80617, 0x04009473, 0x6588078e, 0x68e7ce02, 0x79f2315a, 0xd014735f, + 0x2760a1b3, 0xda20a553, 0x01cac403, 0xe7854dba, 0x08e6bef6, 0xb0c8eda0, 0x10d2a993, 0x736201e9, 0x0e9d7ae2, 0x848e818c, 0x9c004cc0, 0x23360248, 0x03ca22a6, 0x21de8516, 0xeb5f2573, 0x0a9d03c6, + 0x26a6f34d, 0x8502a00a, 0x89293fed, 0x019100f2, 0x35da93a1, 0x30c34384, 0x1fa62d58, 0xfda17578, 0x72416c67, 0x03c230c1, 0xa623de44, 0x3a081a5f, 0x066b0a15, 0x8b23b681, 0x968c23cc, 0x8586b0cb, + 0x100f6c30, 0xd7e90549, 0x0095020c, 0x9c0b7bb0, 0x410a1248, 0x09e5ca57, 0xc06497d0, 0xa69100f4, 0x2034ed10, 0x78042474, 0xfdd01838, 0xeb124644, 0x76e5cb1b, 0xc16497d0, 0x814580f2, 0x86ed50ce, + 0x1861a102, 0x7a0419ac, 0x16489436, 0xe500ed1a, 0x21aefcb2, 0xabc40796, 0x1dbaa3c0, 0x0a9e080e, 0x38a6fd4d, 0x286cf402, 0x0af42c91, 0xc5580076, 0x03c3099a, 0x9a820be2, 0x276a24bd, 0xd6142a7c, + 0x489c020c, 0xa0953a12, 0x6c003b11, 0xd84d9578, 0x225fe203, 0x101e5da5, 0x6f40012a, 0x0c1f038a, 0x12e04171, 0x7232e5fd, 0xd7d03181, 0x01e4208c, 0x9d40b9f1, 0x804f155e, 0x37b06186, 0x86cf41c5, + 0x3552def2, 0xcbb514b6, 0x4031409f, 0xfa00bcb9, 0x3d04519e, 0x03ac72e0, 0x860b5768, 0xdec2854e, 0x476d0714, 0xf9f67776, 0xc440f972, 0xe5cbda35, 0x0579729e, 0x09233df4, 0x456c407a, 0x331a95f8, + 0x0a9d0347, 0x0e29bd85, 0xf5e49138, 0x7cc7a1a2, 0xc812fb1f, 0xb29afbf8, 0x7c2d0388, 0xf23d301e, 0xd7f6313f, 0xec0c112f, 0x833607c9, 0xb7802500, 0xbe45a0fc, 0x2c49aecc, 0x85f3cb95, 0x40e22cf6, + 0x0e03da0b, 0xc62bff16, 0x020bb401, 0xb0a153a0, 0x50129066, 0x48117a81, 0xb7a4647b, 0xb0a33c81, 0x89865caf, 0x9e6d0e55, 0xff178c07, 0xee01c62f, 0x1335b42a, 0xec2854e8, 0x3b683070, 0x66d46bb2, + 0xb3cde91c, 0x5680d12e, 0x44e142ff, 0x85a07136, 0xc5d301eb, 0x38c77fe2, 0x21337680, 0x5850a9d0, 0xfdd00833, 0x7b89e3bc, 0x853003b0, 0x008a02fe, 0xe503850b, 0xa3bd8672, 0x2e1c078e, 0xc637ff16, + 0x4b7b7401, 0x142a780c, 0x54020cd6, 0xda44dea0, 0xa6007618, 0x403b440a, 0x9f30a177, 0x61681c55, 0x62fec07b, 0xf27fe0f1, 0x12bc401d, 0x91ff1d30, 0x85c75d63, 0xb347dcff, 0xc342cfcc, 0x6e4b5830, + 0xb00934b7, 0x8c3d9244, 0x7686d32f, 0x42855d80, 0xa0716677, 0x01e2197d, 0xdee2c583, 0x85030398, 0x8b67823b, 0x411f71e3, 0x76116bc6, 0xd762c980, 0x431f8c47, 0xcd6142a7, 0x245ed020, 0x13050def, + 0x50c3d924, 0x3fed6881, 0x8e142850, 0x5a071764, 0x2b980f60, 0x8f4a8716, 0x8f17f6b9, 0x16745102, 0x0de378f7, 0x16546594, 0x97a7ebb8, 0x54e873f1, 0x17306c28, 0x1d547ee8, 0x4b010512, 0x8fb8154c, + 0x028500ec, 0x08907d7b, 0x012400f2, 0xb94f4b66, 0x12dc200e, 0xc3e6978e, 0xfac19f31, 0xeff0596d, 0x308ecfe1, 0x0a9d0e7e, 0x01cd9d85, 0x4bdea054, 0xd010508a, 0xd1e6dcce, 0xc26c687f, 0x1c65900e, + 0x4ff6d168, 0x2a1c0248, 0x0075cd7d, 0xf0e1e3e1, 0xcc70deb4, 0x870a7067, 0x203354f5, 0xe15d1fa3, 0x30c3439f, 0xf4083358, 0x15af286c, 0xeb7aa709, 0x89102a59, 0x7f6c05c3, 0x40f6071c, 0x6d00abbe, + 0x968c0248, 0x8075c6fe, 0xa76213c8, 0xff7cb647, 0xc7eab71f, 0x49520088, 0x6c255b8f, 0x787f9aac, 0x854e814f, 0x0714dec2, 0xc9d40094, 0xd6386330, 0x2bf2ad63, 0x07101848, 0x240e36c8, 0x063b6694, + 0x73bfa54b, 0x16b2201d, 0xf56c5938, 0x2bd27ec9, 0x49e0dfcd, 0xc8566715, 0x47a11ffe, 0x53f6ed0d, 0x1d00ffa1, 0x29bd8109, 0x1436780e, 0xda1d3475, 0x0c0a7bff, 0xc19b8815, 0xe0500ec8, 0x31648b14, + 0x5b3d9e56, 0xd73bfd2a, 0x00d2a201, 0x8fab3878, 0xb3d6fffa, 0x556b8871, 0x101f4692, 0xa0f40072, 0x96b2ac0f, 0x2854e814, 0xa80419ac, 0x3a012940, 0x42dea581, 0x968e0558, 0xfd8827cb, 0x71626d0b, + 0x8989db60, 0x35b76903, 0x94f4dab4, 0xe35e7f4a, 0x6cf7c039, 0xb5847d03, 0x5b30fd18, 0x3c9d7a7d, 0x51d47ea3, 0x26dd8d6c, 0xddd1c66c, 0x25860d2a, 0x142a7423, 0xa838a6f6, 0x7496a004, 0x291c0414, + 0x205587f4, 0x00e589b6, 0x10dc0e28, 0x384b4ef8, 0x9a32b4a1, 0x5fc0602a, 0x7a54a9a2, 0x888073c2, 0x16c94213, 0xc1ffe2dd, 0x776cb631, 0x70dd8141, 0x881c58ab, 0x435742d4, 0x18ac2469, 0xb0a153a0, + 0xcf01c537, 0x5072a286, 0x1635ff48, 0xd8150c0a, 0x42c31d88, 0x28dc0e2c, 0x244f1a13, 0xa0341caf, 0x4d0a17cb, 0x073c7ba5, 0xe681e760, 0x4ad0a3c8, 0x7f5904fd, 0xcf00f90b, 0x0428c3aa, 0x9cf64f40, + 0xa5a00380, 0x4254b3b6, 0xf0327f8a, 0x0012a01f, 0xd0183876, 0x70b1a8fd, 0x6cb0041c, 0xbcfd6f3c, 0xf181c760, 0x270e15eb, 0x601f6c17, 0xcc389696, 0xe143a290, 0x74132f08, 0x42079d94, 0xf49e1876, + 0xc47912d9, 0x07628e9e, 0xf8c8aacf, 0xd9f10333, 0x0774c422, 0x0d4a4f0e, 0x2062c55d, 0x091d0cbf, 0x00833581, 0x86550025, 0x7deeb31a, 0x709e0548, 0x38c10077, 0x0a12e710, 0x8da43d4d, 0x3fe131b8, + 0x1b52fd07, 0x1c17d0be, 0x34c33807, 0xccfa4e10, 0x0bda07a0, 0xd1238f04, 0x3ee49a56, 0xed38d3de, 0x8e97e238, 0x1d4fa011, 0x8386212c, 0x899a02aa, 0x060e1d85, 0x5a8a1b3d, 0x432c97a4, 0xda205487, + 0x0e394f95, 0xda071420, 0xa470a87f, 0xde71237f, 0xbe3809c0, 0xbb0038e3, 0x7a59041d, 0x8e9674a4, 0x76d0a6d2, 0xb1c5ed8d, 0x1e3b492e, 0x21768566, 0xc55cc290, 0xac1bd00e, 0x42a7401d, 0x1714dec5, + 0xcf02f3b5, 0xc2c7a286, 0x6f7cac27, 0x90f60550, 0x2846d95d, 0x9b87dc0e, 0x49330847, 0x012446c3, 0xfe6fc826, 0x978071c2, 0x3320fb2c, 0x1de3f6b4, 0x23fbbf7a, 0xf9ce38b4, 0x4904225d, 0x4f8f1e3b, + 0x99571217, 0xed2ff6c4, 0xe8466a09, 0x3348a854, 0xd4637cc8, 0xfdd0265c, 0x0f3c165a, 0x5eb4b0d8, 0x66920550, 0x07140730, 0xb2fb889e, 0xcbb06d38, 0xa4a9f8fa, 0x615e007b, 0xe5be6fc8, 0x28730038, + 0x7d678500, 0x9d3f52a5, 0x453925aa, 0x18844b9f, 0x0d591fc2, 0x6830baeb, 0x0e25e981, 0x581fda50, 0x4012a053, 0x6f0b2c64, 0xc8cc13a7, 0x4740a81a, 0x511cfbca, 0x61010567, 0x3ee1b815, 0x607151cf, + 0xfd0ae37f, 0xb4ddfadd, 0x898d1101, 0x157b00f6, 0xe33e142a, 0x80eb0038, 0x9ab459b8, 0x19d641ed, 0xd91a24f4, 0x112efddf, 0xac7910e4, 0x8763f8f7, 0xeeb64fa8, 0xcfa01cb1, 0x478060bf, 0x52a11842, + 0x24ff01f3, 0xca993d7e, 0x6fe860d9, 0xeb3804a8, 0x11c1b419, 0x68157efb, 0xb0b98335, 0xff09c0e2, 0xb502f8dc, 0x6c258c64, 0x8e737121, 0xc00e39ef, 0xa34f04c8, 0x7ed97975, 0x4f451d3c, 0x4b9f77f6, + 0xe12a4084, 0x6c47cc6c, 0x624fad86, 0x52b54b90, 0x2a74007b, 0xcb387594, 0xfe24c081, 0x97d3a928, 0x0bfac31b, 0x78d43678, 0xdf5ba34f, 0xfe6ab31b, 0x35681541, 0x846cb983, 0xb286c0e3, 0x9f70597f, + 0x480f7b62, 0x71ea0f90, 0x1ce7ce23, 0x2abc6007, 0xa067d908, 0xca675b01, 0x5b0cd3cb, 0xc82f368b, 0xa12ac16b, 0xcb896a54, 0xc0329317, 0xa500ed8c, 0x21f2be63, 0x75942a74, 0x6046db3e, 0xb8a9c61b, + 0xbbb1dc06, 0x4099bc10, 0x59453009, 0x01eed1db, 0x9941b815, 0x5e12b113, 0xa6044fd3, 0xd8a7dd46, 0xec124e5e, 0xb1e2de01, 0x708071c9, 0xc3ae6029, 0xa0b4e896, 0x366cb49b, 0x21f629cb, 0x915bf6b3, + 0x881c4c5d, 0xc77ec8fe, 0x525758fe, 0x54e845d9, 0x5688e328, 0xbd499bd2, 0xdc99a23c, 0x70fb23d2, 0x08405ec3, 0x1ce81500, 0xe2b7e6d5, 0x02a7da58, 0x973066ed, 0x8da0a970, 0x15bc7c64, 0x1bca81ab, + 0x89603d91, 0x3f12ecfa, 0x1e2d4431, 0x80768a7f, 0x06429bf0, 0x54bd18ec, 0xf626ebda, 0xc1bf9671, 0x460edb0a, 0x0a249598, 0x0446cbfc, 0xe152d683, 0x1593c35e, 0x68c00960, 0xe8113906, 0x1137c6ce, + 0x9c2692dd, 0x3ff0df62, 0x9456c04f, 0xdb46bc79, 0x1370ded6, 0x8f3ef4be, 0xfeca2377, 0x3139f085, 0xd02d5be8, 0x90ae62ad, 0xb7a35477, 0xc7b73407, 0xfc5e07ac, 0xb46bf850, 0x416a8403, 0x16761b78, + 0x48272ee8, 0xb43f6e3b, 0x3d6ba37b, 0x5bb92423, 0x14c56d2a, 0x398c700c, 0x003ae0d4, 0xbbb92e4e, 0xb42a740a, 0x44f6d0c8, 0xd6d151f2, 0x9f8e5384, 0x1e8042c7, 0x5e051dd0, 0x677bc91c, 0x010707da, + 0xdb65dcbb, 0xf9f7d15d, 0x43428077, 0xcbc40e30, 0xf09d3d53, 0xc6fff159, 0xb04b3504, 0x6f3aec14, 0x15a15a5e, 0x78b53f45, 0x00ed127c, 0x71e09ea1, 0x19b1f19c, 0xc773f670, 0x844b9eba, 0x34f99b40, + 0x5c74c4fe, 0x84891841, 0x78fecf2d, 0x0b7370c5, 0xaed0a9d0, 0x43d1fdb0, 0x373f42b6, 0x16434edd, 0x10b1c30a, 0x0084f258, 0xed768150, 0x6a7536f2, 0xf996a6e5, 0x2c2dcf66, 0x456b054e, 0x3a79784b, + 0xda7c2540, 0xdbe00969, 0xb3d84a03, 0xbffd9b1b, 0x5ffd413c, 0x31f87e3a, 0x478201da, 0xdfb92204, 0xce31c7b5, 0x1da99388, 0x48ed5670, 0x6f578789, 0x8c7f5cf2, 0xfd9e701e, 0x0f77bd71, 0x537400e5, + 0x942a7400, 0xbac40791, 0x074ed689, 0x578e791c, 0x780d1c54, 0x04a07925, 0x64902818, 0x76ed98dd, 0x152855ce, 0x398336e8, 0xcff46aec, 0x00059207, 0xe12706cb, 0x8713a253, 0x7cf2e378, 0x38395f79, + 0x3feed479, 0x6e5e8273, 0x80768d3e, 0xb7183540, 0xa7cf7801, 0x590f9cbb, 0x9d14af02, 0x9573efea, 0xf8414c5d, 0x7a5015a0, 0xd0014d50, 0xc1f0d0a9, 0x2f81e800, 0x5b518d37, 0x6e156fad, 0x02f30642, + 0x90f1e831, 0xe0be9ad4, 0x15287c95, 0x66d76138, 0xd9b7d03c, 0x0b22c547, 0xede1c2d6, 0xb0fe6301, 0x4603da91, 0x5be5fef1, 0x43b1c840, 0xa35fee1b, 0xc2858852, 0xf00ed1cf, 0x639e642a, 0x39d3f40d, + 0x56b53f69, 0x19d4b6ea, 0xec35f2d5, 0x62bf0e3a, 0x55c06384, 0x4d78039f, 0x50a9d001, 0x38100cdc, 0xf03c8194, 0x0a8ddedd, 0xef25f0ea, 0x818f8e9c, 0x9fb3ef17, 0xe8b7fc50, 0x8155dbad, 0xf5fe8dcd, + 0x5a1872dc, 0xbf5bb405, 0x4ffd25e6, 0x61c58238, 0x563cfede, 0xdabbf380, 0x09e2bee9, 0x7423a9ff, 0x97f0b10a, 0xb7f00768, 0x901ce003, 0x0d4bd0a7, 0xfc0ec5ad, 0x8f0ae334, 0xba6993f5, 0xf5a1737a, + 0x8facfb98, 0x3f71d2bb, 0xc17c20b7, 0xbc3c160e, 0x54e800a6, 0xf4007228, 0x1ed06c18, 0xedde0560, 0x10b8206e, 0x5e6de1d0, 0x40aa05da, 0x37050b97, 0x0105e3b5, 0xa51c0a94, 0xd419e24d, 0xecc7f0db, + 0xfa4bdf05, 0xd0877fd3, 0x862e1f64, 0x585dfbf7, 0x8cbfb85f, 0x4a1ec803, 0xf44fa015, 0xff80388b, 0x3a61b084, 0x34912f44, 0xc7eb21cd, 0xa0976dc7, 0xa8b8bd32, 0x69b7e8ef, 0x17e3e1d2, 0xddc64c0e, + 0x997ae53f, 0x9d0014db, 0xe24e2d0a, 0x992e8307, 0xbc0f58f5, 0xc811a037, 0x2dcbd0c4, 0x64b50934, 0x897828af, 0xab9ed5a6, 0x56a85702, 0x413c1700, 0x1bb53ce8, 0xa49c07ce, 0x733fb37f, 0x1721598c, + 0x37472bc0, 0x795c5ed9, 0x3b019d8b, 0xdfaff39b, 0xbe1d5747, 0xd2007414, 0xe71b4006, 0x7be5b5a0, 0xc0bd378f, 0x3f6ecd13, 0xfd7187ec, 0xc0976638, 0xe28e7af4, 0x400536e2, 0x838b42a7, 0x12a7e824, + 0x252c2769, 0xb42ce3dc, 0xa01d810d, 0xe86e4940, 0x6d92ca40, 0x1d5403cf, 0xc0aae316, 0x5ce3e0c6, 0xeec65db0, 0xbf75180a, 0xa5fd98dc, 0xfd492d28, 0x5d6215b5, 0xb0a92722, 0xdd1f6c90, 0x1fdebfee, + 0x423a7071, 0xe9300e7c, 0x26369199, 0xd5aef852, 0x72112ea8, 0x4dadc6c7, 0x60f7bf60, 0x81864bfb, 0x2701d009, 0x9a70a49e, 0xa153a002, 0x932a7168, 0x55d3cfa0, 0x85fb568d, 0x82179e2b, 0x6e5bcaa2, + 0x2717a097, 0x3e49600e, 0x151ce19f, 0xea4bef38, 0x38ad34b9, 0xbb9f7e62, 0x6a5f3842, 0x3466bc29, 0xc661c0e1, 0xdcc76589, 0x5ade6fc8, 0x7050dc2c, 0xc577cad4, 0xb49d003b, 0x6800dab1, 0xed1f2dac, + 0x4fcec38e, 0x178b16ce, 0x760f4fdc, 0x1e009b39, 0x313fb542, 0x0d164be3, 0x5ca83b17, 0xe60addbf, 0x800a69c7, 0x9022854e, 0x3dda9b7d, 0xd54a3fcd, 0x45ec7ae4, 0x47703eb6, 0x19b6e5ed, 0xce28839a, + 0x8bb6bfe1, 0x043a3e48, 0x09e05577, 0x4d800ed6, 0x6341be8a, 0x47426583, 0x6a5fda23, 0xe2ebf85f, 0x46b1b23e, 0xeec77f67, 0x2fece6d0, 0x7a0ff6fd, 0x71c3bec2, 0x63c3ba00, 0xf801b49c, 0xfcb6436c, + 0xdee7ea11, 0xfa8b8df3, 0x3f081478, 0x5aa4cbe1, 0x74e9ef6c, 0xf39e20e7, 0x47491aa1, 0x8d3334dc, 0x9d0014dd, 0x459c450a, 0x212b10b7, 0x709bb43b, 0x081d82ee, 0x54f784f6, 0x00b2b727, 0x8f1441cd, + 0x10d6ae3b, 0x2ad9dfc8, 0x03b99db0, 0xded01ed8, 0x281dad86, 0x3922fafc, 0xf6221e87, 0x2f9fec57, 0xc24eded9, 0x1983fc5d, 0xa0dc143b, 0x0e78cf5a, 0xe6549ff0, 0x036eca7e, 0x7e5b21a0, 0xc3e49d8b, + 0xab1e79b8, 0x6d603fa8, 0x2f57e802, 0x1543bfb8, 0x64499784, 0x43026def, 0x609018c1, 0x37400537, 0xb76cc461, 0x65adc375, 0x4e2bbdd0, 0xc7ebe820, 0x513f6e71, 0x608dcdce, 0xfe49d201, 0xf3f3e91c, + 0x054b7ddf, 0xc0bad0f6, 0x1be411cf, 0x8a5efd1b, 0xf6fc29d2, 0x268d36b0, 0x1676db92, 0xb37b6e86, 0x7c567058, 0x598e00bc, 0x9df291f2, 0xe00e786f, 0x021c7668, 0x436d06f6, 0x3a56fcb6, 0xd798e777, + 0x5bf7ea2a, 0x1d00c09b, 0xe3357a3d, 0x56e1f469, 0xb6e47eaa, 0x9ab1f982, 0xa153a002, 0x3f722d98, 0x45596d1b, 0x40acfd65, 0xa4b19db0, 0xaf997f62, 0x84ce025b, 0x0f3a0e21, 0xff9d52e8, 0x2054b5c9, + 0xba1cf9cb, 0x79695189, 0x0380f642, 0x42c15fcc, 0x41ffc177, 0x27e9ba3a, 0x4fea3d0e, 0xc03b463a, 0xacf95e1a, 0xfc521edc, 0x4e81412d, 0xb3d100ba, 0x89fbcc7e, 0xbb69de6d, 0x026d6fa8, 0x10a64ead, + 0x16cdd073, 0x6bbdb5a2, 0x70ec58ae, 0x86498416, 0x5348618b, 0x142a7400, 0x63853d63, 0xc54c3876, 0x9c10247c, 0xdd56240f, 0xd6d1363b, 0xd1020d08, 0x1afe48c2, 0xf06af36e, 0x0a96ec3f, 0x010aaff4, + 0x5003881a, 0x03d9043b, 0xab318256, 0x26f6d9f3, 0x5c2783fa, 0xa1204809, 0x70704060, 0x191fa01c, 0xfac715a5, 0x0d001b5e, 0xd02ff2de, 0x096e3beb, 0xf376b4e8, 0x8c438efd, 0x7def1d63, 0x13a382e2, + 0xc65baab1, 0x2365b01f, 0x72160ad5, 0x48f011b3, 0xf918e608, 0x904d36e7, 0x5a87a958, 0x78204906, 0xe144f982, 0x1944f535, 0x3548012c, 0x0b1240b0, 0x0de88133, 0x8606c0ab, 0x5dc0a0fc, 0x90eff40b, + 0x287b6fc2, 0x6a584873, 0xcac2dca7, 0xff50ad0c, 0x6a119c32, 0xb45bb80b, 0xf528ec03, 0x3437e923, 0xe1f15647, 0xead069b0, 0x7dba443c, 0x0096ec5d, 0x378fa8b7, 0x705f2181, 0x1c0d34e7, 0xc585256a, + 0x4fe2b765, 0x21f982b5, 0xa9d0014d, 0xdd08ca50, 0xdb422ca4, 0xec82049b, 0xe2449c40, 0xc4020965, 0xf3e2314a, 0x868e0341, 0xaf82e055, 0xbe661fb0, 0x205beedc, 0x318cefd0, 0x3820ac17, 0xfdf6682a, + 0x2c4856cb, 0x4e3773ec, 0x8d2a5014, 0x383f3ba2, 0xd001b74d, 0xee38ffa0, 0x12257885, 0x8afa4de5, 0xfaf1d6fa, 0x5627ded3, 0x0b1eb8ad, 0xc0fc0180, 0x825bda4f, 0x0fcc15af, 0x227408fa, 0xfad562c0, + 0x9bc01f07, 0x14fd2340, 0x5b03904c, 0x59388158, 0x0012e17f, 0xc86184b7, 0xa5fe60e1, 0x50c0ab1b, 0xe048320a, 0x7e231f59, 0x6cfec634, 0x1c6fdbe0, 0x47fd05f0, 0xadf0a288, 0xf8071c41, 0xd1f60a33, + 0x1e3e3c33, 0x859274f1, 0x09fe8879, 0xa2e9da89, 0xa8df093e, 0x1deedda5, 0x4439bf46, 0x3b61c184, 0xe40d9c12, 0x88a62999, 0xa0e0499b, 0x4837377d, 0x9bbfa7e7, 0x88111caa, 0x56788523, 0x6c5002c2, + 0x37610f25, 0x69ef90b9, 0x7cdab7ee, 0x8f1d1142, 0x534e5623, 0x843893e0, 0x81d77bf0, 0x1df2afd0, 0x3a4cb4a0, 0xdb98bab1, 0xf8cec933, 0x1f5c7762, 0x877acb40, 0x09bc71fb, 0xba4b2cf8, 0xc7657046, + 0x74f624da, 0xd7d1ba59, 0x3807e60a, 0xab614b95, 0xbda0b6fc, 0x8b3283bb, 0x5a046162, 0xe520b687, 0x42e45da0, 0x5bd7633e, 0x63013a75, 0xfc844c52, 0x6e620228, 0x50e1779f, 0xe54fffd0, 0xe33fc038, + 0xf1564ffb, 0x8e69a62d, 0x1613fe87, 0x796ec3f9, 0x877afc90, 0x1723dc5f, 0xf0ebee59, 0x112cc174, 0x30568105, 0x0004523f, 0x6a31d7a6, 0x5097ecb9, 0x609ca6f8, 0x0926c1de, 0xa0c24829, 0x7053f673, + 0xed0c6eef, 0x25e0f10a, 0xd16451ab, 0x5f856b20, 0x1507cedc, 0xf418a727, 0x6ffee42f, 0x5ccf6fa1, 0x57a44a00, 0xe01d17de, 0x376040fa, 0xb3e43d5f, 0xa0eeae2c, 0xf3bf7535, 0x091251c3, 0x18f944b6, + 0x75889c06, 0x1a1fb2e8, 0xb6f814fc, 0x1da8a5c1, 0x004247a0, 0x6fbc4360, 0x5b01b821, 0x3190b34f, 0x67bcb029, 0x1310964a, 0x44eda5e1, 0x38dddfbb, 0x01cb60e5, 0x117d734a, 0xe7af0cf2, 0x5c2e16be, + 0x150c181f, 0x92cc7df5, 0x9790cff4, 0x6091c019, 0x8fcc15b0, 0xc5902c8c, 0x8daec761, 0x4fa4be8d, 0x5419994f, 0x59ca7535, 0x78ad09fe, 0x3d0f9de3, 0x889f2c90, 0xd7744567, 0x4049f068, 0xf8071c26, + 0x1d218e77, 0x7ef8b5e3, 0x2d55bd60, 0x58c7376a, 0x5c5495dd, 0x50c12380, 0x068f982b, 0xa2706679, 0x47fe5dad, 0x0c092d0e, 0x2740cc1b, 0x1e4cfc11, 0x60dc6921, 0xf87f57e7, 0x6d760247, 0x8880d214, + 0xba56319e, 0x12d38680, 0x0e716ff6, 0x09754230, 0x1ebd605b, 0xec979bbd, 0x5657d9c7, 0x026b249a, 0xfbe00ca3, 0x6c1872a3, 0xfa84df66, 0x05617086, 0x621c8c3a, 0x36a211ff, 0x29f22b02, 0x0313eaf6, + 0x85a70be3, 0xeb1a14d3, 0x5d5dd300, 0xbbe1c301, 0xb0fec7d6, 0xc30dd605, 0x47210f3e, 0x8d76f0af, 0x1091d03d, 0xbe70c98a, 0x88c3499f, 0x0c3a8bc4, 0x6e8f429c, 0x489e4568, 0x854803be, 0xaffe16d3, + 0x96800e71, 0xc7ffc0e7, 0xe67e9349, 0x8aba1d27, 0xe00ffe83, 0x0ad83048, 0x0cde03e6, 0x0ece3142, 0x1d8cfdf7, 0xf8fcc0cc, 0xc605a3e2, 0x59ed2b83, 0x3067074e, 0x0ef1efe4, 0x2b6f6410, 0xacc7376f, + 0xe525056e, 0x2c15a32c, 0xd225dff0, 0xe0b319d1, 0xddc9f7d6, 0x0acc7dfb, 0xc23940ae, 0x8ffb0d19, 0x9a500ee4, 0xf3968dee, 0xd38ff411, 0xe03084f5, 0x8300d07e, 0xaba2d19d, 0x9b206fa7, 0xf5a1df87, + 0x3f98c26c, 0xa1c33873, 0x8070a5bf, 0x7d703434, 0x58c48f66, 0xa47261cc, 0xafc7a973, 0xaf7576c6, 0xcb4b0e76, 0xd818f139, 0xeabc51ea, 0xc3c6f0d3, 0x039396ff, 0x0f9f01f0, 0xe4d7491a, 0xd991c1c0, + 0x91d9a9a9, 0x0cc981e1, 0xa19a2cdc, 0xcb7db3fd, 0xd3ab33ab, 0xde0397ea, 0xa83f1101, 0x56413db6, 0xd44b5632, 0x11de024b, 0xb6ab9f31, 0x3f42413d, 0xa88cb6a8, 0x90ff3f36, 0x6daaa7a8, 0x0798918f, + 0x55848fd4, 0x1de02cc0, 0xb54f7713, 0xf3e031f1, 0x30663541, 0x3a500a79, 0x9f2283bc, 0x463e36a8, 0xae8e3f46, 0x27dbc6f4, 0xc5477873, 0x3e36a9ae, 0xa83f4646, 0x018fa8ae, 0x3b4587f8, 0x80a560f5, + 0x65f43287, 0x31f1b558, 0x40b06a9c, 0xe47b383d, 0xc7e98435, 0xb279bc4c, 0x140c5387, 0x8d6506aa, 0x2d60869c, 0x0240d219, 0xe9416b72, 0xa992018a, 0xa5b00962, 0x7598f6da, 0x8b40294e, 0x9e8cb05c, + 0xa9294d6c, 0x97863e36, 0xe3b1de09, 0x2fe6d56b, 0xa0d35f01, 0xdb55fb33, 0xd715f09e, 0x02a7d401, 0xac0258ab, 0x813db6aa, 0xb1dc0897, 0xfb6abee3, 0xe637a497, 0x16974369, 0x2df0b012, 0xfed601d7, + 0x7201daac, 0xa12bd029, 0x2de9b553, 0xf52079b8, 0x6be02099, 0xb046741a, 0xb86b3bad, 0x89f52079, 0xe111f000, 0xdaa032da, 0x9e9c36f2, 0x44ea8c60, 0x0e6cfc00, 0x6d56995d, 0x1a7e0b79, 0x7f8ec760, + 0x0c807628, 0xd2d573fb, 0xce459eca, 0xf6f83f9d, 0x0413dd57, 0x5f59a33d, 0xbcb6aace, 0x071887cd, 0xd212dc50, 0x994edaab, 0x929e3221, 0x5028fc07, 0x6d281c79, 0x8e375c30, 0x1e17d2e9, 0x2eaa0513, + 0x04ad5708, 0x786d5658, 0xb068ce07, 0x30e98a45, 0x0a9cc879, 0x910e8ad5, 0x81725de3, 0xa1661733, 0x75281eb4, 0xa627542a, 0xe1dddb54, 0xd5fd9c5d, 0xa7720186, 0xf2223542, 0xe1bcd85c, 0x2858050d, + 0x9ec9072d, 0x4985d50a, 0x6c60245d, 0x80c28010, 0xff67567f, 0xb542a62c, 0xc8091b40, 0x21cc213b, 0xec9094f6, 0x542a6ec8, 0x0207ea31, 0x012a621d, 0xc446bc98, 0x54eb5419, 0xb36a88e8, 0x54c66cbb, + 0xbb0a3002, 0x0a9bf4c2, 0x91955aed, 0x0c1ee099, 0x7efc2b9d, 0xc9750b30, 0x42a6fd60, 0x120bba07, 0x1875549a, 0xcde1b408, 0x4ba83a05, 0x15376b06, 0x372d87da, 0x1d4b760a, 0x626d0206, 0x6518c113, + 0xa9b3502a, 0xe57b71d0, 0x27a6819e, 0x542a8e23, 0x54068081, 0x92f502a8, 0x02dd8700, 0x0d6c0cfe, 0xe2d2a838, 0xa034447e, 0x4ea05cc2, 0xfd2da153, 0x051b7dd4, 0x062d337d, 0xf3e22d02, 0x87551880, + 0x15346804, 0xafc87ada, 0x67001adb, 0x1031ead7, 0x1a3bc168, 0x67514280, 0x21233eb0, 0x9b894367, 0x93dbf4fa, 0x36810316, 0x0ec11d31, 0xbac22a8a, 0xb30e854c, 0x0e9a779a, 0x8973fda7, 0x8ecf060f, + 0xb05ad8f5, 0xe2c3a285, 0x6d0a98f4, 0xccdaca89, 0x7a7efba4, 0xf05a040c, 0x51a3880e, 0x62d20a93, 0x2665b42a, 0x9f576eba, 0x8f4d5faa, 0x910b4081, 0x4d461034, 0x09035041, 0x5f97b030, 0xae499db6, + 0x08193505, 0x052810b4, 0x0111285a, 0x7850a9c0, 0x7f48d933, 0xa9bed56c, 0x404a6937, 0x0b408193, 0x68a00941, 0x1538043a, 0x26eb0bda, 0x1313ed6a, 0xc95b756b, 0x8193405c, 0x4cdc4240, 0x110a8788, + 0x1d0a9d00, 0xfe97d5dd, 0x8aaff797, 0x5a9cedcf, 0xe811b04e, 0x10481032, 0x04480095, 0x4004c828, 0xa14f42a7, 0x22dd3a2d, 0x309bde32, 0x7fd5db52, 0xa8353456, 0xcd140493, 0x88146002, 0x2854e000, + 0xa9f85379, 0x78fac1e4, 0x9bfca3ba, 0xd6453b46, 0x02065d01, 0x0059a209, 0x10a6828c, 0xfd85e360, 0x4486c082, 0xc703454d, 0xe8722f21, 0xe71b3d5e, 0xffe38704, 0x15887e4a, 0x2ff22c02, 0x42a74007, + 0x7bb6b5dd, 0x33612c4b, 0x42467879, 0xe8fd5ea7, 0x744b54f2, 0x40cba200, 0x10b04120, 0x80fb9160, 0x942a7418, 0xe69bc6da, 0xa062f93b, 0x5eef60eb, 0x4d9a6d99, 0x9d73a3dd, 0xe1c2c723, 0x81033681, + 0xa109ee04, 0x0f9f7241, 0x76854e02, 0xdb756e05, 0x2300e76a, 0xee1c1b5e, 0xce6d1e5c, 0xda472cca, 0x9757467a, 0x46a73ab7, 0x265a0786, 0x35070707, 0xe2c21339, 0x802f509d, 0x1dbce3c3, 0x58012800, + 0xeabb174d, 0x2ead8cf7, 0x76756ef7, 0x9a1ef56e, 0xa9ed199b, 0x972b29d7, 0xb1253f3b, 0xe0d9ee7f, 0xef22607e, 0xabdbea90, 0x6079c23f, 0x41039c8b, 0x36a854e8, 0x92da77b3, 0xe4c40824, 0x9d0617dc, + 0xde29d50a, 0x412493d6, 0xbe5ce620, 0x60c7e1cd, 0xfaa63ff6, 0x37cfeaf6, 0xdf201d09, 0x07a10303, 0x73556843, 0x5bee13d3, 0x9ae45802, 0x854e840c, 0xd772956a, 0x30999112, 0x01eeb93f, 0x8d50a9d0, + 0x2534eefa, 0x22d01a51, 0x38003dcf, 0x034c1212, 0xde244219, 0x0d6d7201, 0x8e854e85, 0xdabdbea8, 0x4a0eda23, 0x3dbf24f3, 0xaa153a00, 0xba1c01cf, 0xc8076292, 0x4e800f73, 0xdfc7f685, 0xc5271a81, + 0x1c67900e, 0x350a9d00, 0x9218a069, 0xf22c8166, 0x5380038b, 0xefaa2da1, 0xce24f6af, 0x55e45902, 0x854e800f, 0x0c50349a, 0x0e00eb49, 0x5203d579, 0x68d9dd8f, 0x27ad7f34, 0x47c44771, 0x184d5791, + 0xd26a153a, 0xac243140, 0x74e47b04, 0x2a700856, 0x6281a4d4, 0x84173b10, 0x22033e45, 0x11d0a9d0, 0x775837d5, 0x424207c4, 0x145e72e4, 0xa4d42a74, 0xd4486281, 0x9727ea0d, 0x5df43074, 0xc40d26a1, + 0xd81a4d27, 0x00549721, 0x26a15df4, 0x6527c40d, 0x8722c012, 0xa74208d7, 0x881a4d42, 0x24ca66cf, 0xed0e4580, 0x50a9d0e1, 0x93e20693, 0x443c1dac, 0xfa1032f6, 0x069350ae, 0x809003e2, 0x422bec98, + 0x08f82380, 0xb0efaadd, 0x05ef24de, 0xeb2e73f3, 0x2a74206c, 0x81a4c874, 0x0f1d08b8, 0x6ceb23cd, 0x742a7420, 0xd61df556, 0x8113649b, 0x897eb22c, 0xa15df414, 0x25c40d26, 0xb15125fe, 0x86145c64, + 0xa4c391be, 0xa7ac7881, 0xf3f2056a, 0x43fb5334, 0x1a4d42a7, 0xb85bcb88, 0x048e33fd, 0x5aa153a1, 0x1fa5c31d, 0x22d018c0, 0xe810bed3, 0xea9f6854, 0xe935ac5b, 0xac83017a, 0x0c5e7965, 0xa4d42a70, + 0x3824b881, 0x86478e18, 0xebf04135, 0x881a4dc2, 0x0c707bc3, 0xbb2e7168, 0x81972373, 0x69341ff6, 0x5ba90e20, 0x776482c1, 0x2a743076, 0x3881a4d4, 0x016921fc, 0x0c1f32c8, 0x6931091d, 0x46a00e20, + 0x9d593f00, 0x577e071a, 0x312756a8, 0x6973c66a, 0x4b76400b, 0xa15df800, 0x5bf07d35, 0x8017120c, 0xe80096cc, 0x0349a854, 0x01732345, 0x80096cc8, 0x0693004a, 0x02ea468a, 0x0012d590, 0x49a8577d, + 0x77224503, 0x0968c801, 0xd42bbf00, 0x012281a4, 0xc9048237, 0x9d001722, 0x7d541d0a, 0x5e45f597, 0x8b21c00e, 0x2a742074, 0x2281a4d4, 0xf4049691, 0x0022a0c8, 0x9ce8577d, 0x2dacdbea, 0x3400aab2, + 0xe0828bf2, 0x349a8577, 0xca921450, 0xf7a47e80, 0x42a74005, 0x281a4c3b, 0x007d3902, 0xb764d641, 0x42a74106, 0xb32f7a75, 0x0469485e, 0x4e7b230a, 0xba153a0c, 0xeb3efaa5, 0x2c1a700a, 0xc20db91a, + 0x9350a9d0, 0x53608a06, 0x9b302b42, 0xc5a752fb, 0xc5a153a1, 0x5a77d566, 0x02cc2453, 0x21352144, 0x350a9d0c, 0xb2362069, 0x5234c093, 0xa9d041b3, 0x77d529d0, 0xc832535a, 0x0c487901, 0xa153a105, + 0xe6c40d26, 0x00170cb3, 0x3bca0a14, 0x153a1a41, 0x4c40d26a, 0xd2cc7fd6, 0x8e4f32fe, 0x9a854e83, 0xc0131034, 0x48627099, 0x4e87171b, 0x10349a85, 0x13a0778b, 0xa5904481, 0xa153c103, 0x22c40d26, + 0x21680553, 0xa7420749, 0xaa638b42, 0x648eb5ef, 0xe46d00ab, 0x54e840e8, 0x0be65168, 0xec85abe0, 0xf7348072, 0x418774a5, 0x55c742a7, 0x491d6cdf, 0xe4a06af5, 0x010d68c8, 0x349a854f, 0xda2c9850, + 0xa8c85840, 0x42a7430b, 0xcc281a4d, 0xe055467a, 0x45e0f730, 0xa854e821, 0xc9850349, 0x041408b0, 0x1d0c22de, 0xa0693109, 0x006d7910, 0x0026f018, 0x69350a9e, 0x6df920a0, 0x67681800, 0xb42a7800, + 0xd6fdf557, 0x00371c8e, 0x8012300c, 0xf716854e, 0xbadfbeaa, 0x1b8e55f1, 0x16580600, 0x93004a84, 0x99b00a06, 0xcc301171, 0x083a5013, 0xd26a153a, 0xcf240140, 0xa9008b02, 0x42a78009, 0xe1bea82f, + 0x0d4b919a, 0x75e00820, 0xd0a9d032, 0x3beab4e2, 0x72ef17ae, 0x390401a9, 0x0c567e97, 0x6c5a153a, 0xf5cb7d50, 0xb0499322, 0x0427d278, 0x26a153a1, 0xf323e40d, 0xd228a02e, 0xa9e082c6, 0xb7d561d0, + 0x8ef22b5c, 0x52d63d00, 0xa7410a8e, 0x881a4d42, 0xd008ff26, 0x187f6523, 0x38b42a74, 0xeba6faaa, 0x605d0e44, 0x254e3cd1, 0x805b8603, 0xe9bea9d4, 0x004ca11a, 0xad7aa458, 0xe239b35c, 0x04ea1a59, + 0xaa5ac580, 0x519b406c, 0x935092f1, 0x9749a001, 0x201e300d, 0x19457587, 0x3d949a00, 0xdf54a7a2, 0xa720aabb, 0x293e502e, 0xea662b5f, 0x2ade7079, 0x29360016, 0x0a4641f3, 0x002d5142, 0x4109d21d, + 0xa8a00423, 0x690e8017, 0xccdb045b, 0xac011f5b, 0xeedaacff, 0x16d24f1e, 0x4c148d06, 0x600cd28f, 0xfb697b90, 0x108dea83, 0x4dce4948, 0x7ea471a0, 0x8291b0c3, 0x0024d0e9, 0x139548f0, 0x982918c6, + 0x603d2506, 0x211fd27c, 0x6260a467, 0x20fcc8fe, 0x0da4e490, 0xb8a61106, 0x027d1097, 0xa8c01a40, 0x89829180, 0x0a75ccd8, 0xe9739c4c, 0x8dbb0627, 0x43a38807, 0xea4d8805, 0x82918074, 0x012ad049, + 0xe03ee7e0, 0x05233108, 0x04305105, 0xfce40e24, 0x85052320, 0x1c02a2d0, 0x6094ea4d, 0x8484e4c9, 0x18012cd0, 0x03c031e6, 0xe58c148c, 0x4012fd17, 0x0a35d241, 0x1829185b, 0x1fd80543, 0x0a4601e6, + 0x12a9f972, 0xc763bfec, 0x18078853, 0x854d1829, 0x01e81fd8, 0x51460a46, 0xee07f631, 0x0846dd02, 0xfb20a930, 0x230162c3, 0x84549905, 0x7900f3fe, 0x61829180, 0xb0fecc2a, 0x1a19908c, 0xa56df108, + 0x17561fd9, 0x80305230, 0x365ff9e6, 0x9bef41f7, 0x63c04237, 0xb6d50bfd, 0xd013c74c, 0x03c6c289, 0xae35d61c, 0x0739f2ff, 0x0cd44e3b, 0x3536100d, 0x0a49001a, 0x63104fa0, 0xb9e220a4, 0xe08023c2, + 0x375ec1ac, 0xe160a46d, 0x9ffd96bd, 0x2f3d4809, 0x2179a840, 0xfd8699f0, 0x0a7909df, 0xf8d4084a, 0xa54568fc, 0x07b8a37f, 0x2d68d77b, 0xaaefc14b, 0x70d0fec1, 0x068776b0, 0xfc362b36, 0x160e720b, + 0x14a8d40c, 0xf8e9ae7c, 0x60d61a47, 0x49a9986f, 0xf2055829, 0x0526068f, 0x583cb06b, 0x068ff215, 0xb5a81956, 0xad75b14e, 0x01d23fa7, 0x25350214, 0x70579f05, 0xb59a37f6, 0x1b2f41af, 0x582921ac, + 0xc7bfdb65, 0xa0465063, 0x0560a466, 0xf2887f36, 0xac1e5604, 0xa9b82909, 0x0cb7f0ed, 0x0d60f180, 0x2acfc148, 0x0713f8bd, 0x76a81b6c, 0xc55eb829, 0x2c03bfcb, 0xf9eea81a, 0xff54ead1, 0x873ed641, + 0x2cd50344, 0xab543f05, 0x1f720ffa, 0x55ab05a4, 0x68566e0a, 0xd61a67eb, 0xa3f3307e, 0xfd5eaa41, 0xc907b9f9, 0x4e61570c, 0x4a4ab368, 0xfdace2ff, 0x5408b003, 0x67e8fcdd, 0x99f9dd15, 0x3e807b92, + 0x8292ea91, 0x5bf8f7b5, 0x15409480, 0xdb1ac149, 0xe12069fd, 0xc148b540, 0xf36a2fcd, 0xfb58b7f1, 0x48a37d3c, 0xb7c14875, 0x47fb12b5, 0xd4b7e4f1, 0x4205501e, 0xaeaa8fe0, 0x38f208fc, 0x74ac18b5, + 0xf155a80f, 0x0fb00417, 0xc0d0f454, 0x7f15dadb, 0x1ea49e41, 0xf9dcac21, 0x5135f7d1, 0x5ee611f8, 0x42a078da, 0x6fbbe0a5, 0xe01feacc, 0x70e4ca69, 0xcd810a25, 0xfb5d6b6f, 0x052b48b3, 0xd47e6f2a, + 0x9c11fe6c, 0x5e16a1fb, 0x8aa1ff45, 0xf3a4be46, 0xa8895d24, 0x67e0a482, 0x79f1fb46, 0x2e34b7f2, 0xf0522950, 0xf68c5ffd, 0x1a7ee58b, 0x2908a817, 0xbf3195f8, 0x405ca918, 0xcc68297b, 0x1b34809f, + 0xd052de81, 0x4f213f98, 0x1a044792, 0x307c0d0f, 0xe7392fd2, 0x433a4f3e, 0x5c14a9a2, 0x1b16a9ff, 0xf6b064be, 0x58826134, 0xeef82947, 0x01f6935b, 0x64b7ef70, 0x29374062, 0xfba6bef8, 0xe4f0a47d, + 0x813b44eb, 0xeb629d4e, 0xe6367f6f, 0xd3dac097, 0x00f027bf, 0xf3e0a46d, 0x2fdaccef, 0x883ffb8d, 0xcc5a032f, 0x307fae8f, 0xd65c9f61, 0x01ab0b1e, 0xfdf052f5, 0x9bdca637, 0x173ee701, 0x4a940a0d, + 0x98bf77c1, 0xdc665f72, 0x6dac5aff, 0xdc149944, 0xfd2cc9fc, 0x07ef7319, 0x92501f11, 0xfb7cda13, 0x0e7eb1a5, 0x40f889ac, 0x58d052ee, 0xc7ee573f, 0xa55c81ea, 0xcc5ff3e0, 0xe73b9fca, 0xafc6fe9e, + 0x0524e517, 0xeefabfd7, 0x357d6353, 0xfafbddac, 0x61c1395f, 0xf9f0520e, 0x889a5f4f, 0x1ee72d4f, 0xb67223ff, 0xf8295b2c, 0xa637c3ee, 0xe707a7cc, 0x96c0ff1e, 0x251d9904, 0x37edfcf2, 0x5f759a3f, + 0xfe3dce2b, 0xf9c23ffc, 0xe0a40ce2, 0x7cbf57fa, 0x9afcb269, 0xd7e9e4f2, 0xd9902f41, 0xd5feb629, 0xc118df4f, 0x1e4f3da7, 0x7fb7fe7f, 0x9ca1b4f3, 0x1fe781a1, 0xd8c2fdbe, 0xbb5882be, 0xefe0dc8f, + 0x829702ff, 0xe9b1ef9b, 0x50afd48d, 0xf83f21ce, 0x9eafb7e1, 0xfc3627af, 0x7e1ff7bb, 0x9fcbeef9, 0x7d4f313f, 0xbfa7f055, 0x2f97fa7e, 0x97f6f9bf, 0xf57d9fb7, 0xb64dbbc7, 0xfbbe6c3b, 0x1f67fde3, + 0xbbf6fa7e, 0xa9f2fe2f, 0xf3757bb1, 0xf27ebfa7, 0x7e9f8fc9, 0x08cfe9fa, 0xfe3f3ff4, 0x8d5fc9f8, 0x83060dc5, 0x183060c1, 0xc183060c, 0x0c183060, 0x60c18306, 0xff1f5430, 0x3cd2fa0f, 0x05b0d517, + 0x000000b2, 0x4e454900, 0x6042ae44, 0x00000082, +}; + +const uint32_t uprof_512_len = 12813; + +#endif //MICROPROFILE_EMBED_HTML diff --git a/Minecraft.Client/Windows64/4JLibs/inc/Render/microprofile/stb/stb_sprintf.h b/Minecraft.Client/Windows64/4JLibs/inc/Render/microprofile/stb/stb_sprintf.h new file mode 100644 index 000000000..d8011fa37 --- /dev/null +++ b/Minecraft.Client/Windows64/4JLibs/inc/Render/microprofile/stb/stb_sprintf.h @@ -0,0 +1,1906 @@ +// stb_sprintf - v1.10 - public domain snprintf() implementation +// originally by Jeff Roberts / RAD Game Tools, 2015/10/20 +// http://github.com/nothings/stb +// +// allowed types: sc uidBboXx p AaGgEef n +// lengths : hh h ll j z t I64 I32 I +// +// Contributors: +// Fabian "ryg" Giesen (reformatting) +// github:aganm (attribute format) +// +// Contributors (bugfixes): +// github:d26435 +// github:trex78 +// github:account-login +// Jari Komppa (SI suffixes) +// Rohit Nirmal +// Marcin Wojdyr +// Leonard Ritter +// Stefano Zanotti +// Adam Allison +// Arvid Gerstmann +// Markus Kolb +// +// LICENSE: +// +// See end of file for license information. + +#ifndef STB_SPRINTF_H_INCLUDE +#define STB_SPRINTF_H_INCLUDE + +/* +Single file sprintf replacement. + +Originally written by Jeff Roberts at RAD Game Tools - 2015/10/20. +Hereby placed in public domain. + +This is a full sprintf replacement that supports everything that +the C runtime sprintfs support, including float/double, 64-bit integers, +hex floats, field parameters (%*.*d stuff), length reads backs, etc. + +Why would you need this if sprintf already exists? Well, first off, +it's *much* faster (see below). It's also much smaller than the CRT +versions code-space-wise. We've also added some simple improvements +that are super handy (commas in thousands, callbacks at buffer full, +for example). Finally, the format strings for MSVC and GCC differ +for 64-bit integers (among other small things), so this lets you use +the same format strings in cross platform code. + +It uses the standard single file trick of being both the header file +and the source itself. If you just include it normally, you just get +the header file function definitions. To get the code, you include +it from a C or C++ file and define STB_SPRINTF_IMPLEMENTATION first. + +It only uses va_args macros from the C runtime to do it's work. It +does cast doubles to S64s and shifts and divides U64s, which does +drag in CRT code on most platforms. + +It compiles to roughly 8K with float support, and 4K without. +As a comparison, when using MSVC static libs, calling sprintf drags +in 16K. + +API: +==== +int stbsp_sprintf( char * buf, char const * fmt, ... ) +int stbsp_snprintf( char * buf, int count, char const * fmt, ... ) + Convert an arg list into a buffer. stbsp_snprintf always returns + a zero-terminated string (unlike regular snprintf). + +int stbsp_vsprintf( char * buf, char const * fmt, va_list va ) +int stbsp_vsnprintf( char * buf, int count, char const * fmt, va_list va ) + Convert a va_list arg list into a buffer. stbsp_vsnprintf always returns + a zero-terminated string (unlike regular snprintf). + +int stbsp_vsprintfcb( STBSP_SPRINTFCB * callback, void * user, char * buf, char const * fmt, va_list va ) + typedef char * STBSP_SPRINTFCB( char const * buf, void * user, int len ); + Convert into a buffer, calling back every STB_SPRINTF_MIN chars. + Your callback can then copy the chars out, print them or whatever. + This function is actually the workhorse for everything else. + The buffer you pass in must hold at least STB_SPRINTF_MIN characters. + // you return the next buffer to use or 0 to stop converting + +void stbsp_set_separators( char comma, char period ) + Set the comma and period characters to use. + +FLOATS/DOUBLES: +=============== +This code uses a internal float->ascii conversion method that uses +doubles with error correction (double-doubles, for ~105 bits of +precision). This conversion is round-trip perfect - that is, an atof +of the values output here will give you the bit-exact double back. + +One difference is that our insignificant digits will be different than +with MSVC or GCC (but they don't match each other either). We also +don't attempt to find the minimum length matching float (pre-MSVC15 +doesn't either). + +If you don't need float or doubles at all, define STB_SPRINTF_NOFLOAT +and you'll save 4K of code space. + +64-BIT INTS: +============ +This library also supports 64-bit integers and you can use MSVC style or +GCC style indicators (%I64d or %lld). It supports the C99 specifiers +for size_t and ptr_diff_t (%jd %zd) as well. + +EXTRAS: +======= +Like some GCCs, for integers and floats, you can use a ' (single quote) +specifier and commas will be inserted on the thousands: "%'d" on 12345 +would print 12,345. + +For integers and floats, you can use a "$" specifier and the number +will be converted to float and then divided to get kilo, mega, giga or +tera and then printed, so "%$d" 1000 is "1.0 k", "%$.2d" 2536000 is +"2.53 M", etc. For byte values, use two $:s, like "%$$d" to turn +2536000 to "2.42 Mi". If you prefer JEDEC suffixes to SI ones, use three +$:s: "%$$$d" -> "2.42 M". To remove the space between the number and the +suffix, add "_" specifier: "%_$d" -> "2.53M". + +In addition to octal and hexadecimal conversions, you can print +integers in binary: "%b" for 256 would print 100. + +PERFORMANCE vs MSVC 2008 32-/64-bit (GCC is even slower than MSVC): +=================================================================== +"%d" across all 32-bit ints (4.8x/4.0x faster than 32-/64-bit MSVC) +"%24d" across all 32-bit ints (4.5x/4.2x faster) +"%x" across all 32-bit ints (4.5x/3.8x faster) +"%08x" across all 32-bit ints (4.3x/3.8x faster) +"%f" across e-10 to e+10 floats (7.3x/6.0x faster) +"%e" across e-10 to e+10 floats (8.1x/6.0x faster) +"%g" across e-10 to e+10 floats (10.0x/7.1x faster) +"%f" for values near e-300 (7.9x/6.5x faster) +"%f" for values near e+300 (10.0x/9.1x faster) +"%e" for values near e-300 (10.1x/7.0x faster) +"%e" for values near e+300 (9.2x/6.0x faster) +"%.320f" for values near e-300 (12.6x/11.2x faster) +"%a" for random values (8.6x/4.3x faster) +"%I64d" for 64-bits with 32-bit values (4.8x/3.4x faster) +"%I64d" for 64-bits > 32-bit values (4.9x/5.5x faster) +"%s%s%s" for 64 char strings (7.1x/7.3x faster) +"...512 char string..." ( 35.0x/32.5x faster!) +*/ + +#if defined(__clang__) + #if defined(__has_feature) && defined(__has_attribute) + #if __has_feature(address_sanitizer) + #if __has_attribute(__no_sanitize__) + #define STBSP__ASAN __attribute__((__no_sanitize__("address"))) + #elif __has_attribute(__no_sanitize_address__) + #define STBSP__ASAN __attribute__((__no_sanitize_address__)) + #elif __has_attribute(__no_address_safety_analysis__) + #define STBSP__ASAN __attribute__((__no_address_safety_analysis__)) + #endif + #endif + #endif +#elif defined(__GNUC__) && (__GNUC__ >= 5 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8)) + #if defined(__SANITIZE_ADDRESS__) && __SANITIZE_ADDRESS__ + #define STBSP__ASAN __attribute__((__no_sanitize_address__)) + #endif +#endif + +#ifndef STBSP__ASAN +#define STBSP__ASAN +#endif + +#ifdef STB_SPRINTF_STATIC +#define STBSP__PUBLICDEC static +#define STBSP__PUBLICDEF static STBSP__ASAN +#else +#ifdef __cplusplus +#define STBSP__PUBLICDEC extern "C" +#define STBSP__PUBLICDEF extern "C" STBSP__ASAN +#else +#define STBSP__PUBLICDEC extern +#define STBSP__PUBLICDEF STBSP__ASAN +#endif +#endif + +#if defined(__has_attribute) + #if __has_attribute(format) + #define STBSP__ATTRIBUTE_FORMAT(fmt,va) __attribute__((format(printf,fmt,va))) + #endif +#endif + +#ifndef STBSP__ATTRIBUTE_FORMAT +#define STBSP__ATTRIBUTE_FORMAT(fmt,va) +#endif + +#ifdef _MSC_VER +#define STBSP__NOTUSED(v) (void)(v) +#else +#define STBSP__NOTUSED(v) (void)sizeof(v) +#endif + +#include // for va_arg(), va_list() +#include // size_t, ptrdiff_t + +#ifndef STB_SPRINTF_MIN +#define STB_SPRINTF_MIN 512 // how many characters per callback +#endif +typedef char *STBSP_SPRINTFCB(const char *buf, void *user, int len); + +#ifndef STB_SPRINTF_DECORATE +#define STB_SPRINTF_DECORATE(name) stbsp_##name // define this before including if you want to change the names +#endif + +STBSP__PUBLICDEC int STB_SPRINTF_DECORATE(vsprintf)(char *buf, char const *fmt, va_list va); +STBSP__PUBLICDEC int STB_SPRINTF_DECORATE(vsnprintf)(char *buf, int count, char const *fmt, va_list va); +STBSP__PUBLICDEC int STB_SPRINTF_DECORATE(sprintf)(char *buf, char const *fmt, ...) STBSP__ATTRIBUTE_FORMAT(2,3); +STBSP__PUBLICDEC int STB_SPRINTF_DECORATE(snprintf)(char *buf, int count, char const *fmt, ...) STBSP__ATTRIBUTE_FORMAT(3,4); + +STBSP__PUBLICDEC int STB_SPRINTF_DECORATE(vsprintfcb)(STBSP_SPRINTFCB *callback, void *user, char *buf, char const *fmt, va_list va); +STBSP__PUBLICDEC void STB_SPRINTF_DECORATE(set_separators)(char comma, char period); + +#endif // STB_SPRINTF_H_INCLUDE + +#ifdef STB_SPRINTF_IMPLEMENTATION + +#define stbsp__uint32 unsigned int +#define stbsp__int32 signed int + +#ifdef _MSC_VER +#define stbsp__uint64 unsigned __int64 +#define stbsp__int64 signed __int64 +#else +#define stbsp__uint64 unsigned long long +#define stbsp__int64 signed long long +#endif +#define stbsp__uint16 unsigned short + +#ifndef stbsp__uintptr +#if defined(__ppc64__) || defined(__powerpc64__) || defined(__aarch64__) || defined(_M_X64) || defined(__x86_64__) || defined(__x86_64) || defined(__s390x__) +#define stbsp__uintptr stbsp__uint64 +#else +#define stbsp__uintptr stbsp__uint32 +#endif +#endif + +#ifndef STB_SPRINTF_MSVC_MODE // used for MSVC2013 and earlier (MSVC2015 matches GCC) +#if defined(_MSC_VER) && (_MSC_VER < 1900) +#define STB_SPRINTF_MSVC_MODE +#endif +#endif + +#ifdef STB_SPRINTF_NOUNALIGNED // define this before inclusion to force stbsp_sprintf to always use aligned accesses +#define STBSP__UNALIGNED(code) +#else +#define STBSP__UNALIGNED(code) code +#endif + +#ifndef STB_SPRINTF_NOFLOAT +// internal float utility functions +static stbsp__int32 stbsp__real_to_str(char const **start, stbsp__uint32 *len, char *out, stbsp__int32 *decimal_pos, double value, stbsp__uint32 frac_digits); +static stbsp__int32 stbsp__real_to_parts(stbsp__int64 *bits, stbsp__int32 *expo, double value); +#define STBSP__SPECIAL 0x7000 +#endif + +static char stbsp__period = '.'; +static char stbsp__comma = ','; +static struct +{ + short temp; // force next field to be 2-byte aligned + char pair[201]; +} stbsp__digitpair = +{ + 0, + "00010203040506070809101112131415161718192021222324" + "25262728293031323334353637383940414243444546474849" + "50515253545556575859606162636465666768697071727374" + "75767778798081828384858687888990919293949596979899" +}; + +STBSP__PUBLICDEF void STB_SPRINTF_DECORATE(set_separators)(char pcomma, char pperiod) +{ + stbsp__period = pperiod; + stbsp__comma = pcomma; +} + +#define STBSP__LEFTJUST 1 +#define STBSP__LEADINGPLUS 2 +#define STBSP__LEADINGSPACE 4 +#define STBSP__LEADING_0X 8 +#define STBSP__LEADINGZERO 16 +#define STBSP__INTMAX 32 +#define STBSP__TRIPLET_COMMA 64 +#define STBSP__NEGATIVE 128 +#define STBSP__METRIC_SUFFIX 256 +#define STBSP__HALFWIDTH 512 +#define STBSP__METRIC_NOSPACE 1024 +#define STBSP__METRIC_1024 2048 +#define STBSP__METRIC_JEDEC 4096 + +static void stbsp__lead_sign(stbsp__uint32 fl, char *sign) +{ + sign[0] = 0; + if (fl & STBSP__NEGATIVE) { + sign[0] = 1; + sign[1] = '-'; + } else if (fl & STBSP__LEADINGSPACE) { + sign[0] = 1; + sign[1] = ' '; + } else if (fl & STBSP__LEADINGPLUS) { + sign[0] = 1; + sign[1] = '+'; + } +} + +static STBSP__ASAN stbsp__uint32 stbsp__strlen_limited(char const *s, stbsp__uint32 limit) +{ + char const * sn = s; + + // get up to 4-byte alignment + for (;;) { + if (((stbsp__uintptr)sn & 3) == 0) + break; + + if (!limit || *sn == 0) + return (stbsp__uint32)(sn - s); + + ++sn; + --limit; + } + + // scan over 4 bytes at a time to find terminating 0 + // this will intentionally scan up to 3 bytes past the end of buffers, + // but becase it works 4B aligned, it will never cross page boundaries + // (hence the STBSP__ASAN markup; the over-read here is intentional + // and harmless) + while (limit >= 4) { + stbsp__uint32 v = *(stbsp__uint32 *)sn; + // bit hack to find if there's a 0 byte in there + if ((v - 0x01010101) & (~v) & 0x80808080UL) + break; + + sn += 4; + limit -= 4; + } + + // handle the last few characters to find actual size + while (limit && *sn) { + ++sn; + --limit; + } + + return (stbsp__uint32)(sn - s); +} + +STBSP__PUBLICDEF int STB_SPRINTF_DECORATE(vsprintfcb)(STBSP_SPRINTFCB *callback, void *user, char *buf, char const *fmt, va_list va) +{ + static char hex[] = "0123456789abcdefxp"; + static char hexu[] = "0123456789ABCDEFXP"; + char *bf; + char const *f; + int tlen = 0; + + bf = buf; + f = fmt; + for (;;) { + stbsp__int32 fw, pr, tz; + stbsp__uint32 fl; + + // macros for the callback buffer stuff + #define stbsp__chk_cb_bufL(bytes) \ + { \ + int len = (int)(bf - buf); \ + if ((len + (bytes)) >= STB_SPRINTF_MIN) { \ + tlen += len; \ + if (0 == (bf = buf = callback(buf, user, len))) \ + goto done; \ + } \ + } + #define stbsp__chk_cb_buf(bytes) \ + { \ + if (callback) { \ + stbsp__chk_cb_bufL(bytes); \ + } \ + } + #define stbsp__flush_cb() \ + { \ + stbsp__chk_cb_bufL(STB_SPRINTF_MIN - 1); \ + } // flush if there is even one byte in the buffer + #define stbsp__cb_buf_clamp(cl, v) \ + cl = v; \ + if (callback) { \ + int lg = STB_SPRINTF_MIN - (int)(bf - buf); \ + if (cl > lg) \ + cl = lg; \ + } + + // fast copy everything up to the next % (or end of string) + for (;;) { + while (((stbsp__uintptr)f) & 3) { + schk1: + if (f[0] == '%') + goto scandd; + schk2: + if (f[0] == 0) + goto endfmt; + stbsp__chk_cb_buf(1); + *bf++ = f[0]; + ++f; + } + for (;;) { + // Check if the next 4 bytes contain %(0x25) or end of string. + // Using the 'hasless' trick: + // https://graphics.stanford.edu/~seander/bithacks.html#HasLessInWord + stbsp__uint32 v, c; + v = *(stbsp__uint32 *)f; + c = (~v) & 0x80808080; + if (((v ^ 0x25252525) - 0x01010101) & c) + goto schk1; + if ((v - 0x01010101) & c) + goto schk2; + if (callback) + if ((STB_SPRINTF_MIN - (int)(bf - buf)) < 4) + goto schk1; + #ifdef STB_SPRINTF_NOUNALIGNED + if(((stbsp__uintptr)bf) & 3) { + bf[0] = f[0]; + bf[1] = f[1]; + bf[2] = f[2]; + bf[3] = f[3]; + } else + #endif + { + *(stbsp__uint32 *)bf = v; + } + bf += 4; + f += 4; + } + } + scandd: + + ++f; + + // ok, we have a percent, read the modifiers first + fw = 0; + pr = -1; + fl = 0; + tz = 0; + + // flags + for (;;) { + switch (f[0]) { + // if we have left justify + case '-': + fl |= STBSP__LEFTJUST; + ++f; + continue; + // if we have leading plus + case '+': + fl |= STBSP__LEADINGPLUS; + ++f; + continue; + // if we have leading space + case ' ': + fl |= STBSP__LEADINGSPACE; + ++f; + continue; + // if we have leading 0x + case '#': + fl |= STBSP__LEADING_0X; + ++f; + continue; + // if we have thousand commas + case '\'': + fl |= STBSP__TRIPLET_COMMA; + ++f; + continue; + // if we have kilo marker (none->kilo->kibi->jedec) + case '$': + if (fl & STBSP__METRIC_SUFFIX) { + if (fl & STBSP__METRIC_1024) { + fl |= STBSP__METRIC_JEDEC; + } else { + fl |= STBSP__METRIC_1024; + } + } else { + fl |= STBSP__METRIC_SUFFIX; + } + ++f; + continue; + // if we don't want space between metric suffix and number + case '_': + fl |= STBSP__METRIC_NOSPACE; + ++f; + continue; + // if we have leading zero + case '0': + fl |= STBSP__LEADINGZERO; + ++f; + goto flags_done; + default: goto flags_done; + } + } + flags_done: + + // get the field width + if (f[0] == '*') { + fw = va_arg(va, stbsp__uint32); + ++f; + } else { + while ((f[0] >= '0') && (f[0] <= '9')) { + fw = fw * 10 + f[0] - '0'; + f++; + } + } + // get the precision + if (f[0] == '.') { + ++f; + if (f[0] == '*') { + pr = va_arg(va, stbsp__uint32); + ++f; + } else { + pr = 0; + while ((f[0] >= '0') && (f[0] <= '9')) { + pr = pr * 10 + f[0] - '0'; + f++; + } + } + } + + // handle integer size overrides + switch (f[0]) { + // are we halfwidth? + case 'h': + fl |= STBSP__HALFWIDTH; + ++f; + if (f[0] == 'h') + ++f; // QUARTERWIDTH + break; + // are we 64-bit (unix style) + case 'l': + fl |= ((sizeof(long) == 8) ? STBSP__INTMAX : 0); + ++f; + if (f[0] == 'l') { + fl |= STBSP__INTMAX; + ++f; + } + break; + // are we 64-bit on intmax? (c99) + case 'j': + fl |= (sizeof(size_t) == 8) ? STBSP__INTMAX : 0; + ++f; + break; + // are we 64-bit on size_t or ptrdiff_t? (c99) + case 'z': + fl |= (sizeof(ptrdiff_t) == 8) ? STBSP__INTMAX : 0; + ++f; + break; + case 't': + fl |= (sizeof(ptrdiff_t) == 8) ? STBSP__INTMAX : 0; + ++f; + break; + // are we 64-bit (msft style) + case 'I': + if ((f[1] == '6') && (f[2] == '4')) { + fl |= STBSP__INTMAX; + f += 3; + } else if ((f[1] == '3') && (f[2] == '2')) { + f += 3; + } else { + fl |= ((sizeof(void *) == 8) ? STBSP__INTMAX : 0); + ++f; + } + break; + default: break; + } + + // handle each replacement + switch (f[0]) { + #define STBSP__NUMSZ 512 // big enough for e308 (with commas) or e-307 + char num[STBSP__NUMSZ]; + char lead[8]; + char tail[8]; + char *s; + char const *h; + stbsp__uint32 l, n, cs; + stbsp__uint64 n64; +#ifndef STB_SPRINTF_NOFLOAT + double fv; +#endif + stbsp__int32 dp; + char const *sn; + + case 's': + // get the string + s = va_arg(va, char *); + if (s == 0) + s = (char *)"null"; + // get the length, limited to desired precision + // always limit to ~0u chars since our counts are 32b + l = stbsp__strlen_limited(s, (pr >= 0) ? pr : ~0u); + lead[0] = 0; + tail[0] = 0; + pr = 0; + dp = 0; + cs = 0; + // copy the string in + goto scopy; + + case 'c': // char + // get the character + s = num + STBSP__NUMSZ - 1; + *s = (char)va_arg(va, int); + l = 1; + lead[0] = 0; + tail[0] = 0; + pr = 0; + dp = 0; + cs = 0; + goto scopy; + + case 'n': // weird write-bytes specifier + { + int *d = va_arg(va, int *); + *d = tlen + (int)(bf - buf); + } break; + +#ifdef STB_SPRINTF_NOFLOAT + case 'A': // float + case 'a': // hex float + case 'G': // float + case 'g': // float + case 'E': // float + case 'e': // float + case 'f': // float + va_arg(va, double); // eat it + s = (char *)"No float"; + l = 8; + lead[0] = 0; + tail[0] = 0; + pr = 0; + cs = 0; + STBSP__NOTUSED(dp); + goto scopy; +#else + case 'A': // hex float + case 'a': // hex float + h = (f[0] == 'A') ? hexu : hex; + fv = va_arg(va, double); + if (pr == -1) + pr = 6; // default is 6 + // read the double into a string + if (stbsp__real_to_parts((stbsp__int64 *)&n64, &dp, fv)) + fl |= STBSP__NEGATIVE; + + s = num + 64; + + stbsp__lead_sign(fl, lead); + + if (dp == -1023) + dp = (n64) ? -1022 : 0; + else + n64 |= (((stbsp__uint64)1) << 52); + n64 <<= (64 - 56); + if (pr < 15) + n64 += ((((stbsp__uint64)8) << 56) >> (pr * 4)); +// add leading chars + +#ifdef STB_SPRINTF_MSVC_MODE + *s++ = '0'; + *s++ = 'x'; +#else + lead[1 + lead[0]] = '0'; + lead[2 + lead[0]] = 'x'; + lead[0] += 2; +#endif + *s++ = h[(n64 >> 60) & 15]; + n64 <<= 4; + if (pr) + *s++ = stbsp__period; + sn = s; + + // print the bits + n = pr; + if (n > 13) + n = 13; + if (pr > (stbsp__int32)n) + tz = pr - n; + pr = 0; + while (n--) { + *s++ = h[(n64 >> 60) & 15]; + n64 <<= 4; + } + + // print the expo + tail[1] = h[17]; + if (dp < 0) { + tail[2] = '-'; + dp = -dp; + } else + tail[2] = '+'; + n = (dp >= 1000) ? 6 : ((dp >= 100) ? 5 : ((dp >= 10) ? 4 : 3)); + tail[0] = (char)n; + for (;;) { + tail[n] = '0' + dp % 10; + if (n <= 3) + break; + --n; + dp /= 10; + } + + dp = (int)(s - sn); + l = (int)(s - (num + 64)); + s = num + 64; + cs = 1 + (3 << 24); + goto scopy; + + case 'G': // float + case 'g': // float + h = (f[0] == 'G') ? hexu : hex; + fv = va_arg(va, double); + if (pr == -1) + pr = 6; + else if (pr == 0) + pr = 1; // default is 6 + // read the double into a string + if (stbsp__real_to_str(&sn, &l, num, &dp, fv, (pr - 1) | 0x80000000)) + fl |= STBSP__NEGATIVE; + + // clamp the precision and delete extra zeros after clamp + n = pr; + if (l > (stbsp__uint32)pr) + l = pr; + while ((l > 1) && (pr) && (sn[l - 1] == '0')) { + --pr; + --l; + } + + // should we use %e + if ((dp <= -4) || (dp > (stbsp__int32)n)) { + if (pr > (stbsp__int32)l) + pr = l - 1; + else if (pr) + --pr; // when using %e, there is one digit before the decimal + goto doexpfromg; + } + // this is the insane action to get the pr to match %g semantics for %f + if (dp > 0) { + pr = (dp < (stbsp__int32)l) ? l - dp : 0; + } else { + pr = -dp + ((pr > (stbsp__int32)l) ? (stbsp__int32) l : pr); + } + goto dofloatfromg; + + case 'E': // float + case 'e': // float + h = (f[0] == 'E') ? hexu : hex; + fv = va_arg(va, double); + if (pr == -1) + pr = 6; // default is 6 + // read the double into a string + if (stbsp__real_to_str(&sn, &l, num, &dp, fv, pr | 0x80000000)) + fl |= STBSP__NEGATIVE; + doexpfromg: + tail[0] = 0; + stbsp__lead_sign(fl, lead); + if (dp == STBSP__SPECIAL) { + s = (char *)sn; + cs = 0; + pr = 0; + goto scopy; + } + s = num + 64; + // handle leading chars + *s++ = sn[0]; + + if (pr) + *s++ = stbsp__period; + + // handle after decimal + if ((l - 1) > (stbsp__uint32)pr) + l = pr + 1; + for (n = 1; n < l; n++) + *s++ = sn[n]; + // trailing zeros + tz = pr - (l - 1); + pr = 0; + // dump expo + tail[1] = h[0xe]; + dp -= 1; + if (dp < 0) { + tail[2] = '-'; + dp = -dp; + } else + tail[2] = '+'; +#ifdef STB_SPRINTF_MSVC_MODE + n = 5; +#else + n = (dp >= 100) ? 5 : 4; +#endif + tail[0] = (char)n; + for (;;) { + tail[n] = '0' + dp % 10; + if (n <= 3) + break; + --n; + dp /= 10; + } + cs = 1 + (3 << 24); // how many tens + goto flt_lead; + + case 'f': // float + fv = va_arg(va, double); + doafloat: + // do kilos + if (fl & STBSP__METRIC_SUFFIX) { + double divisor; + divisor = 1000.0f; + if (fl & STBSP__METRIC_1024) + divisor = 1024.0; + while (fl < 0x4000000) { + if ((fv < divisor) && (fv > -divisor)) + break; + fv /= divisor; + fl += 0x1000000; + } + } + if (pr == -1) + pr = 6; // default is 6 + // read the double into a string + if (stbsp__real_to_str(&sn, &l, num, &dp, fv, pr)) + fl |= STBSP__NEGATIVE; + dofloatfromg: + tail[0] = 0; + stbsp__lead_sign(fl, lead); + if (dp == STBSP__SPECIAL) { + s = (char *)sn; + cs = 0; + pr = 0; + goto scopy; + } + s = num + 64; + + // handle the three decimal varieties + if (dp <= 0) { + stbsp__int32 i; + // handle 0.000*000xxxx + *s++ = '0'; + if (pr) + *s++ = stbsp__period; + n = -dp; + if ((stbsp__int32)n > pr) + n = pr; + i = n; + while (i) { + if ((((stbsp__uintptr)s) & 3) == 0) + break; + *s++ = '0'; + --i; + } + while (i >= 4) { + *(stbsp__uint32 *)s = 0x30303030; + s += 4; + i -= 4; + } + while (i) { + *s++ = '0'; + --i; + } + if ((stbsp__int32)(l + n) > pr) + l = pr - n; + i = l; + while (i) { + *s++ = *sn++; + --i; + } + tz = pr - (n + l); + cs = 1 + (3 << 24); // how many tens did we write (for commas below) + } else { + cs = (fl & STBSP__TRIPLET_COMMA) ? ((600 - (stbsp__uint32)dp) % 3) : 0; + if ((stbsp__uint32)dp >= l) { + // handle xxxx000*000.0 + n = 0; + for (;;) { + if ((fl & STBSP__TRIPLET_COMMA) && (++cs == 4)) { + cs = 0; + *s++ = stbsp__comma; + } else { + *s++ = sn[n]; + ++n; + if (n >= l) + break; + } + } + if (n < (stbsp__uint32)dp) { + n = dp - n; + if ((fl & STBSP__TRIPLET_COMMA) == 0) { + while (n) { + if ((((stbsp__uintptr)s) & 3) == 0) + break; + *s++ = '0'; + --n; + } + while (n >= 4) { + *(stbsp__uint32 *)s = 0x30303030; + s += 4; + n -= 4; + } + } + while (n) { + if ((fl & STBSP__TRIPLET_COMMA) && (++cs == 4)) { + cs = 0; + *s++ = stbsp__comma; + } else { + *s++ = '0'; + --n; + } + } + } + cs = (int)(s - (num + 64)) + (3 << 24); // cs is how many tens + if (pr) { + *s++ = stbsp__period; + tz = pr; + } + } else { + // handle xxxxx.xxxx000*000 + n = 0; + for (;;) { + if ((fl & STBSP__TRIPLET_COMMA) && (++cs == 4)) { + cs = 0; + *s++ = stbsp__comma; + } else { + *s++ = sn[n]; + ++n; + if (n >= (stbsp__uint32)dp) + break; + } + } + cs = (int)(s - (num + 64)) + (3 << 24); // cs is how many tens + if (pr) + *s++ = stbsp__period; + if ((l - dp) > (stbsp__uint32)pr) + l = pr + dp; + while (n < l) { + *s++ = sn[n]; + ++n; + } + tz = pr - (l - dp); + } + } + pr = 0; + + // handle k,m,g,t + if (fl & STBSP__METRIC_SUFFIX) { + char idx; + idx = 1; + if (fl & STBSP__METRIC_NOSPACE) + idx = 0; + tail[0] = idx; + tail[1] = ' '; + { + if (fl >> 24) { // SI kilo is 'k', JEDEC and SI kibits are 'K'. + if (fl & STBSP__METRIC_1024) + tail[idx + 1] = "_KMGT"[fl >> 24]; + else + tail[idx + 1] = "_kMGT"[fl >> 24]; + idx++; + // If printing kibits and not in jedec, add the 'i'. + if (fl & STBSP__METRIC_1024 && !(fl & STBSP__METRIC_JEDEC)) { + tail[idx + 1] = 'i'; + idx++; + } + tail[0] = idx; + } + } + }; + + flt_lead: + // get the length that we copied + l = (stbsp__uint32)(s - (num + 64)); + s = num + 64; + goto scopy; +#endif + + case 'B': // upper binary + case 'b': // lower binary + h = (f[0] == 'B') ? hexu : hex; + lead[0] = 0; + if (fl & STBSP__LEADING_0X) { + lead[0] = 2; + lead[1] = '0'; + lead[2] = h[0xb]; + } + l = (8 << 4) | (1 << 8); + goto radixnum; + + case 'o': // octal + h = hexu; + lead[0] = 0; + if (fl & STBSP__LEADING_0X) { + lead[0] = 1; + lead[1] = '0'; + } + l = (3 << 4) | (3 << 8); + goto radixnum; + + case 'p': // pointer + fl |= (sizeof(void *) == 8) ? STBSP__INTMAX : 0; + pr = sizeof(void *) * 2; + fl &= ~STBSP__LEADINGZERO; // 'p' only prints the pointer with zeros + // fall through - to X + + case 'X': // upper hex + case 'x': // lower hex + h = (f[0] == 'X') ? hexu : hex; + l = (4 << 4) | (4 << 8); + lead[0] = 0; + if (fl & STBSP__LEADING_0X) { + lead[0] = 2; + lead[1] = '0'; + lead[2] = h[16]; + } + radixnum: + // get the number + if (fl & STBSP__INTMAX) + n64 = va_arg(va, stbsp__uint64); + else + n64 = va_arg(va, stbsp__uint32); + + s = num + STBSP__NUMSZ; + dp = 0; + // clear tail, and clear leading if value is zero + tail[0] = 0; + if (n64 == 0) { + lead[0] = 0; + if (pr == 0) { + l = 0; + cs = 0; + goto scopy; + } + } + // convert to string + for (;;) { + *--s = h[n64 & ((1 << (l >> 8)) - 1)]; + n64 >>= (l >> 8); + if (!((n64) || ((stbsp__int32)((num + STBSP__NUMSZ) - s) < pr))) + break; + if (fl & STBSP__TRIPLET_COMMA) { + ++l; + if ((l & 15) == ((l >> 4) & 15)) { + l &= ~15; + *--s = stbsp__comma; + } + } + }; + // get the tens and the comma pos + cs = (stbsp__uint32)((num + STBSP__NUMSZ) - s) + ((((l >> 4) & 15)) << 24); + // get the length that we copied + l = (stbsp__uint32)((num + STBSP__NUMSZ) - s); + // copy it + goto scopy; + + case 'u': // unsigned + case 'i': + case 'd': // integer + // get the integer and abs it + if (fl & STBSP__INTMAX) { + stbsp__int64 i64 = va_arg(va, stbsp__int64); + n64 = (stbsp__uint64)i64; + if ((f[0] != 'u') && (i64 < 0)) { + n64 = (stbsp__uint64)-i64; + fl |= STBSP__NEGATIVE; + } + } else { + stbsp__int32 i = va_arg(va, stbsp__int32); + n64 = (stbsp__uint32)i; + if ((f[0] != 'u') && (i < 0)) { + n64 = (stbsp__uint32)-i; + fl |= STBSP__NEGATIVE; + } + } + +#ifndef STB_SPRINTF_NOFLOAT + if (fl & STBSP__METRIC_SUFFIX) { + if (n64 < 1024) + pr = 0; + else if (pr == -1) + pr = 1; + fv = (double)(stbsp__int64)n64; + goto doafloat; + } +#endif + + // convert to string + s = num + STBSP__NUMSZ; + l = 0; + + for (;;) { + // do in 32-bit chunks (avoid lots of 64-bit divides even with constant denominators) + char *o = s - 8; + if (n64 >= 100000000) { + n = (stbsp__uint32)(n64 % 100000000); + n64 /= 100000000; + } else { + n = (stbsp__uint32)n64; + n64 = 0; + } + if ((fl & STBSP__TRIPLET_COMMA) == 0) { + do { + s -= 2; + *(stbsp__uint16 *)s = *(stbsp__uint16 *)&stbsp__digitpair.pair[(n % 100) * 2]; + n /= 100; + } while (n); + } + while (n) { + if ((fl & STBSP__TRIPLET_COMMA) && (l++ == 3)) { + l = 0; + *--s = stbsp__comma; + --o; + } else { + *--s = (char)(n % 10) + '0'; + n /= 10; + } + } + if (n64 == 0) { + if ((s[0] == '0') && (s != (num + STBSP__NUMSZ))) + ++s; + break; + } + while (s != o) + if ((fl & STBSP__TRIPLET_COMMA) && (l++ == 3)) { + l = 0; + *--s = stbsp__comma; + --o; + } else { + *--s = '0'; + } + } + + tail[0] = 0; + stbsp__lead_sign(fl, lead); + + // get the length that we copied + l = (stbsp__uint32)((num + STBSP__NUMSZ) - s); + if (l == 0) { + *--s = '0'; + l = 1; + } + cs = l + (3 << 24); + if (pr < 0) + pr = 0; + + scopy: + // get fw=leading/trailing space, pr=leading zeros + if (pr < (stbsp__int32)l) + pr = l; + n = pr + lead[0] + tail[0] + tz; + if (fw < (stbsp__int32)n) + fw = n; + fw -= n; + pr -= l; + + // handle right justify and leading zeros + if ((fl & STBSP__LEFTJUST) == 0) { + if (fl & STBSP__LEADINGZERO) // if leading zeros, everything is in pr + { + pr = (fw > pr) ? fw : pr; + fw = 0; + } else { + fl &= ~STBSP__TRIPLET_COMMA; // if no leading zeros, then no commas + } + } + + // copy the spaces and/or zeros + if (fw + pr) { + stbsp__int32 i; + stbsp__uint32 c; + + // copy leading spaces (or when doing %8.4d stuff) + if ((fl & STBSP__LEFTJUST) == 0) + while (fw > 0) { + stbsp__cb_buf_clamp(i, fw); + fw -= i; + while (i) { + if ((((stbsp__uintptr)bf) & 3) == 0) + break; + *bf++ = ' '; + --i; + } + while (i >= 4) { + *(stbsp__uint32 *)bf = 0x20202020; + bf += 4; + i -= 4; + } + while (i) { + *bf++ = ' '; + --i; + } + stbsp__chk_cb_buf(1); + } + + // copy leader + sn = lead + 1; + while (lead[0]) { + stbsp__cb_buf_clamp(i, lead[0]); + lead[0] -= (char)i; + while (i) { + *bf++ = *sn++; + --i; + } + stbsp__chk_cb_buf(1); + } + + // copy leading zeros + c = cs >> 24; + cs &= 0xffffff; + cs = (fl & STBSP__TRIPLET_COMMA) ? ((stbsp__uint32)(c - ((pr + cs) % (c + 1)))) : 0; + while (pr > 0) { + stbsp__cb_buf_clamp(i, pr); + pr -= i; + if ((fl & STBSP__TRIPLET_COMMA) == 0) { + while (i) { + if ((((stbsp__uintptr)bf) & 3) == 0) + break; + *bf++ = '0'; + --i; + } + while (i >= 4) { + *(stbsp__uint32 *)bf = 0x30303030; + bf += 4; + i -= 4; + } + } + while (i) { + if ((fl & STBSP__TRIPLET_COMMA) && (cs++ == c)) { + cs = 0; + *bf++ = stbsp__comma; + } else + *bf++ = '0'; + --i; + } + stbsp__chk_cb_buf(1); + } + } + + // copy leader if there is still one + sn = lead + 1; + while (lead[0]) { + stbsp__int32 i; + stbsp__cb_buf_clamp(i, lead[0]); + lead[0] -= (char)i; + while (i) { + *bf++ = *sn++; + --i; + } + stbsp__chk_cb_buf(1); + } + + // copy the string + n = l; + while (n) { + stbsp__int32 i; + stbsp__cb_buf_clamp(i, n); + n -= i; + STBSP__UNALIGNED(while (i >= 4) { + *(stbsp__uint32 volatile *)bf = *(stbsp__uint32 volatile *)s; + bf += 4; + s += 4; + i -= 4; + }) + while (i) { + *bf++ = *s++; + --i; + } + stbsp__chk_cb_buf(1); + } + + // copy trailing zeros + while (tz) { + stbsp__int32 i; + stbsp__cb_buf_clamp(i, tz); + tz -= i; + while (i) { + if ((((stbsp__uintptr)bf) & 3) == 0) + break; + *bf++ = '0'; + --i; + } + while (i >= 4) { + *(stbsp__uint32 *)bf = 0x30303030; + bf += 4; + i -= 4; + } + while (i) { + *bf++ = '0'; + --i; + } + stbsp__chk_cb_buf(1); + } + + // copy tail if there is one + sn = tail + 1; + while (tail[0]) { + stbsp__int32 i; + stbsp__cb_buf_clamp(i, tail[0]); + tail[0] -= (char)i; + while (i) { + *bf++ = *sn++; + --i; + } + stbsp__chk_cb_buf(1); + } + + // handle the left justify + if (fl & STBSP__LEFTJUST) + if (fw > 0) { + while (fw) { + stbsp__int32 i; + stbsp__cb_buf_clamp(i, fw); + fw -= i; + while (i) { + if ((((stbsp__uintptr)bf) & 3) == 0) + break; + *bf++ = ' '; + --i; + } + while (i >= 4) { + *(stbsp__uint32 *)bf = 0x20202020; + bf += 4; + i -= 4; + } + while (i--) + *bf++ = ' '; + stbsp__chk_cb_buf(1); + } + } + break; + + default: // unknown, just copy code + s = num + STBSP__NUMSZ - 1; + *s = f[0]; + l = 1; + fw = fl = 0; + lead[0] = 0; + tail[0] = 0; + pr = 0; + dp = 0; + cs = 0; + goto scopy; + } + ++f; + } +endfmt: + + if (!callback) + *bf = 0; + else + stbsp__flush_cb(); + +done: + return tlen + (int)(bf - buf); +} + +// cleanup +#undef STBSP__LEFTJUST +#undef STBSP__LEADINGPLUS +#undef STBSP__LEADINGSPACE +#undef STBSP__LEADING_0X +#undef STBSP__LEADINGZERO +#undef STBSP__INTMAX +#undef STBSP__TRIPLET_COMMA +#undef STBSP__NEGATIVE +#undef STBSP__METRIC_SUFFIX +#undef STBSP__NUMSZ +#undef stbsp__chk_cb_bufL +#undef stbsp__chk_cb_buf +#undef stbsp__flush_cb +#undef stbsp__cb_buf_clamp + +// ============================================================================ +// wrapper functions + +STBSP__PUBLICDEF int STB_SPRINTF_DECORATE(sprintf)(char *buf, char const *fmt, ...) +{ + int result; + va_list va; + va_start(va, fmt); + result = STB_SPRINTF_DECORATE(vsprintfcb)(0, 0, buf, fmt, va); + va_end(va); + return result; +} + +typedef struct stbsp__context { + char *buf; + int count; + int length; + char tmp[STB_SPRINTF_MIN]; +} stbsp__context; + +static char *stbsp__clamp_callback(const char *buf, void *user, int len) +{ + stbsp__context *c = (stbsp__context *)user; + c->length += len; + + if (len > c->count) + len = c->count; + + if (len) { + if (buf != c->buf) { + const char *s, *se; + char *d; + d = c->buf; + s = buf; + se = buf + len; + do { + *d++ = *s++; + } while (s < se); + } + c->buf += len; + c->count -= len; + } + + if (c->count <= 0) + return c->tmp; + return (c->count >= STB_SPRINTF_MIN) ? c->buf : c->tmp; // go direct into buffer if you can +} + +static char * stbsp__count_clamp_callback( const char * buf, void * user, int len ) +{ + stbsp__context * c = (stbsp__context*)user; + (void) sizeof(buf); + + c->length += len; + return c->tmp; // go direct into buffer if you can +} + +STBSP__PUBLICDEF int STB_SPRINTF_DECORATE( vsnprintf )( char * buf, int count, char const * fmt, va_list va ) +{ + stbsp__context c; + + if ( (count == 0) && !buf ) + { + c.length = 0; + + STB_SPRINTF_DECORATE( vsprintfcb )( stbsp__count_clamp_callback, &c, c.tmp, fmt, va ); + } + else + { + int l; + + c.buf = buf; + c.count = count; + c.length = 0; + + STB_SPRINTF_DECORATE( vsprintfcb )( stbsp__clamp_callback, &c, stbsp__clamp_callback(0,&c,0), fmt, va ); + + // zero-terminate + l = (int)( c.buf - buf ); + if ( l >= count ) // should never be greater, only equal (or less) than count + l = count - 1; + buf[l] = 0; + } + + return c.length; +} + +STBSP__PUBLICDEF int STB_SPRINTF_DECORATE(snprintf)(char *buf, int count, char const *fmt, ...) +{ + int result; + va_list va; + va_start(va, fmt); + + result = STB_SPRINTF_DECORATE(vsnprintf)(buf, count, fmt, va); + va_end(va); + + return result; +} + +STBSP__PUBLICDEF int STB_SPRINTF_DECORATE(vsprintf)(char *buf, char const *fmt, va_list va) +{ + return STB_SPRINTF_DECORATE(vsprintfcb)(0, 0, buf, fmt, va); +} + +// ======================================================================= +// low level float utility functions + +#ifndef STB_SPRINTF_NOFLOAT + +// copies d to bits w/ strict aliasing (this compiles to nothing on /Ox) +#define STBSP__COPYFP(dest, src) \ + { \ + int cn; \ + for (cn = 0; cn < 8; cn++) \ + ((char *)&dest)[cn] = ((char *)&src)[cn]; \ + } + +// get float info +static stbsp__int32 stbsp__real_to_parts(stbsp__int64 *bits, stbsp__int32 *expo, double value) +{ + double d; + stbsp__int64 b = 0; + + // load value and round at the frac_digits + d = value; + + STBSP__COPYFP(b, d); + + *bits = b & ((((stbsp__uint64)1) << 52) - 1); + *expo = (stbsp__int32)(((b >> 52) & 2047) - 1023); + + return (stbsp__int32)((stbsp__uint64) b >> 63); +} + +static double const stbsp__bot[23] = { + 1e+000, 1e+001, 1e+002, 1e+003, 1e+004, 1e+005, 1e+006, 1e+007, 1e+008, 1e+009, 1e+010, 1e+011, + 1e+012, 1e+013, 1e+014, 1e+015, 1e+016, 1e+017, 1e+018, 1e+019, 1e+020, 1e+021, 1e+022 +}; +static double const stbsp__negbot[22] = { + 1e-001, 1e-002, 1e-003, 1e-004, 1e-005, 1e-006, 1e-007, 1e-008, 1e-009, 1e-010, 1e-011, + 1e-012, 1e-013, 1e-014, 1e-015, 1e-016, 1e-017, 1e-018, 1e-019, 1e-020, 1e-021, 1e-022 +}; +static double const stbsp__negboterr[22] = { + -5.551115123125783e-018, -2.0816681711721684e-019, -2.0816681711721686e-020, -4.7921736023859299e-021, -8.1803053914031305e-022, 4.5251888174113741e-023, + 4.5251888174113739e-024, -2.0922560830128471e-025, -6.2281591457779853e-026, -3.6432197315497743e-027, 6.0503030718060191e-028, 2.0113352370744385e-029, + -3.0373745563400371e-030, 1.1806906454401013e-032, -7.7705399876661076e-032, 2.0902213275965398e-033, -7.1542424054621921e-034, -7.1542424054621926e-035, + 2.4754073164739869e-036, 5.4846728545790429e-037, 9.2462547772103625e-038, -4.8596774326570872e-039 +}; +static double const stbsp__top[13] = { + 1e+023, 1e+046, 1e+069, 1e+092, 1e+115, 1e+138, 1e+161, 1e+184, 1e+207, 1e+230, 1e+253, 1e+276, 1e+299 +}; +static double const stbsp__negtop[13] = { + 1e-023, 1e-046, 1e-069, 1e-092, 1e-115, 1e-138, 1e-161, 1e-184, 1e-207, 1e-230, 1e-253, 1e-276, 1e-299 +}; +static double const stbsp__toperr[13] = { + 8388608, + 6.8601809640529717e+028, + -7.253143638152921e+052, + -4.3377296974619174e+075, + -1.5559416129466825e+098, + -3.2841562489204913e+121, + -3.7745893248228135e+144, + -1.7356668416969134e+167, + -3.8893577551088374e+190, + -9.9566444326005119e+213, + 6.3641293062232429e+236, + -5.2069140800249813e+259, + -5.2504760255204387e+282 +}; +static double const stbsp__negtoperr[13] = { + 3.9565301985100693e-040, -2.299904345391321e-063, 3.6506201437945798e-086, 1.1875228833981544e-109, + -5.0644902316928607e-132, -6.7156837247865426e-155, -2.812077463003139e-178, -5.7778912386589953e-201, + 7.4997100559334532e-224, -4.6439668915134491e-247, -6.3691100762962136e-270, -9.436808465446358e-293, + 8.0970921678014997e-317 +}; + +#if defined(_MSC_VER) && (_MSC_VER <= 1200) +static stbsp__uint64 const stbsp__powten[20] = { + 1, + 10, + 100, + 1000, + 10000, + 100000, + 1000000, + 10000000, + 100000000, + 1000000000, + 10000000000, + 100000000000, + 1000000000000, + 10000000000000, + 100000000000000, + 1000000000000000, + 10000000000000000, + 100000000000000000, + 1000000000000000000, + 10000000000000000000U +}; +#define stbsp__tento19th ((stbsp__uint64)1000000000000000000) +#else +static stbsp__uint64 const stbsp__powten[20] = { + 1, + 10, + 100, + 1000, + 10000, + 100000, + 1000000, + 10000000, + 100000000, + 1000000000, + 10000000000ULL, + 100000000000ULL, + 1000000000000ULL, + 10000000000000ULL, + 100000000000000ULL, + 1000000000000000ULL, + 10000000000000000ULL, + 100000000000000000ULL, + 1000000000000000000ULL, + 10000000000000000000ULL +}; +#define stbsp__tento19th (1000000000000000000ULL) +#endif + +#define stbsp__ddmulthi(oh, ol, xh, yh) \ + { \ + double ahi = 0, alo, bhi = 0, blo; \ + stbsp__int64 bt; \ + oh = xh * yh; \ + STBSP__COPYFP(bt, xh); \ + bt &= ((~(stbsp__uint64)0) << 27); \ + STBSP__COPYFP(ahi, bt); \ + alo = xh - ahi; \ + STBSP__COPYFP(bt, yh); \ + bt &= ((~(stbsp__uint64)0) << 27); \ + STBSP__COPYFP(bhi, bt); \ + blo = yh - bhi; \ + ol = ((ahi * bhi - oh) + ahi * blo + alo * bhi) + alo * blo; \ + } + +#define stbsp__ddtoS64(ob, xh, xl) \ + { \ + double ahi = 0, alo, vh, t; \ + ob = (stbsp__int64)xh; \ + vh = (double)ob; \ + ahi = (xh - vh); \ + t = (ahi - xh); \ + alo = (xh - (ahi - t)) - (vh + t); \ + ob += (stbsp__int64)(ahi + alo + xl); \ + } + +#define stbsp__ddrenorm(oh, ol) \ + { \ + double s; \ + s = oh + ol; \ + ol = ol - (s - oh); \ + oh = s; \ + } + +#define stbsp__ddmultlo(oh, ol, xh, xl, yh, yl) ol = ol + (xh * yl + xl * yh); + +#define stbsp__ddmultlos(oh, ol, xh, yl) ol = ol + (xh * yl); + +static void stbsp__raise_to_power10(double *ohi, double *olo, double d, stbsp__int32 power) // power can be -323 to +350 +{ + double ph, pl; + if ((power >= 0) && (power <= 22)) { + stbsp__ddmulthi(ph, pl, d, stbsp__bot[power]); + } else { + stbsp__int32 e, et, eb; + double p2h, p2l; + + e = power; + if (power < 0) + e = -e; + et = (e * 0x2c9) >> 14; /* %23 */ + if (et > 13) + et = 13; + eb = e - (et * 23); + + ph = d; + pl = 0.0; + if (power < 0) { + if (eb) { + --eb; + stbsp__ddmulthi(ph, pl, d, stbsp__negbot[eb]); + stbsp__ddmultlos(ph, pl, d, stbsp__negboterr[eb]); + } + if (et) { + stbsp__ddrenorm(ph, pl); + --et; + stbsp__ddmulthi(p2h, p2l, ph, stbsp__negtop[et]); + stbsp__ddmultlo(p2h, p2l, ph, pl, stbsp__negtop[et], stbsp__negtoperr[et]); + ph = p2h; + pl = p2l; + } + } else { + if (eb) { + e = eb; + if (eb > 22) + eb = 22; + e -= eb; + stbsp__ddmulthi(ph, pl, d, stbsp__bot[eb]); + if (e) { + stbsp__ddrenorm(ph, pl); + stbsp__ddmulthi(p2h, p2l, ph, stbsp__bot[e]); + stbsp__ddmultlos(p2h, p2l, stbsp__bot[e], pl); + ph = p2h; + pl = p2l; + } + } + if (et) { + stbsp__ddrenorm(ph, pl); + --et; + stbsp__ddmulthi(p2h, p2l, ph, stbsp__top[et]); + stbsp__ddmultlo(p2h, p2l, ph, pl, stbsp__top[et], stbsp__toperr[et]); + ph = p2h; + pl = p2l; + } + } + } + stbsp__ddrenorm(ph, pl); + *ohi = ph; + *olo = pl; +} + +// given a float value, returns the significant bits in bits, and the position of the +// decimal point in decimal_pos. +/-INF and NAN are specified by special values +// returned in the decimal_pos parameter. +// frac_digits is absolute normally, but if you want from first significant digits (got %g and %e), or in 0x80000000 +static stbsp__int32 stbsp__real_to_str(char const **start, stbsp__uint32 *len, char *out, stbsp__int32 *decimal_pos, double value, stbsp__uint32 frac_digits) +{ + double d; + stbsp__int64 bits = 0; + stbsp__int32 expo, e, ng, tens; + + d = value; + STBSP__COPYFP(bits, d); + expo = (stbsp__int32)((bits >> 52) & 2047); + ng = (stbsp__int32)((stbsp__uint64) bits >> 63); + if (ng) + d = -d; + + if (expo == 2047) // is nan or inf? + { + *start = (bits & ((((stbsp__uint64)1) << 52) - 1)) ? "NaN" : "Inf"; + *decimal_pos = STBSP__SPECIAL; + *len = 3; + return ng; + } + + if (expo == 0) // is zero or denormal + { + if (((stbsp__uint64) bits << 1) == 0) // do zero + { + *decimal_pos = 1; + *start = out; + out[0] = '0'; + *len = 1; + return ng; + } + // find the right expo for denormals + { + stbsp__int64 v = ((stbsp__uint64)1) << 51; + while ((bits & v) == 0) { + --expo; + v >>= 1; + } + } + } + + // find the decimal exponent as well as the decimal bits of the value + { + double ph, pl; + + // log10 estimate - very specifically tweaked to hit or undershoot by no more than 1 of log10 of all expos 1..2046 + tens = expo - 1023; + tens = (tens < 0) ? ((tens * 617) / 2048) : (((tens * 1233) / 4096) + 1); + + // move the significant bits into position and stick them into an int + stbsp__raise_to_power10(&ph, &pl, d, 18 - tens); + + // get full as much precision from double-double as possible + stbsp__ddtoS64(bits, ph, pl); + + // check if we undershot + if (((stbsp__uint64)bits) >= stbsp__tento19th) + ++tens; + } + + // now do the rounding in integer land + frac_digits = (frac_digits & 0x80000000) ? ((frac_digits & 0x7ffffff) + 1) : (tens + frac_digits); + if ((frac_digits < 24)) { + stbsp__uint32 dg = 1; + if ((stbsp__uint64)bits >= stbsp__powten[9]) + dg = 10; + while ((stbsp__uint64)bits >= stbsp__powten[dg]) { + ++dg; + if (dg == 20) + goto noround; + } + if (frac_digits < dg) { + stbsp__uint64 r; + // add 0.5 at the right position and round + e = dg - frac_digits; + if ((stbsp__uint32)e >= 24) + goto noround; + r = stbsp__powten[e]; + bits = bits + (r / 2); + if ((stbsp__uint64)bits >= stbsp__powten[dg]) + ++tens; + bits /= r; + } + noround:; + } + + // kill long trailing runs of zeros + if (bits) { + stbsp__uint32 n; + for (;;) { + if (bits <= 0xffffffff) + break; + if (bits % 1000) + goto donez; + bits /= 1000; + } + n = (stbsp__uint32)bits; + while ((n % 1000) == 0) + n /= 1000; + bits = n; + donez:; + } + + // convert to string + out += 64; + e = 0; + for (;;) { + stbsp__uint32 n; + char *o = out - 8; + // do the conversion in chunks of U32s (avoid most 64-bit divides, worth it, constant denomiators be damned) + if (bits >= 100000000) { + n = (stbsp__uint32)(bits % 100000000); + bits /= 100000000; + } else { + n = (stbsp__uint32)bits; + bits = 0; + } + while (n) { + out -= 2; + *(stbsp__uint16 *)out = *(stbsp__uint16 *)&stbsp__digitpair.pair[(n % 100) * 2]; + n /= 100; + e += 2; + } + if (bits == 0) { + if ((e) && (out[0] == '0')) { + ++out; + --e; + } + break; + } + while (out != o) { + *--out = '0'; + ++e; + } + } + + *decimal_pos = tens; + *start = out; + *len = e; + return ng; +} + +#undef stbsp__ddmulthi +#undef stbsp__ddrenorm +#undef stbsp__ddmultlo +#undef stbsp__ddmultlos +#undef STBSP__SPECIAL +#undef STBSP__COPYFP + +#endif // STB_SPRINTF_NOFLOAT + +// clean up +#undef stbsp__uint16 +#undef stbsp__uint32 +#undef stbsp__int32 +#undef stbsp__uint64 +#undef stbsp__int64 +#undef STBSP__UNALIGNED + +#endif // STB_SPRINTF_IMPLEMENTATION + +/* +------------------------------------------------------------------------------ +This software is available under 2 licenses -- choose whichever you prefer. +------------------------------------------------------------------------------ +ALTERNATIVE A - MIT License +Copyright (c) 2017 Sean Barrett +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +------------------------------------------------------------------------------ +ALTERNATIVE B - Public Domain (www.unlicense.org) +This is free and unencumbered software released into the public domain. +Anyone is free to copy, modify, publish, use, compile, sell, or distribute this +software, either in source code form or as a compiled binary, for any purpose, +commercial or non-commercial, and by any means. +In jurisdictions that recognize copyright laws, the author or authors of this +software dedicate any and all copyright interest in the software to the public +domain. We make this dedication for the benefit of the public at large and to +the detriment of our heirs and successors. We intend this dedication to be an +overt act of relinquishment in perpetuity of all present and future rights to +this software under copyright law. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +------------------------------------------------------------------------------ +*/ \ No newline at end of file diff --git a/Minecraft.Client/Windows64/4JLibs/inc/Render/shaders/build.bat b/Minecraft.Client/Windows64/4JLibs/inc/Render/shaders/build.bat new file mode 100644 index 000000000..678a86850 --- /dev/null +++ b/Minecraft.Client/Windows64/4JLibs/inc/Render/shaders/build.bat @@ -0,0 +1,21 @@ +@echo off + +set OUT=../ +set OPT=/O3 /nologo + +for /d %%i in ("C:\Program Files (x86)\Windows Kits\10\bin\10.*") do set FXC="%%i\x64\fxc.exe" + +if not defined FXC echo FXC not found && exit /b 1 +if not exist %OUT% mkdir %OUT% + +%FXC% %OPT% /T vs_4_0 /E main /D COMPRESSED /Fh %OUT%/VS_Compressed.h /Vn g_main_VS_Compressed main_VS.hlsl +%FXC% %OPT% /T vs_4_0 /E main /Fh %OUT%/VS_PF3_TF2_CB4_NB4_XW1.h /Vn g_main_VS_PF3_TF2_CB4_NB4_XW1 main_VS.hlsl +%FXC% %OPT% /T vs_4_0 /E main /D LIGHTING /Fh %OUT%/VS_PF3_TF2_CB4_NB4_XW1_LIGHTING.h /Vn g_main_VS_PF3_TF2_CB4_NB4_XW1_LIGHTING main_VS.hlsl +%FXC% %OPT% /T vs_4_0 /E main /D TEXGEN /Fh %OUT%/VS_PF3_TF2_CB4_NB4_XW1_TEXGEN.h /Vn g_main_VS_PF3_TF2_CB4_NB4_XW1_TEXGEN main_VS.hlsl +%FXC% %OPT% /T vs_4_0 /E VS_ScreenSpace /Fh %OUT%/VS_ScreenSpace.h /Vn g_main_VS_ScreenSpace screen_VS.hlsl +%FXC% %OPT% /T vs_4_0 /E VS_ScreenClear /Fh %OUT%/VS_ScreenClear.h /Vn g_main_VS_ScreenClear screen_VS.hlsl +%FXC% %OPT% /T ps_4_0 /E main /Fh %OUT%/PS_Standard.h /Vn g_main_PS_Standard main_PS.hlsl +%FXC% %OPT% /T ps_4_0 /E main /D TEXTURE_PROJECTION /Fh %OUT%/PS_TextureProjection.h /Vn g_main_PS_TextureProjection main_PS.hlsl +%FXC% %OPT% /T ps_4_0 /E main /D FORCE_LOD /Fh %OUT%/PS_ForceLOD.h /Vn g_main_PS_ForceLOD main_PS.hlsl +%FXC% %OPT% /T ps_4_0 /E PS_ScreenSpace /Fh %OUT%/PS_ScreenSpace.h /Vn g_main_PS_ScreenSpace screen_PS.hlsl +%FXC% %OPT% /T ps_4_0 /E PS_ScreenClear /Fh %OUT%/PS_ScreenClear.h /Vn g_main_PS_ScreenClear screen_PS.hlsl \ No newline at end of file diff --git a/Minecraft.Client/Windows64/4JLibs/inc/Render/shaders/main_PS.hlsl b/Minecraft.Client/Windows64/4JLibs/inc/Render/shaders/main_PS.hlsl new file mode 100644 index 000000000..91546a511 --- /dev/null +++ b/Minecraft.Client/Windows64/4JLibs/inc/Render/shaders/main_PS.hlsl @@ -0,0 +1,84 @@ +/* +MIT License + +Copyright (c) 2026 Patoke + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ +cbuffer diffuse : register(b0) +{ + float4 diffuse_colour; +}; +cbuffer fog : register(b1) +{ + float4 fog_colour; +}; +cbuffer alphaTest : register(b3) +{ + float4 alphaTestRef; +}; + +#ifdef FORCE_LOD +cbuffer forcedLOD : register(b5) +{ + float4 forcedLod; +}; +#endif + +SamplerState diffuse_sampler_s : register(s0); +Texture2D diffuse_texture : register(t0); + +struct PS_INPUT +{ + float4 position : SV_POSITION; + float4 colour : COLOR0; + linear centroid float4 texcoord : TEXCOORD0; +}; + +float4 main(PS_INPUT input) : SV_TARGET +{ + float2 uv = input.texcoord.xy; + + #ifdef TEXTURE_PROJECTION + uv /= input.texcoord.w; + #endif + + #ifdef FORCE_LOD + float4 texel = diffuse_texture.SampleLevel( + diffuse_sampler_s, + uv, + forcedLod.x); + #else + float4 texel = + (uv.x > 1.0f) + ? diffuse_texture.SampleLevel(diffuse_sampler_s, uv, 0) + : diffuse_texture.Sample(diffuse_sampler_s, uv); + #endif + + float4 colour = texel * diffuse_colour; + colour.a *= input.colour.a; + + if (colour.a < alphaTestRef.w) + discard; + + float3 shaded = colour.rgb * input.colour.rgb; + float3 fogged = lerp(fog_colour.rgb, shaded, input.texcoord.z); + + return float4(fogged, colour.a); +} \ No newline at end of file diff --git a/Minecraft.Client/Windows64/4JLibs/inc/Render/shaders/main_VS.hlsl b/Minecraft.Client/Windows64/4JLibs/inc/Render/shaders/main_VS.hlsl new file mode 100644 index 000000000..3cc37e298 --- /dev/null +++ b/Minecraft.Client/Windows64/4JLibs/inc/Render/shaders/main_VS.hlsl @@ -0,0 +1,196 @@ + +/* +MIT License + +Copyright (c) 2026 Patoke + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ +#ifndef COMPRESSED + +cbuffer positionTransformWV : register(b0) +{ + float4x4 matWorldView; +}; +cbuffer positionTransformWV2 : register(b1) +{ + float4x4 matWorldView2; +}; +cbuffer positionTransformProj : register(b2) +{ + float4x4 matProjection; +}; +cbuffer textureTransform : register(b3) +{ + float4x4 matUV; +}; +cbuffer textureUV2 : register(b4) +{ + float4 vecUVT2; +}; +cbuffer fog : register(b5) +{ + float4 vecFog; +}; + +#ifdef LIGHTING +cbuffer lighting : register(b6) +{ + float3 vecLight0; + float3 vecLight1; + float4 vecLight0Col; + float4 vecLight1Col; + float4 vecLightAmbientCol; +}; +#endif + +#ifdef TEXGEN +cbuffer texgen : register(b7) +{ + float4x4 matTexGenView; + float4x4 matTexGenObj; +}; +#endif + +#else + +cbuffer positionTransformWV : register(b0) +{ + float4x4 matWorldView; +}; +cbuffer positionTransformProj : register(b2) +{ + float4x4 matProjection; +}; +cbuffer textureUV2 : register(b4) +{ + float4 vecUVT2; +}; +cbuffer fog : register(b5) +{ + float4 vecFog; +}; +cbuffer positionTransform2 : register(b8) +{ + float4 vecWV2Trans; +}; + +#endif + +SamplerState light_sampler_s : register(s0); +Texture2D light_texture : register(t0); + +#ifndef COMPRESSED +struct VS_INPUT +{ + float4 position : POSITION0; + float4 colour : COLOR0; + float3 normal : NORMAL0; + float4 texCoord : TEXCOORD0; + int2 lightMapCoord : TEXCOORD1; +}; +#else +struct VS_INPUT +{ + int4 position : POSITION0; + int4 texCoord : TEXCOORD0; +}; +#endif + +struct VS_OUTPUT +{ + float4 position : SV_POSITION; + float4 colour : COLOR0; + float4 texCoord : TEXCOORD0; +}; + +float CalcFogFactor(float4 vecFog, float viewZ) +{ + float fogLinear = saturate(vecFog.y * (vecFog.x + viewZ)); + float fogExp = min(1.0f, exp2(1.44269502f * vecFog.x * viewZ)); + float f = (vecFog.z == 2) ? fogExp : 1.0f; + return (vecFog.z == 1) ? fogLinear : f; + +} + +VS_OUTPUT main(VS_INPUT input) +{ + VS_OUTPUT output; + + #ifndef COMPRESSED + + float4 skinnedPos = mul(matWorldView2, input.position); + float4 cameraPos = mul(matWorldView, skinnedPos); + output.position = mul(matProjection, cameraPos); + + float2 lightUV = frac(max(vecUVT2.xy, float2(input.lightMapCoord) * 0.00390625f)); + float4 lightSample = light_texture.SampleLevel(light_sampler_s, lightUV, 0); + lightSample.w = 1.0f; + + #ifdef LIGHTING + float3 skinNormal = mul((float3x3)matWorldView2, input.normal); + float3 viewNormal = normalize(mul((float3x3)matWorldView, skinNormal)); + + float diffuse0 = max(0.0f, dot(vecLight0, viewNormal)); + float diffuse1 = max(0.0f, dot(vecLight1, viewNormal)); + float4 litColour = saturate(vecLightAmbientCol + diffuse0 * vecLight0Col + diffuse1 * vecLight1Col); + + output.colour.xyz = litColour.xyz * (input.colour.wzy * lightSample.xyz); + output.colour.w = litColour.w; + #else + output.colour = input.colour.wzyx * lightSample; + #endif + + #ifdef TEXGEN + float4 texGenCoords = mul(matTexGenView, cameraPos) + mul(matTexGenObj, input.position); + output.texCoord.xy = float2(dot(matUV[0], texGenCoords), dot(matUV[1], texGenCoords)); + output.texCoord.w = dot(matUV[3], texGenCoords); + #else + output.texCoord.xy = float2(dot(matUV[0], input.texCoord), dot(matUV[1], input.texCoord)); + output.texCoord.w = 1.0f; + #endif + + output.texCoord.z = CalcFogFactor(vecFog, cameraPos.z); + + #else // COMPRESSED + + float4 pos; + pos.xyz = float3((int3)input.position.xyz) * 0.0009765625f + vecWV2Trans.xyz; + pos.w = 1.0f; + + int packedColor = (int)input.position.w + 32768; + float3 color = frac(packedColor * float3(1.52587891e-05f, 0.00048828125f, 0.03125f)); + + float4 cameraPos = mul(matWorldView, pos); + output.position = mul(matProjection, cameraPos); + + float4 uvs = float4((int4)input.texCoord.zwxy) * float4(0.00390625f, 0.00390625f, 0.000122070312f, 0.000122070312f); + float2 lightUV = frac(max(vecUVT2.xy, uvs.xy)); + float4 lightSample = light_texture.SampleLevel(light_sampler_s, lightUV, 0); + + output.colour.xyz = lightSample.xyz * color; + output.colour.w = 1.0f; + output.texCoord.xy = uvs.zw; + output.texCoord.z = CalcFogFactor(vecFog, cameraPos.z); + output.texCoord.w = 1.0f; + + #endif + + return output; +} diff --git a/Minecraft.Client/Windows64/4JLibs/inc/Render/shaders/screen_PS.hlsl b/Minecraft.Client/Windows64/4JLibs/inc/Render/shaders/screen_PS.hlsl new file mode 100644 index 000000000..376f487b5 --- /dev/null +++ b/Minecraft.Client/Windows64/4JLibs/inc/Render/shaders/screen_PS.hlsl @@ -0,0 +1,40 @@ +/* +MIT License + +Copyright (c) 2026 Patoke + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ +cbuffer cbuff : register(b4) +{ + float4 clearColour; +}; + +SamplerState screen_sampler_s : register(s0); +Texture2D screen_texture : register(t0); + +float4 PS_ScreenSpace(float4 position : SV_POSITION, float2 texcoord : TEXCOORD0) : SV_TARGET +{ + return screen_texture.Sample(screen_sampler_s, texcoord); +} + +float4 PS_ScreenClear(float4 position : SV_POSITION) : SV_TARGET +{ + return clearColour; +} \ No newline at end of file diff --git a/Minecraft.Client/Windows64/4JLibs/inc/Render/shaders/screen_VS.hlsl b/Minecraft.Client/Windows64/4JLibs/inc/Render/shaders/screen_VS.hlsl new file mode 100644 index 000000000..7eec30e25 --- /dev/null +++ b/Minecraft.Client/Windows64/4JLibs/inc/Render/shaders/screen_VS.hlsl @@ -0,0 +1,48 @@ +/* +MIT License + +Copyright (c) 2026 Patoke + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ +cbuffer screenspace_constants : register(b9) +{ + float4 v_scaleoffset; +}; + +void VS_ScreenSpace(uint vertex_id : SV_VertexID, out float4 position : SV_POSITION, out float2 texcoord : TEXCOORD0) +{ + float2 corner = float2( + (vertex_id << 1) & 2, + vertex_id & 2 + ); + + position = float4(corner * float2(2, -2) + float2(-1, 1), 1, 1); + texcoord = corner * 0.5 * v_scaleoffset.zw + v_scaleoffset.xy; +} + +void VS_ScreenClear(uint vertex_id : SV_VertexID, out float4 position : SV_POSITION) +{ + float2 corner = float2( + (vertex_id << 1) & 2, + vertex_id & 2 + ); + + position = float4(corner * float2(2, -2) + float2(-1, 1), 1, 1); +} \ No newline at end of file diff --git a/Minecraft.Client/Windows64/4JLibs/inc/Render/stdafx.cpp b/Minecraft.Client/Windows64/4JLibs/inc/Render/stdafx.cpp new file mode 100644 index 000000000..af112cce5 --- /dev/null +++ b/Minecraft.Client/Windows64/4JLibs/inc/Render/stdafx.cpp @@ -0,0 +1,25 @@ +/* +MIT License + +Copyright (c) 2026 Patoke + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +#include "stdafx.h" diff --git a/Minecraft.Client/Windows64/4JLibs/inc/Render/stdafx.h b/Minecraft.Client/Windows64/4JLibs/inc/Render/stdafx.h new file mode 100644 index 000000000..f59296ee0 --- /dev/null +++ b/Minecraft.Client/Windows64/4JLibs/inc/Render/stdafx.h @@ -0,0 +1,39 @@ +#ifndef PCH_H +#define PCH_H +/* +MIT License + +Copyright (c) 2026 Patoke + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +#define WIN32_LEAN_AND_MEAN +#include + +#include +#include +#include +#include + +#include +#include +#include + +#endif //PCH_H diff --git a/Minecraft.Client/Windows64/4JLibs/inc/Render/zlib/zconf.h b/Minecraft.Client/Windows64/4JLibs/inc/Render/zlib/zconf.h new file mode 100644 index 000000000..40c02e780 --- /dev/null +++ b/Minecraft.Client/Windows64/4JLibs/inc/Render/zlib/zconf.h @@ -0,0 +1,553 @@ +/* zconf.h -- configuration of the zlib compression library + * Copyright (C) 1995-2026 Jean-loup Gailly, Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* @(#) $Id$ */ + +#ifndef ZCONF_H +#define ZCONF_H + +#define NO_snprintf + +/* + * If you *really* need a unique prefix for all types and library functions, + * compile with -DZ_PREFIX. The "standard" zlib should be compiled without it. + * Even better than compiling with -DZ_PREFIX would be to use configure to set + * this permanently in zconf.h using "./configure --zprefix". + */ +#ifdef Z_PREFIX /* may be set to #if 1 by ./configure */ +# define Z_PREFIX_SET + +/* all linked symbols and init macros */ +# define _dist_code z__dist_code +# define _length_code z__length_code +# define _tr_align z__tr_align +# define _tr_flush_bits z__tr_flush_bits +# define _tr_flush_block z__tr_flush_block +# define _tr_init z__tr_init +# define _tr_stored_block z__tr_stored_block +# define _tr_tally z__tr_tally +# define adler32 z_adler32 +# define adler32_combine z_adler32_combine +# define adler32_combine64 z_adler32_combine64 +# define adler32_z z_adler32_z +# ifndef Z_SOLO +# define compress z_compress +# define compress2 z_compress2 +# define compress_z z_compress_z +# define compress2_z z_compress2_z +# define compressBound z_compressBound +# define compressBound_z z_compressBound_z +# endif +# define crc32 z_crc32 +# define crc32_combine z_crc32_combine +# define crc32_combine64 z_crc32_combine64 +# define crc32_combine_gen z_crc32_combine_gen +# define crc32_combine_gen64 z_crc32_combine_gen64 +# define crc32_combine_op z_crc32_combine_op +# define crc32_z z_crc32_z +# define deflate z_deflate +# define deflateBound z_deflateBound +# define deflateBound_z z_deflateBound_z +# define deflateCopy z_deflateCopy +# define deflateEnd z_deflateEnd +# define deflateGetDictionary z_deflateGetDictionary +# define deflateInit z_deflateInit +# define deflateInit2 z_deflateInit2 +# define deflateInit2_ z_deflateInit2_ +# define deflateInit_ z_deflateInit_ +# define deflateParams z_deflateParams +# define deflatePending z_deflatePending +# define deflatePrime z_deflatePrime +# define deflateReset z_deflateReset +# define deflateResetKeep z_deflateResetKeep +# define deflateSetDictionary z_deflateSetDictionary +# define deflateSetHeader z_deflateSetHeader +# define deflateTune z_deflateTune +# define deflateUsed z_deflateUsed +# define deflate_copyright z_deflate_copyright +# define get_crc_table z_get_crc_table +# ifndef Z_SOLO +# define gz_error z_gz_error +# define gz_intmax z_gz_intmax +# define gz_strwinerror z_gz_strwinerror +# define gzbuffer z_gzbuffer +# define gzclearerr z_gzclearerr +# define gzclose z_gzclose +# define gzclose_r z_gzclose_r +# define gzclose_w z_gzclose_w +# define gzdirect z_gzdirect +# define gzdopen z_gzdopen +# define gzeof z_gzeof +# define gzerror z_gzerror +# define gzflush z_gzflush +# define gzfread z_gzfread +# define gzfwrite z_gzfwrite +# define gzgetc z_gzgetc +# define gzgetc_ z_gzgetc_ +# define gzgets z_gzgets +# define gzoffset z_gzoffset +# define gzoffset64 z_gzoffset64 +# define gzopen z_gzopen +# define gzopen64 z_gzopen64 +# ifdef _WIN32 +# define gzopen_w z_gzopen_w +# endif +# define gzprintf z_gzprintf +# define gzputc z_gzputc +# define gzputs z_gzputs +# define gzread z_gzread +# define gzrewind z_gzrewind +# define gzseek z_gzseek +# define gzseek64 z_gzseek64 +# define gzsetparams z_gzsetparams +# define gztell z_gztell +# define gztell64 z_gztell64 +# define gzungetc z_gzungetc +# define gzvprintf z_gzvprintf +# define gzwrite z_gzwrite +# endif +# define inflate z_inflate +# define inflateBack z_inflateBack +# define inflateBackEnd z_inflateBackEnd +# define inflateBackInit z_inflateBackInit +# define inflateBackInit_ z_inflateBackInit_ +# define inflateCodesUsed z_inflateCodesUsed +# define inflateCopy z_inflateCopy +# define inflateEnd z_inflateEnd +# define inflateGetDictionary z_inflateGetDictionary +# define inflateGetHeader z_inflateGetHeader +# define inflateInit z_inflateInit +# define inflateInit2 z_inflateInit2 +# define inflateInit2_ z_inflateInit2_ +# define inflateInit_ z_inflateInit_ +# define inflateMark z_inflateMark +# define inflatePrime z_inflatePrime +# define inflateReset z_inflateReset +# define inflateReset2 z_inflateReset2 +# define inflateResetKeep z_inflateResetKeep +# define inflateSetDictionary z_inflateSetDictionary +# define inflateSync z_inflateSync +# define inflateSyncPoint z_inflateSyncPoint +# define inflateUndermine z_inflateUndermine +# define inflateValidate z_inflateValidate +# define inflate_copyright z_inflate_copyright +# define inflate_fast z_inflate_fast +# define inflate_table z_inflate_table +# define inflate_fixed z_inflate_fixed +# ifndef Z_SOLO +# define uncompress z_uncompress +# define uncompress2 z_uncompress2 +# define uncompress_z z_uncompress_z +# define uncompress2_z z_uncompress2_z +# endif +# define zError z_zError +# ifndef Z_SOLO +# define zcalloc z_zcalloc +# define zcfree z_zcfree +# endif +# define zlibCompileFlags z_zlibCompileFlags +# define zlibVersion z_zlibVersion + +/* all zlib typedefs in zlib.h and zconf.h */ +# define Byte z_Byte +# define Bytef z_Bytef +# define alloc_func z_alloc_func +# define charf z_charf +# define free_func z_free_func +# ifndef Z_SOLO +# define gzFile z_gzFile +# endif +# define gz_header z_gz_header +# define gz_headerp z_gz_headerp +# define in_func z_in_func +# define intf z_intf +# define out_func z_out_func +# define uInt z_uInt +# define uIntf z_uIntf +# define uLong z_uLong +# define uLongf z_uLongf +# define voidp z_voidp +# define voidpc z_voidpc +# define voidpf z_voidpf + +/* all zlib structs in zlib.h and zconf.h */ +# define gz_header_s z_gz_header_s +# define internal_state z_internal_state + +#endif + +#if defined(__MSDOS__) && !defined(MSDOS) +# define MSDOS +#endif +#if (defined(OS_2) || defined(__OS2__)) && !defined(OS2) +# define OS2 +#endif +#if defined(_WINDOWS) && !defined(WINDOWS) +# define WINDOWS +#endif +#if defined(_WIN32) || defined(_WIN32_WCE) || defined(__WIN32__) +# ifndef WIN32 +# define WIN32 +# endif +#endif +#if (defined(MSDOS) || defined(OS2) || defined(WINDOWS)) && !defined(WIN32) +# if !defined(__GNUC__) && !defined(__FLAT__) && !defined(__386__) +# ifndef SYS16BIT +# define SYS16BIT +# endif +# endif +#endif + +/* + * Compile with -DMAXSEG_64K if the alloc function cannot allocate more + * than 64k bytes at a time (needed on systems with 16-bit int). + */ +#ifdef SYS16BIT +# define MAXSEG_64K +#endif +#ifdef MSDOS +# define UNALIGNED_OK +#endif + +#ifdef __STDC_VERSION__ +# ifndef STDC +# define STDC +# endif +# if __STDC_VERSION__ >= 199901L +# ifndef STDC99 +# define STDC99 +# endif +# endif +#endif +#if !defined(STDC) && (defined(__STDC__) || defined(__cplusplus)) +# define STDC +#endif +#if !defined(STDC) && (defined(__GNUC__) || defined(__BORLANDC__)) +# define STDC +#endif +#if !defined(STDC) && (defined(MSDOS) || defined(WINDOWS) || defined(WIN32)) +# define STDC +#endif +#if !defined(STDC) && (defined(OS2) || defined(__HOS_AIX__)) +# define STDC +#endif + +#if defined(__OS400__) && !defined(STDC) /* iSeries (formerly AS/400). */ +# define STDC +#endif + +#ifndef STDC +# ifndef const /* cannot use !defined(STDC) && !defined(const) on Mac */ +# define const /* note: need a more gentle solution here */ +# endif +#endif + +#ifndef z_const +# ifdef ZLIB_CONST +# define z_const const +# else +# define z_const +# endif +#endif + +#ifdef Z_SOLO +# ifdef _WIN64 + typedef unsigned long long z_size_t; +# else + typedef unsigned long z_size_t; +# endif +#else +# define z_longlong long long +# if defined(NO_SIZE_T) + typedef unsigned NO_SIZE_T z_size_t; +# elif defined(STDC) +# include + typedef size_t z_size_t; +# else + typedef unsigned long z_size_t; +# endif +# undef z_longlong +#endif + +/* Maximum value for memLevel in deflateInit2 */ +#ifndef MAX_MEM_LEVEL +# ifdef MAXSEG_64K +# define MAX_MEM_LEVEL 8 +# else +# define MAX_MEM_LEVEL 9 +# endif +#endif + +/* Maximum value for windowBits in deflateInit2 and inflateInit2. + * WARNING: reducing MAX_WBITS makes minigzip unable to extract .gz files + * created by gzip. (Files created by minigzip can still be extracted by + * gzip.) + */ +#ifndef MAX_WBITS +# define MAX_WBITS 15 /* 32K LZ77 window */ +#endif + +/* The memory requirements for deflate are (in bytes): + (1 << (windowBits+2)) + (1 << (memLevel+9)) + that is: 128K for windowBits=15 + 128K for memLevel = 8 (default values) + plus a few kilobytes for small objects. For example, if you want to reduce + the default memory requirements from 256K to 128K, compile with + make CFLAGS="-O -DMAX_WBITS=14 -DMAX_MEM_LEVEL=7" + Of course this will generally degrade compression (there's no free lunch). + + The memory requirements for inflate are (in bytes) 1 << windowBits + that is, 32K for windowBits=15 (default value) plus about 7 kilobytes + for small objects. +*/ + + /* Type declarations */ + +#ifndef OF /* function prototypes */ +# ifdef STDC +# define OF(args) args +# else +# define OF(args) () +# endif +#endif + +/* The following definitions for FAR are needed only for MSDOS mixed + * model programming (small or medium model with some far allocations). + * This was tested only with MSC; for other MSDOS compilers you may have + * to define NO_MEMCPY in zutil.h. If you don't need the mixed model, + * just define FAR to be empty. + */ +#ifdef SYS16BIT +# if defined(M_I86SM) || defined(M_I86MM) + /* MSC small or medium model */ +# define SMALL_MEDIUM +# ifdef _MSC_VER +# define FAR _far +# else +# define FAR far +# endif +# endif +# if (defined(__SMALL__) || defined(__MEDIUM__)) + /* Turbo C small or medium model */ +# define SMALL_MEDIUM +# ifdef __BORLANDC__ +# define FAR _far +# else +# define FAR far +# endif +# endif +#endif + +#if defined(WINDOWS) || defined(WIN32) + /* If building or using zlib as a DLL, define ZLIB_DLL. + * This is not mandatory, but it offers a little performance increase. + */ +# ifdef ZLIB_DLL +# if defined(WIN32) && (!defined(__BORLANDC__) || (__BORLANDC__ >= 0x500)) +# ifdef ZLIB_INTERNAL +# define ZEXTERN extern __declspec(dllexport) +# else +# define ZEXTERN extern __declspec(dllimport) +# endif +# endif +# endif /* ZLIB_DLL */ + /* If building or using zlib with the WINAPI/WINAPIV calling convention, + * define ZLIB_WINAPI. + * Caution: the standard ZLIB1.DLL is NOT compiled using ZLIB_WINAPI. + */ +# ifdef ZLIB_WINAPI +# ifdef FAR +# undef FAR +# endif +# ifndef WIN32_LEAN_AND_MEAN +# define WIN32_LEAN_AND_MEAN +# endif +# include + /* No need for _export, use ZLIB.DEF instead. */ + /* For complete Windows compatibility, use WINAPI, not __stdcall. */ +# define ZEXPORT WINAPI +# ifdef WIN32 +# define ZEXPORTVA WINAPIV +# else +# define ZEXPORTVA FAR CDECL +# endif +# endif +#endif + +#if defined (__BEOS__) +# ifdef ZLIB_DLL +# ifdef ZLIB_INTERNAL +# define ZEXPORT __declspec(dllexport) +# define ZEXPORTVA __declspec(dllexport) +# else +# define ZEXPORT __declspec(dllimport) +# define ZEXPORTVA __declspec(dllimport) +# endif +# endif +#endif + +#ifndef ZEXTERN +# define ZEXTERN extern +#endif +#ifndef ZEXPORT +# define ZEXPORT +#endif +#ifndef ZEXPORTVA +# define ZEXPORTVA +#endif + +#ifndef FAR +# define FAR +#endif + +#if !defined(__MACTYPES__) +typedef unsigned char Byte; /* 8 bits */ +#endif +typedef unsigned int uInt; /* 16 bits or more */ +typedef unsigned long uLong; /* 32 bits or more */ + +#ifdef SMALL_MEDIUM + /* Borland C/C++ and some old MSC versions ignore FAR inside typedef */ +# define Bytef Byte FAR +#else + typedef Byte FAR Bytef; +#endif +typedef char FAR charf; +typedef int FAR intf; +typedef uInt FAR uIntf; +typedef uLong FAR uLongf; + +#ifdef STDC + typedef void const *voidpc; + typedef void FAR *voidpf; + typedef void *voidp; +#else + typedef Byte const *voidpc; + typedef Byte FAR *voidpf; + typedef Byte *voidp; +#endif + +#if !defined(Z_U4) && !defined(Z_SOLO) && defined(STDC) +# include +# if (UINT_MAX == 0xffffffffUL) +# define Z_U4 unsigned +# elif (ULONG_MAX == 0xffffffffUL) +# define Z_U4 unsigned long +# elif (USHRT_MAX == 0xffffffffUL) +# define Z_U4 unsigned short +# endif +#endif + +#ifdef Z_U4 + typedef Z_U4 z_crc_t; +#else + typedef unsigned long z_crc_t; +#endif + +#if HAVE_UNISTD_H-0 /* may be set to #if 1 by ./configure */ +# define Z_HAVE_UNISTD_H +#endif + +#if HAVE_STDARG_H-0 /* may be set to #if 1 by ./configure */ +# define Z_HAVE_STDARG_H +#endif + +#ifdef STDC +# ifndef Z_SOLO +# include /* for off_t */ +# endif +#endif + +#if defined(STDC) || defined(Z_HAVE_STDARG_H) +# ifndef Z_SOLO +# include /* for va_list */ +# endif +#endif + +#ifdef _WIN32 +# ifndef Z_SOLO +# include /* for wchar_t */ +# endif +#endif + +/* a little trick to accommodate both "#define _LARGEFILE64_SOURCE" and + * "#define _LARGEFILE64_SOURCE 1" as requesting 64-bit operations, (even + * though the former does not conform to the LFS document), but considering + * both "#undef _LARGEFILE64_SOURCE" and "#define _LARGEFILE64_SOURCE 0" as + * equivalently requesting no 64-bit operations + */ +#if defined(_LARGEFILE64_SOURCE) && -_LARGEFILE64_SOURCE - -1 == 1 +# undef _LARGEFILE64_SOURCE +#endif + +#ifndef Z_HAVE_UNISTD_H +# if defined(__WATCOMC__) || defined(__GO32__) || \ + (defined(_LARGEFILE64_SOURCE) && !defined(_WIN32)) +# define Z_HAVE_UNISTD_H +# endif +#endif +#ifndef Z_SOLO +# if defined(Z_HAVE_UNISTD_H) +# include /* for SEEK_*, off_t, and _LFS64_LARGEFILE */ +# ifdef VMS +# include /* for off_t */ +# endif +# ifndef z_off_t +# define z_off_t off_t +# endif +# endif +#endif + +#if defined(_LFS64_LARGEFILE) && _LFS64_LARGEFILE-0 +# define Z_LFS64 +#endif + +#if defined(_LARGEFILE64_SOURCE) && defined(Z_LFS64) +# define Z_LARGE64 +#endif + +#if defined(_FILE_OFFSET_BITS) && _FILE_OFFSET_BITS-0 == 64 && defined(Z_LFS64) +# define Z_WANT64 +#endif + +#if !defined(SEEK_SET) && !defined(Z_SOLO) +# define SEEK_SET 0 /* Seek from beginning of file. */ +# define SEEK_CUR 1 /* Seek from current position. */ +# define SEEK_END 2 /* Set file pointer to EOF plus "offset" */ +#endif + +#ifndef z_off_t +# define z_off_t long long +#endif + +#if !defined(_WIN32) && defined(Z_LARGE64) +# define z_off64_t off64_t +#elif defined(__MINGW32__) +# define z_off64_t long long +#elif defined(_WIN32) && !defined(__GNUC__) +# define z_off64_t __int64 +#elif defined(__GO32__) +# define z_off64_t offset_t +#else +# define z_off64_t z_off_t +#endif + +/* MVS linker does not support external names larger than 8 bytes */ +#if defined(__MVS__) + #pragma map(deflateInit_,"DEIN") + #pragma map(deflateInit2_,"DEIN2") + #pragma map(deflateEnd,"DEEND") + #pragma map(deflateBound,"DEBND") + #pragma map(inflateInit_,"ININ") + #pragma map(inflateInit2_,"ININ2") + #pragma map(inflateEnd,"INEND") + #pragma map(inflateSync,"INSY") + #pragma map(inflateSetDictionary,"INSEDI") + #pragma map(compressBound,"CMBND") + #pragma map(inflate_table,"INTABL") + #pragma map(inflate_fast,"INFA") + #pragma map(inflate_copyright,"INCOPY") +#endif + +#endif /* ZCONF_H */ diff --git a/Minecraft.Client/Windows64/4JLibs/inc/Render/zlib/zlib.h b/Minecraft.Client/Windows64/4JLibs/inc/Render/zlib/zlib.h new file mode 100644 index 000000000..a57d33613 --- /dev/null +++ b/Minecraft.Client/Windows64/4JLibs/inc/Render/zlib/zlib.h @@ -0,0 +1,2057 @@ +/* zlib.h -- interface of the 'zlib' general purpose compression library + version 1.3.2, February 17th, 2026 + + Copyright (C) 1995-2026 Jean-loup Gailly and Mark Adler + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + Jean-loup Gailly Mark Adler + jloup@gzip.org madler@alumni.caltech.edu + + + The data format used by the zlib library is described by RFCs (Request for + Comments) 1950 to 1952 at https://datatracker.ietf.org/doc/html/rfc1950 + (zlib format), rfc1951 (deflate format) and rfc1952 (gzip format). +*/ + +#ifndef ZLIB_H +#define ZLIB_H + +#ifdef ZLIB_BUILD +# include +#else +# include "zconf.h" +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#define ZLIB_VERSION "1.3.2" +#define ZLIB_VERNUM 0x1320 +#define ZLIB_VER_MAJOR 1 +#define ZLIB_VER_MINOR 3 +#define ZLIB_VER_REVISION 2 +#define ZLIB_VER_SUBREVISION 0 + +/* + The 'zlib' compression library provides in-memory compression and + decompression functions, including integrity checks of the uncompressed data. + This version of the library supports only one compression method (deflation) + but other algorithms will be added later and will have the same stream + interface. + + Compression can be done in a single step if the buffers are large enough, + or can be done by repeated calls of the compression function. In the latter + case, the application must provide more input and/or consume the output + (providing more output space) before each call. + + The compressed data format used by default by the in-memory functions is + the zlib format, which is a zlib wrapper documented in RFC 1950, wrapped + around a deflate stream, which is itself documented in RFC 1951. + + The library also supports reading and writing files in gzip (.gz) format + with an interface similar to that of stdio using the functions that start + with "gz". The gzip format is different from the zlib format. gzip is a + gzip wrapper, documented in RFC 1952, wrapped around a deflate stream. + + This library can optionally read and write gzip and raw deflate streams in + memory as well. + + The zlib format was designed to be compact and fast for use in memory + and on communications channels. The gzip format was designed for single- + file compression on file systems, has a larger header than zlib to maintain + directory information, and uses a different, slower check method than zlib. + + The library does not install any signal handler. The decoder checks + the consistency of the compressed data, so the library should never crash + even in the case of corrupted input. +*/ + +typedef voidpf (*alloc_func)(voidpf opaque, uInt items, uInt size); +typedef void (*free_func)(voidpf opaque, voidpf address); + +struct internal_state; + +typedef struct z_stream_s { + z_const Bytef *next_in; /* next input byte */ + uInt avail_in; /* number of bytes available at next_in */ + uLong total_in; /* total number of input bytes read so far */ + + Bytef *next_out; /* next output byte will go here */ + uInt avail_out; /* remaining free space at next_out */ + uLong total_out; /* total number of bytes output so far */ + + z_const char *msg; /* last error message, NULL if no error */ + struct internal_state FAR *state; /* not visible by applications */ + + alloc_func zalloc; /* used to allocate the internal state */ + free_func zfree; /* used to free the internal state */ + voidpf opaque; /* private data object passed to zalloc and zfree */ + + int data_type; /* best guess about the data type: binary or text + for deflate, or the decoding state for inflate */ + uLong adler; /* Adler-32 or CRC-32 value of the uncompressed data */ + uLong reserved; /* reserved for future use */ +} z_stream; + +typedef z_stream FAR *z_streamp; + +/* + gzip header information passed to and from zlib routines. See RFC 1952 + for more details on the meanings of these fields. +*/ +typedef struct gz_header_s { + int text; /* true if compressed data believed to be text */ + uLong time; /* modification time */ + int xflags; /* extra flags (not used when writing a gzip file) */ + int os; /* operating system */ + Bytef *extra; /* pointer to extra field or Z_NULL if none */ + uInt extra_len; /* extra field length (valid if extra != Z_NULL) */ + uInt extra_max; /* space at extra (only when reading header) */ + Bytef *name; /* pointer to zero-terminated file name or Z_NULL */ + uInt name_max; /* space at name (only when reading header) */ + Bytef *comment; /* pointer to zero-terminated comment or Z_NULL */ + uInt comm_max; /* space at comment (only when reading header) */ + int hcrc; /* true if there was or will be a header crc */ + int done; /* true when done reading gzip header (not used + when writing a gzip file) */ +} gz_header; + +typedef gz_header FAR *gz_headerp; + +/* + The application must update next_in and avail_in when avail_in has dropped + to zero. It must update next_out and avail_out when avail_out has dropped + to zero. The application must initialize zalloc, zfree and opaque before + calling the init function. All other fields are set by the compression + library and must not be updated by the application. + + The opaque value provided by the application will be passed as the first + parameter for calls of zalloc and zfree. This can be useful for custom + memory management. The compression library attaches no meaning to the + opaque value. + + zalloc must return Z_NULL if there is not enough memory for the object. + If zlib is used in a multi-threaded application, zalloc and zfree must be + thread safe. In that case, zlib is thread-safe. When zalloc and zfree are + Z_NULL on entry to the initialization function, they are set to internal + routines that use the standard library functions malloc() and free(). + + On 16-bit systems, the functions zalloc and zfree must be able to allocate + exactly 65536 bytes, but will not be required to allocate more than this if + the symbol MAXSEG_64K is defined (see zconf.h). WARNING: On MSDOS, pointers + returned by zalloc for objects of exactly 65536 bytes *must* have their + offset normalized to zero. The default allocation function provided by this + library ensures this (see zutil.c). To reduce memory requirements and avoid + any allocation of 64K objects, at the expense of compression ratio, compile + the library with -DMAX_WBITS=14 (see zconf.h). + + The fields total_in and total_out can be used for statistics or progress + reports. After compression, total_in holds the total size of the + uncompressed data and may be saved for use by the decompressor (particularly + if the decompressor wants to decompress everything in a single step). +*/ + + /* constants */ + +#define Z_NO_FLUSH 0 +#define Z_PARTIAL_FLUSH 1 +#define Z_SYNC_FLUSH 2 +#define Z_FULL_FLUSH 3 +#define Z_FINISH 4 +#define Z_BLOCK 5 +#define Z_TREES 6 +/* Allowed flush values; see deflate() and inflate() below for details */ + +#define Z_OK 0 +#define Z_STREAM_END 1 +#define Z_NEED_DICT 2 +#define Z_ERRNO (-1) +#define Z_STREAM_ERROR (-2) +#define Z_DATA_ERROR (-3) +#define Z_MEM_ERROR (-4) +#define Z_BUF_ERROR (-5) +#define Z_VERSION_ERROR (-6) +/* Return codes for the compression/decompression functions. Negative values + * are errors, positive values are used for special but normal events. + */ + +#define Z_NO_COMPRESSION 0 +#define Z_BEST_SPEED 1 +#define Z_BEST_COMPRESSION 9 +#define Z_DEFAULT_COMPRESSION (-1) +/* compression levels */ + +#define Z_FILTERED 1 +#define Z_HUFFMAN_ONLY 2 +#define Z_RLE 3 +#define Z_FIXED 4 +#define Z_DEFAULT_STRATEGY 0 +/* compression strategy; see deflateInit2() below for details */ + +#define Z_BINARY 0 +#define Z_TEXT 1 +#define Z_ASCII Z_TEXT /* for compatibility with 1.2.2 and earlier */ +#define Z_UNKNOWN 2 +/* Possible values of the data_type field for deflate() */ + +#define Z_DEFLATED 8 +/* The deflate compression method (the only one supported in this version) */ + +#define Z_NULL 0 /* for initializing zalloc, zfree, opaque */ + +#define zlib_version zlibVersion() +/* for compatibility with versions < 1.0.2 */ + + + /* basic functions */ + +ZEXTERN const char * ZEXPORT zlibVersion(void); +/* The application can compare zlibVersion and ZLIB_VERSION for consistency. + If the first character differs, the library code actually used is not + compatible with the zlib.h header file used by the application. This check + is automatically made by deflateInit and inflateInit. + */ + +/* +ZEXTERN int ZEXPORT deflateInit(z_streamp strm, int level); + + Initializes the internal stream state for compression. The fields + zalloc, zfree and opaque must be initialized before by the caller. If + zalloc and zfree are set to Z_NULL, deflateInit updates them to use default + allocation functions. total_in, total_out, adler, and msg are initialized. + + The compression level must be Z_DEFAULT_COMPRESSION, or between 0 and 9: + 1 gives best speed, 9 gives best compression, 0 gives no compression at all + (the input data is simply copied a block at a time). Z_DEFAULT_COMPRESSION + requests a default compromise between speed and compression (currently + equivalent to level 6). + + deflateInit returns Z_OK if success, Z_MEM_ERROR if there was not enough + memory, Z_STREAM_ERROR if level is not a valid compression level, or + Z_VERSION_ERROR if the zlib library version (zlib_version) is incompatible + with the version assumed by the caller (ZLIB_VERSION). msg is set to null + if there is no error message. deflateInit does not perform any compression: + this will be done by deflate(). +*/ + + +ZEXTERN int ZEXPORT deflate(z_streamp strm, int flush); +/* + deflate compresses as much data as possible, and stops when the input + buffer becomes empty or the output buffer becomes full. It may introduce + some output latency (reading input without producing any output) except when + forced to flush. + + The detailed semantics are as follows. deflate performs one or both of the + following actions: + + - Compress more input starting at next_in and update next_in and avail_in + accordingly. If not all input can be processed (because there is not + enough room in the output buffer), next_in and avail_in are updated and + processing will resume at this point for the next call of deflate(). + + - Generate more output starting at next_out and update next_out and avail_out + accordingly. This action is forced if the parameter flush is non zero. + Forcing flush frequently degrades the compression ratio, so this parameter + should be set only when necessary. Some output may be provided even if + flush is zero. + + Before the call of deflate(), the application should ensure that at least + one of the actions is possible, by providing more input and/or consuming more + output, and updating avail_in or avail_out accordingly; avail_out should + never be zero before the call. The application can consume the compressed + output when it wants, for example when the output buffer is full (avail_out + == 0), or after each call of deflate(). If deflate returns Z_OK and with + zero avail_out, it must be called again after making room in the output + buffer because there might be more output pending. See deflatePending(), + which can be used if desired to determine whether or not there is more output + in that case. + + Normally the parameter flush is set to Z_NO_FLUSH, which allows deflate to + decide how much data to accumulate before producing output, in order to + maximize compression. + + If the parameter flush is set to Z_SYNC_FLUSH, all pending output is + flushed to the output buffer and the output is aligned on a byte boundary, so + that the decompressor can get all input data available so far. (In + particular avail_in is zero after the call if enough output space has been + provided before the call.) Flushing may degrade compression for some + compression algorithms and so it should be used only when necessary. This + completes the current deflate block and follows it with an empty stored block + that is three bits plus filler bits to the next byte, followed by four bytes + (00 00 ff ff). + + If flush is set to Z_PARTIAL_FLUSH, all pending output is flushed to the + output buffer, but the output is not aligned to a byte boundary. All of the + input data so far will be available to the decompressor, as for Z_SYNC_FLUSH. + This completes the current deflate block and follows it with an empty fixed + codes block that is 10 bits long. This assures that enough bytes are output + in order for the decompressor to finish the block before the empty fixed + codes block. + + If flush is set to Z_BLOCK, a deflate block is completed and emitted, as + for Z_SYNC_FLUSH, but the output is not aligned on a byte boundary, and up to + seven bits of the current block are held to be written as the next byte after + the next deflate block is completed. In this case, the decompressor may not + be provided enough bits at this point in order to complete decompression of + the data provided so far to the compressor. It may need to wait for the next + block to be emitted. This is for advanced applications that need to control + the emission of deflate blocks. + + If flush is set to Z_FULL_FLUSH, all output is flushed as with + Z_SYNC_FLUSH, and the compression state is reset so that decompression can + restart from this point if previous compressed data has been damaged or if + random access is desired. Using Z_FULL_FLUSH too often can seriously degrade + compression. + + If deflate returns with avail_out == 0, this function must be called again + with the same value of the flush parameter and more output space (updated + avail_out), until the flush is complete (deflate returns with non-zero + avail_out). In the case of a Z_FULL_FLUSH or Z_SYNC_FLUSH, make sure that + avail_out is greater than six when the flush marker begins, in order to avoid + repeated flush markers upon calling deflate() again when avail_out == 0. + + If the parameter flush is set to Z_FINISH, pending input is processed, + pending output is flushed and deflate returns with Z_STREAM_END if there was + enough output space. If deflate returns with Z_OK or Z_BUF_ERROR, this + function must be called again with Z_FINISH and more output space (updated + avail_out) but no more input data, until it returns with Z_STREAM_END or an + error. After deflate has returned Z_STREAM_END, the only possible operations + on the stream are deflateReset or deflateEnd. + + Z_FINISH can be used in the first deflate call after deflateInit if all the + compression is to be done in a single step. In order to complete in one + call, avail_out must be at least the value returned by deflateBound (see + below). Then deflate is guaranteed to return Z_STREAM_END. If not enough + output space is provided, deflate will not return Z_STREAM_END, and it must + be called again as described above. + + deflate() sets strm->adler to the Adler-32 checksum of all input read + so far (that is, total_in bytes). If a gzip stream is being generated, then + strm->adler will be the CRC-32 checksum of the input read so far. (See + deflateInit2 below.) + + deflate() may update strm->data_type if it can make a good guess about + the input data type (Z_BINARY or Z_TEXT). If in doubt, the data is + considered binary. This field is only for information purposes and does not + affect the compression algorithm in any manner. + + deflate() returns Z_OK if some progress has been made (more input + processed or more output produced), Z_STREAM_END if all input has been + consumed and all output has been produced (only when flush is set to + Z_FINISH), Z_STREAM_ERROR if the stream state was inconsistent (for example + if next_in or next_out was Z_NULL or the state was inadvertently written over + by the application), or Z_BUF_ERROR if no progress is possible (for example + avail_in or avail_out was zero). Note that Z_BUF_ERROR is not fatal, and + deflate() can be called again with more input and more output space to + continue compressing. +*/ + + +ZEXTERN int ZEXPORT deflateEnd(z_streamp strm); +/* + All dynamically allocated data structures for this stream are freed. + This function discards any unprocessed input and does not flush any pending + output. + + deflateEnd returns Z_OK if success, Z_STREAM_ERROR if the + stream state was inconsistent, Z_DATA_ERROR if the stream was freed + prematurely (some input or output was discarded). In the error case, msg + may be set but then points to a static string (which must not be + deallocated). +*/ + + +/* +ZEXTERN int ZEXPORT inflateInit(z_streamp strm); + + Initializes the internal stream state for decompression. The fields + next_in, avail_in, zalloc, zfree and opaque must be initialized before by + the caller. In the current version of inflate, the provided input is not + read or consumed. The allocation of a sliding window will be deferred to + the first call of inflate (if the decompression does not complete on the + first call). If zalloc and zfree are set to Z_NULL, inflateInit updates + them to use default allocation functions. total_in, total_out, adler, and + msg are initialized. + + inflateInit returns Z_OK if success, Z_MEM_ERROR if there was not enough + memory, Z_VERSION_ERROR if the zlib library version is incompatible with the + version assumed by the caller, or Z_STREAM_ERROR if the parameters are + invalid, such as a null pointer to the structure. msg is set to null if + there is no error message. inflateInit does not perform any decompression. + Actual decompression will be done by inflate(). So next_in, and avail_in, + next_out, and avail_out are unused and unchanged. The current + implementation of inflateInit() does not process any header information -- + that is deferred until inflate() is called. +*/ + + +ZEXTERN int ZEXPORT inflate(z_streamp strm, int flush); +/* + inflate decompresses as much data as possible, and stops when the input + buffer becomes empty or the output buffer becomes full. It may introduce + some output latency (reading input without producing any output) except when + forced to flush. + + The detailed semantics are as follows. inflate performs one or both of the + following actions: + + - Decompress more input starting at next_in and update next_in and avail_in + accordingly. If not all input can be processed (because there is not + enough room in the output buffer), then next_in and avail_in are updated + accordingly, and processing will resume at this point for the next call of + inflate(). + + - Generate more output starting at next_out and update next_out and avail_out + accordingly. inflate() provides as much output as possible, until there is + no more input data or no more space in the output buffer (see below about + the flush parameter). + + Before the call of inflate(), the application should ensure that at least + one of the actions is possible, by providing more input and/or consuming more + output, and updating the next_* and avail_* values accordingly. If the + caller of inflate() does not provide both available input and available + output space, it is possible that there will be no progress made. The + application can consume the uncompressed output when it wants, for example + when the output buffer is full (avail_out == 0), or after each call of + inflate(). If inflate returns Z_OK and with zero avail_out, it must be + called again after making room in the output buffer because there might be + more output pending. + + The flush parameter of inflate() can be Z_NO_FLUSH, Z_SYNC_FLUSH, Z_FINISH, + Z_BLOCK, or Z_TREES. Z_SYNC_FLUSH requests that inflate() flush as much + output as possible to the output buffer. Z_BLOCK requests that inflate() + stop if and when it gets to the next deflate block boundary. When decoding + the zlib or gzip format, this will cause inflate() to return immediately + after the header and before the first block. When doing a raw inflate, + inflate() will go ahead and process the first block, and will return when it + gets to the end of that block, or when it runs out of data. + + The Z_BLOCK option assists in appending to or combining deflate streams. + To assist in this, on return inflate() always sets strm->data_type to the + number of unused bits in the input taken from strm->next_in, plus 64 if + inflate() is currently decoding the last block in the deflate stream, plus + 128 if inflate() returned immediately after decoding an end-of-block code or + decoding the complete header up to just before the first byte of the deflate + stream. The end-of-block will not be indicated until all of the uncompressed + data from that block has been written to strm->next_out. The number of + unused bits may in general be greater than seven, except when bit 7 of + data_type is set, in which case the number of unused bits will be less than + eight. data_type is set as noted here every time inflate() returns for all + flush options, and so can be used to determine the amount of currently + consumed input in bits. + + The Z_TREES option behaves as Z_BLOCK does, but it also returns when the + end of each deflate block header is reached, before any actual data in that + block is decoded. This allows the caller to determine the length of the + deflate block header for later use in random access within a deflate block. + 256 is added to the value of strm->data_type when inflate() returns + immediately after reaching the end of the deflate block header. + + inflate() should normally be called until it returns Z_STREAM_END or an + error. However if all decompression is to be performed in a single step (a + single call of inflate), the parameter flush should be set to Z_FINISH. In + this case all pending input is processed and all pending output is flushed; + avail_out must be large enough to hold all of the uncompressed data for the + operation to complete. (The size of the uncompressed data may have been + saved by the compressor for this purpose.) The use of Z_FINISH is not + required to perform an inflation in one step. However it may be used to + inform inflate that a faster approach can be used for the single inflate() + call. Z_FINISH also informs inflate to not maintain a sliding window if the + stream completes, which reduces inflate's memory footprint. If the stream + does not complete, either because not all of the stream is provided or not + enough output space is provided, then a sliding window will be allocated and + inflate() can be called again to continue the operation as if Z_NO_FLUSH had + been used. + + In this implementation, inflate() always flushes as much output as + possible to the output buffer, and always uses the faster approach on the + first call. So the effects of the flush parameter in this implementation are + on the return value of inflate() as noted below, when inflate() returns early + when Z_BLOCK or Z_TREES is used, and when inflate() avoids the allocation of + memory for a sliding window when Z_FINISH is used. + + If a preset dictionary is needed after this call (see inflateSetDictionary + below), inflate sets strm->adler to the Adler-32 checksum of the dictionary + chosen by the compressor and returns Z_NEED_DICT; otherwise it sets + strm->adler to the Adler-32 checksum of all output produced so far (that is, + total_out bytes) and returns Z_OK, Z_STREAM_END or an error code as described + below. At the end of the stream, inflate() checks that its computed Adler-32 + checksum is equal to that saved by the compressor and returns Z_STREAM_END + only if the checksum is correct. + + inflate() can decompress and check either zlib-wrapped or gzip-wrapped + deflate data. The header type is detected automatically, if requested when + initializing with inflateInit2(). Any information contained in the gzip + header is not retained unless inflateGetHeader() is used. When processing + gzip-wrapped deflate data, strm->adler32 is set to the CRC-32 of the output + produced so far. The CRC-32 is checked against the gzip trailer, as is the + uncompressed length, modulo 2^32. + + inflate() returns Z_OK if some progress has been made (more input processed + or more output produced), Z_STREAM_END if the end of the compressed data has + been reached and all uncompressed output has been produced, Z_NEED_DICT if a + preset dictionary is needed at this point, Z_DATA_ERROR if the input data was + corrupted (input stream not conforming to the zlib format or incorrect check + value, in which case strm->msg points to a string with a more specific + error), Z_STREAM_ERROR if the stream structure was inconsistent (for example + next_in or next_out was Z_NULL, or the state was inadvertently written over + by the application), Z_MEM_ERROR if there was not enough memory, Z_BUF_ERROR + if no progress was possible or if there was not enough room in the output + buffer when Z_FINISH is used. Note that Z_BUF_ERROR is not fatal, and + inflate() can be called again with more input and more output space to + continue decompressing. If Z_DATA_ERROR is returned, the application may + then call inflateSync() to look for a good compression block if a partial + recovery of the data is to be attempted. +*/ + + +ZEXTERN int ZEXPORT inflateEnd(z_streamp strm); +/* + All dynamically allocated data structures for this stream are freed. + This function discards any unprocessed input and does not flush any pending + output. + + inflateEnd returns Z_OK if success, or Z_STREAM_ERROR if the stream state + was inconsistent. +*/ + + + /* Advanced functions */ + +/* + The following functions are needed only in some special applications. +*/ + +/* +ZEXTERN int ZEXPORT deflateInit2(z_streamp strm, + int level, + int method, + int windowBits, + int memLevel, + int strategy); + + This is another version of deflateInit with more compression options. The + fields zalloc, zfree and opaque must be initialized before by the caller. + + The method parameter is the compression method. It must be Z_DEFLATED in + this version of the library. + + The windowBits parameter is the base two logarithm of the window size + (the size of the history buffer). It should be in the range 8..15 for this + version of the library. Larger values of this parameter result in better + compression at the expense of memory usage. The default value is 15 if + deflateInit is used instead. + + For the current implementation of deflate(), a windowBits value of 8 (a + window size of 256 bytes) is not supported. As a result, a request for 8 + will result in 9 (a 512-byte window). In that case, providing 8 to + inflateInit2() will result in an error when the zlib header with 9 is + checked against the initialization of inflate(). The remedy is to not use 8 + with deflateInit2() with this initialization, or at least in that case use 9 + with inflateInit2(). + + windowBits can also be -8..-15 for raw deflate. In this case, -windowBits + determines the window size. deflate() will then generate raw deflate data + with no zlib header or trailer, and will not compute a check value. + + windowBits can also be greater than 15 for optional gzip encoding. Add + 16 to windowBits to write a simple gzip header and trailer around the + compressed data instead of a zlib wrapper. The gzip header will have no + file name, no extra data, no comment, no modification time (set to zero), no + header crc, and the operating system will be set to the appropriate value, + if the operating system was determined at compile time. If a gzip stream is + being written, strm->adler is a CRC-32 instead of an Adler-32. + + For raw deflate or gzip encoding, a request for a 256-byte window is + rejected as invalid, since only the zlib header provides a means of + transmitting the window size to the decompressor. + + The memLevel parameter specifies how much memory should be allocated + for the internal compression state. memLevel=1 uses minimum memory but is + slow and reduces compression ratio; memLevel=9 uses maximum memory for + optimal speed. The default value is 8. See zconf.h for total memory usage + as a function of windowBits and memLevel. + + The strategy parameter is used to tune the compression algorithm. Use the + value Z_DEFAULT_STRATEGY for normal data, Z_FILTERED for data produced by a + filter (or predictor), Z_RLE to limit match distances to one (run-length + encoding), or Z_HUFFMAN_ONLY to force Huffman encoding only (no string + matching). Filtered data consists mostly of small values with a somewhat + random distribution, as produced by the PNG filters. In this case, the + compression algorithm is tuned to compress them better. The effect of + Z_FILTERED is to force more Huffman coding and less string matching than the + default; it is intermediate between Z_DEFAULT_STRATEGY and Z_HUFFMAN_ONLY. + Z_RLE is almost as fast as Z_HUFFMAN_ONLY, but should give better + compression for PNG image data than Huffman only. The degree of string + matching from most to none is: Z_DEFAULT_STRATEGY, Z_FILTERED, Z_RLE, then + Z_HUFFMAN_ONLY. The strategy parameter affects the compression ratio but + never the correctness of the compressed output, even if it is not set + optimally for the given data. Z_FIXED uses the default string matching, but + prevents the use of dynamic Huffman codes, allowing for a simpler decoder + for special applications. + + deflateInit2 returns Z_OK if success, Z_MEM_ERROR if there was not enough + memory, Z_STREAM_ERROR if any parameter is invalid (such as an invalid + method), or Z_VERSION_ERROR if the zlib library version (zlib_version) is + incompatible with the version assumed by the caller (ZLIB_VERSION). msg is + set to null if there is no error message. deflateInit2 does not perform any + compression: this will be done by deflate(). +*/ + +ZEXTERN int ZEXPORT deflateSetDictionary(z_streamp strm, + const Bytef *dictionary, + uInt dictLength); +/* + Initializes the compression dictionary from the given byte sequence + without producing any compressed output. When using the zlib format, this + function must be called immediately after deflateInit, deflateInit2 or + deflateReset, and before any call of deflate. When doing raw deflate, this + function must be called either before any call of deflate, or immediately + after the completion of a deflate block, i.e. after all input has been + consumed and all output has been delivered when using any of the flush + options Z_BLOCK, Z_PARTIAL_FLUSH, Z_SYNC_FLUSH, or Z_FULL_FLUSH. The + compressor and decompressor must use exactly the same dictionary (see + inflateSetDictionary). + + The dictionary should consist of strings (byte sequences) that are likely + to be encountered later in the data to be compressed, with the most commonly + used strings preferably put towards the end of the dictionary. Using a + dictionary is most useful when the data to be compressed is short and can be + predicted with good accuracy; the data can then be compressed better than + with the default empty dictionary. + + Depending on the size of the compression data structures selected by + deflateInit or deflateInit2, a part of the dictionary may in effect be + discarded, for example if the dictionary is larger than the window size + provided in deflateInit or deflateInit2. Thus the strings most likely to be + useful should be put at the end of the dictionary, not at the front. In + addition, the current implementation of deflate will use at most the window + size minus 262 bytes of the provided dictionary. + + Upon return of this function, strm->adler is set to the Adler-32 value + of the dictionary; the decompressor may later use this value to determine + which dictionary has been used by the compressor. (The Adler-32 value + applies to the whole dictionary even if only a subset of the dictionary is + actually used by the compressor.) If a raw deflate was requested, then the + Adler-32 value is not computed and strm->adler is not set. + + deflateSetDictionary returns Z_OK if success, or Z_STREAM_ERROR if a + parameter is invalid (e.g. dictionary being Z_NULL) or the stream state is + inconsistent (for example if deflate has already been called for this stream + or if not at a block boundary for raw deflate). deflateSetDictionary does + not perform any compression: this will be done by deflate(). +*/ + +ZEXTERN int ZEXPORT deflateGetDictionary(z_streamp strm, + Bytef *dictionary, + uInt *dictLength); +/* + Returns the sliding dictionary being maintained by deflate. dictLength is + set to the number of bytes in the dictionary, and that many bytes are copied + to dictionary. dictionary must have enough space, where 32768 bytes is + always enough. If deflateGetDictionary() is called with dictionary equal to + Z_NULL, then only the dictionary length is returned, and nothing is copied. + Similarly, if dictLength is Z_NULL, then it is not set. + + deflateGetDictionary() may return a length less than the window size, even + when more than the window size in input has been provided. It may return up + to 258 bytes less in that case, due to how zlib's implementation of deflate + manages the sliding window and lookahead for matches, where matches can be + up to 258 bytes long. If the application needs the last window-size bytes of + input, then that would need to be saved by the application outside of zlib. + + deflateGetDictionary returns Z_OK on success, or Z_STREAM_ERROR if the + stream state is inconsistent. +*/ + +ZEXTERN int ZEXPORT deflateCopy(z_streamp dest, + z_streamp source); +/* + Sets the destination stream as a complete copy of the source stream. + + This function can be useful when several compression strategies will be + tried, for example when there are several ways of pre-processing the input + data with a filter. The streams that will be discarded should then be freed + by calling deflateEnd. Note that deflateCopy duplicates the internal + compression state which can be quite large, so this strategy is slow and can + consume lots of memory. + + deflateCopy returns Z_OK if success, Z_MEM_ERROR if there was not + enough memory, Z_STREAM_ERROR if the source stream state was inconsistent + (such as zalloc being Z_NULL). msg is left unchanged in both source and + destination. +*/ + +ZEXTERN int ZEXPORT deflateReset(z_streamp strm); +/* + This function is equivalent to deflateEnd followed by deflateInit, but + does not free and reallocate the internal compression state. The stream + will leave the compression level and any other attributes that may have been + set unchanged. total_in, total_out, adler, and msg are initialized. + + deflateReset returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent (such as zalloc or state being Z_NULL). +*/ + +ZEXTERN int ZEXPORT deflateParams(z_streamp strm, + int level, + int strategy); +/* + Dynamically update the compression level and compression strategy. The + interpretation of level and strategy is as in deflateInit2(). This can be + used to switch between compression and straight copy of the input data, or + to switch to a different kind of input data requiring a different strategy. + If the compression approach (which is a function of the level) or the + strategy is changed, and if there have been any deflate() calls since the + state was initialized or reset, then the input available so far is + compressed with the old level and strategy using deflate(strm, Z_BLOCK). + There are three approaches for the compression levels 0, 1..3, and 4..9 + respectively. The new level and strategy will take effect at the next call + of deflate(). + + If a deflate(strm, Z_BLOCK) is performed by deflateParams(), and it does + not have enough output space to complete, then the parameter change will not + take effect. In this case, deflateParams() can be called again with the + same parameters and more output space to try again. + + In order to assure a change in the parameters on the first try, the + deflate stream should be flushed using deflate() with Z_BLOCK or other flush + request until strm.avail_out is not zero, before calling deflateParams(). + Then no more input data should be provided before the deflateParams() call. + If this is done, the old level and strategy will be applied to the data + compressed before deflateParams(), and the new level and strategy will be + applied to the data compressed after deflateParams(). + + deflateParams returns Z_OK on success, Z_STREAM_ERROR if the source stream + state was inconsistent or if a parameter was invalid, or Z_BUF_ERROR if + there was not enough output space to complete the compression of the + available input data before a change in the strategy or approach. Note that + in the case of a Z_BUF_ERROR, the parameters are not changed. A return + value of Z_BUF_ERROR is not fatal, in which case deflateParams() can be + retried with more output space. +*/ + +ZEXTERN int ZEXPORT deflateTune(z_streamp strm, + int good_length, + int max_lazy, + int nice_length, + int max_chain); +/* + Fine tune deflate's internal compression parameters. This should only be + used by someone who understands the algorithm used by zlib's deflate for + searching for the best matching string, and even then only by the most + fanatic optimizer trying to squeeze out the last compressed bit for their + specific input data. Read the deflate.c source code for the meaning of the + max_lazy, good_length, nice_length, and max_chain parameters. + + deflateTune() can be called after deflateInit() or deflateInit2(), and + returns Z_OK on success, or Z_STREAM_ERROR for an invalid deflate stream. + */ + +ZEXTERN uLong ZEXPORT deflateBound(z_streamp strm, uLong sourceLen); +ZEXTERN z_size_t ZEXPORT deflateBound_z(z_streamp strm, z_size_t sourceLen); +/* + deflateBound() returns an upper bound on the compressed size after + deflation of sourceLen bytes. It must be called after deflateInit() or + deflateInit2(), and after deflateSetHeader(), if used. This would be used + to allocate an output buffer for deflation in a single pass, and so would be + called before deflate(). If that first deflate() call is provided the + sourceLen input bytes, an output buffer allocated to the size returned by + deflateBound(), and the flush value Z_FINISH, then deflate() is guaranteed + to return Z_STREAM_END. Note that it is possible for the compressed size to + be larger than the value returned by deflateBound() if flush options other + than Z_FINISH or Z_NO_FLUSH are used. + + delfateBound_z() is the same, but takes and returns a size_t length. Note + that a long is 32 bits on Windows. +*/ + +ZEXTERN int ZEXPORT deflatePending(z_streamp strm, + unsigned *pending, + int *bits); +/* + deflatePending() returns the number of bytes and bits of output that have + been generated, but not yet provided in the available output. The bytes not + provided would be due to the available output space having being consumed. + The number of bits of output not provided are between 0 and 7, where they + await more bits to join them in order to fill out a full byte. If pending + or bits are Z_NULL, then those values are not set. + + deflatePending returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent. If an int is 16 bits and memLevel is 9, then + it is possible for the number of pending bytes to not fit in an unsigned. In + that case Z_BUF_ERROR is returned and *pending is set to the maximum value + of an unsigned. + */ + +ZEXTERN int ZEXPORT deflateUsed(z_streamp strm, + int *bits); +/* + deflateUsed() returns in *bits the most recent number of deflate bits used + in the last byte when flushing to a byte boundary. The result is in 1..8, or + 0 if there has not yet been a flush. This helps determine the location of + the last bit of a deflate stream. + + deflateUsed returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent. + */ + +ZEXTERN int ZEXPORT deflatePrime(z_streamp strm, + int bits, + int value); +/* + deflatePrime() inserts bits in the deflate output stream. The intent + is that this function is used to start off the deflate output with the bits + leftover from a previous deflate stream when appending to it. As such, this + function can only be used for raw deflate, and must be used before the first + deflate() call after a deflateInit2() or deflateReset(). bits must be less + than or equal to 16, and that many of the least significant bits of value + will be inserted in the output. + + deflatePrime returns Z_OK if success, Z_BUF_ERROR if there was not enough + room in the internal buffer to insert the bits, or Z_STREAM_ERROR if the + source stream state was inconsistent. +*/ + +ZEXTERN int ZEXPORT deflateSetHeader(z_streamp strm, + gz_headerp head); +/* + deflateSetHeader() provides gzip header information for when a gzip + stream is requested by deflateInit2(). deflateSetHeader() may be called + after deflateInit2() or deflateReset() and before the first call of + deflate(). The text, time, os, extra field, name, and comment information + in the provided gz_header structure are written to the gzip header (xflag is + ignored -- the extra flags are set according to the compression level). The + caller must assure that, if not Z_NULL, name and comment are terminated with + a zero byte, and that if extra is not Z_NULL, that extra_len bytes are + available there. If hcrc is true, a gzip header crc is included. Note that + the current versions of the command-line version of gzip (up through version + 1.3.x) do not support header crc's, and will report that it is a "multi-part + gzip file" and give up. + + If deflateSetHeader is not used, the default gzip header has text false, + the time set to zero, and os set to the current operating system, with no + extra, name, or comment fields. The gzip header is returned to the default + state by deflateReset(). + + deflateSetHeader returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent. +*/ + +/* +ZEXTERN int ZEXPORT inflateInit2(z_streamp strm, + int windowBits); + + This is another version of inflateInit with an extra parameter. The + fields next_in, avail_in, zalloc, zfree and opaque must be initialized + before by the caller. + + The windowBits parameter is the base two logarithm of the maximum window + size (the size of the history buffer). It should be in the range 8..15 for + this version of the library. The default value is 15 if inflateInit is used + instead. windowBits must be greater than or equal to the windowBits value + provided to deflateInit2() while compressing, or it must be equal to 15 if + deflateInit2() was not used. If a compressed stream with a larger window + size is given as input, inflate() will return with the error code + Z_DATA_ERROR instead of trying to allocate a larger window. + + windowBits can also be zero to request that inflate use the window size in + the zlib header of the compressed stream. + + windowBits can also be -8..-15 for raw inflate. In this case, -windowBits + determines the window size. inflate() will then process raw deflate data, + not looking for a zlib or gzip header, not generating a check value, and not + looking for any check values for comparison at the end of the stream. This + is for use with other formats that use the deflate compressed data format + such as zip. Those formats provide their own check values. If a custom + format is developed using the raw deflate format for compressed data, it is + recommended that a check value such as an Adler-32 or a CRC-32 be applied to + the uncompressed data as is done in the zlib, gzip, and zip formats. For + most applications, the zlib format should be used as is. Note that comments + above on the use in deflateInit2() applies to the magnitude of windowBits. + + windowBits can also be greater than 15 for optional gzip decoding. Add + 32 to windowBits to enable zlib and gzip decoding with automatic header + detection, or add 16 to decode only the gzip format (the zlib format will + return a Z_DATA_ERROR). If a gzip stream is being decoded, strm->adler is a + CRC-32 instead of an Adler-32. Unlike the gunzip utility and gzread() (see + below), inflate() will *not* automatically decode concatenated gzip members. + inflate() will return Z_STREAM_END at the end of the gzip member. The state + would need to be reset to continue decoding a subsequent gzip member. This + *must* be done if there is more data after a gzip member, in order for the + decompression to be compliant with the gzip standard (RFC 1952). + + inflateInit2 returns Z_OK if success, Z_MEM_ERROR if there was not enough + memory, Z_VERSION_ERROR if the zlib library version is incompatible with the + version assumed by the caller, or Z_STREAM_ERROR if the parameters are + invalid, such as a null pointer to the structure. msg is set to null if + there is no error message. inflateInit2 does not perform any decompression + apart from possibly reading the zlib header if present: actual decompression + will be done by inflate(). (So next_in and avail_in may be modified, but + next_out and avail_out are unused and unchanged.) The current implementation + of inflateInit2() does not process any header information -- that is + deferred until inflate() is called. +*/ + +ZEXTERN int ZEXPORT inflateSetDictionary(z_streamp strm, + const Bytef *dictionary, + uInt dictLength); +/* + Initializes the decompression dictionary from the given uncompressed byte + sequence. This function must be called immediately after a call of inflate, + if that call returned Z_NEED_DICT. The dictionary chosen by the compressor + can be determined from the Adler-32 value returned by that call of inflate. + The compressor and decompressor must use exactly the same dictionary (see + deflateSetDictionary). For raw inflate, this function can be called at any + time to set the dictionary. If the provided dictionary is smaller than the + window and there is already data in the window, then the provided dictionary + will amend what's there. The application must insure that the dictionary + that was used for compression is provided. + + inflateSetDictionary returns Z_OK if success, Z_STREAM_ERROR if a + parameter is invalid (e.g. dictionary being Z_NULL) or the stream state is + inconsistent, Z_DATA_ERROR if the given dictionary doesn't match the + expected one (incorrect Adler-32 value). inflateSetDictionary does not + perform any decompression: this will be done by subsequent calls of + inflate(). +*/ + +ZEXTERN int ZEXPORT inflateGetDictionary(z_streamp strm, + Bytef *dictionary, + uInt *dictLength); +/* + Returns the sliding dictionary being maintained by inflate. dictLength is + set to the number of bytes in the dictionary, and that many bytes are copied + to dictionary. dictionary must have enough space, where 32768 bytes is + always enough. If inflateGetDictionary() is called with dictionary equal to + Z_NULL, then only the dictionary length is returned, and nothing is copied. + Similarly, if dictLength is Z_NULL, then it is not set. + + inflateGetDictionary returns Z_OK on success, or Z_STREAM_ERROR if the + stream state is inconsistent. +*/ + +ZEXTERN int ZEXPORT inflateSync(z_streamp strm); +/* + Skips invalid compressed data until a possible full flush point (see above + for the description of deflate with Z_FULL_FLUSH) can be found, or until all + available input is skipped. No output is provided. + + inflateSync searches for a 00 00 FF FF pattern in the compressed data. + All full flush points have this pattern, but not all occurrences of this + pattern are full flush points. + + inflateSync returns Z_OK if a possible full flush point has been found, + Z_BUF_ERROR if no more input was provided, Z_DATA_ERROR if no flush point + has been found, or Z_STREAM_ERROR if the stream structure was inconsistent. + In the success case, the application may save the current value of total_in + which indicates where valid compressed data was found. In the error case, + the application may repeatedly call inflateSync, providing more input each + time, until success or end of the input data. +*/ + +ZEXTERN int ZEXPORT inflateCopy(z_streamp dest, + z_streamp source); +/* + Sets the destination stream as a complete copy of the source stream. + + This function can be useful when randomly accessing a large stream. The + first pass through the stream can periodically record the inflate state, + allowing restarting inflate at those points when randomly accessing the + stream. + + inflateCopy returns Z_OK if success, Z_MEM_ERROR if there was not + enough memory, Z_STREAM_ERROR if the source stream state was inconsistent + (such as zalloc being Z_NULL). msg is left unchanged in both source and + destination. +*/ + +ZEXTERN int ZEXPORT inflateReset(z_streamp strm); +/* + This function is equivalent to inflateEnd followed by inflateInit, + but does not free and reallocate the internal decompression state. The + stream will keep attributes that may have been set by inflateInit2. + total_in, total_out, adler, and msg are initialized. + + inflateReset returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent (such as zalloc or state being Z_NULL). +*/ + +ZEXTERN int ZEXPORT inflateReset2(z_streamp strm, + int windowBits); +/* + This function is the same as inflateReset, but it also permits changing + the wrap and window size requests. The windowBits parameter is interpreted + the same as it is for inflateInit2. If the window size is changed, then the + memory allocated for the window is freed, and the window will be reallocated + by inflate() if needed. + + inflateReset2 returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent (such as zalloc or state being Z_NULL), or if + the windowBits parameter is invalid. +*/ + +ZEXTERN int ZEXPORT inflatePrime(z_streamp strm, + int bits, + int value); +/* + This function inserts bits in the inflate input stream. The intent is to + use inflatePrime() to start inflating at a bit position in the middle of a + byte. The provided bits will be used before any bytes are used from + next_in. This function should be used with raw inflate, before the first + inflate() call, after inflateInit2() or inflateReset(). It can also be used + after an inflate() return indicates the end of a deflate block or header + when using Z_BLOCK. bits must be less than or equal to 16, and that many of + the least significant bits of value will be inserted in the input. The + other bits in value can be non-zero, and will be ignored. + + If bits is negative, then the input stream bit buffer is emptied. Then + inflatePrime() can be called again to put bits in the buffer. This is used + to clear out bits leftover after feeding inflate a block description prior + to feeding inflate codes. + + inflatePrime returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent, or if bits is out of range. If inflate was + in the middle of processing a header, trailer, or stored block lengths, then + it is possible for there to be only eight bits available in the bit buffer. + In that case, bits > 8 is considered out of range. However, when used as + outlined above, there will always be 16 bits available in the buffer for + insertion. As noted in its documentation above, inflate records the number + of bits in the bit buffer on return in data_type. 32 minus that is the + number of bits available for insertion. inflatePrime does not update + data_type with the new number of bits in buffer. +*/ + +ZEXTERN long ZEXPORT inflateMark(z_streamp strm); +/* + This function returns two values, one in the lower 16 bits of the return + value, and the other in the remaining upper bits, obtained by shifting the + return value down 16 bits. If the upper value is -1 and the lower value is + zero, then inflate() is currently decoding information outside of a block. + If the upper value is -1 and the lower value is non-zero, then inflate is in + the middle of a stored block, with the lower value equaling the number of + bytes from the input remaining to copy. If the upper value is not -1, then + it is the number of bits back from the current bit position in the input of + the code (literal or length/distance pair) currently being processed. In + that case the lower value is the number of bytes already emitted for that + code. + + A code is being processed if inflate is waiting for more input to complete + decoding of the code, or if it has completed decoding but is waiting for + more output space to write the literal or match data. + + inflateMark() is used to mark locations in the input data for random + access, which may be at bit positions, and to note those cases where the + output of a code may span boundaries of random access blocks. The current + location in the input stream can be determined from avail_in and data_type + as noted in the description for the Z_BLOCK flush parameter for inflate. + + inflateMark returns the value noted above, or -65536 if the provided + source stream state was inconsistent. +*/ + +ZEXTERN int ZEXPORT inflateGetHeader(z_streamp strm, + gz_headerp head); +/* + inflateGetHeader() requests that gzip header information be stored in the + provided gz_header structure. inflateGetHeader() may be called after + inflateInit2() or inflateReset(), and before the first call of inflate(). + As inflate() processes the gzip stream, head->done is zero until the header + is completed, at which time head->done is set to one. If a zlib stream is + being decoded, then head->done is set to -1 to indicate that there will be + no gzip header information forthcoming. Note that Z_BLOCK or Z_TREES can be + used to force inflate() to return immediately after header processing is + complete and before any actual data is decompressed. + + The text, time, xflags, and os fields are filled in with the gzip header + contents. hcrc is set to true if there is a header CRC. (The header CRC + was valid if done is set to one.) The extra, name, and comment pointers + much each be either Z_NULL or point to space to store that information from + the header. If extra is not Z_NULL, then extra_max contains the maximum + number of bytes that can be written to extra. Once done is true, extra_len + contains the actual extra field length, and extra contains the extra field, + or that field truncated if extra_max is less than extra_len. If name is not + Z_NULL, then up to name_max characters, including the terminating zero, are + written there. If comment is not Z_NULL, then up to comm_max characters, + including the terminating zero, are written there. The application can tell + that the name or comment did not fit in the provided space by the absence of + a terminating zero. If any of extra, name, or comment are not present in + the header, then that field's pointer is set to Z_NULL. This allows the use + of deflateSetHeader() with the returned structure to duplicate the header. + Note that if those fields initially pointed to allocated memory, then the + application will need to save them elsewhere so that they can be eventually + freed. + + If inflateGetHeader is not used, then the header information is simply + discarded. The header is always checked for validity, including the header + CRC if present. inflateReset() will reset the process to discard the header + information. The application would need to call inflateGetHeader() again to + retrieve the header from the next gzip stream. + + inflateGetHeader returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent. +*/ + +/* +ZEXTERN int ZEXPORT inflateBackInit(z_streamp strm, int windowBits, + unsigned char FAR *window); + + Initialize the internal stream state for decompression using inflateBack() + calls. The fields zalloc, zfree and opaque in strm must be initialized + before the call. If zalloc and zfree are Z_NULL, then the default library- + derived memory allocation routines are used. windowBits is the base two + logarithm of the window size, in the range 8..15. window is a caller + supplied buffer of that size. Except for special applications where it is + assured that deflate was used with small window sizes, windowBits must be 15 + and a 32K byte window must be supplied to be able to decompress general + deflate streams. + + See inflateBack() for the usage of these routines. + + inflateBackInit will return Z_OK on success, Z_STREAM_ERROR if any of + the parameters are invalid, Z_MEM_ERROR if the internal state could not be + allocated, or Z_VERSION_ERROR if the version of the library does not match + the version of the header file. +*/ + +typedef unsigned (*in_func)(void FAR *, + z_const unsigned char FAR * FAR *); +typedef int (*out_func)(void FAR *, unsigned char FAR *, unsigned); + +ZEXTERN int ZEXPORT inflateBack(z_streamp strm, + in_func in, void FAR *in_desc, + out_func out, void FAR *out_desc); +/* + inflateBack() does a raw inflate with a single call using a call-back + interface for input and output. This is potentially more efficient than + inflate() for file i/o applications, in that it avoids copying between the + output and the sliding window by simply making the window itself the output + buffer. inflate() can be faster on modern CPUs when used with large + buffers. inflateBack() trusts the application to not change the output + buffer passed by the output function, at least until inflateBack() returns. + + inflateBackInit() must be called first to allocate the internal state + and to initialize the state with the user-provided window buffer. + inflateBack() may then be used multiple times to inflate a complete, raw + deflate stream with each call. inflateBackEnd() is then called to free the + allocated state. + + A raw deflate stream is one with no zlib or gzip header or trailer. + This routine would normally be used in a utility that reads zip or gzip + files and writes out uncompressed files. The utility would decode the + header and process the trailer on its own, hence this routine expects only + the raw deflate stream to decompress. This is different from the default + behavior of inflate(), which expects a zlib header and trailer around the + deflate stream. + + inflateBack() uses two subroutines supplied by the caller that are then + called by inflateBack() for input and output. inflateBack() calls those + routines until it reads a complete deflate stream and writes out all of the + uncompressed data, or until it encounters an error. The function's + parameters and return types are defined above in the in_func and out_func + typedefs. inflateBack() will call in(in_desc, &buf) which should return the + number of bytes of provided input, and a pointer to that input in buf. If + there is no input available, in() must return zero -- buf is ignored in that + case -- and inflateBack() will return a buffer error. inflateBack() will + call out(out_desc, buf, len) to write the uncompressed data buf[0..len-1]. + out() should return zero on success, or non-zero on failure. If out() + returns non-zero, inflateBack() will return with an error. Neither in() nor + out() are permitted to change the contents of the window provided to + inflateBackInit(), which is also the buffer that out() uses to write from. + The length written by out() will be at most the window size. Any non-zero + amount of input may be provided by in(). + + For convenience, inflateBack() can be provided input on the first call by + setting strm->next_in and strm->avail_in. If that input is exhausted, then + in() will be called. Therefore strm->next_in must be initialized before + calling inflateBack(). If strm->next_in is Z_NULL, then in() will be called + immediately for input. If strm->next_in is not Z_NULL, then strm->avail_in + must also be initialized, and then if strm->avail_in is not zero, input will + initially be taken from strm->next_in[0 .. strm->avail_in - 1]. + + The in_desc and out_desc parameters of inflateBack() is passed as the + first parameter of in() and out() respectively when they are called. These + descriptors can be optionally used to pass any information that the caller- + supplied in() and out() functions need to do their job. + + On return, inflateBack() will set strm->next_in and strm->avail_in to + pass back any unused input that was provided by the last in() call. The + return values of inflateBack() can be Z_STREAM_END on success, Z_BUF_ERROR + if in() or out() returned an error, Z_DATA_ERROR if there was a format error + in the deflate stream (in which case strm->msg is set to indicate the nature + of the error), or Z_STREAM_ERROR if the stream was not properly initialized. + In the case of Z_BUF_ERROR, an input or output error can be distinguished + using strm->next_in which will be Z_NULL only if in() returned an error. If + strm->next_in is not Z_NULL, then the Z_BUF_ERROR was due to out() returning + non-zero. (in() will always be called before out(), so strm->next_in is + assured to be defined if out() returns non-zero.) Note that inflateBack() + cannot return Z_OK. +*/ + +ZEXTERN int ZEXPORT inflateBackEnd(z_streamp strm); +/* + All memory allocated by inflateBackInit() is freed. + + inflateBackEnd() returns Z_OK on success, or Z_STREAM_ERROR if the stream + state was inconsistent. +*/ + +ZEXTERN uLong ZEXPORT zlibCompileFlags(void); +/* Return flags indicating compile-time options. + + Type sizes, two bits each, 00 = 16 bits, 01 = 32, 10 = 64, 11 = other: + 1.0: size of uInt + 3.2: size of uLong + 5.4: size of voidpf (pointer) + 7.6: size of z_off_t + + Compiler, assembler, and debug options: + 8: ZLIB_DEBUG + 9: ASMV or ASMINF -- use ASM code + 10: ZLIB_WINAPI -- exported functions use the WINAPI calling convention + 11: 0 (reserved) + + One-time table building (smaller code, but not thread-safe if true): + 12: BUILDFIXED -- build static block decoding tables when needed + 13: DYNAMIC_CRC_TABLE -- build CRC calculation tables when needed + 14,15: 0 (reserved) + + Library content (indicates missing functionality): + 16: NO_GZCOMPRESS -- gz* functions cannot compress (to avoid linking + deflate code when not needed) + 17: NO_GZIP -- deflate can't write gzip streams, and inflate can't detect + and decode gzip streams (to avoid linking crc code) + 18-19: 0 (reserved) + + Operation variations (changes in library functionality): + 20: PKZIP_BUG_WORKAROUND -- slightly more permissive inflate + 21: FASTEST -- deflate algorithm with only one, lowest compression level + 22,23: 0 (reserved) + + The sprintf variant used by gzprintf (all zeros is best): + 24: 0 = vs*, 1 = s* -- 1 means limited to 20 arguments after the format + 25: 0 = *nprintf, 1 = *printf -- 1 means gzprintf() is not secure! + 26: 0 = returns value, 1 = void -- 1 means inferred string length returned + 27: 0 = gzprintf() present, 1 = not -- 1 means gzprintf() returns an error + + Remainder: + 28-31: 0 (reserved) + */ + +#ifndef Z_SOLO + + /* utility functions */ + +/* + The following utility functions are implemented on top of the basic + stream-oriented functions. To simplify the interface, some default options + are assumed (compression level and memory usage, standard memory allocation + functions). The source code of these utility functions can be modified if + you need special options. The _z versions of the functions use the size_t + type for lengths. Note that a long is 32 bits on Windows. +*/ + +ZEXTERN int ZEXPORT compress(Bytef *dest, uLongf *destLen, + const Bytef *source, uLong sourceLen); +ZEXTERN int ZEXPORT compress_z(Bytef *dest, z_size_t *destLen, + const Bytef *source, z_size_t sourceLen); +/* + Compresses the source buffer into the destination buffer. sourceLen is + the byte length of the source buffer. Upon entry, destLen is the total size + of the destination buffer, which must be at least the value returned by + compressBound(sourceLen). Upon exit, destLen is the actual size of the + compressed data. compress() is equivalent to compress2() with a level + parameter of Z_DEFAULT_COMPRESSION. + + compress returns Z_OK if success, Z_MEM_ERROR if there was not + enough memory, Z_BUF_ERROR if there was not enough room in the output + buffer. +*/ + +ZEXTERN int ZEXPORT compress2(Bytef *dest, uLongf *destLen, + const Bytef *source, uLong sourceLen, + int level); +ZEXTERN int ZEXPORT compress2_z(Bytef *dest, z_size_t *destLen, + const Bytef *source, z_size_t sourceLen, + int level); +/* + Compresses the source buffer into the destination buffer. The level + parameter has the same meaning as in deflateInit. sourceLen is the byte + length of the source buffer. Upon entry, destLen is the total size of the + destination buffer, which must be at least the value returned by + compressBound(sourceLen). Upon exit, destLen is the actual size of the + compressed data. + + compress2 returns Z_OK if success, Z_MEM_ERROR if there was not enough + memory, Z_BUF_ERROR if there was not enough room in the output buffer, + Z_STREAM_ERROR if the level parameter is invalid. +*/ + +ZEXTERN uLong ZEXPORT compressBound(uLong sourceLen); +ZEXTERN z_size_t ZEXPORT compressBound_z(z_size_t sourceLen); +/* + compressBound() returns an upper bound on the compressed size after + compress() or compress2() on sourceLen bytes. It would be used before a + compress() or compress2() call to allocate the destination buffer. +*/ + +ZEXTERN int ZEXPORT uncompress(Bytef *dest, uLongf *destLen, + const Bytef *source, uLong sourceLen); +ZEXTERN int ZEXPORT uncompress_z(Bytef *dest, z_size_t *destLen, + const Bytef *source, z_size_t sourceLen); +/* + Decompresses the source buffer into the destination buffer. sourceLen is + the byte length of the source buffer. On entry, *destLen is the total size + of the destination buffer, which must be large enough to hold the entire + uncompressed data. (The size of the uncompressed data must have been saved + previously by the compressor and transmitted to the decompressor by some + mechanism outside the scope of this compression library.) On exit, *destLen + is the actual size of the uncompressed data. + + uncompress returns Z_OK if success, Z_MEM_ERROR if there was not + enough memory, Z_BUF_ERROR if there was not enough room in the output + buffer, or Z_DATA_ERROR if the input data was corrupted or incomplete. In + the case where there is not enough room, uncompress() will fill the output + buffer with the uncompressed data up to that point. +*/ + +ZEXTERN int ZEXPORT uncompress2(Bytef *dest, uLongf *destLen, + const Bytef *source, uLong *sourceLen); +ZEXTERN int ZEXPORT uncompress2_z(Bytef *dest, z_size_t *destLen, + const Bytef *source, z_size_t *sourceLen); +/* + Same as uncompress, except that sourceLen is a pointer, where the + length of the source is *sourceLen. On return, *sourceLen is the number of + source bytes consumed. +*/ + + /* gzip file access functions */ + +/* + This library supports reading and writing files in gzip (.gz) format with + an interface similar to that of stdio, using the functions that start with + "gz". The gzip format is different from the zlib format. gzip is a gzip + wrapper, documented in RFC 1952, wrapped around a deflate stream. +*/ + +typedef struct gzFile_s *gzFile; /* semi-opaque gzip file descriptor */ + +/* +ZEXTERN gzFile ZEXPORT gzopen(const char *path, const char *mode); + + Open the gzip (.gz) file at path for reading and decompressing, or + compressing and writing. The mode parameter is as in fopen ("rb" or "wb") + but can also include a compression level ("wb9") or a strategy: 'f' for + filtered data as in "wb6f", 'h' for Huffman-only compression as in "wb1h", + 'R' for run-length encoding as in "wb1R", or 'F' for fixed code compression + as in "wb9F". (See the description of deflateInit2 for more information + about the strategy parameter.) 'T' will request transparent writing or + appending with no compression and not using the gzip format. 'T' cannot be + used to force transparent reading. Transparent reading is automatically + performed if there is no gzip header at the start. Transparent reading can + be disabled with the 'G' option, which will instead return an error if there + is no gzip header. 'N' will open the file in non-blocking mode. + + 'a' can be used instead of 'w' to request that the gzip stream that will + be written be appended to the file. '+' will result in an error, since + reading and writing to the same gzip file is not supported. The addition of + 'x' when writing will create the file exclusively, which fails if the file + already exists. On systems that support it, the addition of 'e' when + reading or writing will set the flag to close the file on an execve() call. + + These functions, as well as gzip, will read and decode a sequence of gzip + streams in a file. The append function of gzopen() can be used to create + such a file. (Also see gzflush() for another way to do this.) When + appending, gzopen does not test whether the file begins with a gzip stream, + nor does it look for the end of the gzip streams to begin appending. gzopen + will simply append a gzip stream to the existing file. + + gzopen can be used to read a file which is not in gzip format; in this + case gzread will directly read from the file without decompression. When + reading, this will be detected automatically by looking for the magic two- + byte gzip header. + + gzopen returns NULL if the file could not be opened, if there was + insufficient memory to allocate the gzFile state, or if an invalid mode was + specified (an 'r', 'w', or 'a' was not provided, or '+' was provided). + errno can be checked to determine if the reason gzopen failed was that the + file could not be opened. Note that if 'N' is in mode for non-blocking, the + open() itself can fail in order to not block. In that case gzopen() will + return NULL and errno will be EAGAIN or ENONBLOCK. The call to gzopen() can + then be re-tried. If the application would like to block on opening the + file, then it can use open() without O_NONBLOCK, and then gzdopen() with the + resulting file descriptor and 'N' in the mode, which will set it to non- + blocking. +*/ + +ZEXTERN gzFile ZEXPORT gzdopen(int fd, const char *mode); +/* + Associate a gzFile with the file descriptor fd. File descriptors are + obtained from calls like open, dup, creat, pipe or fileno (if the file has + been previously opened with fopen). The mode parameter is as in gzopen. An + 'e' in mode will set fd's flag to close the file on an execve() call. An 'N' + in mode will set fd's non-blocking flag. + + The next call of gzclose on the returned gzFile will also close the file + descriptor fd, just like fclose(fdopen(fd, mode)) closes the file descriptor + fd. If you want to keep fd open, use fd = dup(fd_keep); gz = gzdopen(fd, + mode);. The duplicated descriptor should be saved to avoid a leak, since + gzdopen does not close fd if it fails. If you are using fileno() to get the + file descriptor from a FILE *, then you will have to use dup() to avoid + double-close()ing the file descriptor. Both gzclose() and fclose() will + close the associated file descriptor, so they need to have different file + descriptors. + + gzdopen returns NULL if there was insufficient memory to allocate the + gzFile state, if an invalid mode was specified (an 'r', 'w', or 'a' was not + provided, or '+' was provided), or if fd is -1. The file descriptor is not + used until the next gz* read, write, seek, or close operation, so gzdopen + will not detect if fd is invalid (unless fd is -1). +*/ + +ZEXTERN int ZEXPORT gzbuffer(gzFile file, unsigned size); +/* + Set the internal buffer size used by this library's functions for file to + size. The default buffer size is 8192 bytes. This function must be called + after gzopen() or gzdopen(), and before any other calls that read or write + the file. The buffer memory allocation is always deferred to the first read + or write. Three times that size in buffer space is allocated. A larger + buffer size of, for example, 64K or 128K bytes will noticeably increase the + speed of decompression (reading). + + The new buffer size also affects the maximum length for gzprintf(). + + gzbuffer() returns 0 on success, or -1 on failure, such as being called + too late. +*/ + +ZEXTERN int ZEXPORT gzsetparams(gzFile file, int level, int strategy); +/* + Dynamically update the compression level and strategy for file. See the + description of deflateInit2 for the meaning of these parameters. Previously + provided data is flushed before applying the parameter changes. + + gzsetparams returns Z_OK if success, Z_STREAM_ERROR if the file was not + opened for writing, Z_ERRNO if there is an error writing the flushed data, + or Z_MEM_ERROR if there is a memory allocation error. +*/ + +ZEXTERN int ZEXPORT gzread(gzFile file, voidp buf, unsigned len); +/* + Read and decompress up to len uncompressed bytes from file into buf. If + the input file is not in gzip format, gzread copies the given number of + bytes into the buffer directly from the file. + + After reaching the end of a gzip stream in the input, gzread will continue + to read, looking for another gzip stream. Any number of gzip streams may be + concatenated in the input file, and will all be decompressed by gzread(). + If something other than a gzip stream is encountered after a gzip stream, + that remaining trailing garbage is ignored (and no error is returned). + + gzread can be used to read a gzip file that is being concurrently written. + Upon reaching the end of the input, gzread will return with the available + data. If the error code returned by gzerror is Z_OK or Z_BUF_ERROR, then + gzclearerr can be used to clear the end of file indicator in order to permit + gzread to be tried again. Z_OK indicates that a gzip stream was completed + on the last gzread. Z_BUF_ERROR indicates that the input file ended in the + middle of a gzip stream. Note that gzread does not return -1 in the event + of an incomplete gzip stream. This error is deferred until gzclose(), which + will return Z_BUF_ERROR if the last gzread ended in the middle of a gzip + stream. Alternatively, gzerror can be used before gzclose to detect this + case. + + gzread can be used to read a gzip file on a non-blocking device. If the + input stalls and there is no uncompressed data to return, then gzread() will + return -1, and errno will be EAGAIN or EWOULDBLOCK. gzread() can then be + called again. + + gzread returns the number of uncompressed bytes actually read, less than + len for end of file, or -1 for error. If len is too large to fit in an int, + then nothing is read, -1 is returned, and the error state is set to + Z_STREAM_ERROR. If some data was read before an error, then that data is + returned until exhausted, after which the next call will signal the error. +*/ + +ZEXTERN z_size_t ZEXPORT gzfread(voidp buf, z_size_t size, z_size_t nitems, + gzFile file); +/* + Read and decompress up to nitems items of size size from file into buf, + otherwise operating as gzread() does. This duplicates the interface of + stdio's fread(), with size_t request and return types. If the library + defines size_t, then z_size_t is identical to size_t. If not, then z_size_t + is an unsigned integer type that can contain a pointer. + + gzfread() returns the number of full items read of size size, or zero if + the end of the file was reached and a full item could not be read, or if + there was an error. gzerror() must be consulted if zero is returned in + order to determine if there was an error. If the multiplication of size and + nitems overflows, i.e. the product does not fit in a z_size_t, then nothing + is read, zero is returned, and the error state is set to Z_STREAM_ERROR. + + In the event that the end of file is reached and only a partial item is + available at the end, i.e. the remaining uncompressed data length is not a + multiple of size, then the final partial item is nevertheless read into buf + and the end-of-file flag is set. The length of the partial item read is not + provided, but could be inferred from the result of gztell(). This behavior + is the same as that of fread() implementations in common libraries. This + could result in data loss if used with size != 1 when reading a concurrently + written file or a non-blocking file. In that case, use size == 1 or gzread() + instead. +*/ + +ZEXTERN int ZEXPORT gzwrite(gzFile file, voidpc buf, unsigned len); +/* + Compress and write the len uncompressed bytes at buf to file. gzwrite + returns the number of uncompressed bytes written, or 0 in case of error or + if len is 0. If the write destination is non-blocking, then gzwrite() may + return a number of bytes written that is not 0 and less than len. + + If len does not fit in an int, then 0 is returned and nothing is written. +*/ + +ZEXTERN z_size_t ZEXPORT gzfwrite(voidpc buf, z_size_t size, + z_size_t nitems, gzFile file); +/* + Compress and write nitems items of size size from buf to file, duplicating + the interface of stdio's fwrite(), with size_t request and return types. If + the library defines size_t, then z_size_t is identical to size_t. If not, + then z_size_t is an unsigned integer type that can contain a pointer. + + gzfwrite() returns the number of full items written of size size, or zero + if there was an error. If the multiplication of size and nitems overflows, + i.e. the product does not fit in a z_size_t, then nothing is written, zero + is returned, and the error state is set to Z_STREAM_ERROR. + + If writing a concurrently read file or a non-blocking file with size != 1, + a partial item could be written, with no way of knowing how much of it was + not written, resulting in data loss. In that case, use size == 1 or + gzwrite() instead. +*/ + +#if defined(STDC) || defined(Z_HAVE_STDARG_H) +ZEXTERN int ZEXPORTVA gzprintf(gzFile file, const char *format, ...); +#else +ZEXTERN int ZEXPORTVA gzprintf(); +#endif +/* + Convert, format, compress, and write the arguments (...) to file under + control of the string format, as in fprintf. gzprintf returns the number of + uncompressed bytes actually written, or a negative zlib error code in case + of error. The number of uncompressed bytes written is limited to 8191, or + one less than the buffer size given to gzbuffer(). The caller should assure + that this limit is not exceeded. If it is exceeded, then gzprintf() will + return an error (0) with nothing written. + + In that last case, there may also be a buffer overflow with unpredictable + consequences, which is possible only if zlib was compiled with the insecure + functions sprintf() or vsprintf(), because the secure snprintf() and + vsnprintf() functions were not available. That would only be the case for + a non-ANSI C compiler. zlib may have been built without gzprintf() because + secure functions were not available and having gzprintf() be insecure was + not an option, in which case, gzprintf() returns Z_STREAM_ERROR. All of + these possibilities can be determined using zlibCompileFlags(). + + If a Z_BUF_ERROR is returned, then nothing was written due to a stall on + the non-blocking write destination. +*/ + +ZEXTERN int ZEXPORT gzputs(gzFile file, const char *s); +/* + Compress and write the given null-terminated string s to file, excluding + the terminating null character. + + gzputs returns the number of characters written, or -1 in case of error. + The number of characters written may be less than the length of the string + if the write destination is non-blocking. + + If the length of the string does not fit in an int, then -1 is returned + and nothing is written. +*/ + +ZEXTERN char * ZEXPORT gzgets(gzFile file, char *buf, int len); +/* + Read and decompress bytes from file into buf, until len-1 characters are + read, or until a newline character is read and transferred to buf, or an + end-of-file condition is encountered. If any characters are read or if len + is one, the string is terminated with a null character. If no characters + are read due to an end-of-file or len is less than one, then the buffer is + left untouched. + + gzgets returns buf which is a null-terminated string, or it returns NULL + for end-of-file or in case of error. If some data was read before an error, + then that data is returned until exhausted, after which the next call will + return NULL to signal the error. + + gzgets can be used on a file being concurrently written, and on a non- + blocking device, both as for gzread(). However lines may be broken in the + middle, leaving it up to the application to reassemble them as needed. +*/ + +ZEXTERN int ZEXPORT gzputc(gzFile file, int c); +/* + Compress and write c, converted to an unsigned char, into file. gzputc + returns the value that was written, or -1 in case of error. +*/ + +ZEXTERN int ZEXPORT gzgetc(gzFile file); +/* + Read and decompress one byte from file. gzgetc returns this byte or -1 in + case of end of file or error. If some data was read before an error, then + that data is returned until exhausted, after which the next call will return + -1 to signal the error. + + This is implemented as a macro for speed. As such, it does not do all of + the checking the other functions do. I.e. it does not check to see if file + is NULL, nor whether the structure file points to has been clobbered or not. + + gzgetc can be used to read a gzip file on a non-blocking device. If the + input stalls and there is no uncompressed data to return, then gzgetc() will + return -1, and errno will be EAGAIN or EWOULDBLOCK. gzread() can then be + called again. +*/ + +ZEXTERN int ZEXPORT gzungetc(int c, gzFile file); +/* + Push c back onto the stream for file to be read as the first character on + the next read. At least one character of push-back is always allowed. + gzungetc() returns the character pushed, or -1 on failure. gzungetc() will + fail if c is -1, and may fail if a character has been pushed but not read + yet. If gzungetc is used immediately after gzopen or gzdopen, at least the + output buffer size of pushed characters is allowed. (See gzbuffer above.) + The pushed character will be discarded if the stream is repositioned with + gzseek() or gzrewind(). + + gzungetc(-1, file) will force any pending seek to execute. Then gztell() + will report the position, even if the requested seek reached end of file. + This can be used to determine the number of uncompressed bytes in a gzip + file without having to read it into a buffer. +*/ + +ZEXTERN int ZEXPORT gzflush(gzFile file, int flush); +/* + Flush all pending output to file. The parameter flush is as in the + deflate() function. The return value is the zlib error number (see function + gzerror below). gzflush is only permitted when writing. + + If the flush parameter is Z_FINISH, the remaining data is written and the + gzip stream is completed in the output. If gzwrite() is called again, a new + gzip stream will be started in the output. gzread() is able to read such + concatenated gzip streams. + + gzflush should be called only when strictly necessary because it will + degrade compression if called too often. +*/ + +/* +ZEXTERN z_off_t ZEXPORT gzseek(gzFile file, + z_off_t offset, int whence); + + Set the starting position to offset relative to whence for the next gzread + or gzwrite on file. The offset represents a number of bytes in the + uncompressed data stream. The whence parameter is defined as in lseek(2); + the value SEEK_END is not supported. + + If the file is opened for reading, this function is emulated but can be + extremely slow. If the file is opened for writing, only forward seeks are + supported; gzseek then compresses a sequence of zeroes up to the new + starting position. For reading or writing, any actual seeking is deferred + until the next read or write operation, or close operation when writing. + + gzseek returns the resulting offset location as measured in bytes from + the beginning of the uncompressed stream, or -1 in case of error, in + particular if the file is opened for writing and the new starting position + would be before the current position. +*/ + +ZEXTERN int ZEXPORT gzrewind(gzFile file); +/* + Rewind file. This function is supported only for reading. + + gzrewind(file) is equivalent to (int)gzseek(file, 0L, SEEK_SET). +*/ + +/* +ZEXTERN z_off_t ZEXPORT gztell(gzFile file); + + Return the starting position for the next gzread or gzwrite on file. + This position represents a number of bytes in the uncompressed data stream, + and is zero when starting, even if appending or reading a gzip stream from + the middle of a file using gzdopen(). + + gztell(file) is equivalent to gzseek(file, 0L, SEEK_CUR) +*/ + +/* +ZEXTERN z_off_t ZEXPORT gzoffset(gzFile file); + + Return the current compressed (actual) read or write offset of file. This + offset includes the count of bytes that precede the gzip stream, for example + when appending or when using gzdopen() for reading. When reading, the + offset does not include as yet unused buffered input. This information can + be used for a progress indicator. On error, gzoffset() returns -1. +*/ + +ZEXTERN int ZEXPORT gzeof(gzFile file); +/* + Return true (1) if the end-of-file indicator for file has been set while + reading, false (0) otherwise. Note that the end-of-file indicator is set + only if the read tried to go past the end of the input, but came up short. + Therefore, just like feof(), gzeof() may return false even if there is no + more data to read, in the event that the last read request was for the exact + number of bytes remaining in the input file. This will happen if the input + file size is an exact multiple of the buffer size. + + If gzeof() returns true, then the read functions will return no more data, + unless the end-of-file indicator is reset by gzclearerr() and the input file + has grown since the previous end of file was detected. +*/ + +ZEXTERN int ZEXPORT gzdirect(gzFile file); +/* + Return true (1) if file is being copied directly while reading, or false + (0) if file is a gzip stream being decompressed. + + If the input file is empty, gzdirect() will return true, since the input + does not contain a gzip stream. + + If gzdirect() is used immediately after gzopen() or gzdopen() it will + cause buffers to be allocated to allow reading the file to determine if it + is a gzip file. Therefore if gzbuffer() is used, it should be called before + gzdirect(). If the input is being written concurrently or the device is non- + blocking, then gzdirect() may give a different answer once four bytes of + input have been accumulated, which is what is needed to confirm or deny a + gzip header. Before this, gzdirect() will return true (1). + + When writing, gzdirect() returns true (1) if transparent writing was + requested ("wT" for the gzopen() mode), or false (0) otherwise. (Note: + gzdirect() is not needed when writing. Transparent writing must be + explicitly requested, so the application already knows the answer. When + linking statically, using gzdirect() will include all of the zlib code for + gzip file reading and decompression, which may not be desired.) +*/ + +ZEXTERN int ZEXPORT gzclose(gzFile file); +/* + Flush all pending output for file, if necessary, close file and + deallocate the (de)compression state. Note that once file is closed, you + cannot call gzerror with file, since its structures have been deallocated. + gzclose must not be called more than once on the same file, just as free + must not be called more than once on the same allocation. + + gzclose will return Z_STREAM_ERROR if file is not valid, Z_ERRNO on a + file operation error, Z_MEM_ERROR if out of memory, Z_BUF_ERROR if the + last read ended in the middle of a gzip stream, or Z_OK on success. +*/ + +ZEXTERN int ZEXPORT gzclose_r(gzFile file); +ZEXTERN int ZEXPORT gzclose_w(gzFile file); +/* + Same as gzclose(), but gzclose_r() is only for use when reading, and + gzclose_w() is only for use when writing or appending. The advantage to + using these instead of gzclose() is that they avoid linking in zlib + compression or decompression code that is not used when only reading or only + writing respectively. If gzclose() is used, then both compression and + decompression code will be included the application when linking to a static + zlib library. +*/ + +ZEXTERN const char * ZEXPORT gzerror(gzFile file, int *errnum); +/* + Return the error message for the last error which occurred on file. + If errnum is not NULL, *errnum is set to zlib error number. If an error + occurred in the file system and not in the compression library, *errnum is + set to Z_ERRNO and the application may consult errno to get the exact error + code. + + The application must not modify the returned string. Future calls to + this function may invalidate the previously returned string. If file is + closed, then the string previously returned by gzerror will no longer be + available. + + gzerror() should be used to distinguish errors from end-of-file for those + functions above that do not distinguish those cases in their return values. +*/ + +ZEXTERN void ZEXPORT gzclearerr(gzFile file); +/* + Clear the error and end-of-file flags for file. This is analogous to the + clearerr() function in stdio. This is useful for continuing to read a gzip + file that is being written concurrently. +*/ + +#endif /* !Z_SOLO */ + + /* checksum functions */ + +/* + These functions are not related to compression but are exported + anyway because they might be useful in applications using the compression + library. +*/ + +ZEXTERN uLong ZEXPORT adler32(uLong adler, const Bytef *buf, uInt len); +/* + Update a running Adler-32 checksum with the bytes buf[0..len-1] and + return the updated checksum. An Adler-32 value is in the range of a 32-bit + unsigned integer. If buf is Z_NULL, this function returns the required + initial value for the checksum. + + An Adler-32 checksum is almost as reliable as a CRC-32 but can be computed + much faster. + + Usage example: + + uLong adler = adler32(0L, Z_NULL, 0); + + while (read_buffer(buffer, length) != EOF) { + adler = adler32(adler, buffer, length); + } + if (adler != original_adler) error(); +*/ + +ZEXTERN uLong ZEXPORT adler32_z(uLong adler, const Bytef *buf, + z_size_t len); +/* + Same as adler32(), but with a size_t length. Note that a long is 32 bits + on Windows. +*/ + +/* +ZEXTERN uLong ZEXPORT adler32_combine(uLong adler1, uLong adler2, + z_off_t len2); + + Combine two Adler-32 checksums into one. For two sequences of bytes, seq1 + and seq2 with lengths len1 and len2, Adler-32 checksums were calculated for + each, adler1 and adler2. adler32_combine() returns the Adler-32 checksum of + seq1 and seq2 concatenated, requiring only adler1, adler2, and len2. Note + that the z_off_t type (like off_t) is a signed integer. If len2 is + negative, the result has no meaning or utility. +*/ + +ZEXTERN uLong ZEXPORT crc32(uLong crc, const Bytef *buf, uInt len); +/* + Update a running CRC-32 with the bytes buf[0..len-1] and return the + updated CRC-32. A CRC-32 value is in the range of a 32-bit unsigned integer. + If buf is Z_NULL, this function returns the required initial value for the + crc. Pre- and post-conditioning (one's complement) is performed within this + function so it shouldn't be done by the application. + + Usage example: + + uLong crc = crc32(0L, Z_NULL, 0); + + while (read_buffer(buffer, length) != EOF) { + crc = crc32(crc, buffer, length); + } + if (crc != original_crc) error(); +*/ + +ZEXTERN uLong ZEXPORT crc32_z(uLong crc, const Bytef *buf, + z_size_t len); +/* + Same as crc32(), but with a size_t length. Note that a long is 32 bits on + Windows. +*/ + +/* +ZEXTERN uLong ZEXPORT crc32_combine(uLong crc1, uLong crc2, z_off_t len2); + + Combine two CRC-32 check values into one. For two sequences of bytes, + seq1 and seq2 with lengths len1 and len2, CRC-32 check values were + calculated for each, crc1 and crc2. crc32_combine() returns the CRC-32 + check value of seq1 and seq2 concatenated, requiring only crc1, crc2, and + len2. len2 must be non-negative, otherwise zero is returned. +*/ + +/* +ZEXTERN uLong ZEXPORT crc32_combine_gen(z_off_t len2); + + Return the operator corresponding to length len2, to be used with + crc32_combine_op(). len2 must be non-negative, otherwise zero is returned. +*/ + +ZEXTERN uLong ZEXPORT crc32_combine_op(uLong crc1, uLong crc2, uLong op); +/* + Give the same result as crc32_combine(), using op in place of len2. op is + is generated from len2 by crc32_combine_gen(). This will be faster than + crc32_combine() if the generated op is used more than once. +*/ + + + /* various hacks, don't look :) */ + +/* deflateInit and inflateInit are macros to allow checking the zlib version + * and the compiler's view of z_stream: + */ +ZEXTERN int ZEXPORT deflateInit_(z_streamp strm, int level, + const char *version, int stream_size); +ZEXTERN int ZEXPORT inflateInit_(z_streamp strm, + const char *version, int stream_size); +ZEXTERN int ZEXPORT deflateInit2_(z_streamp strm, int level, int method, + int windowBits, int memLevel, + int strategy, const char *version, + int stream_size); +ZEXTERN int ZEXPORT inflateInit2_(z_streamp strm, int windowBits, + const char *version, int stream_size); +ZEXTERN int ZEXPORT inflateBackInit_(z_streamp strm, int windowBits, + unsigned char FAR *window, + const char *version, + int stream_size); +#ifdef Z_PREFIX_SET +# define z_deflateInit(strm, level) \ + deflateInit_((strm), (level), ZLIB_VERSION, (int)sizeof(z_stream)) +# define z_inflateInit(strm) \ + inflateInit_((strm), ZLIB_VERSION, (int)sizeof(z_stream)) +# define z_deflateInit2(strm, level, method, windowBits, memLevel, strategy) \ + deflateInit2_((strm),(level),(method),(windowBits),(memLevel),\ + (strategy), ZLIB_VERSION, (int)sizeof(z_stream)) +# define z_inflateInit2(strm, windowBits) \ + inflateInit2_((strm), (windowBits), ZLIB_VERSION, \ + (int)sizeof(z_stream)) +# define z_inflateBackInit(strm, windowBits, window) \ + inflateBackInit_((strm), (windowBits), (window), \ + ZLIB_VERSION, (int)sizeof(z_stream)) +#else +# define deflateInit(strm, level) \ + deflateInit_((strm), (level), ZLIB_VERSION, (int)sizeof(z_stream)) +# define inflateInit(strm) \ + inflateInit_((strm), ZLIB_VERSION, (int)sizeof(z_stream)) +# define deflateInit2(strm, level, method, windowBits, memLevel, strategy) \ + deflateInit2_((strm),(level),(method),(windowBits),(memLevel),\ + (strategy), ZLIB_VERSION, (int)sizeof(z_stream)) +# define inflateInit2(strm, windowBits) \ + inflateInit2_((strm), (windowBits), ZLIB_VERSION, \ + (int)sizeof(z_stream)) +# define inflateBackInit(strm, windowBits, window) \ + inflateBackInit_((strm), (windowBits), (window), \ + ZLIB_VERSION, (int)sizeof(z_stream)) +#endif + +#ifndef Z_SOLO + +/* gzgetc() macro and its supporting function and exposed data structure. Note + * that the real internal state is much larger than the exposed structure. + * This abbreviated structure exposes just enough for the gzgetc() macro. The + * user should not mess with these exposed elements, since their names or + * behavior could change in the future, perhaps even capriciously. They can + * only be used by the gzgetc() macro. You have been warned. + */ +struct gzFile_s { + unsigned have; + unsigned char *next; + z_off64_t pos; +}; +ZEXTERN int ZEXPORT gzgetc_(gzFile file); /* backward compatibility */ +#ifdef Z_PREFIX_SET +# undef z_gzgetc +# define z_gzgetc(g) \ + ((g)->have ? ((g)->have--, (g)->pos++, *((g)->next)++) : (gzgetc)(g)) +#else +# define gzgetc(g) \ + ((g)->have ? ((g)->have--, (g)->pos++, *((g)->next)++) : (gzgetc)(g)) +#endif + +/* provide 64-bit offset functions if _LARGEFILE64_SOURCE defined, and/or + * change the regular functions to 64 bits if _FILE_OFFSET_BITS is 64 (if + * both are true, the application gets the *64 functions, and the regular + * functions are changed to 64 bits) -- in case these are set on systems + * without large file support, _LFS64_LARGEFILE must also be true + */ +#ifdef Z_LARGE64 + ZEXTERN gzFile ZEXPORT gzopen64(const char *, const char *); + ZEXTERN z_off64_t ZEXPORT gzseek64(gzFile, z_off64_t, int); + ZEXTERN z_off64_t ZEXPORT gztell64(gzFile); + ZEXTERN z_off64_t ZEXPORT gzoffset64(gzFile); + ZEXTERN uLong ZEXPORT adler32_combine64(uLong, uLong, z_off64_t); + ZEXTERN uLong ZEXPORT crc32_combine64(uLong, uLong, z_off64_t); + ZEXTERN uLong ZEXPORT crc32_combine_gen64(z_off64_t); +#endif + +#if !defined(ZLIB_INTERNAL) && defined(Z_WANT64) +# ifdef Z_PREFIX_SET +# define z_gzopen z_gzopen64 +# define z_gzseek z_gzseek64 +# define z_gztell z_gztell64 +# define z_gzoffset z_gzoffset64 +# define z_adler32_combine z_adler32_combine64 +# define z_crc32_combine z_crc32_combine64 +# define z_crc32_combine_gen z_crc32_combine_gen64 +# else +# define gzopen gzopen64 +# define gzseek gzseek64 +# define gztell gztell64 +# define gzoffset gzoffset64 +# define adler32_combine adler32_combine64 +# define crc32_combine crc32_combine64 +# define crc32_combine_gen crc32_combine_gen64 +# endif +# ifndef Z_LARGE64 + ZEXTERN gzFile ZEXPORT gzopen64(const char *, const char *); + ZEXTERN z_off_t ZEXPORT gzseek64(gzFile, z_off_t, int); + ZEXTERN z_off_t ZEXPORT gztell64(gzFile); + ZEXTERN z_off_t ZEXPORT gzoffset64(gzFile); + ZEXTERN uLong ZEXPORT adler32_combine64(uLong, uLong, z_off64_t); + ZEXTERN uLong ZEXPORT crc32_combine64(uLong, uLong, z_off64_t); + ZEXTERN uLong ZEXPORT crc32_combine_gen64(z_off64_t); +# endif +#else + ZEXTERN gzFile ZEXPORT gzopen(const char *, const char *); + ZEXTERN z_off_t ZEXPORT gzseek(gzFile, z_off_t, int); + ZEXTERN z_off_t ZEXPORT gztell(gzFile); + ZEXTERN z_off_t ZEXPORT gzoffset(gzFile); + ZEXTERN uLong ZEXPORT adler32_combine(uLong, uLong, z_off_t); + ZEXTERN uLong ZEXPORT crc32_combine(uLong, uLong, z_off_t); + ZEXTERN uLong ZEXPORT crc32_combine_gen(z_off_t); +#endif + +#else /* Z_SOLO */ + + ZEXTERN uLong ZEXPORT adler32_combine(uLong, uLong, z_off_t); + ZEXTERN uLong ZEXPORT crc32_combine(uLong, uLong, z_off_t); + ZEXTERN uLong ZEXPORT crc32_combine_gen(z_off_t); + +#endif /* !Z_SOLO */ + +/* undocumented functions */ +ZEXTERN const char * ZEXPORT zError(int); +ZEXTERN int ZEXPORT inflateSyncPoint(z_streamp); +ZEXTERN const z_crc_t FAR * ZEXPORT get_crc_table(void); +ZEXTERN int ZEXPORT inflateUndermine(z_streamp, int); +ZEXTERN int ZEXPORT inflateValidate(z_streamp, int); +ZEXTERN unsigned long ZEXPORT inflateCodesUsed(z_streamp); +ZEXTERN int ZEXPORT inflateResetKeep(z_streamp); +ZEXTERN int ZEXPORT deflateResetKeep(z_streamp); +#if defined(_WIN32) && !defined(Z_SOLO) +ZEXTERN gzFile ZEXPORT gzopen_w(const wchar_t *path, + const char *mode); +#endif +#if defined(STDC) || defined(Z_HAVE_STDARG_H) +# ifndef Z_SOLO +ZEXTERN int ZEXPORTVA gzvprintf(gzFile file, + const char *format, + va_list va); +# endif +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* ZLIB_H */ diff --git a/Minecraft.Client/Windows64/4JLibs/libs/4J_Render_PC.lib b/Minecraft.Client/Windows64/4JLibs/libs/4J_Render_PC.lib index 38c566597d13f1df41618c4d06c5457778996218..e9f4995cf99e843876ea32c4d0872315a3074d12 100644 GIT binary patch literal 9223278 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
    K;Tj~U5evgNKsaQ?KmTBX-_)=N0S!?hn$`nKJ4 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

    f&`e!5Xa|ijT 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

      }P<%Y#je(k9|1yW2yF(eCa>-$eG2D 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

      Se+v<_W?qJV?e=1VE+DH6?! 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

      E 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.R>X4$n#fpf4UkH^;dO&x*8Joxw=1{ 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�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 z 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