@ -1 +1 @@ | |||||
Subproject commit f1764a0ed33533a45c2da8251dac6b284156082d | |||||
Subproject commit 5a11f8e3db0b7b014b99d91be196cea582e1688a |
@ -1 +1 @@ | |||||
Subproject commit 3c26a6fe64a782d7aba0db7ee331e646060cc095 | |||||
Subproject commit 66f114dbf78bdbe42a5ea4905b0414c347ae45c5 |
@ -0,0 +1,7 @@ | |||||
--- | |||||
BasedOnStyle: Google | |||||
IndentWidth: 2 | |||||
TabWidth: 2 | |||||
UseTab: Never | |||||
BreakBeforeBraces: Attach | |||||
Standard: Cpp03 |
@ -0,0 +1,7 @@ | |||||
image: bradrydzewski/base | |||||
script: | |||||
- make | |||||
notify: | |||||
email: | |||||
recipients: | |||||
- syoyo@lighttransport.com |
@ -0,0 +1 @@ | |||||
blank_issues_enabled: false |
@ -0,0 +1,26 @@ | |||||
--- | |||||
name: Issue report | |||||
about: Create a report to help us improve | |||||
title: '' | |||||
labels: '' | |||||
assignees: '' | |||||
--- | |||||
**Describe the issue** | |||||
A clear and concise description of what the issue is. | |||||
**To Reproduce** | |||||
Steps to reproduce the behavior: | |||||
1. Compile TinyEXR with '...' | |||||
2. Load EXR Image '...' | |||||
3. See error | |||||
Please attach minimal and reproducible files(source codes, EXR files, etc) | |||||
**Expected behavior** | |||||
A clear and concise description of what you expected to happen. | |||||
**Environment** | |||||
- OS: [e.g. Linux] | |||||
- Compiler [e.g. gcc 7.3] |
@ -0,0 +1,2 @@ | |||||
output.exr | |||||
test_tinyexr |
@ -0,0 +1,3 @@ | |||||
[submodule "deps/ZFP"] | |||||
path = deps/ZFP | |||||
url = https://github.com/LLNL/ZFP.git |
@ -0,0 +1,12 @@ | |||||
# FIXME(syoyo): Following settings does not work well. | |||||
path_classifiers: | |||||
maintainablity: | |||||
- exclude: "deps/*" | |||||
- exclude: kuroga.py | |||||
useless-code: | |||||
- exclude: kuroga.py | |||||
extraction: | |||||
python: | |||||
index: | |||||
exclude: /kuroga.py |
@ -0,0 +1,124 @@ | |||||
env: | |||||
global: | |||||
- secure: "iGrwT5ZeamAmTrJ/u+ewb1YQvUWeOo2wbOiN2XgcHGKvEWOA2AFUY0LnwgSMV1RJbCzV0VCSglh6kvRXePDEdKNmEb8F6pPfc1GyZyFXX7e+gcG6nkq1NwnpSreLP5pIvEboNH1K57+UEre3buGynVTz/dgrGrIsOIJ1nylu3jbznwDS5wy/thyVoOTV+pMdP+6jsGaQKPJIEtdDJ7/zZ4yVqhdvP8HeJrNeoxGlQsLbgrKxMtZpKYaGcrKgP7nBJs50OHPDs04CxRz0rdEbVwYGP6SsWOAO/IYvNtcMBO4lyvaVmAWyjDITd3EH+q+QkJ8sP+6Vwj3VEqFTAFTFsR5rJJVu/CcvP1Wuq+oNpti7EJkCK8xSLfngLu+OBJMrYxR7W6zPaz4dgMAI7tqJLLSnOo0bQ8bS6aU64EnuXHDy6U7XUaYsAEqsTbmerk4KMtNBMWA667ef2XuXYPz+fK7CZQtPuU15+MBpFBaelnVpXW6qUQbDCtyGD5AHZZMF9llkKXhn2Hn/VBs3GjgP7huUDesH27x3+VJ5MHaghVyaCh0BviI9yuXpTQspm3WItzdBoGqJROFv9yTsJxs/JQz4ytqbMdiBnLlQVFdBEpWo+GjySGjlvFvlLIeCtCuULw0kL0zoY5FU2xCT35isIV5A0+aGAL0vhHaVR/CkNOo=" | |||||
language: cpp | |||||
matrix: | |||||
include: | |||||
# works on Precise and Trusty | |||||
- os: linux | |||||
addons: | |||||
apt: | |||||
sources: | |||||
- ubuntu-toolchain-r-test | |||||
packages: | |||||
- g++-4.9 | |||||
env: | |||||
- MATRIX_EVAL="CC=gcc-4.9 && CXX=g++-4.9" | |||||
# works on Precise and Trusty | |||||
- os: linux | |||||
addons: | |||||
apt: | |||||
sources: | |||||
- ubuntu-toolchain-r-test | |||||
packages: | |||||
- g++-5 | |||||
env: | |||||
- MATRIX_EVAL="CC=gcc-5 && CXX=g++-5" | |||||
# works on Precise and Trusty | |||||
- os: linux | |||||
addons: | |||||
apt: | |||||
sources: | |||||
- ubuntu-toolchain-r-test | |||||
packages: | |||||
- g++-6 | |||||
env: | |||||
- MATRIX_EVAL="CC=gcc-6 && CXX=g++-6" | |||||
# works on Precise and Trusty | |||||
- os: linux | |||||
addons: | |||||
apt: | |||||
sources: | |||||
- ubuntu-toolchain-r-test | |||||
packages: | |||||
- g++-7 | |||||
env: | |||||
- MATRIX_EVAL="CC=gcc-7 && CXX=g++-7" | |||||
# works on Precise and Trusty | |||||
- os: linux | |||||
addons: | |||||
apt: | |||||
sources: | |||||
- ubuntu-toolchain-r-test | |||||
- llvm-toolchain-precise-3.6 | |||||
packages: | |||||
- clang-3.6 | |||||
env: | |||||
- MATRIX_EVAL="CC=clang-3.6 && CXX=clang++-3.6" | |||||
# works on Precise and Trusty | |||||
- os: linux | |||||
addons: | |||||
apt: | |||||
sources: | |||||
- ubuntu-toolchain-r-test | |||||
- llvm-toolchain-precise-3.8 | |||||
packages: | |||||
- clang-3.8 | |||||
env: | |||||
- MATRIX_EVAL="CC=clang-3.8 && CXX=clang++-3.8" | |||||
# works on Trusty | |||||
- os: linux | |||||
addons: | |||||
apt: | |||||
sources: | |||||
- llvm-toolchain-trusty-3.9 | |||||
packages: | |||||
- clang-3.9 | |||||
env: | |||||
- MATRIX_EVAL="CC=clang-3.9 && CXX=clang++-3.9" | |||||
# works on Trusty | |||||
- os: linux | |||||
addons: | |||||
apt: | |||||
sources: | |||||
- llvm-toolchain-trusty-4.0 | |||||
packages: | |||||
- clang-4.0 | |||||
env: | |||||
- MATRIX_EVAL="CC=clang-4.0 && CXX=clang++-4.0" | |||||
# Disable for a while since Travis failed to install clang-5.0 for some reason | |||||
# # works on Trusty | |||||
# - os: linux | |||||
# addons: | |||||
# apt: | |||||
# sources: | |||||
# - llvm-toolchain-trusty-5.0 | |||||
# packages: | |||||
# - clang-5.0 | |||||
# env: | |||||
# - MATRIX_EVAL="CC=clang-5.0 && CXX=clang++-5.0" | |||||
before_install: | |||||
- eval "${MATRIX_EVAL}" | |||||
script: | |||||
- make | |||||
- make test | |||||
addons: | |||||
coverity_scan: | |||||
project: | |||||
name: "syoyo/tinyexr" | |||||
description: "Build submitted via Travis CI" | |||||
notification_email: syoyo@lighttransport.com | |||||
build_command: "make" | |||||
branch_pattern: master |
@ -0,0 +1,105 @@ | |||||
# this cmake file is for compile tests. | |||||
# Not usable for your cmake app. | |||||
cmake_minimum_required(VERSION 3.5) | |||||
set(BUILD_TARGET "tinyexr") | |||||
set(SAMPLE_TARGET "test_tinyexr") | |||||
project(${BUILD_TARGET} CXX) | |||||
# options | |||||
option(TINYEXR_BUILD_SAMPLE "Build a sample" ON) | |||||
option(TINYEXR_USE_MINIZ "Use miniz" ON) | |||||
# cmake modules | |||||
list(APPEND CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake) | |||||
list(APPEND CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake/sanitizers) | |||||
find_package(Sanitizers) # Address sanitizer (-DSANITIZE_ADDRESS=ON) | |||||
# Require strict C++11 mode(e.g. `-std=c++11`) | |||||
set(CMAKE_CXX_STANDARD 11) | |||||
set(CMAKE_CXX_STANDARD_REQUIRED ON) | |||||
set(CMAKE_CXX_EXTENSIONS OFF) | |||||
set_property(GLOBAL PROPERTY USE_FOLDERS ON) | |||||
set(TINYEXR_SOURCES | |||||
${PROJECT_SOURCE_DIR}/tinyexr.cc | |||||
) | |||||
if(TINYEXR_USE_MINIZ) | |||||
enable_language(C) | |||||
add_library(miniz STATIC deps/miniz/miniz.c) | |||||
target_include_directories(miniz PUBLIC deps/miniz) | |||||
set_target_properties(miniz PROPERTIES FOLDER "deps") | |||||
list(APPEND TINYEXR_EXT_LIBRARIES miniz) | |||||
endif() | |||||
add_library(${BUILD_TARGET} ${TINYEXR_SOURCES} ${TINYEXR_DEP_SOURCES}) | |||||
add_sanitizers(${BUILD_TARGET}) | |||||
target_compile_definitions(${BUILD_TARGET} PUBLIC TINYEXR_USE_MINIZ=0) | |||||
target_compile_definitions(${BUILD_TARGET} PUBLIC TINYEXR_USE_STB_ZLIB=1) | |||||
target_include_directories(${BUILD_TARGET} PRIVATE ${PROJECT_SOURCE_DIR}) | |||||
target_link_libraries(${BUILD_TARGET} ${TINYEXR_EXT_LIBRARIES} ${CMAKE_DL_LIBS}) | |||||
# Install library | |||||
install(TARGETS ${PROJECT_NAME} | |||||
EXPORT ${PROJECT_NAME}-targets | |||||
ARCHIVE DESTINATION lib | |||||
LIBRARY DESTINATION lib | |||||
RUNTIME DESTINATION bin) | |||||
# Install headers | |||||
install( | |||||
FILES | |||||
${PROJECT_SOURCE_DIR}/tinyexr.h | |||||
DESTINATION include) | |||||
# Install CMake config file | |||||
install(EXPORT ${PROJECT_NAME}-targets | |||||
FILE ${PROJECT_NAME}-targets.cmake | |||||
DESTINATION lib/cmake/${PROJECT_NAME}) | |||||
include(CMakePackageConfigHelpers) | |||||
configure_package_config_file( | |||||
${PROJECT_SOURCE_DIR}/${PROJECT_NAME}-config.cmake.in | |||||
${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}-config.cmake | |||||
INSTALL_DESTINATION lib/cmake/${PROJECT_NAME}) | |||||
install( | |||||
FILES ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}-config.cmake | |||||
DESTINATION lib/cmake/${PROJECT_NAME}) | |||||
# Increase warning level for clang. | |||||
IF (CMAKE_CXX_COMPILER_ID MATCHES "Clang") | |||||
set_source_files_properties(${TINYEXR_SOURCES} PROPERTIES COMPILE_FLAGS "-Weverything -Werror -Wno-padded -Wno-c++98-compat-pedantic -Wno-documentation -Wno-unused-member-function") | |||||
ENDIF () | |||||
if (TINYEXR_BUILD_SAMPLE) | |||||
set(TINYEXR_SAMPLE_SOURCES | |||||
${PROJECT_SOURCE_DIR}/test_tinyexr.cc | |||||
) | |||||
add_executable(${SAMPLE_TARGET} ${TINYEXR_SAMPLE_SOURCES}) | |||||
target_link_libraries(${SAMPLE_TARGET} ${TINYEXR_EXT_LIBRARIES}) | |||||
add_sanitizers(${SAMPLE_TARGET}) | |||||
if (WIN32) | |||||
target_compile_definitions(${SAMPLE_TARGET} PUBLIC UNICODE) | |||||
target_compile_definitions(${SAMPLE_TARGET} PUBLIC _UNICODE) | |||||
# Set ${SAMPLE_TARGET} as a startup project for VS IDE | |||||
set_property(DIRECTORY PROPERTY VS_STARTUP_PROJECT ${SAMPLE_TARGET}) | |||||
# For easier debugging in VS IDE(cmake 3.8.0 or later required) Set working | |||||
# directory to ${BUILD_TARGET} git repo root. | |||||
if(CMAKE_VERSION VERSION_GREATER 3.8.0) | |||||
set_target_properties(${SAMPLE_TARGET} | |||||
PROPERTIES VS_DEBUGGER_WORKING_DIRECTORY | |||||
"${CMAKE_CURRENT_SOURCE_DIR}") | |||||
endif() | |||||
endif(WIN32) | |||||
endif (TINYEXR_BUILD_SAMPLE) |
@ -0,0 +1,25 @@ | |||||
#CXX ?= clang++ | |||||
#CXXFLAGS ?= -fsanitize=address -Werror -Wall -Wextra -g -O0 -DTINYEXR_USE_MINIZ=0 -DTINYEXR_USE_PIZ=0 | |||||
#LDFLAGS ?= -lz | |||||
# ZFP | |||||
#CXXFLAGS += -DTINYEXR_USE_ZFP=1 -I./deps/ZFP/inc | |||||
#LDFLAGS += -L./deps/ZFP/lib -lzfp | |||||
CFLAGS += -I./deps/miniz | |||||
CXXFLAGS += -I./deps/miniz | |||||
.PHONY: test clean | |||||
all: miniz.o | |||||
$(CXX) $(CXXFLAGS) -o test_tinyexr test_tinyexr.cc miniz.o $(LDFLAGS) | |||||
miniz.o: | |||||
$(CC) $(CFLAGS) -c ./deps/miniz/miniz.c | |||||
test: | |||||
./test_tinyexr asakusa.exr | |||||
clean: | |||||
rm -rf test_tinyexr miniz.o | |||||
@ -0,0 +1,18 @@ | |||||
#CXX ?= clang++ | |||||
#CXXFLAGS ?= -fsanitize=address -Werror -Wall -Wextra -g -O0 -DTINYEXR_USE_MINIZ=0 -DTINYEXR_USE_PIZ=0 | |||||
#LDFLAGS ?= -lz | |||||
CXX=x86_64-w64-mingw32-g++ | |||||
# ZFP | |||||
#CXXFLAGS += -DTINYEXR_USE_ZFP=1 -I./deps/ZFP/inc | |||||
#LDFLAGS += -L./deps/ZFP/lib -lzfp | |||||
all: | |||||
$(CC) $(CFLAGS) -c ./deps/miniz/miniz.c | |||||
$(CXX) $(CXXFLAGS) -I./deps/miniz -o test_tinyexr test_tinyexr.cc miniz.o $(LDFLAGS) | |||||
test: | |||||
./test_tinyexr asakusa.exr | |||||
.PHONY: test |
@ -0,0 +1,16 @@ | |||||
#CXX ?= clang++ | |||||
#CXXFLAGS ?= -fsanitize=address -Werror -Wall -Wextra -g -O0 -DTINYEXR_USE_MINIZ=0 -DTINYEXR_USE_PIZ=0 | |||||
#LDFLAGS ?= -lz | |||||
CXX=g++ | |||||
# -municode is not yet implemented | |||||
CXXFLAGS=-DUNICODE -D_UNICODE -mwindows | |||||
all: | |||||
$(CC) $(CFLAGS) -I./deps/miniz ./deps/miniz.c | |||||
$(CXX) $(CXXFLAGS) -I./deps/miniz -o test_tinyexr test_tinyexr.cc miniz.o $(LDFLAGS) | |||||
test: | |||||
./test_tinyexr asakusa.exr | |||||
.PHONY: test |
@ -0,0 +1,25 @@ | |||||
#CXX ?= clang++ | |||||
#CXXFLAGS ?= -fsanitize=address -Werror -Wall -Wextra -g -O0 -DTINYEXR_USE_MINIZ=0 -DTINYEXR_USE_PIZ=0 | |||||
#LDFLAGS ?= -lz | |||||
CC=clang | |||||
CXX=clang++ | |||||
# ZFP | |||||
#CXXFLAGS += -DTINYEXR_USE_ZFP=1 -I./deps/ZFP/inc | |||||
#LDFLAGS += -L./deps/ZFP/lib -lzfp | |||||
CXXFLAGS += -DTINYEXR_USE_MINIZ=1 -I./deps/miniz | |||||
CFLAGS += -DTINYEXR_USE_MINIZ=1 -I./deps/miniz | |||||
# miniz | |||||
miniz.o: | |||||
$(CC) $(CFLAGS) -c ./deps/miniz.c | |||||
# | |||||
all: miniz.o | |||||
$(CXX) $(CXXFLAGS) -o test_tinyexr test_tinyexr.cc miniz.o $(LDFLAGS) | |||||
test: | |||||
./test_tinyexr asakusa.exr | |||||
.PHONY: test |
@ -0,0 +1,575 @@ | |||||
# Tiny OpenEXR image library. | |||||
[![Total alerts](https://img.shields.io/lgtm/alerts/g/syoyo/tinyexr.svg?logo=lgtm&logoWidth=18)](https://lgtm.com/projects/g/syoyo/tinyexr/alerts/) | |||||
![Example](https://github.com/syoyo/tinyexr/blob/master/asakusa.png?raw=true) | |||||
[![AppVeyor build status](https://ci.appveyor.com/api/projects/status/k07ftfe4ph057qau/branch/master?svg=true)](https://ci.appveyor.com/project/syoyo/tinyexr/branch/master) | |||||
[![Travis build Status](https://travis-ci.org/syoyo/tinyexr.svg)](https://travis-ci.org/syoyo/tinyexr) | |||||
[![Coverity Scan Build Status](https://scan.coverity.com/projects/5827/badge.svg)](https://scan.coverity.com/projects/5827) | |||||
`tinyexr` is a small, single header-only library to load and save OpenEXR (.exr) images. | |||||
`tinyexr` is written in portable C++ (no library dependency except for STL), thus `tinyexr` is good to embed into your application. | |||||
To use `tinyexr`, simply copy `tinyexr.h`, `miniz.c` and `miniz.h`(for zlib. You can use system-installed zlib instead of miniz, or the zlib implementation included in `stb_image[_write].h`. Controlled with `TINYEXR_USE_MINIZ` and `TINYEXR_USE_STB_ZLIB` compile flags) into your project. | |||||
# Features | |||||
Current status of `tinyexr` is: | |||||
- OpenEXR v1 image | |||||
- [x] Scanline format | |||||
- [x] Tiled format | |||||
- [x] Tile format with no LoD (load). | |||||
- [x] Tile format with LoD (load). | |||||
- [x] Tile format with no LoD (save). | |||||
- [x] Tile format with LoD (save). | |||||
- [x] Custom attributes | |||||
- OpenEXR v2 image | |||||
- [ ] Multipart format | |||||
- [x] Load multi-part image | |||||
- [x] Save multi-part image | |||||
- [ ] Load multi-part deep image | |||||
- [ ] Save multi-part deep image | |||||
- OpenEXR v2 deep image | |||||
- [x] Loading scanline + ZIPS + HALF or FLOAT pixel type. | |||||
- Compression | |||||
- [x] NONE | |||||
- [x] RLE | |||||
- [x] ZIP | |||||
- [x] ZIPS | |||||
- [x] PIZ | |||||
- [x] ZFP (tinyexr extension) | |||||
- [ ] B44? | |||||
- [ ] B44A? | |||||
- [ ] PIX24? | |||||
- [ ] DWA (not planned, patent encumbered) | |||||
- Line order. | |||||
- [x] Increasing, decreasing (load) | |||||
- [ ] Random? | |||||
- [x] Increasing (save) | |||||
- [ ] decreasing (save) | |||||
- Pixel format (UINT, FLOAT). | |||||
- [x] UINT, FLOAT (load) | |||||
- [x] UINT, FLOAT (deep load) | |||||
- [x] UINT, FLOAT (save) | |||||
- [ ] UINT, FLOAT (deep save) | |||||
- Support for big endian machine. | |||||
- [x] Loading scanline image | |||||
- [x] Saving scanline image | |||||
- [x] Loading multi-part channel EXR (not tested) | |||||
- [x] Saving multi-part channel EXR (not tested) | |||||
- [ ] Loading deep image | |||||
- [ ] Saving deep image | |||||
- Optimization | |||||
- [x] C++11 thread loading | |||||
- [ ] C++11 thread saving | |||||
- [ ] ISPC? | |||||
- [x] OpenMP multi-threading in EXR loading. | |||||
- [x] OpenMP multi-threading in EXR saving. | |||||
- [ ] OpenMP multi-threading in deep image loading. | |||||
- [ ] OpenMP multi-threading in deep image saving. | |||||
* C interface. | |||||
* You can easily write language bindings (e.g. golang) | |||||
# Supported platform | |||||
* [x] x86-64 | |||||
* [x] Windows 7 or later | |||||
* [x] Linux(posix) system | |||||
* [x] macOS | |||||
* [x] AARCH64 | |||||
* [x] aarch64 linux(e.g. Raspberry Pi) | |||||
* [x] Android | |||||
* [x] iOS | |||||
* [x] macOS | |||||
* [ ] RISC-V(Should work) | |||||
* [x] Big endian machine(not maintained, but should work) | |||||
* SPARC, PowerPC, ... | |||||
* [x] WebAssembly(JavaScript) | |||||
* Loader only(See ![js](experimental/js/)) | |||||
* [x] Python binding | |||||
* Loader only https://pypi.org/project/pytinyexr/ | |||||
# Requirements | |||||
* C++ compiler(C++11 recommended. C++03 may work) | |||||
# Use case | |||||
## New TinyEXR (v0.9.5+) | |||||
* Godot. Multi-platform 2D and 3D game engine https://godotengine.org/ | |||||
* Filament. PBR engine(used in a converter tool). https://github.com/google/filament | |||||
* PyEXR. Loading OpenEXR (.exr) images using Python. https://github.com/ialhashim/PyEXR | |||||
* The-Forge. The Forge Cross-Platform Rendering Framework PC, Linux, Ray Tracing, macOS / iOS, Android, XBOX, PS4 https://github.com/ConfettiFX/The-Forge | |||||
* psdr-cuda. Path-space differentiable renderer. https://github.com/uci-rendering/psdr-cuda | |||||
* Studying Microfacets BSDFs https://virtualgonio.pages.xlim.fr/ | |||||
* Your project here! | |||||
## Older TinyEXR (v0.9.0) | |||||
* mallie https://github.com/lighttransport/mallie | |||||
* Cinder 0.9.0 https://libcinder.org/notes/v0.9.0 | |||||
* Piccante (develop branch) http://piccantelib.net/ | |||||
* Your project here! | |||||
## Examples | |||||
* [examples/deepview/](examples/deepview) Deep image view | |||||
* [examples/rgbe2exr/](examples/rgbe2exr) .hdr to EXR converter | |||||
* [examples/exr2rgbe/](examples/exr2rgbe) EXR to .hdr converter | |||||
* [examples/ldr2exr/](examples/exr2rgbe) LDR to EXR converter | |||||
* [examples/exr2ldr/](examples/exr2ldr) EXR to LDR converter | |||||
* [examples/exr2fptiff/](examples/exr2fptiff) EXR to 32bit floating point TIFF converter | |||||
* for 32bit floating point TIFF to EXR convert, see https://github.com/syoyo/tinydngloader/tree/master/examples/fptiff2exr | |||||
* [examples/cube2longlat/](examples/cube2longlat) Cubemap to longlat (equirectangler) converter | |||||
## Experimental | |||||
* [experimental/js/](experimental/js) JavaScript port using Emscripten | |||||
## Usage | |||||
NOTE: **API is still subject to change**. See the source code for details. | |||||
Include `tinyexr.h` with `TINYEXR_IMPLEMENTATION` flag (do this only for **one** .cc file). | |||||
```cpp | |||||
//Please include your own zlib-compatible API header before | |||||
//including `tinyexr.h` when you disable `TINYEXR_USE_MINIZ` | |||||
//#define TINYEXR_USE_MINIZ 0 | |||||
//#include "zlib.h" | |||||
//Or, if your project uses `stb_image[_write].h`, use their | |||||
//zlib implementation: | |||||
//#define TINYEXR_USE_STB_ZLIB 1 | |||||
#define TINYEXR_IMPLEMENTATION | |||||
#include "tinyexr.h" | |||||
``` | |||||
### Compile flags | |||||
* `TINYEXR_USE_MINIZ` Use miniz (default = 1). Please include `zlib.h` header before `tinyexr.h` if you disable miniz support(e.g. use system's zlib). | |||||
* `TINYEXR_USE_STB_ZLIB` Use zlib from `stb_image[_write].h` instead of miniz or the system's zlib (default = 0). | |||||
* `TINYEXR_USE_PIZ` Enable PIZ compression support (default = 1) | |||||
* `TINYEXR_USE_ZFP` Enable ZFP compression supoort (TinyEXR extension, default = 0) | |||||
* `TINYEXR_USE_THREAD` Enable threaded loading using C++11 thread (Requires C++11 compiler, default = 0) | |||||
* `TINYEXR_USE_OPENMP` Enable OpenMP threading support (default = 1 if `_OPENMP` is defined) | |||||
* Use `TINYEXR_USE_OPENMP=0` to force disable OpenMP code path even if OpenMP is available/enabled in the compiler. | |||||
### Quickly reading RGB(A) EXR file. | |||||
```cpp | |||||
const char* input = "asakusa.exr"; | |||||
float* out; // width * height * RGBA | |||||
int width; | |||||
int height; | |||||
const char* err = NULL; // or nullptr in C++11 | |||||
int ret = LoadEXR(&out, &width, &height, input, &err); | |||||
if (ret != TINYEXR_SUCCESS) { | |||||
if (err) { | |||||
fprintf(stderr, "ERR : %s\n", err); | |||||
FreeEXRErrorMessage(err); // release memory of error message. | |||||
} | |||||
} else { | |||||
... | |||||
free(out); // release memory of image data | |||||
} | |||||
``` | |||||
### Reading layered RGB(A) EXR file. | |||||
If you want to read EXR image with layer info (channel has a name with delimiter `.`), please use `LoadEXRWithLayer` API. | |||||
You need to know layer name in advance (e.g. through `EXRLayers` API). | |||||
```cpp | |||||
const char* input = ...; | |||||
const char* layer_name = "diffuse"; // or use EXRLayers to get list of layer names in .exr | |||||
float* out; // width * height * RGBA | |||||
int width; | |||||
int height; | |||||
const char* err = NULL; // or nullptr in C++11 | |||||
// will read `diffuse.R`, `diffuse.G`, `diffuse.B`, (`diffuse.A`) channels | |||||
int ret = LoadEXRWithLayer(&out, &width, &height, input, layer_name, &err); | |||||
if (ret != TINYEXR_SUCCESS) { | |||||
if (err) { | |||||
fprintf(stderr, "ERR : %s\n", err); | |||||
FreeEXRErrorMessage(err); // release memory of error message. | |||||
} | |||||
} else { | |||||
... | |||||
free(out); // release memory of image data | |||||
} | |||||
``` | |||||
### Loading Singlepart EXR from a file. | |||||
Scanline and tiled format are supported. | |||||
```cpp | |||||
// 1. Read EXR version. | |||||
EXRVersion exr_version; | |||||
int ret = ParseEXRVersionFromFile(&exr_version, argv[1]); | |||||
if (ret != 0) { | |||||
fprintf(stderr, "Invalid EXR file: %s\n", argv[1]); | |||||
return -1; | |||||
} | |||||
if (exr_version.multipart) { | |||||
// must be multipart flag is false. | |||||
return -1; | |||||
} | |||||
// 2. Read EXR header | |||||
EXRHeader exr_header; | |||||
InitEXRHeader(&exr_header); | |||||
const char* err = NULL; // or `nullptr` in C++11 or later. | |||||
ret = ParseEXRHeaderFromFile(&exr_header, &exr_version, argv[1], &err); | |||||
if (ret != 0) { | |||||
fprintf(stderr, "Parse EXR err: %s\n", err); | |||||
FreeEXRErrorMessage(err); // free's buffer for an error message | |||||
return ret; | |||||
} | |||||
// // Read HALF channel as FLOAT. | |||||
// for (int i = 0; i < exr_header.num_channels; i++) { | |||||
// if (exr_header.pixel_types[i] == TINYEXR_PIXELTYPE_HALF) { | |||||
// exr_header.requested_pixel_types[i] = TINYEXR_PIXELTYPE_FLOAT; | |||||
// } | |||||
// } | |||||
EXRImage exr_image; | |||||
InitEXRImage(&exr_image); | |||||
ret = LoadEXRImageFromFile(&exr_image, &exr_header, argv[1], &err); | |||||
if (ret != 0) { | |||||
fprintf(stderr, "Load EXR err: %s\n", err); | |||||
FreeEXRHeader(&exr_header); | |||||
FreeEXRErrorMessage(err); // free's buffer for an error message | |||||
return ret; | |||||
} | |||||
// 3. Access image data | |||||
// `exr_image.images` will be filled when EXR is scanline format. | |||||
// `exr_image.tiled` will be filled when EXR is tiled format. | |||||
// 4. Free image data | |||||
FreeEXRImage(&exr_image); | |||||
FreeEXRHeader(&exr_header); | |||||
``` | |||||
### Loading Multipart EXR from a file. | |||||
Scanline and tiled format are supported. | |||||
```cpp | |||||
// 1. Read EXR version. | |||||
EXRVersion exr_version; | |||||
int ret = ParseEXRVersionFromFile(&exr_version, argv[1]); | |||||
if (ret != 0) { | |||||
fprintf(stderr, "Invalid EXR file: %s\n", argv[1]); | |||||
return -1; | |||||
} | |||||
if (!exr_version.multipart) { | |||||
// must be multipart flag is true. | |||||
return -1; | |||||
} | |||||
// 2. Read EXR headers in the EXR. | |||||
EXRHeader **exr_headers; // list of EXRHeader pointers. | |||||
int num_exr_headers; | |||||
const char *err = NULL; // or nullptr in C++11 or later | |||||
// Memory for EXRHeader is allocated inside of ParseEXRMultipartHeaderFromFile, | |||||
ret = ParseEXRMultipartHeaderFromFile(&exr_headers, &num_exr_headers, &exr_version, argv[1], &err); | |||||
if (ret != 0) { | |||||
fprintf(stderr, "Parse EXR err: %s\n", err); | |||||
FreeEXRErrorMessage(err); // free's buffer for an error message | |||||
return ret; | |||||
} | |||||
printf("num parts = %d\n", num_exr_headers); | |||||
// 3. Load images. | |||||
// Prepare array of EXRImage. | |||||
std::vector<EXRImage> images(num_exr_headers); | |||||
for (int i =0; i < num_exr_headers; i++) { | |||||
InitEXRImage(&images[i]); | |||||
} | |||||
ret = LoadEXRMultipartImageFromFile(&images.at(0), const_cast<const EXRHeader**>(exr_headers), num_exr_headers, argv[1], &err); | |||||
if (ret != 0) { | |||||
fprintf(stderr, "Parse EXR err: %s\n", err); | |||||
FreeEXRErrorMessage(err); // free's buffer for an error message | |||||
return ret; | |||||
} | |||||
printf("Loaded %d part images\n", num_exr_headers); | |||||
// 4. Access image data | |||||
// `exr_image.images` will be filled when EXR is scanline format. | |||||
// `exr_image.tiled` will be filled when EXR is tiled format. | |||||
// 5. Free images | |||||
for (int i =0; i < num_exr_headers; i++) { | |||||
FreeEXRImage(&images.at(i)); | |||||
} | |||||
// 6. Free headers. | |||||
for (int i =0; i < num_exr_headers; i++) { | |||||
FreeEXRHeader(exr_headers[i]); | |||||
free(exr_headers[i]); | |||||
} | |||||
free(exr_headers); | |||||
``` | |||||
Saving Scanline EXR file. | |||||
```cpp | |||||
// See `examples/rgbe2exr/` for more details. | |||||
bool SaveEXR(const float* rgb, int width, int height, const char* outfilename) { | |||||
EXRHeader header; | |||||
InitEXRHeader(&header); | |||||
EXRImage image; | |||||
InitEXRImage(&image); | |||||
image.num_channels = 3; | |||||
std::vector<float> images[3]; | |||||
images[0].resize(width * height); | |||||
images[1].resize(width * height); | |||||
images[2].resize(width * height); | |||||
// Split RGBRGBRGB... into R, G and B layer | |||||
for (int i = 0; i < width * height; i++) { | |||||
images[0][i] = rgb[3*i+0]; | |||||
images[1][i] = rgb[3*i+1]; | |||||
images[2][i] = rgb[3*i+2]; | |||||
} | |||||
float* image_ptr[3]; | |||||
image_ptr[0] = &(images[2].at(0)); // B | |||||
image_ptr[1] = &(images[1].at(0)); // G | |||||
image_ptr[2] = &(images[0].at(0)); // R | |||||
image.images = (unsigned char**)image_ptr; | |||||
image.width = width; | |||||
image.height = height; | |||||
header.num_channels = 3; | |||||
header.channels = (EXRChannelInfo *)malloc(sizeof(EXRChannelInfo) * header.num_channels); | |||||
// Must be (A)BGR order, since most of EXR viewers expect this channel order. | |||||
strncpy(header.channels[0].name, "B", 255); header.channels[0].name[strlen("B")] = '\0'; | |||||
strncpy(header.channels[1].name, "G", 255); header.channels[1].name[strlen("G")] = '\0'; | |||||
strncpy(header.channels[2].name, "R", 255); header.channels[2].name[strlen("R")] = '\0'; | |||||
header.pixel_types = (int *)malloc(sizeof(int) * header.num_channels); | |||||
header.requested_pixel_types = (int *)malloc(sizeof(int) * header.num_channels); | |||||
for (int i = 0; i < header.num_channels; i++) { | |||||
header.pixel_types[i] = TINYEXR_PIXELTYPE_FLOAT; // pixel type of input image | |||||
header.requested_pixel_types[i] = TINYEXR_PIXELTYPE_HALF; // pixel type of output image to be stored in .EXR | |||||
} | |||||
const char* err = NULL; // or nullptr in C++11 or later. | |||||
int ret = SaveEXRImageToFile(&image, &header, outfilename, &err); | |||||
if (ret != TINYEXR_SUCCESS) { | |||||
fprintf(stderr, "Save EXR err: %s\n", err); | |||||
FreeEXRErrorMessage(err); // free's buffer for an error message | |||||
return ret; | |||||
} | |||||
printf("Saved exr file. [ %s ] \n", outfilename); | |||||
free(rgb); | |||||
free(header.channels); | |||||
free(header.pixel_types); | |||||
free(header.requested_pixel_types); | |||||
} | |||||
``` | |||||
Reading deep image EXR file. | |||||
See `example/deepview` for actual usage. | |||||
```cpp | |||||
const char* input = "deepimage.exr"; | |||||
const char* err = NULL; // or nullptr | |||||
DeepImage deepImage; | |||||
int ret = LoadDeepEXR(&deepImage, input, &err); | |||||
// access to each sample in the deep pixel. | |||||
for (int y = 0; y < deepImage.height; y++) { | |||||
int sampleNum = deepImage.offset_table[y][deepImage.width-1]; | |||||
for (int x = 0; x < deepImage.width-1; x++) { | |||||
int s_start = deepImage.offset_table[y][x]; | |||||
int s_end = deepImage.offset_table[y][x+1]; | |||||
if (s_start >= sampleNum) { | |||||
continue; | |||||
} | |||||
s_end = (s_end < sampleNum) ? s_end : sampleNum; | |||||
for (int s = s_start; s < s_end; s++) { | |||||
float val = deepImage.image[depthChan][y][s]; | |||||
... | |||||
} | |||||
} | |||||
} | |||||
``` | |||||
### deepview | |||||
`examples/deepview` is simple deep image viewer in OpenGL. | |||||
![DeepViewExample](https://github.com/syoyo/tinyexr/blob/master/examples/deepview/deepview_screencast.gif?raw=true) | |||||
## TinyEXR extension | |||||
### ZFP | |||||
#### NOTE | |||||
TinyEXR adds ZFP compression as an experimemtal support (Linux and MacOSX only). | |||||
ZFP only supports FLOAT format pixel, and its image width and height must be the multiple of 4, since ZFP compresses pixels with 4x4 pixel block. | |||||
#### Setup | |||||
Checkout zfp repo as an submodule. | |||||
$ git submodule update --init | |||||
#### Build | |||||
Then build ZFP | |||||
$ cd deps/ZFP | |||||
$ mkdir -p lib # Create `lib` directory if not exist | |||||
$ make | |||||
Set `1` to `TINYEXT_USE_ZFP` define in `tinyexr.h` | |||||
Build your app with linking `deps/ZFP/lib/libzfp.a` | |||||
#### ZFP attribute | |||||
For ZFP EXR image, the following attribute must exist in its EXR image. | |||||
* `zfpCompressionType` (uchar). | |||||
* 0 = fixed rate compression | |||||
* 1 = precision based variable rate compression | |||||
* 2 = accuracy based variable rate compression | |||||
And the one of following attributes must exist in EXR, depending on the `zfpCompressionType` value. | |||||
* `zfpCompressionRate` (double) | |||||
* Specifies compression rate for fixed rate compression. | |||||
* `zfpCompressionPrecision` (int32) | |||||
* Specifies the number of bits for precision based variable rate compression. | |||||
* `zfpCompressionTolerance` (double) | |||||
* Specifies the tolerance value for accuracy based variable rate compression. | |||||
#### Note on ZFP compression. | |||||
At least ZFP code itself works well on big endian machine. | |||||
## Unit tests | |||||
See `test/unit` directory. | |||||
## TODO | |||||
Contribution is welcome! | |||||
- [ ] Compression | |||||
- [ ] B44? | |||||
- [ ] B44A? | |||||
- [ ] PIX24? | |||||
- [ ] Custom attributes | |||||
- [x] Normal image (EXR 1.x) | |||||
- [ ] Deep image (EXR 2.x) | |||||
- [ ] JavaScript library (experimental, using Emscripten) | |||||
- [x] LoadEXRFromMemory | |||||
- [ ] SaveMultiChannelEXR | |||||
- [ ] Deep image save/load | |||||
- [ ] Write from/to memory buffer. | |||||
- [ ] Deep image save/load | |||||
- [ ] Tile format. | |||||
- [x] Tile format with no LoD (load). | |||||
- [ ] Tile format with LoD (load). | |||||
- [ ] Tile format with no LoD (save). | |||||
- [ ] Tile format with LoD (save). | |||||
- [ ] Support for custom compression type. | |||||
- [x] zfp compression (Not in OpenEXR spec, though) | |||||
- [ ] zstd? | |||||
- [x] Multi-channel. | |||||
- [ ] Multi-part (EXR2.0) | |||||
- [x] Load multi-part image | |||||
- [ ] Load multi-part deep image | |||||
- [ ] Line order. | |||||
- [x] Increasing, decreasing (load) | |||||
- [ ] Random? | |||||
- [ ] Increasing, decreasing (save) | |||||
- [ ] Pixel format (UINT, FLOAT). | |||||
- [x] UINT, FLOAT (load) | |||||
- [x] UINT, FLOAT (deep load) | |||||
- [x] UINT, FLOAT (save) | |||||
- [ ] UINT, FLOAT (deep save) | |||||
- [ ] Support for big endian machine. | |||||
- [ ] Loading multi-part channel EXR | |||||
- [ ] Saving multi-part channel EXR | |||||
- [ ] Loading deep image | |||||
- [ ] Saving deep image | |||||
- [ ] Optimization | |||||
- [ ] ISPC? | |||||
- [x] OpenMP multi-threading in EXR loading. | |||||
- [x] OpenMP multi-threading in EXR saving. | |||||
- [ ] OpenMP multi-threading in deep image loading. | |||||
- [ ] OpenMP multi-threading in deep image saving. | |||||
## Python bindings | |||||
`pytinyexr` is available: https://pypi.org/project/pytinyexr/ (loading only as of 0.9.1) | |||||
## Similar or related projects | |||||
* miniexr: https://github.com/aras-p/miniexr (Write OpenEXR) | |||||
* stb_image_resize.h: https://github.com/nothings/stb (Good for HDR image resizing) | |||||
## License | |||||
3-clause BSD | |||||
`tinyexr` uses miniz, which is developed by Rich Geldreich <richgel99@gmail.com>, and licensed under public domain. | |||||
`tinyexr` tools uses stb, which is licensed under public domain: https://github.com/nothings/stb | |||||
`tinyexr` uses some code from OpenEXR, which is licensed under 3-clause BSD license. | |||||
## Author(s) | |||||
Syoyo Fujita (syoyo@lighttransport.com) | |||||
## Contributor(s) | |||||
* Matt Ebb (http://mattebb.com): deep image example. Thanks! | |||||
* Matt Pharr (http://pharr.org/matt/): Testing tinyexr with OpenEXR(IlmImf). Thanks! | |||||
* Andrew Bell (https://github.com/andrewfb) & Richard Eakin (https://github.com/richardeakin): Improving TinyEXR API. Thanks! | |||||
* Mike Wong (https://github.com/mwkm): ZIPS compression support in loading. Thanks! |
@ -0,0 +1,13 @@ | |||||
version: 0.9.{build} | |||||
platform: x64 | |||||
configuration: Release | |||||
before_build: | |||||
- echo running cmake... | |||||
- cd test | |||||
- cmake -G "Visual Studio 12 Win64" -Bbuild -H.. | |||||
build: | |||||
parallel: true | |||||
project: C:\projects\tinyexr\test\build\tinyexr.sln |
@ -0,0 +1,59 @@ | |||||
# The MIT License (MIT) | |||||
# | |||||
# Copyright (c) | |||||
# 2013 Matthew Arsenault | |||||
# 2015-2016 RWTH Aachen University, Federal Republic of Germany | |||||
# | |||||
# Permission is hereby granted, free of charge, to any person obtaining a copy | |||||
# of this software and associated documentation files (the "Software"), to deal | |||||
# in the Software without restriction, including without limitation the rights | |||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |||||
# copies of the Software, and to permit persons to whom the Software is | |||||
# furnished to do so, subject to the following conditions: | |||||
# | |||||
# The above copyright notice and this permission notice shall be included in all | |||||
# copies or substantial portions of the Software. | |||||
# | |||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | |||||
# SOFTWARE. | |||||
option(SANITIZE_ADDRESS "Enable AddressSanitizer for sanitized targets." Off) | |||||
set(FLAG_CANDIDATES | |||||
# Clang 3.2+ use this version. The no-omit-frame-pointer option is optional. | |||||
"-g -fsanitize=address -fno-omit-frame-pointer" | |||||
"-g -fsanitize=address" | |||||
# Older deprecated flag for ASan | |||||
"-g -faddress-sanitizer" | |||||
) | |||||
if (SANITIZE_ADDRESS AND (SANITIZE_THREAD OR SANITIZE_MEMORY)) | |||||
message(FATAL_ERROR "AddressSanitizer is not compatible with " | |||||
"ThreadSanitizer or MemorySanitizer.") | |||||
endif () | |||||
include(sanitize-helpers) | |||||
if (SANITIZE_ADDRESS) | |||||
sanitizer_check_compiler_flags("${FLAG_CANDIDATES}" "AddressSanitizer" | |||||
"ASan") | |||||
find_program(ASan_WRAPPER "asan-wrapper" PATHS ${CMAKE_MODULE_PATH}) | |||||
mark_as_advanced(ASan_WRAPPER) | |||||
endif () | |||||
function (add_sanitize_address TARGET) | |||||
if (NOT SANITIZE_ADDRESS) | |||||
return() | |||||
endif () | |||||
sanitizer_add_flags(${TARGET} "AddressSanitizer" "ASan") | |||||
endfunction () |
@ -0,0 +1,57 @@ | |||||
# The MIT License (MIT) | |||||
# | |||||
# Copyright (c) | |||||
# 2013 Matthew Arsenault | |||||
# 2015-2016 RWTH Aachen University, Federal Republic of Germany | |||||
# | |||||
# Permission is hereby granted, free of charge, to any person obtaining a copy | |||||
# of this software and associated documentation files (the "Software"), to deal | |||||
# in the Software without restriction, including without limitation the rights | |||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |||||
# copies of the Software, and to permit persons to whom the Software is | |||||
# furnished to do so, subject to the following conditions: | |||||
# | |||||
# The above copyright notice and this permission notice shall be included in all | |||||
# copies or substantial portions of the Software. | |||||
# | |||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | |||||
# SOFTWARE. | |||||
option(SANITIZE_MEMORY "Enable MemorySanitizer for sanitized targets." Off) | |||||
set(FLAG_CANDIDATES | |||||
"-g -fsanitize=memory" | |||||
) | |||||
include(sanitize-helpers) | |||||
if (SANITIZE_MEMORY) | |||||
if (NOT ${CMAKE_SYSTEM_NAME} STREQUAL "Linux") | |||||
message(WARNING "MemorySanitizer disabled for target ${TARGET} because " | |||||
"MemorySanitizer is supported for Linux systems only.") | |||||
set(SANITIZE_MEMORY Off CACHE BOOL | |||||
"Enable MemorySanitizer for sanitized targets." FORCE) | |||||
elseif (NOT ${CMAKE_SIZEOF_VOID_P} EQUAL 8) | |||||
message(WARNING "MemorySanitizer disabled for target ${TARGET} because " | |||||
"MemorySanitizer is supported for 64bit systems only.") | |||||
set(SANITIZE_MEMORY Off CACHE BOOL | |||||
"Enable MemorySanitizer for sanitized targets." FORCE) | |||||
else () | |||||
sanitizer_check_compiler_flags("${FLAG_CANDIDATES}" "MemorySanitizer" | |||||
"MSan") | |||||
endif () | |||||
endif () | |||||
function (add_sanitize_memory TARGET) | |||||
if (NOT SANITIZE_MEMORY) | |||||
return() | |||||
endif () | |||||
sanitizer_add_flags(${TARGET} "MemorySanitizer" "MSan") | |||||
endfunction () |
@ -0,0 +1,94 @@ | |||||
# The MIT License (MIT) | |||||
# | |||||
# Copyright (c) | |||||
# 2013 Matthew Arsenault | |||||
# 2015-2016 RWTH Aachen University, Federal Republic of Germany | |||||
# | |||||
# Permission is hereby granted, free of charge, to any person obtaining a copy | |||||
# of this software and associated documentation files (the "Software"), to deal | |||||
# in the Software without restriction, including without limitation the rights | |||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |||||
# copies of the Software, and to permit persons to whom the Software is | |||||
# furnished to do so, subject to the following conditions: | |||||
# | |||||
# The above copyright notice and this permission notice shall be included in all | |||||
# copies or substantial portions of the Software. | |||||
# | |||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | |||||
# SOFTWARE. | |||||
# If any of the used compiler is a GNU compiler, add a second option to static | |||||
# link against the sanitizers. | |||||
option(SANITIZE_LINK_STATIC "Try to link static against sanitizers." Off) | |||||
set(FIND_QUIETLY_FLAG "") | |||||
if (DEFINED Sanitizers_FIND_QUIETLY) | |||||
set(FIND_QUIETLY_FLAG "QUIET") | |||||
endif () | |||||
find_package(ASan ${FIND_QUIETLY_FLAG}) | |||||
find_package(TSan ${FIND_QUIETLY_FLAG}) | |||||
find_package(MSan ${FIND_QUIETLY_FLAG}) | |||||
find_package(UBSan ${FIND_QUIETLY_FLAG}) | |||||
function(sanitizer_add_blacklist_file FILE) | |||||
if(NOT IS_ABSOLUTE ${FILE}) | |||||
set(FILE "${CMAKE_CURRENT_SOURCE_DIR}/${FILE}") | |||||
endif() | |||||
get_filename_component(FILE "${FILE}" REALPATH) | |||||
sanitizer_check_compiler_flags("-fsanitize-blacklist=${FILE}" | |||||
"SanitizerBlacklist" "SanBlist") | |||||
endfunction() | |||||
function(add_sanitizers ...) | |||||
# If no sanitizer is enabled, return immediately. | |||||
if (NOT (SANITIZE_ADDRESS OR SANITIZE_MEMORY OR SANITIZE_THREAD OR | |||||
SANITIZE_UNDEFINED)) | |||||
return() | |||||
endif () | |||||
foreach (TARGET ${ARGV}) | |||||
# Check if this target will be compiled by exactly one compiler. Other- | |||||
# wise sanitizers can't be used and a warning should be printed once. | |||||
get_target_property(TARGET_TYPE ${TARGET} TYPE) | |||||
if (TARGET_TYPE STREQUAL "INTERFACE_LIBRARY") | |||||
message(WARNING "Can't use any sanitizers for target ${TARGET}, " | |||||
"because it is an interface library and cannot be " | |||||
"compiled directly.") | |||||
return() | |||||
endif () | |||||
sanitizer_target_compilers(${TARGET} TARGET_COMPILER) | |||||
list(LENGTH TARGET_COMPILER NUM_COMPILERS) | |||||
if (NUM_COMPILERS GREATER 1) | |||||
message(WARNING "Can't use any sanitizers for target ${TARGET}, " | |||||
"because it will be compiled by incompatible compilers. " | |||||
"Target will be compiled without sanitizers.") | |||||
return() | |||||
# If the target is compiled by no or no known compiler, give a warning. | |||||
elseif (NUM_COMPILERS EQUAL 0) | |||||
message(WARNING "Sanitizers for target ${TARGET} may not be" | |||||
" usable, because it uses no or an unknown compiler. " | |||||
"This is a false warning for targets using only " | |||||
"object lib(s) as input.") | |||||
endif () | |||||
# Add sanitizers for target. | |||||
add_sanitize_address(${TARGET}) | |||||
add_sanitize_thread(${TARGET}) | |||||
add_sanitize_memory(${TARGET}) | |||||
add_sanitize_undefined(${TARGET}) | |||||
endforeach () | |||||
endfunction(add_sanitizers) |
@ -0,0 +1,65 @@ | |||||
# The MIT License (MIT) | |||||
# | |||||
# Copyright (c) | |||||
# 2013 Matthew Arsenault | |||||
# 2015-2016 RWTH Aachen University, Federal Republic of Germany | |||||
# | |||||
# Permission is hereby granted, free of charge, to any person obtaining a copy | |||||
# of this software and associated documentation files (the "Software"), to deal | |||||
# in the Software without restriction, including without limitation the rights | |||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |||||
# copies of the Software, and to permit persons to whom the Software is | |||||
# furnished to do so, subject to the following conditions: | |||||
# | |||||
# The above copyright notice and this permission notice shall be included in all | |||||
# copies or substantial portions of the Software. | |||||
# | |||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | |||||
# SOFTWARE. | |||||
option(SANITIZE_THREAD "Enable ThreadSanitizer for sanitized targets." Off) | |||||
set(FLAG_CANDIDATES | |||||
"-g -fsanitize=thread" | |||||
) | |||||
# ThreadSanitizer is not compatible with MemorySanitizer. | |||||
if (SANITIZE_THREAD AND SANITIZE_MEMORY) | |||||
message(FATAL_ERROR "ThreadSanitizer is not compatible with " | |||||
"MemorySanitizer.") | |||||
endif () | |||||
include(sanitize-helpers) | |||||
if (SANITIZE_THREAD) | |||||
if (NOT ${CMAKE_SYSTEM_NAME} STREQUAL "Linux" AND | |||||
NOT ${CMAKE_SYSTEM_NAME} STREQUAL "Darwin") | |||||
message(WARNING "ThreadSanitizer disabled for target ${TARGET} because " | |||||
"ThreadSanitizer is supported for Linux systems and macOS only.") | |||||
set(SANITIZE_THREAD Off CACHE BOOL | |||||
"Enable ThreadSanitizer for sanitized targets." FORCE) | |||||
elseif (NOT ${CMAKE_SIZEOF_VOID_P} EQUAL 8) | |||||
message(WARNING "ThreadSanitizer disabled for target ${TARGET} because " | |||||
"ThreadSanitizer is supported for 64bit systems only.") | |||||
set(SANITIZE_THREAD Off CACHE BOOL | |||||
"Enable ThreadSanitizer for sanitized targets." FORCE) | |||||
else () | |||||
sanitizer_check_compiler_flags("${FLAG_CANDIDATES}" "ThreadSanitizer" | |||||
"TSan") | |||||
endif () | |||||
endif () | |||||
function (add_sanitize_thread TARGET) | |||||
if (NOT SANITIZE_THREAD) | |||||
return() | |||||
endif () | |||||
sanitizer_add_flags(${TARGET} "ThreadSanitizer" "TSan") | |||||
endfunction () |
@ -0,0 +1,46 @@ | |||||
# The MIT License (MIT) | |||||
# | |||||
# Copyright (c) | |||||
# 2013 Matthew Arsenault | |||||
# 2015-2016 RWTH Aachen University, Federal Republic of Germany | |||||
# | |||||
# Permission is hereby granted, free of charge, to any person obtaining a copy | |||||
# of this software and associated documentation files (the "Software"), to deal | |||||
# in the Software without restriction, including without limitation the rights | |||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |||||
# copies of the Software, and to permit persons to whom the Software is | |||||
# furnished to do so, subject to the following conditions: | |||||
# | |||||
# The above copyright notice and this permission notice shall be included in all | |||||
# copies or substantial portions of the Software. | |||||
# | |||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | |||||
# SOFTWARE. | |||||
option(SANITIZE_UNDEFINED | |||||
"Enable UndefinedBehaviorSanitizer for sanitized targets." Off) | |||||
set(FLAG_CANDIDATES | |||||
"-g -fsanitize=undefined" | |||||
) | |||||
include(sanitize-helpers) | |||||
if (SANITIZE_UNDEFINED) | |||||
sanitizer_check_compiler_flags("${FLAG_CANDIDATES}" | |||||
"UndefinedBehaviorSanitizer" "UBSan") | |||||
endif () | |||||
function (add_sanitize_undefined TARGET) | |||||
if (NOT SANITIZE_UNDEFINED) | |||||
return() | |||||
endif () | |||||
sanitizer_add_flags(${TARGET} "UndefinedBehaviorSanitizer" "UBSan") | |||||
endfunction () |
@ -0,0 +1,55 @@ | |||||
#!/bin/sh | |||||
# The MIT License (MIT) | |||||
# | |||||
# Copyright (c) | |||||
# 2013 Matthew Arsenault | |||||
# 2015-2016 RWTH Aachen University, Federal Republic of Germany | |||||
# | |||||
# Permission is hereby granted, free of charge, to any person obtaining a copy | |||||
# of this software and associated documentation files (the "Software"), to deal | |||||
# in the Software without restriction, including without limitation the rights | |||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |||||
# copies of the Software, and to permit persons to whom the Software is | |||||
# furnished to do so, subject to the following conditions: | |||||
# | |||||
# The above copyright notice and this permission notice shall be included in all | |||||
# copies or substantial portions of the Software. | |||||
# | |||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | |||||
# SOFTWARE. | |||||
# This script is a wrapper for AddressSanitizer. In some special cases you need | |||||
# to preload AddressSanitizer to avoid error messages - e.g. if you're | |||||
# preloading another library to your application. At the moment this script will | |||||
# only do something, if we're running on a Linux platform. OSX might not be | |||||
# affected. | |||||
# Exit immediately, if platform is not Linux. | |||||
if [ "$(uname)" != "Linux" ] | |||||
then | |||||
exec $@ | |||||
fi | |||||
# Get the used libasan of the application ($1). If a libasan was found, it will | |||||
# be prepended to LD_PRELOAD. | |||||
libasan=$(ldd $1 | grep libasan | sed "s/^[[:space:]]//" | cut -d' ' -f1) | |||||
if [ -n "$libasan" ] | |||||
then | |||||
if [ -n "$LD_PRELOAD" ] | |||||
then | |||||
export LD_PRELOAD="$libasan:$LD_PRELOAD" | |||||
else | |||||
export LD_PRELOAD="$libasan" | |||||
fi | |||||
fi | |||||
# Execute the application. | |||||
exec $@ |
@ -0,0 +1,177 @@ | |||||
# The MIT License (MIT) | |||||
# | |||||
# Copyright (c) | |||||
# 2013 Matthew Arsenault | |||||
# 2015-2016 RWTH Aachen University, Federal Republic of Germany | |||||
# | |||||
# Permission is hereby granted, free of charge, to any person obtaining a copy | |||||
# of this software and associated documentation files (the "Software"), to deal | |||||
# in the Software without restriction, including without limitation the rights | |||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |||||
# copies of the Software, and to permit persons to whom the Software is | |||||
# furnished to do so, subject to the following conditions: | |||||
# | |||||
# The above copyright notice and this permission notice shall be included in all | |||||
# copies or substantial portions of the Software. | |||||
# | |||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | |||||
# SOFTWARE. | |||||
# Helper function to get the language of a source file. | |||||
function (sanitizer_lang_of_source FILE RETURN_VAR) | |||||
get_filename_component(LONGEST_EXT "${FILE}" EXT) | |||||
# If extension is empty return. This can happen for extensionless headers | |||||
if("${LONGEST_EXT}" STREQUAL "") | |||||
set(${RETURN_VAR} "" PARENT_SCOPE) | |||||
return() | |||||
endif() | |||||
# Get shortest extension as some files can have dot in their names | |||||
string(REGEX REPLACE "^.*(\\.[^.]+)$" "\\1" FILE_EXT ${LONGEST_EXT}) | |||||
string(TOLOWER "${FILE_EXT}" FILE_EXT) | |||||
string(SUBSTRING "${FILE_EXT}" 1 -1 FILE_EXT) | |||||
get_property(ENABLED_LANGUAGES GLOBAL PROPERTY ENABLED_LANGUAGES) | |||||
foreach (LANG ${ENABLED_LANGUAGES}) | |||||
list(FIND CMAKE_${LANG}_SOURCE_FILE_EXTENSIONS "${FILE_EXT}" TEMP) | |||||
if (NOT ${TEMP} EQUAL -1) | |||||
set(${RETURN_VAR} "${LANG}" PARENT_SCOPE) | |||||
return() | |||||
endif () | |||||
endforeach() | |||||
set(${RETURN_VAR} "" PARENT_SCOPE) | |||||
endfunction () | |||||
# Helper function to get compilers used by a target. | |||||
function (sanitizer_target_compilers TARGET RETURN_VAR) | |||||
# Check if all sources for target use the same compiler. If a target uses | |||||
# e.g. C and Fortran mixed and uses different compilers (e.g. clang and | |||||
# gfortran) this can trigger huge problems, because different compilers may | |||||
# use different implementations for sanitizers. | |||||
set(BUFFER "") | |||||
get_target_property(TSOURCES ${TARGET} SOURCES) | |||||
foreach (FILE ${TSOURCES}) | |||||
# If expression was found, FILE is a generator-expression for an object | |||||
# library. Object libraries will be ignored. | |||||
string(REGEX MATCH "TARGET_OBJECTS:([^ >]+)" _file ${FILE}) | |||||
if ("${_file}" STREQUAL "") | |||||
sanitizer_lang_of_source(${FILE} LANG) | |||||
if (LANG) | |||||
list(APPEND BUFFER ${CMAKE_${LANG}_COMPILER_ID}) | |||||
endif () | |||||
endif () | |||||
endforeach () | |||||
list(REMOVE_DUPLICATES BUFFER) | |||||
set(${RETURN_VAR} "${BUFFER}" PARENT_SCOPE) | |||||
endfunction () | |||||
# Helper function to check compiler flags for language compiler. | |||||
function (sanitizer_check_compiler_flag FLAG LANG VARIABLE) | |||||
if (${LANG} STREQUAL "C") | |||||
include(CheckCCompilerFlag) | |||||
check_c_compiler_flag("${FLAG}" ${VARIABLE}) | |||||
elseif (${LANG} STREQUAL "CXX") | |||||
include(CheckCXXCompilerFlag) | |||||
check_cxx_compiler_flag("${FLAG}" ${VARIABLE}) | |||||
elseif (${LANG} STREQUAL "Fortran") | |||||
# CheckFortranCompilerFlag was introduced in CMake 3.x. To be compatible | |||||
# with older Cmake versions, we will check if this module is present | |||||
# before we use it. Otherwise we will define Fortran coverage support as | |||||
# not available. | |||||
include(CheckFortranCompilerFlag OPTIONAL RESULT_VARIABLE INCLUDED) | |||||
if (INCLUDED) | |||||
check_fortran_compiler_flag("${FLAG}" ${VARIABLE}) | |||||
elseif (NOT CMAKE_REQUIRED_QUIET) | |||||
message(STATUS "Performing Test ${VARIABLE}") | |||||
message(STATUS "Performing Test ${VARIABLE}" | |||||
" - Failed (Check not supported)") | |||||
endif () | |||||
endif() | |||||
endfunction () | |||||
# Helper function to test compiler flags. | |||||
function (sanitizer_check_compiler_flags FLAG_CANDIDATES NAME PREFIX) | |||||
set(CMAKE_REQUIRED_QUIET ${${PREFIX}_FIND_QUIETLY}) | |||||
get_property(ENABLED_LANGUAGES GLOBAL PROPERTY ENABLED_LANGUAGES) | |||||
foreach (LANG ${ENABLED_LANGUAGES}) | |||||
# Sanitizer flags are not dependend on language, but the used compiler. | |||||
# So instead of searching flags foreach language, search flags foreach | |||||
# compiler used. | |||||
set(COMPILER ${CMAKE_${LANG}_COMPILER_ID}) | |||||
if (NOT DEFINED ${PREFIX}_${COMPILER}_FLAGS) | |||||
foreach (FLAG ${FLAG_CANDIDATES}) | |||||
if(NOT CMAKE_REQUIRED_QUIET) | |||||
message(STATUS "Try ${COMPILER} ${NAME} flag = [${FLAG}]") | |||||
endif() | |||||
set(CMAKE_REQUIRED_FLAGS "${FLAG}") | |||||
unset(${PREFIX}_FLAG_DETECTED CACHE) | |||||
sanitizer_check_compiler_flag("${FLAG}" ${LANG} | |||||
${PREFIX}_FLAG_DETECTED) | |||||
if (${PREFIX}_FLAG_DETECTED) | |||||
# If compiler is a GNU compiler, search for static flag, if | |||||
# SANITIZE_LINK_STATIC is enabled. | |||||
if (SANITIZE_LINK_STATIC AND (${COMPILER} STREQUAL "GNU")) | |||||
string(TOLOWER ${PREFIX} PREFIX_lower) | |||||
sanitizer_check_compiler_flag( | |||||
"-static-lib${PREFIX_lower}" ${LANG} | |||||
${PREFIX}_STATIC_FLAG_DETECTED) | |||||
if (${PREFIX}_STATIC_FLAG_DETECTED) | |||||
set(FLAG "-static-lib${PREFIX_lower} ${FLAG}") | |||||
endif () | |||||
endif () | |||||
set(${PREFIX}_${COMPILER}_FLAGS "${FLAG}" CACHE STRING | |||||
"${NAME} flags for ${COMPILER} compiler.") | |||||
mark_as_advanced(${PREFIX}_${COMPILER}_FLAGS) | |||||
break() | |||||
endif () | |||||
endforeach () | |||||
if (NOT ${PREFIX}_FLAG_DETECTED) | |||||
set(${PREFIX}_${COMPILER}_FLAGS "" CACHE STRING | |||||
"${NAME} flags for ${COMPILER} compiler.") | |||||
mark_as_advanced(${PREFIX}_${COMPILER}_FLAGS) | |||||
message(WARNING "${NAME} is not available for ${COMPILER} " | |||||
"compiler. Targets using this compiler will be " | |||||
"compiled without ${NAME}.") | |||||
endif () | |||||
endif () | |||||
endforeach () | |||||
endfunction () | |||||
# Helper to assign sanitizer flags for TARGET. | |||||
function (sanitizer_add_flags TARGET NAME PREFIX) | |||||
# Get list of compilers used by target and check, if sanitizer is available | |||||
# for this target. Other compiler checks like check for conflicting | |||||
# compilers will be done in add_sanitizers function. | |||||
sanitizer_target_compilers(${TARGET} TARGET_COMPILER) | |||||
list(LENGTH TARGET_COMPILER NUM_COMPILERS) | |||||
if ("${${PREFIX}_${TARGET_COMPILER}_FLAGS}" STREQUAL "") | |||||
return() | |||||
endif() | |||||
# Set compile- and link-flags for target. | |||||
set_property(TARGET ${TARGET} APPEND_STRING | |||||
PROPERTY COMPILE_FLAGS " ${${PREFIX}_${TARGET_COMPILER}_FLAGS}") | |||||
set_property(TARGET ${TARGET} APPEND_STRING | |||||
PROPERTY COMPILE_FLAGS " ${SanBlist_${TARGET_COMPILER}_FLAGS}") | |||||
set_property(TARGET ${TARGET} APPEND_STRING | |||||
PROPERTY LINK_FLAGS " ${${PREFIX}_${TARGET_COMPILER}_FLAGS}") | |||||
endfunction () |
@ -0,0 +1,78 @@ | |||||
exe = "test_tinyexr.exe" | |||||
# "gnu" or "msvc" are provided as predefined toolchain | |||||
toolchain = "msvc" | |||||
# optional | |||||
link_pool_depth = 1 | |||||
# optional | |||||
builddir = { | |||||
"gnu" : "build" | |||||
, "msvc" : "build" | |||||
, "clang" : "build" | |||||
} | |||||
# required | |||||
includes = { | |||||
"gnu" : [ "-I." ] | |||||
, "msvc" : [ "/I." ] | |||||
, "clang" : [ "-I." ] | |||||
} | |||||
# required | |||||
defines = { | |||||
"gnu" : [ "-DEXAMPLE=1" ] | |||||
, "msvc" : [ "/DEXAMPLE=1" ] | |||||
, "clang" : [ "-DEXAMPLE=1" ] | |||||
} | |||||
# required | |||||
cflags = { | |||||
"gnu" : [ "-O2", "-g" ] | |||||
, "msvc" : [ "/O2" ] | |||||
, "clang" : [ "-O2", "-g" ] | |||||
} | |||||
# required | |||||
cxxflags = { | |||||
"gnu" : [ "-O2", "-g" ] | |||||
, "msvc" : [ "/O2", "/W4" ] | |||||
, "clang" : [ "-O2", "-g", "-fsanitize=address" ] | |||||
} | |||||
# required | |||||
ldflags = { | |||||
"gnu" : [ ] | |||||
, "msvc" : [ ] | |||||
, "clang" : [ "-fsanitize=address" ] | |||||
} | |||||
# optionsl | |||||
cxx_files = [ "test_tinyexr.cc" ] | |||||
c_files = [ ] | |||||
# You can register your own toolchain through register_toolchain function | |||||
def register_toolchain(ninja): | |||||
pass | |||||
#ninja.rule('clangcxx', description='CXX $out', | |||||
# command='$clangcxx -MMD -MF $out.d $clangdefines $clangincludes $clangcxxflags -c $in -o $out', | |||||
# depfile='$out.d', deps='gcc') | |||||
#ninja.rule('clangcc', description='CC $out', | |||||
# command='$clangcc -MMD -MF $out.d $clangdefines $clangincludes $clangcflags -c $in -o $out', | |||||
# depfile='$out.d', deps='gcc') | |||||
#ninja.rule('clanglink', description='LINK $out', pool='link_pool', | |||||
# command='$clangld $clangldflags -o $out $in $libs') | |||||
#ninja.rule('clangar', description='AR $out', pool='link_pool', | |||||
# command='$clangar rsc $out $in') | |||||
#ninja.rule('clangstamp', description='STAMP $out', command='touch $out') | |||||
#ninja.newline() | |||||
#ninja.variable('clangcxx', 'clang++') | |||||
#ninja.variable('clangcc', 'clang') | |||||
#ninja.variable('clangld', 'clang++') | |||||
#ninja.variable('clangar', 'ar') | |||||
#ninja.newline() | |||||
@ -0,0 +1,196 @@ | |||||
## Changelog | |||||
### 2.2.0 | |||||
- Fix examples with amalgamation | |||||
- Modified cmake script to support shared library mode and find_package | |||||
- Fix for misleading doc comment on `mz_zip_reader_init_cfile` function | |||||
- Add include location tolerance and stop forcing `_GNU_SOURCE` | |||||
- Fix: mz_zip_reader_locate_file_v2 returns an mz_bool | |||||
- Fix large file system checks | |||||
- Add #elif to enable an external mz_crc32() to be linked in | |||||
- Write with dynamic size (size of file/data to be added not known before adding) | |||||
- Added uncompress2 for zlib compatibility | |||||
- Add support for building as a Meson subproject | |||||
- Added OSSFuzz support; Integrate with CIFuzz | |||||
- Add pkg-config file | |||||
- Fixed use-of-uninitialized value msan error when copying dist bytes with no output bytes written. | |||||
- mz_zip_validate_file(): fix memory leak on errors | |||||
- Fixed MSAN use-of-uninitialized in tinfl_decompress when invalid dist is decoded. In this instance dist was 31 which s_dist_base translates as 0 | |||||
- Add flag to set (compressed) size in local file header | |||||
- avoid use of uninitialized value in tdefl_record_literal | |||||
### 2.1.0 | |||||
- More instances of memcpy instead of cast and use memcpy per default | |||||
- Remove inline for c90 support | |||||
- New function to read files via callback functions when adding them | |||||
- Fix out of bounds read while reading Zip64 extended information | |||||
- guard memcpy when n == 0 because buffer may be NULL | |||||
- Implement inflateReset() function | |||||
- Move comp/decomp alloc/free prototypes under guarding #ifndef MZ_NO_MALLOC | |||||
- Fix large file support under Windows | |||||
- Don't warn if _LARGEFILE64_SOURCE is not defined to 1 | |||||
- Fixes for MSVC warnings | |||||
- Remove check that path of file added to archive contains ':' or '\' | |||||
- Add !defined check on MINIZ_USE_ALIGNED_LOADS_AND_STORES | |||||
### 2.0.8 | |||||
- Remove unimplemented functions (mz_zip_locate_file and mz_zip_locate_file_v2) | |||||
- Add license, changelog, readme and example files to release zip | |||||
- Fix heap overflow to user buffer in tinfl_status tinfl_decompress | |||||
- Fix corrupt archive if uncompressed file smaller than 4 byte and the file is added by mz_zip_writer_add_mem* | |||||
### 2.0.7 | |||||
- Removed need in C++ compiler in cmake build | |||||
- Fixed a lot of uninitialized value errors found with Valgrind by memsetting m_dict to 0 in tdefl_init | |||||
- Fix resource leak in mz_zip_reader_init_file_v2 | |||||
- Fix assert with mz_zip_writer_add_mem* w/MZ_DEFAULT_COMPRESSION | |||||
- cmake build: install library and headers | |||||
- Remove _LARGEFILE64_SOURCE requirement from apple defines for large files | |||||
### 2.0.6 | |||||
- Improve MZ_ZIP_FLAG_WRITE_ZIP64 documentation | |||||
- Remove check for cur_archive_file_ofs > UINT_MAX because cur_archive_file_ofs is not used after this point | |||||
- Add cmake debug configuration | |||||
- Fix PNG height when creating png files | |||||
- Add "iterative" file extraction method based on mz_zip_reader_extract_to_callback. | |||||
- Option to use memcpy for unaligned data access | |||||
- Define processor/arch macros as zero if not set to one | |||||
### 2.0.4/2.0.5 | |||||
- Fix compilation with the various omission compile definitions | |||||
### 2.0.3 | |||||
- Fix GCC/clang compile warnings | |||||
- Added callback for periodic flushes (for ZIP file streaming) | |||||
- Use UTF-8 for file names in ZIP files per default | |||||
### 2.0.2 | |||||
- Fix source backwards compatibility with 1.x | |||||
- Fix a ZIP bit not being set correctly | |||||
### 2.0.1 | |||||
- Added some tests | |||||
- Added CI | |||||
- Make source code ANSI C compatible | |||||
### 2.0.0 beta | |||||
- Matthew Sitton merged miniz 1.x to Rich Geldreich's vogl ZIP64 changes. Miniz is now licensed as MIT since the vogl code base is MIT licensed | |||||
- Miniz is now split into several files | |||||
- Miniz does now not seek backwards when creating ZIP files. That is the ZIP files can be streamed | |||||
- Miniz automatically switches to the ZIP64 format when the created ZIP files goes over ZIP file limits | |||||
- Similar to [SQLite](https://www.sqlite.org/amalgamation.html) the Miniz source code is amalgamated into one miniz.c/miniz.h pair in a build step (amalgamate.sh). Please use miniz.c/miniz.h in your projects | |||||
- Miniz 2 is only source back-compatible with miniz 1.x. It breaks binary compatibility because structures changed | |||||
### v1.16 BETA Oct 19, 2013 | |||||
Still testing, this release is downloadable from [here](http://www.tenacioussoftware.com/miniz_v116_beta_r1.7z). Two key inflator-only robustness and streaming related changes. Also merged in tdefl_compressor_alloc(), tdefl_compressor_free() helpers to make script bindings easier for rustyzip. I would greatly appreciate any help with testing or any feedback. | |||||
The inflator in raw (non-zlib) mode is now usable on gzip or similar streams that have a bunch of bytes following the raw deflate data (problem discovered by rustyzip author williamw520). This version should never read beyond the last byte of the raw deflate data independent of how many bytes you pass into the input buffer. | |||||
The inflator now has a new failure status TINFL_STATUS_FAILED_CANNOT_MAKE_PROGRESS (-4). Previously, if the inflator was starved of bytes and could not make progress (because the input buffer was empty and the caller did not set the TINFL_FLAG_HAS_MORE_INPUT flag - say on truncated or corrupted compressed data stream) it would append all 0's to the input and try to soldier on. This is scary behavior if the caller didn't know when to stop accepting output (because it didn't know how much uncompressed data was expected, or didn't enforce a sane maximum). v1.16 will instead return TINFL_STATUS_FAILED_CANNOT_MAKE_PROGRESS immediately if it needs 1 or more bytes to make progress, the input buf is empty, and the caller has indicated that no more input is available. This is a "soft" failure, so you can call the inflator again with more input and it will try to continue, or you can give up and fail. This could be very useful in network streaming scenarios. | |||||
- The inflator coroutine func. is subtle and complex so I'm being cautious about this release. I would greatly appreciate any help with testing or any feedback. | |||||
I feel good about these changes, and they've been through several hours of automated testing, but they will probably not fix anything for the majority of prev. users so I'm | |||||
going to mark this release as beta for a few weeks and continue testing it at work/home on various things. | |||||
- The inflator in raw (non-zlib) mode is now usable on gzip or similar data streams that have a bunch of bytes following the raw deflate data (problem discovered by rustyzip author williamw520). | |||||
This version should *never* read beyond the last byte of the raw deflate data independent of how many bytes you pass into the input buffer. This issue was caused by the various Huffman bitbuffer lookahead optimizations, and | |||||
would not be an issue if the caller knew and enforced the precise size of the raw compressed data *or* if the compressed data was in zlib format (i.e. always followed by the byte aligned zlib adler32). | |||||
So in other words, you can now call the inflator on deflate streams that are followed by arbitrary amounts of data and it's guaranteed that decompression will stop exactly on the last byte. | |||||
- The inflator now has a new failure status: TINFL_STATUS_FAILED_CANNOT_MAKE_PROGRESS (-4). Previously, if the inflator was starved of bytes and could not make progress (because the input buffer was empty and the | |||||
caller did not set the TINFL_FLAG_HAS_MORE_INPUT flag - say on truncated or corrupted compressed data stream) it would append all 0's to the input and try to soldier on. | |||||
This is scary, because in the worst case, I believe it was possible for the prev. inflator to start outputting large amounts of literal data. If the caller didn't know when to stop accepting output | |||||
(because it didn't know how much uncompressed data was expected, or didn't enforce a sane maximum) it could continue forever. v1.16 cannot fall into this failure mode, instead it'll return | |||||
TINFL_STATUS_FAILED_CANNOT_MAKE_PROGRESS immediately if it needs 1 or more bytes to make progress, the input buf is empty, and the caller has indicated that no more input is available. This is a "soft" | |||||
failure, so you can call the inflator again with more input and it will try to continue, or you can give up and fail. This could be very useful in network streaming scenarios. | |||||
- Added documentation to all the tinfl return status codes, fixed miniz_tester so it accepts double minus params for Linux, tweaked example1.c, added a simple "follower bytes" test to miniz_tester.cpp. | |||||
### v1.15 r4 STABLE - Oct 13, 2013 | |||||
Merged over a few very minor bug fixes that I fixed in the zip64 branch. This is downloadable from [here](http://code.google.com/p/miniz/downloads/list) and also in SVN head (as of 10/19/13). | |||||
### v1.15 - Oct. 13, 2013 | |||||
Interim bugfix release while I work on the next major release with zip64 and streaming compression/decompression support. Fixed the MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY bug (thanks kahmyong.moon@hp.com), which could cause the locate files func to not find files when this flag was specified. Also fixed a bug in mz_zip_reader_extract_to_mem_no_alloc() with user provided read buffers (thanks kymoon). I also merged lots of compiler fixes from various github repo branches and Google Code issue reports. I finally added cmake support (only tested under for Linux so far), compiled and tested with clang v3.3 and gcc 4.6 (under Linux), added defl_write_image_to_png_file_in_memory_ex() (supports Y flipping for OpenGL use, real-time compression), added a new PNG example (example6.c - Mandelbrot), and I added 64-bit file I/O support (stat64(), etc.) for glibc. | |||||
- Critical fix for the MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY bug (thanks kahmyong.moon@hp.com) which could cause locate files to not find files. This bug | |||||
would only have occured in earlier versions if you explicitly used this flag, OR if you used mz_zip_extract_archive_file_to_heap() or mz_zip_add_mem_to_archive_file_in_place() | |||||
(which used this flag). If you can't switch to v1.15 but want to fix this bug, just remove the uses of this flag from both helper funcs (and of course don't use the flag). | |||||
- Bugfix in mz_zip_reader_extract_to_mem_no_alloc() from kymoon when pUser_read_buf is not NULL and compressed size is > uncompressed size | |||||
- Fixing mz_zip_reader_extract_*() funcs so they don't try to extract compressed data from directory entries, to account for weird zipfiles which contain zero-size compressed data on dir entries. | |||||
Hopefully this fix won't cause any issues on weird zip archives, because it assumes the low 16-bits of zip external attributes are DOS attributes (which I believe they always are in practice). | |||||
- Fixing mz_zip_reader_is_file_a_directory() so it doesn't check the internal attributes, just the filename and external attributes | |||||
- mz_zip_reader_init_file() - missing MZ_FCLOSE() call if the seek failed | |||||
- Added cmake support for Linux builds which builds all the examples, tested with clang v3.3 and gcc v4.6. | |||||
- Clang fix for tdefl_write_image_to_png_file_in_memory() from toffaletti | |||||
- Merged MZ_FORCEINLINE fix from hdeanclark | |||||
- Fix <time.h> include before config #ifdef, thanks emil.brink | |||||
- Added tdefl_write_image_to_png_file_in_memory_ex(): supports Y flipping (super useful for OpenGL apps), and explicit control over the compression level (so you can | |||||
set it to 1 for real-time compression). | |||||
- Merged in some compiler fixes from paulharris's github repro. | |||||
- Retested this build under Windows (VS 2010, including static analysis), tcc 0.9.26, gcc v4.6 and clang v3.3. | |||||
- Added example6.c, which dumps an image of the mandelbrot set to a PNG file. | |||||
- Modified example2 to help test the MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY flag more. | |||||
- In r3: Bugfix to mz_zip_writer_add_file() found during merge: Fix possible src file fclose() leak if alignment bytes+local header file write faiiled | |||||
- In r4: Minor bugfix to mz_zip_writer_add_from_zip_reader(): Was pushing the wrong central dir header offset, appears harmless in this release, but it became a problem in the zip64 branch | |||||
### v1.14 - May 20, 2012 | |||||
(SVN Only) Minor tweaks to get miniz.c compiling with the Tiny C Compiler, added #ifndef MINIZ_NO_TIME guards around utime.h includes. Adding mz_free() function, so the caller can free heap blocks returned by miniz using whatever heap functions it has been configured to use, MSVC specific fixes to use "safe" variants of several functions (localtime_s, fopen_s, freopen_s). | |||||
MinGW32/64 GCC 4.6.1 compiler fixes: added MZ_FORCEINLINE, #include <time.h> (thanks fermtect). | |||||
Compiler specific fixes, some from fermtect. I upgraded to TDM GCC 4.6.1 and now static __forceinline is giving it fits, so I'm changing all usage of __forceinline to MZ_FORCEINLINE and forcing gcc to use __attribute__((__always_inline__)) (and MSVC to use __forceinline). Also various fixes from fermtect for MinGW32: added #include , 64-bit ftell/fseek fixes. | |||||
### v1.13 - May 19, 2012 | |||||
From jason@cornsyrup.org and kelwert@mtu.edu - Most importantly, fixed mz_crc32() so it doesn't compute the wrong CRC-32's when mz_ulong is 64-bits. Temporarily/locally slammed in "typedef unsigned long mz_ulong" and re-ran a randomized regression test on ~500k files. Other stuff: | |||||
Eliminated a bunch of warnings when compiling with GCC 32-bit/64. Ran all examples, miniz.c, and tinfl.c through MSVC 2008's /analyze (static analysis) option and fixed all warnings (except for the silly "Use of the comma-operator in a tested expression.." analysis warning, which I purposely use to work around a MSVC compiler warning). | |||||
Created 32-bit and 64-bit Codeblocks projects/workspace. Built and tested Linux executables. The codeblocks workspace is compatible with Linux+Win32/x64. Added miniz_tester solution/project, which is a useful little app derived from LZHAM's tester app that I use as part of the regression test. Ran miniz.c and tinfl.c through another series of regression testing on ~500,000 files and archives. Modified example5.c so it purposely disables a bunch of high-level functionality (MINIZ_NO_STDIO, etc.). (Thanks to corysama for the MINIZ_NO_STDIO bug report.) | |||||
Fix ftell() usage in a few of the examples so they exit with an error on files which are too large (a limitation of the examples, not miniz itself). Fix fail logic handling in mz_zip_add_mem_to_archive_file_in_place() so it always calls mz_zip_writer_finalize_archive() and mz_zip_writer_end(), even if the file add fails. | |||||
- From jason@cornsyrup.org and kelwert@mtu.edu - Fix mz_crc32() so it doesn't compute the wrong CRC-32's when mz_ulong is 64-bit. | |||||
- Temporarily/locally slammed in "typedef unsigned long mz_ulong" and re-ran a randomized regression test on ~500k files. | |||||
- Eliminated a bunch of warnings when compiling with GCC 32-bit/64. | |||||
- Ran all examples, miniz.c, and tinfl.c through MSVC 2008's /analyze (static analysis) option and fixed all warnings (except for the silly | |||||
"Use of the comma-operator in a tested expression.." analysis warning, which I purposely use to work around a MSVC compiler warning). | |||||
- Created 32-bit and 64-bit Codeblocks projects/workspace. Built and tested Linux executables. The codeblocks workspace is compatible with Linux+Win32/x64. | |||||
- Added miniz_tester solution/project, which is a useful little app derived from LZHAM's tester app that I use as part of the regression test. | |||||
- Ran miniz.c and tinfl.c through another series of regression testing on ~500,000 files and archives. | |||||
- Modified example5.c so it purposely disables a bunch of high-level functionality (MINIZ_NO_STDIO, etc.). (Thanks to corysama for the MINIZ_NO_STDIO bug report.) | |||||
- Fix ftell() usage in examples so they exit with an error on files which are too large (a limitation of the examples, not miniz itself). | |||||
### v1.12 - 4/12/12 | |||||
More comments, added low-level example5.c, fixed a couple minor level_and_flags issues in the archive API's. | |||||
level_and_flags can now be set to MZ_DEFAULT_COMPRESSION. Thanks to Bruce Dawson <bruced@valvesoftware.com> for the feedback/bug report. | |||||
### v1.11 - 5/28/11 | |||||
Added statement from unlicense.org | |||||
### v1.10 - 5/27/11 | |||||
- Substantial compressor optimizations: | |||||
- Level 1 is now ~4x faster than before. The L1 compressor's throughput now varies between 70-110MB/sec. on a Core i7 (actual throughput varies depending on the type of data, and x64 vs. x86). | |||||
- Improved baseline L2-L9 compression perf. Also, greatly improved compression perf. issues on some file types. | |||||
- Refactored the compression code for better readability and maintainability. | |||||
- Added level 10 compression level (L10 has slightly better ratio than level 9, but could have a potentially large drop in throughput on some files). | |||||
### v1.09 - 5/15/11 | |||||
Initial stable release. | |||||
@ -0,0 +1,22 @@ | |||||
Copyright 2013-2014 RAD Game Tools and Valve Software | |||||
Copyright 2010-2014 Rich Geldreich and Tenacious Software LLC | |||||
All Rights Reserved. | |||||
Permission is hereby granted, free of charge, to any person obtaining a copy | |||||
of this software and associated documentation files (the "Software"), to deal | |||||
in the Software without restriction, including without limitation the rights | |||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |||||
copies of the Software, and to permit persons to whom the Software is | |||||
furnished to do so, subject to the following conditions: | |||||
The above copyright notice and this permission notice shall be included in | |||||
all copies or substantial portions of the Software. | |||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |||||
THE SOFTWARE. |
@ -0,0 +1,105 @@ | |||||
// example1.c - Demonstrates miniz.c's compress() and uncompress() functions (same as zlib's). | |||||
// Public domain, May 15 2011, Rich Geldreich, richgel99@gmail.com. See "unlicense" statement at the end of tinfl.c. | |||||
#include <stdio.h> | |||||
#include "miniz.h" | |||||
typedef unsigned char uint8; | |||||
typedef unsigned short uint16; | |||||
typedef unsigned int uint; | |||||
// The string to compress. | |||||
static const char *s_pStr = "Good morning Dr. Chandra. This is Hal. I am ready for my first lesson." \ | |||||
"Good morning Dr. Chandra. This is Hal. I am ready for my first lesson." \ | |||||
"Good morning Dr. Chandra. This is Hal. I am ready for my first lesson." \ | |||||
"Good morning Dr. Chandra. This is Hal. I am ready for my first lesson." \ | |||||
"Good morning Dr. Chandra. This is Hal. I am ready for my first lesson." \ | |||||
"Good morning Dr. Chandra. This is Hal. I am ready for my first lesson." \ | |||||
"Good morning Dr. Chandra. This is Hal. I am ready for my first lesson."; | |||||
int main(int argc, char *argv[]) | |||||
{ | |||||
uint step = 0; | |||||
int cmp_status; | |||||
uLong src_len = (uLong)strlen(s_pStr); | |||||
uLong cmp_len = compressBound(src_len); | |||||
uLong uncomp_len = src_len; | |||||
uint8 *pCmp, *pUncomp; | |||||
uint total_succeeded = 0; | |||||
(void)argc, (void)argv; | |||||
printf("miniz.c version: %s\n", MZ_VERSION); | |||||
do | |||||
{ | |||||
// Allocate buffers to hold compressed and uncompressed data. | |||||
pCmp = (mz_uint8 *)malloc((size_t)cmp_len); | |||||
pUncomp = (mz_uint8 *)malloc((size_t)src_len); | |||||
if ((!pCmp) || (!pUncomp)) | |||||
{ | |||||
printf("Out of memory!\n"); | |||||
return EXIT_FAILURE; | |||||
} | |||||
// Compress the string. | |||||
cmp_status = compress(pCmp, &cmp_len, (const unsigned char *)s_pStr, src_len); | |||||
if (cmp_status != Z_OK) | |||||
{ | |||||
printf("compress() failed!\n"); | |||||
free(pCmp); | |||||
free(pUncomp); | |||||
return EXIT_FAILURE; | |||||
} | |||||
printf("Compressed from %u to %u bytes\n", (mz_uint32)src_len, (mz_uint32)cmp_len); | |||||
if (step) | |||||
{ | |||||
// Purposely corrupt the compressed data if fuzzy testing (this is a very crude fuzzy test). | |||||
uint n = 1 + (rand() % 3); | |||||
while (n--) | |||||
{ | |||||
uint i = rand() % cmp_len; | |||||
pCmp[i] ^= (rand() & 0xFF); | |||||
} | |||||
} | |||||
// Decompress. | |||||
cmp_status = uncompress(pUncomp, &uncomp_len, pCmp, cmp_len); | |||||
total_succeeded += (cmp_status == Z_OK); | |||||
if (step) | |||||
{ | |||||
printf("Simple fuzzy test: step %u total_succeeded: %u\n", step, total_succeeded); | |||||
} | |||||
else | |||||
{ | |||||
if (cmp_status != Z_OK) | |||||
{ | |||||
printf("uncompress failed!\n"); | |||||
free(pCmp); | |||||
free(pUncomp); | |||||
return EXIT_FAILURE; | |||||
} | |||||
printf("Decompressed from %u to %u bytes\n", (mz_uint32)cmp_len, (mz_uint32)uncomp_len); | |||||
// Ensure uncompress() returned the expected data. | |||||
if ((uncomp_len != src_len) || (memcmp(pUncomp, s_pStr, (size_t)src_len))) | |||||
{ | |||||
printf("Decompression failed!\n"); | |||||
free(pCmp); | |||||
free(pUncomp); | |||||
return EXIT_FAILURE; | |||||
} | |||||
} | |||||
free(pCmp); | |||||
free(pUncomp); | |||||
step++; | |||||
// Keep on fuzzy testing if there's a non-empty command line. | |||||
} while (argc >= 2); | |||||
printf("Success.\n"); | |||||
return EXIT_SUCCESS; | |||||
} |
@ -0,0 +1,164 @@ | |||||
// example2.c - Simple demonstration of miniz.c's ZIP archive API's. | |||||
// Note this test deletes the test archive file "__mz_example2_test__.zip" in the current directory, then creates a new one with test data. | |||||
// Public domain, May 15 2011, Rich Geldreich, richgel99@gmail.com. See "unlicense" statement at the end of tinfl.c. | |||||
#if defined(__GNUC__) | |||||
// Ensure we get the 64-bit variants of the CRT's file I/O calls | |||||
#ifndef _FILE_OFFSET_BITS | |||||
#define _FILE_OFFSET_BITS 64 | |||||
#endif | |||||
#ifndef _LARGEFILE64_SOURCE | |||||
#define _LARGEFILE64_SOURCE 1 | |||||
#endif | |||||
#endif | |||||
#include <stdio.h> | |||||
#include "miniz.h" | |||||
typedef unsigned char uint8; | |||||
typedef unsigned short uint16; | |||||
typedef unsigned int uint; | |||||
// The string to compress. | |||||
static const char *s_pTest_str = | |||||
"MISSION CONTROL I wouldn't worry too much about the computer. First of all, there is still a chance that he is right, despite your tests, and" \ | |||||
"if it should happen again, we suggest eliminating this possibility by allowing the unit to remain in place and seeing whether or not it" \ | |||||
"actually fails. If the computer should turn out to be wrong, the situation is still not alarming. The type of obsessional error he may be" \ | |||||
"guilty of is not unknown among the latest generation of HAL 9000 computers. It has almost always revolved around a single detail, such as" \ | |||||
"the one you have described, and it has never interfered with the integrity or reliability of the computer's performance in other areas." \ | |||||
"No one is certain of the cause of this kind of malfunctioning. It may be over-programming, but it could also be any number of reasons. In any" \ | |||||
"event, it is somewhat analogous to human neurotic behavior. Does this answer your query? Zero-five-three-Zero, MC, transmission concluded."; | |||||
static const char *s_pComment = "This is a comment"; | |||||
int main(int argc, char *argv[]) | |||||
{ | |||||
int i, sort_iter; | |||||
mz_bool status; | |||||
size_t uncomp_size; | |||||
mz_zip_archive zip_archive; | |||||
void *p; | |||||
const int N = 50; | |||||
char data[2048]; | |||||
char archive_filename[64]; | |||||
static const char *s_Test_archive_filename = "__mz_example2_test__.zip"; | |||||
assert((strlen(s_pTest_str) + 64) < sizeof(data)); | |||||
printf("miniz.c version: %s\n", MZ_VERSION); | |||||
(void)argc, (void)argv; | |||||
// Delete the test archive, so it doesn't keep growing as we run this test | |||||
remove(s_Test_archive_filename); | |||||
// Append a bunch of text files to the test archive | |||||
for (i = (N - 1); i >= 0; --i) | |||||
{ | |||||
sprintf(archive_filename, "%u.txt", i); | |||||
sprintf(data, "%u %s %u", (N - 1) - i, s_pTest_str, i); | |||||
// Add a new file to the archive. Note this is an IN-PLACE operation, so if it fails your archive is probably hosed (its central directory may not be complete) but it should be recoverable using zip -F or -FF. So use caution with this guy. | |||||
// A more robust way to add a file to an archive would be to read it into memory, perform the operation, then write a new archive out to a temp file and then delete/rename the files. | |||||
// Or, write a new archive to disk to a temp file, then delete/rename the files. For this test this API is fine. | |||||
status = mz_zip_add_mem_to_archive_file_in_place(s_Test_archive_filename, archive_filename, data, strlen(data) + 1, s_pComment, (uint16)strlen(s_pComment), MZ_BEST_COMPRESSION); | |||||
if (!status) | |||||
{ | |||||
printf("mz_zip_add_mem_to_archive_file_in_place failed!\n"); | |||||
return EXIT_FAILURE; | |||||
} | |||||
} | |||||
// Add a directory entry for testing | |||||
status = mz_zip_add_mem_to_archive_file_in_place(s_Test_archive_filename, "directory/", NULL, 0, "no comment", (uint16)strlen("no comment"), MZ_BEST_COMPRESSION); | |||||
if (!status) | |||||
{ | |||||
printf("mz_zip_add_mem_to_archive_file_in_place failed!\n"); | |||||
return EXIT_FAILURE; | |||||
} | |||||
// Now try to open the archive. | |||||
memset(&zip_archive, 0, sizeof(zip_archive)); | |||||
status = mz_zip_reader_init_file(&zip_archive, s_Test_archive_filename, 0); | |||||
if (!status) | |||||
{ | |||||
printf("mz_zip_reader_init_file() failed!\n"); | |||||
return EXIT_FAILURE; | |||||
} | |||||
// Get and print information about each file in the archive. | |||||
for (i = 0; i < (int)mz_zip_reader_get_num_files(&zip_archive); i++) | |||||
{ | |||||
mz_zip_archive_file_stat file_stat; | |||||
if (!mz_zip_reader_file_stat(&zip_archive, i, &file_stat)) | |||||
{ | |||||
printf("mz_zip_reader_file_stat() failed!\n"); | |||||
mz_zip_reader_end(&zip_archive); | |||||
return EXIT_FAILURE; | |||||
} | |||||
printf("Filename: \"%s\", Comment: \"%s\", Uncompressed size: %u, Compressed size: %u, Is Dir: %u\n", file_stat.m_filename, file_stat.m_comment, (uint)file_stat.m_uncomp_size, (uint)file_stat.m_comp_size, mz_zip_reader_is_file_a_directory(&zip_archive, i)); | |||||
if (!strcmp(file_stat.m_filename, "directory/")) | |||||
{ | |||||
if (!mz_zip_reader_is_file_a_directory(&zip_archive, i)) | |||||
{ | |||||
printf("mz_zip_reader_is_file_a_directory() didn't return the expected results!\n"); | |||||
mz_zip_reader_end(&zip_archive); | |||||
return EXIT_FAILURE; | |||||
} | |||||
} | |||||
} | |||||
// Close the archive, freeing any resources it was using | |||||
mz_zip_reader_end(&zip_archive); | |||||
// Now verify the compressed data | |||||
for (sort_iter = 0; sort_iter < 2; sort_iter++) | |||||
{ | |||||
memset(&zip_archive, 0, sizeof(zip_archive)); | |||||
status = mz_zip_reader_init_file(&zip_archive, s_Test_archive_filename, sort_iter ? MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY : 0); | |||||
if (!status) | |||||
{ | |||||
printf("mz_zip_reader_init_file() failed!\n"); | |||||
return EXIT_FAILURE; | |||||
} | |||||
for (i = 0; i < N; i++) | |||||
{ | |||||
sprintf(archive_filename, "%u.txt", i); | |||||
sprintf(data, "%u %s %u", (N - 1) - i, s_pTest_str, i); | |||||
// Try to extract all the files to the heap. | |||||
p = mz_zip_reader_extract_file_to_heap(&zip_archive, archive_filename, &uncomp_size, 0); | |||||
if (!p) | |||||
{ | |||||
printf("mz_zip_reader_extract_file_to_heap() failed!\n"); | |||||
mz_zip_reader_end(&zip_archive); | |||||
return EXIT_FAILURE; | |||||
} | |||||
// Make sure the extraction really succeeded. | |||||
if ((uncomp_size != (strlen(data) + 1)) || (memcmp(p, data, strlen(data)))) | |||||
{ | |||||
printf("mz_zip_reader_extract_file_to_heap() failed to extract the proper data\n"); | |||||
mz_free(p); | |||||
mz_zip_reader_end(&zip_archive); | |||||
return EXIT_FAILURE; | |||||
} | |||||
printf("Successfully extracted file \"%s\", size %u\n", archive_filename, (uint)uncomp_size); | |||||
printf("File data: \"%s\"\n", (const char *)p); | |||||
// We're done. | |||||
mz_free(p); | |||||
} | |||||
// Close the archive, freeing any resources it was using | |||||
mz_zip_reader_end(&zip_archive); | |||||
} | |||||
printf("Success.\n"); | |||||
return EXIT_SUCCESS; | |||||
} |
@ -0,0 +1,269 @@ | |||||
// example3.c - Demonstrates how to use miniz.c's deflate() and inflate() functions for simple file compression. | |||||
// Public domain, May 15 2011, Rich Geldreich, richgel99@gmail.com. See "unlicense" statement at the end of tinfl.c. | |||||
// For simplicity, this example is limited to files smaller than 4GB, but this is not a limitation of miniz.c. | |||||
#include <stdio.h> | |||||
#include <limits.h> | |||||
#include "miniz.h" | |||||
typedef unsigned char uint8; | |||||
typedef unsigned short uint16; | |||||
typedef unsigned int uint; | |||||
#define my_max(a,b) (((a) > (b)) ? (a) : (b)) | |||||
#define my_min(a,b) (((a) < (b)) ? (a) : (b)) | |||||
#define BUF_SIZE (1024 * 1024) | |||||
static uint8 s_inbuf[BUF_SIZE]; | |||||
static uint8 s_outbuf[BUF_SIZE]; | |||||
int main(int argc, char *argv[]) | |||||
{ | |||||
const char *pMode; | |||||
FILE *pInfile, *pOutfile; | |||||
uint infile_size; | |||||
int level = Z_BEST_COMPRESSION; | |||||
z_stream stream; | |||||
int p = 1; | |||||
const char *pSrc_filename; | |||||
const char *pDst_filename; | |||||
long file_loc; | |||||
printf("miniz.c version: %s\n", MZ_VERSION); | |||||
if (argc < 4) | |||||
{ | |||||
printf("Usage: example3 [options] [mode:c or d] infile outfile\n"); | |||||
printf("\nModes:\n"); | |||||
printf("c - Compresses file infile to a zlib stream in file outfile\n"); | |||||
printf("d - Decompress zlib stream in file infile to file outfile\n"); | |||||
printf("\nOptions:\n"); | |||||
printf("-l[0-10] - Compression level, higher values are slower.\n"); | |||||
return EXIT_FAILURE; | |||||
} | |||||
while ((p < argc) && (argv[p][0] == '-')) | |||||
{ | |||||
switch (argv[p][1]) | |||||
{ | |||||
case 'l': | |||||
{ | |||||
level = atoi(&argv[1][2]); | |||||
if ((level < 0) || (level > 10)) | |||||
{ | |||||
printf("Invalid level!\n"); | |||||
return EXIT_FAILURE; | |||||
} | |||||
break; | |||||
} | |||||
default: | |||||
{ | |||||
printf("Invalid option: %s\n", argv[p]); | |||||
return EXIT_FAILURE; | |||||
} | |||||
} | |||||
p++; | |||||
} | |||||
if ((argc - p) < 3) | |||||
{ | |||||
printf("Must specify mode, input filename, and output filename after options!\n"); | |||||
return EXIT_FAILURE; | |||||
} | |||||
else if ((argc - p) > 3) | |||||
{ | |||||
printf("Too many filenames!\n"); | |||||
return EXIT_FAILURE; | |||||
} | |||||
pMode = argv[p++]; | |||||
if (!strchr("cCdD", pMode[0])) | |||||
{ | |||||
printf("Invalid mode!\n"); | |||||
return EXIT_FAILURE; | |||||
} | |||||
pSrc_filename = argv[p++]; | |||||
pDst_filename = argv[p++]; | |||||
printf("Mode: %c, Level: %u\nInput File: \"%s\"\nOutput File: \"%s\"\n", pMode[0], level, pSrc_filename, pDst_filename); | |||||
// Open input file. | |||||
pInfile = fopen(pSrc_filename, "rb"); | |||||
if (!pInfile) | |||||
{ | |||||
printf("Failed opening input file!\n"); | |||||
return EXIT_FAILURE; | |||||
} | |||||
// Determine input file's size. | |||||
fseek(pInfile, 0, SEEK_END); | |||||
file_loc = ftell(pInfile); | |||||
fseek(pInfile, 0, SEEK_SET); | |||||
if ((file_loc < 0) || (file_loc > INT_MAX)) | |||||
{ | |||||
// This is not a limitation of miniz or tinfl, but this example. | |||||
printf("File is too large to be processed by this example.\n"); | |||||
return EXIT_FAILURE; | |||||
} | |||||
infile_size = (uint)file_loc; | |||||
// Open output file. | |||||
pOutfile = fopen(pDst_filename, "wb"); | |||||
if (!pOutfile) | |||||
{ | |||||
printf("Failed opening output file!\n"); | |||||
return EXIT_FAILURE; | |||||
} | |||||
printf("Input file size: %u\n", infile_size); | |||||
// Init the z_stream | |||||
memset(&stream, 0, sizeof(stream)); | |||||
stream.next_in = s_inbuf; | |||||
stream.avail_in = 0; | |||||
stream.next_out = s_outbuf; | |||||
stream.avail_out = BUF_SIZE; | |||||
if ((pMode[0] == 'c') || (pMode[0] == 'C')) | |||||
{ | |||||
// Compression. | |||||
uint infile_remaining = infile_size; | |||||
if (deflateInit(&stream, level) != Z_OK) | |||||
{ | |||||
printf("deflateInit() failed!\n"); | |||||
return EXIT_FAILURE; | |||||
} | |||||
for ( ; ; ) | |||||
{ | |||||
int status; | |||||
if (!stream.avail_in) | |||||
{ | |||||
// Input buffer is empty, so read more bytes from input file. | |||||
uint n = my_min(BUF_SIZE, infile_remaining); | |||||
if (fread(s_inbuf, 1, n, pInfile) != n) | |||||
{ | |||||
printf("Failed reading from input file!\n"); | |||||
return EXIT_FAILURE; | |||||
} | |||||
stream.next_in = s_inbuf; | |||||
stream.avail_in = n; | |||||
infile_remaining -= n; | |||||
//printf("Input bytes remaining: %u\n", infile_remaining); | |||||
} | |||||
status = deflate(&stream, infile_remaining ? Z_NO_FLUSH : Z_FINISH); | |||||
if ((status == Z_STREAM_END) || (!stream.avail_out)) | |||||
{ | |||||
// Output buffer is full, or compression is done, so write buffer to output file. | |||||
uint n = BUF_SIZE - stream.avail_out; | |||||
if (fwrite(s_outbuf, 1, n, pOutfile) != n) | |||||
{ | |||||
printf("Failed writing to output file!\n"); | |||||
return EXIT_FAILURE; | |||||
} | |||||
stream.next_out = s_outbuf; | |||||
stream.avail_out = BUF_SIZE; | |||||
} | |||||
if (status == Z_STREAM_END) | |||||
break; | |||||
else if (status != Z_OK) | |||||
{ | |||||
printf("deflate() failed with status %i!\n", status); | |||||
return EXIT_FAILURE; | |||||
} | |||||
} | |||||
if (deflateEnd(&stream) != Z_OK) | |||||
{ | |||||
printf("deflateEnd() failed!\n"); | |||||
return EXIT_FAILURE; | |||||
} | |||||
} | |||||
else if ((pMode[0] == 'd') || (pMode[0] == 'D')) | |||||
{ | |||||
// Decompression. | |||||
uint infile_remaining = infile_size; | |||||
if (inflateInit(&stream)) | |||||
{ | |||||
printf("inflateInit() failed!\n"); | |||||
return EXIT_FAILURE; | |||||
} | |||||
for ( ; ; ) | |||||
{ | |||||
int status; | |||||
if (!stream.avail_in) | |||||
{ | |||||
// Input buffer is empty, so read more bytes from input file. | |||||
uint n = my_min(BUF_SIZE, infile_remaining); | |||||
if (fread(s_inbuf, 1, n, pInfile) != n) | |||||
{ | |||||
printf("Failed reading from input file!\n"); | |||||
return EXIT_FAILURE; | |||||
} | |||||
stream.next_in = s_inbuf; | |||||
stream.avail_in = n; | |||||
infile_remaining -= n; | |||||
} | |||||
status = inflate(&stream, Z_SYNC_FLUSH); | |||||
if ((status == Z_STREAM_END) || (!stream.avail_out)) | |||||
{ | |||||
// Output buffer is full, or decompression is done, so write buffer to output file. | |||||
uint n = BUF_SIZE - stream.avail_out; | |||||
if (fwrite(s_outbuf, 1, n, pOutfile) != n) | |||||
{ | |||||
printf("Failed writing to output file!\n"); | |||||
return EXIT_FAILURE; | |||||
} | |||||
stream.next_out = s_outbuf; | |||||
stream.avail_out = BUF_SIZE; | |||||
} | |||||
if (status == Z_STREAM_END) | |||||
break; | |||||
else if (status != Z_OK) | |||||
{ | |||||
printf("inflate() failed with status %i!\n", status); | |||||
return EXIT_FAILURE; | |||||
} | |||||
} | |||||
if (inflateEnd(&stream) != Z_OK) | |||||
{ | |||||
printf("inflateEnd() failed!\n"); | |||||
return EXIT_FAILURE; | |||||
} | |||||
} | |||||
else | |||||
{ | |||||
printf("Invalid mode!\n"); | |||||
return EXIT_FAILURE; | |||||
} | |||||
fclose(pInfile); | |||||
if (EOF == fclose(pOutfile)) | |||||
{ | |||||
printf("Failed writing to output file!\n"); | |||||
return EXIT_FAILURE; | |||||
} | |||||
printf("Total input bytes: %u\n", (mz_uint32)stream.total_in); | |||||
printf("Total output bytes: %u\n", (mz_uint32)stream.total_out); | |||||
printf("Success.\n"); | |||||
return EXIT_SUCCESS; | |||||
} |
@ -0,0 +1,102 @@ | |||||
// example4.c - Uses tinfl.c to decompress a zlib stream in memory to an output file | |||||
// Public domain, May 15 2011, Rich Geldreich, richgel99@gmail.com. See "unlicense" statement at the end of tinfl.c. | |||||
#include "miniz.h" | |||||
#include <stdio.h> | |||||
#include <limits.h> | |||||
typedef unsigned char uint8; | |||||
typedef unsigned short uint16; | |||||
typedef unsigned int uint; | |||||
#define my_max(a,b) (((a) > (b)) ? (a) : (b)) | |||||
#define my_min(a,b) (((a) < (b)) ? (a) : (b)) | |||||
static int tinfl_put_buf_func(const void* pBuf, int len, void *pUser) | |||||
{ | |||||
return len == (int)fwrite(pBuf, 1, len, (FILE*)pUser); | |||||
} | |||||
int main(int argc, char *argv[]) | |||||
{ | |||||
int status; | |||||
FILE *pInfile, *pOutfile; | |||||
uint infile_size, outfile_size; | |||||
size_t in_buf_size; | |||||
uint8 *pCmp_data; | |||||
long file_loc; | |||||
if (argc != 3) | |||||
{ | |||||
printf("Usage: example4 infile outfile\n"); | |||||
printf("Decompresses zlib stream in file infile to file outfile.\n"); | |||||
printf("Input file must be able to fit entirely in memory.\n"); | |||||
printf("example3 can be used to create compressed zlib streams.\n"); | |||||
return EXIT_FAILURE; | |||||
} | |||||
// Open input file. | |||||
pInfile = fopen(argv[1], "rb"); | |||||
if (!pInfile) | |||||
{ | |||||
printf("Failed opening input file!\n"); | |||||
return EXIT_FAILURE; | |||||
} | |||||
// Determine input file's size. | |||||
fseek(pInfile, 0, SEEK_END); | |||||
file_loc = ftell(pInfile); | |||||
fseek(pInfile, 0, SEEK_SET); | |||||
if ((file_loc < 0) || (file_loc > INT_MAX)) | |||||
{ | |||||
// This is not a limitation of miniz or tinfl, but this example. | |||||
printf("File is too large to be processed by this example.\n"); | |||||
return EXIT_FAILURE; | |||||
} | |||||
infile_size = (uint)file_loc; | |||||
pCmp_data = (uint8 *)malloc(infile_size); | |||||
if (!pCmp_data) | |||||
{ | |||||
printf("Out of memory!\n"); | |||||
return EXIT_FAILURE; | |||||
} | |||||
if (fread(pCmp_data, 1, infile_size, pInfile) != infile_size) | |||||
{ | |||||
printf("Failed reading input file!\n"); | |||||
return EXIT_FAILURE; | |||||
} | |||||
// Open output file. | |||||
pOutfile = fopen(argv[2], "wb"); | |||||
if (!pOutfile) | |||||
{ | |||||
printf("Failed opening output file!\n"); | |||||
return EXIT_FAILURE; | |||||
} | |||||
printf("Input file size: %u\n", infile_size); | |||||
in_buf_size = infile_size; | |||||
status = tinfl_decompress_mem_to_callback(pCmp_data, &in_buf_size, tinfl_put_buf_func, pOutfile, TINFL_FLAG_PARSE_ZLIB_HEADER); | |||||
if (!status) | |||||
{ | |||||
printf("tinfl_decompress_mem_to_callback() failed with status %i!\n", status); | |||||
return EXIT_FAILURE; | |||||
} | |||||
outfile_size = ftell(pOutfile); | |||||
fclose(pInfile); | |||||
if (EOF == fclose(pOutfile)) | |||||
{ | |||||
printf("Failed writing to output file!\n"); | |||||
return EXIT_FAILURE; | |||||
} | |||||
printf("Total input bytes: %u\n", (uint)in_buf_size); | |||||
printf("Total output bytes: %u\n", outfile_size); | |||||
printf("Success.\n"); | |||||
return EXIT_SUCCESS; | |||||
} |
@ -0,0 +1,327 @@ | |||||
// example5.c - Demonstrates how to use miniz.c's low-level tdefl_compress() and tinfl_inflate() API's for simple file to file compression/decompression. | |||||
// The low-level API's are the fastest, make no use of dynamic memory allocation, and are the most flexible functions exposed by miniz.c. | |||||
// Public domain, April 11 2012, Rich Geldreich, richgel99@gmail.com. See "unlicense" statement at the end of tinfl.c. | |||||
// For simplicity, this example is limited to files smaller than 4GB, but this is not a limitation of miniz.c. | |||||
// Purposely disable a whole bunch of stuff this low-level example doesn't use. | |||||
#define MINIZ_NO_STDIO | |||||
#define MINIZ_NO_ARCHIVE_APIS | |||||
#define MINIZ_NO_TIME | |||||
#define MINIZ_NO_ZLIB_APIS | |||||
#define MINIZ_NO_MALLOC | |||||
#include "miniz.h" | |||||
// Now include stdio.h because this test uses fopen(), etc. (but we still don't want miniz.c's stdio stuff, for testing). | |||||
#include <stdio.h> | |||||
#include <limits.h> | |||||
typedef unsigned char uint8; | |||||
typedef unsigned short uint16; | |||||
typedef unsigned int uint; | |||||
#define my_max(a,b) (((a) > (b)) ? (a) : (b)) | |||||
#define my_min(a,b) (((a) < (b)) ? (a) : (b)) | |||||
// IN_BUF_SIZE is the size of the file read buffer. | |||||
// IN_BUF_SIZE must be >= 1 | |||||
#define IN_BUF_SIZE (1024*512) | |||||
static uint8 s_inbuf[IN_BUF_SIZE]; | |||||
// COMP_OUT_BUF_SIZE is the size of the output buffer used during compression. | |||||
// COMP_OUT_BUF_SIZE must be >= 1 and <= OUT_BUF_SIZE | |||||
#define COMP_OUT_BUF_SIZE (1024*512) | |||||
// OUT_BUF_SIZE is the size of the output buffer used during decompression. | |||||
// OUT_BUF_SIZE must be a power of 2 >= TINFL_LZ_DICT_SIZE (because the low-level decompressor not only writes, but reads from the output buffer as it decompresses) | |||||
//#define OUT_BUF_SIZE (TINFL_LZ_DICT_SIZE) | |||||
#define OUT_BUF_SIZE (1024*512) | |||||
static uint8 s_outbuf[OUT_BUF_SIZE]; | |||||
// tdefl_compressor contains all the state needed by the low-level compressor so it's a pretty big struct (~300k). | |||||
// This example makes it a global vs. putting it on the stack, of course in real-world usage you'll probably malloc() or new it. | |||||
tdefl_compressor g_deflator; | |||||
int main(int argc, char *argv[]) | |||||
{ | |||||
const char *pMode; | |||||
FILE *pInfile, *pOutfile; | |||||
uint infile_size; | |||||
int level = 9; | |||||
int p = 1; | |||||
const char *pSrc_filename; | |||||
const char *pDst_filename; | |||||
const void *next_in = s_inbuf; | |||||
size_t avail_in = 0; | |||||
void *next_out = s_outbuf; | |||||
size_t avail_out = OUT_BUF_SIZE; | |||||
size_t total_in = 0, total_out = 0; | |||||
long file_loc; | |||||
assert(COMP_OUT_BUF_SIZE <= OUT_BUF_SIZE); | |||||
printf("miniz.c example5 (demonstrates tinfl/tdefl)\n"); | |||||
if (argc < 4) | |||||
{ | |||||
printf("File to file compression/decompression using the low-level tinfl/tdefl API's.\n"); | |||||
printf("Usage: example5 [options] [mode:c or d] infile outfile\n"); | |||||
printf("\nModes:\n"); | |||||
printf("c - Compresses file infile to a zlib stream in file outfile\n"); | |||||
printf("d - Decompress zlib stream in file infile to file outfile\n"); | |||||
printf("\nOptions:\n"); | |||||
printf("-l[0-10] - Compression level, higher values are slower, 0 is none.\n"); | |||||
return EXIT_FAILURE; | |||||
} | |||||
while ((p < argc) && (argv[p][0] == '-')) | |||||
{ | |||||
switch (argv[p][1]) | |||||
{ | |||||
case 'l': | |||||
{ | |||||
level = atoi(&argv[1][2]); | |||||
if ((level < 0) || (level > 10)) | |||||
{ | |||||
printf("Invalid level!\n"); | |||||
return EXIT_FAILURE; | |||||
} | |||||
break; | |||||
} | |||||
default: | |||||
{ | |||||
printf("Invalid option: %s\n", argv[p]); | |||||
return EXIT_FAILURE; | |||||
} | |||||
} | |||||
p++; | |||||
} | |||||
if ((argc - p) < 3) | |||||
{ | |||||
printf("Must specify mode, input filename, and output filename after options!\n"); | |||||
return EXIT_FAILURE; | |||||
} | |||||
else if ((argc - p) > 3) | |||||
{ | |||||
printf("Too many filenames!\n"); | |||||
return EXIT_FAILURE; | |||||
} | |||||
pMode = argv[p++]; | |||||
if (!strchr("cCdD", pMode[0])) | |||||
{ | |||||
printf("Invalid mode!\n"); | |||||
return EXIT_FAILURE; | |||||
} | |||||
pSrc_filename = argv[p++]; | |||||
pDst_filename = argv[p++]; | |||||
printf("Mode: %c, Level: %u\nInput File: \"%s\"\nOutput File: \"%s\"\n", pMode[0], level, pSrc_filename, pDst_filename); | |||||
// Open input file. | |||||
pInfile = fopen(pSrc_filename, "rb"); | |||||
if (!pInfile) | |||||
{ | |||||
printf("Failed opening input file!\n"); | |||||
return EXIT_FAILURE; | |||||
} | |||||
// Determine input file's size. | |||||
fseek(pInfile, 0, SEEK_END); | |||||
file_loc = ftell(pInfile); | |||||
fseek(pInfile, 0, SEEK_SET); | |||||
if ((file_loc < 0) || (file_loc > INT_MAX)) | |||||
{ | |||||
// This is not a limitation of miniz or tinfl, but this example. | |||||
printf("File is too large to be processed by this example.\n"); | |||||
return EXIT_FAILURE; | |||||
} | |||||
infile_size = (uint)file_loc; | |||||
// Open output file. | |||||
pOutfile = fopen(pDst_filename, "wb"); | |||||
if (!pOutfile) | |||||
{ | |||||
printf("Failed opening output file!\n"); | |||||
return EXIT_FAILURE; | |||||
} | |||||
printf("Input file size: %u\n", infile_size); | |||||
if ((pMode[0] == 'c') || (pMode[0] == 'C')) | |||||
{ | |||||
// The number of dictionary probes to use at each compression level (0-10). 0=implies fastest/minimal possible probing. | |||||
static const mz_uint s_tdefl_num_probes[11] = { 0, 1, 6, 32, 16, 32, 128, 256, 512, 768, 1500 }; | |||||
tdefl_status status; | |||||
uint infile_remaining = infile_size; | |||||
// create tdefl() compatible flags (we have to compose the low-level flags ourselves, or use tdefl_create_comp_flags_from_zip_params() but that means MINIZ_NO_ZLIB_APIS can't be defined). | |||||
mz_uint comp_flags = TDEFL_WRITE_ZLIB_HEADER | s_tdefl_num_probes[MZ_MIN(10, level)] | ((level <= 3) ? TDEFL_GREEDY_PARSING_FLAG : 0); | |||||
if (!level) | |||||
comp_flags |= TDEFL_FORCE_ALL_RAW_BLOCKS; | |||||
// Initialize the low-level compressor. | |||||
status = tdefl_init(&g_deflator, NULL, NULL, comp_flags); | |||||
if (status != TDEFL_STATUS_OKAY) | |||||
{ | |||||
printf("tdefl_init() failed!\n"); | |||||
return EXIT_FAILURE; | |||||
} | |||||
avail_out = COMP_OUT_BUF_SIZE; | |||||
// Compression. | |||||
for ( ; ; ) | |||||
{ | |||||
size_t in_bytes, out_bytes; | |||||
if (!avail_in) | |||||
{ | |||||
// Input buffer is empty, so read more bytes from input file. | |||||
uint n = my_min(IN_BUF_SIZE, infile_remaining); | |||||
if (fread(s_inbuf, 1, n, pInfile) != n) | |||||
{ | |||||
printf("Failed reading from input file!\n"); | |||||
return EXIT_FAILURE; | |||||
} | |||||
next_in = s_inbuf; | |||||
avail_in = n; | |||||
infile_remaining -= n; | |||||
//printf("Input bytes remaining: %u\n", infile_remaining); | |||||
} | |||||
in_bytes = avail_in; | |||||
out_bytes = avail_out; | |||||
// Compress as much of the input as possible (or all of it) to the output buffer. | |||||
status = tdefl_compress(&g_deflator, next_in, &in_bytes, next_out, &out_bytes, infile_remaining ? TDEFL_NO_FLUSH : TDEFL_FINISH); | |||||
next_in = (const char *)next_in + in_bytes; | |||||
avail_in -= in_bytes; | |||||
total_in += in_bytes; | |||||
next_out = (char *)next_out + out_bytes; | |||||
avail_out -= out_bytes; | |||||
total_out += out_bytes; | |||||
if ((status != TDEFL_STATUS_OKAY) || (!avail_out)) | |||||
{ | |||||
// Output buffer is full, or compression is done or failed, so write buffer to output file. | |||||
uint n = COMP_OUT_BUF_SIZE - (uint)avail_out; | |||||
if (fwrite(s_outbuf, 1, n, pOutfile) != n) | |||||
{ | |||||
printf("Failed writing to output file!\n"); | |||||
return EXIT_FAILURE; | |||||
} | |||||
next_out = s_outbuf; | |||||
avail_out = COMP_OUT_BUF_SIZE; | |||||
} | |||||
if (status == TDEFL_STATUS_DONE) | |||||
{ | |||||
// Compression completed successfully. | |||||
break; | |||||
} | |||||
else if (status != TDEFL_STATUS_OKAY) | |||||
{ | |||||
// Compression somehow failed. | |||||
printf("tdefl_compress() failed with status %i!\n", status); | |||||
return EXIT_FAILURE; | |||||
} | |||||
} | |||||
} | |||||
else if ((pMode[0] == 'd') || (pMode[0] == 'D')) | |||||
{ | |||||
// Decompression. | |||||
uint infile_remaining = infile_size; | |||||
tinfl_decompressor inflator; | |||||
tinfl_init(&inflator); | |||||
for ( ; ; ) | |||||
{ | |||||
size_t in_bytes, out_bytes; | |||||
tinfl_status status; | |||||
if (!avail_in) | |||||
{ | |||||
// Input buffer is empty, so read more bytes from input file. | |||||
uint n = my_min(IN_BUF_SIZE, infile_remaining); | |||||
if (fread(s_inbuf, 1, n, pInfile) != n) | |||||
{ | |||||
printf("Failed reading from input file!\n"); | |||||
return EXIT_FAILURE; | |||||
} | |||||
next_in = s_inbuf; | |||||
avail_in = n; | |||||
infile_remaining -= n; | |||||
} | |||||
in_bytes = avail_in; | |||||
out_bytes = avail_out; | |||||
status = tinfl_decompress(&inflator, (const mz_uint8 *)next_in, &in_bytes, s_outbuf, (mz_uint8 *)next_out, &out_bytes, (infile_remaining ? TINFL_FLAG_HAS_MORE_INPUT : 0) | TINFL_FLAG_PARSE_ZLIB_HEADER); | |||||
avail_in -= in_bytes; | |||||
next_in = (const mz_uint8 *)next_in + in_bytes; | |||||
total_in += in_bytes; | |||||
avail_out -= out_bytes; | |||||
next_out = (mz_uint8 *)next_out + out_bytes; | |||||
total_out += out_bytes; | |||||
if ((status <= TINFL_STATUS_DONE) || (!avail_out)) | |||||
{ | |||||
// Output buffer is full, or decompression is done, so write buffer to output file. | |||||
uint n = OUT_BUF_SIZE - (uint)avail_out; | |||||
if (fwrite(s_outbuf, 1, n, pOutfile) != n) | |||||
{ | |||||
printf("Failed writing to output file!\n"); | |||||
return EXIT_FAILURE; | |||||
} | |||||
next_out = s_outbuf; | |||||
avail_out = OUT_BUF_SIZE; | |||||
} | |||||
// If status is <= TINFL_STATUS_DONE then either decompression is done or something went wrong. | |||||
if (status <= TINFL_STATUS_DONE) | |||||
{ | |||||
if (status == TINFL_STATUS_DONE) | |||||
{ | |||||
// Decompression completed successfully. | |||||
break; | |||||
} | |||||
else | |||||
{ | |||||
// Decompression failed. | |||||
printf("tinfl_decompress() failed with status %i!\n", status); | |||||
return EXIT_FAILURE; | |||||
} | |||||
} | |||||
} | |||||
} | |||||
else | |||||
{ | |||||
printf("Invalid mode!\n"); | |||||
return EXIT_FAILURE; | |||||
} | |||||
fclose(pInfile); | |||||
if (EOF == fclose(pOutfile)) | |||||
{ | |||||
printf("Failed writing to output file!\n"); | |||||
return EXIT_FAILURE; | |||||
} | |||||
printf("Total input bytes: %u\n", (mz_uint32)total_in); | |||||
printf("Total output bytes: %u\n", (mz_uint32)total_out); | |||||
printf("Success.\n"); | |||||
return EXIT_SUCCESS; | |||||
} |
@ -0,0 +1,162 @@ | |||||
// example6.c - Demonstrates how to miniz's PNG writer func | |||||
// Public domain, April 11 2012, Rich Geldreich, richgel99@gmail.com. See "unlicense" statement at the end of tinfl.c. | |||||
// Mandlebrot set code from http://rosettacode.org/wiki/Mandelbrot_set#C | |||||
// Must link this example against libm on Linux. | |||||
// Purposely disable a whole bunch of stuff this low-level example doesn't use. | |||||
#define MINIZ_NO_STDIO | |||||
#define MINIZ_NO_TIME | |||||
#define MINIZ_NO_ZLIB_APIS | |||||
#include "miniz.h" | |||||
// Now include stdio.h because this test uses fopen(), etc. (but we still don't want miniz.c's stdio stuff, for testing). | |||||
#include <stdio.h> | |||||
#include <limits.h> | |||||
#include <math.h> | |||||
typedef unsigned char uint8; | |||||
typedef unsigned short uint16; | |||||
typedef unsigned int uint; | |||||
typedef struct | |||||
{ | |||||
uint8 r, g, b; | |||||
} rgb_t; | |||||
static void hsv_to_rgb(int hue, int min, int max, rgb_t *p) | |||||
{ | |||||
const int invert = 0; | |||||
const int saturation = 1; | |||||
const int color_rotate = 0; | |||||
if (min == max) max = min + 1; | |||||
if (invert) hue = max - (hue - min); | |||||
if (!saturation) { | |||||
p->r = p->g = p->b = 255 * (max - hue) / (max - min); | |||||
return; | |||||
} | |||||
double h = fmod(color_rotate + 1e-4 + 4.0 * (hue - min) / (max - min), 6); | |||||
double c = 255.0f * saturation; | |||||
double X = c * (1 - fabs(fmod(h, 2) - 1)); | |||||
p->r = p->g = p->b = 0; | |||||
switch((int)h) { | |||||
case 0: p->r = c; p->g = X; return; | |||||
case 1: p->r = X; p->g = c; return; | |||||
case 2: p->g = c; p->b = X; return; | |||||
case 3: p->g = X; p->b = c; return; | |||||
case 4: p->r = X; p->b = c; return; | |||||
default:p->r = c; p->b = X; | |||||
} | |||||
} | |||||
int main(int argc, char *argv[]) | |||||
{ | |||||
(void)argc, (void)argv; | |||||
// Image resolution | |||||
const int iXmax = 4096; | |||||
const int iYmax = 4096; | |||||
// Output filename | |||||
static const char *pFilename = "mandelbrot.png"; | |||||
int iX, iY; | |||||
const double CxMin = -2.5; | |||||
const double CxMax = 1.5; | |||||
const double CyMin = -2.0; | |||||
const double CyMax = 2.0; | |||||
double PixelWidth = (CxMax - CxMin) / iXmax; | |||||
double PixelHeight = (CyMax - CyMin) / iYmax; | |||||
// Z=Zx+Zy*i ; Z0 = 0 | |||||
double Zx, Zy; | |||||
double Zx2, Zy2; // Zx2=Zx*Zx; Zy2=Zy*Zy | |||||
int Iteration; | |||||
const int IterationMax = 200; | |||||
// bail-out value , radius of circle | |||||
const double EscapeRadius = 2; | |||||
double ER2=EscapeRadius * EscapeRadius; | |||||
uint8 *pImage = (uint8 *)malloc(iXmax * 3 * iYmax); | |||||
// world ( double) coordinate = parameter plane | |||||
double Cx,Cy; | |||||
int MinIter = 9999, MaxIter = 0; | |||||
for(iY = 0; iY < iYmax; iY++) | |||||
{ | |||||
Cy = CyMin + iY * PixelHeight; | |||||
if (fabs(Cy) < PixelHeight/2) | |||||
Cy = 0.0; // Main antenna | |||||
for(iX = 0; iX < iXmax; iX++) | |||||
{ | |||||
uint8 *color = pImage + (iX * 3) + (iY * iXmax * 3); | |||||
Cx = CxMin + iX * PixelWidth; | |||||
// initial value of orbit = critical point Z= 0 | |||||
Zx = 0.0; | |||||
Zy = 0.0; | |||||
Zx2 = Zx * Zx; | |||||
Zy2 = Zy * Zy; | |||||
for (Iteration=0;Iteration<IterationMax && ((Zx2+Zy2)<ER2);Iteration++) | |||||
{ | |||||
Zy = 2 * Zx * Zy + Cy; | |||||
Zx =Zx2 - Zy2 + Cx; | |||||
Zx2 = Zx * Zx; | |||||
Zy2 = Zy * Zy; | |||||
}; | |||||
color[0] = (uint8)Iteration; | |||||
color[1] = (uint8)Iteration >> 8; | |||||
color[2] = 0; | |||||
if (Iteration < MinIter) | |||||
MinIter = Iteration; | |||||
if (Iteration > MaxIter) | |||||
MaxIter = Iteration; | |||||
} | |||||
} | |||||
for(iY = 0; iY < iYmax; iY++) | |||||
{ | |||||
for(iX = 0; iX < iXmax; iX++) | |||||
{ | |||||
uint8 *color = (uint8 *)(pImage + (iX * 3) + (iY * iXmax * 3)); | |||||
uint Iterations = color[0] | (color[1] << 8U); | |||||
hsv_to_rgb(Iterations, MinIter, MaxIter, (rgb_t *)color); | |||||
} | |||||
} | |||||
// Now write the PNG image. | |||||
{ | |||||
size_t png_data_size = 0; | |||||
void *pPNG_data = tdefl_write_image_to_png_file_in_memory_ex(pImage, iXmax, iYmax, 3, &png_data_size, 6, MZ_FALSE); | |||||
if (!pPNG_data) | |||||
fprintf(stderr, "tdefl_write_image_to_png_file_in_memory_ex() failed!\n"); | |||||
else | |||||
{ | |||||
FILE *pFile = fopen(pFilename, "wb"); | |||||
fwrite(pPNG_data, 1, png_data_size, pFile); | |||||
fclose(pFile); | |||||
printf("Wrote %s\n", pFilename); | |||||
} | |||||
// mz_free() is by default just an alias to free() internally, but if you've overridden miniz's allocation funcs you'll probably need to call mz_free(). | |||||
mz_free(pPNG_data); | |||||
} | |||||
free(pImage); | |||||
return EXIT_SUCCESS; | |||||
} |
@ -0,0 +1,34 @@ | |||||
## Miniz | |||||
Miniz is a lossless, high performance data compression library in a single source file that implements the zlib (RFC 1950) and Deflate (RFC 1951) compressed data format specification standards. It supports the most commonly used functions exported by the zlib library, but is a completely independent implementation so zlib's licensing requirements do not apply. Miniz also contains simple to use functions for writing .PNG format image files and reading/writing/appending .ZIP format archives. Miniz's compression speed has been tuned to be comparable to zlib's, and it also has a specialized real-time compressor function designed to compare well against fastlz/minilzo. | |||||
## Usage | |||||
Please use the files from the [releases page](https://github.com/richgel999/miniz/releases) in your projects. Do not use the git checkout directly! The different source and header files are [amalgamated](https://www.sqlite.org/amalgamation.html) into one `miniz.c`/`miniz.h` pair in a build step (`amalgamate.sh`). Include `miniz.c` and `miniz.h` in your project to use Miniz. | |||||
## Features | |||||
* MIT licensed | |||||
* A portable, single source and header file library written in plain C. Tested with GCC, clang and Visual Studio. | |||||
* Easily tuned and trimmed down by defines | |||||
* A drop-in replacement for zlib's most used API's (tested in several open source projects that use zlib, such as libpng and libzip). | |||||
* Fills a single threaded performance vs. compression ratio gap between several popular real-time compressors and zlib. For example, at level 1, miniz.c compresses around 5-9% better than minilzo, but is approx. 35% slower. At levels 2-9, miniz.c is designed to compare favorably against zlib's ratio and speed. See the miniz performance comparison page for example timings. | |||||
* Not a block based compressor: miniz.c fully supports stream based processing using a coroutine-style implementation. The zlib-style API functions can be called a single byte at a time if that's all you've got. | |||||
* Easy to use. The low-level compressor (tdefl) and decompressor (tinfl) have simple state structs which can be saved/restored as needed with simple memcpy's. The low-level codec API's don't use the heap in any way. | |||||
* Entire inflater (including optional zlib header parsing and Adler-32 checking) is implemented in a single function as a coroutine, which is separately available in a small (~550 line) source file: miniz_tinfl.c | |||||
* A fairly complete (but totally optional) set of .ZIP archive manipulation and extraction API's. The archive functionality is intended to solve common problems encountered in embedded, mobile, or game development situations. (The archive API's are purposely just powerful enough to write an entire archiver given a bit of additional higher-level logic.) | |||||
## Known Problems | |||||
* No support for encrypted archives. Not sure how useful this stuff is in practice. | |||||
* Minimal documentation. The assumption is that the user is already familiar with the basic zlib API. I need to write an API wiki - for now I've tried to place key comments before each enum/API, and I've included 6 examples that demonstrate how to use the module's major features. | |||||
## Special Thanks | |||||
Thanks to Alex Evans for the PNG writer function. Also, thanks to Paul Holden and Thorsten Scheuermann for feedback and testing, Matt Pritchard for all his encouragement, and Sean Barrett's various public domain libraries for inspiration (and encouraging me to write miniz.c in C, which was much more enjoyable and less painful than I thought it would be considering I've been programming in C++ for so long). | |||||
Thanks to Bruce Dawson for reporting a problem with the level_and_flags archive API parameter (which is fixed in v1.12) and general feedback, and Janez Zemva for indirectly encouraging me into writing more examples. | |||||
## Patents | |||||
I was recently asked if miniz avoids patent issues. miniz purposely uses the same core algorithms as the ones used by zlib. The compressor uses vanilla hash chaining as described [here](https://datatracker.ietf.org/doc/html/rfc1951#section-4). Also see the [gzip FAQ](https://web.archive.org/web/20160308045258/http://www.gzip.org/#faq11). In my opinion, if miniz falls prey to a patent attack then zlib/gzip are likely to be at serious risk too. |
@ -0,0 +1,28 @@ | |||||
INC_DIR = -I../../ -I../common -I ../../deps/miniz | |||||
INCLUDES := ../../tinyexr.h | |||||
OBJS := miniz.o tinyexr.o cube2longlat.o | |||||
CFLAGS := -fsanitize=address -O2 | |||||
CXXFLAGS := $(CFLAGS) -std=c++11 | |||||
LDFLAGS := -fsanitize=address | |||||
TARGET=cube2longlat | |||||
all: $(TARGET) | |||||
$(TARGET): $(OBJS) | |||||
$(CXX) -o $(TARGET) $(LDFLAGS) $(OBJS) | |||||
miniz.o: ../../deps/miniz/miniz.c | |||||
$(CC) $(CFLAGS) -c $(INC_DIR) $< | |||||
tinyexr.o: ../../tinyexr.cc | |||||
$(CXX) $(CXXFLAGS) -c $(INC_DIR) $< | |||||
cube2longlat.o: cube2longlat.cc | |||||
$(CXX) $(CXXFLAGS) -c $(INC_DIR) $< | |||||
.PHONY: clean | |||||
clean: | |||||
rm -rf $(TARGET) $(OBJS) |
@ -0,0 +1,46 @@ | |||||
# Simple HDR cubemap to longlat(longitude latitude. or known as equirectangular) converter. | |||||
## Requirements | |||||
* C++11 compiler | |||||
## Coordinate definition. | |||||
* Y-up | |||||
* Right-handed | |||||
* Center is -z | |||||
## Usage | |||||
Assume cubemap image is given by 6 images(6 faces). | |||||
``` | |||||
$ ./cube2longlat px.exr nx.exr py.exr ny.exr pz.exr nz.exr 512 longlat.exr (phi_offset) | |||||
``` | |||||
Optional `phi_offset` is used to add offset(by angle) to phi to rotate X and Z faces. | |||||
## Supported input image format | |||||
* [ ] RGBM(Filament's RGBM encoding. Multiplier is 16, and gamma corrected) Implemented but not tested. | |||||
* [x] EXR | |||||
## Supported output image format | |||||
* [ ] RGBM(Filament's RGBM encoding. Multiplier is 16, and gamma corrected) Implemented but not tested. | |||||
* [x] EXR | |||||
* [x] RGBE | |||||
## Note | |||||
When you create cubemap using Filament's cmgen https://github.com/google/filament/tree/master/tools/cmgen , its generated cubemap images are mirrored by X direction. | |||||
Use `--mirror` when invoking `cmgen` if required. | |||||
## TODO | |||||
* Single cubemap image(cross layout) | |||||
* Better antialiasing | |||||
* theta offset | |||||
* Mirroring. | |||||
@ -0,0 +1,470 @@ | |||||
#include "tinyexr.h" | |||||
#define STB_IMAGE_IMPLEMENTATION | |||||
#include "stb_image.h" | |||||
#define STB_IMAGE_WRITE_IMPLEMENTATION | |||||
#include "stb_image_write.h" | |||||
#include <array> | |||||
#include <cmath> | |||||
#include <iostream> | |||||
#include <string> | |||||
#include <vector> | |||||
// From Filament. | |||||
static inline void RGBMtoLinear(const float rgbm[4], float linear[3]) { | |||||
linear[0] = rgbm[0] * rgbm[3] * 16.0f; | |||||
linear[1] = rgbm[1] * rgbm[3] * 16.0f; | |||||
linear[2] = rgbm[2] * rgbm[3] * 16.0f; | |||||
// Gamma to linear space | |||||
linear[0] = linear[0] * linear[0]; | |||||
linear[1] = linear[1] * linear[1]; | |||||
linear[2] = linear[2] * linear[2]; | |||||
} | |||||
static inline void LinearToRGBM(const float linear[3], float rgbm[4]) { | |||||
rgbm[0] = linear[0]; | |||||
rgbm[1] = linear[1]; | |||||
rgbm[2] = linear[2]; | |||||
rgbm[3] = 1.0f; | |||||
// Linear to gamma space | |||||
rgbm[0] = rgbm[0] * rgbm[0]; | |||||
rgbm[1] = rgbm[1] * rgbm[1]; | |||||
rgbm[2] = rgbm[2] * rgbm[2]; | |||||
// Set the range | |||||
rgbm[0] /= 16.0f; | |||||
rgbm[1] /= 16.0f; | |||||
rgbm[2] /= 16.0f; | |||||
float maxComponent = | |||||
std::max(std::max(rgbm[0], rgbm[1]), std::max(rgbm[2], 1e-6f)); | |||||
// Don't let M go below 1 in the [0..16] range | |||||
rgbm[3] = std::max(1.0f / 16.0f, std::min(maxComponent, 1.0f)); | |||||
rgbm[3] = std::ceil(rgbm[3] * 255.0f) / 255.0f; | |||||
// saturate([0.0, 1.0]) | |||||
rgbm[0] = std::max(0.0f, std::min(1.0f, rgbm[0] / rgbm[3])); | |||||
rgbm[1] = std::max(0.0f, std::min(1.0f, rgbm[1] / rgbm[3])); | |||||
rgbm[2] = std::max(0.0f, std::min(1.0f, rgbm[2] / rgbm[3])); | |||||
} | |||||
static std::string GetFileExtension(const std::string& filename) { | |||||
if (filename.find_last_of(".") != std::string::npos) | |||||
return filename.substr(filename.find_last_of(".") + 1); | |||||
return ""; | |||||
} | |||||
struct Image { | |||||
int width; | |||||
int height; | |||||
std::vector<float> data; | |||||
}; | |||||
static bool LoadCubemaps(const std::array<std::string, 6> face_filenames, | |||||
std::array<Image, 6>* output) { | |||||
for (size_t i = 0; i < 6; i++) { | |||||
std::string ext = GetFileExtension(face_filenames[i]); | |||||
Image image; | |||||
if ((ext.compare("exr") == 0) || (ext.compare("EXR") == 0)) { | |||||
int width, height; | |||||
float* rgba; | |||||
const char* err; | |||||
int ret = | |||||
LoadEXR(&rgba, &width, &height, face_filenames[i].c_str(), &err); | |||||
if (ret != 0) { | |||||
if (err) { | |||||
std::cerr << "EXR load error: " << err << std::endl; | |||||
} else { | |||||
std::cerr << "EXR load error: code " << ret << std::endl; | |||||
} | |||||
return false; | |||||
} | |||||
image.width = width; | |||||
image.height = height; | |||||
image.data.resize(width * height * 3); | |||||
// RGBA -> RGB | |||||
for (size_t j = 0; j < size_t(width * height); j++) { | |||||
image.data[3 * j + 0] = rgba[4 * j + 0]; | |||||
image.data[3 * j + 1] = rgba[4 * j + 1]; | |||||
image.data[3 * j + 2] = rgba[4 * j + 2]; | |||||
} | |||||
free(rgba); | |||||
(*output)[i] = std::move(image); | |||||
} else if ((ext.compare("rgbm") == 0) || (ext.compare("RGBM") == 0)) { | |||||
int width, height; | |||||
int n; | |||||
unsigned char* data = stbi_load(face_filenames[i].c_str(), &width, | |||||
&height, &n, STBI_default); | |||||
if (!data) { | |||||
std::cerr << "Failed to load file: " << face_filenames[i] << std::endl; | |||||
return false; | |||||
} | |||||
if ((n != 4)) { | |||||
std::cerr << "Not a RGBM encoded image: " << face_filenames[i] | |||||
<< std::endl; | |||||
return false; | |||||
} | |||||
image.width = width; | |||||
image.height = height; | |||||
image.data.resize(size_t(width * height)); | |||||
for (size_t i = 0; i < size_t(width * height); i++) { | |||||
float rgbm[4]; | |||||
// [0, 1.0] | |||||
rgbm[0] = data[4 * i + 0] / 255.0f; | |||||
rgbm[1] = data[4 * i + 1] / 255.0f; | |||||
rgbm[2] = data[4 * i + 2] / 255.0f; | |||||
rgbm[3] = data[4 * i + 3] / 255.0f; | |||||
float linear[3]; | |||||
RGBMtoLinear(rgbm, linear); | |||||
image.data[3 * i + 0] = linear[0]; | |||||
image.data[3 * i + 1] = linear[1]; | |||||
image.data[3 * i + 2] = linear[2]; | |||||
} | |||||
(*output)[i] = std::move(image); | |||||
} else { | |||||
std::cerr << "Unknown file extension : " << ext << std::endl; | |||||
return false; | |||||
} | |||||
std::cout << "Loaded " << face_filenames[i] << std::endl; | |||||
} | |||||
return true; | |||||
} | |||||
void convert_xyz_to_cube_uv(float x, float y, float z, int* index, float* u, | |||||
float* v) { | |||||
float absX = fabs(x); | |||||
float absY = fabs(y); | |||||
float absZ = fabs(z); | |||||
int isXPositive = x > 0.0f ? 1 : 0; | |||||
int isYPositive = y > 0.0f ? 1 : 0; | |||||
int isZPositive = z > 0.0f ? 1 : 0; | |||||
float maxAxis, uc, vc; | |||||
// POSITIVE X | |||||
if (isXPositive && absX >= absY && absX >= absZ) { | |||||
// u (0 to 1) goes from +z to -z | |||||
// v (0 to 1) goes from -y to +y | |||||
maxAxis = absX; | |||||
uc = -z; | |||||
vc = y; | |||||
*index = 0; | |||||
} | |||||
// NEGATIVE X | |||||
if (!isXPositive && absX >= absY && absX >= absZ) { | |||||
// u (0 to 1) goes from -z to +z | |||||
// v (0 to 1) goes from -y to +y | |||||
maxAxis = absX; | |||||
uc = z; | |||||
vc = y; | |||||
*index = 1; | |||||
} | |||||
// POSITIVE Y | |||||
if (isYPositive && absY >= absX && absY >= absZ) { | |||||
// u (0 to 1) goes from -x to +x | |||||
// v (0 to 1) goes from +z to -z | |||||
maxAxis = absY; | |||||
uc = x; | |||||
vc = -z; | |||||
*index = 2; | |||||
} | |||||
// NEGATIVE Y | |||||
if (!isYPositive && absY >= absX && absY >= absZ) { | |||||
// u (0 to 1) goes from -x to +x | |||||
// v (0 to 1) goes from -z to +z | |||||
maxAxis = absY; | |||||
uc = x; | |||||
vc = z; | |||||
*index = 3; | |||||
} | |||||
// POSITIVE Z | |||||
if (isZPositive && (absZ >= absX) && (absZ >= absY)) { | |||||
// u (0 to 1) goes from -x to +x | |||||
// v (0 to 1) goes from -y to +y | |||||
maxAxis = absZ; | |||||
uc = x; | |||||
vc = y; | |||||
*index = 4; | |||||
} | |||||
// NEGATIVE Z | |||||
if (!isZPositive && (absZ >= absX) && (absZ >= absY)) { | |||||
// u (0 to 1) goes from +x to -x | |||||
// v (0 to 1) goes from -y to +y | |||||
maxAxis = absZ; | |||||
uc = -x; | |||||
vc = y; | |||||
*index = 5; | |||||
} | |||||
// Convert range from -1 to 1 to 0 to 1 | |||||
*u = 0.5f * (uc / maxAxis + 1.0f); | |||||
*v = 0.5f * (vc / maxAxis + 1.0f); | |||||
} | |||||
// | |||||
// Simple bilinear texture filtering. | |||||
// | |||||
static void SampleTexture(float* rgba, float u, float v, int width, int height, | |||||
int channels, const float* texels) { | |||||
float sx = std::floor(u); | |||||
float sy = std::floor(v); | |||||
// Wrap mode = repeat | |||||
float uu = u - sx; | |||||
float vv = v - sy; | |||||
// clamp | |||||
uu = std::max(uu, 0.0f); | |||||
uu = std::min(uu, 1.0f); | |||||
vv = std::max(vv, 0.0f); | |||||
vv = std::min(vv, 1.0f); | |||||
float px = (width - 1) * uu; | |||||
float py = (height - 1) * vv; | |||||
int x0 = std::max(0, std::min((int)px, (width - 1))); | |||||
int y0 = std::max(0, std::min((int)py, (height - 1))); | |||||
int x1 = std::max(0, std::min((x0 + 1), (width - 1))); | |||||
int y1 = std::max(0, std::min((y0 + 1), (height - 1))); | |||||
float dx = px - (float)x0; | |||||
float dy = py - (float)y0; | |||||
float w[4]; | |||||
w[0] = (1.0f - dx) * (1.0 - dy); | |||||
w[1] = (1.0f - dx) * (dy); | |||||
w[2] = (dx) * (1.0 - dy); | |||||
w[3] = (dx) * (dy); | |||||
int i00 = channels * (y0 * width + x0); | |||||
int i01 = channels * (y0 * width + x1); | |||||
int i10 = channels * (y1 * width + x0); | |||||
int i11 = channels * (y1 * width + x1); | |||||
for (int i = 0; i < channels; i++) { | |||||
rgba[i] = w[0] * texels[i00 + i] + w[1] * texels[i10 + i] + | |||||
w[2] * texels[i01 + i] + w[3] * texels[i11 + i]; | |||||
} | |||||
} | |||||
static void SampleCubemap(const std::array<Image, 6>& cubemap_faces, | |||||
const float n[3], float col[3]) { | |||||
int face; | |||||
float u, v; | |||||
convert_xyz_to_cube_uv(n[0], n[1], n[2], &face, &u, &v); | |||||
v = 1.0f - v; | |||||
// std::cout << "face = " << face << std::endl; | |||||
// TODO(syoyo): Do we better consider seams on the cubemap face border? | |||||
const Image& tex = cubemap_faces[face]; | |||||
// std::cout << "n = " << n[0] << ", " << n[1] << ", " << n[2] << ", uv = " << | |||||
// u << ", " << v << std::endl; | |||||
SampleTexture(col, u, v, tex.width, tex.height, /* RGB */ 3, tex.data.data()); | |||||
// col[0] = u; | |||||
// col[1] = v; | |||||
// col[2] = 0.0f; | |||||
#if 0 | |||||
if (face == 0) { | |||||
col[0] = 1.0f; | |||||
col[1] = 0.0f; | |||||
col[2] = 0.0f; | |||||
} else if (face == 1) { | |||||
col[0] = 0.0f; | |||||
col[1] = 1.0f; | |||||
col[2] = 0.0f; | |||||
} else if (face == 2) { | |||||
col[0] = 0.0f; | |||||
col[1] = 0.0f; | |||||
col[2] = 1.0f; | |||||
} else if (face == 3) { | |||||
col[0] = 1.0f; | |||||
col[1] = 0.0f; | |||||
col[2] = 1.0f; | |||||
} else if (face == 4) { | |||||
col[0] = 0.0f; | |||||
col[1] = 1.0f; | |||||
col[2] = 1.0f; | |||||
} else if (face == 5) { | |||||
col[0] = 1.0f; | |||||
col[1] = 1.0f; | |||||
col[2] = 1.0f; | |||||
} | |||||
#endif | |||||
} | |||||
static void CubemapToLonglat(const std::array<Image, 6>& cubemap_faces, | |||||
const float phi_offset, /* in angle */ | |||||
const int width, Image* longlat) { | |||||
int height = width / 2; | |||||
longlat->width = width; | |||||
longlat->height = height; | |||||
longlat->data.resize(size_t(width * height * 3)); // RGB | |||||
const float kPI = 3.141592f; | |||||
for (size_t y = 0; y < size_t(height); y++) { | |||||
float theta = ((y + 0.5f) / float(height)) * kPI; // [0, pi] | |||||
for (size_t x = 0; x < size_t(width); x++) { | |||||
float phi = ((x + 0.5f) / float(width)) * 2.0f * kPI; // [0, 2 pi] | |||||
phi += (phi_offset) * kPI / 180.0f; | |||||
float n[3]; | |||||
// Y-up | |||||
n[0] = std::sin(theta) * std::cos(phi); | |||||
n[1] = std::cos(theta); | |||||
n[2] = -std::sin(theta) * std::sin(phi); | |||||
float col[3]; | |||||
SampleCubemap(cubemap_faces, n, col); | |||||
longlat->data[3 * size_t(y * width + x) + 0] = col[0]; | |||||
longlat->data[3 * size_t(y * width + x) + 1] = col[1]; | |||||
longlat->data[3 * size_t(y * width + x) + 2] = col[2]; | |||||
} | |||||
} | |||||
} | |||||
static unsigned char ftouc(const float f) { | |||||
int i(f * 255.0f); | |||||
i = std::max(0, std::min(255, i)); | |||||
return static_cast<unsigned char>(i); | |||||
} | |||||
int main(int argc, char** argv) { | |||||
float phi_offset = 0.0f; | |||||
if (argc < 9) { | |||||
printf( | |||||
"Usage: cube2longlat px.exr nx.exr py.exr ny.exr pz.exr nz.exr " | |||||
"output_width output.exr\n"); | |||||
exit(-1); | |||||
} | |||||
std::array<std::string, 6> face_filenames; | |||||
face_filenames[0] = argv[1]; | |||||
face_filenames[1] = argv[2]; | |||||
face_filenames[2] = argv[3]; | |||||
face_filenames[3] = argv[4]; | |||||
face_filenames[4] = argv[5]; | |||||
face_filenames[5] = argv[6]; | |||||
int output_width = atoi(argv[7]); | |||||
std::string output_filename = argv[8]; | |||||
if (argc > 9) { | |||||
phi_offset = atof(argv[9]); | |||||
} | |||||
std::array<Image, 6> cubemaps; | |||||
if (!LoadCubemaps(face_filenames, &cubemaps)) { | |||||
std::cerr << "Failed to load cubemap faces." << std::endl; | |||||
return EXIT_FAILURE; | |||||
} | |||||
Image longlat; | |||||
CubemapToLonglat(cubemaps, phi_offset, output_width, &longlat); | |||||
{ | |||||
std::string ext = GetFileExtension(output_filename); | |||||
if ((ext.compare("exr") == 0) || (ext.compare("EXR") == 0)) { | |||||
const char *err; | |||||
int ret = SaveEXR(longlat.data.data(), longlat.width, longlat.height, | |||||
/* RGB */ 3, /* fp16 */ 0, output_filename.c_str(), &err); | |||||
if (ret != TINYEXR_SUCCESS) { | |||||
if (err) { | |||||
std::cout << "Failed to save image as EXR. msg = " << err << ", code = " << ret << std::endl; | |||||
FreeEXRErrorMessage(err); | |||||
} else { | |||||
std::cout << "Failed to save image as EXR. code = " << ret << std::endl; | |||||
} | |||||
return EXIT_FAILURE; | |||||
} | |||||
} else if ((ext.compare("rgbm") == 0) || (ext.compare("RGBM") == 0)) { | |||||
std::vector<unsigned char> rgbm_image; | |||||
for (size_t j = 0; j < size_t(longlat.width * longlat.height); j++) { | |||||
float linear[3]; | |||||
linear[0] = longlat.data[3 * j + 0]; | |||||
linear[1] = longlat.data[3 * j + 1]; | |||||
linear[2] = longlat.data[3 * j + 2]; | |||||
float rgbm[4]; | |||||
LinearToRGBM(linear, rgbm); | |||||
rgbm_image[4 * j + 0] = ftouc(rgbm[0]); | |||||
rgbm_image[4 * j + 1] = ftouc(rgbm[1]); | |||||
rgbm_image[4 * j + 2] = ftouc(rgbm[2]); | |||||
rgbm_image[4 * j + 3] = ftouc(rgbm[2]); | |||||
} | |||||
// Save as PNG. | |||||
int ret = | |||||
stbi_write_png(output_filename.c_str(), longlat.width, longlat.height, | |||||
4, rgbm_image.data(), longlat.width * 4); | |||||
if (ret == 0) { | |||||
std::cerr << "Failed to save image as RGBM file : " << output_filename | |||||
<< std::endl; | |||||
return EXIT_FAILURE; | |||||
} | |||||
} else { | |||||
if ((ext.compare("hdr") == 0) || (ext.compare("HDR") == 0)) { | |||||
// ok | |||||
} else { | |||||
std::cout << "Unknown file extension. Interpret it as RGBE format : " | |||||
<< ext << std::endl; | |||||
} | |||||
int ret = stbi_write_hdr(output_filename.c_str(), longlat.width, | |||||
longlat.height, 3, longlat.data.data()); | |||||
if (ret == 0) { | |||||
std::cerr << "Failed to save image as HDR file : " << output_filename | |||||
<< std::endl; | |||||
return EXIT_FAILURE; | |||||
} | |||||
} | |||||
} | |||||
std::cout << "Write " << output_filename << std::endl; | |||||
return 0; | |||||
} |
@ -0,0 +1,2 @@ | |||||
all: | |||||
g++ -I ../../deps/miniz -o deepview -g -O2 main.cc trackball.cc ../../tinyexr.cc ../../deps/miniz/miniz.c -framework OpenGL -framework GLUT |
@ -0,0 +1,277 @@ | |||||
#include <stdio.h> | |||||
#include <stdlib.h> | |||||
#include <assert.h> | |||||
#include <float.h> | |||||
#include <string.h> | |||||
#include <string.h> | |||||
#ifdef __APPLE__ | |||||
#include <GLUT/glut.h> | |||||
#else | |||||
#include <GL/glut.h> | |||||
#endif | |||||
#include "../../tinyexr.h" | |||||
#include "trackball.h" | |||||
static int mouse_x, mouse_y; | |||||
static int mouse_m_pressed; | |||||
static int mouse_r_pressed; | |||||
static int mouse_moving; | |||||
static int width = 512, height = 512; | |||||
static float view_org[3], view_tgt[3]; | |||||
static float curr_quat[4], prev_quat[4]; | |||||
static float color_scale = 1.0f; | |||||
DeepImage gDeepImage; | |||||
// | |||||
// -- | |||||
// | |||||
static void reshape(int w, int h) { | |||||
glViewport(0, 0, w, h); | |||||
glMatrixMode(GL_PROJECTION); | |||||
glLoadIdentity(); | |||||
gluPerspective(5.0, (float)w / (float)h, 0.1f, 1000.0f); | |||||
glMatrixMode(GL_MODELVIEW); | |||||
glLoadIdentity(); | |||||
width = w; | |||||
height = h; | |||||
} | |||||
static void draw_samples() { | |||||
glPointSize(1.0f); | |||||
glColor3f(1.0f, 1.0f, 1.0f); | |||||
glBegin(GL_POINTS); | |||||
// find depth channel. | |||||
// @todo { Do this only once. } | |||||
int depthChan = 0; | |||||
int rChan = -1; | |||||
int raChan = -1; | |||||
int gChan = -1; | |||||
int gaChan = -1; | |||||
int bChan = -1; | |||||
int baChan = -1; | |||||
for (int c = 0; c < gDeepImage.num_channels; c++) { | |||||
if (strcmp("Z", gDeepImage.channel_names[c]) == 0) { | |||||
depthChan = c; | |||||
} else if (strcmp("R", gDeepImage.channel_names[c]) == 0) { | |||||
rChan = c; | |||||
} else if (strcmp("RA", gDeepImage.channel_names[c]) == 0) { | |||||
raChan = c; | |||||
} else if (strcmp("G", gDeepImage.channel_names[c]) == 0) { | |||||
gChan = c; | |||||
} else if (strcmp("GA", gDeepImage.channel_names[c]) == 0) { | |||||
gaChan = c; | |||||
} else if (strcmp("B", gDeepImage.channel_names[c]) == 0) { | |||||
bChan = c; | |||||
} else if (strcmp("BA", gDeepImage.channel_names[c]) == 0) { | |||||
baChan = c; | |||||
} | |||||
} | |||||
for (int y = 0; y < gDeepImage.height; y++) { | |||||
float py = 2.0f * ((gDeepImage.height - y - 1) / (float)gDeepImage.height) - | |||||
1.0f; // upside down? | |||||
int sampleNum = gDeepImage.offset_table[y][gDeepImage.width - 1]; | |||||
int s_start = 0; // First pixel data starts at 0 | |||||
for (int x = 0; x < gDeepImage.width; x++) { | |||||
float px = 2.0f * (x / (float)gDeepImage.width) - 1.0f; | |||||
int s_end = gDeepImage.offset_table[y][x]; | |||||
if (s_start >= sampleNum || s_end >= sampleNum) { | |||||
continue; | |||||
} | |||||
for (int s = s_start; s < s_end; s++) { | |||||
float pz = -gDeepImage.image[depthChan][y][s]; | |||||
float red = 1.0f; | |||||
float green = 1.0f; | |||||
float blue = 1.0f; | |||||
float red_alpha = 1.0f; | |||||
float green_alpha = 1.0f; | |||||
float blue_alpha = 1.0f; | |||||
if (rChan >= 0) { | |||||
red = gDeepImage.image[rChan][y][s]; | |||||
} | |||||
if (raChan >= 0) { | |||||
red_alpha = gDeepImage.image[raChan][y][s]; | |||||
} | |||||
if (gChan >= 0) { | |||||
green = gDeepImage.image[gChan][y][s]; | |||||
} | |||||
if (gaChan >= 0) { | |||||
green_alpha = gDeepImage.image[gaChan][y][s]; | |||||
} | |||||
if (bChan >= 0) { | |||||
blue = gDeepImage.image[bChan][y][s]; | |||||
} | |||||
if (baChan >= 0) { | |||||
blue_alpha = gDeepImage.image[baChan][y][s]; | |||||
} | |||||
// unmultiply and apply scaling | |||||
red *= color_scale / red_alpha; | |||||
green *= color_scale / green_alpha; | |||||
blue *= color_scale / blue_alpha; | |||||
glColor3f(red, green, blue); | |||||
glVertex3f(px, py, pz); | |||||
} | |||||
s_start = s_end; | |||||
} | |||||
} | |||||
glEnd(); | |||||
} | |||||
static void display() { | |||||
GLfloat mat[4][4]; | |||||
glClearColor(0.0f, 0.0f, 0.0f, 0.0f); | |||||
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); | |||||
glEnable(GL_DEPTH_TEST); | |||||
glMatrixMode(GL_MODELVIEW); | |||||
glLoadIdentity(); | |||||
// camera & rotate | |||||
gluLookAt(view_org[0], view_org[1], view_org[2], view_tgt[0], view_tgt[1], | |||||
view_tgt[2], 0.0, 1.0, 0.0); | |||||
build_rotmatrix(mat, curr_quat); | |||||
glMultMatrixf(&mat[0][0]); | |||||
draw_samples(); | |||||
// glBegin(GL_POLYGON); | |||||
// glTexCoord2f(0 , 0); glVertex2f(-0.9 , -0.9); | |||||
// glTexCoord2f(0 , 1); glVertex2f(-0.9 , 0.9); | |||||
// glTexCoord2f(1 , 1); glVertex2f(0.9 , 0.9); | |||||
// glTexCoord2f(1 , 0); glVertex2f(0.9 , -0.9); | |||||
// glEnd(); | |||||
glutSwapBuffers(); | |||||
} | |||||
static void keyboard(unsigned char key, int x, int y) { | |||||
switch (key) { | |||||
case 'q': | |||||
case 27: | |||||
exit(0); | |||||
break; | |||||
case 'c': | |||||
color_scale += 1.0f; | |||||
break; | |||||
case 'x': | |||||
color_scale -= 1.0f; | |||||
if (color_scale < 1.0f) | |||||
color_scale = 1.0f; | |||||
break; | |||||
default: | |||||
break; | |||||
} | |||||
glutPostRedisplay(); | |||||
} | |||||
static void mouse(int button, int state, int x, int y) { | |||||
int mod = glutGetModifiers(); | |||||
if (button == GLUT_LEFT_BUTTON && state == GLUT_DOWN) { | |||||
if (mod == GLUT_ACTIVE_SHIFT) { | |||||
mouse_m_pressed = 1; | |||||
} else if (mod == GLUT_ACTIVE_CTRL) { | |||||
mouse_r_pressed = 1; | |||||
} else { | |||||
trackball(prev_quat, 0, 0, 0, 0); | |||||
} | |||||
mouse_moving = 1; | |||||
mouse_x = x; | |||||
mouse_y = y; | |||||
} else if (button == GLUT_LEFT_BUTTON && state == GLUT_UP) { | |||||
mouse_m_pressed = 0; | |||||
mouse_r_pressed = 0; | |||||
mouse_moving = 0; | |||||
} | |||||
} | |||||
static void motion(int x, int y) { | |||||
float w = 1.0; | |||||
float mw = 0.1; | |||||
if (mouse_moving) { | |||||
if (mouse_r_pressed) { | |||||
view_org[2] += mw * (mouse_y - y); | |||||
view_tgt[2] += mw * (mouse_y - y); | |||||
} else if (mouse_m_pressed) { | |||||
view_org[0] += mw * (mouse_x - x); | |||||
view_org[1] -= mw * (mouse_y - y); | |||||
view_tgt[0] += mw * (mouse_x - x); | |||||
view_tgt[1] -= mw * (mouse_y - y); | |||||
} else { | |||||
trackball(prev_quat, w * (2.0 * mouse_x - width) / width, | |||||
w * (height - 2.0 * mouse_y) / height, | |||||
w * (2.0 * x - width) / width, w * (height - 2.0 * y) / height); | |||||
add_quats(prev_quat, curr_quat, curr_quat); | |||||
} | |||||
mouse_x = x; | |||||
mouse_y = y; | |||||
} | |||||
glutPostRedisplay(); | |||||
} | |||||
static void init() { | |||||
trackball(curr_quat, 0, 0, 0, 0); | |||||
view_org[0] = 0.0f; | |||||
view_org[1] = 0.0f; | |||||
view_org[2] = 3.0f; | |||||
view_tgt[0] = 0.0f; | |||||
view_tgt[1] = 0.0f; | |||||
view_tgt[2] = 0.0f; | |||||
} | |||||
int main(int argc, char **argv) { | |||||
const char *input = "input.exr"; | |||||
if (argc < 2) { | |||||
printf("Usage: deepview <input.exr>\n"); | |||||
exit(1); | |||||
} | |||||
input = argv[1]; | |||||
const char *err; | |||||
int ret = LoadDeepEXR(&gDeepImage, input, &err); | |||||
if (ret != 0) { | |||||
if (err) { | |||||
fprintf(stderr, "ERR: %s\n", err); | |||||
} | |||||
exit(-1); | |||||
} | |||||
glutInit(&argc, argv); | |||||
glutInitWindowSize(512, 512); | |||||
glutInitDisplayMode(GLUT_DOUBLE | GLUT_DEPTH | GLUT_RGBA); | |||||
init(); | |||||
glutCreateWindow("deepimage viewer"); | |||||
glutReshapeFunc(reshape); | |||||
glutDisplayFunc(display); | |||||
glutKeyboardFunc(keyboard); | |||||
glutMouseFunc(mouse); | |||||
glutMotionFunc(motion); | |||||
glutMainLoop(); | |||||
return 0; | |||||
} |
@ -0,0 +1,324 @@ | |||||
/* | |||||
* (c) Copyright 1993, 1994, Silicon Graphics, Inc. | |||||
* ALL RIGHTS RESERVED | |||||
* Permission to use, copy, modify, and distribute this software for | |||||
* any purpose and without fee is hereby granted, provided that the above | |||||
* copyright notice appear in all copies and that both the copyright notice | |||||
* and this permission notice appear in supporting documentation, and that | |||||
* the name of Silicon Graphics, Inc. not be used in advertising | |||||
* or publicity pertaining to distribution of the software without specific, | |||||
* written prior permission. | |||||
* | |||||
* THE MATERIAL EMBODIED ON THIS SOFTWARE IS PROVIDED TO YOU "AS-IS" | |||||
* AND WITHOUT WARRANTY OF ANY KIND, EXPRESS, IMPLIED OR OTHERWISE, | |||||
* INCLUDING WITHOUT LIMITATION, ANY WARRANTY OF MERCHANTABILITY OR | |||||
* FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL SILICON | |||||
* GRAPHICS, INC. BE LIABLE TO YOU OR ANYONE ELSE FOR ANY DIRECT, | |||||
* SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY | |||||
* KIND, OR ANY DAMAGES WHATSOEVER, INCLUDING WITHOUT LIMITATION, | |||||
* LOSS OF PROFIT, LOSS OF USE, SAVINGS OR REVENUE, OR THE CLAIMS OF | |||||
* THIRD PARTIES, WHETHER OR NOT SILICON GRAPHICS, INC. HAS BEEN | |||||
* ADVISED OF THE POSSIBILITY OF SUCH LOSS, HOWEVER CAUSED AND ON | |||||
* ANY THEORY OF LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE | |||||
* POSSESSION, USE OR PERFORMANCE OF THIS SOFTWARE. | |||||
* | |||||
* US Government Users Restricted Rights | |||||
* Use, duplication, or disclosure by the Government is subject to | |||||
* restrictions set forth in FAR 52.227.19(c)(2) or subparagraph | |||||
* (c)(1)(ii) of the Rights in Technical Data and Computer Software | |||||
* clause at DFARS 252.227-7013 and/or in similar or successor | |||||
* clauses in the FAR or the DOD or NASA FAR Supplement. | |||||
* Unpublished-- rights reserved under the copyright laws of the | |||||
* United States. Contractor/manufacturer is Silicon Graphics, | |||||
* Inc., 2011 N. Shoreline Blvd., Mountain View, CA 94039-7311. | |||||
* | |||||
* OpenGL(TM) is a trademark of Silicon Graphics, Inc. | |||||
*/ | |||||
/* | |||||
* Trackball code: | |||||
* | |||||
* Implementation of a virtual trackball. | |||||
* Implemented by Gavin Bell, lots of ideas from Thant Tessman and | |||||
* the August '88 issue of Siggraph's "Computer Graphics," pp. 121-129. | |||||
* | |||||
* Vector manip code: | |||||
* | |||||
* Original code from: | |||||
* David M. Ciemiewicz, Mark Grossman, Henry Moreton, and Paul Haeberli | |||||
* | |||||
* Much mucking with by: | |||||
* Gavin Bell | |||||
*/ | |||||
#include <math.h> | |||||
#include "trackball.h" | |||||
/* | |||||
* This size should really be based on the distance from the center of | |||||
* rotation to the point on the object underneath the mouse. That | |||||
* point would then track the mouse as closely as possible. This is a | |||||
* simple example, though, so that is left as an Exercise for the | |||||
* Programmer. | |||||
*/ | |||||
#define TRACKBALLSIZE (0.8) | |||||
/* | |||||
* Local function prototypes (not defined in trackball.h) | |||||
*/ | |||||
static float tb_project_to_sphere(float, float, float); | |||||
static void normalize_quat(float [4]); | |||||
void | |||||
vzero(float *v) | |||||
{ | |||||
v[0] = 0.0; | |||||
v[1] = 0.0; | |||||
v[2] = 0.0; | |||||
} | |||||
void | |||||
vset(float *v, float x, float y, float z) | |||||
{ | |||||
v[0] = x; | |||||
v[1] = y; | |||||
v[2] = z; | |||||
} | |||||
void | |||||
vsub(const float *src1, const float *src2, float *dst) | |||||
{ | |||||
dst[0] = src1[0] - src2[0]; | |||||
dst[1] = src1[1] - src2[1]; | |||||
dst[2] = src1[2] - src2[2]; | |||||
} | |||||
void | |||||
vcopy(const float *v1, float *v2) | |||||
{ | |||||
register int i; | |||||
for (i = 0 ; i < 3 ; i++) | |||||
v2[i] = v1[i]; | |||||
} | |||||
void | |||||
vcross(const float *v1, const float *v2, float *cross) | |||||
{ | |||||
float temp[3]; | |||||
temp[0] = (v1[1] * v2[2]) - (v1[2] * v2[1]); | |||||
temp[1] = (v1[2] * v2[0]) - (v1[0] * v2[2]); | |||||
temp[2] = (v1[0] * v2[1]) - (v1[1] * v2[0]); | |||||
vcopy(temp, cross); | |||||
} | |||||
float | |||||
vlength(const float *v) | |||||
{ | |||||
return sqrt(v[0] * v[0] + v[1] * v[1] + v[2] * v[2]); | |||||
} | |||||
void | |||||
vscale(float *v, float div) | |||||
{ | |||||
v[0] *= div; | |||||
v[1] *= div; | |||||
v[2] *= div; | |||||
} | |||||
void | |||||
vnormal(float *v) | |||||
{ | |||||
vscale(v,1.0/vlength(v)); | |||||
} | |||||
float | |||||
vdot(const float *v1, const float *v2) | |||||
{ | |||||
return v1[0]*v2[0] + v1[1]*v2[1] + v1[2]*v2[2]; | |||||
} | |||||
void | |||||
vadd(const float *src1, const float *src2, float *dst) | |||||
{ | |||||
dst[0] = src1[0] + src2[0]; | |||||
dst[1] = src1[1] + src2[1]; | |||||
dst[2] = src1[2] + src2[2]; | |||||
} | |||||
/* | |||||
* Ok, simulate a track-ball. Project the points onto the virtual | |||||
* trackball, then figure out the axis of rotation, which is the cross | |||||
* product of P1 P2 and O P1 (O is the center of the ball, 0,0,0) | |||||
* Note: This is a deformed trackball-- is a trackball in the center, | |||||
* but is deformed into a hyperbolic sheet of rotation away from the | |||||
* center. This particular function was chosen after trying out | |||||
* several variations. | |||||
* | |||||
* It is assumed that the arguments to this routine are in the range | |||||
* (-1.0 ... 1.0) | |||||
*/ | |||||
void | |||||
trackball(float q[4], float p1x, float p1y, float p2x, float p2y) | |||||
{ | |||||
float a[3]; /* Axis of rotation */ | |||||
float phi; /* how much to rotate about axis */ | |||||
float p1[3], p2[3], d[3]; | |||||
float t; | |||||
if (p1x == p2x && p1y == p2y) { | |||||
/* Zero rotation */ | |||||
vzero(q); | |||||
q[3] = 1.0; | |||||
return; | |||||
} | |||||
/* | |||||
* First, figure out z-coordinates for projection of P1 and P2 to | |||||
* deformed sphere | |||||
*/ | |||||
vset(p1,p1x,p1y,tb_project_to_sphere(TRACKBALLSIZE,p1x,p1y)); | |||||
vset(p2,p2x,p2y,tb_project_to_sphere(TRACKBALLSIZE,p2x,p2y)); | |||||
/* | |||||
* Now, we want the cross product of P1 and P2 | |||||
*/ | |||||
vcross(p2,p1,a); | |||||
/* | |||||
* Figure out how much to rotate around that axis. | |||||
*/ | |||||
vsub(p1,p2,d); | |||||
t = vlength(d) / (2.0*TRACKBALLSIZE); | |||||
/* | |||||
* Avoid problems with out-of-control values... | |||||
*/ | |||||
if (t > 1.0) t = 1.0; | |||||
if (t < -1.0) t = -1.0; | |||||
phi = 2.0 * asin(t); | |||||
axis_to_quat(a,phi,q); | |||||
} | |||||
/* | |||||
* Given an axis and angle, compute quaternion. | |||||
*/ | |||||
void | |||||
axis_to_quat(float a[3], float phi, float q[4]) | |||||
{ | |||||
vnormal(a); | |||||
vcopy(a,q); | |||||
vscale(q,sin(phi/2.0)); | |||||
q[3] = cos(phi/2.0); | |||||
} | |||||
/* | |||||
* Project an x,y pair onto a sphere of radius r OR a hyperbolic sheet | |||||
* if we are away from the center of the sphere. | |||||
*/ | |||||
static float | |||||
tb_project_to_sphere(float r, float x, float y) | |||||
{ | |||||
float d, t, z; | |||||
d = sqrt(x*x + y*y); | |||||
if (d < r * 0.70710678118654752440) { /* Inside sphere */ | |||||
z = sqrt(r*r - d*d); | |||||
} else { /* On hyperbola */ | |||||
t = r / 1.41421356237309504880; | |||||
z = t*t / d; | |||||
} | |||||
return z; | |||||
} | |||||
/* | |||||
* Given two rotations, e1 and e2, expressed as quaternion rotations, | |||||
* figure out the equivalent single rotation and stuff it into dest. | |||||
* | |||||
* This routine also normalizes the result every RENORMCOUNT times it is | |||||
* called, to keep error from creeping in. | |||||
* | |||||
* NOTE: This routine is written so that q1 or q2 may be the same | |||||
* as dest (or each other). | |||||
*/ | |||||
#define RENORMCOUNT 97 | |||||
void | |||||
add_quats(float q1[4], float q2[4], float dest[4]) | |||||
{ | |||||
static int count=0; | |||||
float t1[4], t2[4], t3[4]; | |||||
float tf[4]; | |||||
vcopy(q1,t1); | |||||
vscale(t1,q2[3]); | |||||
vcopy(q2,t2); | |||||
vscale(t2,q1[3]); | |||||
vcross(q2,q1,t3); | |||||
vadd(t1,t2,tf); | |||||
vadd(t3,tf,tf); | |||||
tf[3] = q1[3] * q2[3] - vdot(q1,q2); | |||||
dest[0] = tf[0]; | |||||
dest[1] = tf[1]; | |||||
dest[2] = tf[2]; | |||||
dest[3] = tf[3]; | |||||
if (++count > RENORMCOUNT) { | |||||
count = 0; | |||||
normalize_quat(dest); | |||||
} | |||||
} | |||||
/* | |||||
* Quaternions always obey: a^2 + b^2 + c^2 + d^2 = 1.0 | |||||
* If they don't add up to 1.0, dividing by their magnitued will | |||||
* renormalize them. | |||||
* | |||||
* Note: See the following for more information on quaternions: | |||||
* | |||||
* - Shoemake, K., Animating rotation with quaternion curves, Computer | |||||
* Graphics 19, No 3 (Proc. SIGGRAPH'85), 245-254, 1985. | |||||
* - Pletinckx, D., Quaternion calculus as a basic tool in computer | |||||
* graphics, The Visual Computer 5, 2-13, 1989. | |||||
*/ | |||||
static void | |||||
normalize_quat(float q[4]) | |||||
{ | |||||
int i; | |||||
float mag; | |||||
mag = (q[0]*q[0] + q[1]*q[1] + q[2]*q[2] + q[3]*q[3]); | |||||
for (i = 0; i < 4; i++) q[i] /= mag; | |||||
} | |||||
/* | |||||
* Build a rotation matrix, given a quaternion rotation. | |||||
* | |||||
*/ | |||||
void | |||||
build_rotmatrix(float m[4][4], float q[4]) | |||||
{ | |||||
m[0][0] = 1.0 - 2.0 * (q[1] * q[1] + q[2] * q[2]); | |||||
m[0][1] = 2.0 * (q[0] * q[1] - q[2] * q[3]); | |||||
m[0][2] = 2.0 * (q[2] * q[0] + q[1] * q[3]); | |||||
m[0][3] = 0.0; | |||||
m[1][0] = 2.0 * (q[0] * q[1] + q[2] * q[3]); | |||||
m[1][1]= 1.0 - 2.0 * (q[2] * q[2] + q[0] * q[0]); | |||||
m[1][2] = 2.0 * (q[1] * q[2] - q[0] * q[3]); | |||||
m[1][3] = 0.0; | |||||
m[2][0] = 2.0 * (q[2] * q[0] - q[1] * q[3]); | |||||
m[2][1] = 2.0 * (q[1] * q[2] + q[0] * q[3]); | |||||
m[2][2] = 1.0 - 2.0 * (q[1] * q[1] + q[0] * q[0]); | |||||
m[2][3] = 0.0; | |||||
m[3][0] = 0.0; | |||||
m[3][1] = 0.0; | |||||
m[3][2] = 0.0; | |||||
m[3][3] = 1.0; | |||||
} | |||||
@ -0,0 +1,81 @@ | |||||
/* | |||||
* (c) Copyright 1993, 1994, Silicon Graphics, Inc. | |||||
* ALL RIGHTS RESERVED | |||||
* Permission to use, copy, modify, and distribute this software for | |||||
* any purpose and without fee is hereby granted, provided that the above | |||||
* copyright notice appear in all copies and that both the copyright notice | |||||
* and this permission notice appear in supporting documentation, and that | |||||
* the name of Silicon Graphics, Inc. not be used in advertising | |||||
* or publicity pertaining to distribution of the software without specific, | |||||
* written prior permission. | |||||
* | |||||
* THE MATERIAL EMBODIED ON THIS SOFTWARE IS PROVIDED TO YOU "AS-IS" | |||||
* AND WITHOUT WARRANTY OF ANY KIND, EXPRESS, IMPLIED OR OTHERWISE, | |||||
* INCLUDING WITHOUT LIMITATION, ANY WARRANTY OF MERCHANTABILITY OR | |||||
* FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL SILICON | |||||
* GRAPHICS, INC. BE LIABLE TO YOU OR ANYONE ELSE FOR ANY DIRECT, | |||||
* SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY | |||||
* KIND, OR ANY DAMAGES WHATSOEVER, INCLUDING WITHOUT LIMITATION, | |||||
* LOSS OF PROFIT, LOSS OF USE, SAVINGS OR REVENUE, OR THE CLAIMS OF | |||||
* THIRD PARTIES, WHETHER OR NOT SILICON GRAPHICS, INC. HAS BEEN | |||||
* ADVISED OF THE POSSIBILITY OF SUCH LOSS, HOWEVER CAUSED AND ON | |||||
* ANY THEORY OF LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE | |||||
* POSSESSION, USE OR PERFORMANCE OF THIS SOFTWARE. | |||||
* | |||||
* US Government Users Restricted Rights | |||||
* Use, duplication, or disclosure by the Government is subject to | |||||
* restrictions set forth in FAR 52.227.19(c)(2) or subparagraph | |||||
* (c)(1)(ii) of the Rights in Technical Data and Computer Software | |||||
* clause at DFARS 252.227-7013 and/or in similar or successor | |||||
* clauses in the FAR or the DOD or NASA FAR Supplement. | |||||
* Unpublished-- rights reserved under the copyright laws of the | |||||
* United States. Contractor/manufacturer is Silicon Graphics, | |||||
* Inc., 2011 N. Shoreline Blvd., Mountain View, CA 94039-7311. | |||||
* | |||||
* OpenGL(TM) is a trademark of Silicon Graphics, Inc. | |||||
*/ | |||||
/* | |||||
* trackball.h | |||||
* A virtual trackball implementation | |||||
* Written by Gavin Bell for Silicon Graphics, November 1988. | |||||
*/ | |||||
/* | |||||
* Pass the x and y coordinates of the last and current positions of | |||||
* the mouse, scaled so they are from (-1.0 ... 1.0). | |||||
* | |||||
* The resulting rotation is returned as a quaternion rotation in the | |||||
* first paramater. | |||||
*/ | |||||
void | |||||
trackball(float q[4], float p1x, float p1y, float p2x, float p2y); | |||||
void | |||||
negate_quat(float *q, float *qn); | |||||
/* | |||||
* Given two quaternions, add them together to get a third quaternion. | |||||
* Adding quaternions to get a compound rotation is analagous to adding | |||||
* translations to get a compound translation. When incrementally | |||||
* adding rotations, the first argument here should be the new | |||||
* rotation, the second and third the total rotation (which will be | |||||
* over-written with the resulting new total rotation). | |||||
*/ | |||||
void | |||||
add_quats(float *q1, float *q2, float *dest); | |||||
/* | |||||
* A useful function, builds a rotation matrix in Matrix based on | |||||
* given quaternion. | |||||
*/ | |||||
void | |||||
build_rotmatrix(float m[4][4], float q[4]); | |||||
/* | |||||
* This function computes a quaternion based on an axis (defined by | |||||
* the given vector) and an angle about which to rotate. The angle is | |||||
* expressed in radians. The result is put into the third argument. | |||||
*/ | |||||
void | |||||
axis_to_quat(float a[3], float phi, float q[4]); | |||||
@ -0,0 +1,32 @@ | |||||
CXX=clang++ | |||||
CC=clang | |||||
CFLAGS = -fsanitize=address -Weverything -Wno-padded -g -O2 -I../../ -I../../deps/miniz | |||||
CXXFLAGS = -std=c++11 -Wno-c++98-compat -Wno-variadic-macros $(CFLAGS) | |||||
LDFLAGS = -fsanitize=address | |||||
# ZFP | |||||
#CXXFLAGS += -DTINYEXR_USE_ZFP=1 -I/home/syoyo/work/zfp/include | |||||
#LDFLAGS += -L/home/syoyo/work/zfp/build/lib -lzfp | |||||
all: exr2fptiff | |||||
exr2fptiff: exr2fptiff.o tinyexr.o miniz.o | |||||
$(CXX) -o $@ $^ $(LDFLAGS) | |||||
exr2fptiff.o: exr2fptiff.cc tiny_dng_writer.h | |||||
$(CXX) $(CXXFLAGS) -c -o $@ $< | |||||
tinyexr.o: ../../tinyexr.cc | |||||
$(CXX) $(CXXFLAGS) -c -o $@ $< | |||||
miniz.o: ../../deps/miniz/miniz.c | |||||
$(CC) $(CFLAGS) -c $(INC_DIR) $< | |||||
.PHONY: clean | |||||
clean: | |||||
rm -rf tinyexr.o exr2fptiff.o |
@ -0,0 +1,7 @@ | |||||
# exr2fptiff | |||||
OpenEXR to 32bit float TIFF converter. | |||||
## Limitation | |||||
Input EXR image must be grayscale, RGB or RGBA. |
@ -0,0 +1,199 @@ | |||||
#include <cstdio> | |||||
#include <cstdlib> | |||||
#include <vector> | |||||
#include "tinyexr.h" | |||||
#define TINY_DNG_WRITER_IMPLEMENTATION | |||||
#include "tiny_dng_writer.h" | |||||
static bool Create32bitFpTiff( | |||||
const float *data, // [width x height x in_channels] | |||||
const size_t width, | |||||
const size_t height, | |||||
const size_t in_channels, | |||||
const size_t channels, | |||||
tinydngwriter::DNGImage *dng_image) { | |||||
if (in_channels < 1) return false; | |||||
unsigned int image_width = uint32_t(width); | |||||
unsigned int image_height = uint32_t(height); | |||||
//dng_image->SetSubfileType(false, false, false); | |||||
dng_image->SetImageWidth(image_width); | |||||
dng_image->SetImageLength(image_height); | |||||
dng_image->SetRowsPerStrip(image_height); | |||||
dng_image->SetSamplesPerPixel(uint16_t(channels)); | |||||
std::vector<uint16_t> bps(channels); | |||||
for (size_t i = 0; i < bps.size(); i++) { | |||||
bps[i] = 32; | |||||
} | |||||
dng_image->SetBitsPerSample(static_cast<unsigned int>(channels), bps.data()); | |||||
dng_image->SetPlanarConfig(tinydngwriter::PLANARCONFIG_CONTIG); | |||||
dng_image->SetCompression(tinydngwriter::COMPRESSION_NONE); | |||||
if (channels == 1) { | |||||
dng_image->SetPhotometric( | |||||
tinydngwriter::PHOTOMETRIC_BLACK_IS_ZERO); // grayscale | |||||
} else { | |||||
dng_image->SetPhotometric( | |||||
tinydngwriter::PHOTOMETRIC_RGB); | |||||
} | |||||
dng_image->SetXResolution(1.0); | |||||
dng_image->SetYResolution(1.0); | |||||
dng_image->SetResolutionUnit(tinydngwriter::RESUNIT_NONE); | |||||
std::vector<uint16_t> formats(channels); | |||||
for (size_t i = 0; i < formats.size(); i++) { | |||||
formats[i] = tinydngwriter::SAMPLEFORMAT_IEEEFP; | |||||
} | |||||
dng_image->SetSampleFormat(static_cast<unsigned int>(channels), formats.data()); | |||||
std::vector<float> buf; | |||||
buf.resize(size_t(channels) * image_width * image_height); | |||||
for (size_t i = 0; i < image_width * image_height; i++) { | |||||
size_t in_c = 0; | |||||
for (size_t c = 0; c < channels; c++) { | |||||
buf[channels * i + c] = data[in_channels * i + in_c]; | |||||
in_c++; | |||||
in_c = std::min(in_c, in_channels - 1); | |||||
} | |||||
} | |||||
//size_t max_dump_pixels = 4096; | |||||
//for (size_t i = 0; i < std::min(max_dump_pixels, buf.size()); i++) { | |||||
// std::cout << "val[" << i << "] = " << buf[i] << "\n"; | |||||
//} | |||||
//std::cout << "last = " << buf.at(image_width * image_height * channels - 1) << "\n"; | |||||
// We must retain pointer address of `buf` until calling DNGWriter::WriteToFile | |||||
dng_image->SetImageData(reinterpret_cast<unsigned char *>(buf.data()), | |||||
buf.size() * sizeof(float)); | |||||
if (!dng_image->Error().empty()) { | |||||
std::cout << "Err: " << dng_image->Error() << "\n"; | |||||
return false; | |||||
} | |||||
return true; | |||||
} | |||||
int main(int argc, char** argv) | |||||
{ | |||||
if (argc < 3) { | |||||
printf("Usage: exr2fptiff input.exr output.tiff\n"); | |||||
exit(-1); | |||||
} | |||||
std::string input_filename = argv[1]; | |||||
std::string output_filename = argv[2]; | |||||
// Get # of layers | |||||
size_t num_layers{0}; | |||||
{ | |||||
EXRVersion exr_version; | |||||
{ | |||||
int ret = ParseEXRVersionFromFile(&exr_version, input_filename.c_str()); | |||||
if (ret != 0) { | |||||
std::cerr << "Invalid EXR file: " << input_filename << "\n"; | |||||
return EXIT_FAILURE; | |||||
} | |||||
if (exr_version.multipart) { | |||||
std::cerr << "Multipart EXR file is not supported in this example.\n"; | |||||
return EXIT_FAILURE; | |||||
} | |||||
} | |||||
EXRHeader exr_header; | |||||
InitEXRHeader(&exr_header); | |||||
const char* err = nullptr; | |||||
int ret = ParseEXRHeaderFromFile(&exr_header, &exr_version, argv[1], &err); | |||||
if (ret != TINYEXR_SUCCESS) { | |||||
if (err) { | |||||
std::cerr << "Parse EXR error: " << err << "\n"; | |||||
FreeEXRErrorMessage(err); // free's buffer for an error message | |||||
} else { | |||||
std::cerr << "Parse EXR error.\n"; | |||||
} | |||||
return EXIT_FAILURE; | |||||
} | |||||
num_layers = size_t(exr_header.num_channels); | |||||
if (num_layers == 0) { | |||||
std::cerr << "no layers found\n"; | |||||
return EXIT_FAILURE; | |||||
} | |||||
if (num_layers > 4) { | |||||
std::cerr << "This program supports up to 4(e.g. RGBA) layers.\n"; | |||||
return EXIT_FAILURE; | |||||
} | |||||
FreeEXRHeader(&exr_header); | |||||
} | |||||
std::cout << "# of channels = " << num_layers << "\n"; | |||||
// Use legacy but easy-to-use API to read image. | |||||
float *rgba{nullptr}; | |||||
int width; | |||||
int height; | |||||
{ | |||||
const char *err; | |||||
int ret = LoadEXR(&rgba, &width, &height, input_filename.c_str(), &err); | |||||
if (ret != TINYEXR_SUCCESS) { | |||||
if (err) { | |||||
std::cerr << "Load EXR error: " << err << "\n"; | |||||
FreeEXRErrorMessage(err); // free's buffer for an error message | |||||
} else { | |||||
std::cerr << "Load EXR error.\n"; | |||||
} | |||||
return EXIT_FAILURE; | |||||
} | |||||
} | |||||
bool big_endian = false; | |||||
tinydngwriter::DNGImage tiff; | |||||
tiff.SetBigEndian(big_endian); | |||||
bool ret = Create32bitFpTiff(rgba, size_t(width), size_t(height), /* in_channels */4, size_t(num_layers), &tiff); | |||||
if (!ret) { | |||||
std::cerr << "Failed to create floating point tiff data\n"; | |||||
return EXIT_FAILURE; | |||||
} | |||||
// 4. Free image data | |||||
free(rgba); | |||||
tinydngwriter::DNGWriter dng_writer(big_endian); | |||||
ret = dng_writer.AddImage(&tiff); | |||||
if (!ret) { | |||||
std::cerr << "Failed to add TIFF image to TIFF writer.\n"; | |||||
return EXIT_FAILURE; | |||||
} | |||||
// 5. write tiff | |||||
std::string err; | |||||
ret = dng_writer.WriteToFile(output_filename.c_str(), &err); | |||||
if (!err.empty()) { | |||||
std::cerr << err; | |||||
} | |||||
if (!ret) { | |||||
return EXIT_FAILURE; | |||||
} | |||||
return EXIT_SUCCESS; | |||||
} |
@ -0,0 +1,3 @@ | |||||
all: | |||||
clang -g -O2 -c -I../../deps/miniz ../../deps/miniz/miniz.c | |||||
clang++ -g -fsanitize=address -O2 -o exr2ldr -I../common -I../../ -I../../deps/miniz exr2ldr.cc ../../tinyexr.cc miniz.o |
@ -0,0 +1,111 @@ | |||||
#include <cstdio> | |||||
#include <cstdlib> | |||||
#include <vector> | |||||
#ifdef __clang__ | |||||
#pragma clang diagnostic push | |||||
#pragma clang diagnostic ignored "-Weverything" | |||||
#endif | |||||
#define STB_IMAGE_RESIZE_IMPLEMENTATION | |||||
#include "stb_image_resize.h" | |||||
#define STB_IMAGE_WRITE_IMPLEMENTATION | |||||
#include "stb_image_write.h" | |||||
#ifdef __clang__ | |||||
#pragma clang diagnostic pop | |||||
#endif | |||||
#include "tinyexr.h" | |||||
inline unsigned char ftouc(float f, float gamma) | |||||
{ | |||||
int i = static_cast<int>(255.0f * powf(f, 1.0f / gamma)); | |||||
if (i > 255) i = 255; | |||||
if (i < 0) i = 0; | |||||
return static_cast<unsigned char>(i); | |||||
} | |||||
bool SaveImage(const char* filename, const float* rgba, float scale, float gamma, int width, int height, bool ignore_alpha) { | |||||
std::vector<unsigned char> dst(width * height * 4); | |||||
// alpha channel is also affected by `scale` parameter. | |||||
if(ignore_alpha) { | |||||
for (size_t i = 0; i < width * height; i++) { | |||||
dst[i * 4 + 0] = ftouc(rgba[i * 4 + 0] * scale, gamma); | |||||
dst[i * 4 + 1] = ftouc(rgba[i * 4 + 1] * scale, gamma); | |||||
dst[i * 4 + 2] = ftouc(rgba[i * 4 + 2] * scale, gamma); | |||||
dst[i * 4 + 3] = 255; | |||||
} | |||||
} else { | |||||
for (size_t i = 0; i < width * height * 4; i++) { | |||||
dst[i] = ftouc(rgba[i] * scale, gamma); | |||||
} | |||||
} | |||||
int ret = stbi_write_png(filename, width, height, 4, static_cast<const void*>(dst.data()), width * 4); | |||||
return (ret > 0); | |||||
} | |||||
int main(int argc, char** argv) | |||||
{ | |||||
if (argc < 3) { | |||||
printf("Usage: exr2ldr input.exr output.png (scale) (resize_factor) (gammavalue) (-i or --ignore-alpha).\n"); | |||||
printf(" Pixel value [0.0, 1.0] in EXR is mapped to [0, 255] for LDR image.\n"); | |||||
printf(" You can adjust pixel value by `scale`(default = 1.0).\n"); | |||||
printf(" Resize image using `resize_factor`(default = 1.0). 2 = create half size image, 4 = 1/4 image, and so on\n"); | |||||
printf(" gammmavalue will be used for gamma correction when saving png image(default = 2.2).\n"); | |||||
printf(" Ignore alpha value of input using -i or --ignore-alpha flag, and alpha of output is set to 255.\n"); | |||||
exit(-1); | |||||
} | |||||
float scale = 1.0f; | |||||
if (argc > 3) { | |||||
scale = atof(argv[3]); | |||||
} | |||||
float resize_factor = 1.0f; | |||||
if (argc > 4) { | |||||
resize_factor = atof(argv[4]); | |||||
} | |||||
float gamma = 2.2f; | |||||
if (argc > 5) { | |||||
gamma = atof(argv[5]); | |||||
} | |||||
bool ignore_alpha = false; | |||||
if (argc > 6 && (strcmp(argv[6], "-i") == 0 || strcmp(argv[6], "--ignore-alpha") == 0)) { | |||||
ignore_alpha = true; | |||||
} | |||||
int width, height; | |||||
float* rgba; | |||||
const char* err; | |||||
{ | |||||
int ret = LoadEXR(&rgba, &width, &height, argv[1], &err); | |||||
if (ret != 0) { | |||||
printf("err: %s\n", err); | |||||
return -1; | |||||
} | |||||
} | |||||
int dst_width = width / resize_factor; | |||||
int dst_height = height / resize_factor; | |||||
printf("dst = %d, %d\n", dst_width, dst_height); | |||||
std::vector<float> buf(dst_width * dst_height * 4); | |||||
int ret = stbir_resize_float(rgba, width, height, width*4*sizeof(float), &buf.at(0), dst_width, dst_height,dst_width*4*sizeof(float), 4); | |||||
assert(ret != 0); | |||||
bool ok = SaveImage(argv[2], &buf.at(0), scale, gamma, dst_width, dst_height, ignore_alpha); | |||||
free(rgba); | |||||
return (ok ? 0 : -1); | |||||
} |
@ -0,0 +1,3 @@ | |||||
all: | |||||
gcc -O2 -c -I../../deps/miniz ../../deps/miniz/miniz.c | |||||
g++ -O2 -o exr2rgbe -I../common -I../../ -I../../deps/miniz exr2rgbe.cc ../../tinyexr.cc miniz.o |
@ -0,0 +1,38 @@ | |||||
#include <cstdio> | |||||
#include <cstdlib> | |||||
#include <vector> | |||||
#define STB_IMAGE_WRITE_IMPLEMENTATION | |||||
#include "stb_image_write.h" | |||||
#include "tinyexr.h" | |||||
int main(int argc, char** argv) | |||||
{ | |||||
if (argc < 3) { | |||||
printf("Usage: exr2rgbe input.exr output.hdr\n"); | |||||
exit(-1); | |||||
} | |||||
int width, height; | |||||
float* rgba; | |||||
const char* err; | |||||
{ | |||||
int ret = LoadEXR(&rgba, &width, &height, argv[1], &err); | |||||
if (ret != 0) { | |||||
printf("err: %s\n", err); | |||||
return -1; | |||||
} | |||||
} | |||||
{ | |||||
int ret = stbi_write_hdr(argv[2], width, height, 4, rgba); | |||||
if (ret == 0) { | |||||
return -1; // fail | |||||
} | |||||
} | |||||
return 0; | |||||
} |
@ -0,0 +1,3 @@ | |||||
all: | |||||
gcc -O2 -c -I../../deps/miniz ../../deps/miniz/miniz.c | |||||
g++ -std=c++11 -O2 -o exrfilter -I../common -I../../ -I ../../deps/miniz exrfilter.cc ../../tinyexr.cc miniz.o |
@ -0,0 +1,5 @@ | |||||
Simple EXR filtering program. | |||||
Currently implemented are | |||||
- Clip min/max intensity(RGB) |
@ -0,0 +1,150 @@ | |||||
#include <cstdio> | |||||
#include <cstdlib> | |||||
#include <vector> | |||||
#include "tinyexr.h" | |||||
#include "cxxopts.hpp" | |||||
int main(int argc, char** argv) | |||||
{ | |||||
float min_value = -std::numeric_limits<float>::max(); | |||||
float max_value = std::numeric_limits<float>::max(); | |||||
float rgb_min[3] = {min_value, min_value, min_value}; | |||||
float rgb_max[3] = {max_value, max_value, max_value}; | |||||
cxxopts::Options options("normalmap", "help"); | |||||
options.show_positional_help(); | |||||
options.add_options() | |||||
("max", "Max intensity(apply all RGB channels)", cxxopts::value<float>()) | |||||
("rmax", "Max Red intensity", cxxopts::value<float>()) | |||||
("gmax", "Max Green intensity", cxxopts::value<float>()) | |||||
("bmax", "Max Blue intensity", cxxopts::value<float>()) | |||||
("min", "Min intensity(apply all RGB channels)", cxxopts::value<float>()) | |||||
("rmin", "Min Red intensity", cxxopts::value<float>()) | |||||
("gmin", "Min Green intensity", cxxopts::value<float>()) | |||||
("bmin", "Min Blue intensity", cxxopts::value<float>()) | |||||
("i,input", "Input filename", cxxopts::value<std::string>()) | |||||
("o,output", "Output filename", cxxopts::value<std::string>()) | |||||
("help", "Print help") | |||||
; | |||||
auto result = options.parse(argc, argv); | |||||
if (result.count("help")) { | |||||
std::cout << options.help() << std::endl; | |||||
} | |||||
if (result.count("input") == 0) { | |||||
std::cerr << "input filename missing" << std::endl; | |||||
std::cout << options.help() << std::endl; | |||||
return EXIT_FAILURE; | |||||
} | |||||
if (result.count("output") == 0) { | |||||
std::cerr << "output filename missing" << std::endl; | |||||
std::cout << options.help() << std::endl; | |||||
return EXIT_FAILURE; | |||||
} | |||||
std::string input_filename = result["input"].as<std::string>(); | |||||
std::string output_filename = result["output"].as<std::string>(); | |||||
if (result.count("max")) { | |||||
rgb_max[0] = result["max"].as<float>(); | |||||
rgb_max[1] = result["max"].as<float>(); | |||||
rgb_max[2] = result["max"].as<float>(); | |||||
} | |||||
if (result.count("rmax")) { | |||||
rgb_max[0] = result["rmax"].as<float>(); | |||||
} | |||||
if (result.count("gmax")) { | |||||
rgb_max[1] = result["gmax"].as<float>(); | |||||
} | |||||
if (result.count("bmax")) { | |||||
rgb_max[2] = result["bmax"].as<float>(); | |||||
} | |||||
if (result.count("min")) { | |||||
rgb_min[0] = result["min"].as<float>(); | |||||
rgb_min[1] = result["min"].as<float>(); | |||||
rgb_min[2] = result["min"].as<float>(); | |||||
} | |||||
if (result.count("rmin")) { | |||||
rgb_min[0] = result["rmin"].as<float>(); | |||||
} | |||||
if (result.count("gmin")) { | |||||
rgb_min[1] = result["gmin"].as<float>(); | |||||
} | |||||
if (result.count("bmin")) { | |||||
rgb_min[2] = result["bmin"].as<float>(); | |||||
} | |||||
float *rgba = nullptr; | |||||
int width, height; | |||||
const char *err = nullptr; | |||||
{ | |||||
int ret = LoadEXR(&rgba, &width, &height, input_filename.c_str(), &err); | |||||
if (TINYEXR_SUCCESS != ret) { | |||||
std::cerr << "Failed to load EXR file [" << input_filename << "] code = " << ret << std::endl; | |||||
if (err) { | |||||
std::cerr << err << std::endl; | |||||
FreeEXRErrorMessage(err); | |||||
} | |||||
return EXIT_FAILURE; | |||||
} | |||||
} | |||||
// clip pixel values. | |||||
// ignore alpha channel for now. | |||||
std::vector<float> rgb; | |||||
rgb.resize(width * height * 3); | |||||
float v_max[3] = { | |||||
-std::numeric_limits<float>::max(), | |||||
-std::numeric_limits<float>::max(), | |||||
-std::numeric_limits<float>::max()}; | |||||
float v_min[3] = { | |||||
std::numeric_limits<float>::max(), | |||||
std::numeric_limits<float>::max(), | |||||
std::numeric_limits<float>::max()}; | |||||
{ | |||||
for (size_t i = 0; i < width * height; i++) { | |||||
rgb[3 * i + 0] = std::max(rgb_min[0], std::min(rgb_max[0], rgba[4 * i + 0])); | |||||
rgb[3 * i + 1] = std::max(rgb_min[1], std::min(rgb_max[1], rgba[4 * i + 1])); | |||||
rgb[3 * i + 2] = std::max(rgb_min[2], std::min(rgb_max[2], rgba[4 * i + 2])); | |||||
v_max[0] = std::max(rgb[3 * i + 0], v_max[0]); | |||||
v_max[1] = std::max(rgb[3 * i + 1], v_max[1]); | |||||
v_max[2] = std::max(rgb[3 * i + 2], v_max[2]); | |||||
v_min[0] = std::min(rgb[3 * i + 0], v_min[0]); | |||||
v_min[1] = std::min(rgb[3 * i + 1], v_min[1]); | |||||
v_min[2] = std::min(rgb[3 * i + 2], v_min[2]); | |||||
} | |||||
} | |||||
std::cout << "v min = " << v_min[0] << ", " << v_min[1] << ", " << v_min[2] << std::endl; | |||||
std::cout << "v max = " << v_max[0] << ", " << v_max[1] << ", " << v_max[2] << std::endl; | |||||
{ | |||||
int ret = SaveEXR(rgb.data(), width, height, /* component */3, /* fp16 */0, output_filename.c_str(), &err); | |||||
if (TINYEXR_SUCCESS != ret) { | |||||
if (err) { | |||||
std::cerr << err << std::endl; | |||||
FreeEXRErrorMessage(err); | |||||
} | |||||
std::cerr << "Failed to save EXR file [" << input_filename << "] code = " << ret << std::endl; | |||||
return EXIT_FAILURE; | |||||
} | |||||
} | |||||
return EXIT_SUCCESS; | |||||
} |
@ -0,0 +1,130 @@ | |||||
#ifndef B3G_WINDOW_INTERFACE_H | |||||
#define B3G_WINDOW_INTERFACE_H | |||||
typedef void (*b3WheelCallback)(float deltax, float deltay); | |||||
typedef void (*b3ResizeCallback)( float width, float height); | |||||
typedef void (*b3MouseMoveCallback)( float x, float y); | |||||
typedef void (*b3MouseButtonCallback)(int button, int state, float x, float y); | |||||
typedef void (*b3KeyboardCallback)(int keycode, int state); | |||||
typedef void (*b3RenderCallback) (); | |||||
enum { | |||||
B3G_ESCAPE = 27, | |||||
B3G_F1 = 0xff00, | |||||
B3G_F2, | |||||
B3G_F3, | |||||
B3G_F4, | |||||
B3G_F5, | |||||
B3G_F6, | |||||
B3G_F7, | |||||
B3G_F8, | |||||
B3G_F9, | |||||
B3G_F10, | |||||
B3G_F11, | |||||
B3G_F12, | |||||
B3G_F13, | |||||
B3G_F14, | |||||
B3G_F15, | |||||
B3G_LEFT_ARROW, | |||||
B3G_RIGHT_ARROW, | |||||
B3G_UP_ARROW, | |||||
B3G_DOWN_ARROW, | |||||
B3G_PAGE_UP, | |||||
B3G_PAGE_DOWN, | |||||
B3G_END, | |||||
B3G_HOME, | |||||
B3G_INSERT, | |||||
B3G_DELETE, | |||||
B3G_BACKSPACE, | |||||
B3G_SHIFT, | |||||
B3G_CONTROL, | |||||
B3G_ALT, | |||||
B3G_RETURN | |||||
}; | |||||
struct b3gWindowConstructionInfo | |||||
{ | |||||
int m_width; | |||||
int m_height; | |||||
bool m_fullscreen; | |||||
int m_colorBitsPerPixel; | |||||
void* m_windowHandle; | |||||
const char* m_title; | |||||
int m_openglVersion; | |||||
b3gWindowConstructionInfo(int width=1024, int height=768) | |||||
:m_width(width), | |||||
m_height(height), | |||||
m_fullscreen(false), | |||||
m_colorBitsPerPixel(32), | |||||
m_windowHandle(0), | |||||
m_title("title"), | |||||
m_openglVersion(3) | |||||
{ | |||||
} | |||||
}; | |||||
class CommonWindowInterface | |||||
{ | |||||
public: | |||||
virtual ~CommonWindowInterface() | |||||
{ | |||||
} | |||||
virtual void createDefaultWindow(int width, int height, const char* title) | |||||
{ | |||||
b3gWindowConstructionInfo ci(width,height); | |||||
ci.m_title = title; | |||||
createWindow(ci); | |||||
} | |||||
virtual void createWindow(const b3gWindowConstructionInfo& ci)=0; | |||||
virtual void closeWindow()=0; | |||||
virtual void runMainLoop()=0; | |||||
virtual float getTimeInSeconds()=0; | |||||
virtual bool requestedExit() const = 0; | |||||
virtual void setRequestExit() = 0; | |||||
virtual void startRendering()=0; | |||||
virtual void endRendering()=0; | |||||
virtual bool isModifierKeyPressed(int key) = 0; | |||||
virtual void setMouseMoveCallback(b3MouseMoveCallback mouseCallback)=0; | |||||
virtual b3MouseMoveCallback getMouseMoveCallback()=0; | |||||
virtual void setMouseButtonCallback(b3MouseButtonCallback mouseCallback)=0; | |||||
virtual b3MouseButtonCallback getMouseButtonCallback()=0; | |||||
virtual void setResizeCallback(b3ResizeCallback resizeCallback)=0; | |||||
virtual b3ResizeCallback getResizeCallback()=0; | |||||
virtual void setWheelCallback(b3WheelCallback wheelCallback)=0; | |||||
virtual b3WheelCallback getWheelCallback()=0; | |||||
virtual void setKeyboardCallback( b3KeyboardCallback keyboardCallback)=0; | |||||
virtual b3KeyboardCallback getKeyboardCallback()=0; | |||||
virtual void setRenderCallback( b3RenderCallback renderCallback) = 0; | |||||
virtual void setWindowTitle(const char* title)=0; | |||||
virtual float getRetinaScale() const =0; | |||||
virtual void setAllowRetina(bool allow) =0; | |||||
virtual int getWidth() const = 0; | |||||
virtual int getHeight() const = 0; | |||||
virtual int fileOpenDialog(char* fileName, int maxFileNameLength) = 0; | |||||
}; | |||||
#endif //B3G_WINDOW_INTERFACE_H |
@ -0,0 +1,118 @@ | |||||
#ifndef MAC_OPENGL_WINDOW_H | |||||
#define MAC_OPENGL_WINDOW_H | |||||
#include "CommonWindowInterface.h" | |||||
#define b3gDefaultOpenGLWindow MacOpenGLWindow | |||||
class MacOpenGLWindow : public CommonWindowInterface | |||||
{ | |||||
struct MacOpenGLWindowInternalData* m_internalData; | |||||
float m_mouseX; | |||||
float m_mouseY; | |||||
int m_modifierFlags; | |||||
b3MouseButtonCallback m_mouseButtonCallback; | |||||
b3MouseMoveCallback m_mouseMoveCallback; | |||||
b3WheelCallback m_wheelCallback; | |||||
b3KeyboardCallback m_keyboardCallback; | |||||
b3RenderCallback m_renderCallback; | |||||
float m_retinaScaleFactor; | |||||
bool m_allowRetina; | |||||
public: | |||||
MacOpenGLWindow(); | |||||
virtual ~MacOpenGLWindow(); | |||||
void init(int width, int height, const char* windowTitle); | |||||
void closeWindow(); | |||||
void startRendering(); | |||||
void endRendering();//swap buffers | |||||
virtual bool requestedExit() const; | |||||
virtual void setRequestExit(); | |||||
void getMouseCoordinates(int& x, int& y); | |||||
void runMainLoop(); | |||||
virtual bool isModifierKeyPressed(int key); | |||||
void setMouseButtonCallback(b3MouseButtonCallback mouseCallback) | |||||
{ | |||||
m_mouseButtonCallback = mouseCallback; | |||||
} | |||||
void setMouseMoveCallback(b3MouseMoveCallback mouseCallback) | |||||
{ | |||||
m_mouseMoveCallback = mouseCallback; | |||||
} | |||||
void setResizeCallback(b3ResizeCallback resizeCallback); | |||||
void setKeyboardCallback( b3KeyboardCallback keyboardCallback) | |||||
{ | |||||
m_keyboardCallback = keyboardCallback; | |||||
} | |||||
virtual b3MouseMoveCallback getMouseMoveCallback() | |||||
{ | |||||
return m_mouseMoveCallback; | |||||
} | |||||
virtual b3MouseButtonCallback getMouseButtonCallback() | |||||
{ | |||||
return m_mouseButtonCallback; | |||||
} | |||||
virtual b3ResizeCallback getResizeCallback(); | |||||
virtual b3WheelCallback getWheelCallback() | |||||
{ | |||||
return m_wheelCallback; | |||||
} | |||||
b3KeyboardCallback getKeyboardCallback() | |||||
{ | |||||
return m_keyboardCallback; | |||||
} | |||||
void setWheelCallback (b3WheelCallback wheelCallback) | |||||
{ | |||||
m_wheelCallback = wheelCallback; | |||||
} | |||||
float getRetinaScale() const | |||||
{ | |||||
return m_retinaScaleFactor; | |||||
} | |||||
virtual void setAllowRetina(bool allow) | |||||
{ | |||||
m_allowRetina = allow; | |||||
} | |||||
virtual void createWindow(const b3gWindowConstructionInfo& ci); | |||||
virtual float getTimeInSeconds(); | |||||
virtual int getWidth() const; | |||||
virtual int getHeight() const; | |||||
virtual void setRenderCallback( b3RenderCallback renderCallback); | |||||
virtual void setWindowTitle(const char* title); | |||||
int fileOpenDialog(char* filename, int maxNameLength); | |||||
}; | |||||
#endif | |||||
@ -0,0 +1,59 @@ | |||||
/* | |||||
Copyright (c) 2012 Advanced Micro Devices, Inc. | |||||
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. | |||||
*/ | |||||
//Originally written by Erwin Coumans | |||||
#ifndef __OPENGL_INCLUDE_H | |||||
#define __OPENGL_INCLUDE_H | |||||
//think different | |||||
#if defined(__APPLE__) && !defined (VMDMESA) | |||||
#include <OpenGL/OpenGL.h> | |||||
#include <OpenGL/gl.h> | |||||
#else | |||||
#ifdef GLEW_STATIC | |||||
#include "CustomGL/glew.h" | |||||
#else | |||||
#include <GL/glew.h> | |||||
#endif//GLEW_STATIC | |||||
#ifdef _WINDOWS | |||||
#include <windows.h> | |||||
//#include <GL/gl.h> | |||||
//#include <GL/glu.h> | |||||
#else | |||||
//#include <GL/gl.h> | |||||
//#include <GL/glu.h> | |||||
#endif //_WINDOWS | |||||
#endif //APPLE | |||||
//disable glGetError | |||||
//#undef glGetError | |||||
//#define glGetError MyGetError | |||||
// | |||||
//GLenum inline MyGetError() | |||||
//{ | |||||
// return 0; | |||||
//} | |||||
///on Linux only glDrawElementsInstancedARB is defined?!? | |||||
//#ifdef __linux | |||||
//#define glDrawElementsInstanced glDrawElementsInstancedARB | |||||
// | |||||
//#endif //__linux | |||||
#endif //__OPENGL_INCLUDE_H | |||||
@ -0,0 +1,66 @@ | |||||
/* | |||||
Copyright (c) 2012 Advanced Micro Devices, Inc. | |||||
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. | |||||
*/ | |||||
//Originally written by Erwin Coumans | |||||
#ifndef __OPENGL_INCLUDE_H | |||||
#define __OPENGL_INCLUDE_H | |||||
//think different | |||||
#if defined(__APPLE__) && !defined (VMDMESA) | |||||
#include <OpenGL/OpenGL.h> | |||||
//#include <OpenGL/gl.h> | |||||
//#include <OpenGL/glu.h> | |||||
//#import <Cocoa/Cocoa.h> | |||||
#if defined (USE_OPENGL2) || defined (NO_OPENGL3) | |||||
#include <OpenGL/gl.h> | |||||
#else | |||||
#include <OpenGL/gl3.h> | |||||
#endif | |||||
#else | |||||
#ifdef GLEW_STATIC | |||||
#include "CustomGL/glew.h" | |||||
#else | |||||
#include <GL/glew.h> | |||||
#endif //GLEW_STATIC | |||||
#ifdef _WINDOWS | |||||
#include <windows.h> | |||||
//#include <GL/gl.h> | |||||
//#include <GL/glu.h> | |||||
#else | |||||
//#include <GL/gl.h> | |||||
//#include <GL/glu.h> | |||||
#endif //_WINDOWS | |||||
#endif //APPLE | |||||
//disable glGetError | |||||
//#undef glGetError | |||||
//#define glGetError MyGetError | |||||
// | |||||
//GLenum inline MyGetError() | |||||
//{ | |||||
// return 0; | |||||
//} | |||||
///on Linux only glDrawElementsInstancedARB is defined?!? | |||||
//#ifdef __linux | |||||
//#define glDrawElementsInstanced glDrawElementsInstancedARB | |||||
// | |||||
//#endif //__linux | |||||
#endif //__OPENGL_INCLUDE_H | |||||
@ -0,0 +1,68 @@ | |||||
#ifndef WIN32_INTERNAL_WINDOW_DATA_H | |||||
#define WIN32_INTERNAL_WINDOW_DATA_H | |||||
#include <windows.h> | |||||
struct InternalData2 | |||||
{ | |||||
HWND m_hWnd;; | |||||
int m_fullWindowWidth;//includes borders etc | |||||
int m_fullWindowHeight; | |||||
int m_openglViewportWidth;//just the 3d viewport/client area | |||||
int m_openglViewportHeight; | |||||
HDC m_hDC; | |||||
HGLRC m_hRC; | |||||
bool m_OpenGLInitialized; | |||||
int m_oldScreenWidth; | |||||
int m_oldHeight; | |||||
int m_oldBitsPerPel; | |||||
bool m_quit; | |||||
int m_mouseLButton; | |||||
int m_mouseRButton; | |||||
int m_mouseMButton; | |||||
int m_mouseXpos; | |||||
int m_mouseYpos; | |||||
int m_internalKeyModifierFlags; | |||||
b3WheelCallback m_wheelCallback; | |||||
b3MouseMoveCallback m_mouseMoveCallback; | |||||
b3MouseButtonCallback m_mouseButtonCallback; | |||||
b3ResizeCallback m_resizeCallback; | |||||
b3KeyboardCallback m_keyboardCallback; | |||||
InternalData2() | |||||
{ | |||||
m_hWnd = 0; | |||||
m_mouseLButton=0; | |||||
m_mouseRButton=0; | |||||
m_mouseMButton=0; | |||||
m_internalKeyModifierFlags = 0; | |||||
m_fullWindowWidth = 0; | |||||
m_fullWindowHeight= 0; | |||||
m_openglViewportHeight=0; | |||||
m_openglViewportWidth=0; | |||||
m_hDC = 0; | |||||
m_hRC = 0; | |||||
m_OpenGLInitialized = false; | |||||
m_oldScreenWidth = 0; | |||||
m_oldHeight = 0; | |||||
m_oldBitsPerPel = 0; | |||||
m_quit = false; | |||||
m_keyboardCallback = 0; | |||||
m_mouseMoveCallback = 0; | |||||
m_mouseButtonCallback = 0; | |||||
m_resizeCallback = 0; | |||||
m_wheelCallback = 0; | |||||
} | |||||
}; | |||||
#endif //WIN32_INTERNAL_WINDOW_DATA_H |
@ -0,0 +1,200 @@ | |||||
#ifdef _WIN32 | |||||
/* | |||||
Copyright (c) 2012 Advanced Micro Devices, Inc. | |||||
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. | |||||
*/ | |||||
//Originally written by Erwin Coumans | |||||
#include "Win32OpenGLWindow.h" | |||||
#include "OpenGLInclude.h" | |||||
//#include "Bullet3Common/b3Vector3.h" | |||||
#include "Win32InternalWindowData.h" | |||||
#include <stdio.h> | |||||
static void printGLString(const char *name, GLenum s) { | |||||
const char *v = (const char *) glGetString(s); | |||||
printf("%s = %s\n",name, v); | |||||
} | |||||
bool sOpenGLVerbose = true; | |||||
void Win32OpenGLWindow::enableOpenGL() | |||||
{ | |||||
PIXELFORMATDESCRIPTOR pfd; | |||||
int format; | |||||
// get the device context (DC) | |||||
m_data->m_hDC = GetDC( m_data->m_hWnd ); | |||||
// set the pixel format for the DC | |||||
ZeroMemory( &pfd, sizeof( pfd ) ); | |||||
pfd.nSize = sizeof( pfd ); | |||||
pfd.nVersion = 1; | |||||
pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER; | |||||
pfd.iPixelType = PFD_TYPE_RGBA; | |||||
pfd.cColorBits = 32; | |||||
pfd.cRedBits = 8; | |||||
pfd.cGreenBits = 8; | |||||
pfd.cBlueBits = 8; | |||||
pfd.cAlphaBits = 8; | |||||
pfd.cDepthBits = 24; | |||||
pfd.cStencilBits = 8;//1; | |||||
pfd.iLayerType = PFD_MAIN_PLANE; | |||||
format = ChoosePixelFormat( m_data->m_hDC, &pfd ); | |||||
SetPixelFormat( m_data->m_hDC, format, &pfd ); | |||||
// create and enable the render context (RC) | |||||
m_data->m_hRC = wglCreateContext( m_data->m_hDC ); | |||||
wglMakeCurrent( m_data->m_hDC, m_data->m_hRC ); | |||||
if (sOpenGLVerbose) | |||||
{ | |||||
printGLString("Version", GL_VERSION); | |||||
printGLString("Vendor", GL_VENDOR); | |||||
printGLString("Renderer", GL_RENDERER); | |||||
} | |||||
//printGLString("Extensions", GL_EXTENSIONS); | |||||
} | |||||
void Win32OpenGLWindow::disableOpenGL() | |||||
{ | |||||
wglMakeCurrent( NULL, NULL ); | |||||
wglDeleteContext( m_data->m_hRC ); | |||||
// ReleaseDC( m_data->m_hWnd, m_data->m_hDC ); | |||||
} | |||||
void Win32OpenGLWindow::createWindow(const b3gWindowConstructionInfo& ci) | |||||
{ | |||||
Win32Window::createWindow(ci); | |||||
//VideoDriver = video::createOpenGLDriver(CreationParams, FileSystem, this); | |||||
enableOpenGL(); | |||||
} | |||||
Win32OpenGLWindow::Win32OpenGLWindow() | |||||
{ | |||||
} | |||||
Win32OpenGLWindow::~Win32OpenGLWindow() | |||||
{ | |||||
} | |||||
void Win32OpenGLWindow::closeWindow() | |||||
{ | |||||
disableOpenGL(); | |||||
Win32Window::closeWindow(); | |||||
} | |||||
void Win32OpenGLWindow::startRendering() | |||||
{ | |||||
pumpMessage(); | |||||
//don't clear all 3 buffers because some AMD drivers are buggy | |||||
//glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT|GL_STENCIL_BUFFER_BIT); | |||||
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); | |||||
//glCullFace(GL_BACK); | |||||
//glFrontFace(GL_CCW); | |||||
glEnable(GL_DEPTH_TEST); | |||||
} | |||||
void Win32OpenGLWindow::renderAllObjects() | |||||
{ | |||||
} | |||||
void Win32OpenGLWindow::endRendering() | |||||
{ | |||||
SwapBuffers( m_data->m_hDC ); | |||||
} | |||||
int Win32OpenGLWindow::fileOpenDialog(char* fileName, int maxFileNameLength) | |||||
{ | |||||
//wchar_t wideChars[1024]; | |||||
OPENFILENAME ofn ; | |||||
ZeroMemory( &ofn , sizeof( ofn)); | |||||
ofn.lStructSize = sizeof ( ofn ); | |||||
ofn.hwndOwner = NULL ; | |||||
#ifdef UNICODE | |||||
WCHAR bla[1024]; | |||||
ofn.lpstrFile = bla; | |||||
ofn.lpstrFile[0] = '\0'; | |||||
ofn.nMaxFile = 1023; | |||||
ofn.lpstrFilter = L"All Files\0*.*\0URDF\0*.urdf\0.bullet\0*.bullet\0"; | |||||
#else | |||||
ofn.lpstrFile = fileName; | |||||
ofn.lpstrFile[0] = '\0'; | |||||
ofn.nMaxFile = 1023; | |||||
//ofn.lpstrFilter = "All\0*.*\0Text\0*.TXT\0"; | |||||
ofn.lpstrFilter = "All Files\0*.*\0URDF\0*.urdf\0.bullet\0*.bullet\0"; | |||||
#endif | |||||
ofn.nFilterIndex =1; | |||||
ofn.lpstrFileTitle = NULL ; | |||||
ofn.nMaxFileTitle = 0 ; | |||||
ofn.lpstrInitialDir=NULL ; | |||||
ofn.Flags = OFN_PATHMUSTEXIST|OFN_FILEMUSTEXIST ; | |||||
GetOpenFileName( &ofn ); | |||||
return strlen(fileName); | |||||
//return 0; | |||||
} | |||||
int Win32OpenGLWindow::getWidth() const | |||||
{ | |||||
if (m_data) | |||||
return m_data->m_openglViewportWidth; | |||||
return 0; | |||||
} | |||||
int Win32OpenGLWindow::getHeight() const | |||||
{ | |||||
if (m_data) | |||||
return m_data->m_openglViewportHeight; | |||||
return 0; | |||||
} | |||||
#endif | |||||
@ -0,0 +1,64 @@ | |||||
/* | |||||
Copyright (c) 2012 Advanced Micro Devices, Inc. | |||||
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. | |||||
*/ | |||||
//Originally written by Erwin Coumans | |||||
#ifndef _WIN32_OPENGL_RENDER_MANAGER_H | |||||
#define _WIN32_OPENGL_RENDER_MANAGER_H | |||||
#include "Win32Window.h" | |||||
#define b3gDefaultOpenGLWindow Win32OpenGLWindow | |||||
class Win32OpenGLWindow : public Win32Window | |||||
{ | |||||
bool m_OpenGLInitialized; | |||||
protected: | |||||
void enableOpenGL(); | |||||
void disableOpenGL(); | |||||
public: | |||||
Win32OpenGLWindow(); | |||||
virtual ~Win32OpenGLWindow(); | |||||
virtual void createWindow(const b3gWindowConstructionInfo& ci); | |||||
virtual void closeWindow(); | |||||
virtual void startRendering(); | |||||
virtual void renderAllObjects(); | |||||
virtual void endRendering(); | |||||
virtual float getRetinaScale() const {return 1.f;} | |||||
virtual void setAllowRetina(bool /*allowRetina*/) {}; | |||||
virtual int getWidth() const; | |||||
virtual int getHeight() const; | |||||
virtual int fileOpenDialog(char* fileName, int maxFileNameLength); | |||||
}; | |||||
#endif //_WIN32_OPENGL_RENDER_MANAGER_H |
@ -0,0 +1,810 @@ | |||||
#ifdef _WIN32 | |||||
/* | |||||
Copyright (c) 2012 Advanced Micro Devices, Inc. | |||||
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. | |||||
*/ | |||||
//Originally written by Erwin Coumans | |||||
#include "Win32Window.h" | |||||
#include "OpenGLInclude.h" | |||||
#include <wchar.h> | |||||
static InternalData2* sData = 0; | |||||
#include "Win32InternalWindowData.h" | |||||
enum | |||||
{ | |||||
INTERNAL_SHIFT_MODIFIER=1, | |||||
INTERNAL_ALT_MODIFIER=2, | |||||
INTERNAL_CONTROL_MODIFIER=4, | |||||
}; | |||||
void Win32Window::pumpMessage() | |||||
{ | |||||
MSG msg; | |||||
// check for messages | |||||
//'if' instead of 'while' can make mainloop smoother. | |||||
//@todo: use separate threads for input and rendering | |||||
while( PeekMessage( &msg, NULL, 0, 0, PM_REMOVE ) ) | |||||
{ | |||||
// handle or dispatch messages | |||||
if ( msg.message == WM_QUIT ) | |||||
{ | |||||
m_data->m_quit = TRUE; | |||||
} | |||||
else | |||||
{ | |||||
TranslateMessage( &msg ); | |||||
DispatchMessage( &msg ); | |||||
} | |||||
// gDemoApplication->displayCallback(); | |||||
}; | |||||
} | |||||
int getSpecialKeyFromVirtualKeycode(int virtualKeyCode) | |||||
{ | |||||
int keycode = -1; | |||||
if (virtualKeyCode >= 'A' && virtualKeyCode <= 'Z') | |||||
{ | |||||
return virtualKeyCode+32;//todo: fix the ascii A vs a input | |||||
} | |||||
switch (virtualKeyCode) | |||||
{ | |||||
case VK_RETURN: {keycode = B3G_RETURN; break; }; | |||||
case VK_F1: {keycode = B3G_F1; break;} | |||||
case VK_F2: {keycode = B3G_F2; break;} | |||||
case VK_F3: {keycode = B3G_F3; break;} | |||||
case VK_F4: {keycode = B3G_F4; break;} | |||||
case VK_F5: {keycode = B3G_F5; break;} | |||||
case VK_F6: {keycode = B3G_F6; break;} | |||||
case VK_F7: {keycode = B3G_F7; break;} | |||||
case VK_F8: {keycode = B3G_F8; break;} | |||||
case VK_F9: {keycode = B3G_F9; break;} | |||||
case VK_F10: {keycode= B3G_F10; break;} | |||||
//case VK_SPACE: {keycode= ' '; break;} | |||||
case VK_NEXT: {keycode= B3G_PAGE_DOWN; break;} | |||||
case VK_PRIOR: {keycode= B3G_PAGE_UP; break;} | |||||
case VK_INSERT: {keycode= B3G_INSERT; break;} | |||||
case VK_BACK: {keycode= B3G_BACKSPACE; break;} | |||||
case VK_DELETE: {keycode= B3G_DELETE; break;} | |||||
case VK_END:{keycode= B3G_END; break;} | |||||
case VK_HOME:{keycode= B3G_HOME; break;} | |||||
case VK_LEFT:{keycode= B3G_LEFT_ARROW; break;} | |||||
case VK_UP:{keycode= B3G_UP_ARROW; break;} | |||||
case VK_RIGHT:{keycode= B3G_RIGHT_ARROW; break;} | |||||
case VK_DOWN:{keycode= B3G_DOWN_ARROW; break;} | |||||
case VK_SHIFT:{keycode=B3G_SHIFT;break;} | |||||
case VK_MENU:{keycode=B3G_ALT;break;} | |||||
case VK_CONTROL:{keycode=B3G_CONTROL;break;} | |||||
default: | |||||
{ | |||||
//keycode = MapVirtualKey( virtualKeyCode, MAPVK_VK_TO_CHAR ) & 0x0000FFFF; | |||||
} | |||||
}; | |||||
return keycode; | |||||
} | |||||
int getAsciiCodeFromVirtualKeycode(int virtualKeyCode) | |||||
{ | |||||
int keycode = 0xffffffff; | |||||
if (virtualKeyCode >= 'a' && virtualKeyCode <= 'z') | |||||
{ | |||||
return virtualKeyCode; | |||||
} | |||||
if (virtualKeyCode >= 'A' && virtualKeyCode <= 'Z') | |||||
{ | |||||
return virtualKeyCode+32;//todo: fix the ascii A vs a input | |||||
} | |||||
return keycode; | |||||
} | |||||
bool Win32Window::isModifierKeyPressed(int key) | |||||
{ | |||||
bool isPressed = false; | |||||
switch (key) | |||||
{ | |||||
case B3G_ALT: | |||||
{ | |||||
isPressed = ((sData->m_internalKeyModifierFlags&INTERNAL_ALT_MODIFIER)!=0); | |||||
break; | |||||
}; | |||||
case B3G_SHIFT: | |||||
{ | |||||
isPressed = ((sData->m_internalKeyModifierFlags&INTERNAL_SHIFT_MODIFIER)!=0); | |||||
break; | |||||
}; | |||||
case B3G_CONTROL: | |||||
{ | |||||
isPressed = ((sData->m_internalKeyModifierFlags&INTERNAL_CONTROL_MODIFIER)!=0); | |||||
break; | |||||
}; | |||||
default: | |||||
{ | |||||
} | |||||
}; | |||||
return isPressed;//m_internalKeyModifierFlags | |||||
} | |||||
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) | |||||
{ | |||||
//printf("msg = %d\n", message); | |||||
switch (message) | |||||
{ | |||||
case WM_PAINT: | |||||
{ | |||||
PAINTSTRUCT ps; | |||||
BeginPaint(hWnd, &ps); | |||||
EndPaint(hWnd, &ps); | |||||
} | |||||
return 1; | |||||
case WM_ERASEBKGND: | |||||
return 1; | |||||
case WM_CLOSE: | |||||
if (sData) | |||||
sData->m_quit = true; | |||||
//PostQuitMessage(0); | |||||
return 1; | |||||
case WM_DESTROY: | |||||
if (sData) | |||||
sData->m_quit = true; | |||||
//PostQuitMessage(0); | |||||
return 1; | |||||
case WM_SYSKEYUP: | |||||
case WM_KEYUP: | |||||
{ | |||||
int keycode = getSpecialKeyFromVirtualKeycode(wParam); | |||||
switch (keycode) | |||||
{ | |||||
case B3G_ALT: | |||||
{ | |||||
sData->m_internalKeyModifierFlags&=~INTERNAL_ALT_MODIFIER; | |||||
break; | |||||
}; | |||||
case B3G_SHIFT: | |||||
{ | |||||
sData->m_internalKeyModifierFlags &= ~INTERNAL_SHIFT_MODIFIER; | |||||
break; | |||||
}; | |||||
case B3G_CONTROL: | |||||
{ | |||||
sData->m_internalKeyModifierFlags &=~INTERNAL_CONTROL_MODIFIER; | |||||
break; | |||||
}; | |||||
} | |||||
if (keycode>=0 && sData && sData->m_keyboardCallback ) | |||||
{ | |||||
int state=0; | |||||
(*sData->m_keyboardCallback)(keycode,state); | |||||
} | |||||
return 0; | |||||
} | |||||
case WM_CHAR: | |||||
{ | |||||
//skip 'enter' key, it is processed in WM_KEYUP/WM_KEYDOWN | |||||
int keycode = getAsciiCodeFromVirtualKeycode(wParam); | |||||
if (keycode < 0) | |||||
{ | |||||
if (sData && sData->m_keyboardCallback && ((HIWORD(lParam) & KF_REPEAT) == 0)) | |||||
{ | |||||
int state = 1; | |||||
(*sData->m_keyboardCallback)(wParam, state); | |||||
} | |||||
} | |||||
return 0; | |||||
} | |||||
case WM_SYSKEYDOWN: | |||||
case WM_KEYDOWN: | |||||
{ | |||||
int keycode = getSpecialKeyFromVirtualKeycode(wParam); | |||||
switch (keycode) | |||||
{ | |||||
case B3G_ALT: | |||||
{ | |||||
sData->m_internalKeyModifierFlags|=INTERNAL_ALT_MODIFIER; | |||||
break; | |||||
}; | |||||
case B3G_SHIFT: | |||||
{ | |||||
sData->m_internalKeyModifierFlags |= INTERNAL_SHIFT_MODIFIER; | |||||
break; | |||||
}; | |||||
case B3G_CONTROL: | |||||
{ | |||||
sData->m_internalKeyModifierFlags |=INTERNAL_CONTROL_MODIFIER; | |||||
break; | |||||
}; | |||||
} | |||||
if (keycode>=0 && sData && sData->m_keyboardCallback)// && ((HIWORD(lParam) & KF_REPEAT) == 0)) | |||||
{ | |||||
int state = 1; | |||||
(*sData->m_keyboardCallback)(keycode,state); | |||||
return 1; | |||||
} | |||||
return 0; | |||||
} | |||||
case WM_MBUTTONUP: | |||||
{ | |||||
int xPos = LOWORD(lParam); | |||||
int yPos = HIWORD(lParam); | |||||
if (sData) | |||||
{ | |||||
sData->m_mouseMButton=0; | |||||
sData->m_mouseXpos = xPos; | |||||
sData->m_mouseYpos = yPos; | |||||
if (sData && sData->m_mouseButtonCallback) | |||||
(*sData->m_mouseButtonCallback)(1,0,xPos,yPos); | |||||
} | |||||
break; | |||||
} | |||||
case WM_MBUTTONDOWN: | |||||
{ | |||||
int xPos = LOWORD(lParam); | |||||
int yPos = HIWORD(lParam); | |||||
if (sData) | |||||
{ | |||||
sData->m_mouseMButton=1; | |||||
sData->m_mouseXpos = xPos; | |||||
sData->m_mouseYpos = yPos; | |||||
if (sData && sData->m_mouseButtonCallback) | |||||
(*sData->m_mouseButtonCallback)(1,1,xPos,yPos); | |||||
} | |||||
break; | |||||
} | |||||
case WM_LBUTTONUP: | |||||
{ | |||||
int xPos = LOWORD(lParam); | |||||
int yPos = HIWORD(lParam); | |||||
if (sData) | |||||
{ | |||||
sData->m_mouseLButton=0; | |||||
sData->m_mouseXpos = xPos; | |||||
sData->m_mouseYpos = yPos; | |||||
if (sData && sData->m_mouseButtonCallback) | |||||
(*sData->m_mouseButtonCallback)(0,0,xPos,yPos); | |||||
} | |||||
// gDemoApplication->mouseFunc(0,1,xPos,yPos); | |||||
break; | |||||
} | |||||
case WM_LBUTTONDOWN: | |||||
{ | |||||
int xPos = LOWORD(lParam); | |||||
int yPos = HIWORD(lParam); | |||||
if (sData) | |||||
{ | |||||
sData->m_mouseLButton=1; | |||||
sData->m_mouseXpos = xPos; | |||||
sData->m_mouseYpos = yPos; | |||||
if (sData && sData->m_mouseButtonCallback) | |||||
(*sData->m_mouseButtonCallback)(0,1,xPos,yPos); | |||||
} | |||||
break; | |||||
} | |||||
case 0x020e://WM_MOUSEWHEEL_LEFT_RIGHT | |||||
{ | |||||
int zDelta = (short)HIWORD(wParam); | |||||
int xPos = LOWORD(lParam); | |||||
int yPos = HIWORD(lParam); | |||||
//m_cameraDistance -= zDelta*0.01; | |||||
if (sData && sData->m_wheelCallback) | |||||
(*sData->m_wheelCallback)(-float(zDelta)*0.05f,0); | |||||
return 1; | |||||
break; | |||||
} | |||||
case 0x020A://WM_MOUSEWHEEL: | |||||
{ | |||||
int zDelta = (short)HIWORD(wParam); | |||||
int xPos = LOWORD(lParam); | |||||
int yPos = HIWORD(lParam); | |||||
//m_cameraDistance -= zDelta*0.01; | |||||
if (sData && sData->m_wheelCallback) | |||||
(*sData->m_wheelCallback)(0,float(zDelta)*0.05f); | |||||
return 1; | |||||
break; | |||||
} | |||||
case WM_MOUSEMOVE: | |||||
{ | |||||
int xPos = LOWORD(lParam); | |||||
int yPos = HIWORD(lParam); | |||||
sData->m_mouseXpos = xPos; | |||||
sData->m_mouseYpos = yPos; | |||||
if (sData && sData->m_mouseMoveCallback) | |||||
(*sData->m_mouseMoveCallback)(xPos,yPos); | |||||
break; | |||||
} | |||||
case WM_RBUTTONUP: | |||||
{ | |||||
int xPos = LOWORD(lParam); | |||||
int yPos = HIWORD(lParam); | |||||
sData->m_mouseRButton = 1; | |||||
if (sData && sData->m_mouseButtonCallback) | |||||
(*sData->m_mouseButtonCallback)(2,0,sData->m_mouseXpos,sData->m_mouseYpos); | |||||
//gDemoApplication->mouseFunc(2,1,xPos,yPos); | |||||
break; | |||||
} | |||||
case WM_RBUTTONDOWN: | |||||
{ | |||||
int xPos = LOWORD(lParam); | |||||
int yPos = HIWORD(lParam); | |||||
sData->m_mouseRButton = 0; | |||||
if (sData && sData->m_mouseButtonCallback) | |||||
(*sData->m_mouseButtonCallback)(2,1,sData->m_mouseXpos,sData->m_mouseYpos); | |||||
break; | |||||
} | |||||
case WM_QUIT: | |||||
{ | |||||
return 0; | |||||
break; | |||||
} | |||||
case WM_SIZE: // Size Action Has Taken Place | |||||
RECT clientRect; | |||||
GetClientRect(hWnd,&clientRect); | |||||
switch (wParam) // Evaluate Size Action | |||||
{ | |||||
case SIZE_MINIMIZED: // Was Window Minimized? | |||||
return 0; // Return | |||||
case SIZE_MAXIMIZED: // Was Window Maximized? | |||||
case SIZE_RESTORED: // Was Window Restored? | |||||
RECT wr; | |||||
GetWindowRect(hWnd,&wr); | |||||
sData->m_fullWindowWidth = wr.right-wr.left; | |||||
sData->m_fullWindowHeight = wr.bottom-wr.top;//LOWORD (lParam) HIWORD (lParam); | |||||
sData->m_openglViewportWidth = clientRect.right; | |||||
sData->m_openglViewportHeight = clientRect.bottom; | |||||
glViewport(0, 0, sData->m_openglViewportWidth, sData->m_openglViewportHeight); | |||||
if (sData->m_resizeCallback) | |||||
(*sData->m_resizeCallback)(sData->m_openglViewportWidth,sData->m_openglViewportHeight); | |||||
//if (sOpenGLInitialized) | |||||
//{ | |||||
// //gDemoApplication->reshape(sWidth,sHeight); | |||||
//} | |||||
return 0; // Return | |||||
} | |||||
break; | |||||
default:{ | |||||
} | |||||
}; | |||||
return DefWindowProc(hWnd, message, wParam, lParam); | |||||
} | |||||
void Win32Window::setWindowTitle(const char* titleChar) | |||||
{ | |||||
wchar_t windowTitle[1024]; | |||||
swprintf(windowTitle, 1024, L"%hs", titleChar); | |||||
DWORD dwResult; | |||||
#ifdef _WIN64 | |||||
SetWindowTextW(m_data->m_hWnd, windowTitle); | |||||
#else | |||||
SendMessageTimeoutW(m_data->m_hWnd, WM_SETTEXT, 0, | |||||
reinterpret_cast<LPARAM>(windowTitle), | |||||
SMTO_ABORTIFHUNG, 2000, &dwResult); | |||||
#endif | |||||
} | |||||
void Win32Window::createWindow(const b3gWindowConstructionInfo& ci) | |||||
{ | |||||
int oglViewportWidth = ci.m_width; | |||||
int oglViewportHeight = ci.m_height; | |||||
bool fullscreen = ci.m_fullscreen; | |||||
int colorBitsPerPixel = ci.m_colorBitsPerPixel; | |||||
void* windowHandle = ci.m_windowHandle; | |||||
// get handle to exe file | |||||
HINSTANCE hInstance = GetModuleHandle(0); | |||||
// create the window if we need to and we do not use the null device | |||||
if (!windowHandle) | |||||
{ | |||||
#ifdef UNICODE | |||||
const wchar_t * ClassName = L"DeviceWin32"; | |||||
const wchar_t* emptyString= L""; | |||||
#else | |||||
const char* ClassName = "DeviceWin32"; | |||||
const char* emptyString = ""; | |||||
#endif | |||||
// Register Class | |||||
WNDCLASSEX wcex; | |||||
wcex.cbSize = sizeof(WNDCLASSEX); | |||||
wcex.style = CS_HREDRAW | CS_VREDRAW; | |||||
wcex.lpfnWndProc = WndProc; | |||||
wcex.cbClsExtra = 0; | |||||
wcex.cbWndExtra = 0; | |||||
wcex.hInstance = hInstance; | |||||
wcex.hIcon = LoadIcon( NULL, IDI_APPLICATION ); //(HICON)LoadImage(hInstance, "bullet_ico.ico", IMAGE_ICON, 0,0, LR_LOADTRANSPARENT);//LR_LOADFROMFILE); | |||||
wcex.hCursor = LoadCursor(NULL, IDC_ARROW); | |||||
wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1); | |||||
wcex.lpszMenuName = 0; | |||||
wcex.lpszClassName = ClassName; | |||||
wcex.hIconSm = 0; | |||||
// if there is an icon, load it | |||||
// wcex.hIcon = (HICON)LoadImage(hInstance, "bullet.ico", IMAGE_ICON, 0,0, LR_LOADFROMFILE); | |||||
RegisterClassEx(&wcex); | |||||
// calculate client size | |||||
RECT clientSize; | |||||
clientSize.top = 0; | |||||
clientSize.left = 0; | |||||
clientSize.right = oglViewportWidth; | |||||
clientSize.bottom = oglViewportHeight; | |||||
DWORD style = WS_POPUP; | |||||
if (!fullscreen) | |||||
style = WS_SYSMENU | WS_BORDER | WS_CAPTION | WS_CLIPCHILDREN | WS_CLIPSIBLINGS | WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_SIZEBOX; | |||||
AdjustWindowRect(&clientSize, style, false); | |||||
m_data->m_fullWindowWidth = clientSize.right - clientSize.left; | |||||
m_data->m_fullWindowHeight = clientSize.bottom - clientSize.top; | |||||
int windowLeft = (GetSystemMetrics(SM_CXSCREEN) - m_data->m_fullWindowWidth) / 2; | |||||
int windowTop = (GetSystemMetrics(SM_CYSCREEN) - m_data->m_fullWindowHeight) / 2; | |||||
if (fullscreen) | |||||
{ | |||||
windowLeft = 0; | |||||
windowTop = 0; | |||||
} | |||||
// create window | |||||
m_data->m_hWnd = CreateWindow( ClassName, emptyString, style, windowLeft, windowTop, | |||||
m_data->m_fullWindowWidth, m_data->m_fullWindowHeight,NULL, NULL, hInstance, NULL); | |||||
RECT clientRect; | |||||
GetClientRect(m_data->m_hWnd,&clientRect); | |||||
ShowWindow(m_data->m_hWnd, SW_SHOW); | |||||
UpdateWindow(m_data->m_hWnd); | |||||
MoveWindow(m_data->m_hWnd, windowLeft, windowTop, m_data->m_fullWindowWidth, m_data->m_fullWindowHeight, TRUE); | |||||
GetClientRect(m_data->m_hWnd,&clientRect); | |||||
int w = clientRect.right-clientRect.left; | |||||
int h = clientRect.bottom-clientRect.top; | |||||
// printf("actual client OpenGL viewport width / height = %d, %d\n",w,h); | |||||
m_data->m_openglViewportHeight = h; | |||||
m_data->m_openglViewportWidth = w; | |||||
} | |||||
else if (windowHandle) | |||||
{ | |||||
// attach external window | |||||
m_data->m_hWnd = static_cast<HWND>(windowHandle); | |||||
RECT r; | |||||
GetWindowRect(m_data->m_hWnd, &r); | |||||
m_data->m_fullWindowWidth = r.right - r.left; | |||||
m_data->m_fullWindowHeight= r.bottom - r.top; | |||||
//sFullScreen = false; | |||||
//sExternalWindow = true; | |||||
} | |||||
if (fullscreen) | |||||
{ | |||||
DEVMODE dm; | |||||
memset(&dm, 0, sizeof(dm)); | |||||
dm.dmSize = sizeof(dm); | |||||
// use default values from current setting | |||||
EnumDisplaySettings(NULL, ENUM_CURRENT_SETTINGS, &dm); | |||||
m_data->m_oldScreenWidth = dm.dmPelsWidth; | |||||
m_data->m_oldHeight = dm.dmPelsHeight; | |||||
m_data->m_oldBitsPerPel = dm.dmBitsPerPel; | |||||
dm.dmPelsWidth = oglViewportWidth; | |||||
dm.dmPelsHeight = oglViewportHeight; | |||||
if (colorBitsPerPixel) | |||||
{ | |||||
dm.dmBitsPerPel = colorBitsPerPixel; | |||||
} | |||||
dm.dmFields = DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT | DM_DISPLAYFREQUENCY; | |||||
LONG res = ChangeDisplaySettings(&dm, CDS_FULLSCREEN); | |||||
if (res != DISP_CHANGE_SUCCESSFUL) | |||||
{ // try again without forcing display frequency | |||||
dm.dmFields = DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT; | |||||
res = ChangeDisplaySettings(&dm, CDS_FULLSCREEN); | |||||
} | |||||
} | |||||
} | |||||
void Win32Window::switchFullScreen(bool fullscreen,int width,int height,int colorBitsPerPixel) | |||||
{ | |||||
LONG res; | |||||
DEVMODE dm; | |||||
memset(&dm, 0, sizeof(dm)); | |||||
dm.dmSize = sizeof(dm); | |||||
// use default values from current setting | |||||
EnumDisplaySettings(NULL, ENUM_CURRENT_SETTINGS, &dm); | |||||
dm.dmFields = DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT | DM_DISPLAYFREQUENCY; | |||||
if (fullscreen && !m_data->m_oldScreenWidth) | |||||
{ | |||||
m_data->m_oldScreenWidth = dm.dmPelsWidth; | |||||
m_data->m_oldHeight = dm.dmPelsHeight; | |||||
m_data->m_oldBitsPerPel = dm.dmBitsPerPel; | |||||
if (width && height) | |||||
{ | |||||
dm.dmPelsWidth = width; | |||||
dm.dmPelsHeight = height; | |||||
} else | |||||
{ | |||||
dm.dmPelsWidth = m_data->m_fullWindowWidth; | |||||
dm.dmPelsHeight = m_data->m_fullWindowHeight; | |||||
} | |||||
if (colorBitsPerPixel) | |||||
{ | |||||
dm.dmBitsPerPel = colorBitsPerPixel; | |||||
} | |||||
} else | |||||
{ | |||||
if (m_data->m_oldScreenWidth) | |||||
{ | |||||
dm.dmPelsWidth = m_data->m_oldScreenWidth; | |||||
dm.dmPelsHeight= m_data->m_oldHeight; | |||||
dm.dmBitsPerPel = m_data->m_oldBitsPerPel; | |||||
} | |||||
} | |||||
if (fullscreen) | |||||
{ | |||||
res = ChangeDisplaySettings(&dm, CDS_FULLSCREEN); | |||||
if (!res) | |||||
{ | |||||
dm.dmFields = DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT; | |||||
res = ChangeDisplaySettings(&dm, CDS_FULLSCREEN); | |||||
} | |||||
DWORD style = WS_POPUP; | |||||
SetWindowLong(m_data->m_hWnd, GWL_STYLE, style); | |||||
MoveWindow(m_data->m_hWnd, 0, 0, m_data->m_fullWindowWidth, m_data->m_fullWindowHeight, TRUE); | |||||
SetWindowPos(m_data->m_hWnd, NULL,0,0, (int)width, (int)height, | |||||
SWP_FRAMECHANGED |SWP_SHOWWINDOW);//|SWP_NOACTIVATE | SWP_NOCOPYBITS | SWP_NOOWNERZORDER | SWP_NOREPOSITION | SWP_NOZORDER); | |||||
} else | |||||
{ | |||||
res = ChangeDisplaySettings(&dm, 0); | |||||
DWORD style = WS_SYSMENU | WS_BORDER | WS_CAPTION | WS_CLIPCHILDREN | WS_CLIPSIBLINGS | WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_SIZEBOX; | |||||
SetWindowLong(m_data->m_hWnd, GWL_STYLE, style); | |||||
SetWindowPos(m_data->m_hWnd, NULL,0,0, (int)width, (int)height, | |||||
SWP_FRAMECHANGED |SWP_SHOWWINDOW); | |||||
//|SWP_NOACTIVATE | SWP_NOCOPYBITS | SWP_NOOWNERZORDER | SWP_NOREPOSITION | SWP_NOZORDER); | |||||
} | |||||
} | |||||
Win32Window::Win32Window() | |||||
{ | |||||
m_data = new InternalData2(); | |||||
sData = m_data; | |||||
} | |||||
Win32Window::~Win32Window() | |||||
{ | |||||
setKeyboardCallback(0); | |||||
setMouseMoveCallback(0); | |||||
setMouseButtonCallback(0); | |||||
setWheelCallback(0); | |||||
setResizeCallback(0); | |||||
sData = 0; | |||||
delete m_data; | |||||
} | |||||
void Win32Window::setRenderCallback( b3RenderCallback renderCallback) | |||||
{ | |||||
} | |||||
void Win32Window::closeWindow() | |||||
{ | |||||
setKeyboardCallback(0); | |||||
setMouseMoveCallback(0); | |||||
setMouseButtonCallback(0); | |||||
setWheelCallback(0); | |||||
setResizeCallback(0); | |||||
setRenderCallback(0); | |||||
DestroyWindow(this->m_data->m_hWnd); | |||||
} | |||||
void Win32Window::getMouseCoordinates(int& x, int& y) | |||||
{ | |||||
x = m_data->m_mouseXpos; | |||||
y = m_data->m_mouseYpos; | |||||
} | |||||
void Win32Window::runMainLoop() | |||||
{ | |||||
} | |||||
void Win32Window::startRendering() | |||||
{ | |||||
pumpMessage(); | |||||
// glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); //clear buffers | |||||
//glCullFace(GL_BACK); | |||||
//glFrontFace(GL_CCW); | |||||
// glEnable(GL_DEPTH_TEST); | |||||
} | |||||
void Win32Window::renderAllObjects() | |||||
{ | |||||
} | |||||
void Win32Window::endRendering() | |||||
{ | |||||
SwapBuffers( m_data->m_hDC ); | |||||
} | |||||
float Win32Window::getTimeInSeconds() | |||||
{ | |||||
return 0.f; | |||||
} | |||||
void Win32Window::setDebugMessage(int x,int y,const char* message) | |||||
{ | |||||
} | |||||
void Win32Window::setRequestExit() | |||||
{ | |||||
m_data->m_quit = true; | |||||
} | |||||
bool Win32Window::requestedExit() const | |||||
{ | |||||
return m_data->m_quit; | |||||
} | |||||
void Win32Window::setWheelCallback(b3WheelCallback wheelCallback) | |||||
{ | |||||
m_data->m_wheelCallback = wheelCallback; | |||||
} | |||||
void Win32Window::setMouseMoveCallback(b3MouseMoveCallback mouseCallback) | |||||
{ | |||||
m_data->m_mouseMoveCallback = mouseCallback; | |||||
} | |||||
void Win32Window::setMouseButtonCallback(b3MouseButtonCallback mouseCallback) | |||||
{ | |||||
m_data->m_mouseButtonCallback = mouseCallback; | |||||
} | |||||
void Win32Window::setResizeCallback(b3ResizeCallback resizeCallback) | |||||
{ | |||||
m_data->m_resizeCallback = resizeCallback; | |||||
if (m_data->m_resizeCallback) | |||||
(*m_data->m_resizeCallback)(m_data->m_openglViewportWidth,m_data->m_openglViewportHeight); | |||||
} | |||||
void Win32Window::setKeyboardCallback( b3KeyboardCallback keyboardCallback) | |||||
{ | |||||
m_data->m_keyboardCallback = keyboardCallback; | |||||
} | |||||
b3KeyboardCallback Win32Window::getKeyboardCallback() | |||||
{ | |||||
return m_data->m_keyboardCallback; | |||||
} | |||||
b3MouseMoveCallback Win32Window::getMouseMoveCallback() | |||||
{ | |||||
return m_data->m_mouseMoveCallback; | |||||
} | |||||
b3MouseButtonCallback Win32Window::getMouseButtonCallback() | |||||
{ | |||||
return m_data->m_mouseButtonCallback; | |||||
} | |||||
b3ResizeCallback Win32Window::getResizeCallback() | |||||
{ | |||||
return m_data->m_resizeCallback; | |||||
} | |||||
b3WheelCallback Win32Window::getWheelCallback() | |||||
{ | |||||
return m_data->m_wheelCallback; | |||||
} | |||||
#endif | |||||
@ -0,0 +1,86 @@ | |||||
/* | |||||
Copyright (c) 2012 Advanced Micro Devices, Inc. | |||||
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. | |||||
*/ | |||||
//Originally written by Erwin Coumans | |||||
#ifndef _WIN32_WINDOW_H | |||||
#define _WIN32_WINDOW_H | |||||
struct InternalData2; | |||||
#include "CommonWindowInterface.h" | |||||
class Win32Window : public CommonWindowInterface | |||||
{ | |||||
protected: | |||||
struct InternalData2* m_data; | |||||
void pumpMessage(); | |||||
public: | |||||
Win32Window(); | |||||
virtual ~Win32Window(); | |||||
virtual void createWindow(const b3gWindowConstructionInfo& ci); | |||||
virtual void switchFullScreen(bool fullscreen,int width=0,int height=0,int colorBitsPerPixel=0); | |||||
virtual void closeWindow(); | |||||
virtual void runMainLoop(); | |||||
virtual void startRendering(); | |||||
virtual void renderAllObjects(); | |||||
virtual void endRendering(); | |||||
virtual float getTimeInSeconds(); | |||||
virtual void setDebugMessage(int x,int y,const char* message); | |||||
virtual bool requestedExit() const; | |||||
virtual void setRequestExit(); | |||||
virtual void getMouseCoordinates(int& x, int& y); | |||||
virtual void setMouseMoveCallback(b3MouseMoveCallback mouseCallback); | |||||
virtual void setMouseButtonCallback(b3MouseButtonCallback mouseCallback); | |||||
virtual void setResizeCallback(b3ResizeCallback resizeCallback); | |||||
virtual void setWheelCallback(b3WheelCallback wheelCallback); | |||||
virtual void setKeyboardCallback( b3KeyboardCallback keyboardCallback); | |||||
virtual b3MouseMoveCallback getMouseMoveCallback(); | |||||
virtual b3MouseButtonCallback getMouseButtonCallback(); | |||||
virtual b3ResizeCallback getResizeCallback(); | |||||
virtual b3WheelCallback getWheelCallback(); | |||||
virtual b3KeyboardCallback getKeyboardCallback(); | |||||
virtual void setRenderCallback( b3RenderCallback renderCallback); | |||||
virtual void setWindowTitle(const char* title); | |||||
virtual bool isModifierKeyPressed(int key); | |||||
}; | |||||
#endif //_WIN32_WINDOW_H |
@ -0,0 +1,78 @@ | |||||
#ifndef X11_OPENGL_WINDOW_H | |||||
#define X11_OPENGL_WINDOW_H | |||||
#define b3gDefaultOpenGLWindow X11OpenGLWindow | |||||
#include "CommonWindowInterface.h" | |||||
class X11OpenGLWindow : public CommonWindowInterface | |||||
{ | |||||
struct InternalData2* m_data; | |||||
bool m_OpenGLInitialized; | |||||
bool m_requestedExit; | |||||
protected: | |||||
void enableOpenGL(); | |||||
void disableOpenGL(); | |||||
void pumpMessage(); | |||||
int getAsciiCodeFromVirtualKeycode(int orgCode); | |||||
public: | |||||
X11OpenGLWindow(); | |||||
virtual ~X11OpenGLWindow(); | |||||
virtual void createWindow(const b3gWindowConstructionInfo& ci); | |||||
virtual void closeWindow(); | |||||
virtual void startRendering(); | |||||
virtual void renderAllObjects(); | |||||
virtual void endRendering(); | |||||
virtual float getRetinaScale() const {return 1.f;} | |||||
virtual void setAllowRetina(bool /*allowRetina*/) {}; | |||||
virtual void runMainLoop(); | |||||
virtual float getTimeInSeconds(); | |||||
virtual bool requestedExit() const; | |||||
virtual void setRequestExit() ; | |||||
virtual bool isModifierKeyPressed(int key); | |||||
virtual void setMouseMoveCallback(b3MouseMoveCallback mouseCallback); | |||||
virtual void setMouseButtonCallback(b3MouseButtonCallback mouseCallback); | |||||
virtual void setResizeCallback(b3ResizeCallback resizeCallback); | |||||
virtual void setWheelCallback(b3WheelCallback wheelCallback); | |||||
virtual void setKeyboardCallback( b3KeyboardCallback keyboardCallback); | |||||
virtual b3MouseMoveCallback getMouseMoveCallback(); | |||||
virtual b3MouseButtonCallback getMouseButtonCallback(); | |||||
virtual b3ResizeCallback getResizeCallback(); | |||||
virtual b3WheelCallback getWheelCallback(); | |||||
virtual b3KeyboardCallback getKeyboardCallback(); | |||||
virtual void setRenderCallback( b3RenderCallback renderCallback); | |||||
virtual void setWindowTitle(const char* title); | |||||
virtual int getWidth() const; | |||||
virtual int getHeight() const; | |||||
int fileOpenDialog(char* filename, int maxNameLength); | |||||
}; | |||||
#endif | |||||
@ -0,0 +1,56 @@ | |||||
# exrview | |||||
![ScreenShot](screenshots/exrview.png) | |||||
Simple cross-platform OpenEXR viewer using TinyEXR, bullet3's windows library and nukler UI library. | |||||
Currently only EXR image with RGB channels can be displayed. | |||||
## Requirements | |||||
* premake5 | |||||
* Visual Studio 2015(2013 will work) | |||||
* OpenGL 2.x | |||||
* GTK+3(optional and Linux only. Used for file dialog support on Linux) | |||||
## Build on Windows | |||||
$ premake5 vs2015 | |||||
## Build on Linux | |||||
$ premake5 gmake | |||||
If you want nativefiledialog support(File dialog UI), Install GTK+3 then, | |||||
$ premake5 --with-gtk3nfd gmake | |||||
## Build on Mac | |||||
$ premake5 gmake | |||||
## Usage | |||||
$ ./bin/native/Release/exrview input.exr | |||||
## TODO | |||||
* [ ] Retina resolution | |||||
* [ ] Display arbitrary channel EXR | |||||
* [ ] Zoom and pan image | |||||
* [ ] Tone mapping | |||||
## Third party licenses | |||||
`OpenGLWindow` and `CommonInterfaces` is grabbed from bullet3, which is licensed under zlib lince. | |||||
https://github.com/bulletphysics/bullet://github.com/bulletphysics/bullet3 | |||||
`ThirdPartyLibs/Glew/` is licensed under licensed under the Modified BSD License, the Mesa 3-D License (MIT) and the Khronos License (MIT). | |||||
http://glew.sourceforge.net/ | |||||
nuklear is licensed under MIT. | |||||
https://github.com/vurtun/nuklear | |||||
See `ThirdPartyLibs/nativefiledialog/LICENSE` for nativefiledialog license. |
@ -0,0 +1,16 @@ | |||||
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. | |||||
@ -0,0 +1,137 @@ | |||||
# Native File Dialog # | |||||
A tiny, neat C library that portably invokes native file open and save dialogs. Write dialog code once and have it pop up native dialogs on all supported platforms. Avoid linking large dependencies like wxWidgets and qt. | |||||
Features: | |||||
- Lean C API, static library -- no ObjC, no C++, no STL. | |||||
- Zlib licensed. | |||||
- Consistent UTF-8 support on all platforms. | |||||
- Simple universal file filter syntax. | |||||
- Paid support available. | |||||
- Multiple file selection support. | |||||
- 64-bit and 32-bit friendly. | |||||
- GCC, Clang and Visual Studio supported. | |||||
- No third party dependencies. | |||||
- Support for Vista's modern `IFileDialog` on Windows. | |||||
- Support for non-deprecated Cocoa APIs on OS X. | |||||
- GTK+3 dialog on Linux. | |||||
- Tested, works alongside [http://www.libsdl.org](SDL2) on all platforms, for the game developers out there. | |||||
# Example Usage # | |||||
```C | |||||
#include <nfd.h> | |||||
#include <stdio.h> | |||||
#include <stdlib.h> | |||||
int main( void ) | |||||
{ | |||||
nfdchar_t *outPath = NULL; | |||||
nfdresult_t result = NFD_OpenDialog( NULL, NULL, &outPath ); | |||||
if ( result == NFD_OKAY ) { | |||||
puts("Success!"); | |||||
puts(outPath); | |||||
free(outPath); | |||||
} | |||||
else if ( result == NFD_CANCEL ) { | |||||
puts("User pressed cancel."); | |||||
} | |||||
else { | |||||
printf("Error: %s\n", NFD_GetError() ); | |||||
} | |||||
return 0; | |||||
} | |||||
``` | |||||
See [NFD.h](src/include/nfd.h) for more options. | |||||
# Screenshots # | |||||
![Windows 8 rendering an IFileOpenDialog](screens/open_win8.png?raw=true) | |||||
![GTK3 on Linux](screens/open_gtk3.png?raw=true) | |||||
![Cocoa on Yosemite](screens/open_cocoa.png?raw=true) | |||||
## Building ## | |||||
NFD uses [SCons](http://www.scons.org) for cross-platform builds. After installing SCons, build it with: | |||||
cd src | |||||
scons debug=[0,1] | |||||
Alternatively, you can avoid Scons by just including NFD files to your existing project: | |||||
1. Add all header files in `src/` and `src/include` to your project. | |||||
2. Add `src/include` to your include search path or copy it into your existing search path. | |||||
3. Add `src/nfd_common.c` to your project. | |||||
4. Add `src/nfd_<platform>` to your project, where `<platform>` is the NFD backend for the platform you are fixing to build. | |||||
5. On Visual Studio, define `_CRT_SECURE_NO_WARNINGS` to avoid warnings. | |||||
### Compiling Your Programs ### | |||||
1. Add `src/include` to your include search path. | |||||
2. Add `nfd.lib` to the list of list of static libraries to link against. | |||||
3. Add `src/` to the library search path. | |||||
On Linux, you must compile and link against GTK+. Recommend use of `pkg-config --cflags --libs gtk+-3.0`. | |||||
On Mac OS X, add `AppKit` to the list of frameworks. | |||||
On Windows, ensure you are building against `comctl32.lib`. | |||||
## Usage ## | |||||
See `NFD.h` for API calls. See `tests/*.c` for example code. | |||||
See `tests/SConstruct` for a working build script that compiles on all platforms. | |||||
## File Filter Syntax ## | |||||
There is a form of file filtering in every file dialog, but no consistent means of supporting it. NFD provides support for filtering files by groups of extensions, providing its own descriptions (where applicable) for the extensions. | |||||
A wildcard filter is always added to every dialog. | |||||
### Separators ### | |||||
- `;` Begin a new filter. | |||||
- `,` Add a separate type to the filter. | |||||
#### Examples #### | |||||
`txt` The default filter is for text files. There is a wildcard option in a dropdown. | |||||
`png,jpg;psd` The default filter is for png and jpg files. A second filter is available for psd files. There is a wildcard option in a dropdown. | |||||
`NULL` Wildcard only. | |||||
## Iterating Over PathSets ## | |||||
See [test_opendialogmultiple.c](test/test_opendialogmultiple.c). | |||||
# Known Limitations # | |||||
I accept quality code patches, or will resolve these and other matters through support. | |||||
- No support for Windows XP's legacy dialogs such as `GetOpenFileName`. | |||||
- No support for file filter names -- ex: "Image Files" (*.png, *.jpg). Nameless filters are supported, though. | |||||
- No support for selecting folders instead of files. | |||||
- On Linux, GTK+ cannot be uninitialized to save memory. Launching a file dialog costs memory. I am open to accepting an alternative `nfd_zenity.c` implementation which uses Zenity and pipes. | |||||
# Copyright and Credit # | |||||
Copyright © 2014 [Frogtoss Games](http://www.frogtoss.com), Inc. | |||||
File [LICENSE](LICENSE) covers all files in this repo. | |||||
Native File Dialog by Michael Labbe | |||||
<mike@frogtoss.com> | |||||
Tomasz Konojacki for [microutf8](http://puszcza.gnu.org.ua/software/microutf8/) | |||||
## Support ## | |||||
Directed support for this work is available from the original author under a paid agreement. | |||||
[Contact Frogtoss Games](http://www.frogtoss.com/pages/contact.html). |
@ -0,0 +1,99 @@ | |||||
# | |||||
# Native File Dialog | |||||
# | |||||
# Scons build script -- GCC, Clang, Visual Studio | |||||
# Does not build test | |||||
import os | |||||
# target arch is build arch -- extend here for OS cross compiling | |||||
target_os=str(Platform()) | |||||
# Corresponds to TARGET_ARCH set to environ. | |||||
target_arch = ARGUMENTS.get('target_arch', None) | |||||
# visual studio does not import from environment | |||||
if target_os != 'win32': | |||||
IMPORT_FROM_ENV =['CC', 'CXX', 'CFLAGS', 'CXXFLAGS', 'ARFLAGS'] | |||||
else: | |||||
IMPORT_FROM_ENV =[] | |||||
debug = int(ARGUMENTS.get( 'debug', 0 )) | |||||
nfd_files = ['nfd_common.c'] | |||||
# Due to a Scons limitation, TARGET_ARCH cannot be appended to an existing environment. | |||||
if target_arch != None: | |||||
nfd_env = Environment( TARGET_ARCH=target_arch ) | |||||
else: | |||||
nfd_env = Environment() | |||||
# import specific environment variables from the command line, overriding | |||||
# Scons environment defaults | |||||
for env_key in IMPORT_FROM_ENV: | |||||
if env_key in os.environ: | |||||
print "Making %s => %s" % ( env_key, os.environ[env_key] ) | |||||
nfd_env[env_key] = os.environ[env_key] | |||||
# Windows runtime library types | |||||
win_rtl = {'debug': '/MDd', | |||||
'release': '/MD'} | |||||
def set_debug(env): | |||||
if target_os == 'win32': | |||||
env.Append( CCFLAGS=['/Z7', # obj contains full symbols | |||||
win_rtl['debug'] | |||||
]) | |||||
else: | |||||
env.Append( CFLAGS=['-g'] ) | |||||
def set_release(env): | |||||
if target_os == 'win32': | |||||
env.Append( CCFLAGS=[win_rtl['release'], | |||||
'/O2'] ) | |||||
else: | |||||
env.Append( CFLAGS=['-O3'] ) | |||||
def set_warnings(env): | |||||
if target_os == 'win32': | |||||
env.Append( CCFLAGS=['/W3'], | |||||
CPPDEFINES=['_CRT_SECURE_NO_WARNINGS'] ) | |||||
else: | |||||
env.Append( CFLAGS=['-Wall', '-pedantic'] ) | |||||
def get_lib_name(base, is_debug): | |||||
if is_debug: | |||||
return base + '_d' | |||||
else: | |||||
return base | |||||
# Cocoa OS X builds - clang | |||||
if target_os == 'darwin': | |||||
nfd_files.append('nfd_cocoa.m') | |||||
nfd_env.CC='clang -fcolor-diagnostics' | |||||
# Linux GTK+ 3 builds - GCC | |||||
elif target_os == 'posix': | |||||
nfd_files.append('nfd_gtk.c') | |||||
nfd_env.ParseConfig( 'pkg-config --cflags gtk+-3.0' ) | |||||
# Windows builds - Visual Studio | |||||
elif target_os == 'win32': | |||||
nfd_files.append('nfd_win.cpp') | |||||
if debug: | |||||
set_debug(nfd_env) | |||||
else: | |||||
set_release(nfd_env) | |||||
set_warnings(nfd_env) | |||||
nfd_env.Append( CPPPATH=['.','./include'] ) | |||||
nfd_env.StaticLibrary( get_lib_name('nfd', debug), nfd_files ) |
@ -0,0 +1,21 @@ | |||||
/* | |||||
Native File Dialog | |||||
Internal, common across platforms | |||||
http://www.frogtoss.com/labs | |||||
*/ | |||||
#ifndef _NFD_COMMON_H | |||||
#define _NFD_COMMON_H | |||||
#define NFD_MAX_STRLEN 256 | |||||
#define _NFD_UNUSED(x) ((void)x) | |||||
void *NFDi_Malloc( size_t bytes ); | |||||
void NFDi_Free( void *ptr ); | |||||
void NFDi_SetError( const char *msg ); | |||||
void NFDi_SafeStrncpy( char *dst, const char *src, size_t maxCopy ); | |||||
#endif |
@ -0,0 +1,69 @@ | |||||
/* | |||||
Native File Dialog | |||||
User API | |||||
http://www.frogtoss.com/labs | |||||
*/ | |||||
#ifndef _NFD_H | |||||
#define _NFD_H | |||||
#ifdef __cplusplus | |||||
extern "C" { | |||||
#endif | |||||
#include <stddef.h> | |||||
/* denotes UTF-8 char */ | |||||
typedef char nfdchar_t; | |||||
/* opaque data structure -- see NFD_PathSet_* */ | |||||
typedef struct { | |||||
nfdchar_t *buf; | |||||
size_t *indices; /* byte offsets into buf */ | |||||
size_t count; /* number of indices into buf */ | |||||
}nfdpathset_t; | |||||
typedef enum { | |||||
NFD_ERROR, /* programmatic error */ | |||||
NFD_OKAY, /* user pressed okay, or successful return */ | |||||
NFD_CANCEL /* user pressed cancel */ | |||||
}nfdresult_t; | |||||
/* nfd_<targetplatform>.c */ | |||||
/* single file open dialog */ | |||||
nfdresult_t NFD_OpenDialog( const nfdchar_t *filterList, | |||||
const nfdchar_t *defaultPath, | |||||
nfdchar_t **outPath ); | |||||
/* multiple file open dialog */ | |||||
nfdresult_t NFD_OpenDialogMultiple( const nfdchar_t *filterList, | |||||
const nfdchar_t *defaultPath, | |||||
nfdpathset_t *outPaths ); | |||||
/* save dialog */ | |||||
nfdresult_t NFD_SaveDialog( const nfdchar_t *filterList, | |||||
const nfdchar_t *defaultPath, | |||||
nfdchar_t **outPath ); | |||||
/* nfd_common.c */ | |||||
/* get last error -- set when nfdresult_t returns NFD_ERROR */ | |||||
const char *NFD_GetError( void ); | |||||
/* get the number of entries stored in pathSet */ | |||||
size_t NFD_PathSet_GetCount( const nfdpathset_t *pathSet ); | |||||
/* Get the UTF-8 path at offset index */ | |||||
nfdchar_t *NFD_PathSet_GetPath( const nfdpathset_t *pathSet, size_t index ); | |||||
/* Free the pathSet */ | |||||
void NFD_PathSet_Free( nfdpathset_t *pathSet ); | |||||
#ifdef __cplusplus | |||||
} | |||||
#endif | |||||
#endif |
@ -0,0 +1,235 @@ | |||||
/* | |||||
Native File Dialog | |||||
http://www.frogtoss.com/labs | |||||
*/ | |||||
#include <AppKit/AppKit.h> | |||||
#include "nfd.h" | |||||
#include "nfd_common.h" | |||||
static NSArray *BuildAllowedFileTypes( const char *filterList ) | |||||
{ | |||||
// Commas and semicolons are the same thing on this platform | |||||
NSMutableArray *buildFilterList = [[NSMutableArray alloc] init]; | |||||
char typebuf[NFD_MAX_STRLEN] = {0}; | |||||
size_t filterListLen = strlen(filterList); | |||||
char *p_typebuf = typebuf; | |||||
for ( size_t i = 0; i < filterListLen+1; ++i ) | |||||
{ | |||||
if ( filterList[i] == ',' || filterList[i] == ';' || filterList[i] == '\0' ) | |||||
{ | |||||
++p_typebuf; | |||||
*p_typebuf = '\0'; | |||||
NSString *thisType = [NSString stringWithUTF8String: typebuf]; | |||||
[buildFilterList addObject:thisType]; | |||||
p_typebuf = typebuf; | |||||
*p_typebuf = '\0'; | |||||
} | |||||
else | |||||
{ | |||||
*p_typebuf = filterList[i]; | |||||
++p_typebuf; | |||||
} | |||||
} | |||||
NSArray *returnArray = [NSArray arrayWithArray:buildFilterList]; | |||||
[buildFilterList release]; | |||||
return returnArray; | |||||
} | |||||
static void AddFilterListToDialog( NSSavePanel *dialog, const char *filterList ) | |||||
{ | |||||
if ( !filterList || strlen(filterList) == 0 ) | |||||
return; | |||||
NSArray *allowedFileTypes = BuildAllowedFileTypes( filterList ); | |||||
if ( [allowedFileTypes count] != 0 ) | |||||
{ | |||||
[dialog setAllowedFileTypes:allowedFileTypes]; | |||||
} | |||||
} | |||||
static void SetDefaultPath( NSSavePanel *dialog, const nfdchar_t *defaultPath ) | |||||
{ | |||||
if ( !defaultPath || strlen(defaultPath) == 0 ) | |||||
return; | |||||
NSString *defaultPathString = [NSString stringWithUTF8String: defaultPath]; | |||||
NSURL *url = [NSURL fileURLWithPath:defaultPathString isDirectory:YES]; | |||||
[dialog setDirectoryURL:url]; | |||||
} | |||||
/* fixme: pathset should be pathSet */ | |||||
static nfdresult_t AllocPathSet( NSArray *urls, nfdpathset_t *pathset ) | |||||
{ | |||||
assert(pathset); | |||||
assert([urls count]); | |||||
pathset->count = (size_t)[urls count]; | |||||
pathset->indices = NFDi_Malloc( sizeof(size_t)*pathset->count ); | |||||
if ( !pathset->indices ) | |||||
{ | |||||
return NFD_ERROR; | |||||
} | |||||
// count the total space needed for buf | |||||
size_t bufsize = 0; | |||||
for ( NSURL *url in urls ) | |||||
{ | |||||
NSString *path = [url path]; | |||||
bufsize += [path lengthOfBytesUsingEncoding:NSUTF8StringEncoding] + 1; | |||||
} | |||||
pathset->buf = NFDi_Malloc( sizeof(nfdchar_t) * bufsize ); | |||||
if ( !pathset->buf ) | |||||
{ | |||||
return NFD_ERROR; | |||||
} | |||||
// fill buf | |||||
nfdchar_t *p_buf = pathset->buf; | |||||
size_t count = 0; | |||||
for ( NSURL *url in urls ) | |||||
{ | |||||
NSString *path = [url path]; | |||||
const nfdchar_t *utf8Path = [path UTF8String]; | |||||
size_t byteLen = [path lengthOfBytesUsingEncoding:NSUTF8StringEncoding] + 1; | |||||
memcpy( p_buf, utf8Path, byteLen ); | |||||
ptrdiff_t index = p_buf - pathset->buf; | |||||
assert( index >= 0 ); | |||||
pathset->indices[count] = (size_t)index; | |||||
p_buf += byteLen; | |||||
++count; | |||||
} | |||||
return NFD_OKAY; | |||||
} | |||||
/* public */ | |||||
nfdresult_t NFD_OpenDialog( const char *filterList, | |||||
const nfdchar_t *defaultPath, | |||||
nfdchar_t **outPath ) | |||||
{ | |||||
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; | |||||
NSOpenPanel *dialog = [NSOpenPanel openPanel]; | |||||
[dialog setAllowsMultipleSelection:NO]; | |||||
// Build the filter list | |||||
AddFilterListToDialog(dialog, filterList); | |||||
// Set the starting directory | |||||
SetDefaultPath(dialog, defaultPath); | |||||
nfdresult_t nfdResult = NFD_CANCEL; | |||||
if ( [dialog runModal] == NSModalResponseOK ) | |||||
{ | |||||
NSURL *url = [dialog URL]; | |||||
const char *utf8Path = [[url path] UTF8String]; | |||||
// byte count, not char count | |||||
size_t len = strlen(utf8Path);//NFDi_UTF8_Strlen(utf8Path); | |||||
*outPath = NFDi_Malloc( len+1 ); | |||||
if ( !*outPath ) | |||||
{ | |||||
[pool release]; | |||||
return NFD_ERROR; | |||||
} | |||||
memcpy( *outPath, utf8Path, len+1 ); /* copy null term */ | |||||
nfdResult = NFD_OKAY; | |||||
} | |||||
[pool release]; | |||||
return nfdResult; | |||||
} | |||||
nfdresult_t NFD_OpenDialogMultiple( const nfdchar_t *filterList, | |||||
const nfdchar_t *defaultPath, | |||||
nfdpathset_t *outPaths ) | |||||
{ | |||||
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; | |||||
NSOpenPanel *dialog = [NSOpenPanel openPanel]; | |||||
[dialog setAllowsMultipleSelection:YES]; | |||||
// Build the fiter list. | |||||
AddFilterListToDialog(dialog, filterList); | |||||
// Set the starting directory | |||||
SetDefaultPath(dialog, defaultPath); | |||||
nfdresult_t nfdResult = NFD_CANCEL; | |||||
if ( [dialog runModal] == NSModalResponseOK ) | |||||
{ | |||||
NSArray *urls = [dialog URLs]; | |||||
if ( [urls count] == 0 ) | |||||
{ | |||||
[pool release]; | |||||
return NFD_CANCEL; | |||||
} | |||||
if ( AllocPathSet( urls, outPaths ) == NFD_ERROR ) | |||||
{ | |||||
[pool release]; | |||||
return NFD_ERROR; | |||||
} | |||||
nfdResult = NFD_OKAY; | |||||
} | |||||
[pool release]; | |||||
return nfdResult; | |||||
} | |||||
nfdresult_t NFD_SaveDialog( const nfdchar_t *filterList, | |||||
const nfdchar_t *defaultPath, | |||||
nfdchar_t **outPath ) | |||||
{ | |||||
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; | |||||
NSSavePanel *dialog = [NSSavePanel savePanel]; | |||||
[dialog setExtensionHidden:NO]; | |||||
// Build the filter list. | |||||
AddFilterListToDialog(dialog, filterList); | |||||
// Set the starting directory | |||||
SetDefaultPath(dialog, defaultPath); | |||||
nfdresult_t nfdResult = NFD_CANCEL; | |||||
if ( [dialog runModal] == NSModalResponseOK ) | |||||
{ | |||||
NSURL *url = [dialog URL]; | |||||
const char *utf8Path = [[url path] UTF8String]; | |||||
size_t byteLen = [url.path lengthOfBytesUsingEncoding:NSUTF8StringEncoding] + 1; | |||||
*outPath = NFDi_Malloc( byteLen ); | |||||
if ( !*outPath ) | |||||
{ | |||||
[pool release]; | |||||
return NFD_ERROR; | |||||
} | |||||
memcpy( *outPath, utf8Path, byteLen ); | |||||
nfdResult = NFD_OKAY; | |||||
} | |||||
[pool release]; | |||||
return nfdResult; | |||||
} |
@ -0,0 +1,142 @@ | |||||
/* | |||||
Native File Dialog | |||||
http://www.frogtoss.com/labs | |||||
*/ | |||||
#include <stdlib.h> | |||||
#include <assert.h> | |||||
#include <string.h> | |||||
#include "nfd_common.h" | |||||
static char g_errorstr[NFD_MAX_STRLEN] = {0}; | |||||
/* public routines */ | |||||
const char *NFD_GetError( void ) | |||||
{ | |||||
return g_errorstr; | |||||
} | |||||
size_t NFD_PathSet_GetCount( const nfdpathset_t *pathset ) | |||||
{ | |||||
assert(pathset); | |||||
return pathset->count; | |||||
} | |||||
nfdchar_t *NFD_PathSet_GetPath( const nfdpathset_t *pathset, size_t num ) | |||||
{ | |||||
assert(pathset); | |||||
assert(num < pathset->count); | |||||
return pathset->buf + pathset->indices[num]; | |||||
} | |||||
void NFD_PathSet_Free( nfdpathset_t *pathset ) | |||||
{ | |||||
assert(pathset); | |||||
NFDi_Free( pathset->indices ); | |||||
NFDi_Free( pathset->buf ); | |||||
} | |||||
/* internal routines */ | |||||
void *NFDi_Malloc( size_t bytes ) | |||||
{ | |||||
void *ptr = malloc(bytes); | |||||
if ( !ptr ) | |||||
NFDi_SetError("NFDi_Malloc failed."); | |||||
return ptr; | |||||
} | |||||
void NFDi_Free( void *ptr ) | |||||
{ | |||||
assert(ptr); | |||||
free(ptr); | |||||
} | |||||
void NFDi_SetError( const char *msg ) | |||||
{ | |||||
int bTruncate = NFDi_SafeStrncpy( g_errorstr, msg, NFD_MAX_STRLEN ); | |||||
assert( !bTruncate ); _NFD_UNUSED(bTruncate); | |||||
} | |||||
int NFDi_SafeStrncpy( char *dst, const char *src, size_t maxCopy ) | |||||
{ | |||||
size_t n = maxCopy; | |||||
char *d = dst; | |||||
assert( src ); | |||||
assert( dst ); | |||||
while ( n > 0 && *src != '\0' ) | |||||
{ | |||||
*d++ = *src++; | |||||
--n; | |||||
} | |||||
/* Truncation case - | |||||
terminate string and return true */ | |||||
if ( n == 0 ) | |||||
{ | |||||
dst[maxCopy-1] = '\0'; | |||||
return 1; | |||||
} | |||||
/* No truncation. Append a single NULL and return. */ | |||||
*d = '\0'; | |||||
return 0; | |||||
} | |||||
/* adapted from microutf8 */ | |||||
size_t NFDi_UTF8_Strlen( const nfdchar_t *str ) | |||||
{ | |||||
/* This function doesn't properly check validity of UTF-8 character | |||||
sequence, it is supposed to use only with valid UTF-8 strings. */ | |||||
size_t character_count = 0; | |||||
size_t i = 0; /* Counter used to iterate over string. */ | |||||
nfdchar_t maybe_bom[4]; | |||||
/* If there is UTF-8 BOM ignore it. */ | |||||
if (strlen(str) > 2) | |||||
{ | |||||
strncpy(maybe_bom, str, 3); | |||||
maybe_bom[3] = 0; | |||||
if (strcmp(maybe_bom, (nfdchar_t*)NFD_UTF8_BOM) == 0) | |||||
i += 3; | |||||
} | |||||
while(str[i]) | |||||
{ | |||||
if (str[i] >> 7 == 0) | |||||
{ | |||||
/* If bit pattern begins with 0 we have ascii character. */ | |||||
++character_count; | |||||
} | |||||
else if (str[i] >> 6 == 3) | |||||
{ | |||||
/* If bit pattern begins with 11 it is beginning of UTF-8 byte sequence. */ | |||||
++character_count; | |||||
} | |||||
else if (str[i] >> 6 == 2) | |||||
; /* If bit pattern begins with 10 it is middle of utf-8 byte sequence. */ | |||||
else | |||||
{ | |||||
/* In any other case this is not valid UTF-8. */ | |||||
return -1; | |||||
} | |||||
++i; | |||||
} | |||||
return character_count; | |||||
} | |||||
int NFDi_IsFilterSegmentChar( char ch ) | |||||
{ | |||||
return (ch==','||ch==';'||ch=='\0'); | |||||
} | |||||
@ -0,0 +1,37 @@ | |||||
/* | |||||
Native File Dialog | |||||
Internal, common across platforms | |||||
http://www.frogtoss.com/labs | |||||
*/ | |||||
#ifndef _NFD_COMMON_H | |||||
#define _NFD_COMMON_H | |||||
#include "nfd.h" | |||||
#ifdef __cplusplus | |||||
extern "C" { | |||||
#endif | |||||
#define NFD_MAX_STRLEN 256 | |||||
#define _NFD_UNUSED(x) ((void)x) | |||||
#define NFD_UTF8_BOM "\xEF\xBB\xBF" | |||||
void *NFDi_Malloc( size_t bytes ); | |||||
void NFDi_Free( void *ptr ); | |||||
void NFDi_SetError( const char *msg ); | |||||
int NFDi_SafeStrncpy( char *dst, const char *src, size_t maxCopy ); | |||||
size_t NFDi_UTF8_Strlen( const nfdchar_t *str ); | |||||
int NFDi_IsFilterSegmentChar( char ch ); | |||||
#ifdef __cplusplus | |||||
} | |||||
#endif | |||||
#endif |
@ -0,0 +1,326 @@ | |||||
/* | |||||
Native File Dialog | |||||
http://www.frogtoss.com/labs | |||||
*/ | |||||
#include <stdio.h> | |||||
#include <assert.h> | |||||
#include <string.h> | |||||
#include <gtk/gtk.h> | |||||
#include "nfd.h" | |||||
#include "nfd_common.h" | |||||
const char INIT_FAIL_MSG[] = "gtk_init_check failed to initilaize GTK+"; | |||||
static void AddTypeToFilterName( const char *typebuf, char *filterName, size_t bufsize ) | |||||
{ | |||||
const char SEP[] = ", "; | |||||
size_t len = strlen(filterName); | |||||
if ( len != 0 ) | |||||
{ | |||||
strncat( filterName, SEP, bufsize - len - 1 ); | |||||
len += strlen(SEP); | |||||
} | |||||
strncat( filterName, typebuf, bufsize - len - 1 ); | |||||
} | |||||
static void AddFiltersToDialog( GtkWidget *dialog, const char *filterList ) | |||||
{ | |||||
GtkFileFilter *filter; | |||||
char typebuf[NFD_MAX_STRLEN] = {0}; | |||||
const char *p_filterList = filterList; | |||||
char *p_typebuf = typebuf; | |||||
char filterName[NFD_MAX_STRLEN] = {0}; | |||||
if ( !filterList || strlen(filterList) == 0 ) | |||||
return; | |||||
filter = gtk_file_filter_new(); | |||||
while ( 1 ) | |||||
{ | |||||
if ( NFDi_IsFilterSegmentChar(*p_filterList) ) | |||||
{ | |||||
char typebufWildcard[NFD_MAX_STRLEN]; | |||||
/* add another type to the filter */ | |||||
assert( strlen(typebuf) > 0 ); | |||||
assert( strlen(typebuf) < NFD_MAX_STRLEN-1 ); | |||||
snprintf( typebufWildcard, NFD_MAX_STRLEN, "*.%s", typebuf ); | |||||
AddTypeToFilterName( typebuf, filterName, NFD_MAX_STRLEN ); | |||||
gtk_file_filter_add_pattern( filter, typebufWildcard ); | |||||
p_typebuf = typebuf; | |||||
memset( typebuf, 0, sizeof(char) * NFD_MAX_STRLEN ); | |||||
} | |||||
if ( *p_filterList == ';' || *p_filterList == '\0' ) | |||||
{ | |||||
/* end of filter -- add it to the dialog */ | |||||
gtk_file_filter_set_name( filter, filterName ); | |||||
gtk_file_chooser_add_filter( GTK_FILE_CHOOSER(dialog), filter ); | |||||
filterName[0] = '\0'; | |||||
if ( *p_filterList == '\0' ) | |||||
break; | |||||
filter = gtk_file_filter_new(); | |||||
} | |||||
if ( !NFDi_IsFilterSegmentChar( *p_filterList ) ) | |||||
{ | |||||
*p_typebuf = *p_filterList; | |||||
p_typebuf++; | |||||
} | |||||
p_filterList++; | |||||
} | |||||
/* always append a wildcard option to the end*/ | |||||
filter = gtk_file_filter_new(); | |||||
gtk_file_filter_set_name( filter, "*.*" ); | |||||
gtk_file_filter_add_pattern( filter, "*" ); | |||||
gtk_file_chooser_add_filter( GTK_FILE_CHOOSER(dialog), filter ); | |||||
} | |||||
static void SetDefaultPath( GtkWidget *dialog, const char *defaultPath ) | |||||
{ | |||||
if ( !defaultPath || strlen(defaultPath) == 0 ) | |||||
return; | |||||
/* GTK+ manual recommends not specifically setting the default path. | |||||
We do it anyway in order to be consistent across platforms. | |||||
If consistency with the native OS is preferred, this is the line | |||||
to comment out. -ml */ | |||||
gtk_file_chooser_set_current_folder( GTK_FILE_CHOOSER(dialog), defaultPath ); | |||||
} | |||||
static nfdresult_t AllocPathSet( GSList *fileList, nfdpathset_t *pathSet ) | |||||
{ | |||||
size_t bufSize = 0; | |||||
GSList *node; | |||||
nfdchar_t *p_buf; | |||||
size_t count = 0; | |||||
assert(fileList); | |||||
assert(pathSet); | |||||
pathSet->count = (size_t)g_slist_length( fileList ); | |||||
assert( pathSet->count > 0 ); | |||||
pathSet->indices = NFDi_Malloc( sizeof(size_t)*pathSet->count ); | |||||
if ( !pathSet->indices ) | |||||
{ | |||||
return NFD_ERROR; | |||||
} | |||||
/* count the total space needed for buf */ | |||||
for ( node = fileList; node; node = node->next ) | |||||
{ | |||||
assert(node->data); | |||||
bufSize += strlen( (const gchar*)node->data ) + 1; | |||||
} | |||||
pathSet->buf = NFDi_Malloc( sizeof(nfdchar_t) * bufSize ); | |||||
/* fill buf */ | |||||
p_buf = pathSet->buf; | |||||
for ( node = fileList; node; node = node->next ) | |||||
{ | |||||
nfdchar_t *path = (nfdchar_t*)(node->data); | |||||
size_t byteLen = strlen(path)+1; | |||||
ptrdiff_t index; | |||||
memcpy( p_buf, path, byteLen ); | |||||
g_free(node->data); | |||||
index = p_buf - pathSet->buf; | |||||
assert( index >= 0 ); | |||||
pathSet->indices[count] = (size_t)index; | |||||
p_buf += byteLen; | |||||
++count; | |||||
} | |||||
g_slist_free( fileList ); | |||||
return NFD_OKAY; | |||||
} | |||||
static void WaitForCleanup(void) | |||||
{ | |||||
while (gtk_events_pending()) | |||||
gtk_main_iteration(); | |||||
} | |||||
/* public */ | |||||
nfdresult_t NFD_OpenDialog( const char *filterList, | |||||
const nfdchar_t *defaultPath, | |||||
nfdchar_t **outPath ) | |||||
{ | |||||
GtkWidget *dialog; | |||||
nfdresult_t result; | |||||
if ( !gtk_init_check( NULL, NULL ) ) | |||||
{ | |||||
NFDi_SetError(INIT_FAIL_MSG); | |||||
return NFD_ERROR; | |||||
} | |||||
dialog = gtk_file_chooser_dialog_new( "Open File", | |||||
NULL, | |||||
GTK_FILE_CHOOSER_ACTION_OPEN, | |||||
"_Cancel", GTK_RESPONSE_CANCEL, | |||||
"_Open", GTK_RESPONSE_ACCEPT, | |||||
NULL ); | |||||
/* Build the filter list */ | |||||
AddFiltersToDialog(dialog, filterList); | |||||
/* Set the default path */ | |||||
SetDefaultPath(dialog, defaultPath); | |||||
result = NFD_CANCEL; | |||||
if ( gtk_dialog_run( GTK_DIALOG(dialog) ) == GTK_RESPONSE_ACCEPT ) | |||||
{ | |||||
char *filename; | |||||
filename = gtk_file_chooser_get_filename( GTK_FILE_CHOOSER(dialog) ); | |||||
{ | |||||
size_t len = strlen(filename); | |||||
*outPath = NFDi_Malloc( len + 1 ); | |||||
memcpy( *outPath, filename, len + 1 ); | |||||
if ( !*outPath ) | |||||
{ | |||||
g_free( filename ); | |||||
gtk_widget_destroy(dialog); | |||||
return NFD_ERROR; | |||||
} | |||||
} | |||||
g_free( filename ); | |||||
result = NFD_OKAY; | |||||
} | |||||
WaitForCleanup(); | |||||
gtk_widget_destroy(dialog); | |||||
WaitForCleanup(); | |||||
return result; | |||||
} | |||||
nfdresult_t NFD_OpenDialogMultiple( const nfdchar_t *filterList, | |||||
const nfdchar_t *defaultPath, | |||||
nfdpathset_t *outPaths ) | |||||
{ | |||||
GtkWidget *dialog; | |||||
nfdresult_t result; | |||||
if ( !gtk_init_check( NULL, NULL ) ) | |||||
{ | |||||
NFDi_SetError(INIT_FAIL_MSG); | |||||
return NFD_ERROR; | |||||
} | |||||
dialog = gtk_file_chooser_dialog_new( "Open Files", | |||||
NULL, | |||||
GTK_FILE_CHOOSER_ACTION_OPEN, | |||||
"_Cancel", GTK_RESPONSE_CANCEL, | |||||
"_Open", GTK_RESPONSE_ACCEPT, | |||||
NULL ); | |||||
gtk_file_chooser_set_select_multiple( GTK_FILE_CHOOSER(dialog), TRUE ); | |||||
/* Build the filter list */ | |||||
AddFiltersToDialog(dialog, filterList); | |||||
/* Set the default path */ | |||||
SetDefaultPath(dialog, defaultPath); | |||||
result = NFD_CANCEL; | |||||
if ( gtk_dialog_run( GTK_DIALOG(dialog) ) == GTK_RESPONSE_ACCEPT ) | |||||
{ | |||||
GSList *fileList = gtk_file_chooser_get_filenames( GTK_FILE_CHOOSER(dialog) ); | |||||
if ( AllocPathSet( fileList, outPaths ) == NFD_ERROR ) | |||||
{ | |||||
gtk_widget_destroy(dialog); | |||||
return NFD_ERROR; | |||||
} | |||||
result = NFD_OKAY; | |||||
} | |||||
WaitForCleanup(); | |||||
gtk_widget_destroy(dialog); | |||||
WaitForCleanup(); | |||||
return result; | |||||
} | |||||
nfdresult_t NFD_SaveDialog( const nfdchar_t *filterList, | |||||
const nfdchar_t *defaultPath, | |||||
nfdchar_t **outPath ) | |||||
{ | |||||
GtkWidget *dialog; | |||||
nfdresult_t result; | |||||
if ( !gtk_init_check( NULL, NULL ) ) | |||||
{ | |||||
NFDi_SetError(INIT_FAIL_MSG); | |||||
return NFD_ERROR; | |||||
} | |||||
dialog = gtk_file_chooser_dialog_new( "Save File", | |||||
NULL, | |||||
GTK_FILE_CHOOSER_ACTION_SAVE, | |||||
"_Cancel", GTK_RESPONSE_CANCEL, | |||||
"_Save", GTK_RESPONSE_ACCEPT, | |||||
NULL ); | |||||
gtk_file_chooser_set_do_overwrite_confirmation( GTK_FILE_CHOOSER(dialog), TRUE ); | |||||
/* Build the filter list */ | |||||
AddFiltersToDialog(dialog, filterList); | |||||
/* Set the default path */ | |||||
SetDefaultPath(dialog, defaultPath); | |||||
result = NFD_CANCEL; | |||||
if ( gtk_dialog_run( GTK_DIALOG(dialog) ) == GTK_RESPONSE_ACCEPT ) | |||||
{ | |||||
char *filename; | |||||
filename = gtk_file_chooser_get_filename( GTK_FILE_CHOOSER(dialog) ); | |||||
{ | |||||
size_t len = strlen(filename); | |||||
*outPath = NFDi_Malloc( len + 1 ); | |||||
memcpy( *outPath, filename, len + 1 ); | |||||
if ( !*outPath ) | |||||
{ | |||||
g_free( filename ); | |||||
gtk_widget_destroy(dialog); | |||||
return NFD_ERROR; | |||||
} | |||||
} | |||||
g_free(filename); | |||||
result = NFD_OKAY; | |||||
} | |||||
WaitForCleanup(); | |||||
gtk_widget_destroy(dialog); | |||||
WaitForCleanup(); | |||||
return result; | |||||
} |
@ -0,0 +1,619 @@ | |||||
/* | |||||
Native File Dialog | |||||
http://www.frogtoss.com/labs | |||||
*/ | |||||
/* only locally define UNICODE in this compilation unit */ | |||||
#ifndef UNICODE | |||||
#define UNICODE | |||||
#endif | |||||
#include <wchar.h> | |||||
#include <stdio.h> | |||||
#include <assert.h> | |||||
#include <atlbase.h> | |||||
#include <windows.h> | |||||
#include <ShObjIdl.h> | |||||
#include "nfd_common.h" | |||||
// allocs the space in outPath -- call free() | |||||
static void CopyWCharToNFDChar( const wchar_t *inStr, nfdchar_t **outStr ) | |||||
{ | |||||
int inStrCharacterCount = static_cast<int>(wcslen(inStr)); | |||||
int bytesNeeded = WideCharToMultiByte( CP_UTF8, 0, | |||||
inStr, inStrCharacterCount, | |||||
NULL, 0, NULL, NULL ); | |||||
assert( bytesNeeded ); | |||||
bytesNeeded += 1; | |||||
*outStr = (nfdchar_t*)NFDi_Malloc( bytesNeeded ); | |||||
if ( !*outStr ) | |||||
return; | |||||
int bytesWritten = WideCharToMultiByte( CP_UTF8, 0, | |||||
inStr, -1, | |||||
*outStr, bytesNeeded, | |||||
NULL, NULL ); | |||||
assert( bytesWritten ); _NFD_UNUSED( bytesWritten ); | |||||
} | |||||
/* includes NULL terminator byte in return */ | |||||
static size_t GetUTF8ByteCountForWChar( const wchar_t *str ) | |||||
{ | |||||
int bytesNeeded = WideCharToMultiByte( CP_UTF8, 0, | |||||
str, -1, | |||||
NULL, 0, NULL, NULL ); | |||||
assert( bytesNeeded ); | |||||
return bytesNeeded+1; | |||||
} | |||||
// write to outPtr -- no free() necessary. No memory stomp tests are done -- they must be done | |||||
// before entering this function. | |||||
static int CopyWCharToExistingNFDCharBuffer( const wchar_t *inStr, nfdchar_t *outPtr ) | |||||
{ | |||||
int inStrCharacterCount = static_cast<int>(wcslen(inStr)); | |||||
int bytesNeeded = static_cast<int>(GetUTF8ByteCountForWChar( inStr )); | |||||
/* invocation copies null term */ | |||||
int bytesWritten = WideCharToMultiByte( CP_UTF8, 0, | |||||
inStr, -1, | |||||
outPtr, bytesNeeded, | |||||
NULL, 0 ); | |||||
assert( bytesWritten ); | |||||
return bytesWritten; | |||||
} | |||||
// allocs the space in outStr -- call free() | |||||
static void CopyNFDCharToWChar( const nfdchar_t *inStr, wchar_t **outStr ) | |||||
{ | |||||
int inStrByteCount = static_cast<int>(strlen(inStr)); | |||||
int charsNeeded = MultiByteToWideChar(CP_UTF8, 0, | |||||
inStr, inStrByteCount, | |||||
NULL, 0 ); | |||||
assert( charsNeeded ); | |||||
assert( !*outStr ); | |||||
charsNeeded += 1; // terminator | |||||
*outStr = (wchar_t*)NFDi_Malloc( charsNeeded * sizeof(wchar_t) ); | |||||
if ( !*outStr ) | |||||
return; | |||||
int ret = MultiByteToWideChar(CP_UTF8, 0, | |||||
inStr, inStrByteCount, | |||||
*outStr, charsNeeded); | |||||
(*outStr)[charsNeeded-1] = '\0'; | |||||
#ifdef _DEBUG | |||||
int inStrCharacterCount = static_cast<int>(NFDi_UTF8_Strlen(inStr)); | |||||
assert( ret == inStrCharacterCount ); | |||||
#else | |||||
_NFD_UNUSED(ret); | |||||
#endif | |||||
} | |||||
/* ext is in format "jpg", no wildcards or separators */ | |||||
static int AppendExtensionToSpecBuf( const char *ext, char *specBuf, size_t specBufLen ) | |||||
{ | |||||
const char SEP[] = ";"; | |||||
assert( specBufLen > strlen(ext)+3 ); | |||||
if ( strlen(specBuf) > 0 ) | |||||
{ | |||||
strncat( specBuf, SEP, specBufLen - strlen(specBuf) - 1 ); | |||||
specBufLen += strlen(SEP); | |||||
} | |||||
char extWildcard[NFD_MAX_STRLEN]; | |||||
int bytesWritten = sprintf_s( extWildcard, NFD_MAX_STRLEN, "*.%s", ext ); | |||||
assert( bytesWritten == strlen(ext)+2 ); | |||||
strncat( specBuf, extWildcard, specBufLen - strlen(specBuf) - 1 ); | |||||
return NFD_OKAY; | |||||
} | |||||
static nfdresult_t AddFiltersToDialog( ::IFileDialog *fileOpenDialog, const char *filterList ) | |||||
{ | |||||
const wchar_t EMPTY_WSTR[] = L""; | |||||
const wchar_t WILDCARD[] = L"*.*"; | |||||
if ( !filterList || strlen(filterList) == 0 ) | |||||
return NFD_OKAY; | |||||
// Count rows to alloc | |||||
UINT filterCount = 1; /* guaranteed to have one filter on a correct, non-empty parse */ | |||||
const char *p_filterList; | |||||
for ( p_filterList = filterList; *p_filterList; ++p_filterList ) | |||||
{ | |||||
if ( *p_filterList == ';' ) | |||||
++filterCount; | |||||
} | |||||
assert(filterCount); | |||||
if ( !filterCount ) | |||||
{ | |||||
NFDi_SetError("Error parsing filters."); | |||||
return NFD_ERROR; | |||||
} | |||||
/* filterCount plus 1 because we hardcode the *.* wildcard after the while loop */ | |||||
COMDLG_FILTERSPEC *specList = (COMDLG_FILTERSPEC*)NFDi_Malloc( sizeof(COMDLG_FILTERSPEC) * (filterCount + 1) ); | |||||
if ( !specList ) | |||||
{ | |||||
return NFD_ERROR; | |||||
} | |||||
for (size_t i = 0; i < filterCount+1; ++i ) | |||||
{ | |||||
specList[i].pszName = NULL; | |||||
specList[i].pszSpec = NULL; | |||||
} | |||||
size_t specIdx = 0; | |||||
p_filterList = filterList; | |||||
char typebuf[NFD_MAX_STRLEN] = {0}; /* one per comma or semicolon */ | |||||
char *p_typebuf = typebuf; | |||||
char filterName[NFD_MAX_STRLEN] = {0}; | |||||
char specbuf[NFD_MAX_STRLEN] = {0}; /* one per semicolon */ | |||||
while ( 1 ) | |||||
{ | |||||
if ( NFDi_IsFilterSegmentChar(*p_filterList) ) | |||||
{ | |||||
/* append a type to the specbuf (pending filter) */ | |||||
AppendExtensionToSpecBuf( typebuf, specbuf, NFD_MAX_STRLEN ); | |||||
p_typebuf = typebuf; | |||||
memset( typebuf, 0, sizeof(char)*NFD_MAX_STRLEN ); | |||||
} | |||||
if ( *p_filterList == ';' || *p_filterList == '\0' ) | |||||
{ | |||||
/* end of filter -- add it to specList */ | |||||
// Empty filter name -- Windows describes them by extension. | |||||
specList[specIdx].pszName = EMPTY_WSTR; | |||||
CopyNFDCharToWChar( specbuf, (wchar_t**)&specList[specIdx].pszSpec ); | |||||
memset( specbuf, 0, sizeof(char)*NFD_MAX_STRLEN ); | |||||
++specIdx; | |||||
if ( specIdx == filterCount ) | |||||
break; | |||||
} | |||||
if ( !NFDi_IsFilterSegmentChar( *p_filterList )) | |||||
{ | |||||
*p_typebuf = *p_filterList; | |||||
++p_typebuf; | |||||
} | |||||
++p_filterList; | |||||
} | |||||
/* Add wildcard */ | |||||
specList[specIdx].pszSpec = WILDCARD; | |||||
specList[specIdx].pszName = EMPTY_WSTR; | |||||
fileOpenDialog->SetFileTypes( filterCount+1, specList ); | |||||
/* free speclist */ | |||||
for ( size_t i = 0; i < filterCount; ++i ) | |||||
{ | |||||
NFDi_Free( (void*)specList[i].pszSpec ); | |||||
} | |||||
NFDi_Free( specList ); | |||||
return NFD_OKAY; | |||||
} | |||||
static nfdresult_t AllocPathSet( IShellItemArray *shellItems, nfdpathset_t *pathSet ) | |||||
{ | |||||
const char ERRORMSG[] = "Error allocating pathset."; | |||||
assert(shellItems); | |||||
assert(pathSet); | |||||
// How many items in shellItems? | |||||
DWORD numShellItems; | |||||
HRESULT result = shellItems->GetCount(&numShellItems); | |||||
if ( !SUCCEEDED(result) ) | |||||
{ | |||||
NFDi_SetError(ERRORMSG); | |||||
return NFD_ERROR; | |||||
} | |||||
pathSet->count = static_cast<size_t>(numShellItems); | |||||
assert( pathSet->count > 0 ); | |||||
pathSet->indices = (size_t*)NFDi_Malloc( sizeof(size_t)*pathSet->count ); | |||||
if ( !pathSet->indices ) | |||||
{ | |||||
return NFD_ERROR; | |||||
} | |||||
/* count the total bytes needed for buf */ | |||||
size_t bufSize = 0; | |||||
for ( DWORD i = 0; i < numShellItems; ++i ) | |||||
{ | |||||
::IShellItem *shellItem; | |||||
result = shellItems->GetItemAt(i, &shellItem); | |||||
if ( !SUCCEEDED(result) ) | |||||
{ | |||||
NFDi_SetError(ERRORMSG); | |||||
return NFD_ERROR; | |||||
} | |||||
// Confirm SFGAO_FILESYSTEM is true for this shellitem, or ignore it. | |||||
SFGAOF attribs; | |||||
result = shellItem->GetAttributes( SFGAO_FILESYSTEM, &attribs ); | |||||
if ( !SUCCEEDED(result) ) | |||||
{ | |||||
NFDi_SetError(ERRORMSG); | |||||
return NFD_ERROR; | |||||
} | |||||
if ( !(attribs & SFGAO_FILESYSTEM) ) | |||||
continue; | |||||
LPWSTR name; | |||||
shellItem->GetDisplayName(SIGDN_FILESYSPATH, &name); | |||||
// Calculate length of name with UTF-8 encoding | |||||
bufSize += GetUTF8ByteCountForWChar( name ); | |||||
} | |||||
assert(bufSize); | |||||
pathSet->buf = (nfdchar_t*)NFDi_Malloc( sizeof(nfdchar_t) * bufSize ); | |||||
memset( pathSet->buf, 0, sizeof(nfdchar_t) * bufSize ); | |||||
/* fill buf */ | |||||
nfdchar_t *p_buf = pathSet->buf; | |||||
for (DWORD i = 0; i < numShellItems; ++i ) | |||||
{ | |||||
::IShellItem *shellItem; | |||||
result = shellItems->GetItemAt(i, &shellItem); | |||||
if ( !SUCCEEDED(result) ) | |||||
{ | |||||
NFDi_SetError(ERRORMSG); | |||||
return NFD_ERROR; | |||||
} | |||||
// Confirm SFGAO_FILESYSTEM is true for this shellitem, or ignore it. | |||||
SFGAOF attribs; | |||||
result = shellItem->GetAttributes( SFGAO_FILESYSTEM, &attribs ); | |||||
if ( !SUCCEEDED(result) ) | |||||
{ | |||||
NFDi_SetError(ERRORMSG); | |||||
return NFD_ERROR; | |||||
} | |||||
if ( !(attribs & SFGAO_FILESYSTEM) ) | |||||
continue; | |||||
LPWSTR name; | |||||
shellItem->GetDisplayName(SIGDN_FILESYSPATH, &name); | |||||
int bytesWritten = CopyWCharToExistingNFDCharBuffer(name, p_buf); | |||||
ptrdiff_t index = p_buf - pathSet->buf; | |||||
assert( index >= 0 ); | |||||
pathSet->indices[i] = static_cast<size_t>(index); | |||||
p_buf += bytesWritten; | |||||
} | |||||
return NFD_OKAY; | |||||
} | |||||
static nfdresult_t SetDefaultPath( IFileDialog *dialog, const char *defaultPath ) | |||||
{ | |||||
if ( !defaultPath || strlen(defaultPath) == 0 ) | |||||
return NFD_OKAY; | |||||
wchar_t *defaultPathW = {0}; | |||||
CopyNFDCharToWChar( defaultPath, &defaultPathW ); | |||||
IShellItem *folder; | |||||
HRESULT result = SHCreateItemFromParsingName( defaultPathW, NULL, IID_PPV_ARGS(&folder) ); | |||||
// Valid non results. | |||||
if ( result == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) || result == HRESULT_FROM_WIN32(ERROR_INVALID_DRIVE) ) | |||||
{ | |||||
NFDi_Free( defaultPathW ); | |||||
return NFD_OKAY; | |||||
} | |||||
if ( !SUCCEEDED(result) ) | |||||
{ | |||||
NFDi_SetError("Error creating ShellItem"); | |||||
NFDi_Free( defaultPathW ); | |||||
return NFD_ERROR; | |||||
} | |||||
// Could also call SetDefaultFolder(), but this guarantees defaultPath -- more consistency across API. | |||||
dialog->SetFolder( folder ); | |||||
NFDi_Free( defaultPathW ); | |||||
folder->Release(); | |||||
return NFD_OKAY; | |||||
} | |||||
/* public */ | |||||
nfdresult_t NFD_OpenDialog( const char *filterList, | |||||
const nfdchar_t *defaultPath, | |||||
nfdchar_t **outPath ) | |||||
{ | |||||
nfdresult_t nfdResult = NFD_ERROR; | |||||
// Init COM library. | |||||
HRESULT result = ::CoInitializeEx(NULL, | |||||
::COINIT_APARTMENTTHREADED | | |||||
::COINIT_DISABLE_OLE1DDE ); | |||||
if ( !SUCCEEDED(result)) | |||||
{ | |||||
NFDi_SetError("Could not initialize COM."); | |||||
goto end; | |||||
} | |||||
::IFileOpenDialog *fileOpenDialog(NULL); | |||||
// Create dialog | |||||
result = ::CoCreateInstance(::CLSID_FileOpenDialog, NULL, | |||||
CLSCTX_ALL, ::IID_IFileOpenDialog, | |||||
reinterpret_cast<void**>(&fileOpenDialog) ); | |||||
if ( !SUCCEEDED(result) ) | |||||
{ | |||||
NFDi_SetError("Could not create dialog."); | |||||
goto end; | |||||
} | |||||
// Build the filter list | |||||
if ( !AddFiltersToDialog( fileOpenDialog, filterList ) ) | |||||
{ | |||||
goto end; | |||||
} | |||||
// Set the default path | |||||
if ( !SetDefaultPath( fileOpenDialog, defaultPath ) ) | |||||
{ | |||||
goto end; | |||||
} | |||||
// Show the dialog. | |||||
result = fileOpenDialog->Show(NULL); | |||||
if ( SUCCEEDED(result) ) | |||||
{ | |||||
// Get the file name | |||||
::IShellItem *shellItem(NULL); | |||||
result = fileOpenDialog->GetResult(&shellItem); | |||||
if ( !SUCCEEDED(result) ) | |||||
{ | |||||
NFDi_SetError("Could not get shell item from dialog."); | |||||
goto end; | |||||
} | |||||
wchar_t *filePath(NULL); | |||||
result = shellItem->GetDisplayName(::SIGDN_FILESYSPATH, &filePath); | |||||
if ( !SUCCEEDED(result) ) | |||||
{ | |||||
NFDi_SetError("Could not get file path for selected."); | |||||
goto end; | |||||
} | |||||
CopyWCharToNFDChar( filePath, outPath ); | |||||
CoTaskMemFree(filePath); | |||||
if ( !*outPath ) | |||||
{ | |||||
/* error is malloc-based, error message would be redundant */ | |||||
goto end; | |||||
} | |||||
nfdResult = NFD_OKAY; | |||||
shellItem->Release(); | |||||
} | |||||
else if (result == HRESULT_FROM_WIN32(ERROR_CANCELLED) ) | |||||
{ | |||||
nfdResult = NFD_CANCEL; | |||||
} | |||||
else | |||||
{ | |||||
NFDi_SetError("File dialog box show failed."); | |||||
nfdResult = NFD_ERROR; | |||||
} | |||||
end: | |||||
::CoUninitialize(); | |||||
return nfdResult; | |||||
} | |||||
nfdresult_t NFD_OpenDialogMultiple( const nfdchar_t *filterList, | |||||
const nfdchar_t *defaultPath, | |||||
nfdpathset_t *outPaths ) | |||||
{ | |||||
nfdresult_t nfdResult = NFD_ERROR; | |||||
// Init COM library. | |||||
HRESULT result = ::CoInitializeEx(NULL, | |||||
::COINIT_APARTMENTTHREADED | | |||||
::COINIT_DISABLE_OLE1DDE ); | |||||
if ( !SUCCEEDED(result)) | |||||
{ | |||||
NFDi_SetError("Could not initialize COM."); | |||||
return NFD_ERROR; | |||||
} | |||||
::IFileOpenDialog *fileOpenDialog(NULL); | |||||
// Create dialog | |||||
result = ::CoCreateInstance(::CLSID_FileOpenDialog, NULL, | |||||
CLSCTX_ALL, ::IID_IFileOpenDialog, | |||||
reinterpret_cast<void**>(&fileOpenDialog) ); | |||||
if ( !SUCCEEDED(result) ) | |||||
{ | |||||
NFDi_SetError("Could not create dialog."); | |||||
goto end; | |||||
} | |||||
// Build the filter list | |||||
if ( !AddFiltersToDialog( fileOpenDialog, filterList ) ) | |||||
{ | |||||
goto end; | |||||
} | |||||
// Set the default path | |||||
if ( !SetDefaultPath( fileOpenDialog, defaultPath ) ) | |||||
{ | |||||
goto end; | |||||
} | |||||
// Set a flag for multiple options | |||||
DWORD dwFlags; | |||||
result = fileOpenDialog->GetOptions(&dwFlags); | |||||
if ( !SUCCEEDED(result) ) | |||||
{ | |||||
NFDi_SetError("Could not get options."); | |||||
goto end; | |||||
} | |||||
result = fileOpenDialog->SetOptions(dwFlags | FOS_ALLOWMULTISELECT); | |||||
if ( !SUCCEEDED(result) ) | |||||
{ | |||||
NFDi_SetError("Could not set options."); | |||||
goto end; | |||||
} | |||||
// Show the dialog. | |||||
result = fileOpenDialog->Show(NULL); | |||||
if ( SUCCEEDED(result) ) | |||||
{ | |||||
IShellItemArray *shellItems; | |||||
result = fileOpenDialog->GetResults( &shellItems ); | |||||
if ( !SUCCEEDED(result) ) | |||||
{ | |||||
NFDi_SetError("Could not get shell items."); | |||||
goto end; | |||||
} | |||||
if ( AllocPathSet( shellItems, outPaths ) == NFD_ERROR ) | |||||
{ | |||||
goto end; | |||||
} | |||||
shellItems->Release(); | |||||
nfdResult = NFD_OKAY; | |||||
} | |||||
else if (result == HRESULT_FROM_WIN32(ERROR_CANCELLED) ) | |||||
{ | |||||
nfdResult = NFD_CANCEL; | |||||
} | |||||
else | |||||
{ | |||||
NFDi_SetError("File dialog box show failed."); | |||||
nfdResult = NFD_ERROR; | |||||
} | |||||
end: | |||||
::CoUninitialize(); | |||||
return nfdResult; | |||||
} | |||||
nfdresult_t NFD_SaveDialog( const nfdchar_t *filterList, | |||||
const nfdchar_t *defaultPath, | |||||
nfdchar_t **outPath ) | |||||
{ | |||||
nfdresult_t nfdResult = NFD_ERROR; | |||||
// Init COM library. | |||||
HRESULT result = ::CoInitializeEx(NULL, | |||||
::COINIT_APARTMENTTHREADED | | |||||
::COINIT_DISABLE_OLE1DDE ); | |||||
if ( !SUCCEEDED(result)) | |||||
{ | |||||
NFDi_SetError("Could not initialize COM."); | |||||
return NFD_ERROR; | |||||
} | |||||
::IFileSaveDialog *fileSaveDialog(NULL); | |||||
// Create dialog | |||||
result = ::CoCreateInstance(::CLSID_FileSaveDialog, NULL, | |||||
CLSCTX_ALL, ::IID_IFileSaveDialog, | |||||
reinterpret_cast<void**>(&fileSaveDialog) ); | |||||
if ( !SUCCEEDED(result) ) | |||||
{ | |||||
NFDi_SetError("Could not create dialog."); | |||||
goto end; | |||||
} | |||||
// Build the filter list | |||||
if ( !AddFiltersToDialog( fileSaveDialog, filterList ) ) | |||||
{ | |||||
goto end; | |||||
} | |||||
// Set the default path | |||||
if ( !SetDefaultPath( fileSaveDialog, defaultPath ) ) | |||||
{ | |||||
goto end; | |||||
} | |||||
// Show the dialog. | |||||
result = fileSaveDialog->Show(NULL); | |||||
if ( SUCCEEDED(result) ) | |||||
{ | |||||
// Get the file name | |||||
::IShellItem *shellItem; | |||||
result = fileSaveDialog->GetResult(&shellItem); | |||||
if ( !SUCCEEDED(result) ) | |||||
{ | |||||
NFDi_SetError("Could not get shell item from dialog."); | |||||
goto end; | |||||
} | |||||
wchar_t *filePath(NULL); | |||||
result = shellItem->GetDisplayName(::SIGDN_FILESYSPATH, &filePath); | |||||
if ( !SUCCEEDED(result) ) | |||||
{ | |||||
NFDi_SetError("Could not get file path for selected."); | |||||
goto end; | |||||
} | |||||
CopyWCharToNFDChar( filePath, outPath ); | |||||
CoTaskMemFree(filePath); | |||||
if ( !*outPath ) | |||||
{ | |||||
/* error is malloc-based, error message would be redundant */ | |||||
goto end; | |||||
} | |||||
nfdResult = NFD_OKAY; | |||||
shellItem->Release(); | |||||
} | |||||
else if (result == HRESULT_FROM_WIN32(ERROR_CANCELLED) ) | |||||
{ | |||||
nfdResult = NFD_CANCEL; | |||||
} | |||||
else | |||||
{ | |||||
NFDi_SetError("File dialog box show failed."); | |||||
nfdResult = NFD_ERROR; | |||||
} | |||||
end: | |||||
::CoUninitialize(); | |||||
return nfdResult; | |||||
} |
@ -0,0 +1,70 @@ | |||||
# | |||||
# Native file dialog | |||||
# | |||||
# Build tests | |||||
target_arch=str(Platform()) | |||||
debug = int(ARGUMENTS.get( 'debug', 0 )) | |||||
files = {'test_opendialog': ['test_opendialog.c'], | |||||
'test_opendialogmultiple': ['test_opendialogmultiple.c'], | |||||
'test_savedialog': ['test_savedialog.c']} | |||||
test_env = Environment() | |||||
# Windows runtime library types | |||||
win_rtl = {'debug': '/MDd', | |||||
'release': '/MD'} | |||||
def set_debug(env): | |||||
if target_arch == 'win32': | |||||
env.Append( CFLAGS=['/Z7', # obj contains full symbols | |||||
win_rtl['debug'] ] ) | |||||
else: | |||||
env.Append( CFLAGS=['-g'] ) | |||||
def set_release(env): | |||||
if target_arch == 'win32': | |||||
env.Append( CFLAGS=[win_rtl['release'], | |||||
'/O2', | |||||
]) | |||||
else: | |||||
env.Append( CFLAGS=['-O3'] ) | |||||
def get_lib_name(base, is_debug): | |||||
if is_debug: | |||||
return base + '_d' | |||||
else: | |||||
return base | |||||
if debug: | |||||
set_debug(test_env) | |||||
else: | |||||
set_release(test_env) | |||||
test_env.Append( CPPPATH=['../src/include'], # API header path only, no internals allowed | |||||
LIBPATH=['../src'], | |||||
LIBS=get_lib_name('nfd', debug) ) | |||||
# Cocoa OS X builds | |||||
if target_arch == 'darwin': | |||||
test_env.Append( FRAMEWORKS='AppKit' ) | |||||
test_env.CC='clang -fcolor-diagnostics' | |||||
# Linux GTK+ 3 builds | |||||
elif target_arch == 'posix': | |||||
test_env.ParseConfig( 'pkg-config --cflags --libs gtk+-3.0' ) | |||||
elif target_arch == 'win32': | |||||
test_env.Append( | |||||
LINKFLAGS=['/NODEFAULTLIB:LIBCMT']) | |||||
for codebase in files: | |||||
output_name = get_lib_name(codebase, debug) | |||||
test_env.Program( output_name, files[codebase] ) |
@ -0,0 +1,29 @@ | |||||
#include <nfd.h> | |||||
#include <stdio.h> | |||||
#include <stdlib.h> | |||||
/* this test should compile on all supported platforms */ | |||||
int main( void ) | |||||
{ | |||||
nfdchar_t *outPath = NULL; | |||||
nfdresult_t result = NFD_OpenDialog( "png,jpg;pdf", NULL, &outPath ); | |||||
if ( result == NFD_OKAY ) | |||||
{ | |||||
puts("Success!"); | |||||
puts(outPath); | |||||
free(outPath); | |||||
} | |||||
else if ( result == NFD_CANCEL ) | |||||
{ | |||||
puts("User pressed cancel."); | |||||
} | |||||
else | |||||
{ | |||||
printf("Error: %s\n", NFD_GetError() ); | |||||
} | |||||
return 0; | |||||
} |
@ -0,0 +1,34 @@ | |||||
#include <nfd.h> | |||||
#include <stdio.h> | |||||
#include <stdlib.h> | |||||
/* this test should compile on all supported platforms */ | |||||
int main( void ) | |||||
{ | |||||
nfdchar_t *outPath = NULL; | |||||
nfdpathset_t pathSet; | |||||
nfdresult_t result = NFD_OpenDialogMultiple( "png,jpg;pdf", NULL, &pathSet ); | |||||
if ( result == NFD_OKAY ) | |||||
{ | |||||
size_t i; | |||||
for ( i = 0; i < NFD_PathSet_GetCount(&pathSet); ++i ) | |||||
{ | |||||
nfdchar_t *path = NFD_PathSet_GetPath(&pathSet, i); | |||||
printf("Path %li: %s\n", i, path ); | |||||
} | |||||
NFD_PathSet_Free(&pathSet); | |||||
} | |||||
else if ( result == NFD_CANCEL ) | |||||
{ | |||||
puts("User pressed cancel."); | |||||
} | |||||
else | |||||
{ | |||||
printf("Error: %s\n", NFD_GetError() ); | |||||
} | |||||
return 0; | |||||
} |
@ -0,0 +1,28 @@ | |||||
#include <nfd.h> | |||||
#include <stdio.h> | |||||
#include <stdlib.h> | |||||
/* this test should compile on all supported platforms */ | |||||
int main( void ) | |||||
{ | |||||
nfdchar_t *savePath = NULL; | |||||
nfdresult_t result = NFD_SaveDialog( "png,jpg;pdf", NULL, &savePath ); | |||||
if ( result == NFD_OKAY ) | |||||
{ | |||||
puts("Success!"); | |||||
puts(savePath); | |||||
free(savePath); | |||||
} | |||||
else if ( result == NFD_CANCEL ) | |||||
{ | |||||
puts("User pressed cancel."); | |||||
} | |||||
else | |||||
{ | |||||
printf("Error: %s\n", NFD_GetError() ); | |||||
} | |||||
return 0; | |||||
} |
@ -0,0 +1,60 @@ | |||||
#define TINYEXR_IMPLEMENTATION | |||||
#include "tinyexr.h" | |||||
#include "exr-io.h" | |||||
#include <cstdio> | |||||
#include <iostream> | |||||
namespace exrio { | |||||
bool GetEXRLayers(const char *filename) | |||||
{ | |||||
const char** layer_names = nullptr; | |||||
int num_layers = 0; | |||||
const char *err = nullptr; | |||||
int ret = EXRLayers(filename, &layer_names, &num_layers, &err); | |||||
if (err) { | |||||
fprintf(stderr, "EXR error = %s\n", err); | |||||
} | |||||
if (ret != 0) { | |||||
fprintf(stderr, "Load EXR err: %s\n", err); | |||||
return false; | |||||
} | |||||
if (num_layers > 0) | |||||
{ | |||||
fprintf(stdout, "EXR Contains %i Layers\n", num_layers); | |||||
for (size_t i = 0; i < num_layers; ++i) { | |||||
fprintf(stdout, "Layer %i : %s\n", i + 1, layer_names[i]); | |||||
} | |||||
} | |||||
free(layer_names); | |||||
return true; | |||||
} | |||||
bool LoadEXRRGBA(float** rgba, int *w, int *h, const char* filename, const char* layername) | |||||
{ | |||||
int width, height; | |||||
float* image; | |||||
const char *err = nullptr; | |||||
int ret = LoadEXRWithLayer(&image, &width, &height, filename, layername, &err); | |||||
if (err) { | |||||
fprintf(stderr, "EXR error = %s\n", err); | |||||
} | |||||
if (ret != 0) { | |||||
fprintf(stderr, "Load EXR err: %s\n", err); | |||||
return false; | |||||
} | |||||
(*rgba) = image; | |||||
(*w) = width; | |||||
(*h) = height; | |||||
return true; | |||||
} | |||||
} |
@ -0,0 +1,16 @@ | |||||
#ifndef EXR_IO_H_ | |||||
#define EXR_IO_H_ | |||||
#include "tinyexr.h" | |||||
namespace exrio | |||||
{ | |||||
bool GetEXRLayers(const char *filename); | |||||
bool LoadEXRRGBA(float** rgba, int* w, int *h, const char* filename, const char* layername = nullptr); | |||||
} | |||||
#endif // EXR_IO_H_ |