aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRay <raysan5@gmail.com>2014-12-31 19:28:38 +0100
committerRay <raysan5@gmail.com>2014-12-31 19:28:38 +0100
commit3c4a91658e8586ecb5504d3167796d043e79d065 (patch)
tree89aeddb683c133c6d94f1a03f9d1a1399d2a6bf4
parent0c606092689d7a1823f7acd5e8b849052e297b7a (diff)
parentfad81f36e4cfd37901caad8555c49c41ac65aaee (diff)
downloadraylib-3c4a91658e8586ecb5504d3167796d043e79d065.tar.gz
raylib-3c4a91658e8586ecb5504d3167796d043e79d065.zip
Merge pull request #12 from raysan5/develop
Integration from develop branch
-rw-r--r--CHANGELOG17
-rw-r--r--README.md13
-rw-r--r--ROADMAP.md6
-rw-r--r--examples/makefile107
-rw-r--r--examples/text_font_select.c6
-rw-r--r--games/floppy/floppy.c210
-rw-r--r--games/floppy/resources/background.pngbin0 -> 228930 bytes
-rw-r--r--games/floppy/resources/coin.wavbin0 -> 37396 bytes
-rw-r--r--games/floppy/resources/floppy.pngbin0 -> 2030 bytes
-rw-r--r--games/floppy/resources/jump.wavbin0 -> 14540 bytes
-rw-r--r--games/floppy/resources/tubes.pngbin0 -> 19623 bytes
-rw-r--r--games/raylib_demo/makefile123
-rw-r--r--games/raylib_demo/raylib_demo.c926
-rw-r--r--games/raylib_demo/resources/audio/coin.wavbin0 -> 4776 bytes
-rw-r--r--games/raylib_demo/resources/audio/guitar_noodling.oggbin0 -> 506938 bytes
-rw-r--r--games/raylib_demo/resources/audio/spring.wavbin0 -> 10850 bytes
-rw-r--r--games/raylib_demo/resources/audio/tanatana.oggbin0 -> 57328 bytes
-rw-r--r--games/raylib_demo/resources/audio/weird.wavbin0 -> 6246 bytes
-rw-r--r--games/raylib_demo/resources/cat.obj4731
-rw-r--r--games/raylib_demo/resources/catsham.pngbin0 -> 315813 bytes
-rw-r--r--games/raylib_demo/resources/catwhite.pngbin0 -> 308798 bytes
-rw-r--r--games/raylib_demo/resources/fonts/alagard.rbmfbin0 -> 2159 bytes
-rw-r--r--games/raylib_demo/resources/fonts/alpha_beta.rbmfbin0 -> 2160 bytes
-rw-r--r--games/raylib_demo/resources/fonts/custom_alagard.pngbin0 -> 37935 bytes
-rw-r--r--games/raylib_demo/resources/fonts/custom_jupiter_crash.pngbin0 -> 23596 bytes
-rw-r--r--games/raylib_demo/resources/fonts/custom_mecha.pngbin0 -> 26597 bytes
-rw-r--r--games/raylib_demo/resources/fonts/jupiter_crash.rbmfbin0 -> 2160 bytes
-rw-r--r--games/raylib_demo/resources/fonts/mecha.rbmfbin0 -> 2160 bytes
-rw-r--r--games/raylib_demo/resources/fonts/pixantiqua.rbmfbin0 -> 2160 bytes
-rw-r--r--games/raylib_demo/resources/fonts/pixelplay.rbmfbin0 -> 2160 bytes
-rw-r--r--games/raylib_demo/resources/fonts/romulus.rbmfbin0 -> 2160 bytes
-rw-r--r--games/raylib_demo/resources/fonts/setback.rbmfbin0 -> 2160 bytes
-rw-r--r--games/raylib_demo/resources/lena.pngbin0 -> 473831 bytes
-rw-r--r--games/raylib_demo/resources/mandrill.pngbin0 -> 628015 bytes
-rw-r--r--games/raylib_demo/resources/platforms.pngbin0 -> 41531 bytes
-rw-r--r--games/raylib_demo/resources/raylib_logo.pngbin0 -> 3760 bytes
-rw-r--r--games/raylib_demo/resources/raylib_logo128x128.pngbin0 -> 1868 bytes
-rw-r--r--games/raylib_demo/resources/raylib_window.pngbin0 -> 4714 bytes
-rw-r--r--games/raylib_demo/resources/raylib_window_01.pngbin0 -> 2056 bytes
-rw-r--r--games/raylib_demo/resources/raylib_window_02.pngbin0 -> 3078 bytes
-rw-r--r--games/raylib_demo/resources/raylib_window_03.pngbin0 -> 3052 bytes
-rw-r--r--release/html5/libraylib.bcbin0 -> 316248 bytes
-rw-r--r--release/html5/raylib.h506
-rw-r--r--release/win32-mingw/include/raylib.h45
-rw-r--r--release/win32-mingw/lib/libraylib.abin248132 -> 249958 bytes
-rw-r--r--src/audio.c106
-rw-r--r--src/core.c315
-rw-r--r--src/makefile48
-rw-r--r--src/models.c93
-rw-r--r--src/raylib.h33
-rw-r--r--src/raymath.c49
-rw-r--r--src/raymath.h8
-rw-r--r--src/rlgl.c70
-rw-r--r--src/rlgl.h7
-rw-r--r--src/shapes.c4
-rw-r--r--src/stb_image.h2810
-rw-r--r--src/text.c180
-rw-r--r--src/textures.c173
-rw-r--r--src/utils.c46
-rw-r--r--src/utils.h1
60 files changed, 9657 insertions, 976 deletions
diff --git a/CHANGELOG b/CHANGELOG
index 33360095..47d90069 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -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)
---------------------------------------------------------------
diff --git a/README.md b/README.md
index 202193ab..64ee1684 100644
--- a/README.md
+++ b/README.md
@@ -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
-----------------
diff --git a/ROADMAP.md b/ROADMAP.md
index b48c3044..0814e0b9 100644
--- a/ROADMAP.md
+++ b/ROADMAP.md
@@ -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 076c9594..b9cdd15d 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 ?= PLATFORM_RPI
+# 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 any libraries to link into executable
@@ -74,6 +85,10 @@ else
endif
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
@@ -81,6 +96,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 \
@@ -109,7 +128,7 @@ EXAMPLES = \
models_billboard \
models_obj_loading \
models_heightmap \
- models_cubesmap \
+ models_cubicmap \
audio_sound_loading \
audio_music_stream \
#core_input_gamepad \
@@ -124,125 +143,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:
@@ -254,9 +273,13 @@ ifeq ($(PLATFORM),PLATFORM_DESKTOP_LINUX)
find . -type f -executable -delete
rm -f *.o
else
+ifeq ($(PLATFORM),PLATFORM_WEB)
+ del *.o *.html *.js
+else
del *.o *.exe
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/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
new file mode 100644
index 00000000..eab9d865
--- /dev/null
+++ b/games/floppy/resources/background.png
Binary files differ
diff --git a/games/floppy/resources/coin.wav b/games/floppy/resources/coin.wav
new file mode 100644
index 00000000..d3b6e93c
--- /dev/null
+++ b/games/floppy/resources/coin.wav
Binary files differ
diff --git a/games/floppy/resources/floppy.png b/games/floppy/resources/floppy.png
new file mode 100644
index 00000000..7c851086
--- /dev/null
+++ b/games/floppy/resources/floppy.png
Binary files differ
diff --git a/games/floppy/resources/jump.wav b/games/floppy/resources/jump.wav
new file mode 100644
index 00000000..1f68d336
--- /dev/null
+++ b/games/floppy/resources/jump.wav
Binary files differ
diff --git a/games/floppy/resources/tubes.png b/games/floppy/resources/tubes.png
new file mode 100644
index 00000000..a3ca8e7e
--- /dev/null
+++ b/games/floppy/resources/tubes.png
Binary files differ
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
new file mode 100644
index 00000000..6007509b
--- /dev/null
+++ b/games/raylib_demo/resources/audio/coin.wav
Binary files differ
diff --git a/games/raylib_demo/resources/audio/guitar_noodling.ogg b/games/raylib_demo/resources/audio/guitar_noodling.ogg
new file mode 100644
index 00000000..f5022040
--- /dev/null
+++ b/games/raylib_demo/resources/audio/guitar_noodling.ogg
Binary files differ
diff --git a/games/raylib_demo/resources/audio/spring.wav b/games/raylib_demo/resources/audio/spring.wav
new file mode 100644
index 00000000..c7fbf1b9
--- /dev/null
+++ b/games/raylib_demo/resources/audio/spring.wav
Binary files differ
diff --git a/games/raylib_demo/resources/audio/tanatana.ogg b/games/raylib_demo/resources/audio/tanatana.ogg
new file mode 100644
index 00000000..90b1795a
--- /dev/null
+++ b/games/raylib_demo/resources/audio/tanatana.ogg
Binary files differ
diff --git a/games/raylib_demo/resources/audio/weird.wav b/games/raylib_demo/resources/audio/weird.wav
new file mode 100644
index 00000000..101029c5
--- /dev/null
+++ b/games/raylib_demo/resources/audio/weird.wav
Binary files differ
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
new file mode 100644
index 00000000..8d7978e0
--- /dev/null
+++ b/games/raylib_demo/resources/catsham.png
Binary files differ
diff --git a/games/raylib_demo/resources/catwhite.png b/games/raylib_demo/resources/catwhite.png
new file mode 100644
index 00000000..b849c4c0
--- /dev/null
+++ b/games/raylib_demo/resources/catwhite.png
Binary files differ
diff --git a/games/raylib_demo/resources/fonts/alagard.rbmf b/games/raylib_demo/resources/fonts/alagard.rbmf
new file mode 100644
index 00000000..8c9b68d3
--- /dev/null
+++ b/games/raylib_demo/resources/fonts/alagard.rbmf
Binary files differ
diff --git a/games/raylib_demo/resources/fonts/alpha_beta.rbmf b/games/raylib_demo/resources/fonts/alpha_beta.rbmf
new file mode 100644
index 00000000..bdb2e752
--- /dev/null
+++ b/games/raylib_demo/resources/fonts/alpha_beta.rbmf
Binary files differ
diff --git a/games/raylib_demo/resources/fonts/custom_alagard.png b/games/raylib_demo/resources/fonts/custom_alagard.png
new file mode 100644
index 00000000..c3eb63b7
--- /dev/null
+++ b/games/raylib_demo/resources/fonts/custom_alagard.png
Binary files differ
diff --git a/games/raylib_demo/resources/fonts/custom_jupiter_crash.png b/games/raylib_demo/resources/fonts/custom_jupiter_crash.png
new file mode 100644
index 00000000..451b591f
--- /dev/null
+++ b/games/raylib_demo/resources/fonts/custom_jupiter_crash.png
Binary files differ
diff --git a/games/raylib_demo/resources/fonts/custom_mecha.png b/games/raylib_demo/resources/fonts/custom_mecha.png
new file mode 100644
index 00000000..59caab2c
--- /dev/null
+++ b/games/raylib_demo/resources/fonts/custom_mecha.png
Binary files differ
diff --git a/games/raylib_demo/resources/fonts/jupiter_crash.rbmf b/games/raylib_demo/resources/fonts/jupiter_crash.rbmf
new file mode 100644
index 00000000..d797e0d6
--- /dev/null
+++ b/games/raylib_demo/resources/fonts/jupiter_crash.rbmf
Binary files differ
diff --git a/games/raylib_demo/resources/fonts/mecha.rbmf b/games/raylib_demo/resources/fonts/mecha.rbmf
new file mode 100644
index 00000000..0266a065
--- /dev/null
+++ b/games/raylib_demo/resources/fonts/mecha.rbmf
Binary files differ
diff --git a/games/raylib_demo/resources/fonts/pixantiqua.rbmf b/games/raylib_demo/resources/fonts/pixantiqua.rbmf
new file mode 100644
index 00000000..04ef0e25
--- /dev/null
+++ b/games/raylib_demo/resources/fonts/pixantiqua.rbmf
Binary files differ
diff --git a/games/raylib_demo/resources/fonts/pixelplay.rbmf b/games/raylib_demo/resources/fonts/pixelplay.rbmf
new file mode 100644
index 00000000..31d14038
--- /dev/null
+++ b/games/raylib_demo/resources/fonts/pixelplay.rbmf
Binary files differ
diff --git a/games/raylib_demo/resources/fonts/romulus.rbmf b/games/raylib_demo/resources/fonts/romulus.rbmf
new file mode 100644
index 00000000..be9da01a
--- /dev/null
+++ b/games/raylib_demo/resources/fonts/romulus.rbmf
Binary files differ
diff --git a/games/raylib_demo/resources/fonts/setback.rbmf b/games/raylib_demo/resources/fonts/setback.rbmf
new file mode 100644
index 00000000..09572215
--- /dev/null
+++ b/games/raylib_demo/resources/fonts/setback.rbmf
Binary files differ
diff --git a/games/raylib_demo/resources/lena.png b/games/raylib_demo/resources/lena.png
new file mode 100644
index 00000000..59ef68aa
--- /dev/null
+++ b/games/raylib_demo/resources/lena.png
Binary files differ
diff --git a/games/raylib_demo/resources/mandrill.png b/games/raylib_demo/resources/mandrill.png
new file mode 100644
index 00000000..a1267573
--- /dev/null
+++ b/games/raylib_demo/resources/mandrill.png
Binary files differ
diff --git a/games/raylib_demo/resources/platforms.png b/games/raylib_demo/resources/platforms.png
new file mode 100644
index 00000000..ff69a614
--- /dev/null
+++ b/games/raylib_demo/resources/platforms.png
Binary files differ
diff --git a/games/raylib_demo/resources/raylib_logo.png b/games/raylib_demo/resources/raylib_logo.png
new file mode 100644
index 00000000..66545627
--- /dev/null
+++ b/games/raylib_demo/resources/raylib_logo.png
Binary files differ
diff --git a/games/raylib_demo/resources/raylib_logo128x128.png b/games/raylib_demo/resources/raylib_logo128x128.png
new file mode 100644
index 00000000..99ba5437
--- /dev/null
+++ b/games/raylib_demo/resources/raylib_logo128x128.png
Binary files differ
diff --git a/games/raylib_demo/resources/raylib_window.png b/games/raylib_demo/resources/raylib_window.png
new file mode 100644
index 00000000..e5e08bd4
--- /dev/null
+++ b/games/raylib_demo/resources/raylib_window.png
Binary files differ
diff --git a/games/raylib_demo/resources/raylib_window_01.png b/games/raylib_demo/resources/raylib_window_01.png
new file mode 100644
index 00000000..26607b01
--- /dev/null
+++ b/games/raylib_demo/resources/raylib_window_01.png
Binary files differ
diff --git a/games/raylib_demo/resources/raylib_window_02.png b/games/raylib_demo/resources/raylib_window_02.png
new file mode 100644
index 00000000..4a636e69
--- /dev/null
+++ b/games/raylib_demo/resources/raylib_window_02.png
Binary files differ
diff --git a/games/raylib_demo/resources/raylib_window_03.png b/games/raylib_demo/resources/raylib_window_03.png
new file mode 100644
index 00000000..176b2672
--- /dev/null
+++ b/games/raylib_demo/resources/raylib_window_03.png
Binary files differ
diff --git a/release/html5/libraylib.bc b/release/html5/libraylib.bc
new file mode 100644
index 00000000..c73d2480
--- /dev/null
+++ b/release/html5/libraylib.bc
Binary files differ
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
index 7c561f93..d9321248 100644
--- a/release/win32-mingw/lib/libraylib.a
+++ b/release/win32-mingw/lib/libraylib.a
Binary files differ
diff --git a/src/audio.c b/src/audio.c
index fd482534..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);
+
+ 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, "[%s] Sound file loaded successfully", fileName);
- TraceLog(INFO, "[%s] Sample rate: %i - Channels: %i", fileName, wave.sampleRate, wave.channels);
+ 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);
diff --git a/src/core.c b/src/core.c
index dec08eeb..5ff44f89 100644
--- a/src/core.c
+++ b/src/core.c
@@ -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)
@@ -1597,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");
}
}
@@ -1715,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
@@ -1723,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);
@@ -1737,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;
@@ -1806,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;
@@ -1923,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 bb37748a..501bd0c9 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 ?= PLATFORM_RPI
+# 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 -fgnu89-inline
+ CFLAGS = -O1 -Wall -std=c99
endif
+
#CFLAGSEXTRA = -Wextra -Wmissing-prototypes -Wstrict-prototypes
# define any directories containing required header files
@@ -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
@@ -120,9 +134,13 @@ ifeq ($(PLATFORM),PLATFORM_DESKTOP_LINUX)
find . -type f -executable -delete
rm -f *.o libraylib.a
else
+ifeq ($(PLATFORM),PLATFORM_WEB)
+ del *.o libraylib.bc
+else
del *.o libraylib.a
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 2d2af038..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));
@@ -1263,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)
@@ -1490,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 9809d823..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
@@ -275,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
@@ -289,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
@@ -302,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
@@ -321,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
@@ -340,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
@@ -394,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
@@ -433,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))
@@ -446,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
@@ -464,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)
diff --git a/src/rlgl.c b/src/rlgl.c
index 5e51b2e2..6f2027c7 100644
--- a/src/rlgl.c
+++ b/src/rlgl.c
@@ -168,21 +168,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
//----------------------------------------------------------------------------------
@@ -298,10 +300,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);
@@ -760,10 +773,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
@@ -848,8 +864,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);
@@ -1113,6 +1129,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);
@@ -1305,7 +1323,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);
@@ -1324,7 +1343,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;
}
@@ -1347,7 +1366,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)
@@ -1562,6 +1581,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"
@@ -1673,33 +1693,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;
}
@@ -1798,7 +1820,7 @@ static void InitializeBuffersGPU(void)
glGenVertexArrays(1, &vaoTriangles);
glBindVertexArray(vaoTriangles);
}
-
+
// Create buffers for our vertex data
glGenBuffers(2, trianglesBuffer);
@@ -1823,7 +1845,7 @@ static void InitializeBuffersGPU(void)
glGenVertexArrays(1, &vaoQuads);
glBindVertexArray(vaoQuads);
}
-
+
// Create buffers for our vertex data
glGenBuffers(4, quadsBuffer);
@@ -1859,6 +1881,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
@@ -1973,7 +1997,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++)
{
@@ -2044,7 +2068,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;
}
diff --git a/src/rlgl.h b/src/rlgl.h
index 2e1d18f4..5fa9c486 100644
--- a/src/rlgl.h
+++ b/src/rlgl.h
@@ -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
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.h b/src/stb_image.h
index 461ef087..39cbb7ad 100644
--- a/src/stb_image.h
+++ b/src/stb_image.h
@@ -1,19 +1,27 @@
-/* stb_image - v1.46 - 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.
- #define STBI_ASSERT(x) to avoid using assert.h.
+ // 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
@@ -22,33 +30,138 @@
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.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
- 1.44 (2014-08-07) warnings
- 1.43 (2014-07-15) fix MSVC-only bug in 1.42
- 1.42 (2014-07-09) no _CRT_SECURE_NO_WARNINGS; error-path fixes; STBI_ASSERT
- 1.41 (2014-06-25) fix search&replace that messed up comments/error messages
- 1.40 (2014-06-22) gcc warning
- 1.39 (2014-06-15) TGA optimization bugfix, multiple BMP fixes
- 1.38 (2014-06-06) suppress MSVC run-time warnings, fix accidental rename of 'skip'
- 1.37 (2014-06-04) remove duplicate typedef
- 1.36 (2014-06-03) converted to header file, allow reading incorrect iphoned-images without iphone flag
- 1.35 (2014-05-27) warnings, bugfixes, TGA optimization, etc
+ - SIMD acceleration on x86/x64 (SSE2) and ARM (NEON)
+
+ 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
+
+ - 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.
- TODO:
- stbi_info support for BMP,PSD,HDR,PIC
-
============================ Contributors =========================
-
+
Image formats Bug fixes & warning fixes
Sean Barrett (jpeg, png, bmp) Marc LeBlanc
Nicolas Schulz (hdr, psd) Christpher Lloyd
@@ -56,42 +169,52 @@
Jean-Marc Lienher (gif) Won Chun
Tom Seddon (pic) the Horde3D community
Thatcher Ulrich (psd) Janez Zemva
- Jonathan Blow
+ Ken Miller (pgm, ppm) Jonathan Blow
Laurent Gomila
- Extensions, features Aruelien Pocheville
- Jetro Lauha (stbi_info) Ryamond Barbiero
- James "moose2000" Brown (iPhone PNG) David Woo
- Ben "Disch" Wenger (io callbacks) Roy Eltham
- Martin "SpartanJ" Golini Luke Graham
- Thomas Ruf
+ 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
- Optimizations & bugfixes Ken Hamada
- Fabian "ryg" Giesen Cort Stratton
- Arseny Kapoulkine Blazej Dariusz Roszkowski
- Thibault Reuille
+ Ken Hamada
+ Optimizations & bugfixes Cort Stratton
+ Fabian "ryg" Giesen Blazej Dariusz Roszkowski
+ Arseny Kapoulkine Thibault Reuille
Paul Du Bois
Guillaume George
- Jerry Jansson
- If your name should be here but Hayaki Saito
- isn't, let Sean know. Johan Duparc
+ 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
+// 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
@@ -104,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:
@@ -133,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.
//
// ===========================================================================
//
@@ -167,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:
@@ -184,17 +357,22 @@
//
// ===========================================================================
//
-// 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
#include <stdio.h>
@@ -238,41 +416,43 @@ extern "C" {
// load image by filename, open file, or memory buffer
//
-STBIDEF 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
-STBIDEF stbi_uc *stbi_load (char const *filename, int *x, int *y, int *comp, int req_comp);
-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
-
typedef struct
{
- int (*read) (void *user,char *data,int size); // fill 'data' with 'size' bytes. return number of bytes actually read
+ 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;
-STBIDEF 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
- STBIDEF 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
- STBIDEF float *stbi_loadf (char const *filename, 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
-
- STBIDEF float *stbi_loadf_from_callbacks (stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp, int req_comp);
+#endif
+#ifndef STBI_NO_HDR
STBIDEF void stbi_hdr_to_ldr_gamma(float gamma);
STBIDEF void stbi_hdr_to_ldr_scale(float scale);
+#endif
+#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
+// 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
@@ -283,7 +463,7 @@ STBIDEF int stbi_is_hdr_from_file(FILE *f);
// get a VERY brief reason for failure
// NOT THREADSAFE
-STBIDEF const char *stbi_failure_reason (void);
+STBIDEF const char *stbi_failure_reason (void);
// free the loaded image -- this is just free()
STBIDEF void stbi_image_free (void *retval_from_stbi_load);
@@ -321,26 +501,6 @@ STBIDEF char *stbi_zlib_decode_noheader_malloc(const char *buffer, int len, int
STBIDEF int stbi_zlib_decode_noheader_buffer(char *obuffer, int olen, const char *ibuffer, int ilen);
-// 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
-
-STBIDEF void stbi_install_idct(stbi_idct_8x8 func);
-STBIDEF void stbi_install_YCbCr_to_RGB(stbi_YCbCr_to_RGB_run func);
-#endif // STBI_SIMD
-
-
#ifdef __cplusplus
}
#endif
@@ -352,22 +512,62 @@ STBIDEF void stbi_install_YCbCr_to_RGB(stbi_YCbCr_to_RGB_run func);
#ifdef STB_IMAGE_IMPLEMENTATION
-#ifndef STBI_NO_HDR
+#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
+
+#if defined(STBI_NO_PNG) && !defined(STBI_SUPPORT_ZLIB) && !defined(STBI_NO_ZLIB)
+#define STBI_NO_ZLIB
+#endif
+
+
+#include <stdarg.h>
+#include <stddef.h> // ptrdiff_t on osx
+#include <stdlib.h>
+#include <string.h>
+
+#if !defined(STBI_NO_LINEAR) || !defined(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 <string.h>
+
#ifndef STBI_ASSERT
#include <assert.h>
#define STBI_ASSERT(x) assert(x)
#endif
-#include <stdarg.h>
-#include <stddef.h> // ptrdiff_t on osx
+
#ifndef _MSC_VER
#ifdef __cplusplus
@@ -412,6 +612,86 @@ typedef unsigned char validate_uint32[sizeof(stbi__uint32)==4 ? 1 : -1];
#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
@@ -422,7 +702,7 @@ typedef struct
{
stbi__uint32 img_x, img_y;
int img_n, img_out_n;
-
+
stbi_io_callbacks io;
void *io_user_data;
@@ -498,29 +778,59 @@ static void stbi__rewind(stbi__context *s)
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;
@@ -538,7 +848,7 @@ static int stbi__err(const char *str)
static void *stbi__malloc(size_t size)
{
- return malloc(size);
+ return STBI_MALLOC(size);
}
// stbi__err - error
@@ -558,22 +868,40 @@ static void *stbi__malloc(size_t size)
STBIDEF void stbi_image_free(void *retval_from_stbi_load)
{
- free(retval_from_stbi_load);
+ STBI_FREE(retval_from_stbi_load);
}
-#ifndef STBI_NO_HDR
+#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)) {
@@ -582,15 +910,18 @@ static unsigned char *stbi_load_main(stbi__context *s, int *x, int *y, int *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
-FILE *stbi__fopen(char const *filename, char const *mode)
+static FILE *stbi__fopen(char const *filename, char const *mode)
{
FILE *f;
#if defined(_MSC_VER) && _MSC_VER >= 1400
@@ -603,7 +934,7 @@ FILE *stbi__fopen(char const *filename, char const *mode)
}
-STBIDEF unsigned char *stbi_load(char const *filename, 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)
{
FILE *f = stbi__fopen(filename, "rb");
unsigned char *result;
@@ -613,7 +944,7 @@ STBIDEF unsigned char *stbi_load(char const *filename, int *x, int *y, int *comp
return result;
}
-STBIDEF unsigned char *stbi_load_from_file(FILE *f, int *x, int *y, int *comp, int req_comp)
+STBIDEF stbi_uc *stbi_load_from_file(FILE *f, int *x, int *y, int *comp, int req_comp)
{
unsigned char *result;
stbi__context s;
@@ -627,23 +958,22 @@ STBIDEF unsigned char *stbi_load_from_file(FILE *f, int *x, int *y, int *comp, i
}
#endif //!STBI_NO_STDIO
-STBIDEF unsigned char *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_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);
}
-unsigned char *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_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_HDR
-
-float *stbi_loadf_main(stbi__context *s, int *x, int *y, int *comp, int 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
@@ -656,14 +986,14 @@ float *stbi_loadf_main(stbi__context *s, int *x, int *y, int *comp, int req_comp
return stbi__errpf("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)
+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);
}
-float *stbi_loadf_from_callbacks(stbi_io_callbacks const *clbk, void *user, 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)
{
stbi__context s;
stbi__start_callbacks(&s, (stbi_io_callbacks *) clbk, user);
@@ -671,7 +1001,7 @@ float *stbi_loadf_from_callbacks(stbi_io_callbacks const *clbk, void *user, int
}
#ifndef STBI_NO_STDIO
-float *stbi_loadf(char const *filename, int *x, int *y, int *comp, int req_comp)
+STBIDEF float *stbi_loadf(char const *filename, int *x, int *y, int *comp, int req_comp)
{
float *result;
FILE *f = stbi__fopen(filename, "rb");
@@ -681,7 +1011,7 @@ float *stbi_loadf(char const *filename, int *x, int *y, int *comp, int req_comp)
return result;
}
-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)
{
stbi__context s;
stbi__start_file(&s,f);
@@ -689,13 +1019,13 @@ float *stbi_loadf_from_file(FILE *f, int *x, int *y, int *comp, int req_comp)
}
#endif // !STBI_NO_STDIO
-#endif // !STBI_NO_HDR
+#endif // !STBI_NO_LINEAR
-// 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
+// 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!
-int stbi_is_hdr_from_memory(stbi_uc const *buffer, int len)
+STBIDEF int stbi_is_hdr_from_memory(stbi_uc const *buffer, int len)
{
#ifndef STBI_NO_HDR
stbi__context s;
@@ -743,17 +1073,17 @@ STBIDEF int stbi_is_hdr_from_callbacks(stbi_io_callbacks const *clbk, void
#endif
}
-#ifndef STBI_NO_HDR
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;
-void stbi_hdr_to_ldr_gamma(float gamma) { stbi__h2l_gamma_i = 1/gamma; }
-void stbi_hdr_to_ldr_scale(float scale) { stbi__h2l_scale_i = 1/scale; }
-
-void stbi_ldr_to_hdr_gamma(float gamma) { stbi__l2h_gamma = gamma; }
-void stbi_ldr_to_hdr_scale(float scale) { stbi__l2h_scale = scale; }
+#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; }
+
//////////////////////////////////////////////////////////////////////////////
//
@@ -762,9 +1092,9 @@ void stbi_ldr_to_hdr_scale(float scale) { stbi__l2h_scale = scale; }
enum
{
- SCAN_load=0,
- SCAN_type,
- SCAN_header
+ STBI__SCAN_load=0,
+ STBI__SCAN_type,
+ STBI__SCAN_header
};
static void stbi__refill_buffer(stbi__context *s)
@@ -803,7 +1133,7 @@ stbi_inline static int stbi__at_eof(stbi__context *s)
if (s->read_from_callbacks == 0) return 1;
}
- return s->img_buffer >= s->img_buffer_end;
+ return s->img_buffer >= s->img_buffer_end;
}
static void stbi__skip(stbi__context *s, int n)
@@ -827,7 +1157,7 @@ static int stbi__getn(stbi__context *s, stbi_uc *buffer, int 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;
@@ -867,6 +1197,9 @@ static stbi__uint32 stbi__get32le(stbi__context *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
@@ -893,7 +1226,7 @@ static unsigned char *stbi__convert_format(unsigned char *data, int img_n, int r
good = (unsigned char *) stbi__malloc(req_comp * x * y);
if (good == NULL) {
- free(data);
+ STBI_FREE(data);
return stbi__errpuc("outofmem", "Out of memory");
}
@@ -923,16 +1256,16 @@ static unsigned char *stbi__convert_format(unsigned char *data, int img_n, int r
#undef CASE
}
- free(data);
+ STBI_FREE(data);
return good;
}
-#ifndef STBI_NO_HDR
+#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) { free(data); return stbi__errpf("outofmem", "Out of memory"); }
+ 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) {
@@ -941,16 +1274,18 @@ static float *stbi__ldr_to_hdr(stbi_uc *data, int x, int y, int comp)
}
if (k < comp) output[i*comp + k] = data[i*comp+k]/255.0f;
}
- free(data);
+ 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) { free(data); return stbi__errpuc("outofmem", "Out of memory"); }
+ 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) {
@@ -967,17 +1302,16 @@ static stbi_uc *stbi__hdr_to_ldr(float *data, int x, int y, int comp)
output[i*comp + k] = (stbi_uc) stbi__float2int(z);
}
}
- free(data);
+ STBI_FREE(data);
return output;
}
#endif
//////////////////////////////////////////////////////////////////////////////
//
-// "baseline" JPEG/JFIF decoder (not actually fully baseline implementation)
+// "baseline" JPEG/JFIF decoder
//
// 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
@@ -991,13 +1325,10 @@ static stbi_uc *stbi__hdr_to_ldr(float *data, int x, int y, int comp)
// - 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
-// - 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)
+
+#ifndef STBI_NO_JPEG
// huffman decoding acceleration
#define FAST_BITS 9 // larger handles more cases; smaller stomps less cache
@@ -1015,13 +1346,11 @@ typedef struct
typedef struct
{
- #ifdef STBI_SIMD
- unsigned short dequant2[4][64];
- #endif
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;
@@ -1039,17 +1368,31 @@ typedef struct
int x,y,w2,h2;
stbi_uc *data;
- void *raw_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
+ 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)
@@ -1093,6 +1436,33 @@ static int stbi__build_huffman(stbi__huffman *h, int *count)
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 {
@@ -1163,31 +1533,45 @@ stbi_inline static int stbi__jpeg_huff_decode(stbi__jpeg *j, stbi__huffman *h)
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 m = 1 << (n-1);
unsigned int k;
+ int sgn;
if (j->code_bits < n) stbi__grow_buffer_unsafe(j);
- #if 1
+ 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;
- #else
- k = (j->code_buffer >> (32 - n)) & stbi__bmask[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;
- 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;
+ 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,
@@ -1208,10 +1592,13 @@ static stbi_uc stbi__jpeg_dezigzag[64+15] =
};
// decode one 64-entry block--
-static int stbi__jpeg_decode_block(stbi__jpeg *j, short data[64], stbi__huffman *hdc, stbi__huffman *hac, int b)
+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 = stbi__jpeg_huff_decode(j, hdc);
+ 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
@@ -1220,28 +1607,195 @@ static int stbi__jpeg_decode_block(stbi__jpeg *j, short data[64], stbi__huffman
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;
+ data[0] = (short) (dc * dequant[0]);
// decode AC components, see JPEG spec
k = 1;
do {
- int r,s;
- 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;
+ 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
- data[stbi__jpeg_dezigzag[k++]] = (short) stbi__extend_receive(j,s);
+ 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)
{
@@ -1253,21 +1807,21 @@ stbi_inline static stbi_uc stbi__clamp(int x)
return (stbi_uc) x;
}
-#define stbi__f2f(x) (int) (((x) * 4096 + 0.5))
+#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) \
+#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); \
+ 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); \
+ t0 = stbi__fsh(p2+p3); \
+ t1 = stbi__fsh(p2-p3); \
x0 = t0+t3; \
x3 = t0-t3; \
x1 = t1+t2; \
@@ -1280,36 +1834,28 @@ stbi_inline static stbi_uc stbi__clamp(int x)
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); \
+ 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;
-#ifdef STBI_SIMD
-typedef unsigned short stbi_dequantize_t;
-#else
-typedef stbi_uc stbi_dequantize_t;
-#endif
-
-// .344 seconds on 3*anemones.jpg
-static void stbi__idct_block(stbi_uc *out, int out_stride, short data[64], stbi_dequantize_t *dequantize)
+static void stbi__idct_block(stbi_uc *out, int out_stride, short data[64])
{
int i,val[64],*v=val;
- stbi_dequantize_t *dq = dequantize;
stbi_uc *o;
short *d = data;
// columns
- for (i=0; i < 8; ++i,++d,++dq, ++v) {
+ 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) {
@@ -1317,11 +1863,10 @@ static void stbi__idct_block(stbi_uc *out, int out_stride, short data[64], stbi_
// (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;
+ 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]*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])
+ 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;
@@ -1362,14 +1907,394 @@ static void stbi__idct_block(stbi_uc *out, int out_stride, short data[64], stbi_
}
}
-#ifdef STBI_SIMD
-static stbi_idct_8x8 stbi__idct_installed = stbi__idct_block;
+#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)); \
+ }
-STBIDEF void stbi_install_idct(stbi_idct_8x8 func)
-{
- stbi__idct_installed = func;
+ // 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
+
+#endif // STBI_NEON
#define STBI__MARKER_none 0xff
// if there's a pending marker from the entropy stream, return that
@@ -1400,6 +2325,7 @@ static void stbi__jpeg_reset(stbi__jpeg *j)
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
}
@@ -1407,73 +2333,152 @@ static void stbi__jpeg_reset(stbi__jpeg *j)
static int stbi__parse_entropy_coded_data(stbi__jpeg *z)
{
stbi__jpeg_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 (!stbi__jpeg_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
- stbi__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) 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);
+ 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 { // 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 (!stbi__jpeg_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
- stbi__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
+ } 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);
+ }
}
- // 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 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;
+ }
+ }
+}
+
+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);
}
}
}
}
- return 1;
}
static int stbi__process_marker(stbi__jpeg *z, int m)
@@ -1483,9 +2488,6 @@ static int stbi__process_marker(stbi__jpeg *z, int m)
case STBI__MARKER_none: // no marker found
return stbi__err("expected marker","Corrupt JPEG");
- case 0xC2: // stbi__SOF - progressive
- return stbi__err("progressive jpeg","JPEG format not supported (progressive)");
-
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);
@@ -1501,10 +2503,6 @@ static int stbi__process_marker(stbi__jpeg *z, int m)
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);
- #ifdef STBI_SIMD
- for (i=0; i < 64; ++i)
- z->dequant2[t][i] = z->dequant[t][i];
- #endif
L -= 65;
}
return L==0;
@@ -1532,6 +2530,8 @@ static int stbi__process_marker(stbi__jpeg *z, int m)
}
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;
@@ -1544,28 +2544,42 @@ static int stbi__process_marker(stbi__jpeg *z, int m)
return 0;
}
-// after we see stbi__SOS
+// 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 stbi__SOS component count","Corrupt JPEG");
- if (Ls != 6+2*z->scan_n) return stbi__err("bad stbi__SOS len","Corrupt JPEG");
+ 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;
+ 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;
}
- if (stbi__get8(z->s) != 0) return stbi__err("bad stbi__SOS","Corrupt JPEG");
- stbi__get8(z->s); // should be 63, but might be 0
- if (stbi__get8(z->s) != 0) return stbi__err("bad stbi__SOS","Corrupt JPEG");
+
+ {
+ 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;
}
@@ -1574,8 +2588,8 @@ 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 stbi__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
+ 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);
@@ -1586,7 +2600,7 @@ static int stbi__process_frame_header(stbi__jpeg *z, int scan)
z->img_comp[i].linebuf = NULL;
}
- if (Lf != 8+3*s->img_n) return stbi__err("bad stbi__SOF len","Corrupt JPEG");
+ 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);
@@ -1599,7 +2613,7 @@ static int stbi__process_frame_header(stbi__jpeg *z, int scan)
z->img_comp[i].tq = stbi__get8(s); if (z->img_comp[i].tq > 3) return stbi__err("bad TQ","Corrupt JPEG");
}
- if (scan != SCAN_load) return 1;
+ 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");
@@ -1627,54 +2641,68 @@ static int stbi__process_frame_header(stbi__jpeg *z, int scan)
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) {
- free(z->img_comp[i].raw_data);
+ STBI_FREE(z->img_comp[i].raw_data);
z->img_comp[i].data = NULL;
}
return stbi__err("outofmem", "Out of memory");
}
- // align blocks for installable-idct using mmx/sse
+ // 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. stbi__SOF)
+// 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)
+#define stbi__SOF(x) ((x) == 0xc0 || (x) == 0xc1 || (x) == 0xc2)
#define stbi__SOS(x) ((x) == 0xda)
-static int decode_jpeg_header(stbi__jpeg *z, int scan)
+#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 stbi__SOI","Corrupt JPEG");
- if (scan == SCAN_type) return 1;
+ 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 stbi__SOF", "Corrupt JPEG");
+ 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;
}
-static int decode_jpeg_image(stbi__jpeg *j)
+// decode image to YCbCr format
+static int stbi__decode_jpeg_image(stbi__jpeg *j)
{
int m;
j->restart_interval = 0;
- if (!decode_jpeg_header(j, SCAN_load)) return 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)) {
@@ -1688,7 +2716,7 @@ static int decode_jpeg_image(stbi__jpeg *j)
j->marker = stbi__get8(j->s);
break;
} else if (x != 0) {
- return 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
@@ -1698,6 +2726,8 @@ static int decode_jpeg_image(stbi__jpeg *j)
}
m = stbi__get_marker(j);
}
+ if (j->progressive)
+ stbi__jpeg_finish(j);
return 1;
}
@@ -1781,6 +2811,123 @@ static stbi_uc *stbi__resample_row_hv_2(stbi_uc *out, stbi_uc *in_near, stbi_uc
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
@@ -1792,10 +2939,10 @@ static stbi_uc *stbi__resample_row_generic(stbi_uc *out, stbi_uc *in_near, stbi_
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))
-
-// 0.38 seconds on 3*anemones.jpg (0.25 with processor = Pro)
-// VC6 without processor=Pro is generating multiple LEAs per multiply!
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;
@@ -1820,16 +2967,196 @@ static void stbi__YCbCr_to_RGB_row(stbi_uc *out, const stbi_uc *y, const stbi_uc
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
-#ifdef STBI_SIMD
-static stbi_YCbCr_to_RGB_run stbi__YCbCr_installed = stbi__YCbCr_to_RGB_row;
+#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
-STBIDEF void stbi_install_YCbCr_to_RGB(stbi_YCbCr_to_RGB_run func)
-{
- stbi__YCbCr_installed = func;
+#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)
@@ -1837,12 +3164,17 @@ 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) {
- free(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) {
- free(j->img_comp[i].linebuf);
+ STBI_FREE(j->img_comp[i].linebuf);
j->img_comp[i].linebuf = NULL;
}
}
@@ -1853,7 +3185,7 @@ 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 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;
@@ -1866,8 +3198,8 @@ static stbi_uc *load_jpeg_image(stbi__jpeg *z, int *out_x, int *out_y, int *comp
// 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
- if (!decode_jpeg_image(z)) { stbi__cleanup_jpeg(z); return NULL; }
+ // 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;
@@ -1904,7 +3236,7 @@ static stbi_uc *load_jpeg_image(stbi__jpeg *z, int *out_x, int *out_y, int *comp
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 = stbi__resample_row_hv_2;
+ else if (r->hs == 2 && r->vs == 2) r->resample = z->resample_row_hv_2_kernel;
else r->resample = stbi__resample_row_generic;
}
@@ -1932,11 +3264,7 @@ static stbi_uc *load_jpeg_image(stbi__jpeg *z, int *out_x, int *out_y, int *comp
if (n >= 3) {
stbi_uc *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
- stbi__YCbCr_to_RGB_row(out, y, coutput[1], coutput[2], z->s->img_x, n);
- #endif
+ 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];
@@ -1963,6 +3291,7 @@ static unsigned char *stbi__jpeg_load(stbi__context *s, int *x, int *y, int *com
{
stbi__jpeg j;
j.s = s;
+ stbi__setup_jpeg(&j);
return load_jpeg_image(&j, x,y,comp,req_comp);
}
@@ -1971,14 +3300,15 @@ static int stbi__jpeg_test(stbi__context *s)
int r;
stbi__jpeg j;
j.s = s;
- r = decode_jpeg_header(&j, SCAN_type);
+ 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 (!decode_jpeg_header(j, SCAN_header)) {
+ if (!stbi__decode_jpeg_header(j, STBI__SCAN_header)) {
stbi__rewind( j->s );
return 0;
}
@@ -1994,6 +3324,7 @@ static int stbi__jpeg_info(stbi__context *s, int *x, int *y, int *comp)
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
@@ -2002,6 +3333,8 @@ static int stbi__jpeg_info(stbi__context *s, int *x, int *y, int *comp)
// 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)
@@ -2015,7 +3348,7 @@ typedef struct
int maxcode[17];
stbi__uint16 firstsymbol[16];
stbi_uc size[288];
- stbi__uint16 value[288];
+ stbi__uint16 value[288];
} stbi__zhuffman;
stbi_inline static int stbi__bitreverse16(int n)
@@ -2042,8 +3375,8 @@ static int stbi__zbuild_huffman(stbi__zhuffman *z, stbi_uc *sizelist, int num)
// DEFLATE spec for generating codes
memset(sizes, 0, sizeof(sizes));
- memset(z->fast, 255, sizeof(z->fast));
- for (i=0; i < num; ++i)
+ 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)
@@ -2065,12 +3398,13 @@ static int stbi__zbuild_huffman(stbi__zhuffman *z, stbi_uc *sizelist, int num)
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] = (stbi__uint16) c;
+ z->fast[k] = fastv;
k += (1 << s);
}
}
@@ -2122,21 +3456,12 @@ stbi_inline static unsigned int stbi__zreceive(stbi__zbuf *z, int n)
k = z->code_buffer & ((1 << n) - 1);
z->code_buffer >>= n;
z->num_bits -= n;
- return k;
+ return k;
}
-stbi_inline static int stbi__zhuffman_decode(stbi__zbuf *a, stbi__zhuffman *z)
+static int stbi__zhuffman_decode_slowpath(stbi__zbuf *a, stbi__zhuffman *z)
{
int b,s,k;
- if (a->num_bits < 16) stbi__fill_bits(a);
- b = z->fast[a->code_buffer & STBI__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 = stbi__bit_reverse(a->code_buffer, 16);
@@ -2152,16 +3477,31 @@ stbi_inline static int stbi__zhuffman_decode(stbi__zbuf *a, stbi__zhuffman *z)
return z->value[b];
}
-static int stbi__zexpand(stbi__zbuf *z, int n) // need to make room for n bytes
+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 *) realloc(z->zout_start, limit);
+ 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;
@@ -2174,7 +3514,7 @@ static int stbi__zlength_base[31] = {
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]=
+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,
@@ -2185,16 +3525,23 @@ static int stbi__zdist_extra[32] =
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 (a->zout >= a->zout_end) if (!stbi__zexpand(a, 1)) return 0;
- *a->zout++ = (char) z;
+ 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) return 1;
+ 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]);
@@ -2202,11 +3549,18 @@ static int stbi__parse_huffman_block(stbi__zbuf *a)
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 (a->zout - a->zout_start < dist) return stbi__err("bad dist","Corrupt PNG");
- if (a->zout + len > a->zout_end) if (!stbi__zexpand(a, len)) return 0;
- p = (stbi_uc *) (a->zout - dist);
- while (len--)
- *a->zout++ = *p++;
+ 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);
+ }
}
}
}
@@ -2279,7 +3633,7 @@ static int stbi__parse_uncomperssed_block(stbi__zbuf *a)
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, len)) return 0;
+ if (!stbi__zexpand(a, a->zout, len)) return 0;
memcpy(a->zout, a->zbuffer, len);
a->zbuffer += len;
a->zout += len;
@@ -2362,7 +3716,7 @@ STBIDEF char *stbi_zlib_decode_malloc_guesssize(const char *buffer, int len, int
if (outlen) *outlen = (int) (a.zout - a.zout_start);
return a.zout_start;
} else {
- free(a.zout_start);
+ STBI_FREE(a.zout_start);
return NULL;
}
}
@@ -2383,7 +3737,7 @@ STBIDEF char *stbi_zlib_decode_malloc_guesssize_headerflag(const char *buffer, i
if (outlen) *outlen = (int) (a.zout - a.zout_start);
return a.zout_start;
} else {
- free(a.zout_start);
+ STBI_FREE(a.zout_start);
return NULL;
}
}
@@ -2410,7 +3764,7 @@ STBIDEF char *stbi_zlib_decode_noheader_malloc(char const *buffer, int len, int
if (outlen) *outlen = (int) (a.zout - a.zout_start);
return a.zout_start;
} else {
- free(a.zout_start);
+ STBI_FREE(a.zout_start);
return NULL;
}
}
@@ -2425,6 +3779,7 @@ STBIDEF int stbi_zlib_decode_noheader_buffer(char *obuffer, int olen, const char
else
return -1;
}
+#endif
// public domain "baseline" PNG decoder v0.10 Sean Barrett 2006-11-18
// simple implementation
@@ -2436,15 +3791,13 @@ STBIDEF int stbi_zlib_decode_noheader_buffer(char *obuffer, int olen, const char
// 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;
-#define PNG_TYPE(a,b,c,d) (((a) << 24) + ((b) << 16) + ((c) << 8) + (d))
-
static stbi__pngchunk stbi__get_chunk_header(stbi__context *s)
{
stbi__pngchunk c;
@@ -2470,13 +3823,23 @@ typedef struct
enum {
- STBI__F_none=0, STBI__F_sub=1, STBI__F_up=2, STBI__F_avg=3, STBI__F_paeth=4,
- STBI__F_avg_first, STBI__F_paeth_first
+ 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
+ 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)
@@ -2490,32 +3853,50 @@ static int stbi__paeth(int a, int b, int c)
return c;
}
-#define STBI__BYTECAST(x) ((stbi_uc) ((x) & 255)) // truncate int to byte without warnings
+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)
+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);
+ 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_n * x + 1) * y) return stbi__err("not enough pixels","Corrupt PNG");
+ if (raw_len != img_len) return stbi__err("not enough pixels","Corrupt PNG");
} else { // interlaced:
- if (raw_len < (img_n * x + 1) * y) return stbi__err("not enough pixels","Corrupt PNG");
+ 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++;
- if (filter > 4) return stbi__err("invalid filter","Corrupt PNG");
+ 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 pixel explicitly
- for (k=0; k < img_n; ++k) {
+
+ // 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;
@@ -2526,26 +3907,37 @@ static int stbi__create_png_image_raw(stbi__png *a, stbi_uc *raw, stbi__uint32 r
case STBI__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;
+
+ 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 (img_n == out_n) {
+ if (depth < 8 || img_n == out_n) {
+ int nk = (width - 1)*img_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)
+ for (k=0; k < nk; ++k)
switch (filter) {
- CASE(STBI__F_none) cur[k] = raw[k]; break;
- CASE(STBI__F_sub) cur[k] = STBI__BYTECAST(raw[k] + cur[k-img_n]); break;
+ // "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-img_n])>>1)); break;
- CASE(STBI__F_paeth) cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k-img_n],prior[k],prior[k-img_n])); break;
- CASE(STBI__F_avg_first) cur[k] = STBI__BYTECAST(raw[k] + (cur[k-img_n] >> 1)); break;
- CASE(STBI__F_paeth_first) cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k-img_n],0,0)); 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) \
@@ -2564,15 +3956,90 @@ static int stbi__create_png_image_raw(stbi__png *a, stbi_uc *raw, stbi__uint32 r
#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 *raw, stbi__uint32 raw_len, int out_n, int interlaced)
+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, raw, raw_len, out_n, a->s->img_x, a->s->img_y);
+ 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);
@@ -2586,17 +4053,22 @@ static int stbi__create_png_image(stbi__png *a, stbi_uc *raw, stbi__uint32 raw_l
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 (!stbi__create_png_image_raw(a, raw, raw_len, out_n, x, y)) {
- free(final);
+ 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)
- memcpy(final + (j*yspc[p]+yorig[p])*a->s->img_x*out_n + (i*xspc[p]+xorig[p])*out_n,
+ 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);
- free(a->out);
- raw += (x*out_n+1)*y;
- raw_len -= (x*out_n+1)*y;
+ }
+ }
+ STBI_FREE(a->out);
+ image_data += img_len;
+ image_data_len -= img_len;
}
}
a->out = final;
@@ -2658,7 +4130,7 @@ static int stbi__expand_png_palette(stbi__png *a, stbi_uc *palette, int len, int
p += 4;
}
}
- free(a->out);
+ STBI_FREE(a->out);
a->out = temp_out;
STBI_NOTUSED(len);
@@ -2706,7 +4178,7 @@ static void stbi__de_iphone(stbi__png *z)
} else {
p[0] = p[2];
p[2] = t;
- }
+ }
p += 4;
}
} else {
@@ -2721,12 +4193,14 @@ static void stbi__de_iphone(stbi__png *z)
}
}
+#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, is_iphone=0;
+ int first=1,k,interlace=0, color=0, depth=0, is_iphone=0;
stbi__context *s = z->s;
z->expanded = NULL;
@@ -2735,23 +4209,23 @@ static int stbi__parse_png_file(stbi__png *z, int scan, int req_comp)
if (!stbi__check_png_header(s)) return 0;
- if (scan == SCAN_type) return 1;
+ if (scan == STBI__SCAN_type) return 1;
for (;;) {
stbi__pngchunk c = stbi__get_chunk_header(s);
switch (c.type) {
- case PNG_TYPE('C','g','B','I'):
+ case STBI__PNG_TYPE('C','g','B','I'):
is_iphone = 1;
stbi__skip(s, c.length);
break;
- case PNG_TYPE('I','H','D','R'): {
- int depth,color,comp,filter;
+ 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 != 8) return stbi__err("8bit only","PNG not supported: 8-bit only");
+ 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");
@@ -2761,7 +4235,7 @@ static int stbi__parse_png_file(stbi__png *z, int scan, int req_comp)
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 == SCAN_header) return 1;
+ 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.
@@ -2772,7 +4246,7 @@ static int stbi__parse_png_file(stbi__png *z, int scan, int req_comp)
break;
}
- case PNG_TYPE('P','L','T','E'): {
+ 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;
@@ -2786,11 +4260,11 @@ static int stbi__parse_png_file(stbi__png *z, int scan, int req_comp)
break;
}
- case PNG_TYPE('t','R','N','S'): {
+ 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 == SCAN_header) { s->img_n = 4; return 1; }
+ 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;
@@ -2801,21 +4275,21 @@ static int stbi__parse_png_file(stbi__png *z, int scan, int req_comp)
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); // non 8-bit images will be larger
+ tc[k] = (stbi_uc) (stbi__get16be(s) & 255) * stbi__depth_scale_table[depth]; // non 8-bit images will be larger
}
break;
}
- case PNG_TYPE('I','D','A','T'): {
+ 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 == SCAN_header) { s->img_n = pal_img_n; return 1; }
+ 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 *) realloc(z->idata, idata_limit); if (p == NULL) return stbi__err("outofmem", "Out of memory");
+ 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");
@@ -2823,19 +4297,22 @@ static int stbi__parse_png_file(stbi__png *z, int scan, int req_comp)
break;
}
- case PNG_TYPE('I','E','N','D'): {
- stbi__uint32 raw_len;
+ 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 != SCAN_load) return 1;
+ if (scan != STBI__SCAN_load) return 1;
if (z->idata == NULL) return stbi__err("no IDAT","Corrupt PNG");
- z->expanded = (stbi_uc *) stbi_zlib_decode_malloc_guesssize_headerflag((char *) z->idata, ioff, 16384, (int *) &raw_len, !is_iphone);
+ // 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
- free(z->idata); z->idata = NULL;
+ 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, interlace)) return 0;
+ 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)
@@ -2848,7 +4325,7 @@ static int stbi__parse_png_file(stbi__png *z, int scan, int req_comp)
if (!stbi__expand_png_palette(z, palette, pal_len, s->img_out_n))
return 0;
}
- free(z->expanded); z->expanded = NULL;
+ STBI_FREE(z->expanded); z->expanded = NULL;
return 1;
}
@@ -2878,7 +4355,7 @@ static unsigned char *stbi__do_png(stbi__png *p, int *x, int *y, int *n, int req
{
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, SCAN_load, req_comp)) {
+ 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) {
@@ -2890,9 +4367,9 @@ static unsigned char *stbi__do_png(stbi__png *p, int *x, int *y, int *n, int req
*y = p->s->img_y;
if (n) *n = p->s->img_out_n;
}
- free(p->out); p->out = NULL;
- free(p->expanded); p->expanded = NULL;
- free(p->idata); p->idata = NULL;
+ STBI_FREE(p->out); p->out = NULL;
+ STBI_FREE(p->expanded); p->expanded = NULL;
+ STBI_FREE(p->idata); p->idata = NULL;
return result;
}
@@ -2914,7 +4391,7 @@ static int stbi__png_test(stbi__context *s)
static int stbi__png_info_raw(stbi__png *p, int *x, int *y, int *comp)
{
- if (!stbi__parse_png_file(p, SCAN_header, 0)) {
+ if (!stbi__parse_png_file(p, STBI__SCAN_header, 0)) {
stbi__rewind( p->s );
return 0;
}
@@ -2930,8 +4407,11 @@ static int stbi__png_info(stbi__context *s, int *x, int *y, int *comp)
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;
@@ -3094,7 +4574,7 @@ static stbi_uc *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int
if (!out) return stbi__errpuc("outofmem", "Out of memory");
if (bpp < 16) {
int z=0;
- if (psize == 0 || psize > 256) { free(out); return stbi__errpuc("invalid", "Corrupt BMP"); }
+ 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);
@@ -3105,7 +4585,7 @@ static stbi_uc *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int
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 { free(out); return stbi__errpuc("bad bpp", "Corrupt BMP"); }
+ 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) {
@@ -3143,7 +4623,7 @@ static stbi_uc *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int
easy = 2;
}
if (!easy) {
- if (!mr || !mg || !mb) { free(out); return stbi__errpuc("bad masks", "Corrupt BMP"); }
+ 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);
@@ -3169,7 +4649,7 @@ static stbi_uc *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int
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);
+ if (target == 4) out[z++] = STBI__BYTECAST(a);
}
}
stbi__skip(s, pad);
@@ -3196,10 +4676,11 @@ static stbi_uc *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int
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;
@@ -3340,12 +4821,12 @@ static stbi_uc *stbi__tga_load(stbi__context *s, int *x, int *y, int *comp, int
// load the palette
tga_palette = (unsigned char*)stbi__malloc( tga_palette_len * tga_palette_bits / 8 );
if (!tga_palette) {
- free(tga_data);
+ STBI_FREE(tga_data);
return stbi__errpuc("outofmem", "Out of memory");
}
if (!stbi__getn(s, tga_palette, tga_palette_len * tga_palette_bits / 8 )) {
- free(tga_data);
- free(tga_palette);
+ STBI_FREE(tga_data);
+ STBI_FREE(tga_palette);
return stbi__errpuc("bad palette", "Corrupt TGA");
}
}
@@ -3427,7 +4908,7 @@ static stbi_uc *stbi__tga_load(stbi__context *s, int *x, int *y, int *comp, int
// clear my palette, if I had one
if ( tga_palette != NULL )
{
- free( tga_palette );
+ STBI_FREE( tga_palette );
}
}
@@ -3455,10 +4936,12 @@ static stbi_uc *stbi__tga_load(stbi__context *s, int *x, int *y, int *comp, int
// 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);
@@ -3493,7 +4976,7 @@ static stbi_uc *stbi__psd_load(stbi__context *s, int *x, int *y, int *comp, int
// 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");
@@ -3535,7 +5018,7 @@ static stbi_uc *stbi__psd_load(stbi__context *s, int *x, int *y, int *comp, int
// Initialize the data to zero.
//memset( out, 0, pixelCount * 4 );
-
+
// Finally, the image data.
if (compression) {
// RLE as used by .PSD and .TIFF
@@ -3553,7 +5036,7 @@ static stbi_uc *stbi__psd_load(stbi__context *s, int *x, int *y, int *comp, int
// 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.
@@ -3591,15 +5074,15 @@ static stbi_uc *stbi__psd_load(stbi__context *s, int *x, int *y, int *comp, int
}
}
}
-
+
} 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.
@@ -3620,9 +5103,10 @@ static stbi_uc *stbi__psd_load(stbi__context *s, int *x, int *y, int *comp, int
if (comp) *comp = channelCount;
*y = h;
*x = w;
-
+
return out;
}
+#endif
// *************************************************************************************************
// Softimage PIC loader
@@ -3631,6 +5115,7 @@ static stbi_uc *stbi__psd_load(stbi__context *s, int *x, int *y, int *comp, int
// 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;
@@ -3818,7 +5303,7 @@ static stbi_uc *stbi__pic_load(stbi__context *s,int *px,int *py,int *comp,int re
memset(result, 0xff, x*y*4);
if (!stbi__pic_load_core(s,x,y,comp, result)) {
- free(result);
+ STBI_FREE(result);
result=0;
}
*px = x;
@@ -3835,10 +5320,13 @@ static int stbi__pic_test(stbi__context *s)
stbi__rewind(s);
return r;
}
+#endif
// *************************************************************************************************
// GIF loader -- public domain by Jean-Marc Lienher -- simplified/shrunk by stb
-typedef struct
+
+#ifndef STBI_NO_GIF
+typedef struct
{
stbi__int16 prefix;
stbi_uc first;
@@ -3886,8 +5374,8 @@ static void stbi__gif_parse_colortable(stbi__context *s, stbi_uc pal[256][4], in
pal[i][2] = stbi__get8(s);
pal[i][1] = stbi__get8(s);
pal[i][0] = stbi__get8(s);
- pal[i][3] = transp ? 0 : 255;
- }
+ pal[i][3] = transp == i ? 0 : 255;
+ }
}
static int stbi__gif_header(stbi__context *s, stbi__gif *g, int *comp, int is_info)
@@ -3898,8 +5386,8 @@ static int stbi__gif_header(stbi__context *s, stbi__gif *g, int *comp, int is_in
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");
-
+ 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);
@@ -3920,7 +5408,7 @@ static int stbi__gif_header(stbi__context *s, stbi__gif *g, int *comp, int is_in
static int stbi__gif_info_raw(stbi__context *s, int *x, int *y, int *comp)
{
- stbi__gif g;
+ stbi__gif g;
if (!stbi__gif_header(s, &g, comp, 1)) {
stbi__rewind( s );
return 0;
@@ -3940,7 +5428,7 @@ static void stbi__out_gif_code(stbi__gif *g, stbi__uint16 code)
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];
@@ -3994,7 +5482,7 @@ static stbi_uc *stbi__process_gif_raster(stbi__context *s, stbi__gif *g)
if (valid_bits < codesize) {
if (len == 0) {
len = stbi__get8(s); // start new block
- if (len == 0)
+ if (len == 0)
return g->out;
}
--len;
@@ -4039,7 +5527,7 @@ static stbi_uc *stbi__process_gif_raster(stbi__context *s, stbi__gif *g)
} else {
return stbi__errpuc("illegal code in raster", "Corrupt GIF");
}
- }
+ }
}
}
@@ -4077,7 +5565,7 @@ static stbi_uc *stbi__gif_load_next(stbi__context *s, stbi__gif *g, int *comp, i
memcpy(g->out, old_out, g->w*g->h*4);
}
}
-
+
for (;;) {
switch (stbi__get8(s)) {
case 0x2C: /* Image Descriptor */
@@ -4112,16 +5600,16 @@ static stbi_uc *stbi__gif_load_next(stbi__context *s, stbi__gif *g, int *comp, i
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;
+ 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;
+ 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;
@@ -4178,7 +5666,7 @@ 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
@@ -4267,7 +5755,7 @@ static float *stbi__hdr_load(stbi__context *s, int *x, int *y, int *comp, int re
// 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);
@@ -4328,14 +5816,14 @@ static float *stbi__hdr_load(stbi__context *s, int *x, int *y, int *comp, int re
stbi__hdr_convert(hdr_data, rgbe, req_comp);
i = 1;
j = 0;
- free(scanline);
+ STBI_FREE(scanline);
goto main_decode_loop; // yes, this makes no sense
}
len <<= 8;
len |= stbi__get8(s);
- if (len != width) { free(hdr_data); free(scanline); return stbi__errpf("invalid decoded scanline length", "corrupt HDR"); }
+ 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) {
@@ -4356,7 +5844,7 @@ static float *stbi__hdr_load(stbi__context *s, int *x, int *y, int *comp, int re
for (i=0; i < width; ++i)
stbi__hdr_convert(hdr_data+(j*width + i)*req_comp, scanline + i*4, req_comp);
}
- free(scanline);
+ STBI_FREE(scanline);
}
return hdr_data;
@@ -4402,6 +5890,7 @@ static int stbi__hdr_info(stbi__context *s, int *x, int *y, int *comp)
}
#endif // STBI_NO_HDR
+#ifndef STBI_NO_BMP
static int stbi__bmp_info(stbi__context *s, int *x, int *y, int *comp)
{
int hsz;
@@ -4429,7 +5918,9 @@ static int stbi__bmp_info(stbi__context *s, int *x, int *y, int *comp)
*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;
@@ -4460,7 +5951,9 @@ static int stbi__psd_info(stbi__context *s, int *x, int *y, int *comp)
*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;
@@ -4505,28 +5998,156 @@ static int stbi__pic_info(stbi__context *s, int *x, int *y, int *comp)
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)
{
- 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_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;
+ 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");
}
@@ -4571,123 +6192,134 @@ STBIDEF int stbi_info_from_callbacks(stbi_io_callbacks const *c, void *user, int
/*
revision history:
- 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)
+ 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
+ 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/text.c b/src/text.c
index 8b7e5b12..94481857 100644
--- a/src/text.c
+++ b/src/text.c
@@ -63,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);
@@ -135,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
@@ -168,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)
@@ -195,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);
@@ -207,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);
}
@@ -475,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)
{
@@ -517,91 +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
- // TODO: Check if file could be loaded! (rbmfFile == NULL)?
+ if (rbmfFile == NULL)
+ {
+ TraceLog(WARNING, "[%s] rBMF font file could not be opened", fileName);
+ }
+ else
+ {
+ fread(&rbmfHeader, sizeof(rbmfInfoHeader), 1, rbmfFile);
- 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 929cba78..f701c380 100644
--- a/src/textures.c
+++ b/src/textures.c
@@ -126,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, file format not recognized", fileName);
+ else TraceLog(WARNING, "[%s] Image could not be loaded, file not recognized", fileName);
}
else if (strcmp(GetExtension(fileName),"dds") == 0)
{
@@ -175,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;
@@ -189,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)
@@ -295,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)
{
@@ -334,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);
}
}
@@ -345,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;
@@ -369,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)
{
@@ -436,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
@@ -528,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
{
@@ -544,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
@@ -636,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
@@ -679,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
{
@@ -690,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 b5112111..c3c20b47 100644
--- a/src/utils.c
+++ b/src/utils.c
@@ -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