initial commit
This commit is contained in:
72
CMakeLists.txt
Normal file
72
CMakeLists.txt
Normal file
@@ -0,0 +1,72 @@
|
||||
project(fpng_test)
|
||||
|
||||
cmake_minimum_required(VERSION 3.0)
|
||||
option(BUILD_X64 "build 64-bit" TRUE)
|
||||
|
||||
message("Initial BUILD_X64=${BUILD_X64}")
|
||||
|
||||
if( NOT CMAKE_BUILD_TYPE )
|
||||
set( CMAKE_BUILD_TYPE Release )
|
||||
endif()
|
||||
|
||||
message( ${PROJECT_NAME} " build type: " ${CMAKE_BUILD_TYPE} )
|
||||
|
||||
if (BUILD_X64)
|
||||
message("Building 64-bit")
|
||||
else()
|
||||
message("Building 32-bit")
|
||||
endif()
|
||||
|
||||
if (NOT MSVC)
|
||||
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -g")
|
||||
set(CMAKE_C_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -g")
|
||||
|
||||
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE}")
|
||||
set(CMAKE_C_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE}")
|
||||
|
||||
set(CMAKE_CXX_FLAGS -std=c++11)
|
||||
set(GCC_COMPILE_FLAGS "-fvisibility=hidden -fPIC -fno-strict-aliasing -D_LARGEFILE64_SOURCE=1 -D_FILE_OFFSET_BITS=64 -Wall -Wextra")
|
||||
|
||||
if (NOT BUILD_X64)
|
||||
set(GCC_COMPILE_FLAGS "${GCC_COMPILE_FLAGS} -m32")
|
||||
endif()
|
||||
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS}")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}")
|
||||
|
||||
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${GCC_LINK_FLAGS} -Wl,-rpath .")
|
||||
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${GCC_COMPILE_FLAGS}")
|
||||
set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} ${GCC_COMPILE_FLAGS}")
|
||||
set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} ${GCC_COMPILE_FLAGS} -D_DEBUG")
|
||||
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${GCC_COMPILE_FLAGS}")
|
||||
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} ${GCC_COMPILE_FLAGS}")
|
||||
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} ${GCC_COMPILE_FLAGS} -D_DEBUG")
|
||||
else()
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS}")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}")
|
||||
endif()
|
||||
|
||||
set(FPNG_SRC_LIST ${COMMON_SRC_LIST}
|
||||
fpng.cpp
|
||||
Crc32.cpp
|
||||
fpng_test.cpp
|
||||
lodepng.cpp
|
||||
)
|
||||
|
||||
if (APPLE)
|
||||
set(BIN_DIRECTORY "bin_osx")
|
||||
else()
|
||||
set(BIN_DIRECTORY "bin")
|
||||
endif()
|
||||
|
||||
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/${BIN_DIRECTORY})
|
||||
|
||||
add_executable(fpng_test ${FPNG_SRC_LIST})
|
||||
|
||||
if (NOT MSVC)
|
||||
target_link_libraries(fpng_test m pthread)
|
||||
endif()
|
||||
|
||||
install(TARGETS fpng_test DESTINATION bin)
|
69
Crc32.h
Normal file
69
Crc32.h
Normal file
@@ -0,0 +1,69 @@
|
||||
// //////////////////////////////////////////////////////////
|
||||
// Crc32.h
|
||||
// Copyright (c) 2011-2019 Stephan Brumme. All rights reserved.
|
||||
// Slicing-by-16 contributed by Bulat Ziganshin
|
||||
// Tableless bytewise CRC contributed by Hagai Gold
|
||||
// see http://create.stephan-brumme.com/disclaimer.html
|
||||
//
|
||||
|
||||
// if running on an embedded system, you might consider shrinking the
|
||||
// big Crc32Lookup table by undefining these lines:
|
||||
//#define CRC32_USE_LOOKUP_TABLE_BYTE
|
||||
//#define CRC32_USE_LOOKUP_TABLE_SLICING_BY_4
|
||||
#define CRC32_USE_LOOKUP_TABLE_SLICING_BY_8
|
||||
//#define CRC32_USE_LOOKUP_TABLE_SLICING_BY_16
|
||||
// - crc32_bitwise doesn't need it at all
|
||||
// - crc32_halfbyte has its own small lookup table
|
||||
// - crc32_1byte_tableless and crc32_1byte_tableless2 don't need it at all
|
||||
// - crc32_1byte needs only Crc32Lookup[0]
|
||||
// - crc32_4bytes needs only Crc32Lookup[0..3]
|
||||
// - crc32_8bytes needs only Crc32Lookup[0..7]
|
||||
// - crc32_4x8bytes needs only Crc32Lookup[0..7]
|
||||
// - crc32_16bytes needs all of Crc32Lookup
|
||||
// using the aforementioned #defines the table is automatically fitted to your needs
|
||||
|
||||
// uint8_t, uint32_t, int32_t
|
||||
#include <stdint.h>
|
||||
// size_t
|
||||
#include <cstddef>
|
||||
|
||||
// crc32_fast selects the fastest algorithm depending on flags (CRC32_USE_LOOKUP_...)
|
||||
/// compute CRC32 using the fastest algorithm for large datasets on modern CPUs
|
||||
uint32_t crc32_fast (const void* data, size_t length, uint32_t previousCrc32 = 0);
|
||||
|
||||
/// merge two CRC32 such that result = crc32(dataB, lengthB, crc32(dataA, lengthA))
|
||||
uint32_t crc32_combine (uint32_t crcA, uint32_t crcB, size_t lengthB);
|
||||
|
||||
/// compute CRC32 (bitwise algorithm)
|
||||
uint32_t crc32_bitwise (const void* data, size_t length, uint32_t previousCrc32 = 0);
|
||||
/// compute CRC32 (half-byte algoritm)
|
||||
uint32_t crc32_halfbyte(const void* data, size_t length, uint32_t previousCrc32 = 0);
|
||||
|
||||
#ifdef CRC32_USE_LOOKUP_TABLE_BYTE
|
||||
/// compute CRC32 (standard algorithm)
|
||||
uint32_t crc32_1byte (const void* data, size_t length, uint32_t previousCrc32 = 0);
|
||||
#endif
|
||||
|
||||
/// compute CRC32 (byte algorithm) without lookup tables
|
||||
uint32_t crc32_1byte_tableless (const void* data, size_t length, uint32_t previousCrc32 = 0);
|
||||
/// compute CRC32 (byte algorithm) without lookup tables
|
||||
uint32_t crc32_1byte_tableless2(const void* data, size_t length, uint32_t previousCrc32 = 0);
|
||||
|
||||
#ifdef CRC32_USE_LOOKUP_TABLE_SLICING_BY_4
|
||||
/// compute CRC32 (Slicing-by-4 algorithm)
|
||||
uint32_t crc32_4bytes (const void* data, size_t length, uint32_t previousCrc32 = 0);
|
||||
#endif
|
||||
|
||||
#ifdef CRC32_USE_LOOKUP_TABLE_SLICING_BY_8
|
||||
/// compute CRC32 (Slicing-by-8 algorithm)
|
||||
uint32_t crc32_8bytes (const void* data, size_t length, uint32_t previousCrc32 = 0);
|
||||
/// compute CRC32 (Slicing-by-8 algorithm), unroll inner loop 4 times
|
||||
uint32_t crc32_4x8bytes(const void* data, size_t length, uint32_t previousCrc32 = 0);
|
||||
#endif
|
||||
|
||||
#ifdef CRC32_USE_LOOKUP_TABLE_SLICING_BY_16
|
||||
/// compute CRC32 (Slicing-by-16 algorithm)
|
||||
uint32_t crc32_16bytes (const void* data, size_t length, uint32_t previousCrc32 = 0);
|
||||
/// compute CRC32 (Slicing-by-16 algorithm, prefetch upcoming data blocks)
|
||||
uint32_t crc32_16bytes_prefetch(const void* data, size_t length, uint32_t previousCrc32 = 0, size_t prefetchAhead = 256);
|
||||
#endif
|
16
fpng.h
Normal file
16
fpng.h
Normal file
@@ -0,0 +1,16 @@
|
||||
// fpng.h - R. Geldreich, Jr. - public domain or zlib license (see end of fpng.cpp for Public Domain waiver/optional license)
|
||||
#pragma once
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <vector>
|
||||
|
||||
namespace fpng
|
||||
{
|
||||
bool fpng_encode_image_to_memory(const void* pImage, int w, int h, int num_chans, bool flip, std::vector<uint8_t>& out_buf);
|
||||
|
||||
#ifndef FPNG_NO_STDIO
|
||||
bool fpng_encode_image_to_file(const char* pFilename, const void* pImage, int w, int h, int num_chans, bool flip);
|
||||
#endif
|
||||
|
||||
} // namespace fpng
|
31
fpng.sln
Normal file
31
fpng.sln
Normal file
@@ -0,0 +1,31 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio Version 16
|
||||
VisualStudioVersion = 16.0.31911.196
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "fpng", "fpng.vcxproj", "{33211B65-F564-4E23-A640-FA7F02007CB3}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|x64 = Debug|x64
|
||||
Debug|x86 = Debug|x86
|
||||
Release|x64 = Release|x64
|
||||
Release|x86 = Release|x86
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{33211B65-F564-4E23-A640-FA7F02007CB3}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{33211B65-F564-4E23-A640-FA7F02007CB3}.Debug|x64.Build.0 = Debug|x64
|
||||
{33211B65-F564-4E23-A640-FA7F02007CB3}.Debug|x86.ActiveCfg = Debug|Win32
|
||||
{33211B65-F564-4E23-A640-FA7F02007CB3}.Debug|x86.Build.0 = Debug|Win32
|
||||
{33211B65-F564-4E23-A640-FA7F02007CB3}.Release|x64.ActiveCfg = Release|x64
|
||||
{33211B65-F564-4E23-A640-FA7F02007CB3}.Release|x64.Build.0 = Release|x64
|
||||
{33211B65-F564-4E23-A640-FA7F02007CB3}.Release|x86.ActiveCfg = Release|Win32
|
||||
{33211B65-F564-4E23-A640-FA7F02007CB3}.Release|x86.Build.0 = Release|Win32
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {4225CF7B-633D-443D-A767-C66A2D22BCDA}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
162
fpng.vcxproj
Normal file
162
fpng.vcxproj
Normal file
@@ -0,0 +1,162 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup Label="ProjectConfigurations">
|
||||
<ProjectConfiguration Include="Debug|Win32">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|Win32">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Debug|x64">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|x64">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Globals">
|
||||
<VCProjectVersion>16.0</VCProjectVersion>
|
||||
<Keyword>Win32Proj</Keyword>
|
||||
<ProjectGuid>{33211b65-f564-4e23-a640-fa7f02007cb3}</ProjectGuid>
|
||||
<RootNamespace>fpng</RootNamespace>
|
||||
<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v142</PlatformToolset>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<PlatformToolset>v142</PlatformToolset>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v142</PlatformToolset>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<PlatformToolset>v142</PlatformToolset>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||
<ImportGroup Label="ExtensionSettings">
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="Shared">
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<PropertyGroup Label="UserMacros" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<LinkIncremental>true</LinkIncremental>
|
||||
<OutDir>bin</OutDir>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<LinkIncremental>false</LinkIncremental>
|
||||
<OutDir>bin</OutDir>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<LinkIncremental>true</LinkIncremental>
|
||||
<OutDir>.\bin</OutDir>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<LinkIncremental>false</LinkIncremental>
|
||||
<OutDir>bin</OutDir>
|
||||
</PropertyGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="Crc32.cpp" />
|
||||
<ClCompile Include="fpng.cpp" />
|
||||
<ClCompile Include="fpng_test.cpp" />
|
||||
<ClCompile Include="lodepng.cpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="Crc32.h" />
|
||||
<ClInclude Include="fpng.h" />
|
||||
<ClInclude Include="lodepng.h" />
|
||||
<ClInclude Include="qoi.h" />
|
||||
<ClInclude Include="stb_image.h" />
|
||||
<ClInclude Include="stb_image_write.h" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
</ImportGroup>
|
||||
</Project>
|
51
fpng.vcxproj.filters
Normal file
51
fpng.vcxproj.filters
Normal file
@@ -0,0 +1,51 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup>
|
||||
<Filter Include="Source Files">
|
||||
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
|
||||
<Extensions>cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
|
||||
</Filter>
|
||||
<Filter Include="Header Files">
|
||||
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
|
||||
<Extensions>h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd</Extensions>
|
||||
</Filter>
|
||||
<Filter Include="Resource Files">
|
||||
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
|
||||
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
|
||||
</Filter>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="fpng.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Crc32.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="fpng_test.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="lodepng.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="Crc32.h">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="fpng.h">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="qoi.h">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="stb_image.h">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="stb_image_write.h">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="lodepng.h">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
</Project>
|
532
fpng_test.cpp
Normal file
532
fpng_test.cpp
Normal file
@@ -0,0 +1,532 @@
|
||||
#define _CRT_SECURE_NO_WARNINGS
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
#if defined(_WIN32)
|
||||
// For QueryPerformanceCounter/QueryPerformanceFrequency
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <windows.h>
|
||||
#endif
|
||||
|
||||
#include "fpng.h"
|
||||
#include "lodepng.h"
|
||||
|
||||
#define STB_IMAGE_IMPLEMENTATION
|
||||
#include "stb_image.h"
|
||||
|
||||
#define STB_IMAGE_WRITE_IMPLEMENTATION
|
||||
#include "stb_image_write.h"
|
||||
|
||||
#define QOI_IMPLEMENTATION
|
||||
#include "qoi.h"
|
||||
|
||||
typedef std::vector<uint8_t> uint8_vec;
|
||||
|
||||
typedef uint64_t timer_ticks;
|
||||
|
||||
template <typename S> static inline S maximum(S a, S b) { return (a > b) ? a : b; }
|
||||
template <typename S> static inline S minimum(S a, S b) { return (a < b) ? a : b; }
|
||||
|
||||
class interval_timer
|
||||
{
|
||||
public:
|
||||
interval_timer();
|
||||
|
||||
void start();
|
||||
void stop();
|
||||
|
||||
double get_elapsed_secs() const;
|
||||
inline double get_elapsed_ms() const { return 1000.0f * get_elapsed_secs(); }
|
||||
|
||||
static void init();
|
||||
static inline timer_ticks get_ticks_per_sec() { return g_freq; }
|
||||
static timer_ticks get_ticks();
|
||||
static double ticks_to_secs(timer_ticks ticks);
|
||||
static inline double ticks_to_ms(timer_ticks ticks) { return ticks_to_secs(ticks) * 1000.0f; }
|
||||
|
||||
private:
|
||||
static timer_ticks g_init_ticks, g_freq;
|
||||
static double g_timer_freq;
|
||||
|
||||
timer_ticks m_start_time, m_stop_time;
|
||||
|
||||
bool m_started, m_stopped;
|
||||
};
|
||||
|
||||
timer_ticks interval_timer::g_init_ticks, interval_timer::g_freq;
|
||||
double interval_timer::g_timer_freq;
|
||||
|
||||
#if defined(_WIN32)
|
||||
inline void query_counter(timer_ticks* pTicks)
|
||||
{
|
||||
QueryPerformanceCounter(reinterpret_cast<LARGE_INTEGER*>(pTicks));
|
||||
}
|
||||
inline void query_counter_frequency(timer_ticks* pTicks)
|
||||
{
|
||||
QueryPerformanceFrequency(reinterpret_cast<LARGE_INTEGER*>(pTicks));
|
||||
}
|
||||
#elif defined(__APPLE__)
|
||||
#include <sys/time.h>
|
||||
inline void query_counter(timer_ticks* pTicks)
|
||||
{
|
||||
struct timeval cur_time;
|
||||
gettimeofday(&cur_time, NULL);
|
||||
*pTicks = static_cast<unsigned long long>(cur_time.tv_sec) * 1000000ULL + static_cast<unsigned long long>(cur_time.tv_usec);
|
||||
}
|
||||
inline void query_counter_frequency(timer_ticks* pTicks)
|
||||
{
|
||||
*pTicks = 1000000;
|
||||
}
|
||||
#elif defined(__GNUC__)
|
||||
#include <sys/timex.h>
|
||||
inline void query_counter(timer_ticks* pTicks)
|
||||
{
|
||||
struct timeval cur_time;
|
||||
gettimeofday(&cur_time, NULL);
|
||||
*pTicks = static_cast<unsigned long long>(cur_time.tv_sec) * 1000000ULL + static_cast<unsigned long long>(cur_time.tv_usec);
|
||||
}
|
||||
inline void query_counter_frequency(timer_ticks* pTicks)
|
||||
{
|
||||
*pTicks = 1000000;
|
||||
}
|
||||
#else
|
||||
#error TODO
|
||||
#endif
|
||||
|
||||
interval_timer::interval_timer() : m_start_time(0), m_stop_time(0), m_started(false), m_stopped(false)
|
||||
{
|
||||
if (!g_timer_freq)
|
||||
init();
|
||||
}
|
||||
|
||||
void interval_timer::start()
|
||||
{
|
||||
query_counter(&m_start_time);
|
||||
m_started = true;
|
||||
m_stopped = false;
|
||||
}
|
||||
|
||||
void interval_timer::stop()
|
||||
{
|
||||
assert(m_started);
|
||||
query_counter(&m_stop_time);
|
||||
m_stopped = true;
|
||||
}
|
||||
|
||||
double interval_timer::get_elapsed_secs() const
|
||||
{
|
||||
assert(m_started);
|
||||
if (!m_started)
|
||||
return 0;
|
||||
|
||||
timer_ticks stop_time = m_stop_time;
|
||||
if (!m_stopped)
|
||||
query_counter(&stop_time);
|
||||
|
||||
timer_ticks delta = stop_time - m_start_time;
|
||||
return delta * g_timer_freq;
|
||||
}
|
||||
|
||||
void interval_timer::init()
|
||||
{
|
||||
if (!g_timer_freq)
|
||||
{
|
||||
query_counter_frequency(&g_freq);
|
||||
g_timer_freq = 1.0f / g_freq;
|
||||
query_counter(&g_init_ticks);
|
||||
}
|
||||
}
|
||||
|
||||
timer_ticks interval_timer::get_ticks()
|
||||
{
|
||||
if (!g_timer_freq)
|
||||
init();
|
||||
timer_ticks ticks;
|
||||
query_counter(&ticks);
|
||||
return ticks - g_init_ticks;
|
||||
}
|
||||
|
||||
double interval_timer::ticks_to_secs(timer_ticks ticks)
|
||||
{
|
||||
if (!g_timer_freq)
|
||||
init();
|
||||
return ticks * g_timer_freq;
|
||||
}
|
||||
|
||||
bool read_file_to_vec(const char* pFilename, uint8_vec& data)
|
||||
{
|
||||
FILE* pFile = nullptr;
|
||||
#ifdef _WIN32
|
||||
fopen_s(&pFile, pFilename, "rb");
|
||||
#else
|
||||
pFile = fopen(pFilename, "rb");
|
||||
#endif
|
||||
if (!pFile)
|
||||
return false;
|
||||
|
||||
fseek(pFile, 0, SEEK_END);
|
||||
#ifdef _WIN32
|
||||
int64_t filesize = _ftelli64(pFile);
|
||||
#else
|
||||
int64_t filesize = ftello(pFile);
|
||||
#endif
|
||||
if (filesize < 0)
|
||||
{
|
||||
fclose(pFile);
|
||||
return false;
|
||||
}
|
||||
fseek(pFile, 0, SEEK_SET);
|
||||
|
||||
if (sizeof(size_t) == sizeof(uint32_t))
|
||||
{
|
||||
if (filesize > 0x70000000)
|
||||
{
|
||||
// File might be too big to load safely in one alloc
|
||||
fclose(pFile);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
data.resize((size_t)filesize);
|
||||
|
||||
if (filesize)
|
||||
{
|
||||
if (fread(&data[0], 1, (size_t)filesize, pFile) != (size_t)filesize)
|
||||
{
|
||||
fclose(pFile);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
fclose(pFile);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool write_data_to_file_internal(const char* pFilename, const void* pData, size_t len)
|
||||
{
|
||||
FILE* pFile = nullptr;
|
||||
#ifdef _WIN32
|
||||
fopen_s(&pFile, pFilename, "wb");
|
||||
#else
|
||||
pFile = fopen(pFilename, "wb");
|
||||
#endif
|
||||
if (!pFile)
|
||||
return false;
|
||||
|
||||
if (len)
|
||||
{
|
||||
if (fwrite(pData, 1, len, pFile) != len)
|
||||
{
|
||||
fclose(pFile);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return fclose(pFile) != EOF;
|
||||
}
|
||||
|
||||
bool write_data_to_file(const char* pFilename, const void* pData, size_t len)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
const uint32_t MAX_TRIES = 10;
|
||||
#else
|
||||
const uint32_t MAX_TRIES = 1;
|
||||
#endif
|
||||
|
||||
for (uint32_t i = 0; i < MAX_TRIES; i++)
|
||||
{
|
||||
if (write_data_to_file_internal(pFilename, pData, len))
|
||||
return true;
|
||||
#ifdef _WIN32
|
||||
Sleep(100);
|
||||
#endif
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
struct color_rgba
|
||||
{
|
||||
uint8_t m_c[4];
|
||||
};
|
||||
|
||||
static void write_func_stbi(void* context, void* data, int size)
|
||||
{
|
||||
if (!size)
|
||||
return;
|
||||
|
||||
uint8_vec* pVec = (uint8_vec*)context;
|
||||
|
||||
size_t cur_s = pVec->size();
|
||||
pVec->resize(cur_s + size);
|
||||
memcpy(pVec->data() + cur_s, data, size);
|
||||
}
|
||||
|
||||
int main(int arg_c, char **arg_v)
|
||||
{
|
||||
if (arg_c < 2)
|
||||
{
|
||||
printf("Usage: fpng_test filename\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
const char* pFilename = nullptr;
|
||||
bool csv_flag = false;
|
||||
for (int i = 1; i < arg_c; i++)
|
||||
{
|
||||
const char* pArg = arg_v[i];
|
||||
if (pArg[0] == '-')
|
||||
{
|
||||
if (pArg[1] == 'c')
|
||||
{
|
||||
csv_flag = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
fprintf(stderr, "Recognized option: %s\n", pArg);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (pFilename)
|
||||
{
|
||||
fprintf(stderr, "Too many filenames: %s\n", pArg);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
pFilename = pArg;
|
||||
}
|
||||
}
|
||||
|
||||
if (!csv_flag)
|
||||
printf("Filename: %s\n", pFilename);
|
||||
|
||||
uint8_vec source_file_data;
|
||||
if (!read_file_to_vec(pFilename, source_file_data))
|
||||
{
|
||||
fprintf(stderr, "Failed reading source file data \"%s\"\n", pFilename);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
unsigned int source_width = 0, source_height = 0;
|
||||
unsigned char* pSource_image_buffer = nullptr;
|
||||
unsigned error = lodepng_decode_memory(&pSource_image_buffer, &source_width, &source_height, source_file_data.data(), source_file_data.size(), LCT_RGBA, 8);
|
||||
if (error != 0)
|
||||
{
|
||||
fprintf(stderr, "Failed unpacking source file \"%s\"\n", pFilename);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
const color_rgba* pSource_pixels32 = (const color_rgba*)pSource_image_buffer;
|
||||
uint32_t total_source_pixels = source_width * source_height;
|
||||
bool has_alpha = false;
|
||||
for (uint32_t i = 0; i < total_source_pixels; i++)
|
||||
{
|
||||
if (pSource_pixels32[i].m_c[3] < 255)
|
||||
{
|
||||
has_alpha = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
const uint32_t source_chans = has_alpha ? 4 : 3;
|
||||
|
||||
if (!csv_flag)
|
||||
printf("Dimensions: %ux%u, Has Alpha: %u, Total Pixels: %u, bytes: %u (%f MB)\n", source_width, source_height, has_alpha, total_source_pixels, total_source_pixels * source_chans, total_source_pixels * source_chans / (1024.0f * 1024.0f));
|
||||
|
||||
uint8_vec source_image_buffer24(total_source_pixels * 3);
|
||||
for (uint32_t i = 0; i < total_source_pixels; i++)
|
||||
{
|
||||
source_image_buffer24[i * 3 + 0] = pSource_pixels32[i].m_c[0];
|
||||
source_image_buffer24[i * 3 + 1] = pSource_pixels32[i].m_c[1];
|
||||
source_image_buffer24[i * 3 + 2] = pSource_pixels32[i].m_c[2];
|
||||
}
|
||||
|
||||
const uint8_t* pSource_pixels24 = source_image_buffer24.data();
|
||||
|
||||
const uint32_t NUM_TIMES_TO_ENCODE = 3;
|
||||
interval_timer tm;
|
||||
|
||||
// FPNG
|
||||
|
||||
std::vector<uint8_t> fpng_file_buf;
|
||||
double fpng_best_time = 1e+9f;
|
||||
for (uint32_t i = 0; i < NUM_TIMES_TO_ENCODE; i++)
|
||||
{
|
||||
tm.start();
|
||||
if (!fpng::fpng_encode_image_to_memory((source_chans == 3) ? (const void *)pSource_pixels24 : (const void*)pSource_pixels32, source_width, source_height, source_chans, false, fpng_file_buf))
|
||||
{
|
||||
fprintf(stderr, "fpng_encode_image_to_memory() failed!\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
fpng_best_time = minimum(fpng_best_time, tm.get_elapsed_secs());
|
||||
}
|
||||
|
||||
if (!csv_flag)
|
||||
printf("FPNG: %f secs, %u bytes, %f MB\n", fpng_best_time, (uint32_t)fpng_file_buf.size(), fpng_file_buf.size() / (1024.0f * 1024.0f));
|
||||
|
||||
if (!write_data_to_file("fpng.png", fpng_file_buf.data(), fpng_file_buf.size()))
|
||||
{
|
||||
fprintf(stderr, "Failed writing to file fpng.png\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
// Verify FPNG's output data using lodepng
|
||||
{
|
||||
unsigned int lodepng_decoded_w = 0, lodepng_decoded_h = 0;
|
||||
unsigned char* lodepng_decoded_buffer = nullptr;
|
||||
error = lodepng_decode_memory(&lodepng_decoded_buffer, &lodepng_decoded_w, &lodepng_decoded_h, (unsigned char*)fpng_file_buf.data(), fpng_file_buf.size(), LCT_RGBA, 8);
|
||||
if (error != 0)
|
||||
{
|
||||
fprintf(stderr, "lodepng failed decompressing FPNG's output PNG file!\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
if (memcmp(lodepng_decoded_buffer, pSource_pixels32, total_source_pixels * 4) != 0)
|
||||
{
|
||||
fprintf(stderr, "FPNG decode verification failed (using lodepng)!\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
free(lodepng_decoded_buffer);
|
||||
}
|
||||
|
||||
// Verify FPNG's output data using stb_image.h
|
||||
{
|
||||
int x, y, c;
|
||||
void* p = stbi_load_from_memory(fpng_file_buf.data(), (int)fpng_file_buf.size(), &x, &y, &c, 4);
|
||||
if (!p)
|
||||
{
|
||||
fprintf(stderr, "stb_image.h failed decompressing FPNG's output PNG file!\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
if (memcmp(p, pSource_pixels32, total_source_pixels * 4) != 0)
|
||||
{
|
||||
fprintf(stderr, "FPNG decode verification failed (using stb_image.h)!\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
free(p);
|
||||
}
|
||||
|
||||
// lodepng
|
||||
|
||||
uint8_vec lodepng_file_buf;
|
||||
double lodepng_best_time = 1e+9f;
|
||||
|
||||
for (uint32_t i = 0; i < NUM_TIMES_TO_ENCODE; i++)
|
||||
{
|
||||
tm.start();
|
||||
lodepng_file_buf.resize(0);
|
||||
error = lodepng::encode(lodepng_file_buf, pSource_image_buffer, source_width, source_height, LCT_RGBA, 8);
|
||||
lodepng_best_time = minimum(lodepng_best_time, tm.get_elapsed_secs());
|
||||
|
||||
if (error != 0)
|
||||
{
|
||||
fprintf(stderr, "lodepng::encode() failed!\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
if (!csv_flag)
|
||||
printf("lodepng: %f secs, %u bytes, %f MB\n", lodepng_best_time, (uint32_t)lodepng_file_buf.size(), (double)lodepng_file_buf.size() / (1024.0f * 1024.0f));
|
||||
|
||||
if (!write_data_to_file("lodepng.png", lodepng_file_buf.data(), lodepng_file_buf.size()))
|
||||
{
|
||||
fprintf(stderr, "Failed writing to file lodepng.png\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
// stb_image_write.h
|
||||
uint8_vec stbi_file_buf;
|
||||
stbi_file_buf.reserve(total_source_pixels * source_chans);
|
||||
double stbi_best_time = 1e+9f;
|
||||
|
||||
for (uint32_t i = 0; i < NUM_TIMES_TO_ENCODE; i++)
|
||||
{
|
||||
stbi_file_buf.resize(0);
|
||||
|
||||
tm.start();
|
||||
|
||||
int res = stbi_write_png_to_func(write_func_stbi, &stbi_file_buf, source_width, source_height, source_chans, (source_chans == 3) ? (void *)pSource_pixels24 : (void*)pSource_pixels32, source_width * source_chans);
|
||||
if (!res)
|
||||
{
|
||||
fprintf(stderr, "stbi_write_png_to_func() failed!\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
stbi_best_time = minimum(stbi_best_time, tm.get_elapsed_secs());
|
||||
}
|
||||
|
||||
if (!csv_flag)
|
||||
printf("stbi: %f secs, %u bytes, %f MB\n", stbi_best_time, (uint32_t)stbi_file_buf.size(), (double)stbi_file_buf.size() / (1024.0f * 1024.0f));
|
||||
|
||||
if (!write_data_to_file("stbi.png", stbi_file_buf.data(), stbi_file_buf.size()))
|
||||
{
|
||||
fprintf(stderr, "Failed writing to file stbi.png\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
// QOI
|
||||
qoi_desc qdesc;
|
||||
qdesc.channels = source_chans;
|
||||
qdesc.width = source_width;
|
||||
qdesc.height = source_height;
|
||||
qdesc.colorspace = QOI_SRGB;
|
||||
|
||||
int qoi_len = 0;
|
||||
|
||||
void* pQOI_data = nullptr;
|
||||
double qoi_best_time = 1e+9f;
|
||||
for (uint32_t i = 0; i < NUM_TIMES_TO_ENCODE; i++)
|
||||
{
|
||||
if (pQOI_data)
|
||||
free(pQOI_data);
|
||||
|
||||
tm.start();
|
||||
|
||||
pQOI_data = qoi_encode((source_chans == 4) ? (void*)pSource_pixels32 : (void*)pSource_pixels24, &qdesc, &qoi_len);
|
||||
|
||||
qoi_best_time = minimum(qoi_best_time, tm.get_elapsed_secs());
|
||||
}
|
||||
|
||||
if (!write_data_to_file("qoi.qoi", pQOI_data, qoi_len))
|
||||
{
|
||||
fprintf(stderr, "Failed writing to file qoi.qoi\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
if (!csv_flag)
|
||||
printf("qoi: %f secs, %i bytes, %f MB\n", qoi_best_time, qoi_len, (double)qoi_len / (1024.0f * 1024.0f));
|
||||
|
||||
// Validate QOI's output file
|
||||
{
|
||||
qoi_desc qddesc;
|
||||
void* pQOI_decomp_data = qoi_decode(pQOI_data, qoi_len, &qddesc, 4);
|
||||
|
||||
if (memcmp(pQOI_decomp_data, pSource_pixels32, total_source_pixels * 4) != 0)
|
||||
{
|
||||
fprintf(stderr, "QOI verification failure!\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
free(pQOI_decomp_data);
|
||||
pQOI_decomp_data = nullptr;
|
||||
}
|
||||
|
||||
if (csv_flag)
|
||||
{
|
||||
const double MB = 1024.0f * 1024.0f;
|
||||
|
||||
printf("%s, %u, %u, %u, %f, %u, %f, %f, %u, %f, %f, %u, %f, %f, %u, %f\n",
|
||||
pFilename, source_width, source_height, source_chans,
|
||||
qoi_best_time, (uint32_t)qoi_len, (double)qoi_len / MB,
|
||||
fpng_best_time, (uint32_t)fpng_file_buf.size(), (double)fpng_file_buf.size() / MB,
|
||||
lodepng_best_time, (uint32_t)lodepng_file_buf.size(), (double)lodepng_file_buf.size() / MB,
|
||||
stbi_best_time, (uint32_t)stbi_file_buf.size(), (double)stbi_file_buf.size() / MB
|
||||
);
|
||||
}
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
6497
lodepng.cpp
Normal file
6497
lodepng.cpp
Normal file
File diff suppressed because it is too large
Load Diff
656
qoi.h
Normal file
656
qoi.h
Normal file
@@ -0,0 +1,656 @@
|
||||
/*
|
||||
|
||||
QOI - The "Quite OK Image" format for fast, lossless image compression
|
||||
|
||||
Dominic Szablewski - https://phoboslab.org
|
||||
|
||||
|
||||
-- LICENSE: The MIT License(MIT)
|
||||
|
||||
Copyright(c) 2021 Dominic Szablewski
|
||||
|
||||
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.
|
||||
|
||||
|
||||
-- About
|
||||
|
||||
QOI encodes and decodes images in a lossless format. An encoded QOI image is
|
||||
usually around 10--30% larger than a decently optimized PNG image.
|
||||
|
||||
QOI outperforms simpler PNG encoders in compression ratio and performance. QOI
|
||||
images are typically 20% smaller than PNGs written with stbi_image. Encoding is
|
||||
25-50x faster and decoding is 3-4x faster than stbi_image or libpng.
|
||||
|
||||
|
||||
-- Synopsis
|
||||
|
||||
// Define `QOI_IMPLEMENTATION` in *one* C/C++ file before including this
|
||||
// library to create the implementation.
|
||||
|
||||
#define QOI_IMPLEMENTATION
|
||||
#include "qoi.h"
|
||||
|
||||
// Encode and store an RGBA buffer to the file system. The qoi_desc describes
|
||||
// the input pixel data.
|
||||
qoi_write("image_new.qoi", rgba_pixels, &(qoi_desc){
|
||||
.width = 1920,
|
||||
.height = 1080,
|
||||
.channels = 4,
|
||||
.colorspace = QOI_SRGB
|
||||
});
|
||||
|
||||
// Load and decode a QOI image from the file system into a 32bbp RGBA buffer.
|
||||
// The qoi_desc struct will be filled with the width, height, number of channels
|
||||
// and colorspace read from the file header.
|
||||
qoi_desc desc;
|
||||
void *rgba_pixels = qoi_read("image.qoi", &desc, 4);
|
||||
|
||||
|
||||
|
||||
-- Documentation
|
||||
|
||||
This library provides the following functions;
|
||||
- qoi_read -- read and decode a QOI file
|
||||
- qoi_decode -- decode the raw bytes of a QOI image from memory
|
||||
- qoi_write -- encode and write a QOI file
|
||||
- qoi_encode -- encode an rgba buffer into a QOI image in memory
|
||||
|
||||
See the function declaration below for the signature and more information.
|
||||
|
||||
If you don't want/need the qoi_read and qoi_write functions, you can define
|
||||
QOI_NO_STDIO before including this library.
|
||||
|
||||
This library uses malloc() and free(). To supply your own malloc implementation
|
||||
you can define QOI_MALLOC and QOI_FREE before including this library.
|
||||
|
||||
This library uses memset() to zero-initialize the index. To supply your own
|
||||
implementation you can define QOI_ZEROARR before including this library.
|
||||
|
||||
|
||||
-- Data Format
|
||||
|
||||
A QOI file has a 14 byte header, followed by any number of data "chunks" and 8
|
||||
zero-bytes to mark the end of the data stream.
|
||||
|
||||
struct qoi_header_t {
|
||||
char magic[4]; // magic bytes "qoif"
|
||||
uint32_t width; // image width in pixels (BE)
|
||||
uint32_t height; // image height in pixels (BE)
|
||||
uint8_t channels; // 3 = RGB, 4 = RGBA
|
||||
uint8_t colorspace; // 0 = sRGB with linear alpha, 1 = all channels linear
|
||||
};
|
||||
|
||||
The decoder and encoder start with {r: 0, g: 0, b: 0, a: 255} as the previous
|
||||
pixel value. Pixels are either encoded as
|
||||
- a run of the previous pixel
|
||||
- an index into an array of previously seen pixels
|
||||
- a difference to the previous pixel value in r,g,b
|
||||
- full r,g,b or r,g,b,a values
|
||||
|
||||
The color channels are assumed to not be premultiplied with the alpha channel
|
||||
("un-premultiplied alpha").
|
||||
|
||||
A running array[64] (zero-initialized) of previously seen pixel values is
|
||||
maintained by the encoder and decoder. Each pixel that is seen by the encoder
|
||||
and decoder is put into this array at the position formed by a hash function of
|
||||
the color value. In the encoder, if the pixel value at the index matches the
|
||||
current pixel, this index position is written to the stream as QOI_OP_INDEX.
|
||||
The hash function for the index is:
|
||||
|
||||
index_position = (r * 3 + g * 5 + b * 7 + a * 11) % 64
|
||||
|
||||
Each chunk starts with a 2- or 8-bit tag, followed by a number of data bits. The
|
||||
bit length of chunks is divisible by 8 - i.e. all chunks are byte aligned. All
|
||||
values encoded in these data bits have the most significant bit on the left.
|
||||
|
||||
The 8-bit tags have precedence over the 2-bit tags. A decoder must check for the
|
||||
presence of an 8-bit tag first.
|
||||
|
||||
The byte stream is padded with 8 zero-bytes at the end.
|
||||
|
||||
|
||||
The possible chunks are:
|
||||
|
||||
|
||||
.- QOI_OP_INDEX ----------.
|
||||
| Byte[0] |
|
||||
| 7 6 5 4 3 2 1 0 |
|
||||
|-------+-----------------|
|
||||
| 0 0 | index |
|
||||
`-------------------------`
|
||||
2-bit tag b00
|
||||
6-bit index into the color index array: 0..63
|
||||
|
||||
|
||||
.- QOI_OP_DIFF -----------.
|
||||
| Byte[0] |
|
||||
| 7 6 5 4 3 2 1 0 |
|
||||
|-------+-----+-----+-----|
|
||||
| 0 1 | dr | dg | db |
|
||||
`-------------------------`
|
||||
2-bit tag b01
|
||||
2-bit red channel difference from the previous pixel between -2..1
|
||||
2-bit green channel difference from the previous pixel between -2..1
|
||||
2-bit blue channel difference from the previous pixel between -2..1
|
||||
|
||||
The difference to the current channel values are using a wraparound operation,
|
||||
so "1 - 2" will result in 255, while "255 + 1" will result in 0.
|
||||
|
||||
Values are stored as unsigned integers with a bias of 2. E.g. -2 is stored as
|
||||
0 (b00). 1 is stored as 3 (b11).
|
||||
|
||||
|
||||
.- QOI_OP_LUMA -------------------------------------.
|
||||
| Byte[0] | Byte[1] |
|
||||
| 7 6 5 4 3 2 1 0 | 7 6 5 4 3 2 1 0 |
|
||||
|-------+-----------------+-------------+-----------|
|
||||
| 1 0 | green diff | dr - dg | db - dg |
|
||||
`---------------------------------------------------`
|
||||
2-bit tag b10
|
||||
6-bit green channel difference from the previous pixel -32..31
|
||||
4-bit red channel difference minus green channel difference -8..7
|
||||
4-bit blue channel difference minus green channel difference -8..7
|
||||
|
||||
The green channel is used to indicate the general direction of change and is
|
||||
encoded in 6 bits. The red and green channels (dr and db) base their diffs off
|
||||
of the green channel difference and are encoded in 4 bits. I.e.:
|
||||
dr_dg = (last_px.r - cur_px.r) - (last_px.g - cur_px.g)
|
||||
db_dg = (last_px.b - cur_px.b) - (last_px.g - cur_px.g)
|
||||
|
||||
The difference to the current channel values are using a wraparound operation,
|
||||
so "10 - 13" will result in 253, while "250 + 7" will result in 1.
|
||||
|
||||
Values are stored as unsigned integers with a bias of 32 for the green channel
|
||||
and a bias of 8 for the red and blue channel.
|
||||
|
||||
|
||||
.- QOI_OP_RUN ------------.
|
||||
| Byte[0] |
|
||||
| 7 6 5 4 3 2 1 0 |
|
||||
|-------+-----------------|
|
||||
| 1 1 | run |
|
||||
`-------------------------`
|
||||
2-bit tag b11
|
||||
6-bit run-length repeating the previous pixel: 1..62
|
||||
|
||||
The run-length is stored with a bias of 1. Note that the run-lengths 63 and 64
|
||||
(b111110 and b111111) are illegal as they are occupied by the QOI_OP_RGB and
|
||||
QOI_OP_RGBA tags.
|
||||
|
||||
|
||||
.- QOI_OP_RGB ------------------------------------------.
|
||||
| Byte[0] | Byte[1] | Byte[2] | Byte[3] |
|
||||
| 7 6 5 4 3 2 1 0 | 7 .. 0 | 7 .. 0 | 7 .. 0 |
|
||||
|-------------------------+---------+---------+---------|
|
||||
| 1 1 1 1 1 1 1 0 | red | green | blue |
|
||||
`-------------------------------------------------------`
|
||||
8-bit tag b11111110
|
||||
8-bit red channel value
|
||||
8-bit green channel value
|
||||
8-bit blue channel value
|
||||
|
||||
|
||||
.- QOI_OP_RGBA ---------------------------------------------------.
|
||||
| Byte[0] | Byte[1] | Byte[2] | Byte[3] | Byte[4] |
|
||||
| 7 6 5 4 3 2 1 0 | 7 .. 0 | 7 .. 0 | 7 .. 0 | 7 .. 0 |
|
||||
|-------------------------+---------+---------+---------+---------|
|
||||
| 1 1 1 1 1 1 1 1 | red | green | blue | alpha |
|
||||
`-----------------------------------------------------------------`
|
||||
8-bit tag b11111111
|
||||
8-bit red channel value
|
||||
8-bit green channel value
|
||||
8-bit blue channel value
|
||||
8-bit alpha channel value
|
||||
|
||||
|
||||
The byte stream is padded at the end with 8 zero bytes. Since the longest legal
|
||||
chunk is 5 bytes (QOI_OP_RGBA), with this padding it is possible to check for an
|
||||
overrun only once per decode loop iteration. These 0x00 bytes also mark the end
|
||||
of the data stream, as an encoder should never produce 8 consecutive zero bytes
|
||||
within the stream.
|
||||
|
||||
*/
|
||||
|
||||
|
||||
/* -----------------------------------------------------------------------------
|
||||
Header - Public functions */
|
||||
|
||||
#ifndef QOI_H
|
||||
#define QOI_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* A pointer to a qoi_desc struct has to be supplied to all of qoi's functions.
|
||||
It describes either the input format (for qoi_write and qoi_encode), or is
|
||||
filled with the description read from the file header (for qoi_read and
|
||||
qoi_decode).
|
||||
|
||||
The colorspace in this qoi_desc is an enum where
|
||||
0 = sRGB, i.e. gamma scaled RGB channels and a linear alpha channel
|
||||
1 = all channels are linear
|
||||
You may use the constants QOI_SRGB or QOI_LINEAR. The colorspace is purely
|
||||
informative. It will be saved to the file header, but does not affect
|
||||
en-/decoding in any way. */
|
||||
|
||||
#define QOI_SRGB 0
|
||||
#define QOI_LINEAR 1
|
||||
|
||||
typedef struct {
|
||||
unsigned int width;
|
||||
unsigned int height;
|
||||
unsigned char channels;
|
||||
unsigned char colorspace;
|
||||
} qoi_desc;
|
||||
|
||||
#ifndef QOI_NO_STDIO
|
||||
|
||||
/* Encode raw RGB or RGBA pixels into a QOI image and write it to the file
|
||||
system. The qoi_desc struct must be filled with the image width, height,
|
||||
number of channels (3 = RGB, 4 = RGBA) and the colorspace.
|
||||
|
||||
The function returns 0 on failure (invalid parameters, or fopen or malloc
|
||||
failed) or the number of bytes written on success. */
|
||||
|
||||
int qoi_write(const char *filename, const void *data, const qoi_desc *desc);
|
||||
|
||||
|
||||
/* Read and decode a QOI image from the file system. If channels is 0, the
|
||||
number of channels from the file header is used. If channels is 3 or 4 the
|
||||
output format will be forced into this number of channels.
|
||||
|
||||
The function either returns NULL on failure (invalid data, or malloc or fopen
|
||||
failed) or a pointer to the decoded pixels. On success, the qoi_desc struct
|
||||
will be filled with the description from the file header.
|
||||
|
||||
The returned pixel data should be free()d after use. */
|
||||
|
||||
void *qoi_read(const char *filename, qoi_desc *desc, int channels);
|
||||
|
||||
#endif /* QOI_NO_STDIO */
|
||||
|
||||
|
||||
/* Encode raw RGB or RGBA pixels into a QOI image in memory.
|
||||
|
||||
The function either returns NULL on failure (invalid parameters or malloc
|
||||
failed) or a pointer to the encoded data on success. On success the out_len
|
||||
is set to the size in bytes of the encoded data.
|
||||
|
||||
The returned qoi data should be free()d after use. */
|
||||
|
||||
void *qoi_encode(const void *data, const qoi_desc *desc, int *out_len);
|
||||
|
||||
|
||||
/* Decode a QOI image from memory.
|
||||
|
||||
The function either returns NULL on failure (invalid parameters or malloc
|
||||
failed) or a pointer to the decoded pixels. On success, the qoi_desc struct
|
||||
is filled with the description from the file header.
|
||||
|
||||
The returned pixel data should be free()d after use. */
|
||||
|
||||
void *qoi_decode(const void *data, int size, qoi_desc *desc, int channels);
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif /* QOI_H */
|
||||
|
||||
|
||||
/* -----------------------------------------------------------------------------
|
||||
Implementation */
|
||||
|
||||
#ifdef QOI_IMPLEMENTATION
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#ifndef QOI_MALLOC
|
||||
#define QOI_MALLOC(sz) malloc(sz)
|
||||
#define QOI_FREE(p) free(p)
|
||||
#endif
|
||||
#ifndef QOI_ZEROARR
|
||||
#define QOI_ZEROARR(a) memset((a),0,sizeof(a))
|
||||
#endif
|
||||
|
||||
#define QOI_OP_INDEX 0x00 /* 00xxxxxx */
|
||||
#define QOI_OP_DIFF 0x40 /* 01xxxxxx */
|
||||
#define QOI_OP_LUMA 0x80 /* 10xxxxxx */
|
||||
#define QOI_OP_RUN 0xc0 /* 11xxxxxx */
|
||||
#define QOI_OP_RGB 0xfe /* 11111110 */
|
||||
#define QOI_OP_RGBA 0xff /* 11111111 */
|
||||
|
||||
#define QOI_MASK_2 0xc0 /* 11000000 */
|
||||
|
||||
#define QOI_COLOR_HASH(C) (C.rgba.r*3 + C.rgba.g*5 + C.rgba.b*7 + C.rgba.a*11)
|
||||
#define QOI_MAGIC \
|
||||
(((unsigned int)'q') << 24 | ((unsigned int)'o') << 16 | \
|
||||
((unsigned int)'i') << 8 | ((unsigned int)'f'))
|
||||
#define QOI_HEADER_SIZE 14
|
||||
#define QOI_PADDING 8
|
||||
|
||||
typedef union {
|
||||
struct { unsigned char r, g, b, a; } rgba;
|
||||
unsigned int v;
|
||||
} qoi_rgba_t;
|
||||
|
||||
void qoi_write_32(unsigned char *bytes, int *p, unsigned int v) {
|
||||
bytes[(*p)++] = (0xff000000 & v) >> 24;
|
||||
bytes[(*p)++] = (0x00ff0000 & v) >> 16;
|
||||
bytes[(*p)++] = (0x0000ff00 & v) >> 8;
|
||||
bytes[(*p)++] = (0x000000ff & v);
|
||||
}
|
||||
|
||||
unsigned int qoi_read_32(const unsigned char *bytes, int *p) {
|
||||
unsigned int a = bytes[(*p)++];
|
||||
unsigned int b = bytes[(*p)++];
|
||||
unsigned int c = bytes[(*p)++];
|
||||
unsigned int d = bytes[(*p)++];
|
||||
return a << 24 | b << 16 | c << 8 | d;
|
||||
}
|
||||
|
||||
void *qoi_encode(const void *data, const qoi_desc *desc, int *out_len) {
|
||||
int i, max_size, p, run;
|
||||
int px_len, px_end, px_pos, channels;
|
||||
unsigned char *bytes;
|
||||
const unsigned char *pixels;
|
||||
qoi_rgba_t index[64];
|
||||
qoi_rgba_t px, px_prev;
|
||||
|
||||
if (
|
||||
data == NULL || out_len == NULL || desc == NULL ||
|
||||
desc->width == 0 || desc->height == 0 ||
|
||||
desc->channels < 3 || desc->channels > 4 ||
|
||||
desc->colorspace > 2
|
||||
) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
max_size =
|
||||
desc->width * desc->height * (desc->channels + 1) +
|
||||
QOI_HEADER_SIZE + QOI_PADDING;
|
||||
|
||||
p = 0;
|
||||
bytes = (unsigned char *) QOI_MALLOC(max_size);
|
||||
if (!bytes) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
qoi_write_32(bytes, &p, QOI_MAGIC);
|
||||
qoi_write_32(bytes, &p, desc->width);
|
||||
qoi_write_32(bytes, &p, desc->height);
|
||||
bytes[p++] = desc->channels;
|
||||
bytes[p++] = desc->colorspace;
|
||||
|
||||
|
||||
pixels = (const unsigned char *)data;
|
||||
|
||||
QOI_ZEROARR(index);
|
||||
|
||||
run = 0;
|
||||
px_prev.rgba.r = 0;
|
||||
px_prev.rgba.g = 0;
|
||||
px_prev.rgba.b = 0;
|
||||
px_prev.rgba.a = 255;
|
||||
px = px_prev;
|
||||
|
||||
px_len = desc->width * desc->height * desc->channels;
|
||||
px_end = px_len - desc->channels;
|
||||
channels = desc->channels;
|
||||
|
||||
for (px_pos = 0; px_pos < px_len; px_pos += channels) {
|
||||
if (channels == 4) {
|
||||
px = *(qoi_rgba_t *)(pixels + px_pos);
|
||||
}
|
||||
else {
|
||||
px.rgba.r = pixels[px_pos + 0];
|
||||
px.rgba.g = pixels[px_pos + 1];
|
||||
px.rgba.b = pixels[px_pos + 2];
|
||||
}
|
||||
|
||||
if (px.v == px_prev.v) {
|
||||
run++;
|
||||
if (run == 62 || px_pos == px_end) {
|
||||
bytes[p++] = QOI_OP_RUN | (run - 1);
|
||||
run = 0;
|
||||
}
|
||||
}
|
||||
else {
|
||||
int index_pos;
|
||||
|
||||
if (run > 0) {
|
||||
bytes[p++] = QOI_OP_RUN | (run - 1);
|
||||
run = 0;
|
||||
}
|
||||
|
||||
index_pos = QOI_COLOR_HASH(px) % 64;
|
||||
|
||||
if (index[index_pos].v == px.v) {
|
||||
bytes[p++] = QOI_OP_INDEX | index_pos;
|
||||
}
|
||||
else {
|
||||
index[index_pos] = px;
|
||||
|
||||
if (px.rgba.a == px_prev.rgba.a) {
|
||||
signed char vr = px.rgba.r - px_prev.rgba.r;
|
||||
signed char vg = px.rgba.g - px_prev.rgba.g;
|
||||
signed char vb = px.rgba.b - px_prev.rgba.b;
|
||||
|
||||
signed char vg_r = vr - vg;
|
||||
signed char vg_b = vb - vg;
|
||||
|
||||
if (
|
||||
vr > -3 && vr < 2 &&
|
||||
vg > -3 && vg < 2 &&
|
||||
vb > -3 && vb < 2
|
||||
) {
|
||||
bytes[p++] = QOI_OP_DIFF | (vr + 2) << 4 | (vg + 2) << 2 | (vb + 2);
|
||||
}
|
||||
else if (
|
||||
vg_r > -9 && vg_r < 8 &&
|
||||
vg > -33 && vg < 32 &&
|
||||
vg_b > -9 && vg_b < 8
|
||||
) {
|
||||
bytes[p++] = QOI_OP_LUMA | (vg + 32);
|
||||
bytes[p++] = (vg_r + 8) << 4 | (vg_b + 8);
|
||||
}
|
||||
else {
|
||||
bytes[p++] = QOI_OP_RGB;
|
||||
bytes[p++] = px.rgba.r;
|
||||
bytes[p++] = px.rgba.g;
|
||||
bytes[p++] = px.rgba.b;
|
||||
}
|
||||
}
|
||||
else {
|
||||
bytes[p++] = QOI_OP_RGBA;
|
||||
bytes[p++] = px.rgba.r;
|
||||
bytes[p++] = px.rgba.g;
|
||||
bytes[p++] = px.rgba.b;
|
||||
bytes[p++] = px.rgba.a;
|
||||
}
|
||||
}
|
||||
}
|
||||
px_prev = px;
|
||||
}
|
||||
|
||||
for (i = 0; i < QOI_PADDING; i++) {
|
||||
bytes[p++] = 0;
|
||||
}
|
||||
|
||||
*out_len = p;
|
||||
return bytes;
|
||||
}
|
||||
|
||||
void *qoi_decode(const void *data, int size, qoi_desc *desc, int channels) {
|
||||
const unsigned char *bytes;
|
||||
unsigned int header_magic;
|
||||
unsigned char *pixels;
|
||||
qoi_rgba_t index[64];
|
||||
qoi_rgba_t px;
|
||||
int px_len, chunks_len, px_pos;
|
||||
int p = 0, run = 0;
|
||||
|
||||
if (
|
||||
data == NULL || desc == NULL ||
|
||||
(channels != 0 && channels != 3 && channels != 4) ||
|
||||
size < QOI_HEADER_SIZE + QOI_PADDING
|
||||
) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bytes = (const unsigned char *)data;
|
||||
|
||||
header_magic = qoi_read_32(bytes, &p);
|
||||
desc->width = qoi_read_32(bytes, &p);
|
||||
desc->height = qoi_read_32(bytes, &p);
|
||||
desc->channels = bytes[p++];
|
||||
desc->colorspace = bytes[p++];
|
||||
|
||||
if (
|
||||
desc->width == 0 || desc->height == 0 ||
|
||||
desc->channels < 3 || desc->channels > 4 ||
|
||||
desc->colorspace > 2 ||
|
||||
header_magic != QOI_MAGIC
|
||||
) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (channels == 0) {
|
||||
channels = desc->channels;
|
||||
}
|
||||
|
||||
px_len = desc->width * desc->height * channels;
|
||||
pixels = (unsigned char *) QOI_MALLOC(px_len);
|
||||
if (!pixels) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
QOI_ZEROARR(index);
|
||||
px.rgba.r = 0;
|
||||
px.rgba.g = 0;
|
||||
px.rgba.b = 0;
|
||||
px.rgba.a = 255;
|
||||
|
||||
chunks_len = size - QOI_PADDING;
|
||||
for (px_pos = 0; px_pos < px_len; px_pos += channels) {
|
||||
if (run > 0) {
|
||||
run--;
|
||||
}
|
||||
else if (p < chunks_len) {
|
||||
int b1 = bytes[p++];
|
||||
|
||||
if (b1 == QOI_OP_RGB) {
|
||||
px.rgba.r = bytes[p++];
|
||||
px.rgba.g = bytes[p++];
|
||||
px.rgba.b = bytes[p++];
|
||||
}
|
||||
else if (b1 == QOI_OP_RGBA) {
|
||||
px.rgba.r = bytes[p++];
|
||||
px.rgba.g = bytes[p++];
|
||||
px.rgba.b = bytes[p++];
|
||||
px.rgba.a = bytes[p++];
|
||||
}
|
||||
else if ((b1 & QOI_MASK_2) == QOI_OP_INDEX) {
|
||||
px = index[b1];
|
||||
}
|
||||
else if ((b1 & QOI_MASK_2) == QOI_OP_DIFF) {
|
||||
px.rgba.r += ((b1 >> 4) & 0x03) - 2;
|
||||
px.rgba.g += ((b1 >> 2) & 0x03) - 2;
|
||||
px.rgba.b += ( b1 & 0x03) - 2;
|
||||
}
|
||||
else if ((b1 & QOI_MASK_2) == QOI_OP_LUMA) {
|
||||
int b2 = bytes[p++];
|
||||
int vg = (b1 & 0x3f) - 32;
|
||||
px.rgba.r += vg - 8 + ((b2 >> 4) & 0x0f);
|
||||
px.rgba.g += vg;
|
||||
px.rgba.b += vg - 8 + (b2 & 0x0f);
|
||||
}
|
||||
else if ((b1 & QOI_MASK_2) == QOI_OP_RUN) {
|
||||
run = (b1 & 0x3f);
|
||||
}
|
||||
|
||||
index[QOI_COLOR_HASH(px) % 64] = px;
|
||||
}
|
||||
|
||||
if (channels == 4) {
|
||||
*(qoi_rgba_t*)(pixels + px_pos) = px;
|
||||
}
|
||||
else {
|
||||
pixels[px_pos + 0] = px.rgba.r;
|
||||
pixels[px_pos + 1] = px.rgba.g;
|
||||
pixels[px_pos + 2] = px.rgba.b;
|
||||
}
|
||||
}
|
||||
|
||||
return pixels;
|
||||
}
|
||||
|
||||
#ifndef QOI_NO_STDIO
|
||||
#include <stdio.h>
|
||||
|
||||
int qoi_write(const char *filename, const void *data, const qoi_desc *desc) {
|
||||
FILE *f = fopen(filename, "wb");
|
||||
int size;
|
||||
void *encoded;
|
||||
|
||||
if (!f) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
encoded = qoi_encode(data, desc, &size);
|
||||
if (!encoded) {
|
||||
fclose(f);
|
||||
return 0;
|
||||
}
|
||||
|
||||
fwrite(encoded, 1, size, f);
|
||||
fclose(f);
|
||||
|
||||
QOI_FREE(encoded);
|
||||
return size;
|
||||
}
|
||||
|
||||
void *qoi_read(const char *filename, qoi_desc *desc, int channels) {
|
||||
FILE *f = fopen(filename, "rb");
|
||||
int size, bytes_read;
|
||||
void *pixels, *data;
|
||||
|
||||
if (!f) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
fseek(f, 0, SEEK_END);
|
||||
size = ftell(f);
|
||||
fseek(f, 0, SEEK_SET);
|
||||
|
||||
data = QOI_MALLOC(size);
|
||||
if (!data) {
|
||||
fclose(f);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bytes_read = fread(data, 1, size, f);
|
||||
fclose(f);
|
||||
|
||||
pixels = qoi_decode(data, bytes_read, desc, channels);
|
||||
QOI_FREE(data);
|
||||
return pixels;
|
||||
}
|
||||
|
||||
#endif /* QOI_NO_STDIO */
|
||||
#endif /* QOI_IMPLEMENTATION */
|
7897
stb_image.h
Normal file
7897
stb_image.h
Normal file
File diff suppressed because it is too large
Load Diff
1724
stb_image_write.h
Normal file
1724
stb_image_write.h
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user