├── blossom ├── link.exe ├── shader_minifier-1.1.6.exe ├── present.frag ├── shaders.targets ├── config.h ├── gldefs.h ├── draw.frag ├── blossom.vcxproj ├── main.cpp └── stb_image_write.h ├── dist ├── wooden-structure.exe └── wooden-structure.png ├── .gitignore ├── README.md ├── blossom.sln ├── crinkler-LICENSE.txt └── crinkler-manual.txt /blossom/link.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fms-cat/wooden-structure/HEAD/blossom/link.exe -------------------------------------------------------------------------------- /dist/wooden-structure.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fms-cat/wooden-structure/HEAD/dist/wooden-structure.exe -------------------------------------------------------------------------------- /dist/wooden-structure.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fms-cat/wooden-structure/HEAD/dist/wooden-structure.png -------------------------------------------------------------------------------- /blossom/shader_minifier-1.1.6.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fms-cat/wooden-structure/HEAD/blossom/shader_minifier-1.1.6.exe -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /Debug 2 | /Release 3 | /blossom/Debug 4 | /blossom/Release 5 | /blossom/frag_draw.h 6 | /blossom/frag_present.h 7 | /blossom/out.html 8 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Wooden Structure 2 | 3 | ![Wooden Structure](./dist/wooden-structure.png) 4 | 5 | 2KB executable graphics for Outline Online 2021 6 | 7 | Made with slightly modified [Blossom](https://github.com/lunasorcery/Blossom) by yx. 8 | 9 | Inspired by the Byte Battle experience in [Outline Online 2021](https://outlinedemoparty.nl/). 10 | 11 | Manually edit `frag_draw.h` (the output of shader minifier) and replace all for loops to `for(int Z=0;Z 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /blossom.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 15 4 | VisualStudioVersion = 15.0.28307.168 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "blossom", "blossom\blossom.vcxproj", "{200ACA63-A9E9-4579-9E38-BF7059DE15F1}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Capture|x86 = Capture|x86 11 | Debug|x86 = Debug|x86 12 | Release|x86 = Release|x86 13 | EndGlobalSection 14 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 15 | {200ACA63-A9E9-4579-9E38-BF7059DE15F1}.Capture|x86.ActiveCfg = Capture|Win32 16 | {200ACA63-A9E9-4579-9E38-BF7059DE15F1}.Capture|x86.Build.0 = Capture|Win32 17 | {200ACA63-A9E9-4579-9E38-BF7059DE15F1}.Debug|x86.ActiveCfg = Debug|Win32 18 | {200ACA63-A9E9-4579-9E38-BF7059DE15F1}.Debug|x86.Build.0 = Debug|Win32 19 | {200ACA63-A9E9-4579-9E38-BF7059DE15F1}.Release|x86.ActiveCfg = Release|Win32 20 | {200ACA63-A9E9-4579-9E38-BF7059DE15F1}.Release|x86.Build.0 = Release|Win32 21 | EndGlobalSection 22 | GlobalSection(SolutionProperties) = preSolution 23 | HideSolutionNode = FALSE 24 | EndGlobalSection 25 | GlobalSection(ExtensibilityGlobals) = postSolution 26 | SolutionGuid = {1F19EDF3-F58A-4D3C-86F8-FAB69A403631} 27 | EndGlobalSection 28 | EndGlobal 29 | -------------------------------------------------------------------------------- /blossom/config.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // like Leviathan, we have some risky tricks to shave the last few bytes 4 | #define DESPERATE 0 5 | 6 | // releasing at Revision? here's a handy toggle for some compo-safe config presets 7 | #define REVISION_RULESET 0 8 | 9 | 10 | #if _DEBUG 11 | #define WINDOW_FULLSCREEN 0 12 | #define WINDOW_AUTO_SIZE 0 13 | #define CANVAS_WIDTH 1280 14 | #define CANVAS_HEIGHT 720 15 | 16 | #define RENDER_MAX_TIME_MS 30000 17 | //#define RENDER_MIN_SAMPLES 1 18 | //#define RENDER_MAX_SAMPLES 256 19 | 20 | #define RENDER_PROGRESSIVE 1 21 | #endif 22 | 23 | #if RELEASE 24 | // party release config, based on Revision ruleset 25 | #if REVISION_RULESET 26 | #define WINDOW_FULLSCREEN 1 27 | #define WINDOW_AUTO_SIZE 0 28 | #define CANVAS_WIDTH 1920 29 | #define CANVAS_HEIGHT 1080 30 | 31 | #define RENDER_MAX_TIME_MS 30000 // Revision Rules: don't exceed 30 seconds 32 | 33 | #define RENDER_MAX_SAMPLES 256 // customizable, optional 34 | 35 | #define RENDER_PROGRESSIVE 0 // Revision Rules: no progressive rendering. 36 | #else 37 | #define WINDOW_FULLSCREEN 1 38 | #define WINDOW_AUTO_SIZE 0 39 | #define CANVAS_WIDTH 1920 40 | #define CANVAS_HEIGHT 1080 41 | 42 | #define RENDER_EXACT_SAMPLES 512 // without the constraints of party rules, we can set an exact quality bar, if we want. 43 | 44 | #define RENDER_PROGRESSIVE 0 45 | #endif 46 | #endif 47 | 48 | #if CAPTURE 49 | #define WINDOW_AUTO_SIZE 0 50 | #define CANVAS_WIDTH 3840 51 | #define CANVAS_HEIGHT 2160 52 | 53 | #define RENDER_EXACT_SAMPLES 1024 54 | 55 | // which formats to save 56 | #define CAPTURE_SAVE_PNG 1 57 | #define CAPTURE_SAVE_JPG 0 58 | #define CAPTURE_SAVE_U8_BIN 0 59 | #define CAPTURE_SAVE_F32_BIN 0 60 | #endif 61 | 62 | 63 | 64 | // ==== housekeeping stuff, you don't need to touch this ==== // 65 | 66 | #if CAPTURE 67 | #define WINDOW_FULLSCREEN 0 68 | 69 | #define RENDER_PROGRESSIVE 1 70 | 71 | #if !CAPTURE_SAVE_PNG && !CAPTURE_SAVE_JPG && !CAPTURE_SAVE_U8_BIN && !CAPTURE_SAVE_F32_BIN 72 | #define CAPTURE_SAVE_PNG 1 73 | #endif 74 | #endif 75 | -------------------------------------------------------------------------------- /crinkler-LICENSE.txt: -------------------------------------------------------------------------------- 1 | Crinkler - compressing linker for Windows 2 | Copyright (c) 2005-2020 Aske Simon Christensen and Rune L. H. Stubbe. 3 | 4 | This software is provided 'as-is', without any express or implied 5 | warranty. In no event will the authors be held liable for any damages 6 | arising from the use of this software. 7 | 8 | Permission is granted to anyone to use this software for any purpose, 9 | including commercial applications, and to alter it and redistribute it 10 | freely, subject to the following restrictions: 11 | 12 | 1. The origin of this software must not be misrepresented; you must not 13 | claim that you wrote the original software. If you use this software 14 | in a product, an acknowledgment in the product documentation would be 15 | appreciated but is not required. 16 | 2. Altered source versions must be plainly marked as such, and must not be 17 | misrepresented as being the original software. 18 | 3. This notice may not be removed or altered from any source distribution. 19 | 20 | 21 | 22 | :[diStorm64}: 23 | The ultimate disassembler library. 24 | Copyright (c) 2003,2004,2005,2006,2007,2008, Gil Dabah 25 | All rights reserved. 26 | 27 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 28 | 29 | * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 30 | * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 31 | * Neither the name of the diStorm nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. 32 | 33 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 34 | -------------------------------------------------------------------------------- /blossom/gldefs.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #define glAttachShader ((PFNGLATTACHSHADERPROC) wglGetProcAddress("glAttachShader")) 4 | #define glBindFramebuffer ((PFNGLBINDFRAMEBUFFERPROC) wglGetProcAddress("glBindFramebuffer")) 5 | #define glCompileShader ((PFNGLCOMPILESHADERPROC) wglGetProcAddress("glCompileShader")) 6 | #define glCreateProgram ((PFNGLCREATEPROGRAMPROC) wglGetProcAddress("glCreateProgram")) 7 | #define glCreateShader ((PFNGLCREATESHADERPROC) wglGetProcAddress("glCreateShader")) 8 | #define glCreateShaderProgramv ((PFNGLCREATESHADERPROGRAMVPROC)wglGetProcAddress("glCreateShaderProgramv")) 9 | #define glDebugMessageCallback ((PFNGLDEBUGMESSAGECALLBACKPROC)wglGetProcAddress("glDebugMessageCallback")) 10 | #define glDeleteProgram ((PFNGLDELETEPROGRAMPROC) wglGetProcAddress("glDeleteProgram")) 11 | #define glDeleteShader ((PFNGLDELETESHADERPROC) wglGetProcAddress("glDeleteShader")) 12 | #define glDrawBuffers ((PFNGLDRAWBUFFERSPROC) wglGetProcAddress("glDrawBuffers")) 13 | #define glFramebufferTexture ((PFNGLFRAMEBUFFERTEXTUREPROC) wglGetProcAddress("glFramebufferTexture")) 14 | #define glGenFramebuffers ((PFNGLGENFRAMEBUFFERSPROC) wglGetProcAddress("glGenFramebuffers")) 15 | #define glGetProgramInfoLog ((PFNGLGETPROGRAMINFOLOGPROC) wglGetProcAddress("glGetProgramInfoLog")) 16 | #define glGetProgramiv ((PFNGLGETPROGRAMIVPROC) wglGetProcAddress("glGetProgramiv")) 17 | #define glGetShaderInfoLog ((PFNGLGETSHADERINFOLOGPROC) wglGetProcAddress("glGetShaderInfoLog")) 18 | #define glGetShaderiv ((PFNGLGETSHADERIVPROC) wglGetProcAddress("glGetShaderiv")) 19 | #define glGetUniformLocation ((PFNGLGETUNIFORMLOCATIONPROC) wglGetProcAddress("glGetUniformLocation")) 20 | #define glLinkProgram ((PFNGLLINKPROGRAMPROC) wglGetProcAddress("glLinkProgram")) 21 | #define glShaderSource ((PFNGLSHADERSOURCEPROC) wglGetProcAddress("glShaderSource")) 22 | #define glUniform1i ((PFNGLUNIFORM1IPROC) wglGetProcAddress("glUniform1i")) 23 | #define glUniform2i ((PFNGLUNIFORM2IPROC) wglGetProcAddress("glUniform2i")) 24 | #define glUniform3i ((PFNGLUNIFORM3IPROC) wglGetProcAddress("glUniform3i")) 25 | #define glUniform4i ((PFNGLUNIFORM4IPROC) wglGetProcAddress("glUniform4i")) 26 | #define glUniform1f ((PFNGLUNIFORM1FPROC) wglGetProcAddress("glUniform1f")) 27 | #define glUniform2f ((PFNGLUNIFORM2FPROC) wglGetProcAddress("glUniform2f")) 28 | #define glUniform3f ((PFNGLUNIFORM3FPROC) wglGetProcAddress("glUniform3f")) 29 | #define glUniform4f ((PFNGLUNIFORM4FPROC) wglGetProcAddress("glUniform4f")) 30 | #define glUseProgram ((PFNGLUSEPROGRAMPROC) wglGetProcAddress("glUseProgram")) 31 | -------------------------------------------------------------------------------- /blossom/draw.frag: -------------------------------------------------------------------------------- 1 | /* framework header */ 2 | #version 430 3 | layout(location = 0) uniform vec2 iResolution; 4 | layout(location = 1) uniform int iFrame; 5 | 6 | /* vvv your shader goes here vvv */ 7 | 8 | #define L(i,j) (floor((i)/(j))*(j)) 9 | #define F(i) (fract(sin((i)*111.)*111.)) 10 | 11 | // const float PI = acos( -1. ); 12 | 13 | float cellSize; 14 | float seed; 15 | vec2 cellPos; 16 | vec4 cellHash; 17 | 18 | float random() { 19 | seed = F( seed ); 20 | return seed; 21 | } 22 | 23 | mat3 orthBas( vec3 d ) { 24 | vec3 z = normalize( d ); 25 | vec3 x = normalize( cross( 26 | abs( z.y ) < 0.999 ? vec3( 0.0, 1.0, 0.0 ) : vec3( 1.0, 0.0, 0.0 ), 27 | z 28 | ) ); 29 | return mat3( x, cross( z, x ), z ); 30 | } 31 | 32 | float cyclicNoise( vec3 p, vec3 b, float pump ) { 33 | mat3 bas = orthBas( b ); 34 | vec2 sum = vec2( 0.0 ); 35 | 36 | for ( int i = 0; i < 6; i ++ ) { 37 | p *= bas * 2.0; 38 | p += sin( p.yzx ); 39 | sum = pump * sum + vec2( dot( sin( p.zxy ), cos( p ) ), 1.0 ); 40 | } 41 | 42 | return sum.x / sum.y; 43 | } 44 | 45 | float mapWater( vec3 p ) { 46 | return p.z + 0.04 * cyclicNoise( p, vec3( 1 ), 2.0 ); 47 | } 48 | 49 | vec3 importanceSampleGGX( float roughness, vec3 N ) { 50 | float phi = 6.2832 * random(); 51 | float cosTheta = random(); 52 | cosTheta = roughness > 1.0 // use lambert ??? 53 | ? cos( asin( sqrt( cosTheta ) ) ) 54 | : sqrt( ( 1.0 - cosTheta ) / ( 1.0 + ( pow( roughness, 4.0 ) - 1.0 ) * cosTheta ) ); 55 | float sinTheta = sqrt( 1.0 - cosTheta * cosTheta ); 56 | 57 | return orthBas( N ) * vec3( 58 | cos( phi ) * sinTheta, 59 | sin( phi ) * sinTheta, 60 | cosTheta 61 | ); 62 | } 63 | 64 | // distance 65 | float sdbox( vec3 p, vec3 s ) { 66 | vec3 d = abs( p ) - s; 67 | return min( max( max( d.x, d.y ), d.z ), 0.0 ) + length( max( d, vec3( 0 ) ) ); 68 | } 69 | 70 | float map( vec3 p ) { 71 | p -= vec3( 72 | cellPos, 73 | ( 74 | 0.4 * cellHash.x 75 | + 0.2 * ( cellPos.x + cellPos.y ) - 8.2 76 | ) 77 | ); 78 | 79 | return max( 80 | sdbox( p, step( cellHash.y, 0.9 ) * vec2( 81 | 0.5 * cellSize - 1.0 / 64.0, 82 | 8 83 | ).xxy ), 84 | -sdbox( p, step( cellHash.y, 0.4 ) * vec2( 85 | 0.5 * cellSize - 3.0 / 64.0, 86 | 9 87 | ).xxy ) 88 | ); 89 | } 90 | 91 | void main() { 92 | // seed = float( iFrame ) + hash21( gl_FragCoord.xy ); 93 | seed = float( iFrame ); 94 | 95 | vec2 p = ( gl_FragCoord.xy + vec2( random(), random() ) - 0.5 * iResolution.xy ) / iResolution.y; 96 | 97 | gl_FragColor = vec4( 0, 0, 0, 1 ); 98 | 99 | float medium = 1.0; 100 | float rl = 2.5; 101 | vec3 ro = vec3( 0, 0, rl ); 102 | vec3 rd = normalize( vec3( p, -1 ) ); 103 | vec3 rp = ro + rd * rl; 104 | 105 | vec3 colRem = vec3( 1.0 - 0.2 * length( p ) ); 106 | 107 | ro.xy += 0.05 * vec2( random(), random() ); 108 | 109 | rp *= orthBas( vec3( 2, 2, 5 ) ); 110 | ro *= orthBas( vec3( 2, 2, 5 ) ); 111 | rp.xy *= mat2( 0.6, 0.7, -0.7, 0.6 ); 112 | ro.xy *= mat2( 0.6, 0.7, -0.7, 0.6 ); 113 | 114 | rd = normalize( rp - ro ); 115 | 116 | for ( int i = 0; i < 5; i ++ ) { 117 | vec2 rdxy = normalize( rd.xy ); 118 | float dist; 119 | 120 | // water?? 121 | rl = 0.01; 122 | rp = ro + rd * rl; 123 | 124 | for ( int i = 0; i < 20; i ++ ) { 125 | // dist = mapWater( rp ) * medium; 126 | rl += 0.9 * mapWater( rp ) * medium; 127 | rp = ro + rd * rl; 128 | } 129 | 130 | // float rlWater = dist < 1E-2 ? rl : 1E2; 131 | float rlWater = rl; 132 | // water end 133 | 134 | rl = 4E-3; 135 | rp = ro + rd * rl; 136 | 137 | for ( int i = 0; i < 50; i ++ ) { 138 | // quadtree begin 139 | // https://www.shadertoy.com/view/7d2Szc 140 | cellSize = 1.0; 141 | 142 | for ( int i = 0; i < 4; i ++ ) { 143 | cellSize *= 0.5; 144 | cellPos = L( rp.xy, cellSize ) + 0.5 * cellSize; 145 | cellHash = F( F( F( cellPos.y ) + cellPos.x ) + vec4( 0, 1, 2, 3 ) ); 146 | if ( cellSize < 0.7 * cellHash.x ) break; 147 | } 148 | // quadtree end 149 | 150 | // segment begin 151 | // vec2 tow = sign( rdxy ) * cellSize * 0.5; 152 | // vec2 v = ( tow - ( rp.xy - cellPos ) ) / rdxy; 153 | vec2 v = ( ( sign( rdxy ) * cellSize * 0.5 ) - rp.xy + cellPos ) / rd.xy * length( rd ); 154 | 155 | // vec2 nextCell = tow * ( ( v.x < v.y ) ? vec2( 2, 0 ) : vec2( 0, 2 ) ), 156 | 157 | // float seg = min( v.x, v.y ); 158 | // segment end 159 | 160 | float rlNext = rl + min( v.x, v.y ) + 1E-3; 161 | 162 | // march start 163 | for ( int i = 0; i < 100; i ++ ) { 164 | dist = map( rp ); 165 | rl += dist; 166 | rp = ro + rd * rl; 167 | 168 | if ( dist < 1E-3 ) break; 169 | if ( rlNext < rl ) break; 170 | } 171 | // march end 172 | 173 | if ( dist < 1E-2 ) break; 174 | 175 | rl = rlNext; 176 | rp = ro + rd * rl; 177 | } 178 | 179 | vec2 d = vec2( 0, 1E-3 ); 180 | 181 | if ( dist > 1E-2 ) { 182 | gl_FragColor = vec4( 183 | colRem * pow( max( 0.0, rd.z + abs( rd.y ) ), 2.0 ), 184 | 1 185 | ); 186 | break; 187 | } 188 | 189 | rl = max( min( rl, rlWater ), 0.0 ); 190 | colRem *= exp( 8.0 * ( medium - 1.0 ) * rl ); 191 | 192 | if ( rl == rlWater ) { 193 | ro = ro + rd * rl; 194 | 195 | vec3 N = normalize( vec3( 196 | mapWater( ro + d.yxx ), 197 | mapWater( ro + d.xyx ), 198 | mapWater( ro + d.xxy ) 199 | ) - mapWater( ro ) ); 200 | 201 | // if ( random() < F ) { 202 | // if ( random() < mix( 0.04, 1.0, pow( 1.0 - dot( -rd, N ), 5.0 ) ) ) { 203 | if ( random() < 0.04 + pow( 1.0 - dot( -rd, N ), 5.0 ) ) { 204 | rd = reflect( rd, N ); 205 | } else { 206 | rd = refract( rd, N, 1.04 - 0.3 * medium ); 207 | medium = -medium; 208 | } 209 | } else { 210 | vec3 N = normalize( vec3( 211 | map( rp + d.yxx ), 212 | map( rp + d.xyx ), 213 | map( rp + d.xxy ) 214 | ) - map( rp ) ); 215 | 216 | ro = rp; 217 | 218 | // Ref: https://www.shadertoy.com/view/ldscDM 219 | float ring = 200.0 * ( dot( rp, 0.5 - cellHash.xyz ) + cyclicNoise( 0.5 * rp, cellHash.xyz, 3.0 ) ); 220 | ring = pow( sin( ring ) * 0.5 + 0.5, 9.0 ) + cos( ring ) * 0.7; 221 | 222 | // float F = mix( 0.04, 1.0, pow( 1.0 - dot( -rd, N ), 5.0 ) ); 223 | // if ( random() < F / mix( 1.0 / PI, 1.0, F ) ) { 224 | if ( random() < 0.12 + 2.0 * smoothstep( 0.7, -0.8, dot( -rd, N ) ) ) { // what the fuck 225 | // weight should be F 226 | rd = reflect( 227 | rd, 228 | importanceSampleGGX( ( 229 | 0.5 230 | - 0.3 * cyclicNoise( 8.0 * rp, cellHash.xyz, 1.0 ) 231 | - 0.1 * ring 232 | ), N ) 233 | ); 234 | } else { 235 | // weight should be (1.0 - F) / PI (albedo * (1.0 - F) / PI) 236 | colRem *= pow( 237 | 0.5 + 0.3 * sin( cellPos.x + cellPos.y + cellHash.w + vec3( 0, 1.5, 2.5 ) ), 238 | vec3( 2.0 - 0.2 * ring ) 239 | ); // colRem *= 0.0; 240 | rd = importanceSampleGGX( 2.0, N ); 241 | } 242 | } 243 | } 244 | } 245 | -------------------------------------------------------------------------------- /blossom/blossom.vcxproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Capture 6 | Win32 7 | 8 | 9 | Debug 10 | Win32 11 | 12 | 13 | Release 14 | Win32 15 | 16 | 17 | 18 | 16.0 19 | {200ACA63-A9E9-4579-9E38-BF7059DE15F1} 20 | blossom 21 | 10.0.17763.0 22 | 23 | 24 | true 25 | 26 | 27 | 28 | Application 29 | true 30 | v141 31 | MultiByte 32 | 33 | 34 | Application 35 | true 36 | v141 37 | MultiByte 38 | 39 | 40 | Application 41 | false 42 | v141 43 | true 44 | MultiByte 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | $(ProjectDir);$(ExecutablePath) 63 | 64 | 65 | false 66 | $(ExecutablePath) 67 | 68 | 69 | Build 70 | 71 | 72 | 73 | Level3 74 | Disabled 75 | true 76 | false 77 | false 78 | Default 79 | MultiThreadedDebug 80 | _CRT_SECURE_NO_WARNINGS;_MBCS;_DEBUG;%(PreprocessorDefinitions) 81 | 82 | 83 | Console 84 | false 85 | opengl32.lib;Winmm.lib;%(AdditionalDependencies) 86 | 87 | 88 | 89 | 90 | Level3 91 | MaxSpeed 92 | true 93 | false 94 | false 95 | Default 96 | MultiThreaded 97 | _CRT_SECURE_NO_WARNINGS;_MBCS;CAPTURE;%(PreprocessorDefinitions) 98 | false 99 | EditAndContinue 100 | 101 | 102 | Console 103 | false 104 | opengl32.lib;Winmm.lib;%(AdditionalDependencies) 105 | true 106 | UseLinkTimeCodeGeneration 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | Level3 116 | MaxSpeed 117 | true 118 | true 119 | true 120 | true 121 | MultiThreaded 122 | false 123 | false 124 | false 125 | _MBCS;RELEASE;%(PreprocessorDefinitions) 126 | 127 | 128 | Windows 129 | opengl32.lib;Winmm.lib;%(AdditionalDependencies) 130 | true 131 | /CRINKLER /RANGE:opengl32 /HASHTRIES:300 /COMPMODE:SLOW /ORDERTRIES:1000 /REPORT:out.html /PROGRESSGUI /HASHSIZE:100 /UNSAFEIMPORT /UNALIGNCODE %(AdditionalOptions) 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | -------------------------------------------------------------------------------- /blossom/main.cpp: -------------------------------------------------------------------------------- 1 | // system 2 | #include 3 | #include 4 | #include 5 | 6 | // gl 7 | #include 8 | #include "glext.h" 9 | #include "gldefs.h" 10 | 11 | // config 12 | #include "config.h" 13 | 14 | // shaders 15 | #include "frag_draw.h" 16 | #undef VAR_IRESOLUTION 17 | #include "frag_present.h" 18 | 19 | // requirements for capture mode 20 | #if CAPTURE 21 | #define STB_IMAGE_WRITE_IMPLEMENTATION 22 | #include "stb_image_write.h" 23 | #include 24 | #endif 25 | 26 | // shaders 27 | GLuint gShaderDraw; 28 | GLuint gShaderPresent; 29 | 30 | // framebuffers 31 | GLuint fbAccumulator; 32 | 33 | // uniform bindings 34 | int const kUniformResolution = 0; 35 | int const kUniformFrame = 1; 36 | int const kSamplerAccumulatorTex = 0; 37 | 38 | // === resolutions === 39 | #if WINDOW_AUTO_SIZE 40 | // the ugliest comma operator hack I will ever write 41 | int const kCanvasWidth = (SetProcessDPIAware(), GetSystemMetrics(SM_CXSCREEN)); 42 | int const kCanvasHeight = GetSystemMetrics(SM_CYSCREEN); 43 | #else 44 | #define kCanvasWidth CANVAS_WIDTH 45 | #define kCanvasHeight CANVAS_HEIGHT 46 | #endif 47 | 48 | #define kWindowWidth kCanvasWidth 49 | #define kWindowHeight kCanvasHeight 50 | // ===================== 51 | 52 | 53 | 54 | // capture GL errors 55 | #if _DEBUG 56 | void __stdcall 57 | MessageCallback(GLenum source, 58 | GLenum type, 59 | GLuint id, 60 | GLenum severity, 61 | GLsizei length, 62 | const GLchar* message, 63 | const void* userParam) 64 | { 65 | fprintf(stderr, "GL CALLBACK: %s type = 0x%x, severity = 0x%x, message = %s\n", 66 | (type == GL_DEBUG_TYPE_ERROR ? "** GL ERROR **" : ""), 67 | type, severity, message); 68 | 69 | __debugbreak(); 70 | } 71 | #endif 72 | 73 | GLuint makeFramebuffer() 74 | { 75 | GLuint name, backing; 76 | glGenFramebuffers(1, &name); 77 | glBindFramebuffer(GL_FRAMEBUFFER, name); 78 | glGenTextures(1, &backing); 79 | glBindTexture(GL_TEXTURE_2D, backing); 80 | glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA32F, kWindowWidth, kWindowHeight, 0, GL_RGBA, GL_FLOAT, 0); 81 | 82 | // don't remove these! 83 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); 84 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); 85 | 86 | glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, backing, 0); 87 | GLenum drawBuffers[] = { GL_COLOR_ATTACHMENT0 }; 88 | glDrawBuffers(1, drawBuffers); 89 | return name; 90 | } 91 | 92 | GLuint makeShader(const char* source) 93 | { 94 | #if _DEBUG 95 | GLuint shader = glCreateShader(GL_FRAGMENT_SHADER); 96 | 97 | glShaderSource(shader, 1, &source, 0); 98 | glCompileShader(shader); 99 | 100 | // shader compiler errors 101 | GLint isCompiled = 0; 102 | glGetShaderiv(shader, GL_COMPILE_STATUS, &isCompiled); 103 | if (isCompiled == GL_FALSE) 104 | { 105 | const int maxLength = 1024; 106 | GLchar errorLog[maxLength]; 107 | glGetShaderInfoLog(shader, maxLength, 0, errorLog); 108 | puts(errorLog); 109 | glDeleteShader(shader); 110 | __debugbreak(); 111 | } 112 | 113 | // link shader 114 | GLuint m_program = glCreateProgram(); 115 | glAttachShader(m_program, shader); 116 | glLinkProgram(m_program); 117 | 118 | GLint isLinked = 0; 119 | glGetProgramiv(m_program, GL_LINK_STATUS, &isLinked); 120 | if (isLinked == GL_FALSE) 121 | { 122 | const int maxLength = 1024; 123 | GLchar errorLog[maxLength]; 124 | glGetProgramInfoLog(m_program, maxLength, 0, errorLog); 125 | puts(errorLog); 126 | glDeleteProgram(m_program); 127 | __debugbreak(); 128 | } 129 | 130 | return m_program; 131 | #else 132 | return glCreateShaderProgramv(GL_FRAGMENT_SHADER, 1, &source); 133 | #endif 134 | 135 | } 136 | 137 | void bindSharedUniforms() 138 | { 139 | glUniform2f( 140 | kUniformResolution, 141 | (float)kCanvasWidth, 142 | (float)kCanvasHeight); 143 | } 144 | 145 | static inline void accumulatorSetup() 146 | { 147 | glUseProgram(gShaderDraw); 148 | glBindFramebuffer(GL_FRAMEBUFFER, fbAccumulator); 149 | 150 | glEnable(GL_BLEND); 151 | glBlendFunc(GL_ONE, GL_ONE); 152 | 153 | bindSharedUniforms(); 154 | } 155 | 156 | static inline void presentSetup(int destFb) 157 | { 158 | glUseProgram(gShaderPresent); 159 | glBindFramebuffer(GL_FRAMEBUFFER, destFb); 160 | 161 | glBlendFunc(GL_ONE, GL_ZERO); 162 | 163 | bindSharedUniforms(); 164 | glBindTexture(GL_TEXTURE_2D, fbAccumulator); 165 | } 166 | 167 | static inline void accumulatorRender(int sampleCount) 168 | { 169 | glUniform1i(kUniformFrame, sampleCount); 170 | glRecti(-1, -1, 1, 1); 171 | 172 | #ifdef RENDER_MAX_TIME_MS 173 | // deliberately block so we don't queue up more work than we have time for 174 | glFinish(); 175 | #endif 176 | } 177 | 178 | static inline void presentRender(HDC hDC) 179 | { 180 | glRecti(-1, -1, 1, 1); 181 | SwapBuffers(hDC); 182 | } 183 | 184 | #if defined(RELEASE) 185 | int WinMainCRTStartup() 186 | #else 187 | int main() 188 | #endif 189 | { 190 | #ifndef RENDER_EXACT_SAMPLES 191 | unsigned int startTime = timeGetTime(); 192 | #endif 193 | 194 | DEVMODE screenSettings = { 195 | {0}, 0, 0, sizeof(screenSettings), 0, DM_PELSWIDTH | DM_PELSHEIGHT, 196 | {0}, 0, 0, 0, 0, 0, {0}, 0, 0, (DWORD)kWindowWidth, (DWORD)kWindowHeight, 0, 0, 197 | #if(WINVER >= 0x0400) 198 | 0, 0, 0, 0, 0, 0, 199 | #if (WINVER >= 0x0500) || (_WIN32_WINNT >= 0x0400) 200 | 0, 0 201 | #endif 202 | #endif 203 | }; 204 | const PIXELFORMATDESCRIPTOR pfd = { 205 | sizeof(pfd), 1, PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER, PFD_TYPE_RGBA, 206 | 32, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 32, 0, 0, PFD_MAIN_PLANE, 0, 0, 0, 0 207 | }; 208 | 209 | #if WINDOW_FULLSCREEN 210 | ChangeDisplaySettings(&screenSettings, CDS_FULLSCREEN); 211 | ShowCursor(0); 212 | HDC hDC = GetDC(CreateWindow((LPCSTR)0xC018, 0, WS_POPUP | WS_VISIBLE | WS_MAXIMIZE, 0, 0, 0, 0, 0, 0, 0, 0)); 213 | #else 214 | HDC hDC = GetDC(CreateWindow((LPCSTR)0xC018, 0, WS_POPUP | WS_VISIBLE, 0, 0, kWindowWidth, kWindowHeight, 0, 0, 0, 0)); 215 | #endif 216 | 217 | // set pixel format and make opengl context 218 | SetPixelFormat(hDC, ChoosePixelFormat(hDC, &pfd), &pfd); 219 | wglMakeCurrent(hDC, wglCreateContext(hDC)); 220 | SwapBuffers(hDC); 221 | 222 | // enable opengl debug messages 223 | #if _DEBUG 224 | glEnable(GL_DEBUG_OUTPUT); 225 | glDebugMessageCallback(MessageCallback, 0); 226 | #endif 227 | 228 | // make framebuffer 229 | fbAccumulator = makeFramebuffer(); 230 | 231 | // optional extra buffers/textures 232 | #if CAPTURE 233 | GLuint const fbCapture = makeFramebuffer(); 234 | float* cpuFramebufferFloat = new float[kCanvasWidth * kCanvasHeight * 4]; 235 | uint8_t* cpuFramebufferU8 = new uint8_t[kCanvasWidth * kCanvasHeight * 3]; 236 | #endif 237 | 238 | // make shaders 239 | gShaderDraw = makeShader(draw_frag); 240 | gShaderPresent = makeShader(present_frag); 241 | 242 | // main accumulator loop 243 | accumulatorSetup(); 244 | #if !DESPERATE 245 | glClearColor(0, 0, 0, 0); 246 | glClear(GL_COLOR_BUFFER_BIT); 247 | #endif 248 | for ( 249 | int sampleCount = 0; 250 | #ifdef RENDER_EXACT_SAMPLES 251 | sampleCount < RENDER_EXACT_SAMPLES; 252 | #else 253 | #ifdef RENDER_MIN_SAMPLES 254 | (sampleCount < RENDER_MIN_SAMPLES) || 255 | #endif 256 | #ifdef RENDER_MAX_SAMPLES 257 | (sampleCount < RENDER_MAX_SAMPLES) && 258 | #endif 259 | (timeGetTime() < startTime + RENDER_MAX_TIME_MS); 260 | #endif 261 | ++sampleCount 262 | ) 263 | { 264 | #if _DEBUG 265 | printf("accumulate sample %d\n", sampleCount); 266 | #endif 267 | 268 | #if !DESPERATE 269 | PeekMessage(0, 0, 0, 0, PM_REMOVE); 270 | #endif 271 | accumulatorRender(sampleCount); 272 | 273 | // To prevent accidentally hitting esc during long renders. Use Alt+F4 instead. 274 | #if !CAPTURE 275 | if (GetAsyncKeyState(VK_ESCAPE)) 276 | goto abort; 277 | #endif 278 | 279 | 280 | #if RENDER_PROGRESSIVE 281 | #if CAPTURE 282 | if ((sampleCount&(sampleCount-1))==0) 283 | #endif 284 | { 285 | presentSetup(0); 286 | presentRender(hDC); 287 | 288 | accumulatorSetup(); 289 | } 290 | #endif 291 | } 292 | 293 | #if CAPTURE 294 | presentSetup(fbCapture); 295 | presentRender(hDC); 296 | glFinish(); 297 | glReadPixels(0, 0, kCanvasWidth, kCanvasHeight, GL_RGBA, GL_FLOAT, cpuFramebufferFloat); 298 | 299 | for (int y = 0; y < kCanvasHeight; ++y) { 300 | int invy = kCanvasHeight - y - 1; 301 | for (int x = 0; x < kCanvasWidth; ++x) { 302 | for (int i = 0; i < 3; ++i) { 303 | float const fval = cpuFramebufferFloat[(invy*kCanvasWidth + x) * 4 + i]; 304 | uint8_t const u8val = fval < 0 ? 0 : (fval > 1 ? 255 : (uint8_t)(fval * 255)); 305 | cpuFramebufferU8[(y*kCanvasWidth + x) * 3 + i] = u8val; 306 | } 307 | } 308 | } 309 | delete[] cpuFramebufferFloat; 310 | 311 | char name[256]; 312 | sprintf(name, "%llu_%dx%d_%dspp", time(NULL), kCanvasWidth, kCanvasHeight, RENDER_EXACT_SAMPLES); 313 | 314 | #if CAPTURE_SAVE_U8_BIN 315 | { 316 | OutputDebugString("saving u8 bin\n"); 317 | char binname[256]; 318 | sprintf(binname, "%s.u8.bin", name); 319 | FILE* fh = fopen(binname, "wb"); 320 | fwrite(cpuFramebufferU8, 1, kCanvasWidth * kCanvasHeight * 3, fh); 321 | fclose(fh); 322 | } 323 | #endif 324 | 325 | #if CAPTURE_SAVE_F32_BIN 326 | { 327 | OutputDebugString("saving f32 bin\n"); 328 | char binname[256]; 329 | sprintf(binname, "%s.f32.bin", name); 330 | FILE* fh = fopen(binname, "wb"); 331 | fwrite(cpuFramebufferFloat, 4, kCanvasWidth * kCanvasHeight * 4, fh); 332 | fclose(fh); 333 | } 334 | #endif 335 | 336 | #if CAPTURE_SAVE_JPG 337 | { 338 | OutputDebugString("saving jpg\n"); 339 | char jpgname[256]; 340 | sprintf(jpgname, "%s.jpg", name); 341 | stbi_write_jpg(jpgname, kCanvasWidth, kCanvasHeight, 3, cpuFramebufferU8, 100); 342 | } 343 | #endif 344 | 345 | #if CAPTURE_SAVE_PNG 346 | { 347 | OutputDebugString("saving png\n"); 348 | char pngname[256]; 349 | sprintf(pngname, "%s.png", name); 350 | stbi_write_png(pngname, kCanvasWidth, kCanvasHeight, 3, cpuFramebufferU8, kCanvasWidth * 3); 351 | } 352 | #endif 353 | 354 | delete[] cpuFramebufferU8; 355 | #else 356 | presentSetup(0); 357 | while (!GetAsyncKeyState(VK_ESCAPE)) 358 | { 359 | PeekMessage(0, 0, 0, 0, PM_REMOVE); 360 | presentRender(hDC); 361 | } 362 | #endif 363 | 364 | abort: 365 | ExitProcess(0); 366 | return 0; 367 | } 368 | -------------------------------------------------------------------------------- /crinkler-manual.txt: -------------------------------------------------------------------------------- 1 | 2 | CRINKLER - Compressing linker for Windows specialized for 4k intros 3 | 4 | Aske Simon Christensen "Blueberry/Loonies" 5 | Rune L. H. Stubbe "Mentor/TBC" 6 | 7 | Version 2.3 (July 21, 2020) 8 | 9 | Web: http://crinkler.net/ 10 | GitHub: https://github.com/runestubbe/Crinkler 11 | Forum: http://www.pouet.net/prod.php?which=18158 12 | Mail: authors@crinkler.net 13 | 14 | 15 | VERSION HISTORY 16 | --------------- 17 | 21.07.20: 2.3: Crinkler is now open source under the Zlib license! 18 | /TINYHEADER size reduced by another 3 bytes. 19 | Faster hash size optimization, especially with many cores. 20 | Include function name in unresolved symbol error message. 21 | Automatically import from msvcrt. No need for lib any more. 22 | Built-in, minimal console statup code supplying args to main. 23 | Disable built-in startup and msvcrt import via /NODEFAULTLIB. 24 | Support more kinds of lib files (from rustc, for instance). 25 | Don't crash on missing export table (for Wine kernel32.dll). 26 | Only write one dump file if several threads crash. 27 | 28 | 15.06.19: 2.2: /TINYHEADER decompression code is 6 bytes smaller. 29 | Fixed memory size increase of recompressed /TINYHEADER intros. 30 | 31 | 19.01.19: 2.1a: Fixed width of report to make room for the 32 hex columns. 32 | /REUSEMODE:WRITE to write the reuse file without reading it. 33 | 34 | 18.12.18: 2.1: Crinkler executable built for both 32 bit and 64 bit. 35 | New, slightly different model estimation. 8-12x speedup. 36 | Optimized section reordering. About 3-4x speedup. 37 | Optimized and multi-threaded hash size optimization. 38 | New /COMPMODE:VERYSLOW option for a few extra bytes. 39 | Changed default compression mode to SLOW. 40 | Changed default HASHSIZE to 500 and HASHTRIES to 100. 41 | /REUSE option: Use models and ordering from last run. 42 | /REUSEMODE:STABLE to quickly iterate when making changes. 43 | /REUSEMODE:IMPROVE to improve upon previous compression. 44 | Print output file size in report. 45 | More compact bits-per-byte color legend in report. 46 | Choose configuration instead of hiding/showing in report. 47 | 32 column hex view and other adjustments in report. 48 | Avoid crash if an existing file could not be opened. 49 | Updated internal function list to Windows version 1809. 50 | 51 | 28.03.18: 2.0a: Fixed Crinkler crash on recent Windows SDK versions. 52 | Fixed Crinkler crash on forwards from ole32.dll. 53 | Corrected horizontal alignment issue in HTML report. 54 | Support forwarded RVA imports with /TINYIMPORT. 55 | Fixed spurious import of MessageBox with /TINYIMPORT. 56 | Print compatibility warning when using /TINYIMPORT. 57 | Updated internal function list to Windows version 1803. 58 | Extended description in the manual of /TINYIMPORT. 59 | Updated download link for the lib file for msvcrt.dll. 60 | 61 | 28.07.15: 2.0: /TINYHEADER option: smaller decompressor for 1k intros. 62 | /TINYIMPORT option: smaller import code for 1k intros. 63 | /EXPORT option to export code and data symbols. 64 | /SATURATE option to saturate context counters. 65 | /FALLBACKDLL option for when a DLL is not available. 66 | /UNALIGNCODE option to set alignment of all code to 1. 67 | Support for /REPLACEDLL during recompression. 68 | Consistent size between model estimation and reordering. 69 | Header size reduced by 2 bytes. 70 | Print previous size of output file. 71 | Accept version specifier after /SUBSYSTEM value. 72 | Switched from Intel OpenMP to MSVC concurrency API. 73 | 74 | 19.01.13: 1.4: Output EXE files work with recent NVIDIA drivers. 75 | New zero-section header layout saving around 30-50 bytes. 76 | Forwarded RVA imports supported via link-time forwarding. 77 | Dynamic C++ initializers supported. 78 | Support for producing Large Address Aware executables. 79 | Crinkler is Large Address Aware, handling larger inputs. 80 | Report all unresolved symbols and the location of each. 81 | Better resolving of ambiguous label references in report. 82 | Various adjustments to textual output. 83 | /RECOMPRESS overwrites input file by default. 84 | 85 | 05.03.11: 1.3: Fixed Crinkler crash on some AMD systems. 86 | Header size reduced by 21 bytes. 87 | Slightly improved model hash function. 88 | /OVERRIDEALIGNMENTS option to specify label alignments. 89 | No limit on the number of calls in call transform. 90 | Import code and entry point movable by section reordering. 91 | Fixed bug in handling of files with absolute path. 92 | Fixed labels in report showing up in the wrong section. 93 | Crinkler writes .dmp files in case of a crash. 94 | 95 | 05.09.09: 1.2: Output EXE files are now Windows 7 compatible. 96 | Output EXE files are no longer Windows 2000 compatible. 97 | Header size reduced by 16 bytes. 98 | Non-range import code is (usually) slightly smaller. 99 | Slightly improved section ordering estimation. 100 | /RECOMPRESS option to recompress Crinkler-compressed 101 | executables, optionally with different parameters. 102 | /FIX removed, as it is subsumed by /RECOMPRESS. 103 | 104 | 14.01.09: 1.1a: Fixed /TRUNCATEFLOATS crashing in some cases. 105 | Improved /ORDERTRIES estimation when call transform is used. 106 | Sometimes sections were misplaced in the HTML report. 107 | Various improvements to the HTML report. 108 | The /FIX option can input and output to the same file. 109 | Helpful error messages for various unsupported features. 110 | Prefer a custom entry point to a standard library one. 111 | New section in the manual about runtime libraries. 112 | 113 | 12.01.08: 1.1: Support for weak externals (virtual C++ destructors). 114 | Fixed compatibility with Data Execution Prevention. 115 | /REPORT option for a colorful HTML compression report. 116 | /TRUNCATEFLOATS option to mutilate float constants. 117 | /SAFEIMPORT is now default, disabled with /UNSAFEIMPORT. 118 | Slightly smaller overhead if range importing is not used. 119 | Fixed some problems with compressing very small files. 120 | /VERBOSE:FUNCTIONS removed, as it is subsumed by /REPORT. 121 | Remaining /VERBOSE options renamed to /PRINT. 122 | Maximum number of ORDERTRIES increased to 100000. 123 | 124 | 07.01.07: 1.0a: New /VERBOSE:FUNCTIONS options to sort the functions. 125 | Various verbose output fixes. 126 | Various crash fixes. 127 | A fix to the /FIX Crinkler version recognizer. 128 | 129 | 27.12.06: 1.0: Output EXE files are now Windows Vista compatible. 130 | Compression tweak for greatly improved compression ratio. 131 | Much faster compression. 132 | Automatically takes advantage of multiple processors. 133 | Improved Visual Studio 2005 integration. 134 | /COMPMODE:INSTANT option for very quick compression. 135 | /ORDERTRIES option to try out different section orderings. 136 | /SAFEIMPORT option to insert a check for nonexistent DLLs. 137 | /PROGRESSGUI option for a graphical progress bar. 138 | /REPLACEDLL option to replace one DLL with another. 139 | /FIX option to fix compatibility problems of older versions. 140 | 141 | 09.02.06: 0.4a: Fixed linker crash problem with blank member entries 142 | in some library files (such as glut32). 143 | The /PRIORITY option was not mentioned in the 144 | commandline usage help. 145 | 146 | 18.12.05: 0.4: Changed header and import code to make output EXE files 147 | compatible with 64-bit versions of Windows. 148 | Fixed a bug in the ordinal range import mechanism. 149 | Added a switch to control the process priority. 150 | Added a warning for range import of an unused DLL. 151 | Some more header squeezing. 152 | 153 | 31.10.05: 0.3: Output EXE files are now Windows 2000 compatible. 154 | Added a number of verbose options to output useful 155 | information about the program being compressed. 156 | Added an option for transforming function calls to 157 | use absolute offsets to improve compression. 158 | Fixed a bug in the linker regarding identically named 159 | sections. 160 | Fixed a potential crash bug in the linker. 161 | Various small tweaks and optimizations. 162 | 163 | 23.07.05: 0.2: Fixed bug in the decompressor. 164 | Changed the behaviour of the /CRINKLER option. 165 | Added timing to the progress bars. 166 | Some updates to the manual and usage description. 167 | 168 | 21.07.05: 0.1: First release. 169 | 170 | 171 | 172 | BACKGROUND 173 | ---------- 174 | 175 | Ever since the concept of size-limited demo competitions was 176 | introduced in the early 1990's (and before that as well), people have 177 | been using executable file compressors to reduce the size of their 178 | final executables. An executable file compressor is a program that 179 | takes as input an executable file and produces a new executable file 180 | which has the same behaviour as the original one but is (hopefully) 181 | smaller. 182 | 183 | The usual technique employed by executable file compressors is to 184 | compress the contents of the executable file using some general 185 | purpose data compression method and prepend to this compressed data a 186 | small piece of code (the decompressor) which decompresses the contents 187 | into memory in such a way that it looks to the code as if the original 188 | executable file had been loaded into memory in the normal way. 189 | 190 | The size of the decompressor is usually around a few hundred bytes, 191 | depending on the complexity of the compression method. This 192 | constitutes an unavoidable overhead in the compressed file, which is 193 | particularly evident for small files, such as 4k intros. Furthermore, 194 | the header of the Windows EXE file format contains a lot of 195 | information that needs to be there at fixed offsets in order for 196 | Windows to be able to load the file. The presence of these overheads 197 | from the header and decompressor motivated people to look for other 198 | means of compressing their 4k intros. 199 | 200 | Until Crinkler came around, the most popular strategy for compressing 201 | 4k intros for Windows was CAB dropping: A few simple transformations 202 | are performed on the executable to make it compress better (such as 203 | merging sections and setting unused header fields to zero), and the 204 | result is compressed using the Cabinet Compression tool included with 205 | Windows. The resulting .CAB file is renamed to have .BAT extension, 206 | and some commands are inserted into the file such that when the .BAT 207 | file is executed, it decompresses the executable to disk (using the 208 | Cabinet decompression command), runs the executable and then deletes 209 | the executable again. This saves the size of the decompression code 210 | (since an external program is used to do the decompression) and some 211 | of the size of the header (since the header can be compressed). 212 | 213 | Various dropping strategies combined with other space-saving hacks 214 | people employed on their 4k intros (in particular import by ordinal) 215 | caused severe compatibility problems. More often than not, people 216 | who wanted to run a newly released 4k intro found that it did not 217 | work on their own machine. It became customary to include a 218 | 'compatible' version in the distribution which was larger than 4k 219 | but worked on all machines. For a time, it seemed that the term 220 | '4k intro' meant '4k on the compo machine' intro. 221 | 222 | The main motivation for starting the Crinkler project was the feeling 223 | that the existing means available for compressing 4k intros were 224 | unsatisfactory. We want 4k intros that are self-contained EXE 225 | files. We want 4k intros that are 4 kilobytes in size. Our aim for 226 | Crinkler is to be the cleanest, most effective and most compatible 227 | executable file compressor for Windows 4k intros. 228 | 229 | 230 | 231 | COMPATIBILITY 232 | ------------- 233 | 234 | The goal of Crinkler is for the produced EXE files to be compatible 235 | with all widely used Windows versions and configurations. As of 236 | version 2.0a, the EXE files produced by Crinkler are, to the best of 237 | our knowledge, compatible with Windows XP, Windows Vista, Windows 7, 238 | Windows 8 and Windows 10, both 32 bit and 64 bit versions. They are 239 | compatible with Data Execution Prevention and with execution hooks 240 | that inspect the import or export table of launched executables 241 | (graphics drivers are known to do this). 242 | 243 | It is not a primary goal of Crinkler to anticipate incompatibilities 244 | that may arise in the future as a consequence of new Windows versions, 245 | graphics drivers or other widespread system changes. Guaranteeing such 246 | compatibility would require Crinkler to follow the EXE file format 247 | specification to the letter, precluding most of the header hacks that 248 | Crinkler utilizes in order to reduce the size overhead of the EXE 249 | format as much as possible. Rather, we strive to continually monitor 250 | the compatibility situation and release a new, fixed version of 251 | Crinkler whenever a situation arises that affects the compatibility 252 | severely (such as a new, incompatible version of Windows). This has 253 | occurred several times already throughout the history of Crinkler. 254 | 255 | Each new version of Crinkler not only produces executables that are 256 | compatible with the current majority of targeted systems. It also 257 | includes a way of fixing old Crinkler executables to have the same 258 | level of compatibility. See the section on recompression for more 259 | details on this feature. 260 | 261 | This compatibility strategy ensures that intros made using Crinkler 262 | will continue to be accessible to their audience, even if the Windows 263 | EXE loader changes in an incompatible way that could not be 264 | anticipated at the time the intro was produced. 265 | 266 | 267 | 268 | INTRODUCTION 269 | ------------ 270 | 271 | Crinkler is a different approach to executable file compression. While 272 | an ordinary executable file compressor operates on the executable file 273 | produced by the linker from object files, Crinkler replaces the linker 274 | by a combined linker and compressor. The result is an EXE file which 275 | does not do any kind of dropping. It decompresses into memory like a 276 | traditional executable file compressor. 277 | 278 | Crinkler employs a range of techniques to reduce the size of the 279 | resulting EXE file beyond what is usually obtained by using CAB 280 | compression: 281 | 282 | - Having control over the linking step gives much more flexibility in 283 | the optimizations and transformations possible on the data before 284 | and after compression. 285 | 286 | - The compression technique used by Crinkler is based on context 287 | modelling, which is far superior in compression ratio to the LZ 288 | variants used by CAB and most other compressors. The disadvantage of 289 | context modelling is that it is extremely slow, but this is of 290 | little importance when only 4 kilobytes need to be compressed. It 291 | also needs quite a lot of memory for decompression, but this is 292 | again not a problem, since the typical 4k intro uses a lot of memory 293 | anyway. 294 | 295 | - The actual compression algorithm performs many passes over the data 296 | in order to optimize the internal parameters of the compressor. This 297 | results in slower compression, but this is usually a reasonable 298 | price to pay for the extra bytes gained on the file size. 299 | 300 | - The contents of the executable are split into two parts - a code 301 | part and a data part - and each of these are compressed 302 | individually. This leads to better compression, as code and data are 303 | usually very different in structure and so do not benefit from being 304 | compressed together. 305 | 306 | - DLL functions are imported by hash code. This is robust to 307 | structural changes to the DLL between different versions while being 308 | quite compact - only 4 bytes per imported function. For DLLs with 309 | fixed relative ordinals (such as opengl32), a special technique, 310 | ordinal range import, can be used to further reduce the number of 311 | hash codes needed. 312 | 313 | - Much of the data in the EXE header is actually ignored by the EXE 314 | loader. This space is used for some of the decompression code. 315 | 316 | Using Crinkler is somewhat different from using an ordinary executable 317 | file compressor because of the linking step. In the following 318 | sections, we describe its use in detail. 319 | 320 | 321 | 322 | INSTALLATION 323 | ------------ 324 | 325 | To use as a stand-alone linker, Crinkler does not need any 326 | installation. Simply run crinkler.exe from the commandline with 327 | appropriate arguments, as described in the next section. 328 | 329 | However, if you are using Microsoft Visual Studio to develop your 330 | intro, the easiest way to use Crinkler is to run it in place of the 331 | normal Visual Studio linker. Crinkler has been designed as a drop-in 332 | replacement of the Visual Studio linker, supporting the same basic 333 | options. All of the options can then be set using the Visual Studio 334 | configuration window. 335 | 336 | Unfortunately, Visual Studio does not (as of this writing) support 337 | replacing its linker by a different one. So what you have to do to 338 | make Visual Studio use Crinkler for linking is the following: 339 | 340 | - Copy crinkler.exe to your project directory or to some other 341 | directory of your choice and rename it to link.exe. If you are using 342 | some other linker with a different name, such as the one used with 343 | the Intel C++ compiler, call it whatever the name of the linker is. 344 | 345 | - For Visual Studio 2008 and older, select Tools/Options... and go to 346 | Projects and Solutions/VC++ Directories. For Visual Studio 2010 or 347 | newer, open a project, select View/Property Manager, expand a project 348 | and a configuration, double click on Microsoft.Cpp.Win32.user and go 349 | to Common Properties/VC++ Directories. 350 | 351 | - At the top of the list for Executable files, add the directory where 352 | you placed Crinkler named link.exe, or add $(SolutionDir) to make it 353 | search in the project directory. 354 | 355 | - In the Release configuration (or whichever configuration you want to 356 | enable compression), under Linker/Command Line/Additional Options, 357 | type in /CRINKLER, along with any other Crinkler options you want to 358 | set. See the next section for more details on options. Also set 359 | Linker/Manifest File/Generate Manifest to No and 360 | C/C++/Optimization/Whole Program Optimization to No. 361 | 362 | If you have Visual Studio installed but want to run Crinkler from the 363 | commandline, the easiest way is to use the Visual Studio Command 364 | Prompt (available from the Start menu), since this sets up the LIB 365 | environment variable correctly. You can read off the value of the 366 | environment variables by running the 'set' command in this command 367 | prompt. If you are using a different command prompt, you will have to 368 | set up the LIB environment variable manually, or use the /LIBPATH 369 | option. 370 | 371 | 372 | 373 | USAGE 374 | ----- 375 | 376 | The general form of the command line for Crinkler is: 377 | 378 | CRINKLER [options] [object files] [library files] [@commandfile] 379 | 380 | When running from within Visual Studio, the object files will be the 381 | ones generated from the sources in the project. The library files will 382 | be the standard set of Win32 libraries, plus any additional library 383 | files specified under Linker/Input/Additional Dependencies. 384 | 385 | Crinkler automatically links to msvcrt.dll - the Visual Studio 6 386 | runtime library. You don't need to specify a library file for this. 387 | 388 | 389 | The following options are compatible with the VS linker and can be set 390 | using switches in the Visual Studio configuration window: 391 | 392 | /SUBSYSTEM:CONSOLE 393 | /SUBSYSTEM:WINDOWS 394 | (Linker/System/SubSystem) 395 | 396 | Specify the Windows subsystem to use. If the subsystem is CONSOLE, 397 | a console window will be opened when the program starts. The 398 | subsystem also determines the name of the default entry point (see 399 | /ENTRY). The default subsystem is WINDOWS. 400 | 401 | /LARGEADDRESSAWARE 402 | /LARGEADDRESSAWARE:NO 403 | (Linker/System/Enable Large Addresses) 404 | 405 | Specify whether the executable is able to handle addresses above 406 | 2 gigabytes. If this option is enabled, the executable will be able 407 | to allocate close to 4 gigabytes of memory. 408 | 409 | /OUT:[file] 410 | (Linker/General/Output File) 411 | 412 | Specify the name of the resulting executable file. The default 413 | name is out.exe. 414 | 415 | /ENTRY:[symbol] 416 | (Linker/Advanced/Entry Point) 417 | 418 | Specify the entry label in the code. The default entry label is 419 | mainCRTStartup for CONSOLE subsystem applications and 420 | WinMainCRTStartup for WINDOWS subsystem applications. 421 | 422 | If you use the CONSOLE subsystem without changing the entry point from 423 | its default value and don't define mainCRTStartup yourself, Crinkler 424 | will insert a small entry point that will read the commandline and 425 | call main with the usual argc and argv parameters. 426 | 427 | /NODEFAULTLIB 428 | (Linker/Input/Ignore All Default Libraries) 429 | 430 | Disable the automatic linking against msvcrt.dll and the automatic 431 | insertion of mainCRTStartup for CONSOLE applications. 432 | 433 | /LIBPATH:[path] 434 | (Linker/General/Additional Library Directories) 435 | 436 | Add a number of directories (separated by semicolons) to the ones 437 | searched for library files. If a library is not found in any of 438 | these, the directories mentioned in the LIB environment variable 439 | are searched. 440 | 441 | @commandfile 442 | 443 | Commandline arguments will be read from the given file, as if they 444 | were given directly on the commandline. 445 | 446 | 447 | In addition to the above options, a number of options can be given to 448 | control the compression process. These can be specified under 449 | Linker/Command Line/Additional Options: 450 | 451 | /CRINKLER 452 | 453 | Enable the Crinkler compressor. If this option is disabled, 454 | Crinkler will search through the path for a command with the same 455 | name as itself, skipping itself, and pass all arguments on to this 456 | command instead. This will normally invoke the Visual Studio 457 | linker. If the name of the Crinkler executable is crinkler.exe, 458 | this option is enabled by default, otherwise it is disabled by 459 | default. 460 | 461 | /RECOMPRESS 462 | 463 | Decompress a Crinkler-compressed executable and recompress it 464 | using the given options. The resulting executable will have the 465 | same level of compatibility as one produced directly by the 466 | current version of Crinkler. See the section on compatibility for 467 | more information on the compatibility of Crinkler-produced 468 | executables. 469 | 470 | When this option is specified, Crinkler takes a single file 471 | argument, which must be an EXE file produced by Crinkler 0.4 or 472 | newer. 473 | 474 | See the section on recompression below for a description of the 475 | options that can be given to control the decompression process. 476 | 477 | /PRIORITY:IDLE 478 | /PRIORITY:BELOWNORMAL 479 | /PRIORITY:NORMAL 480 | 481 | Select the process priority at which Crinkler will run while 482 | compressing. The default priority is BELOWNORMAL. Use IDLE if you 483 | want Crinkler to disturb you as little as possible. Use NORMAL if 484 | you don't need your machine for anything else while compressing. 485 | 486 | /COMPMODE:INSTANT 487 | /COMPMODE:FAST 488 | /COMPMODE:SLOW 489 | /COMPMODE:VERYSLOW 490 | 491 | Choose between four different algorithms for the model estimation. 492 | The FAST compression mode performs a very quick estimation, whereas 493 | the SLOW mode takes up to some tens of seconds for a typical 4k, 494 | but also compresses significantly better. VERYSLOW is about 5-10x 495 | slower than SLOW and typically a few bytes better. INSTANT skips 496 | model estimation entirely and just uses a fixed set of models and 497 | weights. It also skips section reordering and hash table size 498 | optimization. Use INSTANT if you just want to check that your 499 | program works in compressed form and don't care about the size. 500 | The default compression mode is SLOW. 501 | 502 | /SATURATE 503 | 504 | The compressor and decompressor use pairs of 8-bit counters to 505 | track the distributions of 0 and 1 bits for each context. If your 506 | data is very repetitive (contains large blocks of the same pattern 507 | of values repeated over and over again), these counters may wrap 508 | around, which can sometimes hurt compression of these repetitive 509 | areas. 510 | 511 | This option inserts extra code in the decompression header to keep 512 | these counters from wrapping. It is worth trying out if you have 513 | large, repetitive regions and see in the compression report that 514 | the data in these regions suddenly jumps up from lightest green to 515 | slightly darker green for no apparent reason. 516 | 517 | /HASHSIZE:[memory size] 518 | 519 | Specify the amount of memory the decompressor is allowed to use 520 | while decompressing, in megabytes. In general, the more memory the 521 | decompressor is allowed to use, the better the compression ratio 522 | will be, though only slightly. The memory requirements of the 523 | final executable (the size of the executable image when loaded 524 | into memory) will be the maximum of this value and the original 525 | image size. The memory will not be deallocated until the program 526 | terminates, and any heap allocation the program performs will add 527 | to this memory usage. The default value is 100, which is usually a 528 | good compromise. 529 | 530 | /HASHTRIES:[number of retries] 531 | 532 | Specify the number of different hash table sizes the compressor 533 | will try in order to find one with few collisions. More tries lead 534 | to longer compression time but slightly better compression. The 535 | default value is 20. Higher values rarely improve the size by more 536 | than a few bytes. 537 | 538 | /TINYHEADER 539 | Enables an alternative compression algorithm trading off some 540 | compression efficiency for an even smaller decompression overhead. 541 | This can be beneficial when targeting extremely small file sizes 542 | such as 1kb. The simpler decompressor gathers statistics by 543 | repeated linear searches instead of hashing. This results in 544 | an O(n^2) decompression time which can become prohibitively slow 545 | for files significantly larger than 1kb. 546 | 547 | The COMPMODE, HASHSIZE, HASHTRIES, REUSE, SATURATE and EXPORT 548 | options are ignored when TINYHEADER is enabled. 549 | 550 | /TINYIMPORT 551 | Enables a more compact, but less future-proof, function importing 552 | scheme which does not require the explicit storage of function 553 | name hashes. This is achieved by indiscriminately importing every 554 | function from the relevant DLLs. The imported functions are 555 | scattered in an import table based on their function name hashes. 556 | Intuitively, this embeds the hash code entropy directly into the 557 | call instruction. 558 | 559 | Crinkler ensures that the import table size and hash function are 560 | chosen such that there are no collisions between the functions 561 | used by the linked program and other functions which are imported 562 | later from the DLLs. This way, the desired function pointers will 563 | be intact in the import table. 564 | 565 | However, Crinkler can only ensure this for functions that it knows 566 | about. These include the functions present in the DLLs on the 567 | system on which Crinkler is run, plus an internal list consisting 568 | of functions from commonly imported DLLs covering most supported 569 | Windows versions available at the time of release (Spring Creators 570 | Update 2018 version 1803 as of Crinkler 2.0a). 571 | 572 | Thus, this import technique is less resilient to changes in 573 | future windows versions, since when functions are added in a 574 | future version of the DLL, they may collide with functions used by 575 | the program, in which case the program will cease to work. 576 | Programs broken this way cannot be fixed by recompression. 577 | 578 | When using this options, it is strongly recommended to also 579 | distribute safe versions using ths normal import mechanism. 580 | 581 | The UNSAFEIMPORT, FALLBACKDLL and RANGE options are ignored 582 | when TINYIMPORT is enabled. 583 | 584 | /ORDERTRIES:[number of retries] 585 | 586 | Specify the number of section reordering iterations that the 587 | linker will try out in search for the ordering that gives the best 588 | compression ratio. The default is not to do any reordering. 589 | Crinkler starts from a heuristic ordering (the one used when 590 | initially estimating models) and incrementally makes small, random 591 | changes to the ordering to see if it can find one that compresses 592 | better. 593 | 594 | Specifying this option drastically increases the compression time, 595 | since Crinkler has to calculate the compressed size anew on every 596 | reordering. Usually, the size does not improve noticeably after a 597 | few thousand iterations. 598 | 599 | /REUSE:[reuse parameter file name] 600 | /REUSEMODE:STABLE 601 | /REUSEMODE:IMPROVE 602 | /REUSEMODE:WRITE 603 | /REUSEMODE:OFF 604 | 605 | After compression, write information about the selected models, 606 | the ordering of sections and the optimized hash table size to a 607 | text file with the specified name. If the file exists already, 608 | use the parameters in the file as input to the compression in a 609 | manner dependent on the chosen REUSEMODE: 610 | 611 | With STABLE (the default), skip all model estimation, section 612 | reordering and hash table size optimization and simply use the 613 | parameters exactly as in the file. Keep the reuse file as is. 614 | This option can be used to try out small changes to the contents 615 | of the code and data with a stable compression. Thus, it gives 616 | a much more reliable estimation of whether the change was an 617 | improvement or not. It is also useful as a way to compress very 618 | quickly after the first time with a similar compression ratio. 619 | 620 | With IMPROVE, only the section ordering from the file is reused, 621 | and a normal compression procedure is performed. If section 622 | reordering is enabled, it starts from the ordering in the reuse 623 | file and tries to optimize the ordering based on that. The file is 624 | written back only if the final file size is smaller than what 625 | the parameters in the reuse file would have given (which is not 626 | necessarily the size of the existing file, depending on what 627 | changes and operations are performed in the meantime). 628 | The option can be used to check whether better parameters can be 629 | found than the ones cached in the reuse file. It is also a way to 630 | run some extra reordering iterations (if reordering is enabled) 631 | to see if this improves compression. 632 | 633 | For both modes, it can be useful to edit the reuse file by hand 634 | to try out parameters manually or to nudge Crinkler in some 635 | direction. 636 | 637 | With WRITE, the reuse file is not read, but is still written 638 | after compression, overwriting the file if it exists. This can be 639 | conveniently used when reuse is not desired, such that it can be 640 | switched on at any time (by changing the reuse mode to STABLE or 641 | IMPROVE) without needing another compression run. 642 | 643 | With OFF, it is as if no reuse file is specified. This is simply 644 | a way to disable the option without removing the file from the 645 | commandline. 646 | 647 | If COMPMODE is set to INSTANT, the reuse mode is also considered 648 | to be OFF. 649 | 650 | /RANGE:[DLL name] 651 | 652 | Import functions from the given DLL (without the .dll suffix) 653 | using ordinal range import. Ordinal range import imports the first 654 | used function by hash and the rest by ordinal relative to the 655 | first one. Ordinal range import is safe to use on DLLs in which 656 | the ordinals are fixed relative to each other, such as opengl32 or 657 | d3dx9_??. This option can be specified multiple times, for 658 | different DLLs. 659 | 660 | /REPLACEDLL:[oldDLL]=[newDLL] 661 | 662 | Whenever a function is imported from oldDLL, import it from newDLL 663 | instead. DLL replacement is useful when the end user might not 664 | have the version of the DLL that you are linking to. A typical use 665 | is to replace one version of d3dx9_?? by another. Only use this 666 | option if you know that the two DLLs are compatible. When 667 | REPLACEDLL and RANGE are used together, RANGE must refer to the 668 | new DLL. 669 | 670 | /FALLBACKDLL:[firstDLL]=[otherDLL] 671 | 672 | If firstDLL fails to load, try loading otherDLL and import the 673 | functions from there instead. For instance, to use d3dcompiler_47 674 | when available but fall back to d3dcompiler_43 otherwise (since 675 | the shader compiler in d3dcompiler_47 is much faster), link 676 | to d3dcompiler_47 and use: 677 | 678 | /FALLBACKDLL:d3dcompiler_47=d3dcompiler_43 679 | 680 | The FALLBACKDLL option can be used together with REPLACEDLL to 681 | specify a primary DLL other than the one your SDK links to. For 682 | instance, if you are using the legacy DirectX SDK (which links to 683 | d3dcompiler_43) and want to have the above prioritization, use: 684 | 685 | /REPLACEDLL:d3dcompiler_43=d3dcompiler_47 686 | /FALLBACKDLL:d3dcompiler_47=d3dcompiler_43 687 | 688 | Arbitrarily long chains of DLL fallback can be used by specifying 689 | the FALLBACKDLL option multiple times, though the chains can of 690 | course not be cyclic. 691 | 692 | /EXPORT:[name] 693 | /EXPORT:[name]=[symbol] 694 | /EXPORT:[name]=[value] 695 | 696 | Include an export table into the executable, containing an export 697 | with the given name. 698 | 699 | The first version exports an existing symbol under its existing 700 | name. The second version exports an existing symbol under a 701 | different name. The third version creates a 32-bit integer with 702 | the given value and exports it under the given name. The value 703 | can be specified in octal (prefixed with 0), decimal or hexadecimal 704 | (prefixed with 0x) format. 705 | 706 | The first version is compatible with the VS linker, but there is 707 | currently no specific field for it in the configuration window. 708 | 709 | The export table will be compressed along with the other data 710 | in the executable and decompressed to the memory address specified 711 | in the export table pointer in the PE header. Thus, the exports 712 | defined this way are only visible to code inspecting the export 713 | table after decompression has taken place. 714 | 715 | For PE header technical reasons, all exports must be placed earlier 716 | in memory than the export table. Thus, only symbols in the code and 717 | data sections can be exported. If an uninitialized (BSS) symbol is 718 | exported, it will be automatically moved to the data section (with 719 | a warning). Beware that this will move the whole section containing 720 | the symbol, so other symbols might be moved along with it. 721 | 722 | The EXPORT option can be used to signal to the graphics driver that 723 | your program desires to run on the high-performance GPU in a multi- 724 | GPU system. This saves the user from having to right-click on the 725 | executable and select "Run with graphics processor...". 726 | 727 | To request high performance on NVIDIA Optimus systems, use: 728 | 729 | /EXPORT:NvOptimusEnablement=1 730 | 731 | To request high performance on AMD PowerXpress/Enduro systems, use: 732 | 733 | /EXPORT:AmdPowerXpressRequestHighPerformance=1 734 | 735 | An arbitrary number of exports can be specified, so the two high 736 | performance declarations can be used together if you have space 737 | enough to spare. 738 | 739 | /UNSAFEIMPORT 740 | 741 | If the executable fails to load some DLL, it will normally pop up 742 | a message box with the DLL name. This option disables this check 743 | to save a few bytes (usually around 20). With unsafe import, the 744 | executable will crash if a needed DLL is not found. 745 | 746 | /TRANSFORM:CALLS 747 | 748 | Change the relative jump offsets in all internal call instructions 749 | (E8 opcode) into absolute offsets from the start of the code. This 750 | usually improves compression, since multiple calls to the same 751 | function become identical. The transformation has an overhead of 752 | about 20 bytes for the detransformation code, but the net savings 753 | on a full 4k can be as large as 50 bytes, depending on the number 754 | of calls in your code. 755 | 756 | /NOINITIALIZERS 757 | 758 | Disable the inclusion of dynamic C++ initializers. The default is 759 | to insert calls to each of the initializers just before the entry 760 | point. 761 | 762 | /TRUNCATEFLOATS:[number of bits] 763 | 764 | Floating point constants can take up a significant amount of space 765 | in an intro, and often much of this space is wasted because the 766 | constants have more precision than needed. Typically, many bytes 767 | can be saved by rounding floating point constants to "nice" values 768 | - that is, values where many bits in the mantissa are zero. 769 | However, such rounding is cumbersome, especially when the 770 | constants are written in decimal notation. 771 | 772 | The purpose of the /TRUNCATEFLOATS option is to automate this 773 | rounding process. When this option is given, Crinkler tries to 774 | identify float and double constants and round them to the number 775 | of bits given (between 1 and 64). If no number is given, 64 is 776 | assumed. 777 | 778 | Typically, object files do not contain any information about what 779 | data is floating point constants and what is not (though the file 780 | format does support such information). This means that in order to 781 | identify floating point constants, Crinkler has to resort to 782 | heuristics based on label names. These heuristics are able to 783 | recognize constants in code and some variables, but far from all. 784 | 785 | You can tell Crinkler explicitly that some variable contains float 786 | data and how much it should be truncated by having the variable 787 | name (or label) start with tf[n]_ where [n] is the number of bits 788 | to truncate the constants to. The number of bits can be omitted, 789 | in which case the number of bits given in the argument to 790 | /TRUNCATEFLOATS is used. Such variables will still only be 791 | truncated if the /TRUNCATEFLOATS option is given. Example: 792 | 793 | const float tf14_positions[] = { 0.1f, 0.35f, 0.25f }; 794 | 795 | This will truncate the constants in the table to 14 bits (5 bits 796 | of mantissa), resulting in the values 0.099609375, 0.3515625 and 797 | 0.25, respectively. Tip: rather than changing the variable name 798 | and all references to it each time you want to change the 799 | truncation precision, use a define: 800 | 801 | #define positions tf14_positions 802 | 803 | Note that /TRUNCATEFLOATS is an unstable and highly experimental 804 | feature. Make sure to test the compressed file to verify that the 805 | result is acceptable. Remember to include the musician in this 806 | verification process. :) 807 | 808 | /OVERRIDEALIGNMENTS:[bits of alignment] 809 | 810 | It is often possible to improve compression by placing 811 | uninitialized variables at addresses divisible by high powers of 812 | two, since this will cause all references to these addresses to 813 | contain more zeros. 814 | 815 | The PE file format only supports up to 13 bits of alignment 816 | (8192), and some tools do not even expose this support fully (for 817 | instance, Nasm only supports alignments up to 64). Usually, much 818 | higher alignments are desirable. 819 | 820 | Crinkler supports explicit alignment of labels at up to one 821 | gigabyte (30 bits). When you specify the /OVERRIDEALIGNMENTS 822 | option, Crinkler will look for labels containing the string 823 | align[n] where [n] is the number of bits of alignment desired 824 | (e.g. 8 for 256-byte alignment). It will then align the section 825 | containing that label such that the label address is divisible by 826 | 2^[n]. The label does not have to be at the beginning of the 827 | section, but there can be at most one explicitly aligned label in 828 | each section. 829 | 830 | The alignment specifier can optionally include an alignment 831 | offset, specified by the string align[n]_[m] where [n] is the 832 | number of bits of alignment and [m] is the offset in bytes. This 833 | will place the label [m] bytes after an aligned address, i.e. such 834 | that the address minus [m] is divisible by 2^[n]. 835 | 836 | If a numerical argument is given to /OVERRIDEALIGNMENTS, all 837 | uninitialized sections which do not contain an explicitly aligned 838 | label will be aligned to the given number of bits (if larger than 839 | their original alignment). If the option is specified without 840 | argument, uninitialized sections which do not contain an 841 | explicitly aligned label will be aligned as specified in the 842 | object file, as normally. 843 | 844 | A convenient way to specify explicit alignments in C++ code is in 845 | a header file included by all files in the project, containing 846 | definitions like this: 847 | 848 | #define MusicBuffer MusicBuffer_align24 849 | 850 | In assembler files, alignments can be specified as local labels: 851 | 852 | MusicBuffer: 853 | .align24 854 | ; buffer space here 855 | 856 | Explicit alignment can be used on code and data sections as well, 857 | except for the section containing the entry point, which will 858 | always be 1-byte aligned. The space between the sections will be 859 | padded with zero bytes. 860 | 861 | /UNALIGNCODE 862 | 863 | Force all code sections to use alignment of 1, eliminating all 864 | padding between them. This usually improves compression, but 865 | can result in slightly lower performance if some functions are 866 | called in performance critical loops. 867 | 868 | The /OVERRIDEALIGNMENTS mechanism has priority over /UNALIGNCODE, 869 | so if you want to excempt a few functions from being unaligned, 870 | you can specify an explicit alignment for these as described for 871 | /OVERRIDEALIGNMENTS. 872 | 873 | 874 | Finally, Crinkler has a number of options for controlling the output 875 | during compression. Just like the other options, these can be 876 | specified under Linker/Command Line/Additional Options: 877 | 878 | /REPORT:[HTML file name] 879 | 880 | Write an HTML file with a detailed, colorful, interactive report 881 | on the compression result. The code section will be shown as hex 882 | dump and disassembly of the code, and the data section will be 883 | shown as hex and ascii dump. All bytes will be colored to show how 884 | much that byte was compressed. This report can be useful in 885 | determining which parts of the executable take up the most space 886 | and which things to change to reduce the size. 887 | 888 | /PRINT:LABELS 889 | 890 | Print a list of all labels in the program along with uncompressed 891 | and compressed sizes for the data between the labels. This is a 892 | stripped down version of the information provided by the /REPORT 893 | option. 894 | 895 | /PRINT:IMPORTS 896 | 897 | List all functions imported from DLLs. The functions are grouped 898 | by DLL, and functions imported by ordinal range import are grouped 899 | into ranges. 900 | 901 | /PRINT:MODELS 902 | 903 | List the model masks and weights selected by the compressor. This 904 | is mostly for internal use. 905 | 906 | /PROGRESSGUI 907 | 908 | Open a window showing a graphical progress indicator. 909 | 910 | 911 | An example commandline for linking and compressing an intro could look 912 | like this (split on multiple lines for readability): 913 | 914 | crinkler.exe /OUT:micropolis.exe /SUBSYSTEM:WINDOWS /RANGE:opengl32 915 | /COMPMODE:SLOW /ORDERTRIES:1000 /PRINT:IMPORTS /PRINT:LABELS 916 | kernel32.lib user32.lib gdi32.lib opengl32.lib glu32.lib winmm.lib 917 | micropolis\startup.obj micropolis\render.obj 918 | micropolis\render-asm.obj micropolis\sound.obj 919 | micropolis\sound-asm.obj 920 | 921 | 922 | 923 | RECOMPRESSION 924 | ------------- 925 | 926 | A new feature in Crinkler 1.2 is the abillity to recompress an already 927 | Crinkler-compressed executable. The main purpose for the feature is to 928 | patch an executable compressed using an earlier version of Crinkler so 929 | that it runs on recent Windows versions. But it can also be used 930 | more generally to change some of the compression parameters of a 931 | compressed program without performing the whole linking and 932 | compression process from scratch and without access to the original 933 | object files. Particularly, if your output executable after a long 934 | time spent compressing is just a few bytes too big due to bytes lost 935 | to hashing, you can recompress the output executable, specifying a 936 | higher value for /HASHSIZE and/or /HASHTRIES, and thus avoid running 937 | through the whole compression process again. 938 | 939 | Recompression mode is activated by the /RECOMPRESS option. When this 940 | option is specified, Crinkler takes a single file argument, which must 941 | be an EXE file produced by Crinkler 0.4 or newer. Most options then 942 | take on slightly different meanings, as described here. 943 | 944 | The /CRINKLER, /PRIORITY, @commandfile and /PROGRESSGUI options work 945 | as normally. The /ENTRY, /LIBPATH, /ORDERTRIES, /RANGE, /FALLBACKDLL, 946 | /UNSAFEIMPORT, /TRANSFORM:CALLS, /NOINITIALIZERS, /TRUNCATEFLOATS, 947 | /OVERRIDEALIGNMENTS, /UNALIGNCODE, /TINYHEADER and /TINYIMPORT options 948 | are ignored, as the parameters specified by these options cannot be 949 | changed via recompression. The /PRINT options are also ignored. The 950 | remaining options work as follows: 951 | 952 | /SUBSYSTEM:CONSOLE 953 | /SUBSYSTEM:WINDOWS 954 | 955 | If this option is given, it specifies the Windows subsystem to use 956 | as normally. If it is omitted, the original subsystem will be 957 | used. 958 | 959 | /LARGEADDRESSAWARE 960 | /LARGEADDRESSAWARE:NO 961 | 962 | If this option is given, it specifies large address awareness of the 963 | executable as normally. If it is omitted, the original large address 964 | awareness will be used. 965 | 966 | /OUT:[file] 967 | 968 | Specify the name of the resulting executable file. The default is 969 | to overwrite the input file. 970 | 971 | /COMPMODE:INSTANT 972 | /COMPMODE:FAST 973 | /COMPMODE:SLOW 974 | /COMPMODE:VERYSLOW 975 | 976 | If this option is specified, the compression models will be 977 | reestimated using the specified compression mode. If the option is 978 | omitted, the models used for the original compression will be used 979 | for the recompression, and no model estimation will be performed. 980 | If the executable was originally produced by Crinkler 1.0 or 981 | newer, this will typically yield a compression ratio similar to 982 | the original compression. 983 | 984 | /SATURATE 985 | /SATURATE:NO 986 | 987 | If this option is given, it specifies saturation as normally. If 988 | it is omitted, the original saturation mode will be used. 989 | 990 | /HASHSIZE:[memory size] 991 | 992 | If neither this option nor a compression mode is specified, the 993 | original, optimized hash size will be used. Recompression speed 994 | will be similar to INSTANT compression mode in this case. 995 | 996 | If a compression mode is specified but this option is omitted, 997 | hash size optimization will be performed using the hash size 998 | specified for the original file. 999 | 1000 | If this option is given, hash size optimization takes place 1001 | normally, using the specified maximum size. 1002 | 1003 | /HASHTRIES:[number of retries] 1004 | 1005 | If hash size optimization takes place, this option specifies the 1006 | number of tries as normally. Otherwise it is ignored. 1007 | 1008 | /REPLACEDLL:[oldDLL]=[newDLL] 1009 | 1010 | Replaces an original DLL by a new one. Only works if the names 1011 | of the DLLs are exactly the same length. 1012 | 1013 | /STRIPEXPORTS 1014 | 1015 | This is a recompression specific option which instructs Crinkler 1016 | to strip away any existing exports from the executable. New exports 1017 | can be added using the /EXPORT option whether or not the existing 1018 | exports are stripped away. 1019 | 1020 | /EXPORT:[name] 1021 | /EXPORT:[name]=[symbol] 1022 | /EXPORT:[name]=[value] 1023 | 1024 | Adds an export to the executable, as normally. The first two 1025 | versions can only refer to an existing export in the executable 1026 | that was exported using one of the first two versions in the first 1027 | place. They can refer to such an export even if existing exports 1028 | are stripped away using the /STRIPEXPORTS option. 1029 | 1030 | If an export already exists with the same name, the new export 1031 | replaces the existing one. 1032 | 1033 | /REPORT:[HTML file name] 1034 | 1035 | Writes out an HTML file as normally. Since no symbol information 1036 | is available, this will be a plain disassembly/hex dump without 1037 | labels or cross-linking. 1038 | 1039 | 1040 | 1041 | STANDARD RUNTIME LIBRARIES 1042 | -------------------------- 1043 | 1044 | Under normal circumstances, the Visual Studio compiler generates code 1045 | that requires a C runtime library containing standard C functions and 1046 | various support functions. These functions can either be linked in 1047 | statically (included into the executable) or dynamically via a runtime 1048 | DLL. For size-sensitive applications, you should always link 1049 | dynamically, which is achieved by setting C/C++/Code 1050 | Generation/Runtime Library to Multi-threaded DLL (/MD). 1051 | 1052 | Note however, that the standard runtime libraries for Visual Studio 1053 | 2005 or newer will not work with Crinkler-compressed executables, 1054 | since these runtime libraries require a manifest in the executable, 1055 | and Crinkler does not support manifests. Furthermore, these DLLs are 1056 | not present by default on Windows installations, so you will usually 1057 | not want your program to be dependent on them. 1058 | 1059 | Unless explicitly disabled by using the /NODEFAULTLIB option, Crinkler 1060 | automatically links to the Visual Studio 6 runtime library - msvcrt.dll 1061 | - which is distributed with all Windows versions. 1062 | 1063 | There are a couple of caveats to using an older runtime library than 1064 | the compiler expects, though. With out-of-the-box compilation 1065 | options, the Visual Studio compiler generates code that requires some 1066 | support functions which are only present in newer runtime DLLs. To 1067 | avoid these dependencies, set the following options under C/C++/Code 1068 | Generation: 1069 | 1070 | - Basic Runtime Checks: Default 1071 | - Buffer Security Check: No (/GS-) 1072 | 1073 | Also, do not use C++ exception handling in your code. And do not use 1074 | STL classes, since they use exceptions all over. 1075 | 1076 | The best strategy is of course to avoid linking to a runtime DLL at 1077 | all, assuming you can do without the functions provided by the 1078 | standard runtime library. This will save the space for importing the 1079 | runtime DLL. 1080 | 1081 | To reduce the dependencies on the standard runtime DLL as much as 1082 | possible, set the following options: 1083 | 1084 | - C/C++/Optimization/Enable Intrinsic Functions: Yes (/Oi). This will 1085 | cause several standard functions (mainly math, string and memory 1086 | functions) to generate inline code rather than a function call. 1087 | - C/C++/Code Generation/Floating Point Model: Fast (/fp:fast). 1088 | - C/C++/Command Line: Add the option /QIfist. This will cause 1089 | conversions from floating point to integer to use the FIST 1090 | instruction rather than calling a conversion function. Note that 1091 | this changes the semantics of conversions from truncation to 1092 | round-to-nearest (unless you explicitly change the rounding mode of 1093 | the FPU). On the other hand, it will also give a considerable speed 1094 | boost. 1095 | 1096 | 1097 | 1098 | RECOMMENDATIONS 1099 | --------------- 1100 | 1101 | There are a number of things you can do as intro programmer to boost 1102 | the compression achieved by Crinkler even further. This section 1103 | gives some advice on these. 1104 | 1105 | - Since much of the effectiveness of Crinkler comes from separating 1106 | code and data into different parts of the file and compressing each 1107 | part individually, it is important that this separation is 1108 | possible. Mark your code and data sections as containing code and 1109 | data, respectively, and do not put both code and data into the same 1110 | section. See your assembler manual for information about how to do 1111 | this. For instance, in Nasm, you can write the keyword "text" or 1112 | "data" after the section name and give sections different names to 1113 | prevent them from being merged by the assembler. 1114 | 1115 | - Split both your code and your data into as many sections as 1116 | possible. This gives Crinkler more opportunities to select the 1117 | ordering of the sections to optimize the compression ratio. 1118 | 1119 | - If you are using OpenGL, try using ordinal range import for 1120 | opengl32. If you are using Direct3D, try using ordinal range import 1121 | for d3dx9_??. This may reduce the space needed for function hash 1122 | codes. 1123 | 1124 | - If you are only importing functions from DLLs which are present on 1125 | all Windows systems (d3dx9_?? is not), you can "safely" use the 1126 | /UNSAFEIMPORT option. Run Crinkler with the /PRINT:IMPORTS option 1127 | to check which DLLs you are importing from. 1128 | 1129 | - Avoid large blocks of data, even if they are all zero. Use 1130 | uninitialized (bss) sections instead. Crinkler does not cope well 1131 | with large amounts of data. Be aware that the compressor may use an 1132 | amount of memory up to about 4000 times the uncompressed code/data 1133 | size (whichever is largest). 1134 | 1135 | - When you perform detailed size comparisons, always use the SLOW 1136 | compression mode with plenty of ORDERTRIES and compare the 1137 | "Ideal compressed total size" values. The INSTANT and FAST modes 1138 | are only intended for use during testing and to give a rough estimate 1139 | of the compressed size. Use the /REUSE option when making small 1140 | changes to achieve stable size comparison. Also note that the 1141 | compression is tuned for the 4k size target, so any size comparisons 1142 | you perform on smaller files might turn out to behave differently 1143 | when you get nearer to 4k. 1144 | 1145 | - As a matter of good conduct, do not use TINYIMPORT or UNSAFEIMPORT 1146 | if you can spare the space, and do not set HASHSIZE higher than 1147 | you need. In other words, if your final intro is well below the size 1148 | limit, remove the UNSAFEIMPORT option (if you added it in the 1149 | first place) and then lower HASHSIZE in order not to waste memory 1150 | unnecessarily. Also consider adding the high-performance GPU request 1151 | exports as described under the EXPORT option if your intro could 1152 | benefit from it. 1153 | 1154 | 1155 | 1156 | COMMON PROBLEMS, KNOWN BUGS AND LIMITATIONS 1157 | ------------------------------------------- 1158 | 1159 | Any DLL that is needed by a program that Crinkler compresses must be 1160 | available to Crinkler itself. If you get the error message 'Could not 1161 | open DLL ...', it means that Crinkler needed the DLL but could not 1162 | find it. You must place it either in the same directory as the 1163 | Crinkler executable or somewhere in the DLL path, such as 1164 | C:\WINDOWS\system32. Alternatively, you can use the REPLACEDLL option 1165 | to replace it by one that is available. If you get this message for 1166 | msvcr?? DLLs, you have a dependency on the runtime DLL you need to get 1167 | rid of. See the section on standard libraries. 1168 | 1169 | When running inside Visual Studio, the textual progress bars are not 1170 | updated correctly, since the Visual Studio console does not flush the 1171 | output until a newline is reached, even when explicitly flushed by the 1172 | running program. Use the /PROGRESSGUI option to get a graphical 1173 | progress bar. 1174 | 1175 | The code for parsing object and library files contains only a minimum 1176 | of sanity checks. If you pass a corrupt file to Crinkler, it will most 1177 | likely crash. 1178 | 1179 | The final compressed size must be less than 128k, or Crinkler will fail 1180 | horribly. You shouldn't use it for such big files anyway. 1181 | 1182 | If Crinkler crashes, it will write two dump files named 1183 | dump_mini.dmp and dump_full.dmp, where is an integer making 1184 | the file name unique. These files contain information about the 1185 | execution state of Crinkler at the time of the crash. When reporting a 1186 | crash, please include at least the mini dump, or, if possible, both. 1187 | 1188 | 1189 | 1190 | ACKNOWLEDGEMENTS 1191 | ---------------- 1192 | 1193 | The compression technique used by Crinkler is much inspired by the PAQ 1194 | compressor by Matt Mahoney. 1195 | 1196 | The import code is loosely based on the hashed imports code by Peci. 1197 | 1198 | The disassembly feature of the compression report uses the diStorm 1199 | disassembler library by Gil Dabah. 1200 | 1201 | Many thanks to all the people who have given us comments, bug reports 1202 | and test material, in particular to Rambo, Kusma, Polaris, Gargaj, 1203 | Frenetic, Buzzie, Shash, Auld, Minas, Skarab, Dwing, Freak5, Hunta, 1204 | Snq, Darkblade, Abductee, iq, Las, pirx, Hitchhikr, Gloom, Zephod, 1205 | coda, KK, XMunkki, KammutierSpule, acidbrain, xTrim, jix, SubV242, 1206 | w23, ryg, shinmai, Decipher, xtrium, TomasRiker, smoothstep, XT95, 1207 | NeKoFu, n3Xus, Moerder, merry, RCL, zoom, vampire7, Key-Real, 1208 | quiller, Seven, and all the ones we have forgotten. Also thanks to 1209 | Dwarf, Polygon7 and Gargaj for suggestions for our web design. 1210 | 1211 | Big thanks to Rrrola and TomCat for their valuable suggestions for 1212 | optimizing the decompression code, and to qkumba for his guidance on 1213 | the zero-section header and for tracking down the NVIDIA driver issue. 1214 | 1215 | Our special thanks to the many people who have demonstrated the 1216 | usefulness of Crinkler by using it for their own productions. 1217 | 1218 | Keep it going! We greatly appreciate your feedback. 1219 | -------------------------------------------------------------------------------- /blossom/stb_image_write.h: -------------------------------------------------------------------------------- 1 | /* stb_image_write - v1.15 - public domain - http://nothings.org/stb 2 | writes out PNG/BMP/TGA/JPEG/HDR images to C stdio - Sean Barrett 2010-2015 3 | no warranty implied; use at your own risk 4 | 5 | Before #including, 6 | 7 | #define STB_IMAGE_WRITE_IMPLEMENTATION 8 | 9 | in the file that you want to have the implementation. 10 | 11 | Will probably not work correctly with strict-aliasing optimizations. 12 | 13 | ABOUT: 14 | 15 | This header file is a library for writing images to C stdio or a callback. 16 | 17 | The PNG output is not optimal; it is 20-50% larger than the file 18 | written by a decent optimizing implementation; though providing a custom 19 | zlib compress function (see STBIW_ZLIB_COMPRESS) can mitigate that. 20 | This library is designed for source code compactness and simplicity, 21 | not optimal image file size or run-time performance. 22 | 23 | BUILDING: 24 | 25 | You can #define STBIW_ASSERT(x) before the #include to avoid using assert.h. 26 | You can #define STBIW_MALLOC(), STBIW_REALLOC(), and STBIW_FREE() to replace 27 | malloc,realloc,free. 28 | You can #define STBIW_MEMMOVE() to replace memmove() 29 | You can #define STBIW_ZLIB_COMPRESS to use a custom zlib-style compress function 30 | for PNG compression (instead of the builtin one), it must have the following signature: 31 | unsigned char * my_compress(unsigned char *data, int data_len, int *out_len, int quality); 32 | The returned data will be freed with STBIW_FREE() (free() by default), 33 | so it must be heap allocated with STBIW_MALLOC() (malloc() by default), 34 | 35 | UNICODE: 36 | 37 | If compiling for Windows and you wish to use Unicode filenames, compile 38 | with 39 | #define STBIW_WINDOWS_UTF8 40 | and pass utf8-encoded filenames. Call stbiw_convert_wchar_to_utf8 to convert 41 | Windows wchar_t filenames to utf8. 42 | 43 | USAGE: 44 | 45 | There are five functions, one for each image file format: 46 | 47 | int stbi_write_png(char const *filename, int w, int h, int comp, const void *data, int stride_in_bytes); 48 | int stbi_write_bmp(char const *filename, int w, int h, int comp, const void *data); 49 | int stbi_write_tga(char const *filename, int w, int h, int comp, const void *data); 50 | int stbi_write_jpg(char const *filename, int w, int h, int comp, const void *data, int quality); 51 | int stbi_write_hdr(char const *filename, int w, int h, int comp, const float *data); 52 | 53 | void stbi_flip_vertically_on_write(int flag); // flag is non-zero to flip data vertically 54 | 55 | There are also five equivalent functions that use an arbitrary write function. You are 56 | expected to open/close your file-equivalent before and after calling these: 57 | 58 | int stbi_write_png_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data, int stride_in_bytes); 59 | int stbi_write_bmp_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data); 60 | int stbi_write_tga_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data); 61 | int stbi_write_hdr_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const float *data); 62 | int stbi_write_jpg_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void *data, int quality); 63 | 64 | where the callback is: 65 | void stbi_write_func(void *context, void *data, int size); 66 | 67 | You can configure it with these global variables: 68 | int stbi_write_tga_with_rle; // defaults to true; set to 0 to disable RLE 69 | int stbi_write_png_compression_level; // defaults to 8; set to higher for more compression 70 | int stbi_write_force_png_filter; // defaults to -1; set to 0..5 to force a filter mode 71 | 72 | 73 | You can define STBI_WRITE_NO_STDIO to disable the file variant of these 74 | functions, so the library will not use stdio.h at all. However, this will 75 | also disable HDR writing, because it requires stdio for formatted output. 76 | 77 | Each function returns 0 on failure and non-0 on success. 78 | 79 | The functions create an image file defined by the parameters. The image 80 | is a rectangle of pixels stored from left-to-right, top-to-bottom. 81 | Each pixel contains 'comp' channels of data stored interleaved with 8-bits 82 | per channel, in the following order: 1=Y, 2=YA, 3=RGB, 4=RGBA. (Y is 83 | monochrome color.) The rectangle is 'w' pixels wide and 'h' pixels tall. 84 | The *data pointer points to the first byte of the top-left-most pixel. 85 | For PNG, "stride_in_bytes" is the distance in bytes from the first byte of 86 | a row of pixels to the first byte of the next row of pixels. 87 | 88 | PNG creates output files with the same number of components as the input. 89 | The BMP format expands Y to RGB in the file format and does not 90 | output alpha. 91 | 92 | PNG supports writing rectangles of data even when the bytes storing rows of 93 | data are not consecutive in memory (e.g. sub-rectangles of a larger image), 94 | by supplying the stride between the beginning of adjacent rows. The other 95 | formats do not. (Thus you cannot write a native-format BMP through the BMP 96 | writer, both because it is in BGR order and because it may have padding 97 | at the end of the line.) 98 | 99 | PNG allows you to set the deflate compression level by setting the global 100 | variable 'stbi_write_png_compression_level' (it defaults to 8). 101 | 102 | HDR expects linear float data. Since the format is always 32-bit rgb(e) 103 | data, alpha (if provided) is discarded, and for monochrome data it is 104 | replicated across all three channels. 105 | 106 | TGA supports RLE or non-RLE compressed data. To use non-RLE-compressed 107 | data, set the global variable 'stbi_write_tga_with_rle' to 0. 108 | 109 | JPEG does ignore alpha channels in input data; quality is between 1 and 100. 110 | Higher quality looks better but results in a bigger image. 111 | JPEG baseline (no JPEG progressive). 112 | 113 | CREDITS: 114 | 115 | 116 | Sean Barrett - PNG/BMP/TGA 117 | Baldur Karlsson - HDR 118 | Jean-Sebastien Guay - TGA monochrome 119 | Tim Kelsey - misc enhancements 120 | Alan Hickman - TGA RLE 121 | Emmanuel Julien - initial file IO callback implementation 122 | Jon Olick - original jo_jpeg.cpp code 123 | Daniel Gibson - integrate JPEG, allow external zlib 124 | Aarni Koskela - allow choosing PNG filter 125 | 126 | bugfixes: 127 | github:Chribba 128 | Guillaume Chereau 129 | github:jry2 130 | github:romigrou 131 | Sergio Gonzalez 132 | Jonas Karlsson 133 | Filip Wasil 134 | Thatcher Ulrich 135 | github:poppolopoppo 136 | Patrick Boettcher 137 | github:xeekworx 138 | Cap Petschulat 139 | Simon Rodriguez 140 | Ivan Tikhonov 141 | github:ignotion 142 | Adam Schackart 143 | 144 | LICENSE 145 | 146 | See end of file for license information. 147 | 148 | */ 149 | 150 | #ifndef INCLUDE_STB_IMAGE_WRITE_H 151 | #define INCLUDE_STB_IMAGE_WRITE_H 152 | 153 | #include 154 | 155 | // if STB_IMAGE_WRITE_STATIC causes problems, try defining STBIWDEF to 'inline' or 'static inline' 156 | #ifndef STBIWDEF 157 | #ifdef STB_IMAGE_WRITE_STATIC 158 | #define STBIWDEF static 159 | #else 160 | #ifdef __cplusplus 161 | #define STBIWDEF extern "C" 162 | #else 163 | #define STBIWDEF extern 164 | #endif 165 | #endif 166 | #endif 167 | 168 | #ifndef STB_IMAGE_WRITE_STATIC // C++ forbids static forward declarations 169 | extern int stbi_write_tga_with_rle; 170 | extern int stbi_write_png_compression_level; 171 | extern int stbi_write_force_png_filter; 172 | #endif 173 | 174 | #ifndef STBI_WRITE_NO_STDIO 175 | STBIWDEF int stbi_write_png(char const *filename, int w, int h, int comp, const void *data, int stride_in_bytes); 176 | STBIWDEF int stbi_write_bmp(char const *filename, int w, int h, int comp, const void *data); 177 | STBIWDEF int stbi_write_tga(char const *filename, int w, int h, int comp, const void *data); 178 | STBIWDEF int stbi_write_hdr(char const *filename, int w, int h, int comp, const float *data); 179 | STBIWDEF int stbi_write_jpg(char const *filename, int x, int y, int comp, const void *data, int quality); 180 | 181 | #ifdef STBI_WINDOWS_UTF8 182 | STBIWDEF int stbiw_convert_wchar_to_utf8(char *buffer, size_t bufferlen, const wchar_t* input); 183 | #endif 184 | #endif 185 | 186 | typedef void stbi_write_func(void *context, void *data, int size); 187 | 188 | STBIWDEF int stbi_write_png_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data, int stride_in_bytes); 189 | STBIWDEF int stbi_write_bmp_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data); 190 | STBIWDEF int stbi_write_tga_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data); 191 | STBIWDEF int stbi_write_hdr_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const float *data); 192 | STBIWDEF int stbi_write_jpg_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void *data, int quality); 193 | 194 | STBIWDEF void stbi_flip_vertically_on_write(int flip_boolean); 195 | 196 | #endif//INCLUDE_STB_IMAGE_WRITE_H 197 | 198 | #ifdef STB_IMAGE_WRITE_IMPLEMENTATION 199 | 200 | #ifdef _WIN32 201 | #ifndef _CRT_SECURE_NO_WARNINGS 202 | #define _CRT_SECURE_NO_WARNINGS 203 | #endif 204 | #ifndef _CRT_NONSTDC_NO_DEPRECATE 205 | #define _CRT_NONSTDC_NO_DEPRECATE 206 | #endif 207 | #endif 208 | 209 | #ifndef STBI_WRITE_NO_STDIO 210 | #include 211 | #endif // STBI_WRITE_NO_STDIO 212 | 213 | #include 214 | #include 215 | #include 216 | #include 217 | 218 | #if defined(STBIW_MALLOC) && defined(STBIW_FREE) && (defined(STBIW_REALLOC) || defined(STBIW_REALLOC_SIZED)) 219 | // ok 220 | #elif !defined(STBIW_MALLOC) && !defined(STBIW_FREE) && !defined(STBIW_REALLOC) && !defined(STBIW_REALLOC_SIZED) 221 | // ok 222 | #else 223 | #error "Must define all or none of STBIW_MALLOC, STBIW_FREE, and STBIW_REALLOC (or STBIW_REALLOC_SIZED)." 224 | #endif 225 | 226 | #ifndef STBIW_MALLOC 227 | #define STBIW_MALLOC(sz) malloc(sz) 228 | #define STBIW_REALLOC(p,newsz) realloc(p,newsz) 229 | #define STBIW_FREE(p) free(p) 230 | #endif 231 | 232 | #ifndef STBIW_REALLOC_SIZED 233 | #define STBIW_REALLOC_SIZED(p,oldsz,newsz) STBIW_REALLOC(p,newsz) 234 | #endif 235 | 236 | 237 | #ifndef STBIW_MEMMOVE 238 | #define STBIW_MEMMOVE(a,b,sz) memmove(a,b,sz) 239 | #endif 240 | 241 | 242 | #ifndef STBIW_ASSERT 243 | #include 244 | #define STBIW_ASSERT(x) assert(x) 245 | #endif 246 | 247 | #define STBIW_UCHAR(x) (unsigned char) ((x) & 0xff) 248 | 249 | #ifdef STB_IMAGE_WRITE_STATIC 250 | static int stbi_write_png_compression_level = 8; 251 | static int stbi_write_tga_with_rle = 1; 252 | static int stbi_write_force_png_filter = -1; 253 | #else 254 | int stbi_write_png_compression_level = 8; 255 | int stbi_write_tga_with_rle = 1; 256 | int stbi_write_force_png_filter = -1; 257 | #endif 258 | 259 | static int stbi__flip_vertically_on_write = 0; 260 | 261 | STBIWDEF void stbi_flip_vertically_on_write(int flag) 262 | { 263 | stbi__flip_vertically_on_write = flag; 264 | } 265 | 266 | typedef struct 267 | { 268 | stbi_write_func *func; 269 | void *context; 270 | unsigned char buffer[64]; 271 | int buf_used; 272 | } stbi__write_context; 273 | 274 | // initialize a callback-based context 275 | static void stbi__start_write_callbacks(stbi__write_context *s, stbi_write_func *c, void *context) 276 | { 277 | s->func = c; 278 | s->context = context; 279 | } 280 | 281 | #ifndef STBI_WRITE_NO_STDIO 282 | 283 | static void stbi__stdio_write(void *context, void *data, int size) 284 | { 285 | fwrite(data,1,size,(FILE*) context); 286 | } 287 | 288 | #if defined(_MSC_VER) && defined(STBI_WINDOWS_UTF8) 289 | #ifdef __cplusplus 290 | #define STBIW_EXTERN extern "C" 291 | #else 292 | #define STBIW_EXTERN extern 293 | #endif 294 | STBIW_EXTERN __declspec(dllimport) int __stdcall MultiByteToWideChar(unsigned int cp, unsigned long flags, const char *str, int cbmb, wchar_t *widestr, int cchwide); 295 | STBIW_EXTERN __declspec(dllimport) int __stdcall WideCharToMultiByte(unsigned int cp, unsigned long flags, const wchar_t *widestr, int cchwide, char *str, int cbmb, const char *defchar, int *used_default); 296 | 297 | STBIWDEF int stbiw_convert_wchar_to_utf8(char *buffer, size_t bufferlen, const wchar_t* input) 298 | { 299 | return WideCharToMultiByte(65001 /* UTF8 */, 0, input, -1, buffer, (int) bufferlen, NULL, NULL); 300 | } 301 | #endif 302 | 303 | static FILE *stbiw__fopen(char const *filename, char const *mode) 304 | { 305 | FILE *f; 306 | #if defined(_MSC_VER) && defined(STBI_WINDOWS_UTF8) 307 | wchar_t wMode[64]; 308 | wchar_t wFilename[1024]; 309 | if (0 == MultiByteToWideChar(65001 /* UTF8 */, 0, filename, -1, wFilename, sizeof(wFilename))) 310 | return 0; 311 | 312 | if (0 == MultiByteToWideChar(65001 /* UTF8 */, 0, mode, -1, wMode, sizeof(wMode))) 313 | return 0; 314 | 315 | #if _MSC_VER >= 1400 316 | if (0 != _wfopen_s(&f, wFilename, wMode)) 317 | f = 0; 318 | #else 319 | f = _wfopen(wFilename, wMode); 320 | #endif 321 | 322 | #elif defined(_MSC_VER) && _MSC_VER >= 1400 323 | if (0 != fopen_s(&f, filename, mode)) 324 | f=0; 325 | #else 326 | f = fopen(filename, mode); 327 | #endif 328 | return f; 329 | } 330 | 331 | static int stbi__start_write_file(stbi__write_context *s, const char *filename) 332 | { 333 | FILE *f = stbiw__fopen(filename, "wb"); 334 | stbi__start_write_callbacks(s, stbi__stdio_write, (void *) f); 335 | return f != NULL; 336 | } 337 | 338 | static void stbi__end_write_file(stbi__write_context *s) 339 | { 340 | fclose((FILE *)s->context); 341 | } 342 | 343 | #endif // !STBI_WRITE_NO_STDIO 344 | 345 | typedef unsigned int stbiw_uint32; 346 | typedef int stb_image_write_test[sizeof(stbiw_uint32)==4 ? 1 : -1]; 347 | 348 | static void stbiw__writefv(stbi__write_context *s, const char *fmt, va_list v) 349 | { 350 | while (*fmt) { 351 | switch (*fmt++) { 352 | case ' ': break; 353 | case '1': { unsigned char x = STBIW_UCHAR(va_arg(v, int)); 354 | s->func(s->context,&x,1); 355 | break; } 356 | case '2': { int x = va_arg(v,int); 357 | unsigned char b[2]; 358 | b[0] = STBIW_UCHAR(x); 359 | b[1] = STBIW_UCHAR(x>>8); 360 | s->func(s->context,b,2); 361 | break; } 362 | case '4': { stbiw_uint32 x = va_arg(v,int); 363 | unsigned char b[4]; 364 | b[0]=STBIW_UCHAR(x); 365 | b[1]=STBIW_UCHAR(x>>8); 366 | b[2]=STBIW_UCHAR(x>>16); 367 | b[3]=STBIW_UCHAR(x>>24); 368 | s->func(s->context,b,4); 369 | break; } 370 | default: 371 | STBIW_ASSERT(0); 372 | return; 373 | } 374 | } 375 | } 376 | 377 | static void stbiw__writef(stbi__write_context *s, const char *fmt, ...) 378 | { 379 | va_list v; 380 | va_start(v, fmt); 381 | stbiw__writefv(s, fmt, v); 382 | va_end(v); 383 | } 384 | 385 | static void stbiw__write_flush(stbi__write_context *s) 386 | { 387 | if (s->buf_used) { 388 | s->func(s->context, &s->buffer, s->buf_used); 389 | s->buf_used = 0; 390 | } 391 | } 392 | 393 | static void stbiw__putc(stbi__write_context *s, unsigned char c) 394 | { 395 | s->func(s->context, &c, 1); 396 | } 397 | 398 | static void stbiw__write1(stbi__write_context *s, unsigned char a) 399 | { 400 | if (s->buf_used + 1 > sizeof(s->buffer)) 401 | stbiw__write_flush(s); 402 | s->buffer[s->buf_used++] = a; 403 | } 404 | 405 | static void stbiw__write3(stbi__write_context *s, unsigned char a, unsigned char b, unsigned char c) 406 | { 407 | int n; 408 | if (s->buf_used + 3 > sizeof(s->buffer)) 409 | stbiw__write_flush(s); 410 | n = s->buf_used; 411 | s->buf_used = n+3; 412 | s->buffer[n+0] = a; 413 | s->buffer[n+1] = b; 414 | s->buffer[n+2] = c; 415 | } 416 | 417 | static void stbiw__write_pixel(stbi__write_context *s, int rgb_dir, int comp, int write_alpha, int expand_mono, unsigned char *d) 418 | { 419 | unsigned char bg[3] = { 255, 0, 255}, px[3]; 420 | int k; 421 | 422 | if (write_alpha < 0) 423 | stbiw__write1(s, d[comp - 1]); 424 | 425 | switch (comp) { 426 | case 2: // 2 pixels = mono + alpha, alpha is written separately, so same as 1-channel case 427 | case 1: 428 | if (expand_mono) 429 | stbiw__write3(s, d[0], d[0], d[0]); // monochrome bmp 430 | else 431 | stbiw__write1(s, d[0]); // monochrome TGA 432 | break; 433 | case 4: 434 | if (!write_alpha) { 435 | // composite against pink background 436 | for (k = 0; k < 3; ++k) 437 | px[k] = bg[k] + ((d[k] - bg[k]) * d[3]) / 255; 438 | stbiw__write3(s, px[1 - rgb_dir], px[1], px[1 + rgb_dir]); 439 | break; 440 | } 441 | /* FALLTHROUGH */ 442 | case 3: 443 | stbiw__write3(s, d[1 - rgb_dir], d[1], d[1 + rgb_dir]); 444 | break; 445 | } 446 | if (write_alpha > 0) 447 | stbiw__write1(s, d[comp - 1]); 448 | } 449 | 450 | static void stbiw__write_pixels(stbi__write_context *s, int rgb_dir, int vdir, int x, int y, int comp, void *data, int write_alpha, int scanline_pad, int expand_mono) 451 | { 452 | stbiw_uint32 zero = 0; 453 | int i,j, j_end; 454 | 455 | if (y <= 0) 456 | return; 457 | 458 | if (stbi__flip_vertically_on_write) 459 | vdir *= -1; 460 | 461 | if (vdir < 0) { 462 | j_end = -1; j = y-1; 463 | } else { 464 | j_end = y; j = 0; 465 | } 466 | 467 | for (; j != j_end; j += vdir) { 468 | for (i=0; i < x; ++i) { 469 | unsigned char *d = (unsigned char *) data + (j*x+i)*comp; 470 | stbiw__write_pixel(s, rgb_dir, comp, write_alpha, expand_mono, d); 471 | } 472 | stbiw__write_flush(s); 473 | s->func(s->context, &zero, scanline_pad); 474 | } 475 | } 476 | 477 | static int stbiw__outfile(stbi__write_context *s, int rgb_dir, int vdir, int x, int y, int comp, int expand_mono, void *data, int alpha, int pad, const char *fmt, ...) 478 | { 479 | if (y < 0 || x < 0) { 480 | return 0; 481 | } else { 482 | va_list v; 483 | va_start(v, fmt); 484 | stbiw__writefv(s, fmt, v); 485 | va_end(v); 486 | stbiw__write_pixels(s,rgb_dir,vdir,x,y,comp,data,alpha,pad, expand_mono); 487 | return 1; 488 | } 489 | } 490 | 491 | static int stbi_write_bmp_core(stbi__write_context *s, int x, int y, int comp, const void *data) 492 | { 493 | int pad = (-x*3) & 3; 494 | return stbiw__outfile(s,-1,-1,x,y,comp,1,(void *) data,0,pad, 495 | "11 4 22 4" "4 44 22 444444", 496 | 'B', 'M', 14+40+(x*3+pad)*y, 0,0, 14+40, // file header 497 | 40, x,y, 1,24, 0,0,0,0,0,0); // bitmap header 498 | } 499 | 500 | STBIWDEF int stbi_write_bmp_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void *data) 501 | { 502 | stbi__write_context s = { 0 }; 503 | stbi__start_write_callbacks(&s, func, context); 504 | return stbi_write_bmp_core(&s, x, y, comp, data); 505 | } 506 | 507 | #ifndef STBI_WRITE_NO_STDIO 508 | STBIWDEF int stbi_write_bmp(char const *filename, int x, int y, int comp, const void *data) 509 | { 510 | stbi__write_context s = { 0 }; 511 | if (stbi__start_write_file(&s,filename)) { 512 | int r = stbi_write_bmp_core(&s, x, y, comp, data); 513 | stbi__end_write_file(&s); 514 | return r; 515 | } else 516 | return 0; 517 | } 518 | #endif //!STBI_WRITE_NO_STDIO 519 | 520 | static int stbi_write_tga_core(stbi__write_context *s, int x, int y, int comp, void *data) 521 | { 522 | int has_alpha = (comp == 2 || comp == 4); 523 | int colorbytes = has_alpha ? comp-1 : comp; 524 | int format = colorbytes < 2 ? 3 : 2; // 3 color channels (RGB/RGBA) = 2, 1 color channel (Y/YA) = 3 525 | 526 | if (y < 0 || x < 0) 527 | return 0; 528 | 529 | if (!stbi_write_tga_with_rle) { 530 | return stbiw__outfile(s, -1, -1, x, y, comp, 0, (void *) data, has_alpha, 0, 531 | "111 221 2222 11", 0, 0, format, 0, 0, 0, 0, 0, x, y, (colorbytes + has_alpha) * 8, has_alpha * 8); 532 | } else { 533 | int i,j,k; 534 | int jend, jdir; 535 | 536 | stbiw__writef(s, "111 221 2222 11", 0,0,format+8, 0,0,0, 0,0,x,y, (colorbytes + has_alpha) * 8, has_alpha * 8); 537 | 538 | if (stbi__flip_vertically_on_write) { 539 | j = 0; 540 | jend = y; 541 | jdir = 1; 542 | } else { 543 | j = y-1; 544 | jend = -1; 545 | jdir = -1; 546 | } 547 | for (; j != jend; j += jdir) { 548 | unsigned char *row = (unsigned char *) data + j * x * comp; 549 | int len; 550 | 551 | for (i = 0; i < x; i += len) { 552 | unsigned char *begin = row + i * comp; 553 | int diff = 1; 554 | len = 1; 555 | 556 | if (i < x - 1) { 557 | ++len; 558 | diff = memcmp(begin, row + (i + 1) * comp, comp); 559 | if (diff) { 560 | const unsigned char *prev = begin; 561 | for (k = i + 2; k < x && len < 128; ++k) { 562 | if (memcmp(prev, row + k * comp, comp)) { 563 | prev += comp; 564 | ++len; 565 | } else { 566 | --len; 567 | break; 568 | } 569 | } 570 | } else { 571 | for (k = i + 2; k < x && len < 128; ++k) { 572 | if (!memcmp(begin, row + k * comp, comp)) { 573 | ++len; 574 | } else { 575 | break; 576 | } 577 | } 578 | } 579 | } 580 | 581 | if (diff) { 582 | unsigned char header = STBIW_UCHAR(len - 1); 583 | stbiw__write1(s, header); 584 | for (k = 0; k < len; ++k) { 585 | stbiw__write_pixel(s, -1, comp, has_alpha, 0, begin + k * comp); 586 | } 587 | } else { 588 | unsigned char header = STBIW_UCHAR(len - 129); 589 | stbiw__write1(s, header); 590 | stbiw__write_pixel(s, -1, comp, has_alpha, 0, begin); 591 | } 592 | } 593 | } 594 | stbiw__write_flush(s); 595 | } 596 | return 1; 597 | } 598 | 599 | STBIWDEF int stbi_write_tga_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void *data) 600 | { 601 | stbi__write_context s = { 0 }; 602 | stbi__start_write_callbacks(&s, func, context); 603 | return stbi_write_tga_core(&s, x, y, comp, (void *) data); 604 | } 605 | 606 | #ifndef STBI_WRITE_NO_STDIO 607 | STBIWDEF int stbi_write_tga(char const *filename, int x, int y, int comp, const void *data) 608 | { 609 | stbi__write_context s = { 0 }; 610 | if (stbi__start_write_file(&s,filename)) { 611 | int r = stbi_write_tga_core(&s, x, y, comp, (void *) data); 612 | stbi__end_write_file(&s); 613 | return r; 614 | } else 615 | return 0; 616 | } 617 | #endif 618 | 619 | // ************************************************************************************************* 620 | // Radiance RGBE HDR writer 621 | // by Baldur Karlsson 622 | 623 | #define stbiw__max(a, b) ((a) > (b) ? (a) : (b)) 624 | 625 | static void stbiw__linear_to_rgbe(unsigned char *rgbe, float *linear) 626 | { 627 | int exponent; 628 | float maxcomp = stbiw__max(linear[0], stbiw__max(linear[1], linear[2])); 629 | 630 | if (maxcomp < 1e-32f) { 631 | rgbe[0] = rgbe[1] = rgbe[2] = rgbe[3] = 0; 632 | } else { 633 | float normalize = (float) frexp(maxcomp, &exponent) * 256.0f/maxcomp; 634 | 635 | rgbe[0] = (unsigned char)(linear[0] * normalize); 636 | rgbe[1] = (unsigned char)(linear[1] * normalize); 637 | rgbe[2] = (unsigned char)(linear[2] * normalize); 638 | rgbe[3] = (unsigned char)(exponent + 128); 639 | } 640 | } 641 | 642 | static void stbiw__write_run_data(stbi__write_context *s, int length, unsigned char databyte) 643 | { 644 | unsigned char lengthbyte = STBIW_UCHAR(length+128); 645 | STBIW_ASSERT(length+128 <= 255); 646 | s->func(s->context, &lengthbyte, 1); 647 | s->func(s->context, &databyte, 1); 648 | } 649 | 650 | static void stbiw__write_dump_data(stbi__write_context *s, int length, unsigned char *data) 651 | { 652 | unsigned char lengthbyte = STBIW_UCHAR(length); 653 | STBIW_ASSERT(length <= 128); // inconsistent with spec but consistent with official code 654 | s->func(s->context, &lengthbyte, 1); 655 | s->func(s->context, data, length); 656 | } 657 | 658 | static void stbiw__write_hdr_scanline(stbi__write_context *s, int width, int ncomp, unsigned char *scratch, float *scanline) 659 | { 660 | unsigned char scanlineheader[4] = { 2, 2, 0, 0 }; 661 | unsigned char rgbe[4]; 662 | float linear[3]; 663 | int x; 664 | 665 | scanlineheader[2] = (width&0xff00)>>8; 666 | scanlineheader[3] = (width&0x00ff); 667 | 668 | /* skip RLE for images too small or large */ 669 | if (width < 8 || width >= 32768) { 670 | for (x=0; x < width; x++) { 671 | switch (ncomp) { 672 | case 4: /* fallthrough */ 673 | case 3: linear[2] = scanline[x*ncomp + 2]; 674 | linear[1] = scanline[x*ncomp + 1]; 675 | linear[0] = scanline[x*ncomp + 0]; 676 | break; 677 | default: 678 | linear[0] = linear[1] = linear[2] = scanline[x*ncomp + 0]; 679 | break; 680 | } 681 | stbiw__linear_to_rgbe(rgbe, linear); 682 | s->func(s->context, rgbe, 4); 683 | } 684 | } else { 685 | int c,r; 686 | /* encode into scratch buffer */ 687 | for (x=0; x < width; x++) { 688 | switch(ncomp) { 689 | case 4: /* fallthrough */ 690 | case 3: linear[2] = scanline[x*ncomp + 2]; 691 | linear[1] = scanline[x*ncomp + 1]; 692 | linear[0] = scanline[x*ncomp + 0]; 693 | break; 694 | default: 695 | linear[0] = linear[1] = linear[2] = scanline[x*ncomp + 0]; 696 | break; 697 | } 698 | stbiw__linear_to_rgbe(rgbe, linear); 699 | scratch[x + width*0] = rgbe[0]; 700 | scratch[x + width*1] = rgbe[1]; 701 | scratch[x + width*2] = rgbe[2]; 702 | scratch[x + width*3] = rgbe[3]; 703 | } 704 | 705 | s->func(s->context, scanlineheader, 4); 706 | 707 | /* RLE each component separately */ 708 | for (c=0; c < 4; c++) { 709 | unsigned char *comp = &scratch[width*c]; 710 | 711 | x = 0; 712 | while (x < width) { 713 | // find first run 714 | r = x; 715 | while (r+2 < width) { 716 | if (comp[r] == comp[r+1] && comp[r] == comp[r+2]) 717 | break; 718 | ++r; 719 | } 720 | if (r+2 >= width) 721 | r = width; 722 | // dump up to first run 723 | while (x < r) { 724 | int len = r-x; 725 | if (len > 128) len = 128; 726 | stbiw__write_dump_data(s, len, &comp[x]); 727 | x += len; 728 | } 729 | // if there's a run, output it 730 | if (r+2 < width) { // same test as what we break out of in search loop, so only true if we break'd 731 | // find next byte after run 732 | while (r < width && comp[r] == comp[x]) 733 | ++r; 734 | // output run up to r 735 | while (x < r) { 736 | int len = r-x; 737 | if (len > 127) len = 127; 738 | stbiw__write_run_data(s, len, comp[x]); 739 | x += len; 740 | } 741 | } 742 | } 743 | } 744 | } 745 | } 746 | 747 | static int stbi_write_hdr_core(stbi__write_context *s, int x, int y, int comp, float *data) 748 | { 749 | if (y <= 0 || x <= 0 || data == NULL) 750 | return 0; 751 | else { 752 | // Each component is stored separately. Allocate scratch space for full output scanline. 753 | unsigned char *scratch = (unsigned char *) STBIW_MALLOC(x*4); 754 | int i, len; 755 | char buffer[128]; 756 | char header[] = "#?RADIANCE\n# Written by stb_image_write.h\nFORMAT=32-bit_rle_rgbe\n"; 757 | s->func(s->context, header, sizeof(header)-1); 758 | 759 | #ifdef __STDC_WANT_SECURE_LIB__ 760 | len = sprintf_s(buffer, sizeof(buffer), "EXPOSURE= 1.0000000000000\n\n-Y %d +X %d\n", y, x); 761 | #else 762 | len = sprintf(buffer, "EXPOSURE= 1.0000000000000\n\n-Y %d +X %d\n", y, x); 763 | #endif 764 | s->func(s->context, buffer, len); 765 | 766 | for(i=0; i < y; i++) 767 | stbiw__write_hdr_scanline(s, x, comp, scratch, data + comp*x*(stbi__flip_vertically_on_write ? y-1-i : i)); 768 | STBIW_FREE(scratch); 769 | return 1; 770 | } 771 | } 772 | 773 | STBIWDEF int stbi_write_hdr_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const float *data) 774 | { 775 | stbi__write_context s = { 0 }; 776 | stbi__start_write_callbacks(&s, func, context); 777 | return stbi_write_hdr_core(&s, x, y, comp, (float *) data); 778 | } 779 | 780 | #ifndef STBI_WRITE_NO_STDIO 781 | STBIWDEF int stbi_write_hdr(char const *filename, int x, int y, int comp, const float *data) 782 | { 783 | stbi__write_context s = { 0 }; 784 | if (stbi__start_write_file(&s,filename)) { 785 | int r = stbi_write_hdr_core(&s, x, y, comp, (float *) data); 786 | stbi__end_write_file(&s); 787 | return r; 788 | } else 789 | return 0; 790 | } 791 | #endif // STBI_WRITE_NO_STDIO 792 | 793 | 794 | ////////////////////////////////////////////////////////////////////////////// 795 | // 796 | // PNG writer 797 | // 798 | 799 | #ifndef STBIW_ZLIB_COMPRESS 800 | // stretchy buffer; stbiw__sbpush() == vector<>::push_back() -- stbiw__sbcount() == vector<>::size() 801 | #define stbiw__sbraw(a) ((int *) (void *) (a) - 2) 802 | #define stbiw__sbm(a) stbiw__sbraw(a)[0] 803 | #define stbiw__sbn(a) stbiw__sbraw(a)[1] 804 | 805 | #define stbiw__sbneedgrow(a,n) ((a)==0 || stbiw__sbn(a)+n >= stbiw__sbm(a)) 806 | #define stbiw__sbmaybegrow(a,n) (stbiw__sbneedgrow(a,(n)) ? stbiw__sbgrow(a,n) : 0) 807 | #define stbiw__sbgrow(a,n) stbiw__sbgrowf((void **) &(a), (n), sizeof(*(a))) 808 | 809 | #define stbiw__sbpush(a, v) (stbiw__sbmaybegrow(a,1), (a)[stbiw__sbn(a)++] = (v)) 810 | #define stbiw__sbcount(a) ((a) ? stbiw__sbn(a) : 0) 811 | #define stbiw__sbfree(a) ((a) ? STBIW_FREE(stbiw__sbraw(a)),0 : 0) 812 | 813 | static void *stbiw__sbgrowf(void **arr, int increment, int itemsize) 814 | { 815 | int m = *arr ? 2*stbiw__sbm(*arr)+increment : increment+1; 816 | void *p = STBIW_REALLOC_SIZED(*arr ? stbiw__sbraw(*arr) : 0, *arr ? (stbiw__sbm(*arr)*itemsize + sizeof(int)*2) : 0, itemsize * m + sizeof(int)*2); 817 | STBIW_ASSERT(p); 818 | if (p) { 819 | if (!*arr) ((int *) p)[1] = 0; 820 | *arr = (void *) ((int *) p + 2); 821 | stbiw__sbm(*arr) = m; 822 | } 823 | return *arr; 824 | } 825 | 826 | static unsigned char *stbiw__zlib_flushf(unsigned char *data, unsigned int *bitbuffer, int *bitcount) 827 | { 828 | while (*bitcount >= 8) { 829 | stbiw__sbpush(data, STBIW_UCHAR(*bitbuffer)); 830 | *bitbuffer >>= 8; 831 | *bitcount -= 8; 832 | } 833 | return data; 834 | } 835 | 836 | static int stbiw__zlib_bitrev(int code, int codebits) 837 | { 838 | int res=0; 839 | while (codebits--) { 840 | res = (res << 1) | (code & 1); 841 | code >>= 1; 842 | } 843 | return res; 844 | } 845 | 846 | static unsigned int stbiw__zlib_countm(unsigned char *a, unsigned char *b, int limit) 847 | { 848 | int i; 849 | for (i=0; i < limit && i < 258; ++i) 850 | if (a[i] != b[i]) break; 851 | return i; 852 | } 853 | 854 | static unsigned int stbiw__zhash(unsigned char *data) 855 | { 856 | stbiw_uint32 hash = data[0] + (data[1] << 8) + (data[2] << 16); 857 | hash ^= hash << 3; 858 | hash += hash >> 5; 859 | hash ^= hash << 4; 860 | hash += hash >> 17; 861 | hash ^= hash << 25; 862 | hash += hash >> 6; 863 | return hash; 864 | } 865 | 866 | #define stbiw__zlib_flush() (out = stbiw__zlib_flushf(out, &bitbuf, &bitcount)) 867 | #define stbiw__zlib_add(code,codebits) \ 868 | (bitbuf |= (code) << bitcount, bitcount += (codebits), stbiw__zlib_flush()) 869 | #define stbiw__zlib_huffa(b,c) stbiw__zlib_add(stbiw__zlib_bitrev(b,c),c) 870 | // default huffman tables 871 | #define stbiw__zlib_huff1(n) stbiw__zlib_huffa(0x30 + (n), 8) 872 | #define stbiw__zlib_huff2(n) stbiw__zlib_huffa(0x190 + (n)-144, 9) 873 | #define stbiw__zlib_huff3(n) stbiw__zlib_huffa(0 + (n)-256,7) 874 | #define stbiw__zlib_huff4(n) stbiw__zlib_huffa(0xc0 + (n)-280,8) 875 | #define stbiw__zlib_huff(n) ((n) <= 143 ? stbiw__zlib_huff1(n) : (n) <= 255 ? stbiw__zlib_huff2(n) : (n) <= 279 ? stbiw__zlib_huff3(n) : stbiw__zlib_huff4(n)) 876 | #define stbiw__zlib_huffb(n) ((n) <= 143 ? stbiw__zlib_huff1(n) : stbiw__zlib_huff2(n)) 877 | 878 | #define stbiw__ZHASH 16384 879 | 880 | #endif // STBIW_ZLIB_COMPRESS 881 | 882 | STBIWDEF unsigned char * stbi_zlib_compress(unsigned char *data, int data_len, int *out_len, int quality) 883 | { 884 | #ifdef STBIW_ZLIB_COMPRESS 885 | // user provided a zlib compress implementation, use that 886 | return STBIW_ZLIB_COMPRESS(data, data_len, out_len, quality); 887 | #else // use builtin 888 | static unsigned short lengthc[] = { 3,4,5,6,7,8,9,10,11,13,15,17,19,23,27,31,35,43,51,59,67,83,99,115,131,163,195,227,258, 259 }; 889 | static unsigned char lengtheb[]= { 0,0,0,0,0,0,0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0 }; 890 | static unsigned short distc[] = { 1,2,3,4,5,7,9,13,17,25,33,49,65,97,129,193,257,385,513,769,1025,1537,2049,3073,4097,6145,8193,12289,16385,24577, 32768 }; 891 | static unsigned char disteb[] = { 0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13 }; 892 | unsigned int bitbuf=0; 893 | int i,j, bitcount=0; 894 | unsigned char *out = NULL; 895 | unsigned char ***hash_table = (unsigned char***) STBIW_MALLOC(stbiw__ZHASH * sizeof(unsigned char**)); 896 | if (hash_table == NULL) 897 | return NULL; 898 | if (quality < 5) quality = 5; 899 | 900 | stbiw__sbpush(out, 0x78); // DEFLATE 32K window 901 | stbiw__sbpush(out, 0x5e); // FLEVEL = 1 902 | stbiw__zlib_add(1,1); // BFINAL = 1 903 | stbiw__zlib_add(1,2); // BTYPE = 1 -- fixed huffman 904 | 905 | for (i=0; i < stbiw__ZHASH; ++i) 906 | hash_table[i] = NULL; 907 | 908 | i=0; 909 | while (i < data_len-3) { 910 | // hash next 3 bytes of data to be compressed 911 | int h = stbiw__zhash(data+i)&(stbiw__ZHASH-1), best=3; 912 | unsigned char *bestloc = 0; 913 | unsigned char **hlist = hash_table[h]; 914 | int n = stbiw__sbcount(hlist); 915 | for (j=0; j < n; ++j) { 916 | if (hlist[j]-data > i-32768) { // if entry lies within window 917 | int d = stbiw__zlib_countm(hlist[j], data+i, data_len-i); 918 | if (d >= best) { best=d; bestloc=hlist[j]; } 919 | } 920 | } 921 | // when hash table entry is too long, delete half the entries 922 | if (hash_table[h] && stbiw__sbn(hash_table[h]) == 2*quality) { 923 | STBIW_MEMMOVE(hash_table[h], hash_table[h]+quality, sizeof(hash_table[h][0])*quality); 924 | stbiw__sbn(hash_table[h]) = quality; 925 | } 926 | stbiw__sbpush(hash_table[h],data+i); 927 | 928 | if (bestloc) { 929 | // "lazy matching" - check match at *next* byte, and if it's better, do cur byte as literal 930 | h = stbiw__zhash(data+i+1)&(stbiw__ZHASH-1); 931 | hlist = hash_table[h]; 932 | n = stbiw__sbcount(hlist); 933 | for (j=0; j < n; ++j) { 934 | if (hlist[j]-data > i-32767) { 935 | int e = stbiw__zlib_countm(hlist[j], data+i+1, data_len-i-1); 936 | if (e > best) { // if next match is better, bail on current match 937 | bestloc = NULL; 938 | break; 939 | } 940 | } 941 | } 942 | } 943 | 944 | if (bestloc) { 945 | int d = (int) (data+i - bestloc); // distance back 946 | STBIW_ASSERT(d <= 32767 && best <= 258); 947 | for (j=0; best > lengthc[j+1]-1; ++j); 948 | stbiw__zlib_huff(j+257); 949 | if (lengtheb[j]) stbiw__zlib_add(best - lengthc[j], lengtheb[j]); 950 | for (j=0; d > distc[j+1]-1; ++j); 951 | stbiw__zlib_add(stbiw__zlib_bitrev(j,5),5); 952 | if (disteb[j]) stbiw__zlib_add(d - distc[j], disteb[j]); 953 | i += best; 954 | } else { 955 | stbiw__zlib_huffb(data[i]); 956 | ++i; 957 | } 958 | } 959 | // write out final bytes 960 | for (;i < data_len; ++i) 961 | stbiw__zlib_huffb(data[i]); 962 | stbiw__zlib_huff(256); // end of block 963 | // pad with 0 bits to byte boundary 964 | while (bitcount) 965 | stbiw__zlib_add(0,1); 966 | 967 | for (i=0; i < stbiw__ZHASH; ++i) 968 | (void) stbiw__sbfree(hash_table[i]); 969 | STBIW_FREE(hash_table); 970 | 971 | { 972 | // compute adler32 on input 973 | unsigned int s1=1, s2=0; 974 | int blocklen = (int) (data_len % 5552); 975 | j=0; 976 | while (j < data_len) { 977 | for (i=0; i < blocklen; ++i) { s1 += data[j+i]; s2 += s1; } 978 | s1 %= 65521; s2 %= 65521; 979 | j += blocklen; 980 | blocklen = 5552; 981 | } 982 | stbiw__sbpush(out, STBIW_UCHAR(s2 >> 8)); 983 | stbiw__sbpush(out, STBIW_UCHAR(s2)); 984 | stbiw__sbpush(out, STBIW_UCHAR(s1 >> 8)); 985 | stbiw__sbpush(out, STBIW_UCHAR(s1)); 986 | } 987 | *out_len = stbiw__sbn(out); 988 | // make returned pointer freeable 989 | STBIW_MEMMOVE(stbiw__sbraw(out), out, *out_len); 990 | return (unsigned char *) stbiw__sbraw(out); 991 | #endif // STBIW_ZLIB_COMPRESS 992 | } 993 | 994 | static unsigned int stbiw__crc32(unsigned char *buffer, int len) 995 | { 996 | #ifdef STBIW_CRC32 997 | return STBIW_CRC32(buffer, len); 998 | #else 999 | static unsigned int crc_table[256] = 1000 | { 1001 | 0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA, 0x076DC419, 0x706AF48F, 0xE963A535, 0x9E6495A3, 1002 | 0x0eDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988, 0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91, 1003 | 0x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE, 0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7, 1004 | 0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC, 0x14015C4F, 0x63066CD9, 0xFA0F3D63, 0x8D080DF5, 1005 | 0x3B6E20C8, 0x4C69105E, 0xD56041E4, 0xA2677172, 0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B, 1006 | 0x35B5A8FA, 0x42B2986C, 0xDBBBC9D6, 0xACBCF940, 0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59, 1007 | 0x26D930AC, 0x51DE003A, 0xC8D75180, 0xBFD06116, 0x21B4F4B5, 0x56B3C423, 0xCFBA9599, 0xB8BDA50F, 1008 | 0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924, 0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D, 1009 | 0x76DC4190, 0x01DB7106, 0x98D220BC, 0xEFD5102A, 0x71B18589, 0x06B6B51F, 0x9FBFE4A5, 0xE8B8D433, 1010 | 0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818, 0x7F6A0DBB, 0x086D3D2D, 0x91646C97, 0xE6635C01, 1011 | 0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E, 0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457, 1012 | 0x65B0D9C6, 0x12B7E950, 0x8BBEB8EA, 0xFCB9887C, 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65, 1013 | 0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2, 0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB, 1014 | 0x4369E96A, 0x346ED9FC, 0xAD678846, 0xDA60B8D0, 0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9, 1015 | 0x5005713C, 0x270241AA, 0xBE0B1010, 0xC90C2086, 0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F, 1016 | 0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4, 0x59B33D17, 0x2EB40D81, 0xB7BD5C3B, 0xC0BA6CAD, 1017 | 0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A, 0xEAD54739, 0x9DD277AF, 0x04DB2615, 0x73DC1683, 1018 | 0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8, 0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1, 1019 | 0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE, 0xF762575D, 0x806567CB, 0x196C3671, 0x6E6B06E7, 1020 | 0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC, 0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5, 1021 | 0xD6D6A3E8, 0xA1D1937E, 0x38D8C2C4, 0x4FDFF252, 0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B, 1022 | 0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60, 0xDF60EFC3, 0xA867DF55, 0x316E8EEF, 0x4669BE79, 1023 | 0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236, 0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F, 1024 | 0xC5BA3BBE, 0xB2BD0B28, 0x2BB45A92, 0x5CB36A04, 0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D, 1025 | 0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A, 0x9C0906A9, 0xEB0E363F, 0x72076785, 0x05005713, 1026 | 0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38, 0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21, 1027 | 0x86D3D2D4, 0xF1D4E242, 0x68DDB3F8, 0x1FDA836E, 0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777, 1028 | 0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C, 0x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45, 1029 | 0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2, 0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB, 1030 | 0xAED16A4A, 0xD9D65ADC, 0x40DF0B66, 0x37D83BF0, 0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9, 1031 | 0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6, 0xBAD03605, 0xCDD70693, 0x54DE5729, 0x23D967BF, 1032 | 0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94, 0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D 1033 | }; 1034 | 1035 | unsigned int crc = ~0u; 1036 | int i; 1037 | for (i=0; i < len; ++i) 1038 | crc = (crc >> 8) ^ crc_table[buffer[i] ^ (crc & 0xff)]; 1039 | return ~crc; 1040 | #endif 1041 | } 1042 | 1043 | #define stbiw__wpng4(o,a,b,c,d) ((o)[0]=STBIW_UCHAR(a),(o)[1]=STBIW_UCHAR(b),(o)[2]=STBIW_UCHAR(c),(o)[3]=STBIW_UCHAR(d),(o)+=4) 1044 | #define stbiw__wp32(data,v) stbiw__wpng4(data, (v)>>24,(v)>>16,(v)>>8,(v)); 1045 | #define stbiw__wptag(data,s) stbiw__wpng4(data, s[0],s[1],s[2],s[3]) 1046 | 1047 | static void stbiw__wpcrc(unsigned char **data, int len) 1048 | { 1049 | unsigned int crc = stbiw__crc32(*data - len - 4, len+4); 1050 | stbiw__wp32(*data, crc); 1051 | } 1052 | 1053 | static unsigned char stbiw__paeth(int a, int b, int c) 1054 | { 1055 | int p = a + b - c, pa = abs(p-a), pb = abs(p-b), pc = abs(p-c); 1056 | if (pa <= pb && pa <= pc) return STBIW_UCHAR(a); 1057 | if (pb <= pc) return STBIW_UCHAR(b); 1058 | return STBIW_UCHAR(c); 1059 | } 1060 | 1061 | // @OPTIMIZE: provide an option that always forces left-predict or paeth predict 1062 | static void stbiw__encode_png_line(unsigned char *pixels, int stride_bytes, int width, int height, int y, int n, int filter_type, signed char *line_buffer) 1063 | { 1064 | static int mapping[] = { 0,1,2,3,4 }; 1065 | static int firstmap[] = { 0,1,0,5,6 }; 1066 | int *mymap = (y != 0) ? mapping : firstmap; 1067 | int i; 1068 | int type = mymap[filter_type]; 1069 | unsigned char *z = pixels + stride_bytes * (stbi__flip_vertically_on_write ? height-1-y : y); 1070 | int signed_stride = stbi__flip_vertically_on_write ? -stride_bytes : stride_bytes; 1071 | 1072 | if (type==0) { 1073 | memcpy(line_buffer, z, width*n); 1074 | return; 1075 | } 1076 | 1077 | // first loop isn't optimized since it's just one pixel 1078 | for (i = 0; i < n; ++i) { 1079 | switch (type) { 1080 | case 1: line_buffer[i] = z[i]; break; 1081 | case 2: line_buffer[i] = z[i] - z[i-signed_stride]; break; 1082 | case 3: line_buffer[i] = z[i] - (z[i-signed_stride]>>1); break; 1083 | case 4: line_buffer[i] = (signed char) (z[i] - stbiw__paeth(0,z[i-signed_stride],0)); break; 1084 | case 5: line_buffer[i] = z[i]; break; 1085 | case 6: line_buffer[i] = z[i]; break; 1086 | } 1087 | } 1088 | switch (type) { 1089 | case 1: for (i=n; i < width*n; ++i) line_buffer[i] = z[i] - z[i-n]; break; 1090 | case 2: for (i=n; i < width*n; ++i) line_buffer[i] = z[i] - z[i-signed_stride]; break; 1091 | case 3: for (i=n; i < width*n; ++i) line_buffer[i] = z[i] - ((z[i-n] + z[i-signed_stride])>>1); break; 1092 | case 4: for (i=n; i < width*n; ++i) line_buffer[i] = z[i] - stbiw__paeth(z[i-n], z[i-signed_stride], z[i-signed_stride-n]); break; 1093 | case 5: for (i=n; i < width*n; ++i) line_buffer[i] = z[i] - (z[i-n]>>1); break; 1094 | case 6: for (i=n; i < width*n; ++i) line_buffer[i] = z[i] - stbiw__paeth(z[i-n], 0,0); break; 1095 | } 1096 | } 1097 | 1098 | STBIWDEF unsigned char *stbi_write_png_to_mem(const unsigned char *pixels, int stride_bytes, int x, int y, int n, int *out_len) 1099 | { 1100 | int force_filter = stbi_write_force_png_filter; 1101 | int ctype[5] = { -1, 0, 4, 2, 6 }; 1102 | unsigned char sig[8] = { 137,80,78,71,13,10,26,10 }; 1103 | unsigned char *out,*o, *filt, *zlib; 1104 | signed char *line_buffer; 1105 | int j,zlen; 1106 | 1107 | if (stride_bytes == 0) 1108 | stride_bytes = x * n; 1109 | 1110 | if (force_filter >= 5) { 1111 | force_filter = -1; 1112 | } 1113 | 1114 | filt = (unsigned char *) STBIW_MALLOC((x*n+1) * y); if (!filt) return 0; 1115 | line_buffer = (signed char *) STBIW_MALLOC(x * n); if (!line_buffer) { STBIW_FREE(filt); return 0; } 1116 | for (j=0; j < y; ++j) { 1117 | int filter_type; 1118 | if (force_filter > -1) { 1119 | filter_type = force_filter; 1120 | stbiw__encode_png_line((unsigned char*)(pixels), stride_bytes, x, y, j, n, force_filter, line_buffer); 1121 | } else { // Estimate the best filter by running through all of them: 1122 | int best_filter = 0, best_filter_val = 0x7fffffff, est, i; 1123 | for (filter_type = 0; filter_type < 5; filter_type++) { 1124 | stbiw__encode_png_line((unsigned char*)(pixels), stride_bytes, x, y, j, n, filter_type, line_buffer); 1125 | 1126 | // Estimate the entropy of the line using this filter; the less, the better. 1127 | est = 0; 1128 | for (i = 0; i < x*n; ++i) { 1129 | est += abs((signed char) line_buffer[i]); 1130 | } 1131 | if (est < best_filter_val) { 1132 | best_filter_val = est; 1133 | best_filter = filter_type; 1134 | } 1135 | } 1136 | if (filter_type != best_filter) { // If the last iteration already got us the best filter, don't redo it 1137 | stbiw__encode_png_line((unsigned char*)(pixels), stride_bytes, x, y, j, n, best_filter, line_buffer); 1138 | filter_type = best_filter; 1139 | } 1140 | } 1141 | // when we get here, filter_type contains the filter type, and line_buffer contains the data 1142 | filt[j*(x*n+1)] = (unsigned char) filter_type; 1143 | STBIW_MEMMOVE(filt+j*(x*n+1)+1, line_buffer, x*n); 1144 | } 1145 | STBIW_FREE(line_buffer); 1146 | zlib = stbi_zlib_compress(filt, y*( x*n+1), &zlen, stbi_write_png_compression_level); 1147 | STBIW_FREE(filt); 1148 | if (!zlib) return 0; 1149 | 1150 | // each tag requires 12 bytes of overhead 1151 | out = (unsigned char *) STBIW_MALLOC(8 + 12+13 + 12+zlen + 12); 1152 | if (!out) return 0; 1153 | *out_len = 8 + 12+13 + 12+zlen + 12; 1154 | 1155 | o=out; 1156 | STBIW_MEMMOVE(o,sig,8); o+= 8; 1157 | stbiw__wp32(o, 13); // header length 1158 | stbiw__wptag(o, "IHDR"); 1159 | stbiw__wp32(o, x); 1160 | stbiw__wp32(o, y); 1161 | *o++ = 8; 1162 | *o++ = STBIW_UCHAR(ctype[n]); 1163 | *o++ = 0; 1164 | *o++ = 0; 1165 | *o++ = 0; 1166 | stbiw__wpcrc(&o,13); 1167 | 1168 | stbiw__wp32(o, zlen); 1169 | stbiw__wptag(o, "IDAT"); 1170 | STBIW_MEMMOVE(o, zlib, zlen); 1171 | o += zlen; 1172 | STBIW_FREE(zlib); 1173 | stbiw__wpcrc(&o, zlen); 1174 | 1175 | stbiw__wp32(o,0); 1176 | stbiw__wptag(o, "IEND"); 1177 | stbiw__wpcrc(&o,0); 1178 | 1179 | STBIW_ASSERT(o == out + *out_len); 1180 | 1181 | return out; 1182 | } 1183 | 1184 | #ifndef STBI_WRITE_NO_STDIO 1185 | STBIWDEF int stbi_write_png(char const *filename, int x, int y, int comp, const void *data, int stride_bytes) 1186 | { 1187 | FILE *f; 1188 | int len; 1189 | unsigned char *png = stbi_write_png_to_mem((const unsigned char *) data, stride_bytes, x, y, comp, &len); 1190 | if (png == NULL) return 0; 1191 | 1192 | f = stbiw__fopen(filename, "wb"); 1193 | if (!f) { STBIW_FREE(png); return 0; } 1194 | fwrite(png, 1, len, f); 1195 | fclose(f); 1196 | STBIW_FREE(png); 1197 | return 1; 1198 | } 1199 | #endif 1200 | 1201 | STBIWDEF int stbi_write_png_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void *data, int stride_bytes) 1202 | { 1203 | int len; 1204 | unsigned char *png = stbi_write_png_to_mem((const unsigned char *) data, stride_bytes, x, y, comp, &len); 1205 | if (png == NULL) return 0; 1206 | func(context, png, len); 1207 | STBIW_FREE(png); 1208 | return 1; 1209 | } 1210 | 1211 | 1212 | /* *************************************************************************** 1213 | * 1214 | * JPEG writer 1215 | * 1216 | * This is based on Jon Olick's jo_jpeg.cpp: 1217 | * public domain Simple, Minimalistic JPEG writer - http://www.jonolick.com/code.html 1218 | */ 1219 | 1220 | static const unsigned char stbiw__jpg_ZigZag[] = { 0,1,5,6,14,15,27,28,2,4,7,13,16,26,29,42,3,8,12,17,25,30,41,43,9,11,18, 1221 | 24,31,40,44,53,10,19,23,32,39,45,52,54,20,22,33,38,46,51,55,60,21,34,37,47,50,56,59,61,35,36,48,49,57,58,62,63 }; 1222 | 1223 | static void stbiw__jpg_writeBits(stbi__write_context *s, int *bitBufP, int *bitCntP, const unsigned short *bs) { 1224 | int bitBuf = *bitBufP, bitCnt = *bitCntP; 1225 | bitCnt += bs[1]; 1226 | bitBuf |= bs[0] << (24 - bitCnt); 1227 | while(bitCnt >= 8) { 1228 | unsigned char c = (bitBuf >> 16) & 255; 1229 | stbiw__putc(s, c); 1230 | if(c == 255) { 1231 | stbiw__putc(s, 0); 1232 | } 1233 | bitBuf <<= 8; 1234 | bitCnt -= 8; 1235 | } 1236 | *bitBufP = bitBuf; 1237 | *bitCntP = bitCnt; 1238 | } 1239 | 1240 | static void stbiw__jpg_DCT(float *d0p, float *d1p, float *d2p, float *d3p, float *d4p, float *d5p, float *d6p, float *d7p) { 1241 | float d0 = *d0p, d1 = *d1p, d2 = *d2p, d3 = *d3p, d4 = *d4p, d5 = *d5p, d6 = *d6p, d7 = *d7p; 1242 | float z1, z2, z3, z4, z5, z11, z13; 1243 | 1244 | float tmp0 = d0 + d7; 1245 | float tmp7 = d0 - d7; 1246 | float tmp1 = d1 + d6; 1247 | float tmp6 = d1 - d6; 1248 | float tmp2 = d2 + d5; 1249 | float tmp5 = d2 - d5; 1250 | float tmp3 = d3 + d4; 1251 | float tmp4 = d3 - d4; 1252 | 1253 | // Even part 1254 | float tmp10 = tmp0 + tmp3; // phase 2 1255 | float tmp13 = tmp0 - tmp3; 1256 | float tmp11 = tmp1 + tmp2; 1257 | float tmp12 = tmp1 - tmp2; 1258 | 1259 | d0 = tmp10 + tmp11; // phase 3 1260 | d4 = tmp10 - tmp11; 1261 | 1262 | z1 = (tmp12 + tmp13) * 0.707106781f; // c4 1263 | d2 = tmp13 + z1; // phase 5 1264 | d6 = tmp13 - z1; 1265 | 1266 | // Odd part 1267 | tmp10 = tmp4 + tmp5; // phase 2 1268 | tmp11 = tmp5 + tmp6; 1269 | tmp12 = tmp6 + tmp7; 1270 | 1271 | // The rotator is modified from fig 4-8 to avoid extra negations. 1272 | z5 = (tmp10 - tmp12) * 0.382683433f; // c6 1273 | z2 = tmp10 * 0.541196100f + z5; // c2-c6 1274 | z4 = tmp12 * 1.306562965f + z5; // c2+c6 1275 | z3 = tmp11 * 0.707106781f; // c4 1276 | 1277 | z11 = tmp7 + z3; // phase 5 1278 | z13 = tmp7 - z3; 1279 | 1280 | *d5p = z13 + z2; // phase 6 1281 | *d3p = z13 - z2; 1282 | *d1p = z11 + z4; 1283 | *d7p = z11 - z4; 1284 | 1285 | *d0p = d0; *d2p = d2; *d4p = d4; *d6p = d6; 1286 | } 1287 | 1288 | static void stbiw__jpg_calcBits(int val, unsigned short bits[2]) { 1289 | int tmp1 = val < 0 ? -val : val; 1290 | val = val < 0 ? val-1 : val; 1291 | bits[1] = 1; 1292 | while(tmp1 >>= 1) { 1293 | ++bits[1]; 1294 | } 1295 | bits[0] = val & ((1<0)&&(DU[end0pos]==0); --end0pos) { 1338 | } 1339 | // end0pos = first element in reverse order !=0 1340 | if(end0pos == 0) { 1341 | stbiw__jpg_writeBits(s, bitBuf, bitCnt, EOB); 1342 | return DU[0]; 1343 | } 1344 | for(i = 1; i <= end0pos; ++i) { 1345 | int startpos = i; 1346 | int nrzeroes; 1347 | unsigned short bits[2]; 1348 | for (; DU[i]==0 && i<=end0pos; ++i) { 1349 | } 1350 | nrzeroes = i-startpos; 1351 | if ( nrzeroes >= 16 ) { 1352 | int lng = nrzeroes>>4; 1353 | int nrmarker; 1354 | for (nrmarker=1; nrmarker <= lng; ++nrmarker) 1355 | stbiw__jpg_writeBits(s, bitBuf, bitCnt, M16zeroes); 1356 | nrzeroes &= 15; 1357 | } 1358 | stbiw__jpg_calcBits(DU[i], bits); 1359 | stbiw__jpg_writeBits(s, bitBuf, bitCnt, HTAC[(nrzeroes<<4)+bits[1]]); 1360 | stbiw__jpg_writeBits(s, bitBuf, bitCnt, bits); 1361 | } 1362 | if(end0pos != 63) { 1363 | stbiw__jpg_writeBits(s, bitBuf, bitCnt, EOB); 1364 | } 1365 | return DU[0]; 1366 | } 1367 | 1368 | static int stbi_write_jpg_core(stbi__write_context *s, int width, int height, int comp, const void* data, int quality) { 1369 | // Constants that don't pollute global namespace 1370 | static const unsigned char std_dc_luminance_nrcodes[] = {0,0,1,5,1,1,1,1,1,1,0,0,0,0,0,0,0}; 1371 | static const unsigned char std_dc_luminance_values[] = {0,1,2,3,4,5,6,7,8,9,10,11}; 1372 | static const unsigned char std_ac_luminance_nrcodes[] = {0,0,2,1,3,3,2,4,3,5,5,4,4,0,0,1,0x7d}; 1373 | static const unsigned char std_ac_luminance_values[] = { 1374 | 0x01,0x02,0x03,0x00,0x04,0x11,0x05,0x12,0x21,0x31,0x41,0x06,0x13,0x51,0x61,0x07,0x22,0x71,0x14,0x32,0x81,0x91,0xa1,0x08, 1375 | 0x23,0x42,0xb1,0xc1,0x15,0x52,0xd1,0xf0,0x24,0x33,0x62,0x72,0x82,0x09,0x0a,0x16,0x17,0x18,0x19,0x1a,0x25,0x26,0x27,0x28, 1376 | 0x29,0x2a,0x34,0x35,0x36,0x37,0x38,0x39,0x3a,0x43,0x44,0x45,0x46,0x47,0x48,0x49,0x4a,0x53,0x54,0x55,0x56,0x57,0x58,0x59, 1377 | 0x5a,0x63,0x64,0x65,0x66,0x67,0x68,0x69,0x6a,0x73,0x74,0x75,0x76,0x77,0x78,0x79,0x7a,0x83,0x84,0x85,0x86,0x87,0x88,0x89, 1378 | 0x8a,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9a,0xa2,0xa3,0xa4,0xa5,0xa6,0xa7,0xa8,0xa9,0xaa,0xb2,0xb3,0xb4,0xb5,0xb6, 1379 | 0xb7,0xb8,0xb9,0xba,0xc2,0xc3,0xc4,0xc5,0xc6,0xc7,0xc8,0xc9,0xca,0xd2,0xd3,0xd4,0xd5,0xd6,0xd7,0xd8,0xd9,0xda,0xe1,0xe2, 1380 | 0xe3,0xe4,0xe5,0xe6,0xe7,0xe8,0xe9,0xea,0xf1,0xf2,0xf3,0xf4,0xf5,0xf6,0xf7,0xf8,0xf9,0xfa 1381 | }; 1382 | static const unsigned char std_dc_chrominance_nrcodes[] = {0,0,3,1,1,1,1,1,1,1,1,1,0,0,0,0,0}; 1383 | static const unsigned char std_dc_chrominance_values[] = {0,1,2,3,4,5,6,7,8,9,10,11}; 1384 | static const unsigned char std_ac_chrominance_nrcodes[] = {0,0,2,1,2,4,4,3,4,7,5,4,4,0,1,2,0x77}; 1385 | static const unsigned char std_ac_chrominance_values[] = { 1386 | 0x00,0x01,0x02,0x03,0x11,0x04,0x05,0x21,0x31,0x06,0x12,0x41,0x51,0x07,0x61,0x71,0x13,0x22,0x32,0x81,0x08,0x14,0x42,0x91, 1387 | 0xa1,0xb1,0xc1,0x09,0x23,0x33,0x52,0xf0,0x15,0x62,0x72,0xd1,0x0a,0x16,0x24,0x34,0xe1,0x25,0xf1,0x17,0x18,0x19,0x1a,0x26, 1388 | 0x27,0x28,0x29,0x2a,0x35,0x36,0x37,0x38,0x39,0x3a,0x43,0x44,0x45,0x46,0x47,0x48,0x49,0x4a,0x53,0x54,0x55,0x56,0x57,0x58, 1389 | 0x59,0x5a,0x63,0x64,0x65,0x66,0x67,0x68,0x69,0x6a,0x73,0x74,0x75,0x76,0x77,0x78,0x79,0x7a,0x82,0x83,0x84,0x85,0x86,0x87, 1390 | 0x88,0x89,0x8a,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9a,0xa2,0xa3,0xa4,0xa5,0xa6,0xa7,0xa8,0xa9,0xaa,0xb2,0xb3,0xb4, 1391 | 0xb5,0xb6,0xb7,0xb8,0xb9,0xba,0xc2,0xc3,0xc4,0xc5,0xc6,0xc7,0xc8,0xc9,0xca,0xd2,0xd3,0xd4,0xd5,0xd6,0xd7,0xd8,0xd9,0xda, 1392 | 0xe2,0xe3,0xe4,0xe5,0xe6,0xe7,0xe8,0xe9,0xea,0xf2,0xf3,0xf4,0xf5,0xf6,0xf7,0xf8,0xf9,0xfa 1393 | }; 1394 | // Huffman tables 1395 | static const unsigned short YDC_HT[256][2] = { {0,2},{2,3},{3,3},{4,3},{5,3},{6,3},{14,4},{30,5},{62,6},{126,7},{254,8},{510,9}}; 1396 | static const unsigned short UVDC_HT[256][2] = { {0,2},{1,2},{2,2},{6,3},{14,4},{30,5},{62,6},{126,7},{254,8},{510,9},{1022,10},{2046,11}}; 1397 | static const unsigned short YAC_HT[256][2] = { 1398 | {10,4},{0,2},{1,2},{4,3},{11,4},{26,5},{120,7},{248,8},{1014,10},{65410,16},{65411,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, 1399 | {12,4},{27,5},{121,7},{502,9},{2038,11},{65412,16},{65413,16},{65414,16},{65415,16},{65416,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, 1400 | {28,5},{249,8},{1015,10},{4084,12},{65417,16},{65418,16},{65419,16},{65420,16},{65421,16},{65422,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, 1401 | {58,6},{503,9},{4085,12},{65423,16},{65424,16},{65425,16},{65426,16},{65427,16},{65428,16},{65429,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, 1402 | {59,6},{1016,10},{65430,16},{65431,16},{65432,16},{65433,16},{65434,16},{65435,16},{65436,16},{65437,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, 1403 | {122,7},{2039,11},{65438,16},{65439,16},{65440,16},{65441,16},{65442,16},{65443,16},{65444,16},{65445,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, 1404 | {123,7},{4086,12},{65446,16},{65447,16},{65448,16},{65449,16},{65450,16},{65451,16},{65452,16},{65453,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, 1405 | {250,8},{4087,12},{65454,16},{65455,16},{65456,16},{65457,16},{65458,16},{65459,16},{65460,16},{65461,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, 1406 | {504,9},{32704,15},{65462,16},{65463,16},{65464,16},{65465,16},{65466,16},{65467,16},{65468,16},{65469,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, 1407 | {505,9},{65470,16},{65471,16},{65472,16},{65473,16},{65474,16},{65475,16},{65476,16},{65477,16},{65478,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, 1408 | {506,9},{65479,16},{65480,16},{65481,16},{65482,16},{65483,16},{65484,16},{65485,16},{65486,16},{65487,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, 1409 | {1017,10},{65488,16},{65489,16},{65490,16},{65491,16},{65492,16},{65493,16},{65494,16},{65495,16},{65496,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, 1410 | {1018,10},{65497,16},{65498,16},{65499,16},{65500,16},{65501,16},{65502,16},{65503,16},{65504,16},{65505,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, 1411 | {2040,11},{65506,16},{65507,16},{65508,16},{65509,16},{65510,16},{65511,16},{65512,16},{65513,16},{65514,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, 1412 | {65515,16},{65516,16},{65517,16},{65518,16},{65519,16},{65520,16},{65521,16},{65522,16},{65523,16},{65524,16},{0,0},{0,0},{0,0},{0,0},{0,0}, 1413 | {2041,11},{65525,16},{65526,16},{65527,16},{65528,16},{65529,16},{65530,16},{65531,16},{65532,16},{65533,16},{65534,16},{0,0},{0,0},{0,0},{0,0},{0,0} 1414 | }; 1415 | static const unsigned short UVAC_HT[256][2] = { 1416 | {0,2},{1,2},{4,3},{10,4},{24,5},{25,5},{56,6},{120,7},{500,9},{1014,10},{4084,12},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, 1417 | {11,4},{57,6},{246,8},{501,9},{2038,11},{4085,12},{65416,16},{65417,16},{65418,16},{65419,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, 1418 | {26,5},{247,8},{1015,10},{4086,12},{32706,15},{65420,16},{65421,16},{65422,16},{65423,16},{65424,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, 1419 | {27,5},{248,8},{1016,10},{4087,12},{65425,16},{65426,16},{65427,16},{65428,16},{65429,16},{65430,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, 1420 | {58,6},{502,9},{65431,16},{65432,16},{65433,16},{65434,16},{65435,16},{65436,16},{65437,16},{65438,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, 1421 | {59,6},{1017,10},{65439,16},{65440,16},{65441,16},{65442,16},{65443,16},{65444,16},{65445,16},{65446,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, 1422 | {121,7},{2039,11},{65447,16},{65448,16},{65449,16},{65450,16},{65451,16},{65452,16},{65453,16},{65454,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, 1423 | {122,7},{2040,11},{65455,16},{65456,16},{65457,16},{65458,16},{65459,16},{65460,16},{65461,16},{65462,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, 1424 | {249,8},{65463,16},{65464,16},{65465,16},{65466,16},{65467,16},{65468,16},{65469,16},{65470,16},{65471,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, 1425 | {503,9},{65472,16},{65473,16},{65474,16},{65475,16},{65476,16},{65477,16},{65478,16},{65479,16},{65480,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, 1426 | {504,9},{65481,16},{65482,16},{65483,16},{65484,16},{65485,16},{65486,16},{65487,16},{65488,16},{65489,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, 1427 | {505,9},{65490,16},{65491,16},{65492,16},{65493,16},{65494,16},{65495,16},{65496,16},{65497,16},{65498,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, 1428 | {506,9},{65499,16},{65500,16},{65501,16},{65502,16},{65503,16},{65504,16},{65505,16},{65506,16},{65507,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, 1429 | {2041,11},{65508,16},{65509,16},{65510,16},{65511,16},{65512,16},{65513,16},{65514,16},{65515,16},{65516,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, 1430 | {16352,14},{65517,16},{65518,16},{65519,16},{65520,16},{65521,16},{65522,16},{65523,16},{65524,16},{65525,16},{0,0},{0,0},{0,0},{0,0},{0,0}, 1431 | {1018,10},{32707,15},{65526,16},{65527,16},{65528,16},{65529,16},{65530,16},{65531,16},{65532,16},{65533,16},{65534,16},{0,0},{0,0},{0,0},{0,0},{0,0} 1432 | }; 1433 | static const int YQT[] = {16,11,10,16,24,40,51,61,12,12,14,19,26,58,60,55,14,13,16,24,40,57,69,56,14,17,22,29,51,87,80,62,18,22, 1434 | 37,56,68,109,103,77,24,35,55,64,81,104,113,92,49,64,78,87,103,121,120,101,72,92,95,98,112,100,103,99}; 1435 | static const int UVQT[] = {17,18,24,47,99,99,99,99,18,21,26,66,99,99,99,99,24,26,56,99,99,99,99,99,47,66,99,99,99,99,99,99, 1436 | 99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99}; 1437 | static const float aasf[] = { 1.0f * 2.828427125f, 1.387039845f * 2.828427125f, 1.306562965f * 2.828427125f, 1.175875602f * 2.828427125f, 1438 | 1.0f * 2.828427125f, 0.785694958f * 2.828427125f, 0.541196100f * 2.828427125f, 0.275899379f * 2.828427125f }; 1439 | 1440 | int row, col, i, k, subsample; 1441 | float fdtbl_Y[64], fdtbl_UV[64]; 1442 | unsigned char YTable[64], UVTable[64]; 1443 | 1444 | if(!data || !width || !height || comp > 4 || comp < 1) { 1445 | return 0; 1446 | } 1447 | 1448 | quality = quality ? quality : 90; 1449 | subsample = quality <= 90 ? 1 : 0; 1450 | quality = quality < 1 ? 1 : quality > 100 ? 100 : quality; 1451 | quality = quality < 50 ? 5000 / quality : 200 - quality * 2; 1452 | 1453 | for(i = 0; i < 64; ++i) { 1454 | int uvti, yti = (YQT[i]*quality+50)/100; 1455 | YTable[stbiw__jpg_ZigZag[i]] = (unsigned char) (yti < 1 ? 1 : yti > 255 ? 255 : yti); 1456 | uvti = (UVQT[i]*quality+50)/100; 1457 | UVTable[stbiw__jpg_ZigZag[i]] = (unsigned char) (uvti < 1 ? 1 : uvti > 255 ? 255 : uvti); 1458 | } 1459 | 1460 | for(row = 0, k = 0; row < 8; ++row) { 1461 | for(col = 0; col < 8; ++col, ++k) { 1462 | fdtbl_Y[k] = 1 / (YTable [stbiw__jpg_ZigZag[k]] * aasf[row] * aasf[col]); 1463 | fdtbl_UV[k] = 1 / (UVTable[stbiw__jpg_ZigZag[k]] * aasf[row] * aasf[col]); 1464 | } 1465 | } 1466 | 1467 | // Write Headers 1468 | { 1469 | static const unsigned char head0[] = { 0xFF,0xD8,0xFF,0xE0,0,0x10,'J','F','I','F',0,1,1,0,0,1,0,1,0,0,0xFF,0xDB,0,0x84,0 }; 1470 | static const unsigned char head2[] = { 0xFF,0xDA,0,0xC,3,1,0,2,0x11,3,0x11,0,0x3F,0 }; 1471 | const unsigned char head1[] = { 0xFF,0xC0,0,0x11,8,(unsigned char)(height>>8),STBIW_UCHAR(height),(unsigned char)(width>>8),STBIW_UCHAR(width), 1472 | 3,1,(unsigned char)(subsample?0x22:0x11),0,2,0x11,1,3,0x11,1,0xFF,0xC4,0x01,0xA2,0 }; 1473 | s->func(s->context, (void*)head0, sizeof(head0)); 1474 | s->func(s->context, (void*)YTable, sizeof(YTable)); 1475 | stbiw__putc(s, 1); 1476 | s->func(s->context, UVTable, sizeof(UVTable)); 1477 | s->func(s->context, (void*)head1, sizeof(head1)); 1478 | s->func(s->context, (void*)(std_dc_luminance_nrcodes+1), sizeof(std_dc_luminance_nrcodes)-1); 1479 | s->func(s->context, (void*)std_dc_luminance_values, sizeof(std_dc_luminance_values)); 1480 | stbiw__putc(s, 0x10); // HTYACinfo 1481 | s->func(s->context, (void*)(std_ac_luminance_nrcodes+1), sizeof(std_ac_luminance_nrcodes)-1); 1482 | s->func(s->context, (void*)std_ac_luminance_values, sizeof(std_ac_luminance_values)); 1483 | stbiw__putc(s, 1); // HTUDCinfo 1484 | s->func(s->context, (void*)(std_dc_chrominance_nrcodes+1), sizeof(std_dc_chrominance_nrcodes)-1); 1485 | s->func(s->context, (void*)std_dc_chrominance_values, sizeof(std_dc_chrominance_values)); 1486 | stbiw__putc(s, 0x11); // HTUACinfo 1487 | s->func(s->context, (void*)(std_ac_chrominance_nrcodes+1), sizeof(std_ac_chrominance_nrcodes)-1); 1488 | s->func(s->context, (void*)std_ac_chrominance_values, sizeof(std_ac_chrominance_values)); 1489 | s->func(s->context, (void*)head2, sizeof(head2)); 1490 | } 1491 | 1492 | // Encode 8x8 macroblocks 1493 | { 1494 | static const unsigned short fillBits[] = {0x7F, 7}; 1495 | int DCY=0, DCU=0, DCV=0; 1496 | int bitBuf=0, bitCnt=0; 1497 | // comp == 2 is grey+alpha (alpha is ignored) 1498 | int ofsG = comp > 2 ? 1 : 0, ofsB = comp > 2 ? 2 : 0; 1499 | const unsigned char *dataR = (const unsigned char *)data; 1500 | const unsigned char *dataG = dataR + ofsG; 1501 | const unsigned char *dataB = dataR + ofsB; 1502 | int x, y, pos; 1503 | if(subsample) { 1504 | for(y = 0; y < height; y += 16) { 1505 | for(x = 0; x < width; x += 16) { 1506 | float Y[256], U[256], V[256]; 1507 | for(row = y, pos = 0; row < y+16; ++row) { 1508 | // row >= height => use last input row 1509 | int clamped_row = (row < height) ? row : height - 1; 1510 | int base_p = (stbi__flip_vertically_on_write ? (height-1-clamped_row) : clamped_row)*width*comp; 1511 | for(col = x; col < x+16; ++col, ++pos) { 1512 | // if col >= width => use pixel from last input column 1513 | int p = base_p + ((col < width) ? col : (width-1))*comp; 1514 | float r = dataR[p], g = dataG[p], b = dataB[p]; 1515 | Y[pos]= +0.29900f*r + 0.58700f*g + 0.11400f*b - 128; 1516 | U[pos]= -0.16874f*r - 0.33126f*g + 0.50000f*b; 1517 | V[pos]= +0.50000f*r - 0.41869f*g - 0.08131f*b; 1518 | } 1519 | } 1520 | DCY = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, Y+0, 16, fdtbl_Y, DCY, YDC_HT, YAC_HT); 1521 | DCY = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, Y+8, 16, fdtbl_Y, DCY, YDC_HT, YAC_HT); 1522 | DCY = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, Y+128, 16, fdtbl_Y, DCY, YDC_HT, YAC_HT); 1523 | DCY = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, Y+136, 16, fdtbl_Y, DCY, YDC_HT, YAC_HT); 1524 | 1525 | // subsample U,V 1526 | { 1527 | float subU[64], subV[64]; 1528 | int yy, xx; 1529 | for(yy = 0, pos = 0; yy < 8; ++yy) { 1530 | for(xx = 0; xx < 8; ++xx, ++pos) { 1531 | int j = yy*32+xx*2; 1532 | subU[pos] = (U[j+0] + U[j+1] + U[j+16] + U[j+17]) * 0.25f; 1533 | subV[pos] = (V[j+0] + V[j+1] + V[j+16] + V[j+17]) * 0.25f; 1534 | } 1535 | } 1536 | DCU = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, subU, 8, fdtbl_UV, DCU, UVDC_HT, UVAC_HT); 1537 | DCV = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, subV, 8, fdtbl_UV, DCV, UVDC_HT, UVAC_HT); 1538 | } 1539 | } 1540 | } 1541 | } else { 1542 | for(y = 0; y < height; y += 8) { 1543 | for(x = 0; x < width; x += 8) { 1544 | float Y[64], U[64], V[64]; 1545 | for(row = y, pos = 0; row < y+8; ++row) { 1546 | // row >= height => use last input row 1547 | int clamped_row = (row < height) ? row : height - 1; 1548 | int base_p = (stbi__flip_vertically_on_write ? (height-1-clamped_row) : clamped_row)*width*comp; 1549 | for(col = x; col < x+8; ++col, ++pos) { 1550 | // if col >= width => use pixel from last input column 1551 | int p = base_p + ((col < width) ? col : (width-1))*comp; 1552 | float r = dataR[p], g = dataG[p], b = dataB[p]; 1553 | Y[pos]= +0.29900f*r + 0.58700f*g + 0.11400f*b - 128; 1554 | U[pos]= -0.16874f*r - 0.33126f*g + 0.50000f*b; 1555 | V[pos]= +0.50000f*r - 0.41869f*g - 0.08131f*b; 1556 | } 1557 | } 1558 | 1559 | DCY = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, Y, 8, fdtbl_Y, DCY, YDC_HT, YAC_HT); 1560 | DCU = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, U, 8, fdtbl_UV, DCU, UVDC_HT, UVAC_HT); 1561 | DCV = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, V, 8, fdtbl_UV, DCV, UVDC_HT, UVAC_HT); 1562 | } 1563 | } 1564 | } 1565 | 1566 | // Do the bit alignment of the EOI marker 1567 | stbiw__jpg_writeBits(s, &bitBuf, &bitCnt, fillBits); 1568 | } 1569 | 1570 | // EOI 1571 | stbiw__putc(s, 0xFF); 1572 | stbiw__putc(s, 0xD9); 1573 | 1574 | return 1; 1575 | } 1576 | 1577 | STBIWDEF int stbi_write_jpg_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void *data, int quality) 1578 | { 1579 | stbi__write_context s = { 0 }; 1580 | stbi__start_write_callbacks(&s, func, context); 1581 | return stbi_write_jpg_core(&s, x, y, comp, (void *) data, quality); 1582 | } 1583 | 1584 | 1585 | #ifndef STBI_WRITE_NO_STDIO 1586 | STBIWDEF int stbi_write_jpg(char const *filename, int x, int y, int comp, const void *data, int quality) 1587 | { 1588 | stbi__write_context s = { 0 }; 1589 | if (stbi__start_write_file(&s,filename)) { 1590 | int r = stbi_write_jpg_core(&s, x, y, comp, data, quality); 1591 | stbi__end_write_file(&s); 1592 | return r; 1593 | } else 1594 | return 0; 1595 | } 1596 | #endif 1597 | 1598 | #endif // STB_IMAGE_WRITE_IMPLEMENTATION 1599 | 1600 | /* Revision history 1601 | 1.14 (2020-02-02) updated JPEG writer to downsample chroma channels 1602 | 1.13 1603 | 1.12 1604 | 1.11 (2019-08-11) 1605 | 1606 | 1.10 (2019-02-07) 1607 | support utf8 filenames in Windows; fix warnings and platform ifdefs 1608 | 1.09 (2018-02-11) 1609 | fix typo in zlib quality API, improve STB_I_W_STATIC in C++ 1610 | 1.08 (2018-01-29) 1611 | add stbi__flip_vertically_on_write, external zlib, zlib quality, choose PNG filter 1612 | 1.07 (2017-07-24) 1613 | doc fix 1614 | 1.06 (2017-07-23) 1615 | writing JPEG (using Jon Olick's code) 1616 | 1.05 ??? 1617 | 1.04 (2017-03-03) 1618 | monochrome BMP expansion 1619 | 1.03 ??? 1620 | 1.02 (2016-04-02) 1621 | avoid allocating large structures on the stack 1622 | 1.01 (2016-01-16) 1623 | STBIW_REALLOC_SIZED: support allocators with no realloc support 1624 | avoid race-condition in crc initialization 1625 | minor compile issues 1626 | 1.00 (2015-09-14) 1627 | installable file IO function 1628 | 0.99 (2015-09-13) 1629 | warning fixes; TGA rle support 1630 | 0.98 (2015-04-08) 1631 | added STBIW_MALLOC, STBIW_ASSERT etc 1632 | 0.97 (2015-01-18) 1633 | fixed HDR asserts, rewrote HDR rle logic 1634 | 0.96 (2015-01-17) 1635 | add HDR output 1636 | fix monochrome BMP 1637 | 0.95 (2014-08-17) 1638 | add monochrome TGA output 1639 | 0.94 (2014-05-31) 1640 | rename private functions to avoid conflicts with stb_image.h 1641 | 0.93 (2014-05-27) 1642 | warning fixes 1643 | 0.92 (2010-08-01) 1644 | casts to unsigned char to fix warnings 1645 | 0.91 (2010-07-17) 1646 | first public release 1647 | 0.90 first internal release 1648 | */ 1649 | 1650 | /* 1651 | ------------------------------------------------------------------------------ 1652 | This software is available under 2 licenses -- choose whichever you prefer. 1653 | ------------------------------------------------------------------------------ 1654 | ALTERNATIVE A - MIT License 1655 | Copyright (c) 2017 Sean Barrett 1656 | Permission is hereby granted, free of charge, to any person obtaining a copy of 1657 | this software and associated documentation files (the "Software"), to deal in 1658 | the Software without restriction, including without limitation the rights to 1659 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 1660 | of the Software, and to permit persons to whom the Software is furnished to do 1661 | so, subject to the following conditions: 1662 | The above copyright notice and this permission notice shall be included in all 1663 | copies or substantial portions of the Software. 1664 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 1665 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 1666 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 1667 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 1668 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 1669 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 1670 | SOFTWARE. 1671 | ------------------------------------------------------------------------------ 1672 | ALTERNATIVE B - Public Domain (www.unlicense.org) 1673 | This is free and unencumbered software released into the public domain. 1674 | Anyone is free to copy, modify, publish, use, compile, sell, or distribute this 1675 | software, either in source code form or as a compiled binary, for any purpose, 1676 | commercial or non-commercial, and by any means. 1677 | In jurisdictions that recognize copyright laws, the author or authors of this 1678 | software dedicate any and all copyright interest in the software to the public 1679 | domain. We make this dedication for the benefit of the public at large and to 1680 | the detriment of our heirs and successors. We intend this dedication to be an 1681 | overt act of relinquishment in perpetuity of all present and future rights to 1682 | this software under copyright law. 1683 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 1684 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 1685 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 1686 | AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 1687 | ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 1688 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 1689 | ------------------------------------------------------------------------------ 1690 | */ 1691 | --------------------------------------------------------------------------------