diff options
65 files changed, 13905 insertions, 5160 deletions
@@ -1,11 +1,26 @@ changelog --------- -Current Release: raylib 1.2 (16 September 2014) +Current Release: raylib 1.2.2 (31 December 2014) NOTE: Only versions marked as 'Release' are available in installer, updates are only available as source. NOTE: Current Release includes all previous updates. +----------------------------------------------- +Release: raylib 1.2.2 (31 December 2014) +----------------------------------------------- +[*] Added support for HTML5 compiling (emscripten, asm.js) +[core] Corrected bug on input handling (keyboard and mouse) +[textures] Renamed function CreateTexture() to LoadTextureFromImage() +[textures] Added function ConvertToPOT() +[models] Corrected bug on DrawBillboard() +[models] Corrected bug on DrawHeightmap() +[models] Renamed LoadCubesmap() to LoadCubicmap() +[audio] Added function LoadSoundFromWave() +[makefile] Added support for Linux compiling +[stb] Updated to latest headers versions +[*] Lots of tweaks around + --------------------------------------------------------------- Update: raylib 1.2.1 (17 October 2014) (Small Fixes Update) --------------------------------------------------------------- @@ -10,6 +10,9 @@ Allegro and SDL have also been analyzed for reference. Want to see how easy is making games with raylib? Jump to [code examples!] (http://www.raylib.com/examples.htm) +Since version 1.2.2 raylib can compile directly for web (html5) using emscripten and asm.js, +to see a demo of raylib features working on web, [check here!] (http://www.raylib.com/raylib_demo.html) + history ------- @@ -83,7 +86,7 @@ features * Powerful math module for Vector and Matrix operations [raymath] * Audio loading and playing with streaming support (WAV and OGG) * Custom color palette for fancy visuals on raywhite background - * Multiple platforms support: Windows, Linux, Mac, **Android** and **Raspberry Pi** + * Multiple platforms support: Windows, Linux, Mac, **Android**, **Raspberry Pi** and **HTML5** raylib uses on its core module the outstanding [GLFW3] (http://www.glfw.org/) library. The best option by far I found for multiplatform (Windows, Linux, Mac) window/context and input management (clean, focused, great license, well documented, modern, ...). @@ -160,6 +163,14 @@ _Step 3._ Navigate from command line to folder raylib/template_android/ and type NOTE: libraylib.a will be generated in folder raylib/src_android/obj/local/armeabi/, it must be copied to Android project; if using raylib/template_android project, copy it to raylib/template_android/jni/libs/. +**Building raylib sources for Web (HTML5)** + +_Step 1._ Make sure you have installed emscripten SDK: + +> Download latest version from [here].(http://kripken.github.io/emscripten-site/docs/getting_started/downloads.html) I recommend following the portable version installation instructions. + +_Step 2._ TODO + building examples ----------------- @@ -6,16 +6,18 @@ Here it is a list of features I would like to add and functions to improve. Around the source code there are some TODO points with pending revisions/bugs and here it is a list of features I would like to add. +This roadmap is quite outdated... a full list of all the features we are working on should be listed here at some point... + raylib v1.x - [DONE] Review Billboard Drawing functions - [DONE] Review Heightmap Loading and Drawing functions - Load Heightmap directly as a Model - Lighting support (only 3d mode) - [DONE] Simple Collision Detection functions - - Default scene Camera controls (zoom, pan, rotate) + - [IN PROGRESS] Default scene Camera controls (zoom, pan, rotate) - Basic Procedural Image Generation (Gradient, Checked, Spot, Noise, Cellular) - [DONE] Software mipmapping generation and POT conversion (custom implementation) - - TTF fonts support + - [IN PROGRESS] TTF fonts support Any feature missing? Do you have a request? [Let me know!][raysan5] diff --git a/examples/makefile b/examples/makefile index f4453213..d2656888 100644 --- a/examples/makefile +++ b/examples/makefile @@ -1,8 +1,6 @@ #************************************************************************************************** # -# raylib for Raspberry Pi and Windows desktop -# -# makefile to compile raylib examples +# raylib makefile for desktop platforms, Raspberry Pi and HTML5 (emscripten) # # Copyright (c) 2014 Ramon Santamaria (Ray San - raysan@raysanweb.com) # @@ -23,12 +21,19 @@ # #************************************************************************************************** -# define raylib platform (by default, compile for RPI) -# Other possible platforms: PLATFORM_DESKTOP PLATFORM_DESKTOP_LINUX PLATFORM_DESKTOP_OSX -PLATFORM ?= PLATFORM_DESKTOP_OSX +# define raylib platform to compile for +# possible platforms: PLATFORM_DESKTOP PLATFORM_RPI PLATFORM_WEB +# WARNING: To compile examples to HTML5, they must be redesigned to use emscripten.h and emscripten_set_main_loop() +PLATFORM ?= PLATFORM_DESKTOP # define compiler: gcc for C program, define as g++ for C++ -CC = gcc +ifeq ($(PLATFORM),PLATFORM_WEB) + # define emscripten compiler + CC = emcc +else + # define default gcc compiler + CC = gcc +endif # define compiler flags: # -O2 defines optimization level @@ -41,6 +46,12 @@ else endif #CFLAGSEXTRA = -Wextra -Wmissing-prototypes -Wstrict-prototypes +ifeq ($(PLATFORM),PLATFORM_WEB) + CFLAGS = -O1 -Wall -std=c99 -s USE_GLFW=3 -s ASSERTIONS=1 --preload-file resources + #-s ALLOW_MEMORY_GROWTH=1 # to allow memory resizing + #-s TOTAL_MEMORY=16777216 # to specify heap memory size (default = 16MB) +endif + # define any directories containing required header files ifeq ($(PLATFORM),PLATFORM_RPI) INCLUDES = -I. -I../src -I/opt/vc/include -I/opt/vc/include/interface/vcos/pthreads @@ -49,10 +60,10 @@ else endif # define library paths containing required libs -ifeq ($(PLATFORM),PLATFORM_DESKTOP_LINUX) - LFLAGS = -L. -L../src +ifeq ($(PLATFORM),PLATFORM_RPI) + LFLAGS = -L. -L../src -L/opt/vc/lib else - LFLAGS = -L. -L../src -L/opt/vc/lib + LFLAGS = -L. -L../src endif # define library paths containing required libs @@ -82,12 +93,16 @@ ifeq ($(PLATFORM),PLATFORM_DESKTOP_OSX) # libglfw3-dev libopenal-dev libglew-dev libegl1-mesa-dev LIBS = -lraylib -lglfw -framework OpenGL -framework OpenAl -framework Cocoa else +ifeq ($(PLATFORM),PLATFORM_WEB) + LIBS = ../src/libraylib.bc +else # libraries for Windows desktop compiling # NOTE: GLFW3 and OpenAL Soft libraries should be installed LIBS = -lraylib -lglfw3 -lglew32 -lopengl32 -lopenal32 -lgdi32 endif endif endif +endif # define additional parameters and flags for windows ifeq ($(PLATFORM),PLATFORM_DESKTOP) @@ -96,6 +111,10 @@ ifeq ($(PLATFORM),PLATFORM_DESKTOP) WINFLAGS = ../src/resources -Wl,--subsystem,windows endif +ifeq ($(PLATFORM),PLATFORM_WEB) + EXT = .html +endif + # define all object files required EXAMPLES = \ core_basic_window \ @@ -124,7 +143,7 @@ EXAMPLES = \ models_billboard \ models_obj_loading \ models_heightmap \ - models_cubesmap \ + models_cubicmap \ audio_sound_loading \ audio_music_stream \ #core_input_gamepad \ @@ -139,125 +158,125 @@ examples: $(EXAMPLES) # compile [core] example - basic window core_basic_window: core_basic_window.c - $(CC) -o $@ $< $(CFLAGS) $(INCLUDES) $(LFLAGS) $(LIBS) -D$(PLATFORM) $(WINFLAGS) + $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDES) $(LFLAGS) $(LIBS) -D$(PLATFORM) $(WINFLAGS) # compile [core] example - keyboard input core_input_keys: core_input_keys.c - $(CC) -o $@ $< $(CFLAGS) $(INCLUDES) $(LFLAGS) $(LIBS) -D$(PLATFORM) $(WINFLAGS) + $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDES) $(LFLAGS) $(LIBS) -D$(PLATFORM) $(WINFLAGS) # compile [core] example - mouse input core_input_mouse: core_input_mouse.c - $(CC) -o $@ $< $(CFLAGS) $(INCLUDES) $(LFLAGS) $(LIBS) -D$(PLATFORM) $(WINFLAGS) + $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDES) $(LFLAGS) $(LIBS) -D$(PLATFORM) $(WINFLAGS) ifeq ($(PLATFORM),PLATFORM_DESKTOP) # compile [core] example - gamepad input core_input_gamepad: core_input_gamepad.c - $(CC) -o $@ $< $(CFLAGS) $(INCLUDES) $(LFLAGS) $(LIBS) -D$(PLATFORM) $(WINFLAGS) + $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDES) $(LFLAGS) $(LIBS) -D$(PLATFORM) $(WINFLAGS) endif # compile [core] example - mouse wheel core_mouse_wheel: core_mouse_wheel.c - $(CC) -o $@ $< $(CFLAGS) $(INCLUDES) $(LFLAGS) $(LIBS) -D$(PLATFORM) $(WINFLAGS) + $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDES) $(LFLAGS) $(LIBS) -D$(PLATFORM) $(WINFLAGS) # compile [core] example - generate random values core_random_values: core_random_values.c - $(CC) -o $@ $< $(CFLAGS) $(INCLUDES) $(LFLAGS) $(LIBS) -D$(PLATFORM) $(WINFLAGS) + $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDES) $(LFLAGS) $(LIBS) -D$(PLATFORM) $(WINFLAGS) # compile [core] example - color selection (collision detection) core_color_select: core_color_select.c - $(CC) -o $@ $< $(CFLAGS) $(INCLUDES) $(LFLAGS) $(LIBS) -D$(PLATFORM) $(WINFLAGS) + $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDES) $(LFLAGS) $(LIBS) -D$(PLATFORM) $(WINFLAGS) # compile [core] example - 3d mode core_3d_mode: core_3d_mode.c - $(CC) -o $@ $< $(CFLAGS) $(INCLUDES) $(LFLAGS) $(LIBS) -D$(PLATFORM) $(WINFLAGS) + $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDES) $(LFLAGS) $(LIBS) -D$(PLATFORM) $(WINFLAGS) # compile [shapes] example - raylib logo (with basic shapes) shapes_logo_raylib: shapes_logo_raylib.c - $(CC) -o $@ $< $(CFLAGS) $(INCLUDES) $(LFLAGS) $(LIBS) -D$(PLATFORM) $(WINFLAGS) + $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDES) $(LFLAGS) $(LIBS) -D$(PLATFORM) $(WINFLAGS) # compile [shapes] example - basic shapes usage (rectangle, circle, ...) shapes_basic_shapes: shapes_basic_shapes.c - $(CC) -o $@ $< $(CFLAGS) $(INCLUDES) $(LFLAGS) $(LIBS) -D$(PLATFORM) $(WINFLAGS) + $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDES) $(LFLAGS) $(LIBS) -D$(PLATFORM) $(WINFLAGS) # compile [shapes] example - raylib color palette shapes_colors_palette: shapes_colors_palette.c - $(CC) -o $@ $< $(CFLAGS) $(INCLUDES) $(LFLAGS) $(LIBS) -D$(PLATFORM) $(WINFLAGS) + $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDES) $(LFLAGS) $(LIBS) -D$(PLATFORM) $(WINFLAGS) # compile [shapes] example - raylib logo animation shapes_logo_raylib_anim: shapes_logo_raylib_anim.c - $(CC) -o $@ $< $(CFLAGS) $(INCLUDES) $(LFLAGS) $(LIBS) -D$(PLATFORM) $(WINFLAGS) + $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDES) $(LFLAGS) $(LIBS) -D$(PLATFORM) $(WINFLAGS) # compile [textures] example - raylib logo texture loading textures_logo_raylib: textures_logo_raylib.c - $(CC) -o $@ $< $(CFLAGS) $(INCLUDES) $(LFLAGS) $(LIBS) -D$(PLATFORM) $(WINFLAGS) + $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDES) $(LFLAGS) $(LIBS) -D$(PLATFORM) $(WINFLAGS) # compile [textures] example - image loading and conversion to texture textures_image_loading: textures_image_loading.c - $(CC) -o $@ $< $(CFLAGS) $(INCLUDES) $(LFLAGS) $(LIBS) -D$(PLATFORM) $(WINFLAGS) + $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDES) $(LFLAGS) $(LIBS) -D$(PLATFORM) $(WINFLAGS) # compile [textures] example - texture rectangle drawing textures_rectangle: textures_rectangle.c - $(CC) -o $@ $< $(CFLAGS) $(INCLUDES) $(LFLAGS) $(LIBS) -D$(PLATFORM) $(WINFLAGS) + $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDES) $(LFLAGS) $(LIBS) -D$(PLATFORM) $(WINFLAGS) # compile [textures] example - compressed texture loading (DDS) textures_compressed_dds: textures_compressed_dds.c - $(CC) -o $@ $< $(CFLAGS) $(INCLUDES) $(LFLAGS) $(LIBS) -D$(PLATFORM) $(WINFLAGS) + $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDES) $(LFLAGS) $(LIBS) -D$(PLATFORM) $(WINFLAGS) # compile [textures] example - texture mipmaps generation textures_mipmaps: textures_mipmaps.c - $(CC) -o $@ $< $(CFLAGS) $(INCLUDES) $(LFLAGS) $(LIBS) -D$(PLATFORM) $(WINFLAGS) + $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDES) $(LFLAGS) $(LIBS) -D$(PLATFORM) $(WINFLAGS) # compile [textures] example - texture source and destination rectangles textures_srcrec_dstrec: textures_srcrec_dstrec.c - $(CC) -o $@ $< $(CFLAGS) $(INCLUDES) $(LFLAGS) $(LIBS) -D$(PLATFORM) $(WINFLAGS) + $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDES) $(LFLAGS) $(LIBS) -D$(PLATFORM) $(WINFLAGS) # compile [text] example - sprite fonts loading text_sprite_fonts: text_sprite_fonts.c - $(CC) -o $@ $< $(CFLAGS) $(INCLUDES) $(LFLAGS) $(LIBS) -D$(PLATFORM) $(WINFLAGS) + $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDES) $(LFLAGS) $(LIBS) -D$(PLATFORM) $(WINFLAGS) # compile [text] example - raylib bitmap fonts (rBMF) text_rbmf_fonts: text_rbmf_fonts.c - $(CC) -o $@ $< $(CFLAGS) $(INCLUDES) $(LFLAGS) $(LIBS) -D$(PLATFORM) $(WINFLAGS) + $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDES) $(LFLAGS) $(LIBS) -D$(PLATFORM) $(WINFLAGS) # compile [text] example - text formatting text_format_text: text_format_text.c - $(CC) -o $@ $< $(CFLAGS) $(INCLUDES) $(LFLAGS) $(LIBS) -D$(PLATFORM) $(WINFLAGS) + $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDES) $(LFLAGS) $(LIBS) -D$(PLATFORM) $(WINFLAGS) # compile [text] example - font selection program text_font_select: text_font_select.c - $(CC) -o $@ $< $(CFLAGS) $(INCLUDES) $(LFLAGS) $(LIBS) -D$(PLATFORM) $(WINFLAGS) + $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDES) $(LFLAGS) $(LIBS) -D$(PLATFORM) $(WINFLAGS) # compile [models] example - basic geometric 3d shapes models_geometric_shapes: models_geometric_shapes.c - $(CC) -o $@ $< $(CFLAGS) $(INCLUDES) $(LFLAGS) $(LIBS) -D$(PLATFORM) $(WINFLAGS) + $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDES) $(LFLAGS) $(LIBS) -D$(PLATFORM) $(WINFLAGS) # compile [models] example - basic window models_planes: models_planes.c - $(CC) -o $@ $< $(CFLAGS) $(INCLUDES) $(LFLAGS) $(LIBS) -D$(PLATFORM) $(WINFLAGS) + $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDES) $(LFLAGS) $(LIBS) -D$(PLATFORM) $(WINFLAGS) # compile [models] example - billboard usage models_billboard: models_billboard.c - $(CC) -o $@ $< $(CFLAGS) $(INCLUDES) $(LFLAGS) $(LIBS) -D$(PLATFORM) $(WINFLAGS) + $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDES) $(LFLAGS) $(LIBS) -D$(PLATFORM) $(WINFLAGS) # compile [models] example - OBJ model loading models_obj_loading: models_obj_loading.c - $(CC) -o $@ $< $(CFLAGS) $(INCLUDES) $(LFLAGS) $(LIBS) -D$(PLATFORM) $(WINFLAGS) + $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDES) $(LFLAGS) $(LIBS) -D$(PLATFORM) $(WINFLAGS) # compile [models] example - heightmap loading models_heightmap: models_heightmap.c - $(CC) -o $@ $< $(CFLAGS) $(INCLUDES) $(LFLAGS) $(LIBS) -D$(PLATFORM) $(WINFLAGS) + $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDES) $(LFLAGS) $(LIBS) -D$(PLATFORM) $(WINFLAGS) # compile [models] example - cubesmap loading -models_cubesmap: models_cubesmap.c - $(CC) -o $@ $< $(CFLAGS) $(INCLUDES) $(LFLAGS) $(LIBS) -D$(PLATFORM) $(WINFLAGS) +models_cubicmap: models_cubicmap.c + $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDES) $(LFLAGS) $(LIBS) -D$(PLATFORM) $(WINFLAGS) # compile [audio] example - sound loading and playing (WAV and OGG) audio_sound_loading: audio_sound_loading.c - $(CC) -o $@ $< $(CFLAGS) $(INCLUDES) $(LFLAGS) $(LIBS) -D$(PLATFORM) $(WINFLAGS) + $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDES) $(LFLAGS) $(LIBS) -D$(PLATFORM) $(WINFLAGS) # compile [audio] example - music stream playing (OGG) audio_music_stream: audio_music_stream.c - $(CC) -o $@ $< $(CFLAGS) $(INCLUDES) $(LFLAGS) $(LIBS) -D$(PLATFORM) $(WINFLAGS) + $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDES) $(LFLAGS) $(LIBS) -D$(PLATFORM) $(WINFLAGS) # clean everything clean: @@ -272,10 +291,14 @@ else ifeq ($(PLATFORM),PLATFORM_DESKTOP_OSX) rm -f *.o else +ifeq ($(PLATFORM),PLATFORM_WEB) + del *.o *.html *.js +else del *.o *.exe endif endif endif +endif @echo Cleaning done # instead of defining every module one by one, we can define a pattern diff --git a/examples/text_font_select.c b/examples/text_font_select.c index d6976b4f..62538ebc 100644 --- a/examples/text_font_select.c +++ b/examples/text_font_select.c @@ -84,7 +84,7 @@ int main() btnNextInColor = PURPLE; } - if (IsMouseButtonPressed(MOUSE_LEFT_BUTTON)) + if (IsMouseButtonDown(MOUSE_LEFT_BUTTON)) { framesCounter = 20; // Frames button is 'active' btnNextOutColor = MAROON; @@ -97,8 +97,8 @@ int main() btnNextOutColor = DARKBLUE; btnNextInColor = SKYBLUE; } - - if (IsMouseButtonReleased(MOUSE_LEFT_BUTTON) && (framesCounter > 0)) framesCounter--; + + if (framesCounter > 0) framesCounter--; if (framesCounter == 1) // We change font on frame 1 { diff --git a/examples/textures_logo_raylib.c b/examples/textures_logo_raylib.c index b56f5f03..f4aeb738 100644 --- a/examples/textures_logo_raylib.c +++ b/examples/textures_logo_raylib.c @@ -21,7 +21,7 @@ int main() InitWindow(screenWidth, screenHeight, "raylib [textures] example - texture loading and drawing"); // NOTE: Textures MUST be loaded after Window initialization (OpenGL context is required) - Texture2D texture = LoadTexture("./resources/raylib_logo.png"); // Texture loading + Texture2D texture = LoadTexture("resources/raylib_logo.png"); // Texture loading //--------------------------------------------------------------------------------------- // Main game loop diff --git a/games/floppy/floppy.c b/games/floppy/floppy.c new file mode 100644 index 00000000..0617797e --- /dev/null +++ b/games/floppy/floppy.c @@ -0,0 +1,210 @@ +/******************************************************************************************* +* +* raylib game - Floppy Bird +* +* Welcome to raylib! +* +* To test examples, just press F6 and execute raylib_compile_execute script +* Note that compiled executable is placed in the same folder as .c file +* +* You can find all basic examples on C:\raylib\raylib\examples folder or +* raylib official webpage: www.raylib.com +* +* Enjoy using raylib. :) +* +* This game has been created using raylib 1.1 (www.raylib.com) +* raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details) +* +* Copyright (c) 2014 Ramon Santamaria (Ray San - raysan@raysanweb.com) +* +********************************************************************************************/ + +#include "raylib.h" + +#define MAX_TUBES 100 + +int main() +{ + // Initialization + //-------------------------------------------------------------------------------------- + int screenWidth = 800; + int screenHeight = 450; + + InitWindow(screenWidth, screenHeight, "Floppy Bird"); + + InitAudioDevice(); // Initialize audio device + + Sound coin = LoadSound("resources/coin.wav"); + Sound jump = LoadSound("resources/jump.wav"); + + Texture2D background = LoadTexture("resources/background.png"); + Texture2D tubes = LoadTexture("resources/tubes.png"); + Texture2D floppy = LoadTexture("resources/floppy.png"); + + Vector2 floppyPos = { 80, screenHeight/2 - floppy.height/2 }; + + Vector2 tubesPos[MAX_TUBES]; + int tubesSpeedX = 2; + + for (int i = 0; i < MAX_TUBES; i++) + { + tubesPos[i].x = 400 + 280*i; + tubesPos[i].y = -GetRandomValue(0, 120); + } + + Rectangle tubesRecs[MAX_TUBES*2]; + bool tubesActive[MAX_TUBES]; + + for (int i = 0; i < MAX_TUBES*2; i += 2) + { + tubesRecs[i].x = tubesPos[i/2].x; + tubesRecs[i].y = tubesPos[i/2].y; + tubesRecs[i].width = tubes.width; + tubesRecs[i].height = 255; + + tubesRecs[i+1].x = tubesPos[i/2].x; + tubesRecs[i+1].y = 600 + tubesPos[i/2].y - 255; + tubesRecs[i+1].width = tubes.width; + tubesRecs[i+1].height = 255; + + tubesActive[i/2] = true; + } + + int backScroll = 0; + + int score = 0; + int hiscore = 0; + + bool gameover = false; + bool superfx = false; + + SetTargetFPS(60); + //--------------------------------------------------------------------------------------- + + // Main game loop + while (!WindowShouldClose()) // Detect window close button or ESC key + { + // Update + //---------------------------------------------------------------------------------- + backScroll--; + + if (backScroll <= -800) backScroll = 0; + + for (int i = 0; i < MAX_TUBES; i++) tubesPos[i].x -= tubesSpeedX; + + for (int i = 0; i < MAX_TUBES*2; i += 2) + { + tubesRecs[i].x = tubesPos[i/2].x; + tubesRecs[i+1].x = tubesPos[i/2].x; + } + + if (IsKeyDown(KEY_SPACE) && !gameover) floppyPos.y -= 3; + else floppyPos.y += 1; + + if (IsKeyPressed(KEY_SPACE) && !gameover) PlaySound(jump); + + // Check Collisions + for (int i = 0; i < MAX_TUBES*2; i++) + { + if (CheckCollisionCircleRec((Vector2){ floppyPos.x + floppy.width/2, floppyPos.y + floppy.height/2 }, floppy.width/2, tubesRecs[i])) + { + gameover = true; + } + else if ((tubesPos[i/2].x < floppyPos.x) && tubesActive[i/2] && !gameover) + { + score += 100; + tubesActive[i/2] = false; + PlaySound(coin); + + superfx = true; + + if (score > hiscore) hiscore = score; + } + } + + if (gameover && IsKeyPressed(KEY_ENTER)) + { + for (int i = 0; i < MAX_TUBES; i++) + { + tubesPos[i].x = 400 + 280*i; + tubesPos[i].y = -GetRandomValue(0, 120); + } + + for (int i = 0; i < MAX_TUBES*2; i += 2) + { + tubesRecs[i].x = tubesPos[i/2].x; + tubesRecs[i].y = tubesPos[i/2].y; + + tubesRecs[i+1].x = tubesPos[i/2].x; + tubesRecs[i+1].y = 600 + tubesPos[i/2].y - 255; + + tubesActive[i/2] = true; + } + + floppyPos.x = 80; + floppyPos.y = screenHeight/2 - floppy.height/2; + + gameover = false; + score = 0; + } + + //---------------------------------------------------------------------------------- + + // Draw + //---------------------------------------------------------------------------------- + BeginDrawing(); + + ClearBackground(RAYWHITE); + + DrawTexture(background, backScroll, 0, WHITE); + DrawTexture(background, screenWidth + backScroll, 0, WHITE); + + if (!gameover) + { + DrawTextureEx(floppy, floppyPos, 0, 1.0, WHITE); + //DrawCircleLines(floppyPos.x + floppy.width/2, floppyPos.y + floppy.height/2, floppy.width/2, RED); + } + + for (int i = 0; i < MAX_TUBES; i++) + { + if (tubesPos[i].x <= 800) DrawTextureEx(tubes, tubesPos[i], 0, 1.0, WHITE); + + //DrawRectangleLines(tubesRecs[i*2].x, tubesRecs[i*2].y, tubesRecs[i*2].width, tubesRecs[i*2].height, RED); + //DrawRectangleLines(tubesRecs[i*2 + 1].x, tubesRecs[i*2 + 1].y, tubesRecs[i*2 + 1].width, tubesRecs[i*2 + 1].height, RED); + } + + DrawText(FormatText("%04i", score), 20, 20, 40, PINK); + DrawText(FormatText("HI-SCORE: %04i", hiscore), 20, 70, 20, VIOLET); + + if (gameover) + { + DrawText("GAME OVER", 100, 180, 100, MAROON); + DrawText("PRESS ENTER to RETRY!", 280, 280, 20, RED); + } + + if (superfx) + { + DrawRectangle(0, 0, screenWidth, screenHeight, GOLD); + superfx = false; + } + + EndDrawing(); + //---------------------------------------------------------------------------------- + } + + // De-Initialization + //-------------------------------------------------------------------------------------- + UnloadTexture(background); // Texture unloading + UnloadTexture(tubes); // Texture unloading + UnloadTexture(floppy); // Texture unloading + + UnloadSound(coin); // Unload sound data + UnloadSound(jump); // Unload sound data + + CloseAudioDevice(); // Close audio device + + CloseWindow(); // Close window and OpenGL context + //-------------------------------------------------------------------------------------- + + return 0; +}
\ No newline at end of file diff --git a/games/floppy/resources/background.png b/games/floppy/resources/background.png Binary files differnew file mode 100644 index 00000000..eab9d865 --- /dev/null +++ b/games/floppy/resources/background.png diff --git a/games/floppy/resources/coin.wav b/games/floppy/resources/coin.wav Binary files differnew file mode 100644 index 00000000..d3b6e93c --- /dev/null +++ b/games/floppy/resources/coin.wav diff --git a/games/floppy/resources/floppy.png b/games/floppy/resources/floppy.png Binary files differnew file mode 100644 index 00000000..7c851086 --- /dev/null +++ b/games/floppy/resources/floppy.png diff --git a/games/floppy/resources/jump.wav b/games/floppy/resources/jump.wav Binary files differnew file mode 100644 index 00000000..1f68d336 --- /dev/null +++ b/games/floppy/resources/jump.wav diff --git a/games/floppy/resources/tubes.png b/games/floppy/resources/tubes.png Binary files differnew file mode 100644 index 00000000..a3ca8e7e --- /dev/null +++ b/games/floppy/resources/tubes.png diff --git a/games/raylib_demo/makefile b/games/raylib_demo/makefile new file mode 100644 index 00000000..3e2fe614 --- /dev/null +++ b/games/raylib_demo/makefile @@ -0,0 +1,123 @@ +#************************************************************************************************** +# +# raylib - Basic Game +# +# makefile to compile basic game +# +# Copyright (c) 2014 Ramon Santamaria (Ray San - raysan@raysanweb.com) +# +# This software is provided "as-is", without any express or implied warranty. In no event +# will the authors be held liable for any damages arising from the use of this software. +# +# Permission is granted to anyone to use this software for any purpose, including commercial +# applications, and to alter it and redistribute it freely, subject to the following restrictions: +# +# 1. The origin of this software must not be misrepresented; you must not claim that you +# wrote the original software. If you use this software in a product, an acknowledgment +# in the product documentation would be appreciated but is not required. +# +# 2. Altered source versions must be plainly marked as such, and must not be misrepresented +# as being the original software. +# +# 3. This notice may not be removed or altered from any source distribution. +# +#************************************************************************************************** + +# define raylib platform (by default, compile for RPI) +# Other possible platform: PLATFORM_DESKTOP, PLATFORM_WEB, PLATFORM_RPI +PLATFORM ?= PLATFORM_WEB + +# define compiler: gcc for C program, define as g++ for C++ +ifeq ($(PLATFORM),PLATFORM_WEB) + # define emscripten compiler + CC = emcc +else + # define default gcc compiler + CC = gcc +endif + +# define compiler flags: +# -O2 defines optimization level +# -Wall turns on most, but not all, compiler warnings +# -std=c99 use standard C from 1999 revision +ifeq ($(PLATFORM),PLATFORM_RPI) + CFLAGS = -O1 -Wall -std=gnu99 -fgnu89-inline +else + CFLAGS = -O1 -Wall -std=c99 +endif + +ifeq ($(PLATFORM),PLATFORM_WEB) + CFLAGS = -O1 -Wall -std=c99 -s USE_GLFW=3 -s ASSERTIONS=1 --preload-file resources + #-s ALLOW_MEMORY_GROWTH=1 # to allow memory resizing + #-s TOTAL_MEMORY=16777216 # to specify heap memory size (default = 16MB) +endif + +#CFLAGSEXTRA = -Wextra -Wmissing-prototypes -Wstrict-prototypes + +# define any directories containing required header files +ifeq ($(PLATFORM),PLATFORM_RPI) + INCLUDES = -I. -I../../src -I/opt/vc/include -I/opt/vc/include/interface/vcos/pthreads +else + INCLUDES = -I. -I../../src +endif + +# define library paths containing required libs +ifeq ($(PLATFORM),PLATFORM_WEB) + LFLAGS = -L. +else + LFLAGS = -L. -L../../src -L/opt/vc/lib +endif + +# define any libraries to link into executable +# if you want to link libraries (libname.so or libname.a), use the -lname +ifeq ($(PLATFORM),PLATFORM_RPI) + # libraries for Raspberry Pi compiling + # NOTE: OpenAL Soft library should be installed (libopenal1 package) + LIBS = -lraylib -lGLESv2 -lEGL -lpthread -lrt -lm -lbcm_host -lopenal +else + # libraries for Windows desktop compiling + # NOTE: GLFW3 and OpenAL Soft libraries should be installed + LIBS = -lraylib -lglfw3 -lglew32 -lopengl32 -lopenal32 -lgdi32 +endif + +ifeq ($(PLATFORM),PLATFORM_WEB) + LIBS = ../../src/libraylib.bc +endif + +# define additional parameters and flags for windows +ifeq ($(PLATFORM),PLATFORM_DESKTOP) + # resources file contains windows exe icon + # -Wl,--subsystem,windows hides the console window + WINFLAGS = ../../src/resources -Wl,--subsystem,windows +endif + +ifeq ($(PLATFORM),PLATFORM_WEB) + EXT = .html +endif + +# typing 'make' will invoke the first target entry in the file, +# in this case, the 'default' target entry is qidv_raylib +default: raylib_demo + +# compile qidv_raylib +raylib_demo: raylib_demo.c + $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDES) $(LFLAGS) $(LIBS) -D$(PLATFORM) $(WINFLAGS) + +# clean everything +clean: +ifeq ($(PLATFORM),PLATFORM_RPI) + rm -f *.o +# find . -executable -delete +else +ifeq ($(PLATFORM),PLATFORM_WEB) + del *.html *.js +else + del *.o *.exe +endif +endif + @echo Cleaning done + +# instead of defining every module one by one, we can define a pattern +# this pattern below will automatically compile every module defined on $(OBJS) +#%.exe : %.c +# $(CC) -o $@ $< $(CFLAGS) $(INCLUDES) $(LFLAGS) $(LIBS) -D$(PLATFORM) diff --git a/games/raylib_demo/raylib_demo.c b/games/raylib_demo/raylib_demo.c new file mode 100644 index 00000000..5bbccbe8 --- /dev/null +++ b/games/raylib_demo/raylib_demo.c @@ -0,0 +1,926 @@ +/******************************************************************************************* +* +* raylib - Talk: QIDV raylib (Learn Videogames Programming) +* +* Aprende a Programar Videojuegos con raylib +* +* This talk has been created using raylib v1.2 (www.raylib.com) +* raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details) +* +* Copyright (c) 2014 Ramon Santamaria (Ray San - raysan@raysanweb.com) +* +********************************************************************************************/ + +#include "raylib.h" + +#if defined(PLATFORM_WEB) + #include <emscripten/emscripten.h> +#endif + +#include <string.h> +#include <math.h> + +#define MAX_BALLS 16 + +//---------------------------------------------------------------------------------- +// Types and Structures Definition +//---------------------------------------------------------------------------------- +typedef enum TalkScreen { LOADING, LOGO, MODULES, ENDING, PONG } TalkScreen; +typedef enum Modules { CORE = 0, SHAPES, TEXTURES, TEXT, MODELS, AUDIO } Modules; + +//---------------------------------------------------------------------------------- +// Global Variables Definition (local to this module) +//---------------------------------------------------------------------------------- +int screenWidth = 1280; +int screenHeight = 720; + +const char msgLoading[30] = "LOADING..."; +const char msgPressEnter[30] = "Press ENTER to START"; + +const char msgCredits[40] = "by RAMON SANTAMARIA [@raysan5]"; +const char msgWeb[30] = "www.raylib.com"; + +const char msgLogoA[40] = "A simple and easy-to-use library"; +const char msgLogoB[40] = "to learn videogames programming"; + +const char msg1[50] = "THIS is a CUSTOM FONT..."; +const char msg2[50] = "...and ANOTHER CUSTOM ONE..."; +const char msg3[50] = "...AND ONE MORE! :)"; + +bool closeWindow = false; + +int totalTime = 60*60*60; // fps*sec*min +int timeCounter = 0; + +TalkScreen currentScreen = LOADING; + +// LOADING screen variables +int loadBarWidth = 0; +int loadBarMaxWidth = 600; + +// TITLE screen variables +SpriteFont fontAlagard; +SpriteFont fontPixelplay; +SpriteFont fontMecha; +SpriteFont fontSetback; +SpriteFont fontRomulus; + +Vector2 pongBallPosition; +Vector2 pongBallSpeed; +Rectangle pongPlayerRec; +Rectangle pongEnemyRec; +int pongScorePlayer = 0; +int pongScoreEnemy = 0; +bool pongAutoMode = true; +int pongAutoCounter = 0; +bool pongPaused = true; + +int lettersCounter = 0; +char msgBuffer[120] = { ' ' }; + +// LOGO screen variables +int logoPositionX; +int logoPositionY; + +int raylibLettersCount = 0; + +int topSideRecWidth = 16; +int leftSideRecHeight = 16; + +int bottomSideRecWidth = 16; +int rightSideRecHeight = 16; + +char raylib[8] = " \0"; // raylib text array, max 8 letters + +int logoScreenState = 0; // Tracking animation states (State Machine) +bool msgLogoADone = false; +bool msgLogoBDone = false; + +// MODULES screen variables +Modules selectedModule = CORE; + +Texture2D raylibWindow; +Texture2D raylibWindow01; +Texture2D raylibWindow02; +Texture2D raylibWindow03; +Texture2D platforms; +Texture2D raylibLogoB; +Texture2D lena; +Texture2D mandrill; +Texture2D texAlagard; +SpriteFont fontMechaC; +SpriteFont fontAlagardC; +SpriteFont fontJupiterC; + +int coreWindow = 1; + +int windowOffset = 0; +Vector2 ballPosition; + +Camera camera; + +Texture2D catTexture; +Model cat; + +Sound fxWav; +Sound fxOgg; + +Vector2 soundBallsPosition[MAX_BALLS]; +Color soundBallsColor[MAX_BALLS]; +bool soundBallsActive[MAX_BALLS]; +float soundBallsAlpha[MAX_BALLS]; +int soundBallsRadius[MAX_BALLS]; + +float scaleFactor = 0.0f; +float timePlayed = 0; + +// ENDING screen variables +Texture2D raylibLogoA; + +// Required variables to manage screen transitions (fade-in, fade-out) +float transAlpha = 0; +bool onTransition = false; +bool transFadeOut = false; +int transFromScreen = -1; +int transToScreen = -1; + +int framesCounter = 0; + +//---------------------------------------------------------------------------------- +// Local Functions Declaration +//---------------------------------------------------------------------------------- +void TransitionToScreen(int screen); +void UpdateTransition(void); +void DrawTransition(void); + +void UpdateDrawOneFrame(void); + +//---------------------------------------------------------------------------------- +// Main entry point +//---------------------------------------------------------------------------------- +int main() +{ + // Initialization + //-------------------------------------------------------------------------------------- + const char windowTitle[30] = "raylib functionality demo"; + + //SetupFlags(FLAG_FULLSCREEN_MODE); + InitWindow(screenWidth, screenHeight, windowTitle); + + InitAudioDevice(); // Initialize audio device + + // TITLE screen variables Initialization + fontAlagard = LoadSpriteFont("resources/fonts/alagard.rbmf"); // rBMF font loading + fontPixelplay = LoadSpriteFont("resources/fonts/pixelplay.rbmf"); // rBMF font loading + fontMecha = LoadSpriteFont("resources/fonts/mecha.rbmf"); // rBMF font loading + fontSetback = LoadSpriteFont("resources/fonts/setback.rbmf"); // rBMF font loading + fontRomulus = LoadSpriteFont("resources/fonts/romulus.rbmf"); // rBMF font loading + + pongBallPosition = (Vector2){ screenWidth/2, screenHeight/2 + 20 }; + pongBallSpeed = (Vector2){ 6, 6 }; + pongPlayerRec = (Rectangle){ 20, screenHeight/2 - 50 + 40, 20, 100 }; + pongEnemyRec = (Rectangle){ screenWidth - 40, screenHeight/2 - 60, 20, 120 }; + + // LOGO screen variables Initialization + logoPositionX = screenWidth/2 - 128; + logoPositionY = screenHeight/2 - 128; + + // MODULES screen variables Initialization + raylibWindow = LoadTexture("resources/raylib_window.png"); + raylibWindow01 = LoadTexture("resources/raylib_window_01.png"); + raylibWindow02 = LoadTexture("resources/raylib_window_02.png"); + raylibWindow03 = LoadTexture("resources/raylib_window_03.png"); + platforms = LoadTexture("resources/platforms.png"); + raylibLogoB = LoadTexture("resources/raylib_logo128x128.png"); + lena = LoadTexture("resources/lena.png"); + mandrill = LoadTexture("resources/mandrill.png"); + texAlagard = LoadTexture("resources/fonts/custom_alagard.png"); + fontMechaC = LoadSpriteFont("resources/fonts/custom_mecha.png"); + fontAlagardC = LoadSpriteFont("resources/fonts/custom_alagard.png"); + fontJupiterC = LoadSpriteFont("resources/fonts/custom_jupiter_crash.png"); + + ballPosition = (Vector2){ 520 + 656/2, 220 + 399/2 }; + + camera = (Camera){{ 0.0, 12.0, 15.0 }, { 0.0, 3.0, 0.0 }, { 0.0, 1.0, 0.0 }}; + + catTexture = LoadTexture("resources/catsham.png"); // Load model texture + cat = LoadModel("resources/cat.obj"); // Load OBJ model + SetModelTexture(&cat, catTexture); + + fxWav = LoadSound("resources/audio/weird.wav"); // Load WAV audio file + fxOgg = LoadSound("resources/audio/tanatana.ogg"); // Load OGG audio file + + for (int i = 0; i < MAX_BALLS; i++) + { + soundBallsPosition[i] = (Vector2){ 650 + 560/2 + GetRandomValue(-280, 280), 220 + 200 + GetRandomValue(-200, 200) }; + soundBallsColor[i] = (Color){ GetRandomValue(0, 255), GetRandomValue(0, 255), GetRandomValue(0, 255), 255 }; + soundBallsRadius[i] = GetRandomValue(2, 50); + soundBallsAlpha[i] = 1.0f; + + soundBallsActive[i] = false; + } + + // ENDING screen variables Initialization + raylibLogoA = LoadTexture("resources/raylib_logo.png"); + +#ifndef PLATFORM_WEB + SetTargetFPS(60); +#endif + +#if defined(PLATFORM_WEB) + emscripten_set_main_loop(UpdateDrawOneFrame, 0, 1); +#else + //-------------------------------------------------------------------------------------- + + // Main game loop + while (!WindowShouldClose() && !closeWindow) // Detect window close button or ESC key + { + UpdateDrawOneFrame(); + } +#endif + + // De-Initialization + //-------------------------------------------------------------------------------------- + + // Unload all loaded data (textures, fonts, audio) + UnloadSpriteFont(fontAlagard); // SpriteFont unloading + UnloadSpriteFont(fontPixelplay); // SpriteFont unloading + UnloadSpriteFont(fontMecha); // SpriteFont unloading + UnloadSpriteFont(fontSetback); // SpriteFont unloading + UnloadSpriteFont(fontRomulus); // SpriteFont unloading + + UnloadTexture(raylibWindow); + UnloadTexture(raylibWindow01); + UnloadTexture(raylibWindow02); + UnloadTexture(raylibWindow03); + UnloadTexture(platforms); + UnloadTexture(raylibLogoA); + UnloadTexture(raylibLogoB); + UnloadTexture(lena); + UnloadTexture(mandrill); + UnloadTexture(texAlagard); + + UnloadSpriteFont(fontMechaC); + UnloadSpriteFont(fontAlagardC); + UnloadSpriteFont(fontJupiterC); + + UnloadTexture(catTexture); + UnloadModel(cat); + + UnloadSound(fxWav); + UnloadSound(fxOgg); + + CloseAudioDevice(); + + CloseWindow(); // Close window and OpenGL context + //-------------------------------------------------------------------------------------- + + return 0; +} + +void TransitionToScreen(int screen) +{ + onTransition = true; + transFromScreen = currentScreen; + transToScreen = screen; +} + +void UpdateTransition(void) +{ + if (!transFadeOut) + { + transAlpha += 0.02f; + + if (transAlpha >= 1.0) + { + transAlpha = 1.0; + currentScreen = transToScreen; + transFadeOut = true; + framesCounter = 0; + } + } + else // Transition fade out logic + { + transAlpha -= 0.02f; + + if (transAlpha <= 0) + { + transAlpha = 0; + transFadeOut = false; + onTransition = false; + transFromScreen = -1; + transToScreen = -1; + } + } +} + +void DrawTransition(void) +{ + DrawRectangle(0, 0, GetScreenWidth(), GetScreenHeight(), Fade(RAYWHITE, transAlpha)); +} + +void UpdateDrawOneFrame(void) +{ + // Update + //---------------------------------------------------------------------------------- + if (!onTransition) + { + switch(currentScreen) + { + case LOADING: + { + // Update LOADING screen variables + framesCounter++; // Count frames + + if ((loadBarWidth < loadBarMaxWidth) && ((framesCounter%30) == 0)) loadBarWidth++; + + if (IsKeyDown(KEY_SPACE) && (loadBarWidth < loadBarMaxWidth)) loadBarWidth += 4; + + if (IsKeyPressed(KEY_ENTER) && (loadBarWidth >= loadBarMaxWidth)) TransitionToScreen(LOGO); + + } break; + case LOGO: + { + // Update LOGO screen variables + if (logoScreenState == 0) // State 0: Small box blinking + { + framesCounter++; + + if (framesCounter == 120) + { + logoScreenState = 1; + framesCounter = 0; // Reset counter... will be used later... + } + } + else if (logoScreenState == 1) // State 1: Top and left bars growing + { + topSideRecWidth += 4; + leftSideRecHeight += 4; + + if (topSideRecWidth == 256) logoScreenState = 2; + } + else if (logoScreenState == 2) // State 2: Bottom and right bars growing + { + bottomSideRecWidth += 4; + rightSideRecHeight += 4; + + if (bottomSideRecWidth == 256) + { + lettersCounter = 0; + for (int i = 0; i < strlen(msgBuffer); i++) msgBuffer[i] = ' '; + + logoScreenState = 3; + } + } + else if (logoScreenState == 3) // State 3: Letters appearing (one by one) + { + framesCounter++; + + // Every 12 frames, one more letter! + if ((framesCounter%12) == 0) raylibLettersCount++; + + switch (raylibLettersCount) + { + case 1: raylib[0] = 'r'; break; + case 2: raylib[1] = 'a'; break; + case 3: raylib[2] = 'y'; break; + case 4: raylib[3] = 'l'; break; + case 5: raylib[4] = 'i'; break; + case 6: raylib[5] = 'b'; break; + default: break; + } + + if (raylibLettersCount >= 10) + { + // Write raylib description messages + if ((framesCounter%2) == 0) lettersCounter++; + + if (!msgLogoADone) + { + if (lettersCounter <= strlen(msgLogoA)) strncpy(msgBuffer, msgLogoA, lettersCounter); + else + { + for (int i = 0; i < strlen(msgBuffer); i++) msgBuffer[i] = ' '; + + lettersCounter = 0; + msgLogoADone = true; + } + } + else if (!msgLogoBDone) + { + if (lettersCounter <= strlen(msgLogoB)) strncpy(msgBuffer, msgLogoB, lettersCounter); + else + { + msgLogoBDone = true; + framesCounter = 0; + } + } + } + } + + // Press enter to change to MODULES screen + if (IsKeyPressed(KEY_ENTER) && msgLogoBDone) TransitionToScreen(MODULES); + else if (IsKeyPressed(KEY_BACKSPACE)) TransitionToScreen(LOGO); + + } break; + case MODULES: + { + // Update MODULES screen variables here! + framesCounter++; + + if (IsKeyPressed(KEY_RIGHT) && (selectedModule < 5)) + { + selectedModule++; + framesCounter = 0; + } + else if (IsKeyPressed(KEY_LEFT) && (selectedModule > 0)) + { + selectedModule--; + framesCounter = 0; + } + + if (selectedModule == CORE) + { + if ((framesCounter > 60) && (windowOffset < 40)) + { + windowOffset++; + ballPosition.x++; + ballPosition.y++; + } + + if (framesCounter > 140) + { + if (IsKeyDown('A')) ballPosition.x -= 5; + if (IsKeyDown('D')) ballPosition.x += 5; + if (IsKeyDown('W')) ballPosition.y -= 5; + if (IsKeyDown('S')) ballPosition.y += 5; + + if (IsKeyPressed('1')) coreWindow = 1; + if (IsKeyPressed('2')) coreWindow = 2; + if (IsKeyPressed('3')) coreWindow = 3; + if (IsKeyPressed('4')) coreWindow = 4; + } + } + + if (selectedModule == TEXTURES) scaleFactor = (sinf(2*PI/240*framesCounter) + 1.0f)/2; + + if (selectedModule == AUDIO) + { + if (IsKeyPressed(KEY_SPACE) && !MusicIsPlaying()) PlayMusicStream("resources/audio/guitar_noodling.ogg"); // Play music stream + + if (IsKeyPressed('S')) + { + StopMusicStream(); + timePlayed = 0.0f; + + for (int i = 0; i < MAX_BALLS; i++) + { + soundBallsPosition[i] = (Vector2){ 650 + 560/2 + GetRandomValue(-280, 280), 220 + 200 + GetRandomValue(-200, 200) }; + soundBallsColor[i] = (Color){ GetRandomValue(0, 255), GetRandomValue(0, 255), GetRandomValue(0, 255), 255 }; + soundBallsRadius[i] = GetRandomValue(2, 50); + soundBallsAlpha[i] = 1.0f; + + soundBallsActive[i] = false; + } + } + + if (MusicIsPlaying()) + { + timePlayed = GetMusicTimePlayed() / GetMusicTimeLength() * 100 * 4; + + if ((framesCounter%10) == 0) + { + for (int i = 0; i < MAX_BALLS; i++) + { + if (!soundBallsActive[i]) + { + soundBallsActive[i] = true; + break; + } + } + } + + for (int i = 0; i < MAX_BALLS; i++) + { + if (soundBallsActive[i]) soundBallsAlpha[i] -= 0.005f; + + if (soundBallsAlpha[i] <= 0) + { + soundBallsActive[i] = false; + + // Reset ball random + soundBallsPosition[i] = (Vector2){ 650 + 560/2 + GetRandomValue(-280, 280), 220 + 200 + GetRandomValue(-200, 200) }; + soundBallsColor[i] = (Color){ GetRandomValue(0, 255), GetRandomValue(0, 255), GetRandomValue(0, 255), 255 }; + soundBallsRadius[i] = GetRandomValue(2, 60); + soundBallsAlpha[i] = 1.0f; + } + } + } + + if (IsKeyPressed('N')) PlaySound(fxWav); + //if (IsKeyPressed('M')) PlaySound(fxOgg); + } + + // Press enter to change to ENDING screen + if (IsKeyPressed(KEY_ENTER)) TransitionToScreen(ENDING); + else if (IsKeyPressed(KEY_BACKSPACE)) TransitionToScreen(LOGO); + + } break; + case PONG: + { + // Update SECRET screen variables here! + framesCounter++; + + if (IsKeyPressed('P')) pongPaused = !pongPaused; + + if (!pongPaused) + { + pongBallPosition.x += pongBallSpeed.x; + pongBallPosition.y += pongBallSpeed.y; + + if ((pongBallPosition.x >= screenWidth - 5) || (pongBallPosition.x <= 5)) pongBallSpeed.x *= -1; + if ((pongBallPosition.y >= screenHeight - 5) || (pongBallPosition.y <= 5)) pongBallSpeed.y *= -1; + + if (IsKeyDown(KEY_UP) || IsKeyDown('W')) + { + pongPlayerRec.y -= 5; + pongAutoMode = false; + pongAutoCounter = 180; + } + else if (IsKeyDown(KEY_DOWN) || IsKeyDown('S')) + { + pongPlayerRec.y += 5; + pongAutoMode = false; + pongAutoCounter = 180; + } + else if (pongAutoCounter > 0) + { + pongAutoCounter--; + + if (pongAutoCounter == 0) pongAutoMode = true; + } + + if ((pongBallPosition.x < 600) && pongAutoMode) + { + if (pongBallPosition.y > (pongPlayerRec.y + pongPlayerRec.height/2)) pongPlayerRec.y += 5; + else if (pongBallPosition.y < (pongPlayerRec.y + pongPlayerRec.height/2)) pongPlayerRec.y -= 5; + } + + if (pongPlayerRec.y <= 0) pongPlayerRec.y = 0; + else if ((pongPlayerRec.y + pongPlayerRec.height) >= screenHeight) pongPlayerRec.y = screenHeight - pongPlayerRec.height; + + if (pongBallPosition.x > screenWidth - 600) + { + if (pongBallPosition.y > (pongEnemyRec.y + pongEnemyRec.height/2)) pongEnemyRec.y += 5; + else if (pongBallPosition.y < (pongEnemyRec.y + pongEnemyRec.height/2)) pongEnemyRec.y -= 5; + + if (pongEnemyRec.y <= 0) pongEnemyRec.y = 0; + else if ((pongEnemyRec.y + pongEnemyRec.height) >= screenHeight) pongEnemyRec.y = screenHeight - pongEnemyRec.height; + } + + if ((CheckCollisionCircleRec(pongBallPosition, 10, pongPlayerRec)) || (CheckCollisionCircleRec(pongBallPosition, 10, pongEnemyRec))) pongBallSpeed.x *= -1; + + if (pongBallPosition.x >= screenWidth - 5) pongScorePlayer++; + else if (pongBallPosition.x <= 5) pongScoreEnemy++; + } + + // Press enter to move back to MODULES screen + if (IsKeyPressed(KEY_ENTER)) TransitionToScreen(ENDING); + if (IsKeyPressed(KEY_BACKSPACE)) TransitionToScreen(ENDING); + } break; + case ENDING: + { + // Update ENDING screen + framesCounter++; + + // Press enter to move back to MODULES screen + if (IsKeyPressed(KEY_ENTER)) TransitionToScreen(PONG); + if (IsKeyPressed(KEY_BACKSPACE)) TransitionToScreen(MODULES); + + } break; + default: break; + } + + if ((currentScreen != LOADING) && (timeCounter < totalTime)) timeCounter++; + } + else UpdateTransition(); // Update transition (fade-in, fade-out) + //---------------------------------------------------------------------------------- + + // Draw + //---------------------------------------------------------------------------------- + BeginDrawing(); + + ClearBackground(RAYWHITE); + + switch(currentScreen) + { + case LOADING: + { + // Draw LOADING screen + if ((loadBarWidth < loadBarMaxWidth) && ((framesCounter/40)%2)) DrawText(msgLoading, 360, 240, 40, DARKGRAY); + + DrawRectangle(360 - 4, 300 - 4, loadBarMaxWidth + 8, 60 + 8, LIGHTGRAY); + DrawRectangle(360, 300, loadBarWidth - 1, 60, DARKGRAY); + DrawRectangleLines(360 - 4, 300 - 5, loadBarMaxWidth + 8, 60 + 8, DARKGRAY); + + if (loadBarWidth >= loadBarMaxWidth) + { + //DrawText(msgLoading, 360, 240, 40, DARKGRAY); + if ((framesCounter/30)%2) DrawText(msgPressEnter, screenWidth/2 - MeasureText(msgPressEnter, 40)/2 + 20, 400, 40, DARKGRAY); + } + else DrawText("PRESS SPACE to ACCELERATE LOADING! ;)", screenWidth/2 - 200, 400, 20, LIGHTGRAY); + + } break; + case LOGO: + { + // Draw LOGO screen + if (logoScreenState == 0) + { + if ((framesCounter/15)%2) DrawRectangle(logoPositionX, logoPositionY - 60, 16, 16, BLACK); + } + else if (logoScreenState == 1) + { + DrawRectangle(logoPositionX, logoPositionY - 60, topSideRecWidth, 16, BLACK); + DrawRectangle(logoPositionX, logoPositionY - 60, 16, leftSideRecHeight, BLACK); + } + else if (logoScreenState == 2) + { + DrawRectangle(logoPositionX, logoPositionY - 60, topSideRecWidth, 16, BLACK); + DrawRectangle(logoPositionX, logoPositionY - 60, 16, leftSideRecHeight, BLACK); + + DrawRectangle(logoPositionX + 240, logoPositionY - 60, 16, rightSideRecHeight, BLACK); + DrawRectangle(logoPositionX, logoPositionY + 240 - 60, bottomSideRecWidth, 16, BLACK); + } + else if (logoScreenState == 3) + { + DrawRectangle(logoPositionX, logoPositionY - 60, topSideRecWidth, 16, BLACK); + DrawRectangle(logoPositionX, logoPositionY + 16 - 60, 16, leftSideRecHeight - 32, BLACK); + + DrawRectangle(logoPositionX + 240, logoPositionY + 16 - 60, 16, rightSideRecHeight - 32, BLACK); + DrawRectangle(logoPositionX, logoPositionY + 240 - 60, bottomSideRecWidth, 16, BLACK); + + DrawRectangle(screenWidth/2 - 112, screenHeight/2 - 112 - 60, 224, 224, RAYWHITE); + + DrawText(raylib, screenWidth/2 - 44, screenHeight/2 + 48 - 60, 50, BLACK); + + if (!msgLogoADone) DrawText(msgBuffer, screenWidth/2 - MeasureText(msgLogoA, 30)/2, 460, 30, GRAY); + else + { + DrawText(msgLogoA, screenWidth/2 - MeasureText(msgLogoA, 30)/2, 460, 30, GRAY); + + if (!msgLogoBDone) DrawText(msgBuffer, screenWidth/2 - MeasureText(msgLogoB, 30)/2, 510, 30, GRAY); + else + { + DrawText(msgLogoB, screenWidth/2 - MeasureText(msgLogoA, 30)/2, 510, 30, GRAY); + + if ((framesCounter > 90) && ((framesCounter/30)%2)) DrawText("PRESS ENTER to CONTINUE", 930, 650, 20, GRAY); + } + } + } + } break; + case MODULES: + { + // Draw MODULES screen + DrawTexture(raylibLogoB, 40, 40, WHITE); + DrawText("raylib is composed of 6 main modules:", 128 + 40 + 30, 50, 20, GRAY); + + if (framesCounter < 120) + { + if (((framesCounter/30)%2) == 0) DrawRectangle(128 + 40 + 30 - 4 + 175*selectedModule, 128 + 40 - 70 - 8 - 4, 158, 78, RED); + } + else DrawRectangle(128 + 40 + 30 - 4 + 175*selectedModule, 128 + 40 - 70 - 8 - 4, 158, 78, RED); + + if (selectedModule != AUDIO) + { + DrawTriangle((Vector2){950 - 40, 685 - 10}, (Vector2){950 - 60, 685}, (Vector2){950 - 40, 685 + 10}, GRAY); + DrawTriangle((Vector2){950 - 30, 685 - 10}, (Vector2){950 - 30, 685 + 10}, (Vector2){950 - 10, 685}, GRAY); + DrawText("PRESS RIGHT or LEFT to EXPLORE MODULES", 960, 680, 10, GRAY); + } + + switch (selectedModule) + { + case CORE: + { + DrawText("This module give you functions to:", 48, 200, 10, GetColor(0x5c5a5aff)); + + DrawTextEx(fontRomulus, "Open-Close Window", (Vector2){ 48, 230 }, GetFontBaseSize(fontRomulus)*2, 4, GetColor(0x5c5a5aff)); + DrawTextEx(fontRomulus, "Manage Drawing Area", (Vector2){ 48, 260 }, GetFontBaseSize(fontRomulus)*2, 4, GetColor(0x5c5a5aff)); + DrawTextEx(fontRomulus, "Manage Inputs", (Vector2){ 48, 290 }, GetFontBaseSize(fontRomulus)*2, 4, GetColor(0x5c5a5aff)); + DrawTextEx(fontRomulus, "Manage Timming", (Vector2){ 48, 320 }, GetFontBaseSize(fontRomulus)*2, 4, GetColor(0x5c5a5aff)); + DrawTextEx(fontRomulus, "Auxiliar Functions", (Vector2){ 48, 350 }, GetFontBaseSize(fontRomulus)*2, 4, GetColor(0x5c5a5aff)); + + switch (coreWindow) + { + case 1: DrawTexture(raylibWindow, 520, 220, WHITE); break; + case 2: DrawTextureEx(raylibWindow01, (Vector2){ 450, 220 - 45 }, 0.0f, 4.0f, WHITE); break; + case 3: DrawTextureEx(raylibWindow02, (Vector2){ 430, 220 - 40 }, 0.0f, 4.0f, WHITE); break; + case 4: DrawTextureEx(raylibWindow03, (Vector2){ 470, 220 - 65 }, 0.0f, 4.0f, WHITE); break; + default: DrawTexture(raylibWindow, 520, 220, WHITE); break; + } + + if (framesCounter > 140) DrawText("Check the possible windows raylib can run on. PRESS KEY: 1, 2, 3 or 4", 520 + 8 + windowOffset + 160, 220 + windowOffset + 10, 10, LIGHTGRAY); + + DrawText("Compile raylib C code for the folowing platforms:", 48, 400, 10, MAROON); + + DrawTextureRec(platforms, (Rectangle){ 0, 0, platforms.width, platforms.height}, (Vector2){ 75, 420 }, WHITE); + + DrawRectangle(520 + 8 + windowOffset, 220 + 31 + windowOffset, 640, 360, RAYWHITE); + DrawRectangleLines(520 + 8 + windowOffset - 1, 220 + 31 + windowOffset - 2, 640 + 2, 360 + 2, GRAY); + DrawFPS(520 + 8 + windowOffset + 10, 220 + 31 + windowOffset + 10); + + DrawRectangle(ballPosition.x - 50, ballPosition.y - 50, 100, 100, Fade(MAROON, 0.5f)); + DrawRectangleRec(GetCollisionRec((Rectangle){ 520 + 8 + windowOffset - 1, 220 + 31 + windowOffset - 1, 640 + 2, 360 + 2 }, (Rectangle){ (int)ballPosition.x - 50, (int)ballPosition.y - 50, 100, 100 }), MAROON); + + if (framesCounter > 140) + { + DrawTextEx(fontMecha, "MOVE ME", (Vector2){ ballPosition.x - 26, ballPosition.y - 20 }, GetFontBaseSize(fontMecha), 2, BLACK); + DrawTextEx(fontMecha, "[ W A S D ]", (Vector2){ ballPosition.x - 36, ballPosition.y }, GetFontBaseSize(fontMecha), 2, BLACK); + } + } break; + case SHAPES: + { + DrawText("This module give you functions to:", 48, 200, 10, GetColor(0xcd5757ff)); + + DrawTextEx(fontRomulus, "Draw Basic Shapes", (Vector2){ 48, 230 }, GetFontBaseSize(fontRomulus)*2, 4, GetColor(0xcd5757ff)); + DrawTextEx(fontRomulus, "Basic Collision Detection", (Vector2){ 48, 260 }, GetFontBaseSize(fontRomulus)*2, 4, GetColor(0xcd5757ff)); + + DrawCircle(screenWidth/4, 120 + 240, 35, DARKBLUE); + DrawCircleGradient(screenWidth/4, 220 + 240, 60, GREEN, SKYBLUE); + DrawCircleLines(screenWidth/4, 340 + 240, 80, DARKBLUE); + + DrawRectangle(screenWidth/4*2 - 110, 100 + 180, 220, 100, LIME); + DrawRectangleGradient(screenWidth/4*2 - 90, 170 + 240, 180, 130, MAROON, GOLD); + DrawRectangleLines(screenWidth/4*2 - 80, 320 + 240, 160, 80, ORANGE); + + DrawTriangle((Vector2){screenWidth/4*3, 60 + 220}, (Vector2){screenWidth/4*3 - 60, 160 + 220}, (Vector2){screenWidth/4*3 + 60, 160 + 220}, VIOLET); + + DrawTriangleLines((Vector2){screenWidth/4*3, 140 + 220}, (Vector2){screenWidth/4*3 - 60, 210 + 260}, (Vector2){screenWidth/4*3 + 60, 210 + 260}, SKYBLUE); + + DrawPoly((Vector2){screenWidth/4*3, 320 + 240}, 6, 80, 0, BROWN); + + } break; + case TEXTURES: + { + DrawText("This module give you functions to:", 48, 200, 10, GetColor(0x60815aff)); + + DrawTextEx(fontRomulus, "Load Images and Textures", (Vector2){ 48, 230 }, GetFontBaseSize(fontRomulus)*2, 4, GetColor(0x60815aff)); + DrawTextEx(fontRomulus, "Draw Textures", (Vector2){ 48, 260 }, GetFontBaseSize(fontRomulus)*2, 4, GetColor(0x60815aff)); + + DrawRectangle(138, 348, 260, 260, GRAY); + DrawTexturePro(lena, (Rectangle){ 0, 0, lena.width, lena.height }, (Rectangle){ 140 + 128, 350 + 128, lena.width/2*scaleFactor, lena.height/2*scaleFactor }, (Vector2){ lena.width/4*scaleFactor, lena.height/4*scaleFactor }, 0.0f, WHITE); + + DrawTexture(lena, 600, 180, Fade(WHITE, 0.3f)); + DrawTextureRec(lena, (Rectangle){ 225, 240, 155, 50 }, (Vector2){ 600 + 256 - 82 + 50, 180 + 241 }, PINK); + + DrawTexturePro(mandrill, (Rectangle){ 0, 0, mandrill.width, mandrill.height }, (Rectangle){ screenWidth/2 - 40, 350 + 128, mandrill.width/2, mandrill.height/2 }, + (Vector2){ mandrill.width/4, mandrill.height/4 }, framesCounter, GOLD); + + } break; + case TEXT: + { + DrawText("This module give you functions to:", 48, 200, 10, GetColor(0x377764ff)); + + DrawTextEx(fontRomulus, "Load SpriteFonts", (Vector2){ 48, 230 }, GetFontBaseSize(fontRomulus)*2, 4, GetColor(0x377764ff)); + DrawTextEx(fontRomulus, "Draw Text", (Vector2){ 48, 260 }, GetFontBaseSize(fontRomulus)*2, 4, GetColor(0x377764ff)); + DrawTextEx(fontRomulus, "Text Formatting", (Vector2){ 48, 290 }, GetFontBaseSize(fontRomulus)*2, 4, GetColor(0x377764ff)); + + DrawTexture(texAlagard, 60, 360, WHITE); + + DrawTextEx(fontMechaC, msg1, (Vector2){ 540 + 168, 210 }, GetFontBaseSize(fontMechaC), -3, WHITE); + DrawTextEx(fontAlagardC, msg2, (Vector2){ 460 + 140, 260 }, GetFontBaseSize(fontAlagardC), -2, WHITE); + DrawTextEx(fontJupiterC, msg3, (Vector2){ 640 + 70, 300 }, GetFontBaseSize(fontJupiterC), 2, WHITE); + + DrawTextEx(fontAlagard, "It also includes some...", (Vector2){ 650 + 70, 400 }, GetFontBaseSize(fontAlagard)*2, 2, MAROON); + DrawTextEx(fontPixelplay, "...free fonts in rBMF format...", (Vector2){ 705 - 26, 450 }, GetFontBaseSize(fontPixelplay)*2, 4, ORANGE); + DrawTextEx(fontMecha, "...to be used even in...", (Vector2){ 700 + 40, 500 }, GetFontBaseSize(fontMecha)*2, 4, DARKGREEN); + DrawTextEx(fontSetback, "...comercial projects...", (Vector2){ 710, 550 }, GetFontBaseSize(fontSetback)*2, 4, DARKBLUE); + DrawTextEx(fontRomulus, "...completely for free!", (Vector2){ 710 + 17, 600 }, GetFontBaseSize(fontRomulus)*2, 3, DARKPURPLE); + + DrawText("This is a custom font spritesheet, raylib can load it automatically!", 228, 360 + 295, 10, GRAY); + + } break; + case MODELS: + { + DrawText("This module give you functions to:", 48, 200, 10, GetColor(0x417794ff)); + + DrawTextEx(fontRomulus, "Draw Geometric Models", (Vector2){ 48, 230 }, GetFontBaseSize(fontRomulus)*2, 4, GetColor(0x417794ff)); + DrawTextEx(fontRomulus, "Load 3D Models", (Vector2){ 48, 260 }, GetFontBaseSize(fontRomulus)*2, 4, GetColor(0x417794ff)); + DrawTextEx(fontRomulus, "Draw 3D Models", (Vector2){ 48, 290 }, GetFontBaseSize(fontRomulus)*2, 4, GetColor(0x417794ff)); + + Begin3dMode(camera); + + DrawCube((Vector3){-4, 0, 2}, 2, 5, 2, RED); + DrawCubeWires((Vector3){-4, 0, 2}, 2, 5, 2, GOLD); + DrawCubeWires((Vector3){-4, 0, -2}, 3, 6, 2, MAROON); + + DrawSphere((Vector3){-1, 0, -2}, 1, GREEN); + DrawSphereWires((Vector3){1, 0, 2}, 2, 16, 16, LIME); + + DrawCylinder((Vector3){4, 0, -2}, 1, 2, 3, 4, SKYBLUE); + DrawCylinderWires((Vector3){4, 0, -2}, 1, 2, 3, 4, DARKBLUE); + DrawCylinderWires((Vector3){4.5, -1, 2}, 1, 1, 2, 6, BROWN); + + DrawCylinder((Vector3){1, 0, -4}, 0, 1.5, 3, 8, GOLD); + DrawCylinderWires((Vector3){1, 0, -4}, 0, 1.5, 3, 8, PINK); + + DrawModelEx(cat, (Vector3){ 8.0f, 0.0f, 2.0f }, (Vector3){ 0.0f, 0.5f*framesCounter, 0.0f }, (Vector3){ 0.1f, 0.1f, 0.1f }, WHITE); + DrawGizmo((Vector3){ 8.0f, 0.0f, 2.0f }); + + DrawGrid(10.0, 1.0); // Draw a grid + + End3dMode(); + + DrawFPS(900, 220); + + } break; + case AUDIO: + { + DrawText("This module give you functions to:", 48, 200, 10, GetColor(0x8c7539ff)); + + DrawTextEx(fontRomulus, "Load and Play Sounds", (Vector2){ 48, 230 }, GetFontBaseSize(fontRomulus)*2, 4, GetColor(0x8c7539ff)); + DrawTextEx(fontRomulus, "Play Music (streaming)", (Vector2){ 48, 260 }, GetFontBaseSize(fontRomulus)*2, 4, GetColor(0x8c7539ff)); + + DrawText("PRESS SPACE to START PLAYING MUSIC", 135, 350, 20, GRAY); + DrawRectangle(150, 390, 400, 12, LIGHTGRAY); + DrawRectangle(150, 390, (int)timePlayed, 12, MAROON); + + if (MusicIsPlaying()) + { + DrawText("PRESS 'S' to STOP PLAYING MUSIC", 165, 425, 20, GRAY); + + for (int i = 0; i < MAX_BALLS; i++) + { + if (soundBallsActive[i]) DrawPoly(soundBallsPosition[i], 18, soundBallsRadius[i], 0.0f, Fade(soundBallsColor[i], soundBallsAlpha[i])); + } + } + + DrawText("PRESS 'N' to PLAY a SOUND", 200, 540, 20, VIOLET); + + if ((framesCounter/30)%2) DrawText("PRESS ENTER to CONTINUE", 930, 650, 20, GRAY); + + } break; + default: break; + } + + // Draw modules menu + DrawRectangle(128 + 40 + 30, 128 + 40 - 70 - 8, 150, 70, GetColor(0x898888ff)); + DrawRectangle(128 + 40 + 30 + 8, 128 + 40 - 70, 150 - 16, 70 - 16, GetColor(0xe1e1e1ff)); + DrawText("CORE", 128 + 40 + 30 + 8 + 38, 128 + 40 - 50, 20, GetColor(0x5c5a5aff)); + + DrawRectangle(128 + 40 + 30 + 175, 128 + 40 - 70 - 8, 150, 70, GetColor(0xe66666ff)); + DrawRectangle(128 + 40 + 30 + 8 + 175, 128 + 40 - 70, 150 - 16, 70 - 16, GetColor(0xf0d6d6ff)); + DrawText("SHAPES", 128 + 40 + 30 + 8 + 175 + 28, 128 + 40 - 50, 20, GetColor(0xcd5757ff)); + + DrawRectangle(128 + 40 + 30 + 175*2, 128 + 40 - 70 - 8, 150, 70, GetColor(0x75a06dff)); + DrawRectangle(128 + 40 + 30 + 8 + 175*2, 128 + 40 - 70, 150 - 16, 70 - 16, GetColor(0xc8eabfff)); + DrawText("TEXTURES", 128 + 40 + 30 + 175*2 + 8 + 9, 128 + 40 - 50, 20, GetColor(0x60815aff)); + + DrawRectangle(128 + 40 + 30 + 175*3, 128 + 40 - 70 - 8, 150, 70, GetColor(0x52b296ff)); + DrawRectangle(128 + 40 + 30 + 8 + 175*3, 128 + 40 - 70, 150 - 16, 70 - 16, GetColor(0xbef0ddff)); + DrawText("TEXT", 128 + 40 + 30 + 8 + 175*3 + 38, 128 + 40 - 50, 20, GetColor(0x377764ff)); + + DrawRectangle(128 + 40 + 30 + 175*4, 128 + 40 - 70 - 8, 150, 70, GetColor(0x5d9cbdff)); + DrawRectangle(128 + 40 + 30 + 8 + 175*4, 128 + 40 - 70, 150 - 16, 70 - 16, GetColor(0xbedce8ff)); + DrawText("MODELS", 128 + 40 + 30 + 8 + 175*4 + 28, 128 + 40 - 50, 20, GetColor(0x417794ff)); + + DrawRectangle(128 + 40 + 30 + 175*5, 128 + 40 - 70 - 8, 150, 70, GetColor(0xd3b157ff)); + DrawRectangle(128 + 40 + 30 + 8 + 175*5, 128 + 40 - 70, 150 - 16, 70 - 16, GetColor(0xebddaeff)); + DrawText("AUDIO", 128 + 40 + 30 + 8 + 175*5 + 36, 128 + 40 - 50, 20, GetColor(0x8c7539ff)); + + } break; + case ENDING: + { + // Draw ENDING screen + DrawTextEx(fontAlagard, "LEARN VIDEOGAMES PROGRAMMING", (Vector2){ screenWidth/2 - MeasureTextEx(fontAlagard, "LEARN VIDEOGAMES PROGRAMMING", GetFontBaseSize(fontAlagard)*4, 4).x/2, 80 }, GetFontBaseSize(fontAlagard)*4, 4, MAROON); + + DrawTexture(raylibLogoA, logoPositionX, logoPositionY - 40, WHITE); + + DrawText(msgWeb, screenWidth/2 - MeasureText(msgWeb, 40)/2, 470, 40, DARKGRAY); + + if (framesCounter > 60) DrawText(msgCredits, screenWidth/2 - MeasureText(msgCredits, 30)/2, 550, 30, GRAY); + + if (framesCounter > 120) if ((framesCounter/30)%2) DrawText("PRESS ENTER to CONTINUE", screenWidth/2 - MeasureText("PRESS ENTER to CONTINUE", 20)/2, 640, 20, LIGHTGRAY); + + } break; + case PONG: + { + // Pong + DrawCircleV(pongBallPosition, 10, LIGHTGRAY); + DrawRectangleRec(pongPlayerRec, GRAY); + DrawRectangleRec(pongEnemyRec, GRAY); + + DrawText(FormatText("%02i", pongScorePlayer), 150, 10, 80, LIGHTGRAY); + DrawText(FormatText("%02i", pongScoreEnemy), screenWidth - MeasureText("00", 80) - 150, 10, 80, LIGHTGRAY); + + if (pongPaused) if ((framesCounter/30)%2) DrawText("GAME PAUSED [P]", screenWidth/2 - 100, 40, 20, MAROON); + } break; + default: break; + } + + if (currentScreen != LOADING) DrawRectangle(0, screenHeight - 10, ((float)timeCounter/(float)totalTime)*screenWidth, 10, LIGHTGRAY); + + if (onTransition) DrawTransition(); + + EndDrawing(); + //---------------------------------------------------------------------------------- +}
\ No newline at end of file diff --git a/games/raylib_demo/resources/audio/coin.wav b/games/raylib_demo/resources/audio/coin.wav Binary files differnew file mode 100644 index 00000000..6007509b --- /dev/null +++ b/games/raylib_demo/resources/audio/coin.wav diff --git a/games/raylib_demo/resources/audio/guitar_noodling.ogg b/games/raylib_demo/resources/audio/guitar_noodling.ogg Binary files differnew file mode 100644 index 00000000..f5022040 --- /dev/null +++ b/games/raylib_demo/resources/audio/guitar_noodling.ogg diff --git a/games/raylib_demo/resources/audio/spring.wav b/games/raylib_demo/resources/audio/spring.wav Binary files differnew file mode 100644 index 00000000..c7fbf1b9 --- /dev/null +++ b/games/raylib_demo/resources/audio/spring.wav diff --git a/games/raylib_demo/resources/audio/tanatana.ogg b/games/raylib_demo/resources/audio/tanatana.ogg Binary files differnew file mode 100644 index 00000000..90b1795a --- /dev/null +++ b/games/raylib_demo/resources/audio/tanatana.ogg diff --git a/games/raylib_demo/resources/audio/weird.wav b/games/raylib_demo/resources/audio/weird.wav Binary files differnew file mode 100644 index 00000000..101029c5 --- /dev/null +++ b/games/raylib_demo/resources/audio/weird.wav diff --git a/games/raylib_demo/resources/cat.obj b/games/raylib_demo/resources/cat.obj new file mode 100644 index 00000000..1faa9846 --- /dev/null +++ b/games/raylib_demo/resources/cat.obj @@ -0,0 +1,4731 @@ +# +# object Cat +# + +v -0.00 30.40 -1.43 +v -0.00 30.01 1.57 +v 3.07 28.57 1.88 +v 3.23 28.74 -1.28 +v 2.93 30.52 -14.47 +v 0.00 31.65 -14.07 +v 0.00 32.03 -11.02 +v 3.49 30.39 -11.43 +v -0.00 22.66 -19.14 +v 1.98 22.94 -18.73 +v 2.33 19.32 -17.01 +v -0.00 18.98 -17.55 +v 6.09 25.02 -0.72 +v 6.05 25.40 2.15 +v 6.20 18.84 -0.16 +v 5.74 18.56 2.35 +v 6.32 19.78 -7.20 +v 4.52 16.56 -7.33 +v 4.70 16.75 -9.21 +v 6.65 19.94 -8.17 +v -0.00 30.43 5.21 +v 3.15 29.21 6.20 +v 3.46 15.19 3.29 +v 4.18 15.67 0.04 +v 2.28 13.77 0.12 +v 2.04 14.08 2.60 +v 2.38 14.74 -7.70 +v 2.27 15.37 -10.06 +v 5.58 26.84 7.17 +v 2.87 30.20 8.03 +v 4.93 27.37 9.73 +v 5.61 2.21 8.37 +v 6.14 3.22 9.74 +v 6.68 1.73 10.18 +v 5.68 -0.03 9.83 +v 4.08 -0.03 9.37 +v 4.02 1.99 7.98 +v 2.54 -0.03 10.04 +v 2.39 2.21 8.56 +v 1.77 1.73 10.48 +v 2.34 3.22 10.01 +v 2.52 3.27 11.56 +v 2.80 3.64 11.26 +v 3.32 3.62 11.86 +v 5.34 3.62 11.75 +v 5.75 3.64 11.07 +v 4.32 3.96 11.73 +v 4.34 3.73 12.02 +v 6.03 3.27 11.36 +v 7.00 1.16 12.70 +v 7.05 1.09 14.30 +v 6.62 -0.01 14.46 +v 6.67 -0.01 12.74 +v 5.94 -0.01 14.95 +v 5.34 -0.01 14.87 +v 4.29 -0.01 12.84 +v 4.47 -0.01 15.37 +v 3.58 -0.01 14.94 +v 1.67 1.17 12.96 +v 1.91 -0.01 12.93 +v 2.27 -0.01 14.65 +v 1.87 1.10 14.53 +v 2.04 2.51 14.08 +v 2.41 2.98 12.86 +v 3.07 3.33 12.99 +v 2.87 2.89 14.41 +v 4.95 3.61 13.06 +v 4.38 3.90 13.27 +v 4.41 3.41 14.79 +v 5.47 2.54 14.56 +v 6.74 2.51 13.88 +v 6.27 3.03 12.63 +v 5.58 1.24 15.03 +v 4.51 1.34 15.66 +v 3.48 1.25 15.07 +v 2.66 1.19 15.14 +v 3.54 2.56 14.63 +v 2.19 14.25 6.54 +v 1.39 14.12 6.98 +v 2.42 14.98 8.43 +v 1.36 14.78 9.20 +v 2.01 16.83 -13.60 +v -0.00 16.45 -13.91 +v 6.29 19.22 -3.91 +v 5.78 25.21 -4.36 +v 3.33 29.17 -4.90 +v 0.00 31.15 -4.95 +v -0.00 26.87 -17.74 +v 2.39 27.13 -17.55 +v -0.00 14.93 -10.66 +v -0.00 31.09 -16.51 +v 2.17 29.72 -16.78 +v 1.49 36.84 -24.79 +v 1.27 41.55 -26.64 +v 0.00 42.08 -25.44 +v -0.00 37.36 -23.33 +v 1.10 48.04 -27.60 +v 0.00 41.08 -28.03 +v 0.00 47.89 -28.89 +v -0.00 36.18 -26.27 +v -0.02 31.50 -23.55 +v 1.65 32.74 -22.14 +v 1.80 30.64 -19.84 +v -0.00 29.11 -21.24 +v -0.02 33.60 -20.56 +v -0.01 31.57 -18.45 +v 0.00 48.20 -26.51 +v 0.64 48.90 -27.72 +v -0.00 48.99 -27.04 +v 0.00 48.81 -28.52 +v 2.01 29.52 -17.72 +v -0.00 19.75 14.29 +v -0.00 17.45 12.46 +v 1.88 17.93 12.35 +v 1.57 20.12 14.04 +v 2.56 30.98 9.13 +v 4.46 28.79 10.92 +v -0.00 31.95 8.21 +v -0.00 31.29 7.02 +v 5.94 25.26 11.51 +v 4.93 26.82 12.64 +v -0.00 32.95 8.95 +v 2.56 32.43 9.79 +v 1.36 23.45 15.57 +v -0.00 23.15 15.81 +v 3.19 24.31 14.77 +v 3.17 20.72 13.42 +v 5.00 22.20 12.86 +v 4.13 24.96 14.05 +v 4.39 31.01 11.80 +v 4.76 29.65 13.36 +v 4.26 28.06 14.68 +v 3.28 27.06 15.56 +v -0.00 25.73 16.48 +v 1.41 26.15 16.41 +v 2.33 34.56 22.15 +v 2.25 34.47 22.31 +v 3.19 34.48 22.08 +v 3.11 34.55 21.97 +v 1.82 34.76 22.51 +v 1.98 34.86 22.25 +v 4.11 41.24 16.12 +v 4.12 40.53 17.30 +v 5.12 39.89 15.46 +v 5.50 40.64 14.98 +v 3.81 42.25 16.18 +v 5.77 38.26 15.33 +v 5.91 39.09 13.80 +v 7.55 38.39 15.53 +v 6.54 36.03 14.95 +v 5.79 37.39 13.19 +v 1.95 41.74 16.78 +v 1.50 41.82 17.78 +v 1.91 41.74 17.62 +v 4.44 44.21 17.57 +v 3.38 43.19 17.82 +v 4.27 42.62 18.08 +v 4.86 43.93 17.63 +v 6.27 42.25 15.15 +v 4.55 43.61 16.50 +v 8.27 41.13 15.27 +v 7.96 39.76 15.42 +v 6.37 40.27 13.65 +v 8.04 39.75 15.23 +v 7.03 42.62 14.09 +v 4.88 41.33 13.82 +v 5.86 43.29 14.36 +v 5.09 43.90 15.39 +v 3.78 42.25 15.07 +v 5.51 45.24 17.16 +v 5.70 45.19 17.08 +v 7.48 46.20 17.08 +v 7.08 44.48 15.93 +v 7.79 45.24 16.33 +v 8.24 43.92 15.60 +v 8.30 43.97 15.38 +v 7.56 44.53 15.35 +v 7.82 45.29 16.17 +v 6.80 44.94 15.49 +v 6.20 45.28 16.21 +v 7.45 46.19 16.92 +v 5.18 43.72 17.22 +v 8.33 42.51 15.28 +v 8.40 42.58 15.07 +v 5.09 41.88 17.65 +v 3.26 19.02 11.83 +v 4.39 16.04 -3.75 +v 2.33 13.93 -3.88 +v 0.00 13.05 0.04 +v 0.00 13.35 2.73 +v 0.00 14.05 -7.91 +v 0.00 13.86 7.43 +v 0.00 14.78 9.65 +v 2.74 18.21 -13.21 +v 0.00 13.25 -4.01 +v 5.80 25.70 -8.38 +v 3.34 30.05 -8.19 +v 0.00 31.71 -7.95 +v 2.16 35.87 22.31 +v 2.57 36.23 22.18 +v 2.55 36.31 22.34 +v 2.13 35.91 22.52 +v 1.80 35.20 22.55 +v 1.94 35.25 22.33 +v 4.17 35.18 21.38 +v 4.25 35.81 21.29 +v 4.16 35.76 21.28 +v 4.10 35.18 21.41 +v 3.86 34.75 21.73 +v 3.78 34.80 21.68 +v 3.07 36.36 21.96 +v 3.13 36.47 22.14 +v 3.78 3.59 13.14 +v 5.65 3.37 12.82 +v 6.11 2.84 14.28 +v 6.30 1.18 14.99 +v 2.96 -0.01 15.07 +v 6.25 36.31 15.33 +v 7.47 38.42 15.69 +v 3.13 41.25 17.80 +v 2.03 41.16 19.56 +v 2.31 41.56 17.79 +v 7.09 2.43 -19.06 +v 7.57 3.28 -17.72 +v 7.74 1.96 -17.50 +v 7.12 0.00 -18.53 +v 5.75 0.00 -19.00 +v 5.72 2.06 -19.83 +v 4.44 0.00 -18.31 +v 4.40 2.43 -18.89 +v 3.76 1.96 -17.22 +v 3.89 3.29 -17.47 +v 4.49 3.30 -16.19 +v 4.57 3.62 -16.49 +v 5.21 3.62 -15.90 +v 5.96 3.97 -15.94 +v 5.96 3.72 -15.75 +v 6.70 3.61 -16.01 +v 7.24 3.61 -16.67 +v 7.39 3.30 -16.39 +v 8.75 1.24 -15.02 +v 8.64 1.16 -12.80 +v 8.22 0.00 -12.58 +v 8.27 0.00 -14.95 +v 7.55 0.00 -11.91 +v 6.95 0.00 -12.02 +v 5.92 0.00 -14.81 +v 6.10 0.00 -11.32 +v 5.22 0.00 -11.92 +v 3.53 1.16 -12.48 +v 3.19 1.24 -14.66 +v 3.57 0.00 -14.69 +v 3.93 0.00 -12.32 +v 3.70 3.02 -13.10 +v 4.06 3.53 -14.81 +v 4.51 3.43 -12.65 +v 4.71 3.91 -14.62 +v 6.01 4.53 -14.23 +v 6.04 4.00 -12.13 +v 7.09 3.06 -12.44 +v 6.57 4.22 -14.52 +v 8.34 3.02 -13.38 +v 7.88 3.59 -15.12 +v 7.20 1.32 -11.80 +v 6.14 1.42 -10.92 +v 5.12 1.33 -11.73 +v 4.30 1.26 -11.65 +v 5.18 3.08 -12.34 +v 5.42 4.19 -14.41 +v 7.26 3.95 -14.86 +v 7.72 3.38 -12.82 +v 7.91 1.26 -11.85 +v 4.60 0.00 -11.75 +v 8.34 41.12 15.05 +v 1.05 31.27 23.35 +v 1.91 30.72 22.80 +v -0.00 30.42 21.70 +v 0.99 31.21 23.60 +v 0.44 31.50 23.77 +v -0.00 31.85 23.98 +v -0.00 31.64 23.83 +v 0.19 31.62 23.81 +v 0.77 31.44 23.53 +v 4.07 40.21 13.27 +v 2.70 41.60 14.81 +v 9.35 21.36 -11.82 +v 7.34 20.08 -8.75 +v 7.54 15.35 -9.52 +v 9.91 16.45 -13.25 +v 6.26 25.80 -8.91 +v 7.71 25.97 -11.69 +v 7.69 23.23 -16.57 +v 6.48 26.48 -15.68 +v 4.67 24.17 -19.26 +v 4.29 27.08 -17.03 +v 5.40 13.46 -20.28 +v 5.10 15.57 -19.07 +v 8.29 14.42 -17.91 +v 8.49 11.41 -19.26 +v 8.44 18.60 -17.05 +v 4.95 19.86 -19.38 +v 4.86 29.06 -14.93 +v 5.76 28.94 -11.95 +v 8.39 10.36 -17.19 +v 8.26 8.96 -17.63 +v 8.15 9.26 -19.80 +v 5.37 9.04 -22.20 +v 5.39 12.45 -21.46 +v 7.56 6.58 -19.65 +v 7.75 6.67 -17.92 +v 5.46 6.28 -20.78 +v 9.55 12.52 -15.65 +v 3.21 10.61 -16.85 +v 2.99 11.59 -19.22 +v 3.15 9.53 -19.54 +v 3.35 9.18 -17.26 +v 3.93 6.80 -17.69 +v 5.84 6.61 -16.48 +v 6.07 8.72 -15.66 +v 3.87 6.53 -19.23 +v 6.24 9.51 -15.18 +v 6.94 10.91 -13.09 +v 3.41 12.28 -13.94 +v 3.04 13.84 -15.32 +v 3.10 15.11 -17.16 +v 6.80 11.45 5.31 +v 5.08 11.25 4.62 +v 5.44 13.30 3.26 +v 7.01 13.17 4.27 +v 3.68 11.50 5.38 +v 3.96 13.36 4.06 +v 2.61 12.50 6.46 +v 3.54 12.74 10.35 +v 5.44 11.32 10.79 +v 5.71 12.69 10.92 +v 7.21 12.61 9.49 +v 6.74 10.48 9.44 +v 5.54 15.60 2.65 +v 7.56 15.57 3.60 +v 3.35 15.84 10.75 +v 5.70 16.02 11.41 +v 7.55 16.03 10.25 +v 7.92 12.48 7.83 +v 8.73 15.76 7.82 +v 7.08 21.62 3.10 +v 7.64 18.57 3.15 +v 6.65 24.62 10.50 +v 7.22 23.54 7.84 +v 7.48 10.15 8.22 +v 6.52 9.31 6.17 +v 6.51 9.30 6.16 +v 4.96 9.40 5.37 +v 2.67 3.40 8.13 +v 4.14 3.11 7.38 +v 2.39 4.06 8.96 +v 2.88 4.43 10.82 +v 8.62 19.59 7.86 +v 5.89 19.71 11.97 +v 7.55 19.76 10.61 +v 4.84 8.62 10.62 +v 4.95 9.57 10.50 +v 3.02 9.48 10.01 +v 2.97 8.56 10.21 +v 6.17 8.46 9.79 +v 6.32 9.42 9.66 +v 6.98 9.13 8.55 +v 6.79 8.23 8.68 +v 4.50 8.16 6.43 +v 6.08 8.40 7.10 +v 5.96 7.46 7.26 +v 4.41 7.23 6.64 +v 6.51 7.32 8.81 +v 3.52 10.12 5.88 +v 2.46 8.83 7.68 +v 2.69 6.46 7.45 +v 4.31 6.29 6.86 +v 2.48 7.01 8.20 +v 3.10 7.52 10.42 +v 4.48 4.59 11.31 +v 5.68 4.40 10.51 +v 6.11 4.22 9.23 +v 5.55 3.27 7.82 +v 2.19 28.58 17.74 +v -0.00 28.19 17.89 +v 0.00 27.08 17.06 +v 1.97 27.54 16.97 +v 4.13 28.54 16.42 +v 4.34 29.37 17.58 +v 5.44 30.14 15.70 +v 6.10 31.05 16.90 +v 5.84 30.72 18.56 +v 0.00 34.44 9.58 +v 3.01 33.84 10.45 +v 5.46 32.87 12.29 +v 5.78 31.44 14.37 +v 5.89 35.55 12.68 +v 3.31 36.61 10.82 +v -0.00 37.13 9.86 +v 0.92 34.95 23.68 +v -0.00 35.17 23.93 +v -0.00 34.52 24.34 +v 1.00 34.24 24.10 +v 1.13 33.50 24.60 +v -0.00 33.71 24.97 +v -0.00 32.81 24.82 +v 1.16 33.35 24.54 +v 0.17 32.79 24.76 +v 1.88 32.27 24.20 +v 1.06 32.47 24.68 +v 0.92 32.04 24.48 +v 1.63 31.75 24.03 +v 0.74 31.73 24.22 +v 1.35 31.33 23.89 +v 1.29 34.04 23.96 +v 3.21 30.53 21.75 +v 3.96 31.60 22.04 +v 2.75 31.57 23.20 +v 0.00 41.90 18.20 +v -0.00 41.15 20.18 +v 4.22 29.82 19.06 +v 6.42 33.99 19.85 +v 6.16 32.01 19.66 +v 6.88 32.41 18.06 +v 7.09 34.21 18.13 +v -0.00 36.61 23.40 +v -0.00 35.79 23.62 +v 0.93 35.61 23.37 +v 1.12 36.42 23.17 +v -0.00 31.48 23.97 +v 0.40 31.42 23.92 +v 4.14 32.81 22.39 +v 3.02 32.48 23.28 +v 1.78 36.12 22.84 +v 1.42 35.35 23.08 +v 2.07 40.06 21.08 +v -0.00 40.08 21.59 +v 3.19 36.68 22.18 +v 3.82 36.35 21.67 +v 4.00 36.52 21.64 +v 3.51 37.03 22.04 +v 3.07 40.70 12.84 +v -0.00 41.05 12.30 +v 0.00 41.95 14.79 +v 1.34 29.48 22.43 +v 1.78 29.30 21.35 +v 2.55 29.83 21.48 +v 1.89 29.97 22.46 +v 5.17 30.55 19.50 +v 4.32 30.50 20.82 +v 3.37 29.88 20.50 +v 2.28 29.22 19.09 +v 2.10 29.36 20.32 +v -0.00 33.98 24.85 +v 4.63 37.76 21.16 +v 3.35 38.38 21.86 +v 2.79 37.40 22.61 +v 3.80 39.63 20.44 +v 4.97 38.68 19.91 +v 1.86 38.83 22.55 +v 1.56 37.79 23.12 +v 5.08 36.64 20.39 +v 5.59 37.08 19.06 +v 4.94 39.75 16.71 +v 4.92 39.26 18.02 +v 5.85 37.70 17.08 +v 5.89 38.20 15.93 +v 0.84 29.87 23.02 +v -0.00 29.64 23.08 +v 0.00 29.17 22.48 +v -0.00 28.93 21.37 +v -0.00 29.08 20.37 +v -0.00 28.97 19.19 +v 5.06 31.74 21.03 +v 3.76 40.47 18.68 +v -0.00 38.87 22.78 +v 1.33 34.72 23.44 +v 5.28 36.00 20.21 +v 4.40 36.18 21.25 +v 4.48 35.84 21.14 +v -0.00 37.84 23.28 +v 2.39 33.29 23.39 +v 1.36 33.84 23.79 +v 1.63 32.77 24.35 +v 2.56 36.62 22.56 +v 4.19 36.02 21.35 +v 3.56 33.77 22.69 +v 3.37 39.54 11.68 +v -0.00 39.65 10.81 +v 6.54 34.68 14.01 +v 1.15 31.06 23.46 +v 1.39 30.16 23.06 +v 0.18 32.36 24.61 +v 0.15 32.09 24.44 +v -0.00 32.76 24.73 +v -0.00 32.34 24.55 +v -0.00 32.10 24.37 +v 0.81 31.29 23.67 +v 4.73 34.55 21.49 +v 5.24 33.33 21.28 +v 6.91 32.96 15.56 +v 7.07 34.54 16.53 +v 6.04 36.45 16.75 +v 5.76 36.16 18.45 +v -0.00 30.54 23.72 +v 0.85 30.42 23.55 +v 3.73 36.27 21.50 +v 4.11 35.97 21.31 +v 2.30 35.42 22.46 +v 2.29 35.79 22.39 +v 2.37 35.17 22.43 +v 2.58 34.97 22.35 +v 3.08 34.88 22.13 +v 3.42 35.10 21.98 +v 3.55 35.34 21.89 +v 3.46 35.98 21.87 +v 3.56 35.68 21.85 +v 3.04 36.19 22.04 +v 2.61 36.15 22.23 +v 2.97 35.56 22.36 +v 4.73 7.67 10.74 +v 5.94 7.50 9.91 +v 5.76 6.50 7.43 +v 2.55 7.46 7.26 +v 2.39 7.97 7.91 +v 2.76 8.38 7.02 +v 0.00 49.12 -27.78 +v -3.23 28.74 -1.28 +v -3.07 28.57 1.88 +v -2.93 30.52 -14.47 +v -3.49 30.39 -11.43 +v -2.33 19.32 -17.01 +v -1.98 22.94 -18.73 +v -6.09 25.02 -0.72 +v -6.05 25.40 2.15 +v -6.20 18.84 -0.16 +v -5.74 18.55 2.35 +v -6.32 19.78 -7.20 +v -6.65 19.94 -8.17 +v -4.70 16.75 -9.21 +v -4.52 16.56 -7.33 +v -3.15 29.21 6.20 +v -3.46 15.18 3.30 +v -4.18 15.67 0.04 +v -2.28 13.77 0.12 +v -2.04 14.08 2.60 +v -2.38 14.74 -7.70 +v -2.27 15.37 -10.06 +v -5.58 26.84 7.17 +v -4.93 27.37 9.73 +v -2.87 30.20 8.03 +v -5.61 2.21 8.37 +v -5.68 -0.03 9.83 +v -6.68 1.73 10.18 +v -6.14 3.22 9.74 +v -4.02 1.99 7.98 +v -4.08 -0.03 9.37 +v -2.39 2.21 8.56 +v -2.54 -0.03 10.04 +v -1.77 1.73 10.48 +v -2.34 3.22 10.01 +v -2.52 3.27 11.56 +v -2.80 3.64 11.26 +v -3.32 3.62 11.86 +v -4.32 3.96 11.73 +v -5.75 3.64 11.07 +v -5.34 3.62 11.75 +v -4.34 3.73 12.02 +v -6.03 3.27 11.36 +v -7.00 1.16 12.70 +v -6.67 -0.01 12.74 +v -6.62 -0.01 14.46 +v -7.05 1.09 14.30 +v -5.94 -0.01 14.95 +v -4.29 -0.01 12.84 +v -5.34 -0.01 14.87 +v -4.47 -0.01 15.37 +v -3.58 -0.01 14.94 +v -1.67 1.17 12.96 +v -1.87 1.10 14.53 +v -2.27 -0.01 14.65 +v -1.91 -0.01 12.93 +v -2.04 2.51 14.08 +v -2.41 2.98 12.86 +v -3.07 3.33 12.99 +v -2.87 2.89 14.41 +v -4.95 3.61 13.06 +v -5.47 2.54 14.56 +v -4.41 3.41 14.79 +v -4.38 3.90 13.27 +v -6.74 2.51 13.88 +v -6.27 3.03 12.63 +v -5.58 1.24 15.03 +v -4.51 1.34 15.66 +v -3.48 1.25 15.07 +v -2.66 1.19 15.14 +v -3.54 2.56 14.63 +v -2.19 14.25 6.54 +v -1.39 14.12 6.98 +v -2.42 14.98 8.43 +v -1.36 14.78 9.20 +v -2.01 16.83 -13.60 +v -6.29 19.22 -3.91 +v -5.78 25.21 -4.36 +v -3.33 29.17 -4.90 +v -2.39 27.13 -17.55 +v -2.17 29.71 -16.77 +v -1.49 36.84 -24.79 +v -1.27 41.55 -26.64 +v -1.10 48.04 -27.60 +v -1.80 30.62 -19.83 +v -1.73 32.71 -22.16 +v -0.64 48.90 -27.72 +v -2.01 29.49 -17.69 +v -1.57 20.12 14.04 +v -1.88 17.93 12.35 +v -2.56 30.98 9.13 +v -4.46 28.79 10.92 +v -5.94 25.30 11.45 +v -4.93 26.82 12.64 +v -2.56 32.43 9.79 +v -1.36 23.45 15.57 +v -3.19 24.31 14.77 +v -4.13 24.96 14.05 +v -5.00 22.20 12.86 +v -3.17 20.72 13.42 +v -4.39 31.01 11.80 +v -4.26 28.06 14.68 +v -4.76 29.65 13.36 +v -3.28 27.06 15.56 +v -1.41 26.15 16.41 +v -2.36 34.54 22.12 +v -3.17 34.53 21.93 +v -3.24 34.45 22.04 +v -2.28 34.44 22.27 +v -1.85 34.74 22.48 +v -2.01 34.84 22.21 +v -4.11 41.25 16.12 +v -5.14 39.91 15.45 +v -4.10 40.52 17.32 +v -5.51 40.65 14.97 +v -3.82 42.26 16.17 +v -5.78 38.23 15.31 +v -5.93 39.07 13.75 +v -5.77 37.37 13.20 +v -6.53 36.04 14.96 +v -7.55 38.38 15.52 +v -1.95 41.75 16.78 +v -1.91 41.74 17.62 +v -1.50 41.83 17.78 +v -4.44 44.22 17.56 +v -4.87 43.94 17.63 +v -4.27 42.64 18.07 +v -3.39 43.20 17.82 +v -4.55 43.63 16.49 +v -6.28 42.27 15.14 +v -8.27 41.15 15.26 +v -7.97 39.78 15.41 +v -6.38 40.27 13.63 +v -8.05 39.76 15.23 +v -7.04 42.64 14.09 +v -5.86 43.30 14.35 +v -4.81 41.35 13.62 +v -3.78 42.26 15.07 +v -5.09 43.91 15.39 +v -5.51 45.25 17.16 +v -7.48 46.22 17.08 +v -5.70 45.21 17.08 +v -7.08 44.49 15.93 +v -7.79 45.26 16.33 +v -8.24 43.93 15.60 +v -8.30 43.98 15.38 +v -7.82 45.31 16.17 +v -7.57 44.55 15.34 +v -6.80 44.96 15.49 +v -7.45 46.20 16.92 +v -6.20 45.30 16.21 +v -5.19 43.74 17.22 +v -8.33 42.53 15.28 +v -8.40 42.59 15.07 +v -5.08 41.92 17.73 +v -3.26 19.02 11.83 +v -4.39 16.04 -3.75 +v -2.33 13.93 -3.88 +v -2.74 18.21 -13.21 +v -5.80 25.70 -8.38 +v -3.34 30.05 -8.19 +v -2.22 35.87 22.27 +v -2.18 35.91 22.49 +v -2.62 36.32 22.31 +v -2.64 36.24 22.14 +v -1.99 35.24 22.30 +v -1.84 35.19 22.52 +v -4.24 35.17 21.32 +v -4.17 35.17 21.36 +v -4.22 35.77 21.22 +v -4.32 35.81 21.24 +v -3.84 34.78 21.64 +v -3.92 34.73 21.68 +v -3.20 36.48 22.11 +v -3.14 36.37 21.92 +v -3.78 3.59 13.14 +v -6.11 2.84 14.28 +v -5.65 3.37 12.82 +v -6.30 1.18 14.99 +v -2.96 -0.01 15.07 +v -7.48 38.41 15.68 +v -6.27 36.31 15.32 +v -3.13 41.25 17.80 +v -2.31 41.56 17.79 +v -2.03 41.16 19.56 +v -7.09 2.43 -19.06 +v -7.12 0.00 -18.53 +v -7.74 1.96 -17.50 +v -7.57 3.28 -17.72 +v -5.72 2.06 -19.83 +v -5.75 0.00 -19.00 +v -4.40 2.43 -18.89 +v -4.44 0.00 -18.31 +v -3.76 1.96 -17.22 +v -3.89 3.29 -17.47 +v -4.57 3.62 -16.49 +v -4.49 3.30 -16.19 +v -5.96 3.97 -15.94 +v -5.21 3.62 -15.90 +v -5.96 3.72 -15.75 +v -6.70 3.61 -16.01 +v -7.39 3.30 -16.39 +v -7.24 3.61 -16.67 +v -8.75 1.24 -15.02 +v -8.27 0.00 -14.95 +v -8.22 0.00 -12.58 +v -8.64 1.16 -12.80 +v -7.55 0.00 -11.91 +v -5.92 0.00 -14.81 +v -6.95 0.00 -12.02 +v -6.10 0.00 -11.32 +v -5.22 0.00 -11.92 +v -3.53 1.16 -12.48 +v -3.93 0.00 -12.32 +v -3.57 0.00 -14.69 +v -3.19 1.24 -14.66 +v -3.70 3.02 -13.10 +v -4.06 3.53 -14.81 +v -4.71 3.91 -14.62 +v -4.51 3.43 -12.65 +v -6.01 4.53 -14.23 +v -6.57 4.22 -14.52 +v -7.09 3.06 -12.44 +v -6.04 4.00 -12.13 +v -8.34 3.02 -13.38 +v -7.88 3.59 -15.12 +v -7.20 1.32 -11.80 +v -6.14 1.42 -10.92 +v -5.12 1.33 -11.73 +v -4.30 1.26 -11.65 +v -5.18 3.08 -12.34 +v -5.42 4.19 -14.41 +v -7.26 3.95 -14.86 +v -7.72 3.38 -12.82 +v -7.91 1.26 -11.85 +v -4.60 0.00 -11.75 +v -8.35 41.13 15.05 +v -1.05 31.27 23.35 +v -1.91 30.72 22.80 +v -0.44 31.50 23.77 +v -0.99 31.21 23.60 +v -0.18 31.62 23.81 +v -0.76 31.44 23.53 +v -3.97 40.15 13.10 +v -2.70 41.61 14.81 +v -9.35 21.36 -11.82 +v -9.91 16.45 -13.25 +v -7.54 15.35 -9.52 +v -7.34 20.08 -8.75 +v -6.26 25.80 -8.91 +v -7.71 25.97 -11.69 +v -6.48 26.48 -15.68 +v -7.69 23.23 -16.57 +v -4.67 24.17 -19.26 +v -4.29 27.08 -17.03 +v -5.40 13.46 -20.27 +v -8.49 11.41 -19.26 +v -8.29 14.42 -17.91 +v -5.10 15.57 -19.07 +v -4.95 19.86 -19.38 +v -8.44 18.60 -17.05 +v -5.76 28.94 -11.95 +v -4.86 29.06 -14.93 +v -8.39 10.36 -17.19 +v -8.15 9.26 -19.79 +v -8.25 8.95 -17.63 +v -5.39 12.45 -21.46 +v -5.37 9.04 -22.20 +v -7.55 6.57 -19.63 +v -7.74 6.66 -17.92 +v -5.46 6.28 -20.75 +v -9.55 12.52 -15.65 +v -3.21 10.61 -16.85 +v -3.35 9.18 -17.26 +v -3.15 9.53 -19.54 +v -2.99 11.59 -19.22 +v -3.95 6.80 -17.69 +v -6.07 8.71 -15.66 +v -5.84 6.61 -16.49 +v -3.89 6.53 -19.21 +v -6.24 9.51 -15.18 +v -3.41 12.28 -13.94 +v -6.94 10.91 -13.09 +v -3.04 13.84 -15.32 +v -3.10 15.11 -17.16 +v -6.80 11.45 5.31 +v -7.01 13.17 4.27 +v -5.44 13.30 3.26 +v -5.08 11.25 4.62 +v -3.96 13.36 4.06 +v -3.68 11.50 5.38 +v -2.61 12.50 6.46 +v -3.54 12.74 10.35 +v -5.71 12.69 10.92 +v -5.44 11.32 10.79 +v -7.21 12.61 9.49 +v -6.74 10.48 9.44 +v -7.56 15.56 3.62 +v -5.54 15.58 2.66 +v -3.35 15.84 10.75 +v -5.70 16.04 11.42 +v -7.55 16.04 10.26 +v -7.92 12.48 7.83 +v -8.73 15.76 7.84 +v -7.64 18.53 3.16 +v -7.08 21.58 3.09 +v -6.65 24.64 10.45 +v -7.22 23.54 7.80 +v -7.48 10.15 8.22 +v -6.51 9.30 6.16 +v -4.96 9.40 5.37 +v -6.52 9.31 6.17 +v -4.14 3.11 7.38 +v -2.67 3.40 8.13 +v -2.39 4.06 8.96 +v -2.88 4.43 10.82 +v -8.62 19.59 7.86 +v -5.89 19.76 11.97 +v -7.55 19.80 10.61 +v -4.84 8.62 10.62 +v -2.97 8.56 10.21 +v -3.02 9.48 10.01 +v -4.95 9.57 10.50 +v -6.17 8.46 9.79 +v -6.32 9.42 9.66 +v -6.98 9.13 8.55 +v -6.79 8.23 8.68 +v -4.50 8.16 6.43 +v -4.41 7.23 6.64 +v -5.96 7.46 7.26 +v -6.08 8.40 7.10 +v -6.51 7.32 8.81 +v -3.52 10.12 5.88 +v -2.46 8.83 7.68 +v -4.31 6.29 6.86 +v -2.69 6.46 7.45 +v -2.48 7.01 8.20 +v -3.10 7.52 10.42 +v -4.48 4.59 11.31 +v -5.68 4.40 10.51 +v -6.11 4.22 9.23 +v -5.55 3.27 7.82 +v -2.19 28.58 17.74 +v -1.97 27.54 16.97 +v -4.34 29.37 17.58 +v -4.13 28.54 16.42 +v -5.84 30.72 18.56 +v -6.10 31.05 16.90 +v -5.44 30.14 15.70 +v -3.01 33.84 10.45 +v -5.78 31.44 14.37 +v -5.46 32.87 12.29 +v -5.71 35.59 12.81 +v -3.31 36.61 10.82 +v -0.93 34.94 23.68 +v -1.00 34.24 24.10 +v -1.16 33.35 24.54 +v -0.17 32.79 24.76 +v -1.13 33.50 24.60 +v -1.88 32.27 24.20 +v -1.63 31.75 24.03 +v -0.92 32.04 24.48 +v -1.06 32.47 24.68 +v -1.35 31.33 23.89 +v -0.74 31.73 24.22 +v -1.30 34.03 23.96 +v -3.21 30.53 21.75 +v -2.75 31.57 23.20 +v -3.96 31.60 22.04 +v -4.22 29.82 19.06 +v -6.42 33.99 19.85 +v -7.09 34.21 18.13 +v -6.88 32.41 18.06 +v -6.16 32.01 19.66 +v -1.13 36.42 23.17 +v -0.94 35.60 23.36 +v -0.40 31.42 23.92 +v -3.03 32.48 23.28 +v -4.15 32.81 22.39 +v -1.82 36.12 22.82 +v -1.44 35.34 23.07 +v -2.07 40.06 21.08 +v -4.07 36.54 21.59 +v -3.89 36.36 21.63 +v -3.25 36.70 22.15 +v -3.56 37.05 22.01 +v -2.94 40.68 12.68 +v -1.34 29.48 22.43 +v -1.89 29.97 22.46 +v -2.55 29.83 21.48 +v -1.78 29.30 21.35 +v -5.17 30.55 19.50 +v -3.37 29.88 20.50 +v -4.32 30.50 20.82 +v -2.28 29.22 19.09 +v -2.10 29.36 20.32 +v -2.83 37.42 22.60 +v -3.36 38.39 21.85 +v -4.65 37.77 21.15 +v -3.79 39.63 20.44 +v -4.96 38.68 19.91 +v -1.86 38.83 22.55 +v -1.57 37.79 23.12 +v -5.57 37.08 19.06 +v -5.09 36.65 20.37 +v -4.94 39.76 16.71 +v -5.87 38.17 15.92 +v -5.83 37.68 17.08 +v -4.92 39.25 18.02 +v -0.84 29.87 23.02 +v -5.06 31.74 21.03 +v -3.72 40.46 18.75 +v -1.34 34.71 23.44 +v -5.29 36.00 20.18 +v -4.55 35.85 21.09 +v -4.47 36.19 21.20 +v -2.41 33.28 23.39 +v -1.63 32.77 24.35 +v -1.37 33.83 23.79 +v -2.62 36.63 22.54 +v -4.26 36.03 21.29 +v -3.60 33.75 22.67 +v -3.34 39.51 11.62 +v -6.49 34.69 14.03 +v -1.15 31.06 23.46 +v -1.39 30.16 23.06 +v -0.18 32.36 24.61 +v -0.15 32.09 24.44 +v -0.81 31.29 23.67 +v -4.78 34.53 21.45 +v -5.25 33.32 21.27 +v -6.91 32.96 15.56 +v -7.07 34.54 16.53 +v -6.06 36.46 16.74 +v -5.78 36.16 18.45 +v -0.85 30.42 23.55 +v -3.80 36.28 21.45 +v -4.18 35.98 21.26 +v -2.36 35.40 22.43 +v -2.35 35.78 22.36 +v -2.43 35.15 22.39 +v -2.64 34.95 22.32 +v -3.14 34.86 22.10 +v -3.48 35.09 21.94 +v -3.62 35.34 21.85 +v -3.62 35.68 21.80 +v -3.53 35.99 21.82 +v -3.11 36.20 22.00 +v -2.68 36.15 22.20 +v -3.03 35.56 22.32 +v -4.73 7.67 10.74 +v -5.94 7.50 9.91 +v -5.76 6.50 7.43 +v -2.55 7.46 7.26 +v -2.39 7.97 7.91 +v -2.76 8.38 7.02 +v 6.85 37.87 15.86 +v 7.57 39.02 15.60 +v 6.61 39.54 15.81 +v 6.25 40.55 15.97 +v 5.65 41.65 16.34 +v 5.11 42.24 16.66 +v 8.70 40.27 15.35 +v 7.52 41.07 15.29 +v 8.25 42.23 15.29 +v 6.63 42.67 15.79 +v 6.24 43.68 16.20 +v 4.53 42.44 16.97 +v 3.63 40.71 17.31 +v 4.99 43.52 16.35 +v 8.25 38.26 15.78 +v 6.14 36.94 15.33 +v -6.26 40.55 15.97 +v -6.62 39.52 15.80 +v -7.58 39.01 15.59 +v -6.85 37.85 15.85 +v -5.11 42.25 16.66 +v -5.66 41.66 16.34 +v -8.70 40.28 15.34 +v -7.53 41.09 15.28 +v -6.63 42.68 15.79 +v -8.25 42.24 15.29 +v -6.24 43.69 16.20 +v -3.61 40.71 17.32 +v -4.53 42.45 16.97 +v -5.00 43.53 16.35 +v -8.25 38.27 15.78 +v -6.16 36.93 15.32 +# 1013 vertices + +vn -0.00 0.99 0.14 +vn -0.00 1.00 0.00 +vn 0.61 0.79 -0.04 +vn 0.65 0.76 0.07 +vn 0.49 0.82 -0.29 +vn 0.00 0.99 -0.17 +vn 0.00 1.00 0.02 +vn 0.56 0.82 0.10 +vn -0.00 -0.08 -1.00 +vn -0.08 -0.02 -1.00 +vn -0.34 -0.51 -0.79 +vn -0.00 -0.63 -0.78 +vn 0.94 0.33 -0.02 +vn 0.90 0.42 -0.11 +vn 0.96 -0.27 0.06 +vn 0.80 -0.25 -0.55 +vn 0.97 -0.22 0.10 +vn 0.77 -0.64 -0.03 +vn 0.31 -0.83 0.46 +vn 0.77 -0.12 0.62 +vn -0.00 0.97 -0.26 +vn 0.60 0.77 -0.22 +vn 0.28 -0.83 -0.48 +vn 0.78 -0.62 0.03 +vn 0.50 -0.86 0.04 +vn 0.45 -0.89 0.10 +vn 0.47 -0.86 -0.18 +vn 0.41 -0.85 -0.32 +vn 0.82 0.57 -0.03 +vn 0.65 0.71 -0.27 +vn 0.86 0.51 -0.01 +vn 0.65 -0.32 -0.69 +vn 0.98 0.17 -0.11 +vn 0.95 -0.02 -0.30 +vn 0.44 -0.79 -0.42 +vn -0.04 -0.84 -0.53 +vn -0.08 -0.49 -0.87 +vn -0.52 -0.78 -0.35 +vn -0.76 -0.19 -0.62 +vn -0.99 0.03 -0.16 +vn -0.95 0.30 0.02 +vn -0.75 0.64 0.15 +vn -0.67 0.63 0.40 +vn -0.33 0.91 0.24 +vn 0.36 0.91 0.18 +vn 0.70 0.63 0.35 +vn 0.04 0.73 0.69 +vn 0.02 0.96 0.30 +vn 0.77 0.63 0.07 +vn 1.00 -0.00 -0.09 +vn 0.93 -0.01 0.37 +vn 0.60 -0.72 0.35 +vn 0.63 -0.78 -0.06 +vn 0.14 -0.67 0.73 +vn 0.14 -0.82 0.55 +vn 0.00 -1.00 0.00 +vn 0.01 -0.69 0.72 +vn -0.08 -0.82 0.57 +vn -1.00 0.03 0.03 +vn -0.66 -0.75 0.02 +vn -0.56 -0.71 0.42 +vn -0.87 -0.01 0.49 +vn -0.78 0.53 0.34 +vn -0.73 0.68 0.07 +vn -0.43 0.89 0.17 +vn -0.25 0.73 0.63 +vn 0.39 0.91 0.14 +vn 0.00 0.99 0.12 +vn -0.02 0.76 0.65 +vn 0.35 0.54 0.77 +vn 0.82 0.52 0.25 +vn 0.74 0.68 0.01 +vn 0.30 0.11 0.95 +vn 0.02 0.09 1.00 +vn -0.21 0.11 0.97 +vn -0.31 0.12 0.94 +vn -0.29 0.53 0.80 +vn -0.41 -0.91 0.06 +vn 0.27 -0.94 0.19 +vn 0.15 -0.89 0.43 +vn 0.33 -0.81 0.49 +vn 0.54 -0.71 -0.46 +vn 0.00 -0.87 -0.50 +vn 0.97 -0.24 -0.02 +vn 0.94 0.33 0.01 +vn 0.70 0.70 0.14 +vn -0.00 0.98 0.21 +vn -0.00 -0.07 -1.00 +vn 0.22 0.35 -0.91 +vn 0.00 -0.93 -0.36 +vn -0.00 0.99 -0.12 +vn 0.77 0.53 -0.35 +vn 1.00 0.02 -0.04 +vn 1.00 0.03 -0.03 +vn 0.00 0.28 0.96 +vn 0.00 0.51 0.86 +vn 0.97 0.23 0.00 +vn 0.00 -0.23 -0.97 +vn -0.00 0.11 -0.99 +vn 0.00 -0.42 -0.91 +vn 0.00 -0.61 -0.79 +vn 1.00 0.00 -0.05 +vn 1.00 0.03 -0.05 +vn 0.00 -0.77 -0.64 +vn -0.00 0.68 0.74 +vn -0.01 0.90 0.43 +vn 0.00 0.36 0.93 +vn 0.73 0.68 -0.05 +vn -0.00 0.77 0.64 +vn -0.00 0.61 -0.80 +vn 0.92 0.22 -0.32 +vn -0.00 -0.51 0.86 +vn -0.00 -0.68 0.73 +vn 0.44 -0.60 0.67 +vn 0.37 -0.47 0.80 +vn 0.67 0.52 -0.52 +vn 0.92 0.31 -0.22 +vn -0.00 0.75 -0.66 +vn -0.00 0.89 -0.46 +vn 0.86 0.28 0.42 +vn 0.94 0.13 0.31 +vn -0.00 0.46 -0.89 +vn 0.54 0.26 -0.80 +vn 0.35 -0.31 0.88 +vn 0.00 -0.34 0.94 +vn 0.57 -0.23 0.79 +vn 0.43 -0.43 0.80 +vn 0.65 -0.10 0.75 +vn 0.76 -0.09 0.65 +vn 0.85 -0.06 -0.53 +vn 0.97 -0.18 -0.15 +vn 0.90 -0.33 0.29 +vn 0.65 -0.42 0.63 +vn 0.33 -0.37 0.87 +vn 0.34 0.72 0.61 +vn 0.48 0.61 0.63 +vn 0.03 0.84 0.55 +vn 0.04 0.85 0.53 +vn 0.79 0.23 0.57 +vn 0.76 0.30 0.58 +vn 0.84 0.28 0.46 +vn 0.97 0.12 -0.19 +vn 0.68 0.22 0.70 +vn 0.29 0.04 0.96 +vn 0.93 -0.27 0.23 +vn -0.13 0.06 0.99 +vn 0.55 -0.05 -0.83 +vn 0.92 -0.39 -0.07 +vn 0.99 0.12 -0.04 +vn 0.81 0.20 -0.56 +vn -0.21 0.97 -0.11 +vn 0.17 0.97 0.15 +vn -0.07 0.95 0.31 +vn -0.50 0.70 0.50 +vn -0.56 0.64 0.53 +vn 0.18 0.03 0.98 +vn 0.43 0.09 0.90 +vn 0.15 -0.24 0.96 +vn 0.84 -0.52 0.16 +vn -0.10 0.04 0.99 +vn -0.11 0.19 0.98 +vn 0.38 -0.08 -0.92 +vn 0.93 -0.31 -0.20 +vn 0.30 0.25 -0.92 +vn -0.16 0.43 -0.89 +vn -0.21 0.50 -0.84 +vn -0.56 0.64 -0.52 +vn -0.49 0.67 -0.55 +vn -0.32 0.78 0.54 +vn 0.36 -0.26 0.89 +vn 0.12 0.99 -0.05 +vn 0.28 -0.58 0.77 +vn 0.47 -0.61 0.64 +vn 0.20 -0.48 0.85 +vn 0.12 -0.55 0.83 +vn 0.03 -0.35 0.94 +vn 0.88 0.32 -0.35 +vn 0.30 0.56 -0.77 +vn 0.78 0.54 -0.31 +vn -0.07 0.68 -0.73 +vn -0.42 0.77 -0.47 +vn 0.19 0.93 -0.32 +vn 0.71 -0.10 0.70 +vn -0.05 -0.13 0.99 +vn 0.95 0.06 -0.32 +vn 0.70 -0.26 -0.66 +vn 0.75 -0.25 -0.61 +vn 0.24 -0.51 0.82 +vn 0.78 -0.62 -0.05 +vn 0.51 -0.85 -0.10 +vn 0.00 -1.00 0.04 +vn 0.00 -1.00 0.09 +vn 0.00 -0.97 -0.25 +vn 0.00 -0.97 0.23 +vn -0.00 -0.82 0.57 +vn -0.11 -0.94 -0.33 +vn 0.00 -0.99 -0.12 +vn 0.85 0.39 0.35 +vn 0.68 0.72 0.14 +vn 0.83 -0.50 0.25 +vn 0.52 -0.75 0.41 +vn 0.56 -0.73 0.40 +vn 0.83 -0.49 0.26 +vn 0.84 -0.16 0.51 +vn 0.85 -0.22 0.48 +vn 0.43 0.22 0.88 +vn -0.12 -0.11 0.99 +vn -0.16 -0.00 0.99 +vn 0.44 0.29 0.85 +vn 0.04 0.65 0.76 +vn 0.02 0.67 0.75 +vn 0.26 -0.87 0.42 +vn 0.28 -0.86 0.43 +vn -0.38 0.91 0.18 +vn 0.43 0.89 0.13 +vn 0.35 0.71 0.61 +vn 0.39 0.12 0.91 +vn -0.10 -0.68 0.73 +vn -0.20 -0.05 0.98 +vn -0.17 0.11 0.98 +vn 0.34 -0.17 0.93 +vn 0.37 0.39 0.84 +vn 0.72 0.46 0.52 +vn 0.29 0.86 0.42 +vn 0.08 0.66 0.74 +vn 0.76 -0.13 -0.64 +vn 0.95 0.07 -0.32 +vn 0.54 -0.69 -0.48 +vn -0.05 -0.76 -0.64 +vn -0.05 -0.25 -0.97 +vn -0.57 -0.71 -0.41 +vn -0.80 -0.15 -0.58 +vn -0.98 0.05 -0.21 +vn -0.99 0.05 0.11 +vn -0.70 0.71 0.07 +vn -0.68 0.41 0.62 +vn -0.33 0.94 0.08 +vn 0.07 0.47 0.88 +vn 0.00 0.99 0.16 +vn 0.34 0.94 0.02 +vn 0.79 0.41 0.45 +vn 0.74 0.67 -0.09 +vn 0.99 -0.03 -0.16 +vn 0.94 -0.02 0.33 +vn 0.61 -0.73 0.30 +vn 0.60 -0.80 -0.09 +vn 0.17 -0.66 0.73 +vn 0.16 -0.85 0.49 +vn -0.09 -0.85 0.51 +vn -0.90 -0.02 0.43 +vn -1.00 -0.01 -0.05 +vn -0.64 -0.77 -0.03 +vn -0.59 -0.73 0.36 +vn -0.80 0.51 0.31 +vn -0.70 0.70 -0.12 +vn -0.28 0.76 0.59 +vn -0.41 0.91 -0.09 +vn -0.01 1.00 0.01 +vn -0.03 0.81 0.59 +vn 0.46 0.58 0.68 +vn 0.39 0.92 -0.06 +vn 0.84 0.50 0.22 +vn 0.69 0.70 -0.20 +vn 0.39 0.09 0.92 +vn 0.02 0.07 1.00 +vn -0.27 0.10 0.96 +vn -0.37 0.11 0.92 +vn -0.37 0.57 0.74 +vn -0.37 0.93 -0.04 +vn 0.37 0.92 -0.09 +vn 0.32 0.78 0.54 +vn 0.47 0.11 0.88 +vn -0.13 -0.67 0.73 +vn 0.95 -0.12 -0.30 +vn 0.96 0.14 0.25 +vn 0.81 -0.37 0.46 +vn 0.92 0.15 0.37 +vn 0.89 -0.20 0.42 +vn 0.96 0.09 0.27 +vn 0.94 -0.03 0.34 +vn 0.94 -0.11 0.33 +vn 0.17 0.83 -0.53 +vn 0.40 -0.78 -0.48 +vn 0.00 -0.41 -0.91 +vn -0.21 -0.86 0.46 +vn -0.35 -0.80 0.49 +vn 0.00 -0.85 0.53 +vn -0.00 0.87 -0.50 +vn 0.07 0.87 -0.49 +vn 0.08 0.86 -0.51 +vn 0.39 0.56 -0.73 +vn -0.06 0.92 -0.38 +vn 0.97 0.17 0.19 +vn 0.74 0.03 0.67 +vn 0.42 -0.42 0.81 +vn 0.99 -0.05 0.13 +vn 0.71 0.41 0.58 +vn 0.89 0.44 0.14 +vn 0.83 0.28 -0.49 +vn 0.75 0.50 -0.43 +vn 0.25 0.29 -0.92 +vn 0.38 0.60 -0.70 +vn -0.04 0.60 -0.80 +vn -0.21 0.19 -0.96 +vn 0.76 0.15 -0.64 +vn 0.90 0.14 -0.42 +vn 0.81 0.16 -0.56 +vn 0.02 -0.04 -1.00 +vn 0.58 0.69 -0.44 +vn 0.70 0.69 0.16 +vn 0.93 -0.31 0.18 +vn 0.92 -0.22 0.32 +vn 0.88 -0.10 -0.47 +vn -0.09 -0.11 -0.99 +vn -0.05 0.50 -0.86 +vn 0.83 -0.21 -0.52 +vn 0.93 -0.12 0.34 +vn -0.14 -0.34 -0.93 +vn 0.95 -0.30 -0.03 +vn -0.90 -0.29 0.33 +vn -0.92 0.13 -0.37 +vn -0.94 -0.13 -0.33 +vn -0.85 -0.25 0.47 +vn -0.87 -0.12 0.48 +vn 0.02 -0.06 1.00 +vn 0.06 -0.42 0.91 +vn -0.92 -0.19 -0.35 +vn 0.08 -0.74 0.67 +vn 0.21 -0.74 0.64 +vn -0.77 -0.45 0.45 +vn -0.99 -0.09 0.06 +vn -0.92 0.02 -0.39 +vn 0.71 -0.39 -0.58 +vn -0.07 -0.47 -0.88 +vn 0.00 -0.40 -0.92 +vn 0.72 -0.38 -0.58 +vn -0.65 -0.28 -0.70 +vn -0.61 -0.37 -0.70 +vn -0.97 -0.13 -0.22 +vn -0.70 -0.06 0.71 +vn 0.22 -0.17 0.96 +vn 0.25 -0.16 0.95 +vn 0.80 -0.18 0.57 +vn 0.73 -0.19 0.66 +vn 0.06 -0.20 -0.98 +vn 0.76 -0.20 -0.62 +vn -0.66 -0.23 0.72 +vn 0.17 -0.18 0.97 +vn 0.75 -0.13 0.65 +vn 0.98 -0.21 0.04 +vn 0.99 -0.13 0.09 +vn 0.84 0.13 -0.53 +vn 0.76 0.01 -0.65 +vn 0.89 0.36 0.26 +vn 0.93 0.36 -0.03 +vn 0.95 -0.29 0.11 +vn 0.67 -0.51 -0.54 +vn 0.39 -0.61 -0.69 +vn -0.04 -0.50 -0.87 +vn -0.77 -0.14 -0.62 +vn -0.08 -0.32 -0.94 +vn -1.00 0.00 -0.05 +vn -0.76 0.28 0.59 +vn 0.99 0.15 0.06 +vn 0.45 -0.15 0.88 +vn 0.81 0.05 0.58 +vn 0.17 0.11 0.98 +vn 0.15 -0.03 0.99 +vn -0.75 0.11 0.66 +vn -0.71 0.12 0.69 +vn 0.71 -0.05 0.70 +vn 0.69 -0.07 0.72 +vn 0.94 -0.31 0.16 +vn 0.97 -0.23 0.06 +vn -0.03 -0.42 -0.91 +vn 0.69 -0.48 -0.54 +vn 0.70 -0.25 -0.67 +vn 0.03 -0.23 -0.97 +vn 0.98 -0.21 0.06 +vn -0.58 -0.25 -0.77 +vn -1.00 0.03 -0.08 +vn -0.75 -0.15 -0.65 +vn 0.01 -0.19 -0.98 +vn -1.00 -0.03 0.04 +vn -0.67 0.12 0.73 +vn 0.18 0.34 0.92 +vn 0.80 0.23 0.55 +vn 1.00 -0.03 -0.04 +vn 0.68 -0.23 -0.70 +vn 0.28 -0.77 0.57 +vn 0.00 -0.74 0.68 +vn 0.00 -0.49 0.87 +vn 0.35 -0.56 0.75 +vn 0.66 -0.65 0.39 +vn 0.54 -0.79 0.29 +vn 0.85 -0.52 -0.02 +vn 0.83 -0.56 -0.05 +vn 0.74 -0.65 0.19 +vn 0.00 0.23 -0.97 +vn 0.48 0.15 -0.86 +vn 0.80 -0.15 -0.58 +vn 0.89 -0.36 -0.28 +vn 0.79 0.09 -0.60 +vn 0.47 0.16 -0.87 +vn 0.00 0.22 -0.98 +vn 0.48 0.43 0.77 +vn -0.00 0.48 0.88 +vn 0.00 0.61 0.79 +vn 0.50 0.52 0.69 +vn 0.65 0.14 0.75 +vn 0.00 0.15 0.99 +vn -0.00 -0.52 0.85 +vn 0.59 0.10 0.80 +vn 0.12 -0.29 0.95 +vn 0.58 -0.20 0.79 +vn 0.25 -0.18 0.95 +vn 0.41 -0.51 0.76 +vn -0.10 -0.71 0.69 +vn 0.18 -0.70 0.69 +vn 0.84 0.30 0.46 +vn 0.52 -0.61 0.60 +vn 0.63 -0.44 0.64 +vn 0.53 -0.47 0.71 +vn 0.47 -0.51 0.72 +vn 0.00 0.98 0.20 +vn 0.00 0.87 0.50 +vn 0.42 -0.86 0.28 +vn 0.86 0.11 0.50 +vn 0.83 -0.34 0.45 +vn 0.95 -0.30 0.13 +vn -0.00 0.16 0.99 +vn -0.00 0.35 0.94 +vn 0.42 0.25 0.87 +vn 0.32 0.05 0.95 +vn -0.00 0.25 0.97 +vn 0.38 0.31 0.87 +vn 0.27 0.68 0.68 +vn -0.00 0.66 0.75 +vn 0.67 -0.07 0.74 +vn 0.60 -0.04 0.80 +vn 0.51 -0.10 0.85 +vn 0.70 0.08 0.71 +vn 0.30 0.74 0.61 +vn 0.00 0.76 0.65 +vn 0.50 -0.23 0.84 +vn 0.49 -0.32 0.81 +vn 0.57 -0.18 0.80 +vn 0.58 -0.01 0.81 +vn -0.26 -0.76 0.59 +vn -0.16 -0.83 0.54 +vn 0.36 0.80 -0.48 +vn 0.01 0.84 -0.54 +vn 0.00 0.98 -0.18 +vn 0.36 -0.80 0.48 +vn 0.33 -0.93 0.17 +vn 0.51 -0.75 0.42 +vn 0.57 -0.53 0.62 +vn 0.62 -0.70 0.36 +vn 0.57 -0.66 0.48 +vn 0.41 -0.87 0.26 +vn 0.23 -0.93 0.28 +vn 0.25 -0.96 0.08 +vn 0.00 0.55 0.83 +vn 0.75 0.21 0.63 +vn 0.54 0.43 0.73 +vn 0.52 0.06 0.85 +vn 0.55 0.66 0.51 +vn 0.84 0.44 0.32 +vn 0.31 0.56 0.77 +vn 0.26 0.19 0.95 +vn 0.86 0.07 0.50 +vn 0.95 0.16 0.25 +vn 0.82 0.55 0.19 +vn 0.84 0.50 0.19 +vn 0.95 0.26 0.17 +vn 0.95 0.31 0.03 +vn 0.23 -0.68 0.70 +vn -0.00 -0.70 0.71 +vn -0.00 -0.90 0.44 +vn -0.00 -1.00 0.04 +vn -0.00 -1.00 -0.01 +vn 0.00 -0.96 0.30 +vn 0.72 -0.37 0.59 +vn 0.63 0.73 0.28 +vn 0.86 0.49 0.13 +vn 0.94 0.31 -0.16 +vn 0.00 0.57 0.82 +vn 0.77 0.29 0.57 +vn 0.83 0.23 0.51 +vn 0.67 -0.10 0.74 +vn 0.64 0.08 0.76 +vn 0.50 0.45 0.74 +vn 0.79 0.39 0.48 +vn 0.61 0.14 0.78 +vn 0.77 0.33 0.55 +vn 0.50 -0.31 0.81 +vn 0.54 -0.04 0.84 +vn 0.51 -0.15 0.85 +vn 0.59 -0.46 0.67 +vn 0.43 -0.27 0.86 +vn 0.42 0.43 0.80 +vn 0.14 0.69 0.72 +vn 0.42 0.64 0.64 +vn 0.47 0.49 -0.74 +vn 0.01 0.54 -0.84 +vn 0.95 -0.01 -0.32 +vn 0.62 0.13 0.77 +vn 0.56 -0.40 0.72 +vn -0.15 -0.42 0.90 +vn -0.23 -0.66 0.72 +vn -0.30 -0.78 0.55 +vn -0.04 -0.71 0.70 +vn -0.25 -0.45 0.86 +vn -0.32 -0.71 0.62 +vn 0.60 0.31 0.74 +vn 0.56 0.64 0.53 +vn 0.79 -0.15 0.60 +vn 0.62 -0.28 0.74 +vn 0.29 0.58 0.76 +vn 0.58 0.32 0.75 +vn 0.74 -0.00 0.67 +vn 0.24 0.32 0.92 +vn 0.95 -0.23 -0.23 +vn 0.98 0.19 -0.04 +vn 0.95 0.30 0.14 +vn 0.92 0.30 0.23 +vn -0.00 -0.44 0.90 +vn 0.36 -0.41 0.84 +vn 0.67 0.57 0.48 +vn -0.14 -0.87 0.47 +vn -0.04 -0.88 0.47 +vn -0.05 -0.48 0.87 +vn -0.14 -0.48 0.87 +vn -0.12 0.02 0.99 +vn -0.18 0.29 0.94 +vn -0.34 0.36 0.87 +vn -0.29 -0.10 0.95 +vn -0.15 -0.33 0.93 +vn -0.02 -0.23 0.97 +vn 0.17 -0.36 0.92 +vn 0.07 -0.45 0.89 +vn 0.41 -0.33 0.85 +vn 0.37 -0.37 0.85 +vn 0.55 -0.16 0.82 +vn 0.59 -0.11 0.80 +vn 0.67 0.06 0.74 +vn 0.64 0.03 0.77 +vn 0.63 0.12 0.76 +vn 0.41 -0.07 0.91 +vn 0.62 0.26 0.74 +vn 0.59 0.30 0.75 +vn 0.66 0.14 0.74 +vn 0.45 0.33 0.83 +vn 0.50 0.27 0.82 +vn 0.14 0.44 0.89 +vn 0.06 0.54 0.84 +vn 0.19 0.14 0.97 +vn 0.74 -0.02 0.67 +vn 0.70 -0.21 -0.69 +vn -0.78 -0.13 -0.62 +vn -1.00 0.02 -0.03 +vn -0.70 -0.14 -0.70 +vn -0.65 0.76 0.07 +vn -0.61 0.79 -0.04 +vn -0.49 0.82 -0.29 +vn -0.56 0.82 0.10 +vn 0.34 -0.51 -0.79 +vn 0.08 -0.02 -1.00 +vn -0.94 0.33 -0.02 +vn -0.90 0.42 -0.11 +vn -0.96 -0.27 0.06 +vn -0.80 -0.26 -0.55 +vn -0.97 -0.22 0.10 +vn -0.77 -0.12 0.62 +vn -0.31 -0.83 0.46 +vn -0.77 -0.64 -0.03 +vn -0.60 0.77 -0.22 +vn -0.28 -0.83 -0.48 +vn -0.78 -0.62 0.03 +vn -0.50 -0.86 0.04 +vn -0.45 -0.89 0.10 +vn -0.47 -0.86 -0.18 +vn -0.41 -0.85 -0.32 +vn -0.82 0.57 -0.03 +vn -0.86 0.51 -0.01 +vn -0.65 0.71 -0.27 +vn -0.65 -0.32 -0.69 +vn -0.44 -0.79 -0.42 +vn -0.95 -0.02 -0.30 +vn -0.98 0.17 -0.11 +vn 0.08 -0.49 -0.87 +vn 0.04 -0.84 -0.53 +vn 0.76 -0.19 -0.62 +vn 0.52 -0.78 -0.35 +vn 0.99 0.03 -0.16 +vn 0.95 0.30 0.02 +vn 0.75 0.64 0.15 +vn 0.67 0.63 0.40 +vn 0.33 0.91 0.24 +vn -0.04 0.73 0.69 +vn -0.70 0.63 0.35 +vn -0.36 0.91 0.18 +vn -0.02 0.96 0.30 +vn -0.77 0.63 0.07 +vn -1.00 -0.00 -0.09 +vn -0.63 -0.78 -0.06 +vn -0.60 -0.72 0.35 +vn -0.93 -0.01 0.37 +vn -0.14 -0.67 0.73 +vn -0.00 -1.00 0.00 +vn -0.14 -0.82 0.55 +vn -0.01 -0.69 0.72 +vn 0.08 -0.82 0.57 +vn 1.00 0.03 0.03 +vn 0.87 -0.01 0.49 +vn 0.56 -0.71 0.42 +vn 0.66 -0.75 0.02 +vn 0.78 0.53 0.34 +vn 0.73 0.68 0.07 +vn 0.43 0.89 0.17 +vn 0.25 0.73 0.63 +vn -0.39 0.91 0.14 +vn -0.35 0.54 0.77 +vn 0.02 0.76 0.65 +vn -0.00 0.99 0.12 +vn -0.82 0.52 0.25 +vn -0.74 0.68 0.01 +vn -0.30 0.11 0.95 +vn -0.02 0.09 1.00 +vn 0.21 0.11 0.97 +vn 0.31 0.12 0.94 +vn 0.29 0.53 0.80 +vn 0.41 -0.91 0.06 +vn -0.27 -0.94 0.19 +vn -0.15 -0.89 0.43 +vn -0.33 -0.81 0.49 +vn -0.54 -0.71 -0.46 +vn -0.97 -0.24 -0.02 +vn -0.94 0.33 0.01 +vn -0.70 0.70 0.14 +vn -0.21 0.35 -0.91 +vn -0.76 0.53 -0.36 +vn -1.00 0.02 -0.05 +vn -1.00 0.03 -0.03 +vn -0.97 0.23 0.00 +vn -1.00 -0.01 -0.06 +vn -0.73 0.68 -0.05 +vn -0.92 0.22 -0.33 +vn -0.37 -0.47 0.80 +vn -0.44 -0.60 0.67 +vn -0.67 0.52 -0.52 +vn -0.92 0.31 -0.22 +vn -0.87 0.28 0.41 +vn -0.94 0.13 0.31 +vn -0.54 0.26 -0.80 +vn -0.35 -0.31 0.88 +vn -0.57 -0.23 0.79 +vn -0.76 -0.09 0.65 +vn -0.66 -0.10 0.75 +vn -0.43 -0.43 0.79 +vn -0.85 -0.06 -0.53 +vn -0.90 -0.33 0.29 +vn -0.97 -0.18 -0.15 +vn -0.65 -0.42 0.63 +vn -0.33 -0.37 0.87 +vn -0.33 0.72 0.61 +vn -0.04 0.85 0.53 +vn -0.03 0.84 0.55 +vn -0.48 0.61 0.63 +vn -0.79 0.22 0.58 +vn -0.84 0.28 0.46 +vn -0.69 0.22 0.69 +vn -0.98 0.12 -0.16 +vn -0.29 0.04 0.96 +vn -0.93 -0.27 0.24 +vn 0.14 0.05 0.99 +vn -0.57 -0.07 -0.82 +vn -0.79 0.15 -0.60 +vn -0.99 0.10 -0.07 +vn -0.92 -0.38 -0.07 +vn 0.22 0.97 -0.11 +vn 0.07 0.95 0.31 +vn -0.17 0.97 0.15 +vn 0.51 0.70 0.50 +vn -0.43 0.09 0.90 +vn -0.16 0.05 0.99 +vn -0.83 -0.53 0.17 +vn -0.15 -0.24 0.96 +vn 0.10 0.04 0.99 +vn 0.11 0.18 0.98 +vn -0.41 -0.08 -0.91 +vn -0.93 -0.30 -0.20 +vn -0.32 0.26 -0.91 +vn 0.20 0.53 -0.82 +vn 0.17 0.43 -0.88 +vn 0.51 0.68 -0.53 +vn 0.56 0.64 -0.52 +vn -0.12 0.99 -0.05 +vn -0.36 -0.26 0.89 +vn -0.20 -0.48 0.85 +vn -0.47 -0.61 0.64 +vn -0.28 -0.58 0.77 +vn -0.12 -0.55 0.83 +vn -0.03 -0.35 0.94 +vn -0.88 0.32 -0.35 +vn -0.78 0.54 -0.31 +vn -0.30 0.56 -0.77 +vn 0.07 0.68 -0.73 +vn -0.19 0.93 -0.32 +vn 0.42 0.77 -0.47 +vn -0.71 -0.09 0.70 +vn 0.05 -0.13 0.99 +vn -0.95 0.06 -0.32 +vn -0.72 -0.27 -0.64 +vn -0.74 -0.27 -0.61 +vn -0.25 -0.51 0.83 +vn -0.78 -0.62 -0.05 +vn -0.51 -0.85 -0.10 +vn 0.11 -0.94 -0.33 +vn -0.85 0.39 0.35 +vn -0.68 0.72 0.14 +vn -0.82 -0.51 0.25 +vn -0.83 -0.49 0.27 +vn -0.56 -0.73 0.40 +vn -0.53 -0.74 0.42 +vn -0.84 -0.24 0.49 +vn -0.84 -0.18 0.52 +vn -0.43 0.22 0.87 +vn -0.45 0.29 0.85 +vn 0.16 -0.00 0.99 +vn 0.12 -0.11 0.99 +vn -0.02 0.67 0.75 +vn -0.04 0.65 0.76 +vn -0.29 -0.85 0.43 +vn -0.27 -0.86 0.42 +vn 0.38 0.91 0.18 +vn -0.35 0.71 0.61 +vn -0.43 0.89 0.13 +vn -0.39 0.12 0.91 +vn 0.10 -0.68 0.73 +vn 0.17 0.10 0.98 +vn 0.21 -0.05 0.98 +vn -0.31 -0.14 0.94 +vn -0.71 0.47 0.52 +vn -0.36 0.38 0.85 +vn -0.08 0.66 0.74 +vn -0.29 0.86 0.42 +vn -0.76 -0.13 -0.64 +vn -0.54 -0.69 -0.48 +vn -0.95 0.07 -0.32 +vn -1.00 0.03 -0.04 +vn 0.05 -0.25 -0.97 +vn 0.05 -0.76 -0.64 +vn 0.80 -0.14 -0.58 +vn 0.57 -0.71 -0.41 +vn 0.98 0.05 -0.21 +vn 0.99 0.05 0.11 +vn 0.68 0.41 0.62 +vn 0.70 0.71 0.07 +vn -0.07 0.47 0.88 +vn 0.33 0.94 0.08 +vn -0.00 0.99 0.16 +vn -0.34 0.94 0.02 +vn -0.74 0.67 -0.09 +vn -0.79 0.41 0.45 +vn -0.99 -0.03 -0.16 +vn -0.60 -0.80 -0.09 +vn -0.61 -0.73 0.30 +vn -0.94 -0.02 0.33 +vn -0.17 -0.66 0.73 +vn -0.16 -0.85 0.49 +vn 0.09 -0.85 0.51 +vn 0.90 -0.02 0.43 +vn 0.59 -0.73 0.36 +vn 0.64 -0.77 -0.03 +vn 1.00 -0.01 -0.05 +vn 0.80 0.51 0.31 +vn 0.70 0.70 -0.12 +vn 0.41 0.91 -0.09 +vn 0.28 0.76 0.59 +vn 0.01 1.00 0.01 +vn -0.39 0.92 -0.06 +vn -0.46 0.58 0.68 +vn 0.03 0.81 0.59 +vn -0.84 0.50 0.22 +vn -0.69 0.70 -0.20 +vn -0.39 0.09 0.92 +vn -0.02 0.07 1.00 +vn 0.27 0.10 0.96 +vn 0.37 0.11 0.92 +vn 0.37 0.57 0.74 +vn 0.37 0.93 -0.04 +vn -0.37 0.92 -0.09 +vn -0.47 0.11 0.88 +vn 0.13 -0.67 0.73 +vn -0.95 -0.12 -0.30 +vn -0.96 0.14 0.25 +vn -0.92 0.14 0.36 +vn -0.81 -0.37 0.46 +vn -0.89 -0.19 0.41 +vn -0.96 0.09 0.27 +vn -0.94 -0.11 0.33 +vn -0.94 -0.03 0.34 +vn -0.17 0.83 -0.53 +vn -0.40 -0.79 -0.47 +vn 0.35 -0.80 0.49 +vn 0.21 -0.86 0.46 +vn -0.07 0.87 -0.49 +vn -0.08 0.86 -0.51 +vn -0.40 0.56 -0.72 +vn 0.07 0.92 -0.38 +vn -0.97 0.17 0.19 +vn -0.99 -0.05 0.13 +vn -0.42 -0.42 0.81 +vn -0.74 0.03 0.67 +vn -0.71 0.41 0.58 +vn -0.89 0.44 0.14 +vn -0.75 0.50 -0.43 +vn -0.83 0.28 -0.49 +vn -0.25 0.29 -0.92 +vn -0.38 0.60 -0.70 +vn 0.04 0.60 -0.80 +vn -0.90 0.14 -0.42 +vn -0.76 0.15 -0.64 +vn 0.21 0.19 -0.96 +vn -0.02 -0.04 -1.00 +vn -0.81 0.16 -0.56 +vn -0.70 0.69 0.16 +vn -0.58 0.69 -0.44 +vn -0.93 -0.31 0.18 +vn -0.88 -0.11 -0.47 +vn -0.92 -0.22 0.32 +vn 0.05 0.50 -0.86 +vn 0.09 -0.12 -0.99 +vn -0.83 -0.21 -0.52 +vn -0.93 -0.12 0.34 +vn 0.14 -0.34 -0.93 +vn -0.95 -0.30 -0.03 +vn 0.90 -0.29 0.33 +vn 0.85 -0.25 0.47 +vn 0.94 -0.13 -0.33 +vn 0.92 0.13 -0.37 +vn 0.87 -0.12 0.48 +vn -0.06 -0.42 0.91 +vn -0.02 -0.06 1.00 +vn 0.92 -0.19 -0.35 +vn -0.08 -0.74 0.67 +vn 0.77 -0.45 0.45 +vn -0.21 -0.74 0.64 +vn 0.99 -0.09 0.06 +vn 0.92 0.02 -0.39 +vn -0.71 -0.39 -0.58 +vn -0.72 -0.38 -0.58 +vn -0.00 -0.40 -0.92 +vn 0.07 -0.47 -0.88 +vn 0.61 -0.37 -0.70 +vn 0.65 -0.28 -0.70 +vn 0.97 -0.13 -0.22 +vn 0.70 -0.06 0.71 +vn -0.25 -0.17 0.95 +vn -0.22 -0.17 0.96 +vn -0.80 -0.18 0.57 +vn -0.73 -0.19 0.66 +vn -0.76 -0.21 -0.62 +vn -0.06 -0.20 -0.98 +vn 0.66 -0.23 0.72 +vn -0.17 -0.18 0.97 +vn -0.75 -0.13 0.65 +vn -0.98 -0.21 0.04 +vn -0.99 -0.13 0.09 +vn -0.76 0.00 -0.65 +vn -0.83 0.12 -0.54 +vn -0.89 0.36 0.26 +vn -0.93 0.36 -0.03 +vn -0.95 -0.29 0.11 +vn -0.39 -0.61 -0.69 +vn 0.04 -0.50 -0.87 +vn -0.67 -0.51 -0.54 +vn 0.08 -0.32 -0.94 +vn 0.77 -0.14 -0.62 +vn 0.76 0.28 0.59 +vn -0.99 0.15 0.06 +vn -0.46 -0.15 0.88 +vn -0.81 0.05 0.58 +vn 0.71 0.12 0.69 +vn 0.75 0.11 0.66 +vn -0.15 -0.03 0.99 +vn -0.71 -0.05 0.70 +vn -0.69 -0.07 0.72 +vn -0.94 -0.31 0.16 +vn -0.97 -0.23 0.06 +vn 0.03 -0.42 -0.91 +vn -0.03 -0.23 -0.97 +vn -0.70 -0.25 -0.67 +vn -0.69 -0.48 -0.54 +vn -0.98 -0.21 0.06 +vn 0.58 -0.25 -0.77 +vn 1.00 0.03 -0.08 +vn -0.01 -0.19 -0.98 +vn 0.75 -0.15 -0.65 +vn 1.00 -0.03 0.04 +vn 0.67 0.12 0.73 +vn -0.18 0.34 0.92 +vn -0.80 0.23 0.55 +vn -1.00 -0.03 -0.04 +vn -0.68 -0.23 -0.70 +vn -0.28 -0.77 0.57 +vn -0.35 -0.56 0.75 +vn -0.54 -0.79 0.29 +vn -0.66 -0.65 0.39 +vn -0.74 -0.65 0.19 +vn -0.83 -0.56 -0.05 +vn -0.85 -0.52 -0.02 +vn -0.49 0.15 -0.86 +vn -0.89 -0.36 -0.28 +vn -0.80 -0.12 -0.58 +vn -0.76 0.08 -0.64 +vn -0.48 0.43 0.77 +vn -0.50 0.52 0.69 +vn -0.58 0.10 0.80 +vn -0.28 -0.34 0.90 +vn -0.65 0.14 0.75 +vn -0.58 -0.21 0.79 +vn -0.41 -0.51 0.76 +vn -0.25 -0.18 0.95 +vn -0.18 -0.70 0.69 +vn 0.10 -0.71 0.69 +vn -0.84 0.30 0.46 +vn -0.52 -0.61 0.60 +vn -0.47 -0.51 0.72 +vn -0.53 -0.47 0.71 +vn -0.63 -0.44 0.64 +vn -0.42 -0.86 0.28 +vn -0.86 0.11 0.50 +vn -0.95 -0.30 0.13 +vn -0.83 -0.34 0.45 +vn -0.32 0.05 0.95 +vn -0.43 0.25 0.87 +vn -0.27 0.68 0.68 +vn -0.38 0.31 0.87 +vn -0.60 -0.04 0.80 +vn -0.67 -0.07 0.74 +vn -0.51 -0.10 0.86 +vn -0.70 0.07 0.71 +vn -0.30 0.74 0.61 +vn -0.58 -0.18 0.79 +vn -0.49 -0.32 0.81 +vn -0.51 -0.22 0.83 +vn -0.59 -0.01 0.81 +vn 0.16 -0.83 0.54 +vn 0.26 -0.76 0.59 +vn -0.35 0.80 -0.49 +vn -0.36 -0.80 0.48 +vn -0.57 -0.53 0.62 +vn -0.51 -0.75 0.42 +vn -0.33 -0.93 0.17 +vn -0.62 -0.70 0.36 +vn -0.41 -0.87 0.26 +vn -0.57 -0.66 0.48 +vn -0.23 -0.93 0.28 +vn -0.25 -0.96 0.08 +vn -0.52 0.07 0.85 +vn -0.54 0.43 0.73 +vn -0.75 0.21 0.62 +vn -0.55 0.66 0.50 +vn -0.84 0.44 0.32 +vn -0.31 0.56 0.77 +vn -0.26 0.19 0.95 +vn -0.95 0.18 0.25 +vn -0.87 0.07 0.48 +vn -0.82 0.54 0.20 +vn -0.95 0.31 0.06 +vn -0.95 0.27 0.17 +vn -0.84 0.50 0.19 +vn -0.23 -0.68 0.70 +vn -0.72 -0.37 0.59 +vn -0.63 0.73 0.29 +vn -0.87 0.48 0.15 +vn -0.95 0.31 -0.10 +vn -0.77 0.29 0.57 +vn -0.84 0.24 0.50 +vn -0.65 0.08 0.76 +vn -0.68 -0.09 0.73 +vn -0.50 0.45 0.74 +vn -0.60 0.14 0.79 +vn -0.79 0.39 0.48 +vn -0.77 0.33 0.55 +vn -0.51 -0.30 0.81 +vn -0.55 -0.04 0.84 +vn -0.51 -0.15 0.85 +vn -0.44 -0.27 0.86 +vn -0.59 -0.46 0.67 +vn -0.42 0.43 0.80 +vn -0.42 0.66 0.63 +vn -0.13 0.70 0.71 +vn -0.48 0.47 -0.74 +vn -0.93 0.00 -0.36 +vn -0.62 0.13 0.77 +vn -0.56 -0.40 0.72 +vn 0.15 -0.42 0.90 +vn 0.04 -0.24 0.97 +vn 0.24 -0.66 0.72 +vn 0.30 -0.78 0.55 +vn 0.26 -0.45 0.86 +vn 0.10 -0.39 0.92 +vn 0.33 -0.71 0.62 +vn -0.60 0.31 0.74 +vn -0.61 -0.29 0.74 +vn -0.78 -0.17 0.61 +vn -0.28 0.58 0.76 +vn -0.58 0.32 0.75 +vn -0.75 -0.01 0.67 +vn -0.23 0.32 0.92 +vn -0.94 -0.23 -0.24 +vn -0.98 0.19 -0.04 +vn -0.94 0.31 0.14 +vn -0.92 0.32 0.24 +vn -0.36 -0.40 0.84 +vn -0.67 0.57 0.48 +vn 0.03 -0.88 0.47 +vn 0.14 -0.87 0.47 +vn 0.14 -0.48 0.87 +vn 0.05 -0.49 0.87 +vn 0.12 0.03 0.99 +vn 0.29 -0.10 0.95 +vn 0.34 0.37 0.87 +vn 0.18 0.30 0.94 +vn 0.15 -0.33 0.93 +vn 0.02 -0.23 0.97 +vn -0.17 -0.37 0.91 +vn -0.06 -0.45 0.89 +vn -0.41 -0.34 0.85 +vn -0.37 -0.37 0.85 +vn -0.56 -0.16 0.81 +vn -0.59 -0.12 0.80 +vn -0.65 0.02 0.76 +vn -0.68 0.05 0.73 +vn -0.63 0.26 0.73 +vn -0.42 -0.06 0.91 +vn -0.64 0.12 0.76 +vn -0.66 0.13 0.74 +vn -0.60 0.29 0.75 +vn -0.45 0.33 0.83 +vn -0.50 0.27 0.82 +vn -0.14 0.44 0.89 +vn -0.06 0.54 0.84 +vn -0.19 0.14 0.97 +vn -0.74 -0.02 0.67 +vn -0.70 -0.21 -0.69 +vn 0.78 -0.13 -0.62 +vn 1.00 0.02 -0.03 +vn 0.70 -0.14 -0.70 +vn 0.04 -0.08 1.00 +vn 0.16 0.09 0.98 +vn 0.31 -0.03 0.95 +vn 0.16 -0.17 0.97 +vn 0.42 0.01 0.91 +vn 0.51 -0.06 0.86 +vn 0.51 -0.03 0.86 +vn 0.44 0.07 0.90 +vn 0.34 0.14 0.93 +vn 0.25 0.15 0.96 +vn 0.47 0.02 0.88 +vn 0.39 -0.09 0.92 +vn 0.46 -0.04 0.89 +vn 0.37 0.04 0.93 +vn 0.49 0.07 0.87 +vn 0.20 0.22 0.96 +vn 0.20 0.25 0.95 +vn -0.01 0.23 0.97 +vn -0.09 -0.44 0.89 +vn -0.51 -0.06 0.86 +vn -0.42 0.01 0.91 +vn -0.31 -0.03 0.95 +vn -0.16 -0.18 0.97 +vn -0.16 0.09 0.98 +vn -0.04 -0.09 1.00 +vn -0.34 0.15 0.93 +vn -0.44 0.07 0.90 +vn -0.51 -0.03 0.86 +vn -0.25 0.15 0.96 +vn -0.47 0.02 0.88 +vn -0.46 -0.04 0.89 +vn -0.39 -0.09 0.92 +vn -0.37 0.04 0.93 +vn -0.19 0.23 0.95 +vn -0.49 0.07 0.87 +vn -0.20 0.25 0.95 +vn 0.02 0.23 0.97 +vn 0.08 -0.45 0.89 +# 1090 vertex normals + +vt 0.95 0.25 0.00 +vt 0.94 0.29 0.00 +vt 0.90 0.30 0.00 +vt 0.90 0.26 0.00 +vt 0.91 0.09 0.00 +vt 0.95 0.09 0.00 +vt 0.96 0.13 0.00 +vt 0.91 0.13 0.00 +vt 0.80 0.03 0.00 +vt 0.81 0.06 0.00 +vt 0.76 0.07 0.00 +vt 0.75 0.05 0.00 +vt 0.84 0.27 0.00 +vt 0.85 0.30 0.00 +vt 0.76 0.28 0.00 +vt 0.76 0.31 0.00 +vt 0.77 0.19 0.00 +vt 0.72 0.19 0.00 +vt 0.73 0.17 0.00 +vt 0.77 0.18 0.00 +vt 0.95 0.34 0.00 +vt 0.90 0.35 0.00 +vt 0.70 0.32 0.00 +vt 0.71 0.28 0.00 +vt 0.67 0.28 0.00 +vt 0.68 0.31 0.00 +vt 0.69 0.18 0.00 +vt 0.70 0.15 0.00 +vt 0.86 0.37 0.00 +vt 0.91 0.38 0.00 +vt 0.87 0.40 0.00 +vt 0.62 0.88 0.00 +vt 0.63 0.85 0.00 +vt 0.66 0.87 0.00 +vt 0.67 0.92 0.00 +vt 0.60 0.94 0.00 +vt 0.60 0.89 0.00 +vt 0.52 0.92 0.00 +vt 0.57 0.88 0.00 +vt 0.53 0.87 0.00 +vt 0.55 0.86 0.00 +vt 0.55 0.83 0.00 +vt 0.55 0.84 0.00 +vt 0.56 0.83 0.00 +vt 0.61 0.83 0.00 +vt 0.63 0.84 0.00 +vt 0.59 0.84 0.00 +vt 0.59 0.83 0.00 +vt 0.63 0.83 0.00 +vt 0.68 0.80 0.00 +vt 0.67 0.76 0.00 +vt 0.69 0.74 0.00 +vt 0.70 0.80 0.00 +vt 0.39 0.37 0.00 +vt 0.38 0.37 0.00 +vt 0.35 0.31 0.00 +vt 0.42 0.31 0.00 +vt 0.41 0.36 0.00 +vt 0.35 0.38 0.00 +vt 0.33 0.37 0.00 +vt 0.50 0.81 0.00 +vt 0.48 0.81 0.00 +vt 0.49 0.75 0.00 +vt 0.51 0.76 0.00 +vt 0.53 0.78 0.00 +vt 0.55 0.80 0.00 +vt 0.56 0.80 0.00 +vt 0.55 0.77 0.00 +vt 0.60 0.80 0.00 +vt 0.59 0.80 0.00 +vt 0.59 0.76 0.00 +vt 0.62 0.75 0.00 +vt 0.64 0.77 0.00 +vt 0.63 0.80 0.00 +vt 0.63 0.72 0.00 +vt 0.59 0.71 0.00 +vt 0.59 0.68 0.00 +vt 0.63 0.69 0.00 +vt 0.55 0.72 0.00 +vt 0.54 0.69 0.00 +vt 0.53 0.73 0.00 +vt 0.56 0.75 0.00 +vt 0.68 0.36 0.00 +vt 0.67 0.37 0.00 +vt 0.69 0.38 0.00 +vt 0.68 0.40 0.00 +vt 0.72 0.10 0.00 +vt 0.70 0.09 0.00 +vt 0.77 0.23 0.00 +vt 0.85 0.23 0.00 +vt 0.90 0.21 0.00 +vt 0.95 0.21 0.00 +vt 0.87 0.03 0.00 +vt 0.87 0.06 0.00 +vt 0.67 0.13 0.00 +vt 0.94 0.06 0.00 +vt 0.90 0.06 0.00 +vt 0.55 0.41 0.00 +vt 0.56 0.28 0.00 +vt 0.60 0.28 0.00 +vt 0.60 0.41 0.00 +vt 0.57 0.17 0.00 +vt 0.51 0.28 0.00 +vt 0.52 0.17 0.00 +vt 0.51 0.42 0.00 +vt 0.52 0.52 0.00 +vt 0.56 0.51 0.00 +vt 0.57 0.58 0.00 +vt 0.53 0.58 0.00 +vt 0.60 0.50 0.00 +vt 0.61 0.57 0.00 +vt 0.61 0.17 0.00 +vt 0.57 0.15 0.00 +vt 0.60 0.15 0.00 +vt 0.55 0.15 0.00 +vt 0.58 0.62 0.00 +vt 0.54 0.64 0.00 +vt 0.62 0.62 0.00 +vt 0.73 0.48 0.00 +vt 0.70 0.45 0.00 +vt 0.72 0.44 0.00 +vt 0.74 0.46 0.00 +vt 0.92 0.39 0.00 +vt 0.88 0.42 0.00 +vt 0.96 0.38 0.00 +vt 0.96 0.36 0.00 +vt 0.84 0.41 0.00 +vt 0.85 0.44 0.00 +vt 0.90 0.05 0.00 +vt 0.97 0.39 0.00 +vt 0.94 0.41 0.00 +vt 0.78 0.49 0.00 +vt 0.77 0.51 0.00 +vt 0.81 0.47 0.00 +vt 0.76 0.45 0.00 +vt 0.79 0.43 0.00 +vt 0.82 0.46 0.00 +vt 0.91 0.44 0.00 +vt 0.88 0.45 0.00 +vt 0.86 0.47 0.00 +vt 0.84 0.48 0.00 +vt 0.81 0.52 0.00 +vt 0.82 0.51 0.00 +vt 0.83 0.38 0.00 +vt 0.83 0.39 0.00 +vt 0.80 0.41 0.00 +vt 0.81 0.40 0.00 +vt 0.84 0.36 0.00 +vt 0.83 0.36 0.00 +vt 0.11 0.49 0.00 +vt 0.13 0.50 0.00 +vt 0.09 0.51 0.00 +vt 0.08 0.50 0.00 +vt 0.12 0.47 0.00 +vt 0.06 0.54 0.00 +vt 0.06 0.93 0.00 +vt 0.09 0.98 0.00 +vt 0.05 0.99 0.00 +vt 0.04 0.94 0.00 +vt 0.07 0.79 0.00 +vt 0.07 0.77 0.00 +vt 0.08 0.76 0.00 +vt 0.14 0.78 0.00 +vt 0.11 0.77 0.00 +vt 0.12 0.75 0.00 +vt 0.15 0.77 0.00 +vt 0.07 0.46 0.00 +vt 0.13 0.44 0.00 +vt 0.02 0.46 0.00 +vt 0.02 0.49 0.00 +vt 0.09 0.91 0.00 +vt 0.11 0.96 0.00 +vt 0.13 0.89 0.00 +vt 0.08 0.87 0.00 +vt 0.13 0.86 0.00 +vt 0.13 0.83 0.00 +vt 0.09 0.83 0.00 +vt 0.17 0.80 0.00 +vt 0.18 0.79 0.00 +vt 0.22 0.82 0.00 +vt 0.09 0.36 0.00 +vt 0.12 0.39 0.00 +vt 0.08 0.40 0.00 +vt 0.07 0.38 0.00 +vt 0.05 0.40 0.00 +vt 0.19 0.89 0.00 +vt 0.18 0.87 0.00 +vt 0.20 0.85 0.00 +vt 0.18 0.85 0.00 +vt 0.18 0.82 0.00 +vt 0.22 0.83 0.00 +vt 0.16 0.76 0.00 +vt 0.03 0.43 0.00 +vt 0.16 0.91 0.00 +vt 0.16 0.48 0.00 +vt 0.14 0.43 0.00 +vt 0.74 0.42 0.00 +vt 0.71 0.23 0.00 +vt 0.68 0.23 0.00 +vt 0.64 0.28 0.00 +vt 0.65 0.31 0.00 +vt 0.66 0.17 0.00 +vt 0.66 0.38 0.00 +vt 0.67 0.41 0.00 +vt 0.74 0.11 0.00 +vt 0.64 0.22 0.00 +vt 0.85 0.18 0.00 +vt 0.91 0.17 0.00 +vt 0.96 0.17 0.00 +vt 0.80 0.33 0.00 +vt 0.78 0.32 0.00 +vt 0.80 0.32 0.00 +vt 0.83 0.34 0.00 +vt 0.75 0.40 0.00 +vt 0.74 0.39 0.00 +vt 0.74 0.38 0.00 +vt 0.76 0.40 0.00 +vt 0.77 0.41 0.00 +vt 0.78 0.41 0.00 +vt 0.77 0.33 0.00 +vt 0.76 0.32 0.00 +vt 0.58 0.80 0.00 +vt 0.61 0.80 0.00 +vt 0.63 0.76 0.00 +vt 0.65 0.73 0.00 +vt 0.66 0.71 0.00 +vt 0.51 0.71 0.00 +vt 0.31 0.23 0.00 +vt 0.35 0.22 0.00 +vt 0.39 0.23 0.00 +vt 0.29 0.31 0.00 +vt 0.31 0.37 0.00 +vt 0.29 0.36 0.00 +vt 0.03 0.57 0.00 +vt 0.02 0.53 0.00 +vt 0.13 0.73 0.00 +vt 0.09 0.73 0.00 +vt 0.11 0.71 0.00 +vt 0.24 0.37 0.00 +vt 0.21 0.35 0.00 +vt 0.24 0.36 0.00 +vt 0.66 0.86 0.00 +vt 0.67 0.91 0.00 +vt 0.56 0.84 0.00 +vt 0.58 0.84 0.00 +vt 0.61 0.84 0.00 +vt 0.62 0.76 0.00 +vt 0.35 0.21 0.00 +vt 0.39 0.22 0.00 +vt 0.28 0.31 0.00 +vt 0.14 0.94 0.00 +vt 0.07 0.37 0.00 +vt 0.01 0.53 0.00 +vt 0.02 0.57 0.00 +vt 0.01 0.49 0.00 +vt 0.04 0.40 0.00 +vt 0.68 0.51 0.00 +vt 0.72 0.54 0.00 +vt 0.65 0.56 0.00 +vt 0.70 0.59 0.00 +vt 0.69 0.60 0.00 +vt 0.68 0.62 0.00 +vt 0.66 0.49 0.00 +vt 0.06 0.88 0.00 +vt 0.06 0.83 0.00 +vt 0.28 0.69 0.00 +vt 0.22 0.68 0.00 +vt 0.23 0.61 0.00 +vt 0.29 0.61 0.00 +vt 0.24 0.77 0.00 +vt 0.23 0.78 0.00 +vt 0.20 0.68 0.00 +vt 0.28 0.76 0.00 +vt 0.35 0.71 0.00 +vt 0.35 0.77 0.00 +vt 0.40 0.73 0.00 +vt 0.38 0.79 0.00 +vt 0.41 0.55 0.00 +vt 0.42 0.59 0.00 +vt 0.36 0.57 0.00 +vt 0.36 0.53 0.00 +vt 0.36 0.64 0.00 +vt 0.41 0.66 0.00 +vt 0.35 0.82 0.00 +vt 0.30 0.81 0.00 +vt 0.36 0.85 0.00 +vt 0.30 0.85 0.00 +vt 0.33 0.51 0.00 +vt 0.33 0.49 0.00 +vt 0.36 0.50 0.00 +vt 0.41 0.49 0.00 +vt 0.41 0.54 0.00 +vt 0.37 0.46 0.00 +vt 0.34 0.45 0.00 +vt 0.41 0.45 0.00 +vt 0.31 0.55 0.00 +vt 0.50 0.53 0.00 +vt 0.46 0.54 0.00 +vt 0.46 0.50 0.00 +vt 0.50 0.50 0.00 +vt 0.26 0.43 0.00 +vt 0.29 0.44 0.00 +vt 0.28 0.48 0.00 +vt 0.24 0.46 0.00 +vt 0.46 0.46 0.00 +vt 0.48 0.46 0.00 +vt 0.28 0.50 0.00 +vt 0.23 0.46 0.00 +vt 0.26 0.53 0.00 +vt 0.21 0.51 0.00 +vt 0.50 0.59 0.00 +vt 0.46 0.60 0.00 +vt 0.18 0.50 0.00 +vt 0.18 0.61 0.00 +vt 0.41 0.80 0.00 +vt 0.44 0.72 0.00 +vt 0.46 0.66 0.00 +vt 0.33 0.40 0.00 +vt 0.35 0.40 0.00 +vt 0.38 0.39 0.00 +vt 0.41 0.39 0.00 +vt 0.27 0.38 0.00 +vt 0.29 0.39 0.00 +vt 0.31 0.40 0.00 +vt 0.47 0.40 0.00 +vt 0.44 0.39 0.00 +vt 0.89 0.71 0.00 +vt 0.92 0.71 0.00 +vt 0.92 0.74 0.00 +vt 0.90 0.74 0.00 +vt 0.94 0.70 0.00 +vt 0.94 0.73 0.00 +vt 0.97 0.70 0.00 +vt 0.76 0.70 0.00 +vt 0.80 0.69 0.00 +vt 0.79 0.71 0.00 +vt 0.82 0.72 0.00 +vt 0.83 0.68 0.00 +vt 0.93 0.77 0.00 +vt 0.90 0.77 0.00 +vt 0.96 0.76 0.00 +vt 0.99 0.72 0.00 +vt 0.75 0.74 0.00 +vt 0.71 0.69 0.00 +vt 0.72 0.68 0.00 +vt 0.78 0.76 0.00 +vt 0.81 0.76 0.00 +vt 0.85 0.72 0.00 +vt 0.84 0.77 0.00 +vt 0.90 0.85 0.00 +vt 0.90 0.81 0.00 +vt 0.93 0.81 0.00 +vt 0.91 0.91 0.00 +vt 0.79 0.88 0.00 +vt 0.80 0.92 0.00 +vt 0.78 0.88 0.00 +vt 0.83 0.87 0.00 +vt 0.84 0.92 0.00 +vt 0.85 0.68 0.00 +vt 0.89 0.68 0.00 +vt 0.92 0.68 0.00 +vt 0.90 0.57 0.00 +vt 0.93 0.56 0.00 +vt 0.93 0.58 0.00 +vt 0.90 0.58 0.00 +vt 0.95 0.57 0.00 +vt 0.94 0.58 0.00 +vt 0.77 0.57 0.00 +vt 0.78 0.56 0.00 +vt 0.80 0.57 0.00 +vt 0.80 0.58 0.00 +vt 0.83 0.82 0.00 +vt 0.75 0.84 0.00 +vt 0.77 0.81 0.00 +vt 0.72 0.81 0.00 +vt 0.74 0.78 0.00 +vt 0.80 0.81 0.00 +vt 0.72 0.72 0.00 +vt 0.81 0.65 0.00 +vt 0.80 0.66 0.00 +vt 0.78 0.65 0.00 +vt 0.79 0.64 0.00 +vt 0.83 0.65 0.00 +vt 0.83 0.66 0.00 +vt 0.85 0.66 0.00 +vt 0.85 0.65 0.00 +vt 0.91 0.65 0.00 +vt 0.88 0.65 0.00 +vt 0.88 0.64 0.00 +vt 0.91 0.64 0.00 +vt 0.85 0.64 0.00 +vt 0.94 0.68 0.00 +vt 0.75 0.63 0.00 +vt 0.94 0.62 0.00 +vt 0.91 0.62 0.00 +vt 0.95 0.62 0.00 +vt 0.76 0.61 0.00 +vt 0.79 0.62 0.00 +vt 0.82 0.58 0.00 +vt 0.82 0.59 0.00 +vt 0.84 0.59 0.00 +vt 0.84 0.58 0.00 +vt 0.86 0.60 0.00 +vt 0.86 0.58 0.00 +vt 0.88 0.59 0.00 +vt 0.88 0.57 0.00 +vt 0.10 0.09 0.00 +vt 0.06 0.07 0.00 +vt 0.06 0.05 0.00 +vt 0.10 0.07 0.00 +vt 0.14 0.03 0.00 +vt 0.14 0.08 0.00 +vt 0.14 0.11 0.00 +vt 0.18 0.10 0.00 +vt 0.18 0.13 0.00 +vt 0.15 0.15 0.00 +vt 0.32 0.03 0.00 +vt 0.38 0.01 0.00 +vt 0.40 0.05 0.00 +vt 0.33 0.08 0.00 +vt 0.07 0.01 0.00 +vt 0.10 0.02 0.00 +vt 0.22 0.04 0.00 +vt 0.26 0.04 0.00 +vt 0.27 0.10 0.00 +vt 0.22 0.11 0.00 +vt 0.17 0.04 0.00 +vt 0.30 0.15 0.00 +vt 0.36 0.13 0.00 +vt 0.43 0.10 0.00 +vt 0.08 0.28 0.00 +vt 0.07 0.29 0.00 +vt 0.06 0.29 0.00 +vt 0.07 0.27 0.00 +vt 0.06 0.26 0.00 +vt 0.04 0.27 0.00 +vt 0.04 0.25 0.00 +vt 0.06 0.25 0.00 +vt 0.07 0.23 0.00 +vt 0.05 0.24 0.00 +vt 0.05 0.23 0.00 +vt 0.06 0.22 0.00 +vt 0.04 0.23 0.00 +vt 0.05 0.22 0.00 +vt 0.07 0.26 0.00 +vt 0.09 0.18 0.00 +vt 0.10 0.20 0.00 +vt 0.09 0.20 0.00 +vt 0.07 0.19 0.00 +vt 0.23 0.40 0.00 +vt 0.18 0.39 0.00 +vt 0.12 0.13 0.00 +vt 0.17 0.21 0.00 +vt 0.15 0.18 0.00 +vt 0.18 0.17 0.00 +vt 0.19 0.19 0.00 +vt 0.09 0.32 0.00 +vt 0.08 0.30 0.00 +vt 0.09 0.29 0.00 +vt 0.10 0.30 0.00 +vt 0.02 0.21 0.00 +vt 0.03 0.21 0.00 +vt 0.02 0.22 0.00 +vt 0.11 0.21 0.00 +vt 0.09 0.22 0.00 +vt 0.11 0.29 0.00 +vt 0.10 0.28 0.00 +vt 0.18 0.34 0.00 +vt 0.15 0.37 0.00 +vt 0.14 0.28 0.00 +vt 0.15 0.26 0.00 +vt 0.16 0.26 0.00 +vt 0.15 0.28 0.00 +vt 0.03 0.22 0.00 +vt 0.04 0.21 0.00 +vt 0.42 0.25 0.00 +vt 0.40 0.21 0.00 +vt 0.46 0.20 0.00 +vt 0.47 0.25 0.00 +vt 0.04 0.16 0.00 +vt 0.06 0.15 0.00 +vt 0.08 0.16 0.00 +vt 0.06 0.17 0.00 +vt 0.13 0.16 0.00 +vt 0.11 0.17 0.00 +vt 0.10 0.15 0.00 +vt 0.09 0.11 0.00 +vt 0.08 0.13 0.00 +vt 0.05 0.28 0.00 +vt 0.17 0.27 0.00 +vt 0.16 0.30 0.00 +vt 0.14 0.29 0.00 +vt 0.19 0.31 0.00 +vt 0.20 0.28 0.00 +vt 0.14 0.32 0.00 +vt 0.12 0.31 0.00 +vt 0.18 0.25 0.00 +vt 0.20 0.25 0.00 +vt 0.26 0.28 0.00 +vt 0.23 0.28 0.00 +vt 0.23 0.24 0.00 +vt 0.26 0.24 0.00 +vt 0.03 0.17 0.00 +vt 0.02 0.17 0.00 +vt 0.02 0.15 0.00 +vt 0.03 0.13 0.00 +vt 0.04 0.11 0.00 +vt 0.05 0.09 0.00 +vt 0.12 0.19 0.00 +vt 0.47 0.32 0.00 +vt 0.45 0.31 0.00 +vt 0.44 0.29 0.00 +vt 0.22 0.31 0.00 +vt 0.28 0.27 0.00 +vt 0.27 0.23 0.00 +vt 0.12 0.35 0.00 +vt 0.09 0.27 0.00 +vt 0.18 0.24 0.00 +vt 0.16 0.25 0.00 +vt 0.10 0.33 0.00 +vt 0.09 0.24 0.00 +vt 0.08 0.26 0.00 +vt 0.06 0.24 0.00 +vt 0.10 0.26 0.00 +vt 0.13 0.29 0.00 +vt 0.13 0.28 0.00 +vt 0.14 0.27 0.00 +vt 0.11 0.23 0.00 +vt 0.12 0.24 0.00 +vt 0.11 0.25 0.00 +vt 0.39 0.18 0.00 +vt 0.45 0.16 0.00 +vt 0.26 0.16 0.00 +vt 0.31 0.18 0.00 +vt 0.26 0.18 0.00 +vt 0.04 0.20 0.00 +vt 0.04 0.18 0.00 +vt 0.04 0.24 0.00 +vt 0.03 0.24 0.00 +vt 0.03 0.23 0.00 +vt 0.03 0.20 0.00 +vt 0.11 0.27 0.00 +vt 0.12 0.28 0.00 +vt 0.14 0.24 0.00 +vt 0.14 0.23 0.00 +vt 0.14 0.21 0.00 +vt 0.15 0.24 0.00 +vt 0.22 0.15 0.00 +vt 0.22 0.18 0.00 +vt 0.26 0.19 0.00 +vt 0.23 0.21 0.00 +vt 0.20 0.22 0.00 +vt 0.01 0.19 0.00 +vt 0.03 0.18 0.00 +vt 0.25 0.30 0.00 +vt 0.24 0.34 0.00 +vt 0.38 0.21 0.00 +vt 0.75 0.35 0.00 +vt 0.74 0.35 0.00 +vt 0.73 0.37 0.00 +vt 0.74 0.37 0.00 +vt 0.80 0.12 0.00 +vt 0.80 0.13 0.00 +vt 0.80 0.14 0.00 +vt 0.79 0.12 0.00 +vt 0.79 0.10 0.00 +vt 0.80 0.11 0.00 +vt 0.81 0.11 0.00 +vt 0.80 0.09 0.00 +vt 0.82 0.10 0.00 +vt 0.83 0.10 0.00 +vt 0.84 0.11 0.00 +vt 0.85 0.10 0.00 +vt 0.86 0.12 0.00 +vt 0.84 0.12 0.00 +vt 0.86 0.13 0.00 +vt 0.86 0.14 0.00 +vt 0.85 0.15 0.00 +vt 0.84 0.14 0.00 +vt 0.84 0.13 0.00 +vt 0.83 0.15 0.00 +vt 0.81 0.14 0.00 +vt 0.81 0.15 0.00 +vt 0.82 0.13 0.00 +vt 0.13 0.56 0.00 +vt 0.52 0.66 0.00 +vt 0.81 0.63 0.00 +vt 0.83 0.64 0.00 +vt 0.88 0.63 0.00 +vt 0.94 0.63 0.00 +vt 0.95 0.63 0.00 +vt 0.75 0.62 0.00 +vt 0.94 0.64 0.00 +vt 0.95 0.65 0.00 +vt 0.08 0.75 0.00 +vt 0.58 0.13 0.00 +vt 0.46 0.85 0.00 +vt 0.44 0.89 0.00 +vt 0.40 0.88 0.00 +vt 0.41 0.83 0.00 +vt 0.37 0.90 0.00 +vt 0.36 0.86 0.00 +vt 0.32 0.92 0.00 +vt 0.29 0.92 0.00 +vt 0.32 0.87 0.00 +vt 0.44 0.95 0.00 +vt 0.39 0.95 0.00 +vt 0.37 0.99 0.00 +vt 0.32 0.97 0.00 +vt 0.28 0.98 0.00 +vt 0.28 0.91 0.00 +vt 0.31 0.86 0.00 +vt 0.26 0.94 0.00 +vt 0.48 0.89 0.00 +vt 0.46 0.82 0.00 +# 615 texture coords + +g CatBombay +f 1/1/1 2/2/2 3/3/3 +f 3/3/3 4/4/4 1/1/1 +f 5/5/5 6/6/6 7/7/7 +f 7/7/7 8/8/8 5/5/5 +f 9/9/9 10/10/10 11/11/11 +f 11/11/11 12/12/12 9/9/9 +f 13/13/13 4/4/4 3/3/3 +f 3/3/3 14/14/14 13/13/13 +f 15/15/15 13/13/13 14/14/14 +f 14/14/14 16/16/16 15/15/15 +f 17/17/17 18/18/18 19/19/19 +f 19/19/19 20/20/20 17/17/17 +f 2/2/2 21/21/21 22/22/22 +f 22/22/22 3/3/3 2/2/2 +f 23/23/23 24/24/24 15/15/15 +f 15/15/15 16/16/16 23/23/23 +f 25/25/25 24/24/24 23/23/23 +f 23/23/23 26/26/26 25/25/25 +f 27/27/27 28/28/28 19/19/19 +f 19/19/19 18/18/18 27/27/27 +f 29/29/29 22/22/22 30/30/30 +f 30/30/30 31/31/31 29/29/29 +f 32/32/32 33/33/33 34/34/34 +f 34/34/34 35/35/35 32/32/32 +f 35/35/35 36/36/36 37/37/37 +f 37/37/37 32/32/32 35/35/35 +f 36/36/36 38/38/38 39/39/39 +f 39/39/39 37/37/37 36/36/36 +f 40/40/40 41/41/41 39/39/39 +f 39/39/39 38/38/38 40/40/40 +f 42/42/42 43/43/43 41/41/41 +f 41/41/41 40/40/40 42/42/42 +f 42/42/42 44/44/44 43/43/43 +f 45/45/45 46/46/46 47/47/47 +f 47/47/47 48/48/48 45/45/45 +f 33/33/33 46/46/46 49/49/49 +f 49/49/49 34/34/34 33/33/33 +f 50/50/50 51/51/51 52/52/52 +f 52/52/52 53/53/53 50/50/50 +f 54/54/54 55/55/55 56/56/56 +f 56/56/56 53/57/53 54/54/54 +f 53/57/53 52/58/52 54/54/54 +f 56/56/56 55/55/55 57/59/57 +f 57/59/57 58/60/58 56/56/56 +f 59/61/59 60/62/60 61/63/61 +f 61/63/61 62/64/62 59/61/59 +f 63/65/63 64/66/64 59/61/59 +f 59/61/59 62/64/62 63/65/63 +f 65/67/65 64/66/64 63/65/63 +f 63/65/63 66/68/66 65/67/65 +f 67/69/67 68/70/68 69/71/69 +f 69/71/69 70/72/70 67/69/67 +f 71/73/71 51/51/51 50/50/50 +f 50/50/50 72/74/72 71/73/71 +f 73/75/73 74/76/74 57/77/57 +f 57/77/57 55/78/55 73/75/73 +f 75/79/75 58/80/58 57/77/57 +f 57/77/57 74/76/74 75/79/75 +f 76/81/76 77/82/77 66/68/66 +f 66/68/66 63/65/63 76/81/76 +f 63/65/63 62/64/62 76/81/76 +f 70/72/70 69/71/69 74/76/74 +f 74/76/74 73/75/73 70/72/70 +f 78/83/78 79/84/79 26/26/26 +f 26/26/26 23/23/23 78/83/78 +f 80/85/80 81/86/81 79/84/79 +f 79/84/79 78/83/78 80/85/80 +f 82/87/82 83/88/83 12/12/12 +f 12/12/12 11/11/11 82/87/82 +f 22/22/22 29/29/29 14/14/14 +f 14/14/14 3/3/3 22/22/22 +f 84/89/84 85/90/85 13/13/13 +f 13/13/13 15/15/15 84/89/84 +f 85/90/85 86/91/86 4/4/4 +f 4/4/4 13/13/13 85/90/85 +f 86/91/86 87/92/87 1/1/1 +f 1/1/1 4/4/4 86/91/86 +f 88/93/88 89/94/89 10/10/10 +f 10/10/10 9/9/9 88/93/88 +f 82/87/82 28/28/28 90/95/90 +f 90/95/90 83/88/83 82/87/82 +f 91/96/91 6/6/6 5/5/5 +f 5/5/5 92/97/92 91/96/91 +f 93/98/93 94/99/94 95/100/95 +f 95/100/95 96/101/96 93/98/93 +f 97/102/97 94/99/94 98/103/98 +f 98/103/98 99/104/99 97/102/97 +f 98/103/98 94/99/94 93/98/93 +f 93/98/93 100/105/100 98/103/98 +f 101/106/101 102/107/102 103/108/103 +f 103/108/103 104/109/104 101/106/101 +f 103/108/103 102/107/102 105/110/105 +f 105/110/105 106/111/106 103/108/103 +f 93/98/93 96/101/96 105/110/105 +f 105/110/105 102/107/102 93/98/93 +f 94/99/94 97/102/97 107/112/107 +f 107/112/107 95/100/95 94/99/94 +f 107/112/107 97/102/97 108/113/108 +f 108/113/108 109/114/109 107/112/107 +f 99/104/99 110/115/110 108/113/108 +f 108/113/108 97/102/97 99/104/99 +f 101/106/101 100/105/100 93/98/93 +f 93/98/93 102/107/102 101/106/101 +f 104/109/104 103/108/103 111/116/111 +f 111/116/111 88/117/88 104/109/104 +f 103/108/103 106/111/106 91/118/91 +f 91/118/91 111/116/111 103/108/103 +f 112/119/112 113/120/113 114/121/114 +f 114/121/114 115/122/115 112/119/112 +f 116/123/116 117/124/117 31/31/31 +f 31/31/31 30/30/30 116/123/116 +f 118/125/118 116/123/116 30/30/30 +f 30/30/30 119/126/119 118/125/118 +f 120/127/120 31/31/31 117/124/117 +f 117/124/117 121/128/121 120/127/120 +f 91/96/91 92/97/92 111/129/111 +f 89/94/89 88/93/88 111/129/111 +f 89/94/89 111/129/111 92/97/92 +f 92/97/92 5/5/5 89/94/89 +f 21/21/21 119/126/119 30/30/30 +f 30/30/30 22/22/22 21/21/21 +f 116/123/116 118/125/118 122/130/122 +f 122/130/122 123/131/123 116/123/116 +f 115/122/115 124/132/124 125/133/125 +f 125/133/125 112/119/112 115/122/115 +f 126/134/126 127/135/127 128/136/128 +f 128/136/128 129/137/129 126/134/126 +f 120/127/120 121/128/121 129/137/129 +f 129/137/129 128/136/128 120/127/120 +f 116/123/116 123/131/123 130/138/130 +f 130/138/130 117/124/117 116/123/116 +f 117/124/117 130/138/130 131/139/131 +f 131/139/131 132/140/132 121/128/121 +f 121/128/121 117/124/117 131/139/131 +f 132/140/132 129/137/129 121/128/121 +f 132/140/132 133/141/133 126/134/126 +f 126/134/126 129/137/129 132/140/132 +f 134/142/125 125/133/125 124/132/124 +f 124/132/124 135/143/134 134/142/125 +f 136/144/135 137/145/136 138/146/137 +f 138/146/137 139/147/138 136/144/135 +f 140/148/139 137/145/136 136/144/135 +f 136/144/135 141/149/140 140/148/139 +f 142/150/141 143/151/142 144/152/143 +f 145/153/144 146/154/145 142/150/141 +f 142/150/141 144/152/143 145/153/144 +f 144/152/143 147/155/146 145/153/144 +f 148/156/147 149/157/148 150/158/149 +f 150/158/149 151/159/150 148/156/147 +f 152/160/151 153/161/152 154/162/153 +f 155/163/154 156/164/155 157/165/156 +f 157/165/156 158/166/157 155/163/154 +f 146/154/145 145/153/144 159/167/158 +f 159/167/158 160/168/159 146/154/145 +f 161/169/160 159/167/158 145/153/144 +f 145/153/144 162/170/161 161/169/160 +f 163/171/162 164/172/163 149/157/148 +f 149/157/148 148/156/147 163/171/162 +f 165/173/164 163/171/162 166/174/165 +f 166/174/165 167/175/166 165/173/164 +f 155/163/154 168/176/167 169/177/168 +f 169/177/168 156/164/155 155/163/154 +f 170/178/169 171/179/170 172/180/171 +f 172/181/172 171/182/173 173/183/174 +f 173/183/174 174/184/175 172/181/172 +f 173/183/174 175/185/176 174/184/175 +f 176/186/177 177/187/178 178/188/179 +f 179/189/180 180/190/181 181/191/182 +f 181/191/182 178/188/179 179/189/180 +f 178/188/179 177/187/178 179/189/180 +f 181/191/182 180/190/181 170/178/169 +f 170/178/169 172/180/171 181/191/182 +f 170/178/169 155/163/154 158/166/157 +f 158/166/157 182/192/183 171/179/170 +f 171/179/170 170/178/169 158/166/157 +f 173/183/174 171/182/173 160/168/159 +f 160/168/159 159/167/158 173/183/174 +f 159/167/158 183/193/184 175/185/176 +f 175/185/176 173/183/174 159/167/158 +f 165/173/164 167/175/166 177/187/178 +f 177/187/178 176/186/177 165/173/164 +f 176/186/177 184/194/185 165/173/164 +f 167/175/166 179/189/180 177/187/178 +f 170/178/169 180/190/181 168/176/167 +f 168/176/167 155/163/154 170/178/169 +f 143/151/142 142/150/141 146/154/145 +f 146/154/145 185/195/186 143/151/142 +f 160/168/159 182/196/187 185/195/186 +f 185/195/186 146/154/145 160/168/159 +f 160/168/159 171/182/173 182/196/187 +f 114/121/114 81/86/81 80/85/80 +f 80/85/80 186/197/188 114/121/114 +f 18/18/18 17/17/17 84/89/84 +f 84/89/84 187/198/189 18/18/18 +f 27/27/27 18/18/18 187/198/189 +f 187/198/189 188/199/190 27/27/27 +f 189/200/191 25/25/25 26/26/26 +f 26/26/26 190/201/192 189/200/191 +f 28/28/28 27/27/27 191/202/193 +f 191/202/193 90/95/90 28/28/28 +f 190/201/192 26/26/26 79/84/79 +f 79/84/79 192/203/194 190/201/192 +f 192/203/194 79/84/79 81/86/81 +f 81/86/81 193/204/195 192/203/194 +f 19/19/19 28/28/28 82/87/82 +f 82/87/82 194/205/196 19/19/19 +f 115/122/115 114/121/114 186/197/188 +f 186/197/188 127/135/127 115/122/115 +f 124/132/124 115/122/115 127/135/127 +f 127/135/127 126/134/126 124/132/124 +f 113/120/113 193/204/195 81/86/81 +f 81/86/81 114/121/114 113/120/113 +f 195/206/197 188/199/190 25/25/25 +f 25/25/25 189/200/191 195/206/197 +f 133/141/133 135/143/134 124/132/124 +f 124/132/124 126/134/126 133/141/133 +f 17/17/17 20/20/20 196/207/198 +f 196/207/198 8/8/8 197/208/199 +f 7/7/7 198/209/1 197/208/199 +f 197/208/199 8/8/8 7/7/7 +f 199/210/200 200/211/201 201/211/202 +f 201/211/202 202/212/203 199/210/200 +f 202/212/203 203/213/204 204/213/205 +f 204/213/205 199/210/200 202/212/203 +f 205/214/206 206/215/207 207/216/208 +f 207/216/208 208/217/209 205/214/206 +f 138/146/137 209/218/210 210/219/211 +f 210/219/211 139/147/138 138/146/137 +f 200/211/201 211/220/212 212/221/213 +f 212/221/213 201/211/202 200/211/201 +f 203/213/204 140/148/139 141/149/140 +f 141/149/140 204/213/205 203/213/204 +f 69/71/69 68/70/68 213/222/214 +f 213/222/214 77/82/77 69/71/69 +f 72/74/72 214/223/215 215/224/216 +f 215/224/216 71/73/71 72/74/72 +f 216/225/217 54/226/54 52/52/52 +f 52/52/52 51/51/51 216/225/217 +f 61/63/61 217/227/218 76/81/76 +f 76/81/76 62/64/62 61/63/61 +f 77/82/77 75/79/75 74/76/74 +f 74/76/74 69/71/69 77/82/77 +f 216/225/217 51/51/51 71/73/71 +f 71/73/71 215/224/216 216/225/217 +f 34/34/34 50/50/50 53/53/53 +f 53/53/53 35/35/35 34/34/34 +f 56/56/56 38/228/38 36/229/36 +f 36/229/36 35/230/35 56/56/56 +f 35/230/35 53/57/53 56/56/56 +f 56/56/56 60/231/60 38/228/38 +f 60/62/60 59/61/59 40/40/40 +f 40/40/40 38/38/38 60/62/60 +f 40/40/40 59/61/59 64/66/64 +f 64/66/64 42/42/42 40/40/40 +f 64/66/64 65/67/65 44/44/44 +f 44/44/44 42/42/42 64/66/64 +f 67/69/67 214/223/215 45/45/45 +f 45/45/45 48/48/48 67/69/67 +f 48/48/48 68/70/68 67/69/67 +f 34/34/34 49/49/49 72/74/72 +f 72/74/72 50/50/50 34/34/34 +f 44/44/44 65/67/65 213/222/214 +f 213/222/214 48/48/48 44/44/44 +f 213/222/214 68/70/68 48/48/48 +f 45/45/45 214/223/215 72/74/72 +f 72/74/72 49/49/49 45/45/45 +f 213/222/214 65/67/65 66/68/66 +f 66/68/66 77/82/77 213/222/214 +f 215/224/216 214/223/215 67/69/67 +f 67/69/67 70/72/70 215/224/216 +f 216/225/217 73/75/73 55/78/55 +f 55/78/55 54/226/54 216/225/217 +f 217/227/218 58/80/58 75/79/75 +f 75/79/75 76/81/76 217/227/218 +f 76/81/76 75/79/75 77/82/77 +f 215/224/216 70/72/70 73/75/73 +f 73/75/73 216/225/217 215/224/216 +f 217/232/218 60/231/60 56/56/56 +f 56/56/56 58/60/58 217/232/218 +f 217/232/218 61/233/61 60/231/60 +f 45/45/45 49/49/49 46/46/46 +f 47/47/47 43/43/43 44/44/44 +f 44/44/44 48/48/48 47/47/47 +f 147/155/146 218/234/219 219/235/220 +f 219/235/220 162/170/161 145/153/144 +f 145/153/144 147/155/146 219/235/220 +f 185/236/221 157/165/156 220/237/222 +f 220/237/222 143/238/223 185/236/221 +f 182/192/183 158/166/157 157/165/156 +f 157/165/156 185/236/221 182/192/183 +f 153/239/152 221/240/224 222/241/225 +f 222/241/225 154/239/153 153/239/152 +f 223/32/226 224/33/93 225/242/227 +f 225/242/227 226/243/228 223/32/226 +f 226/243/228 227/36/229 228/37/230 +f 228/37/230 223/32/226 226/243/228 +f 228/37/230 227/36/229 229/38/231 +f 229/38/231 230/39/232 228/37/230 +f 231/40/233 232/41/234 230/39/232 +f 230/39/232 229/38/231 231/40/233 +f 232/41/234 231/40/233 233/244/235 +f 233/244/235 234/244/236 232/41/234 +f 235/245/237 236/47/238 234/244/236 +f 234/244/236 233/244/235 235/245/237 +f 237/47/239 238/246/240 236/47/238 +f 224/33/93 239/46/241 240/49/242 +f 240/49/242 225/242/227 224/33/93 +f 241/50/243 242/51/244 243/52/245 +f 243/52/245 244/53/246 241/50/243 +f 245/54/247 246/55/248 247/56/56 +f 247/56/56 244/57/246 245/54/247 +f 244/57/246 243/58/245 245/54/247 +f 247/56/56 246/55/248 248/59/57 +f 248/59/57 249/60/249 247/56/56 +f 250/64/250 251/61/251 252/62/252 +f 252/62/252 253/63/253 250/64/250 +f 254/65/254 255/66/255 251/61/251 +f 251/61/251 250/64/250 254/65/254 +f 254/65/254 256/68/256 257/67/257 +f 257/67/257 255/66/255 254/65/254 +f 258/70/258 259/71/259 260/72/260 +f 260/72/260 261/69/261 258/70/258 +f 262/73/262 242/51/244 241/50/243 +f 241/50/243 263/74/263 262/73/262 +f 264/75/264 265/76/265 248/77/57 +f 248/77/57 246/78/248 264/75/264 +f 266/79/266 249/80/249 248/77/57 +f 248/77/57 265/76/265 266/79/266 +f 267/81/267 268/82/268 256/68/256 +f 256/68/256 254/65/254 267/81/267 +f 254/65/254 250/64/250 267/81/267 +f 260/72/260 259/71/259 265/76/265 +f 265/76/265 264/75/264 260/72/260 +f 258/70/258 269/222/269 268/82/268 +f 268/82/268 259/71/259 258/70/258 +f 270/223/270 271/247/271 262/73/262 +f 262/73/262 263/74/263 270/223/270 +f 272/225/272 245/226/247 243/52/245 +f 243/52/245 242/51/244 272/225/272 +f 253/63/253 273/227/273 267/81/267 +f 267/81/267 250/64/250 253/63/253 +f 268/82/268 266/79/266 265/76/265 +f 265/76/265 259/71/259 268/82/268 +f 262/73/262 271/247/271 272/225/272 +f 272/225/272 242/51/244 262/73/262 +f 226/243/228 225/242/227 241/50/243 +f 241/50/243 244/53/246 226/243/228 +f 247/56/56 229/228/231 227/248/229 +f 227/248/229 226/249/228 247/56/56 +f 226/249/228 244/57/246 247/56/56 +f 247/56/56 252/250/252 229/228/231 +f 229/38/231 252/62/252 251/61/251 +f 251/61/251 231/40/233 229/38/231 +f 231/40/233 251/61/251 255/66/255 +f 255/66/255 233/244/235 231/40/233 +f 255/66/255 257/67/257 235/245/237 +f 235/245/237 233/244/235 255/66/255 +f 261/69/261 270/223/270 238/246/240 +f 238/246/240 237/47/239 261/69/261 +f 237/47/239 269/222/269 258/70/258 +f 258/70/258 261/69/261 237/47/239 +f 225/242/227 240/49/242 263/74/263 +f 263/74/263 241/50/243 225/242/227 +f 235/245/237 257/67/257 269/222/269 +f 269/222/269 237/47/239 235/245/237 +f 238/246/240 270/223/270 263/74/263 +f 263/74/263 240/49/242 238/246/240 +f 269/222/269 257/67/257 256/68/256 +f 256/68/256 268/82/268 269/222/269 +f 270/223/270 261/69/261 260/72/260 +f 260/72/260 271/247/271 270/223/270 +f 272/225/272 264/75/264 246/78/248 +f 246/78/248 245/226/247 272/225/272 +f 273/227/273 249/80/249 266/79/266 +f 266/79/266 267/81/267 273/227/273 +f 267/81/267 266/79/266 268/82/268 +f 271/247/271 260/72/260 264/75/264 +f 264/75/264 272/225/272 271/247/271 +f 273/232/273 252/250/252 247/56/56 +f 247/56/56 249/60/249 273/232/273 +f 273/232/273 253/233/253 252/250/252 +f 239/46/241 236/47/238 238/246/240 +f 238/246/240 240/49/242 239/46/241 +f 235/245/237 237/47/239 236/47/238 +f 159/167/158 161/169/160 183/193/184 +f 184/194/185 274/251/274 165/173/164 +f 274/251/274 164/172/163 163/171/162 +f 163/171/162 165/173/164 274/251/274 +f 178/252/179 181/181/182 172/181/171 +f 172/181/171 174/184/275 178/252/179 +f 149/253/148 219/235/276 218/234/277 +f 218/234/277 150/254/149 149/253/148 +f 164/255/163 162/170/278 219/235/276 +f 219/235/276 149/253/148 164/255/163 +f 178/252/179 174/184/275 175/185/279 +f 175/185/279 176/256/177 178/252/179 +f 184/193/185 183/193/280 161/169/281 +f 161/169/281 274/169/274 184/193/185 +f 176/256/177 175/185/279 183/193/280 +f 183/193/280 184/193/185 176/256/177 +f 274/169/274 161/169/281 162/170/278 +f 162/170/278 164/255/163 274/169/274 +f 275/257/282 276/258/283 277/259/284 +f 277/259/284 278/260/285 279/261/286 +f 279/261/286 280/262/287 277/259/284 +f 276/258/283 278/260/285 277/259/284 +f 277/259/284 281/263/288 282/263/289 +f 282/263/289 283/257/290 277/259/284 +f 283/257/290 275/257/282 277/259/284 +f 151/159/150 284/264/291 148/156/147 +f 163/171/162 148/156/147 284/264/291 +f 284/264/291 166/174/165 163/171/162 +f 169/177/168 166/174/165 284/264/291 +f 284/264/291 285/265/292 169/177/168 +f 167/175/166 166/174/165 169/177/168 +f 169/177/168 168/176/167 167/175/166 +f 168/176/167 180/190/181 179/189/180 +f 179/189/180 167/175/166 168/176/167 +f 286/266/293 287/267/294 288/268/295 +f 288/268/295 289/269/296 286/266/293 +f 287/267/294 290/270/297 196/271/198 +f 196/271/198 20/272/20 287/267/294 +f 291/273/298 286/266/293 292/274/299 +f 292/274/299 293/275/300 291/273/298 +f 294/276/301 295/277/302 293/275/300 +f 293/275/300 292/274/299 294/276/301 +f 296/278/303 297/279/304 298/280/305 +f 298/280/305 299/281/306 296/278/303 +f 292/274/299 300/282/307 301/283/308 +f 301/283/308 294/276/301 292/274/299 +f 293/275/300 302/284/309 303/285/310 +f 303/285/310 291/273/298 293/275/300 +f 303/285/310 302/284/309 5/286/5 +f 5/286/5 8/287/8 303/285/310 +f 286/266/293 289/269/296 300/282/307 +f 300/282/307 292/274/299 286/266/293 +f 304/288/311 305/289/312 306/290/313 +f 306/290/313 299/281/306 304/288/311 +f 307/291/314 308/292/315 299/281/306 +f 299/281/306 306/290/313 307/291/314 +f 309/293/316 306/290/313 305/289/312 +f 305/289/312 310/294/317 309/293/316 +f 309/293/316 311/295/318 307/291/314 +f 307/291/314 306/290/313 309/293/316 +f 196/271/198 290/270/297 303/285/310 +f 303/285/310 8/287/8 196/271/198 +f 304/288/311 299/281/306 298/280/305 +f 298/280/305 312/296/319 304/288/311 +f 313/297/320 314/298/321 315/299/322 +f 315/299/322 316/300/323 313/297/320 +f 307/291/314 315/299/322 314/298/321 +f 314/298/321 308/292/315 307/291/314 +f 317/301/324 318/302/325 319/303/326 +f 319/303/326 316/304/323 317/301/324 +f 316/300/323 315/299/322 320/305/327 +f 320/305/327 317/306/324 316/300/323 +f 320/305/327 315/299/322 307/291/314 +f 307/291/314 311/295/318 320/305/327 +f 319/303/326 321/307/328 313/308/320 +f 313/308/320 316/304/323 319/303/326 +f 321/307/328 322/309/329 323/310/330 +f 323/310/330 313/308/320 321/307/328 +f 293/275/300 295/277/302 302/284/309 +f 324/311/331 325/312/332 314/298/321 +f 314/298/321 313/297/320 324/311/331 +f 313/308/320 323/310/330 324/313/331 +f 19/314/19 323/310/330 322/309/329 +f 322/309/329 288/268/295 19/314/19 +f 288/268/295 322/309/329 312/296/319 +f 312/296/319 289/269/296 288/268/295 +f 312/296/319 298/280/305 300/282/307 +f 300/282/307 289/269/296 312/296/319 +f 298/280/305 297/279/304 301/283/308 +f 301/283/308 300/282/307 298/280/305 +f 89/315/89 295/277/302 294/276/301 +f 294/276/301 10/316/10 89/315/89 +f 5/286/5 302/284/309 295/277/302 +f 295/277/302 89/315/89 5/286/5 +f 299/281/306 308/292/315 296/278/303 +f 296/278/303 308/292/315 314/298/321 +f 314/298/321 325/312/332 297/279/304 +f 297/279/304 296/278/303 314/298/321 +f 325/312/332 11/317/11 301/283/308 +f 301/283/308 297/279/304 325/312/332 +f 301/283/308 11/317/11 10/316/10 +f 10/316/10 294/276/301 301/283/308 +f 239/318/241 224/319/93 310/294/317 +f 224/319/93 223/320/226 309/293/316 +f 309/293/316 310/294/317 224/319/93 +f 311/295/318 309/293/316 223/320/226 +f 223/320/226 228/321/230 311/295/318 +f 317/301/324 232/322/234 234/323/236 +f 234/323/236 318/302/325 317/301/324 +f 234/323/236 236/324/238 318/302/325 +f 320/305/327 232/325/234 317/306/324 +f 230/326/232 320/305/327 311/295/318 +f 311/295/318 228/321/230 230/326/232 +f 288/268/295 287/267/294 20/272/20 +f 20/272/20 19/314/19 288/268/295 +f 326/327/333 327/328/334 328/329/335 +f 328/329/335 329/330/336 326/327/333 +f 327/328/334 330/331/337 331/332/338 +f 331/332/338 328/329/335 327/328/334 +f 332/333/339 331/332/338 330/331/337 +f 333/334/340 334/335/341 335/336/342 +f 336/337/343 335/336/342 334/335/341 +f 334/335/341 337/338/344 336/337/343 +f 328/329/335 338/339/345 339/340/346 +f 339/340/346 329/330/336 328/329/335 +f 331/332/338 23/341/23 338/339/345 +f 338/339/345 328/329/335 331/332/338 +f 78/342/78 23/341/23 331/332/338 +f 331/332/338 332/333/339 78/342/78 +f 340/343/347 78/344/78 332/345/339 +f 332/345/339 333/334/340 340/343/347 +f 341/346/348 340/343/347 333/334/340 +f 333/334/340 335/336/342 341/346/348 +f 342/347/349 341/346/348 335/336/342 +f 335/336/342 336/337/343 342/347/349 +f 343/348/350 344/349/351 342/347/349 +f 342/347/349 336/337/343 343/348/350 +f 345/350/352 346/351/353 16/352/16 +f 16/352/16 14/353/14 345/350/352 +f 347/354/354 31/355/31 120/356/120 +f 347/354/354 348/357/355 29/358/29 +f 29/358/29 31/355/31 347/354/354 +f 349/359/356 343/348/350 336/337/343 +f 336/337/343 337/338/344 349/359/356 +f 326/327/333 350/360/357 351/360/358 +f 351/360/358 352/361/359 327/328/334 +f 327/328/334 326/327/333 351/360/358 +f 37/362/37 39/363/39 353/364/360 +f 353/364/360 354/365/361 37/362/37 +f 353/364/360 39/363/39 41/366/41 +f 41/366/41 355/367/362 353/364/360 +f 355/368/362 41/369/41 43/370/43 +f 43/370/43 356/371/363 355/368/362 +f 349/359/356 350/360/357 326/327/333 +f 326/327/333 343/348/350 349/359/356 +f 326/327/333 329/330/336 343/348/350 +f 346/351/353 357/372/364 344/349/351 +f 344/349/351 339/340/346 346/351/353 +f 14/353/14 29/358/29 348/357/355 +f 348/357/355 345/350/352 14/353/14 +f 128/373/128 358/374/365 120/356/120 +f 358/374/365 128/373/128 127/375/127 +f 127/375/127 186/376/188 358/374/365 +f 120/356/120 358/374/365 359/377/366 +f 359/377/366 347/354/354 120/356/120 +f 357/372/364 348/357/355 347/354/354 +f 347/354/354 359/377/366 357/372/364 +f 357/372/364 346/351/353 345/350/352 +f 345/350/352 348/357/355 357/372/364 +f 339/340/346 338/339/345 16/352/16 +f 16/352/16 346/351/353 339/340/346 +f 23/341/23 16/352/16 338/339/345 +f 340/343/347 80/378/80 78/344/78 +f 186/376/188 340/343/347 341/346/348 +f 341/346/348 358/374/365 186/376/188 +f 358/374/365 341/346/348 342/347/349 +f 342/347/349 359/377/366 358/374/365 +f 359/377/366 342/347/349 344/349/351 +f 344/349/351 357/372/364 359/377/366 +f 329/330/336 339/340/346 344/349/351 +f 344/349/351 343/348/350 329/330/336 +f 340/343/347 186/376/188 80/378/80 +f 360/379/367 361/380/368 362/381/369 +f 362/381/369 363/382/370 360/379/367 +f 364/383/371 365/384/372 361/380/368 +f 361/380/368 360/379/367 364/383/371 +f 366/385/373 365/384/372 364/383/371 +f 364/383/371 367/386/374 366/385/373 +f 368/387/375 369/388/376 370/389/377 +f 370/389/377 371/390/378 368/387/375 +f 367/386/374 372/391/379 370/389/377 +f 370/389/377 369/388/376 367/386/374 +f 369/388/376 366/385/373 367/386/374 +f 352/361/359 373/392/380 330/331/337 +f 330/331/337 327/328/334 352/361/359 +f 332/333/339 330/331/337 373/392/380 +f 332/345/339 374/393/381 362/381/369 +f 362/381/369 333/334/340 332/345/339 +f 354/365/361 353/364/360 375/394/382 +f 375/394/382 376/395/383 354/365/361 +f 375/394/382 353/364/360 355/367/362 +f 355/367/362 377/396/384 375/394/382 +f 377/397/384 355/368/362 356/371/363 +f 356/371/363 378/398/385 377/397/384 +f 47/399/47 379/400/386 356/371/363 +f 356/371/363 43/370/43 47/399/47 +f 380/401/387 379/400/386 47/399/47 +f 47/399/47 46/402/46 380/401/387 +f 381/403/388 380/401/387 46/402/46 +f 46/402/46 33/404/33 381/403/388 +f 354/365/361 382/405/389 32/406/32 +f 32/406/32 37/362/37 354/365/361 +f 382/405/389 381/403/388 33/404/33 +f 33/404/33 32/406/32 382/405/389 +f 383/407/390 384/408/391 385/409/392 +f 385/409/392 386/410/393 383/407/390 +f 386/410/393 133/411/133 387/412/394 +f 387/412/394 383/407/390 386/410/393 +f 387/412/394 388/413/395 383/407/390 +f 388/413/395 387/412/394 389/414/396 +f 390/415/397 391/416/398 388/413/395 +f 388/413/395 389/414/396 390/415/397 +f 123/417/123 122/418/122 392/419/399 +f 392/419/399 393/420/400 123/417/123 +f 134/421/125 135/422/134 386/410/393 +f 386/410/393 385/409/392 134/421/125 +f 131/423/131 130/424/130 394/425/401 +f 394/425/401 395/426/402 131/423/131 +f 133/411/133 132/427/132 389/414/396 +f 389/414/396 387/412/394 133/411/133 +f 394/425/401 130/424/130 123/417/123 +f 123/417/123 393/420/400 394/425/401 +f 132/427/132 131/423/131 395/426/402 +f 395/426/402 389/414/396 132/427/132 +f 396/428/403 394/425/401 393/420/400 +f 393/420/400 397/429/404 396/428/403 +f 392/419/399 398/430/405 397/429/404 +f 397/429/404 393/420/400 392/419/399 +f 399/431/406 400/432/407 401/433/408 +f 401/433/408 402/434/409 399/431/406 +f 403/435/410 404/436/411 405/437/412 +f 405/437/412 406/438/413 403/435/410 +f 405/437/412 407/437/414 406/438/413 +f 408/439/415 409/440/416 410/441/175 +f 410/441/175 411/442/417 408/439/415 +f 411/442/417 410/441/175 412/443/418 +f 412/443/418 413/444/419 411/442/417 +f 403/435/410 406/438/413 414/445/420 +f 414/445/420 402/434/409 403/435/410 +f 415/446/421 416/447/422 417/448/423 +f 417/448/423 276/449/424 415/446/421 +f 221/240/224 153/239/152 418/450/425 +f 418/450/425 419/451/426 221/240/224 +f 391/416/398 420/452/427 388/413/395 +f 421/453/428 422/454/429 423/455/430 +f 423/455/430 424/456/293 421/453/428 +f 425/457/431 426/458/432 427/459/433 +f 427/459/433 428/460/434 425/457/431 +f 429/461/435 430/462/436 282/461/437 +f 282/461/437 281/463/438 429/461/435 +f 416/447/422 431/464/439 432/465/440 +f 432/465/440 417/448/423 416/447/422 +f 433/466/441 428/460/434 427/459/433 +f 427/459/433 434/467/442 433/466/441 +f 435/468/443 221/240/224 419/451/426 +f 419/451/426 436/469/444 435/468/443 +f 437/470/445 438/471/446 439/472/447 +f 439/472/447 440/473/448 437/470/445 +f 411/442/417 413/444/419 276/449/424 +f 276/449/424 417/448/423 411/442/417 +f 432/465/440 408/439/415 411/442/417 +f 411/442/417 417/448/423 432/465/440 +f 413/444/419 412/443/418 279/474/449 +f 279/474/449 278/475/450 413/444/419 +f 285/476/292 441/477/451 442/478/452 +f 442/478/452 443/479/453 285/476/292 +f 444/480/454 445/481/455 446/482/456 +f 446/482/456 447/483/457 444/480/454 +f 448/484/458 449/485/459 450/486/460 +f 450/486/460 420/452/427 448/484/458 +f 388/413/395 420/452/427 451/487/461 +f 451/487/461 383/407/390 388/413/395 +f 447/483/457 446/482/456 415/446/421 +f 415/446/421 276/449/424 447/483/457 +f 450/486/460 452/488/462 451/487/461 +f 451/487/461 420/452/427 450/486/460 +f 453/489/463 403/435/410 402/434/409 +f 402/434/409 401/433/408 453/489/463 +f 440/473/448 439/472/447 454/490/464 +f 454/490/464 455/491/465 440/473/448 +f 455/491/465 456/492/466 440/473/448 +f 457/493/467 455/491/465 454/490/464 +f 454/490/464 458/494/468 457/493/467 +f 459/495/469 460/496/470 456/492/466 +f 456/492/466 455/491/465 459/495/469 +f 454/490/464 461/497/471 462/498/472 +f 462/498/472 458/494/468 454/490/464 +f 463/499/473 464/500/474 465/501/475 +f 465/501/475 466/502/476 463/499/473 +f 467/503/477 468/504/478 469/505/479 +f 469/505/479 444/480/454 467/503/477 +f 445/481/455 444/480/454 469/505/479 +f 469/505/479 470/506/480 445/481/455 +f 445/481/455 470/506/480 471/507/481 +f 471/507/481 452/488/462 445/481/455 +f 383/407/390 451/487/461 472/508/482 +f 472/508/482 384/408/391 383/407/390 +f 473/509/483 449/485/459 448/484/458 +f 448/484/458 422/454/429 473/509/483 +f 443/479/453 418/510/425 153/511/152 +f 153/511/152 152/512/151 443/479/453 +f 221/240/224 435/468/443 457/493/467 +f 457/493/467 474/513/484 221/240/224 +f 144/514/485 463/499/473 466/502/476 +f 466/502/476 147/515/486 144/514/485 +f 435/468/443 459/495/469 455/491/465 +f 455/491/465 457/493/467 435/468/443 +f 436/469/444 475/516/487 459/495/469 +f 459/495/469 435/468/443 436/469/444 +f 476/517/488 399/431/406 402/434/409 +f 402/434/409 414/445/420 476/517/488 +f 477/518/489 461/497/471 478/519/490 +f 478/519/490 479/519/491 477/518/489 +f 459/495/469 475/516/487 480/520/435 +f 480/520/435 460/496/470 459/495/469 +f 481/521/492 482/522/493 406/438/413 +f 406/438/413 483/523/494 481/521/492 +f 482/522/493 140/524/495 476/517/488 +f 476/517/488 414/445/420 482/522/493 +f 440/473/448 456/492/466 484/525/496 +f 484/525/496 437/470/445 440/473/448 +f 460/496/470 428/460/434 433/466/441 +f 433/466/441 456/492/466 460/496/470 +f 433/466/441 484/525/496 456/492/466 +f 206/519/497 479/519/491 478/519/490 +f 478/519/490 485/519/498 206/519/497 +f 437/470/445 484/525/496 201/526/499 +f 201/526/499 212/527/500 437/470/445 +f 486/528/501 138/529/502 137/530/503 +f 137/530/503 481/521/492 486/528/501 +f 422/454/429 448/484/458 391/416/398 +f 391/416/398 390/415/397 423/455/430 +f 423/455/430 422/454/429 391/416/398 +f 487/531/504 397/429/404 398/430/405 +f 398/430/405 488/532/505 487/531/504 +f 489/533/506 396/428/403 151/534/150 +f 151/534/150 150/535/149 489/533/506 +f 396/428/403 397/429/404 487/531/504 +f 487/531/504 151/534/150 396/428/403 +f 386/410/393 135/422/134 133/411/133 +f 490/536/507 491/537/508 447/483/457 +f 447/483/457 276/449/424 490/536/507 +f 453/489/463 404/436/411 403/435/410 +f 409/440/416 406/438/413 407/437/414 +f 409/440/416 407/437/414 492/538/509 +f 492/538/509 410/441/175 409/440/416 +f 410/441/175 492/538/509 493/539/510 +f 493/539/510 412/443/418 410/441/175 +f 280/540/511 279/474/449 412/443/418 +f 412/443/418 493/539/510 280/540/511 +f 407/437/414 405/437/412 494/437/512 +f 495/539/513 492/538/509 407/437/414 +f 407/437/414 494/437/512 495/539/513 +f 493/539/510 492/538/509 495/539/513 +f 495/539/513 496/539/514 493/539/510 +f 282/461/437 430/462/436 497/541/515 +f 497/541/515 283/475/516 282/461/437 +f 434/467/442 203/542/517 202/543/518 +f 202/543/518 433/466/441 434/467/442 +f 209/544/519 138/529/502 486/528/501 +f 486/528/501 498/545/520 209/544/519 +f 140/524/495 203/542/517 434/467/442 +f 434/467/442 476/517/488 140/524/495 +f 399/431/406 476/517/488 434/467/442 +f 434/467/442 427/459/433 399/431/406 +f 400/432/407 399/431/406 427/459/433 +f 427/459/433 426/458/432 400/432/407 +f 432/465/440 431/464/439 486/528/501 +f 486/528/501 481/521/492 432/465/440 +f 408/439/415 432/465/440 481/521/492 +f 481/521/492 483/523/494 408/439/415 +f 422/454/429 421/453/428 499/546/521 +f 499/546/521 473/509/483 422/454/429 +f 205/547/522 498/545/520 479/519/491 +f 479/519/491 206/519/497 205/547/522 +f 421/453/428 477/518/489 479/519/491 +f 479/519/491 498/545/520 421/453/428 +f 500/548/523 489/533/506 150/535/149 +f 150/535/149 501/549/524 500/548/523 +f 218/550/277 147/515/486 466/502/476 +f 466/502/476 502/551/525 218/550/277 +f 466/502/476 465/501/475 502/551/525 +f 395/426/402 500/548/523 390/415/397 +f 390/415/397 389/414/396 395/426/402 +f 489/533/506 500/548/523 395/426/402 +f 395/426/402 394/425/401 489/533/506 +f 202/543/518 201/526/499 484/525/496 +f 484/525/496 433/466/441 202/543/518 +f 480/520/435 425/457/431 428/460/434 +f 428/460/434 460/496/470 480/520/435 +f 477/518/489 421/453/428 424/456/293 +f 424/456/293 503/552/526 477/518/489 +f 477/518/489 503/552/526 462/498/472 +f 462/498/472 461/497/471 477/518/489 +f 431/464/439 499/546/521 498/545/520 +f 498/545/520 486/528/501 431/464/439 +f 431/464/439 416/447/422 473/509/483 +f 473/509/483 499/546/521 431/464/439 +f 415/446/421 449/485/459 473/509/483 +f 473/509/483 416/447/422 415/446/421 +f 446/482/456 450/486/460 449/485/459 +f 449/485/459 415/446/421 446/482/456 +f 452/488/462 471/507/481 472/508/482 +f 472/508/482 451/487/461 452/488/462 +f 445/481/455 452/488/462 450/486/460 +f 450/486/460 446/482/456 445/481/455 +f 458/494/468 464/500/474 474/513/484 +f 474/513/484 457/493/467 458/494/468 +f 462/498/472 465/501/475 464/500/474 +f 464/500/474 458/494/468 462/498/472 +f 465/501/475 462/498/472 503/552/526 +f 503/552/526 502/551/525 465/501/475 +f 424/456/293 501/549/524 502/551/525 +f 502/551/525 503/552/526 424/456/293 +f 501/549/524 150/535/149 218/550/277 +f 218/550/277 502/551/525 501/549/524 +f 430/462/436 429/461/435 504/553/527 +f 505/554/528 497/541/515 430/462/436 +f 430/462/436 504/553/527 505/554/528 +f 467/503/477 505/554/528 504/553/527 +f 504/553/527 468/504/478 467/503/477 +f 497/541/515 490/536/507 275/536/529 +f 275/536/529 283/475/516 497/541/515 +f 491/537/508 490/536/507 497/541/515 +f 497/541/515 505/554/528 491/537/508 +f 491/537/508 505/554/528 467/503/477 +f 444/480/454 447/483/457 491/537/508 +f 491/537/508 467/503/477 444/480/454 +f 488/532/505 442/478/452 441/477/451 +f 441/477/451 487/531/504 488/532/505 +f 489/533/506 394/425/401 396/428/403 +f 275/536/529 490/536/507 276/449/424 +f 278/475/450 276/449/424 413/444/419 +f 474/513/484 464/500/474 463/499/473 +f 463/499/473 143/555/223 474/513/484 +f 409/440/416 483/523/494 406/438/413 +f 140/524/495 482/522/493 481/521/492 +f 481/521/492 137/530/503 140/524/495 +f 205/547/522 209/544/519 498/545/520 +f 391/416/398 448/484/458 420/452/427 +f 493/539/510 496/539/514 280/540/511 +f 482/522/493 414/445/420 406/438/413 +f 408/439/415 483/523/494 409/440/416 +f 474/513/484 143/555/223 220/556/222 +f 220/556/222 222/241/225 221/240/224 +f 221/240/224 474/513/484 220/556/222 +f 143/555/223 463/499/473 144/514/485 +f 498/545/520 499/546/521 421/453/428 +f 501/549/524 424/456/293 423/455/430 +f 423/455/430 500/548/523 501/549/524 +f 284/557/291 487/531/504 441/477/451 +f 285/476/292 284/557/291 441/477/451 +f 151/534/150 487/531/504 284/557/291 +f 423/455/430 390/415/397 500/548/523 +f 437/470/445 212/527/500 438/471/446 +f 478/519/490 439/472/447 438/471/446 +f 438/471/446 485/519/498 478/519/490 +f 454/490/464 439/472/447 478/519/490 +f 478/519/490 461/497/471 454/490/464 +f 304/288/311 321/307/328 319/303/326 +f 319/303/326 305/289/312 304/288/311 +f 310/294/317 305/289/312 319/303/326 +f 319/303/326 318/302/325 310/294/317 +f 318/302/325 236/324/238 239/318/241 +f 239/318/241 310/294/317 318/302/325 +f 211/220/212 506/558/530 438/559/531 +f 438/559/531 212/221/213 211/220/212 +f 209/218/210 205/214/206 208/217/209 +f 208/217/209 210/219/211 209/218/210 +f 207/216/208 206/215/207 485/560/532 +f 485/560/532 507/561/533 207/216/208 +f 438/559/531 506/559/530 507/561/533 +f 507/561/533 485/560/532 438/559/531 +f 508/562/534 509/563/535 199/564/536 +f 199/564/536 204/565/537 508/562/534 +f 141/566/538 510/567/539 508/562/534 +f 508/562/534 204/565/537 141/566/538 +f 511/568/540 510/567/539 141/566/538 +f 141/566/538 136/569/541 511/568/540 +f 512/570/542 511/568/540 136/569/541 +f 136/569/541 139/571/543 512/570/542 +f 513/572/544 512/570/542 139/571/543 +f 139/571/543 210/573/545 513/572/544 +f 210/573/545 208/574/546 514/575/547 +f 514/575/547 513/572/544 210/573/545 +f 207/576/548 507/577/549 506/578/550 +f 515/579/551 516/580/552 207/576/548 +f 207/576/548 506/578/550 515/579/551 +f 517/581/553 515/579/551 506/578/550 +f 506/578/550 211/581/554 517/581/553 +f 518/582/555 517/581/553 211/581/554 +f 211/581/554 200/583/556 518/582/555 +f 208/574/546 207/576/548 516/580/552 +f 516/580/552 514/575/547 208/574/546 +f 200/583/556 199/564/536 509/563/535 +f 509/563/535 518/582/555 200/583/556 +f 519/584/264 518/582/555 509/563/535 +f 509/563/535 508/562/534 519/584/264 +f 518/582/555 519/584/264 517/581/553 +f 519/584/264 516/580/552 515/579/551 +f 515/579/551 517/581/553 519/584/264 +f 516/580/552 519/584/264 514/575/547 +f 514/575/547 519/584/264 513/572/544 +f 513/572/544 519/584/264 512/570/542 +f 512/570/542 519/584/264 511/568/540 +f 519/584/264 508/562/534 510/567/539 +f 510/567/539 511/568/540 519/584/264 +f 11/11/11 194/205/196 82/87/82 +f 85/90/85 84/89/84 17/17/17 +f 17/17/17 196/207/198 85/90/85 +f 86/91/86 85/90/85 196/207/198 +f 196/207/198 197/208/199 86/91/86 +f 87/92/87 86/91/86 197/208/199 +f 197/208/199 198/209/1 87/92/87 +f 187/198/189 84/89/84 15/15/15 +f 15/15/15 24/24/24 187/198/189 +f 188/199/190 187/198/189 24/24/24 +f 24/24/24 25/25/25 188/199/190 +f 27/27/27 188/199/190 195/206/197 +f 195/206/197 191/202/193 27/27/27 +f 323/310/330 19/314/19 194/585/196 +f 194/585/196 324/313/331 323/310/330 +f 11/317/11 325/312/332 324/311/331 +f 324/311/331 194/586/196 11/317/11 +f 320/305/327 230/326/232 232/325/234 +f 290/270/297 287/267/294 286/266/293 +f 286/266/293 291/273/298 290/270/297 +f 290/270/297 291/273/298 303/285/310 +f 379/400/386 520/587/557 378/398/385 +f 378/398/385 356/371/363 379/400/386 +f 380/401/387 521/588/558 520/587/557 +f 520/587/557 379/400/386 380/401/387 +f 381/403/388 372/391/379 521/588/558 +f 521/588/558 380/401/387 381/403/388 +f 376/395/383 522/589/559 382/405/389 +f 382/405/389 354/365/361 376/395/383 +f 522/589/559 372/391/379 381/403/388 +f 381/403/388 382/405/389 522/589/559 +f 376/395/383 375/394/382 523/590/560 +f 523/590/560 371/390/378 376/395/383 +f 523/590/560 375/394/382 377/396/384 +f 377/396/384 524/591/561 523/590/560 +f 524/592/561 377/397/384 378/398/385 +f 378/398/385 363/382/370 524/592/561 +f 520/587/557 360/379/367 363/382/370 +f 363/382/370 378/398/385 520/587/557 +f 521/588/558 364/383/371 360/379/367 +f 360/379/367 520/587/557 521/588/558 +f 367/386/374 364/383/371 521/588/558 +f 521/588/558 372/391/379 367/386/374 +f 371/390/378 370/389/377 522/589/559 +f 522/589/559 376/395/383 371/390/378 +f 372/391/379 522/589/559 370/389/377 +f 371/390/378 523/590/560 525/593/562 +f 525/593/562 368/387/375 371/390/378 +f 525/593/562 523/590/560 524/591/561 +f 524/591/561 374/594/381 525/593/562 +f 374/393/381 524/592/561 363/382/370 +f 363/382/370 362/381/369 374/393/381 +f 361/380/368 334/335/341 333/334/340 +f 333/334/340 362/381/369 361/380/368 +f 337/338/344 334/335/341 361/380/368 +f 361/380/368 365/384/372 337/338/344 +f 349/359/356 337/338/344 365/384/372 +f 365/384/372 366/385/373 349/359/356 +f 350/360/357 349/359/356 366/385/373 +f 366/385/373 369/388/376 350/360/357 +f 368/387/375 351/360/358 350/360/357 +f 350/360/357 369/388/376 368/387/375 +f 351/360/358 368/387/375 352/361/359 +f 373/392/380 352/361/359 368/387/375 +f 368/387/375 525/593/562 373/392/380 +f 525/593/562 374/594/381 332/333/339 +f 332/333/339 373/392/380 525/593/562 +f 304/288/311 312/296/319 322/309/329 +f 322/309/329 321/307/328 304/288/311 +f 154/162/153 222/595/225 156/164/155 +f 169/177/168 285/265/292 152/160/151 +f 156/164/155 169/177/168 152/160/151 +f 154/162/153 156/164/155 152/160/151 +f 157/165/156 156/164/155 222/595/225 +f 222/595/225 220/237/222 157/165/156 +f 152/512/151 285/476/292 443/479/453 +f 108/113/108 110/115/110 526/596/91 +f 526/596/91 109/114/109 108/113/108 +f 1/1/1 527/4/563 528/3/564 +f 528/3/564 2/2/2 1/1/1 +f 529/5/565 530/8/566 7/7/7 +f 7/7/7 6/6/6 529/5/565 +f 9/9/9 12/12/12 531/11/567 +f 531/11/567 532/10/568 9/9/9 +f 533/13/569 534/14/570 528/3/564 +f 528/3/564 527/4/563 533/13/569 +f 535/15/571 536/16/572 534/14/570 +f 534/14/570 533/13/569 535/15/571 +f 537/17/573 538/20/574 539/19/575 +f 539/19/575 540/18/576 537/17/573 +f 2/2/2 528/3/564 541/22/577 +f 541/22/577 21/21/21 2/2/2 +f 542/23/578 536/16/572 535/15/571 +f 535/15/571 543/24/579 542/23/578 +f 544/25/580 545/26/581 542/23/578 +f 542/23/578 543/24/579 544/25/580 +f 546/27/582 540/18/576 539/19/575 +f 539/19/575 547/28/583 546/27/582 +f 548/29/584 549/31/585 550/30/586 +f 550/30/586 541/22/577 548/29/584 +f 551/32/587 552/35/588 553/34/589 +f 553/34/589 554/33/590 551/32/587 +f 552/35/588 551/32/587 555/37/591 +f 555/37/591 556/36/592 552/35/588 +f 556/36/592 555/37/591 557/39/593 +f 557/39/593 558/38/594 556/36/592 +f 559/40/595 558/38/594 557/39/593 +f 557/39/593 560/41/596 559/40/595 +f 561/42/597 559/40/595 560/41/596 +f 560/41/596 562/43/598 561/42/597 +f 561/42/597 562/43/598 563/44/599 +f 564/47/600 565/46/601 566/45/602 +f 566/45/602 567/48/603 564/47/600 +f 554/33/590 553/34/589 568/49/604 +f 568/49/604 565/46/601 554/33/590 +f 569/50/605 570/53/606 571/52/607 +f 571/52/607 572/51/608 569/50/605 +f 573/54/609 571/58/607 570/57/606 +f 574/56/610 575/55/611 573/54/609 +f 573/54/609 570/57/606 574/56/610 +f 576/59/612 575/55/611 574/56/610 +f 574/56/610 577/60/613 576/59/612 +f 578/61/614 579/64/615 580/63/616 +f 580/63/616 581/62/617 578/61/614 +f 582/65/618 579/64/615 578/61/614 +f 578/61/614 583/66/619 582/65/618 +f 584/67/620 585/68/621 582/65/618 +f 582/65/618 583/66/619 584/67/620 +f 586/69/622 587/72/623 588/71/624 +f 588/71/624 589/70/625 586/69/622 +f 590/73/626 591/74/627 569/50/605 +f 569/50/605 572/51/608 590/73/626 +f 592/75/628 575/78/611 576/77/612 +f 576/77/612 593/76/629 592/75/628 +f 594/79/630 593/76/629 576/77/612 +f 576/77/612 577/80/613 594/79/630 +f 595/81/631 579/64/615 582/65/618 +f 585/68/621 596/82/632 595/81/631 +f 595/81/631 582/65/618 585/68/621 +f 587/72/623 592/75/628 593/76/629 +f 593/76/629 588/71/624 587/72/623 +f 597/83/633 542/23/578 545/26/581 +f 545/26/581 598/84/634 597/83/633 +f 599/85/635 597/83/633 598/84/634 +f 598/84/634 600/86/636 599/85/635 +f 601/87/637 531/11/567 12/12/12 +f 12/12/12 83/88/83 601/87/637 +f 541/22/577 528/3/564 534/14/570 +f 534/14/570 548/29/584 541/22/577 +f 602/89/638 535/15/571 533/13/569 +f 533/13/569 603/90/639 602/89/638 +f 603/90/639 533/13/569 527/4/563 +f 527/4/563 604/91/640 603/90/639 +f 604/91/640 527/4/563 1/1/1 +f 1/1/1 87/92/87 604/91/640 +f 88/93/88 9/9/9 532/10/568 +f 532/10/568 605/94/641 88/93/88 +f 601/87/637 83/88/83 90/95/90 +f 90/95/90 547/28/583 601/87/637 +f 529/5/565 6/6/6 91/96/91 +f 91/96/91 606/97/642 529/5/565 +f 607/98/643 96/101/96 95/100/95 +f 95/100/95 608/99/644 607/98/643 +f 609/102/645 99/104/99 98/103/98 +f 98/103/98 608/99/644 609/102/645 +f 98/103/98 100/105/100 607/98/643 +f 607/98/643 608/99/644 98/103/98 +f 101/106/101 104/109/104 610/108/643 +f 610/108/643 611/107/646 101/106/101 +f 610/108/643 106/111/106 105/110/105 +f 105/110/105 611/107/646 610/108/643 +f 607/98/643 611/107/646 105/110/105 +f 105/110/105 96/101/96 607/98/643 +f 608/99/644 95/100/95 107/112/107 +f 107/112/107 609/102/645 608/99/644 +f 107/112/107 109/114/109 612/113/647 +f 612/113/647 609/102/645 107/112/107 +f 99/104/99 609/102/645 612/113/647 +f 612/113/647 110/115/110 99/104/99 +f 101/106/101 611/107/646 607/98/643 +f 607/98/643 100/105/100 101/106/101 +f 104/109/104 88/117/88 613/116/648 +f 613/116/648 610/108/643 104/109/104 +f 610/108/643 613/116/648 91/118/91 +f 91/118/91 106/111/106 610/108/643 +f 112/119/112 614/122/649 615/121/650 +f 615/121/650 113/120/113 112/119/112 +f 616/123/651 550/30/586 549/31/585 +f 549/31/585 617/124/652 616/123/651 +f 118/125/118 119/126/119 550/30/586 +f 550/30/586 616/123/651 118/125/118 +f 618/127/653 619/128/654 617/124/652 +f 617/124/652 549/31/585 618/127/653 +f 91/96/91 613/129/648 606/97/642 +f 605/94/641 613/129/648 88/93/88 +f 606/97/642 613/129/648 605/94/641 +f 605/94/641 529/5/565 606/97/642 +f 21/21/21 541/22/577 550/30/586 +f 550/30/586 119/126/119 21/21/21 +f 616/123/651 620/131/655 122/130/122 +f 122/130/122 118/125/118 616/123/651 +f 614/122/649 112/119/112 125/133/125 +f 125/133/125 621/132/656 614/122/649 +f 622/134/657 623/137/658 624/136/659 +f 624/136/659 625/135/660 622/134/657 +f 618/127/653 624/136/659 623/137/658 +f 623/137/658 619/128/654 618/127/653 +f 616/123/651 617/124/652 626/138/661 +f 626/138/661 620/131/655 616/123/651 +f 619/128/654 627/140/662 628/139/663 +f 628/139/663 617/124/652 619/128/654 +f 628/139/663 626/138/661 617/124/652 +f 619/128/654 623/137/658 627/140/662 +f 627/140/662 623/137/658 622/134/657 +f 622/134/657 629/141/664 627/140/662 +f 134/142/125 630/143/665 621/132/656 +f 621/132/656 125/133/125 134/142/125 +f 631/144/666 632/147/667 633/146/668 +f 633/146/668 634/145/669 631/144/666 +f 635/148/670 636/149/363 631/144/666 +f 631/144/666 634/145/669 635/148/670 +f 637/150/671 638/152/672 639/151/673 +f 640/153/674 638/152/672 637/150/671 +f 637/150/671 641/154/675 640/153/674 +f 638/152/672 640/153/674 642/155/676 +f 643/156/677 644/159/678 645/158/679 +f 645/158/679 646/157/680 643/156/677 +f 647/160/681 648/162/682 649/161/683 +f 650/163/684 651/166/685 652/165/686 +f 652/165/686 653/164/516 650/163/684 +f 641/154/675 654/168/687 655/167/688 +f 655/167/688 640/153/674 641/154/675 +f 656/169/689 657/170/690 640/153/674 +f 640/153/674 655/167/688 656/169/689 +f 658/171/691 643/156/677 646/157/680 +f 646/157/680 659/172/692 658/171/691 +f 660/173/693 661/175/694 662/174/695 +f 662/174/695 658/171/691 660/173/693 +f 650/163/684 653/164/516 663/177/696 +f 663/177/696 664/176/697 650/163/684 +f 665/178/271 666/180/698 667/179/699 +f 668/183/700 667/182/701 666/181/702 +f 666/181/702 669/184/703 668/183/700 +f 668/183/700 669/184/703 670/185/704 +f 671/186/705 672/188/706 673/187/707 +f 674/189/708 673/187/707 672/188/706 +f 675/191/709 676/190/710 674/189/708 +f 674/189/708 672/188/706 675/191/709 +f 675/191/709 666/180/698 665/178/271 +f 665/178/271 676/190/710 675/191/709 +f 667/179/699 677/192/711 651/166/685 +f 651/166/685 665/178/271 667/179/699 +f 651/166/685 650/163/684 665/178/271 +f 668/183/700 655/167/688 654/168/687 +f 654/168/687 667/182/701 668/183/700 +f 655/167/688 668/183/700 670/185/704 +f 670/185/704 678/193/712 655/167/688 +f 660/173/693 679/194/713 671/186/705 +f 673/187/707 661/175/694 660/173/693 +f 660/173/693 671/186/705 673/187/707 +f 673/187/707 674/189/708 661/175/694 +f 665/178/271 650/163/684 664/176/697 +f 664/176/697 676/190/710 665/178/271 +f 639/151/673 680/195/714 641/154/675 +f 641/154/675 637/150/671 639/151/673 +f 654/168/687 641/154/675 680/195/714 +f 680/195/714 677/196/715 654/168/687 +f 654/168/687 677/196/715 667/182/701 +f 615/121/650 681/197/716 599/85/635 +f 599/85/635 600/86/636 615/121/650 +f 540/18/576 682/198/717 602/89/638 +f 602/89/638 537/17/573 540/18/576 +f 546/27/582 683/199/718 682/198/717 +f 682/198/717 540/18/576 546/27/582 +f 189/200/191 190/201/192 545/26/581 +f 545/26/581 544/25/580 189/200/191 +f 547/28/583 90/95/90 191/202/193 +f 191/202/193 546/27/582 547/28/583 +f 190/201/192 192/203/194 598/84/634 +f 598/84/634 545/26/581 190/201/192 +f 192/203/194 193/204/195 600/86/636 +f 600/86/636 598/84/634 192/203/194 +f 539/19/575 684/205/719 601/87/637 +f 601/87/637 547/28/583 539/19/575 +f 614/122/649 625/135/660 681/197/716 +f 681/197/716 615/121/650 614/122/649 +f 621/132/656 622/134/657 625/135/660 +f 625/135/660 614/122/649 621/132/656 +f 113/120/113 615/121/650 600/86/636 +f 600/86/636 193/204/195 113/120/113 +f 195/206/197 189/200/191 544/25/580 +f 544/25/580 683/199/718 195/206/197 +f 629/141/664 622/134/657 621/132/656 +f 621/132/656 630/143/665 629/141/664 +f 537/17/573 685/207/720 538/20/574 +f 685/207/720 686/208/721 530/8/566 +f 7/7/7 530/8/566 686/208/721 +f 686/208/721 198/209/1 7/7/7 +f 687/210/722 688/212/723 689/211/724 +f 689/211/724 690/211/725 687/210/722 +f 688/212/723 687/210/722 691/213/726 +f 691/213/726 692/213/727 688/212/723 +f 693/214/728 694/217/729 695/216/730 +f 695/216/730 696/215/731 693/214/728 +f 633/146/668 632/147/667 697/219/732 +f 697/219/732 698/218/733 633/146/668 +f 690/211/725 689/211/724 699/221/734 +f 699/221/734 700/220/735 690/211/725 +f 692/213/727 691/213/726 636/149/363 +f 636/149/363 635/148/670 692/213/727 +f 588/71/624 596/82/632 701/222/736 +f 701/222/736 589/70/625 588/71/624 +f 591/74/627 590/73/626 702/224/737 +f 702/224/737 703/223/738 591/74/627 +f 704/225/739 572/51/608 571/52/607 +f 571/52/607 573/226/609 704/225/739 +f 580/63/616 579/64/615 595/81/631 +f 595/81/631 705/227/740 580/63/616 +f 596/82/632 588/71/624 593/76/629 +f 593/76/629 594/79/630 596/82/632 +f 704/225/739 702/224/737 590/73/626 +f 590/73/626 572/51/608 704/225/739 +f 553/34/589 552/35/588 570/53/606 +f 570/53/606 569/50/605 553/34/589 +f 574/56/610 570/57/606 552/230/588 +f 556/229/592 558/228/594 574/56/610 +f 574/56/610 552/230/588 556/229/592 +f 558/228/594 581/231/617 574/56/610 +f 581/62/617 558/38/594 559/40/595 +f 559/40/595 578/61/614 581/62/617 +f 559/40/595 561/42/597 583/66/619 +f 583/66/619 578/61/614 559/40/595 +f 583/66/619 561/42/597 563/44/599 +f 563/44/599 584/67/620 583/66/619 +f 586/69/622 589/70/625 567/48/603 +f 566/45/602 703/223/738 586/69/622 +f 586/69/622 567/48/603 566/45/602 +f 553/34/589 569/50/605 591/74/627 +f 591/74/627 568/49/604 553/34/589 +f 567/48/603 589/70/625 701/222/736 +f 701/222/736 584/67/620 563/44/599 +f 563/44/599 567/48/603 701/222/736 +f 566/45/602 568/49/604 591/74/627 +f 591/74/627 703/223/738 566/45/602 +f 701/222/736 596/82/632 585/68/621 +f 585/68/621 584/67/620 701/222/736 +f 702/224/737 587/72/623 586/69/622 +f 586/69/622 703/223/738 702/224/737 +f 704/225/739 573/226/609 575/78/611 +f 575/78/611 592/75/628 704/225/739 +f 705/227/740 595/81/631 594/79/630 +f 594/79/630 577/80/613 705/227/740 +f 596/82/632 594/79/630 595/81/631 +f 702/224/737 704/225/739 592/75/628 +f 592/75/628 587/72/623 702/224/737 +f 574/56/610 581/231/617 705/232/740 +f 705/232/740 577/60/613 574/56/610 +f 581/231/617 580/233/616 705/232/740 +f 566/45/602 565/46/601 568/49/604 +f 563/44/599 562/43/598 564/47/600 +f 564/47/600 567/48/603 563/44/599 +f 640/153/674 657/170/690 706/235/741 +f 706/235/741 642/155/676 640/153/674 +f 706/235/741 707/234/742 642/155/676 +f 680/236/743 639/238/744 708/237/745 +f 708/237/745 652/165/686 680/236/743 +f 677/192/711 680/236/743 652/165/686 +f 652/165/686 651/166/685 677/192/711 +f 649/239/683 648/239/682 709/241/746 +f 709/241/746 710/240/747 649/239/683 +f 711/32/748 712/243/749 713/242/750 +f 713/242/750 714/33/751 711/32/748 +f 712/243/749 711/32/748 715/37/752 +f 715/37/752 716/36/753 712/243/749 +f 715/37/752 717/39/754 718/38/755 +f 718/38/755 716/36/753 715/37/752 +f 719/40/756 718/38/755 717/39/754 +f 717/39/754 720/41/757 719/40/756 +f 720/41/757 721/244/758 722/244/759 +f 722/244/759 719/40/756 720/41/757 +f 721/244/758 723/47/760 724/245/761 +f 724/245/761 722/244/759 721/244/758 +f 725/47/762 723/47/760 726/246/763 +f 714/33/751 713/242/750 727/49/764 +f 727/49/764 728/46/765 714/33/751 +f 729/50/766 730/53/767 731/52/768 +f 731/52/768 732/51/769 729/50/766 +f 733/54/770 731/58/768 730/57/767 +f 734/56/610 735/55/771 733/54/770 +f 733/54/770 730/57/767 734/56/610 +f 736/59/612 735/55/771 734/56/610 +f 734/56/610 737/60/772 736/59/612 +f 738/64/773 739/63/774 740/62/775 +f 740/62/775 741/61/776 738/64/773 +f 742/65/777 738/64/773 741/61/776 +f 741/61/776 743/66/778 742/65/777 +f 742/65/777 743/66/778 744/67/779 +f 744/67/779 745/68/780 742/65/777 +f 746/70/781 747/69/782 748/72/783 +f 748/72/783 749/71/784 746/70/781 +f 750/73/785 751/74/786 729/50/766 +f 729/50/766 732/51/769 750/73/785 +f 752/75/787 735/78/771 736/77/612 +f 736/77/612 753/76/788 752/75/787 +f 754/79/789 753/76/788 736/77/612 +f 736/77/612 737/80/772 754/79/789 +f 755/81/790 738/64/773 742/65/777 +f 745/68/780 756/82/791 755/81/790 +f 755/81/790 742/65/777 745/68/780 +f 748/72/783 752/75/787 753/76/788 +f 753/76/788 749/71/784 748/72/783 +f 746/70/781 749/71/784 756/82/791 +f 756/82/791 757/222/792 746/70/781 +f 758/223/793 751/74/786 750/73/785 +f 750/73/785 759/247/169 758/223/793 +f 760/225/794 732/51/769 731/52/768 +f 731/52/768 733/226/770 760/225/794 +f 739/63/774 738/64/773 755/81/790 +f 755/81/790 761/227/795 739/63/774 +f 756/82/791 749/71/784 753/76/788 +f 753/76/788 754/79/789 756/82/791 +f 750/73/785 732/51/769 760/225/794 +f 760/225/794 759/247/169 750/73/785 +f 712/243/749 730/53/767 729/50/766 +f 729/50/766 713/242/750 712/243/749 +f 734/56/610 730/57/767 712/249/749 +f 716/248/753 718/228/755 734/56/610 +f 734/56/610 712/249/749 716/248/753 +f 718/228/755 740/250/775 734/56/610 +f 718/38/755 719/40/756 741/61/776 +f 741/61/776 740/62/775 718/38/755 +f 719/40/756 722/244/759 743/66/778 +f 743/66/778 741/61/776 719/40/756 +f 743/66/778 722/244/759 724/245/761 +f 724/245/761 744/67/779 743/66/778 +f 746/70/781 757/222/792 725/47/762 +f 725/47/762 747/69/782 746/70/781 +f 726/246/763 758/223/793 747/69/782 +f 747/69/782 725/47/762 726/246/763 +f 713/242/750 729/50/766 751/74/786 +f 751/74/786 727/49/764 713/242/750 +f 757/222/792 744/67/779 724/245/761 +f 724/245/761 725/47/762 757/222/792 +f 726/246/763 727/49/764 751/74/786 +f 751/74/786 758/223/793 726/246/763 +f 757/222/792 756/82/791 745/68/780 +f 745/68/780 744/67/779 757/222/792 +f 758/223/793 759/247/169 748/72/783 +f 748/72/783 747/69/782 758/223/793 +f 760/225/794 733/226/770 735/78/771 +f 735/78/771 752/75/787 760/225/794 +f 761/227/795 755/81/790 754/79/789 +f 754/79/789 737/80/772 761/227/795 +f 756/82/791 754/79/789 755/81/790 +f 759/247/169 760/225/794 752/75/787 +f 752/75/787 748/72/783 759/247/169 +f 734/56/610 740/250/775 761/232/795 +f 761/232/795 737/60/772 734/56/610 +f 740/250/775 739/233/774 761/232/795 +f 726/246/763 723/47/760 728/46/765 +f 728/46/765 727/49/764 726/246/763 +f 724/245/761 723/47/760 725/47/762 +f 655/167/688 678/193/712 656/169/689 +f 679/194/713 660/173/693 762/251/796 +f 762/251/796 660/173/693 658/171/691 +f 658/171/691 659/172/692 762/251/796 +f 672/252/706 669/184/797 666/181/698 +f 666/181/698 675/181/709 672/252/706 +f 646/253/680 645/254/679 707/234/798 +f 707/234/798 706/235/799 646/253/680 +f 659/255/692 646/253/680 706/235/799 +f 706/235/799 657/170/800 659/255/692 +f 672/252/706 671/256/705 670/185/801 +f 670/185/801 669/184/797 672/252/706 +f 679/193/713 762/169/796 656/169/802 +f 656/169/802 678/193/803 679/193/713 +f 671/256/705 679/193/713 678/193/803 +f 678/193/803 670/185/801 671/256/705 +f 762/169/796 659/255/692 657/170/800 +f 657/170/800 656/169/802 762/169/796 +f 763/257/804 277/259/284 764/258/805 +f 765/261/806 766/260/807 277/259/284 +f 277/259/284 280/262/287 765/261/806 +f 764/258/805 277/259/284 766/260/807 +f 767/263/808 281/263/288 277/259/284 +f 277/259/284 768/257/809 767/263/808 +f 768/257/809 277/259/284 763/257/804 +f 644/159/678 643/156/677 769/264/810 +f 658/171/691 662/174/695 769/264/810 +f 769/264/810 643/156/677 658/171/691 +f 663/177/696 770/265/811 769/264/810 +f 769/264/810 662/174/695 663/177/696 +f 661/175/694 664/176/697 663/177/696 +f 663/177/696 662/174/695 661/175/694 +f 664/176/697 661/175/694 674/189/708 +f 674/189/708 676/190/710 664/176/697 +f 771/266/812 772/269/813 773/268/814 +f 773/268/814 774/267/815 771/266/812 +f 774/267/815 538/272/574 685/271/720 +f 685/271/720 775/270/816 774/267/815 +f 776/273/817 777/275/818 778/274/819 +f 778/274/819 771/266/812 776/273/817 +f 779/276/820 778/274/819 777/275/818 +f 777/275/818 780/277/821 779/276/820 +f 781/278/822 782/281/823 783/280/824 +f 783/280/824 784/279/825 781/278/822 +f 778/274/819 779/276/820 785/283/826 +f 785/283/826 786/282/827 778/274/819 +f 777/275/818 776/273/817 787/285/828 +f 787/285/828 788/284/829 777/275/818 +f 787/285/828 530/287/566 529/286/565 +f 529/286/565 788/284/829 787/285/828 +f 771/266/812 778/274/819 786/282/827 +f 786/282/827 772/269/813 771/266/812 +f 789/288/830 782/281/823 790/290/831 +f 790/290/831 791/289/832 789/288/830 +f 782/281/823 792/292/833 793/291/834 +f 793/291/834 790/290/831 782/281/823 +f 794/293/835 795/294/836 791/289/832 +f 791/289/832 790/290/831 794/293/835 +f 794/293/835 790/290/831 793/291/834 +f 793/291/834 796/295/837 794/293/835 +f 685/271/720 530/287/566 787/285/828 +f 787/285/828 775/270/816 685/271/720 +f 789/288/830 797/296/838 783/280/824 +f 783/280/824 782/281/823 789/288/830 +f 798/297/839 799/300/840 800/299/841 +f 800/299/841 801/298/842 798/297/839 +f 801/298/842 800/299/841 793/291/834 +f 793/291/834 792/292/833 801/298/842 +f 802/301/843 799/304/840 803/303/844 +f 803/303/844 804/302/845 802/301/843 +f 799/300/840 802/306/843 805/305/846 +f 805/305/846 800/299/841 799/300/840 +f 805/305/846 796/295/837 793/291/834 +f 793/291/834 800/299/841 805/305/846 +f 803/303/844 799/304/840 798/308/839 +f 798/308/839 806/307/847 803/303/844 +f 806/307/847 798/308/839 807/310/848 +f 807/310/848 808/309/849 806/307/847 +f 777/275/818 788/284/829 780/277/821 +f 809/311/850 798/297/839 801/298/842 +f 801/298/842 810/312/851 809/311/850 +f 798/308/839 809/313/850 807/310/848 +f 539/314/575 773/268/814 808/309/849 +f 808/309/849 807/310/848 539/314/575 +f 773/268/814 772/269/813 797/296/838 +f 797/296/838 808/309/849 773/268/814 +f 797/296/838 772/269/813 786/282/827 +f 786/282/827 783/280/824 797/296/838 +f 783/280/824 786/282/827 785/283/826 +f 785/283/826 784/279/825 783/280/824 +f 605/315/641 532/316/568 779/276/820 +f 779/276/820 780/277/821 605/315/641 +f 529/286/565 605/315/641 780/277/821 +f 780/277/821 788/284/829 529/286/565 +f 782/281/823 781/278/822 792/292/833 +f 781/278/822 801/298/842 792/292/833 +f 801/298/842 781/278/822 784/279/825 +f 784/279/825 810/312/851 801/298/842 +f 810/312/851 784/279/825 785/283/826 +f 785/283/826 531/317/567 810/312/851 +f 532/316/568 531/317/567 785/283/826 +f 785/283/826 779/276/820 532/316/568 +f 728/318/765 795/294/836 714/319/751 +f 794/293/835 711/320/748 714/319/751 +f 714/319/751 795/294/836 794/293/835 +f 796/295/837 715/321/752 711/320/748 +f 711/320/748 794/293/835 796/295/837 +f 804/302/845 723/324/760 721/323/758 +f 721/323/758 720/322/757 802/301/843 +f 802/301/843 804/302/845 721/323/758 +f 805/305/846 802/306/843 720/325/757 +f 717/326/754 715/321/752 796/295/837 +f 796/295/837 805/305/846 717/326/754 +f 773/268/814 539/314/575 538/272/574 +f 538/272/574 774/267/815 773/268/814 +f 811/327/852 812/330/853 813/329/854 +f 813/329/854 814/328/855 811/327/852 +f 814/328/855 813/329/854 815/332/856 +f 815/332/856 816/331/857 814/328/855 +f 817/333/858 816/331/857 815/332/856 +f 818/334/859 819/336/860 820/335/861 +f 821/337/862 822/338/863 820/335/861 +f 820/335/861 819/336/860 821/337/862 +f 813/329/854 812/330/853 823/340/864 +f 823/340/864 824/339/865 813/329/854 +f 815/332/856 813/329/854 824/339/865 +f 824/339/865 542/341/578 815/332/856 +f 597/342/633 817/333/858 815/332/856 +f 815/332/856 542/341/578 597/342/633 +f 825/343/866 818/334/859 817/345/858 +f 817/345/858 597/344/633 825/343/866 +f 826/346/867 819/336/860 818/334/859 +f 818/334/859 825/343/866 826/346/867 +f 827/347/868 821/337/862 819/336/860 +f 819/336/860 826/346/867 827/347/868 +f 828/348/869 821/337/862 827/347/868 +f 827/347/868 829/349/870 828/348/869 +f 536/352/572 830/351/871 831/350/872 +f 831/350/872 534/353/570 536/352/572 +f 832/354/873 618/356/653 549/355/585 +f 832/354/873 549/355/585 548/358/584 +f 548/358/584 833/357/874 832/354/873 +f 834/359/875 822/338/863 821/337/862 +f 821/337/862 828/348/869 834/359/875 +f 835/360/876 811/327/852 814/328/855 +f 814/328/855 836/361/877 835/360/876 +f 835/360/876 837/360/878 811/327/852 +f 555/362/591 838/365/879 839/364/880 +f 839/364/880 557/363/593 555/362/591 +f 839/364/880 840/367/102 560/366/596 +f 560/366/596 557/363/593 839/364/880 +f 840/368/102 841/371/881 562/370/598 +f 562/370/598 560/369/596 840/368/102 +f 834/359/875 828/348/869 811/327/852 +f 811/327/852 837/360/878 834/359/875 +f 811/327/852 828/348/869 812/330/853 +f 830/351/871 823/340/864 829/349/870 +f 829/349/870 842/372/882 830/351/871 +f 534/353/570 831/350/872 833/357/874 +f 833/357/874 548/358/584 534/353/570 +f 624/373/659 618/356/653 843/374/883 +f 843/374/883 681/376/716 625/375/660 +f 625/375/660 624/373/659 843/374/883 +f 618/356/653 832/354/873 844/377/884 +f 844/377/884 843/374/883 618/356/653 +f 842/372/882 844/377/884 832/354/873 +f 832/354/873 833/357/874 842/372/882 +f 842/372/882 833/357/874 831/350/872 +f 831/350/872 830/351/871 842/372/882 +f 823/340/864 830/351/871 536/352/572 +f 536/352/572 824/339/865 823/340/864 +f 542/341/578 824/339/865 536/352/572 +f 825/343/866 597/344/633 599/378/635 +f 681/376/716 843/374/883 826/346/867 +f 826/346/867 825/343/866 681/376/716 +f 843/374/883 844/377/884 827/347/868 +f 827/347/868 826/346/867 843/374/883 +f 844/377/884 842/372/882 829/349/870 +f 829/349/870 827/347/868 844/377/884 +f 812/330/853 828/348/869 829/349/870 +f 829/349/870 823/340/864 812/330/853 +f 825/343/866 599/378/635 681/376/716 +f 845/379/220 846/382/885 847/381/886 +f 847/381/886 848/380/887 845/379/220 +f 849/383/888 845/379/220 848/380/887 +f 848/380/887 850/384/889 849/383/888 +f 851/385/890 852/386/891 849/383/888 +f 849/383/888 850/384/889 851/385/890 +f 853/387/892 854/390/893 855/389/894 +f 855/389/894 856/388/895 853/387/892 +f 852/386/891 851/385/890 856/388/895 +f 855/389/894 857/391/896 852/386/891 +f 852/386/891 856/388/895 855/389/894 +f 836/361/877 814/328/855 816/331/857 +f 816/331/857 858/392/897 836/361/877 +f 817/333/858 858/392/897 816/331/857 +f 817/345/858 818/334/859 847/381/886 +f 847/381/886 859/393/898 817/345/858 +f 838/365/879 860/395/899 861/394/900 +f 861/394/900 839/364/880 838/365/879 +f 861/394/900 862/396/901 840/367/102 +f 840/367/102 839/364/880 861/394/900 +f 862/397/901 863/398/902 841/371/881 +f 841/371/881 840/368/102 862/397/901 +f 564/399/600 562/370/598 841/371/881 +f 841/371/881 864/400/903 564/399/600 +f 865/401/904 565/402/601 564/399/600 +f 564/399/600 864/400/903 865/401/904 +f 866/403/905 554/404/590 565/402/601 +f 565/402/601 865/401/904 866/403/905 +f 838/365/879 555/362/591 551/406/587 +f 551/406/587 867/405/906 838/365/879 +f 867/405/906 551/406/587 554/404/590 +f 554/404/590 866/403/905 867/405/906 +f 868/407/907 869/410/908 385/409/392 +f 385/409/392 384/408/391 868/407/907 +f 868/407/907 870/413/909 871/412/910 +f 871/412/910 629/411/664 869/410/908 +f 869/410/908 868/407/907 871/412/910 +f 870/413/909 872/416/911 873/415/912 +f 873/415/912 874/414/913 870/413/909 +f 874/414/913 871/412/910 870/413/909 +f 620/417/655 875/420/914 392/419/399 +f 392/419/399 122/418/122 620/417/655 +f 134/421/125 385/409/392 869/410/908 +f 869/410/908 630/422/665 134/421/125 +f 628/423/663 876/426/915 877/425/916 +f 877/425/916 626/424/661 628/423/663 +f 629/411/664 871/412/910 874/414/913 +f 874/414/913 627/427/662 629/411/664 +f 877/425/916 875/420/914 620/417/655 +f 620/417/655 626/424/661 877/425/916 +f 627/427/662 874/414/913 876/426/915 +f 876/426/915 628/423/663 627/427/662 +f 878/428/917 879/429/914 875/420/914 +f 875/420/914 877/425/916 878/428/917 +f 392/419/399 875/420/914 879/429/914 +f 879/429/914 398/430/405 392/419/399 +f 880/431/918 881/434/919 401/433/408 +f 401/433/408 400/432/407 880/431/918 +f 882/438/920 883/437/921 405/437/412 +f 405/437/412 404/436/411 884/435/922 +f 884/435/922 882/438/920 405/437/412 +f 885/439/923 886/442/924 887/441/703 +f 887/441/703 888/440/925 885/439/923 +f 886/442/924 889/444/926 890/443/927 +f 890/443/927 887/441/703 886/442/924 +f 884/435/922 881/434/919 891/445/928 +f 891/445/928 882/438/920 884/435/922 +f 892/446/929 764/449/930 893/448/931 +f 893/448/931 894/447/932 892/446/929 +f 710/240/747 419/451/426 418/450/425 +f 418/450/425 649/239/683 710/240/747 +f 870/413/909 895/452/933 872/416/911 +f 896/453/934 897/456/812 898/455/935 +f 898/455/935 899/454/936 896/453/934 +f 425/457/431 900/460/937 901/459/938 +f 901/459/938 426/458/432 425/457/431 +f 429/461/435 281/463/438 767/461/939 +f 767/461/939 902/462/940 429/461/435 +f 894/447/932 893/448/931 903/465/941 +f 903/465/941 904/464/942 894/447/932 +f 905/466/943 906/467/944 901/459/938 +f 901/459/938 900/460/937 905/466/943 +f 907/468/945 436/469/444 419/451/426 +f 419/451/426 710/240/747 907/468/945 +f 908/472/946 909/471/947 910/470/948 +f 910/470/948 911/473/949 908/472/946 +f 886/442/924 893/448/931 764/449/930 +f 764/449/930 889/444/926 886/442/924 +f 903/465/941 893/448/931 886/442/924 +f 886/442/924 885/439/923 903/465/941 +f 889/444/926 766/475/950 765/474/951 +f 765/474/951 890/443/927 889/444/926 +f 770/476/811 443/479/453 442/478/452 +f 442/478/452 912/477/952 770/476/811 +f 913/480/953 914/483/954 915/482/955 +f 915/482/955 916/481/956 913/480/953 +f 917/484/957 895/452/933 918/486/958 +f 918/486/958 919/485/959 917/484/957 +f 870/413/909 868/407/907 920/487/960 +f 920/487/960 895/452/933 870/413/909 +f 914/483/954 764/449/930 892/446/929 +f 892/446/929 915/482/955 914/483/954 +f 918/486/958 895/452/933 920/487/960 +f 920/487/960 921/488/961 918/486/958 +f 453/489/463 401/433/408 881/434/919 +f 881/434/919 884/435/922 453/489/463 +f 911/473/949 922/492/962 923/491/963 +f 923/491/963 924/490/964 911/473/949 +f 925/493/965 926/494/966 924/490/964 +f 924/490/964 923/491/963 925/493/965 +f 927/495/967 923/491/963 922/492/962 +f 922/492/962 928/496/968 927/495/967 +f 924/490/964 926/494/966 929/498/969 +f 929/498/969 930/497/970 924/490/964 +f 931/499/971 932/502/972 933/501/973 +f 933/501/973 934/500/974 931/499/971 +f 935/503/975 913/480/953 469/505/479 +f 469/505/479 468/504/478 935/503/975 +f 916/481/956 470/506/480 469/505/479 +f 469/505/479 913/480/953 916/481/956 +f 916/481/956 921/488/961 471/507/481 +f 471/507/481 470/506/480 916/481/956 +f 868/407/907 384/408/391 472/508/482 +f 472/508/482 920/487/960 868/407/907 +f 936/509/976 899/454/936 917/484/957 +f 917/484/957 919/485/959 936/509/976 +f 443/479/453 647/512/681 649/511/683 +f 649/511/683 418/510/425 443/479/453 +f 710/240/747 937/513/977 925/493/965 +f 925/493/965 907/468/945 710/240/747 +f 638/514/978 642/515/979 932/502/972 +f 932/502/972 931/499/971 638/514/978 +f 907/468/945 925/493/965 923/491/963 +f 923/491/963 927/495/967 907/468/945 +f 436/469/444 907/468/945 927/495/967 +f 927/495/967 475/516/487 436/469/444 +f 938/517/980 891/445/928 881/434/919 +f 881/434/919 880/431/918 938/517/980 +f 924/490/964 908/472/946 911/473/949 +f 939/518/981 940/519/982 941/519/983 +f 941/519/983 930/497/970 939/518/981 +f 927/495/967 928/496/968 480/520/435 +f 480/520/435 475/516/487 927/495/967 +f 942/521/984 943/523/985 882/438/920 +f 882/438/920 944/522/986 942/521/984 +f 944/522/986 891/445/928 938/517/980 +f 938/517/980 635/524/987 944/522/986 +f 911/473/949 910/470/948 945/525/988 +f 945/525/988 922/492/962 911/473/949 +f 922/492/962 945/525/988 905/466/943 +f 905/466/943 900/460/937 928/496/968 +f 928/496/968 922/492/962 905/466/943 +f 696/519/989 946/519/990 941/519/983 +f 941/519/983 940/519/982 696/519/989 +f 910/470/948 699/527/991 689/526/992 +f 689/526/992 945/525/988 910/470/948 +f 947/528/993 942/521/984 634/530/994 +f 634/530/994 633/529/995 947/528/993 +f 898/455/935 873/415/912 872/416/911 +f 872/416/911 899/454/936 898/455/935 +f 872/416/911 917/484/957 899/454/936 +f 948/531/996 488/532/505 398/430/405 +f 398/430/405 879/429/914 948/531/996 +f 949/533/997 645/535/679 644/534/678 +f 644/534/678 878/428/917 949/533/997 +f 878/428/917 644/534/678 948/531/996 +f 948/531/996 879/429/914 878/428/917 +f 629/411/664 630/422/665 869/410/908 +f 950/536/998 764/449/930 914/483/954 +f 914/483/954 951/537/999 950/536/998 +f 453/489/463 884/435/922 404/436/411 +f 888/440/925 952/538/1000 883/437/1001 +f 883/437/1001 882/438/920 888/440/925 +f 888/440/925 887/441/703 952/538/1000 +f 887/441/703 890/443/927 953/539/1002 +f 953/539/1002 952/538/1000 887/441/703 +f 280/540/1003 953/539/1002 890/443/927 +f 890/443/927 765/474/951 280/540/1003 +f 883/437/921 494/437/512 405/437/412 +f 495/539/1004 494/437/1005 883/437/1001 +f 883/437/1001 952/538/1000 495/539/1004 +f 953/539/1002 496/539/1006 495/539/1004 +f 495/539/1004 952/538/1000 953/539/1002 +f 767/461/939 768/475/155 954/541/1007 +f 954/541/1007 902/462/940 767/461/939 +f 906/467/944 905/466/943 688/543/1008 +f 688/543/1008 692/542/1009 906/467/944 +f 698/544/1010 955/545/1011 947/528/993 +f 947/528/993 633/529/995 698/544/1010 +f 635/524/987 938/517/980 906/467/944 +f 906/467/944 692/542/1009 635/524/987 +f 880/431/918 901/459/938 906/467/944 +f 906/467/944 938/517/980 880/431/918 +f 400/432/407 426/458/432 901/459/938 +f 901/459/938 880/431/918 400/432/407 +f 903/465/941 942/521/984 947/528/993 +f 947/528/993 904/464/942 903/465/941 +f 885/439/923 943/523/985 942/521/984 +f 942/521/984 903/465/941 885/439/923 +f 899/454/936 936/509/976 956/546/1012 +f 956/546/1012 896/453/934 899/454/936 +f 693/547/1013 696/519/989 940/519/982 +f 940/519/982 955/545/1011 693/547/1013 +f 896/453/934 955/545/1011 940/519/982 +f 940/519/982 939/518/981 896/453/934 +f 957/548/1014 958/549/1015 645/535/679 +f 645/535/679 949/533/997 957/548/1014 +f 959/551/1016 933/501/973 932/502/972 +f 932/502/972 642/515/979 707/550/798 +f 707/550/798 959/551/1016 932/502/972 +f 876/426/915 874/414/913 873/415/912 +f 873/415/912 957/548/1014 876/426/915 +f 949/533/997 877/425/916 876/426/915 +f 876/426/915 957/548/1014 949/533/997 +f 688/543/1008 905/466/943 945/525/988 +f 945/525/988 689/526/992 688/543/1008 +f 480/520/435 928/496/968 900/460/937 +f 900/460/937 425/457/431 480/520/435 +f 939/518/981 960/552/1017 897/456/812 +f 897/456/812 896/453/934 939/518/981 +f 939/518/981 930/497/970 929/498/969 +f 929/498/969 960/552/1017 939/518/981 +f 904/464/942 947/528/993 955/545/1011 +f 955/545/1011 956/546/1012 904/464/942 +f 904/464/942 956/546/1012 936/509/976 +f 936/509/976 894/447/932 904/464/942 +f 892/446/929 894/447/932 936/509/976 +f 936/509/976 919/485/959 892/446/929 +f 915/482/955 892/446/929 919/485/959 +f 919/485/959 918/486/958 915/482/955 +f 921/488/961 920/487/960 472/508/482 +f 472/508/482 471/507/481 921/488/961 +f 916/481/956 915/482/955 918/486/958 +f 918/486/958 921/488/961 916/481/956 +f 926/494/966 925/493/965 937/513/977 +f 937/513/977 934/500/974 926/494/966 +f 929/498/969 926/494/966 934/500/974 +f 934/500/974 933/501/973 929/498/969 +f 933/501/973 959/551/1016 960/552/1017 +f 960/552/1017 929/498/969 933/501/973 +f 897/456/812 960/552/1017 959/551/1016 +f 959/551/1016 958/549/1015 897/456/812 +f 958/549/1015 959/551/1016 707/550/798 +f 707/550/798 645/535/679 958/549/1015 +f 902/462/940 954/541/1007 961/554/1018 +f 961/554/1018 504/553/527 902/462/940 +f 504/553/527 429/461/435 902/462/940 +f 935/503/975 468/504/478 504/553/527 +f 504/553/527 961/554/1018 935/503/975 +f 954/541/1007 768/475/155 763/536/1019 +f 763/536/1019 950/536/998 954/541/1007 +f 951/537/999 961/554/1018 954/541/1007 +f 954/541/1007 950/536/998 951/537/999 +f 951/537/999 914/483/954 913/480/953 +f 913/480/953 935/503/975 951/537/999 +f 935/503/975 961/554/1018 951/537/999 +f 488/532/505 948/531/996 912/477/952 +f 912/477/952 442/478/452 488/532/505 +f 949/533/997 878/428/917 877/425/916 +f 763/536/1019 764/449/930 950/536/998 +f 766/475/950 889/444/926 764/449/930 +f 931/499/971 934/500/974 937/513/977 +f 937/513/977 639/555/744 931/499/971 +f 888/440/925 882/438/920 943/523/985 +f 635/524/987 634/530/994 942/521/984 +f 942/521/984 944/522/986 635/524/987 +f 693/547/1013 955/545/1011 698/544/1010 +f 872/416/911 895/452/933 917/484/957 +f 953/539/1002 280/540/1003 496/539/1006 +f 944/522/986 882/438/920 891/445/928 +f 885/439/923 888/440/925 943/523/985 +f 710/240/747 709/241/746 708/556/745 +f 708/556/745 937/513/977 710/240/747 +f 708/556/745 639/555/744 937/513/977 +f 639/555/744 638/514/978 931/499/971 +f 955/545/1011 896/453/934 956/546/1012 +f 898/455/935 897/456/812 958/549/1015 +f 958/549/1015 957/548/1014 898/455/935 +f 769/557/810 912/477/952 948/531/996 +f 770/476/811 912/477/952 769/557/810 +f 644/534/678 769/557/810 948/531/996 +f 898/455/935 957/548/1014 873/415/912 +f 909/471/947 699/527/991 910/470/948 +f 941/519/983 946/519/990 909/471/947 +f 909/471/947 908/472/946 941/519/983 +f 924/490/964 930/497/970 941/519/983 +f 941/519/983 908/472/946 924/490/964 +f 789/288/830 791/289/832 803/303/844 +f 803/303/844 806/307/847 789/288/830 +f 795/294/836 804/302/845 803/303/844 +f 803/303/844 791/289/832 795/294/836 +f 804/302/845 795/294/836 728/318/765 +f 728/318/765 723/324/760 804/302/845 +f 700/220/735 699/221/734 909/559/1020 +f 909/559/1020 962/558/1021 700/220/735 +f 698/218/733 697/219/732 694/217/729 +f 694/217/729 693/214/728 698/218/733 +f 695/216/730 963/561/1022 946/560/1023 +f 946/560/1023 696/215/731 695/216/730 +f 909/559/1020 946/560/1023 963/561/1022 +f 963/561/1022 962/559/1021 909/559/1020 +f 964/562/1024 691/565/1025 687/564/1026 +f 687/564/1026 965/563/1027 964/562/1024 +f 636/566/1028 691/565/1025 964/562/1024 +f 964/562/1024 966/567/1029 636/566/1028 +f 967/568/1030 631/569/1031 636/566/1028 +f 636/566/1028 966/567/1029 967/568/1030 +f 968/571/1032 632/571/1033 631/569/1031 +f 631/569/1031 967/568/1030 968/571/1032 +f 969/572/1034 697/573/1035 632/571/1033 +f 632/571/1033 968/571/1032 969/572/1034 +f 697/573/1035 969/572/1034 970/575/1036 +f 970/575/1036 694/574/1037 697/573/1035 +f 962/578/1038 963/577/1039 695/576/1040 +f 695/576/1040 971/580/1041 972/579/1042 +f 972/579/1042 962/578/1038 695/576/1040 +f 973/581/1043 700/581/1044 962/578/1038 +f 962/578/1038 972/579/1042 973/581/1043 +f 974/582/1045 690/583/1046 700/581/1044 +f 700/581/1044 973/581/1043 974/582/1045 +f 694/574/1037 970/575/1036 971/580/1041 +f 971/580/1041 695/576/1040 694/574/1037 +f 690/583/1046 974/582/1045 965/563/1027 +f 965/563/1027 687/564/1026 690/583/1046 +f 965/563/1027 974/582/1045 975/584/787 +f 975/584/787 964/562/1024 965/563/1027 +f 974/582/1045 973/581/1043 975/584/787 +f 972/579/1042 971/580/1041 975/584/787 +f 975/584/787 973/581/1043 972/579/1042 +f 971/580/1041 970/575/1036 975/584/787 +f 970/575/1036 969/572/1034 975/584/787 +f 969/572/1034 968/571/1032 975/584/787 +f 968/571/1032 967/568/1030 975/584/787 +f 966/567/1029 964/562/1024 975/584/787 +f 975/584/787 967/568/1030 966/567/1029 +f 531/11/567 601/87/637 684/205/719 +f 603/90/639 685/207/720 537/17/573 +f 537/17/573 602/89/638 603/90/639 +f 604/91/640 686/208/721 685/207/720 +f 685/207/720 603/90/639 604/91/640 +f 87/92/87 198/209/1 686/208/721 +f 686/208/721 604/91/640 87/92/87 +f 682/198/717 543/24/579 535/15/571 +f 535/15/571 602/89/638 682/198/717 +f 683/199/718 544/25/580 543/24/579 +f 543/24/579 682/198/717 683/199/718 +f 546/27/582 191/202/193 195/206/197 +f 195/206/197 683/199/718 546/27/582 +f 807/310/848 809/313/850 684/585/719 +f 684/585/719 539/314/575 807/310/848 +f 531/317/567 684/586/719 809/311/850 +f 809/311/850 810/312/851 531/317/567 +f 805/305/846 720/325/757 717/326/754 +f 775/270/816 776/273/817 771/266/812 +f 771/266/812 774/267/815 775/270/816 +f 775/270/816 787/285/828 776/273/817 +f 864/400/903 841/371/881 863/398/902 +f 863/398/902 976/587/1047 864/400/903 +f 865/401/904 864/400/903 976/587/1047 +f 976/587/1047 977/588/1048 865/401/904 +f 866/403/905 865/401/904 977/588/1048 +f 977/588/1048 857/391/896 866/403/905 +f 860/395/899 838/365/879 867/405/906 +f 867/405/906 978/589/1049 860/395/899 +f 978/589/1049 867/405/906 866/403/905 +f 866/403/905 857/391/896 978/589/1049 +f 860/395/899 854/390/893 979/590/1050 +f 979/590/1050 861/394/900 860/395/899 +f 979/590/1050 980/591/1051 862/396/901 +f 862/396/901 861/394/900 979/590/1050 +f 980/592/1051 846/382/885 863/398/902 +f 863/398/902 862/397/901 980/592/1051 +f 976/587/1047 863/398/902 846/382/885 +f 846/382/885 845/379/220 976/587/1047 +f 977/588/1048 976/587/1047 845/379/220 +f 845/379/220 849/383/888 977/588/1048 +f 852/386/891 857/391/896 977/588/1048 +f 977/588/1048 849/383/888 852/386/891 +f 854/390/893 860/395/899 978/589/1049 +f 978/589/1049 855/389/894 854/390/893 +f 855/389/894 978/589/1049 857/391/896 +f 854/390/893 853/387/892 981/593/1052 +f 981/593/1052 979/590/1050 854/390/893 +f 981/593/1052 859/594/898 980/591/1051 +f 980/591/1051 979/590/1050 981/593/1052 +f 859/393/898 847/381/886 846/382/885 +f 846/382/885 980/592/1051 859/393/898 +f 848/380/887 847/381/886 818/334/859 +f 818/334/859 820/335/861 848/380/887 +f 822/338/863 850/384/889 848/380/887 +f 848/380/887 820/335/861 822/338/863 +f 834/359/875 851/385/890 850/384/889 +f 850/384/889 822/338/863 834/359/875 +f 837/360/878 856/388/895 851/385/890 +f 851/385/890 834/359/875 837/360/878 +f 835/360/876 836/361/877 853/387/892 +f 837/360/878 835/360/876 853/387/892 +f 853/387/892 856/388/895 837/360/878 +f 858/392/897 981/593/1052 853/387/892 +f 853/387/892 836/361/877 858/392/897 +f 981/593/1052 858/392/897 817/333/858 +f 817/333/858 859/594/898 981/593/1052 +f 789/288/830 806/307/847 808/309/849 +f 808/309/849 797/296/838 789/288/830 +f 648/162/682 653/164/516 709/595/746 +f 647/160/681 770/265/811 663/177/696 +f 647/160/681 663/177/696 653/164/516 +f 648/162/682 647/160/681 653/164/516 +f 652/165/686 708/237/745 709/595/746 +f 709/595/746 653/164/516 652/165/686 +f 647/512/681 443/479/453 770/476/811 +f 526/596/91 110/115/110 612/113/647 +f 612/113/647 109/114/109 526/596/91 +f 982/597/1053 983/598/1054 984/599/1055 +f 984/599/1055 466/600/1056 982/597/1053 +f 984/599/1055 985/601/1057 463/602/1058 +f 463/602/1058 466/600/1056 984/599/1055 +f 463/602/1058 985/601/1057 986/603/1059 +f 986/603/1059 987/604/1060 143/605/1061 +f 143/605/1061 463/602/1058 986/603/1059 +f 984/599/1055 983/598/1054 988/606/1062 +f 985/601/1057 984/599/1055 988/606/1062 +f 988/606/1062 989/607/1063 985/601/1057 +f 990/608/1064 991/609/1065 985/601/1057 +f 985/601/1057 989/607/1063 990/608/1064 +f 985/601/1057 991/609/1065 986/603/1059 +f 992/610/1066 987/604/1060 986/603/1059 +f 986/603/1059 991/609/1065 992/610/1066 +f 987/604/1060 993/611/1067 994/612/1068 +f 994/612/1068 143/605/1061 987/604/1060 +f 995/613/1069 993/611/1067 987/604/1060 +f 987/604/1060 992/610/1066 995/613/1069 +f 982/597/1053 996/614/1070 983/598/1054 +f 466/600/1056 997/615/1071 982/597/1053 +f 931/602/1072 998/601/1073 999/599/1074 +f 999/599/1074 932/600/1075 931/602/1072 +f 999/599/1074 1000/598/1076 1001/597/1077 +f 1001/597/1077 932/600/1075 999/599/1074 +f 639/605/1078 1002/604/1079 1003/603/1080 +f 1003/603/1080 931/602/1072 639/605/1078 +f 1003/603/1080 998/601/1073 931/602/1072 +f 1004/606/1081 999/599/1074 998/601/1073 +f 998/601/1073 1005/607/1082 1004/606/1081 +f 999/599/1074 1004/606/1081 1000/598/1076 +f 998/601/1073 1003/603/1080 1006/609/1083 +f 1007/608/1084 1005/607/1082 998/601/1073 +f 998/601/1073 1006/609/1083 1007/608/1084 +f 1008/610/1085 1006/609/1083 1003/603/1080 +f 1003/603/1080 1002/604/1079 1008/610/1085 +f 1009/612/1086 1010/611/1087 1002/604/1079 +f 1002/604/1079 639/605/1078 1009/612/1086 +f 1011/613/1088 1008/610/1085 1002/604/1079 +f 1002/604/1079 1010/611/1087 1011/613/1088 +f 1000/598/1076 1012/614/1089 1001/597/1077 +f 932/600/1075 1001/597/1077 1013/615/1090 +# 2000 faces + diff --git a/games/raylib_demo/resources/catsham.png b/games/raylib_demo/resources/catsham.png Binary files differnew file mode 100644 index 00000000..8d7978e0 --- /dev/null +++ b/games/raylib_demo/resources/catsham.png diff --git a/games/raylib_demo/resources/catwhite.png b/games/raylib_demo/resources/catwhite.png Binary files differnew file mode 100644 index 00000000..b849c4c0 --- /dev/null +++ b/games/raylib_demo/resources/catwhite.png diff --git a/games/raylib_demo/resources/fonts/alagard.rbmf b/games/raylib_demo/resources/fonts/alagard.rbmf Binary files differnew file mode 100644 index 00000000..8c9b68d3 --- /dev/null +++ b/games/raylib_demo/resources/fonts/alagard.rbmf diff --git a/games/raylib_demo/resources/fonts/alpha_beta.rbmf b/games/raylib_demo/resources/fonts/alpha_beta.rbmf Binary files differnew file mode 100644 index 00000000..bdb2e752 --- /dev/null +++ b/games/raylib_demo/resources/fonts/alpha_beta.rbmf diff --git a/games/raylib_demo/resources/fonts/custom_alagard.png b/games/raylib_demo/resources/fonts/custom_alagard.png Binary files differnew file mode 100644 index 00000000..c3eb63b7 --- /dev/null +++ b/games/raylib_demo/resources/fonts/custom_alagard.png diff --git a/games/raylib_demo/resources/fonts/custom_jupiter_crash.png b/games/raylib_demo/resources/fonts/custom_jupiter_crash.png Binary files differnew file mode 100644 index 00000000..451b591f --- /dev/null +++ b/games/raylib_demo/resources/fonts/custom_jupiter_crash.png diff --git a/games/raylib_demo/resources/fonts/custom_mecha.png b/games/raylib_demo/resources/fonts/custom_mecha.png Binary files differnew file mode 100644 index 00000000..59caab2c --- /dev/null +++ b/games/raylib_demo/resources/fonts/custom_mecha.png diff --git a/games/raylib_demo/resources/fonts/jupiter_crash.rbmf b/games/raylib_demo/resources/fonts/jupiter_crash.rbmf Binary files differnew file mode 100644 index 00000000..d797e0d6 --- /dev/null +++ b/games/raylib_demo/resources/fonts/jupiter_crash.rbmf diff --git a/games/raylib_demo/resources/fonts/mecha.rbmf b/games/raylib_demo/resources/fonts/mecha.rbmf Binary files differnew file mode 100644 index 00000000..0266a065 --- /dev/null +++ b/games/raylib_demo/resources/fonts/mecha.rbmf diff --git a/games/raylib_demo/resources/fonts/pixantiqua.rbmf b/games/raylib_demo/resources/fonts/pixantiqua.rbmf Binary files differnew file mode 100644 index 00000000..04ef0e25 --- /dev/null +++ b/games/raylib_demo/resources/fonts/pixantiqua.rbmf diff --git a/games/raylib_demo/resources/fonts/pixelplay.rbmf b/games/raylib_demo/resources/fonts/pixelplay.rbmf Binary files differnew file mode 100644 index 00000000..31d14038 --- /dev/null +++ b/games/raylib_demo/resources/fonts/pixelplay.rbmf diff --git a/games/raylib_demo/resources/fonts/romulus.rbmf b/games/raylib_demo/resources/fonts/romulus.rbmf Binary files differnew file mode 100644 index 00000000..be9da01a --- /dev/null +++ b/games/raylib_demo/resources/fonts/romulus.rbmf diff --git a/games/raylib_demo/resources/fonts/setback.rbmf b/games/raylib_demo/resources/fonts/setback.rbmf Binary files differnew file mode 100644 index 00000000..09572215 --- /dev/null +++ b/games/raylib_demo/resources/fonts/setback.rbmf diff --git a/games/raylib_demo/resources/lena.png b/games/raylib_demo/resources/lena.png Binary files differnew file mode 100644 index 00000000..59ef68aa --- /dev/null +++ b/games/raylib_demo/resources/lena.png diff --git a/games/raylib_demo/resources/mandrill.png b/games/raylib_demo/resources/mandrill.png Binary files differnew file mode 100644 index 00000000..a1267573 --- /dev/null +++ b/games/raylib_demo/resources/mandrill.png diff --git a/games/raylib_demo/resources/platforms.png b/games/raylib_demo/resources/platforms.png Binary files differnew file mode 100644 index 00000000..ff69a614 --- /dev/null +++ b/games/raylib_demo/resources/platforms.png diff --git a/games/raylib_demo/resources/raylib_logo.png b/games/raylib_demo/resources/raylib_logo.png Binary files differnew file mode 100644 index 00000000..66545627 --- /dev/null +++ b/games/raylib_demo/resources/raylib_logo.png diff --git a/games/raylib_demo/resources/raylib_logo128x128.png b/games/raylib_demo/resources/raylib_logo128x128.png Binary files differnew file mode 100644 index 00000000..99ba5437 --- /dev/null +++ b/games/raylib_demo/resources/raylib_logo128x128.png diff --git a/games/raylib_demo/resources/raylib_window.png b/games/raylib_demo/resources/raylib_window.png Binary files differnew file mode 100644 index 00000000..e5e08bd4 --- /dev/null +++ b/games/raylib_demo/resources/raylib_window.png diff --git a/games/raylib_demo/resources/raylib_window_01.png b/games/raylib_demo/resources/raylib_window_01.png Binary files differnew file mode 100644 index 00000000..26607b01 --- /dev/null +++ b/games/raylib_demo/resources/raylib_window_01.png diff --git a/games/raylib_demo/resources/raylib_window_02.png b/games/raylib_demo/resources/raylib_window_02.png Binary files differnew file mode 100644 index 00000000..4a636e69 --- /dev/null +++ b/games/raylib_demo/resources/raylib_window_02.png diff --git a/games/raylib_demo/resources/raylib_window_03.png b/games/raylib_demo/resources/raylib_window_03.png Binary files differnew file mode 100644 index 00000000..176b2672 --- /dev/null +++ b/games/raylib_demo/resources/raylib_window_03.png diff --git a/release/html5/libraylib.bc b/release/html5/libraylib.bc Binary files differnew file mode 100644 index 00000000..c73d2480 --- /dev/null +++ b/release/html5/libraylib.bc diff --git a/release/html5/raylib.h b/release/html5/raylib.h new file mode 100644 index 00000000..69966069 --- /dev/null +++ b/release/html5/raylib.h @@ -0,0 +1,506 @@ +/********************************************************************************************** +* +* raylib 1.2 (www.raylib.com) +* +* A simple and easy-to-use library to learn videogames programming +* +* Features: +* Library written in plain C code (C99) +* Uses C# PascalCase/camelCase notation +* Hardware accelerated with OpenGL (1.1, 3.3+ or ES2) +* Unique OpenGL abstraction layer [rlgl] +* Powerful fonts module with SpriteFonts support +* Multiple textures support, including DDS and mipmaps generation +* Basic 3d support for Shapes, Models, Heightmaps and Billboards +* Powerful math module for Vector and Matrix operations [raymath] +* Audio loading and playing with streaming support (WAV and OGG) +* Multiplatform support, including Android devices, Raspberry Pi and HTML5 +* +* Used external libs: +* GLFW3 (www.glfw.org) for window/context management and input +* GLEW for OpenGL extensions loading (3.3+ and ES2) +* stb_image (Sean Barret) for images loading (JPEG, PNG, BMP, TGA, PSD, GIF, HDR, PIC) +* stb_image_write (Sean Barret) for image writting (PNG) +* stb_vorbis (Sean Barret) for ogg audio loading +* OpenAL Soft for audio device/context management +* tinfl for data decompression (DEFLATE algorithm) +* +* Some design decisions: +* 32bit Colors - All defined color are always RGBA +* 32bit Textures - All loaded images are converted automatically to RGBA textures +* SpriteFonts - All loaded sprite-font images are converted to RGBA and POT textures +* One custom default font is loaded automatically when InitWindow() +* If using OpenGL 3.3+ or ES2, one default shader is loaded automatically (internally defined) +* +* -- LICENSE (raylib v1.2, September 2014) -- +* +* raylib is licensed under an unmodified zlib/libpng license, which is an OSI-certified, +* BSD-like license that allows static linking with closed source software: +* +* Copyright (c) 2013 Ramon Santamaria (Ray San - raysan@raysanweb.com) +* +* This software is provided "as-is", without any express or implied warranty. In no event +* will the authors be held liable for any damages arising from the use of this software. +* +* Permission is granted to anyone to use this software for any purpose, including commercial +* applications, and to alter it and redistribute it freely, subject to the following restrictions: +* +* 1. The origin of this software must not be misrepresented; you must not claim that you +* wrote the original software. If you use this software in a product, an acknowledgment +* in the product documentation would be appreciated but is not required. +* +* 2. Altered source versions must be plainly marked as such, and must not be misrepresented +* as being the original software. +* +* 3. This notice may not be removed or altered from any source distribution. +* +**********************************************************************************************/ + +#ifndef RAYLIB_H +#define RAYLIB_H + +// Choose your platform here or just define it at compile time: -DPLATFORM_DESKTOP +//#define PLATFORM_DESKTOP // Windows, Linux or OSX +//#define PLATFORM_ANDROID // Android device +//#define PLATFORM_RPI // Raspberry Pi +//#define PLATFORM_WEB // HTML5 (emscripten, asm.js) + +// Security check in case no PLATFORM_* defined +#if !defined(PLATFORM_DESKTOP) && !defined(PLATFORM_ANDROID) && !defined(PLATFORM_RPI) && !defined(PLATFORM_WEB) + #define PLATFORM_DESKTOP +#endif + +#if defined(PLATFORM_ANDROID) + #include <android_native_app_glue.h> // Defines android_app struct +#endif + +//---------------------------------------------------------------------------------- +// Some basic Defines +//---------------------------------------------------------------------------------- +#ifndef PI +#define PI 3.14159265358979323846 +#endif + +#define DEG2RAD (PI / 180.0f) +#define RAD2DEG (180.0f / PI) + +// raylib Config Flags +#define FLAG_FULLSCREEN_MODE 1 +#define FLAG_SHOW_LOGO 2 +#define FLAG_SHOW_MOUSE_CURSOR 4 +#define FLAG_CENTERED_MODE 8 +#define FLAG_MSAA_4X_HINT 16 + +// Keyboard Function Keys +#define KEY_SPACE 32 +#define KEY_ESCAPE 256 +#define KEY_ENTER 257 +#define KEY_BACKSPACE 259 +#define KEY_RIGHT 262 +#define KEY_LEFT 263 +#define KEY_DOWN 264 +#define KEY_UP 265 +#define KEY_F1 290 +#define KEY_F2 291 +#define KEY_F3 292 +#define KEY_F4 293 +#define KEY_F5 294 +#define KEY_F6 295 +#define KEY_F7 296 +#define KEY_F8 297 +#define KEY_F9 298 +#define KEY_F10 299 +#define KEY_LEFT_SHIFT 340 +#define KEY_LEFT_CONTROL 341 +#define KEY_LEFT_ALT 342 +#define KEY_RIGHT_SHIFT 344 +#define KEY_RIGHT_CONTROL 345 +#define KEY_RIGHT_ALT 346 + +// Mouse Buttons +#define MOUSE_LEFT_BUTTON 0 +#define MOUSE_RIGHT_BUTTON 1 +#define MOUSE_MIDDLE_BUTTON 2 + +// Gamepad Number +#define GAMEPAD_PLAYER1 0 +#define GAMEPAD_PLAYER2 1 +#define GAMEPAD_PLAYER3 2 +#define GAMEPAD_PLAYER4 3 + +// Gamepad Buttons +// NOTE: Adjusted for a PS3 USB Controller +#define GAMEPAD_BUTTON_A 2 +#define GAMEPAD_BUTTON_B 1 +#define GAMEPAD_BUTTON_X 3 +#define GAMEPAD_BUTTON_Y 4 +#define GAMEPAD_BUTTON_R1 7 +#define GAMEPAD_BUTTON_R2 5 +#define GAMEPAD_BUTTON_L1 6 +#define GAMEPAD_BUTTON_L2 8 +#define GAMEPAD_BUTTON_SELECT 9 +#define GAMEPAD_BUTTON_START 10 + +// TODO: Review Xbox360 USB Controller Buttons + +// Some Basic Colors +// NOTE: Custom raylib color palette for amazing visuals on WHITE background +#define LIGHTGRAY (Color){ 200, 200, 200, 255 } // Light Gray +#define GRAY (Color){ 130, 130, 130, 255 } // Gray +#define DARKGRAY (Color){ 80, 80, 80, 255 } // Dark Gray +#define YELLOW (Color){ 253, 249, 0, 255 } // Yellow +#define GOLD (Color){ 255, 203, 0, 255 } // Gold +#define ORANGE (Color){ 255, 161, 0, 255 } // Orange +#define PINK (Color){ 255, 109, 194, 255 } // Pink +#define RED (Color){ 230, 41, 55, 255 } // Red +#define MAROON (Color){ 190, 33, 55, 255 } // Maroon +#define GREEN (Color){ 0, 228, 48, 255 } // Green +#define LIME (Color){ 0, 158, 47, 255 } // Lime +#define DARKGREEN (Color){ 0, 117, 44, 255 } // Dark Green +#define SKYBLUE (Color){ 102, 191, 255, 255 } // Sky Blue +#define BLUE (Color){ 0, 121, 241, 255 } // Blue +#define DARKBLUE (Color){ 0, 82, 172, 255 } // Dark Blue +#define PURPLE (Color){ 200, 122, 255, 255 } // Purple +#define VIOLET (Color){ 135, 60, 190, 255 } // Violet +#define DARKPURPLE (Color){ 112, 31, 126, 255 } // Dark Purple +#define BEIGE (Color){ 211, 176, 131, 255 } // Beige +#define BROWN (Color){ 127, 106, 79, 255 } // Brown +#define DARKBROWN (Color){ 76, 63, 47, 255 } // Dark Brown + +#define WHITE (Color){ 255, 255, 255, 255 } // White +#define BLACK (Color){ 0, 0, 0, 255 } // Black +#define BLANK (Color){ 0, 0, 0, 0 } // Blank (Transparent) +#define MAGENTA (Color){ 255, 0, 255, 255 } // Magenta +#define RAYWHITE (Color){ 245, 245, 245, 255 } // My own White (raylib logo) + +//---------------------------------------------------------------------------------- +// Types and Structures Definition +//---------------------------------------------------------------------------------- + +// Boolean type +typedef enum { false, true } bool; + +// byte type +typedef unsigned char byte; + +// Vector2 type +typedef struct Vector2 { + float x; + float y; +} Vector2; + +// Vector3 type +typedef struct Vector3 { + float x; + float y; + float z; +} Vector3; + +// Color type, RGBA (32bit) +typedef struct Color { + unsigned char r; + unsigned char g; + unsigned char b; + unsigned char a; +} Color; + +// Rectangle type +typedef struct Rectangle { + int x; + int y; + int width; + int height; +} Rectangle; + +// Image type, bpp always RGBA (32bit) +// NOTE: Data stored in CPU memory (RAM) +typedef struct Image { + Color *pixels; + int width; + int height; +} Image; + +// Texture2D type, bpp always RGBA (32bit) +// NOTE: Data stored in GPU memory +typedef struct Texture2D { + unsigned int id; // OpenGL id + int width; + int height; +} Texture2D; + +// Character type (one font glyph) +typedef struct Character { + int value; //char value = ' '; (int)value = 32; + int x; + int y; + int w; + int h; +} Character; + +// SpriteFont type, includes texture and charSet array data +typedef struct SpriteFont { + Texture2D texture; + int numChars; + Character *charSet; +} SpriteFont; + +// Camera type, defines a camera position/orientation in 3d space +typedef struct Camera { + Vector3 position; + Vector3 target; + Vector3 up; +} Camera; + +// Vertex data definning a mesh +typedef struct VertexData { + int vertexCount; + float *vertices; // 3 components per vertex + float *texcoords; // 2 components per vertex + float *normals; // 3 components per vertex + unsigned char *colors; // 4 components per vertex +} VertexData; + +// 3d Model type +// NOTE: If using OpenGL 1.1, loaded in CPU (mesh); if OpenGL 3.3+ loaded in GPU (vaoId) +typedef struct Model { + VertexData mesh; + unsigned int vaoId; + unsigned int vboId[4]; + unsigned int textureId; + //Matrix transform; +} Model; + +// Sound source type +typedef struct Sound { + unsigned int source; + unsigned int buffer; +} Sound; + +// Wave type, defines audio wave data +typedef struct Wave { + void *data; // Buffer data pointer + unsigned int dataSize; // Data size in bytes + unsigned int sampleRate; + short bitsPerSample; + short channels; +} Wave; + +#ifdef __cplusplus +extern "C" { // Prevents name mangling of functions +#endif + +//------------------------------------------------------------------------------------ +// Global Variables Definition +//------------------------------------------------------------------------------------ +// It's lonely here... + +//------------------------------------------------------------------------------------ +// Window and Graphics Device Functions (Module: core) +//------------------------------------------------------------------------------------ +#if defined(PLATFORM_ANDROID) +void InitWindow(int width, int height, struct android_app *state); // Init Android activity +#elif defined(PLATFORM_DESKTOP) || defined(PLATFORM_RPI) || defined(PLATFORM_WEB) +void InitWindow(int width, int height, const char *title); // Initialize Window and OpenGL Graphics +#endif + +void CloseWindow(void); // Close Window and Terminate Context +bool WindowShouldClose(void); // Detect if KEY_ESCAPE pressed or Close icon pressed +void ToggleFullscreen(void); // Fullscreen toggle (only PLATFORM_DESKTOP) +#if defined(PLATFORM_DESKTOP) || defined(PLATFORM_RPI) +void SetCustomCursor(const char *cursorImage); // Set a custom cursor icon/image +void SetExitKey(int key); // Set a custom key to exit program (default is ESC) +#endif +int GetScreenWidth(void); // Get current screen width +int GetScreenHeight(void); // Get current screen height +int GetKeyPressed(void); // Get latest key pressed + +void ClearBackground(Color color); // Sets Background Color +void BeginDrawing(void); // Setup drawing canvas to start drawing +void EndDrawing(void); // End canvas drawing and Swap Buffers (Double Buffering) + +void Begin3dMode(Camera cam); // Initializes 3D mode for drawing (Camera setup) +void End3dMode(void); // Ends 3D mode and returns to default 2D orthographic mode + +void SetTargetFPS(int fps); // Set target FPS (maximum) +float GetFPS(void); // Returns current FPS +float GetFrameTime(void); // Returns time in seconds for one frame + +Color GetColor(int hexValue); // Returns a Color struct from hexadecimal value +int GetHexValue(Color color); // Returns hexadecimal value for a Color + +int GetRandomValue(int min, int max); // Returns a random value between min and max (both included) +Color Fade(Color color, float alpha); // Color fade-in or fade-out, alpha goes from 0.0f to 1.0f + +void SetupFlags(char flags); // Enable some window configurations +void ShowLogo(void); // Activates raylib logo at startup (can be done with flags) + +//------------------------------------------------------------------------------------ +// Input Handling Functions (Module: core) +//------------------------------------------------------------------------------------ +#if defined(PLATFORM_DESKTOP) || defined(PLATFORM_RPI) || defined(PLATFORM_WEB) +bool IsKeyPressed(int key); // Detect if a key has been pressed once +bool IsKeyDown(int key); // Detect if a key is being pressed +bool IsKeyReleased(int key); // Detect if a key has been released once +bool IsKeyUp(int key); // Detect if a key is NOT being pressed + +bool IsMouseButtonPressed(int button); // Detect if a mouse button has been pressed once +bool IsMouseButtonDown(int button); // Detect if a mouse button is being pressed +bool IsMouseButtonReleased(int button); // Detect if a mouse button has been released once +bool IsMouseButtonUp(int button); // Detect if a mouse button is NOT being pressed +int GetMouseX(void); // Returns mouse position X +int GetMouseY(void); // Returns mouse position Y +Vector2 GetMousePosition(void); // Returns mouse position XY +void SetMousePosition(Vector2 position); // Set mouse position XY +int GetMouseWheelMove(void); // Returns mouse wheel movement Y +#endif + +#if defined(PLATFORM_DESKTOP) +bool IsGamepadAvailable(int gamepad); // Detect if a gamepad is available +Vector2 GetGamepadMovement(int gamepad); // Return axis movement vector for a gamepad +bool IsGamepadButtonPressed(int gamepad, int button); // Detect if a gamepad button has been pressed once +bool IsGamepadButtonDown(int gamepad, int button); // Detect if a gamepad button is being pressed +bool IsGamepadButtonReleased(int gamepad, int button); // Detect if a gamepad button has been released once +bool IsGamepadButtonUp(int gamepad, int button); // Detect if a gamepad button is NOT being pressed +#endif + +#if defined(PLATFORM_ANDROID) +bool IsScreenTouched(void); // Detect screen touch event +int GetTouchX(void); // Returns touch position X +int GetTouchY(void); // Returns touch position Y +Vector2 GetTouchPosition(void); // Returns touch position XY +#endif + +//------------------------------------------------------------------------------------ +// Basic Shapes Drawing Functions (Module: shapes) +//------------------------------------------------------------------------------------ +void DrawPixel(int posX, int posY, Color color); // Draw a pixel +void DrawPixelV(Vector2 position, Color color); // Draw a pixel (Vector version) +void DrawLine(int startPosX, int startPosY, int endPosX, int endPosY, Color color); // Draw a line +void DrawLineV(Vector2 startPos, Vector2 endPos, Color color); // Draw a line (Vector version) +void DrawCircle(int centerX, int centerY, float radius, Color color); // Draw a color-filled circle +void DrawCircleGradient(int centerX, int centerY, float radius, Color color1, Color color2); // Draw a gradient-filled circle +void DrawCircleV(Vector2 center, float radius, Color color); // Draw a color-filled circle (Vector version) +void DrawCircleLines(int centerX, int centerY, float radius, Color color); // Draw circle outline +void DrawRectangle(int posX, int posY, int width, int height, Color color); // Draw a color-filled rectangle +void DrawRectangleRec(Rectangle rec, Color color); // Draw a color-filled rectangle +void DrawRectangleGradient(int posX, int posY, int width, int height, Color color1, Color color2); // Draw a gradient-filled rectangle +void DrawRectangleV(Vector2 position, Vector2 size, Color color); // Draw a color-filled rectangle (Vector version) +void DrawRectangleLines(int posX, int posY, int width, int height, Color color); // Draw rectangle outline +void DrawTriangle(Vector2 v1, Vector2 v2, Vector2 v3, Color color); // Draw a color-filled triangle +void DrawTriangleLines(Vector2 v1, Vector2 v2, Vector2 v3, Color color); // Draw triangle outline +void DrawPoly(Vector2 center, int sides, float radius, float rotation, Color color); // Draw a regular polygon (Vector version) +void DrawPolyEx(Vector2 *points, int numPoints, Color color); // Draw a closed polygon defined by points +void DrawPolyExLines(Vector2 *points, int numPoints, Color color); // Draw polygon lines + +bool CheckCollisionRecs(Rectangle rec1, Rectangle rec2); // Check collision between two rectangles +bool CheckCollisionCircles(Vector2 center1, float radius1, Vector2 center2, float radius2); // Check collision between two circles +bool CheckCollisionCircleRec(Vector2 center, float radius, Rectangle rec); // Check collision between circle and rectangle +Rectangle GetCollisionRec(Rectangle rec1, Rectangle rec2); // Get collision rectangle for two rectangles collision +bool CheckCollisionPointRec(Vector2 point, Rectangle rec); // Check if point is inside rectangle +bool CheckCollisionPointCircle(Vector2 point, Vector2 center, float radius); // Check if point is inside circle +bool CheckCollisionPointTriangle(Vector2 point, Vector2 p1, Vector2 p2, Vector2 p3); // Check if point is inside a triangle + +//------------------------------------------------------------------------------------ +// Texture Loading and Drawing Functions (Module: textures) +//------------------------------------------------------------------------------------ +Image LoadImage(const char *fileName); // Load an image into CPU memory (RAM) +Image LoadImageFromRES(const char *rresName, int resId); // Load an image from rRES file (raylib Resource) +Texture2D LoadTexture(const char *fileName); // Load an image as texture into GPU memory +Texture2D LoadTextureFromRES(const char *rresName, int resId); // Load an image as texture from rRES file (raylib Resource) +Texture2D LoadTextureFromImage(Image image, bool genMipmaps); // Load a texture from image data (and generate mipmaps) +Texture2D CreateTexture(Image image, bool genMipmaps); // [DEPRECATED] Same as LoadTextureFromImage() +void UnloadImage(Image image); // Unload image from CPU memory (RAM) +void UnloadTexture(Texture2D texture); // Unload texture from GPU memory +void ConvertToPOT(Image *image, Color fillColor); // Convert image to POT (power-of-two) + +void DrawTexture(Texture2D texture, int posX, int posY, Color tint); // Draw a Texture2D +void DrawTextureV(Texture2D texture, Vector2 position, Color tint); // Draw a Texture2D with position defined as Vector2 +void DrawTextureEx(Texture2D texture, Vector2 position, float rotation, float scale, Color tint); // Draw a Texture2D with extended parameters +void DrawTextureRec(Texture2D texture, Rectangle sourceRec, Vector2 position, Color tint); // Draw a part of a texture defined by a rectangle +void DrawTexturePro(Texture2D texture, Rectangle sourceRec, Rectangle destRec, Vector2 origin, // Draw a part of a texture defined by a rectangle with 'pro' parameters + float rotation, Color tint); + +//------------------------------------------------------------------------------------ +// Font Loading and Text Drawing Functions (Module: text) +//------------------------------------------------------------------------------------ +SpriteFont GetDefaultFont(void); // Get the default SpriteFont +SpriteFont LoadSpriteFont(const char *fileName); // Load a SpriteFont image into GPU memory +void UnloadSpriteFont(SpriteFont spriteFont); // Unload SpriteFont from GPU memory + +void DrawText(const char *text, int posX, int posY, int fontSize, Color color); // Draw text (using default font) +void DrawTextEx(SpriteFont spriteFont, const char* text, Vector2 position, // Draw text using SpriteFont and additional parameters + int fontSize, int spacing, Color tint); +int MeasureText(const char *text, int fontSize); // Measure string width for default font +Vector2 MeasureTextEx(SpriteFont spriteFont, const char *text, int fontSize, int spacing); // Measure string size for SpriteFont +int GetFontBaseSize(SpriteFont spriteFont); // Returns the base size for a SpriteFont (chars height) +void DrawFPS(int posX, int posY); // Shows current FPS on top-left corner +const char *FormatText(const char *text, ...); // Formatting of text with variables to 'embed' + +//------------------------------------------------------------------------------------ +// Basic 3d Shapes Drawing Functions (Module: models) +//------------------------------------------------------------------------------------ +void DrawCube(Vector3 position, float width, float height, float lenght, Color color); // Draw cube +void DrawCubeV(Vector3 position, Vector3 size, Color color); // Draw cube (Vector version) +void DrawCubeWires(Vector3 position, float width, float height, float lenght, Color color); // Draw cube wires +void DrawCubeTexture(Texture2D texture, Vector3 position, float width, float height, float lenght, Color color); // Draw cube textured +void DrawSphere(Vector3 centerPos, float radius, Color color); // Draw sphere +void DrawSphereEx(Vector3 centerPos, float radius, int rings, int slices, Color color); // Draw sphere with extended parameters +void DrawSphereWires(Vector3 centerPos, float radius, int rings, int slices, Color color); // Draw sphere wires +void DrawCylinder(Vector3 position, float radiusTop, float radiusBottom, float height, int slices, Color color); // Draw a cylinder/cone +void DrawCylinderWires(Vector3 position, float radiusTop, float radiusBottom, float height, int slices, Color color); // Draw a cylinder/cone wires +void DrawQuad(Vector3 vertices[4], Vector2 textcoords[4], Vector3 normals[4], Color colors[4]); // Draw a quad +void DrawPlane(Vector3 centerPos, Vector2 size, Vector3 rotation, Color color); // Draw a plane +void DrawPlaneEx(Vector3 centerPos, Vector2 size, Vector3 rotation, int slicesX, int slicesZ, Color color); // Draw a plane with divisions +void DrawGrid(int slices, float spacing); // Draw a grid (centered at (0, 0, 0)) +void DrawGizmo(Vector3 position); // Draw simple gizmo +void DrawGizmoEx(Vector3 position, Vector3 rotation, float scale); // Draw gizmo with extended parameters +//DrawTorus(), DrawTeapot() are useless... + +//------------------------------------------------------------------------------------ +// Model 3d Loading and Drawing Functions (Module: models) +//------------------------------------------------------------------------------------ +Model LoadModel(const char *fileName); // Load a 3d model (.OBJ) +//Model LoadModelFromRES(const char *rresName, int resId); // TODO: Load a 3d model from rRES file (raylib Resource) +Model LoadHeightmap(Image heightmap, float maxHeight); // Load a heightmap image as a 3d model +Model LoadCubicmap(Image cubicmap); // Load a map image as a 3d model (cubes based) +void UnloadModel(Model model); // Unload 3d model from memory +void SetModelTexture(Model *model, Texture2D texture); // Link a texture to a model + +void DrawModel(Model model, Vector3 position, float scale, Color tint); // Draw a model (with texture if set) +void DrawModelEx(Model model, Vector3 position, Vector3 rotation, Vector3 scale, Color tint); // Draw a model with extended parameters +void DrawModelWires(Model model, Vector3 position, float scale, Color color); // Draw a model wires (with texture if set) + +void DrawBillboard(Camera camera, Texture2D texture, Vector3 center, float size, Color tint); // Draw a billboard texture +void DrawBillboardRec(Camera camera, Texture2D texture, Rectangle sourceRec, Vector3 center, float size, Color tint); // Draw a billboard texture defined by sourceRec + +//------------------------------------------------------------------------------------ +// Audio Loading and Playing Functions (Module: audio) +//------------------------------------------------------------------------------------ +void InitAudioDevice(void); // Initialize audio device and context +void CloseAudioDevice(void); // Close the audio device and context (and music stream) + +Sound LoadSound(char *fileName); // Load sound to memory +Sound LoadSoundFromWave(Wave wave); // Load sound to memory from wave data +Sound LoadSoundFromRES(const char *rresName, int resId); // Load sound to memory from rRES file (raylib Resource) +void UnloadSound(Sound sound); // Unload sound +void PlaySound(Sound sound); // Play a sound +void PauseSound(Sound sound); // Pause a sound +void StopSound(Sound sound); // Stop playing a sound +bool SoundIsPlaying(Sound sound); // Check if a sound is currently playing +void SetSoundVolume(Sound sound, float volume); // Set volume for a sound (1.0 is max level) +void SetSoundPitch(Sound sound, float pitch); // Set pitch for a sound (1.0 is base level) + +void PlayMusicStream(char *fileName); // Start music playing (open stream) +void StopMusicStream(void); // Stop music playing (close stream) +void PauseMusicStream(void); // Pause music playing +void ResumeMusicStream(void); // Resume playing paused music +bool MusicIsPlaying(void); // Check if music is playing +void SetMusicVolume(float volume); // Set volume for music (1.0 is max level) +float GetMusicTimeLength(void); // Get current music time length (in seconds) +float GetMusicTimePlayed(void); // Get current music time played (in seconds) + +#ifdef __cplusplus +} +#endif + +#endif // RAYLIB_H diff --git a/release/win32-mingw/include/raylib.h b/release/win32-mingw/include/raylib.h index 5257de58..69966069 100644 --- a/release/win32-mingw/include/raylib.h +++ b/release/win32-mingw/include/raylib.h @@ -14,7 +14,7 @@ * Basic 3d support for Shapes, Models, Heightmaps and Billboards * Powerful math module for Vector and Matrix operations [raymath] * Audio loading and playing with streaming support (WAV and OGG) -* Multiplatform support, including Android devices and Raspberry Pi +* Multiplatform support, including Android devices, Raspberry Pi and HTML5 * * Used external libs: * GLFW3 (www.glfw.org) for window/context management and input @@ -63,9 +63,10 @@ //#define PLATFORM_DESKTOP // Windows, Linux or OSX //#define PLATFORM_ANDROID // Android device //#define PLATFORM_RPI // Raspberry Pi +//#define PLATFORM_WEB // HTML5 (emscripten, asm.js) // Security check in case no PLATFORM_* defined -#if !defined(PLATFORM_DESKTOP) && !defined(PLATFORM_ANDROID) && !defined(PLATFORM_RPI) +#if !defined(PLATFORM_DESKTOP) && !defined(PLATFORM_ANDROID) && !defined(PLATFORM_RPI) && !defined(PLATFORM_WEB) #define PLATFORM_DESKTOP #endif @@ -179,6 +180,9 @@ // Boolean type typedef enum { false, true } bool; +// byte type +typedef unsigned char byte; + // Vector2 type typedef struct Vector2 { float x; @@ -225,8 +229,13 @@ typedef struct Texture2D { } Texture2D; // Character type (one font glyph) -// NOTE: Defined in module: text -typedef struct Character Character; +typedef struct Character { + int value; //char value = ' '; (int)value = 32; + int x; + int y; + int w; + int h; +} Character; // SpriteFont type, includes texture and charSet array data typedef struct SpriteFont { @@ -267,6 +276,15 @@ typedef struct Sound { unsigned int buffer; } Sound; +// Wave type, defines audio wave data +typedef struct Wave { + void *data; // Buffer data pointer + unsigned int dataSize; // Data size in bytes + unsigned int sampleRate; + short bitsPerSample; + short channels; +} Wave; + #ifdef __cplusplus extern "C" { // Prevents name mangling of functions #endif @@ -281,7 +299,7 @@ extern "C" { // Prevents name mangling of functions //------------------------------------------------------------------------------------ #if defined(PLATFORM_ANDROID) void InitWindow(int width, int height, struct android_app *state); // Init Android activity -#elif defined(PLATFORM_DESKTOP) || defined(PLATFORM_RPI) +#elif defined(PLATFORM_DESKTOP) || defined(PLATFORM_RPI) || defined(PLATFORM_WEB) void InitWindow(int width, int height, const char *title); // Initialize Window and OpenGL Graphics #endif @@ -294,6 +312,7 @@ void SetExitKey(int key); // Set a custom key #endif int GetScreenWidth(void); // Get current screen width int GetScreenHeight(void); // Get current screen height +int GetKeyPressed(void); // Get latest key pressed void ClearBackground(Color color); // Sets Background Color void BeginDrawing(void); // Setup drawing canvas to start drawing @@ -313,13 +332,12 @@ int GetRandomValue(int min, int max); // Returns a random Color Fade(Color color, float alpha); // Color fade-in or fade-out, alpha goes from 0.0f to 1.0f void SetupFlags(char flags); // Enable some window configurations - -void ShowLogo(void); // Activates raylib logo at startup +void ShowLogo(void); // Activates raylib logo at startup (can be done with flags) //------------------------------------------------------------------------------------ // Input Handling Functions (Module: core) //------------------------------------------------------------------------------------ -#if defined(PLATFORM_DESKTOP) || defined(PLATFORM_RPI) +#if defined(PLATFORM_DESKTOP) || defined(PLATFORM_RPI) || defined(PLATFORM_WEB) bool IsKeyPressed(int key); // Detect if a key has been pressed once bool IsKeyDown(int key); // Detect if a key is being pressed bool IsKeyReleased(int key); // Detect if a key has been released once @@ -332,8 +350,11 @@ bool IsMouseButtonUp(int button); // Detect if a mouse but int GetMouseX(void); // Returns mouse position X int GetMouseY(void); // Returns mouse position Y Vector2 GetMousePosition(void); // Returns mouse position XY +void SetMousePosition(Vector2 position); // Set mouse position XY int GetMouseWheelMove(void); // Returns mouse wheel movement Y +#endif +#if defined(PLATFORM_DESKTOP) bool IsGamepadAvailable(int gamepad); // Detect if a gamepad is available Vector2 GetGamepadMovement(int gamepad); // Return axis movement vector for a gamepad bool IsGamepadButtonPressed(int gamepad, int button); // Detect if a gamepad button has been pressed once @@ -386,9 +407,11 @@ Image LoadImage(const char *fileName); Image LoadImageFromRES(const char *rresName, int resId); // Load an image from rRES file (raylib Resource) Texture2D LoadTexture(const char *fileName); // Load an image as texture into GPU memory Texture2D LoadTextureFromRES(const char *rresName, int resId); // Load an image as texture from rRES file (raylib Resource) -Texture2D CreateTexture(Image image, bool genMipmaps); // Create a Texture2D from Image data (and generate mipmaps) +Texture2D LoadTextureFromImage(Image image, bool genMipmaps); // Load a texture from image data (and generate mipmaps) +Texture2D CreateTexture(Image image, bool genMipmaps); // [DEPRECATED] Same as LoadTextureFromImage() void UnloadImage(Image image); // Unload image from CPU memory (RAM) void UnloadTexture(Texture2D texture); // Unload texture from GPU memory +void ConvertToPOT(Image *image, Color fillColor); // Convert image to POT (power-of-two) void DrawTexture(Texture2D texture, int posX, int posY, Color tint); // Draw a Texture2D void DrawTextureV(Texture2D texture, Vector2 position, Color tint); // Draw a Texture2D with position defined as Vector2 @@ -425,6 +448,7 @@ void DrawSphereEx(Vector3 centerPos, float radius, int rings, int slices, Color void DrawSphereWires(Vector3 centerPos, float radius, int rings, int slices, Color color); // Draw sphere wires void DrawCylinder(Vector3 position, float radiusTop, float radiusBottom, float height, int slices, Color color); // Draw a cylinder/cone void DrawCylinderWires(Vector3 position, float radiusTop, float radiusBottom, float height, int slices, Color color); // Draw a cylinder/cone wires +void DrawQuad(Vector3 vertices[4], Vector2 textcoords[4], Vector3 normals[4], Color colors[4]); // Draw a quad void DrawPlane(Vector3 centerPos, Vector2 size, Vector3 rotation, Color color); // Draw a plane void DrawPlaneEx(Vector3 centerPos, Vector2 size, Vector3 rotation, int slicesX, int slicesZ, Color color); // Draw a plane with divisions void DrawGrid(int slices, float spacing); // Draw a grid (centered at (0, 0, 0)) @@ -438,7 +462,7 @@ void DrawGizmoEx(Vector3 position, Vector3 rotation, float scale); Model LoadModel(const char *fileName); // Load a 3d model (.OBJ) //Model LoadModelFromRES(const char *rresName, int resId); // TODO: Load a 3d model from rRES file (raylib Resource) Model LoadHeightmap(Image heightmap, float maxHeight); // Load a heightmap image as a 3d model -Model LoadCubesmap(Image cubesmap); // Load a map image as a 3d model (cubes based) +Model LoadCubicmap(Image cubicmap); // Load a map image as a 3d model (cubes based) void UnloadModel(Model model); // Unload 3d model from memory void SetModelTexture(Model *model, Texture2D texture); // Link a texture to a model @@ -456,6 +480,7 @@ void InitAudioDevice(void); // Initialize au void CloseAudioDevice(void); // Close the audio device and context (and music stream) Sound LoadSound(char *fileName); // Load sound to memory +Sound LoadSoundFromWave(Wave wave); // Load sound to memory from wave data Sound LoadSoundFromRES(const char *rresName, int resId); // Load sound to memory from rRES file (raylib Resource) void UnloadSound(Sound sound); // Unload sound void PlaySound(Sound sound); // Play a sound diff --git a/release/win32-mingw/lib/libraylib.a b/release/win32-mingw/lib/libraylib.a Binary files differindex 7c561f93..d9321248 100644 --- a/release/win32-mingw/lib/libraylib.a +++ b/release/win32-mingw/lib/libraylib.a diff --git a/src/audio.c b/src/audio.c index 3cdeef94..40c24895 100644 --- a/src/audio.c +++ b/src/audio.c @@ -45,9 +45,14 @@ // Defines and Macros //---------------------------------------------------------------------------------- #define MUSIC_STREAM_BUFFERS 2 -#define MUSIC_BUFFER_SIZE 4096*2 // PCM data buffer (short) - 16Kb - // NOTE: Reduced to avoid frame-stalls on RPI -//#define MUSIC_BUFFER_SIZE 4096*8 // PCM data buffer (short) - 64Kb + +#if defined(PLATFORM_RPI) + // NOTE: On RPI should be lower to avoid frame-stalls + #define MUSIC_BUFFER_SIZE 4096*2 // PCM data buffer (short) - 16Kb (RPI) +#else + // NOTE: On HTML5 (emscripten) this is allocated on heap, by default it's only 16MB!...just take care... + #define MUSIC_BUFFER_SIZE 4096*8 // PCM data buffer (short) - 64Kb +#endif //---------------------------------------------------------------------------------- // Types and Structures Definition @@ -69,15 +74,6 @@ typedef struct Music { } Music; -// Wave file data -typedef struct Wave { - void *data; // Buffer data pointer - unsigned int dataSize; // Data size in bytes - unsigned int sampleRate; - short bitsPerSample; - short channels; -} Wave; - //---------------------------------------------------------------------------------- // Global Variables Definition //---------------------------------------------------------------------------------- @@ -106,7 +102,7 @@ void InitAudioDevice(void) // Open and initialize a device with default settings ALCdevice *device = alcOpenDevice(NULL); - if(!device) TraceLog(ERROR, "Could not open audio device"); + if(!device) TraceLog(ERROR, "Audio device could not be opened"); ALCcontext *context = alcCreateContext(device, NULL); @@ -205,12 +201,64 @@ Sound LoadSound(char *fileName) // Attach sound buffer to source alSourcei(source, AL_BUFFER, buffer); + + TraceLog(INFO, "[%s] Sound file loaded successfully (SampleRate: %i, BitRate: %i, Channels: %i)", fileName, wave.sampleRate, wave.bitsPerSample, wave.channels); // Unallocate WAV data UnloadWave(wave); - TraceLog(INFO, "[%s] Sound file loaded successfully", fileName); - TraceLog(INFO, "[%s] Sample rate: %i - Channels: %i", fileName, wave.sampleRate, wave.channels); + sound.source = source; + sound.buffer = buffer; + } + + return sound; +} + +// Load sound from wave data +Sound LoadSoundFromWave(Wave wave) +{ + Sound sound; + + if (wave.data != NULL) + { + ALenum format = 0; + // The OpenAL format is worked out by looking at the number of channels and the bits per sample + if (wave.channels == 1) + { + if (wave.bitsPerSample == 8 ) format = AL_FORMAT_MONO8; + else if (wave.bitsPerSample == 16) format = AL_FORMAT_MONO16; + } + else if (wave.channels == 2) + { + if (wave.bitsPerSample == 8 ) format = AL_FORMAT_STEREO8; + else if (wave.bitsPerSample == 16) format = AL_FORMAT_STEREO16; + } + + // Create an audio source + ALuint source; + alGenSources(1, &source); // Generate pointer to audio source + + alSourcef(source, AL_PITCH, 1); + alSourcef(source, AL_GAIN, 1); + alSource3f(source, AL_POSITION, 0, 0, 0); + alSource3f(source, AL_VELOCITY, 0, 0, 0); + alSourcei(source, AL_LOOPING, AL_FALSE); + + // Convert loaded data to OpenAL buffer + //---------------------------------------- + ALuint buffer; + alGenBuffers(1, &buffer); // Generate pointer to buffer + + // Upload sound data to buffer + alBufferData(buffer, format, wave.data, wave.dataSize, wave.sampleRate); + + // Attach sound buffer to source + alSourcei(source, AL_BUFFER, buffer); + + // Unallocate WAV data + UnloadWave(wave); + + TraceLog(INFO, "[Wave] Sound file loaded successfully (SampleRate: %i, BitRate: %i, Channels: %i)", wave.sampleRate, wave.bitsPerSample, wave.channels); sound.source = source; sound.buffer = buffer; @@ -235,7 +283,10 @@ Sound LoadSoundFromRES(const char *rresName, int resId) FILE *rresFile = fopen(rresName, "rb"); - if (!rresFile) TraceLog(WARNING, "[%s] Could not open raylib resource file", rresName); + if (rresFile == NULL) + { + TraceLog(WARNING, "[%s] rRES raylib resource file could not be opened", rresName); + } else { // Read rres file (basic file check - id) @@ -327,12 +378,12 @@ Sound LoadSoundFromRES(const char *rresName, int resId) // Attach sound buffer to source alSourcei(source, AL_BUFFER, buffer); + + TraceLog(INFO, "[%s] Sound loaded successfully from resource (SampleRate: %i, BitRate: %i, Channels: %i)", rresName, wave.sampleRate, wave.bitsPerSample, wave.channels); // Unallocate WAV data UnloadWave(wave); - TraceLog(INFO, "[%s] Sound loaded successfully from resource, sample rate: %i", rresName, (int)sampleRate); - sound.source = source; sound.buffer = buffer; } @@ -447,7 +498,10 @@ void PlayMusicStream(char *fileName) // Open audio stream currentMusic.stream = stb_vorbis_open_filename(fileName, NULL, NULL); - if (currentMusic.stream == NULL) TraceLog(WARNING, "[%s] Could not open ogg audio file", fileName); + if (currentMusic.stream == NULL) + { + TraceLog(WARNING, "[%s] OGG audio file could not be opened", fileName); + } else { // Get file info @@ -537,11 +591,13 @@ void ResumeMusicStream(void) // Check if music is playing bool MusicIsPlaying(void) { - ALenum state; + bool playing = false; + ALint state; alGetSourcei(currentMusic.source, AL_SOURCE_STATE, &state); + if (state == AL_PLAYING) playing = true; - return (state == AL_PLAYING); + return playing; } // Set volume for music @@ -712,9 +768,9 @@ static Wave LoadWAV(const char *fileName) wavFile = fopen(fileName, "rb"); - if (!wavFile) + if (wavFile == NULL) { - TraceLog(WARNING, "[%s] Could not open WAV file", fileName); + TraceLog(WARNING, "[%s] WAV file could not be opened", fileName); } else { @@ -766,7 +822,7 @@ static Wave LoadWAV(const char *fileName) wave.channels = waveFormat.numChannels; wave.bitsPerSample = waveFormat.bitsPerSample; - TraceLog(INFO, "[%s] Wave file loaded successfully", fileName); + TraceLog(INFO, "[%s] WAV file loaded successfully (SampleRate: %i, BitRate: %i, Channels: %i)", fileName, wave.sampleRate, wave.bitsPerSample, wave.channels); } } } @@ -815,6 +871,8 @@ static Wave LoadOGG(char *fileName) int samplesObtained = stb_vorbis_get_samples_short_interleaved(oggFile, info.channels, wave.data, totalSamplesLength); TraceLog(DEBUG, "[%s] Samples obtained: %i", fileName, samplesObtained); + + TraceLog(INFO, "[%s] OGG file loaded successfully (SampleRate: %i, BitRate: %i, Channels: %i)", fileName, wave.sampleRate, wave.bitsPerSample, wave.channels); stb_vorbis_close(oggFile); @@ -825,4 +883,4 @@ static Wave LoadOGG(char *fileName) static void UnloadWave(Wave wave) { free(wave.data); -} +}
\ No newline at end of file @@ -8,6 +8,7 @@ * PLATFORM_DESKTOP - Windows, Linux, Mac (OSX) * PLATFORM_ANDROID - Only OpenGL ES 2.0 devices * PLATFORM_RPI - Rapsberry Pi (tested on Raspbian) +* PLATFORM_WEB - Emscripten, HTML5 * * On PLATFORM_DESKTOP, the external lib GLFW3 (www.glfw.com) is used to manage graphic * device, OpenGL context and input on multiple operating systems (Windows, Linux, OSX). @@ -49,7 +50,7 @@ #include <string.h> // String function definitions, memset() #include <errno.h> // Macros for reporting and retrieving error conditions through error codes -#if defined(PLATFORM_DESKTOP) +#if defined(PLATFORM_DESKTOP) || defined(PLATFORM_WEB) #include <GLFW/glfw3.h> // GLFW3 library: Windows, OpenGL context and Input management //#include <GL/gl.h> // OpenGL functions (GLFW3 already includes gl.h) //#define GLFW_DLL // Using GLFW DLL on Windows -> No, we use static version! @@ -91,7 +92,7 @@ //---------------------------------------------------------------------------------- // Defines and Macros //---------------------------------------------------------------------------------- -// ... +#define MAX_TOUCH_POINTS 256 //---------------------------------------------------------------------------------- // Types and Structures Definition @@ -101,7 +102,7 @@ //---------------------------------------------------------------------------------- // Global Variables Definition //---------------------------------------------------------------------------------- -#if defined(PLATFORM_DESKTOP) +#if defined(PLATFORM_DESKTOP) || defined(PLATFORM_WEB) static GLFWwindow *window; // Native window (graphic device) #elif defined(PLATFORM_ANDROID) static struct android_app *app; // Android activity @@ -111,7 +112,14 @@ static bool windowReady = false; // Used to detect display initia // Gestures detection variables static float tapTouchX, tapTouchY; +static int64_t lastTapTime = 0; +static float lastTapX = 0, lastTapY = 0; static bool touchTap = false; +static bool doubleTap = false; +static bool drag = false; +static int stdVector[MAX_TOUCH_POINTS]; +static int indexPosition = 0; +const AInputEvent* eventDrag; static int32_t touchId; const int32_t DOUBLE_TAP_TIMEOUT = 300*1000000; const int32_t DOUBLE_TAP_SLOP = 100; @@ -160,12 +168,12 @@ static int renderOffsetY = 0; // Offset Y from render area (must b static bool fullscreen = false; // Fullscreen mode (useful only for PLATFORM_DESKTOP) static Matrix downscaleView; // Matrix to downscale view (in case screen size bigger than display size) -#if defined(PLATFORM_DESKTOP) || defined(PLATFORM_RPI) +#if defined(PLATFORM_DESKTOP) || defined(PLATFORM_RPI) || defined(PLATFORM_WEB) static const char *windowTitle; // Window text title... static char configFlags = 0; static bool customCursor = false; // Tracks if custom cursor has been set -static bool cursorOnScreen = true; // Tracks if cursor is inside client area +static bool cursorOnScreen = false; // Tracks if cursor is inside client area static Texture2D cursor; // Cursor texture static Vector2 mousePosition; @@ -183,6 +191,7 @@ static int previousMouseWheelY = 0; // Required to track mouse wheel var static int currentMouseWheelY = 0; // Required to track mouse wheel variation static int exitKey = KEY_ESCAPE; // Default exit key (ESC) +static int lastKeyPressed = -1; #endif #if defined(PLATFORM_ANDROID) @@ -226,12 +235,17 @@ static void RestoreKeyboard(void); // Restore keyboard syst static void InitGamepad(void); // Init raw gamepad input #endif -#if defined(PLATFORM_DESKTOP) +#if defined(PLATFORM_DESKTOP) || defined(PLATFORM_WEB) static void ErrorCallback(int error, const char *description); // GLFW3 Error Callback, runs on GLFW3 error static void KeyCallback(GLFWwindow *window, int key, int scancode, int action, int mods); // GLFW3 Keyboard Callback, runs on key pressed +static void MouseButtonCallback(GLFWwindow *window, int button, int action, int mods); // GLFW3 Mouse Button Callback, runs on mouse button pressed +static void CharCallback(GLFWwindow *window, unsigned int key); // GLFW3 Char Key Callback, runs on key pressed (get char value) static void ScrollCallback(GLFWwindow *window, double xoffset, double yoffset); // GLFW3 Srolling Callback, runs on mouse wheel static void CursorEnterCallback(GLFWwindow *window, int enter); // GLFW3 Cursor Enter Callback, cursor enters client area static void WindowSizeCallback(GLFWwindow *window, int width, int height); // GLFW3 WindowSize Callback, runs when window is resized +#endif + +#if defined(PLATFORM_DESKTOP) || defined(PLATFORM_RPI) static void TakeScreenshot(void); // Takes a screenshot and saves it in the same folder as executable #endif @@ -243,7 +257,7 @@ static void CommandCallback(struct android_app *app, int32_t cmd); // //---------------------------------------------------------------------------------- // Module Functions Definition - Window and OpenGL Context Functions //---------------------------------------------------------------------------------- -#if defined(PLATFORM_DESKTOP) || defined(PLATFORM_RPI) +#if defined(PLATFORM_DESKTOP) || defined(PLATFORM_RPI) || defined(PLATFORM_WEB) // Initialize Window and Graphics Context (OpenGL) void InitWindow(int width, int height, const char *title) { @@ -348,7 +362,7 @@ void CloseWindow(void) rlglClose(); // De-init rlgl -#if defined(PLATFORM_DESKTOP) +#if defined(PLATFORM_DESKTOP) || defined(PLATFORM_WEB) glfwDestroyWindow(window); glfwTerminate(); #elif defined(PLATFORM_ANDROID) || defined(PLATFORM_RPI) @@ -380,7 +394,7 @@ void CloseWindow(void) // Detect if KEY_ESCAPE pressed or Close icon pressed bool WindowShouldClose(void) { -#if defined(PLATFORM_DESKTOP) +#if defined(PLATFORM_DESKTOP) || defined(PLATFORM_WEB) return (glfwWindowShouldClose(window)); #elif defined(PLATFORM_ANDROID) || defined(PLATFORM_RPI) return windowShouldClose; @@ -402,7 +416,7 @@ void ToggleFullscreen(void) #endif } -#if defined(PLATFORM_DESKTOP) || defined(PLATFORM_RPI) +#if defined(PLATFORM_DESKTOP) || defined(PLATFORM_RPI) || defined(PLATFORM_WEB) // Set a custom cursor icon/image void SetCustomCursor(const char *cursorImage) { @@ -411,6 +425,7 @@ void SetCustomCursor(const char *cursorImage) cursor = LoadTexture(cursorImage); #if defined(PLATFORM_DESKTOP) + // NOTE: emscripten not implemented glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_HIDDEN); #endif customCursor = true; @@ -436,6 +451,12 @@ int GetScreenHeight(void) return screenHeight; } +// Get the last key pressed +int GetKeyPressed(void) +{ + return lastKeyPressed; +} + // Sets Background Color void ClearBackground(Color color) { @@ -612,19 +633,13 @@ void ShowLogo(void) //---------------------------------------------------------------------------------- // Module Functions Definition - Input (Keyboard, Mouse, Gamepad) Functions //---------------------------------------------------------------------------------- -#if defined(PLATFORM_DESKTOP) || defined(PLATFORM_RPI) +#if defined(PLATFORM_DESKTOP) || defined(PLATFORM_RPI) || defined(PLATFORM_WEB) // Detect if a key has been pressed once bool IsKeyPressed(int key) { bool pressed = false; - currentKeyState[key] = IsKeyDown(key); - - if (currentKeyState[key] != previousKeyState[key]) - { - if (currentKeyState[key]) pressed = true; - previousKeyState[key] = currentKeyState[key]; - } + if ((currentKeyState[key] != previousKeyState[key]) && (currentKeyState[key] == 1)) pressed = true; else pressed = false; return pressed; @@ -642,13 +657,7 @@ bool IsKeyReleased(int key) { bool released = false; - currentKeyState[key] = IsKeyUp(key); - - if (currentKeyState[key] != previousKeyState[key]) - { - if (currentKeyState[key]) released = true; - previousKeyState[key] = currentKeyState[key]; - } + if ((currentKeyState[key] != previousKeyState[key]) && (currentKeyState[key] == 0)) released = true; else released = false; return released; @@ -666,13 +675,7 @@ bool IsMouseButtonPressed(int button) { bool pressed = false; - currentMouseState[button] = IsMouseButtonDown(button); - - if (currentMouseState[button] != previousMouseState[button]) - { - if (currentMouseState[button]) pressed = true; - previousMouseState[button] = currentMouseState[button]; - } + if ((currentMouseState[button] != previousMouseState[button]) && (currentMouseState[button] == 1)) pressed = true; else pressed = false; return pressed; @@ -690,13 +693,7 @@ bool IsMouseButtonReleased(int button) { bool released = false; - currentMouseState[button] = IsMouseButtonUp(button); - - if (currentMouseState[button] != previousMouseState[button]) - { - if (currentMouseState[button]) released = true; - previousMouseState[button] = currentMouseState[button]; - } + if ((currentMouseState[button] != previousMouseState[button]) && (currentMouseState[button] == 0)) released = true; else released = false; return released; @@ -727,6 +724,16 @@ Vector2 GetMousePosition(void) return mousePosition; } +// Set mouse position XY +void SetMousePosition(Vector2 position) +{ + mousePosition = position; +#if defined(PLATFORM_DESKTOP) + // NOTE: emscripten not implemented + glfwSetCursorPos(window, position.x, position.y); +#endif +} + // Returns mouse wheel movement Y int GetMouseWheelMove(void) { @@ -738,7 +745,8 @@ int GetMouseWheelMove(void) } #endif -// TODO: Enable gamepad usage on Rapsberr Pi +// TODO: Enable gamepad usage on Rapsberry Pi +// NOTE: emscripten not implemented #if defined(PLATFORM_DESKTOP) // Detect if a gamepad is available bool IsGamepadAvailable(int gamepad) @@ -842,6 +850,18 @@ bool IsScreenTouched(void) return touchTap; } +bool IsDoubleTap(void) +{ + if (doubleTap) TraceLog(INFO, "DOUBLE TAP gesture detected"); + + return doubleTap; +} + +bool IsDragGesture(void) +{ + return drag; +} + // Returns touch position X int GetTouchX(void) { @@ -861,6 +881,27 @@ Vector2 GetTouchPosition(void) return position; } + +/*bool GetPointer(Vector2 *dragPositions) +{ + //static int stdVector[MAX_TOUCH_POINTS]; + //static int indexPosition = 0; + //if (indexPosition == 0) return false; + Vector2 vec_pointers_[]; + + //eventDrag + int32_t iIndex = FindIndex( eventDrag, vec_pointers_[0] ); + + if (iIndex == -1) return false; + + float x = AMotionEvent_getX(eventDrag, iIndex); + float y = AMotionEvent_getY(eventDrag, iIndex); + + *dragPositions = Vector2( x, y ); + + + return true; +}*/ #endif //---------------------------------------------------------------------------------- @@ -881,11 +922,13 @@ static void InitDisplay(int width, int height) // Downscale matrix is required in case desired screen area is bigger than display area downscaleView = MatrixIdentity(); -#if defined(PLATFORM_DESKTOP) +#if defined(PLATFORM_DESKTOP) || defined(PLATFORM_WEB) glfwSetErrorCallback(ErrorCallback); if (!glfwInit()) TraceLog(ERROR, "Failed to initialize GLFW"); + // NOTE: Getting video modes is not implemented in emscripten GLFW3 version +#if defined(PLATFORM_DESKTOP) // Find monitor resolution const GLFWvidmode *mode = glfwGetVideoMode(glfwGetPrimaryMonitor()); @@ -895,7 +938,11 @@ static void InitDisplay(int width, int height) // Screen size security check if (screenWidth <= 0) screenWidth = displayWidth; if (screenHeight <= 0) screenHeight = displayHeight; - +#elif defined(PLATFORM_WEB) + displayWidth = screenWidth; + displayHeight = screenHeight; +#endif + glfwDefaultWindowHints(); // Set default windows hints glfwWindowHint(GLFW_RESIZABLE, GL_FALSE); // Avoid window being resizable @@ -944,7 +991,9 @@ static void InitDisplay(int width, int height) else { TraceLog(INFO, "Display device initialized successfully"); +#if defined(PLATFORM_DESKTOP) TraceLog(INFO, "Display size: %i x %i", displayWidth, displayHeight); +#endif TraceLog(INFO, "Render size: %i x %i", renderWidth, renderHeight); TraceLog(INFO, "Screen size: %i x %i", screenWidth, screenHeight); TraceLog(INFO, "Viewport offsets: %i, %i", renderOffsetX, renderOffsetY); @@ -953,6 +1002,8 @@ static void InitDisplay(int width, int height) glfwSetWindowSizeCallback(window, WindowSizeCallback); glfwSetCursorEnterCallback(window, CursorEnterCallback); glfwSetKeyCallback(window, KeyCallback); + glfwSetMouseButtonCallback(window, MouseButtonCallback); + glfwSetCharCallback(window, CharCallback); glfwSetScrollCallback(window, ScrollCallback); glfwMakeContextCurrent(window); @@ -1116,7 +1167,7 @@ void InitGraphics(void) #endif } -#if defined(PLATFORM_DESKTOP) +#if defined(PLATFORM_DESKTOP) || defined(PLATFORM_WEB) // GLFW3 Error Callback, runs on GLFW3 error static void ErrorCallback(int error, const char *description) { @@ -1138,10 +1189,31 @@ static void KeyCallback(GLFWwindow *window, int key, int scancode, int action, i // NOTE: Before closing window, while loop must be left! } +#if defined(PLATFORM_DESKTOP) else if (key == GLFW_KEY_F12 && action == GLFW_PRESS) { TakeScreenshot(); } +#endif + else currentKeyState[key] = action; + + // HACK for GuiTextBox, to deteck back key + // TODO: Review... + if ((key == 259) && (action == GLFW_PRESS)) lastKeyPressed = 3; +} + +// GLFW3 Mouse Button Callback, runs on mouse button pressed +static void MouseButtonCallback(GLFWwindow *window, int button, int action, int mods) +{ + currentMouseState[button] = action; +} + +// GLFW3 Char Key Callback, runs on key pressed (get char value) +static void CharCallback(GLFWwindow *window, unsigned int key) +{ + lastKeyPressed = key; + + //TraceLog(INFO, "Char Callback Key pressed: %i\n", key); } // GLFW3 CursorEnter Callback, when cursor enters the window @@ -1177,6 +1249,7 @@ static int32_t InputCallback(struct android_app *app, AInputEvent *event) if (type == AINPUT_EVENT_TYPE_MOTION) { + // Detect TOUCH position if ((screenWidth > displayWidth) || (screenHeight > displayHeight)) { // TODO: Seems to work ok but... review! @@ -1240,7 +1313,124 @@ static int32_t InputCallback(struct android_app *app, AInputEvent *event) //size_t pointerCount = AMotionEvent_getPointerCount(event); //float AMotionEvent_getPressure(const AInputEvent *motion_event, size_t pointer_index); // 0 to 1 //float AMotionEvent_getSize(const AInputEvent *motion_event, size_t pointer_index); // Pressed area + + // Detect DOUBLE TAP event + bool tapDetected = touchTap; + + switch (flags) + { + case AMOTION_EVENT_ACTION_DOWN: + { + int64_t eventTime = AMotionEvent_getEventTime(event); + + if (eventTime - lastTapTime <= DOUBLE_TAP_TIMEOUT) + { + float x = AMotionEvent_getX(event, 0) - lastTapX; + float y = AMotionEvent_getY(event, 0) - lastTapY; + + float densityFactor = 1.0f; + + if ((x*x + y*y) < (DOUBLE_TAP_SLOP*DOUBLE_TAP_SLOP*densityFactor)) + { + // Doubletap detected + doubleTap = true; + + } + } + } break; + case AMOTION_EVENT_ACTION_UP: + { + if (tapDetected) + { + lastTapTime = AMotionEvent_getEventTime(event); + lastTapX = AMotionEvent_getX(event, 0); + lastTapY = AMotionEvent_getY(event, 0); + + } + } break; + } + + + // Detect DRAG event + //int32_t action = AMotionEvent_getAction(event); + + int32_t index = (action & AMOTION_EVENT_ACTION_POINTER_INDEX_MASK) >> AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT; + //uint32_t flags = action & AMOTION_EVENT_ACTION_MASK; + //event_ = event; + int32_t count = AMotionEvent_getPointerCount(event); + + switch (flags) + { + case AMOTION_EVENT_ACTION_DOWN: + { + stdVector[indexPosition] = AMotionEvent_getPointerId(event, 0); + indexPosition++; + TraceLog(INFO, "ACTION_DOWN"); + + //ret = GESTURE_STATE_START; + } break; + case AMOTION_EVENT_ACTION_POINTER_DOWN: + { + stdVector[indexPosition] = AMotionEvent_getPointerId(event, index); + indexPosition++; + TraceLog(INFO, "ACTION_POINTER_DOWN"); + + } break; + case AMOTION_EVENT_ACTION_UP: + { + //int value = stdVector[indexPosition]; + indexPosition--; + //ret = GESTURE_STATE_END; + TraceLog(INFO, "ACTION_UP"); + + } break; + case AMOTION_EVENT_ACTION_POINTER_UP: + { + int32_t releasedPointerId = AMotionEvent_getPointerId(event, index); + + int i = 0; + for (i = 0; i < MAX_TOUCH_POINTS; i++) + { + if (stdVector[i] == releasedPointerId) + { + for (int k = i; k < indexPosition - 1; k++) + { + stdVector[k] = stdVector[k + 1]; + } + + //indexPosition--; + indexPosition = 0; + break; + } + } + + if (i <= 1) + { + // Reset pinch or drag + //if (count == 2) //ret = GESTURE_STATE_START; + } + TraceLog(INFO, "ACTION_POINTER_UP"); + + } break; + case AMOTION_EVENT_ACTION_MOVE: + { + if (count == 1) + { + //TraceLog(INFO, "DRAG gesture detected"); + + drag = true; //ret = GESTURE_STATE_MOVE; + } + else break; + TraceLog(INFO, "ACTION_MOVE"); + + } break; + case AMOTION_EVENT_ACTION_CANCEL: break; + default: break; + } + + //-------------------------------------------------------------------- + return 1; } else if (type == AINPUT_EVENT_TYPE_KEY) @@ -1386,7 +1576,7 @@ static void InitTimer(void) // Get current time measure since InitTimer() static double GetTime(void) { -#if defined(PLATFORM_DESKTOP) +#if defined(PLATFORM_DESKTOP) || defined(PLATFORM_WEB) return glfwGetTime(); #elif defined(PLATFORM_ANDROID) || defined(PLATFORM_RPI) struct timespec ts; @@ -1400,7 +1590,7 @@ static double GetTime(void) // Get one key state static bool GetKeyStatus(int key) { -#if defined(PLATFORM_DESKTOP) +#if defined(PLATFORM_DESKTOP) || defined(PLATFORM_WEB) return glfwGetKey(window, key); #elif defined(PLATFORM_ANDROID) // TODO: Check virtual keyboard (?) @@ -1415,7 +1605,7 @@ static bool GetKeyStatus(int key) // Get one mouse button state static bool GetMouseButtonStatus(int button) { -#if defined(PLATFORM_DESKTOP) +#if defined(PLATFORM_DESKTOP) || defined(PLATFORM_WEB) return glfwGetMouseButton(window, button); #elif defined(PLATFORM_ANDROID) // TODO: Check virtual keyboard (?) @@ -1429,7 +1619,7 @@ static bool GetMouseButtonStatus(int button) // Poll (store) all input events static void PollInputEvents(void) { -#if defined(PLATFORM_DESKTOP) +#if defined(PLATFORM_DESKTOP) || defined(PLATFORM_WEB) // Mouse input polling double mouseX; double mouseY; @@ -1441,14 +1631,23 @@ static void PollInputEvents(void) // Keyboard polling // Automatically managed by GLFW3 through callback - + lastKeyPressed = -1; + + // Register previous keys states + for (int i = 0; i < 512; i++) previousKeyState[i] = currentKeyState[i]; + + // Register previous mouse states + for (int i = 0; i < 3; i++) previousMouseState[i] = currentMouseState[i]; + glfwPollEvents(); // Register keyboard/mouse events #elif defined(PLATFORM_ANDROID) // TODO: Check virtual keyboard (?) - // Reset touchTap event + // Reset touch events touchTap = false; + doubleTap = false; + drag = false; // Poll Events (registered events) while ((ident = ALooper_pollAll(0, NULL, &events,(void**)&source)) >= 0) @@ -1459,9 +1658,10 @@ static void PollInputEvents(void) // Check if we are exiting if (app->destroyRequested != 0) { - TraceLog(INFO, "Closing Window..."); - //CloseWindow(); - windowShouldClose = true; + // NOTE: Never close window, native activity is controlled by the system! + //TraceLog(INFO, "Closing Window..."); + //windowShouldClose = true; + //ANativeActivity_finish(app->activity); } } @@ -1596,14 +1796,17 @@ static void PollInputEvents(void) static void InitMouse(void) { // NOTE: We can use /dev/input/mice to read from all available mice - if ((mouseStream = open(DEFAULT_MOUSE_DEV, O_RDONLY|O_NONBLOCK)) < 0) TraceLog(WARNING, "Could not open mouse device, no mouse available"); + if ((mouseStream = open(DEFAULT_MOUSE_DEV, O_RDONLY|O_NONBLOCK)) < 0) + { + TraceLog(WARNING, "Mouse device could not be opened, no mouse available"); + } else { mouseReady = true; - int err = pthread_create(&mouseThreadId, NULL, &MouseThread, NULL); + int error = pthread_create(&mouseThreadId, NULL, &MouseThread, NULL); - if (err != 0) TraceLog(WARNING, "Error creating mouse input event thread"); + if (error != 0) TraceLog(WARNING, "Error creating mouse input event thread"); else TraceLog(INFO, "Mouse device initialized successfully"); } } @@ -1714,7 +1917,7 @@ static void RestoreKeyboard(void) static void InitGamepad(void) { // TODO: Gamepad support - if ((gamepadStream = open(DEFAULT_GAMEPAD_DEV, O_RDONLY|O_NONBLOCK)) < 0) TraceLog(WARNING, "Could not open gamepad device, no gamepad available"); + if ((gamepadStream = open(DEFAULT_GAMEPAD_DEV, O_RDONLY|O_NONBLOCK)) < 0) TraceLog(WARNING, "Gamepad device could not be opened, no gamepad available"); else TraceLog(INFO, "Gamepad device initialized successfully"); } #endif @@ -1722,7 +1925,7 @@ static void InitGamepad(void) // Copy back buffer to front buffers static void SwapBuffers(void) { -#if defined(PLATFORM_DESKTOP) +#if defined(PLATFORM_DESKTOP) || defined(PLATFORM_WEB) glfwSwapBuffers(window); #elif defined(PLATFORM_ANDROID) || defined(PLATFORM_RPI) eglSwapBuffers(display, surface); @@ -1736,7 +1939,7 @@ static void SetupFramebufferSize(int displayWidth, int displayHeight) // Calculate renderWidth and renderHeight, we have the display size (input params) and the desired screen size (global var) if ((screenWidth > displayWidth) || (screenHeight > displayHeight)) { - TraceLog(WARNING, "DOWNSCALING: Required screen size (%i x %i) is bigger than display size (%i x %i)", screenWidth, screenHeight, displayWidth, displayHeight); + TraceLog(WARNING, "DOWNSCALING: Required screen size (%ix%i) is bigger than display size (%ix%i)", screenWidth, screenHeight, displayWidth, displayHeight); // Downscaling to fit display with border-bars float widthRatio = (float)displayWidth/(float)screenWidth; @@ -1805,6 +2008,7 @@ static void SetupFramebufferSize(int displayWidth, int displayHeight) // Plays raylib logo appearing animation static void LogoAnimation(void) { +#ifndef PLATFORM_WEB int logoPositionX = screenWidth/2 - 128; int logoPositionY = screenHeight/2 - 128; @@ -1922,6 +2126,8 @@ static void LogoAnimation(void) EndDrawing(); //---------------------------------------------------------------------------------- } +#endif showLogo = false; // Prevent for repeating when reloading window (Android) } + diff --git a/src/makefile b/src/makefile index 780aa16b..ed595f64 100644 --- a/src/makefile +++ b/src/makefile @@ -1,8 +1,6 @@ #************************************************************************************************** # -# raylib for Raspberry Pi and Windows desktop -# -# makefile for library compilation (raylib.a) +# raylib makefile for desktop platforms, Raspberry Pi and HTML5 (emscripten) # # Copyright (c) 2014 Ramon Santamaria (Ray San - raysan@raysanweb.com) # @@ -23,9 +21,9 @@ # #************************************************************************************************** -# define raylib platform (by default, compile for RPI) -# Other possible platforms: PLATFORM_DESKTOP PLATFORM_DESKTOP_LINUX PLATFORM_DESKTOP_OSX -PLATFORM ?= PLATFORM_DESKTOP_OSX +# define raylib platform to compile for +# possible platforms: PLATFORM_DESKTOP PLATFORM_RPI PLATFORM_WEB +PLATFORM ?= PLATFORM_DESKTOP # define raylib graphics api depending on selected platform ifeq ($(PLATFORM),PLATFORM_RPI) @@ -37,20 +35,31 @@ else #GRAPHICS = GRAPHICS_API_OPENGL_33 # Uncomment to use OpenGL 3.3 endif +ifeq ($(PLATFORM),PLATFORM_WEB) + GRAPHICS = GRAPHICS_API_OPENGL_ES2 +endif + # NOTE: makefiles targets require tab indentation # define compiler: gcc for C program, define as g++ for C++ -CC = gcc +ifeq ($(PLATFORM),PLATFORM_WEB) + # define emscripten compiler + CC = emcc +else + # define default gcc compiler + CC = gcc +endif # define compiler flags: # -O2 defines optimization level # -Wall turns on most, but not all, compiler warnings # -std=c99 use standard C from 1999 revision ifeq ($(PLATFORM),PLATFORM_RPI) - CFLAGS = -O2 -Wall -std=gnu99 -fgnu89-inline + CFLAGS = -O1 -Wall -std=gnu99 -fgnu89-inline else - CFLAGS = -O2 -Wall -std=c99 + CFLAGS = -O1 -Wall -std=c99 endif + #CFLAGSEXTRA = -Wextra -Wmissing-prototypes -Wstrict-prototypes # define any directories containing required header files @@ -61,7 +70,7 @@ else endif # define all object files required -OBJS = core.o rlgl.o raymath.o shapes.o text.o textures.o models.o audio.o utils.o stb_image.o stb_vorbis.o +OBJS = core.o rlgl.o raymath.o shapes.o text.o textures.o models.o audio.o utils.o stb_vorbis.o # typing 'make' will invoke the first target entry in the file, # in this case, the 'default' target entry is raylib @@ -69,15 +78,20 @@ default: raylib # compile raylib library raylib: $(OBJS) +ifeq ($(PLATFORM),PLATFORM_WEB) + emcc -O1 $(OBJS) -o libraylib.bc +else ar rcs libraylib.a $(OBJS) +endif # compile core module +# emcc core.c -o core.bc -DPLATFORM_DESKTOP core.o: core.c - $(CC) -c core.c $(CFLAGS) $(INCLUDES) -D$(PLATFORM) + $(CC) -c core.c $(CFLAGS) $(INCLUDES) -D$(PLATFORM) -D$(GRAPHICS) # compile rlgl module rlgl.o: rlgl.c - $(CC) -c rlgl.c $(CFLAGS) $(INCLUDES) -D$(GRAPHICS) + $(CC) -c rlgl.c $(CFLAGS) $(INCLUDES) -D$(PLATFORM) -D$(GRAPHICS) # compile raymath module raymath.o: raymath.c @@ -85,19 +99,19 @@ raymath.o: raymath.c # compile shapes module shapes.o: shapes.c - $(CC) -c shapes.c $(CFLAGS) $(INCLUDES) -D$(PLATFORM) + $(CC) -c shapes.c $(CFLAGS) $(INCLUDES) -D$(PLATFORM) -D$(GRAPHICS) # compile textures module textures.o: textures.c - $(CC) -c textures.c $(CFLAGS) $(INCLUDES) -D$(PLATFORM) + $(CC) -c textures.c $(CFLAGS) $(INCLUDES) -D$(PLATFORM) -D$(GRAPHICS) # compile text module text.o: text.c - $(CC) -c text.c $(CFLAGS) $(INCLUDES) -D$(PLATFORM) + $(CC) -c text.c $(CFLAGS) $(INCLUDES) -D$(PLATFORM) -D$(GRAPHICS) # compile models module models.o: models.c - $(CC) -c models.c $(CFLAGS) $(INCLUDES) -D$(PLATFORM) + $(CC) -c models.c $(CFLAGS) $(INCLUDES) -D$(PLATFORM) -D$(GRAPHICS) # compile audio module audio.o: audio.c @@ -107,10 +121,6 @@ audio.o: audio.c utils.o: utils.c $(CC) -c utils.c $(CFLAGS) $(INCLUDES) -D$(PLATFORM) -# compile stb_image library -stb_image.o: stb_image.c - $(CC) -c stb_image.c $(CFLAGS) $(INCLUDES) -D$(PLATFORM) - # compile stb_vorbis library stb_vorbis.o: stb_vorbis.c $(CC) -c stb_vorbis.c $(CFLAGS) $(INCLUDES) -D$(PLATFORM) @@ -127,10 +137,14 @@ else ifeq ($(PLATFORM),PLATFORM_DESKTOP_OSX) rm -f *.o libraylib.a else +ifeq ($(PLATFORM),PLATFORM_WEB) + del *.o libraylib.bc +else del *.o libraylib.a endif endif endif +endif @echo Cleaning done # instead of defining every module one by one, we can define a pattern diff --git a/src/models.c b/src/models.c index d69322a8..e8e4f635 100644 --- a/src/models.c +++ b/src/models.c @@ -50,7 +50,7 @@ //---------------------------------------------------------------------------------- // Global Variables Definition //---------------------------------------------------------------------------------- -// It's lonely here... +extern unsigned int whiteTexture; //---------------------------------------------------------------------------------- // Module specific Functions Declaration @@ -66,14 +66,14 @@ static VertexData LoadOBJ(const char *fileName); // NOTE: Cube position is the center position void DrawCube(Vector3 position, float width, float height, float lenght, Color color) { - float x = position.x; - float y = position.y; - float z = position.z; + float x = 0.0f; + float y = 0.0f; + float z = 0.0f; rlPushMatrix(); // NOTE: Be careful! Function order matters (rotate -> scale -> translate) - //rlTranslatef(0.0f, 0.0f, 0.0f); + rlTranslatef(position.x, position.y, position.z); //rlScalef(2.0f, 2.0f, 2.0f); //rlRotatef(45, 0, 1, 0); @@ -146,12 +146,13 @@ void DrawCubeV(Vector3 position, Vector3 size, Color color) // Draw cube wires void DrawCubeWires(Vector3 position, float width, float height, float lenght, Color color) { - float x = position.x; - float y = position.y; - float z = position.z; + float x = 0.0f; + float y = 0.0f; + float z = 0.0f; rlPushMatrix(); + rlTranslatef(position.x, position.y, position.z); //rlRotatef(45, 0, 1, 0); rlBegin(RL_LINES); @@ -445,11 +446,37 @@ void DrawCylinderWires(Vector3 position, float radiusTop, float radiusBottom, fl rlPopMatrix(); } +// Draw a quad +void DrawQuad(Vector3 vertices[4], Vector2 textcoords[4], Vector3 normals[4], Color colors[4]) +{ + rlBegin(RL_QUADS); + rlColor4ub(colors[0].r, colors[0].g, colors[0].b, colors[0].a); + rlNormal3f(normals[0].x, normals[0].y, normals[0].z); + rlTexCoord2f(textcoords[0].x, textcoords[0].y); + rlVertex3f(vertices[0].x, vertices[0].y, vertices[0].z); + + rlColor4ub(colors[1].r, colors[1].g, colors[1].b, colors[1].a); + rlNormal3f(normals[1].x, normals[1].y, normals[1].z); + rlTexCoord2f(textcoords[1].x, textcoords[1].y); + rlVertex3f(vertices[1].x, vertices[1].y, vertices[1].z); + + rlColor4ub(colors[2].r, colors[2].g, colors[2].b, colors[2].a); + rlNormal3f(normals[2].x, normals[2].y, normals[2].z); + rlTexCoord2f(textcoords[2].x, textcoords[2].y); + rlVertex3f(vertices[2].x, vertices[2].y, vertices[2].z); + + rlColor4ub(colors[3].r, colors[3].g, colors[3].b, colors[3].a); + rlNormal3f(normals[3].x, normals[3].y, normals[3].z); + rlTexCoord2f(textcoords[3].x, textcoords[3].y); + rlVertex3f(vertices[3].x, vertices[3].y, vertices[3].z); + rlEnd(); +} + // Draw a plane void DrawPlane(Vector3 centerPos, Vector2 size, Vector3 rotation, Color color) { // NOTE: QUADS usage require defining a texture on OpenGL 3.3+ - rlEnableTexture(1); // Default white texture + if (rlGetVersion() != OPENGL_11) rlEnableTexture(whiteTexture); // Default white texture // NOTE: Plane is always created on XZ ground and then rotated rlPushMatrix(); @@ -471,7 +498,7 @@ void DrawPlane(Vector3 centerPos, Vector2 size, Vector3 rotation, Color color) rlEnd(); rlPopMatrix(); - rlDisableTexture(); + if (rlGetVersion() != OPENGL_11) rlDisableTexture(); } // Draw a plane with divisions @@ -743,7 +770,7 @@ Model LoadHeightmap(Image heightmap, float maxHeight) vData.texcoords[tcCounter + 7] = vData.texcoords[tcCounter + 5]; vData.texcoords[tcCounter + 8] = vData.texcoords[tcCounter + 2]; - vData.texcoords[tcCounter + 9] = vData.texcoords[tcCounter + 1]; + vData.texcoords[tcCounter + 9] = vData.texcoords[tcCounter + 3]; vData.texcoords[tcCounter + 10] = (float)(x+1) / (mapX-1); vData.texcoords[tcCounter + 11] = (float)(z+1) / (mapZ-1); @@ -785,7 +812,7 @@ Model LoadHeightmap(Image heightmap, float maxHeight) } // Load a map image as a 3d model (cubes based) -Model LoadCubesmap(Image cubesmap) +Model LoadCubicmap(Image cubesmap) { VertexData vData; @@ -1041,8 +1068,6 @@ Model LoadCubesmap(Image cubesmap) // Move data from mapVertices temp arays to vertices float array vData.vertexCount = vCounter; - printf("Vertex count: %i\n", vCounter); - vData.vertices = (float *)malloc(vData.vertexCount * 3 * sizeof(float)); vData.normals = (float *)malloc(vData.vertexCount * 3 * sizeof(float)); vData.texcoords = (float *)malloc(vData.vertexCount * 2 * sizeof(float)); @@ -1145,7 +1170,6 @@ void DrawModelWires(Model model, Vector3 position, float scale, Color color) } // Draw a billboard -// TODO: Math review... void DrawBillboard(Camera camera, Texture2D texture, Vector3 center, float size, Color tint) { // NOTE: Billboard size will maintain texture aspect ratio, size will be billboard width @@ -1157,11 +1181,11 @@ void DrawBillboard(Camera camera, Texture2D texture, Vector3 center, float size, Vector3 right = { viewMatrix.m0, viewMatrix.m4, viewMatrix.m8 }; Vector3 up = { viewMatrix.m1, viewMatrix.m5, viewMatrix.m9 }; /* - d-------c + a-------b | | | * | | | - a-------b + d-------c */ VectorScale(&right, sizeRatio.x/2); VectorScale(&up, sizeRatio.y/2); @@ -1178,18 +1202,17 @@ void DrawBillboard(Camera camera, Texture2D texture, Vector3 center, float size, rlBegin(RL_QUADS); rlColor4ub(tint.r, tint.g, tint.b, tint.a); - rlNormal3f(0.0f, 1.0f, 0.0f); + rlTexCoord2f(0.0f, 0.0f); rlVertex3f(a.x, a.y, a.z); - rlTexCoord2f(1.0f, 0.0f); rlVertex3f(b.x, b.y, b.z); - rlTexCoord2f(1.0f, 1.0f); rlVertex3f(c.x, c.y, c.z); rlTexCoord2f(0.0f, 1.0f); rlVertex3f(d.x, d.y, d.z); + rlTexCoord2f(1.0f, 1.0f); rlVertex3f(c.x, c.y, c.z); + rlTexCoord2f(1.0f, 0.0f); rlVertex3f(b.x, b.y, b.z); rlEnd(); rlDisableTexture(); } // Draw a billboard (part of a texture defined by a rectangle) -// TODO: Math review... void DrawBillboardRec(Camera camera, Texture2D texture, Rectangle sourceRec, Vector3 center, float size, Color tint) { // NOTE: Billboard size will maintain sourceRec aspect ratio, size will represent billboard width @@ -1201,11 +1224,11 @@ void DrawBillboardRec(Camera camera, Texture2D texture, Rectangle sourceRec, Vec Vector3 right = { viewMatrix.m0, viewMatrix.m4, viewMatrix.m8 }; Vector3 up = { viewMatrix.m1, viewMatrix.m5, viewMatrix.m9 }; /* - d-------c + a-------b | | | * | | | - a-------b + d-------c */ VectorScale(&right, sizeRatio.x/2); VectorScale(&up, sizeRatio.y/2); @@ -1226,18 +1249,18 @@ void DrawBillboardRec(Camera camera, Texture2D texture, Rectangle sourceRec, Vec // Bottom-left corner for texture and quad rlTexCoord2f((float)sourceRec.x / texture.width, (float)sourceRec.y / texture.height); rlVertex3f(a.x, a.y, a.z); - - // Bottom-right corner for texture and quad - rlTexCoord2f((float)(sourceRec.x + sourceRec.width) / texture.width, (float)sourceRec.y / texture.height); - rlVertex3f(b.x, b.y, b.z); - + + // Top-left corner for texture and quad + rlTexCoord2f((float)sourceRec.x / texture.width, (float)(sourceRec.y + sourceRec.height) / texture.height); + rlVertex3f(d.x, d.y, d.z); + // Top-right corner for texture and quad rlTexCoord2f((float)(sourceRec.x + sourceRec.width) / texture.width, (float)(sourceRec.y + sourceRec.height) / texture.height); rlVertex3f(c.x, c.y, c.z); - // Top-left corner for texture and quad - rlTexCoord2f((float)sourceRec.x / texture.width, (float)(sourceRec.y + sourceRec.height) / texture.height); - rlVertex3f(d.x, d.y, d.z); + // Bottom-right corner for texture and quad + rlTexCoord2f((float)(sourceRec.x + sourceRec.width) / texture.width, (float)sourceRec.y / texture.height); + rlVertex3f(b.x, b.y, b.z); rlEnd(); rlDisableTexture(); @@ -1265,6 +1288,12 @@ static VertexData LoadOBJ(const char *fileName) FILE *objFile; objFile = fopen(fileName, "rt"); + + if (objFile == NULL) + { + TraceLog(WARNING, "[%s] OBJ file could not be opened", fileName); + return vData; + } // First reading pass: Get numVertex, numNormals, numTexCoords, numTriangles // NOTE: vertex, texcoords and normals could be optimized (to be used indexed on faces definition) @@ -1492,4 +1521,36 @@ static VertexData LoadOBJ(const char *fileName) TraceLog(INFO, "[%s] Model loaded successfully in RAM (CPU)", fileName); return vData; -}
\ No newline at end of file +} + +bool CheckCollisionSpheres(Vector3 centerA, float radiusA, Vector3 centerB, float radiusB) +{ + + return false; +} + +bool CheckCollisionBoxes(Vector3 minBBox1, Vector3 maxBBox1, Vector3 minBBox2, Vector3 maxBBox2) +{ + /* + // Get min and max vertex to construct bounds (AABB) + Vector3 minVertex = tempVertices[0]; + Vector3 maxVertex = tempVertices[0]; + + for (int i = 1; i < tempVertices.Count; i++) + { + minVertex = Vector3.Min(minVertex, tempVertices[i]); + maxVertex = Vector3.Max(maxVertex, tempVertices[i]); + } + + bounds = new BoundingBox(minVertex, maxVertex); + */ + return false; +} + +bool CheckCollisionBoxSphere(Vector3 minBBox, Vector3 maxBBox, Vector3 centerSphere, Vector3 radiusSphere) +{ + + return false; +} + +//BoundingBox GetCollisionArea(BoundingBox box1, BoundingBox box2)
\ No newline at end of file diff --git a/src/raylib.h b/src/raylib.h index 5257de58..69966069 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -14,7 +14,7 @@ * Basic 3d support for Shapes, Models, Heightmaps and Billboards * Powerful math module for Vector and Matrix operations [raymath] * Audio loading and playing with streaming support (WAV and OGG) -* Multiplatform support, including Android devices and Raspberry Pi +* Multiplatform support, including Android devices, Raspberry Pi and HTML5 * * Used external libs: * GLFW3 (www.glfw.org) for window/context management and input @@ -63,9 +63,10 @@ //#define PLATFORM_DESKTOP // Windows, Linux or OSX //#define PLATFORM_ANDROID // Android device //#define PLATFORM_RPI // Raspberry Pi +//#define PLATFORM_WEB // HTML5 (emscripten, asm.js) // Security check in case no PLATFORM_* defined -#if !defined(PLATFORM_DESKTOP) && !defined(PLATFORM_ANDROID) && !defined(PLATFORM_RPI) +#if !defined(PLATFORM_DESKTOP) && !defined(PLATFORM_ANDROID) && !defined(PLATFORM_RPI) && !defined(PLATFORM_WEB) #define PLATFORM_DESKTOP #endif @@ -179,6 +180,9 @@ // Boolean type typedef enum { false, true } bool; +// byte type +typedef unsigned char byte; + // Vector2 type typedef struct Vector2 { float x; @@ -225,8 +229,13 @@ typedef struct Texture2D { } Texture2D; // Character type (one font glyph) -// NOTE: Defined in module: text -typedef struct Character Character; +typedef struct Character { + int value; //char value = ' '; (int)value = 32; + int x; + int y; + int w; + int h; +} Character; // SpriteFont type, includes texture and charSet array data typedef struct SpriteFont { @@ -267,6 +276,15 @@ typedef struct Sound { unsigned int buffer; } Sound; +// Wave type, defines audio wave data +typedef struct Wave { + void *data; // Buffer data pointer + unsigned int dataSize; // Data size in bytes + unsigned int sampleRate; + short bitsPerSample; + short channels; +} Wave; + #ifdef __cplusplus extern "C" { // Prevents name mangling of functions #endif @@ -281,7 +299,7 @@ extern "C" { // Prevents name mangling of functions //------------------------------------------------------------------------------------ #if defined(PLATFORM_ANDROID) void InitWindow(int width, int height, struct android_app *state); // Init Android activity -#elif defined(PLATFORM_DESKTOP) || defined(PLATFORM_RPI) +#elif defined(PLATFORM_DESKTOP) || defined(PLATFORM_RPI) || defined(PLATFORM_WEB) void InitWindow(int width, int height, const char *title); // Initialize Window and OpenGL Graphics #endif @@ -294,6 +312,7 @@ void SetExitKey(int key); // Set a custom key #endif int GetScreenWidth(void); // Get current screen width int GetScreenHeight(void); // Get current screen height +int GetKeyPressed(void); // Get latest key pressed void ClearBackground(Color color); // Sets Background Color void BeginDrawing(void); // Setup drawing canvas to start drawing @@ -313,13 +332,12 @@ int GetRandomValue(int min, int max); // Returns a random Color Fade(Color color, float alpha); // Color fade-in or fade-out, alpha goes from 0.0f to 1.0f void SetupFlags(char flags); // Enable some window configurations - -void ShowLogo(void); // Activates raylib logo at startup +void ShowLogo(void); // Activates raylib logo at startup (can be done with flags) //------------------------------------------------------------------------------------ // Input Handling Functions (Module: core) //------------------------------------------------------------------------------------ -#if defined(PLATFORM_DESKTOP) || defined(PLATFORM_RPI) +#if defined(PLATFORM_DESKTOP) || defined(PLATFORM_RPI) || defined(PLATFORM_WEB) bool IsKeyPressed(int key); // Detect if a key has been pressed once bool IsKeyDown(int key); // Detect if a key is being pressed bool IsKeyReleased(int key); // Detect if a key has been released once @@ -332,8 +350,11 @@ bool IsMouseButtonUp(int button); // Detect if a mouse but int GetMouseX(void); // Returns mouse position X int GetMouseY(void); // Returns mouse position Y Vector2 GetMousePosition(void); // Returns mouse position XY +void SetMousePosition(Vector2 position); // Set mouse position XY int GetMouseWheelMove(void); // Returns mouse wheel movement Y +#endif +#if defined(PLATFORM_DESKTOP) bool IsGamepadAvailable(int gamepad); // Detect if a gamepad is available Vector2 GetGamepadMovement(int gamepad); // Return axis movement vector for a gamepad bool IsGamepadButtonPressed(int gamepad, int button); // Detect if a gamepad button has been pressed once @@ -386,9 +407,11 @@ Image LoadImage(const char *fileName); Image LoadImageFromRES(const char *rresName, int resId); // Load an image from rRES file (raylib Resource) Texture2D LoadTexture(const char *fileName); // Load an image as texture into GPU memory Texture2D LoadTextureFromRES(const char *rresName, int resId); // Load an image as texture from rRES file (raylib Resource) -Texture2D CreateTexture(Image image, bool genMipmaps); // Create a Texture2D from Image data (and generate mipmaps) +Texture2D LoadTextureFromImage(Image image, bool genMipmaps); // Load a texture from image data (and generate mipmaps) +Texture2D CreateTexture(Image image, bool genMipmaps); // [DEPRECATED] Same as LoadTextureFromImage() void UnloadImage(Image image); // Unload image from CPU memory (RAM) void UnloadTexture(Texture2D texture); // Unload texture from GPU memory +void ConvertToPOT(Image *image, Color fillColor); // Convert image to POT (power-of-two) void DrawTexture(Texture2D texture, int posX, int posY, Color tint); // Draw a Texture2D void DrawTextureV(Texture2D texture, Vector2 position, Color tint); // Draw a Texture2D with position defined as Vector2 @@ -425,6 +448,7 @@ void DrawSphereEx(Vector3 centerPos, float radius, int rings, int slices, Color void DrawSphereWires(Vector3 centerPos, float radius, int rings, int slices, Color color); // Draw sphere wires void DrawCylinder(Vector3 position, float radiusTop, float radiusBottom, float height, int slices, Color color); // Draw a cylinder/cone void DrawCylinderWires(Vector3 position, float radiusTop, float radiusBottom, float height, int slices, Color color); // Draw a cylinder/cone wires +void DrawQuad(Vector3 vertices[4], Vector2 textcoords[4], Vector3 normals[4], Color colors[4]); // Draw a quad void DrawPlane(Vector3 centerPos, Vector2 size, Vector3 rotation, Color color); // Draw a plane void DrawPlaneEx(Vector3 centerPos, Vector2 size, Vector3 rotation, int slicesX, int slicesZ, Color color); // Draw a plane with divisions void DrawGrid(int slices, float spacing); // Draw a grid (centered at (0, 0, 0)) @@ -438,7 +462,7 @@ void DrawGizmoEx(Vector3 position, Vector3 rotation, float scale); Model LoadModel(const char *fileName); // Load a 3d model (.OBJ) //Model LoadModelFromRES(const char *rresName, int resId); // TODO: Load a 3d model from rRES file (raylib Resource) Model LoadHeightmap(Image heightmap, float maxHeight); // Load a heightmap image as a 3d model -Model LoadCubesmap(Image cubesmap); // Load a map image as a 3d model (cubes based) +Model LoadCubicmap(Image cubicmap); // Load a map image as a 3d model (cubes based) void UnloadModel(Model model); // Unload 3d model from memory void SetModelTexture(Model *model, Texture2D texture); // Link a texture to a model @@ -456,6 +480,7 @@ void InitAudioDevice(void); // Initialize au void CloseAudioDevice(void); // Close the audio device and context (and music stream) Sound LoadSound(char *fileName); // Load sound to memory +Sound LoadSoundFromWave(Wave wave); // Load sound to memory from wave data Sound LoadSoundFromRES(const char *rresName, int resId); // Load sound to memory from rRES file (raylib Resource) void UnloadSound(Sound sound); // Unload sound void PlaySound(Sound sound); // Play a sound diff --git a/src/raymath.c b/src/raymath.c index e598b381..ed45ee92 100644 --- a/src/raymath.c +++ b/src/raymath.c @@ -329,8 +329,6 @@ void MatrixInvert(Matrix *mat) // Calculate the invert determinant (inlined to avoid double-caching) float invDet = 1/(b00*b11 - b01*b10 + b02*b09 + b03*b08 - b04*b07 + b05*b06); - printf("%f\n", invDet); - temp.m0 = (a11*b11 - a12*b10 + a13*b09)*invDet; temp.m1 = (-a01*b11 + a02*b10 - a03*b09)*invDet; temp.m2 = (a31*b05 - a32*b04 + a33*b03)*invDet; @@ -492,6 +490,48 @@ Matrix MatrixRotate(float angleX, float angleY, float angleZ) return result; } +/* +Matrix MatrixRotate(float angle, float x, float y, float z) +{ + Matrix result = MatrixIdentity(); + + float c = cosf(angle*DEG2RAD); // cosine + float s = sinf(angle*DEG2RAD); // sine + float c1 = 1.0f - c; // 1 - c + + float m0 = result.m0, m4 = result.m4, m8 = result.m8, m12 = result.m12, + m1 = result.m1, m5 = result.m5, m9 = result.m9, m13 = result.m13, + m2 = result.m2, m6 = result.m6, m10 = result.m10, m14 = result.m14; + + // build rotation matrix + float r0 = x * x * c1 + c; + float r1 = x * y * c1 + z * s; + float r2 = x * z * c1 - y * s; + float r4 = x * y * c1 - z * s; + float r5 = y * y * c1 + c; + float r6 = y * z * c1 + x * s; + float r8 = x * z * c1 + y * s; + float r9 = y * z * c1 - x * s; + float r10= z * z * c1 + c; + + // multiply rotation matrix + result.m0 = r0*m0 + r4*m1 + r8*m2; + result.m1 = r1*m0 + r5*m1 + r9*m2; + result.m2 = r2*m0 + r6*m1 + r10*m2; + result.m4 = r0*m4 + r4*m5 + r8*m6; + result.m5 = r1*m4 + r5*m5 + r9*m6; + result.m6 = r2*m4 + r6*m5 + r10*m6; + result.m8 = r0*m8 + r4*m9 + r8*m10; + result.m9 = r1*m8 + r5*m9 + r9*m10; + result.m10 = r2*m8 + r6*m9 + r10*m10; + result.m12 = r0*m12+ r4*m13 + r8*m14; + result.m13 = r1*m12+ r5*m13 + r9*m14; + result.m14 = r2*m12+ r6*m13 + r10*m14; + + return result; +} +*/ + // Create rotation matrix from axis and angle // TODO: Test this function // NOTE: NO prototype defined! @@ -559,8 +599,8 @@ Matrix MatrixFromAxisAngle2(Vector3 axis, float angle) float axisX = axis.x, axisY = axis.y, axisZ = axis.y; // Calculate angles - float cosres = (float)cos(-angle); - float sinres = (float)sin(-angle); + float cosres = (float)cos(angle); + float sinres = (float)sin(angle); float t = 1.0f - cosres; // Do the conversion math once @@ -668,6 +708,7 @@ Matrix MatrixScale(float x, float y, float z) // Returns transformation matrix for a given translation, rotation and scale // NOTE: Transformation order is rotation -> scale -> translation +// NOTE: Rotation angles should come in radians Matrix MatrixTransform(Vector3 translation, Vector3 rotation, Vector3 scale) { Matrix result = MatrixIdentity(); diff --git a/src/raymath.h b/src/raymath.h index c396a347..c8c1a26c 100644 --- a/src/raymath.h +++ b/src/raymath.h @@ -55,7 +55,7 @@ } Vector3; #endif -// Matrix type (OpenGL style 4x4 - right handed) +// Matrix type (OpenGL style 4x4 - right handed, column major) typedef struct Matrix { float m0, m4, m8, m12; float m1, m5, m9, m13; @@ -107,9 +107,9 @@ Matrix MatrixIdentity(void); // Returns identity matr Matrix MatrixAdd(Matrix left, Matrix right); // Add two matrices Matrix MatrixSubstract(Matrix left, Matrix right); // Substract two matrices (left - right) Matrix MatrixTranslate(float x, float y, float z); // Returns translation matrix -Matrix MatrixRotate(float angleX, float angleY, float angleZ); // Returns rotation matrix -Matrix MatrixRotateAroundAxis(Vector3 axis, float angle); // Returns rotation matrix for an angle around an specified axis -Matrix MatrixRotateAroundAxis2(Vector3 axis, float angle); // Returns rotation matrix for an angle around an specified axis (test another implemntation) +Matrix MatrixRotate(float axisX, float axisY, float axisZ); // Returns rotation matrix +Matrix MatrixFromAxisAngle(Vector3 axis, float angle); // Returns rotation matrix for an angle around an specified axis +Matrix MatrixFromAxisAngle2(Vector3 axis, float angle); // Returns rotation matrix for an angle around an specified axis (test another implemntation) Matrix MatrixFromQuaternion(Quaternion q); // Returns rotation matrix for a given quaternion Matrix MatrixRotateX(float angle); // Returns x-rotation matrix (angle in radians) Matrix MatrixRotateY(float angle); // Returns y-rotation matrix (angle in radians) @@ -32,18 +32,16 @@ #include <stdlib.h> // Declares malloc() and free() for memory management, rand() #if defined(GRAPHICS_API_OPENGL_11) - #ifdef __APPLE__ + #ifdef __APPLE__ // OpenGL include for OSX #include <OpenGL/gl.h> - #include <OpenGL/glu.h> - #include <GLUT/glut.h> #else #include <GL/gl.h> // Basic OpenGL include - #endif + #endif #endif #if defined(GRAPHICS_API_OPENGL_33) #define GLEW_STATIC - #ifdef __APPLE__ + #ifdef __APPLE__ // OpenGL include for OSX #include <OpenGL/gl3.h> #else #include <GL/glew.h> // Extensions loading lib @@ -177,21 +175,23 @@ static Vector3 *tempBuffer; static int tempBufferCount = 0; static bool useTempBuffer = false; -// White texture useful for plain color polys (required by shader) -static GLuint whiteTexture; - // Support for VAOs (OpenGL ES2 could not support VAO extensions) static bool vaoSupported = false; #endif #if defined(GRAPHICS_API_OPENGL_ES2) // NOTE: VAO functionality is exposed through extensions (OES) +// emscripten does not support VAOs static PFNGLGENVERTEXARRAYSOESPROC glGenVertexArrays; static PFNGLBINDVERTEXARRAYOESPROC glBindVertexArray; static PFNGLDELETEVERTEXARRAYSOESPROC glDeleteVertexArrays; static PFNGLISVERTEXARRAYOESPROC glIsVertexArray; #endif +// White texture useful for plain color polys (required by shader) +// NOTE: It's required in shapes and models modules! +unsigned int whiteTexture; + //---------------------------------------------------------------------------------- // Module specific Functions Declaration //---------------------------------------------------------------------------------- @@ -307,10 +307,21 @@ void rlRotatef(float angleDeg, float x, float y, float z) // TODO: Support rotation in multiple axes Matrix rot = MatrixIdentity(); + // OPTION 1: It works... if (x == 1) rot = MatrixRotateX(angleDeg*DEG2RAD); else if (y == 1) rot = MatrixRotateY(angleDeg*DEG2RAD); else if (z == 1) rot = MatrixRotateZ(angleDeg*DEG2RAD); - + + // OPTION 2: Requires review... + //Vector3 vec = (Vector3){ 0, 1, 0 }; + //VectorNormalize(&vec); + //rot = MatrixFromAxisAngle(vec, angleDeg*DEG2RAD); // Working? + + // OPTION 3: TODO: Review, it doesn't work! + //Vector3 vec = (Vector3){ x, y, z }; + //VectorNormalize(&vec); + //rot = MatrixRotate(angleDeg*vec.x, angleDeg*vec.x, angleDeg*vec.x); + MatrixTranspose(&rot); *currentMatrix = MatrixMultiply(*currentMatrix, rot); @@ -769,10 +780,13 @@ void rlglInit(void) #endif #if defined(GRAPHICS_API_OPENGL_ES2) + // NOTE: emscripten does not support VAOs natively, it uses emulation and it reduces overall performance... +#if !defined(PLATFORM_WEB) glGenVertexArrays = (PFNGLGENVERTEXARRAYSOESPROC)eglGetProcAddress("glGenVertexArraysOES"); glBindVertexArray = (PFNGLBINDVERTEXARRAYOESPROC)eglGetProcAddress("glBindVertexArrayOES"); glDeleteVertexArrays = (PFNGLDELETEVERTEXARRAYSOESPROC)eglGetProcAddress("glDeleteVertexArraysOES"); glIsVertexArray = (PFNGLISVERTEXARRAYOESPROC)eglGetProcAddress("glIsVertexArrayOES"); +#endif if (glGenVertexArrays == NULL) TraceLog(WARNING, "Could not initialize VAO extensions, VAOs not supported"); else @@ -857,8 +871,8 @@ void rlglInit(void) whiteTexture = rlglLoadTexture(pixels, 1, 1, false); - if (whiteTexture != 0) TraceLog(INFO, "[TEX ID %i] Base white texture created successfully", whiteTexture); - else TraceLog(WARNING, "Base white texture could not be created"); + if (whiteTexture != 0) TraceLog(INFO, "[TEX ID %i] Base white texture loaded successfully", whiteTexture); + else TraceLog(WARNING, "Base white texture could not be loaded"); // Init draw calls tracking system draws = (DrawCall *)malloc(sizeof(DrawCall)*MAX_DRAWS_BY_TEXTURE); @@ -1122,6 +1136,8 @@ void rlglDrawModel(Model model, Vector3 position, Vector3 rotation, Vector3 scal #if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) glUseProgram(shaderProgram); // Use our shader + VectorScale(&rotation, DEG2RAD); + // Get transform matrix (rotation -> scale -> translation) Matrix transform = MatrixTransform(position, rotation, scale); Matrix modelviewworld = MatrixMultiply(transform, modelview); @@ -1314,7 +1330,8 @@ unsigned int rlglLoadTexture(unsigned char *data, int width, int height, bool ge #if defined(GRAPHICS_API_OPENGL_33) // NOTE: We define internal (GPU) format as GL_RGBA8 (probably BGRA8 in practice, driver takes care) - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data); + //glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data); // OpenGL + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data); // WebGL #elif defined(GRAPHICS_API_OPENGL_ES2) // NOTE: On embedded systems, we let the driver choose the best internal format glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data); @@ -1333,7 +1350,7 @@ unsigned int rlglLoadTexture(unsigned char *data, int width, int height, bool ge // Unbind current texture glBindTexture(GL_TEXTURE_2D, 0); - TraceLog(INFO, "[TEX ID %i] Texture created successfully (%i x %i)", id, width, height); + TraceLog(INFO, "[TEX ID %i] Texture created successfully (%ix%i)", id, width, height); return id; } @@ -1356,7 +1373,7 @@ Model rlglLoadModel(VertexData mesh) #elif defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) model.textureId = 1; // Default whiteTexture - GLuint vaoModel; // Vertex Array Objects (VAO) + GLuint vaoModel = 0; // Vertex Array Objects (VAO) GLuint vertexBuffer[3]; // Vertex Buffer Objects (VBO) if (vaoSupported) @@ -1571,6 +1588,7 @@ static GLuint LoadDefaultShaders(void) char fShaderStr[] = " #version 110 \n" // NOTE: Equivalent to version 100 on ES2 #elif defined(GRAPHICS_API_OPENGL_ES2) char fShaderStr[] = " #version 100 \n" // NOTE: Must be defined this way! 110 doesn't work! + "precision mediump float; \n" // WebGL, required for emscripten #endif "uniform sampler2D texture0; \n" "varying vec2 fragTexCoord; \n" @@ -1682,33 +1700,35 @@ static GLuint LoadShaders(char *vertexFileName, char *fragmentFileName) } // Read shader text file -static char *TextFileRead(char *fn) +static char *TextFileRead(char *fileName) { - FILE *fp; + FILE *textFile; char *text = NULL; int count=0; - if (fn != NULL) + if (fileName != NULL) { - fp = fopen(fn,"rt"); + textFile = fopen(fileName,"rt"); - if (fp != NULL) + if (textFile != NULL) { - fseek(fp, 0, SEEK_END); - count = ftell(fp); - rewind(fp); + fseek(textFile, 0, SEEK_END); + count = ftell(textFile); + rewind(textFile); if (count > 0) { text = (char *)malloc(sizeof(char) * (count+1)); - count = fread(text, sizeof(char), count, fp); + count = fread(text, sizeof(char), count, textFile); text[count] = '\0'; } - fclose(fp); + fclose(textFile); } + else TraceLog(WARNING, "[%s] Text file could not be opened", fileName); } + return text; } @@ -1807,7 +1827,7 @@ static void InitializeBuffersGPU(void) glGenVertexArrays(1, &vaoTriangles); glBindVertexArray(vaoTriangles); } - + // Create buffers for our vertex data glGenBuffers(2, trianglesBuffer); @@ -1832,7 +1852,7 @@ static void InitializeBuffersGPU(void) glGenVertexArrays(1, &vaoQuads); glBindVertexArray(vaoQuads); } - + // Create buffers for our vertex data glGenBuffers(4, quadsBuffer); @@ -1868,6 +1888,8 @@ static void InitializeBuffersGPU(void) } // Update VBOs with vertex array data +// TODO: If there is not vertex data, buffers doesn't need to be updated (vertexCount > 0) +// TODO: If no data changed on the CPU arrays --> No need to update GPU arrays every frame! static void UpdateBuffers(void) { // Activate Lines VAO @@ -1982,7 +2004,7 @@ static int GenerateMipmaps(unsigned char *data, int baseWidth, int baseHeight) j++; } - TraceLog(DEBUG, "Mipmap base (%i, %i)", width, height); + TraceLog(DEBUG, "Mipmap base (%ix%i)", width, height); for (int mip = 1; mip < mipmapCount; mip++) { @@ -2053,7 +2075,7 @@ static pixel *GenNextMipmap(pixel *srcData, int srcWidth, int srcHeight) } } - TraceLog(DEBUG, "Mipmap generated successfully (%i, %i)", width, height); + TraceLog(DEBUG, "Mipmap generated successfully (%ix%i)", width, height); return mipmap; } @@ -45,7 +45,7 @@ // Choose opengl version here or just define it at compile time: -DGRAPHICS_API_OPENGL_33 //#define GRAPHICS_API_OPENGL_11 // Only available on PLATFORM_DESKTOP //#define GRAPHICS_API_OPENGL_33 // Only available on PLATFORM_DESKTOP -//#define GRAPHICS_API_OPENGL_ES2 // Only available on PLATFORM_ANDROID or PLATFORM_RPI +//#define GRAPHICS_API_OPENGL_ES2 // Only available on PLATFORM_ANDROID or PLATFORM_RPI or PLATFORM_WEB // Security check in case no GRAPHICS_API_OPENGL_* defined #if !defined(GRAPHICS_API_OPENGL_11) && !defined(GRAPHICS_API_OPENGL_33) && !defined(GRAPHICS_API_OPENGL_ES2) @@ -72,8 +72,9 @@ #define MAX_TRIANGLES_BATCH 4096 #define MAX_QUADS_BATCH 4096 #elif defined(GRAPHICS_API_OPENGL_ES2) - // NOTE: Reduce memory sizes for embedded systems (RPI) - #define MAX_LINES_BATCH 2048 // Critical for wire shapes (sphere) + // NOTE: Reduce memory sizes for embedded systems (RPI and HTML5) + // NOTE: On HTML5 (emscripten) this is allocated on heap, by default it's only 16MB!...just take care... + #define MAX_LINES_BATCH 1024 // Critical for wire shapes (sphere) #define MAX_TRIANGLES_BATCH 2048 // Critical for some shapes (sphere) #define MAX_QUADS_BATCH 1024 // Be careful with text, every letter maps a quad #endif @@ -81,8 +82,6 @@ //---------------------------------------------------------------------------------- // Types and Structures Definition //---------------------------------------------------------------------------------- -typedef unsigned char byte; - typedef enum { RL_PROJECTION, RL_MODELVIEW, RL_TEXTURE } MatrixMode; typedef enum { RL_LINES, RL_TRIANGLES, RL_QUADS } DrawMode; diff --git a/src/shapes.c b/src/shapes.c index 6fa26bee..d872eacf 100644 --- a/src/shapes.c +++ b/src/shapes.c @@ -44,7 +44,7 @@ //---------------------------------------------------------------------------------- // Global Variables Definition //---------------------------------------------------------------------------------- -// It's lonely here... +extern unsigned int whiteTexture; //---------------------------------------------------------------------------------- // Module specific Functions Declaration @@ -197,7 +197,7 @@ void DrawRectangleV(Vector2 position, Vector2 size, Color color) else if ((rlGetVersion() == OPENGL_33) || (rlGetVersion() == OPENGL_ES_20)) { // NOTE: This shape uses QUADS to avoid drawing order issues (view rlglDraw) - rlEnableTexture(1); // Default white texture + rlEnableTexture(whiteTexture); // Default white texture rlBegin(RL_QUADS); rlColor4ub(color.r, color.g, color.b, color.a); diff --git a/src/stb_image.c b/src/stb_image.c deleted file mode 100644 index b9e1b304..00000000 --- a/src/stb_image.c +++ /dev/null @@ -1,4342 +0,0 @@ -#include "stb_image.h" - -#ifndef STBI_HEADER_FILE_ONLY - -#ifndef STBI_NO_HDR -#include <math.h> // ldexp -#include <string.h> // strcmp, strtok -#endif - -#ifndef STBI_NO_STDIO -#include <stdio.h> -#endif -#include <stdlib.h> -#include <memory.h> -#include <assert.h> -#include <stdarg.h> - -#ifndef _MSC_VER - #ifdef __cplusplus - #define stbi_inline inline - #else - #define stbi_inline - #endif -#else - #define stbi_inline __forceinline -#endif - -// implementation: -typedef unsigned char uint8; -typedef unsigned short uint16; -typedef signed short int16; -typedef unsigned int uint32; -typedef signed int int32; -typedef unsigned int uint; - -// should produce compiler error if size is wrong -typedef unsigned char validate_uint32[sizeof(uint32)==4 ? 1 : -1]; - -#if defined(STBI_NO_STDIO) && !defined(STBI_NO_WRITE) -#define STBI_NO_WRITE -#endif - -#define STBI_NOTUSED(v) (void)sizeof(v) - -#ifdef _MSC_VER -#define STBI_HAS_LROTL -#endif - -#ifdef STBI_HAS_LROTL - #define stbi_lrot(x,y) _lrotl(x,y) -#else - #define stbi_lrot(x,y) (((x) << (y)) | ((x) >> (32 - (y)))) -#endif - -/////////////////////////////////////////////// -// -// stbi struct and start_xxx functions - -// stbi structure is our basic context used by all images, so it -// contains all the IO context, plus some basic image information -typedef struct -{ - uint32 img_x, img_y; - int img_n, img_out_n; - - stbi_io_callbacks io; - void *io_user_data; - - int read_from_callbacks; - int buflen; - uint8 buffer_start[128]; - - uint8 *img_buffer, *img_buffer_end; - uint8 *img_buffer_original; -} stbi; - - -static void refill_buffer(stbi *s); - -// initialize a memory-decode context -static void start_mem(stbi *s, uint8 const *buffer, int len) -{ - s->io.read = NULL; - s->read_from_callbacks = 0; - s->img_buffer = s->img_buffer_original = (uint8 *) buffer; - s->img_buffer_end = (uint8 *) buffer+len; -} - -// initialize a callback-based context -static void start_callbacks(stbi *s, stbi_io_callbacks *c, void *user) -{ - s->io = *c; - s->io_user_data = user; - s->buflen = sizeof(s->buffer_start); - s->read_from_callbacks = 1; - s->img_buffer_original = s->buffer_start; - refill_buffer(s); -} - -#ifndef STBI_NO_STDIO - -static int stdio_read(void *user, char *data, int size) -{ - return (int) fread(data,1,size,(FILE*) user); -} - -static void stdio_skip(void *user, unsigned n) -{ - fseek((FILE*) user, n, SEEK_CUR); -} - -static int stdio_eof(void *user) -{ - return feof((FILE*) user); -} - -static stbi_io_callbacks stbi_stdio_callbacks = -{ - stdio_read, - stdio_skip, - stdio_eof, -}; - -static void start_file(stbi *s, FILE *f) -{ - start_callbacks(s, &stbi_stdio_callbacks, (void *) f); -} - -//static void stop_file(stbi *s) { } - -#endif // !STBI_NO_STDIO - -static void stbi_rewind(stbi *s) -{ - // conceptually rewind SHOULD rewind to the beginning of the stream, - // but we just rewind to the beginning of the initial buffer, because - // we only use it after doing 'test', which only ever looks at at most 92 bytes - s->img_buffer = s->img_buffer_original; -} - -static int stbi_jpeg_test(stbi *s); -static stbi_uc *stbi_jpeg_load(stbi *s, int *x, int *y, int *comp, int req_comp); -static int stbi_jpeg_info(stbi *s, int *x, int *y, int *comp); -static int stbi_png_test(stbi *s); -static stbi_uc *stbi_png_load(stbi *s, int *x, int *y, int *comp, int req_comp); -static int stbi_png_info(stbi *s, int *x, int *y, int *comp); -static int stbi_bmp_test(stbi *s); -static stbi_uc *stbi_bmp_load(stbi *s, int *x, int *y, int *comp, int req_comp); -static int stbi_tga_test(stbi *s); -static stbi_uc *stbi_tga_load(stbi *s, int *x, int *y, int *comp, int req_comp); -static int stbi_tga_info(stbi *s, int *x, int *y, int *comp); -static int stbi_psd_test(stbi *s); -static stbi_uc *stbi_psd_load(stbi *s, int *x, int *y, int *comp, int req_comp); -static int stbi_pic_test(stbi *s); -static stbi_uc *stbi_pic_load(stbi *s, int *x, int *y, int *comp, int req_comp); -static int stbi_gif_test(stbi *s); -static stbi_uc *stbi_gif_load(stbi *s, int *x, int *y, int *comp, int req_comp); -static int stbi_gif_info(stbi *s, int *x, int *y, int *comp); - -// RAY: Commented because not used -//static int stbi_hdr_test(stbi *s); -//static float *stbi_hdr_load(stbi *s, int *x, int *y, int *comp, int req_comp); - -// this is not threadsafe -static const char *failure_reason; - -const char *stbi_failure_reason(void) -{ - return failure_reason; -} - -static int e(const char *str) -{ - failure_reason = str; - return 0; -} - -// e - error -// epf - error returning pointer to float -// epuc - error returning pointer to unsigned char - -#ifdef STBI_NO_FAILURE_STRINGS - #define e(x,y) 0 -#elif defined(STBI_FAILURE_USERMSG) - #define e(x,y) e(y) -#else - #define e(x,y) e(x) -#endif - -#define epf(x,y) ((float *) (e(x,y)?NULL:NULL)) -#define epuc(x,y) ((unsigned char *) (e(x,y)?NULL:NULL)) - -void stbi_image_free(void *retval_from_stbi_load) -{ - free(retval_from_stbi_load); -} - -#ifndef STBI_NO_HDR -static float *ldr_to_hdr(stbi_uc *data, int x, int y, int comp); -static stbi_uc *hdr_to_ldr(float *data, int x, int y, int comp); -#endif - -static unsigned char *stbi_load_main(stbi *s, int *x, int *y, int *comp, int req_comp) -{ - if (stbi_jpeg_test(s)) return stbi_jpeg_load(s,x,y,comp,req_comp); - if (stbi_png_test(s)) return stbi_png_load(s,x,y,comp,req_comp); - if (stbi_bmp_test(s)) return stbi_bmp_load(s,x,y,comp,req_comp); - if (stbi_gif_test(s)) return stbi_gif_load(s,x,y,comp,req_comp); - if (stbi_psd_test(s)) return stbi_psd_load(s,x,y,comp,req_comp); - if (stbi_pic_test(s)) return stbi_pic_load(s,x,y,comp,req_comp); - - #ifndef STBI_NO_HDR - if (stbi_hdr_test(s)) { - float *hdr = stbi_hdr_load(s, x,y,comp,req_comp); - return hdr_to_ldr(hdr, *x, *y, req_comp ? req_comp : *comp); - } - #endif - - // test tga last because it's a crappy test! - if (stbi_tga_test(s)) - return stbi_tga_load(s,x,y,comp,req_comp); - return epuc("unknown image type", "Image not of any known type, or corrupt"); -} - -#ifndef STBI_NO_STDIO -unsigned char *stbi_load(char const *filename, int *x, int *y, int *comp, int req_comp) -{ - FILE *f = fopen(filename, "rb"); - unsigned char *result; - if (!f) return epuc("can't fopen", "Unable to open file"); - result = stbi_load_from_file(f,x,y,comp,req_comp); - fclose(f); - return result; -} - -unsigned char *stbi_load_from_file(FILE *f, int *x, int *y, int *comp, int req_comp) -{ - stbi s; - start_file(&s,f); - return stbi_load_main(&s,x,y,comp,req_comp); -} -#endif //!STBI_NO_STDIO - -unsigned char *stbi_load_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp) -{ - stbi s; - start_mem(&s,buffer,len); - return stbi_load_main(&s,x,y,comp,req_comp); -} - -unsigned char *stbi_load_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp, int req_comp) -{ - stbi s; - start_callbacks(&s, (stbi_io_callbacks *) clbk, user); - return stbi_load_main(&s,x,y,comp,req_comp); -} - -#ifndef STBI_NO_HDR - -float *stbi_loadf_main(stbi *s, int *x, int *y, int *comp, int req_comp) -{ - unsigned char *data; - #ifndef STBI_NO_HDR - if (stbi_hdr_test(s)) - return stbi_hdr_load(s,x,y,comp,req_comp); - #endif - data = stbi_load_main(s, x, y, comp, req_comp); - if (data) - return ldr_to_hdr(data, *x, *y, req_comp ? req_comp : *comp); - return epf("unknown image type", "Image not of any known type, or corrupt"); -} - -float *stbi_loadf_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp) -{ - stbi s; - start_mem(&s,buffer,len); - return stbi_loadf_main(&s,x,y,comp,req_comp); -} - -float *stbi_loadf_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp, int req_comp) -{ - stbi s; - start_callbacks(&s, (stbi_io_callbacks *) clbk, user); - return stbi_loadf_main(&s,x,y,comp,req_comp); -} - -#ifndef STBI_NO_STDIO -float *stbi_loadf(char const *filename, int *x, int *y, int *comp, int req_comp) -{ - FILE *f = fopen(filename, "rb"); - float *result; - if (!f) return epf("can't fopen", "Unable to open file"); - result = stbi_loadf_from_file(f,x,y,comp,req_comp); - fclose(f); - return result; -} - -float *stbi_loadf_from_file(FILE *f, int *x, int *y, int *comp, int req_comp) -{ - stbi s; - start_file(&s,f); - return stbi_loadf_main(&s,x,y,comp,req_comp); -} -#endif // !STBI_NO_STDIO - -#endif // !STBI_NO_HDR - -// these is-hdr-or-not is defined independent of whether STBI_NO_HDR is -// defined, for API simplicity; if STBI_NO_HDR is defined, it always -// reports false! - -int stbi_is_hdr_from_memory(stbi_uc const *buffer, int len) -{ - #ifndef STBI_NO_HDR - stbi s; - start_mem(&s,buffer,len); - return stbi_hdr_test(&s); - #else - STBI_NOTUSED(buffer); - STBI_NOTUSED(len); - return 0; - #endif -} - -#ifndef STBI_NO_STDIO -extern int stbi_is_hdr (char const *filename) -{ - FILE *f = fopen(filename, "rb"); - int result=0; - if (f) { - result = stbi_is_hdr_from_file(f); - fclose(f); - } - return result; -} - -extern int stbi_is_hdr_from_file(FILE *f) -{ - #ifndef STBI_NO_HDR - stbi s; - start_file(&s,f); - return stbi_hdr_test(&s); - #else - return 0; - #endif -} -#endif // !STBI_NO_STDIO - -extern int stbi_is_hdr_from_callbacks(stbi_io_callbacks const *clbk, void *user) -{ - #ifndef STBI_NO_HDR - stbi s; - start_callbacks(&s, (stbi_io_callbacks *) clbk, user); - return stbi_hdr_test(&s); - #else - return 0; - #endif -} - -#ifndef STBI_NO_HDR -static float h2l_gamma_i=1.0f/2.2f, h2l_scale_i=1.0f; -static float l2h_gamma=2.2f, l2h_scale=1.0f; - -void stbi_hdr_to_ldr_gamma(float gamma) { h2l_gamma_i = 1/gamma; } -void stbi_hdr_to_ldr_scale(float scale) { h2l_scale_i = 1/scale; } - -void stbi_ldr_to_hdr_gamma(float gamma) { l2h_gamma = gamma; } -void stbi_ldr_to_hdr_scale(float scale) { l2h_scale = scale; } -#endif - - -////////////////////////////////////////////////////////////////////////////// -// -// Common code used by all image loaders -// - -enum -{ - SCAN_load=0, - SCAN_type, - SCAN_header -}; - -static void refill_buffer(stbi *s) -{ - int n = (s->io.read)(s->io_user_data,(char*)s->buffer_start,s->buflen); - if (n == 0) { - // at end of file, treat same as if from memory - s->read_from_callbacks = 0; - s->img_buffer = s->img_buffer_end-1; - *s->img_buffer = 0; - } else { - s->img_buffer = s->buffer_start; - s->img_buffer_end = s->buffer_start + n; - } -} - -stbi_inline static int get8(stbi *s) -{ - if (s->img_buffer < s->img_buffer_end) - return *s->img_buffer++; - if (s->read_from_callbacks) { - refill_buffer(s); - return *s->img_buffer++; - } - return 0; -} - -stbi_inline static int at_eof(stbi *s) -{ - if (s->io.read) { - if (!(s->io.eof)(s->io_user_data)) return 0; - // if feof() is true, check if buffer = end - // special case: we've only got the special 0 character at the end - if (s->read_from_callbacks == 0) return 1; - } - - return s->img_buffer >= s->img_buffer_end; -} - -stbi_inline static uint8 get8u(stbi *s) -{ - return (uint8) get8(s); -} - -static void skip(stbi *s, int n) -{ - if (s->io.read) { - int blen = s->img_buffer_end - s->img_buffer; - if (blen < n) { - s->img_buffer = s->img_buffer_end; - (s->io.skip)(s->io_user_data, n - blen); - return; - } - } - s->img_buffer += n; -} - -static int getn(stbi *s, stbi_uc *buffer, int n) -{ - if (s->io.read) { - int blen = s->img_buffer_end - s->img_buffer; - if (blen < n) { - int res, count; - - memcpy(buffer, s->img_buffer, blen); - - count = (s->io.read)(s->io_user_data, (char*) buffer + blen, n - blen); - res = (count == (n-blen)); - s->img_buffer = s->img_buffer_end; - return res; - } - } - - if (s->img_buffer+n <= s->img_buffer_end) { - memcpy(buffer, s->img_buffer, n); - s->img_buffer += n; - return 1; - } else - return 0; -} - -static int get16(stbi *s) -{ - int z = get8(s); - return (z << 8) + get8(s); -} - -static uint32 get32(stbi *s) -{ - uint32 z = get16(s); - return (z << 16) + get16(s); -} - -static int get16le(stbi *s) -{ - int z = get8(s); - return z + (get8(s) << 8); -} - -static uint32 get32le(stbi *s) -{ - uint32 z = get16le(s); - return z + (get16le(s) << 16); -} - -////////////////////////////////////////////////////////////////////////////// -// -// generic converter from built-in img_n to req_comp -// individual types do this automatically as much as possible (e.g. jpeg -// does all cases internally since it needs to colorspace convert anyway, -// and it never has alpha, so very few cases ). png can automatically -// interleave an alpha=255 channel, but falls back to this for other cases -// -// assume data buffer is malloced, so malloc a new one and free that one -// only failure mode is malloc failing - -static uint8 compute_y(int r, int g, int b) -{ - return (uint8) (((r*77) + (g*150) + (29*b)) >> 8); -} - -static unsigned char *convert_format(unsigned char *data, int img_n, int req_comp, uint x, uint y) -{ - int i,j; - unsigned char *good; - - if (req_comp == img_n) return data; - assert(req_comp >= 1 && req_comp <= 4); - - good = (unsigned char *) malloc(req_comp * x * y); - if (good == NULL) { - free(data); - return epuc("outofmem", "Out of memory"); - } - - for (j=0; j < (int) y; ++j) { - unsigned char *src = data + j * x * img_n ; - unsigned char *dest = good + j * x * req_comp; - - #define COMBO(a,b) ((a)*8+(b)) - #define CASE(a,b) case COMBO(a,b): for(i=x-1; i >= 0; --i, src += a, dest += b) - // convert source image with img_n components to one with req_comp components; - // avoid switch per pixel, so use switch per scanline and massive macros - switch (COMBO(img_n, req_comp)) { - CASE(1,2) dest[0]=src[0], dest[1]=255; break; - CASE(1,3) dest[0]=dest[1]=dest[2]=src[0]; break; - CASE(1,4) dest[0]=dest[1]=dest[2]=src[0], dest[3]=255; break; - CASE(2,1) dest[0]=src[0]; break; - CASE(2,3) dest[0]=dest[1]=dest[2]=src[0]; break; - CASE(2,4) dest[0]=dest[1]=dest[2]=src[0], dest[3]=src[1]; break; - CASE(3,4) dest[0]=src[0],dest[1]=src[1],dest[2]=src[2],dest[3]=255; break; - CASE(3,1) dest[0]=compute_y(src[0],src[1],src[2]); break; - CASE(3,2) dest[0]=compute_y(src[0],src[1],src[2]), dest[1] = 255; break; - CASE(4,1) dest[0]=compute_y(src[0],src[1],src[2]); break; - CASE(4,2) dest[0]=compute_y(src[0],src[1],src[2]), dest[1] = src[3]; break; - CASE(4,3) dest[0]=src[0],dest[1]=src[1],dest[2]=src[2]; break; - default: assert(0); - } - #undef CASE - } - - free(data); - return good; -} - -#ifndef STBI_NO_HDR -static float *ldr_to_hdr(stbi_uc *data, int x, int y, int comp) -{ - int i,k,n; - float *output = (float *) malloc(x * y * comp * sizeof(float)); - if (output == NULL) { free(data); return epf("outofmem", "Out of memory"); } - // compute number of non-alpha components - if (comp & 1) n = comp; else n = comp-1; - for (i=0; i < x*y; ++i) { - for (k=0; k < n; ++k) { - output[i*comp + k] = (float) pow(data[i*comp+k]/255.0f, l2h_gamma) * l2h_scale; - } - if (k < comp) output[i*comp + k] = data[i*comp+k]/255.0f; - } - free(data); - return output; -} - -#define float2int(x) ((int) (x)) -static stbi_uc *hdr_to_ldr(float *data, int x, int y, int comp) -{ - int i,k,n; - stbi_uc *output = (stbi_uc *) malloc(x * y * comp); - if (output == NULL) { free(data); return epuc("outofmem", "Out of memory"); } - // compute number of non-alpha components - if (comp & 1) n = comp; else n = comp-1; - for (i=0; i < x*y; ++i) { - for (k=0; k < n; ++k) { - float z = (float) pow(data[i*comp+k]*h2l_scale_i, h2l_gamma_i) * 255 + 0.5f; - if (z < 0) z = 0; - if (z > 255) z = 255; - output[i*comp + k] = (uint8) float2int(z); - } - if (k < comp) { - float z = data[i*comp+k] * 255 + 0.5f; - if (z < 0) z = 0; - if (z > 255) z = 255; - output[i*comp + k] = (uint8) float2int(z); - } - } - free(data); - return output; -} -#endif - -////////////////////////////////////////////////////////////////////////////// -// -// "baseline" JPEG/JFIF decoder (not actually fully baseline implementation) -// -// simple implementation -// - channel subsampling of at most 2 in each dimension -// - doesn't support delayed output of y-dimension -// - simple interface (only one output format: 8-bit interleaved RGB) -// - doesn't try to recover corrupt jpegs -// - doesn't allow partial loading, loading multiple at once -// - still fast on x86 (copying globals into locals doesn't help x86) -// - allocates lots of intermediate memory (full size of all components) -// - non-interleaved case requires this anyway -// - allows good upsampling (see next) -// high-quality -// - upsampled channels are bilinearly interpolated, even across blocks -// - quality integer IDCT derived from IJG's 'slow' -// performance -// - fast huffman; reasonable integer IDCT -// - uses a lot of intermediate memory, could cache poorly -// - load http://nothings.org/remote/anemones.jpg 3 times on 2.8Ghz P4 -// stb_jpeg: 1.34 seconds (MSVC6, default release build) -// stb_jpeg: 1.06 seconds (MSVC6, processor = Pentium Pro) -// IJL11.dll: 1.08 seconds (compiled by intel) -// IJG 1998: 0.98 seconds (MSVC6, makefile provided by IJG) -// IJG 1998: 0.95 seconds (MSVC6, makefile + proc=PPro) - -// huffman decoding acceleration -#define FAST_BITS 9 // larger handles more cases; smaller stomps less cache - -typedef struct -{ - uint8 fast[1 << FAST_BITS]; - // weirdly, repacking this into AoS is a 10% speed loss, instead of a win - uint16 code[256]; - uint8 values[256]; - uint8 size[257]; - unsigned int maxcode[18]; - int delta[17]; // old 'firstsymbol' - old 'firstcode' -} huffman; - -typedef struct -{ - #ifdef STBI_SIMD - unsigned short dequant2[4][64]; - #endif - stbi *s; - huffman huff_dc[4]; - huffman huff_ac[4]; - uint8 dequant[4][64]; - -// sizes for components, interleaved MCUs - int img_h_max, img_v_max; - int img_mcu_x, img_mcu_y; - int img_mcu_w, img_mcu_h; - -// definition of jpeg image component - struct - { - int id; - int h,v; - int tq; - int hd,ha; - int dc_pred; - - int x,y,w2,h2; - uint8 *data; - void *raw_data; - uint8 *linebuf; - } img_comp[4]; - - uint32 code_buffer; // jpeg entropy-coded buffer - int code_bits; // number of valid bits - unsigned char marker; // marker seen while filling entropy buffer - int nomore; // flag if we saw a marker so must stop - - int scan_n, order[4]; - int restart_interval, todo; -} jpeg; - -static int build_huffman(huffman *h, int *count) -{ - int i,j,k=0,code; - // build size list for each symbol (from JPEG spec) - for (i=0; i < 16; ++i) - for (j=0; j < count[i]; ++j) - h->size[k++] = (uint8) (i+1); - h->size[k] = 0; - - // compute actual symbols (from jpeg spec) - code = 0; - k = 0; - for(j=1; j <= 16; ++j) { - // compute delta to add to code to compute symbol id - h->delta[j] = k - code; - if (h->size[k] == j) { - while (h->size[k] == j) - h->code[k++] = (uint16) (code++); - if (code-1 >= (1 << j)) return e("bad code lengths","Corrupt JPEG"); - } - // compute largest code + 1 for this size, preshifted as needed later - h->maxcode[j] = code << (16-j); - code <<= 1; - } - h->maxcode[j] = 0xffffffff; - - // build non-spec acceleration table; 255 is flag for not-accelerated - memset(h->fast, 255, 1 << FAST_BITS); - for (i=0; i < k; ++i) { - int s = h->size[i]; - if (s <= FAST_BITS) { - int c = h->code[i] << (FAST_BITS-s); - int m = 1 << (FAST_BITS-s); - for (j=0; j < m; ++j) { - h->fast[c+j] = (uint8) i; - } - } - } - return 1; -} - -static void grow_buffer_unsafe(jpeg *j) -{ - do { - int b = j->nomore ? 0 : get8(j->s); - if (b == 0xff) { - int c = get8(j->s); - if (c != 0) { - j->marker = (unsigned char) c; - j->nomore = 1; - return; - } - } - j->code_buffer |= b << (24 - j->code_bits); - j->code_bits += 8; - } while (j->code_bits <= 24); -} - -// (1 << n) - 1 -static uint32 bmask[17]={0,1,3,7,15,31,63,127,255,511,1023,2047,4095,8191,16383,32767,65535}; - -// decode a jpeg huffman value from the bitstream -stbi_inline static int decode(jpeg *j, huffman *h) -{ - unsigned int temp; - int c,k; - - if (j->code_bits < 16) grow_buffer_unsafe(j); - - // look at the top FAST_BITS and determine what symbol ID it is, - // if the code is <= FAST_BITS - c = (j->code_buffer >> (32 - FAST_BITS)) & ((1 << FAST_BITS)-1); - k = h->fast[c]; - if (k < 255) { - int s = h->size[k]; - if (s > j->code_bits) - return -1; - j->code_buffer <<= s; - j->code_bits -= s; - return h->values[k]; - } - - // naive test is to shift the code_buffer down so k bits are - // valid, then test against maxcode. To speed this up, we've - // preshifted maxcode left so that it has (16-k) 0s at the - // end; in other words, regardless of the number of bits, it - // wants to be compared against something shifted to have 16; - // that way we don't need to shift inside the loop. - temp = j->code_buffer >> 16; - for (k=FAST_BITS+1 ; ; ++k) - if (temp < h->maxcode[k]) - break; - if (k == 17) { - // error! code not found - j->code_bits -= 16; - return -1; - } - - if (k > j->code_bits) - return -1; - - // convert the huffman code to the symbol id - c = ((j->code_buffer >> (32 - k)) & bmask[k]) + h->delta[k]; - assert((((j->code_buffer) >> (32 - h->size[c])) & bmask[h->size[c]]) == h->code[c]); - - // convert the id to a symbol - j->code_bits -= k; - j->code_buffer <<= k; - return h->values[c]; -} - -// combined JPEG 'receive' and JPEG 'extend', since baseline -// always extends everything it receives. -stbi_inline static int extend_receive(jpeg *j, int n) -{ - unsigned int m = 1 << (n-1); - unsigned int k; - if (j->code_bits < n) grow_buffer_unsafe(j); - - #if 1 - k = stbi_lrot(j->code_buffer, n); - j->code_buffer = k & ~bmask[n]; - k &= bmask[n]; - j->code_bits -= n; - #else - k = (j->code_buffer >> (32 - n)) & bmask[n]; - j->code_bits -= n; - j->code_buffer <<= n; - #endif - // the following test is probably a random branch that won't - // predict well. I tried to table accelerate it but failed. - // maybe it's compiling as a conditional move? - if (k < m) - return (-1 << n) + k + 1; - else - return k; -} - -// given a value that's at position X in the zigzag stream, -// where does it appear in the 8x8 matrix coded as row-major? -static uint8 dezigzag[64+15] = -{ - 0, 1, 8, 16, 9, 2, 3, 10, - 17, 24, 32, 25, 18, 11, 4, 5, - 12, 19, 26, 33, 40, 48, 41, 34, - 27, 20, 13, 6, 7, 14, 21, 28, - 35, 42, 49, 56, 57, 50, 43, 36, - 29, 22, 15, 23, 30, 37, 44, 51, - 58, 59, 52, 45, 38, 31, 39, 46, - 53, 60, 61, 54, 47, 55, 62, 63, - // let corrupt input sample past end - 63, 63, 63, 63, 63, 63, 63, 63, - 63, 63, 63, 63, 63, 63, 63 -}; - -// decode one 64-entry block-- -static int decode_block(jpeg *j, short data[64], huffman *hdc, huffman *hac, int b) -{ - int diff,dc,k; - int t = decode(j, hdc); - if (t < 0) return e("bad huffman code","Corrupt JPEG"); - - // 0 all the ac values now so we can do it 32-bits at a time - memset(data,0,64*sizeof(data[0])); - - diff = t ? extend_receive(j, t) : 0; - dc = j->img_comp[b].dc_pred + diff; - j->img_comp[b].dc_pred = dc; - data[0] = (short) dc; - - // decode AC components, see JPEG spec - k = 1; - do { - int r,s; - int rs = decode(j, hac); - if (rs < 0) return e("bad huffman code","Corrupt JPEG"); - s = rs & 15; - r = rs >> 4; - if (s == 0) { - if (rs != 0xf0) break; // end block - k += 16; - } else { - k += r; - // decode into unzigzag'd location - data[dezigzag[k++]] = (short) extend_receive(j,s); - } - } while (k < 64); - return 1; -} - -// take a -128..127 value and clamp it and convert to 0..255 -stbi_inline static uint8 clamp(int x) -{ - // trick to use a single test to catch both cases - if ((unsigned int) x > 255) { - if (x < 0) return 0; - if (x > 255) return 255; - } - return (uint8) x; -} - -#define f2f(x) (int) (((x) * 4096 + 0.5)) -#define fsh(x) ((x) << 12) - -// derived from jidctint -- DCT_ISLOW -#define IDCT_1D(s0,s1,s2,s3,s4,s5,s6,s7) \ - int t0,t1,t2,t3,p1,p2,p3,p4,p5,x0,x1,x2,x3; \ - p2 = s2; \ - p3 = s6; \ - p1 = (p2+p3) * f2f(0.5411961f); \ - t2 = p1 + p3*f2f(-1.847759065f); \ - t3 = p1 + p2*f2f( 0.765366865f); \ - p2 = s0; \ - p3 = s4; \ - t0 = fsh(p2+p3); \ - t1 = fsh(p2-p3); \ - x0 = t0+t3; \ - x3 = t0-t3; \ - x1 = t1+t2; \ - x2 = t1-t2; \ - t0 = s7; \ - t1 = s5; \ - t2 = s3; \ - t3 = s1; \ - p3 = t0+t2; \ - p4 = t1+t3; \ - p1 = t0+t3; \ - p2 = t1+t2; \ - p5 = (p3+p4)*f2f( 1.175875602f); \ - t0 = t0*f2f( 0.298631336f); \ - t1 = t1*f2f( 2.053119869f); \ - t2 = t2*f2f( 3.072711026f); \ - t3 = t3*f2f( 1.501321110f); \ - p1 = p5 + p1*f2f(-0.899976223f); \ - p2 = p5 + p2*f2f(-2.562915447f); \ - p3 = p3*f2f(-1.961570560f); \ - p4 = p4*f2f(-0.390180644f); \ - t3 += p1+p4; \ - t2 += p2+p3; \ - t1 += p2+p4; \ - t0 += p1+p3; - -#ifdef STBI_SIMD -typedef unsigned short stbi_dequantize_t; -#else -typedef uint8 stbi_dequantize_t; -#endif - -// .344 seconds on 3*anemones.jpg -static void idct_block(uint8 *out, int out_stride, short data[64], stbi_dequantize_t *dequantize) -{ - int i,val[64],*v=val; - stbi_dequantize_t *dq = dequantize; - uint8 *o; - short *d = data; - - // columns - for (i=0; i < 8; ++i,++d,++dq, ++v) { - // if all zeroes, shortcut -- this avoids dequantizing 0s and IDCTing - if (d[ 8]==0 && d[16]==0 && d[24]==0 && d[32]==0 - && d[40]==0 && d[48]==0 && d[56]==0) { - // no shortcut 0 seconds - // (1|2|3|4|5|6|7)==0 0 seconds - // all separate -0.047 seconds - // 1 && 2|3 && 4|5 && 6|7: -0.047 seconds - int dcterm = d[0] * dq[0] << 2; - v[0] = v[8] = v[16] = v[24] = v[32] = v[40] = v[48] = v[56] = dcterm; - } else { - IDCT_1D(d[ 0]*dq[ 0],d[ 8]*dq[ 8],d[16]*dq[16],d[24]*dq[24], - d[32]*dq[32],d[40]*dq[40],d[48]*dq[48],d[56]*dq[56]) - // constants scaled things up by 1<<12; let's bring them back - // down, but keep 2 extra bits of precision - x0 += 512; x1 += 512; x2 += 512; x3 += 512; - v[ 0] = (x0+t3) >> 10; - v[56] = (x0-t3) >> 10; - v[ 8] = (x1+t2) >> 10; - v[48] = (x1-t2) >> 10; - v[16] = (x2+t1) >> 10; - v[40] = (x2-t1) >> 10; - v[24] = (x3+t0) >> 10; - v[32] = (x3-t0) >> 10; - } - } - - for (i=0, v=val, o=out; i < 8; ++i,v+=8,o+=out_stride) { - // no fast case since the first 1D IDCT spread components out - IDCT_1D(v[0],v[1],v[2],v[3],v[4],v[5],v[6],v[7]) - // constants scaled things up by 1<<12, plus we had 1<<2 from first - // loop, plus horizontal and vertical each scale by sqrt(8) so together - // we've got an extra 1<<3, so 1<<17 total we need to remove. - // so we want to round that, which means adding 0.5 * 1<<17, - // aka 65536. Also, we'll end up with -128 to 127 that we want - // to encode as 0..255 by adding 128, so we'll add that before the shift - x0 += 65536 + (128<<17); - x1 += 65536 + (128<<17); - x2 += 65536 + (128<<17); - x3 += 65536 + (128<<17); - // tried computing the shifts into temps, or'ing the temps to see - // if any were out of range, but that was slower - o[0] = clamp((x0+t3) >> 17); - o[7] = clamp((x0-t3) >> 17); - o[1] = clamp((x1+t2) >> 17); - o[6] = clamp((x1-t2) >> 17); - o[2] = clamp((x2+t1) >> 17); - o[5] = clamp((x2-t1) >> 17); - o[3] = clamp((x3+t0) >> 17); - o[4] = clamp((x3-t0) >> 17); - } -} - -#ifdef STBI_SIMD -static stbi_idct_8x8 stbi_idct_installed = idct_block; - -void stbi_install_idct(stbi_idct_8x8 func) -{ - stbi_idct_installed = func; -} -#endif - -#define MARKER_none 0xff -// if there's a pending marker from the entropy stream, return that -// otherwise, fetch from the stream and get a marker. if there's no -// marker, return 0xff, which is never a valid marker value -static uint8 get_marker(jpeg *j) -{ - uint8 x; - if (j->marker != MARKER_none) { x = j->marker; j->marker = MARKER_none; return x; } - x = get8u(j->s); - if (x != 0xff) return MARKER_none; - while (x == 0xff) - x = get8u(j->s); - return x; -} - -// in each scan, we'll have scan_n components, and the order -// of the components is specified by order[] -#define RESTART(x) ((x) >= 0xd0 && (x) <= 0xd7) - -// after a restart interval, reset the entropy decoder and -// the dc prediction -static void reset(jpeg *j) -{ - j->code_bits = 0; - j->code_buffer = 0; - j->nomore = 0; - j->img_comp[0].dc_pred = j->img_comp[1].dc_pred = j->img_comp[2].dc_pred = 0; - j->marker = MARKER_none; - j->todo = j->restart_interval ? j->restart_interval : 0x7fffffff; - // no more than 1<<31 MCUs if no restart_interal? that's plenty safe, - // since we don't even allow 1<<30 pixels -} - -static int parse_entropy_coded_data(jpeg *z) -{ - reset(z); - if (z->scan_n == 1) { - int i,j; - #ifdef STBI_SIMD - __declspec(align(16)) - #endif - short data[64]; - int n = z->order[0]; - // non-interleaved data, we just need to process one block at a time, - // in trivial scanline order - // number of blocks to do just depends on how many actual "pixels" this - // component has, independent of interleaved MCU blocking and such - int w = (z->img_comp[n].x+7) >> 3; - int h = (z->img_comp[n].y+7) >> 3; - for (j=0; j < h; ++j) { - for (i=0; i < w; ++i) { - if (!decode_block(z, data, z->huff_dc+z->img_comp[n].hd, z->huff_ac+z->img_comp[n].ha, n)) return 0; - #ifdef STBI_SIMD - stbi_idct_installed(z->img_comp[n].data+z->img_comp[n].w2*j*8+i*8, z->img_comp[n].w2, data, z->dequant2[z->img_comp[n].tq]); - #else - idct_block(z->img_comp[n].data+z->img_comp[n].w2*j*8+i*8, z->img_comp[n].w2, data, z->dequant[z->img_comp[n].tq]); - #endif - // every data block is an MCU, so countdown the restart interval - if (--z->todo <= 0) { - if (z->code_bits < 24) grow_buffer_unsafe(z); - // if it's NOT a restart, then just bail, so we get corrupt data - // rather than no data - if (!RESTART(z->marker)) return 1; - reset(z); - } - } - } - } else { // interleaved! - int i,j,k,x,y; - short data[64]; - for (j=0; j < z->img_mcu_y; ++j) { - for (i=0; i < z->img_mcu_x; ++i) { - // scan an interleaved mcu... process scan_n components in order - for (k=0; k < z->scan_n; ++k) { - int n = z->order[k]; - // scan out an mcu's worth of this component; that's just determined - // by the basic H and V specified for the component - for (y=0; y < z->img_comp[n].v; ++y) { - for (x=0; x < z->img_comp[n].h; ++x) { - int x2 = (i*z->img_comp[n].h + x)*8; - int y2 = (j*z->img_comp[n].v + y)*8; - if (!decode_block(z, data, z->huff_dc+z->img_comp[n].hd, z->huff_ac+z->img_comp[n].ha, n)) return 0; - #ifdef STBI_SIMD - stbi_idct_installed(z->img_comp[n].data+z->img_comp[n].w2*y2+x2, z->img_comp[n].w2, data, z->dequant2[z->img_comp[n].tq]); - #else - idct_block(z->img_comp[n].data+z->img_comp[n].w2*y2+x2, z->img_comp[n].w2, data, z->dequant[z->img_comp[n].tq]); - #endif - } - } - } - // after all interleaved components, that's an interleaved MCU, - // so now count down the restart interval - if (--z->todo <= 0) { - if (z->code_bits < 24) grow_buffer_unsafe(z); - // if it's NOT a restart, then just bail, so we get corrupt data - // rather than no data - if (!RESTART(z->marker)) return 1; - reset(z); - } - } - } - } - return 1; -} - -static int process_marker(jpeg *z, int m) -{ - int L; - switch (m) { - case MARKER_none: // no marker found - return e("expected marker","Corrupt JPEG"); - - case 0xC2: // SOF - progressive - return e("progressive jpeg","JPEG format not supported (progressive)"); - - case 0xDD: // DRI - specify restart interval - if (get16(z->s) != 4) return e("bad DRI len","Corrupt JPEG"); - z->restart_interval = get16(z->s); - return 1; - - case 0xDB: // DQT - define quantization table - L = get16(z->s)-2; - while (L > 0) { - int q = get8(z->s); - int p = q >> 4; - int t = q & 15,i; - if (p != 0) return e("bad DQT type","Corrupt JPEG"); - if (t > 3) return e("bad DQT table","Corrupt JPEG"); - for (i=0; i < 64; ++i) - z->dequant[t][dezigzag[i]] = get8u(z->s); - #ifdef STBI_SIMD - for (i=0; i < 64; ++i) - z->dequant2[t][i] = z->dequant[t][i]; - #endif - L -= 65; - } - return L==0; - - case 0xC4: // DHT - define huffman table - L = get16(z->s)-2; - while (L > 0) { - uint8 *v; - int sizes[16],i,m=0; - int q = get8(z->s); - int tc = q >> 4; - int th = q & 15; - if (tc > 1 || th > 3) return e("bad DHT header","Corrupt JPEG"); - for (i=0; i < 16; ++i) { - sizes[i] = get8(z->s); - m += sizes[i]; - } - L -= 17; - if (tc == 0) { - if (!build_huffman(z->huff_dc+th, sizes)) return 0; - v = z->huff_dc[th].values; - } else { - if (!build_huffman(z->huff_ac+th, sizes)) return 0; - v = z->huff_ac[th].values; - } - for (i=0; i < m; ++i) - v[i] = get8u(z->s); - L -= m; - } - return L==0; - } - // check for comment block or APP blocks - if ((m >= 0xE0 && m <= 0xEF) || m == 0xFE) { - skip(z->s, get16(z->s)-2); - return 1; - } - return 0; -} - -// after we see SOS -static int process_scan_header(jpeg *z) -{ - int i; - int Ls = get16(z->s); - z->scan_n = get8(z->s); - if (z->scan_n < 1 || z->scan_n > 4 || z->scan_n > (int) z->s->img_n) return e("bad SOS component count","Corrupt JPEG"); - if (Ls != 6+2*z->scan_n) return e("bad SOS len","Corrupt JPEG"); - for (i=0; i < z->scan_n; ++i) { - int id = get8(z->s), which; - int q = get8(z->s); - for (which = 0; which < z->s->img_n; ++which) - if (z->img_comp[which].id == id) - break; - if (which == z->s->img_n) return 0; - z->img_comp[which].hd = q >> 4; if (z->img_comp[which].hd > 3) return e("bad DC huff","Corrupt JPEG"); - z->img_comp[which].ha = q & 15; if (z->img_comp[which].ha > 3) return e("bad AC huff","Corrupt JPEG"); - z->order[i] = which; - } - if (get8(z->s) != 0) return e("bad SOS","Corrupt JPEG"); - get8(z->s); // should be 63, but might be 0 - if (get8(z->s) != 0) return e("bad SOS","Corrupt JPEG"); - - return 1; -} - -static int process_frame_header(jpeg *z, int scan) -{ - stbi *s = z->s; - int Lf,p,i,q, h_max=1,v_max=1,c; - Lf = get16(s); if (Lf < 11) return e("bad SOF len","Corrupt JPEG"); // JPEG - p = get8(s); if (p != 8) return e("only 8-bit","JPEG format not supported: 8-bit only"); // JPEG baseline - s->img_y = get16(s); if (s->img_y == 0) return e("no header height", "JPEG format not supported: delayed height"); // Legal, but we don't handle it--but neither does IJG - s->img_x = get16(s); if (s->img_x == 0) return e("0 width","Corrupt JPEG"); // JPEG requires - c = get8(s); - if (c != 3 && c != 1) return e("bad component count","Corrupt JPEG"); // JFIF requires - s->img_n = c; - for (i=0; i < c; ++i) { - z->img_comp[i].data = NULL; - z->img_comp[i].linebuf = NULL; - } - - if (Lf != 8+3*s->img_n) return e("bad SOF len","Corrupt JPEG"); - - for (i=0; i < s->img_n; ++i) { - z->img_comp[i].id = get8(s); - if (z->img_comp[i].id != i+1) // JFIF requires - if (z->img_comp[i].id != i) // some version of jpegtran outputs non-JFIF-compliant files! - return e("bad component ID","Corrupt JPEG"); - q = get8(s); - z->img_comp[i].h = (q >> 4); if (!z->img_comp[i].h || z->img_comp[i].h > 4) return e("bad H","Corrupt JPEG"); - z->img_comp[i].v = q & 15; if (!z->img_comp[i].v || z->img_comp[i].v > 4) return e("bad V","Corrupt JPEG"); - z->img_comp[i].tq = get8(s); if (z->img_comp[i].tq > 3) return e("bad TQ","Corrupt JPEG"); - } - - if (scan != SCAN_load) return 1; - - if ((1 << 30) / s->img_x / s->img_n < s->img_y) return e("too large", "Image too large to decode"); - - for (i=0; i < s->img_n; ++i) { - if (z->img_comp[i].h > h_max) h_max = z->img_comp[i].h; - if (z->img_comp[i].v > v_max) v_max = z->img_comp[i].v; - } - - // compute interleaved mcu info - z->img_h_max = h_max; - z->img_v_max = v_max; - z->img_mcu_w = h_max * 8; - z->img_mcu_h = v_max * 8; - z->img_mcu_x = (s->img_x + z->img_mcu_w-1) / z->img_mcu_w; - z->img_mcu_y = (s->img_y + z->img_mcu_h-1) / z->img_mcu_h; - - for (i=0; i < s->img_n; ++i) { - // number of effective pixels (e.g. for non-interleaved MCU) - z->img_comp[i].x = (s->img_x * z->img_comp[i].h + h_max-1) / h_max; - z->img_comp[i].y = (s->img_y * z->img_comp[i].v + v_max-1) / v_max; - // to simplify generation, we'll allocate enough memory to decode - // the bogus oversized data from using interleaved MCUs and their - // big blocks (e.g. a 16x16 iMCU on an image of width 33); we won't - // discard the extra data until colorspace conversion - z->img_comp[i].w2 = z->img_mcu_x * z->img_comp[i].h * 8; - z->img_comp[i].h2 = z->img_mcu_y * z->img_comp[i].v * 8; - z->img_comp[i].raw_data = malloc(z->img_comp[i].w2 * z->img_comp[i].h2+15); - if (z->img_comp[i].raw_data == NULL) { - for(--i; i >= 0; --i) { - free(z->img_comp[i].raw_data); - z->img_comp[i].data = NULL; - } - return e("outofmem", "Out of memory"); - } - // align blocks for installable-idct using mmx/sse - z->img_comp[i].data = (uint8*) (((size_t) z->img_comp[i].raw_data + 15) & ~15); - z->img_comp[i].linebuf = NULL; - } - - return 1; -} - -// use comparisons since in some cases we handle more than one case (e.g. SOF) -#define DNL(x) ((x) == 0xdc) -#define SOI(x) ((x) == 0xd8) -#define EOI(x) ((x) == 0xd9) -#define SOF(x) ((x) == 0xc0 || (x) == 0xc1) -#define SOS(x) ((x) == 0xda) - -static int decode_jpeg_header(jpeg *z, int scan) -{ - int m; - z->marker = MARKER_none; // initialize cached marker to empty - m = get_marker(z); - if (!SOI(m)) return e("no SOI","Corrupt JPEG"); - if (scan == SCAN_type) return 1; - m = get_marker(z); - while (!SOF(m)) { - if (!process_marker(z,m)) return 0; - m = get_marker(z); - while (m == MARKER_none) { - // some files have extra padding after their blocks, so ok, we'll scan - if (at_eof(z->s)) return e("no SOF", "Corrupt JPEG"); - m = get_marker(z); - } - } - if (!process_frame_header(z, scan)) return 0; - return 1; -} - -static int decode_jpeg_image(jpeg *j) -{ - int m; - j->restart_interval = 0; - if (!decode_jpeg_header(j, SCAN_load)) return 0; - m = get_marker(j); - while (!EOI(m)) { - if (SOS(m)) { - if (!process_scan_header(j)) return 0; - if (!parse_entropy_coded_data(j)) return 0; - if (j->marker == MARKER_none ) { - // handle 0s at the end of image data from IP Kamera 9060 - while (!at_eof(j->s)) { - int x = get8(j->s); - if (x == 255) { - j->marker = get8u(j->s); - break; - } else if (x != 0) { - return 0; - } - } - // if we reach eof without hitting a marker, get_marker() below will fail and we'll eventually return 0 - } - } else { - if (!process_marker(j, m)) return 0; - } - m = get_marker(j); - } - return 1; -} - -// static jfif-centered resampling (across block boundaries) - -typedef uint8 *(*resample_row_func)(uint8 *out, uint8 *in0, uint8 *in1, - int w, int hs); - -#define div4(x) ((uint8) ((x) >> 2)) - -static uint8 *resample_row_1(uint8 *out, uint8 *in_near, uint8 *in_far, int w, int hs) -{ - STBI_NOTUSED(out); - STBI_NOTUSED(in_far); - STBI_NOTUSED(w); - STBI_NOTUSED(hs); - return in_near; -} - -static uint8* resample_row_v_2(uint8 *out, uint8 *in_near, uint8 *in_far, int w, int hs) -{ - // need to generate two samples vertically for every one in input - int i; - STBI_NOTUSED(hs); - for (i=0; i < w; ++i) - out[i] = div4(3*in_near[i] + in_far[i] + 2); - return out; -} - -static uint8* resample_row_h_2(uint8 *out, uint8 *in_near, uint8 *in_far, int w, int hs) -{ - // need to generate two samples horizontally for every one in input - int i; - uint8 *input = in_near; - - if (w == 1) { - // if only one sample, can't do any interpolation - out[0] = out[1] = input[0]; - return out; - } - - out[0] = input[0]; - out[1] = div4(input[0]*3 + input[1] + 2); - for (i=1; i < w-1; ++i) { - int n = 3*input[i]+2; - out[i*2+0] = div4(n+input[i-1]); - out[i*2+1] = div4(n+input[i+1]); - } - out[i*2+0] = div4(input[w-2]*3 + input[w-1] + 2); - out[i*2+1] = input[w-1]; - - STBI_NOTUSED(in_far); - STBI_NOTUSED(hs); - - return out; -} - -#define div16(x) ((uint8) ((x) >> 4)) - -static uint8 *resample_row_hv_2(uint8 *out, uint8 *in_near, uint8 *in_far, int w, int hs) -{ - // need to generate 2x2 samples for every one in input - int i,t0,t1; - if (w == 1) { - out[0] = out[1] = div4(3*in_near[0] + in_far[0] + 2); - return out; - } - - t1 = 3*in_near[0] + in_far[0]; - out[0] = div4(t1+2); - for (i=1; i < w; ++i) { - t0 = t1; - t1 = 3*in_near[i]+in_far[i]; - out[i*2-1] = div16(3*t0 + t1 + 8); - out[i*2 ] = div16(3*t1 + t0 + 8); - } - out[w*2-1] = div4(t1+2); - - STBI_NOTUSED(hs); - - return out; -} - -static uint8 *resample_row_generic(uint8 *out, uint8 *in_near, uint8 *in_far, int w, int hs) -{ - // resample with nearest-neighbor - int i,j; - in_far = in_far; - for (i=0; i < w; ++i) - for (j=0; j < hs; ++j) - out[i*hs+j] = in_near[i]; - return out; -} - -#define float2fixed(x) ((int) ((x) * 65536 + 0.5)) - -// 0.38 seconds on 3*anemones.jpg (0.25 with processor = Pro) -// VC6 without processor=Pro is generating multiple LEAs per multiply! -static void YCbCr_to_RGB_row(uint8 *out, const uint8 *y, const uint8 *pcb, const uint8 *pcr, int count, int step) -{ - int i; - for (i=0; i < count; ++i) { - int y_fixed = (y[i] << 16) + 32768; // rounding - int r,g,b; - int cr = pcr[i] - 128; - int cb = pcb[i] - 128; - r = y_fixed + cr*float2fixed(1.40200f); - g = y_fixed - cr*float2fixed(0.71414f) - cb*float2fixed(0.34414f); - b = y_fixed + cb*float2fixed(1.77200f); - r >>= 16; - g >>= 16; - b >>= 16; - if ((unsigned) r > 255) { if (r < 0) r = 0; else r = 255; } - if ((unsigned) g > 255) { if (g < 0) g = 0; else g = 255; } - if ((unsigned) b > 255) { if (b < 0) b = 0; else b = 255; } - out[0] = (uint8)r; - out[1] = (uint8)g; - out[2] = (uint8)b; - out[3] = 255; - out += step; - } -} - -#ifdef STBI_SIMD -static stbi_YCbCr_to_RGB_run stbi_YCbCr_installed = YCbCr_to_RGB_row; - -void stbi_install_YCbCr_to_RGB(stbi_YCbCr_to_RGB_run func) -{ - stbi_YCbCr_installed = func; -} -#endif - - -// clean up the temporary component buffers -static void cleanup_jpeg(jpeg *j) -{ - int i; - for (i=0; i < j->s->img_n; ++i) { - if (j->img_comp[i].data) { - free(j->img_comp[i].raw_data); - j->img_comp[i].data = NULL; - } - if (j->img_comp[i].linebuf) { - free(j->img_comp[i].linebuf); - j->img_comp[i].linebuf = NULL; - } - } -} - -typedef struct -{ - resample_row_func resample; - uint8 *line0,*line1; - int hs,vs; // expansion factor in each axis - int w_lores; // horizontal pixels pre-expansion - int ystep; // how far through vertical expansion we are - int ypos; // which pre-expansion row we're on -} stbi_resample; - -static uint8 *load_jpeg_image(jpeg *z, int *out_x, int *out_y, int *comp, int req_comp) -{ - int n, decode_n; - // validate req_comp - if (req_comp < 0 || req_comp > 4) return epuc("bad req_comp", "Internal error"); - z->s->img_n = 0; - - // load a jpeg image from whichever source - if (!decode_jpeg_image(z)) { cleanup_jpeg(z); return NULL; } - - // determine actual number of components to generate - n = req_comp ? req_comp : z->s->img_n; - - if (z->s->img_n == 3 && n < 3) - decode_n = 1; - else - decode_n = z->s->img_n; - - // resample and color-convert - { - int k; - uint i,j; - uint8 *output; - uint8 *coutput[4]; - - stbi_resample res_comp[4]; - - for (k=0; k < decode_n; ++k) { - stbi_resample *r = &res_comp[k]; - - // allocate line buffer big enough for upsampling off the edges - // with upsample factor of 4 - z->img_comp[k].linebuf = (uint8 *) malloc(z->s->img_x + 3); - if (!z->img_comp[k].linebuf) { cleanup_jpeg(z); return epuc("outofmem", "Out of memory"); } - - r->hs = z->img_h_max / z->img_comp[k].h; - r->vs = z->img_v_max / z->img_comp[k].v; - r->ystep = r->vs >> 1; - r->w_lores = (z->s->img_x + r->hs-1) / r->hs; - r->ypos = 0; - r->line0 = r->line1 = z->img_comp[k].data; - - if (r->hs == 1 && r->vs == 1) r->resample = resample_row_1; - else if (r->hs == 1 && r->vs == 2) r->resample = resample_row_v_2; - else if (r->hs == 2 && r->vs == 1) r->resample = resample_row_h_2; - else if (r->hs == 2 && r->vs == 2) r->resample = resample_row_hv_2; - else r->resample = resample_row_generic; - } - - // can't error after this so, this is safe - output = (uint8 *) malloc(n * z->s->img_x * z->s->img_y + 1); - if (!output) { cleanup_jpeg(z); return epuc("outofmem", "Out of memory"); } - - // now go ahead and resample - for (j=0; j < z->s->img_y; ++j) { - uint8 *out = output + n * z->s->img_x * j; - for (k=0; k < decode_n; ++k) { - stbi_resample *r = &res_comp[k]; - int y_bot = r->ystep >= (r->vs >> 1); - coutput[k] = r->resample(z->img_comp[k].linebuf, - y_bot ? r->line1 : r->line0, - y_bot ? r->line0 : r->line1, - r->w_lores, r->hs); - if (++r->ystep >= r->vs) { - r->ystep = 0; - r->line0 = r->line1; - if (++r->ypos < z->img_comp[k].y) - r->line1 += z->img_comp[k].w2; - } - } - if (n >= 3) { - uint8 *y = coutput[0]; - if (z->s->img_n == 3) { - #ifdef STBI_SIMD - stbi_YCbCr_installed(out, y, coutput[1], coutput[2], z->s.img_x, n); - #else - YCbCr_to_RGB_row(out, y, coutput[1], coutput[2], z->s->img_x, n); - #endif - } else - for (i=0; i < z->s->img_x; ++i) { - out[0] = out[1] = out[2] = y[i]; - out[3] = 255; // not used if n==3 - out += n; - } - } else { - uint8 *y = coutput[0]; - if (n == 1) - for (i=0; i < z->s->img_x; ++i) out[i] = y[i]; - else - for (i=0; i < z->s->img_x; ++i) *out++ = y[i], *out++ = 255; - } - } - cleanup_jpeg(z); - *out_x = z->s->img_x; - *out_y = z->s->img_y; - if (comp) *comp = z->s->img_n; // report original components, not output - return output; - } -} - -static unsigned char *stbi_jpeg_load(stbi *s, int *x, int *y, int *comp, int req_comp) -{ - jpeg j; - j.s = s; - return load_jpeg_image(&j, x,y,comp,req_comp); -} - -static int stbi_jpeg_test(stbi *s) -{ - int r; - jpeg j; - j.s = s; - r = decode_jpeg_header(&j, SCAN_type); - stbi_rewind(s); - return r; -} - -static int stbi_jpeg_info_raw(jpeg *j, int *x, int *y, int *comp) -{ - if (!decode_jpeg_header(j, SCAN_header)) { - stbi_rewind( j->s ); - return 0; - } - if (x) *x = j->s->img_x; - if (y) *y = j->s->img_y; - if (comp) *comp = j->s->img_n; - return 1; -} - -static int stbi_jpeg_info(stbi *s, int *x, int *y, int *comp) -{ - jpeg j; - j.s = s; - return stbi_jpeg_info_raw(&j, x, y, comp); -} - -// public domain zlib decode v0.2 Sean Barrett 2006-11-18 -// simple implementation -// - all input must be provided in an upfront buffer -// - all output is written to a single output buffer (can malloc/realloc) -// performance -// - fast huffman - -// fast-way is faster to check than jpeg huffman, but slow way is slower -#define ZFAST_BITS 9 // accelerate all cases in default tables -#define ZFAST_MASK ((1 << ZFAST_BITS) - 1) - -// zlib-style huffman encoding -// (jpegs packs from left, zlib from right, so can't share code) -typedef struct -{ - uint16 fast[1 << ZFAST_BITS]; - uint16 firstcode[16]; - int maxcode[17]; - uint16 firstsymbol[16]; - uint8 size[288]; - uint16 value[288]; -} zhuffman; - -stbi_inline static int bitreverse16(int n) -{ - n = ((n & 0xAAAA) >> 1) | ((n & 0x5555) << 1); - n = ((n & 0xCCCC) >> 2) | ((n & 0x3333) << 2); - n = ((n & 0xF0F0) >> 4) | ((n & 0x0F0F) << 4); - n = ((n & 0xFF00) >> 8) | ((n & 0x00FF) << 8); - return n; -} - -stbi_inline static int bit_reverse(int v, int bits) -{ - assert(bits <= 16); - // to bit reverse n bits, reverse 16 and shift - // e.g. 11 bits, bit reverse and shift away 5 - return bitreverse16(v) >> (16-bits); -} - -static int zbuild_huffman(zhuffman *z, uint8 *sizelist, int num) -{ - int i,k=0; - int code, next_code[16], sizes[17]; - - // DEFLATE spec for generating codes - memset(sizes, 0, sizeof(sizes)); - memset(z->fast, 255, sizeof(z->fast)); - for (i=0; i < num; ++i) - ++sizes[sizelist[i]]; - sizes[0] = 0; - for (i=1; i < 16; ++i) - assert(sizes[i] <= (1 << i)); - code = 0; - for (i=1; i < 16; ++i) { - next_code[i] = code; - z->firstcode[i] = (uint16) code; - z->firstsymbol[i] = (uint16) k; - code = (code + sizes[i]); - if (sizes[i]) - if (code-1 >= (1 << i)) return e("bad codelengths","Corrupt JPEG"); - z->maxcode[i] = code << (16-i); // preshift for inner loop - code <<= 1; - k += sizes[i]; - } - z->maxcode[16] = 0x10000; // sentinel - for (i=0; i < num; ++i) { - int s = sizelist[i]; - if (s) { - int c = next_code[s] - z->firstcode[s] + z->firstsymbol[s]; - z->size[c] = (uint8)s; - z->value[c] = (uint16)i; - if (s <= ZFAST_BITS) { - int k = bit_reverse(next_code[s],s); - while (k < (1 << ZFAST_BITS)) { - z->fast[k] = (uint16) c; - k += (1 << s); - } - } - ++next_code[s]; - } - } - return 1; -} - -// zlib-from-memory implementation for PNG reading -// because PNG allows splitting the zlib stream arbitrarily, -// and it's annoying structurally to have PNG call ZLIB call PNG, -// we require PNG read all the IDATs and combine them into a single -// memory buffer - -typedef struct -{ - uint8 *zbuffer, *zbuffer_end; - int num_bits; - uint32 code_buffer; - - char *zout; - char *zout_start; - char *zout_end; - int z_expandable; - - zhuffman z_length, z_distance; -} zbuf; - -stbi_inline static int zget8(zbuf *z) -{ - if (z->zbuffer >= z->zbuffer_end) return 0; - return *z->zbuffer++; -} - -static void fill_bits(zbuf *z) -{ - do { - assert(z->code_buffer < (1U << z->num_bits)); - z->code_buffer |= zget8(z) << z->num_bits; - z->num_bits += 8; - } while (z->num_bits <= 24); -} - -stbi_inline static unsigned int zreceive(zbuf *z, int n) -{ - unsigned int k; - if (z->num_bits < n) fill_bits(z); - k = z->code_buffer & ((1 << n) - 1); - z->code_buffer >>= n; - z->num_bits -= n; - return k; -} - -stbi_inline static int zhuffman_decode(zbuf *a, zhuffman *z) -{ - int b,s,k; - if (a->num_bits < 16) fill_bits(a); - b = z->fast[a->code_buffer & ZFAST_MASK]; - if (b < 0xffff) { - s = z->size[b]; - a->code_buffer >>= s; - a->num_bits -= s; - return z->value[b]; - } - - // not resolved by fast table, so compute it the slow way - // use jpeg approach, which requires MSbits at top - k = bit_reverse(a->code_buffer, 16); - for (s=ZFAST_BITS+1; ; ++s) - if (k < z->maxcode[s]) - break; - if (s == 16) return -1; // invalid code! - // code size is s, so: - b = (k >> (16-s)) - z->firstcode[s] + z->firstsymbol[s]; - assert(z->size[b] == s); - a->code_buffer >>= s; - a->num_bits -= s; - return z->value[b]; -} - -static int expand(zbuf *z, int n) // need to make room for n bytes -{ - char *q; - int cur, limit; - if (!z->z_expandable) return e("output buffer limit","Corrupt PNG"); - cur = (int) (z->zout - z->zout_start); - limit = (int) (z->zout_end - z->zout_start); - while (cur + n > limit) - limit *= 2; - q = (char *) realloc(z->zout_start, limit); - if (q == NULL) return e("outofmem", "Out of memory"); - z->zout_start = q; - z->zout = q + cur; - z->zout_end = q + limit; - return 1; -} - -static int length_base[31] = { - 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,0,0 }; - -static int length_extra[31]= -{ 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,0,0 }; - -static int dist_base[32] = { 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,0,0}; - -static int dist_extra[32] = -{ 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}; - -static int parse_huffman_block(zbuf *a) -{ - for(;;) { - int z = zhuffman_decode(a, &a->z_length); - if (z < 256) { - if (z < 0) return e("bad huffman code","Corrupt PNG"); // error in huffman codes - if (a->zout >= a->zout_end) if (!expand(a, 1)) return 0; - *a->zout++ = (char) z; - } else { - uint8 *p; - int len,dist; - if (z == 256) return 1; - z -= 257; - len = length_base[z]; - if (length_extra[z]) len += zreceive(a, length_extra[z]); - z = zhuffman_decode(a, &a->z_distance); - if (z < 0) return e("bad huffman code","Corrupt PNG"); - dist = dist_base[z]; - if (dist_extra[z]) dist += zreceive(a, dist_extra[z]); - if (a->zout - a->zout_start < dist) return e("bad dist","Corrupt PNG"); - if (a->zout + len > a->zout_end) if (!expand(a, len)) return 0; - p = (uint8 *) (a->zout - dist); - while (len--) - *a->zout++ = *p++; - } - } -} - -static int compute_huffman_codes(zbuf *a) -{ - static uint8 length_dezigzag[19] = { 16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15 }; - zhuffman z_codelength; - uint8 lencodes[286+32+137];//padding for maximum single op - uint8 codelength_sizes[19]; - int i,n; - - int hlit = zreceive(a,5) + 257; - int hdist = zreceive(a,5) + 1; - int hclen = zreceive(a,4) + 4; - - memset(codelength_sizes, 0, sizeof(codelength_sizes)); - for (i=0; i < hclen; ++i) { - int s = zreceive(a,3); - codelength_sizes[length_dezigzag[i]] = (uint8) s; - } - if (!zbuild_huffman(&z_codelength, codelength_sizes, 19)) return 0; - - n = 0; - while (n < hlit + hdist) { - int c = zhuffman_decode(a, &z_codelength); - assert(c >= 0 && c < 19); - if (c < 16) - lencodes[n++] = (uint8) c; - else if (c == 16) { - c = zreceive(a,2)+3; - memset(lencodes+n, lencodes[n-1], c); - n += c; - } else if (c == 17) { - c = zreceive(a,3)+3; - memset(lencodes+n, 0, c); - n += c; - } else { - assert(c == 18); - c = zreceive(a,7)+11; - memset(lencodes+n, 0, c); - n += c; - } - } - if (n != hlit+hdist) return e("bad codelengths","Corrupt PNG"); - if (!zbuild_huffman(&a->z_length, lencodes, hlit)) return 0; - if (!zbuild_huffman(&a->z_distance, lencodes+hlit, hdist)) return 0; - return 1; -} - -static int parse_uncompressed_block(zbuf *a) -{ - uint8 header[4]; - int len,nlen,k; - if (a->num_bits & 7) - zreceive(a, a->num_bits & 7); // discard - // drain the bit-packed data into header - k = 0; - while (a->num_bits > 0) { - header[k++] = (uint8) (a->code_buffer & 255); // wtf this warns? - a->code_buffer >>= 8; - a->num_bits -= 8; - } - assert(a->num_bits == 0); - // now fill header the normal way - while (k < 4) - header[k++] = (uint8) zget8(a); - len = header[1] * 256 + header[0]; - nlen = header[3] * 256 + header[2]; - if (nlen != (len ^ 0xffff)) return e("zlib corrupt","Corrupt PNG"); - if (a->zbuffer + len > a->zbuffer_end) return e("read past buffer","Corrupt PNG"); - if (a->zout + len > a->zout_end) - if (!expand(a, len)) return 0; - memcpy(a->zout, a->zbuffer, len); - a->zbuffer += len; - a->zout += len; - return 1; -} - -static int parse_zlib_header(zbuf *a) -{ - int cmf = zget8(a); - int cm = cmf & 15; - /* int cinfo = cmf >> 4; */ - int flg = zget8(a); - if ((cmf*256+flg) % 31 != 0) return e("bad zlib header","Corrupt PNG"); // zlib spec - if (flg & 32) return e("no preset dict","Corrupt PNG"); // preset dictionary not allowed in png - if (cm != 8) return e("bad compression","Corrupt PNG"); // DEFLATE required for png - // window = 1 << (8 + cinfo)... but who cares, we fully buffer output - return 1; -} - -// @TODO: should statically initialize these for optimal thread safety -static uint8 default_length[288], default_distance[32]; -static void init_defaults(void) -{ - int i; // use <= to match clearly with spec - for (i=0; i <= 143; ++i) default_length[i] = 8; - for ( ; i <= 255; ++i) default_length[i] = 9; - for ( ; i <= 279; ++i) default_length[i] = 7; - for ( ; i <= 287; ++i) default_length[i] = 8; - - for (i=0; i <= 31; ++i) default_distance[i] = 5; -} - -int stbi_png_partial; // a quick hack to only allow decoding some of a PNG... I should implement real streaming support instead -static int parse_zlib(zbuf *a, int parse_header) -{ - int final, type; - if (parse_header) - if (!parse_zlib_header(a)) return 0; - a->num_bits = 0; - a->code_buffer = 0; - do { - final = zreceive(a,1); - type = zreceive(a,2); - if (type == 0) { - if (!parse_uncompressed_block(a)) return 0; - } else if (type == 3) { - return 0; - } else { - if (type == 1) { - // use fixed code lengths - if (!default_distance[31]) init_defaults(); - if (!zbuild_huffman(&a->z_length , default_length , 288)) return 0; - if (!zbuild_huffman(&a->z_distance, default_distance, 32)) return 0; - } else { - if (!compute_huffman_codes(a)) return 0; - } - if (!parse_huffman_block(a)) return 0; - } - if (stbi_png_partial && a->zout - a->zout_start > 65536) - break; - } while (!final); - return 1; -} - -static int do_zlib(zbuf *a, char *obuf, int olen, int exp, int parse_header) -{ - a->zout_start = obuf; - a->zout = obuf; - a->zout_end = obuf + olen; - a->z_expandable = exp; - - return parse_zlib(a, parse_header); -} - -char *stbi_zlib_decode_malloc_guesssize(const char *buffer, int len, int initial_size, int *outlen) -{ - zbuf a; - char *p = (char *) malloc(initial_size); - if (p == NULL) return NULL; - a.zbuffer = (uint8 *) buffer; - a.zbuffer_end = (uint8 *) buffer + len; - if (do_zlib(&a, p, initial_size, 1, 1)) { - if (outlen) *outlen = (int) (a.zout - a.zout_start); - return a.zout_start; - } else { - free(a.zout_start); - return NULL; - } -} - -char *stbi_zlib_decode_malloc(char const *buffer, int len, int *outlen) -{ - return stbi_zlib_decode_malloc_guesssize(buffer, len, 16384, outlen); -} - -char *stbi_zlib_decode_malloc_guesssize_headerflag(const char *buffer, int len, int initial_size, int *outlen, int parse_header) -{ - zbuf a; - char *p = (char *) malloc(initial_size); - if (p == NULL) return NULL; - a.zbuffer = (uint8 *) buffer; - a.zbuffer_end = (uint8 *) buffer + len; - if (do_zlib(&a, p, initial_size, 1, parse_header)) { - if (outlen) *outlen = (int) (a.zout - a.zout_start); - return a.zout_start; - } else { - free(a.zout_start); - return NULL; - } -} - -int stbi_zlib_decode_buffer(char *obuffer, int olen, char const *ibuffer, int ilen) -{ - zbuf a; - a.zbuffer = (uint8 *) ibuffer; - a.zbuffer_end = (uint8 *) ibuffer + ilen; - if (do_zlib(&a, obuffer, olen, 0, 1)) - return (int) (a.zout - a.zout_start); - else - return -1; -} - -char *stbi_zlib_decode_noheader_malloc(char const *buffer, int len, int *outlen) -{ - zbuf a; - char *p = (char *) malloc(16384); - if (p == NULL) return NULL; - a.zbuffer = (uint8 *) buffer; - a.zbuffer_end = (uint8 *) buffer+len; - if (do_zlib(&a, p, 16384, 1, 0)) { - if (outlen) *outlen = (int) (a.zout - a.zout_start); - return a.zout_start; - } else { - free(a.zout_start); - return NULL; - } -} - -int stbi_zlib_decode_noheader_buffer(char *obuffer, int olen, const char *ibuffer, int ilen) -{ - zbuf a; - a.zbuffer = (uint8 *) ibuffer; - a.zbuffer_end = (uint8 *) ibuffer + ilen; - if (do_zlib(&a, obuffer, olen, 0, 0)) - return (int) (a.zout - a.zout_start); - else - return -1; -} - -// public domain "baseline" PNG decoder v0.10 Sean Barrett 2006-11-18 -// simple implementation -// - only 8-bit samples -// - no CRC checking -// - allocates lots of intermediate memory -// - avoids problem of streaming data between subsystems -// - avoids explicit window management -// performance -// - uses stb_zlib, a PD zlib implementation with fast huffman decoding - - -typedef struct -{ - uint32 length; - uint32 type; -} chunk; - -#define PNG_TYPE(a,b,c,d) (((a) << 24) + ((b) << 16) + ((c) << 8) + (d)) - -static chunk get_chunk_header(stbi *s) -{ - chunk c; - c.length = get32(s); - c.type = get32(s); - return c; -} - -static int check_png_header(stbi *s) -{ - static uint8 png_sig[8] = { 137,80,78,71,13,10,26,10 }; - int i; - for (i=0; i < 8; ++i) - if (get8u(s) != png_sig[i]) return e("bad png sig","Not a PNG"); - return 1; -} - -typedef struct -{ - stbi *s; - uint8 *idata, *expanded, *out; -} png; - - -enum { - F_none=0, F_sub=1, F_up=2, F_avg=3, F_paeth=4, - F_avg_first, F_paeth_first -}; - -static uint8 first_row_filter[5] = -{ - F_none, F_sub, F_none, F_avg_first, F_paeth_first -}; - -static int paeth(int a, int b, int c) -{ - int p = a + b - c; - int pa = abs(p-a); - int pb = abs(p-b); - int pc = abs(p-c); - if (pa <= pb && pa <= pc) return a; - if (pb <= pc) return b; - return c; -} - -// create the png data from post-deflated data -static int create_png_image_raw(png *a, uint8 *raw, uint32 raw_len, int out_n, uint32 x, uint32 y) -{ - stbi *s = a->s; - uint32 i,j,stride = x*out_n; - int k; - int img_n = s->img_n; // copy it into a local for later - assert(out_n == s->img_n || out_n == s->img_n+1); - if (stbi_png_partial) y = 1; - a->out = (uint8 *) malloc(x * y * out_n); - if (!a->out) return e("outofmem", "Out of memory"); - if (!stbi_png_partial) { - if (s->img_x == x && s->img_y == y) { - if (raw_len != (img_n * x + 1) * y) return e("not enough pixels","Corrupt PNG"); - } else { // interlaced: - if (raw_len < (img_n * x + 1) * y) return e("not enough pixels","Corrupt PNG"); - } - } - for (j=0; j < y; ++j) { - uint8 *cur = a->out + stride*j; - uint8 *prior = cur - stride; - int filter = *raw++; - if (filter > 4) return e("invalid filter","Corrupt PNG"); - // if first row, use special filter that doesn't sample previous row - if (j == 0) filter = first_row_filter[filter]; - // handle first pixel explicitly - for (k=0; k < img_n; ++k) { - switch (filter) { - case F_none : cur[k] = raw[k]; break; - case F_sub : cur[k] = raw[k]; break; - case F_up : cur[k] = raw[k] + prior[k]; break; - case F_avg : cur[k] = raw[k] + (prior[k]>>1); break; - case F_paeth : cur[k] = (uint8) (raw[k] + paeth(0,prior[k],0)); break; - case F_avg_first : cur[k] = raw[k]; break; - case F_paeth_first: cur[k] = raw[k]; break; - } - } - if (img_n != out_n) cur[img_n] = 255; - raw += img_n; - cur += out_n; - prior += out_n; - // this is a little gross, so that we don't switch per-pixel or per-component - if (img_n == out_n) { - #define CASE(f) \ - case f: \ - for (i=x-1; i >= 1; --i, raw+=img_n,cur+=img_n,prior+=img_n) \ - for (k=0; k < img_n; ++k) - switch (filter) { - CASE(F_none) cur[k] = raw[k]; break; - CASE(F_sub) cur[k] = raw[k] + cur[k-img_n]; break; - CASE(F_up) cur[k] = raw[k] + prior[k]; break; - CASE(F_avg) cur[k] = raw[k] + ((prior[k] + cur[k-img_n])>>1); break; - CASE(F_paeth) cur[k] = (uint8) (raw[k] + paeth(cur[k-img_n],prior[k],prior[k-img_n])); break; - CASE(F_avg_first) cur[k] = raw[k] + (cur[k-img_n] >> 1); break; - CASE(F_paeth_first) cur[k] = (uint8) (raw[k] + paeth(cur[k-img_n],0,0)); break; - } - #undef CASE - } else { - assert(img_n+1 == out_n); - #define CASE(f) \ - case f: \ - for (i=x-1; i >= 1; --i, cur[img_n]=255,raw+=img_n,cur+=out_n,prior+=out_n) \ - for (k=0; k < img_n; ++k) - switch (filter) { - CASE(F_none) cur[k] = raw[k]; break; - CASE(F_sub) cur[k] = raw[k] + cur[k-out_n]; break; - CASE(F_up) cur[k] = raw[k] + prior[k]; break; - CASE(F_avg) cur[k] = raw[k] + ((prior[k] + cur[k-out_n])>>1); break; - CASE(F_paeth) cur[k] = (uint8) (raw[k] + paeth(cur[k-out_n],prior[k],prior[k-out_n])); break; - CASE(F_avg_first) cur[k] = raw[k] + (cur[k-out_n] >> 1); break; - CASE(F_paeth_first) cur[k] = (uint8) (raw[k] + paeth(cur[k-out_n],0,0)); break; - } - #undef CASE - } - } - return 1; -} - -static int create_png_image(png *a, uint8 *raw, uint32 raw_len, int out_n, int interlaced) -{ - uint8 *final; - int p; - int save; - if (!interlaced) - return create_png_image_raw(a, raw, raw_len, out_n, a->s->img_x, a->s->img_y); - save = stbi_png_partial; - stbi_png_partial = 0; - - // de-interlacing - final = (uint8 *) malloc(a->s->img_x * a->s->img_y * out_n); - for (p=0; p < 7; ++p) { - int xorig[] = { 0,4,0,2,0,1,0 }; - int yorig[] = { 0,0,4,0,2,0,1 }; - int xspc[] = { 8,8,4,4,2,2,1 }; - int yspc[] = { 8,8,8,4,4,2,2 }; - int i,j,x,y; - // pass1_x[4] = 0, pass1_x[5] = 1, pass1_x[12] = 1 - x = (a->s->img_x - xorig[p] + xspc[p]-1) / xspc[p]; - y = (a->s->img_y - yorig[p] + yspc[p]-1) / yspc[p]; - if (x && y) { - if (!create_png_image_raw(a, raw, raw_len, out_n, x, y)) { - free(final); - return 0; - } - for (j=0; j < y; ++j) - for (i=0; i < x; ++i) - memcpy(final + (j*yspc[p]+yorig[p])*a->s->img_x*out_n + (i*xspc[p]+xorig[p])*out_n, - a->out + (j*x+i)*out_n, out_n); - free(a->out); - raw += (x*out_n+1)*y; - raw_len -= (x*out_n+1)*y; - } - } - a->out = final; - - stbi_png_partial = save; - return 1; -} - -static int compute_transparency(png *z, uint8 tc[3], int out_n) -{ - stbi *s = z->s; - uint32 i, pixel_count = s->img_x * s->img_y; - uint8 *p = z->out; - - // compute color-based transparency, assuming we've - // already got 255 as the alpha value in the output - assert(out_n == 2 || out_n == 4); - - if (out_n == 2) { - for (i=0; i < pixel_count; ++i) { - p[1] = (p[0] == tc[0] ? 0 : 255); - p += 2; - } - } else { - for (i=0; i < pixel_count; ++i) { - if (p[0] == tc[0] && p[1] == tc[1] && p[2] == tc[2]) - p[3] = 0; - p += 4; - } - } - return 1; -} - -static int expand_palette(png *a, uint8 *palette, int len, int pal_img_n) -{ - uint32 i, pixel_count = a->s->img_x * a->s->img_y; - uint8 *p, *temp_out, *orig = a->out; - - p = (uint8 *) malloc(pixel_count * pal_img_n); - if (p == NULL) return e("outofmem", "Out of memory"); - - // between here and free(out) below, exitting would leak - temp_out = p; - - if (pal_img_n == 3) { - for (i=0; i < pixel_count; ++i) { - int n = orig[i]*4; - p[0] = palette[n ]; - p[1] = palette[n+1]; - p[2] = palette[n+2]; - p += 3; - } - } else { - for (i=0; i < pixel_count; ++i) { - int n = orig[i]*4; - p[0] = palette[n ]; - p[1] = palette[n+1]; - p[2] = palette[n+2]; - p[3] = palette[n+3]; - p += 4; - } - } - free(a->out); - a->out = temp_out; - - STBI_NOTUSED(len); - - return 1; -} - -static int stbi_unpremultiply_on_load = 0; -static int stbi_de_iphone_flag = 0; - -void stbi_set_unpremultiply_on_load(int flag_true_if_should_unpremultiply) -{ - stbi_unpremultiply_on_load = flag_true_if_should_unpremultiply; -} -void stbi_convert_iphone_png_to_rgb(int flag_true_if_should_convert) -{ - stbi_de_iphone_flag = flag_true_if_should_convert; -} - -static void stbi_de_iphone(png *z) -{ - stbi *s = z->s; - uint32 i, pixel_count = s->img_x * s->img_y; - uint8 *p = z->out; - - if (s->img_out_n == 3) { // convert bgr to rgb - for (i=0; i < pixel_count; ++i) { - uint8 t = p[0]; - p[0] = p[2]; - p[2] = t; - p += 3; - } - } else { - assert(s->img_out_n == 4); - if (stbi_unpremultiply_on_load) { - // convert bgr to rgb and unpremultiply - for (i=0; i < pixel_count; ++i) { - uint8 a = p[3]; - uint8 t = p[0]; - if (a) { - p[0] = p[2] * 255 / a; - p[1] = p[1] * 255 / a; - p[2] = t * 255 / a; - } else { - p[0] = p[2]; - p[2] = t; - } - p += 4; - } - } else { - // convert bgr to rgb - for (i=0; i < pixel_count; ++i) { - uint8 t = p[0]; - p[0] = p[2]; - p[2] = t; - p += 4; - } - } - } -} - -static int parse_png_file(png *z, int scan, int req_comp) -{ - uint8 palette[1024], pal_img_n=0; - uint8 has_trans=0, tc[3]; - uint32 ioff=0, idata_limit=0, i, pal_len=0; - int first=1,k,interlace=0, iphone=0; - stbi *s = z->s; - - z->expanded = NULL; - z->idata = NULL; - z->out = NULL; - - if (!check_png_header(s)) return 0; - - if (scan == SCAN_type) return 1; - - for (;;) { - chunk c = get_chunk_header(s); - switch (c.type) { - case PNG_TYPE('C','g','B','I'): - iphone = stbi_de_iphone_flag; - skip(s, c.length); - break; - case PNG_TYPE('I','H','D','R'): { - int depth,color,comp,filter; - if (!first) return e("multiple IHDR","Corrupt PNG"); - first = 0; - if (c.length != 13) return e("bad IHDR len","Corrupt PNG"); - s->img_x = get32(s); if (s->img_x > (1 << 24)) return e("too large","Very large image (corrupt?)"); - s->img_y = get32(s); if (s->img_y > (1 << 24)) return e("too large","Very large image (corrupt?)"); - depth = get8(s); if (depth != 8) return e("8bit only","PNG not supported: 8-bit only"); - color = get8(s); if (color > 6) return e("bad ctype","Corrupt PNG"); - if (color == 3) pal_img_n = 3; else if (color & 1) return e("bad ctype","Corrupt PNG"); - comp = get8(s); if (comp) return e("bad comp method","Corrupt PNG"); - filter= get8(s); if (filter) return e("bad filter method","Corrupt PNG"); - interlace = get8(s); if (interlace>1) return e("bad interlace method","Corrupt PNG"); - if (!s->img_x || !s->img_y) return e("0-pixel image","Corrupt PNG"); - if (!pal_img_n) { - s->img_n = (color & 2 ? 3 : 1) + (color & 4 ? 1 : 0); - if ((1 << 30) / s->img_x / s->img_n < s->img_y) return e("too large", "Image too large to decode"); - if (scan == SCAN_header) return 1; - } else { - // if paletted, then pal_n is our final components, and - // img_n is # components to decompress/filter. - s->img_n = 1; - if ((1 << 30) / s->img_x / 4 < s->img_y) return e("too large","Corrupt PNG"); - // if SCAN_header, have to scan to see if we have a tRNS - } - break; - } - - case PNG_TYPE('P','L','T','E'): { - if (first) return e("first not IHDR", "Corrupt PNG"); - if (c.length > 256*3) return e("invalid PLTE","Corrupt PNG"); - pal_len = c.length / 3; - if (pal_len * 3 != c.length) return e("invalid PLTE","Corrupt PNG"); - for (i=0; i < pal_len; ++i) { - palette[i*4+0] = get8u(s); - palette[i*4+1] = get8u(s); - palette[i*4+2] = get8u(s); - palette[i*4+3] = 255; - } - break; - } - - case PNG_TYPE('t','R','N','S'): { - if (first) return e("first not IHDR", "Corrupt PNG"); - if (z->idata) return e("tRNS after IDAT","Corrupt PNG"); - if (pal_img_n) { - if (scan == SCAN_header) { s->img_n = 4; return 1; } - if (pal_len == 0) return e("tRNS before PLTE","Corrupt PNG"); - if (c.length > pal_len) return e("bad tRNS len","Corrupt PNG"); - pal_img_n = 4; - for (i=0; i < c.length; ++i) - palette[i*4+3] = get8u(s); - } else { - if (!(s->img_n & 1)) return e("tRNS with alpha","Corrupt PNG"); - if (c.length != (uint32) s->img_n*2) return e("bad tRNS len","Corrupt PNG"); - has_trans = 1; - for (k=0; k < s->img_n; ++k) - tc[k] = (uint8) get16(s); // non 8-bit images will be larger - } - break; - } - - case PNG_TYPE('I','D','A','T'): { - if (first) return e("first not IHDR", "Corrupt PNG"); - if (pal_img_n && !pal_len) return e("no PLTE","Corrupt PNG"); - if (scan == SCAN_header) { s->img_n = pal_img_n; return 1; } - if (ioff + c.length > idata_limit) { - uint8 *p; - if (idata_limit == 0) idata_limit = c.length > 4096 ? c.length : 4096; - while (ioff + c.length > idata_limit) - idata_limit *= 2; - p = (uint8 *) realloc(z->idata, idata_limit); if (p == NULL) return e("outofmem", "Out of memory"); - z->idata = p; - } - if (!getn(s, z->idata+ioff,c.length)) return e("outofdata","Corrupt PNG"); - ioff += c.length; - break; - } - - case PNG_TYPE('I','E','N','D'): { - uint32 raw_len; - if (first) return e("first not IHDR", "Corrupt PNG"); - if (scan != SCAN_load) return 1; - if (z->idata == NULL) return e("no IDAT","Corrupt PNG"); - z->expanded = (uint8 *) stbi_zlib_decode_malloc_guesssize_headerflag((char *) z->idata, ioff, 16384, (int *) &raw_len, !iphone); - if (z->expanded == NULL) return 0; // zlib should set error - free(z->idata); z->idata = NULL; - if ((req_comp == s->img_n+1 && req_comp != 3 && !pal_img_n) || has_trans) - s->img_out_n = s->img_n+1; - else - s->img_out_n = s->img_n; - if (!create_png_image(z, z->expanded, raw_len, s->img_out_n, interlace)) return 0; - if (has_trans) - if (!compute_transparency(z, tc, s->img_out_n)) return 0; - if (iphone && s->img_out_n > 2) - stbi_de_iphone(z); - if (pal_img_n) { - // pal_img_n == 3 or 4 - s->img_n = pal_img_n; // record the actual colors we had - s->img_out_n = pal_img_n; - if (req_comp >= 3) s->img_out_n = req_comp; - if (!expand_palette(z, palette, pal_len, s->img_out_n)) - return 0; - } - free(z->expanded); z->expanded = NULL; - return 1; - } - - default: - // if critical, fail - if (first) return e("first not IHDR", "Corrupt PNG"); - if ((c.type & (1 << 29)) == 0) { - #ifndef STBI_NO_FAILURE_STRINGS - // not threadsafe - static char invalid_chunk[] = "XXXX chunk not known"; - invalid_chunk[0] = (uint8) (c.type >> 24); - invalid_chunk[1] = (uint8) (c.type >> 16); - invalid_chunk[2] = (uint8) (c.type >> 8); - invalid_chunk[3] = (uint8) (c.type >> 0); - #endif - return e(invalid_chunk, "PNG not supported: unknown chunk type"); - } - skip(s, c.length); - break; - } - // end of chunk, read and skip CRC - get32(s); - } -} - -static unsigned char *do_png(png *p, int *x, int *y, int *n, int req_comp) -{ - unsigned char *result=NULL; - if (req_comp < 0 || req_comp > 4) return epuc("bad req_comp", "Internal error"); - if (parse_png_file(p, SCAN_load, req_comp)) { - result = p->out; - p->out = NULL; - if (req_comp && req_comp != p->s->img_out_n) { - result = convert_format(result, p->s->img_out_n, req_comp, p->s->img_x, p->s->img_y); - p->s->img_out_n = req_comp; - if (result == NULL) return result; - } - *x = p->s->img_x; - *y = p->s->img_y; - if (n) *n = p->s->img_n; - } - free(p->out); p->out = NULL; - free(p->expanded); p->expanded = NULL; - free(p->idata); p->idata = NULL; - - return result; -} - -static unsigned char *stbi_png_load(stbi *s, int *x, int *y, int *comp, int req_comp) -{ - png p; - p.s = s; - return do_png(&p, x,y,comp,req_comp); -} - -static int stbi_png_test(stbi *s) -{ - int r; - r = check_png_header(s); - stbi_rewind(s); - return r; -} - -static int stbi_png_info_raw(png *p, int *x, int *y, int *comp) -{ - if (!parse_png_file(p, SCAN_header, 0)) { - stbi_rewind( p->s ); - return 0; - } - if (x) *x = p->s->img_x; - if (y) *y = p->s->img_y; - if (comp) *comp = p->s->img_n; - return 1; -} - -static int stbi_png_info(stbi *s, int *x, int *y, int *comp) -{ - png p; - p.s = s; - return stbi_png_info_raw(&p, x, y, comp); -} - -// Microsoft/Windows BMP image - -static int bmp_test(stbi *s) -{ - int sz; - if (get8(s) != 'B') return 0; - if (get8(s) != 'M') return 0; - get32le(s); // discard filesize - get16le(s); // discard reserved - get16le(s); // discard reserved - get32le(s); // discard data offset - sz = get32le(s); - if (sz == 12 || sz == 40 || sz == 56 || sz == 108) return 1; - return 0; -} - -static int stbi_bmp_test(stbi *s) -{ - int r = bmp_test(s); - stbi_rewind(s); - return r; -} - - -// returns 0..31 for the highest set bit -static int high_bit(unsigned int z) -{ - int n=0; - if (z == 0) return -1; - if (z >= 0x10000) n += 16, z >>= 16; - if (z >= 0x00100) n += 8, z >>= 8; - if (z >= 0x00010) n += 4, z >>= 4; - if (z >= 0x00004) n += 2, z >>= 2; - if (z >= 0x00002) n += 1, z >>= 1; - return n; -} - -static int bitcount(unsigned int a) -{ - a = (a & 0x55555555) + ((a >> 1) & 0x55555555); // max 2 - a = (a & 0x33333333) + ((a >> 2) & 0x33333333); // max 4 - a = (a + (a >> 4)) & 0x0f0f0f0f; // max 8 per 4, now 8 bits - a = (a + (a >> 8)); // max 16 per 8 bits - a = (a + (a >> 16)); // max 32 per 8 bits - return a & 0xff; -} - -static int shiftsigned(int v, int shift, int bits) -{ - int result; - int z=0; - - if (shift < 0) v <<= -shift; - else v >>= shift; - result = v; - - z = bits; - while (z < 8) { - result += v >> z; - z += bits; - } - return result; -} - -static stbi_uc *bmp_load(stbi *s, int *x, int *y, int *comp, int req_comp) -{ - uint8 *out; - unsigned int mr=0,mg=0,mb=0,ma=0; //fake_a=0; - stbi_uc pal[256][4]; - int psize=0,i,j,compress=0,width; - int bpp, flip_vertically, pad, target, offset, hsz; - if (get8(s) != 'B' || get8(s) != 'M') return epuc("not BMP", "Corrupt BMP"); - get32le(s); // discard filesize - get16le(s); // discard reserved - get16le(s); // discard reserved - offset = get32le(s); - hsz = get32le(s); - if (hsz != 12 && hsz != 40 && hsz != 56 && hsz != 108) return epuc("unknown BMP", "BMP type not supported: unknown"); - if (hsz == 12) { - s->img_x = get16le(s); - s->img_y = get16le(s); - } else { - s->img_x = get32le(s); - s->img_y = get32le(s); - } - if (get16le(s) != 1) return epuc("bad BMP", "bad BMP"); - bpp = get16le(s); - if (bpp == 1) return epuc("monochrome", "BMP type not supported: 1-bit"); - flip_vertically = ((int) s->img_y) > 0; - s->img_y = abs((int) s->img_y); - if (hsz == 12) { - if (bpp < 24) - psize = (offset - 14 - 24) / 3; - } else { - compress = get32le(s); - if (compress == 1 || compress == 2) return epuc("BMP RLE", "BMP type not supported: RLE"); - get32le(s); // discard sizeof - get32le(s); // discard hres - get32le(s); // discard vres - get32le(s); // discard colorsused - get32le(s); // discard max important - if (hsz == 40 || hsz == 56) { - if (hsz == 56) { - get32le(s); - get32le(s); - get32le(s); - get32le(s); - } - if (bpp == 16 || bpp == 32) { - mr = mg = mb = 0; - if (compress == 0) { - if (bpp == 32) { - mr = 0xffu << 16; - mg = 0xffu << 8; - mb = 0xffu << 0; - ma = 0xffu << 24; - //fake_a = 1; // @TODO: check for cases like alpha value is all 0 and switch it to 255 - } else { - mr = 31u << 10; - mg = 31u << 5; - mb = 31u << 0; - } - } else if (compress == 3) { - mr = get32le(s); - mg = get32le(s); - mb = get32le(s); - // not documented, but generated by photoshop and handled by mspaint - if (mr == mg && mg == mb) { - // ?!?!? - return epuc("bad BMP", "bad BMP"); - } - } else - return epuc("bad BMP", "bad BMP"); - } - } else { - assert(hsz == 108); - mr = get32le(s); - mg = get32le(s); - mb = get32le(s); - ma = get32le(s); - get32le(s); // discard color space - for (i=0; i < 12; ++i) - get32le(s); // discard color space parameters - } - if (bpp < 16) - psize = (offset - 14 - hsz) >> 2; - } - s->img_n = ma ? 4 : 3; - if (req_comp && req_comp >= 3) // we can directly decode 3 or 4 - target = req_comp; - else - target = s->img_n; // if they want monochrome, we'll post-convert - out = (stbi_uc *) malloc(target * s->img_x * s->img_y); - if (!out) return epuc("outofmem", "Out of memory"); - if (bpp < 16) { - int z=0; - if (psize == 0 || psize > 256) { free(out); return epuc("invalid", "Corrupt BMP"); } - for (i=0; i < psize; ++i) { - pal[i][2] = get8u(s); - pal[i][1] = get8u(s); - pal[i][0] = get8u(s); - if (hsz != 12) get8(s); - pal[i][3] = 255; - } - skip(s, offset - 14 - hsz - psize * (hsz == 12 ? 3 : 4)); - if (bpp == 4) width = (s->img_x + 1) >> 1; - else if (bpp == 8) width = s->img_x; - else { free(out); return epuc("bad bpp", "Corrupt BMP"); } - pad = (-width)&3; - for (j=0; j < (int) s->img_y; ++j) { - for (i=0; i < (int) s->img_x; i += 2) { - int v=get8(s),v2=0; - if (bpp == 4) { - v2 = v & 15; - v >>= 4; - } - out[z++] = pal[v][0]; - out[z++] = pal[v][1]; - out[z++] = pal[v][2]; - if (target == 4) out[z++] = 255; - if (i+1 == (int) s->img_x) break; - v = (bpp == 8) ? get8(s) : v2; - out[z++] = pal[v][0]; - out[z++] = pal[v][1]; - out[z++] = pal[v][2]; - if (target == 4) out[z++] = 255; - } - skip(s, pad); - } - } else { - int rshift=0,gshift=0,bshift=0,ashift=0,rcount=0,gcount=0,bcount=0,acount=0; - int z = 0; - int easy=0; - skip(s, offset - 14 - hsz); - if (bpp == 24) width = 3 * s->img_x; - else if (bpp == 16) width = 2*s->img_x; - else /* bpp = 32 and pad = 0 */ width=0; - pad = (-width) & 3; - if (bpp == 24) { - easy = 1; - } else if (bpp == 32) { - if (mb == 0xff && mg == 0xff00 && mr == 0x00ff0000 && ma == 0xff000000) - easy = 2; - } - if (!easy) { - if (!mr || !mg || !mb) { free(out); return epuc("bad masks", "Corrupt BMP"); } - // right shift amt to put high bit in position #7 - rshift = high_bit(mr)-7; rcount = bitcount(mr); - gshift = high_bit(mg)-7; gcount = bitcount(mr); - bshift = high_bit(mb)-7; bcount = bitcount(mr); - ashift = high_bit(ma)-7; acount = bitcount(mr); - } - for (j=0; j < (int) s->img_y; ++j) { - if (easy) { - for (i=0; i < (int) s->img_x; ++i) { - int a; - out[z+2] = get8u(s); - out[z+1] = get8u(s); - out[z+0] = get8u(s); - z += 3; - a = (easy == 2 ? get8(s) : 255); - if (target == 4) out[z++] = (uint8) a; - } - } else { - for (i=0; i < (int) s->img_x; ++i) { - uint32 v = (bpp == 16 ? get16le(s) : get32le(s)); - int a; - out[z++] = (uint8) shiftsigned(v & mr, rshift, rcount); - out[z++] = (uint8) shiftsigned(v & mg, gshift, gcount); - out[z++] = (uint8) shiftsigned(v & mb, bshift, bcount); - a = (ma ? shiftsigned(v & ma, ashift, acount) : 255); - if (target == 4) out[z++] = (uint8) a; - } - } - skip(s, pad); - } - } - if (flip_vertically) { - stbi_uc t; - for (j=0; j < (int) s->img_y>>1; ++j) { - stbi_uc *p1 = out + j *s->img_x*target; - stbi_uc *p2 = out + (s->img_y-1-j)*s->img_x*target; - for (i=0; i < (int) s->img_x*target; ++i) { - t = p1[i], p1[i] = p2[i], p2[i] = t; - } - } - } - - if (req_comp && req_comp != target) { - out = convert_format(out, target, req_comp, s->img_x, s->img_y); - if (out == NULL) return out; // convert_format frees input on failure - } - - *x = s->img_x; - *y = s->img_y; - if (comp) *comp = s->img_n; - return out; -} - -static stbi_uc *stbi_bmp_load(stbi *s,int *x, int *y, int *comp, int req_comp) -{ - return bmp_load(s, x,y,comp,req_comp); -} - - -// Targa Truevision - TGA -// by Jonathan Dummer - -static int tga_info(stbi *s, int *x, int *y, int *comp) -{ - int tga_w, tga_h, tga_comp; - int sz; - get8u(s); // discard Offset - sz = get8u(s); // color type - if( sz > 1 ) { - stbi_rewind(s); - return 0; // only RGB or indexed allowed - } - sz = get8u(s); // image type - // only RGB or grey allowed, +/- RLE - if ((sz != 1) && (sz != 2) && (sz != 3) && (sz != 9) && (sz != 10) && (sz != 11)) return 0; - skip(s,9); - tga_w = get16le(s); - if( tga_w < 1 ) { - stbi_rewind(s); - return 0; // test width - } - tga_h = get16le(s); - if( tga_h < 1 ) { - stbi_rewind(s); - return 0; // test height - } - sz = get8(s); // bits per pixel - // only RGB or RGBA or grey allowed - if ((sz != 8) && (sz != 16) && (sz != 24) && (sz != 32)) { - stbi_rewind(s); - return 0; - } - tga_comp = sz; - if (x) *x = tga_w; - if (y) *y = tga_h; - if (comp) *comp = tga_comp / 8; - return 1; // seems to have passed everything -} - -int stbi_tga_info(stbi *s, int *x, int *y, int *comp) -{ - return tga_info(s, x, y, comp); -} - -static int tga_test(stbi *s) -{ - int sz; - get8u(s); // discard Offset - sz = get8u(s); // color type - if ( sz > 1 ) return 0; // only RGB or indexed allowed - sz = get8u(s); // image type - if ( (sz != 1) && (sz != 2) && (sz != 3) && (sz != 9) && (sz != 10) && (sz != 11) ) return 0; // only RGB or grey allowed, +/- RLE - get16(s); // discard palette start - get16(s); // discard palette length - get8(s); // discard bits per palette color entry - get16(s); // discard x origin - get16(s); // discard y origin - if ( get16(s) < 1 ) return 0; // test width - if ( get16(s) < 1 ) return 0; // test height - sz = get8(s); // bits per pixel - if ( (sz != 8) && (sz != 16) && (sz != 24) && (sz != 32) ) return 0; // only RGB or RGBA or grey allowed - return 1; // seems to have passed everything -} - -static int stbi_tga_test(stbi *s) -{ - int res = tga_test(s); - stbi_rewind(s); - return res; -} - -static stbi_uc *tga_load(stbi *s, int *x, int *y, int *comp, int req_comp) -{ - // read in the TGA header stuff - int tga_offset = get8u(s); - int tga_indexed = get8u(s); - int tga_image_type = get8u(s); - int tga_is_RLE = 0; - int tga_palette_start = get16le(s); - int tga_palette_len = get16le(s); - int tga_palette_bits = get8u(s); - int tga_x_origin = get16le(s); - int tga_y_origin = get16le(s); - int tga_width = get16le(s); - int tga_height = get16le(s); - int tga_bits_per_pixel = get8u(s); - int tga_inverted = get8u(s); - // image data - unsigned char *tga_data; - unsigned char *tga_palette = NULL; - int i, j; - unsigned char raw_data[4]; - unsigned char trans_data[4]; - int RLE_count = 0; - int RLE_repeating = 0; - int read_next_pixel = 1; - - // do a tiny bit of precessing - if ( tga_image_type >= 8 ) - { - tga_image_type -= 8; - tga_is_RLE = 1; - } - /* int tga_alpha_bits = tga_inverted & 15; */ - tga_inverted = 1 - ((tga_inverted >> 5) & 1); - - // error check - if ( //(tga_indexed) || - (tga_width < 1) || (tga_height < 1) || - (tga_image_type < 1) || (tga_image_type > 3) || - ((tga_bits_per_pixel != 8) && (tga_bits_per_pixel != 16) && - (tga_bits_per_pixel != 24) && (tga_bits_per_pixel != 32)) - ) - { - return NULL; // we don't report this as a bad TGA because we don't even know if it's TGA - } - - // If I'm paletted, then I'll use the number of bits from the palette - if ( tga_indexed ) - { - tga_bits_per_pixel = tga_palette_bits; - } - - // tga info - *x = tga_width; - *y = tga_height; - if ( (req_comp < 1) || (req_comp > 4) ) - { - // just use whatever the file was - req_comp = tga_bits_per_pixel / 8; - *comp = req_comp; - } else - { - // force a new number of components - *comp = tga_bits_per_pixel/8; - } - tga_data = (unsigned char*)malloc( tga_width * tga_height * req_comp ); - if (!tga_data) return epuc("outofmem", "Out of memory"); - - // skip to the data's starting position (offset usually = 0) - skip(s, tga_offset ); - // do I need to load a palette? - if ( tga_indexed ) - { - // any data to skip? (offset usually = 0) - skip(s, tga_palette_start ); - // load the palette - tga_palette = (unsigned char*)malloc( tga_palette_len * tga_palette_bits / 8 ); - if (!tga_palette) return epuc("outofmem", "Out of memory"); - if (!getn(s, tga_palette, tga_palette_len * tga_palette_bits / 8 )) { - free(tga_data); - free(tga_palette); - return epuc("bad palette", "Corrupt TGA"); - } - } - // load the data - trans_data[0] = trans_data[1] = trans_data[2] = trans_data[3] = 0; - for (i=0; i < tga_width * tga_height; ++i) - { - // if I'm in RLE mode, do I need to get a RLE chunk? - if ( tga_is_RLE ) - { - if ( RLE_count == 0 ) - { - // yep, get the next byte as a RLE command - int RLE_cmd = get8u(s); - RLE_count = 1 + (RLE_cmd & 127); - RLE_repeating = RLE_cmd >> 7; - read_next_pixel = 1; - } else if ( !RLE_repeating ) - { - read_next_pixel = 1; - } - } else - { - read_next_pixel = 1; - } - // OK, if I need to read a pixel, do it now - if ( read_next_pixel ) - { - // load however much data we did have - if ( tga_indexed ) - { - // read in 1 byte, then perform the lookup - int pal_idx = get8u(s); - if ( pal_idx >= tga_palette_len ) - { - // invalid index - pal_idx = 0; - } - pal_idx *= tga_bits_per_pixel / 8; - for (j = 0; j*8 < tga_bits_per_pixel; ++j) - { - raw_data[j] = tga_palette[pal_idx+j]; - } - } else - { - // read in the data raw - for (j = 0; j*8 < tga_bits_per_pixel; ++j) - { - raw_data[j] = get8u(s); - } - } - // convert raw to the intermediate format - switch (tga_bits_per_pixel) - { - case 8: - // Luminous => RGBA - trans_data[0] = raw_data[0]; - trans_data[1] = raw_data[0]; - trans_data[2] = raw_data[0]; - trans_data[3] = 255; - break; - case 16: - // Luminous,Alpha => RGBA - trans_data[0] = raw_data[0]; - trans_data[1] = raw_data[0]; - trans_data[2] = raw_data[0]; - trans_data[3] = raw_data[1]; - break; - case 24: - // BGR => RGBA - trans_data[0] = raw_data[2]; - trans_data[1] = raw_data[1]; - trans_data[2] = raw_data[0]; - trans_data[3] = 255; - break; - case 32: - // BGRA => RGBA - trans_data[0] = raw_data[2]; - trans_data[1] = raw_data[1]; - trans_data[2] = raw_data[0]; - trans_data[3] = raw_data[3]; - break; - } - // clear the reading flag for the next pixel - read_next_pixel = 0; - } // end of reading a pixel - // convert to final format - switch (req_comp) - { - case 1: - // RGBA => Luminance - tga_data[i*req_comp+0] = compute_y(trans_data[0],trans_data[1],trans_data[2]); - break; - case 2: - // RGBA => Luminance,Alpha - tga_data[i*req_comp+0] = compute_y(trans_data[0],trans_data[1],trans_data[2]); - tga_data[i*req_comp+1] = trans_data[3]; - break; - case 3: - // RGBA => RGB - tga_data[i*req_comp+0] = trans_data[0]; - tga_data[i*req_comp+1] = trans_data[1]; - tga_data[i*req_comp+2] = trans_data[2]; - break; - case 4: - // RGBA => RGBA - tga_data[i*req_comp+0] = trans_data[0]; - tga_data[i*req_comp+1] = trans_data[1]; - tga_data[i*req_comp+2] = trans_data[2]; - tga_data[i*req_comp+3] = trans_data[3]; - break; - } - // in case we're in RLE mode, keep counting down - --RLE_count; - } - // do I need to invert the image? - if ( tga_inverted ) - { - for (j = 0; j*2 < tga_height; ++j) - { - int index1 = j * tga_width * req_comp; - int index2 = (tga_height - 1 - j) * tga_width * req_comp; - for (i = tga_width * req_comp; i > 0; --i) - { - unsigned char temp = tga_data[index1]; - tga_data[index1] = tga_data[index2]; - tga_data[index2] = temp; - ++index1; - ++index2; - } - } - } - // clear my palette, if I had one - if ( tga_palette != NULL ) - { - free( tga_palette ); - } - // the things I do to get rid of an error message, and yet keep - // Microsoft's C compilers happy... [8^( - tga_palette_start = tga_palette_len = tga_palette_bits = - tga_x_origin = tga_y_origin = 0; - // OK, done - return tga_data; -} - -static stbi_uc *stbi_tga_load(stbi *s, int *x, int *y, int *comp, int req_comp) -{ - return tga_load(s,x,y,comp,req_comp); -} - - -// ************************************************************************************************* -// Photoshop PSD loader -- PD by Thatcher Ulrich, integration by Nicolas Schulz, tweaked by STB - -static int psd_test(stbi *s) -{ - if (get32(s) != 0x38425053) return 0; // "8BPS" - else return 1; -} - -static int stbi_psd_test(stbi *s) -{ - int r = psd_test(s); - stbi_rewind(s); - return r; -} - -static stbi_uc *psd_load(stbi *s, int *x, int *y, int *comp, int req_comp) -{ - int pixelCount; - int channelCount, compression; - int channel, i, count, len; - int w,h; - uint8 *out; - - // Check identifier - if (get32(s) != 0x38425053) // "8BPS" - return epuc("not PSD", "Corrupt PSD image"); - - // Check file type version. - if (get16(s) != 1) - return epuc("wrong version", "Unsupported version of PSD image"); - - // Skip 6 reserved bytes. - skip(s, 6 ); - - // Read the number of channels (R, G, B, A, etc). - channelCount = get16(s); - if (channelCount < 0 || channelCount > 16) - return epuc("wrong channel count", "Unsupported number of channels in PSD image"); - - // Read the rows and columns of the image. - h = get32(s); - w = get32(s); - - // Make sure the depth is 8 bits. - if (get16(s) != 8) - return epuc("unsupported bit depth", "PSD bit depth is not 8 bit"); - - // Make sure the color mode is RGB. - // Valid options are: - // 0: Bitmap - // 1: Grayscale - // 2: Indexed color - // 3: RGB color - // 4: CMYK color - // 7: Multichannel - // 8: Duotone - // 9: Lab color - if (get16(s) != 3) - return epuc("wrong color format", "PSD is not in RGB color format"); - - // Skip the Mode Data. (It's the palette for indexed color; other info for other modes.) - skip(s,get32(s) ); - - // Skip the image resources. (resolution, pen tool paths, etc) - skip(s, get32(s) ); - - // Skip the reserved data. - skip(s, get32(s) ); - - // Find out if the data is compressed. - // Known values: - // 0: no compression - // 1: RLE compressed - compression = get16(s); - if (compression > 1) - return epuc("bad compression", "PSD has an unknown compression format"); - - // Create the destination image. - out = (stbi_uc *) malloc(4 * w*h); - if (!out) return epuc("outofmem", "Out of memory"); - pixelCount = w*h; - - // Initialize the data to zero. - //memset( out, 0, pixelCount * 4 ); - - // Finally, the image data. - if (compression) { - // RLE as used by .PSD and .TIFF - // Loop until you get the number of unpacked bytes you are expecting: - // Read the next source byte into n. - // If n is between 0 and 127 inclusive, copy the next n+1 bytes literally. - // Else if n is between -127 and -1 inclusive, copy the next byte -n+1 times. - // Else if n is 128, noop. - // Endloop - - // The RLE-compressed data is preceeded by a 2-byte data count for each row in the data, - // which we're going to just skip. - skip(s, h * channelCount * 2 ); - - // Read the RLE data by channel. - for (channel = 0; channel < 4; channel++) { - uint8 *p; - - p = out+channel; - if (channel >= channelCount) { - // Fill this channel with default data. - for (i = 0; i < pixelCount; i++) *p = (channel == 3 ? 255 : 0), p += 4; - } else { - // Read the RLE data. - count = 0; - while (count < pixelCount) { - len = get8(s); - if (len == 128) { - // No-op. - } else if (len < 128) { - // Copy next len+1 bytes literally. - len++; - count += len; - while (len) { - *p = get8u(s); - p += 4; - len--; - } - } else if (len > 128) { - uint8 val; - // Next -len+1 bytes in the dest are replicated from next source byte. - // (Interpret len as a negative 8-bit int.) - len ^= 0x0FF; - len += 2; - val = get8u(s); - count += len; - while (len) { - *p = val; - p += 4; - len--; - } - } - } - } - } - - } else { - // We're at the raw image data. It's each channel in order (Red, Green, Blue, Alpha, ...) - // where each channel consists of an 8-bit value for each pixel in the image. - - // Read the data by channel. - for (channel = 0; channel < 4; channel++) { - uint8 *p; - - p = out + channel; - if (channel > channelCount) { - // Fill this channel with default data. - for (i = 0; i < pixelCount; i++) *p = channel == 3 ? 255 : 0, p += 4; - } else { - // Read the data. - for (i = 0; i < pixelCount; i++) - *p = get8u(s), p += 4; - } - } - } - - if (req_comp && req_comp != 4) { - out = convert_format(out, 4, req_comp, w, h); - if (out == NULL) return out; // convert_format frees input on failure - } - - if (comp) *comp = channelCount; - *y = h; - *x = w; - - return out; -} - -static stbi_uc *stbi_psd_load(stbi *s, int *x, int *y, int *comp, int req_comp) -{ - return psd_load(s,x,y,comp,req_comp); -} - -// ************************************************************************************************* -// Softimage PIC loader -// by Tom Seddon -// -// See http://softimage.wiki.softimage.com/index.php/INFO:_PIC_file_format -// See http://ozviz.wasp.uwa.edu.au/~pbourke/dataformats/softimagepic/ - -static int pic_is4(stbi *s,const char *str) -{ - int i; - for (i=0; i<4; ++i) - if (get8(s) != (stbi_uc)str[i]) - return 0; - - return 1; -} - -static int pic_test(stbi *s) -{ - int i; - - if (!pic_is4(s,"\x53\x80\xF6\x34")) - return 0; - - for(i=0;i<84;++i) - get8(s); - - if (!pic_is4(s,"PICT")) - return 0; - - return 1; -} - -typedef struct -{ - stbi_uc size,type,channel; -} pic_packet_t; - -static stbi_uc *pic_readval(stbi *s, int channel, stbi_uc *dest) -{ - int mask=0x80, i; - - for (i=0; i<4; ++i, mask>>=1) { - if (channel & mask) { - if (at_eof(s)) return epuc("bad file","PIC file too short"); - dest[i]=get8u(s); - } - } - - return dest; -} - -static void pic_copyval(int channel,stbi_uc *dest,const stbi_uc *src) -{ - int mask=0x80,i; - - for (i=0;i<4; ++i, mask>>=1) - if (channel&mask) - dest[i]=src[i]; -} - -static stbi_uc *pic_load2(stbi *s,int width,int height,int *comp, stbi_uc *result) -{ - int act_comp=0,num_packets=0,y,chained; - pic_packet_t packets[10]; - - // this will (should...) cater for even some bizarre stuff like having data - // for the same channel in multiple packets. - do { - pic_packet_t *packet; - - if (num_packets==sizeof(packets)/sizeof(packets[0])) - return epuc("bad format","too many packets"); - - packet = &packets[num_packets++]; - - chained = get8(s); - packet->size = get8u(s); - packet->type = get8u(s); - packet->channel = get8u(s); - - act_comp |= packet->channel; - - if (at_eof(s)) return epuc("bad file","file too short (reading packets)"); - if (packet->size != 8) return epuc("bad format","packet isn't 8bpp"); - } while (chained); - - *comp = (act_comp & 0x10 ? 4 : 3); // has alpha channel? - - for(y=0; y<height; ++y) { - int packet_idx; - - for(packet_idx=0; packet_idx < num_packets; ++packet_idx) { - pic_packet_t *packet = &packets[packet_idx]; - stbi_uc *dest = result+y*width*4; - - switch (packet->type) { - default: - return epuc("bad format","packet has bad compression type"); - - case 0: {//uncompressed - int x; - - for(x=0;x<width;++x, dest+=4) - if (!pic_readval(s,packet->channel,dest)) - return 0; - break; - } - - case 1://Pure RLE - { - int left=width, i; - - while (left>0) { - stbi_uc count,value[4]; - - count=get8u(s); - if (at_eof(s)) return epuc("bad file","file too short (pure read count)"); - - if (count > left) - count = (uint8) left; - - if (!pic_readval(s,packet->channel,value)) return 0; - - for(i=0; i<count; ++i,dest+=4) - pic_copyval(packet->channel,dest,value); - left -= count; - } - } - break; - - case 2: {//Mixed RLE - int left=width; - while (left>0) { - int count = get8(s), i; - if (at_eof(s)) return epuc("bad file","file too short (mixed read count)"); - - if (count >= 128) { // Repeated - stbi_uc value[4]; - int i; - - if (count==128) - count = get16(s); - else - count -= 127; - if (count > left) - return epuc("bad file","scanline overrun"); - - if (!pic_readval(s,packet->channel,value)) - return 0; - - for(i=0;i<count;++i, dest += 4) - pic_copyval(packet->channel,dest,value); - } else { // Raw - ++count; - if (count>left) return epuc("bad file","scanline overrun"); - - for(i=0;i<count;++i, dest+=4) - if (!pic_readval(s,packet->channel,dest)) - return 0; - } - left-=count; - } - break; - } - } - } - } - - return result; -} - -static stbi_uc *pic_load(stbi *s,int *px,int *py,int *comp,int req_comp) -{ - stbi_uc *result; - int i, x,y; - - for (i=0; i<92; ++i) - get8(s); - - x = get16(s); - y = get16(s); - if (at_eof(s)) return epuc("bad file","file too short (pic header)"); - if ((1 << 28) / x < y) return epuc("too large", "Image too large to decode"); - - get32(s); //skip `ratio' - get16(s); //skip `fields' - get16(s); //skip `pad' - - // intermediate buffer is RGBA - result = (stbi_uc *) malloc(x*y*4); - memset(result, 0xff, x*y*4); - - if (!pic_load2(s,x,y,comp, result)) { - free(result); - result=0; - } - *px = x; - *py = y; - if (req_comp == 0) req_comp = *comp; - result=convert_format(result,4,req_comp,x,y); - - return result; -} - -static int stbi_pic_test(stbi *s) -{ - int r = pic_test(s); - stbi_rewind(s); - return r; -} - -static stbi_uc *stbi_pic_load(stbi *s, int *x, int *y, int *comp, int req_comp) -{ - return pic_load(s,x,y,comp,req_comp); -} - -// ************************************************************************************************* -// GIF loader -- public domain by Jean-Marc Lienher -- simplified/shrunk by stb -typedef struct stbi_gif_lzw_struct { - int16 prefix; - uint8 first; - uint8 suffix; -} stbi_gif_lzw; - -typedef struct stbi_gif_struct -{ - int w,h; - stbi_uc *out; // output buffer (always 4 components) - int flags, bgindex, ratio, transparent, eflags; - uint8 pal[256][4]; - uint8 lpal[256][4]; - stbi_gif_lzw codes[4096]; - uint8 *color_table; - int parse, step; - int lflags; - int start_x, start_y; - int max_x, max_y; - int cur_x, cur_y; - int line_size; -} stbi_gif; - -static int gif_test(stbi *s) -{ - int sz; - if (get8(s) != 'G' || get8(s) != 'I' || get8(s) != 'F' || get8(s) != '8') return 0; - sz = get8(s); - if (sz != '9' && sz != '7') return 0; - if (get8(s) != 'a') return 0; - return 1; -} - -static int stbi_gif_test(stbi *s) -{ - int r = gif_test(s); - stbi_rewind(s); - return r; -} - -static void stbi_gif_parse_colortable(stbi *s, uint8 pal[256][4], int num_entries, int transp) -{ - int i; - for (i=0; i < num_entries; ++i) { - pal[i][2] = get8u(s); - pal[i][1] = get8u(s); - pal[i][0] = get8u(s); - pal[i][3] = transp ? 0 : 255; - } -} - -static int stbi_gif_header(stbi *s, stbi_gif *g, int *comp, int is_info) -{ - uint8 version; - if (get8(s) != 'G' || get8(s) != 'I' || get8(s) != 'F' || get8(s) != '8') - return e("not GIF", "Corrupt GIF"); - - version = get8u(s); - if (version != '7' && version != '9') return e("not GIF", "Corrupt GIF"); - if (get8(s) != 'a') return e("not GIF", "Corrupt GIF"); - - failure_reason = ""; - g->w = get16le(s); - g->h = get16le(s); - g->flags = get8(s); - g->bgindex = get8(s); - g->ratio = get8(s); - g->transparent = -1; - - if (comp != 0) *comp = 4; // can't actually tell whether it's 3 or 4 until we parse the comments - - if (is_info) return 1; - - if (g->flags & 0x80) - stbi_gif_parse_colortable(s,g->pal, 2 << (g->flags & 7), -1); - - return 1; -} - -static int stbi_gif_info_raw(stbi *s, int *x, int *y, int *comp) -{ - stbi_gif g; - if (!stbi_gif_header(s, &g, comp, 1)) { - stbi_rewind( s ); - return 0; - } - if (x) *x = g.w; - if (y) *y = g.h; - return 1; -} - -static void stbi_out_gif_code(stbi_gif *g, uint16 code) -{ - uint8 *p, *c; - - // recurse to decode the prefixes, since the linked-list is backwards, - // and working backwards through an interleaved image would be nasty - if (g->codes[code].prefix >= 0) - stbi_out_gif_code(g, g->codes[code].prefix); - - if (g->cur_y >= g->max_y) return; - - p = &g->out[g->cur_x + g->cur_y]; - c = &g->color_table[g->codes[code].suffix * 4]; - - if (c[3] >= 128) { - p[0] = c[2]; - p[1] = c[1]; - p[2] = c[0]; - p[3] = c[3]; - } - g->cur_x += 4; - - if (g->cur_x >= g->max_x) { - g->cur_x = g->start_x; - g->cur_y += g->step; - - while (g->cur_y >= g->max_y && g->parse > 0) { - g->step = (1 << g->parse) * g->line_size; - g->cur_y = g->start_y + (g->step >> 1); - --g->parse; - } - } -} - -static uint8 *stbi_process_gif_raster(stbi *s, stbi_gif *g) -{ - uint8 lzw_cs; - int32 len, code; - uint32 first; - int32 codesize, codemask, avail, oldcode, bits, valid_bits, clear; - stbi_gif_lzw *p; - - lzw_cs = get8u(s); - clear = 1 << lzw_cs; - first = 1; - codesize = lzw_cs + 1; - codemask = (1 << codesize) - 1; - bits = 0; - valid_bits = 0; - for (code = 0; code < clear; code++) { - g->codes[code].prefix = -1; - g->codes[code].first = (uint8) code; - g->codes[code].suffix = (uint8) code; - } - - // support no starting clear code - avail = clear+2; - oldcode = -1; - - len = 0; - for(;;) { - if (valid_bits < codesize) { - if (len == 0) { - len = get8(s); // start new block - if (len == 0) - return g->out; - } - --len; - bits |= (int32) get8(s) << valid_bits; - valid_bits += 8; - } else { - int32 code = bits & codemask; - bits >>= codesize; - valid_bits -= codesize; - // @OPTIMIZE: is there some way we can accelerate the non-clear path? - if (code == clear) { // clear code - codesize = lzw_cs + 1; - codemask = (1 << codesize) - 1; - avail = clear + 2; - oldcode = -1; - first = 0; - } else if (code == clear + 1) { // end of stream code - skip(s, len); - while ((len = get8(s)) > 0) - skip(s,len); - return g->out; - } else if (code <= avail) { - if (first) return epuc("no clear code", "Corrupt GIF"); - - if (oldcode >= 0) { - p = &g->codes[avail++]; - if (avail > 4096) return epuc("too many codes", "Corrupt GIF"); - p->prefix = (int16) oldcode; - p->first = g->codes[oldcode].first; - p->suffix = (code == avail) ? p->first : g->codes[code].first; - } else if (code == avail) - return epuc("illegal code in raster", "Corrupt GIF"); - - stbi_out_gif_code(g, (uint16) code); - - if ((avail & codemask) == 0 && avail <= 0x0FFF) { - codesize++; - codemask = (1 << codesize) - 1; - } - - oldcode = code; - } else { - return epuc("illegal code in raster", "Corrupt GIF"); - } - } - } -} - -static void stbi_fill_gif_background(stbi_gif *g) -{ - int i; - uint8 *c = g->pal[g->bgindex]; - // @OPTIMIZE: write a dword at a time - for (i = 0; i < g->w * g->h * 4; i += 4) { - uint8 *p = &g->out[i]; - p[0] = c[2]; - p[1] = c[1]; - p[2] = c[0]; - p[3] = c[3]; - } -} - -// this function is designed to support animated gifs, although stb_image doesn't support it -static uint8 *stbi_gif_load_next(stbi *s, stbi_gif *g, int *comp, int req_comp) -{ - int i; - uint8 *old_out = 0; - - if (g->out == 0) { - if (!stbi_gif_header(s, g, comp,0)) return 0; // failure_reason set by stbi_gif_header - g->out = (uint8 *) malloc(4 * g->w * g->h); - if (g->out == 0) return epuc("outofmem", "Out of memory"); - stbi_fill_gif_background(g); - } else { - // animated-gif-only path - if (((g->eflags & 0x1C) >> 2) == 3) { - old_out = g->out; - g->out = (uint8 *) malloc(4 * g->w * g->h); - if (g->out == 0) return epuc("outofmem", "Out of memory"); - memcpy(g->out, old_out, g->w*g->h*4); - } - } - - for (;;) { - switch (get8(s)) { - case 0x2C: /* Image Descriptor */ - { - int32 x, y, w, h; - uint8 *o; - - x = get16le(s); - y = get16le(s); - w = get16le(s); - h = get16le(s); - if (((x + w) > (g->w)) || ((y + h) > (g->h))) - return epuc("bad Image Descriptor", "Corrupt GIF"); - - g->line_size = g->w * 4; - g->start_x = x * 4; - g->start_y = y * g->line_size; - g->max_x = g->start_x + w * 4; - g->max_y = g->start_y + h * g->line_size; - g->cur_x = g->start_x; - g->cur_y = g->start_y; - - g->lflags = get8(s); - - if (g->lflags & 0x40) { - g->step = 8 * g->line_size; // first interlaced spacing - g->parse = 3; - } else { - g->step = g->line_size; - g->parse = 0; - } - - if (g->lflags & 0x80) { - stbi_gif_parse_colortable(s,g->lpal, 2 << (g->lflags & 7), g->eflags & 0x01 ? g->transparent : -1); - g->color_table = (uint8 *) g->lpal; - } else if (g->flags & 0x80) { - for (i=0; i < 256; ++i) // @OPTIMIZE: reset only the previous transparent - g->pal[i][3] = 255; - if (g->transparent >= 0 && (g->eflags & 0x01)) - g->pal[g->transparent][3] = 0; - g->color_table = (uint8 *) g->pal; - } else - return epuc("missing color table", "Corrupt GIF"); - - o = stbi_process_gif_raster(s, g); - if (o == NULL) return NULL; - - if (req_comp && req_comp != 4) - o = convert_format(o, 4, req_comp, g->w, g->h); - return o; - } - - case 0x21: // Comment Extension. - { - int len; - if (get8(s) == 0xF9) { // Graphic Control Extension. - len = get8(s); - if (len == 4) { - g->eflags = get8(s); - get16le(s); // delay - g->transparent = get8(s); - } else { - skip(s, len); - break; - } - } - while ((len = get8(s)) != 0) - skip(s, len); - break; - } - - case 0x3B: // gif stream termination code - return (uint8 *) 1; - - default: - return epuc("unknown code", "Corrupt GIF"); - } - } -} - -static stbi_uc *stbi_gif_load(stbi *s, int *x, int *y, int *comp, int req_comp) -{ - uint8 *u = 0; - stbi_gif g={0}; - - u = stbi_gif_load_next(s, &g, comp, req_comp); - if (u == (void *) 1) u = 0; // end of animated gif marker - if (u) { - *x = g.w; - *y = g.h; - } - - return u; -} - -static int stbi_gif_info(stbi *s, int *x, int *y, int *comp) -{ - return stbi_gif_info_raw(s,x,y,comp); -} - - -// ************************************************************************************************* -// Radiance RGBE HDR loader -// originally by Nicolas Schulz -#ifndef STBI_NO_HDR -static int hdr_test(stbi *s) -{ - const char *signature = "#?RADIANCE\n"; - int i; - for (i=0; signature[i]; ++i) - if (get8(s) != signature[i]) - return 0; - return 1; -} - -static int stbi_hdr_test(stbi* s) -{ - int r = hdr_test(s); - stbi_rewind(s); - return r; -} - -#define HDR_BUFLEN 1024 -static char *hdr_gettoken(stbi *z, char *buffer) -{ - int len=0; - char c = '\0'; - - c = (char) get8(z); - - while (!at_eof(z) && c != '\n') { - buffer[len++] = c; - if (len == HDR_BUFLEN-1) { - // flush to end of line - while (!at_eof(z) && get8(z) != '\n') - ; - break; - } - c = (char) get8(z); - } - - buffer[len] = 0; - return buffer; -} - -static void hdr_convert(float *output, stbi_uc *input, int req_comp) -{ - if ( input[3] != 0 ) { - float f1; - // Exponent - f1 = (float) ldexp(1.0f, input[3] - (int)(128 + 8)); - if (req_comp <= 2) - output[0] = (input[0] + input[1] + input[2]) * f1 / 3; - else { - output[0] = input[0] * f1; - output[1] = input[1] * f1; - output[2] = input[2] * f1; - } - if (req_comp == 2) output[1] = 1; - if (req_comp == 4) output[3] = 1; - } else { - switch (req_comp) { - case 4: output[3] = 1; /* fallthrough */ - case 3: output[0] = output[1] = output[2] = 0; - break; - case 2: output[1] = 1; /* fallthrough */ - case 1: output[0] = 0; - break; - } - } -} - -static float *hdr_load(stbi *s, int *x, int *y, int *comp, int req_comp) -{ - char buffer[HDR_BUFLEN]; - char *token; - int valid = 0; - int width, height; - stbi_uc *scanline; - float *hdr_data; - int len; - unsigned char count, value; - int i, j, k, c1,c2, z; - - - // Check identifier - if (strcmp(hdr_gettoken(s,buffer), "#?RADIANCE") != 0) - return epf("not HDR", "Corrupt HDR image"); - - // Parse header - for(;;) { - token = hdr_gettoken(s,buffer); - if (token[0] == 0) break; - if (strcmp(token, "FORMAT=32-bit_rle_rgbe") == 0) valid = 1; - } - - if (!valid) return epf("unsupported format", "Unsupported HDR format"); - - // Parse width and height - // can't use sscanf() if we're not using stdio! - token = hdr_gettoken(s,buffer); - if (strncmp(token, "-Y ", 3)) return epf("unsupported data layout", "Unsupported HDR format"); - token += 3; - height = strtol(token, &token, 10); - while (*token == ' ') ++token; - if (strncmp(token, "+X ", 3)) return epf("unsupported data layout", "Unsupported HDR format"); - token += 3; - width = strtol(token, NULL, 10); - - *x = width; - *y = height; - - *comp = 3; - if (req_comp == 0) req_comp = 3; - - // Read data - hdr_data = (float *) malloc(height * width * req_comp * sizeof(float)); - - // Load image data - // image data is stored as some number of sca - if ( width < 8 || width >= 32768) { - // Read flat data - for (j=0; j < height; ++j) { - for (i=0; i < width; ++i) { - stbi_uc rgbe[4]; - main_decode_loop: - getn(s, rgbe, 4); - hdr_convert(hdr_data + j * width * req_comp + i * req_comp, rgbe, req_comp); - } - } - } else { - // Read RLE-encoded data - scanline = NULL; - - for (j = 0; j < height; ++j) { - c1 = get8(s); - c2 = get8(s); - len = get8(s); - if (c1 != 2 || c2 != 2 || (len & 0x80)) { - // not run-length encoded, so we have to actually use THIS data as a decoded - // pixel (note this can't be a valid pixel--one of RGB must be >= 128) - uint8 rgbe[4]; - rgbe[0] = (uint8) c1; - rgbe[1] = (uint8) c2; - rgbe[2] = (uint8) len; - rgbe[3] = (uint8) get8u(s); - hdr_convert(hdr_data, rgbe, req_comp); - i = 1; - j = 0; - free(scanline); - goto main_decode_loop; // yes, this makes no sense - } - len <<= 8; - len |= get8(s); - if (len != width) { free(hdr_data); free(scanline); return epf("invalid decoded scanline length", "corrupt HDR"); } - if (scanline == NULL) scanline = (stbi_uc *) malloc(width * 4); - - for (k = 0; k < 4; ++k) { - i = 0; - while (i < width) { - count = get8u(s); - if (count > 128) { - // Run - value = get8u(s); - count -= 128; - for (z = 0; z < count; ++z) - scanline[i++ * 4 + k] = value; - } else { - // Dump - for (z = 0; z < count; ++z) - scanline[i++ * 4 + k] = get8u(s); - } - } - } - for (i=0; i < width; ++i) - hdr_convert(hdr_data+(j*width + i)*req_comp, scanline + i*4, req_comp); - } - free(scanline); - } - - return hdr_data; -} - -static float *stbi_hdr_load(stbi *s, int *x, int *y, int *comp, int req_comp) -{ - return hdr_load(s,x,y,comp,req_comp); -} - -static int stbi_hdr_info(stbi *s, int *x, int *y, int *comp) -{ - char buffer[HDR_BUFLEN]; - char *token; - int valid = 0; - - if (strcmp(hdr_gettoken(s,buffer), "#?RADIANCE") != 0) { - stbi_rewind( s ); - return 0; - } - - for(;;) { - token = hdr_gettoken(s,buffer); - if (token[0] == 0) break; - if (strcmp(token, "FORMAT=32-bit_rle_rgbe") == 0) valid = 1; - } - - if (!valid) { - stbi_rewind( s ); - return 0; - } - token = hdr_gettoken(s,buffer); - if (strncmp(token, "-Y ", 3)) { - stbi_rewind( s ); - return 0; - } - token += 3; - *y = strtol(token, &token, 10); - while (*token == ' ') ++token; - if (strncmp(token, "+X ", 3)) { - stbi_rewind( s ); - return 0; - } - token += 3; - *x = strtol(token, NULL, 10); - *comp = 3; - return 1; -} -#endif // STBI_NO_HDR - -static int stbi_bmp_info(stbi *s, int *x, int *y, int *comp) -{ - int hsz; - if (get8(s) != 'B' || get8(s) != 'M') { - stbi_rewind( s ); - return 0; - } - skip(s,12); - hsz = get32le(s); - if (hsz != 12 && hsz != 40 && hsz != 56 && hsz != 108) { - stbi_rewind( s ); - return 0; - } - if (hsz == 12) { - *x = get16le(s); - *y = get16le(s); - } else { - *x = get32le(s); - *y = get32le(s); - } - if (get16le(s) != 1) { - stbi_rewind( s ); - return 0; - } - *comp = get16le(s) / 8; - return 1; -} - -static int stbi_psd_info(stbi *s, int *x, int *y, int *comp) -{ - int channelCount; - if (get32(s) != 0x38425053) { - stbi_rewind( s ); - return 0; - } - if (get16(s) != 1) { - stbi_rewind( s ); - return 0; - } - skip(s, 6); - channelCount = get16(s); - if (channelCount < 0 || channelCount > 16) { - stbi_rewind( s ); - return 0; - } - *y = get32(s); - *x = get32(s); - if (get16(s) != 8) { - stbi_rewind( s ); - return 0; - } - if (get16(s) != 3) { - stbi_rewind( s ); - return 0; - } - *comp = 4; - return 1; -} - -static int stbi_pic_info(stbi *s, int *x, int *y, int *comp) -{ - int act_comp=0,num_packets=0,chained; - pic_packet_t packets[10]; - - skip(s, 92); - - *x = get16(s); - *y = get16(s); - if (at_eof(s)) return 0; - if ( (*x) != 0 && (1 << 28) / (*x) < (*y)) { - stbi_rewind( s ); - return 0; - } - - skip(s, 8); - - do { - pic_packet_t *packet; - - if (num_packets==sizeof(packets)/sizeof(packets[0])) - return 0; - - packet = &packets[num_packets++]; - chained = get8(s); - packet->size = get8u(s); - packet->type = get8u(s); - packet->channel = get8u(s); - act_comp |= packet->channel; - - if (at_eof(s)) { - stbi_rewind( s ); - return 0; - } - if (packet->size != 8) { - stbi_rewind( s ); - return 0; - } - } while (chained); - - *comp = (act_comp & 0x10 ? 4 : 3); - - return 1; -} - -static int stbi_info_main(stbi *s, int *x, int *y, int *comp) -{ - if (stbi_jpeg_info(s, x, y, comp)) - return 1; - if (stbi_png_info(s, x, y, comp)) - return 1; - if (stbi_gif_info(s, x, y, comp)) - return 1; - if (stbi_bmp_info(s, x, y, comp)) - return 1; - if (stbi_psd_info(s, x, y, comp)) - return 1; - if (stbi_pic_info(s, x, y, comp)) - return 1; - #ifndef STBI_NO_HDR - if (stbi_hdr_info(s, x, y, comp)) - return 1; - #endif - // test tga last because it's a crappy test! - if (stbi_tga_info(s, x, y, comp)) - return 1; - return e("unknown image type", "Image not of any known type, or corrupt"); -} - -#ifndef STBI_NO_STDIO -int stbi_info(char const *filename, int *x, int *y, int *comp) -{ - FILE *f = fopen(filename, "rb"); - int result; - if (!f) return e("can't fopen", "Unable to open file"); - result = stbi_info_from_file(f, x, y, comp); - fclose(f); - return result; -} - -int stbi_info_from_file(FILE *f, int *x, int *y, int *comp) -{ - int r; - stbi s; - long pos = ftell(f); - start_file(&s, f); - r = stbi_info_main(&s,x,y,comp); - fseek(f,pos,SEEK_SET); - return r; -} -#endif // !STBI_NO_STDIO - -int stbi_info_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp) -{ - stbi s; - start_mem(&s,buffer,len); - return stbi_info_main(&s,x,y,comp); -} - -int stbi_info_from_callbacks(stbi_io_callbacks const *c, void *user, int *x, int *y, int *comp) -{ - stbi s; - start_callbacks(&s, (stbi_io_callbacks *) c, user); - return stbi_info_main(&s,x,y,comp); -} - -#endif // STBI_HEADER_FILE_ONLY - -/* - revision history: - 1.33 (2011-07-14) - make stbi_is_hdr work in STBI_NO_HDR (as specified), minor compiler-friendly improvements - 1.32 (2011-07-13) - support for "info" function for all supported filetypes (SpartanJ) - 1.31 (2011-06-20) - a few more leak fixes, bug in PNG handling (SpartanJ) - 1.30 (2011-06-11) - added ability to load files via callbacks to accomidate custom input streams (Ben Wenger) - removed deprecated format-specific test/load functions - removed support for installable file formats (stbi_loader) -- would have been broken for IO callbacks anyway - error cases in bmp and tga give messages and don't leak (Raymond Barbiero, grisha) - fix inefficiency in decoding 32-bit BMP (David Woo) - 1.29 (2010-08-16) - various warning fixes from Aurelien Pocheville - 1.28 (2010-08-01) - fix bug in GIF palette transparency (SpartanJ) - 1.27 (2010-08-01) - cast-to-uint8 to fix warnings - 1.26 (2010-07-24) - fix bug in file buffering for PNG reported by SpartanJ - 1.25 (2010-07-17) - refix trans_data warning (Won Chun) - 1.24 (2010-07-12) - perf improvements reading from files on platforms with lock-heavy fgetc() - minor perf improvements for jpeg - deprecated type-specific functions so we'll get feedback if they're needed - attempt to fix trans_data warning (Won Chun) - 1.23 fixed bug in iPhone support - 1.22 (2010-07-10) - removed image *writing* support - stbi_info support from Jetro Lauha - GIF support from Jean-Marc Lienher - iPhone PNG-extensions from James Brown - warning-fixes from Nicolas Schulz and Janez Zemva (i.e. Janez (U+017D)emva) - 1.21 fix use of 'uint8' in header (reported by jon blow) - 1.20 added support for Softimage PIC, by Tom Seddon - 1.19 bug in interlaced PNG corruption check (found by ryg) - 1.18 2008-08-02 - fix a threading bug (local mutable static) - 1.17 support interlaced PNG - 1.16 major bugfix - convert_format converted one too many pixels - 1.15 initialize some fields for thread safety - 1.14 fix threadsafe conversion bug - header-file-only version (#define STBI_HEADER_FILE_ONLY before including) - 1.13 threadsafe - 1.12 const qualifiers in the API - 1.11 Support installable IDCT, colorspace conversion routines - 1.10 Fixes for 64-bit (don't use "unsigned long") - optimized upsampling by Fabian "ryg" Giesen - 1.09 Fix format-conversion for PSD code (bad global variables!) - 1.08 Thatcher Ulrich's PSD code integrated by Nicolas Schulz - 1.07 attempt to fix C++ warning/errors again - 1.06 attempt to fix C++ warning/errors again - 1.05 fix TGA loading to return correct *comp and use good luminance calc - 1.04 default float alpha is 1, not 255; use 'void *' for stbi_image_free - 1.03 bugfixes to STBI_NO_STDIO, STBI_NO_HDR - 1.02 support for (subset of) HDR files, float interface for preferred access to them - 1.01 fix bug: possible bug in handling right-side up bmps... not sure - fix bug: the stbi_bmp_load() and stbi_tga_load() functions didn't work at all - 1.00 interface to zlib that skips zlib header - 0.99 correct handling of alpha in palette - 0.98 TGA loader by lonesock; dynamically add loaders (untested) - 0.97 jpeg errors on too large a file; also catch another malloc failure - 0.96 fix detection of invalid v value - particleman@mollyrocket forum - 0.95 during header scan, seek to markers in case of padding - 0.94 STBI_NO_STDIO to disable stdio usage; rename all #defines the same - 0.93 handle jpegtran output; verbose errors - 0.92 read 4,8,16,24,32-bit BMP files of several formats - 0.91 output 24-bit Windows 3.0 BMP files - 0.90 fix a few more warnings; bump version number to approach 1.0 - 0.61 bugfixes due to Marc LeBlanc, Christopher Lloyd - 0.60 fix compiling as c++ - 0.59 fix warnings: merge Dave Moore's -Wall fixes - 0.58 fix bug: zlib uncompressed mode len/nlen was wrong endian - 0.57 fix bug: jpg last huffman symbol before marker was >9 bits but less than 16 available - 0.56 fix bug: zlib uncompressed mode len vs. nlen - 0.55 fix bug: restart_interval not initialized to 0 - 0.54 allow NULL for 'int *comp' - 0.53 fix bug in png 3->4; speedup png decoding - 0.52 png handles req_comp=3,4 directly; minor cleanup; jpeg comments - 0.51 obey req_comp requests, 1-component jpegs return as 1-component, - on 'test' only check type, not whether we support this variant - 0.50 first released version -*/ diff --git a/src/stb_image.h b/src/stb_image.h index 0eeece13..39cbb7ad 100644 --- a/src/stb_image.h +++ b/src/stb_image.h @@ -1,13 +1,27 @@ -/* stbi-1.33 - public domain JPEG/PNG reader - http://nothings.org/stb_image.c - when you control the images you're loading +/* stb_image - v2.00b - public domain image loader - http://nothings.org/stb_image.h no warranty implied; use at your own risk + Do this: + #define STB_IMAGE_IMPLEMENTATION + before you include this file in *one* C or C++ file to create the implementation. + + // i.e. it should look like this: + #include ... + #include ... + #include ... + #define STB_IMAGE_IMPLEMENTATION + #include "stb_image.h" + + You can #define STBI_ASSERT(x) before the #include to avoid using assert.h. + And #define STBI_MALLOC, STBI_REALLOC, and STBI_FREE to avoid using malloc,realloc,free + + QUICK NOTES: Primarily of interest to game developers and other people who can avoid problematic images and only need the trivial interface - JPEG baseline (no JPEG progressive) - PNG 8-bit-per-channel only + JPEG baseline & progressive (12 bpc/arithmetic not supported, same as stock IJG lib) + PNG 1/2/4/8-bit-per-channel (16 bpc not supported) TGA (not sure what subset, if a subset) BMP non-1bpp, non-RLE @@ -16,71 +30,191 @@ GIF (*comp always reports as 4-channel) HDR (radiance rgbE format) PIC (Softimage PIC) + PNM (PPM and PGM binary only) - decode from memory or through FILE (define STBI_NO_STDIO to remove code) - decode from arbitrary I/O callbacks - - overridable dequantizing-IDCT, YCbCr-to-RGB conversion (define STBI_SIMD) - - Latest revisions: - 1.33 (2011-07-14) minor fixes suggested by Dave Moore - 1.32 (2011-07-13) info support for all filetypes (SpartanJ) - 1.31 (2011-06-19) a few more leak fixes, bug in PNG handling (SpartanJ) - 1.30 (2011-06-11) added ability to load files via io callbacks (Ben Wenger) - 1.29 (2010-08-16) various warning fixes from Aurelien Pocheville - 1.28 (2010-08-01) fix bug in GIF palette transparency (SpartanJ) - 1.27 (2010-08-01) cast-to-uint8 to fix warnings (Laurent Gomila) - allow trailing 0s at end of image data (Laurent Gomila) - 1.26 (2010-07-24) fix bug in file buffering for PNG reported by SpartanJ + - SIMD acceleration on x86/x64 (SSE2) and ARM (NEON) - See end of file for full revision history. + Full documentation under "DOCUMENTATION" below. + + + Revision 2.00 release notes: + + - Progressive JPEG is now supported. + + - PPM and PGM binary formats are now supported, thanks to Ken Miller. + + - x86 platforms now make use of SSE2 SIMD instructions for + JPEG decoding, and ARM platforms can use NEON SIMD if requested. + This work was done by Fabian "ryg" Giesen. SSE2 is used by + default, but NEON must be enabled explicitly; see docs. + + With other JPEG optimizations included in this version, we see + 2x speedup on a JPEG on an x86 machine, and a 1.5x speedup + on a JPEG on an ARM machine, relative to previous versions of this + library. The same results will not obtain for all JPGs and for all + x86/ARM machines. (Note that progressive JPEGs are significantly + slower to decode than regular JPEGs.) This doesn't mean that this + is the fastest JPEG decoder in the land; rather, it brings it + closer to parity with standard libraries. If you want the fastest + decode, look elsewhere. (See "Philosophy" section of docs below.) + + See final bullet items below for more info on SIMD. + + - Added STBI_MALLOC, STBI_REALLOC, and STBI_FREE macros for replacing + the memory allocator. Unlike other STBI libraries, these macros don't + support a context parameter, so if you need to pass a context in to + the allocator, you'll have to store it in a global or a thread-local + variable. + + - Split existing STBI_NO_HDR flag into two flags, STBI_NO_HDR and + STBI_NO_LINEAR. + STBI_NO_HDR: suppress implementation of .hdr reader format + STBI_NO_LINEAR: suppress high-dynamic-range light-linear float API + + - You can suppress implementation of any of the decoders to reduce + your code footprint by #defining one or more of the following + symbols before creating the implementation. + + STBI_NO_JPEG + STBI_NO_PNG + STBI_NO_BMP + STBI_NO_PSD + STBI_NO_TGA + STBI_NO_GIF + STBI_NO_HDR + STBI_NO_PIC + STBI_NO_PNM (.ppm and .pgm) + + - You can request *only* certain decoders and suppress all other ones + (this will be more forward-compatible, as addition of new decoders + doesn't require you to disable them explicitly): + + STBI_ONLY_JPEG + STBI_ONLY_PNG + STBI_ONLY_BMP + STBI_ONLY_PSD + STBI_ONLY_TGA + STBI_ONLY_GIF + STBI_ONLY_HDR + STBI_ONLY_PIC + STBI_ONLY_PNM (.ppm and .pgm) + + Note that you can define multiples of these, and you will get all + of them ("only x" and "only y" is interpreted to mean "only x&y"). + + - If you use STBI_NO_PNG (or _ONLY_ without PNG), and you still + want the zlib decoder to be available, #define STBI_SUPPORT_ZLIB - TODO: - stbi_info support for BMP,PSD,HDR,PIC + - Compilation of all SIMD code can be suppressed with + #define STBI_NO_SIMD + It should not be necessary to disable SIMD unless you have issues + compiling (e.g. using an x86 compiler which doesn't support SSE + intrinsics or that doesn't support the method used to detect + SSE2 support at run-time), and even those can be reported as + bugs so I can refine the built-in compile-time checking to be + smarter. + + - The old STBI_SIMD system which allowed installing a user-defined + IDCT etc. has been removed. If you need this, don't upgrade. My + assumption is that almost nobody was doing this, and those who + were will find the built-in SIMD more satisfactory anyway. + + - RGB values computed for JPEG images are slightly different from + previous versions of stb_image. (This is due to using less + integer precision in SIMD.) The C code has been adjusted so + that the same RGB values will be computed regardless of whether + SIMD support is available, so your app should always produce + consistent results. But these results are slightly different from + previous versions. (Specifically, about 3% of available YCbCr values + will compute different RGB results from pre-1.49 versions by +-1; + most of the deviating values are one smaller in the G channel.) + + - If you must produce consistent results with previous versions of + stb_image, #define STBI_JPEG_OLD and you will get the same results + you used to; however, you will not get the SIMD speedups for + the YCbCr-to-RGB conversion step (although you should still see + significant JPEG speedup from the other changes). + + Please note that STBI_JPEG_OLD is a temporary feature; it will be + removed in future versions of the library. It is only intended for + near-term back-compatibility use. + + + Latest revision history: + 2.00b (2014-12-25) fix STBI_MALLOC in progressive JPEG + 2.00 (2014-12-25) optimize JPEG, including x86 SSE2 & ARM NEON SIMD + progressive JPEG + PGM/PPM support + STBI_MALLOC,STBI_REALLOC,STBI_FREE + STBI_NO_*, STBI_ONLY_* + GIF bugfix + 1.48 (2014-12-14) fix incorrectly-named assert() + 1.47 (2014-12-14) 1/2/4-bit PNG support (both grayscale and paletted) + optimize PNG + fix bug in interlaced PNG with user-specified channel count + 1.46 (2014-08-26) fix broken tRNS chunk in non-paletted PNG + 1.45 (2014-08-16) workaround MSVC-ARM internal compiler error by wrapping malloc + + See end of file for full revision history. ============================ Contributors ========================= - - Image formats Optimizations & bugfixes - Sean Barrett (jpeg, png, bmp) Fabian "ryg" Giesen - Nicolas Schulz (hdr, psd) - Jonathan Dummer (tga) Bug fixes & warning fixes - Jean-Marc Lienher (gif) Marc LeBlanc - Tom Seddon (pic) Christpher Lloyd - Thatcher Ulrich (psd) Dave Moore - Won Chun - the Horde3D community - Extensions, features Janez Zemva - Jetro Lauha (stbi_info) Jonathan Blow - James "moose2000" Brown (iPhone PNG) Laurent Gomila - Ben "Disch" Wenger (io callbacks) Aruelien Pocheville - Martin "SpartanJ" Golini Ryamond Barbiero - David Woo - - - If your name should be here but isn't, let Sean know. + + Image formats Bug fixes & warning fixes + Sean Barrett (jpeg, png, bmp) Marc LeBlanc + Nicolas Schulz (hdr, psd) Christpher Lloyd + Jonathan Dummer (tga) Dave Moore + Jean-Marc Lienher (gif) Won Chun + Tom Seddon (pic) the Horde3D community + Thatcher Ulrich (psd) Janez Zemva + Ken Miller (pgm, ppm) Jonathan Blow + Laurent Gomila + Aruelien Pocheville + Extensions, features Ryamond Barbiero + Jetro Lauha (stbi_info) David Woo + Martin "SpartanJ" Golini (stbi_info) Martin Golini + James "moose2000" Brown (iPhone PNG) Roy Eltham + Ben "Disch" Wenger (io callbacks) Luke Graham + Omar Cornut (1/2/4-bit PNG) Thomas Ruf + John Bartholomew + Ken Hamada + Optimizations & bugfixes Cort Stratton + Fabian "ryg" Giesen Blazej Dariusz Roszkowski + Arseny Kapoulkine Thibault Reuille + Paul Du Bois + Guillaume George + If your name should be here but Jerry Jansson + isn't, let Sean know. Hayaki Saito + Johan Duparc + Ronny Chevalier + Michal Cichon + Tero Hanninen + +License: + This software is in the public domain. Where that dedication is not + recognized, you are granted a perpetual, irrevocable license to copy + and modify this file however you want. */ #ifndef STBI_INCLUDE_STB_IMAGE_H #define STBI_INCLUDE_STB_IMAGE_H -// To get a header file for this, either cut and paste the header, -// or create stb_image.h, #define STBI_HEADER_FILE_ONLY, and -// then include stb_image.c from it. - -//// begin header file //////////////////////////////////////////////////// +// DOCUMENTATION // // Limitations: -// - no jpeg progressive support -// - non-HDR formats support 8-bit samples only (jpeg, png) -// - no delayed line count (jpeg) -- IJG doesn't support either +// - no 16-bit-per-channel PNG +// - no 12-bit-per-channel JPEG +// - no JPEGs with arithmetic coding // - no 1-bit BMP // - GIF always returns *comp=4 // -// Basic usage (see HDR discussion below): +// Basic usage (see HDR discussion below for HDR usage): // int x,y,n; // unsigned char *data = stbi_load(filename, &x, &y, &n, 0); -// // ... process data if not NULL ... +// // ... process data if not NULL ... // // ... x = width, y = height, n = # 8-bit components per pixel ... // // ... replace '0' with '1'..'4' to force that many components per pixel // // ... but 'n' will always be the number that it would have been if you said 0 @@ -93,14 +227,16 @@ // int req_comp -- if non-zero, # of image components requested in result // // The return value from an image loader is an 'unsigned char *' which points -// to the pixel data. The pixel data consists of *y scanlines of *x pixels, +// to the pixel data, or NULL on an allocation failure or if the image is +// corrupt or invalid. The pixel data consists of *y scanlines of *x pixels, // with each pixel consisting of N interleaved 8-bit components; the first // pixel pointed to is top-left-most in the image. There is no padding between // image scanlines or between pixels, regardless of format. The number of // components N is 'req_comp' if req_comp is non-zero, or *comp otherwise. // If req_comp is non-zero, *comp has the number of components that _would_ // have been output otherwise. E.g. if you set req_comp to 4, you will always -// get RGBA output, but you can check *comp to easily see if it's opaque. +// get RGBA output, but you can check *comp to see if it's trivially opaque +// because e.g. there were only 3 channels in the source image. // // An output image with N components has the following components interleaved // in this order in each pixel: @@ -122,18 +258,66 @@ // // =========================================================================== // -// iPhone PNG support: +// Philosophy // -// By default we convert iphone-formatted PNGs back to RGB; nominally they -// would silently load as BGR, except the existing code should have just -// failed on such iPhone PNGs. But you can disable this conversion by -// by calling stbi_convert_iphone_png_to_rgb(0), in which case -// you will always just get the native iphone "format" through. +// stb libraries are designed with the following priorities: // -// Call stbi_set_unpremultiply_on_load(1) as well to force a divide per -// pixel to remove any premultiplied alpha *only* if the image file explicitly -// says there's premultiplied data (currently only happens in iPhone images, -// and only if iPhone convert-to-rgb processing is on). +// 1. easy to use +// 2. easy to maintain +// 3. good performance +// +// Sometimes I let "good performance" creep up in priority over "easy to maintain", +// and for best performance I may provide less-easy-to-use APIs that give higher +// performance, in addition to the easy to use ones. Nevertheless, it's important +// to keep in mind that from the standpoint of you, a client of this library, +// all you care about is #1 and #3, and stb libraries do not emphasize #3 above all. +// +// Some secondary priorities arise directly from the first two, some of which +// make more explicit reasons why performance can't be emphasized. +// +// - Portable ("ease of use") +// - Small footprint ("easy to maintain") +// - No dependencies ("ease of use") +// +// =========================================================================== +// +// I/O callbacks +// +// I/O callbacks allow you to read from arbitrary sources, like packaged +// files or some other source. Data read from callbacks are processed +// through a small internal buffer (currently 128 bytes) to try to reduce +// overhead. +// +// The three functions you must define are "read" (reads some bytes of data), +// "skip" (skips some bytes of data), "eof" (reports if the stream is at the end). +// +// =========================================================================== +// +// SIMD support +// +// The JPEG decoder will try to automatically use SIMD kernels on x86 when +// supported by the compiler. For ARM Neon support, you must explicitly +// request it. +// +// (The old do-it-yourself SIMD API is no longer supported in the current +// code.) +// +// On x86, SSE2 will automatically be used when available based on a run-time +// test; if not, the generic C versions are used as a fall-back. On ARM targets, +// the typical path is to have separate builds for NEON and non-NEON devices +// (at least this is true for iOS and Android). Therefore, the NEON support is +// toggled by a build flag: define STBI_NEON to get NEON loops. +// +// The output of the JPEG decoder is slightly different from versions where +// SIMD support was introduced (that is, for versions before 1.49). The +// difference is only +-1 in the 8-bit RGB channels, and only on a small +// fraction of pixels. You can force the pre-1.49 behavior by defining +// STBI_JPEG_OLD, but this will disable some of the SIMD decoding path +// and hence cost some performance. +// +// If for some reason you do not want to use any of SIMD code, or if +// you have issues compiling it, you can disable it entirely by +// defining STBI_NO_SIMD. // // =========================================================================== // @@ -156,7 +340,7 @@ // (linear) floats to preserve the full dynamic range: // // float *data = stbi_loadf(filename, &x, &y, &n, 0); -// +// // If you load LDR images through this interface, those images will // be promoted to floating point values, run through the inverse of // constants corresponding to the above: @@ -173,26 +357,26 @@ // // =========================================================================== // -// I/O callbacks +// iPhone PNG support: // -// I/O callbacks allow you to read from arbitrary sources, like packaged -// files or some other source. Data read from callbacks are processed -// through a small internal buffer (currently 128 bytes) to try to reduce -// overhead. +// By default we convert iphone-formatted PNGs back to RGB, even though +// they are internally encoded differently. You can disable this conversion +// by by calling stbi_convert_iphone_png_to_rgb(0), in which case +// you will always just get the native iphone "format" through (which +// is BGR stored in RGB). +// +// Call stbi_set_unpremultiply_on_load(1) as well to force a divide per +// pixel to remove any premultiplied alpha *only* if the image file explicitly +// says there's premultiplied data (currently only happens in iPhone images, +// and only if iPhone convert-to-rgb processing is on). // -// The three functions you must define are "read" (reads some bytes of data), -// "skip" (skips some bytes of data), "eof" (reports if the stream is at the end). #define STBI_NO_HDR // RaySan: not required by raylib +#define STBI_NO_SIMD // RaySan: issues when compiling with GCC 4.7.2 #ifndef STBI_NO_STDIO - -#if defined(_MSC_VER) && _MSC_VER >= 0x1400 -#define _CRT_SECURE_NO_WARNINGS // suppress bogus warnings about fopen() -#endif - #include <stdio.h> -#endif +#endif // STBI_NO_STDIO // NOTE: Added to work with raylib on Android #if defined(PLATFORM_ANDROID) @@ -217,6 +401,12 @@ typedef unsigned char stbi_uc; extern "C" { #endif +#ifdef STB_IMAGE_STATIC +#define STBIDEF static +#else +#define STBIDEF extern +#endif + ////////////////////////////////////////////////////////////////////////////// // // PRIMARY API - works on images of any type @@ -226,63 +416,65 @@ extern "C" { // load image by filename, open file, or memory buffer // -extern stbi_uc *stbi_load_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp); - -#ifndef STBI_NO_STDIO -extern stbi_uc *stbi_load (char const *filename, int *x, int *y, int *comp, int req_comp); -extern stbi_uc *stbi_load_from_file (FILE *f, int *x, int *y, int *comp, int req_comp); -// for stbi_load_from_file, file pointer is left pointing immediately after image -#endif - typedef struct { - int (*read) (void *user,char *data,int size); // fill 'data' with 'size' bytes. return number of bytes actually read - void (*skip) (void *user,unsigned n); // skip the next 'n' bytes + int (*read) (void *user,char *data,int size); // fill 'data' with 'size' bytes. return number of bytes actually read + void (*skip) (void *user,int n); // skip the next 'n' bytes, or 'unget' the last -n bytes if negative int (*eof) (void *user); // returns nonzero if we are at end of file/data } stbi_io_callbacks; -extern stbi_uc *stbi_load_from_callbacks (stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp, int req_comp); +STBIDEF stbi_uc *stbi_load (char const *filename, int *x, int *y, int *comp, int req_comp); +STBIDEF stbi_uc *stbi_load_from_memory (stbi_uc const *buffer, int len , int *x, int *y, int *comp, int req_comp); +STBIDEF stbi_uc *stbi_load_from_callbacks(stbi_io_callbacks const *clbk , void *user, int *x, int *y, int *comp, int req_comp); -#ifndef STBI_NO_HDR - extern float *stbi_loadf_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp); +#ifndef STBI_NO_STDIO +STBIDEF stbi_uc *stbi_load_from_file (FILE *f, int *x, int *y, int *comp, int req_comp); +// for stbi_load_from_file, file pointer is left pointing immediately after image +#endif + +#ifndef STBI_NO_LINEAR + STBIDEF float *stbi_loadf (char const *filename, int *x, int *y, int *comp, int req_comp); + STBIDEF float *stbi_loadf_from_memory (stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp); + STBIDEF float *stbi_loadf_from_callbacks (stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp, int req_comp); #ifndef STBI_NO_STDIO - extern float *stbi_loadf (char const *filename, int *x, int *y, int *comp, int req_comp); - extern float *stbi_loadf_from_file (FILE *f, int *x, int *y, int *comp, int req_comp); + STBIDEF float *stbi_loadf_from_file (FILE *f, int *x, int *y, int *comp, int req_comp); #endif - - extern float *stbi_loadf_from_callbacks (stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp, int req_comp); +#endif - extern void stbi_hdr_to_ldr_gamma(float gamma); - extern void stbi_hdr_to_ldr_scale(float scale); +#ifndef STBI_NO_HDR + STBIDEF void stbi_hdr_to_ldr_gamma(float gamma); + STBIDEF void stbi_hdr_to_ldr_scale(float scale); +#endif - extern void stbi_ldr_to_hdr_gamma(float gamma); - extern void stbi_ldr_to_hdr_scale(float scale); +#ifndef STBI_NO_LINEAR + STBIDEF void stbi_ldr_to_hdr_gamma(float gamma); + STBIDEF void stbi_ldr_to_hdr_scale(float scale); #endif // STBI_NO_HDR -// stbi_is_hdr is always defined -extern int stbi_is_hdr_from_callbacks(stbi_io_callbacks const *clbk, void *user); -extern int stbi_is_hdr_from_memory(stbi_uc const *buffer, int len); +// stbi_is_hdr is always defined, but always returns false if STBI_NO_HDR +STBIDEF int stbi_is_hdr_from_callbacks(stbi_io_callbacks const *clbk, void *user); +STBIDEF int stbi_is_hdr_from_memory(stbi_uc const *buffer, int len); #ifndef STBI_NO_STDIO -extern int stbi_is_hdr (char const *filename); -extern int stbi_is_hdr_from_file(FILE *f); +STBIDEF int stbi_is_hdr (char const *filename); +STBIDEF int stbi_is_hdr_from_file(FILE *f); #endif // STBI_NO_STDIO // get a VERY brief reason for failure // NOT THREADSAFE -extern const char *stbi_failure_reason (void); +STBIDEF const char *stbi_failure_reason (void); // free the loaded image -- this is just free() -extern void stbi_image_free (void *retval_from_stbi_load); +STBIDEF void stbi_image_free (void *retval_from_stbi_load); // get image dimensions & components without fully decoding -extern int stbi_info_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp); -extern int stbi_info_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp); +STBIDEF int stbi_info_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp); +STBIDEF int stbi_info_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp); #ifndef STBI_NO_STDIO -extern int stbi_info (char const *filename, int *x, int *y, int *comp); -extern int stbi_info_from_file (FILE *f, int *x, int *y, int *comp); +STBIDEF int stbi_info (char const *filename, int *x, int *y, int *comp); +STBIDEF int stbi_info_from_file (FILE *f, int *x, int *y, int *comp); #endif @@ -291,48 +483,5843 @@ extern int stbi_info_from_file (FILE *f, int *x, int *y, // for image formats that explicitly notate that they have premultiplied alpha, // we just return the colors as stored in the file. set this flag to force // unpremultiplication. results are undefined if the unpremultiply overflow. -extern void stbi_set_unpremultiply_on_load(int flag_true_if_should_unpremultiply); +STBIDEF void stbi_set_unpremultiply_on_load(int flag_true_if_should_unpremultiply); // indicate whether we should process iphone images back to canonical format, // or just pass them through "as-is" -extern void stbi_convert_iphone_png_to_rgb(int flag_true_if_should_convert); +STBIDEF void stbi_convert_iphone_png_to_rgb(int flag_true_if_should_convert); // ZLIB client - used by PNG, available for other purposes -extern char *stbi_zlib_decode_malloc_guesssize(const char *buffer, int len, int initial_size, int *outlen); -extern char *stbi_zlib_decode_malloc(const char *buffer, int len, int *outlen); -extern int stbi_zlib_decode_buffer(char *obuffer, int olen, const char *ibuffer, int ilen); +STBIDEF char *stbi_zlib_decode_malloc_guesssize(const char *buffer, int len, int initial_size, int *outlen); +STBIDEF char *stbi_zlib_decode_malloc_guesssize_headerflag(const char *buffer, int len, int initial_size, int *outlen, int parse_header); +STBIDEF char *stbi_zlib_decode_malloc(const char *buffer, int len, int *outlen); +STBIDEF int stbi_zlib_decode_buffer(char *obuffer, int olen, const char *ibuffer, int ilen); + +STBIDEF char *stbi_zlib_decode_noheader_malloc(const char *buffer, int len, int *outlen); +STBIDEF int stbi_zlib_decode_noheader_buffer(char *obuffer, int olen, const char *ibuffer, int ilen); + + +#ifdef __cplusplus +} +#endif + +// +// +//// end header file ///////////////////////////////////////////////////// +#endif // STBI_INCLUDE_STB_IMAGE_H -extern char *stbi_zlib_decode_noheader_malloc(const char *buffer, int len, int *outlen); -extern int stbi_zlib_decode_noheader_buffer(char *obuffer, int olen, const char *ibuffer, int ilen); +#ifdef STB_IMAGE_IMPLEMENTATION +#if defined(STBI_ONLY_JPEG) || defined(STBI_ONLY_PNG) || defined(STBI_ONLY_BMP) \ + || defined(STBI_ONLY_TGA) || defined(STBI_ONLY_GIF) || defined(STBI_ONLY_PSD) \ + || defined(STBI_ONLY_HDR) || defined(STBI_ONLY_PIC) || defined(STBI_ONLY_PNM) \ + || defined(STBI_ONLY_ZLIB) + #ifndef STBI_ONLY_JPEG + #define STBI_NO_JPEG + #endif + #ifndef STBI_ONLY_PNG + #define STBI_NO_PNG + #endif + #ifndef STBI_ONLY_BMP + #define STBI_NO_BMP + #endif + #ifndef STBI_ONLY_PSD + #define STBI_NO_PSD + #endif + #ifndef STBI_ONLY_TGA + #define STBI_NO_TGA + #endif + #ifndef STBI_ONLY_GIF + #define STBI_NO_GIF + #endif + #ifndef STBI_ONLY_HDR + #define STBI_NO_HDR + #endif + #ifndef STBI_ONLY_PIC + #define STBI_NO_PIC + #endif + #ifndef STBI_ONLY_PNM + #define STBI_NO_PNM + #endif +#endif -// define faster low-level operations (typically SIMD support) -#ifdef STBI_SIMD -typedef void (*stbi_idct_8x8)(stbi_uc *out, int out_stride, short data[64], unsigned short *dequantize); -// compute an integer IDCT on "input" -// input[x] = data[x] * dequantize[x] -// write results to 'out': 64 samples, each run of 8 spaced by 'out_stride' -// CLAMP results to 0..255 -typedef void (*stbi_YCbCr_to_RGB_run)(stbi_uc *output, stbi_uc const *y, stbi_uc const *cb, stbi_uc const *cr, int count, int step); -// compute a conversion from YCbCr to RGB -// 'count' pixels -// write pixels to 'output'; each pixel is 'step' bytes (either 3 or 4; if 4, write '255' as 4th), order R,G,B -// y: Y input channel -// cb: Cb input channel; scale/biased to be 0..255 -// cr: Cr input channel; scale/biased to be 0..255 +#if defined(STBI_NO_PNG) && !defined(STBI_SUPPORT_ZLIB) && !defined(STBI_NO_ZLIB) +#define STBI_NO_ZLIB +#endif -extern void stbi_install_idct(stbi_idct_8x8 func); -extern void stbi_install_YCbCr_to_RGB(stbi_YCbCr_to_RGB_run func); -#endif // STBI_SIMD +#include <stdarg.h> +#include <stddef.h> // ptrdiff_t on osx +#include <stdlib.h> +#include <string.h> -#ifdef __cplusplus +#if !defined(STBI_NO_LINEAR) || !defined(STBI_NO_HDR) +#include <math.h> // ldexp +#endif + +#ifndef STBI_NO_STDIO +#include <stdio.h> +#endif + +#ifndef STBI_ASSERT +#include <assert.h> +#define STBI_ASSERT(x) assert(x) +#endif + + +#ifndef _MSC_VER + #ifdef __cplusplus + #define stbi_inline inline + #else + #define stbi_inline + #endif +#else + #define stbi_inline __forceinline +#endif + + +#ifdef _MSC_VER +typedef unsigned short stbi__uint16; +typedef signed short stbi__int16; +typedef unsigned int stbi__uint32; +typedef signed int stbi__int32; +#else +#include <stdint.h> +typedef uint16_t stbi__uint16; +typedef int16_t stbi__int16; +typedef uint32_t stbi__uint32; +typedef int32_t stbi__int32; +#endif + +// should produce compiler error if size is wrong +typedef unsigned char validate_uint32[sizeof(stbi__uint32)==4 ? 1 : -1]; + +#ifdef _MSC_VER +#define STBI_NOTUSED(v) (void)(v) +#else +#define STBI_NOTUSED(v) (void)sizeof(v) +#endif + +#ifdef _MSC_VER +#define STBI_HAS_LROTL +#endif + +#ifdef STBI_HAS_LROTL + #define stbi_lrot(x,y) _lrotl(x,y) +#else + #define stbi_lrot(x,y) (((x) << (y)) | ((x) >> (32 - (y)))) +#endif + +#if defined(STBI_MALLOC) && defined(STBI_FREE) && defined(STBI_REALLOC) +// ok +#elif !defined(STBI_MALLOC) && !defined(STBI_FREE) && !defined(STBI_REALLOC) +// ok +#else +#error "Must define all or none of STBI_MALLOC, STBI_FREE, and STBI_REALLOC." +#endif + +#ifndef STBI_MALLOC +#define STBI_MALLOC(sz) malloc(sz) +#define STBI_REALLOC(p,sz) realloc(p,sz) +#define STBI_FREE(p) free(p) +#endif + +#if !defined(STBI_NO_SIMD) && (defined(__x86_64__) || defined(_M_X64) || defined(__i386) || defined(_M_IX86)) +#define STBI_SSE2 +#include <emmintrin.h> + +#ifdef _MSC_VER + +#if _MSC_VER >= 1400 // not VC6 +#include <intrin.h> // __cpuid +static int stbi__cpuid3(void) +{ + int info[4]; + __cpuid(info,1); + return info[3]; +} +#else +static int stbi__cpuid3(void) +{ + int res; + __asm { + mov eax,1 + cpuid + mov res,edx + } + return res; +} +#endif + +#define STBI_SIMD_ALIGN(type, name) __declspec(align(16)) type name + +static int stbi__sse2_available() +{ + int info3 = stbi__cpuid3(); + return ((info3 >> 26) & 1) != 0; +} +#else // assume GCC-style if not VC++ +#define STBI_SIMD_ALIGN(type, name) type name __attribute__((aligned(16))) + +static int stbi__sse2_available() +{ +#if defined(__GNUC__) && (__GNUC__ * 100 + __GNUC_MINOR__) >= 408 // GCC 4.8 or later + // GCC 4.8+ has a nice way to do this + return __builtin_cpu_supports("sse2"); +#else + // portable way to do this, preferably without using GCC inline ASM? + // just bail for now. + return 0; +#endif +} +#endif +#endif + +// ARM NEON +#if defined(STBI_NO_SIMD) && defined(STBI_NEON) +#undef STBI_NEON +#endif + +#ifdef STBI_NEON +#include <arm_neon.h> +// assume GCC or Clang on ARM targets +#define STBI_SIMD_ALIGN(type, name) type name __attribute__((aligned(16))) +#endif + +#ifndef STBI_SIMD_ALIGN +#define STBI_SIMD_ALIGN(type, name) type name +#endif + +/////////////////////////////////////////////// +// +// stbi__context struct and start_xxx functions + +// stbi__context structure is our basic context used by all images, so it +// contains all the IO context, plus some basic image information +typedef struct +{ + stbi__uint32 img_x, img_y; + int img_n, img_out_n; + + stbi_io_callbacks io; + void *io_user_data; + + int read_from_callbacks; + int buflen; + stbi_uc buffer_start[128]; + + stbi_uc *img_buffer, *img_buffer_end; + stbi_uc *img_buffer_original; +} stbi__context; + + +static void stbi__refill_buffer(stbi__context *s); + +// initialize a memory-decode context +static void stbi__start_mem(stbi__context *s, stbi_uc const *buffer, int len) +{ + s->io.read = NULL; + s->read_from_callbacks = 0; + s->img_buffer = s->img_buffer_original = (stbi_uc *) buffer; + s->img_buffer_end = (stbi_uc *) buffer+len; +} + +// initialize a callback-based context +static void stbi__start_callbacks(stbi__context *s, stbi_io_callbacks *c, void *user) +{ + s->io = *c; + s->io_user_data = user; + s->buflen = sizeof(s->buffer_start); + s->read_from_callbacks = 1; + s->img_buffer_original = s->buffer_start; + stbi__refill_buffer(s); +} + +#ifndef STBI_NO_STDIO + +static int stbi__stdio_read(void *user, char *data, int size) +{ + return (int) fread(data,1,size,(FILE*) user); +} + +static void stbi__stdio_skip(void *user, int n) +{ + fseek((FILE*) user, n, SEEK_CUR); +} + +static int stbi__stdio_eof(void *user) +{ + return feof((FILE*) user); +} + +static stbi_io_callbacks stbi__stdio_callbacks = +{ + stbi__stdio_read, + stbi__stdio_skip, + stbi__stdio_eof, +}; + +static void stbi__start_file(stbi__context *s, FILE *f) +{ + stbi__start_callbacks(s, &stbi__stdio_callbacks, (void *) f); +} + +//static void stop_file(stbi__context *s) { } + +#endif // !STBI_NO_STDIO + +static void stbi__rewind(stbi__context *s) +{ + // conceptually rewind SHOULD rewind to the beginning of the stream, + // but we just rewind to the beginning of the initial buffer, because + // we only use it after doing 'test', which only ever looks at at most 92 bytes + s->img_buffer = s->img_buffer_original; +} + +#ifndef STBI_NO_JPEG +static int stbi__jpeg_test(stbi__context *s); +static stbi_uc *stbi__jpeg_load(stbi__context *s, int *x, int *y, int *comp, int req_comp); +static int stbi__jpeg_info(stbi__context *s, int *x, int *y, int *comp); +#endif + +#ifndef STBI_NO_PNG +static int stbi__png_test(stbi__context *s); +static stbi_uc *stbi__png_load(stbi__context *s, int *x, int *y, int *comp, int req_comp); +static int stbi__png_info(stbi__context *s, int *x, int *y, int *comp); +#endif + +#ifndef STBI_NO_BMP +static int stbi__bmp_test(stbi__context *s); +static stbi_uc *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int req_comp); +static int stbi__bmp_info(stbi__context *s, int *x, int *y, int *comp); +#endif + +#ifndef STBI_NO_TGA +static int stbi__tga_test(stbi__context *s); +static stbi_uc *stbi__tga_load(stbi__context *s, int *x, int *y, int *comp, int req_comp); +static int stbi__tga_info(stbi__context *s, int *x, int *y, int *comp); +#endif + +#ifndef STBI_NO_PSD +static int stbi__psd_test(stbi__context *s); +static stbi_uc *stbi__psd_load(stbi__context *s, int *x, int *y, int *comp, int req_comp); +static int stbi__psd_info(stbi__context *s, int *x, int *y, int *comp); +#endif + +#ifndef STBI_NO_HDR +static int stbi__hdr_test(stbi__context *s); +static float *stbi__hdr_load(stbi__context *s, int *x, int *y, int *comp, int req_comp); +static int stbi__hdr_info(stbi__context *s, int *x, int *y, int *comp); +#endif + +#ifndef STBI_NO_PIC +static int stbi__pic_test(stbi__context *s); +static stbi_uc *stbi__pic_load(stbi__context *s, int *x, int *y, int *comp, int req_comp); +static int stbi__pic_info(stbi__context *s, int *x, int *y, int *comp); +#endif + +#ifndef STBI_NO_GIF +static int stbi__gif_test(stbi__context *s); +static stbi_uc *stbi__gif_load(stbi__context *s, int *x, int *y, int *comp, int req_comp); +static int stbi__gif_info(stbi__context *s, int *x, int *y, int *comp); +#endif + +#ifndef STBI_NO_PNM +static int stbi__pnm_test(stbi__context *s); +static stbi_uc *stbi__pnm_load(stbi__context *s, int *x, int *y, int *comp, int req_comp); +static int stbi__pnm_info(stbi__context *s, int *x, int *y, int *comp); +#endif + +// this is not threadsafe +static const char *stbi__g_failure_reason; + +STBIDEF const char *stbi_failure_reason(void) +{ + return stbi__g_failure_reason; +} + +static int stbi__err(const char *str) +{ + stbi__g_failure_reason = str; + return 0; +} + +static void *stbi__malloc(size_t size) +{ + return STBI_MALLOC(size); +} + +// stbi__err - error +// stbi__errpf - error returning pointer to float +// stbi__errpuc - error returning pointer to unsigned char + +#ifdef STBI_NO_FAILURE_STRINGS + #define stbi__err(x,y) 0 +#elif defined(STBI_FAILURE_USERMSG) + #define stbi__err(x,y) stbi__err(y) +#else + #define stbi__err(x,y) stbi__err(x) +#endif + +#define stbi__errpf(x,y) ((float *) (stbi__err(x,y)?NULL:NULL)) +#define stbi__errpuc(x,y) ((unsigned char *) (stbi__err(x,y)?NULL:NULL)) + +STBIDEF void stbi_image_free(void *retval_from_stbi_load) +{ + STBI_FREE(retval_from_stbi_load); +} + +#ifndef STBI_NO_LINEAR +static float *stbi__ldr_to_hdr(stbi_uc *data, int x, int y, int comp); +#endif + +#ifndef STBI_NO_HDR +static stbi_uc *stbi__hdr_to_ldr(float *data, int x, int y, int comp); +#endif + +static unsigned char *stbi_load_main(stbi__context *s, int *x, int *y, int *comp, int req_comp) +{ + #ifndef STBI_NO_JPEG + if (stbi__jpeg_test(s)) return stbi__jpeg_load(s,x,y,comp,req_comp); + #endif + #ifndef STBI_NO_PNG + if (stbi__png_test(s)) return stbi__png_load(s,x,y,comp,req_comp); + #endif + #ifndef STBI_NO_BMP + if (stbi__bmp_test(s)) return stbi__bmp_load(s,x,y,comp,req_comp); + #endif + #ifndef STBI_NO_GIF + if (stbi__gif_test(s)) return stbi__gif_load(s,x,y,comp,req_comp); + #endif + #ifndef STBI_NO_PSD + if (stbi__psd_test(s)) return stbi__psd_load(s,x,y,comp,req_comp); + #endif + #ifndef STBI_NO_PIC + if (stbi__pic_test(s)) return stbi__pic_load(s,x,y,comp,req_comp); + #endif + #ifndef STBI_NO_PNM + if (stbi__pnm_test(s)) return stbi__pnm_load(s,x,y,comp,req_comp); + #endif + + #ifndef STBI_NO_HDR + if (stbi__hdr_test(s)) { + float *hdr = stbi__hdr_load(s, x,y,comp,req_comp); + return stbi__hdr_to_ldr(hdr, *x, *y, req_comp ? req_comp : *comp); + } + #endif + + #ifndef STBI_NO_TGA + // test tga last because it's a crappy test! + if (stbi__tga_test(s)) + return stbi__tga_load(s,x,y,comp,req_comp); + #endif + + return stbi__errpuc("unknown image type", "Image not of any known type, or corrupt"); +} + +#ifndef STBI_NO_STDIO + +static FILE *stbi__fopen(char const *filename, char const *mode) +{ + FILE *f; +#if defined(_MSC_VER) && _MSC_VER >= 1400 + if (0 != fopen_s(&f, filename, mode)) + f=0; +#else + f = fopen(filename, mode); +#endif + return f; +} + + +STBIDEF stbi_uc *stbi_load(char const *filename, int *x, int *y, int *comp, int req_comp) +{ + FILE *f = stbi__fopen(filename, "rb"); + unsigned char *result; + if (!f) return stbi__errpuc("can't fopen", "Unable to open file"); + result = stbi_load_from_file(f,x,y,comp,req_comp); + fclose(f); + return result; +} + +STBIDEF stbi_uc *stbi_load_from_file(FILE *f, int *x, int *y, int *comp, int req_comp) +{ + unsigned char *result; + stbi__context s; + stbi__start_file(&s,f); + result = stbi_load_main(&s,x,y,comp,req_comp); + if (result) { + // need to 'unget' all the characters in the IO buffer + fseek(f, - (int) (s.img_buffer_end - s.img_buffer), SEEK_CUR); + } + return result; +} +#endif //!STBI_NO_STDIO + +STBIDEF stbi_uc *stbi_load_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp) +{ + stbi__context s; + stbi__start_mem(&s,buffer,len); + return stbi_load_main(&s,x,y,comp,req_comp); +} + +STBIDEF stbi_uc *stbi_load_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp, int req_comp) +{ + stbi__context s; + stbi__start_callbacks(&s, (stbi_io_callbacks *) clbk, user); + return stbi_load_main(&s,x,y,comp,req_comp); +} + +#ifndef STBI_NO_LINEAR +static float *stbi_loadf_main(stbi__context *s, int *x, int *y, int *comp, int req_comp) +{ + unsigned char *data; + #ifndef STBI_NO_HDR + if (stbi__hdr_test(s)) + return stbi__hdr_load(s,x,y,comp,req_comp); + #endif + data = stbi_load_main(s, x, y, comp, req_comp); + if (data) + return stbi__ldr_to_hdr(data, *x, *y, req_comp ? req_comp : *comp); + return stbi__errpf("unknown image type", "Image not of any known type, or corrupt"); +} + +STBIDEF float *stbi_loadf_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp) +{ + stbi__context s; + stbi__start_mem(&s,buffer,len); + return stbi_loadf_main(&s,x,y,comp,req_comp); +} + +STBIDEF float *stbi_loadf_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp, int req_comp) +{ + stbi__context s; + stbi__start_callbacks(&s, (stbi_io_callbacks *) clbk, user); + return stbi_loadf_main(&s,x,y,comp,req_comp); +} + +#ifndef STBI_NO_STDIO +STBIDEF float *stbi_loadf(char const *filename, int *x, int *y, int *comp, int req_comp) +{ + float *result; + FILE *f = stbi__fopen(filename, "rb"); + if (!f) return stbi__errpf("can't fopen", "Unable to open file"); + result = stbi_loadf_from_file(f,x,y,comp,req_comp); + fclose(f); + return result; +} + +STBIDEF float *stbi_loadf_from_file(FILE *f, int *x, int *y, int *comp, int req_comp) +{ + stbi__context s; + stbi__start_file(&s,f); + return stbi_loadf_main(&s,x,y,comp,req_comp); +} +#endif // !STBI_NO_STDIO + +#endif // !STBI_NO_LINEAR + +// these is-hdr-or-not is defined independent of whether STBI_NO_LINEAR is +// defined, for API simplicity; if STBI_NO_LINEAR is defined, it always +// reports false! + +STBIDEF int stbi_is_hdr_from_memory(stbi_uc const *buffer, int len) +{ + #ifndef STBI_NO_HDR + stbi__context s; + stbi__start_mem(&s,buffer,len); + return stbi__hdr_test(&s); + #else + STBI_NOTUSED(buffer); + STBI_NOTUSED(len); + return 0; + #endif +} + +#ifndef STBI_NO_STDIO +STBIDEF int stbi_is_hdr (char const *filename) +{ + FILE *f = stbi__fopen(filename, "rb"); + int result=0; + if (f) { + result = stbi_is_hdr_from_file(f); + fclose(f); + } + return result; +} + +STBIDEF int stbi_is_hdr_from_file(FILE *f) +{ + #ifndef STBI_NO_HDR + stbi__context s; + stbi__start_file(&s,f); + return stbi__hdr_test(&s); + #else + return 0; + #endif +} +#endif // !STBI_NO_STDIO + +STBIDEF int stbi_is_hdr_from_callbacks(stbi_io_callbacks const *clbk, void *user) +{ + #ifndef STBI_NO_HDR + stbi__context s; + stbi__start_callbacks(&s, (stbi_io_callbacks *) clbk, user); + return stbi__hdr_test(&s); + #else + return 0; + #endif } + +static float stbi__h2l_gamma_i=1.0f/2.2f, stbi__h2l_scale_i=1.0f; +static float stbi__l2h_gamma=2.2f, stbi__l2h_scale=1.0f; + +#ifndef STBI_NO_LINEAR +STBIDEF void stbi_ldr_to_hdr_gamma(float gamma) { stbi__l2h_gamma = gamma; } +STBIDEF void stbi_ldr_to_hdr_scale(float scale) { stbi__l2h_scale = scale; } #endif +STBIDEF void stbi_hdr_to_ldr_gamma(float gamma) { stbi__h2l_gamma_i = 1/gamma; } +STBIDEF void stbi_hdr_to_ldr_scale(float scale) { stbi__h2l_scale_i = 1/scale; } + + +////////////////////////////////////////////////////////////////////////////// // +// Common code used by all image loaders // -//// end header file ///////////////////////////////////////////////////// -#endif // STBI_INCLUDE_STB_IMAGE_H
\ No newline at end of file + +enum +{ + STBI__SCAN_load=0, + STBI__SCAN_type, + STBI__SCAN_header +}; + +static void stbi__refill_buffer(stbi__context *s) +{ + int n = (s->io.read)(s->io_user_data,(char*)s->buffer_start,s->buflen); + if (n == 0) { + // at end of file, treat same as if from memory, but need to handle case + // where s->img_buffer isn't pointing to safe memory, e.g. 0-byte file + s->read_from_callbacks = 0; + s->img_buffer = s->buffer_start; + s->img_buffer_end = s->buffer_start+1; + *s->img_buffer = 0; + } else { + s->img_buffer = s->buffer_start; + s->img_buffer_end = s->buffer_start + n; + } +} + +stbi_inline static stbi_uc stbi__get8(stbi__context *s) +{ + if (s->img_buffer < s->img_buffer_end) + return *s->img_buffer++; + if (s->read_from_callbacks) { + stbi__refill_buffer(s); + return *s->img_buffer++; + } + return 0; +} + +stbi_inline static int stbi__at_eof(stbi__context *s) +{ + if (s->io.read) { + if (!(s->io.eof)(s->io_user_data)) return 0; + // if feof() is true, check if buffer = end + // special case: we've only got the special 0 character at the end + if (s->read_from_callbacks == 0) return 1; + } + + return s->img_buffer >= s->img_buffer_end; +} + +static void stbi__skip(stbi__context *s, int n) +{ + if (s->io.read) { + int blen = (int) (s->img_buffer_end - s->img_buffer); + if (blen < n) { + s->img_buffer = s->img_buffer_end; + (s->io.skip)(s->io_user_data, n - blen); + return; + } + } + s->img_buffer += n; +} + +static int stbi__getn(stbi__context *s, stbi_uc *buffer, int n) +{ + if (s->io.read) { + int blen = (int) (s->img_buffer_end - s->img_buffer); + if (blen < n) { + int res, count; + + memcpy(buffer, s->img_buffer, blen); + + count = (s->io.read)(s->io_user_data, (char*) buffer + blen, n - blen); + res = (count == (n-blen)); + s->img_buffer = s->img_buffer_end; + return res; + } + } + + if (s->img_buffer+n <= s->img_buffer_end) { + memcpy(buffer, s->img_buffer, n); + s->img_buffer += n; + return 1; + } else + return 0; +} + +static int stbi__get16be(stbi__context *s) +{ + int z = stbi__get8(s); + return (z << 8) + stbi__get8(s); +} + +static stbi__uint32 stbi__get32be(stbi__context *s) +{ + stbi__uint32 z = stbi__get16be(s); + return (z << 16) + stbi__get16be(s); +} + +static int stbi__get16le(stbi__context *s) +{ + int z = stbi__get8(s); + return z + (stbi__get8(s) << 8); +} + +static stbi__uint32 stbi__get32le(stbi__context *s) +{ + stbi__uint32 z = stbi__get16le(s); + return z + (stbi__get16le(s) << 16); +} + +#define STBI__BYTECAST(x) ((stbi_uc) ((x) & 255)) // truncate int to byte without warnings + + +////////////////////////////////////////////////////////////////////////////// +// +// generic converter from built-in img_n to req_comp +// individual types do this automatically as much as possible (e.g. jpeg +// does all cases internally since it needs to colorspace convert anyway, +// and it never has alpha, so very few cases ). png can automatically +// interleave an alpha=255 channel, but falls back to this for other cases +// +// assume data buffer is malloced, so malloc a new one and free that one +// only failure mode is malloc failing + +static stbi_uc stbi__compute_y(int r, int g, int b) +{ + return (stbi_uc) (((r*77) + (g*150) + (29*b)) >> 8); +} + +static unsigned char *stbi__convert_format(unsigned char *data, int img_n, int req_comp, unsigned int x, unsigned int y) +{ + int i,j; + unsigned char *good; + + if (req_comp == img_n) return data; + STBI_ASSERT(req_comp >= 1 && req_comp <= 4); + + good = (unsigned char *) stbi__malloc(req_comp * x * y); + if (good == NULL) { + STBI_FREE(data); + return stbi__errpuc("outofmem", "Out of memory"); + } + + for (j=0; j < (int) y; ++j) { + unsigned char *src = data + j * x * img_n ; + unsigned char *dest = good + j * x * req_comp; + + #define COMBO(a,b) ((a)*8+(b)) + #define CASE(a,b) case COMBO(a,b): for(i=x-1; i >= 0; --i, src += a, dest += b) + // convert source image with img_n components to one with req_comp components; + // avoid switch per pixel, so use switch per scanline and massive macros + switch (COMBO(img_n, req_comp)) { + CASE(1,2) dest[0]=src[0], dest[1]=255; break; + CASE(1,3) dest[0]=dest[1]=dest[2]=src[0]; break; + CASE(1,4) dest[0]=dest[1]=dest[2]=src[0], dest[3]=255; break; + CASE(2,1) dest[0]=src[0]; break; + CASE(2,3) dest[0]=dest[1]=dest[2]=src[0]; break; + CASE(2,4) dest[0]=dest[1]=dest[2]=src[0], dest[3]=src[1]; break; + CASE(3,4) dest[0]=src[0],dest[1]=src[1],dest[2]=src[2],dest[3]=255; break; + CASE(3,1) dest[0]=stbi__compute_y(src[0],src[1],src[2]); break; + CASE(3,2) dest[0]=stbi__compute_y(src[0],src[1],src[2]), dest[1] = 255; break; + CASE(4,1) dest[0]=stbi__compute_y(src[0],src[1],src[2]); break; + CASE(4,2) dest[0]=stbi__compute_y(src[0],src[1],src[2]), dest[1] = src[3]; break; + CASE(4,3) dest[0]=src[0],dest[1]=src[1],dest[2]=src[2]; break; + default: STBI_ASSERT(0); + } + #undef CASE + } + + STBI_FREE(data); + return good; +} + +#ifndef STBI_NO_LINEAR +static float *stbi__ldr_to_hdr(stbi_uc *data, int x, int y, int comp) +{ + int i,k,n; + float *output = (float *) stbi__malloc(x * y * comp * sizeof(float)); + if (output == NULL) { STBI_FREE(data); return stbi__errpf("outofmem", "Out of memory"); } + // compute number of non-alpha components + if (comp & 1) n = comp; else n = comp-1; + for (i=0; i < x*y; ++i) { + for (k=0; k < n; ++k) { + output[i*comp + k] = (float) (pow(data[i*comp+k]/255.0f, stbi__l2h_gamma) * stbi__l2h_scale); + } + if (k < comp) output[i*comp + k] = data[i*comp+k]/255.0f; + } + STBI_FREE(data); + return output; +} +#endif + +#ifndef STBI_NO_HDR +#define stbi__float2int(x) ((int) (x)) +static stbi_uc *stbi__hdr_to_ldr(float *data, int x, int y, int comp) +{ + int i,k,n; + stbi_uc *output = (stbi_uc *) stbi__malloc(x * y * comp); + if (output == NULL) { STBI_FREE(data); return stbi__errpuc("outofmem", "Out of memory"); } + // compute number of non-alpha components + if (comp & 1) n = comp; else n = comp-1; + for (i=0; i < x*y; ++i) { + for (k=0; k < n; ++k) { + float z = (float) pow(data[i*comp+k]*stbi__h2l_scale_i, stbi__h2l_gamma_i) * 255 + 0.5f; + if (z < 0) z = 0; + if (z > 255) z = 255; + output[i*comp + k] = (stbi_uc) stbi__float2int(z); + } + if (k < comp) { + float z = data[i*comp+k] * 255 + 0.5f; + if (z < 0) z = 0; + if (z > 255) z = 255; + output[i*comp + k] = (stbi_uc) stbi__float2int(z); + } + } + STBI_FREE(data); + return output; +} +#endif + +////////////////////////////////////////////////////////////////////////////// +// +// "baseline" JPEG/JFIF decoder +// +// simple implementation +// - doesn't support delayed output of y-dimension +// - simple interface (only one output format: 8-bit interleaved RGB) +// - doesn't try to recover corrupt jpegs +// - doesn't allow partial loading, loading multiple at once +// - still fast on x86 (copying globals into locals doesn't help x86) +// - allocates lots of intermediate memory (full size of all components) +// - non-interleaved case requires this anyway +// - allows good upsampling (see next) +// high-quality +// - upsampled channels are bilinearly interpolated, even across blocks +// - quality integer IDCT derived from IJG's 'slow' +// performance +// - fast huffman; reasonable integer IDCT +// - some SIMD kernels for common paths on targets with SSE2/NEON +// - uses a lot of intermediate memory, could cache poorly + +#ifndef STBI_NO_JPEG + +// huffman decoding acceleration +#define FAST_BITS 9 // larger handles more cases; smaller stomps less cache + +typedef struct +{ + stbi_uc fast[1 << FAST_BITS]; + // weirdly, repacking this into AoS is a 10% speed loss, instead of a win + stbi__uint16 code[256]; + stbi_uc values[256]; + stbi_uc size[257]; + unsigned int maxcode[18]; + int delta[17]; // old 'firstsymbol' - old 'firstcode' +} stbi__huffman; + +typedef struct +{ + stbi__context *s; + stbi__huffman huff_dc[4]; + stbi__huffman huff_ac[4]; + stbi_uc dequant[4][64]; + stbi__int16 fast_ac[4][1 << FAST_BITS]; + +// sizes for components, interleaved MCUs + int img_h_max, img_v_max; + int img_mcu_x, img_mcu_y; + int img_mcu_w, img_mcu_h; + +// definition of jpeg image component + struct + { + int id; + int h,v; + int tq; + int hd,ha; + int dc_pred; + + int x,y,w2,h2; + stbi_uc *data; + void *raw_data, *raw_coeff; + stbi_uc *linebuf; + short *coeff; // progressive only + int coeff_w, coeff_h; // number of 8x8 coefficient blocks + } img_comp[4]; + + stbi__uint32 code_buffer; // jpeg entropy-coded buffer + int code_bits; // number of valid bits + unsigned char marker; // marker seen while filling entropy buffer + int nomore; // flag if we saw a marker so must stop + + int progressive; + int spec_start; + int spec_end; + int succ_high; + int succ_low; + int eob_run; + + int scan_n, order[4]; + int restart_interval, todo; + +// kernels + void (*idct_block_kernel)(stbi_uc *out, int out_stride, short data[64]); + void (*YCbCr_to_RGB_kernel)(stbi_uc *out, const stbi_uc *y, const stbi_uc *pcb, const stbi_uc *pcr, int count, int step); + stbi_uc *(*resample_row_hv_2_kernel)(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs); +} stbi__jpeg; + +static int stbi__build_huffman(stbi__huffman *h, int *count) +{ + int i,j,k=0,code; + // build size list for each symbol (from JPEG spec) + for (i=0; i < 16; ++i) + for (j=0; j < count[i]; ++j) + h->size[k++] = (stbi_uc) (i+1); + h->size[k] = 0; + + // compute actual symbols (from jpeg spec) + code = 0; + k = 0; + for(j=1; j <= 16; ++j) { + // compute delta to add to code to compute symbol id + h->delta[j] = k - code; + if (h->size[k] == j) { + while (h->size[k] == j) + h->code[k++] = (stbi__uint16) (code++); + if (code-1 >= (1 << j)) return stbi__err("bad code lengths","Corrupt JPEG"); + } + // compute largest code + 1 for this size, preshifted as needed later + h->maxcode[j] = code << (16-j); + code <<= 1; + } + h->maxcode[j] = 0xffffffff; + + // build non-spec acceleration table; 255 is flag for not-accelerated + memset(h->fast, 255, 1 << FAST_BITS); + for (i=0; i < k; ++i) { + int s = h->size[i]; + if (s <= FAST_BITS) { + int c = h->code[i] << (FAST_BITS-s); + int m = 1 << (FAST_BITS-s); + for (j=0; j < m; ++j) { + h->fast[c+j] = (stbi_uc) i; + } + } + } + return 1; +} + +// build a table that decodes both magnitude and value of small ACs in +// one go. +static void stbi__build_fast_ac(stbi__int16 *fast_ac, stbi__huffman *h) +{ + int i; + for (i=0; i < (1 << FAST_BITS); ++i) { + stbi_uc fast = h->fast[i]; + fast_ac[i] = 0; + if (fast < 255) { + int rs = h->values[fast]; + int run = (rs >> 4) & 15; + int magbits = rs & 15; + int len = h->size[fast]; + + if (magbits && len + magbits <= FAST_BITS) { + // magnitude code followed by receive_extend code + int k = ((i << len) & ((1 << FAST_BITS) - 1)) >> (FAST_BITS - magbits); + int m = 1 << (magbits - 1); + if (k < m) k += (-1 << magbits) + 1; + // if the result is small enough, we can fit it in fast_ac table + if (k >= -128 && k <= 127) + fast_ac[i] = (stbi__int16) ((k << 8) + (run << 4) + (len + magbits)); + } + } + } +} + +static void stbi__grow_buffer_unsafe(stbi__jpeg *j) +{ + do { + int b = j->nomore ? 0 : stbi__get8(j->s); + if (b == 0xff) { + int c = stbi__get8(j->s); + if (c != 0) { + j->marker = (unsigned char) c; + j->nomore = 1; + return; + } + } + j->code_buffer |= b << (24 - j->code_bits); + j->code_bits += 8; + } while (j->code_bits <= 24); +} + +// (1 << n) - 1 +static stbi__uint32 stbi__bmask[17]={0,1,3,7,15,31,63,127,255,511,1023,2047,4095,8191,16383,32767,65535}; + +// decode a jpeg huffman value from the bitstream +stbi_inline static int stbi__jpeg_huff_decode(stbi__jpeg *j, stbi__huffman *h) +{ + unsigned int temp; + int c,k; + + if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); + + // look at the top FAST_BITS and determine what symbol ID it is, + // if the code is <= FAST_BITS + c = (j->code_buffer >> (32 - FAST_BITS)) & ((1 << FAST_BITS)-1); + k = h->fast[c]; + if (k < 255) { + int s = h->size[k]; + if (s > j->code_bits) + return -1; + j->code_buffer <<= s; + j->code_bits -= s; + return h->values[k]; + } + + // naive test is to shift the code_buffer down so k bits are + // valid, then test against maxcode. To speed this up, we've + // preshifted maxcode left so that it has (16-k) 0s at the + // end; in other words, regardless of the number of bits, it + // wants to be compared against something shifted to have 16; + // that way we don't need to shift inside the loop. + temp = j->code_buffer >> 16; + for (k=FAST_BITS+1 ; ; ++k) + if (temp < h->maxcode[k]) + break; + if (k == 17) { + // error! code not found + j->code_bits -= 16; + return -1; + } + + if (k > j->code_bits) + return -1; + + // convert the huffman code to the symbol id + c = ((j->code_buffer >> (32 - k)) & stbi__bmask[k]) + h->delta[k]; + STBI_ASSERT((((j->code_buffer) >> (32 - h->size[c])) & stbi__bmask[h->size[c]]) == h->code[c]); + + // convert the id to a symbol + j->code_bits -= k; + j->code_buffer <<= k; + return h->values[c]; +} + +// bias[n] = (-1<<n) + 1 +static int const stbi__jbias[16] = {0,-1,-3,-7,-15,-31,-63,-127,-255,-511,-1023,-2047,-4095,-8191,-16383,-32767}; + +// combined JPEG 'receive' and JPEG 'extend', since baseline +// always extends everything it receives. +stbi_inline static int stbi__extend_receive(stbi__jpeg *j, int n) +{ + unsigned int k; + int sgn; + if (j->code_bits < n) stbi__grow_buffer_unsafe(j); + + sgn = (stbi__int32)j->code_buffer >> 31; // sign bit is always in MSB + k = stbi_lrot(j->code_buffer, n); + j->code_buffer = k & ~stbi__bmask[n]; + k &= stbi__bmask[n]; + j->code_bits -= n; + return k + (stbi__jbias[n] & ~sgn); +} + +// get some unsigned bits +stbi_inline static int stbi__jpeg_get_bits(stbi__jpeg *j, int n) +{ + unsigned int k; + if (j->code_bits < n) stbi__grow_buffer_unsafe(j); + k = stbi_lrot(j->code_buffer, n); + j->code_buffer = k & ~stbi__bmask[n]; + k &= stbi__bmask[n]; + j->code_bits -= n; + return k; +} + +stbi_inline static int stbi__jpeg_get_bit(stbi__jpeg *j) +{ + unsigned int k; + if (j->code_bits < 1) stbi__grow_buffer_unsafe(j); + k = j->code_buffer; + j->code_buffer <<= 1; + --j->code_bits; + return k & 0x80000000; +} + +// given a value that's at position X in the zigzag stream, +// where does it appear in the 8x8 matrix coded as row-major? +static stbi_uc stbi__jpeg_dezigzag[64+15] = +{ + 0, 1, 8, 16, 9, 2, 3, 10, + 17, 24, 32, 25, 18, 11, 4, 5, + 12, 19, 26, 33, 40, 48, 41, 34, + 27, 20, 13, 6, 7, 14, 21, 28, + 35, 42, 49, 56, 57, 50, 43, 36, + 29, 22, 15, 23, 30, 37, 44, 51, + 58, 59, 52, 45, 38, 31, 39, 46, + 53, 60, 61, 54, 47, 55, 62, 63, + // let corrupt input sample past end + 63, 63, 63, 63, 63, 63, 63, 63, + 63, 63, 63, 63, 63, 63, 63 +}; + +// decode one 64-entry block-- +static int stbi__jpeg_decode_block(stbi__jpeg *j, short data[64], stbi__huffman *hdc, stbi__huffman *hac, stbi__int16 *fac, int b, stbi_uc *dequant) +{ + int diff,dc,k; + int t; + + if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); + t = stbi__jpeg_huff_decode(j, hdc); + if (t < 0) return stbi__err("bad huffman code","Corrupt JPEG"); + + // 0 all the ac values now so we can do it 32-bits at a time + memset(data,0,64*sizeof(data[0])); + + diff = t ? stbi__extend_receive(j, t) : 0; + dc = j->img_comp[b].dc_pred + diff; + j->img_comp[b].dc_pred = dc; + data[0] = (short) (dc * dequant[0]); + + // decode AC components, see JPEG spec + k = 1; + do { + unsigned int zig; + int c,r,s; + if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); + c = (j->code_buffer >> (32 - FAST_BITS)) & ((1 << FAST_BITS)-1); + r = fac[c]; + if (r) { // fast-AC path + k += (r >> 4) & 15; // run + s = r & 15; // combined length + j->code_buffer <<= s; + j->code_bits -= s; + // decode into unzigzag'd location + zig = stbi__jpeg_dezigzag[k++]; + data[zig] = (short) ((r >> 8) * dequant[zig]); + } else { + int rs = stbi__jpeg_huff_decode(j, hac); + if (rs < 0) return stbi__err("bad huffman code","Corrupt JPEG"); + s = rs & 15; + r = rs >> 4; + if (s == 0) { + if (rs != 0xf0) break; // end block + k += 16; + } else { + k += r; + // decode into unzigzag'd location + zig = stbi__jpeg_dezigzag[k++]; + data[zig] = (short) (stbi__extend_receive(j,s) * dequant[zig]); + } + } + } while (k < 64); + return 1; +} + +static int stbi__jpeg_decode_block_prog_dc(stbi__jpeg *j, short data[64], stbi__huffman *hdc, int b) +{ + int diff,dc; + int t; + if (j->spec_end != 0) return stbi__err("can't merge dc and ac", "Corrupt JPEG"); + + if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); + + if (j->succ_high == 0) { + // first scan for DC coefficient, must be first + memset(data,0,64*sizeof(data[0])); // 0 all the ac values now + t = stbi__jpeg_huff_decode(j, hdc); + diff = t ? stbi__extend_receive(j, t) : 0; + + dc = j->img_comp[b].dc_pred + diff; + j->img_comp[b].dc_pred = dc; + data[0] = (short) (dc << j->succ_low); + } else { + // refinement scan for DC coefficient + if (stbi__jpeg_get_bit(j)) + data[0] += (short) (1 << j->succ_low); + } + return 1; +} + +// @OPTIMIZE: store non-zigzagged during the decode passes, +// and only de-zigzag when dequantizing +static int stbi__jpeg_decode_block_prog_ac(stbi__jpeg *j, short data[64], stbi__huffman *hac, stbi__int16 *fac) +{ + int k; + if (j->spec_start == 0) return stbi__err("can't merge dc and ac", "Corrupt JPEG"); + + if (j->succ_high == 0) { + int shift = j->succ_low; + + if (j->eob_run) { + --j->eob_run; + return 1; + } + + k = j->spec_start; + do { + unsigned int zig; + int c,r,s; + if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); + c = (j->code_buffer >> (32 - FAST_BITS)) & ((1 << FAST_BITS)-1); + r = fac[c]; + if (r) { // fast-AC path + k += (r >> 4) & 15; // run + s = r & 15; // combined length + j->code_buffer <<= s; + j->code_bits -= s; + zig = stbi__jpeg_dezigzag[k++]; + data[zig] = (short) ((r >> 8) << shift); + } else { + int rs = stbi__jpeg_huff_decode(j, hac); + if (rs < 0) return stbi__err("bad huffman code","Corrupt JPEG"); + s = rs & 15; + r = rs >> 4; + if (s == 0) { + if (r < 15) { + j->eob_run = (1 << r); + if (r) + j->eob_run += stbi__jpeg_get_bits(j, r); + --j->eob_run; + break; + } + k += 16; + } else { + k += r; + zig = stbi__jpeg_dezigzag[k++]; + data[zig] = (short) (stbi__extend_receive(j,s) << shift); + } + } + } while (k <= j->spec_end); + } else { + // refinement scan for these AC coefficients + + short bit = (short) (1 << j->succ_low); + + if (j->eob_run) { + --j->eob_run; + for (k = j->spec_start; k <= j->spec_end; ++k) { + short *p = &data[stbi__jpeg_dezigzag[k]]; + if (*p != 0) + if (stbi__jpeg_get_bit(j)) + { + if ((*p & bit)==0) + { + if (*p > 0) + *p += bit; + else + *p -= bit; + } + } + } + } else { + k = j->spec_start; + do { + int r,s; + int rs = stbi__jpeg_huff_decode(j, hac); // @OPTIMIZE see if we can use the fast path here, advance-by-r is so slow, eh + if (rs < 0) return stbi__err("bad huffman code","Corrupt JPEG"); + s = rs & 15; + r = rs >> 4; + if (s == 0) { + if (r < 15) { + j->eob_run = (1 << r) - 1; + if (r) + j->eob_run += stbi__jpeg_get_bits(j, r); + r = 64; // force end of block + } else + r = 16; // r=15 is the code for 16 0s + } else { + if (s != 1) return stbi__err("bad huffman code", "Corrupt JPEG"); + // sign bit + if (stbi__jpeg_get_bit(j)) + s = bit; + else + s = -bit; + } + + // advance by r + while (k <= j->spec_end) { + short *p = &data[stbi__jpeg_dezigzag[k]]; + if (*p != 0) { + if (stbi__jpeg_get_bit(j)) + { + if ((*p & bit)==0) + { + if (*p > 0) + *p += bit; + else + *p -= bit; + } + } + ++k; + } else { + if (r == 0) { + if (s) + data[stbi__jpeg_dezigzag[k++]] = s; + break; + } + --r; + ++k; + } + } + } while (k <= j->spec_end); + } + } + return 1; +} + +// take a -128..127 value and stbi__clamp it and convert to 0..255 +stbi_inline static stbi_uc stbi__clamp(int x) +{ + // trick to use a single test to catch both cases + if ((unsigned int) x > 255) { + if (x < 0) return 0; + if (x > 255) return 255; + } + return (stbi_uc) x; +} + +#define stbi__f2f(x) ((int) (((x) * 4096 + 0.5))) +#define stbi__fsh(x) ((x) << 12) + +// derived from jidctint -- DCT_ISLOW +#define STBI__IDCT_1D(s0,s1,s2,s3,s4,s5,s6,s7) \ + int t0,t1,t2,t3,p1,p2,p3,p4,p5,x0,x1,x2,x3; \ + p2 = s2; \ + p3 = s6; \ + p1 = (p2+p3) * stbi__f2f(0.5411961f); \ + t2 = p1 + p3*stbi__f2f(-1.847759065f); \ + t3 = p1 + p2*stbi__f2f( 0.765366865f); \ + p2 = s0; \ + p3 = s4; \ + t0 = stbi__fsh(p2+p3); \ + t1 = stbi__fsh(p2-p3); \ + x0 = t0+t3; \ + x3 = t0-t3; \ + x1 = t1+t2; \ + x2 = t1-t2; \ + t0 = s7; \ + t1 = s5; \ + t2 = s3; \ + t3 = s1; \ + p3 = t0+t2; \ + p4 = t1+t3; \ + p1 = t0+t3; \ + p2 = t1+t2; \ + p5 = (p3+p4)*stbi__f2f( 1.175875602f); \ + t0 = t0*stbi__f2f( 0.298631336f); \ + t1 = t1*stbi__f2f( 2.053119869f); \ + t2 = t2*stbi__f2f( 3.072711026f); \ + t3 = t3*stbi__f2f( 1.501321110f); \ + p1 = p5 + p1*stbi__f2f(-0.899976223f); \ + p2 = p5 + p2*stbi__f2f(-2.562915447f); \ + p3 = p3*stbi__f2f(-1.961570560f); \ + p4 = p4*stbi__f2f(-0.390180644f); \ + t3 += p1+p4; \ + t2 += p2+p3; \ + t1 += p2+p4; \ + t0 += p1+p3; + +static void stbi__idct_block(stbi_uc *out, int out_stride, short data[64]) +{ + int i,val[64],*v=val; + stbi_uc *o; + short *d = data; + + // columns + for (i=0; i < 8; ++i,++d, ++v) { + // if all zeroes, shortcut -- this avoids dequantizing 0s and IDCTing + if (d[ 8]==0 && d[16]==0 && d[24]==0 && d[32]==0 + && d[40]==0 && d[48]==0 && d[56]==0) { + // no shortcut 0 seconds + // (1|2|3|4|5|6|7)==0 0 seconds + // all separate -0.047 seconds + // 1 && 2|3 && 4|5 && 6|7: -0.047 seconds + int dcterm = d[0] << 2; + v[0] = v[8] = v[16] = v[24] = v[32] = v[40] = v[48] = v[56] = dcterm; + } else { + STBI__IDCT_1D(d[ 0],d[ 8],d[16],d[24],d[32],d[40],d[48],d[56]) + // constants scaled things up by 1<<12; let's bring them back + // down, but keep 2 extra bits of precision + x0 += 512; x1 += 512; x2 += 512; x3 += 512; + v[ 0] = (x0+t3) >> 10; + v[56] = (x0-t3) >> 10; + v[ 8] = (x1+t2) >> 10; + v[48] = (x1-t2) >> 10; + v[16] = (x2+t1) >> 10; + v[40] = (x2-t1) >> 10; + v[24] = (x3+t0) >> 10; + v[32] = (x3-t0) >> 10; + } + } + + for (i=0, v=val, o=out; i < 8; ++i,v+=8,o+=out_stride) { + // no fast case since the first 1D IDCT spread components out + STBI__IDCT_1D(v[0],v[1],v[2],v[3],v[4],v[5],v[6],v[7]) + // constants scaled things up by 1<<12, plus we had 1<<2 from first + // loop, plus horizontal and vertical each scale by sqrt(8) so together + // we've got an extra 1<<3, so 1<<17 total we need to remove. + // so we want to round that, which means adding 0.5 * 1<<17, + // aka 65536. Also, we'll end up with -128 to 127 that we want + // to encode as 0..255 by adding 128, so we'll add that before the shift + x0 += 65536 + (128<<17); + x1 += 65536 + (128<<17); + x2 += 65536 + (128<<17); + x3 += 65536 + (128<<17); + // tried computing the shifts into temps, or'ing the temps to see + // if any were out of range, but that was slower + o[0] = stbi__clamp((x0+t3) >> 17); + o[7] = stbi__clamp((x0-t3) >> 17); + o[1] = stbi__clamp((x1+t2) >> 17); + o[6] = stbi__clamp((x1-t2) >> 17); + o[2] = stbi__clamp((x2+t1) >> 17); + o[5] = stbi__clamp((x2-t1) >> 17); + o[3] = stbi__clamp((x3+t0) >> 17); + o[4] = stbi__clamp((x3-t0) >> 17); + } +} + +#ifdef STBI_SSE2 +// sse2 integer IDCT. not the fastest possible implementation but it +// produces bit-identical results to the generic C version so it's +// fully "transparent". +static void stbi__idct_simd(stbi_uc *out, int out_stride, short data[64]) +{ + // This is constructed to match our regular (generic) integer IDCT exactly. + __m128i row0, row1, row2, row3, row4, row5, row6, row7; + __m128i tmp; + + // dot product constant: even elems=x, odd elems=y + #define dct_const(x,y) _mm_setr_epi16((x),(y),(x),(y),(x),(y),(x),(y)) + + // out(0) = c0[even]*x + c0[odd]*y (c0, x, y 16-bit, out 32-bit) + // out(1) = c1[even]*x + c1[odd]*y + #define dct_rot(out0,out1, x,y,c0,c1) \ + __m128i c0##lo = _mm_unpacklo_epi16((x),(y)); \ + __m128i c0##hi = _mm_unpackhi_epi16((x),(y)); \ + __m128i out0##_l = _mm_madd_epi16(c0##lo, c0); \ + __m128i out0##_h = _mm_madd_epi16(c0##hi, c0); \ + __m128i out1##_l = _mm_madd_epi16(c0##lo, c1); \ + __m128i out1##_h = _mm_madd_epi16(c0##hi, c1) + + // out = in << 12 (in 16-bit, out 32-bit) + #define dct_widen(out, in) \ + __m128i out##_l = _mm_srai_epi32(_mm_unpacklo_epi16(_mm_setzero_si128(), (in)), 4); \ + __m128i out##_h = _mm_srai_epi32(_mm_unpackhi_epi16(_mm_setzero_si128(), (in)), 4) + + // wide add + #define dct_wadd(out, a, b) \ + __m128i out##_l = _mm_add_epi32(a##_l, b##_l); \ + __m128i out##_h = _mm_add_epi32(a##_h, b##_h) + + // wide sub + #define dct_wsub(out, a, b) \ + __m128i out##_l = _mm_sub_epi32(a##_l, b##_l); \ + __m128i out##_h = _mm_sub_epi32(a##_h, b##_h) + + // butterfly a/b, add bias, then shift by "s" and pack + #define dct_bfly32o(out0, out1, a,b,bias,s) \ + { \ + __m128i abiased_l = _mm_add_epi32(a##_l, bias); \ + __m128i abiased_h = _mm_add_epi32(a##_h, bias); \ + dct_wadd(sum, abiased, b); \ + dct_wsub(dif, abiased, b); \ + out0 = _mm_packs_epi32(_mm_srai_epi32(sum_l, s), _mm_srai_epi32(sum_h, s)); \ + out1 = _mm_packs_epi32(_mm_srai_epi32(dif_l, s), _mm_srai_epi32(dif_h, s)); \ + } + + // 8-bit interleave step (for transposes) + #define dct_interleave8(a, b) \ + tmp = a; \ + a = _mm_unpacklo_epi8(a, b); \ + b = _mm_unpackhi_epi8(tmp, b) + + // 16-bit interleave step (for transposes) + #define dct_interleave16(a, b) \ + tmp = a; \ + a = _mm_unpacklo_epi16(a, b); \ + b = _mm_unpackhi_epi16(tmp, b) + + #define dct_pass(bias,shift) \ + { \ + /* even part */ \ + dct_rot(t2e,t3e, row2,row6, rot0_0,rot0_1); \ + __m128i sum04 = _mm_add_epi16(row0, row4); \ + __m128i dif04 = _mm_sub_epi16(row0, row4); \ + dct_widen(t0e, sum04); \ + dct_widen(t1e, dif04); \ + dct_wadd(x0, t0e, t3e); \ + dct_wsub(x3, t0e, t3e); \ + dct_wadd(x1, t1e, t2e); \ + dct_wsub(x2, t1e, t2e); \ + /* odd part */ \ + dct_rot(y0o,y2o, row7,row3, rot2_0,rot2_1); \ + dct_rot(y1o,y3o, row5,row1, rot3_0,rot3_1); \ + __m128i sum17 = _mm_add_epi16(row1, row7); \ + __m128i sum35 = _mm_add_epi16(row3, row5); \ + dct_rot(y4o,y5o, sum17,sum35, rot1_0,rot1_1); \ + dct_wadd(x4, y0o, y4o); \ + dct_wadd(x5, y1o, y5o); \ + dct_wadd(x6, y2o, y5o); \ + dct_wadd(x7, y3o, y4o); \ + dct_bfly32o(row0,row7, x0,x7,bias,shift); \ + dct_bfly32o(row1,row6, x1,x6,bias,shift); \ + dct_bfly32o(row2,row5, x2,x5,bias,shift); \ + dct_bfly32o(row3,row4, x3,x4,bias,shift); \ + } + + __m128i rot0_0 = dct_const(stbi__f2f(0.5411961f), stbi__f2f(0.5411961f) + stbi__f2f(-1.847759065f)); + __m128i rot0_1 = dct_const(stbi__f2f(0.5411961f) + stbi__f2f( 0.765366865f), stbi__f2f(0.5411961f)); + __m128i rot1_0 = dct_const(stbi__f2f(1.175875602f) + stbi__f2f(-0.899976223f), stbi__f2f(1.175875602f)); + __m128i rot1_1 = dct_const(stbi__f2f(1.175875602f), stbi__f2f(1.175875602f) + stbi__f2f(-2.562915447f)); + __m128i rot2_0 = dct_const(stbi__f2f(-1.961570560f) + stbi__f2f( 0.298631336f), stbi__f2f(-1.961570560f)); + __m128i rot2_1 = dct_const(stbi__f2f(-1.961570560f), stbi__f2f(-1.961570560f) + stbi__f2f( 3.072711026f)); + __m128i rot3_0 = dct_const(stbi__f2f(-0.390180644f) + stbi__f2f( 2.053119869f), stbi__f2f(-0.390180644f)); + __m128i rot3_1 = dct_const(stbi__f2f(-0.390180644f), stbi__f2f(-0.390180644f) + stbi__f2f( 1.501321110f)); + + // rounding biases in column/row passes, see stbi__idct_block for explanation. + __m128i bias_0 = _mm_set1_epi32(512); + __m128i bias_1 = _mm_set1_epi32(65536 + (128<<17)); + + // load + row0 = _mm_load_si128((const __m128i *) (data + 0*8)); + row1 = _mm_load_si128((const __m128i *) (data + 1*8)); + row2 = _mm_load_si128((const __m128i *) (data + 2*8)); + row3 = _mm_load_si128((const __m128i *) (data + 3*8)); + row4 = _mm_load_si128((const __m128i *) (data + 4*8)); + row5 = _mm_load_si128((const __m128i *) (data + 5*8)); + row6 = _mm_load_si128((const __m128i *) (data + 6*8)); + row7 = _mm_load_si128((const __m128i *) (data + 7*8)); + + // column pass + dct_pass(bias_0, 10); + + { + // 16bit 8x8 transpose pass 1 + dct_interleave16(row0, row4); + dct_interleave16(row1, row5); + dct_interleave16(row2, row6); + dct_interleave16(row3, row7); + + // transpose pass 2 + dct_interleave16(row0, row2); + dct_interleave16(row1, row3); + dct_interleave16(row4, row6); + dct_interleave16(row5, row7); + + // transpose pass 3 + dct_interleave16(row0, row1); + dct_interleave16(row2, row3); + dct_interleave16(row4, row5); + dct_interleave16(row6, row7); + } + + // row pass + dct_pass(bias_1, 17); + + { + // pack + __m128i p0 = _mm_packus_epi16(row0, row1); // a0a1a2a3...a7b0b1b2b3...b7 + __m128i p1 = _mm_packus_epi16(row2, row3); + __m128i p2 = _mm_packus_epi16(row4, row5); + __m128i p3 = _mm_packus_epi16(row6, row7); + + // 8bit 8x8 transpose pass 1 + dct_interleave8(p0, p2); // a0e0a1e1... + dct_interleave8(p1, p3); // c0g0c1g1... + + // transpose pass 2 + dct_interleave8(p0, p1); // a0c0e0g0... + dct_interleave8(p2, p3); // b0d0f0h0... + + // transpose pass 3 + dct_interleave8(p0, p2); // a0b0c0d0... + dct_interleave8(p1, p3); // a4b4c4d4... + + // store + _mm_storel_epi64((__m128i *) out, p0); out += out_stride; + _mm_storel_epi64((__m128i *) out, _mm_shuffle_epi32(p0, 0x4e)); out += out_stride; + _mm_storel_epi64((__m128i *) out, p2); out += out_stride; + _mm_storel_epi64((__m128i *) out, _mm_shuffle_epi32(p2, 0x4e)); out += out_stride; + _mm_storel_epi64((__m128i *) out, p1); out += out_stride; + _mm_storel_epi64((__m128i *) out, _mm_shuffle_epi32(p1, 0x4e)); out += out_stride; + _mm_storel_epi64((__m128i *) out, p3); out += out_stride; + _mm_storel_epi64((__m128i *) out, _mm_shuffle_epi32(p3, 0x4e)); + } + +#undef dct_const +#undef dct_rot +#undef dct_widen +#undef dct_wadd +#undef dct_wsub +#undef dct_bfly32o +#undef dct_interleave8 +#undef dct_interleave16 +#undef dct_pass +} + +#endif // STBI_SSE2 + +#ifdef STBI_NEON + +// NEON integer IDCT. should produce bit-identical +// results to the generic C version. +static void stbi__idct_simd(stbi_uc *out, int out_stride, short data[64]) +{ + int16x8_t row0, row1, row2, row3, row4, row5, row6, row7; + + int16x4_t rot0_0 = vdup_n_s16(stbi__f2f(0.5411961f)); + int16x4_t rot0_1 = vdup_n_s16(stbi__f2f(-1.847759065f)); + int16x4_t rot0_2 = vdup_n_s16(stbi__f2f( 0.765366865f)); + int16x4_t rot1_0 = vdup_n_s16(stbi__f2f( 1.175875602f)); + int16x4_t rot1_1 = vdup_n_s16(stbi__f2f(-0.899976223f)); + int16x4_t rot1_2 = vdup_n_s16(stbi__f2f(-2.562915447f)); + int16x4_t rot2_0 = vdup_n_s16(stbi__f2f(-1.961570560f)); + int16x4_t rot2_1 = vdup_n_s16(stbi__f2f(-0.390180644f)); + int16x4_t rot3_0 = vdup_n_s16(stbi__f2f( 0.298631336f)); + int16x4_t rot3_1 = vdup_n_s16(stbi__f2f( 2.053119869f)); + int16x4_t rot3_2 = vdup_n_s16(stbi__f2f( 3.072711026f)); + int16x4_t rot3_3 = vdup_n_s16(stbi__f2f( 1.501321110f)); + +#define dct_long_mul(out, inq, coeff) \ + int32x4_t out##_l = vmull_s16(vget_low_s16(inq), coeff); \ + int32x4_t out##_h = vmull_s16(vget_high_s16(inq), coeff) + +#define dct_long_mac(out, acc, inq, coeff) \ + int32x4_t out##_l = vmlal_s16(acc##_l, vget_low_s16(inq), coeff); \ + int32x4_t out##_h = vmlal_s16(acc##_h, vget_high_s16(inq), coeff) + +#define dct_widen(out, inq) \ + int32x4_t out##_l = vshll_n_s16(vget_low_s16(inq), 12); \ + int32x4_t out##_h = vshll_n_s16(vget_high_s16(inq), 12) + +// wide add +#define dct_wadd(out, a, b) \ + int32x4_t out##_l = vaddq_s32(a##_l, b##_l); \ + int32x4_t out##_h = vaddq_s32(a##_h, b##_h) + +// wide sub +#define dct_wsub(out, a, b) \ + int32x4_t out##_l = vsubq_s32(a##_l, b##_l); \ + int32x4_t out##_h = vsubq_s32(a##_h, b##_h) + +// butterfly a/b, then shift using "shiftop" by "s" and pack +#define dct_bfly32o(out0,out1, a,b,shiftop,s) \ + { \ + dct_wadd(sum, a, b); \ + dct_wsub(dif, a, b); \ + out0 = vcombine_s16(shiftop(sum_l, s), shiftop(sum_h, s)); \ + out1 = vcombine_s16(shiftop(dif_l, s), shiftop(dif_h, s)); \ + } + +#define dct_pass(shiftop, shift) \ + { \ + /* even part */ \ + int16x8_t sum26 = vaddq_s16(row2, row6); \ + dct_long_mul(p1e, sum26, rot0_0); \ + dct_long_mac(t2e, p1e, row6, rot0_1); \ + dct_long_mac(t3e, p1e, row2, rot0_2); \ + int16x8_t sum04 = vaddq_s16(row0, row4); \ + int16x8_t dif04 = vsubq_s16(row0, row4); \ + dct_widen(t0e, sum04); \ + dct_widen(t1e, dif04); \ + dct_wadd(x0, t0e, t3e); \ + dct_wsub(x3, t0e, t3e); \ + dct_wadd(x1, t1e, t2e); \ + dct_wsub(x2, t1e, t2e); \ + /* odd part */ \ + int16x8_t sum15 = vaddq_s16(row1, row5); \ + int16x8_t sum17 = vaddq_s16(row1, row7); \ + int16x8_t sum35 = vaddq_s16(row3, row5); \ + int16x8_t sum37 = vaddq_s16(row3, row7); \ + int16x8_t sumodd = vaddq_s16(sum17, sum35); \ + dct_long_mul(p5o, sumodd, rot1_0); \ + dct_long_mac(p1o, p5o, sum17, rot1_1); \ + dct_long_mac(p2o, p5o, sum35, rot1_2); \ + dct_long_mul(p3o, sum37, rot2_0); \ + dct_long_mul(p4o, sum15, rot2_1); \ + dct_wadd(sump13o, p1o, p3o); \ + dct_wadd(sump24o, p2o, p4o); \ + dct_wadd(sump23o, p2o, p3o); \ + dct_wadd(sump14o, p1o, p4o); \ + dct_long_mac(x4, sump13o, row7, rot3_0); \ + dct_long_mac(x5, sump24o, row5, rot3_1); \ + dct_long_mac(x6, sump23o, row3, rot3_2); \ + dct_long_mac(x7, sump14o, row1, rot3_3); \ + dct_bfly32o(row0,row7, x0,x7,shiftop,shift); \ + dct_bfly32o(row1,row6, x1,x6,shiftop,shift); \ + dct_bfly32o(row2,row5, x2,x5,shiftop,shift); \ + dct_bfly32o(row3,row4, x3,x4,shiftop,shift); \ + } + + // load + row0 = vld1q_s16(data + 0*8); + row1 = vld1q_s16(data + 1*8); + row2 = vld1q_s16(data + 2*8); + row3 = vld1q_s16(data + 3*8); + row4 = vld1q_s16(data + 4*8); + row5 = vld1q_s16(data + 5*8); + row6 = vld1q_s16(data + 6*8); + row7 = vld1q_s16(data + 7*8); + + // add DC bias + row0 = vaddq_s16(row0, vsetq_lane_s16(1024, vdupq_n_s16(0), 0)); + + // column pass + dct_pass(vrshrn_n_s32, 10); + + // 16bit 8x8 transpose + { +// these three map to a single VTRN.16, VTRN.32, and VSWP, respectively. +// whether compilers actually get this is another story, sadly. +#define dct_trn16(x, y) { int16x8x2_t t = vtrnq_s16(x, y); x = t.val[0]; y = t.val[1]; } +#define dct_trn32(x, y) { int32x4x2_t t = vtrnq_s32(vreinterpretq_s32_s16(x), vreinterpretq_s32_s16(y)); x = vreinterpretq_s16_s32(t.val[0]); y = vreinterpretq_s16_s32(t.val[1]); } +#define dct_trn64(x, y) { int16x8_t x0 = x; int16x8_t y0 = y; x = vcombine_s16(vget_low_s16(x0), vget_low_s16(y0)); y = vcombine_s16(vget_high_s16(x0), vget_high_s16(y0)); } + + // pass 1 + dct_trn16(row0, row1); // a0b0a2b2a4b4a6b6 + dct_trn16(row2, row3); + dct_trn16(row4, row5); + dct_trn16(row6, row7); + + // pass 2 + dct_trn32(row0, row2); // a0b0c0d0a4b4c4d4 + dct_trn32(row1, row3); + dct_trn32(row4, row6); + dct_trn32(row5, row7); + + // pass 3 + dct_trn64(row0, row4); // a0b0c0d0e0f0g0h0 + dct_trn64(row1, row5); + dct_trn64(row2, row6); + dct_trn64(row3, row7); + +#undef dct_trn16 +#undef dct_trn32 +#undef dct_trn64 + } + + // row pass + // vrshrn_n_s32 only supports shifts up to 16, we need + // 17. so do a non-rounding shift of 16 first then follow + // up with a rounding shift by 1. + dct_pass(vshrn_n_s32, 16); + + { + // pack and round + uint8x8_t p0 = vqrshrun_n_s16(row0, 1); + uint8x8_t p1 = vqrshrun_n_s16(row1, 1); + uint8x8_t p2 = vqrshrun_n_s16(row2, 1); + uint8x8_t p3 = vqrshrun_n_s16(row3, 1); + uint8x8_t p4 = vqrshrun_n_s16(row4, 1); + uint8x8_t p5 = vqrshrun_n_s16(row5, 1); + uint8x8_t p6 = vqrshrun_n_s16(row6, 1); + uint8x8_t p7 = vqrshrun_n_s16(row7, 1); + + // again, these can translate into one instruction, but often don't. +#define dct_trn8_8(x, y) { uint8x8x2_t t = vtrn_u8(x, y); x = t.val[0]; y = t.val[1]; } +#define dct_trn8_16(x, y) { uint16x4x2_t t = vtrn_u16(vreinterpret_u16_u8(x), vreinterpret_u16_u8(y)); x = vreinterpret_u8_u16(t.val[0]); y = vreinterpret_u8_u16(t.val[1]); } +#define dct_trn8_32(x, y) { uint32x2x2_t t = vtrn_u32(vreinterpret_u32_u8(x), vreinterpret_u32_u8(y)); x = vreinterpret_u8_u32(t.val[0]); y = vreinterpret_u8_u32(t.val[1]); } + + // sadly can't use interleaved stores here since we only write + // 8 bytes to each scan line! + + // 8x8 8-bit transpose pass 1 + dct_trn8_8(p0, p1); + dct_trn8_8(p2, p3); + dct_trn8_8(p4, p5); + dct_trn8_8(p6, p7); + + // pass 2 + dct_trn8_16(p0, p2); + dct_trn8_16(p1, p3); + dct_trn8_16(p4, p6); + dct_trn8_16(p5, p7); + + // pass 3 + dct_trn8_32(p0, p4); + dct_trn8_32(p1, p5); + dct_trn8_32(p2, p6); + dct_trn8_32(p3, p7); + + // store + vst1_u8(out, p0); out += out_stride; + vst1_u8(out, p1); out += out_stride; + vst1_u8(out, p2); out += out_stride; + vst1_u8(out, p3); out += out_stride; + vst1_u8(out, p4); out += out_stride; + vst1_u8(out, p5); out += out_stride; + vst1_u8(out, p6); out += out_stride; + vst1_u8(out, p7); + +#undef dct_trn8_8 +#undef dct_trn8_16 +#undef dct_trn8_32 + } + +#undef dct_long_mul +#undef dct_long_mac +#undef dct_widen +#undef dct_wadd +#undef dct_wsub +#undef dct_bfly32o +#undef dct_pass +} + +#endif // STBI_NEON + +#define STBI__MARKER_none 0xff +// if there's a pending marker from the entropy stream, return that +// otherwise, fetch from the stream and get a marker. if there's no +// marker, return 0xff, which is never a valid marker value +static stbi_uc stbi__get_marker(stbi__jpeg *j) +{ + stbi_uc x; + if (j->marker != STBI__MARKER_none) { x = j->marker; j->marker = STBI__MARKER_none; return x; } + x = stbi__get8(j->s); + if (x != 0xff) return STBI__MARKER_none; + while (x == 0xff) + x = stbi__get8(j->s); + return x; +} + +// in each scan, we'll have scan_n components, and the order +// of the components is specified by order[] +#define STBI__RESTART(x) ((x) >= 0xd0 && (x) <= 0xd7) + +// after a restart interval, stbi__jpeg_reset the entropy decoder and +// the dc prediction +static void stbi__jpeg_reset(stbi__jpeg *j) +{ + j->code_bits = 0; + j->code_buffer = 0; + j->nomore = 0; + j->img_comp[0].dc_pred = j->img_comp[1].dc_pred = j->img_comp[2].dc_pred = 0; + j->marker = STBI__MARKER_none; + j->todo = j->restart_interval ? j->restart_interval : 0x7fffffff; + j->eob_run = 0; + // no more than 1<<31 MCUs if no restart_interal? that's plenty safe, + // since we don't even allow 1<<30 pixels +} + +static int stbi__parse_entropy_coded_data(stbi__jpeg *z) +{ + stbi__jpeg_reset(z); + if (!z->progressive) { + if (z->scan_n == 1) { + int i,j; + STBI_SIMD_ALIGN(short, data[64]); + int n = z->order[0]; + // non-interleaved data, we just need to process one block at a time, + // in trivial scanline order + // number of blocks to do just depends on how many actual "pixels" this + // component has, independent of interleaved MCU blocking and such + int w = (z->img_comp[n].x+7) >> 3; + int h = (z->img_comp[n].y+7) >> 3; + for (j=0; j < h; ++j) { + for (i=0; i < w; ++i) { + int ha = z->img_comp[n].ha; + if (!stbi__jpeg_decode_block(z, data, z->huff_dc+z->img_comp[n].hd, z->huff_ac+ha, z->fast_ac[ha], n, z->dequant[z->img_comp[n].tq])) return 0; + z->idct_block_kernel(z->img_comp[n].data+z->img_comp[n].w2*j*8+i*8, z->img_comp[n].w2, data); + // every data block is an MCU, so countdown the restart interval + if (--z->todo <= 0) { + if (z->code_bits < 24) stbi__grow_buffer_unsafe(z); + // if it's NOT a restart, then just bail, so we get corrupt data + // rather than no data + if (!STBI__RESTART(z->marker)) return 1; + stbi__jpeg_reset(z); + } + } + } + return 1; + } else { // interleaved + int i,j,k,x,y; + STBI_SIMD_ALIGN(short, data[64]); + for (j=0; j < z->img_mcu_y; ++j) { + for (i=0; i < z->img_mcu_x; ++i) { + // scan an interleaved mcu... process scan_n components in order + for (k=0; k < z->scan_n; ++k) { + int n = z->order[k]; + // scan out an mcu's worth of this component; that's just determined + // by the basic H and V specified for the component + for (y=0; y < z->img_comp[n].v; ++y) { + for (x=0; x < z->img_comp[n].h; ++x) { + int x2 = (i*z->img_comp[n].h + x)*8; + int y2 = (j*z->img_comp[n].v + y)*8; + int ha = z->img_comp[n].ha; + if (!stbi__jpeg_decode_block(z, data, z->huff_dc+z->img_comp[n].hd, z->huff_ac+ha, z->fast_ac[ha], n, z->dequant[z->img_comp[n].tq])) return 0; + z->idct_block_kernel(z->img_comp[n].data+z->img_comp[n].w2*y2+x2, z->img_comp[n].w2, data); + } + } + } + // after all interleaved components, that's an interleaved MCU, + // so now count down the restart interval + if (--z->todo <= 0) { + if (z->code_bits < 24) stbi__grow_buffer_unsafe(z); + if (!STBI__RESTART(z->marker)) return 1; + stbi__jpeg_reset(z); + } + } + } + return 1; + } + } else { + if (z->scan_n == 1) { + int i,j; + int n = z->order[0]; + // non-interleaved data, we just need to process one block at a time, + // in trivial scanline order + // number of blocks to do just depends on how many actual "pixels" this + // component has, independent of interleaved MCU blocking and such + int w = (z->img_comp[n].x+7) >> 3; + int h = (z->img_comp[n].y+7) >> 3; + for (j=0; j < h; ++j) { + for (i=0; i < w; ++i) { + short *data = z->img_comp[n].coeff + 64 * (i + j * z->img_comp[n].coeff_w); + if (z->spec_start == 0) { + if (!stbi__jpeg_decode_block_prog_dc(z, data, &z->huff_dc[z->img_comp[n].hd], n)) + return 0; + } else { + int ha = z->img_comp[n].ha; + if (!stbi__jpeg_decode_block_prog_ac(z, data, &z->huff_ac[ha], z->fast_ac[ha])) + return 0; + } + // every data block is an MCU, so countdown the restart interval + if (--z->todo <= 0) { + if (z->code_bits < 24) stbi__grow_buffer_unsafe(z); + if (!STBI__RESTART(z->marker)) return 1; + stbi__jpeg_reset(z); + } + } + } + return 1; + } else { // interleaved + int i,j,k,x,y; + for (j=0; j < z->img_mcu_y; ++j) { + for (i=0; i < z->img_mcu_x; ++i) { + // scan an interleaved mcu... process scan_n components in order + for (k=0; k < z->scan_n; ++k) { + int n = z->order[k]; + // scan out an mcu's worth of this component; that's just determined + // by the basic H and V specified for the component + for (y=0; y < z->img_comp[n].v; ++y) { + for (x=0; x < z->img_comp[n].h; ++x) { + int x2 = (i*z->img_comp[n].h + x); + int y2 = (j*z->img_comp[n].v + y); + //int ha = z->img_comp[n].ha; // RaySan: Unused, commented to avoid warning + short *data = z->img_comp[n].coeff + 64 * (x2 + y2 * z->img_comp[n].coeff_w); + if (!stbi__jpeg_decode_block_prog_dc(z, data, &z->huff_dc[z->img_comp[n].hd], n)) + return 0; + } + } + } + // after all interleaved components, that's an interleaved MCU, + // so now count down the restart interval + if (--z->todo <= 0) { + if (z->code_bits < 24) stbi__grow_buffer_unsafe(z); + if (!STBI__RESTART(z->marker)) return 1; + stbi__jpeg_reset(z); + } + } + } + return 1; + } + } +} + +static void stbi__jpeg_dequantize(short *data, stbi_uc *dequant) +{ + int i; + for (i=0; i < 64; ++i) + data[i] *= dequant[i]; +} + +static void stbi__jpeg_finish(stbi__jpeg *z) +{ + if (z->progressive) { + // dequantize and idct the data + int i,j,n; + for (n=0; n < z->s->img_n; ++n) { + int w = (z->img_comp[n].x+7) >> 3; + int h = (z->img_comp[n].y+7) >> 3; + for (j=0; j < h; ++j) { + for (i=0; i < w; ++i) { + short *data = z->img_comp[n].coeff + 64 * (i + j * z->img_comp[n].coeff_w); + stbi__jpeg_dequantize(data, z->dequant[z->img_comp[n].tq]); + z->idct_block_kernel(z->img_comp[n].data+z->img_comp[n].w2*j*8+i*8, z->img_comp[n].w2, data); + } + } + } + } +} + +static int stbi__process_marker(stbi__jpeg *z, int m) +{ + int L; + switch (m) { + case STBI__MARKER_none: // no marker found + return stbi__err("expected marker","Corrupt JPEG"); + + case 0xDD: // DRI - specify restart interval + if (stbi__get16be(z->s) != 4) return stbi__err("bad DRI len","Corrupt JPEG"); + z->restart_interval = stbi__get16be(z->s); + return 1; + + case 0xDB: // DQT - define quantization table + L = stbi__get16be(z->s)-2; + while (L > 0) { + int q = stbi__get8(z->s); + int p = q >> 4; + int t = q & 15,i; + if (p != 0) return stbi__err("bad DQT type","Corrupt JPEG"); + if (t > 3) return stbi__err("bad DQT table","Corrupt JPEG"); + for (i=0; i < 64; ++i) + z->dequant[t][stbi__jpeg_dezigzag[i]] = stbi__get8(z->s); + L -= 65; + } + return L==0; + + case 0xC4: // DHT - define huffman table + L = stbi__get16be(z->s)-2; + while (L > 0) { + stbi_uc *v; + int sizes[16],i,n=0; + int q = stbi__get8(z->s); + int tc = q >> 4; + int th = q & 15; + if (tc > 1 || th > 3) return stbi__err("bad DHT header","Corrupt JPEG"); + for (i=0; i < 16; ++i) { + sizes[i] = stbi__get8(z->s); + n += sizes[i]; + } + L -= 17; + if (tc == 0) { + if (!stbi__build_huffman(z->huff_dc+th, sizes)) return 0; + v = z->huff_dc[th].values; + } else { + if (!stbi__build_huffman(z->huff_ac+th, sizes)) return 0; + v = z->huff_ac[th].values; + } + for (i=0; i < n; ++i) + v[i] = stbi__get8(z->s); + if (tc != 0) + stbi__build_fast_ac(z->fast_ac[th], z->huff_ac + th); + L -= n; + } + return L==0; + } + // check for comment block or APP blocks + if ((m >= 0xE0 && m <= 0xEF) || m == 0xFE) { + stbi__skip(z->s, stbi__get16be(z->s)-2); + return 1; + } + return 0; +} + +// after we see SOS +static int stbi__process_scan_header(stbi__jpeg *z) +{ + int i; + int Ls = stbi__get16be(z->s); + z->scan_n = stbi__get8(z->s); + if (z->scan_n < 1 || z->scan_n > 4 || z->scan_n > (int) z->s->img_n) return stbi__err("bad SOS component count","Corrupt JPEG"); + if (Ls != 6+2*z->scan_n) return stbi__err("bad SOS len","Corrupt JPEG"); + for (i=0; i < z->scan_n; ++i) { + int id = stbi__get8(z->s), which; + int q = stbi__get8(z->s); + for (which = 0; which < z->s->img_n; ++which) + if (z->img_comp[which].id == id) + break; + if (which == z->s->img_n) return 0; // no match + z->img_comp[which].hd = q >> 4; if (z->img_comp[which].hd > 3) return stbi__err("bad DC huff","Corrupt JPEG"); + z->img_comp[which].ha = q & 15; if (z->img_comp[which].ha > 3) return stbi__err("bad AC huff","Corrupt JPEG"); + z->order[i] = which; + } + + { + int aa; + z->spec_start = stbi__get8(z->s); + z->spec_end = stbi__get8(z->s); // should be 63, but might be 0 + aa = stbi__get8(z->s); + z->succ_high = (aa >> 4); + z->succ_low = (aa & 15); + if (z->progressive) { + if (z->spec_start > 63 || z->spec_end > 63 || z->spec_start > z->spec_end || z->succ_high > 13 || z->succ_low > 13) + return stbi__err("bad SOS", "Corrupt JPEG"); + } else { + if (z->spec_start != 0) return stbi__err("bad SOS","Corrupt JPEG"); + if (z->succ_high != 0 || z->succ_low != 0) return stbi__err("bad SOS","Corrupt JPEG"); + z->spec_end = 63; + } + } + + return 1; +} + +static int stbi__process_frame_header(stbi__jpeg *z, int scan) +{ + stbi__context *s = z->s; + int Lf,p,i,q, h_max=1,v_max=1,c; + Lf = stbi__get16be(s); if (Lf < 11) return stbi__err("bad SOF len","Corrupt JPEG"); // JPEG + p = stbi__get8(s); if (p != 8) return stbi__err("only 8-bit","JPEG format not supported: 8-bit only"); // JPEG baseline + s->img_y = stbi__get16be(s); if (s->img_y == 0) return stbi__err("no header height", "JPEG format not supported: delayed height"); // Legal, but we don't handle it--but neither does IJG + s->img_x = stbi__get16be(s); if (s->img_x == 0) return stbi__err("0 width","Corrupt JPEG"); // JPEG requires + c = stbi__get8(s); + if (c != 3 && c != 1) return stbi__err("bad component count","Corrupt JPEG"); // JFIF requires + s->img_n = c; + for (i=0; i < c; ++i) { + z->img_comp[i].data = NULL; + z->img_comp[i].linebuf = NULL; + } + + if (Lf != 8+3*s->img_n) return stbi__err("bad SOF len","Corrupt JPEG"); + + for (i=0; i < s->img_n; ++i) { + z->img_comp[i].id = stbi__get8(s); + if (z->img_comp[i].id != i+1) // JFIF requires + if (z->img_comp[i].id != i) // some version of jpegtran outputs non-JFIF-compliant files! + return stbi__err("bad component ID","Corrupt JPEG"); + q = stbi__get8(s); + z->img_comp[i].h = (q >> 4); if (!z->img_comp[i].h || z->img_comp[i].h > 4) return stbi__err("bad H","Corrupt JPEG"); + z->img_comp[i].v = q & 15; if (!z->img_comp[i].v || z->img_comp[i].v > 4) return stbi__err("bad V","Corrupt JPEG"); + z->img_comp[i].tq = stbi__get8(s); if (z->img_comp[i].tq > 3) return stbi__err("bad TQ","Corrupt JPEG"); + } + + if (scan != STBI__SCAN_load) return 1; + + if ((1 << 30) / s->img_x / s->img_n < s->img_y) return stbi__err("too large", "Image too large to decode"); + + for (i=0; i < s->img_n; ++i) { + if (z->img_comp[i].h > h_max) h_max = z->img_comp[i].h; + if (z->img_comp[i].v > v_max) v_max = z->img_comp[i].v; + } + + // compute interleaved mcu info + z->img_h_max = h_max; + z->img_v_max = v_max; + z->img_mcu_w = h_max * 8; + z->img_mcu_h = v_max * 8; + z->img_mcu_x = (s->img_x + z->img_mcu_w-1) / z->img_mcu_w; + z->img_mcu_y = (s->img_y + z->img_mcu_h-1) / z->img_mcu_h; + + for (i=0; i < s->img_n; ++i) { + // number of effective pixels (e.g. for non-interleaved MCU) + z->img_comp[i].x = (s->img_x * z->img_comp[i].h + h_max-1) / h_max; + z->img_comp[i].y = (s->img_y * z->img_comp[i].v + v_max-1) / v_max; + // to simplify generation, we'll allocate enough memory to decode + // the bogus oversized data from using interleaved MCUs and their + // big blocks (e.g. a 16x16 iMCU on an image of width 33); we won't + // discard the extra data until colorspace conversion + z->img_comp[i].w2 = z->img_mcu_x * z->img_comp[i].h * 8; + z->img_comp[i].h2 = z->img_mcu_y * z->img_comp[i].v * 8; + z->img_comp[i].raw_data = stbi__malloc(z->img_comp[i].w2 * z->img_comp[i].h2+15); + + if (z->img_comp[i].raw_data == NULL) { + for(--i; i >= 0; --i) { + STBI_FREE(z->img_comp[i].raw_data); + z->img_comp[i].data = NULL; + } + return stbi__err("outofmem", "Out of memory"); + } + // align blocks for idct using mmx/sse + z->img_comp[i].data = (stbi_uc*) (((size_t) z->img_comp[i].raw_data + 15) & ~15); + z->img_comp[i].linebuf = NULL; + if (z->progressive) { + z->img_comp[i].coeff_w = (z->img_comp[i].w2 + 7) >> 3; + z->img_comp[i].coeff_h = (z->img_comp[i].h2 + 7) >> 3; + z->img_comp[i].raw_coeff = STBI_MALLOC(z->img_comp[i].coeff_w * z->img_comp[i].coeff_h * 64 * sizeof(short) + 15); + z->img_comp[i].coeff = (short*) (((size_t) z->img_comp[i].raw_coeff + 15) & ~15); + } else { + z->img_comp[i].coeff = 0; + z->img_comp[i].raw_coeff = 0; + } + } + + return 1; +} + +// use comparisons since in some cases we handle more than one case (e.g. SOF) +#define stbi__DNL(x) ((x) == 0xdc) +#define stbi__SOI(x) ((x) == 0xd8) +#define stbi__EOI(x) ((x) == 0xd9) +#define stbi__SOF(x) ((x) == 0xc0 || (x) == 0xc1 || (x) == 0xc2) +#define stbi__SOS(x) ((x) == 0xda) + +#define stbi__SOF_progressive(x) ((x) == 0xc2) + +static int stbi__decode_jpeg_header(stbi__jpeg *z, int scan) +{ + int m; + z->marker = STBI__MARKER_none; // initialize cached marker to empty + m = stbi__get_marker(z); + if (!stbi__SOI(m)) return stbi__err("no SOI","Corrupt JPEG"); + if (scan == STBI__SCAN_type) return 1; + m = stbi__get_marker(z); + while (!stbi__SOF(m)) { + if (!stbi__process_marker(z,m)) return 0; + m = stbi__get_marker(z); + while (m == STBI__MARKER_none) { + // some files have extra padding after their blocks, so ok, we'll scan + if (stbi__at_eof(z->s)) return stbi__err("no SOF", "Corrupt JPEG"); + m = stbi__get_marker(z); + } + } + z->progressive = stbi__SOF_progressive(m); + if (!stbi__process_frame_header(z, scan)) return 0; + return 1; +} + +// decode image to YCbCr format +static int stbi__decode_jpeg_image(stbi__jpeg *j) +{ + int m; + j->restart_interval = 0; + if (!stbi__decode_jpeg_header(j, STBI__SCAN_load)) return 0; + m = stbi__get_marker(j); + while (!stbi__EOI(m)) { + if (stbi__SOS(m)) { + if (!stbi__process_scan_header(j)) return 0; + if (!stbi__parse_entropy_coded_data(j)) return 0; + if (j->marker == STBI__MARKER_none ) { + // handle 0s at the end of image data from IP Kamera 9060 + while (!stbi__at_eof(j->s)) { + int x = stbi__get8(j->s); + if (x == 255) { + j->marker = stbi__get8(j->s); + break; + } else if (x != 0) { + return stbi__err("junk before marker", "Corrupt JPEG"); + } + } + // if we reach eof without hitting a marker, stbi__get_marker() below will fail and we'll eventually return 0 + } + } else { + if (!stbi__process_marker(j, m)) return 0; + } + m = stbi__get_marker(j); + } + if (j->progressive) + stbi__jpeg_finish(j); + return 1; +} + +// static jfif-centered resampling (across block boundaries) + +typedef stbi_uc *(*resample_row_func)(stbi_uc *out, stbi_uc *in0, stbi_uc *in1, + int w, int hs); + +#define stbi__div4(x) ((stbi_uc) ((x) >> 2)) + +static stbi_uc *resample_row_1(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) +{ + STBI_NOTUSED(out); + STBI_NOTUSED(in_far); + STBI_NOTUSED(w); + STBI_NOTUSED(hs); + return in_near; +} + +static stbi_uc* stbi__resample_row_v_2(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) +{ + // need to generate two samples vertically for every one in input + int i; + STBI_NOTUSED(hs); + for (i=0; i < w; ++i) + out[i] = stbi__div4(3*in_near[i] + in_far[i] + 2); + return out; +} + +static stbi_uc* stbi__resample_row_h_2(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) +{ + // need to generate two samples horizontally for every one in input + int i; + stbi_uc *input = in_near; + + if (w == 1) { + // if only one sample, can't do any interpolation + out[0] = out[1] = input[0]; + return out; + } + + out[0] = input[0]; + out[1] = stbi__div4(input[0]*3 + input[1] + 2); + for (i=1; i < w-1; ++i) { + int n = 3*input[i]+2; + out[i*2+0] = stbi__div4(n+input[i-1]); + out[i*2+1] = stbi__div4(n+input[i+1]); + } + out[i*2+0] = stbi__div4(input[w-2]*3 + input[w-1] + 2); + out[i*2+1] = input[w-1]; + + STBI_NOTUSED(in_far); + STBI_NOTUSED(hs); + + return out; +} + +#define stbi__div16(x) ((stbi_uc) ((x) >> 4)) + +static stbi_uc *stbi__resample_row_hv_2(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) +{ + // need to generate 2x2 samples for every one in input + int i,t0,t1; + if (w == 1) { + out[0] = out[1] = stbi__div4(3*in_near[0] + in_far[0] + 2); + return out; + } + + t1 = 3*in_near[0] + in_far[0]; + out[0] = stbi__div4(t1+2); + for (i=1; i < w; ++i) { + t0 = t1; + t1 = 3*in_near[i]+in_far[i]; + out[i*2-1] = stbi__div16(3*t0 + t1 + 8); + out[i*2 ] = stbi__div16(3*t1 + t0 + 8); + } + out[w*2-1] = stbi__div4(t1+2); + + STBI_NOTUSED(hs); + + return out; +} + +#if defined(STBI_SSE2) || defined(STBI_NEON) +static stbi_uc *stbi__resample_row_hv_2_simd(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) +{ + // need to generate 2x2 samples for every one in input + int i=0,t0,t1; + + if (w == 1) { + out[0] = out[1] = stbi__div4(3*in_near[0] + in_far[0] + 2); + return out; + } + + t1 = 3*in_near[0] + in_far[0]; + // process groups of 8 pixels for as long as we can. + // note we can't handle the last pixel in a row in this loop + // because we need to handle the filter boundary conditions. + for (; i < ((w-1) & ~7); i += 8) { +#if defined(STBI_SSE2) + // load and perform the vertical filtering pass + // this uses 3*x + y = 4*x + (y - x) + __m128i zero = _mm_setzero_si128(); + __m128i farb = _mm_loadl_epi64((__m128i *) (in_far + i)); + __m128i nearb = _mm_loadl_epi64((__m128i *) (in_near + i)); + __m128i farw = _mm_unpacklo_epi8(farb, zero); + __m128i nearw = _mm_unpacklo_epi8(nearb, zero); + __m128i diff = _mm_sub_epi16(farw, nearw); + __m128i nears = _mm_slli_epi16(nearw, 2); + __m128i curr = _mm_add_epi16(nears, diff); // current row + + // horizontal filter works the same based on shifted vers of current + // row. "prev" is current row shifted right by 1 pixel; we need to + // insert the previous pixel value (from t1). + // "next" is current row shifted left by 1 pixel, with first pixel + // of next block of 8 pixels added in. + __m128i prv0 = _mm_slli_si128(curr, 2); + __m128i nxt0 = _mm_srli_si128(curr, 2); + __m128i prev = _mm_insert_epi16(prv0, t1, 0); + __m128i next = _mm_insert_epi16(nxt0, 3*in_near[i+8] + in_far[i+8], 7); + + // horizontal filter, polyphase implementation since it's convenient: + // even pixels = 3*cur + prev = cur*4 + (prev - cur) + // odd pixels = 3*cur + next = cur*4 + (next - cur) + // note the shared term. + __m128i bias = _mm_set1_epi16(8); + __m128i curs = _mm_slli_epi16(curr, 2); + __m128i prvd = _mm_sub_epi16(prev, curr); + __m128i nxtd = _mm_sub_epi16(next, curr); + __m128i curb = _mm_add_epi16(curs, bias); + __m128i even = _mm_add_epi16(prvd, curb); + __m128i odd = _mm_add_epi16(nxtd, curb); + + // interleave even and odd pixels, then undo scaling. + __m128i int0 = _mm_unpacklo_epi16(even, odd); + __m128i int1 = _mm_unpackhi_epi16(even, odd); + __m128i de0 = _mm_srli_epi16(int0, 4); + __m128i de1 = _mm_srli_epi16(int1, 4); + + // pack and write output + __m128i outv = _mm_packus_epi16(de0, de1); + _mm_storeu_si128((__m128i *) (out + i*2), outv); +#elif defined(STBI_NEON) + // load and perform the vertical filtering pass + // this uses 3*x + y = 4*x + (y - x) + uint8x8_t farb = vld1_u8(in_far + i); + uint8x8_t nearb = vld1_u8(in_near + i); + int16x8_t diff = vreinterpretq_s16_u16(vsubl_u8(farb, nearb)); + int16x8_t nears = vreinterpretq_s16_u16(vshll_n_u8(nearb, 2)); + int16x8_t curr = vaddq_s16(nears, diff); // current row + + // horizontal filter works the same based on shifted vers of current + // row. "prev" is current row shifted right by 1 pixel; we need to + // insert the previous pixel value (from t1). + // "next" is current row shifted left by 1 pixel, with first pixel + // of next block of 8 pixels added in. + int16x8_t prv0 = vextq_s16(curr, curr, 7); + int16x8_t nxt0 = vextq_s16(curr, curr, 1); + int16x8_t prev = vsetq_lane_s16(t1, prv0, 0); + int16x8_t next = vsetq_lane_s16(3*in_near[i+8] + in_far[i+8], nxt0, 7); + + // horizontal filter, polyphase implementation since it's convenient: + // even pixels = 3*cur + prev = cur*4 + (prev - cur) + // odd pixels = 3*cur + next = cur*4 + (next - cur) + // note the shared term. + int16x8_t curs = vshlq_n_s16(curr, 2); + int16x8_t prvd = vsubq_s16(prev, curr); + int16x8_t nxtd = vsubq_s16(next, curr); + int16x8_t even = vaddq_s16(curs, prvd); + int16x8_t odd = vaddq_s16(curs, nxtd); + + // undo scaling and round, then store with even/odd phases interleaved + uint8x8x2_t o; + o.val[0] = vqrshrun_n_s16(even, 4); + o.val[1] = vqrshrun_n_s16(odd, 4); + vst2_u8(out + i*2, o); +#endif + + // "previous" value for next iter + t1 = 3*in_near[i+7] + in_far[i+7]; + } + + t0 = t1; + t1 = 3*in_near[i] + in_far[i]; + out[i*2] = stbi__div16(3*t1 + t0 + 8); + + for (++i; i < w; ++i) { + t0 = t1; + t1 = 3*in_near[i]+in_far[i]; + out[i*2-1] = stbi__div16(3*t0 + t1 + 8); + out[i*2 ] = stbi__div16(3*t1 + t0 + 8); + } + out[w*2-1] = stbi__div4(t1+2); + + STBI_NOTUSED(hs); + + return out; +} +#endif + +static stbi_uc *stbi__resample_row_generic(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) +{ + // resample with nearest-neighbor + int i,j; + STBI_NOTUSED(in_far); + for (i=0; i < w; ++i) + for (j=0; j < hs; ++j) + out[i*hs+j] = in_near[i]; + return out; +} + +#ifdef STBI_JPEG_OLD +// this is the same YCbCr-to-RGB calculation that stb_image has used +// historically before the algorithm changes in 1.49 +#define float2fixed(x) ((int) ((x) * 65536 + 0.5)) +static void stbi__YCbCr_to_RGB_row(stbi_uc *out, const stbi_uc *y, const stbi_uc *pcb, const stbi_uc *pcr, int count, int step) +{ + int i; + for (i=0; i < count; ++i) { + int y_fixed = (y[i] << 16) + 32768; // rounding + int r,g,b; + int cr = pcr[i] - 128; + int cb = pcb[i] - 128; + r = y_fixed + cr*float2fixed(1.40200f); + g = y_fixed - cr*float2fixed(0.71414f) - cb*float2fixed(0.34414f); + b = y_fixed + cb*float2fixed(1.77200f); + r >>= 16; + g >>= 16; + b >>= 16; + if ((unsigned) r > 255) { if (r < 0) r = 0; else r = 255; } + if ((unsigned) g > 255) { if (g < 0) g = 0; else g = 255; } + if ((unsigned) b > 255) { if (b < 0) b = 0; else b = 255; } + out[0] = (stbi_uc)r; + out[1] = (stbi_uc)g; + out[2] = (stbi_uc)b; + out[3] = 255; + out += step; + } +} +#else +// this is a reduced-precision calculation of YCbCr-to-RGB introduced +// to make sure the code produces the same results in both SIMD and scalar +#define float2fixed(x) (((int) ((x) * 4096.0f + 0.5f)) << 8) +static void stbi__YCbCr_to_RGB_row(stbi_uc *out, const stbi_uc *y, const stbi_uc *pcb, const stbi_uc *pcr, int count, int step) +{ + int i; + for (i=0; i < count; ++i) { + int y_fixed = (y[i] << 20) + (1<<19); // rounding + int r,g,b; + int cr = pcr[i] - 128; + int cb = pcb[i] - 128; + r = y_fixed + cr* float2fixed(1.40200f); + g = y_fixed + (cr*-float2fixed(0.71414f)) + ((cb*-float2fixed(0.34414f)) & 0xffff0000); + b = y_fixed + cb* float2fixed(1.77200f); + r >>= 20; + g >>= 20; + b >>= 20; + if ((unsigned) r > 255) { if (r < 0) r = 0; else r = 255; } + if ((unsigned) g > 255) { if (g < 0) g = 0; else g = 255; } + if ((unsigned) b > 255) { if (b < 0) b = 0; else b = 255; } + out[0] = (stbi_uc)r; + out[1] = (stbi_uc)g; + out[2] = (stbi_uc)b; + out[3] = 255; + out += step; + } +} +#endif + +#if defined(STBI_SSE2) || defined(STBI_NEON) +static void stbi__YCbCr_to_RGB_simd(stbi_uc *out, stbi_uc const *y, stbi_uc const *pcb, stbi_uc const *pcr, int count, int step) +{ + int i = 0; + +#ifdef STBI_SSE2 + // step == 3 is pretty ugly on the final interleave, and i'm not convinced + // it's useful in practice (you wouldn't use it for textures, for example). + // so just accelerate step == 4 case. + if (step == 4) { + // this is a fairly straightforward implementation and not super-optimized. + __m128i signflip = _mm_set1_epi8(-0x80); + __m128i cr_const0 = _mm_set1_epi16( (short) ( 1.40200f*4096.0f+0.5f)); + __m128i cr_const1 = _mm_set1_epi16( - (short) ( 0.71414f*4096.0f+0.5f)); + __m128i cb_const0 = _mm_set1_epi16( - (short) ( 0.34414f*4096.0f+0.5f)); + __m128i cb_const1 = _mm_set1_epi16( (short) ( 1.77200f*4096.0f+0.5f)); + __m128i y_bias = _mm_set1_epi8((char) 128); + __m128i xw = _mm_set1_epi16(255); // alpha channel + + for (; i+7 < count; i += 8) { + // load + __m128i y_bytes = _mm_loadl_epi64((__m128i *) (y+i)); + __m128i cr_bytes = _mm_loadl_epi64((__m128i *) (pcr+i)); + __m128i cb_bytes = _mm_loadl_epi64((__m128i *) (pcb+i)); + __m128i cr_biased = _mm_xor_si128(cr_bytes, signflip); // -128 + __m128i cb_biased = _mm_xor_si128(cb_bytes, signflip); // -128 + + // unpack to short (and left-shift cr, cb by 8) + __m128i yw = _mm_unpacklo_epi8(y_bias, y_bytes); + __m128i crw = _mm_unpacklo_epi8(_mm_setzero_si128(), cr_biased); + __m128i cbw = _mm_unpacklo_epi8(_mm_setzero_si128(), cb_biased); + + // color transform + __m128i yws = _mm_srli_epi16(yw, 4); + __m128i cr0 = _mm_mulhi_epi16(cr_const0, crw); + __m128i cb0 = _mm_mulhi_epi16(cb_const0, cbw); + __m128i cb1 = _mm_mulhi_epi16(cbw, cb_const1); + __m128i cr1 = _mm_mulhi_epi16(crw, cr_const1); + __m128i rws = _mm_add_epi16(cr0, yws); + __m128i gwt = _mm_add_epi16(cb0, yws); + __m128i bws = _mm_add_epi16(yws, cb1); + __m128i gws = _mm_add_epi16(gwt, cr1); + + // descale + __m128i rw = _mm_srai_epi16(rws, 4); + __m128i bw = _mm_srai_epi16(bws, 4); + __m128i gw = _mm_srai_epi16(gws, 4); + + // back to byte, set up for transpose + __m128i brb = _mm_packus_epi16(rw, bw); + __m128i gxb = _mm_packus_epi16(gw, xw); + + // transpose to interleave channels + __m128i t0 = _mm_unpacklo_epi8(brb, gxb); + __m128i t1 = _mm_unpackhi_epi8(brb, gxb); + __m128i o0 = _mm_unpacklo_epi16(t0, t1); + __m128i o1 = _mm_unpackhi_epi16(t0, t1); + + // store + _mm_storeu_si128((__m128i *) (out + 0), o0); + _mm_storeu_si128((__m128i *) (out + 16), o1); + out += 32; + } + } +#endif + +#ifdef STBI_NEON + // in this version, step=3 support would be easy to add. but is there demand? + if (step == 4) { + // this is a fairly straightforward implementation and not super-optimized. + uint8x8_t signflip = vdup_n_u8(0x80); + int16x8_t cr_const0 = vdupq_n_s16( (short) ( 1.40200f*4096.0f+0.5f)); + int16x8_t cr_const1 = vdupq_n_s16( - (short) ( 0.71414f*4096.0f+0.5f)); + int16x8_t cb_const0 = vdupq_n_s16( - (short) ( 0.34414f*4096.0f+0.5f)); + int16x8_t cb_const1 = vdupq_n_s16( (short) ( 1.77200f*4096.0f+0.5f)); + + for (; i+7 < count; i += 8) { + // load + uint8x8_t y_bytes = vld1_u8(y + i); + uint8x8_t cr_bytes = vld1_u8(pcr + i); + uint8x8_t cb_bytes = vld1_u8(pcb + i); + int8x8_t cr_biased = vreinterpret_s8_u8(vsub_u8(cr_bytes, signflip)); + int8x8_t cb_biased = vreinterpret_s8_u8(vsub_u8(cb_bytes, signflip)); + + // expand to s16 + int16x8_t yws = vreinterpretq_s16_u16(vshll_n_u8(y_bytes, 4)); + int16x8_t crw = vshll_n_s8(cr_biased, 7); + int16x8_t cbw = vshll_n_s8(cb_biased, 7); + + // color transform + int16x8_t cr0 = vqdmulhq_s16(crw, cr_const0); + int16x8_t cb0 = vqdmulhq_s16(cbw, cb_const0); + int16x8_t cr1 = vqdmulhq_s16(crw, cr_const1); + int16x8_t cb1 = vqdmulhq_s16(cbw, cb_const1); + int16x8_t rws = vaddq_s16(yws, cr0); + int16x8_t gws = vaddq_s16(vaddq_s16(yws, cb0), cr1); + int16x8_t bws = vaddq_s16(yws, cb1); + + // undo scaling, round, convert to byte + uint8x8x4_t o; + o.val[0] = vqrshrun_n_s16(rws, 4); + o.val[1] = vqrshrun_n_s16(gws, 4); + o.val[2] = vqrshrun_n_s16(bws, 4); + o.val[3] = vdup_n_u8(255); + + // store, interleaving r/g/b/a + vst4_u8(out, o); + out += 8*4; + } + } +#endif + + for (; i < count; ++i) { + int y_fixed = (y[i] << 20) + (1<<19); // rounding + int r,g,b; + int cr = pcr[i] - 128; + int cb = pcb[i] - 128; + r = y_fixed + cr* float2fixed(1.40200f); + g = y_fixed + cr*-float2fixed(0.71414f) + ((cb*-float2fixed(0.34414f)) & 0xffff0000); + b = y_fixed + cb* float2fixed(1.77200f); + r >>= 20; + g >>= 20; + b >>= 20; + if ((unsigned) r > 255) { if (r < 0) r = 0; else r = 255; } + if ((unsigned) g > 255) { if (g < 0) g = 0; else g = 255; } + if ((unsigned) b > 255) { if (b < 0) b = 0; else b = 255; } + out[0] = (stbi_uc)r; + out[1] = (stbi_uc)g; + out[2] = (stbi_uc)b; + out[3] = 255; + out += step; + } +} +#endif + +// set up the kernels +static void stbi__setup_jpeg(stbi__jpeg *j) +{ + j->idct_block_kernel = stbi__idct_block; + j->YCbCr_to_RGB_kernel = stbi__YCbCr_to_RGB_row; + j->resample_row_hv_2_kernel = stbi__resample_row_hv_2; + +#ifdef STBI_SSE2 + if (stbi__sse2_available()) { + j->idct_block_kernel = stbi__idct_simd; + #ifndef STBI_JPEG_OLD + j->YCbCr_to_RGB_kernel = stbi__YCbCr_to_RGB_simd; + #endif + j->resample_row_hv_2_kernel = stbi__resample_row_hv_2_simd; + } +#endif + +#ifdef STBI_NEON + j->idct_block_kernel = stbi__idct_simd; + #ifndef STBI_JPEG_OLD + j->YCbCr_to_RGB_kernel = stbi__YCbCr_to_RGB_simd; + #endif + j->resample_row_hv_2_kernel = stbi__resample_row_hv_2_simd; +#endif +} + +// clean up the temporary component buffers +static void stbi__cleanup_jpeg(stbi__jpeg *j) +{ + int i; + for (i=0; i < j->s->img_n; ++i) { + if (j->img_comp[i].raw_data) { + STBI_FREE(j->img_comp[i].raw_data); + j->img_comp[i].raw_data = NULL; + j->img_comp[i].data = NULL; + } + if (j->img_comp[i].raw_coeff) { + STBI_FREE(j->img_comp[i].raw_coeff); + j->img_comp[i].raw_coeff = 0; + j->img_comp[i].coeff = 0; + } + if (j->img_comp[i].linebuf) { + STBI_FREE(j->img_comp[i].linebuf); + j->img_comp[i].linebuf = NULL; + } + } +} + +typedef struct +{ + resample_row_func resample; + stbi_uc *line0,*line1; + int hs,vs; // expansion factor in each axis + int w_lores; // horizontal pixels pre-expansion + int ystep; // how far through vertical expansion we are + int ypos; // which pre-expansion row we're on +} stbi__resample; + +static stbi_uc *load_jpeg_image(stbi__jpeg *z, int *out_x, int *out_y, int *comp, int req_comp) +{ + int n, decode_n; + z->s->img_n = 0; // make stbi__cleanup_jpeg safe + + // validate req_comp + if (req_comp < 0 || req_comp > 4) return stbi__errpuc("bad req_comp", "Internal error"); + + // load a jpeg image from whichever source, but leave in YCbCr format + if (!stbi__decode_jpeg_image(z)) { stbi__cleanup_jpeg(z); return NULL; } + + // determine actual number of components to generate + n = req_comp ? req_comp : z->s->img_n; + + if (z->s->img_n == 3 && n < 3) + decode_n = 1; + else + decode_n = z->s->img_n; + + // resample and color-convert + { + int k; + unsigned int i,j; + stbi_uc *output; + stbi_uc *coutput[4]; + + stbi__resample res_comp[4]; + + for (k=0; k < decode_n; ++k) { + stbi__resample *r = &res_comp[k]; + + // allocate line buffer big enough for upsampling off the edges + // with upsample factor of 4 + z->img_comp[k].linebuf = (stbi_uc *) stbi__malloc(z->s->img_x + 3); + if (!z->img_comp[k].linebuf) { stbi__cleanup_jpeg(z); return stbi__errpuc("outofmem", "Out of memory"); } + + r->hs = z->img_h_max / z->img_comp[k].h; + r->vs = z->img_v_max / z->img_comp[k].v; + r->ystep = r->vs >> 1; + r->w_lores = (z->s->img_x + r->hs-1) / r->hs; + r->ypos = 0; + r->line0 = r->line1 = z->img_comp[k].data; + + if (r->hs == 1 && r->vs == 1) r->resample = resample_row_1; + else if (r->hs == 1 && r->vs == 2) r->resample = stbi__resample_row_v_2; + else if (r->hs == 2 && r->vs == 1) r->resample = stbi__resample_row_h_2; + else if (r->hs == 2 && r->vs == 2) r->resample = z->resample_row_hv_2_kernel; + else r->resample = stbi__resample_row_generic; + } + + // can't error after this so, this is safe + output = (stbi_uc *) stbi__malloc(n * z->s->img_x * z->s->img_y + 1); + if (!output) { stbi__cleanup_jpeg(z); return stbi__errpuc("outofmem", "Out of memory"); } + + // now go ahead and resample + for (j=0; j < z->s->img_y; ++j) { + stbi_uc *out = output + n * z->s->img_x * j; + for (k=0; k < decode_n; ++k) { + stbi__resample *r = &res_comp[k]; + int y_bot = r->ystep >= (r->vs >> 1); + coutput[k] = r->resample(z->img_comp[k].linebuf, + y_bot ? r->line1 : r->line0, + y_bot ? r->line0 : r->line1, + r->w_lores, r->hs); + if (++r->ystep >= r->vs) { + r->ystep = 0; + r->line0 = r->line1; + if (++r->ypos < z->img_comp[k].y) + r->line1 += z->img_comp[k].w2; + } + } + if (n >= 3) { + stbi_uc *y = coutput[0]; + if (z->s->img_n == 3) { + z->YCbCr_to_RGB_kernel(out, y, coutput[1], coutput[2], z->s->img_x, n); + } else + for (i=0; i < z->s->img_x; ++i) { + out[0] = out[1] = out[2] = y[i]; + out[3] = 255; // not used if n==3 + out += n; + } + } else { + stbi_uc *y = coutput[0]; + if (n == 1) + for (i=0; i < z->s->img_x; ++i) out[i] = y[i]; + else + for (i=0; i < z->s->img_x; ++i) *out++ = y[i], *out++ = 255; + } + } + stbi__cleanup_jpeg(z); + *out_x = z->s->img_x; + *out_y = z->s->img_y; + if (comp) *comp = z->s->img_n; // report original components, not output + return output; + } +} + +static unsigned char *stbi__jpeg_load(stbi__context *s, int *x, int *y, int *comp, int req_comp) +{ + stbi__jpeg j; + j.s = s; + stbi__setup_jpeg(&j); + return load_jpeg_image(&j, x,y,comp,req_comp); +} + +static int stbi__jpeg_test(stbi__context *s) +{ + int r; + stbi__jpeg j; + j.s = s; + stbi__setup_jpeg(&j); + r = stbi__decode_jpeg_header(&j, STBI__SCAN_type); + stbi__rewind(s); + return r; +} + +static int stbi__jpeg_info_raw(stbi__jpeg *j, int *x, int *y, int *comp) +{ + if (!stbi__decode_jpeg_header(j, STBI__SCAN_header)) { + stbi__rewind( j->s ); + return 0; + } + if (x) *x = j->s->img_x; + if (y) *y = j->s->img_y; + if (comp) *comp = j->s->img_n; + return 1; +} + +static int stbi__jpeg_info(stbi__context *s, int *x, int *y, int *comp) +{ + stbi__jpeg j; + j.s = s; + return stbi__jpeg_info_raw(&j, x, y, comp); +} +#endif + +// public domain zlib decode v0.2 Sean Barrett 2006-11-18 +// simple implementation +// - all input must be provided in an upfront buffer +// - all output is written to a single output buffer (can malloc/realloc) +// performance +// - fast huffman + +#ifndef STBI_NO_ZLIB + +// fast-way is faster to check than jpeg huffman, but slow way is slower +#define STBI__ZFAST_BITS 9 // accelerate all cases in default tables +#define STBI__ZFAST_MASK ((1 << STBI__ZFAST_BITS) - 1) + +// zlib-style huffman encoding +// (jpegs packs from left, zlib from right, so can't share code) +typedef struct +{ + stbi__uint16 fast[1 << STBI__ZFAST_BITS]; + stbi__uint16 firstcode[16]; + int maxcode[17]; + stbi__uint16 firstsymbol[16]; + stbi_uc size[288]; + stbi__uint16 value[288]; +} stbi__zhuffman; + +stbi_inline static int stbi__bitreverse16(int n) +{ + n = ((n & 0xAAAA) >> 1) | ((n & 0x5555) << 1); + n = ((n & 0xCCCC) >> 2) | ((n & 0x3333) << 2); + n = ((n & 0xF0F0) >> 4) | ((n & 0x0F0F) << 4); + n = ((n & 0xFF00) >> 8) | ((n & 0x00FF) << 8); + return n; +} + +stbi_inline static int stbi__bit_reverse(int v, int bits) +{ + STBI_ASSERT(bits <= 16); + // to bit reverse n bits, reverse 16 and shift + // e.g. 11 bits, bit reverse and shift away 5 + return stbi__bitreverse16(v) >> (16-bits); +} + +static int stbi__zbuild_huffman(stbi__zhuffman *z, stbi_uc *sizelist, int num) +{ + int i,k=0; + int code, next_code[16], sizes[17]; + + // DEFLATE spec for generating codes + memset(sizes, 0, sizeof(sizes)); + memset(z->fast, 0, sizeof(z->fast)); + for (i=0; i < num; ++i) + ++sizes[sizelist[i]]; + sizes[0] = 0; + for (i=1; i < 16; ++i) + STBI_ASSERT(sizes[i] <= (1 << i)); + code = 0; + for (i=1; i < 16; ++i) { + next_code[i] = code; + z->firstcode[i] = (stbi__uint16) code; + z->firstsymbol[i] = (stbi__uint16) k; + code = (code + sizes[i]); + if (sizes[i]) + if (code-1 >= (1 << i)) return stbi__err("bad codelengths","Corrupt JPEG"); + z->maxcode[i] = code << (16-i); // preshift for inner loop + code <<= 1; + k += sizes[i]; + } + z->maxcode[16] = 0x10000; // sentinel + for (i=0; i < num; ++i) { + int s = sizelist[i]; + if (s) { + int c = next_code[s] - z->firstcode[s] + z->firstsymbol[s]; + stbi__uint16 fastv = (stbi__uint16) ((s << 9) | i); + z->size [c] = (stbi_uc ) s; + z->value[c] = (stbi__uint16) i; + if (s <= STBI__ZFAST_BITS) { + int k = stbi__bit_reverse(next_code[s],s); + while (k < (1 << STBI__ZFAST_BITS)) { + z->fast[k] = fastv; + k += (1 << s); + } + } + ++next_code[s]; + } + } + return 1; +} + +// zlib-from-memory implementation for PNG reading +// because PNG allows splitting the zlib stream arbitrarily, +// and it's annoying structurally to have PNG call ZLIB call PNG, +// we require PNG read all the IDATs and combine them into a single +// memory buffer + +typedef struct +{ + stbi_uc *zbuffer, *zbuffer_end; + int num_bits; + stbi__uint32 code_buffer; + + char *zout; + char *zout_start; + char *zout_end; + int z_expandable; + + stbi__zhuffman z_length, z_distance; +} stbi__zbuf; + +stbi_inline static stbi_uc stbi__zget8(stbi__zbuf *z) +{ + if (z->zbuffer >= z->zbuffer_end) return 0; + return *z->zbuffer++; +} + +static void stbi__fill_bits(stbi__zbuf *z) +{ + do { + STBI_ASSERT(z->code_buffer < (1U << z->num_bits)); + z->code_buffer |= stbi__zget8(z) << z->num_bits; + z->num_bits += 8; + } while (z->num_bits <= 24); +} + +stbi_inline static unsigned int stbi__zreceive(stbi__zbuf *z, int n) +{ + unsigned int k; + if (z->num_bits < n) stbi__fill_bits(z); + k = z->code_buffer & ((1 << n) - 1); + z->code_buffer >>= n; + z->num_bits -= n; + return k; +} + +static int stbi__zhuffman_decode_slowpath(stbi__zbuf *a, stbi__zhuffman *z) +{ + int b,s,k; + // not resolved by fast table, so compute it the slow way + // use jpeg approach, which requires MSbits at top + k = stbi__bit_reverse(a->code_buffer, 16); + for (s=STBI__ZFAST_BITS+1; ; ++s) + if (k < z->maxcode[s]) + break; + if (s == 16) return -1; // invalid code! + // code size is s, so: + b = (k >> (16-s)) - z->firstcode[s] + z->firstsymbol[s]; + STBI_ASSERT(z->size[b] == s); + a->code_buffer >>= s; + a->num_bits -= s; + return z->value[b]; +} + +stbi_inline static int stbi__zhuffman_decode(stbi__zbuf *a, stbi__zhuffman *z) +{ + int b,s; + if (a->num_bits < 16) stbi__fill_bits(a); + b = z->fast[a->code_buffer & STBI__ZFAST_MASK]; + if (b) { + s = b >> 9; + a->code_buffer >>= s; + a->num_bits -= s; + return b & 511; + } + return stbi__zhuffman_decode_slowpath(a, z); +} + +static int stbi__zexpand(stbi__zbuf *z, char *zout, int n) // need to make room for n bytes +{ + char *q; + int cur, limit; + z->zout = zout; + if (!z->z_expandable) return stbi__err("output buffer limit","Corrupt PNG"); + cur = (int) (z->zout - z->zout_start); + limit = (int) (z->zout_end - z->zout_start); + while (cur + n > limit) + limit *= 2; + q = (char *) STBI_REALLOC(z->zout_start, limit); + if (q == NULL) return stbi__err("outofmem", "Out of memory"); + z->zout_start = q; + z->zout = q + cur; + z->zout_end = q + limit; + return 1; +} + +static int stbi__zlength_base[31] = { + 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,0,0 }; + +static int stbi__zlength_extra[31]= +{ 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,0,0 }; + +static int stbi__zdist_base[32] = { 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,0,0}; + +static int stbi__zdist_extra[32] = +{ 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}; + +static int stbi__parse_huffman_block(stbi__zbuf *a) +{ + char *zout = a->zout; + for(;;) { + int z = stbi__zhuffman_decode(a, &a->z_length); + if (z < 256) { + if (z < 0) return stbi__err("bad huffman code","Corrupt PNG"); // error in huffman codes + if (zout >= a->zout_end) { + if (!stbi__zexpand(a, zout, 1)) return 0; + zout = a->zout; + } + *zout++ = (char) z; + } else { + stbi_uc *p; + int len,dist; + if (z == 256) { + a->zout = zout; + return 1; + } + z -= 257; + len = stbi__zlength_base[z]; + if (stbi__zlength_extra[z]) len += stbi__zreceive(a, stbi__zlength_extra[z]); + z = stbi__zhuffman_decode(a, &a->z_distance); + if (z < 0) return stbi__err("bad huffman code","Corrupt PNG"); + dist = stbi__zdist_base[z]; + if (stbi__zdist_extra[z]) dist += stbi__zreceive(a, stbi__zdist_extra[z]); + if (zout - a->zout_start < dist) return stbi__err("bad dist","Corrupt PNG"); + if (zout + len > a->zout_end) { + if (!stbi__zexpand(a, zout, len)) return 0; + zout = a->zout; + } + p = (stbi_uc *) (zout - dist); + if (dist == 1) { // run of one byte; common in images. + stbi_uc v = *p; + do *zout++ = v; while (--len); + } else { + do *zout++ = *p++; while (--len); + } + } + } +} + +static int stbi__compute_huffman_codes(stbi__zbuf *a) +{ + static stbi_uc length_dezigzag[19] = { 16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15 }; + stbi__zhuffman z_codelength; + stbi_uc lencodes[286+32+137];//padding for maximum single op + stbi_uc codelength_sizes[19]; + int i,n; + + int hlit = stbi__zreceive(a,5) + 257; + int hdist = stbi__zreceive(a,5) + 1; + int hclen = stbi__zreceive(a,4) + 4; + + memset(codelength_sizes, 0, sizeof(codelength_sizes)); + for (i=0; i < hclen; ++i) { + int s = stbi__zreceive(a,3); + codelength_sizes[length_dezigzag[i]] = (stbi_uc) s; + } + if (!stbi__zbuild_huffman(&z_codelength, codelength_sizes, 19)) return 0; + + n = 0; + while (n < hlit + hdist) { + int c = stbi__zhuffman_decode(a, &z_codelength); + STBI_ASSERT(c >= 0 && c < 19); + if (c < 16) + lencodes[n++] = (stbi_uc) c; + else if (c == 16) { + c = stbi__zreceive(a,2)+3; + memset(lencodes+n, lencodes[n-1], c); + n += c; + } else if (c == 17) { + c = stbi__zreceive(a,3)+3; + memset(lencodes+n, 0, c); + n += c; + } else { + STBI_ASSERT(c == 18); + c = stbi__zreceive(a,7)+11; + memset(lencodes+n, 0, c); + n += c; + } + } + if (n != hlit+hdist) return stbi__err("bad codelengths","Corrupt PNG"); + if (!stbi__zbuild_huffman(&a->z_length, lencodes, hlit)) return 0; + if (!stbi__zbuild_huffman(&a->z_distance, lencodes+hlit, hdist)) return 0; + return 1; +} + +static int stbi__parse_uncomperssed_block(stbi__zbuf *a) +{ + stbi_uc header[4]; + int len,nlen,k; + if (a->num_bits & 7) + stbi__zreceive(a, a->num_bits & 7); // discard + // drain the bit-packed data into header + k = 0; + while (a->num_bits > 0) { + header[k++] = (stbi_uc) (a->code_buffer & 255); // suppress MSVC run-time check + a->code_buffer >>= 8; + a->num_bits -= 8; + } + STBI_ASSERT(a->num_bits == 0); + // now fill header the normal way + while (k < 4) + header[k++] = stbi__zget8(a); + len = header[1] * 256 + header[0]; + nlen = header[3] * 256 + header[2]; + if (nlen != (len ^ 0xffff)) return stbi__err("zlib corrupt","Corrupt PNG"); + if (a->zbuffer + len > a->zbuffer_end) return stbi__err("read past buffer","Corrupt PNG"); + if (a->zout + len > a->zout_end) + if (!stbi__zexpand(a, a->zout, len)) return 0; + memcpy(a->zout, a->zbuffer, len); + a->zbuffer += len; + a->zout += len; + return 1; +} + +static int stbi__parse_zlib_header(stbi__zbuf *a) +{ + int cmf = stbi__zget8(a); + int cm = cmf & 15; + /* int cinfo = cmf >> 4; */ + int flg = stbi__zget8(a); + if ((cmf*256+flg) % 31 != 0) return stbi__err("bad zlib header","Corrupt PNG"); // zlib spec + if (flg & 32) return stbi__err("no preset dict","Corrupt PNG"); // preset dictionary not allowed in png + if (cm != 8) return stbi__err("bad compression","Corrupt PNG"); // DEFLATE required for png + // window = 1 << (8 + cinfo)... but who cares, we fully buffer output + return 1; +} + +// @TODO: should statically initialize these for optimal thread safety +static stbi_uc stbi__zdefault_length[288], stbi__zdefault_distance[32]; +static void stbi__init_zdefaults(void) +{ + int i; // use <= to match clearly with spec + for (i=0; i <= 143; ++i) stbi__zdefault_length[i] = 8; + for ( ; i <= 255; ++i) stbi__zdefault_length[i] = 9; + for ( ; i <= 279; ++i) stbi__zdefault_length[i] = 7; + for ( ; i <= 287; ++i) stbi__zdefault_length[i] = 8; + + for (i=0; i <= 31; ++i) stbi__zdefault_distance[i] = 5; +} + +static int stbi__parse_zlib(stbi__zbuf *a, int parse_header) +{ + int final, type; + if (parse_header) + if (!stbi__parse_zlib_header(a)) return 0; + a->num_bits = 0; + a->code_buffer = 0; + do { + final = stbi__zreceive(a,1); + type = stbi__zreceive(a,2); + if (type == 0) { + if (!stbi__parse_uncomperssed_block(a)) return 0; + } else if (type == 3) { + return 0; + } else { + if (type == 1) { + // use fixed code lengths + if (!stbi__zdefault_distance[31]) stbi__init_zdefaults(); + if (!stbi__zbuild_huffman(&a->z_length , stbi__zdefault_length , 288)) return 0; + if (!stbi__zbuild_huffman(&a->z_distance, stbi__zdefault_distance, 32)) return 0; + } else { + if (!stbi__compute_huffman_codes(a)) return 0; + } + if (!stbi__parse_huffman_block(a)) return 0; + } + } while (!final); + return 1; +} + +static int stbi__do_zlib(stbi__zbuf *a, char *obuf, int olen, int exp, int parse_header) +{ + a->zout_start = obuf; + a->zout = obuf; + a->zout_end = obuf + olen; + a->z_expandable = exp; + + return stbi__parse_zlib(a, parse_header); +} + +STBIDEF char *stbi_zlib_decode_malloc_guesssize(const char *buffer, int len, int initial_size, int *outlen) +{ + stbi__zbuf a; + char *p = (char *) stbi__malloc(initial_size); + if (p == NULL) return NULL; + a.zbuffer = (stbi_uc *) buffer; + a.zbuffer_end = (stbi_uc *) buffer + len; + if (stbi__do_zlib(&a, p, initial_size, 1, 1)) { + if (outlen) *outlen = (int) (a.zout - a.zout_start); + return a.zout_start; + } else { + STBI_FREE(a.zout_start); + return NULL; + } +} + +STBIDEF char *stbi_zlib_decode_malloc(char const *buffer, int len, int *outlen) +{ + return stbi_zlib_decode_malloc_guesssize(buffer, len, 16384, outlen); +} + +STBIDEF char *stbi_zlib_decode_malloc_guesssize_headerflag(const char *buffer, int len, int initial_size, int *outlen, int parse_header) +{ + stbi__zbuf a; + char *p = (char *) stbi__malloc(initial_size); + if (p == NULL) return NULL; + a.zbuffer = (stbi_uc *) buffer; + a.zbuffer_end = (stbi_uc *) buffer + len; + if (stbi__do_zlib(&a, p, initial_size, 1, parse_header)) { + if (outlen) *outlen = (int) (a.zout - a.zout_start); + return a.zout_start; + } else { + STBI_FREE(a.zout_start); + return NULL; + } +} + +STBIDEF int stbi_zlib_decode_buffer(char *obuffer, int olen, char const *ibuffer, int ilen) +{ + stbi__zbuf a; + a.zbuffer = (stbi_uc *) ibuffer; + a.zbuffer_end = (stbi_uc *) ibuffer + ilen; + if (stbi__do_zlib(&a, obuffer, olen, 0, 1)) + return (int) (a.zout - a.zout_start); + else + return -1; +} + +STBIDEF char *stbi_zlib_decode_noheader_malloc(char const *buffer, int len, int *outlen) +{ + stbi__zbuf a; + char *p = (char *) stbi__malloc(16384); + if (p == NULL) return NULL; + a.zbuffer = (stbi_uc *) buffer; + a.zbuffer_end = (stbi_uc *) buffer+len; + if (stbi__do_zlib(&a, p, 16384, 1, 0)) { + if (outlen) *outlen = (int) (a.zout - a.zout_start); + return a.zout_start; + } else { + STBI_FREE(a.zout_start); + return NULL; + } +} + +STBIDEF int stbi_zlib_decode_noheader_buffer(char *obuffer, int olen, const char *ibuffer, int ilen) +{ + stbi__zbuf a; + a.zbuffer = (stbi_uc *) ibuffer; + a.zbuffer_end = (stbi_uc *) ibuffer + ilen; + if (stbi__do_zlib(&a, obuffer, olen, 0, 0)) + return (int) (a.zout - a.zout_start); + else + return -1; +} +#endif + +// public domain "baseline" PNG decoder v0.10 Sean Barrett 2006-11-18 +// simple implementation +// - only 8-bit samples +// - no CRC checking +// - allocates lots of intermediate memory +// - avoids problem of streaming data between subsystems +// - avoids explicit window management +// performance +// - uses stb_zlib, a PD zlib implementation with fast huffman decoding + +#ifndef STBI_NO_PNG +typedef struct +{ + stbi__uint32 length; + stbi__uint32 type; +} stbi__pngchunk; + +static stbi__pngchunk stbi__get_chunk_header(stbi__context *s) +{ + stbi__pngchunk c; + c.length = stbi__get32be(s); + c.type = stbi__get32be(s); + return c; +} + +static int stbi__check_png_header(stbi__context *s) +{ + static stbi_uc png_sig[8] = { 137,80,78,71,13,10,26,10 }; + int i; + for (i=0; i < 8; ++i) + if (stbi__get8(s) != png_sig[i]) return stbi__err("bad png sig","Not a PNG"); + return 1; +} + +typedef struct +{ + stbi__context *s; + stbi_uc *idata, *expanded, *out; +} stbi__png; + + +enum { + STBI__F_none=0, + STBI__F_sub=1, + STBI__F_up=2, + STBI__F_avg=3, + STBI__F_paeth=4, + // synthetic filters used for first scanline to avoid needing a dummy row of 0s + STBI__F_avg_first, + STBI__F_paeth_first +}; + +static stbi_uc first_row_filter[5] = +{ + STBI__F_none, + STBI__F_sub, + STBI__F_none, + STBI__F_avg_first, + STBI__F_paeth_first +}; + +static int stbi__paeth(int a, int b, int c) +{ + int p = a + b - c; + int pa = abs(p-a); + int pb = abs(p-b); + int pc = abs(p-c); + if (pa <= pb && pa <= pc) return a; + if (pb <= pc) return b; + return c; +} + +static stbi_uc stbi__depth_scale_table[9] = { 0, 0xff, 0x55, 0, 0x11, 0,0,0, 0x01 }; + +// create the png data from post-deflated data +static int stbi__create_png_image_raw(stbi__png *a, stbi_uc *raw, stbi__uint32 raw_len, int out_n, stbi__uint32 x, stbi__uint32 y, int depth, int color) +{ + stbi__context *s = a->s; + stbi__uint32 i,j,stride = x*out_n; + stbi__uint32 img_len, img_width_bytes; + int k; + int img_n = s->img_n; // copy it into a local for later + + STBI_ASSERT(out_n == s->img_n || out_n == s->img_n+1); + a->out = (stbi_uc *) stbi__malloc(x * y * out_n); // extra bytes to write off the end into + if (!a->out) return stbi__err("outofmem", "Out of memory"); + + img_width_bytes = (((img_n * x * depth) + 7) >> 3); + img_len = (img_width_bytes + 1) * y; + if (s->img_x == x && s->img_y == y) { + if (raw_len != img_len) return stbi__err("not enough pixels","Corrupt PNG"); + } else { // interlaced: + if (raw_len < img_len) return stbi__err("not enough pixels","Corrupt PNG"); + } + + for (j=0; j < y; ++j) { + stbi_uc *cur = a->out + stride*j; + stbi_uc *prior = cur - stride; + int filter = *raw++; + int filter_bytes = img_n; + int width = x; + if (filter > 4) + return stbi__err("invalid filter","Corrupt PNG"); + + if (depth < 8) { + STBI_ASSERT(img_width_bytes <= x); + cur += x*out_n - img_width_bytes; // store output to the rightmost img_len bytes, so we can decode in place + filter_bytes = 1; + width = img_width_bytes; + } + + // if first row, use special filter that doesn't sample previous row + if (j == 0) filter = first_row_filter[filter]; + + // handle first byte explicitly + for (k=0; k < filter_bytes; ++k) { + switch (filter) { + case STBI__F_none : cur[k] = raw[k]; break; + case STBI__F_sub : cur[k] = raw[k]; break; + case STBI__F_up : cur[k] = STBI__BYTECAST(raw[k] + prior[k]); break; + case STBI__F_avg : cur[k] = STBI__BYTECAST(raw[k] + (prior[k]>>1)); break; + case STBI__F_paeth : cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(0,prior[k],0)); break; + case STBI__F_avg_first : cur[k] = raw[k]; break; + case STBI__F_paeth_first: cur[k] = raw[k]; break; + } + } + + if (depth == 8) { + if (img_n != out_n) + cur[img_n] = 255; // first pixel + raw += img_n; + cur += out_n; + prior += out_n; + } else { + raw += 1; + cur += 1; + prior += 1; + } + + // this is a little gross, so that we don't switch per-pixel or per-component + if (depth < 8 || img_n == out_n) { + int nk = (width - 1)*img_n; + #define CASE(f) \ + case f: \ + for (k=0; k < nk; ++k) + switch (filter) { + // "none" filter turns into a memcpy here; make that explicit. + case STBI__F_none: memcpy(cur, raw, nk); break; + CASE(STBI__F_sub) cur[k] = STBI__BYTECAST(raw[k] + cur[k-filter_bytes]); break; + CASE(STBI__F_up) cur[k] = STBI__BYTECAST(raw[k] + prior[k]); break; + CASE(STBI__F_avg) cur[k] = STBI__BYTECAST(raw[k] + ((prior[k] + cur[k-filter_bytes])>>1)); break; + CASE(STBI__F_paeth) cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k-filter_bytes],prior[k],prior[k-filter_bytes])); break; + CASE(STBI__F_avg_first) cur[k] = STBI__BYTECAST(raw[k] + (cur[k-filter_bytes] >> 1)); break; + CASE(STBI__F_paeth_first) cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k-filter_bytes],0,0)); break; + } + #undef CASE + raw += nk; + } else { + STBI_ASSERT(img_n+1 == out_n); + #define CASE(f) \ + case f: \ + for (i=x-1; i >= 1; --i, cur[img_n]=255,raw+=img_n,cur+=out_n,prior+=out_n) \ + for (k=0; k < img_n; ++k) + switch (filter) { + CASE(STBI__F_none) cur[k] = raw[k]; break; + CASE(STBI__F_sub) cur[k] = STBI__BYTECAST(raw[k] + cur[k-out_n]); break; + CASE(STBI__F_up) cur[k] = STBI__BYTECAST(raw[k] + prior[k]); break; + CASE(STBI__F_avg) cur[k] = STBI__BYTECAST(raw[k] + ((prior[k] + cur[k-out_n])>>1)); break; + CASE(STBI__F_paeth) cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k-out_n],prior[k],prior[k-out_n])); break; + CASE(STBI__F_avg_first) cur[k] = STBI__BYTECAST(raw[k] + (cur[k-out_n] >> 1)); break; + CASE(STBI__F_paeth_first) cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k-out_n],0,0)); break; + } + #undef CASE + } + } + + // we make a separate pass to expand bits to pixels; for performance, + // this could run two scanlines behind the above code, so it won't + // intefere with filtering but will still be in the cache. + if (depth < 8) { + for (j=0; j < y; ++j) { + stbi_uc *cur = a->out + stride*j; + stbi_uc *in = a->out + stride*j + x*out_n - img_width_bytes; + // unpack 1/2/4-bit into a 8-bit buffer. allows us to keep the common 8-bit path optimal at minimal cost for 1/2/4-bit + // png guarante byte alignment, if width is not multiple of 8/4/2 we'll decode dummy trailing data that will be skipped in the later loop + stbi_uc scale = (color == 0) ? stbi__depth_scale_table[depth] : 1; // scale grayscale values to 0..255 range + + // note that the final byte might overshoot and write more data than desired. + // we can allocate enough data that this never writes out of memory, but it + // could also overwrite the next scanline. can it overwrite non-empty data + // on the next scanline? yes, consider 1-pixel-wide scanlines with 1-bit-per-pixel. + // so we need to explicitly clamp the final ones + + if (depth == 4) { + for (k=x*img_n; k >= 2; k-=2, ++in) { + *cur++ = scale * ((*in >> 4) ); + *cur++ = scale * ((*in ) & 0x0f); + } + if (k > 0) *cur++ = scale * ((*in >> 4) ); + } else if (depth == 2) { + for (k=x*img_n; k >= 4; k-=4, ++in) { + *cur++ = scale * ((*in >> 6) ); + *cur++ = scale * ((*in >> 4) & 0x03); + *cur++ = scale * ((*in >> 2) & 0x03); + *cur++ = scale * ((*in ) & 0x03); + } + if (k > 0) *cur++ = scale * ((*in >> 6) ); + if (k > 1) *cur++ = scale * ((*in >> 4) & 0x03); + if (k > 2) *cur++ = scale * ((*in >> 2) & 0x03); + } else if (depth == 1) { + for (k=x*img_n; k >= 8; k-=8, ++in) { + *cur++ = scale * ((*in >> 7) ); + *cur++ = scale * ((*in >> 6) & 0x01); + *cur++ = scale * ((*in >> 5) & 0x01); + *cur++ = scale * ((*in >> 4) & 0x01); + *cur++ = scale * ((*in >> 3) & 0x01); + *cur++ = scale * ((*in >> 2) & 0x01); + *cur++ = scale * ((*in >> 1) & 0x01); + *cur++ = scale * ((*in ) & 0x01); + } + if (k > 0) *cur++ = scale * ((*in >> 7) ); + if (k > 1) *cur++ = scale * ((*in >> 6) & 0x01); + if (k > 2) *cur++ = scale * ((*in >> 5) & 0x01); + if (k > 3) *cur++ = scale * ((*in >> 4) & 0x01); + if (k > 4) *cur++ = scale * ((*in >> 3) & 0x01); + if (k > 5) *cur++ = scale * ((*in >> 2) & 0x01); + if (k > 6) *cur++ = scale * ((*in >> 1) & 0x01); + } + if (img_n != out_n) { + // insert alpha = 255 + stbi_uc *cur = a->out + stride*j; + int i; + if (img_n == 1) { + for (i=x-1; i >= 0; --i) { + cur[i*2+1] = 255; + cur[i*2+0] = cur[i]; + } + } else { + assert(img_n == 3); + for (i=x-1; i >= 0; --i) { + cur[i*4+3] = 255; + cur[i*4+2] = cur[i*3+2]; + cur[i*4+1] = cur[i*3+1]; + cur[i*4+0] = cur[i*3+0]; + } + } + } + } + } + + return 1; +} + +static int stbi__create_png_image(stbi__png *a, stbi_uc *image_data, stbi__uint32 image_data_len, int out_n, int depth, int color, int interlaced) +{ + stbi_uc *final; + int p; + if (!interlaced) + return stbi__create_png_image_raw(a, image_data, image_data_len, out_n, a->s->img_x, a->s->img_y, depth, color); + + // de-interlacing + final = (stbi_uc *) stbi__malloc(a->s->img_x * a->s->img_y * out_n); + for (p=0; p < 7; ++p) { + int xorig[] = { 0,4,0,2,0,1,0 }; + int yorig[] = { 0,0,4,0,2,0,1 }; + int xspc[] = { 8,8,4,4,2,2,1 }; + int yspc[] = { 8,8,8,4,4,2,2 }; + int i,j,x,y; + // pass1_x[4] = 0, pass1_x[5] = 1, pass1_x[12] = 1 + x = (a->s->img_x - xorig[p] + xspc[p]-1) / xspc[p]; + y = (a->s->img_y - yorig[p] + yspc[p]-1) / yspc[p]; + if (x && y) { + stbi__uint32 img_len = ((((a->s->img_n * x * depth) + 7) >> 3) + 1) * y; + if (!stbi__create_png_image_raw(a, image_data, image_data_len, out_n, x, y, depth, color)) { + STBI_FREE(final); + return 0; + } + for (j=0; j < y; ++j) { + for (i=0; i < x; ++i) { + int out_y = j*yspc[p]+yorig[p]; + int out_x = i*xspc[p]+xorig[p]; + memcpy(final + out_y*a->s->img_x*out_n + out_x*out_n, + a->out + (j*x+i)*out_n, out_n); + } + } + STBI_FREE(a->out); + image_data += img_len; + image_data_len -= img_len; + } + } + a->out = final; + + return 1; +} + +static int stbi__compute_transparency(stbi__png *z, stbi_uc tc[3], int out_n) +{ + stbi__context *s = z->s; + stbi__uint32 i, pixel_count = s->img_x * s->img_y; + stbi_uc *p = z->out; + + // compute color-based transparency, assuming we've + // already got 255 as the alpha value in the output + STBI_ASSERT(out_n == 2 || out_n == 4); + + if (out_n == 2) { + for (i=0; i < pixel_count; ++i) { + p[1] = (p[0] == tc[0] ? 0 : 255); + p += 2; + } + } else { + for (i=0; i < pixel_count; ++i) { + if (p[0] == tc[0] && p[1] == tc[1] && p[2] == tc[2]) + p[3] = 0; + p += 4; + } + } + return 1; +} + +static int stbi__expand_png_palette(stbi__png *a, stbi_uc *palette, int len, int pal_img_n) +{ + stbi__uint32 i, pixel_count = a->s->img_x * a->s->img_y; + stbi_uc *p, *temp_out, *orig = a->out; + + p = (stbi_uc *) stbi__malloc(pixel_count * pal_img_n); + if (p == NULL) return stbi__err("outofmem", "Out of memory"); + + // between here and free(out) below, exitting would leak + temp_out = p; + + if (pal_img_n == 3) { + for (i=0; i < pixel_count; ++i) { + int n = orig[i]*4; + p[0] = palette[n ]; + p[1] = palette[n+1]; + p[2] = palette[n+2]; + p += 3; + } + } else { + for (i=0; i < pixel_count; ++i) { + int n = orig[i]*4; + p[0] = palette[n ]; + p[1] = palette[n+1]; + p[2] = palette[n+2]; + p[3] = palette[n+3]; + p += 4; + } + } + STBI_FREE(a->out); + a->out = temp_out; + + STBI_NOTUSED(len); + + return 1; +} + +static int stbi__unpremultiply_on_load = 0; +static int stbi__de_iphone_flag = 0; + +STBIDEF void stbi_set_unpremultiply_on_load(int flag_true_if_should_unpremultiply) +{ + stbi__unpremultiply_on_load = flag_true_if_should_unpremultiply; +} + +STBIDEF void stbi_convert_iphone_png_to_rgb(int flag_true_if_should_convert) +{ + stbi__de_iphone_flag = flag_true_if_should_convert; +} + +static void stbi__de_iphone(stbi__png *z) +{ + stbi__context *s = z->s; + stbi__uint32 i, pixel_count = s->img_x * s->img_y; + stbi_uc *p = z->out; + + if (s->img_out_n == 3) { // convert bgr to rgb + for (i=0; i < pixel_count; ++i) { + stbi_uc t = p[0]; + p[0] = p[2]; + p[2] = t; + p += 3; + } + } else { + STBI_ASSERT(s->img_out_n == 4); + if (stbi__unpremultiply_on_load) { + // convert bgr to rgb and unpremultiply + for (i=0; i < pixel_count; ++i) { + stbi_uc a = p[3]; + stbi_uc t = p[0]; + if (a) { + p[0] = p[2] * 255 / a; + p[1] = p[1] * 255 / a; + p[2] = t * 255 / a; + } else { + p[0] = p[2]; + p[2] = t; + } + p += 4; + } + } else { + // convert bgr to rgb + for (i=0; i < pixel_count; ++i) { + stbi_uc t = p[0]; + p[0] = p[2]; + p[2] = t; + p += 4; + } + } + } +} + +#define STBI__PNG_TYPE(a,b,c,d) (((a) << 24) + ((b) << 16) + ((c) << 8) + (d)) + +static int stbi__parse_png_file(stbi__png *z, int scan, int req_comp) +{ + stbi_uc palette[1024], pal_img_n=0; + stbi_uc has_trans=0, tc[3]; + stbi__uint32 ioff=0, idata_limit=0, i, pal_len=0; + int first=1,k,interlace=0, color=0, depth=0, is_iphone=0; + stbi__context *s = z->s; + + z->expanded = NULL; + z->idata = NULL; + z->out = NULL; + + if (!stbi__check_png_header(s)) return 0; + + if (scan == STBI__SCAN_type) return 1; + + for (;;) { + stbi__pngchunk c = stbi__get_chunk_header(s); + switch (c.type) { + case STBI__PNG_TYPE('C','g','B','I'): + is_iphone = 1; + stbi__skip(s, c.length); + break; + case STBI__PNG_TYPE('I','H','D','R'): { + int comp,filter; + if (!first) return stbi__err("multiple IHDR","Corrupt PNG"); + first = 0; + if (c.length != 13) return stbi__err("bad IHDR len","Corrupt PNG"); + s->img_x = stbi__get32be(s); if (s->img_x > (1 << 24)) return stbi__err("too large","Very large image (corrupt?)"); + s->img_y = stbi__get32be(s); if (s->img_y > (1 << 24)) return stbi__err("too large","Very large image (corrupt?)"); + depth = stbi__get8(s); if (depth != 1 && depth != 2 && depth != 4 && depth != 8) return stbi__err("1/2/4/8-bit only","PNG not supported: 1/2/4/8-bit only"); + color = stbi__get8(s); if (color > 6) return stbi__err("bad ctype","Corrupt PNG"); + if (color == 3) pal_img_n = 3; else if (color & 1) return stbi__err("bad ctype","Corrupt PNG"); + comp = stbi__get8(s); if (comp) return stbi__err("bad comp method","Corrupt PNG"); + filter= stbi__get8(s); if (filter) return stbi__err("bad filter method","Corrupt PNG"); + interlace = stbi__get8(s); if (interlace>1) return stbi__err("bad interlace method","Corrupt PNG"); + if (!s->img_x || !s->img_y) return stbi__err("0-pixel image","Corrupt PNG"); + if (!pal_img_n) { + s->img_n = (color & 2 ? 3 : 1) + (color & 4 ? 1 : 0); + if ((1 << 30) / s->img_x / s->img_n < s->img_y) return stbi__err("too large", "Image too large to decode"); + if (scan == STBI__SCAN_header) return 1; + } else { + // if paletted, then pal_n is our final components, and + // img_n is # components to decompress/filter. + s->img_n = 1; + if ((1 << 30) / s->img_x / 4 < s->img_y) return stbi__err("too large","Corrupt PNG"); + // if SCAN_header, have to scan to see if we have a tRNS + } + break; + } + + case STBI__PNG_TYPE('P','L','T','E'): { + if (first) return stbi__err("first not IHDR", "Corrupt PNG"); + if (c.length > 256*3) return stbi__err("invalid PLTE","Corrupt PNG"); + pal_len = c.length / 3; + if (pal_len * 3 != c.length) return stbi__err("invalid PLTE","Corrupt PNG"); + for (i=0; i < pal_len; ++i) { + palette[i*4+0] = stbi__get8(s); + palette[i*4+1] = stbi__get8(s); + palette[i*4+2] = stbi__get8(s); + palette[i*4+3] = 255; + } + break; + } + + case STBI__PNG_TYPE('t','R','N','S'): { + if (first) return stbi__err("first not IHDR", "Corrupt PNG"); + if (z->idata) return stbi__err("tRNS after IDAT","Corrupt PNG"); + if (pal_img_n) { + if (scan == STBI__SCAN_header) { s->img_n = 4; return 1; } + if (pal_len == 0) return stbi__err("tRNS before PLTE","Corrupt PNG"); + if (c.length > pal_len) return stbi__err("bad tRNS len","Corrupt PNG"); + pal_img_n = 4; + for (i=0; i < c.length; ++i) + palette[i*4+3] = stbi__get8(s); + } else { + if (!(s->img_n & 1)) return stbi__err("tRNS with alpha","Corrupt PNG"); + if (c.length != (stbi__uint32) s->img_n*2) return stbi__err("bad tRNS len","Corrupt PNG"); + has_trans = 1; + for (k=0; k < s->img_n; ++k) + tc[k] = (stbi_uc) (stbi__get16be(s) & 255) * stbi__depth_scale_table[depth]; // non 8-bit images will be larger + } + break; + } + + case STBI__PNG_TYPE('I','D','A','T'): { + if (first) return stbi__err("first not IHDR", "Corrupt PNG"); + if (pal_img_n && !pal_len) return stbi__err("no PLTE","Corrupt PNG"); + if (scan == STBI__SCAN_header) { s->img_n = pal_img_n; return 1; } + if (ioff + c.length > idata_limit) { + stbi_uc *p; + if (idata_limit == 0) idata_limit = c.length > 4096 ? c.length : 4096; + while (ioff + c.length > idata_limit) + idata_limit *= 2; + p = (stbi_uc *) STBI_REALLOC(z->idata, idata_limit); if (p == NULL) return stbi__err("outofmem", "Out of memory"); + z->idata = p; + } + if (!stbi__getn(s, z->idata+ioff,c.length)) return stbi__err("outofdata","Corrupt PNG"); + ioff += c.length; + break; + } + + case STBI__PNG_TYPE('I','E','N','D'): { + stbi__uint32 raw_len, bpl; + if (first) return stbi__err("first not IHDR", "Corrupt PNG"); + if (scan != STBI__SCAN_load) return 1; + if (z->idata == NULL) return stbi__err("no IDAT","Corrupt PNG"); + // initial guess for decoded data size to avoid unnecessary reallocs + bpl = (s->img_x * depth + 7) / 8; // bytes per line, per component + raw_len = bpl * s->img_y * s->img_n /* pixels */ + s->img_y /* filter mode per row */; + z->expanded = (stbi_uc *) stbi_zlib_decode_malloc_guesssize_headerflag((char *) z->idata, ioff, raw_len, (int *) &raw_len, !is_iphone); + if (z->expanded == NULL) return 0; // zlib should set error + STBI_FREE(z->idata); z->idata = NULL; + if ((req_comp == s->img_n+1 && req_comp != 3 && !pal_img_n) || has_trans) + s->img_out_n = s->img_n+1; + else + s->img_out_n = s->img_n; + if (!stbi__create_png_image(z, z->expanded, raw_len, s->img_out_n, depth, color, interlace)) return 0; + if (has_trans) + if (!stbi__compute_transparency(z, tc, s->img_out_n)) return 0; + if (is_iphone && stbi__de_iphone_flag && s->img_out_n > 2) + stbi__de_iphone(z); + if (pal_img_n) { + // pal_img_n == 3 or 4 + s->img_n = pal_img_n; // record the actual colors we had + s->img_out_n = pal_img_n; + if (req_comp >= 3) s->img_out_n = req_comp; + if (!stbi__expand_png_palette(z, palette, pal_len, s->img_out_n)) + return 0; + } + STBI_FREE(z->expanded); z->expanded = NULL; + return 1; + } + + default: + // if critical, fail + if (first) return stbi__err("first not IHDR", "Corrupt PNG"); + if ((c.type & (1 << 29)) == 0) { + #ifndef STBI_NO_FAILURE_STRINGS + // not threadsafe + static char invalid_chunk[] = "XXXX PNG chunk not known"; + invalid_chunk[0] = STBI__BYTECAST(c.type >> 24); + invalid_chunk[1] = STBI__BYTECAST(c.type >> 16); + invalid_chunk[2] = STBI__BYTECAST(c.type >> 8); + invalid_chunk[3] = STBI__BYTECAST(c.type >> 0); + #endif + return stbi__err(invalid_chunk, "PNG not supported: unknown PNG chunk type"); + } + stbi__skip(s, c.length); + break; + } + // end of PNG chunk, read and skip CRC + stbi__get32be(s); + } +} + +static unsigned char *stbi__do_png(stbi__png *p, int *x, int *y, int *n, int req_comp) +{ + unsigned char *result=NULL; + if (req_comp < 0 || req_comp > 4) return stbi__errpuc("bad req_comp", "Internal error"); + if (stbi__parse_png_file(p, STBI__SCAN_load, req_comp)) { + result = p->out; + p->out = NULL; + if (req_comp && req_comp != p->s->img_out_n) { + result = stbi__convert_format(result, p->s->img_out_n, req_comp, p->s->img_x, p->s->img_y); + p->s->img_out_n = req_comp; + if (result == NULL) return result; + } + *x = p->s->img_x; + *y = p->s->img_y; + if (n) *n = p->s->img_out_n; + } + STBI_FREE(p->out); p->out = NULL; + STBI_FREE(p->expanded); p->expanded = NULL; + STBI_FREE(p->idata); p->idata = NULL; + + return result; +} + +static unsigned char *stbi__png_load(stbi__context *s, int *x, int *y, int *comp, int req_comp) +{ + stbi__png p; + p.s = s; + return stbi__do_png(&p, x,y,comp,req_comp); +} + +static int stbi__png_test(stbi__context *s) +{ + int r; + r = stbi__check_png_header(s); + stbi__rewind(s); + return r; +} + +static int stbi__png_info_raw(stbi__png *p, int *x, int *y, int *comp) +{ + if (!stbi__parse_png_file(p, STBI__SCAN_header, 0)) { + stbi__rewind( p->s ); + return 0; + } + if (x) *x = p->s->img_x; + if (y) *y = p->s->img_y; + if (comp) *comp = p->s->img_n; + return 1; +} + +static int stbi__png_info(stbi__context *s, int *x, int *y, int *comp) +{ + stbi__png p; + p.s = s; + return stbi__png_info_raw(&p, x, y, comp); +} +#endif + +// Microsoft/Windows BMP image + +#ifndef STBI_NO_BMP +static int stbi__bmp_test_raw(stbi__context *s) +{ + int r; + int sz; + if (stbi__get8(s) != 'B') return 0; + if (stbi__get8(s) != 'M') return 0; + stbi__get32le(s); // discard filesize + stbi__get16le(s); // discard reserved + stbi__get16le(s); // discard reserved + stbi__get32le(s); // discard data offset + sz = stbi__get32le(s); + r = (sz == 12 || sz == 40 || sz == 56 || sz == 108 || sz == 124); + return r; +} + +static int stbi__bmp_test(stbi__context *s) +{ + int r = stbi__bmp_test_raw(s); + stbi__rewind(s); + return r; +} + + +// returns 0..31 for the highest set bit +static int stbi__high_bit(unsigned int z) +{ + int n=0; + if (z == 0) return -1; + if (z >= 0x10000) n += 16, z >>= 16; + if (z >= 0x00100) n += 8, z >>= 8; + if (z >= 0x00010) n += 4, z >>= 4; + if (z >= 0x00004) n += 2, z >>= 2; + if (z >= 0x00002) n += 1, z >>= 1; + return n; +} + +static int stbi__bitcount(unsigned int a) +{ + a = (a & 0x55555555) + ((a >> 1) & 0x55555555); // max 2 + a = (a & 0x33333333) + ((a >> 2) & 0x33333333); // max 4 + a = (a + (a >> 4)) & 0x0f0f0f0f; // max 8 per 4, now 8 bits + a = (a + (a >> 8)); // max 16 per 8 bits + a = (a + (a >> 16)); // max 32 per 8 bits + return a & 0xff; +} + +static int stbi__shiftsigned(int v, int shift, int bits) +{ + int result; + int z=0; + + if (shift < 0) v <<= -shift; + else v >>= shift; + result = v; + + z = bits; + while (z < 8) { + result += v >> z; + z += bits; + } + return result; +} + +static stbi_uc *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int req_comp) +{ + stbi_uc *out; + unsigned int mr=0,mg=0,mb=0,ma=0, fake_a=0; + stbi_uc pal[256][4]; + int psize=0,i,j,compress=0,width; + int bpp, flip_vertically, pad, target, offset, hsz; + if (stbi__get8(s) != 'B' || stbi__get8(s) != 'M') return stbi__errpuc("not BMP", "Corrupt BMP"); + stbi__get32le(s); // discard filesize + stbi__get16le(s); // discard reserved + stbi__get16le(s); // discard reserved + offset = stbi__get32le(s); + hsz = stbi__get32le(s); + if (hsz != 12 && hsz != 40 && hsz != 56 && hsz != 108 && hsz != 124) return stbi__errpuc("unknown BMP", "BMP type not supported: unknown"); + if (hsz == 12) { + s->img_x = stbi__get16le(s); + s->img_y = stbi__get16le(s); + } else { + s->img_x = stbi__get32le(s); + s->img_y = stbi__get32le(s); + } + if (stbi__get16le(s) != 1) return stbi__errpuc("bad BMP", "bad BMP"); + bpp = stbi__get16le(s); + if (bpp == 1) return stbi__errpuc("monochrome", "BMP type not supported: 1-bit"); + flip_vertically = ((int) s->img_y) > 0; + s->img_y = abs((int) s->img_y); + if (hsz == 12) { + if (bpp < 24) + psize = (offset - 14 - 24) / 3; + } else { + compress = stbi__get32le(s); + if (compress == 1 || compress == 2) return stbi__errpuc("BMP RLE", "BMP type not supported: RLE"); + stbi__get32le(s); // discard sizeof + stbi__get32le(s); // discard hres + stbi__get32le(s); // discard vres + stbi__get32le(s); // discard colorsused + stbi__get32le(s); // discard max important + if (hsz == 40 || hsz == 56) { + if (hsz == 56) { + stbi__get32le(s); + stbi__get32le(s); + stbi__get32le(s); + stbi__get32le(s); + } + if (bpp == 16 || bpp == 32) { + mr = mg = mb = 0; + if (compress == 0) { + if (bpp == 32) { + mr = 0xffu << 16; + mg = 0xffu << 8; + mb = 0xffu << 0; + ma = 0xffu << 24; + fake_a = 1; // @TODO: check for cases like alpha value is all 0 and switch it to 255 + STBI_NOTUSED(fake_a); + } else { + mr = 31u << 10; + mg = 31u << 5; + mb = 31u << 0; + } + } else if (compress == 3) { + mr = stbi__get32le(s); + mg = stbi__get32le(s); + mb = stbi__get32le(s); + // not documented, but generated by photoshop and handled by mspaint + if (mr == mg && mg == mb) { + // ?!?!? + return stbi__errpuc("bad BMP", "bad BMP"); + } + } else + return stbi__errpuc("bad BMP", "bad BMP"); + } + } else { + STBI_ASSERT(hsz == 108 || hsz == 124); + mr = stbi__get32le(s); + mg = stbi__get32le(s); + mb = stbi__get32le(s); + ma = stbi__get32le(s); + stbi__get32le(s); // discard color space + for (i=0; i < 12; ++i) + stbi__get32le(s); // discard color space parameters + if (hsz == 124) { + stbi__get32le(s); // discard rendering intent + stbi__get32le(s); // discard offset of profile data + stbi__get32le(s); // discard size of profile data + stbi__get32le(s); // discard reserved + } + } + if (bpp < 16) + psize = (offset - 14 - hsz) >> 2; + } + s->img_n = ma ? 4 : 3; + if (req_comp && req_comp >= 3) // we can directly decode 3 or 4 + target = req_comp; + else + target = s->img_n; // if they want monochrome, we'll post-convert + out = (stbi_uc *) stbi__malloc(target * s->img_x * s->img_y); + if (!out) return stbi__errpuc("outofmem", "Out of memory"); + if (bpp < 16) { + int z=0; + if (psize == 0 || psize > 256) { STBI_FREE(out); return stbi__errpuc("invalid", "Corrupt BMP"); } + for (i=0; i < psize; ++i) { + pal[i][2] = stbi__get8(s); + pal[i][1] = stbi__get8(s); + pal[i][0] = stbi__get8(s); + if (hsz != 12) stbi__get8(s); + pal[i][3] = 255; + } + stbi__skip(s, offset - 14 - hsz - psize * (hsz == 12 ? 3 : 4)); + if (bpp == 4) width = (s->img_x + 1) >> 1; + else if (bpp == 8) width = s->img_x; + else { STBI_FREE(out); return stbi__errpuc("bad bpp", "Corrupt BMP"); } + pad = (-width)&3; + for (j=0; j < (int) s->img_y; ++j) { + for (i=0; i < (int) s->img_x; i += 2) { + int v=stbi__get8(s),v2=0; + if (bpp == 4) { + v2 = v & 15; + v >>= 4; + } + out[z++] = pal[v][0]; + out[z++] = pal[v][1]; + out[z++] = pal[v][2]; + if (target == 4) out[z++] = 255; + if (i+1 == (int) s->img_x) break; + v = (bpp == 8) ? stbi__get8(s) : v2; + out[z++] = pal[v][0]; + out[z++] = pal[v][1]; + out[z++] = pal[v][2]; + if (target == 4) out[z++] = 255; + } + stbi__skip(s, pad); + } + } else { + int rshift=0,gshift=0,bshift=0,ashift=0,rcount=0,gcount=0,bcount=0,acount=0; + int z = 0; + int easy=0; + stbi__skip(s, offset - 14 - hsz); + if (bpp == 24) width = 3 * s->img_x; + else if (bpp == 16) width = 2*s->img_x; + else /* bpp = 32 and pad = 0 */ width=0; + pad = (-width) & 3; + if (bpp == 24) { + easy = 1; + } else if (bpp == 32) { + if (mb == 0xff && mg == 0xff00 && mr == 0x00ff0000 && ma == 0xff000000) + easy = 2; + } + if (!easy) { + if (!mr || !mg || !mb) { STBI_FREE(out); return stbi__errpuc("bad masks", "Corrupt BMP"); } + // right shift amt to put high bit in position #7 + rshift = stbi__high_bit(mr)-7; rcount = stbi__bitcount(mr); + gshift = stbi__high_bit(mg)-7; gcount = stbi__bitcount(mg); + bshift = stbi__high_bit(mb)-7; bcount = stbi__bitcount(mb); + ashift = stbi__high_bit(ma)-7; acount = stbi__bitcount(ma); + } + for (j=0; j < (int) s->img_y; ++j) { + if (easy) { + for (i=0; i < (int) s->img_x; ++i) { + unsigned char a; + out[z+2] = stbi__get8(s); + out[z+1] = stbi__get8(s); + out[z+0] = stbi__get8(s); + z += 3; + a = (easy == 2 ? stbi__get8(s) : 255); + if (target == 4) out[z++] = a; + } + } else { + for (i=0; i < (int) s->img_x; ++i) { + stbi__uint32 v = (stbi__uint32) (bpp == 16 ? stbi__get16le(s) : stbi__get32le(s)); + int a; + out[z++] = STBI__BYTECAST(stbi__shiftsigned(v & mr, rshift, rcount)); + out[z++] = STBI__BYTECAST(stbi__shiftsigned(v & mg, gshift, gcount)); + out[z++] = STBI__BYTECAST(stbi__shiftsigned(v & mb, bshift, bcount)); + a = (ma ? stbi__shiftsigned(v & ma, ashift, acount) : 255); + if (target == 4) out[z++] = STBI__BYTECAST(a); + } + } + stbi__skip(s, pad); + } + } + if (flip_vertically) { + stbi_uc t; + for (j=0; j < (int) s->img_y>>1; ++j) { + stbi_uc *p1 = out + j *s->img_x*target; + stbi_uc *p2 = out + (s->img_y-1-j)*s->img_x*target; + for (i=0; i < (int) s->img_x*target; ++i) { + t = p1[i], p1[i] = p2[i], p2[i] = t; + } + } + } + + if (req_comp && req_comp != target) { + out = stbi__convert_format(out, target, req_comp, s->img_x, s->img_y); + if (out == NULL) return out; // stbi__convert_format frees input on failure + } + + *x = s->img_x; + *y = s->img_y; + if (comp) *comp = s->img_n; + return out; +} +#endif + +// Targa Truevision - TGA +// by Jonathan Dummer +#ifndef STBI_NO_TGA +static int stbi__tga_info(stbi__context *s, int *x, int *y, int *comp) +{ + int tga_w, tga_h, tga_comp; + int sz; + stbi__get8(s); // discard Offset + sz = stbi__get8(s); // color type + if( sz > 1 ) { + stbi__rewind(s); + return 0; // only RGB or indexed allowed + } + sz = stbi__get8(s); // image type + // only RGB or grey allowed, +/- RLE + if ((sz != 1) && (sz != 2) && (sz != 3) && (sz != 9) && (sz != 10) && (sz != 11)) return 0; + stbi__skip(s,9); + tga_w = stbi__get16le(s); + if( tga_w < 1 ) { + stbi__rewind(s); + return 0; // test width + } + tga_h = stbi__get16le(s); + if( tga_h < 1 ) { + stbi__rewind(s); + return 0; // test height + } + sz = stbi__get8(s); // bits per pixel + // only RGB or RGBA or grey allowed + if ((sz != 8) && (sz != 16) && (sz != 24) && (sz != 32)) { + stbi__rewind(s); + return 0; + } + tga_comp = sz; + if (x) *x = tga_w; + if (y) *y = tga_h; + if (comp) *comp = tga_comp / 8; + return 1; // seems to have passed everything +} + +static int stbi__tga_test(stbi__context *s) +{ + int res; + int sz; + stbi__get8(s); // discard Offset + sz = stbi__get8(s); // color type + if ( sz > 1 ) return 0; // only RGB or indexed allowed + sz = stbi__get8(s); // image type + if ( (sz != 1) && (sz != 2) && (sz != 3) && (sz != 9) && (sz != 10) && (sz != 11) ) return 0; // only RGB or grey allowed, +/- RLE + stbi__get16be(s); // discard palette start + stbi__get16be(s); // discard palette length + stbi__get8(s); // discard bits per palette color entry + stbi__get16be(s); // discard x origin + stbi__get16be(s); // discard y origin + if ( stbi__get16be(s) < 1 ) return 0; // test width + if ( stbi__get16be(s) < 1 ) return 0; // test height + sz = stbi__get8(s); // bits per pixel + if ( (sz != 8) && (sz != 16) && (sz != 24) && (sz != 32) ) + res = 0; + else + res = 1; + stbi__rewind(s); + return res; +} + +static stbi_uc *stbi__tga_load(stbi__context *s, int *x, int *y, int *comp, int req_comp) +{ + // read in the TGA header stuff + int tga_offset = stbi__get8(s); + int tga_indexed = stbi__get8(s); + int tga_image_type = stbi__get8(s); + int tga_is_RLE = 0; + int tga_palette_start = stbi__get16le(s); + int tga_palette_len = stbi__get16le(s); + int tga_palette_bits = stbi__get8(s); + int tga_x_origin = stbi__get16le(s); + int tga_y_origin = stbi__get16le(s); + int tga_width = stbi__get16le(s); + int tga_height = stbi__get16le(s); + int tga_bits_per_pixel = stbi__get8(s); + int tga_comp = tga_bits_per_pixel / 8; + int tga_inverted = stbi__get8(s); + // image data + unsigned char *tga_data; + unsigned char *tga_palette = NULL; + int i, j; + unsigned char raw_data[4]; + int RLE_count = 0; + int RLE_repeating = 0; + int read_next_pixel = 1; + + // do a tiny bit of precessing + if ( tga_image_type >= 8 ) + { + tga_image_type -= 8; + tga_is_RLE = 1; + } + /* int tga_alpha_bits = tga_inverted & 15; */ + tga_inverted = 1 - ((tga_inverted >> 5) & 1); + + // error check + if ( //(tga_indexed) || + (tga_width < 1) || (tga_height < 1) || + (tga_image_type < 1) || (tga_image_type > 3) || + ((tga_bits_per_pixel != 8) && (tga_bits_per_pixel != 16) && + (tga_bits_per_pixel != 24) && (tga_bits_per_pixel != 32)) + ) + { + return NULL; // we don't report this as a bad TGA because we don't even know if it's TGA + } + + // If I'm paletted, then I'll use the number of bits from the palette + if ( tga_indexed ) + { + tga_comp = tga_palette_bits / 8; + } + + // tga info + *x = tga_width; + *y = tga_height; + if (comp) *comp = tga_comp; + + tga_data = (unsigned char*)stbi__malloc( tga_width * tga_height * tga_comp ); + if (!tga_data) return stbi__errpuc("outofmem", "Out of memory"); + + // skip to the data's starting position (offset usually = 0) + stbi__skip(s, tga_offset ); + + if ( !tga_indexed && !tga_is_RLE) { + for (i=0; i < tga_height; ++i) { + int y = tga_inverted ? tga_height -i - 1 : i; + stbi_uc *tga_row = tga_data + y*tga_width*tga_comp; + stbi__getn(s, tga_row, tga_width * tga_comp); + } + } else { + // do I need to load a palette? + if ( tga_indexed) + { + // any data to skip? (offset usually = 0) + stbi__skip(s, tga_palette_start ); + // load the palette + tga_palette = (unsigned char*)stbi__malloc( tga_palette_len * tga_palette_bits / 8 ); + if (!tga_palette) { + STBI_FREE(tga_data); + return stbi__errpuc("outofmem", "Out of memory"); + } + if (!stbi__getn(s, tga_palette, tga_palette_len * tga_palette_bits / 8 )) { + STBI_FREE(tga_data); + STBI_FREE(tga_palette); + return stbi__errpuc("bad palette", "Corrupt TGA"); + } + } + // load the data + for (i=0; i < tga_width * tga_height; ++i) + { + // if I'm in RLE mode, do I need to get a RLE stbi__pngchunk? + if ( tga_is_RLE ) + { + if ( RLE_count == 0 ) + { + // yep, get the next byte as a RLE command + int RLE_cmd = stbi__get8(s); + RLE_count = 1 + (RLE_cmd & 127); + RLE_repeating = RLE_cmd >> 7; + read_next_pixel = 1; + } else if ( !RLE_repeating ) + { + read_next_pixel = 1; + } + } else + { + read_next_pixel = 1; + } + // OK, if I need to read a pixel, do it now + if ( read_next_pixel ) + { + // load however much data we did have + if ( tga_indexed ) + { + // read in 1 byte, then perform the lookup + int pal_idx = stbi__get8(s); + if ( pal_idx >= tga_palette_len ) + { + // invalid index + pal_idx = 0; + } + pal_idx *= tga_bits_per_pixel / 8; + for (j = 0; j*8 < tga_bits_per_pixel; ++j) + { + raw_data[j] = tga_palette[pal_idx+j]; + } + } else + { + // read in the data raw + for (j = 0; j*8 < tga_bits_per_pixel; ++j) + { + raw_data[j] = stbi__get8(s); + } + } + // clear the reading flag for the next pixel + read_next_pixel = 0; + } // end of reading a pixel + + // copy data + for (j = 0; j < tga_comp; ++j) + tga_data[i*tga_comp+j] = raw_data[j]; + + // in case we're in RLE mode, keep counting down + --RLE_count; + } + // do I need to invert the image? + if ( tga_inverted ) + { + for (j = 0; j*2 < tga_height; ++j) + { + int index1 = j * tga_width * tga_comp; + int index2 = (tga_height - 1 - j) * tga_width * tga_comp; + for (i = tga_width * tga_comp; i > 0; --i) + { + unsigned char temp = tga_data[index1]; + tga_data[index1] = tga_data[index2]; + tga_data[index2] = temp; + ++index1; + ++index2; + } + } + } + // clear my palette, if I had one + if ( tga_palette != NULL ) + { + STBI_FREE( tga_palette ); + } + } + + // swap RGB + if (tga_comp >= 3) + { + unsigned char* tga_pixel = tga_data; + for (i=0; i < tga_width * tga_height; ++i) + { + unsigned char temp = tga_pixel[0]; + tga_pixel[0] = tga_pixel[2]; + tga_pixel[2] = temp; + tga_pixel += tga_comp; + } + } + + // convert to target component count + if (req_comp && req_comp != tga_comp) + tga_data = stbi__convert_format(tga_data, tga_comp, req_comp, tga_width, tga_height); + + // the things I do to get rid of an error message, and yet keep + // Microsoft's C compilers happy... [8^( + tga_palette_start = tga_palette_len = tga_palette_bits = + tga_x_origin = tga_y_origin = 0; + // OK, done + return tga_data; +} +#endif + +// ************************************************************************************************* +// Photoshop PSD loader -- PD by Thatcher Ulrich, integration by Nicolas Schulz, tweaked by STB + +#ifndef STBI_NO_PSD +static int stbi__psd_test(stbi__context *s) +{ + int r = (stbi__get32be(s) == 0x38425053); + stbi__rewind(s); + return r; +} + +static stbi_uc *stbi__psd_load(stbi__context *s, int *x, int *y, int *comp, int req_comp) +{ + int pixelCount; + int channelCount, compression; + int channel, i, count, len; + int w,h; + stbi_uc *out; + + // Check identifier + if (stbi__get32be(s) != 0x38425053) // "8BPS" + return stbi__errpuc("not PSD", "Corrupt PSD image"); + + // Check file type version. + if (stbi__get16be(s) != 1) + return stbi__errpuc("wrong version", "Unsupported version of PSD image"); + + // Skip 6 reserved bytes. + stbi__skip(s, 6 ); + + // Read the number of channels (R, G, B, A, etc). + channelCount = stbi__get16be(s); + if (channelCount < 0 || channelCount > 16) + return stbi__errpuc("wrong channel count", "Unsupported number of channels in PSD image"); + + // Read the rows and columns of the image. + h = stbi__get32be(s); + w = stbi__get32be(s); + + // Make sure the depth is 8 bits. + if (stbi__get16be(s) != 8) + return stbi__errpuc("unsupported bit depth", "PSD bit depth is not 8 bit"); + + // Make sure the color mode is RGB. + // Valid options are: + // 0: Bitmap + // 1: Grayscale + // 2: Indexed color + // 3: RGB color + // 4: CMYK color + // 7: Multichannel + // 8: Duotone + // 9: Lab color + if (stbi__get16be(s) != 3) + return stbi__errpuc("wrong color format", "PSD is not in RGB color format"); + + // Skip the Mode Data. (It's the palette for indexed color; other info for other modes.) + stbi__skip(s,stbi__get32be(s) ); + + // Skip the image resources. (resolution, pen tool paths, etc) + stbi__skip(s, stbi__get32be(s) ); + + // Skip the reserved data. + stbi__skip(s, stbi__get32be(s) ); + + // Find out if the data is compressed. + // Known values: + // 0: no compression + // 1: RLE compressed + compression = stbi__get16be(s); + if (compression > 1) + return stbi__errpuc("bad compression", "PSD has an unknown compression format"); + + // Create the destination image. + out = (stbi_uc *) stbi__malloc(4 * w*h); + if (!out) return stbi__errpuc("outofmem", "Out of memory"); + pixelCount = w*h; + + // Initialize the data to zero. + //memset( out, 0, pixelCount * 4 ); + + // Finally, the image data. + if (compression) { + // RLE as used by .PSD and .TIFF + // Loop until you get the number of unpacked bytes you are expecting: + // Read the next source byte into n. + // If n is between 0 and 127 inclusive, copy the next n+1 bytes literally. + // Else if n is between -127 and -1 inclusive, copy the next byte -n+1 times. + // Else if n is 128, noop. + // Endloop + + // The RLE-compressed data is preceeded by a 2-byte data count for each row in the data, + // which we're going to just skip. + stbi__skip(s, h * channelCount * 2 ); + + // Read the RLE data by channel. + for (channel = 0; channel < 4; channel++) { + stbi_uc *p; + + p = out+channel; + if (channel >= channelCount) { + // Fill this channel with default data. + for (i = 0; i < pixelCount; i++) *p = (channel == 3 ? 255 : 0), p += 4; + } else { + // Read the RLE data. + count = 0; + while (count < pixelCount) { + len = stbi__get8(s); + if (len == 128) { + // No-op. + } else if (len < 128) { + // Copy next len+1 bytes literally. + len++; + count += len; + while (len) { + *p = stbi__get8(s); + p += 4; + len--; + } + } else if (len > 128) { + stbi_uc val; + // Next -len+1 bytes in the dest are replicated from next source byte. + // (Interpret len as a negative 8-bit int.) + len ^= 0x0FF; + len += 2; + val = stbi__get8(s); + count += len; + while (len) { + *p = val; + p += 4; + len--; + } + } + } + } + } + + } else { + // We're at the raw image data. It's each channel in order (Red, Green, Blue, Alpha, ...) + // where each channel consists of an 8-bit value for each pixel in the image. + + // Read the data by channel. + for (channel = 0; channel < 4; channel++) { + stbi_uc *p; + + p = out + channel; + if (channel > channelCount) { + // Fill this channel with default data. + for (i = 0; i < pixelCount; i++) *p = channel == 3 ? 255 : 0, p += 4; + } else { + // Read the data. + for (i = 0; i < pixelCount; i++) + *p = stbi__get8(s), p += 4; + } + } + } + + if (req_comp && req_comp != 4) { + out = stbi__convert_format(out, 4, req_comp, w, h); + if (out == NULL) return out; // stbi__convert_format frees input on failure + } + + if (comp) *comp = channelCount; + *y = h; + *x = w; + + return out; +} +#endif + +// ************************************************************************************************* +// Softimage PIC loader +// by Tom Seddon +// +// See http://softimage.wiki.softimage.com/index.php/INFO:_PIC_file_format +// See http://ozviz.wasp.uwa.edu.au/~pbourke/dataformats/softimagepic/ + +#ifndef STBI_NO_PIC +static int stbi__pic_is4(stbi__context *s,const char *str) +{ + int i; + for (i=0; i<4; ++i) + if (stbi__get8(s) != (stbi_uc)str[i]) + return 0; + + return 1; +} + +static int stbi__pic_test_core(stbi__context *s) +{ + int i; + + if (!stbi__pic_is4(s,"\x53\x80\xF6\x34")) + return 0; + + for(i=0;i<84;++i) + stbi__get8(s); + + if (!stbi__pic_is4(s,"PICT")) + return 0; + + return 1; +} + +typedef struct +{ + stbi_uc size,type,channel; +} stbi__pic_packet; + +static stbi_uc *stbi__readval(stbi__context *s, int channel, stbi_uc *dest) +{ + int mask=0x80, i; + + for (i=0; i<4; ++i, mask>>=1) { + if (channel & mask) { + if (stbi__at_eof(s)) return stbi__errpuc("bad file","PIC file too short"); + dest[i]=stbi__get8(s); + } + } + + return dest; +} + +static void stbi__copyval(int channel,stbi_uc *dest,const stbi_uc *src) +{ + int mask=0x80,i; + + for (i=0;i<4; ++i, mask>>=1) + if (channel&mask) + dest[i]=src[i]; +} + +static stbi_uc *stbi__pic_load_core(stbi__context *s,int width,int height,int *comp, stbi_uc *result) +{ + int act_comp=0,num_packets=0,y,chained; + stbi__pic_packet packets[10]; + + // this will (should...) cater for even some bizarre stuff like having data + // for the same channel in multiple packets. + do { + stbi__pic_packet *packet; + + if (num_packets==sizeof(packets)/sizeof(packets[0])) + return stbi__errpuc("bad format","too many packets"); + + packet = &packets[num_packets++]; + + chained = stbi__get8(s); + packet->size = stbi__get8(s); + packet->type = stbi__get8(s); + packet->channel = stbi__get8(s); + + act_comp |= packet->channel; + + if (stbi__at_eof(s)) return stbi__errpuc("bad file","file too short (reading packets)"); + if (packet->size != 8) return stbi__errpuc("bad format","packet isn't 8bpp"); + } while (chained); + + *comp = (act_comp & 0x10 ? 4 : 3); // has alpha channel? + + for(y=0; y<height; ++y) { + int packet_idx; + + for(packet_idx=0; packet_idx < num_packets; ++packet_idx) { + stbi__pic_packet *packet = &packets[packet_idx]; + stbi_uc *dest = result+y*width*4; + + switch (packet->type) { + default: + return stbi__errpuc("bad format","packet has bad compression type"); + + case 0: {//uncompressed + int x; + + for(x=0;x<width;++x, dest+=4) + if (!stbi__readval(s,packet->channel,dest)) + return 0; + break; + } + + case 1://Pure RLE + { + int left=width, i; + + while (left>0) { + stbi_uc count,value[4]; + + count=stbi__get8(s); + if (stbi__at_eof(s)) return stbi__errpuc("bad file","file too short (pure read count)"); + + if (count > left) + count = (stbi_uc) left; + + if (!stbi__readval(s,packet->channel,value)) return 0; + + for(i=0; i<count; ++i,dest+=4) + stbi__copyval(packet->channel,dest,value); + left -= count; + } + } + break; + + case 2: {//Mixed RLE + int left=width; + while (left>0) { + int count = stbi__get8(s), i; + if (stbi__at_eof(s)) return stbi__errpuc("bad file","file too short (mixed read count)"); + + if (count >= 128) { // Repeated + stbi_uc value[4]; + int i; + + if (count==128) + count = stbi__get16be(s); + else + count -= 127; + if (count > left) + return stbi__errpuc("bad file","scanline overrun"); + + if (!stbi__readval(s,packet->channel,value)) + return 0; + + for(i=0;i<count;++i, dest += 4) + stbi__copyval(packet->channel,dest,value); + } else { // Raw + ++count; + if (count>left) return stbi__errpuc("bad file","scanline overrun"); + + for(i=0;i<count;++i, dest+=4) + if (!stbi__readval(s,packet->channel,dest)) + return 0; + } + left-=count; + } + break; + } + } + } + } + + return result; +} + +static stbi_uc *stbi__pic_load(stbi__context *s,int *px,int *py,int *comp,int req_comp) +{ + stbi_uc *result; + int i, x,y; + + for (i=0; i<92; ++i) + stbi__get8(s); + + x = stbi__get16be(s); + y = stbi__get16be(s); + if (stbi__at_eof(s)) return stbi__errpuc("bad file","file too short (pic header)"); + if ((1 << 28) / x < y) return stbi__errpuc("too large", "Image too large to decode"); + + stbi__get32be(s); //skip `ratio' + stbi__get16be(s); //skip `fields' + stbi__get16be(s); //skip `pad' + + // intermediate buffer is RGBA + result = (stbi_uc *) stbi__malloc(x*y*4); + memset(result, 0xff, x*y*4); + + if (!stbi__pic_load_core(s,x,y,comp, result)) { + STBI_FREE(result); + result=0; + } + *px = x; + *py = y; + if (req_comp == 0) req_comp = *comp; + result=stbi__convert_format(result,4,req_comp,x,y); + + return result; +} + +static int stbi__pic_test(stbi__context *s) +{ + int r = stbi__pic_test_core(s); + stbi__rewind(s); + return r; +} +#endif + +// ************************************************************************************************* +// GIF loader -- public domain by Jean-Marc Lienher -- simplified/shrunk by stb + +#ifndef STBI_NO_GIF +typedef struct +{ + stbi__int16 prefix; + stbi_uc first; + stbi_uc suffix; +} stbi__gif_lzw; + +typedef struct +{ + int w,h; + stbi_uc *out; // output buffer (always 4 components) + int flags, bgindex, ratio, transparent, eflags; + stbi_uc pal[256][4]; + stbi_uc lpal[256][4]; + stbi__gif_lzw codes[4096]; + stbi_uc *color_table; + int parse, step; + int lflags; + int start_x, start_y; + int max_x, max_y; + int cur_x, cur_y; + int line_size; +} stbi__gif; + +static int stbi__gif_test_raw(stbi__context *s) +{ + int sz; + if (stbi__get8(s) != 'G' || stbi__get8(s) != 'I' || stbi__get8(s) != 'F' || stbi__get8(s) != '8') return 0; + sz = stbi__get8(s); + if (sz != '9' && sz != '7') return 0; + if (stbi__get8(s) != 'a') return 0; + return 1; +} + +static int stbi__gif_test(stbi__context *s) +{ + int r = stbi__gif_test_raw(s); + stbi__rewind(s); + return r; +} + +static void stbi__gif_parse_colortable(stbi__context *s, stbi_uc pal[256][4], int num_entries, int transp) +{ + int i; + for (i=0; i < num_entries; ++i) { + pal[i][2] = stbi__get8(s); + pal[i][1] = stbi__get8(s); + pal[i][0] = stbi__get8(s); + pal[i][3] = transp == i ? 0 : 255; + } +} + +static int stbi__gif_header(stbi__context *s, stbi__gif *g, int *comp, int is_info) +{ + stbi_uc version; + if (stbi__get8(s) != 'G' || stbi__get8(s) != 'I' || stbi__get8(s) != 'F' || stbi__get8(s) != '8') + return stbi__err("not GIF", "Corrupt GIF"); + + version = stbi__get8(s); + if (version != '7' && version != '9') return stbi__err("not GIF", "Corrupt GIF"); + if (stbi__get8(s) != 'a') return stbi__err("not GIF", "Corrupt GIF"); + + stbi__g_failure_reason = ""; + g->w = stbi__get16le(s); + g->h = stbi__get16le(s); + g->flags = stbi__get8(s); + g->bgindex = stbi__get8(s); + g->ratio = stbi__get8(s); + g->transparent = -1; + + if (comp != 0) *comp = 4; // can't actually tell whether it's 3 or 4 until we parse the comments + + if (is_info) return 1; + + if (g->flags & 0x80) + stbi__gif_parse_colortable(s,g->pal, 2 << (g->flags & 7), -1); + + return 1; +} + +static int stbi__gif_info_raw(stbi__context *s, int *x, int *y, int *comp) +{ + stbi__gif g; + if (!stbi__gif_header(s, &g, comp, 1)) { + stbi__rewind( s ); + return 0; + } + if (x) *x = g.w; + if (y) *y = g.h; + return 1; +} + +static void stbi__out_gif_code(stbi__gif *g, stbi__uint16 code) +{ + stbi_uc *p, *c; + + // recurse to decode the prefixes, since the linked-list is backwards, + // and working backwards through an interleaved image would be nasty + if (g->codes[code].prefix >= 0) + stbi__out_gif_code(g, g->codes[code].prefix); + + if (g->cur_y >= g->max_y) return; + + p = &g->out[g->cur_x + g->cur_y]; + c = &g->color_table[g->codes[code].suffix * 4]; + + if (c[3] >= 128) { + p[0] = c[2]; + p[1] = c[1]; + p[2] = c[0]; + p[3] = c[3]; + } + g->cur_x += 4; + + if (g->cur_x >= g->max_x) { + g->cur_x = g->start_x; + g->cur_y += g->step; + + while (g->cur_y >= g->max_y && g->parse > 0) { + g->step = (1 << g->parse) * g->line_size; + g->cur_y = g->start_y + (g->step >> 1); + --g->parse; + } + } +} + +static stbi_uc *stbi__process_gif_raster(stbi__context *s, stbi__gif *g) +{ + stbi_uc lzw_cs; + stbi__int32 len, code; + stbi__uint32 first; + stbi__int32 codesize, codemask, avail, oldcode, bits, valid_bits, clear; + stbi__gif_lzw *p; + + lzw_cs = stbi__get8(s); + clear = 1 << lzw_cs; + first = 1; + codesize = lzw_cs + 1; + codemask = (1 << codesize) - 1; + bits = 0; + valid_bits = 0; + for (code = 0; code < clear; code++) { + g->codes[code].prefix = -1; + g->codes[code].first = (stbi_uc) code; + g->codes[code].suffix = (stbi_uc) code; + } + + // support no starting clear code + avail = clear+2; + oldcode = -1; + + len = 0; + for(;;) { + if (valid_bits < codesize) { + if (len == 0) { + len = stbi__get8(s); // start new block + if (len == 0) + return g->out; + } + --len; + bits |= (stbi__int32) stbi__get8(s) << valid_bits; + valid_bits += 8; + } else { + stbi__int32 code = bits & codemask; + bits >>= codesize; + valid_bits -= codesize; + // @OPTIMIZE: is there some way we can accelerate the non-clear path? + if (code == clear) { // clear code + codesize = lzw_cs + 1; + codemask = (1 << codesize) - 1; + avail = clear + 2; + oldcode = -1; + first = 0; + } else if (code == clear + 1) { // end of stream code + stbi__skip(s, len); + while ((len = stbi__get8(s)) > 0) + stbi__skip(s,len); + return g->out; + } else if (code <= avail) { + if (first) return stbi__errpuc("no clear code", "Corrupt GIF"); + + if (oldcode >= 0) { + p = &g->codes[avail++]; + if (avail > 4096) return stbi__errpuc("too many codes", "Corrupt GIF"); + p->prefix = (stbi__int16) oldcode; + p->first = g->codes[oldcode].first; + p->suffix = (code == avail) ? p->first : g->codes[code].first; + } else if (code == avail) + return stbi__errpuc("illegal code in raster", "Corrupt GIF"); + + stbi__out_gif_code(g, (stbi__uint16) code); + + if ((avail & codemask) == 0 && avail <= 0x0FFF) { + codesize++; + codemask = (1 << codesize) - 1; + } + + oldcode = code; + } else { + return stbi__errpuc("illegal code in raster", "Corrupt GIF"); + } + } + } +} + +static void stbi__fill_gif_background(stbi__gif *g) +{ + int i; + stbi_uc *c = g->pal[g->bgindex]; + // @OPTIMIZE: write a dword at a time + for (i = 0; i < g->w * g->h * 4; i += 4) { + stbi_uc *p = &g->out[i]; + p[0] = c[2]; + p[1] = c[1]; + p[2] = c[0]; + p[3] = c[3]; + } +} + +// this function is designed to support animated gifs, although stb_image doesn't support it +static stbi_uc *stbi__gif_load_next(stbi__context *s, stbi__gif *g, int *comp, int req_comp) +{ + int i; + stbi_uc *old_out = 0; + + if (g->out == 0) { + if (!stbi__gif_header(s, g, comp,0)) return 0; // stbi__g_failure_reason set by stbi__gif_header + g->out = (stbi_uc *) stbi__malloc(4 * g->w * g->h); + if (g->out == 0) return stbi__errpuc("outofmem", "Out of memory"); + stbi__fill_gif_background(g); + } else { + // animated-gif-only path + if (((g->eflags & 0x1C) >> 2) == 3) { + old_out = g->out; + g->out = (stbi_uc *) stbi__malloc(4 * g->w * g->h); + if (g->out == 0) return stbi__errpuc("outofmem", "Out of memory"); + memcpy(g->out, old_out, g->w*g->h*4); + } + } + + for (;;) { + switch (stbi__get8(s)) { + case 0x2C: /* Image Descriptor */ + { + stbi__int32 x, y, w, h; + stbi_uc *o; + + x = stbi__get16le(s); + y = stbi__get16le(s); + w = stbi__get16le(s); + h = stbi__get16le(s); + if (((x + w) > (g->w)) || ((y + h) > (g->h))) + return stbi__errpuc("bad Image Descriptor", "Corrupt GIF"); + + g->line_size = g->w * 4; + g->start_x = x * 4; + g->start_y = y * g->line_size; + g->max_x = g->start_x + w * 4; + g->max_y = g->start_y + h * g->line_size; + g->cur_x = g->start_x; + g->cur_y = g->start_y; + + g->lflags = stbi__get8(s); + + if (g->lflags & 0x40) { + g->step = 8 * g->line_size; // first interlaced spacing + g->parse = 3; + } else { + g->step = g->line_size; + g->parse = 0; + } + + if (g->lflags & 0x80) { + stbi__gif_parse_colortable(s,g->lpal, 2 << (g->lflags & 7), g->eflags & 0x01 ? g->transparent : -1); + g->color_table = (stbi_uc *) g->lpal; + } else if (g->flags & 0x80) { + for (i=0; i < 256; ++i) // @OPTIMIZE: stbi__jpeg_reset only the previous transparent + g->pal[i][3] = 255; + if (g->transparent >= 0 && (g->eflags & 0x01)) + g->pal[g->transparent][3] = 0; + g->color_table = (stbi_uc *) g->pal; + } else + return stbi__errpuc("missing color table", "Corrupt GIF"); + + o = stbi__process_gif_raster(s, g); + if (o == NULL) return NULL; + + if (req_comp && req_comp != 4) + o = stbi__convert_format(o, 4, req_comp, g->w, g->h); + return o; + } + + case 0x21: // Comment Extension. + { + int len; + if (stbi__get8(s) == 0xF9) { // Graphic Control Extension. + len = stbi__get8(s); + if (len == 4) { + g->eflags = stbi__get8(s); + stbi__get16le(s); // delay + g->transparent = stbi__get8(s); + } else { + stbi__skip(s, len); + break; + } + } + while ((len = stbi__get8(s)) != 0) + stbi__skip(s, len); + break; + } + + case 0x3B: // gif stream termination code + return (stbi_uc *) s; // using '1' causes warning on some compilers + + default: + return stbi__errpuc("unknown code", "Corrupt GIF"); + } + } +} + +static stbi_uc *stbi__gif_load(stbi__context *s, int *x, int *y, int *comp, int req_comp) +{ + stbi_uc *u = 0; + stbi__gif g; + memset(&g, 0, sizeof(g)); + + u = stbi__gif_load_next(s, &g, comp, req_comp); + if (u == (stbi_uc *) s) u = 0; // end of animated gif marker + if (u) { + *x = g.w; + *y = g.h; + } + + return u; +} + +static int stbi__gif_info(stbi__context *s, int *x, int *y, int *comp) +{ + return stbi__gif_info_raw(s,x,y,comp); +} +#endif + +// ************************************************************************************************* +// Radiance RGBE HDR loader +// originally by Nicolas Schulz +#ifndef STBI_NO_HDR +static int stbi__hdr_test_core(stbi__context *s) +{ + const char *signature = "#?RADIANCE\n"; + int i; + for (i=0; signature[i]; ++i) + if (stbi__get8(s) != signature[i]) + return 0; + return 1; +} + +static int stbi__hdr_test(stbi__context* s) +{ + int r = stbi__hdr_test_core(s); + stbi__rewind(s); + return r; +} + +#define STBI__HDR_BUFLEN 1024 +static char *stbi__hdr_gettoken(stbi__context *z, char *buffer) +{ + int len=0; + char c = '\0'; + + c = (char) stbi__get8(z); + + while (!stbi__at_eof(z) && c != '\n') { + buffer[len++] = c; + if (len == STBI__HDR_BUFLEN-1) { + // flush to end of line + while (!stbi__at_eof(z) && stbi__get8(z) != '\n') + ; + break; + } + c = (char) stbi__get8(z); + } + + buffer[len] = 0; + return buffer; +} + +static void stbi__hdr_convert(float *output, stbi_uc *input, int req_comp) +{ + if ( input[3] != 0 ) { + float f1; + // Exponent + f1 = (float) ldexp(1.0f, input[3] - (int)(128 + 8)); + if (req_comp <= 2) + output[0] = (input[0] + input[1] + input[2]) * f1 / 3; + else { + output[0] = input[0] * f1; + output[1] = input[1] * f1; + output[2] = input[2] * f1; + } + if (req_comp == 2) output[1] = 1; + if (req_comp == 4) output[3] = 1; + } else { + switch (req_comp) { + case 4: output[3] = 1; /* fallthrough */ + case 3: output[0] = output[1] = output[2] = 0; + break; + case 2: output[1] = 1; /* fallthrough */ + case 1: output[0] = 0; + break; + } + } +} + +static float *stbi__hdr_load(stbi__context *s, int *x, int *y, int *comp, int req_comp) +{ + char buffer[STBI__HDR_BUFLEN]; + char *token; + int valid = 0; + int width, height; + stbi_uc *scanline; + float *hdr_data; + int len; + unsigned char count, value; + int i, j, k, c1,c2, z; + + + // Check identifier + if (strcmp(stbi__hdr_gettoken(s,buffer), "#?RADIANCE") != 0) + return stbi__errpf("not HDR", "Corrupt HDR image"); + + // Parse header + for(;;) { + token = stbi__hdr_gettoken(s,buffer); + if (token[0] == 0) break; + if (strcmp(token, "FORMAT=32-bit_rle_rgbe") == 0) valid = 1; + } + + if (!valid) return stbi__errpf("unsupported format", "Unsupported HDR format"); + + // Parse width and height + // can't use sscanf() if we're not using stdio! + token = stbi__hdr_gettoken(s,buffer); + if (strncmp(token, "-Y ", 3)) return stbi__errpf("unsupported data layout", "Unsupported HDR format"); + token += 3; + height = (int) strtol(token, &token, 10); + while (*token == ' ') ++token; + if (strncmp(token, "+X ", 3)) return stbi__errpf("unsupported data layout", "Unsupported HDR format"); + token += 3; + width = (int) strtol(token, NULL, 10); + + *x = width; + *y = height; + + if (comp) *comp = 3; + if (req_comp == 0) req_comp = 3; + + // Read data + hdr_data = (float *) stbi__malloc(height * width * req_comp * sizeof(float)); + + // Load image data + // image data is stored as some number of sca + if ( width < 8 || width >= 32768) { + // Read flat data + for (j=0; j < height; ++j) { + for (i=0; i < width; ++i) { + stbi_uc rgbe[4]; + main_decode_loop: + stbi__getn(s, rgbe, 4); + stbi__hdr_convert(hdr_data + j * width * req_comp + i * req_comp, rgbe, req_comp); + } + } + } else { + // Read RLE-encoded data + scanline = NULL; + + for (j = 0; j < height; ++j) { + c1 = stbi__get8(s); + c2 = stbi__get8(s); + len = stbi__get8(s); + if (c1 != 2 || c2 != 2 || (len & 0x80)) { + // not run-length encoded, so we have to actually use THIS data as a decoded + // pixel (note this can't be a valid pixel--one of RGB must be >= 128) + stbi_uc rgbe[4]; + rgbe[0] = (stbi_uc) c1; + rgbe[1] = (stbi_uc) c2; + rgbe[2] = (stbi_uc) len; + rgbe[3] = (stbi_uc) stbi__get8(s); + stbi__hdr_convert(hdr_data, rgbe, req_comp); + i = 1; + j = 0; + STBI_FREE(scanline); + goto main_decode_loop; // yes, this makes no sense + } + len <<= 8; + len |= stbi__get8(s); + if (len != width) { STBI_FREE(hdr_data); STBI_FREE(scanline); return stbi__errpf("invalid decoded scanline length", "corrupt HDR"); } + if (scanline == NULL) scanline = (stbi_uc *) stbi__malloc(width * 4); + + for (k = 0; k < 4; ++k) { + i = 0; + while (i < width) { + count = stbi__get8(s); + if (count > 128) { + // Run + value = stbi__get8(s); + count -= 128; + for (z = 0; z < count; ++z) + scanline[i++ * 4 + k] = value; + } else { + // Dump + for (z = 0; z < count; ++z) + scanline[i++ * 4 + k] = stbi__get8(s); + } + } + } + for (i=0; i < width; ++i) + stbi__hdr_convert(hdr_data+(j*width + i)*req_comp, scanline + i*4, req_comp); + } + STBI_FREE(scanline); + } + + return hdr_data; +} + +static int stbi__hdr_info(stbi__context *s, int *x, int *y, int *comp) +{ + char buffer[STBI__HDR_BUFLEN]; + char *token; + int valid = 0; + + if (strcmp(stbi__hdr_gettoken(s,buffer), "#?RADIANCE") != 0) { + stbi__rewind( s ); + return 0; + } + + for(;;) { + token = stbi__hdr_gettoken(s,buffer); + if (token[0] == 0) break; + if (strcmp(token, "FORMAT=32-bit_rle_rgbe") == 0) valid = 1; + } + + if (!valid) { + stbi__rewind( s ); + return 0; + } + token = stbi__hdr_gettoken(s,buffer); + if (strncmp(token, "-Y ", 3)) { + stbi__rewind( s ); + return 0; + } + token += 3; + *y = (int) strtol(token, &token, 10); + while (*token == ' ') ++token; + if (strncmp(token, "+X ", 3)) { + stbi__rewind( s ); + return 0; + } + token += 3; + *x = (int) strtol(token, NULL, 10); + *comp = 3; + return 1; +} +#endif // STBI_NO_HDR + +#ifndef STBI_NO_BMP +static int stbi__bmp_info(stbi__context *s, int *x, int *y, int *comp) +{ + int hsz; + if (stbi__get8(s) != 'B' || stbi__get8(s) != 'M') { + stbi__rewind( s ); + return 0; + } + stbi__skip(s,12); + hsz = stbi__get32le(s); + if (hsz != 12 && hsz != 40 && hsz != 56 && hsz != 108 && hsz != 124) { + stbi__rewind( s ); + return 0; + } + if (hsz == 12) { + *x = stbi__get16le(s); + *y = stbi__get16le(s); + } else { + *x = stbi__get32le(s); + *y = stbi__get32le(s); + } + if (stbi__get16le(s) != 1) { + stbi__rewind( s ); + return 0; + } + *comp = stbi__get16le(s) / 8; + return 1; +} +#endif + +#ifndef STBI_NO_PSD +static int stbi__psd_info(stbi__context *s, int *x, int *y, int *comp) +{ + int channelCount; + if (stbi__get32be(s) != 0x38425053) { + stbi__rewind( s ); + return 0; + } + if (stbi__get16be(s) != 1) { + stbi__rewind( s ); + return 0; + } + stbi__skip(s, 6); + channelCount = stbi__get16be(s); + if (channelCount < 0 || channelCount > 16) { + stbi__rewind( s ); + return 0; + } + *y = stbi__get32be(s); + *x = stbi__get32be(s); + if (stbi__get16be(s) != 8) { + stbi__rewind( s ); + return 0; + } + if (stbi__get16be(s) != 3) { + stbi__rewind( s ); + return 0; + } + *comp = 4; + return 1; +} +#endif + +#ifndef STBI_NO_PIC +static int stbi__pic_info(stbi__context *s, int *x, int *y, int *comp) +{ + int act_comp=0,num_packets=0,chained; + stbi__pic_packet packets[10]; + + stbi__skip(s, 92); + + *x = stbi__get16be(s); + *y = stbi__get16be(s); + if (stbi__at_eof(s)) return 0; + if ( (*x) != 0 && (1 << 28) / (*x) < (*y)) { + stbi__rewind( s ); + return 0; + } + + stbi__skip(s, 8); + + do { + stbi__pic_packet *packet; + + if (num_packets==sizeof(packets)/sizeof(packets[0])) + return 0; + + packet = &packets[num_packets++]; + chained = stbi__get8(s); + packet->size = stbi__get8(s); + packet->type = stbi__get8(s); + packet->channel = stbi__get8(s); + act_comp |= packet->channel; + + if (stbi__at_eof(s)) { + stbi__rewind( s ); + return 0; + } + if (packet->size != 8) { + stbi__rewind( s ); + return 0; + } + } while (chained); + + *comp = (act_comp & 0x10 ? 4 : 3); + + return 1; +} +#endif + +// ************************************************************************************************* +// Portable Gray Map and Portable Pixel Map loader +// by Ken Miller +// +// PGM: http://netpbm.sourceforge.net/doc/pgm.html +// PPM: http://netpbm.sourceforge.net/doc/ppm.html +// +// Known limitations: +// Does not support comments in the header section +// Does not support ASCII image data (formats P2 and P3) +// Does not support 16-bit-per-channel + +#ifndef STBI_NO_PNM + +static int stbi__pnm_test(stbi__context *s) +{ + char p, t; + p = (char) stbi__get8(s); + t = (char) stbi__get8(s); + if (p != 'P' || (t != '5' && t != '6')) { + stbi__rewind( s ); + return 0; + } + return 1; +} + +static stbi_uc *stbi__pnm_load(stbi__context *s, int *x, int *y, int *comp, int req_comp) +{ + stbi_uc *out; + if (!stbi__pnm_info(s, (int *)&s->img_x, (int *)&s->img_y, (int *)&s->img_n)) + return 0; + *x = s->img_x; + *y = s->img_y; + *comp = s->img_n; + + out = (stbi_uc *) stbi__malloc(s->img_n * s->img_x * s->img_y); + if (!out) return stbi__errpuc("outofmem", "Out of memory"); + stbi__getn(s, out, s->img_n * s->img_x * s->img_y); + + if (req_comp && req_comp != s->img_n) { + out = stbi__convert_format(out, s->img_n, req_comp, s->img_x, s->img_y); + if (out == NULL) return out; // stbi__convert_format frees input on failure + } + return out; +} + +static int stbi__pnm_isspace(char c) +{ + return c == ' ' || c == '\t' || c == '\n' || c == '\v' || c == '\f' || c == '\r'; +} + +static void stbi__pnm_skip_whitespace(stbi__context *s, char *c) +{ + while (!stbi__at_eof(s) && stbi__pnm_isspace(*c)) + *c = (char) stbi__get8(s); +} + +static int stbi__pnm_isdigit(char c) +{ + return c >= '0' && c <= '9'; +} + +static int stbi__pnm_getinteger(stbi__context *s, char *c) +{ + int value = 0; + + while (!stbi__at_eof(s) && stbi__pnm_isdigit(*c)) { + value = value*10 + (*c - '0'); + *c = (char) stbi__get8(s); + } + + return value; +} + +static int stbi__pnm_info(stbi__context *s, int *x, int *y, int *comp) +{ + int maxv; + char c, p, t; + + stbi__rewind( s ); + + // Get identifier + p = (char) stbi__get8(s); + t = (char) stbi__get8(s); + if (p != 'P' || (t != '5' && t != '6')) { + stbi__rewind( s ); + return 0; + } + + *comp = (t == '6') ? 3 : 1; // '5' is 1-component .pgm; '6' is 3-component .ppm + + c = (char) stbi__get8(s); + stbi__pnm_skip_whitespace(s, &c); + + *x = stbi__pnm_getinteger(s, &c); // read width + stbi__pnm_skip_whitespace(s, &c); + + *y = stbi__pnm_getinteger(s, &c); // read height + stbi__pnm_skip_whitespace(s, &c); + + maxv = stbi__pnm_getinteger(s, &c); // read max value + + if (maxv > 255) + return stbi__err("max value > 255", "PPM image not 8-bit"); + else + return 1; +} +#endif + +static int stbi__info_main(stbi__context *s, int *x, int *y, int *comp) +{ + #ifndef STBI_NO_JPEG + if (stbi__jpeg_info(s, x, y, comp)) return 1; + #endif + + #ifndef STBI_NO_PNG + if (stbi__png_info(s, x, y, comp)) return 1; + #endif + + #ifndef STBI_NO_GIF + if (stbi__gif_info(s, x, y, comp)) return 1; + #endif + + #ifndef STBI_NO_BMP + if (stbi__bmp_info(s, x, y, comp)) return 1; + #endif + + #ifndef STBI_NO_PSD + if (stbi__psd_info(s, x, y, comp)) return 1; + #endif + + #ifndef STBI_NO_PIC + if (stbi__pic_info(s, x, y, comp)) return 1; + #endif + + #ifndef STBI_NO_PNM + if (stbi__pnm_info(s, x, y, comp)) return 1; + #endif + + #ifndef STBI_NO_HDR + if (stbi__hdr_info(s, x, y, comp)) return 1; + #endif + + // test tga last because it's a crappy test! + #ifndef STBI_NO_TGA + if (stbi__tga_info(s, x, y, comp)) + return 1; + #endif + return stbi__err("unknown image type", "Image not of any known type, or corrupt"); +} + +#ifndef STBI_NO_STDIO +STBIDEF int stbi_info(char const *filename, int *x, int *y, int *comp) +{ + FILE *f = stbi__fopen(filename, "rb"); + int result; + if (!f) return stbi__err("can't fopen", "Unable to open file"); + result = stbi_info_from_file(f, x, y, comp); + fclose(f); + return result; +} + +STBIDEF int stbi_info_from_file(FILE *f, int *x, int *y, int *comp) +{ + int r; + stbi__context s; + long pos = ftell(f); + stbi__start_file(&s, f); + r = stbi__info_main(&s,x,y,comp); + fseek(f,pos,SEEK_SET); + return r; +} +#endif // !STBI_NO_STDIO + +STBIDEF int stbi_info_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp) +{ + stbi__context s; + stbi__start_mem(&s,buffer,len); + return stbi__info_main(&s,x,y,comp); +} + +STBIDEF int stbi_info_from_callbacks(stbi_io_callbacks const *c, void *user, int *x, int *y, int *comp) +{ + stbi__context s; + stbi__start_callbacks(&s, (stbi_io_callbacks *) c, user); + return stbi__info_main(&s,x,y,comp); +} + +#endif // STB_IMAGE_IMPLEMENTATION + +/* + revision history: + 2.00b (2014-12-25) fix STBI_MALLOC in progressive JPEG + 2.00 (2014-12-25) optimize JPG, including x86 SSE2 & NEON SIMD (ryg) + progressive JPEG (stb) + PGM/PPM support (Ken Miller) + STBI_MALLOC,STBI_REALLOC,STBI_FREE + GIF bugfix -- seemingly never worked + STBI_NO_*, STBI_ONLY_* + 1.48 (2014-12-14) fix incorrectly-named assert() + 1.47 (2014-12-14) 1/2/4-bit PNG support, both direct and paletted (Omar Cornut & stb) + optimize PNG (ryg) + fix bug in interlaced PNG with user-specified channel count (stb) + 1.46 (2014-08-26) + fix broken tRNS chunk (colorkey-style transparency) in non-paletted PNG + 1.45 (2014-08-16) + fix MSVC-ARM internal compiler error by wrapping malloc + 1.44 (2014-08-07) + various warning fixes from Ronny Chevalier + 1.43 (2014-07-15) + fix MSVC-only compiler problem in code changed in 1.42 + 1.42 (2014-07-09) + don't define _CRT_SECURE_NO_WARNINGS (affects user code) + fixes to stbi__cleanup_jpeg path + added STBI_ASSERT to avoid requiring assert.h + 1.41 (2014-06-25) + fix search&replace from 1.36 that messed up comments/error messages + 1.40 (2014-06-22) + fix gcc struct-initialization warning + 1.39 (2014-06-15) + fix to TGA optimization when req_comp != number of components in TGA; + fix to GIF loading because BMP wasn't rewinding (whoops, no GIFs in my test suite) + add support for BMP version 5 (more ignored fields) + 1.38 (2014-06-06) + suppress MSVC warnings on integer casts truncating values + fix accidental rename of 'skip' field of I/O + 1.37 (2014-06-04) + remove duplicate typedef + 1.36 (2014-06-03) + convert to header file single-file library + if de-iphone isn't set, load iphone images color-swapped instead of returning NULL + 1.35 (2014-05-27) + various warnings + fix broken STBI_SIMD path + fix bug where stbi_load_from_file no longer left file pointer in correct place + fix broken non-easy path for 32-bit BMP (possibly never used) + TGA optimization by Arseny Kapoulkine + 1.34 (unknown) + use STBI_NOTUSED in stbi__resample_row_generic(), fix one more leak in tga failure case + 1.33 (2011-07-14) + make stbi_is_hdr work in STBI_NO_HDR (as specified), minor compiler-friendly improvements + 1.32 (2011-07-13) + support for "info" function for all supported filetypes (SpartanJ) + 1.31 (2011-06-20) + a few more leak fixes, bug in PNG handling (SpartanJ) + 1.30 (2011-06-11) + added ability to load files via callbacks to accomidate custom input streams (Ben Wenger) + removed deprecated format-specific test/load functions + removed support for installable file formats (stbi_loader) -- would have been broken for IO callbacks anyway + error cases in bmp and tga give messages and don't leak (Raymond Barbiero, grisha) + fix inefficiency in decoding 32-bit BMP (David Woo) + 1.29 (2010-08-16) + various warning fixes from Aurelien Pocheville + 1.28 (2010-08-01) + fix bug in GIF palette transparency (SpartanJ) + 1.27 (2010-08-01) + cast-to-stbi_uc to fix warnings + 1.26 (2010-07-24) + fix bug in file buffering for PNG reported by SpartanJ + 1.25 (2010-07-17) + refix trans_data warning (Won Chun) + 1.24 (2010-07-12) + perf improvements reading from files on platforms with lock-heavy fgetc() + minor perf improvements for jpeg + deprecated type-specific functions so we'll get feedback if they're needed + attempt to fix trans_data warning (Won Chun) + 1.23 fixed bug in iPhone support + 1.22 (2010-07-10) + removed image *writing* support + stbi_info support from Jetro Lauha + GIF support from Jean-Marc Lienher + iPhone PNG-extensions from James Brown + warning-fixes from Nicolas Schulz and Janez Zemva (i.stbi__err. Janez (U+017D)emva) + 1.21 fix use of 'stbi_uc' in header (reported by jon blow) + 1.20 added support for Softimage PIC, by Tom Seddon + 1.19 bug in interlaced PNG corruption check (found by ryg) + 1.18 2008-08-02 + fix a threading bug (local mutable static) + 1.17 support interlaced PNG + 1.16 major bugfix - stbi__convert_format converted one too many pixels + 1.15 initialize some fields for thread safety + 1.14 fix threadsafe conversion bug + header-file-only version (#define STBI_HEADER_FILE_ONLY before including) + 1.13 threadsafe + 1.12 const qualifiers in the API + 1.11 Support installable IDCT, colorspace conversion routines + 1.10 Fixes for 64-bit (don't use "unsigned long") + optimized upsampling by Fabian "ryg" Giesen + 1.09 Fix format-conversion for PSD code (bad global variables!) + 1.08 Thatcher Ulrich's PSD code integrated by Nicolas Schulz + 1.07 attempt to fix C++ warning/errors again + 1.06 attempt to fix C++ warning/errors again + 1.05 fix TGA loading to return correct *comp and use good luminance calc + 1.04 default float alpha is 1, not 255; use 'void *' for stbi_image_free + 1.03 bugfixes to STBI_NO_STDIO, STBI_NO_HDR + 1.02 support for (subset of) HDR files, float interface for preferred access to them + 1.01 fix bug: possible bug in handling right-side up bmps... not sure + fix bug: the stbi__bmp_load() and stbi__tga_load() functions didn't work at all + 1.00 interface to zlib that skips zlib header + 0.99 correct handling of alpha in palette + 0.98 TGA loader by lonesock; dynamically add loaders (untested) + 0.97 jpeg errors on too large a file; also catch another malloc failure + 0.96 fix detection of invalid v value - particleman@mollyrocket forum + 0.95 during header scan, seek to markers in case of padding + 0.94 STBI_NO_STDIO to disable stdio usage; rename all #defines the same + 0.93 handle jpegtran output; verbose errors + 0.92 read 4,8,16,24,32-bit BMP files of several formats + 0.91 output 24-bit Windows 3.0 BMP files + 0.90 fix a few more warnings; bump version number to approach 1.0 + 0.61 bugfixes due to Marc LeBlanc, Christopher Lloyd + 0.60 fix compiling as c++ + 0.59 fix warnings: merge Dave Moore's -Wall fixes + 0.58 fix bug: zlib uncompressed mode len/nlen was wrong endian + 0.57 fix bug: jpg last huffman symbol before marker was >9 bits but less than 16 available + 0.56 fix bug: zlib uncompressed mode len vs. nlen + 0.55 fix bug: restart_interval not initialized to 0 + 0.54 allow NULL for 'int *comp' + 0.53 fix bug in png 3->4; speedup png decoding + 0.52 png handles req_comp=3,4 directly; minor cleanup; jpeg comments + 0.51 obey req_comp requests, 1-component jpegs return as 1-component, + on 'test' only check type, not whether we support this variant + 0.50 first released version +*/ diff --git a/src/stb_image_write.h b/src/stb_image_write.h index b78cd212..da3f7e22 100644 --- a/src/stb_image_write.h +++ b/src/stb_image_write.h @@ -1,14 +1,16 @@ -/* stbiw-0.92 - public domain - http://nothings.org/stb/stb_image_write.h +/* stb_image_write - v0.95 - public domain - http://nothings.org/stb/stb_image_write.h writes out PNG/BMP/TGA images to C stdio - Sean Barrett 2010 no warranty implied; use at your own risk -Before #including, +Before including, #define STB_IMAGE_WRITE_IMPLEMENTATION in the file that you want to have the implementation. +Will probably not work correctly with strict-aliasing optimizations. + ABOUT: @@ -33,23 +35,20 @@ USAGE: The functions create an image file defined by the parameters. The image is a rectangle of pixels stored from left-to-right, top-to-bottom. Each pixel contains 'comp' channels of data stored interleaved with 8-bits - per channel, in the following order: 1=L, 2=LA, 3=RGB, 4=RGBA. (L is - luminance, i.e. monochrome "color", i.e. grey value.) The rectangle is - 'w' pixels wide and 'h' pixels tall. The *data pointer points to the - first byte of the top-left-most pixel. For PNG, "stride_in_bytes" is - the distance in bytes from the first byte of a row of pixels to the - first byte of the next row of pixels. Other file formats assume the - first byte of the each row of pixels begins immediately after the last - byte of the previous row. + per channel, in the following order: 1=Y, 2=YA, 3=RGB, 4=RGBA. (Y is + monochrome color.) The rectangle is 'w' pixels wide and 'h' pixels tall. + The *data pointer points to the first byte of the top-left-most pixel. + For PNG, "stride_in_bytes" is the distance in bytes from the first byte of + a row of pixels to the first byte of the next row of pixels. PNG creates output files with the same number of components as the input. - The BMP and TGA formats expand Y to RGB in the file format. BMP does not + The BMP format expands Y to RGB in the file format and does not output alpha. PNG supports writing rectangles of data even when the bytes storing rows of data are not consecutive in memory (e.g. sub-rectangles of a larger image), by supplying the stride between the beginning of adjacent rows. The other - formats do not. (Thus you cannot write an in-memory BMP through the BMP + formats do not. (Thus you cannot write a native-format BMP through the BMP writer, both because it is in BGR order and because it may have padding at the end of the line.) */ @@ -129,8 +128,8 @@ static void write_pixels(FILE *f, int rgb_dir, int vdir, int x, int y, int comp, if (write_alpha < 0) fwrite(&d[comp-1], 1, 1, f); switch (comp) { - case 1: - case 2: write3(f, d[0],d[0],d[0]); + case 1: + case 2: fwrite(d, 1, 1, f); break; case 4: if (!write_alpha) { @@ -179,48 +178,50 @@ int stbi_write_bmp(char const *filename, int x, int y, int comp, const void *dat int stbi_write_tga(char const *filename, int x, int y, int comp, const void *data) { - int has_alpha = !(comp & 1); + int has_alpha = (comp == 2 || comp == 4); + int colorbytes = has_alpha ? comp-1 : comp; + int format = colorbytes < 2 ? 3 : 2; // 3 color channels (RGB/RGBA) = 2, 1 color channel (Y/YA) = 3 return outfile(filename, -1,-1, x, y, comp, (void *) data, has_alpha, 0, - "111 221 2222 11", 0,0,2, 0,0,0, 0,0,x,y, 24+8*has_alpha, 8*has_alpha); + "111 221 2222 11", 0,0,format, 0,0,0, 0,0,x,y, (colorbytes+has_alpha)*8, has_alpha*8); } -// stretchy buffer; stbi__sbpush() == vector<>::push_back() -- stbi__sbcount() == vector<>::size() -#define stbi__sbraw(a) ((int *) (a) - 2) -#define stbi__sbm(a) stbi__sbraw(a)[0] -#define stbi__sbn(a) stbi__sbraw(a)[1] +// stretchy buffer; stbiw__sbpush() == vector<>::push_back() -- stbiw__sbcount() == vector<>::size() +#define stbiw__sbraw(a) ((int *) (a) - 2) +#define stbiw__sbm(a) stbiw__sbraw(a)[0] +#define stbiw__sbn(a) stbiw__sbraw(a)[1] -#define stbi__sbneedgrow(a,n) ((a)==0 || stbi__sbn(a)+n >= stbi__sbm(a)) -#define stbi__sbmaybegrow(a,n) (stbi__sbneedgrow(a,(n)) ? stbi__sbgrow(a,n) : 0) -#define stbi__sbgrow(a,n) stbi__sbgrowf((void **) &(a), (n), sizeof(*(a))) +#define stbiw__sbneedgrow(a,n) ((a)==0 || stbiw__sbn(a)+n >= stbiw__sbm(a)) +#define stbiw__sbmaybegrow(a,n) (stbiw__sbneedgrow(a,(n)) ? stbiw__sbgrow(a,n) : 0) +#define stbiw__sbgrow(a,n) stbiw__sbgrowf((void **) &(a), (n), sizeof(*(a))) -#define stbi__sbpush(a, v) (stbi__sbmaybegrow(a,1), (a)[stbi__sbn(a)++] = (v)) -#define stbi__sbcount(a) ((a) ? stbi__sbn(a) : 0) -#define stbi__sbfree(a) ((a) ? free(stbi__sbraw(a)),0 : 0) +#define stbiw__sbpush(a, v) (stbiw__sbmaybegrow(a,1), (a)[stbiw__sbn(a)++] = (v)) +#define stbiw__sbcount(a) ((a) ? stbiw__sbn(a) : 0) +#define stbiw__sbfree(a) ((a) ? free(stbiw__sbraw(a)),0 : 0) -static void *stbi__sbgrowf(void **arr, int increment, int itemsize) +static void *stbiw__sbgrowf(void **arr, int increment, int itemsize) { - int m = *arr ? 2*stbi__sbm(*arr)+increment : increment+1; - void *p = realloc(*arr ? stbi__sbraw(*arr) : 0, itemsize * m + sizeof(int)*2); + int m = *arr ? 2*stbiw__sbm(*arr)+increment : increment+1; + void *p = realloc(*arr ? stbiw__sbraw(*arr) : 0, itemsize * m + sizeof(int)*2); assert(p); if (p) { if (!*arr) ((int *) p)[1] = 0; *arr = (void *) ((int *) p + 2); - stbi__sbm(*arr) = m; + stbiw__sbm(*arr) = m; } return *arr; } -static unsigned char *stbi__zlib_flushf(unsigned char *data, unsigned int *bitbuffer, int *bitcount) +static unsigned char *stbiw__zlib_flushf(unsigned char *data, unsigned int *bitbuffer, int *bitcount) { while (*bitcount >= 8) { - stbi__sbpush(data, (unsigned char) *bitbuffer); + stbiw__sbpush(data, (unsigned char) *bitbuffer); *bitbuffer >>= 8; *bitcount -= 8; } return data; } -static int stbi__zlib_bitrev(int code, int codebits) +static int stbiw__zlib_bitrev(int code, int codebits) { int res=0; while (codebits--) { @@ -230,7 +231,7 @@ static int stbi__zlib_bitrev(int code, int codebits) return res; } -static unsigned int stbi__zlib_countm(unsigned char *a, unsigned char *b, int limit) +static unsigned int stbiw__zlib_countm(unsigned char *a, unsigned char *b, int limit) { int i; for (i=0; i < limit && i < 258; ++i) @@ -238,7 +239,7 @@ static unsigned int stbi__zlib_countm(unsigned char *a, unsigned char *b, int li return i; } -static unsigned int stbi__zhash(unsigned char *data) +static unsigned int stbiw__zhash(unsigned char *data) { stbiw_uint32 hash = data[0] + (data[1] << 8) + (data[2] << 16); hash ^= hash << 3; @@ -250,19 +251,19 @@ static unsigned int stbi__zhash(unsigned char *data) return hash; } -#define stbi__zlib_flush() (out = stbi__zlib_flushf(out, &bitbuf, &bitcount)) -#define stbi__zlib_add(code,codebits) \ - (bitbuf |= (code) << bitcount, bitcount += (codebits), stbi__zlib_flush()) -#define stbi__zlib_huffa(b,c) stbi__zlib_add(stbi__zlib_bitrev(b,c),c) +#define stbiw__zlib_flush() (out = stbiw__zlib_flushf(out, &bitbuf, &bitcount)) +#define stbiw__zlib_add(code,codebits) \ + (bitbuf |= (code) << bitcount, bitcount += (codebits), stbiw__zlib_flush()) +#define stbiw__zlib_huffa(b,c) stbiw__zlib_add(stbiw__zlib_bitrev(b,c),c) // default huffman tables -#define stbi__zlib_huff1(n) stbi__zlib_huffa(0x30 + (n), 8) -#define stbi__zlib_huff2(n) stbi__zlib_huffa(0x190 + (n)-144, 9) -#define stbi__zlib_huff3(n) stbi__zlib_huffa(0 + (n)-256,7) -#define stbi__zlib_huff4(n) stbi__zlib_huffa(0xc0 + (n)-280,8) -#define stbi__zlib_huff(n) ((n) <= 143 ? stbi__zlib_huff1(n) : (n) <= 255 ? stbi__zlib_huff2(n) : (n) <= 279 ? stbi__zlib_huff3(n) : stbi__zlib_huff4(n)) -#define stbi__zlib_huffb(n) ((n) <= 143 ? stbi__zlib_huff1(n) : stbi__zlib_huff2(n)) +#define stbiw__zlib_huff1(n) stbiw__zlib_huffa(0x30 + (n), 8) +#define stbiw__zlib_huff2(n) stbiw__zlib_huffa(0x190 + (n)-144, 9) +#define stbiw__zlib_huff3(n) stbiw__zlib_huffa(0 + (n)-256,7) +#define stbiw__zlib_huff4(n) stbiw__zlib_huffa(0xc0 + (n)-280,8) +#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)) +#define stbiw__zlib_huffb(n) ((n) <= 143 ? stbiw__zlib_huff1(n) : stbiw__zlib_huff2(n)) -#define stbi__ZHASH 16384 +#define stbiw__ZHASH 16384 unsigned char * stbi_zlib_compress(unsigned char *data, int data_len, int *out_len, int quality) { @@ -273,45 +274,45 @@ unsigned char * stbi_zlib_compress(unsigned char *data, int data_len, int *out_l unsigned int bitbuf=0; int i,j, bitcount=0; unsigned char *out = NULL; - unsigned char **hash_table[stbi__ZHASH]; // 64KB on the stack! + unsigned char **hash_table[stbiw__ZHASH]; // 64KB on the stack! if (quality < 5) quality = 5; - stbi__sbpush(out, 0x78); // DEFLATE 32K window - stbi__sbpush(out, 0x5e); // FLEVEL = 1 - stbi__zlib_add(1,1); // BFINAL = 1 - stbi__zlib_add(1,2); // BTYPE = 1 -- fixed huffman + stbiw__sbpush(out, 0x78); // DEFLATE 32K window + stbiw__sbpush(out, 0x5e); // FLEVEL = 1 + stbiw__zlib_add(1,1); // BFINAL = 1 + stbiw__zlib_add(1,2); // BTYPE = 1 -- fixed huffman - for (i=0; i < stbi__ZHASH; ++i) + for (i=0; i < stbiw__ZHASH; ++i) hash_table[i] = NULL; i=0; while (i < data_len-3) { // hash next 3 bytes of data to be compressed - int h = stbi__zhash(data+i)&(stbi__ZHASH-1), best=3; + int h = stbiw__zhash(data+i)&(stbiw__ZHASH-1), best=3; unsigned char *bestloc = 0; unsigned char **hlist = hash_table[h]; - int n = stbi__sbcount(hlist); + int n = stbiw__sbcount(hlist); for (j=0; j < n; ++j) { if (hlist[j]-data > i-32768) { // if entry lies within window - int d = stbi__zlib_countm(hlist[j], data+i, data_len-i); + int d = stbiw__zlib_countm(hlist[j], data+i, data_len-i); if (d >= best) best=d,bestloc=hlist[j]; } } // when hash table entry is too long, delete half the entries - if (hash_table[h] && stbi__sbn(hash_table[h]) == 2*quality) { + if (hash_table[h] && stbiw__sbn(hash_table[h]) == 2*quality) { memcpy(hash_table[h], hash_table[h]+quality, sizeof(hash_table[h][0])*quality); - stbi__sbn(hash_table[h]) = quality; + stbiw__sbn(hash_table[h]) = quality; } - stbi__sbpush(hash_table[h],data+i); + stbiw__sbpush(hash_table[h],data+i); if (bestloc) { // "lazy matching" - check match at *next* byte, and if it's better, do cur byte as literal - h = stbi__zhash(data+i+1)&(stbi__ZHASH-1); + h = stbiw__zhash(data+i+1)&(stbiw__ZHASH-1); hlist = hash_table[h]; - n = stbi__sbcount(hlist); + n = stbiw__sbcount(hlist); for (j=0; j < n; ++j) { if (hlist[j]-data > i-32767) { - int e = stbi__zlib_countm(hlist[j], data+i+1, data_len-i-1); + int e = stbiw__zlib_countm(hlist[j], data+i+1, data_len-i-1); if (e > best) { // if next match is better, bail on current match bestloc = NULL; break; @@ -321,30 +322,30 @@ unsigned char * stbi_zlib_compress(unsigned char *data, int data_len, int *out_l } if (bestloc) { - int d = data+i - bestloc; // distance back + int d = (int) (data+i - bestloc); // distance back assert(d <= 32767 && best <= 258); for (j=0; best > lengthc[j+1]-1; ++j); - stbi__zlib_huff(j+257); - if (lengtheb[j]) stbi__zlib_add(best - lengthc[j], lengtheb[j]); + stbiw__zlib_huff(j+257); + if (lengtheb[j]) stbiw__zlib_add(best - lengthc[j], lengtheb[j]); for (j=0; d > distc[j+1]-1; ++j); - stbi__zlib_add(stbi__zlib_bitrev(j,5),5); - if (disteb[j]) stbi__zlib_add(d - distc[j], disteb[j]); + stbiw__zlib_add(stbiw__zlib_bitrev(j,5),5); + if (disteb[j]) stbiw__zlib_add(d - distc[j], disteb[j]); i += best; } else { - stbi__zlib_huffb(data[i]); + stbiw__zlib_huffb(data[i]); ++i; } } // write out final bytes for (;i < data_len; ++i) - stbi__zlib_huffb(data[i]); - stbi__zlib_huff(256); // end of block + stbiw__zlib_huffb(data[i]); + stbiw__zlib_huff(256); // end of block // pad with 0 bits to byte boundary while (bitcount) - stbi__zlib_add(0,1); + stbiw__zlib_add(0,1); - for (i=0; i < stbi__ZHASH; ++i) - (void) stbi__sbfree(hash_table[i]); + for (i=0; i < stbiw__ZHASH; ++i) + (void) stbiw__sbfree(hash_table[i]); { // compute adler32 on input @@ -356,18 +357,18 @@ unsigned char * stbi_zlib_compress(unsigned char *data, int data_len, int *out_l j += blocklen; blocklen = 5552; } - stbi__sbpush(out, (unsigned char) (s2 >> 8)); - stbi__sbpush(out, (unsigned char) s2); - stbi__sbpush(out, (unsigned char) (s1 >> 8)); - stbi__sbpush(out, (unsigned char) s1); + stbiw__sbpush(out, (unsigned char) (s2 >> 8)); + stbiw__sbpush(out, (unsigned char) s2); + stbiw__sbpush(out, (unsigned char) (s1 >> 8)); + stbiw__sbpush(out, (unsigned char) s1); } - *out_len = stbi__sbn(out); + *out_len = stbiw__sbn(out); // make returned pointer freeable - memmove(stbi__sbraw(out), out, *out_len); - return (unsigned char *) stbi__sbraw(out); + memmove(stbiw__sbraw(out), out, *out_len); + return (unsigned char *) stbiw__sbraw(out); } -unsigned int stbi__crc32(unsigned char *buffer, int len) +unsigned int stbiw__crc32(unsigned char *buffer, int len) { static unsigned int crc_table[256]; unsigned int crc = ~0u; @@ -381,17 +382,17 @@ unsigned int stbi__crc32(unsigned char *buffer, int len) return ~crc; } -#define stbi__wpng4(o,a,b,c,d) ((o)[0]=(unsigned char)(a),(o)[1]=(unsigned char)(b),(o)[2]=(unsigned char)(c),(o)[3]=(unsigned char)(d),(o)+=4) -#define stbi__wp32(data,v) stbi__wpng4(data, (v)>>24,(v)>>16,(v)>>8,(v)); -#define stbi__wptag(data,s) stbi__wpng4(data, s[0],s[1],s[2],s[3]) +#define stbiw__wpng4(o,a,b,c,d) ((o)[0]=(unsigned char)(a),(o)[1]=(unsigned char)(b),(o)[2]=(unsigned char)(c),(o)[3]=(unsigned char)(d),(o)+=4) +#define stbiw__wp32(data,v) stbiw__wpng4(data, (v)>>24,(v)>>16,(v)>>8,(v)); +#define stbiw__wptag(data,s) stbiw__wpng4(data, s[0],s[1],s[2],s[3]) -static void stbi__wpcrc(unsigned char **data, int len) +static void stbiw__wpcrc(unsigned char **data, int len) { - unsigned int crc = stbi__crc32(*data - len - 4, len+4); - stbi__wp32(*data, crc); + unsigned int crc = stbiw__crc32(*data - len - 4, len+4); + stbiw__wp32(*data, crc); } -static unsigned char stbi__paeth(int a, int b, int c) +static unsigned char stbiw__paeth(int a, int b, int c) { int p = a + b - c, pa = abs(p-a), pb = abs(p-b), pc = abs(p-c); if (pa <= pb && pa <= pc) return (unsigned char) a; @@ -427,7 +428,7 @@ unsigned char *stbi_write_png_to_mem(unsigned char *pixels, int stride_bytes, in case 1: line_buffer[i] = z[i]; break; case 2: line_buffer[i] = z[i] - z[i-stride_bytes]; break; case 3: line_buffer[i] = z[i] - (z[i-stride_bytes]>>1); break; - case 4: line_buffer[i] = (signed char) (z[i] - stbi__paeth(0,z[i-stride_bytes],0)); break; + case 4: line_buffer[i] = (signed char) (z[i] - stbiw__paeth(0,z[i-stride_bytes],0)); break; case 5: line_buffer[i] = z[i]; break; case 6: line_buffer[i] = z[i]; break; } @@ -437,9 +438,9 @@ unsigned char *stbi_write_png_to_mem(unsigned char *pixels, int stride_bytes, in case 1: line_buffer[i] = z[i] - z[i-n]; break; case 2: line_buffer[i] = z[i] - z[i-stride_bytes]; break; case 3: line_buffer[i] = z[i] - ((z[i-n] + z[i-stride_bytes])>>1); break; - case 4: line_buffer[i] = z[i] - stbi__paeth(z[i-n], z[i-stride_bytes], z[i-stride_bytes-n]); break; + case 4: line_buffer[i] = z[i] - stbiw__paeth(z[i-n], z[i-stride_bytes], z[i-stride_bytes-n]); break; case 5: line_buffer[i] = z[i] - (z[i-n]>>1); break; - case 6: line_buffer[i] = z[i] - stbi__paeth(z[i-n], 0,0); break; + case 6: line_buffer[i] = z[i] - stbiw__paeth(z[i-n], 0,0); break; } } if (p) break; @@ -464,25 +465,25 @@ unsigned char *stbi_write_png_to_mem(unsigned char *pixels, int stride_bytes, in o=out; memcpy(o,sig,8); o+= 8; - stbi__wp32(o, 13); // header length - stbi__wptag(o, "IHDR"); - stbi__wp32(o, x); - stbi__wp32(o, y); + stbiw__wp32(o, 13); // header length + stbiw__wptag(o, "IHDR"); + stbiw__wp32(o, x); + stbiw__wp32(o, y); *o++ = 8; *o++ = (unsigned char) ctype[n]; *o++ = 0; *o++ = 0; *o++ = 0; - stbi__wpcrc(&o,13); + stbiw__wpcrc(&o,13); - stbi__wp32(o, zlen); - stbi__wptag(o, "IDAT"); + stbiw__wp32(o, zlen); + stbiw__wptag(o, "IDAT"); memcpy(o, zlib, zlen); o += zlen; free(zlib); - stbi__wpcrc(&o, zlen); + stbiw__wpcrc(&o, zlen); - stbi__wp32(o,0); - stbi__wptag(o, "IEND"); - stbi__wpcrc(&o,0); + stbiw__wp32(o,0); + stbiw__wptag(o, "IEND"); + stbiw__wpcrc(&o,0); assert(o == out + *out_len); @@ -506,6 +507,12 @@ int stbi_write_png(char const *filename, int x, int y, int comp, const void *dat /* Revision history + 0.95 (2014-08-17) + add monochrome TGA output + 0.94 (2014-05-31) + rename private functions to avoid conflicts with stb_image.h + 0.93 (2014-05-27) + warning fixes 0.92 (2010-08-01) casts to unsigned char to fix warnings 0.91 (2010-07-17) diff --git a/src/stb_vorbis.c b/src/stb_vorbis.c index 7a45725b..8bee7e1a 100644 --- a/src/stb_vorbis.c +++ b/src/stb_vorbis.c @@ -1,36 +1,5 @@ -// Ogg Vorbis I audio decoder -- version 0.99996 -// -// Written in April 2007 by Sean Barrett, sponsored by RAD Game Tools. -// -// Placed in the public domain April 2007 by the author: no copyright is -// claimed, and you may use it for any purpose you like. -// -// No warranty for any purpose is expressed or implied by the author (nor -// by RAD Game Tools). Report bugs and send enhancements to the author. -// -// Get the latest version and other information at: -// http://nothings.org/stb_vorbis/ - - -// Todo: -// -// - seeking (note you can seek yourself using the pushdata API) -// -// Limitations: -// -// - floor 0 not supported (used in old ogg vorbis files) -// - lossless sample-truncation at beginning ignored -// - cannot concatenate multiple vorbis streams -// - sample positions are 32-bit, limiting seekable 192Khz -// files to around 6 hours (Ogg supports 64-bit) -// -// All of these limitations may be removed in future versions. - #include "stb_vorbis.h" -#ifdef __linux -#include <alloca.h> -#endif #ifndef STB_VORBIS_HEADER_ONLY @@ -488,7 +457,7 @@ extern int my_prof(int slot); //#define stb_prof my_prof #ifndef stb_prof -#define stb_prof(x) 0 +#define stb_prof(x) ((void) 0) #endif #if defined(STB_VORBIS_NO_PUSHDATA_API) @@ -572,7 +541,7 @@ static void *setup_temp_malloc(vorb *f, int sz) return malloc(sz); } -static void setup_temp_free(vorb *f, void *p, size_t sz) +static void setup_temp_free(vorb *f, void *p, int sz) { if (f->alloc.alloc_buffer) { f->temp_offset += (sz+3)&~3; @@ -590,7 +559,7 @@ static void crc32_init(void) uint32 s; for(i=0; i < 256; i++) { for (s=i<<24, j=0; j < 8; ++j) - s = (s << 1) ^ (s >= (1<<31) ? CRC32_POLY : 0); + s = (s << 1) ^ (s >= (1U<<31) ? CRC32_POLY : 0); crc_table[i] = s; } } @@ -624,15 +593,15 @@ static int ilog(int32 n) static signed char log2_4[16] = { 0,1,2,2,3,3,3,3,4,4,4,4,4,4,4,4 }; // 2 compares if n < 16, 3 compares otherwise (4 if signed or n > 1<<29) - if (n < (1U << 14)) - if (n < (1U << 4)) return 0 + log2_4[n ]; - else if (n < (1U << 9)) return 5 + log2_4[n >> 5]; + if (n < (1 << 14)) + if (n < (1 << 4)) return 0 + log2_4[n ]; + else if (n < (1 << 9)) return 5 + log2_4[n >> 5]; else return 10 + log2_4[n >> 10]; - else if (n < (1U << 24)) - if (n < (1U << 19)) return 15 + log2_4[n >> 15]; + else if (n < (1 << 24)) + if (n < (1 << 19)) return 15 + log2_4[n >> 15]; else return 20 + log2_4[n >> 20]; - else if (n < (1U << 29)) return 25 + log2_4[n >> 25]; - else if (n < (1U << 31)) return 30 + log2_4[n >> 30]; + else if (n < (1 << 29)) return 25 + log2_4[n >> 25]; + else if (n < (1 << 31)) return 30 + log2_4[n >> 30]; else return 0; // signed n returns 0 } @@ -745,7 +714,13 @@ static void compute_accelerated_huffman(Codebook *c) } } -static int uint32_compare(const void *p, const void *q) +#ifdef _MSC_VER +#define STBV_CDECL __cdecl +#else +#define STBV_CDECL +#endif + +static int STBV_CDECL uint32_compare(const void *p, const void *q) { uint32 x = * (uint32 *) p; uint32 y = * (uint32 *) q; @@ -901,7 +876,7 @@ typedef struct uint16 x,y; } Point; -int point_compare(const void *p, const void *q) +static int STBV_CDECL point_compare(const void *p, const void *q) { Point *a = (Point *) p; Point *b = (Point *) q; @@ -1027,7 +1002,7 @@ static int capture_pattern(vorb *f) static int start_page_no_capturepattern(vorb *f) { - uint32 loc0,loc1,n,i; + uint32 loc0,loc1,n; // stream structure version if (0 != get8(f)) return error(f, VORBIS_invalid_stream_structure_version); // header flag @@ -1050,7 +1025,8 @@ static int start_page_no_capturepattern(vorb *f) return error(f, VORBIS_unexpected_eof); // assume we _don't_ know any the sample position of any segments f->end_seg_with_known_loc = -2; - if (loc0 != ~0 || loc1 != ~0) { + if (loc0 != ~0U || loc1 != ~0U) { + int i; // determine which packet is the last one that will complete for (i=f->segment_count-1; i >= 0; --i) if (f->segments[i] < 255) @@ -1147,9 +1123,10 @@ static int next_segment(vorb *f) static int get8_packet_raw(vorb *f) { - if (!f->bytes_in_seg) + if (!f->bytes_in_seg) { // CLANG! if (f->last_seg) return EOP; else if (!next_segment(f)) return EOP; + } assert(f->bytes_in_seg > 0); --f->bytes_in_seg; ++f->packet_bytes; @@ -1200,14 +1177,6 @@ static uint32 get_bits(vorb *f, int n) return z; } -static int32 get_bits_signed(vorb *f, int n) -{ - uint32 z = get_bits(f, n); - if (z & (1 << (n-1))) - z += ~((1 << n) - 1); - return (int32) z; -} - // @OPTIMIZE: primary accumulator for huffman // expand the buffer to as many bits as possible without reading off end of packet // it might be nice to allow f->valid_bits and f->acc to be stored in registers, @@ -1291,23 +1260,6 @@ static int codebook_decode_scalar_raw(vorb *f, Codebook *c) return -1; } -static int codebook_decode_scalar(vorb *f, Codebook *c) -{ - int i; - if (f->valid_bits < STB_VORBIS_FAST_HUFFMAN_LENGTH) - prep_huffman(f); - // fast huffman table lookup - i = f->acc & FAST_HUFFMAN_TABLE_MASK; - i = c->fast_huffman[i]; - if (i >= 0) { - f->acc >>= c->codeword_lengths[i]; - f->valid_bits -= c->codeword_lengths[i]; - if (f->valid_bits < 0) { f->valid_bits = 0; return -1; } - return i; - } - return codebook_decode_scalar_raw(f,c); -} - #ifndef STB_VORBIS_NO_INLINE_DECODE #define DECODE_RAW(var, f,c) \ @@ -1326,6 +1278,23 @@ static int codebook_decode_scalar(vorb *f, Codebook *c) #else +static int codebook_decode_scalar(vorb *f, Codebook *c) +{ + int i; + if (f->valid_bits < STB_VORBIS_FAST_HUFFMAN_LENGTH) + prep_huffman(f); + // fast huffman table lookup + i = f->acc & FAST_HUFFMAN_TABLE_MASK; + i = c->fast_huffman[i]; + if (i >= 0) { + f->acc >>= c->codeword_lengths[i]; + f->valid_bits -= c->codeword_lengths[i]; + if (f->valid_bits < 0) { f->valid_bits = 0; return -1; } + return i; + } + return codebook_decode_scalar_raw(f,c); +} + #define DECODE_RAW(var,f,c) var = codebook_decode_scalar(f,c); #endif @@ -1357,7 +1326,7 @@ static int codebook_decode_scalar(vorb *f, Codebook *c) #define CODEBOOK_ELEMENT_BASE(c) (0) #endif -static int codebook_decode_start(vorb *f, Codebook *c, int len) +static int codebook_decode_start(vorb *f, Codebook *c) { int z = -1; @@ -1379,7 +1348,7 @@ static int codebook_decode_start(vorb *f, Codebook *c, int len) static int codebook_decode(vorb *f, Codebook *c, float *output, int len) { - int i,z = codebook_decode_start(f,c,len); + int i,z = codebook_decode_start(f,c); if (z < 0) return FALSE; if (len > c->dimensions) len = c->dimensions; @@ -1418,7 +1387,7 @@ static int codebook_decode(vorb *f, Codebook *c, float *output, int len) static int codebook_decode_step(vorb *f, Codebook *c, float *output, int len, int step) { - int i,z = codebook_decode_start(f,c,len); + int i,z = codebook_decode_start(f,c); float last = CODEBOOK_ELEMENT_BASE(c); if (z < 0) return FALSE; if (len > c->dimensions) len = c->dimensions; @@ -1482,7 +1451,8 @@ static int codebook_decode_deinterleave_repeat(vorb *f, Codebook *c, float **out for (i=0; i < effective; ++i) { int off = (z / div) % c->lookup_values; float val = CODEBOOK_ELEMENT_FAST(c,off) + last; - outputs[c_inter][p_inter] += val; + if (outputs[c_inter]) + outputs[c_inter][p_inter] += val; if (++c_inter == ch) { c_inter = 0; ++p_inter; } if (c->sequence_p) last = val; div *= c->lookup_values; @@ -1494,14 +1464,16 @@ static int codebook_decode_deinterleave_repeat(vorb *f, Codebook *c, float **out if (c->sequence_p) { for (i=0; i < effective; ++i) { float val = CODEBOOK_ELEMENT_FAST(c,z+i) + last; - outputs[c_inter][p_inter] += val; + if (outputs[c_inter]) + outputs[c_inter][p_inter] += val; if (++c_inter == ch) { c_inter = 0; ++p_inter; } last = val; } } else { for (i=0; i < effective; ++i) { float val = CODEBOOK_ELEMENT_FAST(c,z+i) + last; - outputs[c_inter][p_inter] += val; + if (outputs[c_inter]) + outputs[c_inter][p_inter] += val; if (++c_inter == ch) { c_inter = 0; ++p_inter; } } } @@ -1549,7 +1521,8 @@ static int codebook_decode_deinterleave_repeat_2(vorb *f, Codebook *c, float **o // haven't optimized this case because I don't have any examples for (i=0; i < effective; ++i) { float val = CODEBOOK_ELEMENT_FAST(c,z+i) + last; - outputs[c_inter][p_inter] += val; + if (outputs[c_inter]) + outputs[c_inter][p_inter] += val; if (++c_inter == 2) { c_inter = 0; ++p_inter; } last = val; } @@ -1557,7 +1530,8 @@ static int codebook_decode_deinterleave_repeat_2(vorb *f, Codebook *c, float **o i=0; if (c_inter == 1) { float val = CODEBOOK_ELEMENT_FAST(c,z+i) + last; - outputs[c_inter][p_inter] += val; + if (outputs[c_inter]) + outputs[c_inter][p_inter] += val; c_inter = 0; ++p_inter; ++i; } @@ -1565,15 +1539,20 @@ static int codebook_decode_deinterleave_repeat_2(vorb *f, Codebook *c, float **o float *z0 = outputs[0]; float *z1 = outputs[1]; for (; i+1 < effective;) { - z0[p_inter] += CODEBOOK_ELEMENT_FAST(c,z+i) + last; - z1[p_inter] += CODEBOOK_ELEMENT_FAST(c,z+i+1) + last; + float v0 = CODEBOOK_ELEMENT_FAST(c,z+i) + last; + float v1 = CODEBOOK_ELEMENT_FAST(c,z+i+1) + last; + if (z0) + z0[p_inter] += v0; + if (z1) + z1[p_inter] += v1; ++p_inter; i += 2; } } if (i < effective) { float val = CODEBOOK_ELEMENT_FAST(c,z+i) + last; - outputs[c_inter][p_inter] += val; + if (outputs[c_inter]) + outputs[c_inter][p_inter] += val; if (++c_inter == 2) { c_inter = 0; ++p_inter; } } } @@ -1774,7 +1753,6 @@ static void decode_residue(vorb *f, float *residue_buffers[], int ch, int n, int memset(residue_buffers[i], 0, sizeof(float) * n); if (rtype == 2 && ch != 1) { - int len = ch * n; for (j=0; j < ch; ++j) if (!do_not_decode[j]) break; @@ -2020,7 +1998,7 @@ void inverse_mdct_slow(float *buffer, int n, vorb *f, int blocktype) } free(x); } -#else +#elif 0 // transform to use a slow dct-iv; this is STILL basically trivial, // but only requires half as many ops void dct_iv_slow(float *buffer, int n) @@ -2036,10 +2014,8 @@ void dct_iv_slow(float *buffer, int n) float acc = 0; for (j=0; j < n; ++j) acc += x[j] * mcos[((2 * i + 1)*(2*j+1)) & nmask]; - //acc += x[j] * cos(M_PI / n * (i + 0.5) * (j + 0.5)); buffer[i] = acc; } - free(x); } void inverse_mdct_slow(float *buffer, int n, vorb *f, int blocktype) @@ -2280,7 +2256,6 @@ static __forceinline void iter_54(float *z) static void imdct_step3_inner_s_loop_ld654(int n, float *e, int i_off, float *A, int base_n) { - int k_off = -8; int a_off = base_n >> 3; float A2 = A[0+a_off]; float *z = e + i_off; @@ -2326,7 +2301,7 @@ static void imdct_step3_inner_s_loop_ld654(int n, float *e, int i_off, float *A, static void inverse_mdct(float *buffer, int n, vorb *f, int blocktype) { int n2 = n >> 1, n4 = n >> 2, n8 = n >> 3, l; - int n3_4 = n - n4, ld; + int ld; // @OPTIMIZE: reduce register pressure by using fewer variables? int save_point = temp_alloc_save(f); float *buf2 = (float *) temp_alloc(f, n2 * sizeof(*buf2)); @@ -2860,13 +2835,10 @@ static int vorbis_decode_packet_rest(vorb *f, int *len, Mode *m, int left_start, int i,j,k,n,n2; int zero_channel[256]; int really_zero_channel[256]; - int window_center; // WINDOWING n = f->blocksize[m->blockflag]; - window_center = n >> 1; - map = &f->mapping[m->mapping]; // FLOORS @@ -2980,7 +2952,7 @@ static int vorbis_decode_packet_rest(vorb *f, int *len, Mode *m, int left_start, // RESIDUE DECODE for (i=0; i < map->submaps; ++i) { float *residue_buffers[STB_VORBIS_MAX_CHANNELS]; - int r,t; + int r; uint8 do_not_decode[256]; int ch = 0; for (j=0; j < f->channels; ++j) { @@ -2996,7 +2968,6 @@ static int vorbis_decode_packet_rest(vorb *f, int *len, Mode *m, int left_start, } } r = map->submap_residue[i]; - t = f->residue_types[r]; decode_residue(f, residue_buffers, ch, n2, r, do_not_decode); } @@ -3238,7 +3209,7 @@ static int is_whole_packet_present(stb_vorbis *f, int end_page) } if (end_page) if (s < n-1) return error(f, VORBIS_invalid_stream); - if (s == f->segment_count) + if (s == n) s = -1; // set 'crosses page' flag if (p > f->stream_end) return error(f, VORBIS_need_more_data); first = FALSE; @@ -3389,7 +3360,6 @@ static int start_decoder(vorb *f) // compute the size of the sorted tables if (c->sparse) { sorted_count = total; - //assert(total != 0); } else { sorted_count = 0; #ifndef STB_VORBIS_NO_HUFFMAN_BINARY_SEARCH @@ -3506,10 +3476,12 @@ static int start_decoder(vorb *f) #else for (j=0; j < (int) c->lookup_values; ++j) c->multiplicands[j] = mults[j] * c->delta_value + c->minimum_value; - setup_temp_free(f, mults,sizeof(mults[0])*c->lookup_values); #endif + setup_temp_free(f, mults,sizeof(mults[0])*c->lookup_values); } +#ifndef STB_VORBIS_DIVIDES_IN_CODEBOOK skip:; +#endif #ifdef STB_VORBIS_CODEBOOK_FLOATS if (c->lookup_type == 2 && c->sequence_p) { @@ -3590,7 +3562,7 @@ static int start_decoder(vorb *f) g->sorted_order[j] = (uint8) p[j].y; // precompute the neighbors for (j=2; j < g->values; ++j) { - int low,hi; + int low = 0,hi = 0; neighbors(g->Xlist, j, &low,&hi); g->neighbors[j][0] = low; g->neighbors[j][1] = hi; @@ -3656,7 +3628,7 @@ static int start_decoder(vorb *f) if (mapping_type != 0) return error(f, VORBIS_invalid_setup); m->chan = (MappingChannel *) setup_malloc(f, f->channels * sizeof(*m->chan)); if (get_bits(f,1)) - m->submaps = get_bits(f,4); + m->submaps = get_bits(f,4)+1; else m->submaps = 1; if (m->submaps > max_submaps) @@ -3664,8 +3636,8 @@ static int start_decoder(vorb *f) if (get_bits(f,1)) { m->coupling_steps = get_bits(f,8)+1; for (k=0; k < m->coupling_steps; ++k) { - m->chan[k].magnitude = get_bits(f, ilog(f->channels)-1); - m->chan[k].angle = get_bits(f, ilog(f->channels)-1); + m->chan[k].magnitude = get_bits(f, ilog(f->channels-1)); + m->chan[k].angle = get_bits(f, ilog(f->channels-1)); if (m->chan[k].magnitude >= f->channels) return error(f, VORBIS_invalid_setup); if (m->chan[k].angle >= f->channels) return error(f, VORBIS_invalid_setup); if (m->chan[k].magnitude == m->chan[k].angle) return error(f, VORBIS_invalid_setup); @@ -3814,6 +3786,7 @@ static void vorbis_deinit(stb_vorbis *p) setup_free(p, p->B[i]); setup_free(p, p->C[i]); setup_free(p, p->window[i]); + setup_free(p, p->bit_reverse[i]); } #ifndef STB_VORBIS_NO_STDIO if (p->close_on_free) fclose(p->f); @@ -3970,7 +3943,7 @@ static int vorbis_search_for_page_pushdata(vorb *f, uint8 *data, int data_len) f->next_seg = -1; // start a new page f->current_loc = f->scan[i].sample_loc; // set the current sample location // to the amount we'd have decoded had we decoded this page - f->current_loc_valid = f->current_loc != ~0; + f->current_loc_valid = f->current_loc != ~0U; return data_len; } // delete entry @@ -4153,11 +4126,12 @@ static uint32 vorbis_find_page(stb_vorbis *f, uint32 *end, uint32 *last) // invalid-but-useful files? if (end) *end = stb_vorbis_get_file_offset(f); - if (last) + if (last) { if (header[5] & 0x04) *last = 1; else *last = 0; + } set_file_offset(f, retry_loc-1); return 1; } @@ -4190,7 +4164,7 @@ static int vorbis_analyze_page(stb_vorbis *f, ProbedPage *z) { uint8 header[27], lacing[255]; uint8 packet_type[255]; - int num_packet, packet_start, previous =0; + int num_packet, packet_start; int i,len; uint32 samples; @@ -4223,7 +4197,7 @@ static int vorbis_analyze_page(stb_vorbis *f, ProbedPage *z) // scan through the frames to determine the sample-count of each one... // our goal is the sample # of the first fully-decoded sample on the - // page, which is the first decoded sample of the 2nd page + // page, which is the first decoded sample of the 2nd packet num_packet=0; @@ -4231,18 +4205,15 @@ static int vorbis_analyze_page(stb_vorbis *f, ProbedPage *z) for (i=0; i < header[26]; ++i) { if (packet_start) { - uint8 n,b,m; + uint8 n,b; if (lacing[i] == 0) goto bail; // trying to read from zero-length packet n = get8(f); // if bottom bit is non-zero, we've got corruption if (n & 1) goto bail; n >>= 1; b = ilog(f->mode_count-1); - m = n >> b; n &= (1 << b)-1; if (n >= f->mode_count) goto bail; - if (num_packet == 0 && f->mode_config[n].blockflag) - previous = (m & 1); packet_type[num_packet++] = f->mode_config[n].blockflag; skip(f, lacing[i]-1); } else @@ -4531,7 +4502,7 @@ unsigned int stb_vorbis_stream_length_in_samples(stb_vorbis *f) if (IS_PUSH_MODE(f)) return error(f, VORBIS_invalid_api_mixing); if (!f->total_samples) { - int last; + unsigned int last; uint32 lo,hi; char header[6]; @@ -4549,7 +4520,7 @@ unsigned int stb_vorbis_stream_length_in_samples(stb_vorbis *f) // previous_safe is now our candidate 'earliest known place that seeking // to will lead to the final page' - if (!vorbis_find_page(f, &end, (int unsigned *)&last)) { + if (!vorbis_find_page(f, &end, &last)) { // if we can't find a page, we're hosed! f->error = VORBIS_cant_find_last_page; f->total_samples = 0xffffffff; @@ -4564,7 +4535,7 @@ unsigned int stb_vorbis_stream_length_in_samples(stb_vorbis *f) // explicitly checking the length of the section while (!last) { set_file_offset(f, end); - if (!vorbis_find_page(f, &end, (int unsigned *)&last)) { + if (!vorbis_find_page(f, &end, &last)) { // the last page we found didn't have the 'last page' flag // set. whoops! break; @@ -4663,7 +4634,7 @@ stb_vorbis * stb_vorbis_open_file(FILE *file, int close_on_free, int *error, stb return stb_vorbis_open_file_section(file, close_on_free, error, alloc, len); } -stb_vorbis * stb_vorbis_open_filename(char *filename, int *error, stb_vorbis_alloc *alloc) +stb_vorbis * stb_vorbis_open_filename(const char *filename, int *error, stb_vorbis_alloc *alloc) { FILE *f = fopen(filename, "rb"); if (f) @@ -4673,14 +4644,14 @@ stb_vorbis * stb_vorbis_open_filename(char *filename, int *error, stb_vorbis_all } #endif // STB_VORBIS_NO_STDIO -stb_vorbis * stb_vorbis_open_memory(unsigned char *data, int len, int *error, stb_vorbis_alloc *alloc) +stb_vorbis * stb_vorbis_open_memory(const unsigned char *data, int len, int *error, stb_vorbis_alloc *alloc) { stb_vorbis *f, p; if (data == NULL) return NULL; vorbis_init(&p, alloc); - p.stream = data; - p.stream_end = data + len; - p.stream_start = p.stream; + p.stream = (uint8 *) data; + p.stream_end = (uint8 *) data + len; + p.stream_start = (uint8 *) p.stream; p.stream_len = len; p.push_mode = FALSE; if (start_decoder(&p)) { @@ -4773,7 +4744,6 @@ static void compute_samples(int mask, short *output, int num_c, float **data, in } } -static int channel_selector[3][2] = { {0}, {PLAYBACK_MONO}, {PLAYBACK_LEFT, PLAYBACK_RIGHT} }; static void compute_stereo_samples(short *output, int num_c, float **data, int d_offset, int len) { #define BUFFER_SIZE 32 @@ -4823,7 +4793,7 @@ static void convert_samples_short(int buf_c, short **buffer, int b_offset, int d } else { int limit = buf_c < data_c ? buf_c : data_c; for (i=0; i < limit; ++i) - copy_samples(buffer[i]+b_offset, data[i], samples); + copy_samples(buffer[i]+b_offset, data[i]+d_offset, samples); for ( ; i < buf_c; ++i) memset(buffer[i]+b_offset, 0, sizeof(short) * samples); } @@ -4919,7 +4889,7 @@ int stb_vorbis_get_samples_short(stb_vorbis *f, int channels, short **buffer, in } #ifndef STB_VORBIS_NO_STDIO -int stb_vorbis_decode_filename(char *filename, int *channels, short **output) +int stb_vorbis_decode_filename(const char *filename, int *channels, int *sample_rate, short **output) { int data_len, offset, total, limit, error; short *data; @@ -4927,6 +4897,8 @@ int stb_vorbis_decode_filename(char *filename, int *channels, short **output) if (v == NULL) return -1; limit = v->channels * 4096; *channels = v->channels; + if (sample_rate) + *sample_rate = v->sample_rate; offset = data_len = 0; total = limit; data = (short *) malloc(total * sizeof(*data)); @@ -4952,11 +4924,12 @@ int stb_vorbis_decode_filename(char *filename, int *channels, short **output) } } *output = data; + stb_vorbis_close(v); return data_len; } #endif // NO_STDIO -int stb_vorbis_decode_memory(uint8 *mem, int len, int *channels, short **output) +int stb_vorbis_decode_memory(const uint8 *mem, int len, int *channels, int *sample_rate, short **output) { int data_len, offset, total, limit, error; short *data; @@ -4964,6 +4937,8 @@ int stb_vorbis_decode_memory(uint8 *mem, int len, int *channels, short **output) if (v == NULL) return -1; limit = v->channels * 4096; *channels = v->channels; + if (sample_rate) + *sample_rate = v->sample_rate; offset = data_len = 0; total = limit; data = (short *) malloc(total * sizeof(*data)); @@ -4989,9 +4964,10 @@ int stb_vorbis_decode_memory(uint8 *mem, int len, int *channels, short **output) } } *output = data; + stb_vorbis_close(v); return data_len; } -#endif +#endif // STB_VORBIS_NO_INTEGER_CONVERSION int stb_vorbis_get_samples_float_interleaved(stb_vorbis *f, int channels, float *buffer, int num_floats) { @@ -5012,8 +4988,10 @@ int stb_vorbis_get_samples_float_interleaved(stb_vorbis *f, int channels, float } n += k; f->channel_buffer_start += k; - if (n == len) break; - if (!stb_vorbis_get_frame_float(f, NULL, &outputs)) break; + if (n == len) + break; + if (!stb_vorbis_get_frame_float(f, NULL, &outputs)) + break; } return n; } @@ -5030,17 +5008,63 @@ int stb_vorbis_get_samples_float(stb_vorbis *f, int channels, float **buffer, in if (n+k >= num_samples) k = num_samples - n; if (k) { for (i=0; i < z; ++i) - memcpy(buffer[i]+n, f->channel_buffers+f->channel_buffer_start, sizeof(float)*k); + memcpy(buffer[i]+n, f->channel_buffers[i]+f->channel_buffer_start, sizeof(float)*k); for ( ; i < channels; ++i) memset(buffer[i]+n, 0, sizeof(float) * k); } n += k; f->channel_buffer_start += k; - if (n == num_samples) break; - if (!stb_vorbis_get_frame_float(f, NULL, &outputs)) break; + if (n == num_samples) + break; + if (!stb_vorbis_get_frame_float(f, NULL, &outputs)) + break; } return n; } #endif // STB_VORBIS_NO_PULLDATA_API +/* Version history + 1.04 - 2014/08/27 - fix missing const-correct case in API + 1.03 - 2014/08/07 - Warning fixes + 1.02 - 2014/07/09 - Declare qsort compare function _cdecl on windows + 1.01 - 2014/06/18 - fix stb_vorbis_get_samples_float + 1.0 - 2014/05/26 - fix memory leaks; fix warnings; fix bugs in multichannel + (API change) report sample rate for decode-full-file funcs + 0.99996 - bracket #include <malloc.h> for macintosh compilation by Laurent Gomila + 0.99995 - use union instead of pointer-cast for fast-float-to-int to avoid alias-optimization problem + 0.99994 - change fast-float-to-int to work in single-precision FPU mode, remove endian-dependence + 0.99993 - remove assert that fired on legal files with empty tables + 0.99992 - rewind-to-start + 0.99991 - bugfix to stb_vorbis_get_samples_short by Bernhard Wodo + 0.9999 - (should have been 0.99990) fix no-CRT support, compiling as C++ + 0.9998 - add a full-decode function with a memory source + 0.9997 - fix a bug in the read-from-FILE case in 0.9996 addition + 0.9996 - query length of vorbis stream in samples/seconds + 0.9995 - bugfix to another optimization that only happened in certain files + 0.9994 - bugfix to one of the optimizations that caused significant (but inaudible?) errors + 0.9993 - performance improvements; runs in 99% to 104% of time of reference implementation + 0.9992 - performance improvement of IMDCT; now performs close to reference implementation + 0.9991 - performance improvement of IMDCT + 0.999 - (should have been 0.9990) performance improvement of IMDCT + 0.998 - no-CRT support from Casey Muratori + 0.997 - bugfixes for bugs found by Terje Mathisen + 0.996 - bugfix: fast-huffman decode initialized incorrectly for sparse codebooks; fixing gives 10% speedup - found by Terje Mathisen + 0.995 - bugfix: fix to 'effective' overrun detection - found by Terje Mathisen + 0.994 - bugfix: garbage decode on final VQ symbol of a non-multiple - found by Terje Mathisen + 0.993 - bugfix: pushdata API required 1 extra byte for empty page (failed to consume final page if empty) - found by Terje Mathisen + 0.992 - fixes for MinGW warning + 0.991 - turn fast-float-conversion on by default + 0.990 - fix push-mode seek recovery if you seek into the headers + 0.98b - fix to bad release of 0.98 + 0.98 - fix push-mode seek recovery; robustify float-to-int and support non-fast mode + 0.97 - builds under c++ (typecasting, don't use 'class' keyword) + 0.96 - somehow MY 0.95 was right, but the web one was wrong, so here's my 0.95 rereleased as 0.96, fixes a typo in the clamping code + 0.95 - clamping code for 16-bit functions + 0.94 - not publically released + 0.93 - fixed all-zero-floor case (was decoding garbage) + 0.92 - fixed a memory leak + 0.91 - conditional compiles to omit parts of the API and the infrastructure to support them: STB_VORBIS_NO_PULLDATA_API, STB_VORBIS_NO_PUSHDATA_API, STB_VORBIS_NO_STDIO, STB_VORBIS_NO_INTEGER_CONVERSION + 0.90 - first public release +*/ + #endif // STB_VORBIS_HEADER_ONLY diff --git a/src/stb_vorbis.h b/src/stb_vorbis.h index bff27496..eb3afe3b 100644 --- a/src/stb_vorbis.h +++ b/src/stb_vorbis.h @@ -1,30 +1,51 @@ -// Ogg Vorbis I audio decoder -- version 0.99996 +// Ogg Vorbis audio decoder - v1.04 - public domain +// http://nothings.org/stb_vorbis/ // -// Written in April 2007 by Sean Barrett, sponsored by RAD Game Tools. +// Written by Sean Barrett in 2007, last updated in 2014 +// Sponsored by RAD Game Tools. // -// Placed in the public domain April 2007 by the author: no copyright is -// claimed, and you may use it for any purpose you like. +// Placed in the public domain April 2007 by the author: no copyright +// is claimed, and you may use it for any purpose you like. // // No warranty for any purpose is expressed or implied by the author (nor // by RAD Game Tools). Report bugs and send enhancements to the author. // -// Get the latest version and other information at: -// http://nothings.org/stb_vorbis/ - - -// Todo: -// -// - seeking (note you can seek yourself using the pushdata API) -// // Limitations: // -// - floor 0 not supported (used in old ogg vorbis files) +// - seeking not supported except manually via PUSHDATA api +// - floor 0 not supported (used in old ogg vorbis files pre-2004) // - lossless sample-truncation at beginning ignored // - cannot concatenate multiple vorbis streams // - sample positions are 32-bit, limiting seekable 192Khz // files to around 6 hours (Ogg supports 64-bit) -// -// All of these limitations may be removed in future versions. +// +// Bugfix/warning contributors: +// Terje Mathisen Niklas Frykholm Andy Hill +// Casey Muratori John Bolton Gargaj +// Laurent Gomila Marc LeBlanc Ronny Chevalier +// Bernhard Wodo Evan Balster "alxprd"@github +// Tom Beaumont Ingo Leitgeb +// (If you reported a bug but do not appear in this list, it is because +// someone else reported the bug before you. There were too many of you to +// list them all because I was lax about updating for a long time, sorry.) +// +// Partial history: +// 1.04 - 2014/08/27 - fix missing const-correct case in API +// 1.03 - 2014/08/07 - warning fixes +// 1.02 - 2014/07/09 - declare qsort comparison as explicitly _cdecl in Windows +// 1.01 - 2014/06/18 - fix stb_vorbis_get_samples_float (interleaved was correct) +// 1.0 - 2014/05/26 - fix memory leaks; fix warnings; fix bugs in >2-channel; +// (API change) report sample rate for decode-full-file funcs +// 0.99996 - - bracket #include <malloc.h> for macintosh compilation +// 0.99995 - - avoid alias-optimization issue in float-to-int conversion +// +// See end of file for full version history. + + +////////////////////////////////////////////////////////////////////////////// +// +// HEADER BEGINS HERE +// #ifndef STB_VORBIS_INCLUDE_STB_VORBIS_H #define STB_VORBIS_INCLUDE_STB_VORBIS_H @@ -37,9 +58,8 @@ #include <stdio.h> #endif -// NOTE: Added to work with raylib on Android -#if defined(PLATFORM_ANDROID) - #include "utils.h" // Android fopen function map +#ifdef __linux + #include <alloca.h> #endif #ifdef __cplusplus @@ -152,6 +172,7 @@ extern int stb_vorbis_decode_frame_pushdata( // decode a frame of audio sample data if possible from the passed-in data block // // return value: number of bytes we used from datablock +// // possible cases: // 0 bytes used, 0 samples output (need more data) // N bytes used, 0 samples output (resynching the stream, keep going) @@ -196,21 +217,23 @@ extern void stb_vorbis_flush_pushdata(stb_vorbis *f); // just want to go ahead and use pushdata.) #if !defined(STB_VORBIS_NO_STDIO) && !defined(STB_VORBIS_NO_INTEGER_CONVERSION) -extern int stb_vorbis_decode_filename(char *filename, int *channels, short **output); +extern int stb_vorbis_decode_filename(const char *filename, int *channels, int *sample_rate, short **output); +#endif +#if !defined(STB_VORBIS_NO_INTEGER_CONVERSION) +extern int stb_vorbis_decode_memory(const unsigned char *mem, int len, int *channels, int *sample_rate, short **output); #endif -extern int stb_vorbis_decode_memory(unsigned char *mem, int len, int *channels, short **output); // decode an entire file and output the data interleaved into a malloc()ed // buffer stored in *output. The return value is the number of samples // decoded, or -1 if the file could not be opened or was not an ogg vorbis file. // When you're done with it, just free() the pointer returned in *output. -extern stb_vorbis * stb_vorbis_open_memory(unsigned char *data, int len, +extern stb_vorbis * stb_vorbis_open_memory(const unsigned char *data, int len, int *error, stb_vorbis_alloc *alloc_buffer); // create an ogg vorbis decoder from an ogg vorbis stream in memory (note // this must be the entire stream!). on failure, returns NULL and sets *error #ifndef STB_VORBIS_NO_STDIO -extern stb_vorbis * stb_vorbis_open_filename(char *filename, +extern stb_vorbis * stb_vorbis_open_filename(const char *filename, int *error, stb_vorbis_alloc *alloc_buffer); // create an ogg vorbis decoder from a filename via fopen(). on failure, // returns NULL and sets *error (possibly to VORBIS_file_open_failure). @@ -349,4 +372,8 @@ enum STBVorbisError } #endif -#endif // STB_VORBIS_INCLUDE_STB_VORBIS_H
\ No newline at end of file +#endif // STB_VORBIS_INCLUDE_STB_VORBIS_H +// +// HEADER ENDS HERE +// +//////////////////////////////////////////////////////////////////////////////
\ No newline at end of file @@ -45,16 +45,7 @@ //---------------------------------------------------------------------------------- // Types and Structures Definition //---------------------------------------------------------------------------------- -typedef unsigned char byte; - -// SpriteFont one Character (Glyph) data -typedef struct Character { - int value; //char value = ' '; (int)value = 32; - int x; - int y; - int w; - int h; -} Character; +// ... //---------------------------------------------------------------------------------- // Global variables @@ -72,7 +63,6 @@ static SpriteFont defaultFont; // Default font provided by raylib //---------------------------------------------------------------------------------- static bool PixelIsMagenta(Color p); // Check if a pixel is magenta static int ParseImageData(Color *imgDataPixel, int imgWidth, int imgHeight, Character **charSet); // Parse image pixel data to obtain character set measures -static int GetNextPOT(int num); // Calculate next power-of-two value for a given value static SpriteFont LoadRBMF(const char *fileName); // Load a rBMF font file (raylib BitMap Font) extern void LoadDefaultFont(void); @@ -144,7 +134,7 @@ extern void LoadDefaultFont(void) if (counter > 256) counter = 0; // Security check... } - defaultFont.texture = CreateTexture(image, false); // Convert loaded image to OpenGL texture + defaultFont.texture = LoadTextureFromImage(image, false); // Convert loaded image to OpenGL texture UnloadImage(image); // Reconstruct charSet using charsWidth[], charsHeight, charsDivisor, numChars @@ -177,7 +167,7 @@ extern void LoadDefaultFont(void) else currentPosX = testPosX; } - TraceLog(INFO, "Default font loaded successfully"); + TraceLog(INFO, "[TEX ID %i] Default font loaded successfully", defaultFont.texture.id); } extern void UnloadDefaultFont(void) @@ -204,9 +194,10 @@ SpriteFont LoadSpriteFont(const char *fileName) Image image = LoadImage(fileName); // At this point we have a pixel array with all the data... - - TraceLog(INFO, "[%s] SpriteFont image loaded: %i x %i", fileName, image.width, image.height); - + +#if defined(PLATFORM_RPI) || defined(PLATFORM_WEB) + ConvertToPOT(&image, MAGENTA); +#endif // Process bitmap Font pixel data to get measures (Character array) // spriteFont.charSet data is filled inside the function and memory is allocated! int numChars = ParseImageData(image.pixels, image.width, image.height, &spriteFont.charSet); @@ -216,40 +207,8 @@ SpriteFont LoadSpriteFont(const char *fileName) spriteFont.numChars = numChars; - // Convert image font to POT image before conversion to texture - // NOTE: Not required, we skip this step -/* - // Just add the required amount of pixels at the right and bottom sides of image... - int potWidth = GetNextPOT(image.width); - int potHeight = GetNextPOT(image.height); - - // Check if POT texture generation is required (if texture is not already POT) - if ((potWidth != image.width) || (potHeight != image.height)) - { - Color *imgDataPixelPOT = NULL; - - // Generate POT array from NPOT data - imgDataPixelPOT = (Color *)malloc(potWidth * potHeight * sizeof(Color)); - - for (int j = 0; j < potHeight; j++) - { - for (int i = 0; i < potWidth; i++) - { - if ((j < image.height) && (i < image.width)) imgDataPixelPOT[j*potWidth + i] = image.pixels[j*image.width + i]; - else imgDataPixelPOT[j*potWidth + i] = MAGENTA; - } - } - - TraceLog(WARNING, "SpriteFont texture converted to POT: %ix%i", potWidth, potHeight); - - free(image.pixels); - - image.pixels = imgDataPixelPOT; - image.width = potWidth; - image.height = potHeight; - } -*/ - spriteFont.texture = CreateTexture(image, false); // Convert loaded image to OpenGL texture + spriteFont.texture = LoadTextureFromImage(image, false); // Convert loaded image to OpenGL texture + UnloadImage(image); } @@ -484,23 +443,6 @@ static int ParseImageData(Color *imgDataPixel, int imgWidth, int imgHeight, Char return index; } -// Calculate next power-of-two value for a given num -static int GetNextPOT(int num) -{ - if (num != 0) - { - num--; - num |= (num >> 1); // Or first 2 bits - num |= (num >> 2); // Or next 2 bits - num |= (num >> 4); // Or next 4 bits - num |= (num >> 8); // Or next 8 bits - num |= (num >> 16); // Or next 16 bits - num++; - } - - return num; -} - // Load a rBMF font file (raylib BitMap Font) static SpriteFont LoadRBMF(const char *fileName) { @@ -526,89 +468,96 @@ static SpriteFont LoadRBMF(const char *fileName) Image image; rbmfInfoHeader rbmfHeader; - unsigned int *rbmfFileData; - unsigned char *rbmfCharWidthData; + unsigned int *rbmfFileData = NULL; + unsigned char *rbmfCharWidthData = NULL; int charsDivisor = 1; // Every char is separated from the consecutive by a 1 pixel divisor, horizontally and vertically FILE *rbmfFile = fopen(fileName, "rb"); // Define a pointer to bitmap file and open it in read-binary mode - fread(&rbmfHeader, sizeof(rbmfInfoHeader), 1, rbmfFile); + if (rbmfFile == NULL) + { + TraceLog(WARNING, "[%s] rBMF font file could not be opened", fileName); + } + else + { + fread(&rbmfHeader, sizeof(rbmfInfoHeader), 1, rbmfFile); - TraceLog(INFO, "[%s] Loading rBMF file, size: %ix%i, numChars: %i, charHeight: %i", fileName, rbmfHeader.imgWidth, rbmfHeader.imgHeight, rbmfHeader.numChars, rbmfHeader.charHeight); + TraceLog(INFO, "[%s] Loading rBMF file, size: %ix%i, numChars: %i, charHeight: %i", fileName, rbmfHeader.imgWidth, rbmfHeader.imgHeight, rbmfHeader.numChars, rbmfHeader.charHeight); - spriteFont.numChars = (int)rbmfHeader.numChars; + spriteFont.numChars = (int)rbmfHeader.numChars; - image.width = (int)rbmfHeader.imgWidth; - image.height = (int)rbmfHeader.imgHeight; + image.width = (int)rbmfHeader.imgWidth; + image.height = (int)rbmfHeader.imgHeight; - int numPixelBits = rbmfHeader.imgWidth * rbmfHeader.imgHeight / 32; + int numPixelBits = rbmfHeader.imgWidth * rbmfHeader.imgHeight / 32; - rbmfFileData = (unsigned int *)malloc(numPixelBits * sizeof(unsigned int)); + rbmfFileData = (unsigned int *)malloc(numPixelBits * sizeof(unsigned int)); - for(int i = 0; i < numPixelBits; i++) fread(&rbmfFileData[i], sizeof(unsigned int), 1, rbmfFile); + for(int i = 0; i < numPixelBits; i++) fread(&rbmfFileData[i], sizeof(unsigned int), 1, rbmfFile); - rbmfCharWidthData = (unsigned char *)malloc(spriteFont.numChars * sizeof(unsigned char)); + rbmfCharWidthData = (unsigned char *)malloc(spriteFont.numChars * sizeof(unsigned char)); - for(int i = 0; i < spriteFont.numChars; i++) fread(&rbmfCharWidthData[i], sizeof(unsigned char), 1, rbmfFile); + for(int i = 0; i < spriteFont.numChars; i++) fread(&rbmfCharWidthData[i], sizeof(unsigned char), 1, rbmfFile); - // Re-construct image from rbmfFileData - //----------------------------------------- - image.pixels = (Color *)malloc(image.width * image.height * sizeof(Color)); + // Re-construct image from rbmfFileData + //----------------------------------------- + image.pixels = (Color *)malloc(image.width * image.height * sizeof(Color)); - for (int i = 0; i < image.width * image.height; i++) image.pixels[i] = BLANK; // Initialize array + for (int i = 0; i < image.width * image.height; i++) image.pixels[i] = BLANK; // Initialize array - int counter = 0; // Font data elements counter + int counter = 0; // Font data elements counter - // Fill image data (convert from bit to pixel!) - for (int i = 0; i < image.width * image.height; i += 32) - { - for (int j = 31; j >= 0; j--) + // Fill image data (convert from bit to pixel!) + for (int i = 0; i < image.width * image.height; i += 32) { - if (BIT_CHECK(rbmfFileData[counter], j)) image.pixels[i+j] = WHITE; - } + for (int j = 31; j >= 0; j--) + { + if (BIT_CHECK(rbmfFileData[counter], j)) image.pixels[i+j] = WHITE; + } - counter++; - } + counter++; + } - TraceLog(INFO, "[%s] Image reconstructed correctly, now converting it to texture", fileName); + TraceLog(INFO, "[%s] Image reconstructed correctly, now converting it to texture", fileName); - spriteFont.texture = CreateTexture(image, false); - UnloadImage(image); // Unload image data + spriteFont.texture = LoadTextureFromImage(image, false); + UnloadImage(image); // Unload image data - TraceLog(INFO, "[%s] Starting charSet reconstruction", fileName); + //TraceLog(INFO, "[%s] Starting charSet reconstruction", fileName); - // Reconstruct charSet using rbmfCharWidthData, rbmfHeader.charHeight, charsDivisor, rbmfHeader.numChars - spriteFont.charSet = (Character *)malloc(spriteFont.numChars * sizeof(Character)); // Allocate space for our character data + // Reconstruct charSet using rbmfCharWidthData, rbmfHeader.charHeight, charsDivisor, rbmfHeader.numChars + spriteFont.charSet = (Character *)malloc(spriteFont.numChars * sizeof(Character)); // Allocate space for our character data - int currentLine = 0; - int currentPosX = charsDivisor; - int testPosX = charsDivisor; + int currentLine = 0; + int currentPosX = charsDivisor; + int testPosX = charsDivisor; - for (int i = 0; i < spriteFont.numChars; i++) - { - spriteFont.charSet[i].value = (int)rbmfHeader.firstChar + i; - spriteFont.charSet[i].x = currentPosX; - spriteFont.charSet[i].y = charsDivisor + currentLine * ((int)rbmfHeader.charHeight + charsDivisor); - spriteFont.charSet[i].w = (int)rbmfCharWidthData[i]; - spriteFont.charSet[i].h = (int)rbmfHeader.charHeight; + for (int i = 0; i < spriteFont.numChars; i++) + { + spriteFont.charSet[i].value = (int)rbmfHeader.firstChar + i; + spriteFont.charSet[i].x = currentPosX; + spriteFont.charSet[i].y = charsDivisor + currentLine * ((int)rbmfHeader.charHeight + charsDivisor); + spriteFont.charSet[i].w = (int)rbmfCharWidthData[i]; + spriteFont.charSet[i].h = (int)rbmfHeader.charHeight; - testPosX += (spriteFont.charSet[i].w + charsDivisor); + testPosX += (spriteFont.charSet[i].w + charsDivisor); - if (testPosX > spriteFont.texture.width) - { - currentLine++; - currentPosX = 2 * charsDivisor + (int)rbmfCharWidthData[i]; - testPosX = currentPosX; + if (testPosX > spriteFont.texture.width) + { + currentLine++; + currentPosX = 2 * charsDivisor + (int)rbmfCharWidthData[i]; + testPosX = currentPosX; - spriteFont.charSet[i].x = charsDivisor; - spriteFont.charSet[i].y = charsDivisor + currentLine * (rbmfHeader.charHeight + charsDivisor); + spriteFont.charSet[i].x = charsDivisor; + spriteFont.charSet[i].y = charsDivisor + currentLine * (rbmfHeader.charHeight + charsDivisor); + } + else currentPosX = testPosX; } - else currentPosX = testPosX; - } - - TraceLog(INFO, "[%s] rBMF file loaded correctly as SpriteFont", fileName); + TraceLog(INFO, "[%s] rBMF file loaded correctly as SpriteFont", fileName); + } + fclose(rbmfFile); free(rbmfFileData); // Now we can free loaded data from RAM memory diff --git a/src/textures.c b/src/textures.c index e342e21b..f701c380 100644 --- a/src/textures.c +++ b/src/textures.c @@ -36,6 +36,7 @@ #include "utils.h" // rRES data decompression utility function // NOTE: Includes Android fopen function map +#define STB_IMAGE_IMPLEMENTATION #include "stb_image.h" // Used to read image data (multiple formats support) //---------------------------------------------------------------------------------- @@ -46,8 +47,6 @@ //---------------------------------------------------------------------------------- // Types and Structures Definition //---------------------------------------------------------------------------------- -typedef unsigned char byte; - typedef struct { unsigned char *data; // Image raw data int width; // Image base width @@ -104,6 +103,8 @@ Image LoadImage(const char *fileName) // Force loading to 4 components (RGBA) byte *imgData = stbi_load(fileName, &imgWidth, &imgHeight, &imgBpp, 4); + // TODO: Check if file could be loaded! (imgData == NULL)? + if (imgData != NULL) { // Convert array to pixel array for working convenience @@ -125,9 +126,9 @@ Image LoadImage(const char *fileName) image.width = imgWidth; image.height = imgHeight; - TraceLog(INFO, "[%s] Image loaded successfully", fileName); + TraceLog(INFO, "[%s] Image loaded successfully (%ix%i)", fileName, image.width, image.height); } - else TraceLog(WARNING, "[%s] Image could not be loaded", fileName); + else TraceLog(WARNING, "[%s] Image could not be loaded, file not recognized", fileName); } else if (strcmp(GetExtension(fileName),"dds") == 0) { @@ -174,8 +175,6 @@ Image LoadImage(const char *fileName) // Load an image from rRES file (raylib Resource) Image LoadImageFromRES(const char *rresName, int resId) { - // TODO: rresName could be directly a char array with all the data! --> support it! :P - Image image; bool found = false; @@ -188,7 +187,10 @@ Image LoadImageFromRES(const char *rresName, int resId) FILE *rresFile = fopen(rresName, "rb"); - if (!rresFile) TraceLog(WARNING, "[%s] Could not open raylib resource file", rresName); + if (rresFile == NULL) + { + TraceLog(WARNING, "[%s] rRES raylib resource file could not be opened", rresName); + } else { // Read rres file (basic file check - id) @@ -294,6 +296,11 @@ Image LoadImageFromRES(const char *rresName, int resId) Texture2D LoadTexture(const char *fileName) { Texture2D texture; + + // Init texture to default values + texture.id = 0; + texture.width = 0; + texture.height = 0; if (strcmp(GetExtension(fileName),"dds") == 0) { @@ -333,10 +340,13 @@ Texture2D LoadTexture(const char *fileName) else { Image image = LoadImage(fileName); - + if (image.pixels != NULL) { - texture = CreateTexture(image, false); +#if defined(PLATFORM_RPI) || defined(PLATFORM_WEB) + ConvertToPOT(&image, BLANK); +#endif + texture = LoadTextureFromImage(image, false); UnloadImage(image); } } @@ -344,13 +354,66 @@ Texture2D LoadTexture(const char *fileName) return texture; } +// Load a texture from image data +// NOTE: image is not unloaded, it must be done manually +Texture2D LoadTextureFromImage(Image image, bool genMipmaps) +{ + Texture2D texture; + + // Init texture to default values + texture.id = 0; + texture.width = 0; + texture.height = 0; + + if ((image.pixels != NULL) && (image.width > 0) && (image.height > 0)) + { + unsigned char *imgData = malloc(image.width * image.height * 4); + + int j = 0; + + for (int i = 0; i < image.width * image.height * 4; i += 4) + { + imgData[i] = image.pixels[j].r; + imgData[i+1] = image.pixels[j].g; + imgData[i+2] = image.pixels[j].b; + imgData[i+3] = image.pixels[j].a; + + j++; + } + + // NOTE: rlglLoadTexture() can generate mipmaps (POT image required) + texture.id = rlglLoadTexture(imgData, image.width, image.height, genMipmaps); + + texture.width = image.width; + texture.height = image.height; + + free(imgData); + } + else TraceLog(WARNING, "Texture could not be loaded, image data is not valid"); + + return texture; +} + +// [DEPRECATED] Load a texture from image data +// NOTE: Use LoadTextureFromImage() instead +Texture2D CreateTexture(Image image, bool genMipmaps) +{ + Texture2D texture; + + texture = LoadTextureFromImage(image, genMipmaps); + + TraceLog(INFO, "Created texture id: %i", texture.id); + + return texture; +} + // Load an image as texture from rRES file (raylib Resource) Texture2D LoadTextureFromRES(const char *rresName, int resId) { Texture2D texture; Image image = LoadImageFromRES(rresName, resId); - texture = CreateTexture(image, false); + texture = LoadTextureFromImage(image, false); UnloadImage(image); return texture; @@ -368,6 +431,41 @@ void UnloadTexture(Texture2D texture) rlDeleteTextures(texture.id); } +// Convert image to POT (power-of-two) +// NOTE: Requirement on OpenGL ES 2.0 (RPI, HTML5) +void ConvertToPOT(Image *image, Color fillColor) +{ + // Just add the required amount of pixels at the right and bottom sides of image... + int potWidth = GetNextPOT(image->width); + int potHeight = GetNextPOT(image->height); + + // Check if POT texture generation is required (if texture is not already POT) + if ((potWidth != image->width) || (potHeight != image->height)) + { + Color *imgDataPixelPOT = NULL; + + // Generate POT array from NPOT data + imgDataPixelPOT = (Color *)malloc(potWidth * potHeight * sizeof(Color)); + + for (int j = 0; j < potHeight; j++) + { + for (int i = 0; i < potWidth; i++) + { + if ((j < image->height) && (i < image->width)) imgDataPixelPOT[j*potWidth + i] = image->pixels[j*image->width + i]; + else imgDataPixelPOT[j*potWidth + i] = fillColor; + } + } + + TraceLog(WARNING, "Image converted to POT: (%ix%i) -> (%ix%i)", image->width, image->height, potWidth, potHeight); + + free(image->pixels); + + image->pixels = imgDataPixelPOT; + image->width = potWidth; + image->height = potHeight; + } +} + // Draw a Texture2D void DrawTexture(Texture2D texture, int posX, int posY, Color tint) { @@ -435,49 +533,13 @@ void DrawTexturePro(Texture2D texture, Rectangle sourceRec, Rectangle destRec, V rlDisableTexture(); } -// Create a texture from an image -// NOTE: image is not unloaded, iot must be done manually -Texture2D CreateTexture(Image image, bool genMipmaps) -{ - Texture2D texture; - - // Init texture to default values - texture.id = 0; - texture.width = 0; - texture.height = 0; - - if (image.pixels != NULL) - { - unsigned char *imgData = malloc(image.width * image.height * 4); - - int j = 0; - - for (int i = 0; i < image.width * image.height * 4; i += 4) - { - imgData[i] = image.pixels[j].r; - imgData[i+1] = image.pixels[j].g; - imgData[i+2] = image.pixels[j].b; - imgData[i+3] = image.pixels[j].a; - - j++; - } - - // NOTE: rlglLoadTexture() can generate mipmaps (POT image required) - texture.id = rlglLoadTexture(imgData, image.width, image.height, genMipmaps); - - texture.width = image.width; - texture.height = image.height; - - free(imgData); - } - else TraceLog(WARNING, "Texture could not be created, image data is not valid"); - - return texture; -} +//---------------------------------------------------------------------------------- +// Module specific Functions Definition +//---------------------------------------------------------------------------------- // Loading DDS image data (compressed or uncompressed) // NOTE: Compressed data loading not supported on OpenGL 1.1 -ImageEx LoadDDS(const char *fileName) +static ImageEx LoadDDS(const char *fileName) { #define FOURCC_DXT1 0x31545844 // Equivalent to "DXT1" in ASCII #define FOURCC_DXT3 0x33545844 // Equivalent to "DXT3" in ASCII @@ -527,12 +589,18 @@ ImageEx LoadDDS(const char *fileName) ImageEx image; ddsHeader header; + + image.data = NULL; + image.width = 0; + image.height = 0; + image.mipmaps = 0; + image.compFormat = 0; FILE *ddsFile = fopen(fileName, "rb"); if (ddsFile == NULL) { - TraceLog(WARNING, "DDS File could not be opened"); + TraceLog(WARNING, "[%s] DDS image file could not be opened", fileName); } else { @@ -543,7 +611,7 @@ ImageEx LoadDDS(const char *fileName) if (strncmp(filecode, "DDS ", 4) != 0) { - TraceLog(WARNING, "DDS File does not seem to be valid"); + TraceLog(WARNING, "[%s] DDS file does not seem to be a valid image", fileName); fclose(ddsFile); } else @@ -635,7 +703,7 @@ ImageEx LoadDDS(const char *fileName) // Loading PKM image data (ETC1/ETC2 compression) // NOTE: KTX is the standard Khronos Group compression format (ETC1/ETC2, mipmaps) // PKM is a much simpler file format used mainly to contain a single ETC1/ETC2 compressed image (no mipmaps) -ImageEx LoadPKM(const char *fileName) +static ImageEx LoadPKM(const char *fileName) { // If OpenGL ES 2.0. the following format could be supported (ETC1): //GL_ETC1_RGB8_OES @@ -678,7 +746,7 @@ ImageEx LoadPKM(const char *fileName) if (pkmFile == NULL) { - TraceLog(WARNING, "[%s] PKM File could not be opened", fileName); + TraceLog(WARNING, "[%s] PKM image file could not be opened", fileName); } else { @@ -689,7 +757,7 @@ ImageEx LoadPKM(const char *fileName) if (strncmp(filecode, "PKM ", 4) != 0) { - TraceLog(WARNING, "[%s] PKM File does not seem to be valid", fileName); + TraceLog(WARNING, "[%s] PKM file does not seem to be a valid image", fileName); fclose(pkmFile); } else diff --git a/src/utils.c b/src/utils.c index 26a44065..c3c20b47 100644 --- a/src/utils.c +++ b/src/utils.c @@ -40,7 +40,7 @@ #include <stdarg.h> // Used for functions with variable number of parameters (TraceLog()) //#include <string.h> // String management functions: strlen(), strrchr(), strcmp() -#if defined(PLATFORM_DESKTOP) || defined(PLATFORM_RPI) || defined(PLATFORM_DESKTOP_OSX) +#if defined(PLATFORM_DESKTOP) || defined(PLATFORM_RPI) #define STB_IMAGE_WRITE_IMPLEMENTATION #include "stb_image_write.h" // Create PNG file #endif @@ -107,7 +107,7 @@ unsigned char *DecompressData(const unsigned char *data, unsigned long compSize, return pUncomp; } -#if defined(PLATFORM_DESKTOP) || defined(PLATFORM_RPI) || defined(PLATFORM_DESKTOP_OSX) +#if defined(PLATFORM_DESKTOP) || defined(PLATFORM_RPI) // Creates a bitmap (BMP) file from an array of pixel data // NOTE: This function is not explicitly available to raylib users void WriteBitmap(const char *fileName, unsigned char *imgData, int width, int height) @@ -133,18 +133,25 @@ void WriteBitmap(const char *fileName, unsigned char *imgData, int width, int he FILE *bmpFile = fopen(fileName, "wb"); // Define a pointer to bitmap file and open it in write-binary mode - // NOTE: fwrite parameters are: data pointer, size in bytes of each element to be written, number of elements, file-to-write pointer - fwrite(bmpFileHeader, sizeof(unsigned char), 14, bmpFile); // Write BMP file header data - fwrite(bmpInfoHeader, sizeof(unsigned char), 40, bmpFile); // Write BMP info header data - - // Write pixel data to file - for (int y = 0; y < height ; y++) + if (bmpFile == NULL) + { + TraceLog(WARNING, "[%s] BMP file could not be created", fileName); + } + else { - for (int x = 0; x < width; x++) + // NOTE: fwrite parameters are: data pointer, size in bytes of each element to be written, number of elements, file-to-write pointer + fwrite(bmpFileHeader, sizeof(unsigned char), 14, bmpFile); // Write BMP file header data + fwrite(bmpInfoHeader, sizeof(unsigned char), 40, bmpFile); // Write BMP info header data + + // Write pixel data to file + for (int y = 0; y < height ; y++) { - fputc(imgData[(x*4)+2 + (y*width*4)], bmpFile); - fputc(imgData[(x*4)+1 + (y*width*4)], bmpFile); - fputc(imgData[(x*4) + (y*width*4)], bmpFile); + for (int x = 0; x < width; x++) + { + fputc(imgData[(x*4)+2 + (y*width*4)], bmpFile); + fputc(imgData[(x*4)+1 + (y*width*4)], bmpFile); + fputc(imgData[(x*4) + (y*width*4)], bmpFile); + } } } @@ -157,7 +164,9 @@ void WritePNG(const char *fileName, unsigned char *imgData, int width, int heigh { stbi_write_png(fileName, width, height, 4, imgData, width*4); // It WORKS!!! } +#endif +#if defined(PLATFORM_DESKTOP) || defined(PLATFORM_RPI) || defined(PLATFORM_WEB) // Outputs a trace log message (INFO, ERROR, WARNING) // NOTE: If a file has been init, output log is written there void TraceLog(int msgType, const char *text, ...) @@ -262,6 +271,23 @@ const char *GetExtension(const char *fileName) return (dot + 1); } +// Calculate next power-of-two value for a given num +int GetNextPOT(int num) +{ + if (num != 0) + { + num--; + num |= (num >> 1); // Or first 2 bits + num |= (num >> 2); // Or next 2 bits + num |= (num >> 4); // Or next 4 bits + num |= (num >> 8); // Or next 8 bits + num |= (num >> 16); // Or next 16 bits + num++; + } + + return num; +} + //---------------------------------------------------------------------------------- // Module specific Functions Definition //---------------------------------------------------------------------------------- diff --git a/src/utils.h b/src/utils.h index 784c7926..882aebf6 100644 --- a/src/utils.h +++ b/src/utils.h @@ -77,6 +77,7 @@ void WritePNG(const char *fileName, unsigned char *imgData, int width, int heigh void TraceLog(int msgType, const char *text, ...); // Outputs a trace log message const char *GetExtension(const char *fileName); // Returns extension of a filename +int GetNextPOT(int num); // Calculate next power-of-two value for a given num #if defined(PLATFORM_ANDROID) void InitAssetManager(AAssetManager *manager); // Initialize asset manager from android app |
