Merge submodule contents for hecl/master

This commit is contained in:
Luke Street 2021-04-06 13:04:59 -04:00
commit 5efaf5b7db
140 changed files with 72497 additions and 0 deletions

16
.gitmodules vendored
View File

@ -46,3 +46,19 @@
path = Editor/locale path = Editor/locale
url = ../urde-translations.git url = ../urde-translations.git
branch = master branch = master
[submodule "extern/libSquish"]
path = hecl/extern/libSquish
url = ../libSquish.git
branch = master
[submodule "extern/athena"]
path = hecl/extern/athena
url = ../../libAthena/athena.git
branch = master
[submodule "extern/boo"]
path = hecl/extern/boo
url = ../boo.git
branch = master
[submodule "extern/libjpeg-turbo"]
path = hecl/extern/libjpeg-turbo
url = ../libjpeg-turbo.git
branch = thp

2
hecl/.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
DataSpecRegistry.hpp
.DS_Store

View File

@ -0,0 +1,63 @@
include_guard(GLOBAL)
file(RELATIVE_PATH REL_PATH ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_LIST_DIR})
include_directories(${CMAKE_CURRENT_BINARY_DIR}/${REL_PATH})
unset(HECL_APPLICATION_REPS_TARGETS_LIST CACHE)
unset(HECL_APPLICATION_REPS_INCLUDES_LIST CACHE)
unset(HECL_APPLICATION_PIPELINE_REPS_UNIVERSAL CACHE)
unset(HECL_APPLICATION_PIPELINE_REPS CACHE)
unset(HECL_APPLICATION_STAGE_REPS CACHE)
# add_pipeline_rep(my::fully::qualified::class my_class_header.hpp [UNIVERSAL])
function(add_pipeline_rep name header)
if(IS_ABSOLUTE ${header})
set(theHeader ${header})
else()
set(theHeader ${CMAKE_CURRENT_SOURCE_DIR}/${header})
endif()
if (NOT ${theHeader} IN_LIST HECL_APPLICATION_REPS_INCLUDES_LIST)
set(HECL_APPLICATION_REPS_INCLUDES_LIST "${HECL_APPLICATION_REPS_INCLUDES_LIST};${theHeader}" CACHE INTERNAL "")
endif()
if ("${ARGV2}" STREQUAL "UNIVERSAL")
set(HECL_APPLICATION_PIPELINE_REPS_UNIVERSAL "${HECL_APPLICATION_PIPELINE_REPS_UNIVERSAL};${name}" CACHE INTERNAL "")
else()
set(HECL_APPLICATION_PIPELINE_REPS "${HECL_APPLICATION_PIPELINE_REPS};${name}" CACHE INTERNAL "")
endif()
endfunction()
# add_stage_rep(my::fully::qualified::class my_class_header.hpp)
function(add_stage_rep name header)
if(IS_ABSOLUTE ${header})
set(theHeader ${header})
else()
set(theHeader ${CMAKE_CURRENT_SOURCE_DIR}/${header})
endif()
if (NOT ${theHeader} IN_LIST HECL_APPLICATION_REPS_INCLUDES_LIST)
set(HECL_APPLICATION_REPS_INCLUDES_LIST "${HECL_APPLICATION_REPS_INCLUDES_LIST};${theHeader}" CACHE INTERNAL "")
endif()
set(HECL_APPLICATION_STAGE_REPS "${HECL_APPLICATION_STAGE_REPS};${name}" CACHE INTERNAL "")
endfunction()
function(add_shader_target target)
if (NOT ${target} IN_LIST HECL_APPLICATION_REPS_TARGETS_LIST)
set(HECL_APPLICATION_REPS_TARGETS_LIST "${HECL_APPLICATION_REPS_TARGETS_LIST};${target}" CACHE INTERNAL "")
endif()
endfunction()
function(add_shader file)
get_filename_component(name ${file} NAME)
get_filename_component(dir ${file} DIRECTORY)
shaderc(${CMAKE_CURRENT_BINARY_DIR}/${dir}/shader_${name} ${file}.shader)
add_stage_rep(shader_${name} ${CMAKE_CURRENT_BINARY_DIR}/${dir}/shader_${name}.hpp)
add_pipeline_rep(shader_${name} ${CMAKE_CURRENT_BINARY_DIR}/${dir}/shader_${name}.hpp UNIVERSAL)
add_library(shader_${name} ${CMAKE_CURRENT_BINARY_DIR}/${dir}/shader_${name}.hpp ${CMAKE_CURRENT_BINARY_DIR}/${dir}/shader_${name}.cpp)
add_shader_target(shader_${name})
endfunction()
function(add_special_shader name)
add_stage_rep(${name} ${name}.hpp)
add_pipeline_rep(${name} ${name}.hpp UNIVERSAL)
add_library(${name} ${name}.hpp ${ARGN})
add_shader_target(${name})
endfunction()

74
hecl/CMakeLists.txt Normal file
View File

@ -0,0 +1,74 @@
if(CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR)
cmake_minimum_required(VERSION 3.10 FATAL_ERROR) # because of c++17
project(hecl)
if(MSVC)
add_compile_options(
# Disable exceptions
$<$<COMPILE_LANGUAGE:CXX>:/EHsc>
/wd4267 /wd4244
)
add_compile_definitions(UNICODE=1 _UNICODE=1 _CRT_SECURE_NO_WARNINGS=1)
else()
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
add_compile_options(
# Disable exceptions
$<$<COMPILE_LANGUAGE:CXX>:-fno-exceptions>
-Wno-multichar
)
endif()
endif()
include(ApplicationTools.cmake)
configure_file(DataSpecRegistry.hpp.in ${CMAKE_CURRENT_BINARY_DIR}/DataSpecRegistry.hpp @ONLY)
unset(HECL_APPLICATION_REPS_INCLUDES_LOCAL)
foreach(theHeader ${HECL_APPLICATION_REPS_INCLUDES_LIST})
set(HECL_APPLICATION_REPS_INCLUDES_LOCAL "${HECL_APPLICATION_REPS_INCLUDES_LOCAL}#include \"${theHeader}\"\n")
endforeach()
unset(HECL_APPLICATION_PIPELINE_REPS_UNIVERSAL_LOCAL)
foreach(name ${HECL_APPLICATION_PIPELINE_REPS_UNIVERSAL})
set(HECL_APPLICATION_PIPELINE_REPS_UNIVERSAL_LOCAL "${HECL_APPLICATION_PIPELINE_REPS_UNIVERSAL_LOCAL}UNIVERSAL_PIPELINES_${name} \\\n")
endforeach()
unset(HECL_APPLICATION_PIPELINE_REPS_OPENGL_LOCAL)
unset(HECL_APPLICATION_PIPELINE_REPS_VULKAN_LOCAL)
unset(HECL_APPLICATION_PIPELINE_REPS_D3D11_LOCAL)
unset(HECL_APPLICATION_PIPELINE_REPS_METAL_LOCAL)
unset(HECL_APPLICATION_PIPELINE_REPS_NX_LOCAL)
foreach(name ${HECL_APPLICATION_PIPELINE_REPS})
set(HECL_APPLICATION_PIPELINE_REPS_OPENGL_LOCAL "${HECL_APPLICATION_PIPELINE_REPS_OPENGL_LOCAL}OPENGL_PIPELINES_${name} \\\n")
set(HECL_APPLICATION_PIPELINE_REPS_VULKAN_LOCAL "${HECL_APPLICATION_PIPELINE_REPS_VULKAN_LOCAL}VULKAN_PIPELINES_${name} \\\n")
set(HECL_APPLICATION_PIPELINE_REPS_D3D11_LOCAL "${HECL_APPLICATION_PIPELINE_REPS_D3D11_LOCAL}D3D11_PIPELINES_${name} \\\n")
set(HECL_APPLICATION_PIPELINE_REPS_METAL_LOCAL "${HECL_APPLICATION_PIPELINE_REPS_METAL_LOCAL}METAL_PIPELINES_${name} \\\n")
set(HECL_APPLICATION_PIPELINE_REPS_NX_LOCAL "${HECL_APPLICATION_PIPELINE_REPS_NX_LOCAL}NX_PIPELINES_${name} \\\n")
endforeach()
unset(HECL_APPLICATION_STAGE_REPS_LOCAL)
foreach(name ${HECL_APPLICATION_STAGE_REPS})
set(HECL_APPLICATION_STAGE_REPS_LOCAL "${HECL_APPLICATION_STAGE_REPS_LOCAL}STAGES_${name}(P, S) \\\n")
endforeach()
configure_file(include/hecl/ApplicationReps.hpp.in ${CMAKE_CURRENT_BINARY_DIR}/ApplicationReps.hpp @ONLY)
add_subdirectory(extern)
add_subdirectory(bintoc)
if(NOT TARGET bintoc)
# Use native if cross compiling
find_package(hecl-bintoc REQUIRED)
endif()
if(NOT TARGET atdna)
# Import native atdna if cross-compiling
find_package(atdna REQUIRED)
if(NOT TARGET atdna)
message(FATAL_ERROR "atdna required for building hecl; please verify LLVM installation")
endif()
endif()
add_subdirectory(lib)
add_subdirectory(blender)
add_subdirectory(driver)
install(DIRECTORY include/hecl DESTINATION include/hecl)

View File

@ -0,0 +1,21 @@
/* Include this file once in the main translation unit of any executable file
* using HECL's database functionality (see driver/main.cpp)
*/
#ifdef DATA_SPEC_REGISTRY_HPP
#error DataSpecRegistry.hpp may only be included once
#endif
#define DATA_SPEC_REGISTRY_HPP
#include "hecl/Database.hpp"
namespace hecl::Database {
/* Centralized registry for DataSpec lookup */
std::vector<const struct DataSpecEntry*> DATA_SPEC_REGISTRY;
}
@HECL_DATASPEC_DECLS@
/* Please Call Me! */
void HECLRegisterDataSpecs() {
@HECL_DATASPEC_PUSHES@
}

2362
hecl/Doxyfile Normal file

File diff suppressed because it is too large Load Diff

22
hecl/LICENSE Normal file
View File

@ -0,0 +1,22 @@
The MIT License
Copyright (c) 2015-2018 HECL Contributors
Original Authors: Jack Andersen and Phillip "Antidote" Stephens
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.

29
hecl/README.md Normal file
View File

@ -0,0 +1,29 @@
### HECL (high-level, extensible combiner language)
**HECL** is a toolkit for building custom asset pipelines, assisting the development of conversion tools and runtime loaders.
The most significant feature is the intermediate HECL language, using an expressive command syntax to represent cross-platform shaders. This includes a common source representation and intermediate binary representation. Complete vertex and fragment shader programs are generated for supported platforms and may be built on-demand as part of a 3D application runtime.
```py
# Combiner 1: Opaque *Diffuse* and *Emissive*
HECLOpaque(Texture(0, UV(0)) * Lighting() + Texture(1, UV(0)))
# Combiner 2: Alpha-blended single-texture
# (both texture-accesses folded to a single sample operation)
HECLAlpha(Texture(0, UV(0)), Texture(0, UV(0)).a)
# Combiner 3: Additive-blended single-texture
# (modern graphics APIs require blending configuration along with all shader configs)
HECLAdditive(Texture(0, UV(0)), Texture(0, UV(0)).a)
```
Beyond shaders, HECL also defines a rigged mesh format called HMDL. Meshes using this encoding interact with HECL, with pose transforms applied via the vertex shader.
For asset pipelines, HECL provides a project system with dependency-resolution much like an IDE or `make`. Assets in their editable representation are *cooked* in-bulk and whenever the source file is updated. Currently, blender is the only-supported input format for rigged meshes with node-materials.
#### Supported Backends
* GLSL 330 *(with optional SPIR-V conversion)*
* HLSL (Shader Model 4)
* Metal 1.1
* GX *(complete TexCoordGen and TEV configs in intermediate structures)*

View File

@ -0,0 +1,62 @@
if(NOT CMAKE_CROSSCOMPILING)
add_executable(bintoc bintoc.c)
target_include_directories(bintoc PRIVATE ${ZLIB_INCLUDE_DIR})
target_link_libraries(bintoc ${ZLIB_LIBRARIES})
function(bintoc out in sym)
if(IS_ABSOLUTE ${out})
set(theOut ${out})
else()
set(theOut ${CMAKE_CURRENT_BINARY_DIR}/${out})
endif()
if(IS_ABSOLUTE ${in})
set(theIn ${in})
else()
set(theIn ${CMAKE_CURRENT_SOURCE_DIR}/${in})
endif()
get_filename_component(outDir ${theOut} DIRECTORY)
file(MAKE_DIRECTORY ${outDir})
add_custom_command(OUTPUT ${theOut}
COMMAND $<TARGET_FILE:bintoc> ARGS ${theIn} ${theOut} ${sym}
DEPENDS ${theIn} bintoc)
endfunction()
function(bintoc_compress out in sym)
if(IS_ABSOLUTE ${out})
set(theOut ${out})
else()
set(theOut ${CMAKE_CURRENT_BINARY_DIR}/${out})
endif()
if(IS_ABSOLUTE ${in})
set(theIn ${in})
else()
set(theIn ${CMAKE_CURRENT_SOURCE_DIR}/${in})
endif()
get_filename_component(outDir ${theOut} DIRECTORY)
file(MAKE_DIRECTORY ${outDir})
add_custom_command(OUTPUT ${theOut}
COMMAND $<TARGET_FILE:bintoc> ARGS --compress ${theIn} ${theOut} ${sym}
DEPENDS ${theIn} bintoc)
endfunction()
##################
# Package Export #
##################
# Add all targets to the build-tree export set
export(TARGETS bintoc FILE "${CMAKE_CURRENT_BINARY_DIR}/hecl-bintocTargets.cmake")
# Export the package for use from the build-tree
# (this registers the build-tree with a global CMake-registry)
export(PACKAGE hecl-bintoc)
# Create the atdnaConfig.cmake
# ... for the build tree
configure_file(hecl-bintocConfig.cmake.in "${CMAKE_CURRENT_BINARY_DIR}/hecl-bintocConfig.cmake" @ONLY)
# ... for the install tree
configure_file(hecl-bintocConfig.cmake.in "${CMAKE_CURRENT_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/hecl-bintocConfig.cmake" @ONLY)
# ... for both
configure_file(hecl-bintocConfigVersion.cmake.in "${CMAKE_CURRENT_BINARY_DIR}/hecl-bintocConfigVersion.cmake" @ONLY)
else()
# Use native if cross compiling
find_package(hecl-bintoc REQUIRED)
endif()

95
hecl/bintoc/bintoc.c Normal file
View File

@ -0,0 +1,95 @@
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <stdbool.h>
#include <zlib.h>
#define CHUNK 16384
#define LINE_BREAK 32
static uint8_t buf[CHUNK];
static uint8_t zbuf[CHUNK];
void print_usage() { fprintf(stderr, "Usage: bintoc [--compress] <in> <out> <symbol>\n"); }
int main(int argc, char** argv) {
if (argc < 4) {
print_usage();
return 1;
}
char* input = argv[1];
char* output = argv[2];
char* symbol = argv[3];
bool compress = false;
if (strcmp(input, "--compress") == 0) {
if (argc < 5) {
print_usage();
return 1;
}
input = argv[2];
output = argv[3];
symbol = argv[4];
compress = true;
}
FILE* fin = fopen(input, "rb");
if (!fin) {
fprintf(stderr, "Unable to open %s for reading\n", input);
return 1;
}
FILE* fout = fopen(output, "wb");
if (!fout) {
fprintf(stderr, "Unable to open %s for writing\n", output);
return 1;
}
fprintf(fout, "#include <cstdint>\n#include <cstddef>\n");
fprintf(fout, "extern \"C\" const uint8_t %s[] =\n{\n ", symbol);
size_t totalSz = 0;
size_t readSz;
if (compress) {
size_t compressedSz = 0;
z_stream strm = {.zalloc = Z_NULL, .zfree = Z_NULL, .opaque = Z_NULL};
int ret = deflateInit2(&strm, Z_BEST_COMPRESSION, Z_DEFLATED, MAX_WBITS | 16, MAX_MEM_LEVEL, Z_DEFAULT_STRATEGY);
if (ret != Z_OK) {
fprintf(stderr, "zlib initialization failed %d\n", ret);
return 1;
}
while ((strm.avail_in = fread(buf, 1, sizeof(buf), fin))) {
totalSz += strm.avail_in;
strm.next_in = buf;
int eof = feof(fin);
do {
strm.next_out = zbuf;
strm.avail_out = sizeof(zbuf);
ret = deflate(&strm, eof ? Z_FINISH : Z_NO_FLUSH);
if (ret == Z_STREAM_ERROR) {
fprintf(stderr, "zlib compression failed %d\n", ret);
return 1;
}
size_t sz = sizeof(zbuf) - strm.avail_out;
if (sz > 0) {
for (int b = 0; b < sz; ++b) {
fprintf(fout, "0x%02X, ", zbuf[b]);
if ((compressedSz + b + 1) % LINE_BREAK == 0)
fprintf(fout, "\n ");
}
compressedSz += sz;
}
} while (strm.avail_out == 0 || (eof && (ret == Z_OK || ret == Z_BUF_ERROR)));
}
deflateEnd(&strm);
fprintf(fout, "0x00};\nextern \"C\" const size_t %s_SZ = %zu;\n", symbol, compressedSz);
fprintf(fout, "extern \"C\" const size_t %s_DECOMPRESSED_SZ = %zu;\n", symbol, totalSz);
} else {
while ((readSz = fread(buf, 1, sizeof(buf), fin))) {
for (int b = 0; b < readSz; ++b) {
fprintf(fout, "0x%02X, ", buf[b]);
if ((totalSz + b + 1) % LINE_BREAK == 0)
fprintf(fout, "\n ");
}
totalSz += readSz;
}
fprintf(fout, "0x0};\nextern \"C\" const size_t %s_SZ = %zu;\n", symbol, totalSz);
}
fclose(fin);
fclose(fout);
return 0;
}

View File

@ -0,0 +1,45 @@
# - Config file for the bintoc package
# Compute paths
get_filename_component(BINTOC_CMAKE_DIR "${CMAKE_CURRENT_LIST_FILE}" PATH)
# Our library dependencies (contains definitions for IMPORTED targets)
if(NOT TARGET bintoc AND NOT bintoc_BINARY_DIR)
include("${BINTOC_CMAKE_DIR}/hecl-bintocTargets.cmake")
endif()
function(bintoc out in sym)
if(IS_ABSOLUTE ${out})
set(theOut ${out})
else()
set(theOut ${CMAKE_CURRENT_BINARY_DIR}/${out})
endif()
if(IS_ABSOLUTE ${in})
set(theIn ${in})
else()
set(theIn ${CMAKE_CURRENT_SOURCE_DIR}/${in})
endif()
get_filename_component(outDir ${theOut} DIRECTORY)
file(MAKE_DIRECTORY ${outDir})
add_custom_command(OUTPUT ${theOut}
COMMAND $<TARGET_FILE:bintoc> ARGS ${theIn} ${theOut} ${sym}
DEPENDS ${theIn})
endfunction()
function(bintoc_compress out in sym)
if(IS_ABSOLUTE ${out})
set(theOut ${out})
else()
set(theOut ${CMAKE_CURRENT_BINARY_DIR}/${out})
endif()
if(IS_ABSOLUTE ${in})
set(theIn ${in})
else()
set(theIn ${CMAKE_CURRENT_SOURCE_DIR}/${in})
endif()
get_filename_component(outDir ${theOut} DIRECTORY)
file(MAKE_DIRECTORY ${outDir})
add_custom_command(OUTPUT ${theOut}
COMMAND $<TARGET_FILE:bintoc> ARGS --compress ${theIn} ${theOut} ${sym}
DEPENDS ${theIn})
endfunction()

View File

@ -0,0 +1,12 @@
set(PACKAGE_VERSION "@BINTOC_VERSION@")
# Check whether the requested PACKAGE_FIND_VERSION is compatible
if("${PACKAGE_VERSION}" VERSION_LESS "${PACKAGE_FIND_VERSION}")
set(PACKAGE_VERSION_COMPATIBLE FALSE)
else()
set(PACKAGE_VERSION_COMPATIBLE TRUE)
if ("${PACKAGE_VERSION}" VERSION_EQUAL "${PACKAGE_FIND_VERSION}")
set(PACKAGE_VERSION_EXACT TRUE)
endif()
endif()

View File

@ -0,0 +1,35 @@
list(APPEND PY_SOURCES
hecl/__init__.py
hecl/Nodegrid.py
hecl/Patching.py
hecl/hmdl/__init__.py
hecl/hmdl/HMDLMesh.py
hecl/hmdl/HMDLShader.py
hecl/sact/__init__.py
hecl/sact/SACTAction.py
hecl/sact/SACTSubtype.py
hecl/srea/__init__.py
hecl/swld/__init__.py
hecl/armature.py
hecl/mapa.py
hecl/mapu.py
hecl/frme.py
hecl/path.py)
bintoc(hecl_blendershell.cpp hecl_blendershell.py HECL_BLENDERSHELL)
find_package(Python COMPONENTS Interpreter REQUIRED)
add_custom_command(OUTPUT hecl.zip DEPENDS ${PY_SOURCES}
COMMAND ${Python_EXECUTABLE} ARGS zip_package.py ${CMAKE_CURRENT_BINARY_DIR}/hecl.zip
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
COMMENT "Generating addon package")
bintoc(hecl_addon.cpp "${CMAKE_CURRENT_BINARY_DIR}/hecl.zip" HECL_ADDON)
add_library(hecl-blender-addon
hecl_blendershell.py
hecl_blendershell.cpp
zip_package.py
hecl.zip
hecl_addon.cpp
${PY_SOURCES})

View File

@ -0,0 +1,59 @@
# Node Grid Arranger Class
NODE_PADDING = 80
FRAME_NAMES = ['Textures','Output','Blend']
FRAME_WIDTHS = [400, 180, 180]
TOTAL_WIDTH = 0.0
for width in FRAME_WIDTHS:
TOTAL_WIDTH += width + NODE_PADDING
FRAME_COLORS = [(0.6,0.48,0.44),(0.53,0.6,0.47),(0.56,0.46,0.90)]
class Nodegrid:
def __init__(self, nodetree, cycles=False):
self.ncol = len(FRAME_NAMES)
self.heights = []
self.frames = []
self.col_roffs = [[0.0,0.0]] * self.ncol
for i in range(self.ncol):
if cycles and i<1:
self.heights.append(-1600.0)
self.frames.append(None)
continue
elif cycles:
self.heights.append(-1600.0)
else:
self.heights.append(0.0)
frame_node = nodetree.nodes.new('NodeFrame')
frame_node.label = FRAME_NAMES[i]
frame_node.use_custom_color = True
frame_node.color = FRAME_COLORS[i]
self.frames.append(frame_node)
def place_node(self, node, col, update_height = True):
if col < 0 or col >= self.ncol:
return False
x_pos = NODE_PADDING
for i in range(col):
x_pos += FRAME_WIDTHS[i] + NODE_PADDING*2
node.location[0] = x_pos - TOTAL_WIDTH/2
node.location[1] = self.heights[col]
if update_height:
self.heights[col] -= node.height + NODE_PADDING
self.frames[col].height += node.height + NODE_PADDING
node.parent = self.frames[col]
return True
def place_node_right(self, node, col, srow):
heights_backup = self.heights[col]
if self.place_node(node, col):
node.location[0] += self.col_roffs[col][srow]
if srow == 1:
node.location[1] -= 175
self.col_roffs[col][srow] += 200
self.heights[col] = heights_backup
def row_break(self, col):
self.heights[col] -= 350
self.col_roffs[col][0] = 0.0
self.col_roffs[col][1] = 0.0

View File

@ -0,0 +1,144 @@
import bpy
import os
import os.path
from pathlib import Path
def _mkdir(path):
try:
os.mkdir(path)
except:
pass
def path_components(path):
retval = []
base, end = os.path.split(path)
while end != '':
retval.insert(0, end)
base, end = os.path.split(base)
return retval
def find_project_root():
if bpy.data.filepath == '':
return None
path = os.path.split(bpy.data.filepath)
test_path = os.path.join(path[0], '.hecl')
while not os.path.exists(test_path):
path = os.path.split(path[0])
test_path = os.path.join(path[0], '.hecl')
if os.path.exists(test_path):
return path[0]
return None
def get_patching_dirs(make_dirs=False):
proj_root = find_project_root()
if not proj_root:
return None, None
rel_to_blend = os.path.relpath(bpy.data.filepath, start=proj_root)
rel_to_blend_comps = path_components(rel_to_blend)
trace_dir = os.path.join(proj_root, '.hecl', 'patches')
global_out = trace_dir
if not make_dirs and not os.path.exists(trace_dir):
return None, global_out
_mkdir(trace_dir)
for comp in rel_to_blend_comps:
ext_pair = os.path.splitext(comp)
if ext_pair[1] == '.blend':
trace_dir = os.path.join(trace_dir, ext_pair[0])
if not make_dirs and not os.path.exists(trace_dir):
return None, global_out
_mkdir(trace_dir)
return trace_dir, global_out
trace_dir = os.path.join(trace_dir, comp)
if not make_dirs and not os.path.exists(trace_dir):
return None, global_out
_mkdir(trace_dir)
class FILE_OT_hecl_patching_save(bpy.types.Operator):
'''Save text datablocks to hecl patching directory'''
bl_idname = "file.hecl_patching_save"
bl_label = "Save HECL Patches"
bl_options = {'REGISTER'}
def execute(self, context):
patching_dir, global_dir = get_patching_dirs(make_dirs=True)
if not patching_dir:
self.report({'WARNING'}, 'Unable to save patches for ' + bpy.data.filepath)
return {'CANCELLED'}
count = 0
for text in bpy.data.texts:
if not text.name.endswith('.py') or text.name.startswith('g_'):
continue
text_abspath = os.path.join(patching_dir, text.name)
text_file = open(text_abspath, 'w')
text_file.write(text.as_string())
text_file.close()
count += 1
if count == 1:
self.report({'INFO'}, 'saved 1 patch')
else:
self.report({'INFO'}, 'saved %d patches' % count)
return {'FINISHED'}
class FILE_OT_hecl_patching_load(bpy.types.Operator):
'''Load text datablocks from hecl patching directory'''
bl_idname = "file.hecl_patching_load"
bl_label = "Load HECL Patches"
bl_options = {'REGISTER'}
def execute(self, context):
patching_dir, global_dir = get_patching_dirs()
count = 0
# Locals
if patching_dir:
p = Path(patching_dir)
for path in p.glob('*.py'):
path = path.name
text_abspath = os.path.join(patching_dir, path)
text_file = open(text_abspath, 'r')
if path in bpy.data.texts:
text = bpy.data.texts[path]
else:
text = bpy.data.texts.new(path)
text.from_string(text_file.read())
text_file.close()
count += 1
# Globals
if global_dir:
p = Path(global_dir)
for path in p.glob('g_*.py'):
path = path.name
text_abspath = os.path.join(global_dir, path)
text_file = open(text_abspath, 'r')
if path in bpy.data.texts:
text = bpy.data.texts[path]
else:
text = bpy.data.texts.new(path)
text.from_string(text_file.read())
text_file.close()
count += 1
if count == 1:
self.report({'INFO'}, 'loaded 1 patch')
else:
self.report({'INFO'}, 'loaded %d patches' % count)
return {'FINISHED'}
def save_func(self, context):
self.layout.operator("file.hecl_patching_save", text="Save HECL Patches")
def load_func(self, context):
self.layout.operator("file.hecl_patching_load", text="Load HECL Patches")
def register():
bpy.utils.register_class(FILE_OT_hecl_patching_save)
bpy.utils.register_class(FILE_OT_hecl_patching_load)
bpy.types.TOPBAR_MT_file_external_data.append(load_func)
bpy.types.TOPBAR_MT_file_external_data.append(save_func)
def unregister():
bpy.utils.unregister_class(FILE_OT_hecl_patching_save)
bpy.utils.unregister_class(FILE_OT_hecl_patching_load)
bpy.types.TOPBAR_MT_file_external_data.remove(load_func)
bpy.types.TOPBAR_MT_file_external_data.remove(save_func)

View File

@ -0,0 +1,267 @@
bl_info = {
"name": "HECL",
"author": "Jack Andersen <jackoalan@gmail.com>",
"version": (1, 0),
"blender": (2, 80, 0),
"tracker_url": "https://github.com/AxioDL/hecl/issues/new",
"location": "Properties > Scene > HECL",
"description": "Enables blender to gather meshes, materials, and textures for hecl",
"category": "System"}
# Package import
from . import hmdl, sact, srea, swld, armature, mapa, mapu, frme, path, Nodegrid, Patching
Nodegrid = Nodegrid.Nodegrid
parent_armature = sact.SACTSubtype.parent_armature
import bpy, os, sys, struct, math
from mathutils import Vector
# Appendable list allowing external addons to register additional resource types
hecl_typeS = [
('NONE', "None", "Active scene not using HECL", None),
('MESH', "Mesh", "Active scene represents an HMDL Mesh", hmdl.draw),
('CMESH', "Collision Mesh", "Active scene represents a Collision Mesh", None),
('ARMATURE', "Armature", "Active scene represents an Armature", armature.draw),
('ACTOR', "Actor", "Active scene represents a HECL Actor", sact.draw),
('AREA', "Area", "Active scene represents a HECL Area", srea.draw),
('WORLD', "World", "Active scene represents a HECL World", swld.draw),
('MAPAREA', "Map Area", "Active scene represents a HECL Map Area", mapa.draw),
('MAPUNIVERSE', "Map Universe", "Active scene represents a HECL Map Universe", mapu.draw),
('FRAME', "Gui Frame", "Active scene represents a HECL Gui Frame", frme.draw),
('PATH', "Path Mesh", "Active scene represents a HECL Path Mesh", path.draw)]
# Main Scene Panel
class hecl_scene_panel(bpy.types.Panel):
bl_idname = "SCENE_PT_hecl"
bl_label = "HECL"
bl_space_type = 'PROPERTIES'
bl_region_type = 'WINDOW'
bl_context = "scene"
@classmethod
def poll(cls, context):
return (context.scene is not None)
def draw(self, context):
layout = self.layout
type_row = layout.row(align=True)
type_row.prop_menu_enum(context.scene, 'hecl_type', text='Export Type')
if context.scene.hecl_type == 'MESH' or context.scene.hecl_type == 'AREA' or context.scene.hecl_type == 'ACTOR':
sm_row = layout.row(align=True)
sm_row.prop_enum(context.scene, 'hecl_shader_model', 'ORIGINAL')
sm_row.prop_enum(context.scene, 'hecl_shader_model', 'PBR')
layout.prop(context.scene, 'hecl_mp3_bloom', text='View MP3 Bloom')
for exp_type in hecl_typeS:
if exp_type[0] == context.scene.hecl_type and callable(exp_type[3]):
exp_type[3](self.layout, context)
break
# Light Panel
class hecl_light_panel(bpy.types.Panel):
bl_idname = "DATA_PT_hecl_light"
bl_label = "HECL"
bl_space_type = 'PROPERTIES'
bl_region_type = 'WINDOW'
bl_context = "data"
@classmethod
def poll(cls, context):
return context.light
def draw(self, context):
layout = self.layout
layout.prop(context.light, 'hecl_falloff_constant')
layout.prop(context.light, 'hecl_falloff_linear')
layout.prop(context.light, 'hecl_falloff_quadratic')
# Blender export-type registration
def register_export_type_enum():
bpy.types.Scene.hecl_type = bpy.props.EnumProperty(items=
[tp[:3] for tp in hecl_typeS],
name="HECL Export Type",
description="Selects how active scene is exported by HECL")
# Function for external addons to register export types with HECL
def add_export_type(type_tuple):
type_tup = tuple(type_tuple)
for tp in hecl_typeS:
if tp[0] == type_tup[0]:
raise RuntimeError("Type already registered with HECL")
hecl_types.append(type_tup)
register_export_type_enum()
# Shell command receiver (from HECL driver)
def command(cmdline, writepipeline, writepipebuf):
pass
def mesh_aabb(writepipebuf):
scene = bpy.context.scene
total_min = Vector((99999.0, 99999.0, 99999.0))
total_max = Vector((-99999.0, -99999.0, -99999.0))
if bpy.context.scene.hecl_type == 'ACTOR':
sact_data = bpy.context.scene.hecl_sact_data
for subtype in sact_data.subtypes:
if subtype.linked_mesh in bpy.data.objects:
mesh = bpy.data.objects[subtype.linked_mesh]
minPt = mesh.bound_box[0]
maxPt = mesh.bound_box[6]
for comp in range(3):
if minPt[comp] < total_min[comp]:
total_min[comp] = minPt[comp]
for comp in range(3):
if maxPt[comp] > total_max[comp]:
total_max[comp] = maxPt[comp]
elif bpy.context.scene.hecl_type == 'MESH':
meshName = bpy.context.scene.hecl_mesh_obj
if meshName in bpy.data.objects:
mesh = bpy.data.objects[meshName]
minPt = mesh.bound_box[0]
maxPt = mesh.bound_box[6]
for comp in range(3):
if minPt[comp] < total_min[comp]:
total_min[comp] = minPt[comp]
for comp in range(3):
if maxPt[comp] > total_max[comp]:
total_max[comp] = maxPt[comp]
writepipebuf(struct.pack('fff', total_min[0], total_min[1], total_min[2]))
writepipebuf(struct.pack('fff', total_max[0], total_max[1], total_max[2]))
def shader_model_update(self, context):
value = 0.0
if self.hecl_shader_model == 'PBR':
value = 1.0
bloom_value = 0.0
if self.hecl_mp3_bloom:
bloom_value = 1.0
for shad in ('RetroShader', 'RetroDynamicShader', 'RetroDynamicAlphaShader', 'RetroDynamicCharacterShader'):
if shad in bpy.data.node_groups and 'NewShaderModel' in bpy.data.node_groups[shad].nodes:
bpy.data.node_groups[shad].nodes['NewShaderModel'].outputs[0].default_value = value
for shad in ('RetroShaderMP3',):
if shad in bpy.data.node_groups and 'Mix Shader' in bpy.data.node_groups[shad].nodes:
bpy.data.node_groups[shad].nodes['Mix Shader'].inputs[0].default_value = bloom_value
# Load scene callback
from bpy.app.handlers import persistent
@persistent
def scene_loaded(dummy):
# Hide everything from an external library
if bpy.context.scene.hecl_type != 'FRAME':
for o in bpy.context.scene.objects:
if o.library or (o.data and o.data.library):
o.hide_set(True)
# Show PATH library objects as wireframes
if bpy.context.scene.hecl_type == 'PATH':
if bpy.context.scene.background_set:
for o in bpy.context.scene.background_set.objects:
o.display_type = 'WIRE'
if bpy.context.scene.hecl_path_obj in bpy.context.scene.objects:
path_obj = bpy.context.scene.objects[bpy.context.scene.hecl_path_obj]
path_obj.show_wire = True
# Linked-Child Detection
for scene in bpy.data.scenes:
if scene.hecl_type == 'ACTOR':
actor_data = scene.hecl_sact_data
for subtype in actor_data.subtypes:
if subtype.linked_mesh in bpy.data.objects:
mesh_obj = bpy.data.objects[subtype.linked_mesh]
if subtype.linked_armature in bpy.data.objects:
arm_obj = bpy.data.objects[subtype.linked_armature]
parent_armature(mesh_obj, arm_obj)
for overlay in subtype.overlays:
if overlay.linked_mesh in bpy.data.objects:
mesh_obj = bpy.data.objects[overlay.linked_mesh]
parent_armature(mesh_obj, arm_obj)
# Show only the active mesh and action
if sact.SACTSubtype.SACTSubtype_load.poll(bpy.context):
bpy.ops.scene.sactsubtype_load()
if sact.SACTAction.SACTAction_load.poll(bpy.context):
bpy.ops.scene.sactaction_load()
shader_model_update(bpy.context.scene, bpy.context)
def power_of_distance(context, light, dist):
color = light.color
return dist * dist * context.scene.eevee.light_threshold / max(color[0], max(color[1], color[2]))
def power_of_coefficients(context, light):
epsilon = 1.19e-07
if light.hecl_falloff_linear < epsilon and light.hecl_falloff_quadratic < epsilon:
return 0.0
color = light.color
intens = max(color[0], max(color[1], color[2]))
if light.hecl_falloff_quadratic > epsilon:
if intens <= epsilon:
return 0.0
return power_of_distance(context, light, math.sqrt(intens / (0.0588235 * light.hecl_falloff_quadratic)))
if light.hecl_falloff_linear > epsilon:
return power_of_distance(context, light, intens / (0.0588235 * light.hecl_falloff_linear))
return 0.0
def set_light_falloff(self, context):
self.energy = power_of_coefficients(context, self)
# Registration
def register():
register_export_type_enum()
hmdl.register()
sact.register()
srea.register()
frme.register()
mapa.register()
mapu.register()
path.register()
armature.register()
bpy.utils.register_class(hecl_scene_panel)
bpy.utils.register_class(hecl_light_panel)
bpy.types.Scene.hecl_auto_select = bpy.props.BoolProperty(name='HECL Auto Select', default=True)
bpy.types.Light.hecl_falloff_constant = bpy.props.FloatProperty(
name="HECL Falloff Constant",
description="Constant falloff coefficient",
update=set_light_falloff,
default=1.0,
min=0.0)
bpy.types.Light.hecl_falloff_linear = bpy.props.FloatProperty(
name="HECL Falloff Linear",
description="Linear falloff coefficient",
update=set_light_falloff,
default=0.0,
min=0.0)
bpy.types.Light.hecl_falloff_quadratic = bpy.props.FloatProperty(
name="HECL Falloff Quadratic",
description="Quadratic falloff coefficient",
update=set_light_falloff,
default=0.0,
min=0.0)
bpy.types.Scene.hecl_shader_model = bpy.props.EnumProperty(name="HECL Shader Model",
description="Which shader model to use for rendering",
items=[
('ORIGINAL', "Original", "Close approximation of GameCube materials"),
('PBR', "PBR", "Hybrid PBR materials replacing original reflection")],
update=shader_model_update,
default='ORIGINAL')
bpy.types.Scene.hecl_mp3_bloom = bpy.props.BoolProperty(name="HECL View MP3 Bloom",
description="Preview MP3 bloom factors of model",
update=shader_model_update,
default=False)
bpy.app.handlers.load_post.append(scene_loaded)
Patching.register()
def unregister():
bpy.app.handlers.load_post.remove(scene_loaded)
hmdl.unregister()
sact.unregister()
srea.unregister()
path.unregister()
bpy.utils.unregister_class(hecl_scene_panel)
bpy.utils.unregister_class(hecl_light_panel)
Patching.unregister()
if __name__ == "__main__":
register()

View File

@ -0,0 +1,35 @@
import struct
def cook(writebuf, arm):
writebuf(struct.pack('I', len(arm.bones)))
for bone in arm.bones:
writebuf(struct.pack('I', len(bone.name)))
writebuf(bone.name.encode())
writebuf(struct.pack('fff', bone.head_local[0], bone.head_local[1], bone.head_local[2]))
if bone.parent:
writebuf(struct.pack('i', arm.bones.find(bone.parent.name)))
else:
writebuf(struct.pack('i', -1))
writebuf(struct.pack('I', len(bone.children)))
for child in bone.children:
writebuf(struct.pack('i', arm.bones.find(child.name)))
def draw(layout, context):
layout.prop_search(context.scene, 'hecl_arm_obj', context.scene, 'objects')
if not len(context.scene.hecl_arm_obj):
layout.label(text="Armature not specified", icon='ERROR')
elif context.scene.hecl_arm_obj not in context.scene.objects:
layout.label(text="'"+context.scene.hecl_arm_obj+"' not in scene", icon='ERROR')
else:
obj = context.scene.objects[context.scene.hecl_arm_obj]
if obj.type != 'ARMATURE':
layout.label(text="'"+context.scene.hecl_arm_obj+"' not an 'ARMATURE'", icon='ERROR')
import bpy
def register():
bpy.types.Scene.hecl_arm_obj = bpy.props.StringProperty(
name='HECL Armature Object',
description='Blender Armature Object to export during HECL\'s cook process')

391
hecl/blender/hecl/frme.py Normal file
View File

@ -0,0 +1,391 @@
import bpy, struct, math
from mathutils import Quaternion
def draw(layout, context):
if bpy.context.active_object:
obj = bpy.context.active_object
layout.label(text="Widget Settings:", icon='OBJECT_DATA')
layout.prop_menu_enum(obj, 'retro_widget_type', text='Widget Type')
#layout.prop_search(obj, 'retro_widget_parent', context.scene, 'objects', text='Widget Parent')
row = layout.row(align=True)
row.prop(obj, 'retro_widget_default_visible', text='Visible')
row.prop(obj, 'retro_widget_default_active', text='Active')
row.prop(obj, 'retro_widget_cull_faces', text='Cull Faces')
layout.prop(obj, 'retro_widget_color', text='Color')
layout.prop_menu_enum(obj, 'retro_widget_model_draw_flags', text='Draw Flags')
row = layout.row(align=True)
row.prop(obj, 'retro_widget_is_worker', text='Is Worker')
if obj.retro_widget_is_worker:
row.prop(obj, 'retro_widget_worker_id', text='Worker Id')
if obj.retro_widget_type == 'RETRO_MODL':
layout.prop(obj, 'retro_model_light_mask', text='Light Mask')
elif obj.retro_widget_type == 'RETRO_PANE':
layout.prop(obj, 'retro_pane_dimensions', text='Dimensions')
layout.prop(obj, 'retro_pane_scale_center', text='Center')
elif obj.retro_widget_type == 'RETRO_TXPN':
layout.prop(obj, 'retro_pane_dimensions', text='Dimensions')
layout.prop(obj, 'retro_pane_scale_center', text='Center')
layout.prop(obj, 'retro_textpane_font_path', text='Font Path')
row = layout.row(align=True)
row.prop(obj, 'retro_textpane_word_wrap', text='Word Wrap')
row.prop(obj, 'retro_textpane_horizontal', text='Horizontal')
layout.prop(obj, 'retro_textpane_fill_color', text='Fill Color')
layout.prop(obj, 'retro_textpane_outline_color', text='Outline Color')
layout.prop(obj, 'retro_textpane_block_extent', text='Point Dimensions')
layout.prop(obj, 'retro_textpane_jp_font_path', text='JP Font Path')
layout.prop(obj, 'retro_textpane_jp_font_scale', text='JP Point Dimensions')
layout.prop_menu_enum(obj, 'retro_textpane_hjustification', text='Horizontal Justification')
layout.prop_menu_enum(obj, 'retro_textpane_vjustification', text='Vertical Justification')
elif obj.retro_widget_type == 'RETRO_TBGP':
layout.prop(obj, 'retro_tablegroup_elem_count', text='Element Count')
layout.prop(obj, 'retro_tablegroup_elem_default', text='Default Element')
layout.prop(obj, 'retro_tablegroup_wraparound', text='Wraparound')
elif obj.retro_widget_type == 'RETRO_GRUP':
layout.prop(obj, 'retro_group_default_worker', text='Default Worker')
elif obj.retro_widget_type == 'RETRO_SLGP':
row = layout.row(align=True)
row.prop(obj, 'retro_slider_min', text='Min')
row.prop(obj, 'retro_slider_max', text='Max')
layout.prop(obj, 'retro_slider_default', text='Default')
layout.prop(obj, 'retro_slider_increment', text='Increment')
elif obj.retro_widget_type == 'RETRO_ENRG':
layout.prop(obj, 'retro_energybar_texture_path', text='Energy Bar Texture Path')
elif obj.retro_widget_type == 'RETRO_METR':
layout.prop(obj, 'retro_meter_no_round_up', text='No Round Up')
layout.prop(obj, 'retro_meter_max_capacity', text='Max Capacity')
layout.prop(obj, 'retro_meter_worker_count', text='Worker Count')
elif obj.retro_widget_type == 'RETRO_LITE':
if obj.data and obj.type == 'LIGHT':
layout.prop(obj.data, 'retro_light_index', text='Index')
layout.label(text="Angular Falloff:", icon='LIGHT')
row = layout.row(align=True)
row.prop(obj.data, 'retro_light_angle_constant', text='Constant')
row.prop(obj.data, 'retro_light_angle_linear', text='Linear')
row.prop(obj.data, 'retro_light_angle_quadratic', text='Quadratic')
hjustifications = None
vjustifications = None
model_draw_flags_e = None
def recursive_cook(buffer, obj, version, path_hasher, parent_name):
buffer += struct.pack('>4s', obj.retro_widget_type[6:].encode())
buffer += obj.name.encode() + b'\0'
buffer += parent_name.encode() + b'\0'
buffer += struct.pack('>bbbbffffI',
False,
obj.retro_widget_default_visible,
obj.retro_widget_default_active,
obj.retro_widget_cull_faces,
obj.retro_widget_color[0],
obj.retro_widget_color[1],
obj.retro_widget_color[2],
obj.retro_widget_color[3],
model_draw_flags_e[obj.retro_widget_model_draw_flags])
angle = Quaternion((1.0, 0.0, 0.0), 0)
if obj.retro_widget_type == 'RETRO_CAMR':
angle = Quaternion((1.0, 0.0, 0.0), math.radians(-90.0))
aspect = bpy.context.scene.render.resolution_x / bpy.context.scene.render.resolution_y
if obj.data.type == 'PERSP':
if aspect > 1.0:
fov = math.degrees(math.atan(math.tan(obj.data.angle / 2.0) / aspect)) * 2.0
else:
fov = math.degrees(obj.data.angle)
buffer += struct.pack('>Iffff', 0, fov, aspect, obj.data.clip_start, obj.data.clip_end)
elif obj.data.type == 'ORTHO':
ortho_half = obj.data.ortho_scale / 2.0
buffer += struct.pack('>Iffffff', 1, -ortho_half, ortho_half, ortho_half / aspect,
-ortho_half / aspect, obj.data.clip_start, obj.data.clip_end)
elif obj.retro_widget_type == 'RETRO_MODL':
if len(obj.children) == 0:
raise RuntimeException('Model Widget must have a child model object')
model_obj = obj.children[0]
if model_obj.type != 'MESH':
raise RuntimeException('Model Widget must have a child MESH')
if not model_obj.data.library:
raise RuntimeException('Model Widget must have a linked library MESH')
path = bpy.path.abspath(model_obj.data.library.filepath)
path_hash = path_hasher.hashpath32(path)
buffer += struct.pack('>III', path_hash, 0, obj.retro_model_light_mask)
elif obj.retro_widget_type == 'RETRO_PANE':
buffer += struct.pack('>fffff',
obj.retro_pane_dimensions[0],
obj.retro_pane_dimensions[1],
obj.retro_pane_scale_center[0],
obj.retro_pane_scale_center[1],
obj.retro_pane_scale_center[2])
elif obj.retro_widget_type == 'RETRO_TXPN':
path_hash = path_hasher.hashpath32(obj.retro_textpane_font_path)
buffer += struct.pack('>fffffIbbIIffffffffff',
obj.retro_pane_dimensions[0],
obj.retro_pane_dimensions[1],
obj.retro_pane_scale_center[0],
obj.retro_pane_scale_center[1],
obj.retro_pane_scale_center[2],
path_hash,
obj.retro_textpane_word_wrap,
obj.retro_textpane_horizontal,
hjustifications[obj.retro_textpane_hjustification],
vjustifications[obj.retro_textpane_vjustification],
obj.retro_textpane_fill_color[0],
obj.retro_textpane_fill_color[1],
obj.retro_textpane_fill_color[2],
obj.retro_textpane_fill_color[3],
obj.retro_textpane_outline_color[0],
obj.retro_textpane_outline_color[1],
obj.retro_textpane_outline_color[2],
obj.retro_textpane_outline_color[3],
obj.retro_textpane_block_extent[0],
obj.retro_textpane_block_extent[1])
if version >= 1:
path_hash = path_hasher.hashpath32(obj.retro_textpane_jp_font_path)
buffer += struct.pack('>III',
path_hash,
obj.retro_textpane_jp_font_scale[0],
obj.retro_textpane_jp_font_scale[1])
elif obj.retro_widget_type == 'RETRO_TBGP':
buffer += struct.pack('>HHIHHbbffbfHHHH',
obj.retro_tablegroup_elem_count,
0,
0,
obj.retro_tablegroup_elem_default,
0,
obj.retro_tablegroup_wraparound,
False,
0.0,
0.0,
False,
0.0,
0,
0,
0,
0)
elif obj.retro_widget_type == 'RETRO_GRUP':
buffer += struct.pack('>Hb',
obj.retro_group_default_worker,
False)
elif obj.retro_widget_type == 'RETRO_SLGP':
buffer += struct.pack('>ffff',
obj.retro_slider_min,
obj.retro_slider_max,
obj.retro_slider_default,
obj.retro_slider_increment)
elif obj.retro_widget_type == 'RETRO_ENRG':
path_hash = path_hasher.hashpath32(obj.retro_energybar_texture_path)
buffer += struct.pack('>I', path_hash)
elif obj.retro_widget_type == 'RETRO_METR':
buffer += struct.pack('>bbII',
False,
obj.retro_meter_no_round_up,
obj.retro_meter_max_capacity,
obj.retro_meter_worker_count)
elif obj.retro_widget_type == 'RETRO_LITE':
angle = Quaternion((1.0, 0.0, 0.0), math.radians(-90.0))
type_enum = 0
constant = 1.0
linear = 0.0
quadratic = 0.0
cutoff = 0.0
if obj.data.type == 'POINT':
type_enum = 4
elif obj.data.type == 'SUN':
type_enum = 2
elif obj.data.type == 'SPOT':
type_enum = 0
cutoff = obj.data.spot_size
if obj.data.type == 'POINT' or obj.data.type == 'SPOT':
constant = obj.data.constant_coefficient
linear = obj.data.linear_coefficient
quadratic = obj.data.quadratic_coefficient
buffer += struct.pack('>IffffffI',
type_enum, constant, linear, quadratic,
obj.data.retro_light_angle_constant,
obj.data.retro_light_angle_linear,
obj.data.retro_light_angle_quadratic,
obj.data.retro_light_index)
if obj.data.type == 'SPOT':
buffer += struct.pack('>f', cutoff)
elif obj.retro_widget_type == 'RETRO_IMGP':
if obj.type != 'MESH':
raise RuntimeException('Imagepane Widget must be a MESH')
if len(obj.data.loops) < 4:
raise RuntimeException('Imagepane Widget must be a MESH with 4 verts')
if len(obj.data.uv_layers) < 1:
raise RuntimeException('Imagepane Widget must ba a MESH with a UV layer')
path_hash = 0xffffffff
if len(obj.data.materials):
material = obj.data.materials[0]
if 'Image Texture' in material.node_tree.nodes:
image_node = material.node_tree.nodes['Image Texture']
if image_node.image:
image = image_node.image
path = bpy.path.abspath(image.filepath)
path_hash = path_hasher.hashpath32(path)
buffer += struct.pack('>IIII', path_hash, 0, 0, 4)
for i in range(4):
vi = obj.data.loops[i].vertex_index
co = obj.data.vertices[vi].co
buffer += struct.pack('>fff', co[0], co[1], co[2])
buffer += struct.pack('>I', 4)
for i in range(4):
co = obj.data.uv_layers[0].data[i].uv
buffer += struct.pack('>ff', co[0], co[1])
if obj.retro_widget_is_worker:
buffer += struct.pack('>bH', True, obj.retro_widget_worker_id)
else:
buffer += struct.pack('>b', False)
angMtx = angle.to_matrix() @ obj.matrix_local.to_3x3()
buffer += struct.pack('>fffffffffffffffIH',
obj.matrix_local[0][3],
obj.matrix_local[1][3],
obj.matrix_local[2][3],
angMtx[0][0], angMtx[0][1], angMtx[0][2],
angMtx[1][0], angMtx[1][1], angMtx[1][2],
angMtx[2][0], angMtx[2][1], angMtx[2][2],
0.0, 0.0, 0.0, 0, 0)
ch_list = []
for ch in obj.children:
ch_list.append((ch.pass_index, ch.name))
for s_pair in sorted(ch_list):
ch = bpy.data.objects[s_pair[1]]
if ch.retro_widget_type != 'RETRO_NONE':
recursive_cook(buffer, ch, version, path_hasher, obj.name)
def cook(writepipebuf, version, path_hasher):
global hjustifications, vjustifications, model_draw_flags_e
hjustifications = dict((i[0], i[3]) for i in bpy.types.Object.retro_textpane_hjustification[1]['items'])
vjustifications = dict((i[0], i[3]) for i in bpy.types.Object.retro_textpane_vjustification[1]['items'])
model_draw_flags_e = dict((i[0], i[3]) for i in bpy.types.Object.retro_widget_model_draw_flags[1]['items'])
buffer = bytearray()
buffer += struct.pack('>IIII', 0, 0, 0, 0)
widget_count = 0
for obj in bpy.data.objects:
if obj.retro_widget_type != 'RETRO_NONE':
widget_count += 1
buffer += struct.pack('>I', widget_count)
for obj in bpy.data.objects:
if obj.retro_widget_type != 'RETRO_NONE' and not obj.parent:
recursive_cook(buffer, obj, version, path_hasher, 'kGSYS_DummyWidgetID')
return buffer
# Registration
def register():
frame_widget_types = [
('RETRO_NONE', 'Not a Widget', '', 0),
('RETRO_BWIG', 'Base Widget', '', 1),
('RETRO_CAMR', 'Camera', '', 2),
('RETRO_ENRG', 'Energy Bar', '', 3),
('RETRO_GRUP', 'Group', '', 4),
('RETRO_HWIG', 'Head Widget', '', 5),
('RETRO_IMGP', 'Image Pane', '', 6),
('RETRO_LITE', 'Light', '', 7),
('RETRO_MODL', 'Model', '', 8),
('RETRO_METR', 'Meter', '', 9),
('RETRO_PANE', 'Pane', '', 10),
('RETRO_SLGP', 'Slider Group', '', 11),
('RETRO_TBGP', 'Table Group', '', 12),
('RETRO_TXPN', 'Text Pane', '', 13)]
bpy.types.Object.retro_widget_type = bpy.props.EnumProperty(items=frame_widget_types, name='Retro: FRME Widget Type', default='RETRO_NONE')
model_draw_flags = [
('RETRO_SHADELESS', 'Shadeless', '', 0),
('RETRO_OPAQUE', 'Opaque', '', 1),
('RETRO_ALPHA', 'Alpha', '', 2),
('RETRO_ADDITIVE', 'Additive', '', 3),
('RETRO_ALPHA_ADDITIVE_OVERDRAW', 'Alpha Additive Overdraw', '', 4)]
bpy.types.Object.retro_widget_parent = bpy.props.StringProperty(name='Retro: FRME Widget Parent', description='Refers to internal frame widgets')
bpy.types.Object.retro_widget_use_anim_controller = bpy.props.BoolProperty(name='Retro: Use Animiation Conroller')
bpy.types.Object.retro_widget_default_visible = bpy.props.BoolProperty(name='Retro: Default Visible', description='Sets widget is visible by default')
bpy.types.Object.retro_widget_default_active = bpy.props.BoolProperty(name='Retro: Default Active', description='Sets widget is cases by default')
bpy.types.Object.retro_widget_cull_faces = bpy.props.BoolProperty(name='Retro: Cull Faces', description='Enables face culling')
bpy.types.Object.retro_widget_color = bpy.props.FloatVectorProperty(name='Retro: Color', description='Sets widget color', subtype='COLOR', size=4, min=0.0, max=1.0)
bpy.types.Object.retro_widget_model_draw_flags = bpy.props.EnumProperty(items=model_draw_flags, name='Retro: Model Draw Flags', default='RETRO_ALPHA')
bpy.types.Object.retro_widget_is_worker = bpy.props.BoolProperty(name='Retro: Is Worker Widget', default=False)
bpy.types.Object.retro_widget_worker_id = bpy.props.IntProperty(name='Retro: Worker Widget ID', min=0, default=0)
bpy.types.Object.retro_model_light_mask = bpy.props.IntProperty(name='Retro: Model Light Mask', min=0, default=0)
bpy.types.Object.retro_pane_dimensions = bpy.props.FloatVectorProperty(name='Retro: Pane Dimensions', min=0.0, size=2)
bpy.types.Object.retro_pane_scale_center = bpy.props.FloatVectorProperty(name='Retro: Scale Center', size=3)
bpy.types.Object.retro_textpane_font_path = bpy.props.StringProperty(name='Retro: Font Path')
bpy.types.Object.retro_textpane_word_wrap = bpy.props.BoolProperty(name='Retro: Word Wrap')
bpy.types.Object.retro_textpane_horizontal = bpy.props.BoolProperty(name='Retro: Horizontal', default=True)
bpy.types.Object.retro_textpane_fill_color = bpy.props.FloatVectorProperty(name='Retro: Fill Color', min=0.0, max=1.0, size=4, subtype='COLOR')
bpy.types.Object.retro_textpane_outline_color = bpy.props.FloatVectorProperty(name='Retro: Outline Color', min=0.0, max=1.0, size=4, subtype='COLOR')
bpy.types.Object.retro_textpane_block_extent = bpy.props.FloatVectorProperty(name='Retro: Block Extent', min=0.0, size=2)
bpy.types.Object.retro_textpane_jp_font_path = bpy.props.StringProperty(name='Retro: Japanese Font Path')
bpy.types.Object.retro_textpane_jp_font_scale = bpy.props.IntVectorProperty(name='Retro: Japanese Font Scale', min=0, size=2)
frame_textpane_hjustifications = [
('LEFT', 'Left', '', 0),
('CENTER', 'Center', '', 1),
('RIGHT', 'Right', '', 2),
('FULL', 'Full', '', 3),
('NLEFT', 'Left Normalized', '', 4),
('NCENTER', 'Center Normalized', '', 5),
('NRIGHT', 'Right Normalized', '', 6),
('LEFTMONO', 'Left Monospaced', '', 7),
('CENTERMONO', 'Center Monospaced', '', 8),
('RIGHTMONO', 'Right Monospaced', '', 9)]
bpy.types.Object.retro_textpane_hjustification = bpy.props.EnumProperty(items=frame_textpane_hjustifications, name='Retro: Horizontal Justification', default='LEFT')
frame_textpane_vjustifications = [
('TOP', 'Top', '', 0),
('CENTER', 'Center', '', 1),
('BOTTOM', 'Bottom', '', 2),
('FULL', 'Full', '', 3),
('NTOP', 'Top Normalized', '', 4),
('NCENTER', 'Center Normalized', '', 5),
('NBOTTOM', 'Bottom Normalized', '', 6),
('TOPMONO', 'Top Monospaced', '', 7),
('CENTERMONO', 'Center Monospaced', '', 8),
('BOTTOMMONO', 'Bottom Monospaced', '', 9)]
bpy.types.Object.retro_textpane_vjustification = bpy.props.EnumProperty(items=frame_textpane_vjustifications, name='Retro: Vertical Justification', default='TOP')
bpy.types.Object.retro_tablegroup_elem_count = bpy.props.IntProperty(name='Retro: Table Group Element Count', min=0, default=0)
bpy.types.Object.retro_tablegroup_elem_default = bpy.props.IntProperty(name='Retro: Table Group Default Element', min=0, default=0)
bpy.types.Object.retro_tablegroup_wraparound = bpy.props.BoolProperty(name='Retro: Table Group Wraparound', default=False)
bpy.types.Object.retro_group_default_worker = bpy.props.IntProperty(name='Retro: Group Default Worker', min=0, default=0)
bpy.types.Object.retro_slider_min = bpy.props.FloatProperty(name='Retro: Slider Min', default=0.0)
bpy.types.Object.retro_slider_max = bpy.props.FloatProperty(name='Retro: Slider Max', default=1.0)
bpy.types.Object.retro_slider_default = bpy.props.FloatProperty(name='Retro: Slider Default', default=0.0)
bpy.types.Object.retro_slider_increment = bpy.props.FloatProperty(name='Retro: Slider Increment', min=0.0, default=1.0)
bpy.types.Object.retro_energybar_texture_path = bpy.props.StringProperty(name='Retro: Energy Bar Texture Path')
bpy.types.Object.retro_meter_no_round_up = bpy.props.BoolProperty(name='Retro: No Round Up', default=True)
bpy.types.Object.retro_meter_max_capacity = bpy.props.IntProperty(name='Retro: Max Capacity', min=0, default=100)
bpy.types.Object.retro_meter_worker_count = bpy.props.IntProperty(name='Retro: Worker Count', min=0, default=1)
bpy.types.Light.retro_light_index = bpy.props.IntProperty(name='Retro: Light Index', min=0, default=0)
bpy.types.Light.retro_light_angle_constant = bpy.props.FloatProperty(name='Retro: Light Angle Constant', min=0.0, default=0.0)
bpy.types.Light.retro_light_angle_linear = bpy.props.FloatProperty(name='Retro: Light Angle Linear', min=0.0, default=0.0)
bpy.types.Light.retro_light_angle_quadratic = bpy.props.FloatProperty(name='Retro: Light Angle Quadratic', min=0.0, default=0.0)

View File

@ -0,0 +1,100 @@
import bpy, bmesh, operator, struct
# Function to quantize normals to 15-bit precision
def quant_norm(n):
nf = n.copy()
for i in range(3):
nf[i] = int(nf[i] * 16384) / 16384.0
return nf.freeze()
# Function to quantize lightmap UVs to 15-bit precision
def quant_luv(n):
uf = n.copy()
for i in range(2):
uf[i] = int(uf[i] * 32768) / 32768.0
return uf.freeze()
# Function to output all mesh attribute values
def write_mesh_attrs(writebuf, bm, rna_loops, use_luv, material_slots):
dlay = None
if len(bm.verts.layers.deform):
dlay = bm.verts.layers.deform[0]
clays = []
for cl in range(len(bm.loops.layers.color)):
clays.append(bm.loops.layers.color[cl])
writebuf(struct.pack('I', len(clays)))
luvlay = None
if use_luv:
luvlay = bm.loops.layers.uv[0]
ulays = []
for ul in range(len(bm.loops.layers.uv)):
ulays.append(bm.loops.layers.uv[ul])
writebuf(struct.pack('I', len(ulays)))
# Verts
writebuf(struct.pack('I', len(bm.verts)))
for v in bm.verts:
writebuf(struct.pack('fff', v.co[0], v.co[1], v.co[2]))
if dlay:
sf = tuple(sorted(v[dlay].items()))
writebuf(struct.pack('I', len(sf)))
total_len = 0.0
for ent in sf:
total_len += ent[1]
for ent in sf:
writebuf(struct.pack('If', ent[0], ent[1] / total_len))
else:
writebuf(struct.pack('I', 0))
# Loops
loop_count = 0
for f in bm.faces:
for l in f.loops:
loop_count += 1
writebuf(struct.pack('I', loop_count))
for f in bm.faces:
for l in f.loops:
if rna_loops:
nf = quant_norm(rna_loops[l.index].normal)
else:
nf = quant_norm(l.vert.normal)
writebuf(struct.pack('fff', nf[0], nf[1], nf[2]))
for cl in range(len(clays)):
col = l[clays[cl]]
writebuf(struct.pack('fff', col[0], col[1], col[2]))
for cl in range(len(ulays)):
if luvlay and cl == 0 and material_slots[l.face.material_index].material['retro_lightmapped']:
uv = quant_luv(l[luvlay].uv)
else:
uv = l[ulays[cl]].uv
writebuf(struct.pack('ff', uv[0], uv[1]))
writebuf(struct.pack('IIIII', l.vert.index, l.edge.index, l.face.index,
l.link_loop_next.index, l.link_loop_prev.index))
if l.edge.is_contiguous:
writebuf(struct.pack('II', l.link_loop_radial_next.index, l.link_loop_radial_prev.index))
else:
writebuf(struct.pack('II', 0xffffffff, 0xffffffff))
# Edges
writebuf(struct.pack('I', len(bm.edges)))
for e in bm.edges:
for v in e.verts:
writebuf(struct.pack('I', v.index))
writebuf(struct.pack('I', len(e.link_faces)))
for f in e.link_faces:
writebuf(struct.pack('I', f.index))
writebuf(struct.pack('b', e.is_contiguous))
# Faces
writebuf(struct.pack('I', len(bm.faces)))
for f in bm.faces:
norm = f.normal
writebuf(struct.pack('fff', norm[0], norm[1], norm[2]))
centroid = f.calc_center_bounds()
writebuf(struct.pack('fff', centroid[0], centroid[1], centroid[2]))
writebuf(struct.pack('I', f.material_index))
for l in f.loops:
writebuf(struct.pack('I', l.index))

View File

@ -0,0 +1,164 @@
import bpy, bpy.path, os.path, struct
def get_texture_path(image):
return os.path.normpath(bpy.path.abspath(image.filepath))
SHADER_TYPES = {
'RetroShader': b'RSHD',
'RetroDynamicShader': b'RDYN',
'RetroDynamicAlphaShader': b'RDAL',
'RetroDynamicCharacterShader': b'RCHR',
}
PASS_TYPE = {
'Lightmap': b'LMAP',
'Diffuse': b'DIFF',
'DiffuseMod': b'DIFM',
'Emissive': b'EMIS',
'Specular': b'SPEC',
'ExtendedSpecular': b'ESPC',
'Reflection': b'REFL',
'IndirectTex': b'INDR',
'Alpha': b'ALPH',
'AlphaMod': b'ALPM'
}
def write_chunks(writebuf, mat_obj, mesh_obj):
if not mat_obj.use_nodes:
raise RuntimeError("HMDL *requires* that shader nodes are used; '{0}' does not".format(mat_obj.name))
if 'Output' not in mat_obj.node_tree.nodes or mat_obj.node_tree.nodes['Output'].type != 'GROUP':
raise RuntimeError("HMDL *requires* that an group shader node named 'Output' is present")
# Root (output) node
output_node = mat_obj.node_tree.nodes['Output']
if output_node.node_tree.name not in SHADER_TYPES:
raise RuntimeError("HMDL *requires* one of the RetroShader group nodes for the 'Output' node")
writebuf(SHADER_TYPES[output_node.node_tree.name])
# Count sockets
chunk_count = 0
for inp, def_inp in zip(output_node.inputs, output_node.node_tree.inputs):
if inp.name in PASS_TYPE:
if inp.is_linked:
chunk_count += 1
else:
# Color pass
color_set = False
if inp.type == 'VALUE':
color_set = inp.default_value != def_inp.default_value
else:
for comp, def_comp in zip(inp.default_value, def_inp.default_value):
color_set |= comp != def_comp
if color_set:
chunk_count += 1
writebuf(struct.pack('I', chunk_count))
# Enumerate sockets
for inp, def_inp in zip(output_node.inputs, output_node.node_tree.inputs):
if inp.name in PASS_TYPE:
pass_fourcc = PASS_TYPE[inp.name]
if inp.is_linked:
socket = inp.links[0].from_socket
node = socket.node
if node.type != 'TEX_IMAGE':
raise RuntimeError("HMDL requires all group node inputs connect to Image Texture nodes")
if not node.image:
raise RuntimeError("HMDL texture nodes must specify an image object")
if not node.inputs['Vector'].is_linked:
raise RuntimeError("HMDL texture nodes must have a 'Texture Coordinate', 'UV Map' or 'Group' UV modifier node linked")
# Determine matrix generator type
tex_coord_source = 0xff
uv_anim_type = 0xff
uv_anim_args = []
soc_from = node.inputs['Vector'].links[0].from_socket
if soc_from.node.type == 'GROUP':
if soc_from.node.node_tree.name.startswith('RetroUVMode'):
uv_anim_type = int(soc_from.node.node_tree.name[11:12])
if len(soc_from.node.inputs)-1:
for s in range(len(soc_from.node.inputs)-1):
soc = soc_from.node.inputs[s+1]
if len(soc.links):
raise RuntimeError("UV Modifier nodes may not have parameter links (default values only)")
if soc.type == 'VALUE':
uv_anim_args.append(soc.default_value)
else:
uv_anim_args.append(soc.default_value[0])
uv_anim_args.append(soc.default_value[1])
soc_from = soc_from.node.inputs[0].links[0].from_socket
elif soc_from.node.type == 'UVMAP' or soc_from.node.type == 'TEX_COORD':
pass
else:
raise RuntimeError("HMDL texture nodes must have a 'Texture Coordinate', 'UV Map' or 'Group' UV modifier node linked")
if soc_from.node.type != 'UVMAP' and soc_from.node.type != 'TEX_COORD':
raise RuntimeError("Matrix animator nodes must connect to 'Texture Coordinate' or 'UV Map' node")
# Resolve map and matrix index
node_label = soc_from.node.label
matrix_idx = None
if node_label.startswith('MTX_'):
matrix_idx = int(node_label[4:])
if soc_from.name == 'UV':
if hasattr(soc_from.node, 'uv_map'):
uv_name = soc_from.node.uv_map
uv_idx = mesh_obj.data.uv_layers.find(uv_name)
if uv_idx == -1:
raise RuntimeError('UV Layer "%s" doesn\'t exist' % uv_name)
tex_coord_source = uv_idx + 2
else:
tex_coord_source = 2
elif soc_from.name == 'Normal':
tex_coord_source = 1
elif soc_from.name == 'Window':
tex_coord_source = 0
else:
raise RuntimeError("Only the 'UV', 'Normal' and 'Window' sockets may be used from 'Texture Coordinate' nodes")
alpha = False
if socket.name == 'Alpha':
alpha = True
writebuf(b'PASS')
writebuf(pass_fourcc)
path = get_texture_path(node.image)
writebuf(struct.pack('I', len(path)))
writebuf(path.encode())
writebuf(struct.pack('B', tex_coord_source))
writebuf(struct.pack('B', uv_anim_type))
writebuf(struct.pack('I', len(uv_anim_args)))
for arg in uv_anim_args:
writebuf(struct.pack('f', arg))
writebuf(struct.pack('B', alpha))
else:
# Color pass
color_set = False
if inp.type == 'VALUE':
color_set = inp.default_value != def_inp.default_value
else:
for comp, def_comp in zip(inp.default_value, def_inp.default_value):
color_set |= comp != def_comp
if color_set:
writebuf(b'CLR ')
writebuf(pass_fourcc)
if inp.type == 'VALUE':
writebuf(struct.pack('ffff', inp.default_value, inp.default_value,
inp.default_value, inp.default_value))
else:
writebuf(struct.pack('ffff', inp.default_value[0], inp.default_value[1],
inp.default_value[2], inp.default_value[3]))

View File

@ -0,0 +1,312 @@
import struct, bpy, bmesh
from . import HMDLShader, HMDLMesh
BLEND_TYPES = {
'HECLAdditiveOutput': 2,
'HECLBlendOutput': 1,
'HECLOpaqueOutput': 0,
}
def write_out_material(writebuf, mat, mesh_obj):
writebuf(struct.pack('I', len(mat.name)))
writebuf(mat.name.encode())
writebuf(struct.pack('I', mat.pass_index))
HMDLShader.write_chunks(writebuf, mat, mesh_obj)
prop_count = 0
for prop in mat.items():
if isinstance(prop[1], int):
prop_count += 1
writebuf(struct.pack('I', prop_count))
for prop in mat.items():
if isinstance(prop[1], int):
writebuf(struct.pack('I', len(prop[0])))
writebuf(prop[0].encode())
writebuf(struct.pack('i', prop[1]))
blend_node = mat.node_tree.nodes['Blend']
if blend_node.node_tree.name not in BLEND_TYPES:
raise RuntimeError("HMDL *requires* one of the HMDL*Output group nodes for the 'Blend' node")
writebuf(struct.pack('I', BLEND_TYPES[blend_node.node_tree.name]))
# Takes a Blender 'Mesh' object (not the datablock)
# and performs a one-shot conversion process to HMDL
def cook(writebuf, mesh_obj, use_luv=False):
if mesh_obj.type != 'MESH':
raise RuntimeError("%s is not a mesh" % mesh_obj.name)
# Copy mesh (and apply mesh modifiers with triangulation)
copy_name = mesh_obj.name + "_hmdltri"
copy_mesh = bpy.data.meshes.new_from_object(mesh_obj, preserve_all_data_layers=True,
depsgraph=bpy.context.evaluated_depsgraph_get())
copy_obj = bpy.data.objects.new(copy_name, copy_mesh)
copy_obj.scale = mesh_obj.scale
bpy.context.scene.collection.objects.link(copy_obj)
bpy.ops.object.select_all(action='DESELECT')
bpy.context.view_layer.objects.active = copy_obj
copy_obj.select_set(True)
bpy.ops.object.mode_set(mode='EDIT')
bpy.ops.mesh.select_all(action='SELECT')
bpy.ops.mesh.quads_convert_to_tris()
bpy.ops.mesh.select_all(action='DESELECT')
bpy.context.scene.update_tag()
bpy.ops.object.mode_set(mode='OBJECT')
copy_mesh.calc_normals_split()
rna_loops = copy_mesh.loops
# Send scene matrix
wmtx = mesh_obj.matrix_world
writebuf(struct.pack('ffffffffffffffff',
wmtx[0][0], wmtx[0][1], wmtx[0][2], wmtx[0][3],
wmtx[1][0], wmtx[1][1], wmtx[1][2], wmtx[1][3],
wmtx[2][0], wmtx[2][1], wmtx[2][2], wmtx[2][3],
wmtx[3][0], wmtx[3][1], wmtx[3][2], wmtx[3][3]))
# Filter out useless AABB points and send data
pt = copy_obj.bound_box[0]
writebuf(struct.pack('fff', pt[0], pt[1], pt[2]))
pt = copy_obj.bound_box[6]
writebuf(struct.pack('fff', pt[0], pt[1], pt[2]))
# Create master BMesh
bm_master = bmesh.new()
bm_master.from_mesh(copy_mesh)
# Generate shaders
if mesh_obj.data.hecl_material_count > 0:
writebuf(struct.pack('I', mesh_obj.data.hecl_material_count))
for grp_idx in range(mesh_obj.data.hecl_material_count):
writebuf(struct.pack('I', len(mesh_obj.data.materials)))
for ref_mat in mesh_obj.data.materials:
ref_mat_split = ref_mat.name.split('_')
if len(ref_mat_split) != 3:
raise RuntimeError('material names must follow MAT_%u_%u format')
ref_mat_idx = int(ref_mat_split[2])
found = False
for mat in bpy.data.materials:
if mat.name.endswith('_%u_%u' % (grp_idx, ref_mat_idx)):
write_out_material(writebuf, mat, mesh_obj)
found = True
break
if not found:
raise RuntimeError('uneven material set %d in %s' % (grp_idx, mesh_obj.name))
else:
writebuf(struct.pack('II', 1, len(mesh_obj.data.materials)))
for mat in mesh_obj.data.materials:
write_out_material(writebuf, mat, mesh_obj)
# Output attribute lists
HMDLMesh.write_mesh_attrs(writebuf, bm_master, rna_loops, use_luv, mesh_obj.material_slots)
# Vertex groups
writebuf(struct.pack('I', len(mesh_obj.vertex_groups)))
for vgrp in mesh_obj.vertex_groups:
writebuf(struct.pack('I', len(vgrp.name)))
writebuf(vgrp.name.encode())
# Enumerate custom props
writebuf(struct.pack('I', len(mesh_obj.keys())))
for k in mesh_obj.keys():
writebuf(struct.pack('I', len(k)))
writebuf(k.encode())
val_str = str(mesh_obj[k])
writebuf(struct.pack('I', len(val_str)))
writebuf(val_str.encode())
# Delete copied mesh from scene
bm_master.free()
#bpy.context.scene.objects.unlink(copy_obj)
bpy.data.objects.remove(copy_obj)
bpy.data.meshes.remove(copy_mesh)
def prop_val_from_colmat(name, m):
if name in m:
return m[name]
return False
# Takes a Blender 'Mesh' object (not the datablock)
# and performs a one-shot conversion process to collision geometry
def cookcol(writebuf, mesh_obj):
if mesh_obj.type != 'MESH':
raise RuntimeError("%s is not a mesh" % mesh_obj.name)
# Copy mesh (and apply mesh modifiers with triangulation)
copy_name = mesh_obj.name + "_hmdltri"
copy_mesh = bpy.data.meshes.new_from_object(mesh_obj, preserve_all_data_layers=True,
depsgraph=bpy.context.evaluated_depsgraph_get())
copy_obj = bpy.data.objects.new(copy_name, copy_mesh)
copy_obj.scale = mesh_obj.scale
bpy.context.scene.collection.objects.link(copy_obj)
bpy.ops.object.select_all(action='DESELECT')
bpy.context.view_layer.objects.active = copy_obj
copy_obj.select_set(True)
bpy.ops.object.mode_set(mode='EDIT')
bpy.ops.mesh.select_all(action='SELECT')
bpy.ops.mesh.quads_convert_to_tris()
bpy.ops.mesh.select_all(action='DESELECT')
bpy.context.scene.update_tag()
bpy.ops.object.mode_set(mode='OBJECT')
copy_mesh.calc_normals_split()
rna_loops = copy_mesh.loops
# Send scene matrix
wmtx = mesh_obj.matrix_world
#writebuf(struct.pack('ffffffffffffffff',
#wmtx[0][0], wmtx[0][1], wmtx[0][2], wmtx[0][3],
#wmtx[1][0], wmtx[1][1], wmtx[1][2], wmtx[1][3],
#wmtx[2][0], wmtx[2][1], wmtx[2][2], wmtx[2][3],
#wmtx[3][0], wmtx[3][1], wmtx[3][2], wmtx[3][3]))
# Filter out useless AABB points and send data
#pt = wmtx * Vector(copy_obj.bound_box[0])
#writebuf(struct.pack('fff', pt[0], pt[1], pt[2]))
#pt = wmtx * Vector(copy_obj.bound_box[6])
#writebuf(struct.pack('fff', pt[0], pt[1], pt[2]))
# Send materials
writebuf(struct.pack('I', len(copy_mesh.materials)))
for m in copy_mesh.materials:
writebuf(struct.pack('I', len(m.name)))
writebuf(m.name.encode())
unknown = prop_val_from_colmat('retro_unknown', m)
surfaceStone = prop_val_from_colmat('retro_surface_stone', m)
surfaceMetal = prop_val_from_colmat('retro_surface_metal', m)
surfaceGrass = prop_val_from_colmat('retro_surface_grass', m)
surfaceIce = prop_val_from_colmat('retro_surface_ice', m)
pillar = prop_val_from_colmat('retro_pillar', m)
surfaceMetalGrating = prop_val_from_colmat('retro_surface_metal_grating', m)
surfacePhazon = prop_val_from_colmat('retro_surface_phazon', m)
surfaceDirt = prop_val_from_colmat('retro_surface_dirt', m)
surfaceLava = prop_val_from_colmat('retro_surface_lava', m)
surfaceSPMetal = prop_val_from_colmat('retro_surface_sp_metal', m)
surfaceStoneRock = prop_val_from_colmat('retro_surface_lava_stone', m)
surfaceSnow = prop_val_from_colmat('retro_surface_snow', m)
surfaceMudSlow = prop_val_from_colmat('retro_surface_mud_slow', m)
surfaceFabric = prop_val_from_colmat('retro_surface_fabric', m)
halfPipe = prop_val_from_colmat('retro_half_pipe', m)
surfaceMud = prop_val_from_colmat('retro_surface_mud', m)
surfaceGlass = prop_val_from_colmat('retro_surface_glass', m)
unused3 = prop_val_from_colmat('retro_unused3', m)
unused4 = prop_val_from_colmat('retro_unused4', m)
surfaceShield = prop_val_from_colmat('retro_surface_shield', m)
surfaceSand = prop_val_from_colmat('retro_surface_sand', m)
surfaceMothOrSeedOrganics = prop_val_from_colmat('retro_surface_moth_or_seed_organics', m)
surfaceWeb = prop_val_from_colmat('retro_surface_web', m)
projPassthrough = prop_val_from_colmat('retro_projectile_passthrough', m)
solid = prop_val_from_colmat('retro_solid', m)
noPlatformCollision = prop_val_from_colmat('retro_no_platform_collision', m)
camPassthrough = prop_val_from_colmat('retro_camera_passthrough', m)
surfaceWood = prop_val_from_colmat('retro_surface_wood', m)
surfaceOrganic = prop_val_from_colmat('retro_surface_organic', m)
noEdgeCollision = prop_val_from_colmat('retro_no_edge_collision', m)
surfaceRubber = prop_val_from_colmat('retro_surface_rubber', m)
seeThrough = prop_val_from_colmat('retro_see_through', m)
scanPassthrough = prop_val_from_colmat('retro_scan_passthrough', m)
aiPassthrough = prop_val_from_colmat('retro_ai_passthrough', m)
ceiling = prop_val_from_colmat('retro_ceiling', m)
wall = prop_val_from_colmat('retro_wall', m)
floor = prop_val_from_colmat('retro_floor', m)
aiBlock = prop_val_from_colmat('retro_ai_block', m)
jumpNotAllowed = prop_val_from_colmat('retro_jump_not_allowed', m)
spiderBall = prop_val_from_colmat('retro_spider_ball', m)
screwAttackWallJump = prop_val_from_colmat('retro_screw_attack_wall_jump', m)
writebuf(struct.pack('bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb', unknown, surfaceStone, surfaceMetal, surfaceGrass,
surfaceIce, pillar, surfaceMetalGrating, surfacePhazon, surfaceDirt, surfaceLava, surfaceSPMetal,
surfaceStoneRock, surfaceSnow, surfaceMudSlow, surfaceFabric, halfPipe, surfaceMud, surfaceGlass,
unused3, unused4, surfaceShield, surfaceSand, surfaceMothOrSeedOrganics, surfaceWeb, projPassthrough,
solid, noPlatformCollision, camPassthrough, surfaceWood, surfaceOrganic, noEdgeCollision, surfaceRubber,
seeThrough, scanPassthrough, aiPassthrough, ceiling, wall, floor, aiBlock, jumpNotAllowed, spiderBall,
screwAttackWallJump))
# Send verts
writebuf(struct.pack('I', len(copy_mesh.vertices)))
for v in copy_mesh.vertices:
xfVert = wmtx @ v.co
writebuf(struct.pack('fff', xfVert[0], xfVert[1], xfVert[2]))
# Send edges
writebuf(struct.pack('I', len(copy_mesh.edges)))
for e in copy_mesh.edges:
writebuf(struct.pack('IIb', e.vertices[0], e.vertices[1], e.use_seam))
# Send trianges
writebuf(struct.pack('I', len(copy_mesh.polygons)))
for p in copy_mesh.polygons:
edge_idxs = []
for loopi in p.loop_indices:
edge_idxs.append(copy_mesh.loops[loopi].edge_index)
l0 = copy_mesh.loops[p.loop_indices[0]]
e0 = copy_mesh.edges[l0.edge_index]
flip = l0.vertex_index != e0.vertices[0]
writebuf(struct.pack('IIIIb', edge_idxs[0], edge_idxs[1], edge_idxs[2], p.material_index, flip))
# Delete copied mesh from scene
#bpy.context.scene.objects.unlink(copy_obj)
bpy.data.objects.remove(copy_obj)
bpy.data.meshes.remove(copy_mesh)
def draw(layout, context):
layout.prop_search(context.scene, 'hecl_mesh_obj', context.scene, 'objects')
if not len(context.scene.hecl_mesh_obj):
layout.label(text="Mesh not specified", icon='ERROR')
elif context.scene.hecl_mesh_obj not in context.scene.objects:
layout.label(text="'"+context.scene.hecl_mesh_obj+"' not in scene", icon='ERROR')
else:
obj = context.scene.objects[context.scene.hecl_mesh_obj]
if obj.type != 'MESH':
layout.label(text="'"+context.scene.hecl_mesh_obj+"' not a 'MESH'", icon='ERROR')
layout.prop(obj.data, 'cskr_id')
layout.prop(obj.data, 'hecl_active_material')
layout.prop(obj.data, 'hecl_material_count')
# Material update
def material_update(self, context):
target_idx = self.hecl_active_material
if target_idx >= self.hecl_material_count or target_idx < 0:
return
slot_count = len(self.materials)
for mat_idx in range(slot_count):
for mat in bpy.data.materials:
if mat.name.endswith('_%u_%u' % (target_idx, mat_idx)):
self.materials[mat_idx] = mat
def fake_writebuf(by):
pass
# DEBUG operator
import bpy
class hecl_mesh_operator(bpy.types.Operator):
bl_idname = "scene.hecl_mesh"
bl_label = "DEBUG HECL mesh maker"
bl_description = "Test mesh generation utility"
@classmethod
def poll(cls, context):
return context.object and context.object.type == 'MESH'
def execute(self, context):
cook(fake_writebuf, context.object, -1)
return {'FINISHED'}
import bpy
def register():
bpy.types.Scene.hecl_mesh_obj = bpy.props.StringProperty(
name='HECL Mesh Object',
description='Blender Mesh Object to export during HECL\'s cook process')
bpy.types.Scene.hecl_actor_obj = bpy.props.StringProperty(
name='HECL Actor Object',
description='Blender Empty Object to export during HECL\'s cook process')
bpy.types.Mesh.cskr_id = bpy.props.StringProperty(name='Original CSKR ID')
bpy.types.Mesh.hecl_material_count = bpy.props.IntProperty(name='HECL Material Count', default=0, min=0)
bpy.types.Mesh.hecl_active_material = bpy.props.IntProperty(name='HECL Active Material', default=0, min=0, update=material_update)
bpy.utils.register_class(hecl_mesh_operator)
pass
def unregister():
bpy.utils.unregister_class(hecl_mesh_operator)
pass

460
hecl/blender/hecl/mapa.py Normal file
View File

@ -0,0 +1,460 @@
import bpy, struct, bmesh, operator
from mathutils import Vector
# Function to quantize normals to 15-bit precision
def quant_norm(n):
nf = n.copy()
for i in range(3):
nf[i] = int(nf[i] * 16384) / 16384.0
return nf.freeze()
# Function to quantize lightmap UVs to 15-bit precision
def quant_luv(n):
uf = n.copy()
for i in range(2):
uf[i] = int(uf[i] * 32768) / 32768.0
return uf.freeze()
# Class for building unique sets of vertex attributes for VBO generation
class VertPool:
# Initialize hash-unique index for each available attribute
def __init__(self, bm, rna_loops, use_luv, material_slots):
self.bm = bm
self.rna_loops = rna_loops
self.material_slots = material_slots
self.pos = {}
self.norm = {}
self.skin = {}
self.color = {}
self.uv = {}
self.luv = {}
self.dlay = None
self.clays = []
self.ulays = []
self.luvlay = None
dlay = None
if len(bm.verts.layers.deform):
dlay = bm.verts.layers.deform[0]
self.dlay = dlay
clays = []
for cl in range(len(bm.loops.layers.color)):
clays.append(bm.loops.layers.color[cl])
self.clays = clays
luvlay = None
if use_luv:
luvlay = bm.loops.layers.uv[0]
self.luvlay = luvlay
ulays = []
for ul in range(len(bm.loops.layers.uv)):
ulays.append(bm.loops.layers.uv[ul])
self.ulays = ulays
# Per-vert pool attributes
for v in bm.verts:
pf = v.co.copy().freeze()
if pf not in self.pos:
self.pos[pf] = len(self.pos)
if not rna_loops:
nf = quant_norm(v.normal)
if nf not in self.norm:
self.norm[nf] = len(self.norm)
if dlay:
sf = tuple(sorted(v[dlay].items()))
if sf not in self.skin:
self.skin[sf] = len(self.skin)
# Per-loop pool attributes
for f in bm.faces:
lightmapped = f.material_index < len(material_slots) and \
material_slots[f.material_index].material['retro_lightmapped']
for l in f.loops:
if rna_loops:
nf = quant_norm(rna_loops[l.index].normal)
if nf not in self.norm:
self.norm[nf] = len(self.norm)
for cl in range(len(clays)):
cf = l[clays[cl]].copy().freeze()
if cf not in self.color:
self.color[cf] = len(self.color)
start_uvlay = 0
if use_luv and lightmapped:
start_uvlay = 1
uf = quant_luv(l[luvlay].uv)
if uf not in self.luv:
self.luv[uf] = len(self.luv)
for ul in range(start_uvlay, len(ulays)):
uf = l[ulays[ul]].uv.copy().freeze()
if uf not in self.uv:
self.uv[uf] = len(self.uv)
def write_out(self, writebuf, vert_groups):
writebuf(struct.pack('I', len(self.pos)))
for p in sorted(self.pos.items(), key=operator.itemgetter(1)):
writebuf(struct.pack('fff', p[0][0], p[0][1], p[0][2]))
writebuf(struct.pack('I', len(self.norm)))
for n in sorted(self.norm.items(), key=operator.itemgetter(1)):
writebuf(struct.pack('fff', n[0][0], n[0][1], n[0][2]))
writebuf(struct.pack('II', len(self.clays), len(self.color)))
for c in sorted(self.color.items(), key=operator.itemgetter(1)):
writebuf(struct.pack('fff', c[0][0], c[0][1], c[0][2]))
writebuf(struct.pack('II', len(self.ulays), len(self.uv)))
for u in sorted(self.uv.items(), key=operator.itemgetter(1)):
writebuf(struct.pack('ff', u[0][0], u[0][1]))
luv_count = 0
if self.luvlay is not None:
luv_count = 1
writebuf(struct.pack('II', luv_count, len(self.luv)))
for u in sorted(self.luv.items(), key=operator.itemgetter(1)):
writebuf(struct.pack('ff', u[0][0], u[0][1]))
writebuf(struct.pack('I', len(vert_groups)))
for vgrp in vert_groups:
writebuf(struct.pack('I', len(vgrp.name)))
writebuf(vgrp.name.encode())
writebuf(struct.pack('I', len(self.skin)))
for s in sorted(self.skin.items(), key=operator.itemgetter(1)):
entries = s[0]
writebuf(struct.pack('I', len(entries)))
if len(entries):
total_len = 0.0
for ent in entries:
total_len += ent[1]
for ent in entries:
writebuf(struct.pack('If', ent[0], ent[1] / total_len))
def write_out_map(self, writebuf):
writebuf(struct.pack('I', len(self.pos)))
for p in sorted(self.pos.items(), key=operator.itemgetter(1)):
writebuf(struct.pack('fff', p[0][0], p[0][1], p[0][2]))
def get_pos_idx(self, vert):
pf = vert.co.copy().freeze()
return self.pos[pf]
def get_norm_idx(self, loop):
if self.rna_loops:
nf = quant_norm(self.rna_loops[loop.index].normal)
else:
nf = quant_norm(loop.vert.normal)
return self.norm[nf]
def get_skin_idx(self, vert):
if not self.dlay:
return 0
sf = tuple(sorted(vert[self.dlay].items()))
return self.skin[sf]
def get_color_idx(self, loop, cidx):
cf = loop[self.clays[cidx]].copy().freeze()
return self.color[cf]
def get_uv_idx(self, loop, uidx):
if self.luvlay is not None and uidx == 0:
if self.material_slots[loop.face.material_index].material['retro_lightmapped']:
uf = quant_luv(loop[self.luvlay].uv)
return self.luv[uf]
uf = loop[self.ulays[uidx]].uv.copy().freeze()
return self.uv[uf]
def loops_contiguous(self, la, lb):
if la.vert != lb.vert:
return False
if self.get_norm_idx(la) != self.get_norm_idx(lb):
return False
for cl in range(len(self.clays)):
if self.get_color_idx(la, cl) != self.get_color_idx(lb, cl):
return False
for ul in range(len(self.ulays)):
if self.get_uv_idx(la, ul) != self.get_uv_idx(lb, ul):
return False
return True
def splitable_edge(self, edge):
if len(edge.link_faces) < 2:
return False
for v in edge.verts:
found = None
for f in edge.link_faces:
for l in f.loops:
if l.vert == v:
if not found:
found = l
break
else:
if not self.loops_contiguous(found, l):
return True
break
return False
def loop_out(self, writebuf, loop):
writebuf(struct.pack('B', 1))
writebuf(struct.pack('II', self.get_pos_idx(loop.vert), self.get_norm_idx(loop)))
for cl in range(len(self.clays)):
writebuf(struct.pack('I', self.get_color_idx(loop, cl)))
for ul in range(len(self.ulays)):
writebuf(struct.pack('I', self.get_uv_idx(loop, ul)))
sp = struct.pack('I', self.get_skin_idx(loop.vert))
writebuf(sp)
def null_loop_out(self, writebuf):
writebuf(struct.pack('B', 1))
writebuf(struct.pack('I', 0xffffffff))
def loop_out_map(self, writebuf, loop):
writebuf(struct.pack('B', 1))
writebuf(struct.pack('I', self.get_pos_idx(loop.vert)))
def vert_out_map(self, writebuf, vert):
writebuf(struct.pack('B', 1))
writebuf(struct.pack('I', self.get_pos_idx(vert)))
def strip_next_loop(prev_loop, out_count):
if out_count & 1:
radial_loop = prev_loop.link_loop_radial_next
loop = radial_loop.link_loop_prev
return loop, loop
else:
radial_loop = prev_loop.link_loop_radial_prev
loop = radial_loop.link_loop_next
return loop.link_loop_next, loop
def recursive_faces_islands(list_out, rem_list, face):
if face not in rem_list:
return []
list_out.append(face)
rem_list.remove(face)
next_faces = []
for e in face.edges:
if not e.is_contiguous or e.seam:
continue
for f in e.link_faces:
if f == face:
continue
next_faces.append(f)
return next_faces
def cook(writebuf, mesh_obj):
if mesh_obj.type != 'MESH':
raise RuntimeError("%s is not a mesh" % mesh_obj.name)
obj_vismodes = dict((i[0], i[3]) for i in bpy.types.Object.retro_mapobj_vis_mode[1]['items'])
# Write out visibility type
vis_types = dict((i[0], i[3]) for i in bpy.types.Scene.retro_map_vis_mode[1]['items'])
writebuf(struct.pack('I', vis_types[bpy.context.scene.retro_map_vis_mode]))
# Copy mesh (and apply mesh modifiers with triangulation)
copy_name = mesh_obj.name + "_hmdltri"
copy_mesh = bpy.data.meshes.new_from_object(mesh_obj, preserve_all_data_layers=True,
depsgraph=bpy.context.evaluated_depsgraph_get())
copy_obj = bpy.data.objects.new(copy_name, copy_mesh)
copy_obj.scale = mesh_obj.scale
bpy.context.scene.collection.objects.link(copy_obj)
bpy.ops.object.select_all(action='DESELECT')
bpy.context.view_layer.objects.active = copy_obj
copy_obj.select_set(True)
bpy.ops.object.mode_set(mode='EDIT')
bpy.ops.mesh.select_all(action='SELECT')
bpy.ops.mesh.quads_convert_to_tris()
bpy.ops.mesh.select_all(action='DESELECT')
bpy.context.scene.update_tag()
bpy.ops.object.mode_set(mode='OBJECT')
copy_mesh.calc_normals_split()
rna_loops = copy_mesh.loops
# Create master BMesh and VertPool
bm_master = bmesh.new()
bm_master.from_mesh(copy_obj.data)
vert_pool = VertPool(bm_master, rna_loops, False, mesh_obj.material_slots)
# Output vert pool
vert_pool.write_out_map(writebuf)
# Create map surfaces and borders
faces_rem = list(bm_master.faces)
loop_iter = 0
loop_ranges = []
while len(faces_rem):
island_faces = []
faces = [faces_rem[0]]
while len(faces):
next_faces = []
ret_faces = None
for f in faces:
ret_faces = recursive_faces_islands(island_faces, faces_rem, f)
if ret_faces == False:
break
next_faces.extend(ret_faces)
if ret_faces == False:
break
faces = next_faces
# island_faces now holds one island (map edge delimited)
prev_loop_emit = None
loop_set = set()
edge_set = set()
out_count = 0
loop_count = 0
while len(island_faces):
sel_lists_local = []
restore_out_count = out_count
for start_face in island_faces:
for l in start_face.loops:
out_count = restore_out_count
island_local = list(island_faces)
if out_count & 1:
prev_loop = l.link_loop_prev
loop = prev_loop.link_loop_prev
sel_list = [l, prev_loop, loop]
prev_loop = loop
else:
prev_loop = l.link_loop_next
loop = prev_loop.link_loop_next
sel_list = [l, prev_loop, loop]
out_count += 3
island_local.remove(start_face)
while True:
if not prev_loop.edge.is_contiguous or prev_loop.edge.seam:
break
loop, prev_loop = strip_next_loop(prev_loop, out_count)
face = loop.face
if face not in island_local:
break
sel_list.append(loop)
island_local.remove(face)
out_count += 1
sel_lists_local.append((sel_list, island_local, out_count))
max_count = 0
max_sl = None
max_island_faces = None
for sl in sel_lists_local:
if len(sl[0]) > max_count:
max_count = len(sl[0])
max_sl = sl[0]
max_island_faces = sl[1]
out_count = sl[2]
island_faces = max_island_faces
if prev_loop_emit:
vert_pool.loop_out_map(writebuf, prev_loop_emit)
vert_pool.loop_out_map(writebuf, max_sl[0])
loop_count += 2
loop_set.add(prev_loop_emit)
loop_set.add(max_sl[0])
loop_count += len(max_sl)
for loop in max_sl:
vert_pool.loop_out_map(writebuf, loop)
prev_loop_emit = loop
loop_set.add(loop)
for edge in loop.face.edges:
if edge.seam:
edge_set.add(edge)
# Create island surface with edges
if len(edge_set):
trace_edge = edge_set.pop()
else:
trace_edge = None
edge_ranges = []
edge_iter = loop_iter + loop_count
while trace_edge:
edge_count = 0
vert_pool.vert_out_map(writebuf, trace_edge.verts[0])
vert_pool.vert_out_map(writebuf, trace_edge.verts[1])
edge_count += 2
next_vert = trace_edge.verts[1]
found_edge = True
while found_edge:
found_edge = False
for edge in next_vert.link_edges:
if edge in edge_set:
edge_set.remove(edge)
next_vert = edge.other_vert(next_vert)
vert_pool.vert_out_map(writebuf, next_vert)
edge_count += 1
found_edge = True
break
if len(edge_set):
trace_edge = edge_set.pop()
else:
trace_edge = None
edge_ranges.append((edge_iter, edge_count))
edge_iter += edge_count
pos_avg = Vector()
norm_avg = Vector()
if len(loop_set):
for loop in loop_set:
pos_avg += loop.vert.co
norm_avg += loop.vert.normal
pos_avg /= len(loop_set)
norm_avg /= len(loop_set)
norm_avg.normalize()
loop_ranges.append((loop_iter, loop_count, edge_ranges, pos_avg, norm_avg))
loop_iter = edge_iter
# No more surfaces
writebuf(struct.pack('B', 0))
# Write out loop ranges and averages
writebuf(struct.pack('I', len(loop_ranges)))
for loop_range in loop_ranges:
writebuf(struct.pack('fff', loop_range[3][0], loop_range[3][1], loop_range[3][2]))
writebuf(struct.pack('fff', loop_range[4][0], loop_range[4][1], loop_range[4][2]))
writebuf(struct.pack('II', loop_range[0], loop_range[1]))
writebuf(struct.pack('I', len(loop_range[2])))
for edge_range in loop_range[2]:
writebuf(struct.pack('II', edge_range[0], edge_range[1]))
# Write out mappable objects
poi_count = 0
for obj in bpy.context.scene.objects:
if obj.retro_mappable_type != -1:
poi_count += 1
writebuf(struct.pack('I', poi_count))
for obj in bpy.context.scene.objects:
if obj.retro_mappable_type != -1:
writebuf(struct.pack('III',
obj.retro_mappable_type, obj_vismodes[obj.retro_mapobj_vis_mode], int(obj.retro_mappable_sclyid, 0)))
writebuf(struct.pack('ffffffffffffffff',
obj.matrix_world[0][0], obj.matrix_world[0][1], obj.matrix_world[0][2], obj.matrix_world[0][3],
obj.matrix_world[1][0], obj.matrix_world[1][1], obj.matrix_world[1][2], obj.matrix_world[1][3],
obj.matrix_world[2][0], obj.matrix_world[2][1], obj.matrix_world[2][2], obj.matrix_world[2][3],
obj.matrix_world[3][0], obj.matrix_world[3][1], obj.matrix_world[3][2], obj.matrix_world[3][3]))
def draw(layout, context):
obj = context.active_object
layout.prop(context.scene, 'retro_map_vis_mode', text='Visibility Mode')
if obj and obj.retro_mappable_type != -1:
layout.prop(obj, 'retro_mappable_type', text='Object Type')
layout.prop(obj, 'retro_mapobj_vis_mode', text='Object Visibility Mode')
layout.prop(obj, 'retro_mappable_sclyid', text='Object ID')
def register():
bpy.types.Object.retro_mappable_type = bpy.props.IntProperty(name='Retro: MAPA object type', default=-1)
bpy.types.Object.retro_mappable_sclyid = bpy.props.StringProperty(name='Retro: MAPA object SCLY ID')
bpy.types.Scene.retro_map_vis_mode = bpy.props.EnumProperty(items=[('ALWAYS', 'Always', 'Always Visible', 0),
('MAPSTATIONORVISIT', 'Map Station or Visit', 'Visible after Map Station or Visit', 1),
('VISIT', 'Visit', 'Visible after Visit', 2),
('NEVER', 'Never', 'Never Visible', 3)],
name='Retro: Map Visibility Mode')
bpy.types.Object.retro_mapobj_vis_mode = bpy.props.EnumProperty(items=[('ALWAYS', 'Always', 'Always Visible', 0),
('MAPSTATIONORVISIT', 'Map Station or Visit', 'Visible after Map Station or Visit', 1),
('VISIT', 'Visit', 'Visible after Door Visit', 2),
('NEVER', 'Never', 'Never Visible', 3),
('MAPSTATIONORVISIT2', 'Map Station or Visit 2', 'Visible after Map Station or Visit', 4)],
name='Retro: Map Object Visibility Mode')

55
hecl/blender/hecl/mapu.py Normal file
View File

@ -0,0 +1,55 @@
import bpy, os, struct
def cook(writebuf):
found_lib = False
for obj in bpy.context.scene.objects:
if obj.data and obj.data.library:
path = os.path.normpath(bpy.path.abspath(obj.data.library.filepath))
writebuf(struct.pack('I', len(path)))
writebuf(path.encode())
found_lib = True
break
if not found_lib:
raise RuntimeError('No hexagon segments present')
world_count = 0
for obj in bpy.context.scene.objects:
if not obj.parent and obj.type == 'EMPTY':
world_count += 1
writebuf(struct.pack('I', world_count))
for obj in bpy.context.scene.objects:
if not obj.parent and obj.type == 'EMPTY':
writebuf(struct.pack('I', len(obj.name)))
writebuf(obj.name.encode())
writebuf(struct.pack('ffffffffffffffff',
obj.matrix_local[0][0], obj.matrix_local[0][1], obj.matrix_local[0][2], obj.matrix_local[0][3],
obj.matrix_local[1][0], obj.matrix_local[1][1], obj.matrix_local[1][2], obj.matrix_local[1][3],
obj.matrix_local[2][0], obj.matrix_local[2][1], obj.matrix_local[2][2], obj.matrix_local[2][3],
obj.matrix_local[3][0], obj.matrix_local[3][1], obj.matrix_local[3][2], obj.matrix_local[3][3]))
writebuf(struct.pack('I', len(obj.children)))
for child in obj.children:
writebuf(struct.pack('ffffffffffffffff',
child.matrix_local[0][0], child.matrix_local[0][1], child.matrix_local[0][2], child.matrix_local[0][3],
child.matrix_local[1][0], child.matrix_local[1][1], child.matrix_local[1][2], child.matrix_local[1][3],
child.matrix_local[2][0], child.matrix_local[2][1], child.matrix_local[2][2], child.matrix_local[2][3],
child.matrix_local[3][0], child.matrix_local[3][1], child.matrix_local[3][2], child.matrix_local[3][3]))
writebuf(struct.pack('ffff', obj.retro_mapworld_color[0], obj.retro_mapworld_color[1],
obj.retro_mapworld_color[2], obj.retro_mapworld_color[3]))
writebuf(struct.pack('I', len(obj.retro_mapworld_path)))
writebuf(obj.retro_mapworld_path.encode())
def draw(layout, context):
obj = context.active_object
if not obj:
return
while obj.parent:
obj = obj.parent
layout.prop(obj, 'retro_mapworld_color', text='Color')
layout.prop(obj, 'retro_mapworld_path', text='Path')
# Registration
def register():
bpy.types.Object.retro_mapworld_color = bpy.props.FloatVectorProperty(name='Retro: MapWorld Color',\
description='Sets map world color', subtype='COLOR', size=4, min=0.0, max=1.0)
bpy.types.Object.retro_mapworld_path = bpy.props.StringProperty(name='Retro: MapWorld Path', description='Sets path to World root')

388
hecl/blender/hecl/path.py Normal file
View File

@ -0,0 +1,388 @@
import bpy, gpu, sys, bmesh, struct
from mathutils import Vector
from gpu_extras.batch import batch_for_shader
# Convenience class that automatically brings active edit mesh's face into scope for get/set
class HeightRef:
def __init__(self):
self.bm = None
context = bpy.context
obj = context.scene.objects[context.scene.hecl_path_obj]
if obj.type != 'MESH':
return
if context.edit_object != obj:
return
bm = bmesh.from_edit_mesh(obj.data)
if 'Height' not in bm.faces.layers.float:
return
self.height_lay = bm.faces.layers.float['Height']
self.bm = bm
@property
def ready(self):
return self.bm is not None and self.bm.faces.active is not None
@property
def value(self):
if self.ready:
return self.bm.faces.active[self.height_lay]
@value.setter
def value(self, value):
if self.ready:
for f in self.bm.faces:
if f.select:
f[self.height_lay] = value
# Active edit face height get
def get_height(self):
return HeightRef().value
# Selected edit face(s) height set
def set_height(self, val):
HeightRef().value = val
for ar in bpy.context.screen.areas:
if ar.type == 'VIEW_3D':
ar.tag_redraw()
# Simple AABB class
class AABB:
def __init__(self):
self.min = Vector((999999.0, 999999.0, 999999.0))
self.max = Vector((-999999.0, -999999.0, -999999.0))
def accumulate(self, vec):
for i in range(3):
if vec[i] < self.min[i]:
self.min[i] = vec[i]
if vec[i] > self.max[i]:
self.max[i] = vec[i]
def isValid(self):
for i in range(3):
if self.min[i] > self.max[i]:
return False
return True
# Simple adjacency calculator
class AdjacencySet:
def __init__(self, bm, mesh_obj, type_mask):
self.faces = {}
for f in bm.faces:
material = mesh_obj.material_slots[f.material_index].material
if (material.retro_path_type_mask & type_mask) == 0:
continue
face_set = set()
face_set.add(f)
# Breadth-first loop to avoid crashing python with large recursion
next_level = [f]
while len(next_level):
next_next_level = []
for of in next_level:
for e in of.edges:
for of2 in e.link_faces:
if of2 == of:
continue
if of2 not in face_set:
material = mesh_obj.material_slots[of2.material_index].material
if material.retro_path_type_mask & type_mask:
face_set.add(of2)
next_next_level.append(of2)
next_level = next_next_level
self.faces[f] = face_set
def has_adjacency(self, face_a, face_b):
if face_a not in self.faces:
return False
return face_b in self.faces[face_a]
# Cooking entry point
def cook(writebuf, mesh_obj):
ba = bytearray()
# Version 4
ba += struct.pack('>I', 4)
bm = bmesh.new()
bm.from_mesh(mesh_obj.data)
bm.faces.ensure_lookup_table()
height_lay = None
if 'Height' in bm.faces.layers.float:
height_lay = bm.faces.layers.float['Height']
# Gather immediate adjacencies
node_list = []
link_list = []
region_list = []
up_vec = Vector((0.0, 0.0, 1.0))
for f in bm.faces:
start_loop = f.loops[0]
cur_loop = start_loop
node_idx = 0
start_node = len(node_list)
start_link = len(link_list)
while True:
node_list.append([cur_loop, up_vec])
nv1 = cur_loop.vert.co
cur_loop = cur_loop.link_loop_prev
nv0 = cur_loop.vert.co
node_list[-1][1] = (nv0 - nv1).cross(up_vec).normalized()
for other_face in cur_loop.edge.link_faces:
if other_face == f:
continue
link_list.append((node_idx, other_face.index, cur_loop.edge.calc_length()))
node_idx += 1
if cur_loop == start_loop:
break
region_list.append((f, range(start_node, len(node_list)), range(start_link, len(link_list))))
# Emit nodes
ba += struct.pack('>I', len(node_list))
for n in node_list:
v = n[0].vert
normal = n[1]
ba += struct.pack('>ffffff', v.co[0], v.co[1], v.co[2], normal[0], normal[1], normal[2])
# Emit links
ba += struct.pack('>I', len(link_list))
for l in link_list:
ba += struct.pack('>IIff', l[0], l[1], l[2], 1.0 / l[2])
# Emit regions
ba += struct.pack('>I', len(region_list))
for r in region_list:
material = mesh_obj.material_slots[r[0].material_index].material
height = 1.0
if height_lay is not None:
height = r[0][height_lay]
center = r[0].calc_center_median_weighted()
aabb = AABB()
for v in r[0].verts:
aabb.accumulate(v.co)
aabb.accumulate(v.co + Vector((0.0, 0.0, height)))
ba += struct.pack('>IIIIHHffffIfffffffffI', len(r[1]), r[1].start, len(r[2]), r[2].start,
material.retro_path_idx_mask, material.retro_path_type_mask,
height, r[0].normal[0], r[0].normal[1], r[0].normal[2],
r[0].index, center[0], center[1], center[2],
aabb.min[0], aabb.min[1], aabb.min[2],
aabb.max[0], aabb.max[1], aabb.max[2],
r[0].index)
num_regions = len(region_list)
total_adjacencies = num_regions * (num_regions - 1) // 2
num_words = (total_adjacencies + 31) // 32
# Find ground adjacencies
words = [0] * num_words
ground_adjacencies = AdjacencySet(bm, mesh_obj, 0x1)
for i in range(num_regions):
for j in range(num_regions):
if i == j:
continue
i1 = i
i2 = j
if i1 > i2:
continue
if ground_adjacencies.has_adjacency(bm.faces[i], bm.faces[j]):
rem_regions = num_regions - i1
rem_connections = rem_regions * (rem_regions - 1) // 2
bit = total_adjacencies - rem_connections + i2 - (i1 + 1)
words[bit // 32] |= 1 << (bit % 32)
for w in words:
ba += struct.pack('>I', w)
# Find flyer adjacencies
words = [0] * num_words
flyer_adjacencies = AdjacencySet(bm, mesh_obj, 0x3)
for i in range(num_regions):
for j in range(num_regions):
if i == j:
continue
i1 = i
i2 = j
if i1 > i2:
continue
if flyer_adjacencies.has_adjacency(bm.faces[i], bm.faces[j]):
rem_regions = num_regions - i1
rem_connections = rem_regions * (rem_regions - 1) // 2
bit = total_adjacencies - rem_connections + i2 - (i1 + 1)
words[bit // 32] |= 1 << (bit % 32)
for w in words:
ba += struct.pack('>I', w)
# Unused zero bits
for i in range((((num_regions * num_regions) + 31) // 32 - num_words) * 2):
ba += struct.pack('>I', 0)
# Empty octree (to be generated by hecl)
ba += struct.pack('>II', 0, 0)
# Write out
writebuf(struct.pack('I', len(ba)))
writebuf(ba)
try:
line_shader = gpu.shader.from_builtin('3D_FLAT_COLOR')
except:
pass
# Line draw helper
def draw_line_3d(pos_vbo, color_vbo, color, start, end):
pos_vbo.append(start)
pos_vbo.append(end)
color_vbo.append(color)
color_vbo.append(color)
# Draw RNA polygon
def draw_poly(pos_vbo, color_vbo, p, obj, obj_mtx, height, top_color, side_color):
for ek in p.edge_keys:
co0 = obj_mtx @ obj.data.vertices[ek[0]].co
co1 = obj_mtx @ obj.data.vertices[ek[1]].co
draw_line_3d(pos_vbo, color_vbo, top_color, co0 + Vector((0.0, 0.0, height)),
co1 + Vector((0.0, 0.0, height)))
for vk in p.vertices:
co = obj_mtx @ obj.data.vertices[vk].co
draw_line_3d(pos_vbo, color_vbo, side_color, co, co + Vector((0.0, 0.0, height)))
# Draw bmesh face
def draw_face(pos_vbo, color_vbo, f, obj_mtx, height, top_color, side_color):
for e in f.edges:
co0 = obj_mtx @ e.verts[0].co
co1 = obj_mtx @ e.verts[1].co
draw_line_3d(pos_vbo, color_vbo, top_color, co0 + Vector((0.0, 0.0, height)),
co1 + Vector((0.0, 0.0, height)))
for v in f.verts:
co = obj_mtx @ v.co
draw_line_3d(pos_vbo, color_vbo, side_color, co, co + Vector((0.0, 0.0, height)))
# Viewport hook callback
def draw_callback_3d(self, context):
# object locations
if context.scene.hecl_path_obj not in context.scene.objects:
return
obj = context.scene.objects[context.scene.hecl_path_obj]
if obj.type != 'MESH':
return
obj_mtx = obj.matrix_world
bm = None
height_lay = None
if obj == context.edit_object:
bm = bmesh.from_edit_mesh(obj.data)
if 'Height' in bm.faces.layers.float:
height_lay = bm.faces.layers.float['Height']
else:
if 'Height' in obj.data.polygon_layers_float:
height_lay = obj.data.polygon_layers_float['Height']
pos_vbo = []
color_vbo = []
# Deselected colors
top_color = (0.0, 0.0, 1.0, 0.7)
side_color = (1.0, 0.0, 0.0, 0.7)
if bm is not None:
for f in bm.faces:
height = 1.0
if height_lay is not None:
selected = f.select
if selected:
continue
height = f[height_lay]
draw_face(pos_vbo, color_vbo, f, obj_mtx, height, top_color, side_color)
else:
for p in obj.data.polygons:
height = 1.0
if height_lay is not None:
height = height_lay.data[p.index].value
draw_poly(pos_vbo, color_vbo, p, obj, obj_mtx, height, top_color, side_color)
# Selected colors
if bm is not None:
top_color = (1.0, 0.0, 1.0, 0.7)
side_color = (0.0, 1.0, 0.0, 0.7)
# Avoid z-fighting on selected lines
#bgl.glDepthRange(-0.001, 0.999)
for f in bm.faces:
if height_lay is not None:
selected = f.select
if not selected:
continue
height = f[height_lay]
draw_face(pos_vbo, color_vbo, f, obj_mtx, height, top_color, side_color)
#bgl.glEnd()
#bgl.glDepthRange(0.0, 1.0)
line_shader.bind()
batch = batch_for_shader(line_shader, 'LINES', {"pos": pos_vbo, "color": color_vbo})
batch.draw(line_shader)
# Toggle height viz button
class PathHeightDrawOperator(bpy.types.Operator):
bl_idname = "view3d.toggle_path_height_visualization"
bl_label = "Toggle PATH height visualization"
_handle_3d = None
def execute(self, context):
# the arguments we pass the the callback
args = (self, context)
# Add the region OpenGL drawing callback
# draw in view space with 'POST_VIEW' and 'PRE_VIEW'
if self._handle_3d is None:
PathHeightDrawOperator._handle_3d = \
bpy.types.SpaceView3D.draw_handler_add(draw_callback_3d, args, 'WINDOW', 'POST_VIEW')
else:
bpy.types.SpaceView3D.draw_handler_remove(PathHeightDrawOperator._handle_3d, 'WINDOW')
PathHeightDrawOperator._handle_3d = None
for ar in bpy.context.screen.areas:
if ar.type == 'VIEW_3D':
ar.tag_redraw()
return {'FINISHED'}
# Toggle background wire button
class PathBackgroundWireframeOperator(bpy.types.Operator):
bl_idname = "view3d.toggle_path_background_wireframe"
bl_label = "Toggle PATH background wireframe"
def execute(self, context):
if context.scene.background_set:
to_wire = False
for o in context.scene.background_set.objects:
if o.display_type != 'WIRE':
to_wire = True
break
if to_wire:
for o in context.scene.background_set.objects:
o.display_type = 'WIRE'
else:
for o in context.scene.background_set.objects:
o.display_type = 'TEXTURED'
return {'FINISHED'}
# Edit panel
def draw(layout, context):
layout.prop_search(context.scene, 'hecl_path_obj', context.scene, 'objects')
layout.operator('view3d.toggle_path_background_wireframe', text='Toggle Background Wire', icon='SHADING_WIRE')
if PathHeightDrawOperator._handle_3d:
icon = 'HIDE_OFF'
else:
icon = 'HIDE_ON'
layout.operator('view3d.toggle_path_height_visualization', text='Toggle Height Viz', icon=icon)
if HeightRef().ready:
layout.prop(context.window_manager, 'hecl_height_prop', text='Height')
# Registration
def register():
bpy.types.Material.retro_path_idx_mask = bpy.props.IntProperty(name='Retro: Path Index Mask')
bpy.types.Material.retro_path_type_mask = bpy.props.IntProperty(name='Retro: Path Type Mask')
bpy.types.WindowManager.hecl_height_prop = bpy.props.FloatProperty(
get=get_height, set=set_height, options={'HIDDEN'})
bpy.types.Scene.hecl_path_obj = bpy.props.StringProperty(
name='HECL Path Object',
description='Blender Mesh Object to export during PATH\'s cook process')
bpy.utils.register_class(PathHeightDrawOperator)
bpy.utils.register_class(PathBackgroundWireframeOperator)
def unregister():
bpy.utils.unregister_class(PathHeightDrawOperator)
bpy.utils.unregister_class(PathBackgroundWireframeOperator)

View File

@ -0,0 +1,208 @@
'''
This file provides a means to encode animation key-channels
in an interleaved, sparse array for use by the runtime
'''
import re
import struct
import mathutils
# Regex RNA path matchers
scale_matcher = re.compile(r'pose.bones\["(\S+)"\].scale')
rotation_matcher = re.compile(r'pose.bones\["(\S+)"\].rotation')
location_matcher = re.compile(r'pose.bones\["(\S+)"\].location')
# Effect transform modes
EFFECT_XF_MODES = {'STATIONARY':0, 'WORLD':1, 'LOCAL':2}
# Generate animation info
def generate_animation_info(action, res_db, rani_db_id, arg_package, endian_char='<'):
# Set of frame indices
frame_set = set()
# Set of unique bone names
bone_set = set()
# Scan through all fcurves to build animated bone set
for fcurve in action.fcurves:
data_path = fcurve.data_path
scale_match = scale_matcher.match(data_path)
rotation_match = rotation_matcher.match(data_path)
location_match = location_matcher.match(data_path)
if scale_match:
bone_set.add(scale_match.group(1))
elif rotation_match:
bone_set.add(rotation_match.group(1))
elif location_match:
bone_set.add(location_match.group(1))
else:
continue
# Count unified keyframes for interleaving channel data
for key in fcurve.keyframe_points:
frame_set.add(int(key.co[0]))
# Relate fcurves per-frame / per-bone and assemble data
key_stream = bytearray()
key_stream += struct.pack(endian_char + 'II', len(frame_set), len(bone_set))
duration = action.frame_range[1] / action.hecl_fps
interval = 1.0 / action.hecl_fps
key_stream += struct.pack(endian_char + 'ff', duration, interval)
# Generate keyframe bitmap
fr = int(round(action.frame_range[1]))
key_stream += struct.pack(endian_char + 'I', fr)
bitmap_words = [0] * (fr // 32)
if fr % 32:
bitmap_words.append(0)
for i in range(fr):
if i in frame_set:
bitmap_words[i//32] |= 1 << i%32
for word in bitmap_words:
key_stream += struct.pack(endian_char + 'I', word)
# Build bone table
bone_list = []
for bone in bone_set:
fc_dict = dict()
rotation_mode = None
property_bits = 0
for fcurve in action.fcurves:
if fcurve.data_path == 'pose.bones["'+bone+'"].scale':
if 'scale' not in fc_dict:
fc_dict['scale'] = [None, None, None]
property_bits |= 1
fc_dict['scale'][fcurve.array_index] = fcurve
elif fcurve.data_path == 'pose.bones["'+bone+'"].rotation_euler':
if 'rotation_euler' not in fc_dict:
fc_dict['rotation_euler'] = [None, None, None]
rotation_mode = 'rotation_euler'
property_bits |= 2
fc_dict['rotation_euler'][fcurve.array_index] = fcurve
elif fcurve.data_path == 'pose.bones["'+bone+'"].rotation_quaternion':
if 'rotation_quaternion' not in fc_dict:
fc_dict['rotation_quaternion'] = [None, None, None, None]
rotation_mode = 'rotation_quaternion'
property_bits |= 2
fc_dict['rotation_quaternion'][fcurve.array_index] = fcurve
elif fcurve.data_path == 'pose.bones["'+bone+'"].rotation_axis_angle':
if 'rotation_axis_angle' not in fc_dict:
fc_dict['rotation_axis_angle'] = [None, None, None, None]
rotation_mode = 'rotation_axis_angle'
property_bits |= 2
fc_dict['rotation_axis_angle'][fcurve.array_index] = fcurve
elif fcurve.data_path == 'pose.bones["'+bone+'"].location':
if 'location' not in fc_dict:
fc_dict['location'] = [None, None, None]
property_bits |= 4
fc_dict['location'][fcurve.array_index] = fcurve
bone_list.append((bone, rotation_mode, fc_dict))
bone_head = hashbone(bone)
bone_head |= (property_bits << 28)
key_stream += struct.pack(endian_char + 'I', bone_head)
# Interleave / interpolate keyframe data
for frame in sorted(frame_set):
for bone in bone_list:
bone_name = bone[0]
rotation_mode = bone[1]
fc_dict = bone[2]
# Scale curves
if 'scale' in fc_dict:
for comp in range(3):
if fc_dict['scale'][comp]:
key_stream += struct.pack(endian_char + 'f', fc_dict['scale'][comp].evaluate(frame))
else:
key_stream += struct.pack(endian_char + 'f', 0.0)
# Rotation curves
if rotation_mode == 'rotation_quaternion':
for comp in range(4):
if fc_dict['rotation_quaternion'][comp]:
key_stream += struct.pack(endian_char + 'f', fc_dict['rotation_quaternion'][comp].evaluate(frame))
else:
key_stream += struct.pack(endian_char + 'f', 0.0)
elif rotation_mode == 'rotation_euler':
euler = [0.0, 0.0, 0.0]
for comp in range(3):
if fc_dict['rotation_euler'][comp]:
euler[comp] = fc_dict['rotation_euler'][comp].evaluate(frame)
euler_o = mathutils.Euler(euler, 'XYZ')
quat = euler_o.to_quaternion()
key_stream += struct.pack(endian_char + 'ffff', quat[0], quat[1], quat[2], quat[3])
elif rotation_mode == 'rotation_axis_angle':
axis_angle = [0.0, 0.0, 0.0, 0.0]
for comp in range(4):
if fc_dict['rotation_axis_angle'][comp]:
axis_angle[comp] = fc_dict['rotation_axis_angle'][comp].evaluate(frame)
quat = mathutils.Quaternion(axis_angle[1:4], axis_angle[0])
key_stream += struct.pack(endian_char + 'ffff', quat[0], quat[1], quat[2], quat[3])
# Location curves
if 'location' in fc_dict:
for comp in range(3):
if fc_dict['location'][comp]:
key_stream += struct.pack(endian_char + 'f', fc_dict['location'][comp].evaluate(frame))
else:
key_stream += struct.pack(endian_char + 'f', 0.0)
# Generate event buffer
event_buf = bytearray()
if hasattr(action, 'hecl_events'):
c1 = 0
c2 = 0
c3 = 0
c4 = 0
for event in action.hecl_events:
if event.type == 'LOOP':
c1 += 1
elif event.type == 'UEVT':
c2 += 1
elif event.type == 'EFFECT':
c3 += 1
elif event.type == 'SOUND':
c4 += 1
event_buf += struct.pack(endian_char + 'IIII', c1, c2, c3, c4)
for event in action.hecl_events:
if event.type == 'LOOP':
event_buf += struct.pack(endian_char + 'fi', event.time, event.loop_data.bool)
for event in action.hecl_events:
if event.type == 'UEVT':
event_buf += struct.pack(endian_char + 'fii', event.time, event.uevt_data.type,
hashbone(event.uevt_data.bone_name))
for event in action.hecl_events:
if event.type == 'EFFECT':
effect_db_id, effect_hash = res_db.search_for_resource(event.effect_data.uid, arg_package)
if effect_hash:
res_db.register_dependency(rani_db_id, effect_db_id)
else:
raise RuntimeError("Error - unable to find effect '{0}'".format(event.effect_data.uid))
event_buf += struct.pack(endian_char + 'fiifi', event.time, event.effect_data.frame_count,
hashbone(event.effect_data.bone_name), event.effect_data.scale,
EFFECT_XF_MODES[event.effect_data.transform_mode])
event_buf += effect_hash
for event in action.hecl_events:
if event.type == 'SOUND':
sid = int.from_bytes(event.sound_data.sound_id.encode()[:4], byteorder='big', signed=False)
event_buf += struct.pack(endian_char + 'fIff', event.time, sid,
event.sound_data.ref_amp, event.sound_data.ref_dist)
else:
event_buf += struct.pack('IIII',0,0,0,0)
return key_stream + event_buf

View File

@ -0,0 +1,220 @@
import bpy
# Action update (if anything important changes)
def active_action_update(self, context):
if not bpy.app.background:
if context.scene.hecl_type == 'ACTOR' and context.scene.hecl_auto_select:
if SACTAction_load.poll(context):
bpy.ops.scene.sactaction_load()
# Action type update
def action_type_update(self, context):
if not bpy.app.background:
actor_data = context.scene.hecl_sact_data
active_action_update(self, context)
# Actor action class
class SACTAction(bpy.types.PropertyGroup):
name: bpy.props.StringProperty(name="Action Name")
# Panel draw
def draw(layout, context):
actor_data = context.scene.hecl_sact_data
row = layout.row(align=True)
row.alignment = 'LEFT'
row.prop(actor_data, 'show_actions', text="Actions", icon='ACTION', emboss=False)
if actor_data.show_actions:
row = layout.row()
row.template_list("UI_UL_list", "SCENE_UL_SACTActions",
actor_data, 'actions', actor_data, 'active_action')
col = row.column(align=True)
col.operator("scene.sactaction_add", icon="ADD", text="")
col.operator("scene.sactaction_remove", icon="REMOVE", text="")
if len(actor_data.actions) and actor_data.active_action >= 0:
action = actor_data.actions[actor_data.active_action]
# Load action operator
if not bpy.context.scene.hecl_auto_select:
layout.operator("scene.sactaction_load", icon='FILE_TICK', text="Load Action")
# Name edit field
layout.prop_search(action, 'name', bpy.data, 'actions', text="Name")
linked_action = None
if bpy.data.actions.find(action.name) != -1:
linked_action = bpy.data.actions[action.name]
# Validate
if linked_action is None:
layout.label(text="Source action not set", icon='ERROR')
else:
#layout.prop(linked_action, 'hecl_index', text="Index")
#layout.prop(linked_action, 'hecl_anim_props', text="Props")
layout.prop(linked_action, 'anim_id', text="ANIM ID")
layout.prop(linked_action, 'hecl_fps', text="Frame Rate")
row = layout.row()
row.prop(context.scene, 'hecl_auto_remap', text="60-fps Remap")
row.prop(linked_action, 'hecl_additive', text="Additive")
#row.prop(linked_action, 'hecl_looping', text="Looping")
# Action 'add' operator
class SACTAction_add(bpy.types.Operator):
bl_idname = "scene.sactaction_add"
bl_label = "New HECL Actor Action"
bl_description = "Add New HECL Actor Action to active scene"
@classmethod
def poll(cls, context):
return (context.scene is not None and
not context.scene.library and
context.scene.hecl_type == 'ACTOR')
def execute(self, context):
actor_data = context.scene.hecl_sact_data
action_name = 'ActorAction'
if action_name in actor_data.actions:
action_name = 'ActorAction.001'
action_idx = 1
while action_name in actor_data.actions:
action_idx += 1
action_name = 'ActorAction.{:0>3}'.format(action_idx)
action = actor_data.actions.add()
action.name = action_name
actor_data.active_action = len(actor_data.actions)-1
return {'FINISHED'}
# Action 'remove' operator
class SACTAction_remove(bpy.types.Operator):
bl_idname = "scene.sactaction_remove"
bl_label = "Remove HECL Actor Action"
bl_description = "Remove HECL Actor Action from active scene"
@classmethod
def poll(cls, context):
actor_data = context.scene.hecl_sact_data
return (context.scene is not None and
not context.scene.library and
context.scene.hecl_type == 'ACTOR' and
actor_data.active_action >= 0 and
len(actor_data.actions))
def execute(self, context):
actor_data = context.scene.hecl_sact_data
actor_data.actions.remove(actor_data.active_action)
actor_data.active_action -= 1
if actor_data.active_action == -1:
actor_data.active_action = 0
return {'FINISHED'}
# Action 'load' operator
class SACTAction_load(bpy.types.Operator):
bl_idname = "scene.sactaction_load"
bl_label = "Load HECL Actor Action"
bl_description = "Loads Action for playback in active scene"
@classmethod
def poll(cls, context):
return (context.scene is not None and
context.scene.hecl_type == 'ACTOR' and
len(context.scene.hecl_sact_data.actions) and
context.scene.hecl_sact_data.active_action >= 0)
def execute(self, context):
actor_data = context.scene.hecl_sact_data
if actor_data.active_action not in range(len(actor_data.actions)):
return {'CANCELLED'}
if actor_data.active_subtype not in range(len(actor_data.subtypes)):
return {'CANCELLED'}
action_data = actor_data.actions[actor_data.active_action]
subtype = actor_data.subtypes[actor_data.active_subtype]
# Refresh event markers
#SACTEvent.clear_event_markers(actor_data, context)
#SACTEvent.update_action_events(None)
#SACTEvent.active_event_update(None, context)
# Clear animation data for all subtypes
for s in range(len(actor_data.subtypes)):
st = actor_data.subtypes[s]
if st.linked_armature in bpy.data.objects:
am = bpy.data.objects[st.linked_armature]
am.animation_data_clear()
# Set single action into armature
if subtype.linked_armature in bpy.data.objects:
armature_objs = [bpy.data.objects[subtype.linked_armature]]
for attachment in actor_data.attachments:
if attachment.linked_armature in bpy.data.objects:
attachment_armature = bpy.data.objects[attachment.linked_armature]
armature_objs.append(attachment_armature)
for armature_obj in armature_objs:
for bone in armature_obj.pose.bones:
bone.location = (0,0,0)
bone.rotation_quaternion = (1,0,0,0)
bone.scale = (1,1,1)
if action_data.name in bpy.data.actions:
action_obj =\
bpy.data.actions[action_data.name]
armature_obj.animation_data_clear()
armature_obj.animation_data_create()
armature_obj.animation_data.action = action_obj
# Time remapping
if context.scene.hecl_auto_remap:
bpy.context.scene.render.fps = 60
bpy.context.scene.render.frame_map_old = action_obj.hecl_fps
bpy.context.scene.render.frame_map_new = 60
bpy.context.scene.frame_start = 0
bpy.context.scene.frame_end = action_obj.frame_range[1] * (60 / action_obj.hecl_fps)
else:
bpy.context.scene.render.fps = action_obj.hecl_fps
bpy.context.scene.render.frame_map_old = action_obj.hecl_fps
bpy.context.scene.render.frame_map_new = action_obj.hecl_fps
bpy.context.scene.frame_start = 0
bpy.context.scene.frame_end = action_obj.frame_range[1]
# Events
#SACTEvent.clear_action_events(self, context, actor_data)
#SACTEvent.load_action_events(self, context, action_obj, 0)
else:
armature_obj.animation_data_clear()
self.report({'WARNING'}, "Unable to load action; check HECL panel")
return {'FINISHED'}
return {'FINISHED'}
else:
self.report({'WARNING'}, "Unable to load armature; check HECL panel")
return {'FINISHED'}
# Registration
def register():
bpy.types.Action.hecl_fps = bpy.props.IntProperty(name="HECL Actor Sub-action Frame-rate",
description="Frame-rate at which action is authored; to be interpolated at 60-fps by runtime",
min=1, max=60, default=30, update=active_action_update)
bpy.utils.register_class(SACTAction)
bpy.utils.register_class(SACTAction_add)
bpy.utils.register_class(SACTAction_load)
bpy.utils.register_class(SACTAction_remove)
def unregister():
bpy.utils.unregister_class(SACTAction)
bpy.utils.unregister_class(SACTAction_add)
bpy.utils.unregister_class(SACTAction_load)
bpy.utils.unregister_class(SACTAction_remove)

View File

@ -0,0 +1,408 @@
import bpy
# Subtype update (if anything important changes)
def active_subtype_update(self, context):
if context.scene.hecl_type == 'ACTOR' and context.scene.hecl_auto_select:
if SACTSubtype_load.poll(context):
bpy.ops.scene.sactsubtype_load()
# Actor subtype overlay class
class SACTSubtypeOverlay(bpy.types.PropertyGroup):
name: bpy.props.StringProperty(name="Overlay Name")
linked_mesh: bpy.props.StringProperty(name="Linked Mesh Object Source", update=active_subtype_update)
show_overlay: bpy.props.BoolProperty(name="Show Overlay Mesh", update=active_subtype_update)
# Actor attachment class
class SACTAttachment(bpy.types.PropertyGroup):
name: bpy.props.StringProperty(name="Attachment Name")
linked_armature: bpy.props.StringProperty(name="Linked Armature Object Source", update=active_subtype_update)
linked_mesh: bpy.props.StringProperty(name="Linked Mesh Object Source", update=active_subtype_update)
show_attachment: bpy.props.BoolProperty(name="Show Attachment Mesh", update=active_subtype_update)
# Actor subtype class
class SACTSubtype(bpy.types.PropertyGroup):
name: bpy.props.StringProperty(name="Actor Mesh Name")
linked_armature: bpy.props.StringProperty(name="Linked Armature Object Source", update=active_subtype_update)
linked_mesh: bpy.props.StringProperty(name="Linked Mesh Object Source", update=active_subtype_update)
show_mesh: bpy.props.BoolProperty(name="Show Mesh", default=True, update=active_subtype_update)
overlays: bpy.props.CollectionProperty(type=SACTSubtypeOverlay, name="Subtype Overlay List")
active_overlay: bpy.props.IntProperty(name="Active Subtype Overlay", default=0, update=active_subtype_update)
# Panel draw
def draw(layout, context):
actor_data = context.scene.hecl_sact_data
row = layout.row(align=True)
row.alignment = 'LEFT'
row.prop(actor_data, 'show_subtypes', text="Subtypes", icon='MESH_DATA', emboss=False)
if actor_data.show_subtypes:
row = layout.row()
row.template_list("UI_UL_list", "SCENE_UL_SACTSubtypes",
actor_data, 'subtypes', actor_data, 'active_subtype')
col = row.column(align=True)
col.operator("scene.sactsubtype_add", icon="ADD", text="")
col.operator("scene.sactsubtype_remove", icon="REMOVE", text="")
if len(actor_data.subtypes) and actor_data.active_subtype >= 0:
subtype = actor_data.subtypes[actor_data.active_subtype]
# Load subtype operator
if not bpy.context.scene.hecl_auto_select:
layout.operator("scene.sactsubtype_load", icon='FILE_TICK', text="Load Subtype")
# Name edit field
layout.prop(subtype, 'name', text="Name")
# Link external armature search
layout.prop_search(subtype, 'linked_armature', bpy.data, 'objects', text="Armature")
linked_armature = None
if subtype.linked_armature in bpy.data.objects:
linked_armature = bpy.data.objects[subtype.linked_armature]
# Validate
if linked_armature is None:
layout.label(text="Source armature not set", icon='ERROR')
elif linked_armature is not None and linked_armature.type != 'ARMATURE':
layout.label(text="Source armature is not an 'ARMATURE'", icon='ERROR')
# Link external mesh search
layout.prop_search(subtype, 'linked_mesh', bpy.data, 'objects', text="Mesh")
linked_mesh = None
if subtype.linked_mesh in bpy.data.objects:
linked_mesh = bpy.data.objects[subtype.linked_mesh]
layout.prop(subtype, 'show_mesh', text="Show Mesh")
# Mesh overlays
layout.label(text="Overlay Meshes:")
row = layout.row()
row.template_list("UI_UL_list", "SCENE_UL_SACTSubtypeOverlays",
subtype, 'overlays', subtype, 'active_overlay')
col = row.column(align=True)
col.operator("scene.sactsubtypeoverlay_add", icon="ADD", text="")
col.operator("scene.sactsubtypeoverlay_remove", icon="REMOVE", text="")
overlay_mesh = None
if len(subtype.overlays) and subtype.active_overlay >= 0:
overlay = subtype.overlays[subtype.active_overlay]
layout.prop(overlay, 'name', text="Name")
layout.prop_search(overlay, 'linked_mesh', bpy.data, 'objects', text="Mesh")
if overlay.linked_mesh in bpy.data.objects:
overlay_mesh = bpy.data.objects[overlay.linked_mesh]
layout.prop(overlay, 'show_overlay', text="Show Overlay")
# Mesh attachments
layout.label(text="Attachment Meshes:")
row = layout.row()
row.template_list("UI_UL_list", "SCENE_UL_SACTAttachments",
actor_data, 'attachments', actor_data, 'active_attachment')
col = row.column(align=True)
col.operator("scene.sactattachment_add", icon="ADD", text="")
col.operator("scene.sactattachment_remove", icon="REMOVE", text="")
attachment_armature = linked_armature
attachment_mesh = None
if len(actor_data.attachments) and actor_data.active_attachment >= 0:
attachment = actor_data.attachments[actor_data.active_attachment]
layout.prop(attachment, 'name', text="Name")
layout.prop_search(attachment, 'linked_armature', bpy.data, 'objects', text="Armature")
if attachment.linked_armature in bpy.data.objects:
attachment_armature = bpy.data.objects[attachment.linked_armature]
layout.prop_search(attachment, 'linked_mesh', bpy.data, 'objects', text="Mesh")
if attachment.linked_mesh in bpy.data.objects:
attachment_mesh = bpy.data.objects[attachment.linked_mesh]
layout.prop(attachment, 'show_attachment', text="Show Attachment")
# Validate
if linked_mesh is None:
layout.label(text="Source mesh not set", icon='ERROR')
elif linked_mesh.type != 'MESH':
layout.label(text="Source mesh not 'MESH'", icon='ERROR')
elif linked_armature is not None and linked_mesh not in linked_armature.children:
layout.label(linked_mesh.name+" not a child of "+linked_armature.name, icon='ERROR')
elif linked_mesh.parent_type != 'ARMATURE':
layout.label(text="Source mesh not 'ARMATURE' parent type", icon='ERROR')
if overlay_mesh:
if overlay_mesh.type != 'MESH':
layout.label(text="Overlay mesh not 'MESH'", icon='ERROR')
elif overlay_mesh.parent_type != 'ARMATURE':
layout.label(text="Overlay mesh not 'ARMATURE' parent type", icon='ERROR')
if attachment_mesh:
if attachment_mesh.type != 'MESH':
layout.label(text="Attachment mesh not 'MESH'", icon='ERROR')
elif attachment_armature is not None and attachment_mesh not in attachment_armature.children:
layout.label(attachment_mesh.name+" not a child of "+attachment_armature.name, icon='ERROR')
elif attachment_mesh.parent_type != 'ARMATURE':
layout.label(text="Attachment mesh not 'ARMATURE' parent type", icon='ERROR')
# Subtype 'add' operator
class SACTSubtype_add(bpy.types.Operator):
bl_idname = "scene.sactsubtype_add"
bl_label = "New HECL Actor Subtype"
bl_description = "Add New HECL Actor Subtype to active scene"
@classmethod
def poll(cls, context):
return (context.scene is not None and
not context.scene.library and
context.scene.hecl_type == 'ACTOR')
def execute(self, context):
actor_data = context.scene.hecl_sact_data
mesh_name = 'ActorMesh'
if mesh_name in actor_data.subtypes:
mesh_name = 'ActorMesh.001'
mesh_idx = 1
while mesh_name in actor_data.subtypes:
mesh_idx += 1
mesh_name = 'ActorMesh.{:0>3}'.format(mesh_idx)
mesh = actor_data.subtypes.add()
mesh.name = mesh_name
actor_data.active_subtype = len(actor_data.subtypes)-1
return {'FINISHED'}
# Subtype 'remove' operator
class SACTSubtype_remove(bpy.types.Operator):
bl_idname = "scene.sactsubtype_remove"
bl_label = "Remove HECL Actor Subtype"
bl_description = "Remove HECL Actor Subtype from active scene"
@classmethod
def poll(cls, context):
actor_data = context.scene.hecl_sact_data
return (context.scene is not None and
not context.scene.library and
context.scene.hecl_type == 'ACTOR' and
actor_data.active_subtype >= 0 and
len(actor_data.subtypes))
def execute(self, context):
actor_data = context.scene.hecl_sact_data
actor_data.subtypes.remove(actor_data.active_subtype)
actor_data.active_subtype -= 1
if actor_data.active_subtype == -1:
actor_data.active_subtype = 0
return {'FINISHED'}
def parent_armature(mesh_obj, arm_obj):
mesh_obj.parent = None
for mod in mesh_obj.modifiers:
if mod.type == 'ARMATURE':
mod.object = arm_obj
return
mod = mesh_obj.modifiers.new('Parent', 'ARMATURE')
mod.object = arm_obj
#mesh_obj.parent = arm_obj
#mesh_obj.parent_type = 'ARMATURE'
# Subtype 'load' operator
class SACTSubtype_load(bpy.types.Operator):
bl_idname = "scene.sactsubtype_load"
bl_label = "Load HECL Actor Subtype"
bl_description = "Loads Subtype for viewing in active scene"
@classmethod
def poll(cls, context):
return (context.scene is not None and
context.scene.hecl_type == 'ACTOR' and
len(context.scene.hecl_sact_data.subtypes) and
context.scene.hecl_sact_data.active_subtype >= 0)
def execute(self, context):
actor_data = context.scene.hecl_sact_data
subtype = actor_data.subtypes[actor_data.active_subtype]
# Armature
linked_armature = None
if subtype.linked_armature in bpy.data.objects:
linked_armature = bpy.data.objects[subtype.linked_armature]
else:
return {'FINISHED'}
# Hide armature children
for object in linked_armature.children:
if object.name in context.scene.objects:
object.hide_set(True)
# Hide all meshes (incl overlays)
for subtype_data in actor_data.subtypes:
if subtype_data.linked_mesh in bpy.data.objects:
mesh = bpy.data.objects[subtype_data.linked_mesh]
if mesh.name in context.scene.objects:
mesh.hide_set(True)
for overlay in subtype_data.overlays:
if overlay.linked_mesh in bpy.data.objects:
mesh = bpy.data.objects[overlay.linked_mesh]
if mesh.name in context.scene.objects:
mesh.hide_set(True)
# Hide/Show selected attachment meshes
for attachment in actor_data.attachments:
if attachment.linked_mesh in bpy.data.objects:
mesh_obj = bpy.data.objects[attachment.linked_mesh]
if mesh_obj.name in context.scene.objects:
mesh_obj.hide_set(not attachment.show_attachment)
attachment_armature = linked_armature
if attachment.linked_armature in bpy.data.objects:
attachment_armature = bpy.data.objects[attachment.linked_armature]
if mesh_obj != attachment_armature:
parent_armature(mesh_obj, attachment_armature)
# Show only the chosen subtype (and selected overlays)
if subtype.linked_mesh in bpy.data.objects:
mesh_obj = bpy.data.objects[subtype.linked_mesh]
if subtype.show_mesh:
mesh_obj.hide_set(False)
if mesh_obj != linked_armature:
parent_armature(mesh_obj, linked_armature)
for overlay in subtype.overlays:
if overlay.linked_mesh in bpy.data.objects:
mesh_obj = bpy.data.objects[overlay.linked_mesh]
if overlay.show_overlay:
mesh_obj.hide_set(False)
if mesh_obj != linked_armature:
parent_armature(mesh_obj, linked_armature)
return {'FINISHED'}
# Subtype overlay 'add' operator
class SACTSubtypeOverlay_add(bpy.types.Operator):
bl_idname = "scene.sactsubtypeoverlay_add"
bl_label = "New HECL Actor Subtype Overlay"
bl_description = "Add New HECL Actor Subtype Overlay"
@classmethod
def poll(cls, context):
actor_data = context.scene.hecl_sact_data
return (context.scene is not None and
not context.scene.library and
context.scene.hecl_type == 'ACTOR' and
len(actor_data.subtypes) and actor_data.active_subtype >= 0)
def execute(self, context):
actor_data = context.scene.hecl_sact_data
subtype = actor_data.subtypes[actor_data.active_subtype]
overlay_name = 'ActorOverlay'
if overlay_name in subtype.overlays:
overlay_name = 'ActorOverlay.001'
overlay_idx = 1
while overlay_name in subtype.overlays:
overlay_idx += 1
overlay_name = 'ActorOverlay.{:0>3}'.format(overlay_idx)
overlay = subtype.overlays.add()
overlay.name = overlay_name
subtype.active_overlay = len(subtype.overlays)-1
return {'FINISHED'}
# Subtype overlay 'remove' operator
class SACTSubtypeOverlay_remove(bpy.types.Operator):
bl_idname = "scene.sactsubtypeoverlay_remove"
bl_label = "Remove HECL Actor Subtype Overlay"
bl_description = "Remove HECL Actor Subtype Overlay"
@classmethod
def poll(cls, context):
actor_data = context.scene.hecl_sact_data
return (context.scene is not None and
not context.scene.library and
context.scene.hecl_type == 'ACTOR' and
actor_data.active_subtype >= 0 and
len(actor_data.subtypes) and
actor_data.subtypes[actor_data.active_subtype].active_overlay >= 0 and
len(actor_data.subtypes[actor_data.active_subtype].overlays))
def execute(self, context):
actor_data = context.scene.hecl_sact_data
subtype = actor_data.subtypes[actor_data.active_subtype]
subtype.overlays.remove(subtype.active_overlay)
subtype.active_overlay -= 1
if subtype.active_overlay == -1:
subtype.active_overlay = 0
return {'FINISHED'}
# Subtype overlay 'add' operator
class SACTAttachment_add(bpy.types.Operator):
bl_idname = "scene.sactattachment_add"
bl_label = "New HECL Actor Attachment"
bl_description = "Add New HECL Actor Attachment"
@classmethod
def poll(cls, context):
actor_data = context.scene.hecl_sact_data
return (context.scene is not None and
not context.scene.library and
context.scene.hecl_type == 'ACTOR')
def execute(self, context):
actor_data = context.scene.hecl_sact_data
attachment_name = 'ActorAttachment'
if attachment_name in actor_data.attachments:
attachment_name = 'ActorAttachment.001'
attachment_idx = 1
while attachment_name in actor_data.attachments:
attachment_idx += 1
attachment_name = 'ActorAttachment.{:0>3}'.format(attachment_idx)
attachment = actor_data.attachments.add()
attachment.name = attachment_name
actor_data.active_attachment = len(actor_data.attachments)-1
return {'FINISHED'}
# Subtype overlay 'remove' operator
class SACTAttachment_remove(bpy.types.Operator):
bl_idname = "scene.sactattachment_remove"
bl_label = "Remove HECL Actor Attachment"
bl_description = "Remove HECL Actor Attachment"
@classmethod
def poll(cls, context):
actor_data = context.scene.hecl_sact_data
return (context.scene is not None and
not context.scene.library and
context.scene.hecl_type == 'ACTOR' and
actor_data.active_attachment >= 0 and
len(actor_data.attachments))
def execute(self, context):
actor_data = context.scene.hecl_sact_data
actor_data.attachments.remove(actor_data.active_attachment)
actor_data.active_attachment -= 1
if actor_data.active_attachment == -1:
actor_data.active_attachment = 0
return {'FINISHED'}
# Registration
def register():
bpy.utils.register_class(SACTSubtypeOverlay)
bpy.utils.register_class(SACTSubtypeOverlay_add)
bpy.utils.register_class(SACTSubtypeOverlay_remove)
bpy.utils.register_class(SACTAttachment)
bpy.utils.register_class(SACTAttachment_add)
bpy.utils.register_class(SACTAttachment_remove)
bpy.utils.register_class(SACTSubtype)
bpy.utils.register_class(SACTSubtype_add)
bpy.utils.register_class(SACTSubtype_remove)
bpy.utils.register_class(SACTSubtype_load)
def unregister():
bpy.utils.unregister_class(SACTSubtype)
bpy.utils.unregister_class(SACTSubtype_add)
bpy.utils.unregister_class(SACTSubtype_remove)
bpy.utils.unregister_class(SACTSubtype_load)
bpy.utils.unregister_class(SACTAttachment)
bpy.utils.unregister_class(SACTAttachment_add)
bpy.utils.unregister_class(SACTAttachment_remove)
bpy.utils.unregister_class(SACTSubtypeOverlay)
bpy.utils.unregister_class(SACTSubtypeOverlay_add)
bpy.utils.unregister_class(SACTSubtypeOverlay_remove)

View File

@ -0,0 +1,488 @@
from . import SACTSubtype, SACTAction, ANIM
from .. import armature
import bpy
import bpy.path
import re
import struct
from mathutils import Vector, Quaternion, Euler
# Actor data class
class SACTData(bpy.types.PropertyGroup):
subtypes: bpy.props.CollectionProperty(type=SACTSubtype.SACTSubtype, name="Actor Subtype List")
active_subtype: bpy.props.IntProperty(name="Active Actor Subtype", default=0, update=SACTSubtype.active_subtype_update)
show_subtypes: bpy.props.BoolProperty()
attachments: bpy.props.CollectionProperty(type=SACTSubtype.SACTAttachment, name="Attachment List")
active_attachment: bpy.props.IntProperty(name="Active Attachment", default=0, update=SACTSubtype.active_subtype_update)
actions: bpy.props.CollectionProperty(type=SACTAction.SACTAction, name="Actor Action List")
active_action: bpy.props.IntProperty(name="Active Actor Action", default=0, update=SACTAction.active_action_update)
show_actions: bpy.props.BoolProperty()
# Regex RNA path matchers
scale_matcher = re.compile(r'pose.bones\["(\S+)"\].scale')
rotation_matcher = re.compile(r'pose.bones\["(\S+)"\].rotation')
location_matcher = re.compile(r'pose.bones\["(\S+)"\].location')
def write_action_channels(writebuf, action):
# Set of frame indices
frame_set = set()
# Set of unique bone names
bone_set = []
# Scan through all fcurves to build animated bone set
for fcurve in action.fcurves:
data_path = fcurve.data_path
scale_match = scale_matcher.match(data_path)
rotation_match = rotation_matcher.match(data_path)
location_match = location_matcher.match(data_path)
if scale_match:
if scale_match.group(1) not in bone_set:
bone_set.append(scale_match.group(1))
elif rotation_match:
if rotation_match.group(1) not in bone_set:
bone_set.append(rotation_match.group(1))
elif location_match:
if location_match.group(1) not in bone_set:
bone_set.append(location_match.group(1))
else:
continue
# Count unified keyframes for interleaving channel data
for key in fcurve.keyframe_points:
frame_set.add(int(key.co[0]))
# Build bone table
bone_list = []
for bone in bone_set:
fc_dict = dict()
rotation_mode = None
property_bits = 0
for fcurve in action.fcurves:
if fcurve.data_path == 'pose.bones["'+bone+'"].scale':
if 'scale' not in fc_dict:
fc_dict['scale'] = [None, None, None]
property_bits |= 4
fc_dict['scale'][fcurve.array_index] = fcurve
elif fcurve.data_path == 'pose.bones["'+bone+'"].rotation_euler':
if 'rotation_euler' not in fc_dict:
fc_dict['rotation_euler'] = [None, None, None]
rotation_mode = 'rotation_euler'
property_bits |= 1
fc_dict['rotation_euler'][fcurve.array_index] = fcurve
elif fcurve.data_path == 'pose.bones["'+bone+'"].rotation_quaternion':
if 'rotation_quaternion' not in fc_dict:
fc_dict['rotation_quaternion'] = [None, None, None, None]
rotation_mode = 'rotation_quaternion'
property_bits |= 1
fc_dict['rotation_quaternion'][fcurve.array_index] = fcurve
elif fcurve.data_path == 'pose.bones["'+bone+'"].rotation_axis_angle':
if 'rotation_axis_angle' not in fc_dict:
fc_dict['rotation_axis_angle'] = [None, None, None, None]
rotation_mode = 'rotation_axis_angle'
property_bits |= 1
fc_dict['rotation_axis_angle'][fcurve.array_index] = fcurve
elif fcurve.data_path == 'pose.bones["'+bone+'"].location':
if 'location' not in fc_dict:
fc_dict['location'] = [None, None, None]
property_bits |= 2
fc_dict['location'][fcurve.array_index] = fcurve
bone_list.append((bone, rotation_mode, fc_dict, property_bits))
# Write out frame indices
sorted_frames = sorted(frame_set)
writebuf(struct.pack('I', len(sorted_frames)))
for frame in sorted_frames:
writebuf(struct.pack('i', frame))
# Interleave / interpolate keyframe data
writebuf(struct.pack('I', len(bone_list)))
for bone in bone_list:
bone_name = bone[0]
rotation_mode = bone[1]
fc_dict = bone[2]
property_bits = bone[3]
writebuf(struct.pack('I', len(bone_name)))
writebuf(bone_name.encode())
writebuf(struct.pack('I', property_bits))
writebuf(struct.pack('I', len(sorted_frames)))
for frame in sorted_frames:
# Rotation curves
if rotation_mode == 'rotation_quaternion':
quat = [0.0]*4
for comp in range(4):
if fc_dict['rotation_quaternion'][comp]:
quat[comp] = fc_dict['rotation_quaternion'][comp].evaluate(frame)
quat = Quaternion(quat).normalized()
writebuf(struct.pack('ffff', quat[0], quat[1], quat[2], quat[3]))
elif rotation_mode == 'rotation_euler':
euler = [0.0]*3
for comp in range(3):
if fc_dict['rotation_euler'][comp]:
euler[comp] = fc_dict['rotation_euler'][comp].evaluate(frame)
euler_o = Euler(euler, 'XYZ')
quat = euler_o.to_quaternion()
writebuf(struct.pack('ffff', quat[0], quat[1], quat[2], quat[3]))
elif rotation_mode == 'rotation_axis_angle':
axis_angle = [0.0]*4
for comp in range(4):
if fc_dict['rotation_axis_angle'][comp]:
axis_angle[comp] = fc_dict['rotation_axis_angle'][comp].evaluate(frame)
quat = Quaternion(axis_angle[1:4], axis_angle[0])
writebuf(struct.pack('ffff', quat[0], quat[1], quat[2], quat[3]))
# Location curves
if 'location' in fc_dict:
writevec = [0.0]*3
for comp in range(3):
if fc_dict['location'][comp]:
writevec[comp] = fc_dict['location'][comp].evaluate(frame)
writebuf(struct.pack('fff', writevec[0], writevec[1], writevec[2]))
# Scale curves
if 'scale' in fc_dict:
writevec = [1.0]*3
for comp in range(3):
if fc_dict['scale'][comp]:
writevec[comp] = fc_dict['scale'][comp].evaluate(frame)
writebuf(struct.pack('fff', writevec[0], writevec[1], writevec[2]))
def write_action_aabb(writebuf, arm_obj, mesh_obj, action):
scene = bpy.context.scene
# Mute root channels
for fcurve in action.fcurves:
fcurve.mute = fcurve.data_path == 'pose.bones["root"].location'
# Transform against root
root_bone = arm_obj.pose.bones['root']
root_bone.location = (0.0,0.0,0.0)
if root_bone.rotation_mode == 'QUATERNION':
root_bone.rotation_quaternion = (1.0,0.0,0.0,0.0)
else:
root_bone.rotation_euler = (0.0,0.0,0.0)
# Frame 1
scene.frame_set(1)
root_aabb_min = Vector(mesh_obj.bound_box[0])
root_aabb_max = Vector(mesh_obj.bound_box[6])
# Accumulate AABB for each frame
for frame_idx in range(2, scene.frame_end + 1):
scene.frame_set(frame_idx)
test_aabb_min = Vector(mesh_obj.bound_box[0])
test_aabb_max = Vector(mesh_obj.bound_box[6])
for comp in range(3):
if test_aabb_min[comp] < root_aabb_min[comp]:
root_aabb_min[comp] = test_aabb_min[comp]
for comp in range(3):
if test_aabb_max[comp] > root_aabb_max[comp]:
root_aabb_max[comp] = test_aabb_max[comp]
# Unmute root channels
for fcurve in action.fcurves:
fcurve.mute = False
writebuf(struct.pack('ffffff',
root_aabb_min[0], root_aabb_min[1], root_aabb_min[2],
root_aabb_max[0], root_aabb_max[1], root_aabb_max[2]))
def _out_armatures(sact_data, writebuf):
writebuf(struct.pack('I', len(bpy.data.armatures)))
for arm in bpy.data.armatures:
writebuf(struct.pack('I', len(arm.name)))
writebuf(arm.name.encode())
if arm.library:
arm_path = bpy.path.abspath(arm.library.filepath)
writebuf(struct.pack('I', len(arm_path)))
writebuf(arm_path.encode())
else:
writebuf(struct.pack('I', 0))
armature.cook(writebuf, arm)
def _out_subtypes(sact_data, writebuf):
writebuf(struct.pack('I', len(sact_data.subtypes)))
for subtype in sact_data.subtypes:
writebuf(struct.pack('I', len(subtype.name)))
writebuf(subtype.name.encode())
mesh = None
if subtype.linked_mesh in bpy.data.objects:
mesh = bpy.data.objects[subtype.linked_mesh]
cskr_id = mesh.data.cskr_id
writebuf(struct.pack('I', len(cskr_id)))
writebuf(cskr_id.encode())
else:
writebuf(struct.pack('I', 0))
if mesh and mesh.data.library:
mesh_path = bpy.path.abspath(mesh.data.library.filepath)
writebuf(struct.pack('I', len(mesh_path)))
writebuf(mesh_path.encode())
else:
writebuf(struct.pack('I', 0))
arm = None
if subtype.linked_armature in bpy.data.objects:
arm = bpy.data.objects[subtype.linked_armature]
arm_idx = -1
if arm:
arm_idx = bpy.data.armatures.find(arm.name)
writebuf(struct.pack('i', arm_idx))
writebuf(struct.pack('I', len(subtype.overlays)))
for overlay in subtype.overlays:
writebuf(struct.pack('I', len(overlay.name)))
writebuf(overlay.name.encode())
mesh = None
if overlay.linked_mesh in bpy.data.objects:
mesh = bpy.data.objects[overlay.linked_mesh]
cskr_id = mesh.data.cskr_id
writebuf(struct.pack('I', len(cskr_id)))
writebuf(cskr_id.encode())
else:
writebuf(struct.pack('I', 0))
if mesh and mesh.data.library:
mesh_path = bpy.path.abspath(mesh.data.library.filepath)
writebuf(struct.pack('I', len(mesh_path)))
writebuf(mesh_path.encode())
else:
writebuf(struct.pack('I', 0))
def _out_attachments(sact_data, writebuf):
writebuf(struct.pack('I', len(sact_data.attachments)))
for attachment in sact_data.attachments:
writebuf(struct.pack('I', len(attachment.name)))
writebuf(attachment.name.encode())
mesh = None
if attachment.linked_mesh in bpy.data.objects:
mesh = bpy.data.objects[attachment.linked_mesh]
cskr_id = mesh.data.cskr_id
writebuf(struct.pack('I', len(cskr_id)))
writebuf(cskr_id.encode())
else:
writebuf(struct.pack('I', 0))
if mesh and mesh.data.library:
mesh_path = bpy.path.abspath(mesh.data.library.filepath)
writebuf(struct.pack('I', len(mesh_path)))
writebuf(mesh_path.encode())
else:
writebuf(struct.pack('I', 0))
arm = None
if attachment.linked_armature in bpy.data.objects:
arm = bpy.data.objects[attachment.linked_armature]
arm_idx = -1
if arm:
arm_idx = bpy.data.armatures.find(arm.name)
writebuf(struct.pack('i', arm_idx))
def _out_actions(sact_data, writebuf):
writebuf(struct.pack('I', len(sact_data.actions)))
for action_idx in range(len(sact_data.actions)):
sact_data.active_action = action_idx
action = sact_data.actions[action_idx]
writebuf(struct.pack('I', len(action.name)))
writebuf(action.name.encode())
bact = None
if action.name in bpy.data.actions:
bact = bpy.data.actions[action.name]
anim_id = bact.anim_id
writebuf(struct.pack('I', len(anim_id)))
writebuf(anim_id.encode())
if not bact:
raise RuntimeError('action %s not found' % action.name)
writebuf(struct.pack('f', 1.0 / bact.hecl_fps))
writebuf(struct.pack('b', int(bact.hecl_additive)))
writebuf(struct.pack('b', int(bact.hecl_looping)))
write_action_channels(writebuf, bact)
writebuf(struct.pack('I', len(sact_data.subtypes)))
for subtype_idx in range(len(sact_data.subtypes)):
subtype = sact_data.subtypes[subtype_idx]
sact_data.active_subtype = subtype_idx
bpy.ops.scene.sactaction_load()
if subtype.linked_armature not in bpy.data.objects:
raise RuntimeError('armature %s not found' % subtype.linked_armature)
arm = bpy.data.objects[subtype.linked_armature]
if subtype.linked_mesh not in bpy.data.objects:
raise RuntimeError('mesh %s not found' % subtype.linked_mesh)
mesh = bpy.data.objects[subtype.linked_mesh]
write_action_aabb(writebuf, arm, mesh, bact)
def _out_action_no_subtypes(sact_data, writebuf, action_name):
for action_idx in range(len(sact_data.actions)):
action = sact_data.actions[action_idx]
if action.name == action_name:
sact_data.active_action = action_idx
writebuf(struct.pack('I', len(action.name)))
writebuf(action.name.encode())
bact = None
if action.name in bpy.data.actions:
bact = bpy.data.actions[action.name]
anim_id = bact.anim_id
writebuf(struct.pack('I', len(anim_id)))
writebuf(anim_id.encode())
if not bact:
raise RuntimeError('action %s not found' % action.name)
writebuf(struct.pack('f', 1.0 / bact.hecl_fps))
writebuf(struct.pack('b', int(bact.hecl_additive)))
writebuf(struct.pack('b', int(bact.hecl_looping)))
write_action_channels(writebuf, bact)
writebuf(struct.pack('I', 0))
return
raise RuntimeError("Unable to find action '%s'" % action_name)
# Cook
def cook(writebuf):
bpy.context.scene.hecl_auto_remap = False
sact_data = bpy.context.scene.hecl_sact_data
# Output armatures
_out_armatures(sact_data, writebuf)
# Output subtypes
_out_subtypes(sact_data, writebuf)
# Output attachments
_out_attachments(sact_data, writebuf)
# Output actions
_out_actions(sact_data, writebuf)
# Cook Character Data only
def cook_character_only(writebuf):
sact_data = bpy.context.scene.hecl_sact_data
# Output armatures
_out_armatures(sact_data, writebuf)
# Output subtypes
_out_subtypes(sact_data, writebuf)
# Output attachments
_out_attachments(sact_data, writebuf)
# Output no actions
writebuf(struct.pack('I', 0))
def cook_action_channels_only(writebuf, action_name):
sact_data = bpy.context.scene.hecl_sact_data
# Output action without AABBs
_out_action_no_subtypes(sact_data, writebuf, action_name)
# Access actor's contained subtype names
def get_subtype_names(writebuf):
sact_data = bpy.context.scene.hecl_sact_data
writebuf(struct.pack('I', len(sact_data.subtypes)))
for sub_idx in range(len(sact_data.subtypes)):
subtype = sact_data.subtypes[sub_idx]
writebuf(struct.pack('I', len(subtype.name)))
writebuf(subtype.name.encode())
obj = bpy.data.objects[subtype.linked_mesh]
cskr_id = obj.data.cskr_id
writebuf(struct.pack('I', len(cskr_id)))
writebuf(cskr_id.encode())
# Access subtype's contained overlay names
def get_subtype_overlay_names(writebuf, subtypeName):
sact_data = bpy.context.scene.hecl_sact_data
for sub_idx in range(len(sact_data.subtypes)):
subtype = sact_data.subtypes[sub_idx]
if subtype.name == subtypeName:
writebuf(struct.pack('I', len(subtype.overlays)))
for overlay in subtype.overlays:
writebuf(struct.pack('I', len(overlay.name)))
writebuf(overlay.name.encode())
obj = bpy.data.objects[overlay.linked_mesh]
cskr_id = obj.data.cskr_id
writebuf(struct.pack('I', len(cskr_id)))
writebuf(cskr_id.encode())
return
writebuf(struct.pack('I', 0))
# Access contained attachment names
def get_attachment_names(writebuf):
sact_data = bpy.context.scene.hecl_sact_data
writebuf(struct.pack('I', len(sact_data.attachments)))
for att_idx in range(len(sact_data.attachments)):
attachment = sact_data.attachments[att_idx]
writebuf(struct.pack('I', len(attachment.name)))
writebuf(attachment.name.encode())
obj = bpy.data.objects[attachment.linked_mesh]
cskr_id = obj.data.cskr_id
writebuf(struct.pack('I', len(cskr_id)))
writebuf(cskr_id.encode())
# Access actor's contained action names
def get_action_names(writebuf):
sact_data = bpy.context.scene.hecl_sact_data
writebuf(struct.pack('I', len(sact_data.actions)))
for action_idx in range(len(sact_data.actions)):
action = sact_data.actions[action_idx]
writebuf(struct.pack('I', len(action.name)))
writebuf(action.name.encode())
anim_id = bpy.data.actions[action.name].anim_id
writebuf(struct.pack('I', len(anim_id)))
writebuf(anim_id.encode())
# Panel draw
def draw(layout, context):
SACTSubtype.draw(layout.box(), context)
SACTAction.draw(layout.box(), context)
# Time-remap option update
def time_remap_update(self, context):
if context.scene.hecl_type == 'ACTOR':
SACTAction.active_action_update(self, context)
# Registration
def register():
SACTSubtype.register()
SACTAction.register()
bpy.utils.register_class(SACTData)
bpy.types.Scene.hecl_sact_data = bpy.props.PointerProperty(type=SACTData)
bpy.types.Action.anim_id = bpy.props.StringProperty(name='Original ANIM ID')
bpy.types.Action.hecl_fps = bpy.props.IntProperty(name='HECL Action FPS', default=30)
bpy.types.Action.hecl_additive = bpy.props.BoolProperty(name='HECL Additive Action', default=False)
bpy.types.Action.hecl_looping = bpy.props.BoolProperty(name='HECL Looping Action', default=False)
bpy.types.Scene.hecl_auto_remap = bpy.props.BoolProperty(name="Auto Remap",
description="Enables automatic 60-fps time-remapping for playback-validation purposes",
default=True, update=time_remap_update)
def unregister():
bpy.utils.unregister_class(SACTData)
SACTSubtype.unregister()
SACTAction.unregister()

View File

@ -0,0 +1,490 @@
import bpy
from bpy.app.handlers import persistent
from mathutils import Quaternion, Color
import math
import os.path
from .. import swld
# Preview update func (for lighting preview)
def preview_update(self, context):
if context.scene.hecl_type == 'AREA':
area_data = context.scene.hecl_srea_data
# Original Lightmaps
if area_data.lightmap_mode == 'ORIGINAL':
for material in bpy.data.materials:
if material.hecl_lightmap and 'Lightmap' in material.node_tree.nodes:
lm_node = material.node_tree.nodes['Lightmap']
# Reference original game lightmaps
if material.hecl_lightmap in bpy.data.images:
lm_node.image = bpy.data.images[material.hecl_lightmap]
else:
lm_node.image = None
# Cycles Lightmaps
elif area_data.lightmap_mode == 'CYCLES':
for material in bpy.data.materials:
if material.hecl_lightmap and 'Lightmap' in material.node_tree.nodes:
lm_node = material.node_tree.nodes['Lightmap']
# Reference newly-generated lightmaps
img_name = material.hecl_lightmap + '_CYCLES'
if img_name in bpy.data.images:
lm_node.image = bpy.data.images[img_name]
else:
lm_node.image = None
# White Lightmaps
elif area_data.lightmap_mode == 'NONE':
img_name = 'NONE'
img = None
if img_name in bpy.data.images:
img = bpy.data.images[img_name]
else:
img = bpy.data.images.new(img_name, width=1, height=1)
pixels = [1.0] * (4 * 1 * 1)
img.pixels = pixels
img.use_fake_user = True
img.file_format = 'PNG'
for material in bpy.data.materials:
if material.hecl_lightmap and 'Lightmap' in material.node_tree.nodes:
lm_node = material.node_tree.nodes['Lightmap']
# Reference NONE
lm_node.image = img
# Update lightmap output-resolution
def set_lightmap_resolution(self, context):
area_data = context.scene.hecl_srea_data
pixel_size = int(area_data.lightmap_resolution)
for mat in bpy.data.materials:
if not mat.hecl_lightmap:
continue
# Determine proportional aspect
old_image = bpy.data.images[mat.hecl_lightmap]
width_fac = 1
height_fac = 1
if old_image.size[0] > old_image.size[1]:
height_fac = old_image.size[0] // old_image.size[1]
else:
width_fac = old_image.size[1] // old_image.size[0]
width = pixel_size // width_fac
height = pixel_size // height_fac
# Find target texture and scale connected image
if 'CYCLES_OUT' in mat.node_tree.nodes:
out_node = mat.node_tree.nodes['CYCLES_OUT']
if out_node.type == 'TEX_IMAGE':
image = out_node.image
if image:
image.scale(width, height)
def make_or_load_cycles_image(mat, area_data):
if not mat.hecl_lightmap:
return
pixel_size = int(area_data.lightmap_resolution)
tex_name = mat.hecl_lightmap + '_CYCLES'
if area_data.adjacent_area < 0:
path_name = '//' + mat.hecl_lightmap + '_CYCLES.png'
else:
path_name = '//' + mat.hecl_lightmap + '_CYCLES_%d.png' % area_data.adjacent_area
# Determine proportional aspect
old_image = bpy.data.images[mat.hecl_lightmap]
width_fac = 1
height_fac = 1
if old_image.size[0] > old_image.size[1]:
height_fac = old_image.size[0] // old_image.size[1]
else:
width_fac = old_image.size[1] // old_image.size[0]
width = pixel_size // width_fac
height = pixel_size // height_fac
# Check for consistency with on-disk image
if tex_name in bpy.data.images:
image = bpy.data.images[tex_name]
image.use_fake_user = True
image.file_format = 'PNG'
image.filepath = path_name
good = True
if image.size[0] != width or image.size[1] != height:
try:
image.scale(width, height)
except:
good = False
if good:
return image
# Remove and recreate if we get here
bpy.data.images.remove(bpy.data.images[tex_name])
# New image (or load from disk if available)
try:
new_image = bpy.data.images.load(path_name)
new_image.name = tex_name
new_image.use_fake_user = True
if new_image.size[0] != width or new_image.size[1] != height:
new_image.scale(width, height)
except:
new_image = bpy.data.images.new(tex_name, width, height)
new_image.use_fake_user = True
new_image.file_format = 'PNG'
new_image.filepath = path_name
return new_image
# Set adjacent area lightmaps
def set_adjacent_area(self, context):
bg_scene = context.scene.background_set
dock_idx = context.scene.hecl_srea_data.adjacent_area
if bg_scene is None:
self.report({'ERROR_INVALID_CONTEXT'}, 'No background world scene is set')
return
if bg_scene.hecl_type != 'WORLD':
self.report({'ERROR_INVALID_CONTEXT'}, 'Scene "%s" is not a hecl WORLD' % bg_scene.name)
return
adjacent = dock_idx >= 0
if len(context.scene.render.layers):
context.scene.view_layers[0].use_sky = not adjacent
# Remove linked lamps and show/hide locals
for obj in bpy.data.objects:
if obj.library is not None and (obj.type == 'LIGHT' or obj.type == 'MESH'):
try:
context.scene.collection.children.unlink(obj)
except:
pass
continue
if obj.type == 'LIGHT':
obj.hide_render = adjacent
# Remove linked scenes
to_remove = []
for scene in bpy.data.scenes:
if scene.hecl_type == 'AREA' and scene.library is not None:
to_remove.append(scene)
for scene in to_remove:
bpy.data.scenes.remove(scene)
# Link scene, meshes, and lamps
if dock_idx >= 0:
other_area_name = get_other_area_name(self, bg_scene, dock_idx)
if other_area_name is None:
return
other_area_scene_name = None
this_dir = os.path.split(bpy.data.filepath)[0]
try:
with bpy.data.libraries.load('%s/../%s/!area.blend' % (this_dir, other_area_name),
link=True, relative=True) as (data_from, data_to):
for scene in data_from.scenes:
other_area_scene_name = scene
data_to.scenes = [other_area_scene_name]
break
except Exception as e:
self.report({'ERROR_INVALID_CONTEXT'}, 'Unable to open "%s" blend file: %s' % (other_area_name, str(e)))
return
if other_area_scene_name is None:
self.report({'ERROR_INVALID_CONTEXT'}, '"%s" does not have an area scene' % other_area_name)
return
other_scene = bpy.data.scenes[other_area_scene_name]
if other_scene.hecl_type != 'AREA':
self.report({'ERROR_INVALID_CONTEXT'}, '"%s" does not have an area scene' % other_area_name)
bpy.data.scenes.remove(other_scene)
return
for obj in other_scene.objects:
if (obj.type == 'LIGHT' or obj.type == 'MESH') and obj.layers[0]:
context.scene.collection.objects.link(obj)
obj.hide_render = False
# Ensure filepaths target the current dock index
for mat in bpy.data.materials:
if not mat.library and mat.use_nodes and 'CYCLES_OUT' in mat.node_tree.nodes:
texture_node = mat.node_tree.nodes['CYCLES_OUT']
texture_node.image = make_or_load_cycles_image(mat, context.scene.hecl_srea_data)
# Area data class
class SREAData(bpy.types.PropertyGroup):
lightmap_resolution: bpy.props.EnumProperty(name="HECL Area Lightmap Resolution",
description="Set square resolution to use when rendering new lightmaps",
items=[
('256', "256", "256x256 (original quality)"),
('512', "512", "512x512"),
('1024', "1024", "1024x1024"),
('2048', "2048", "2048x2048"),
('4096', "4096", "4096x4096")],
update=set_lightmap_resolution,
default='1024')
lightmap_mode: bpy.props.EnumProperty(name="HECL Area Lightmap Mode",
description="Simple way to manipulate all lightmap-using materials",
items=[
('NONE', "None", "Pure white lightmaps"),
('ORIGINAL', "Original", "Original extracted lightmaps"),
('CYCLES', "Cycles", "Blender-rendered lightmaps")],
update=preview_update,
default='ORIGINAL')
adjacent_area: bpy.props.IntProperty(name="HECL Adjacent Area Lightmap",
description="Dock index of adjacent area to render, or -1 for local lights",
update=set_adjacent_area,
default=-1,
min=-1,
max=8)
def report(self, code, string):
pass
# Trace color output searching for material node and making list from it
def recursive_build_material_chain(node):
if node.type == 'OUTPUT':
if node.inputs[0].is_linked:
ret = recursive_build_material_chain(node.inputs[0].links[0].from_node)
if ret:
ret.append(node)
return ret
elif node.type == 'MIX_RGB':
if node.inputs[1].is_linked:
ret = recursive_build_material_chain(node.inputs[1].links[0].from_node)
if ret:
ret.append(node)
return ret
if node.inputs[2].is_linked:
ret = recursive_build_material_chain(node.inputs[2].links[0].from_node)
if ret:
ret.append(node)
return ret
elif node.type == 'MATERIAL':
return [node]
return None
# Get Diffuse and Emissive output sockets from chain
def get_de_sockets(chain):
found_add = None
found_mul = None
n = None
for nn in reversed(chain):
if not n:
n = nn
continue
if n.type == 'MIX_RGB':
if not found_add and n.blend_type == 'ADD' and nn.type != 'MATERIAL':
use_idx = 1
if n.inputs[1].is_linked:
tn = n.inputs[1].links[0].from_node
if tn == nn:
use_idx = 2
if n.inputs[use_idx].is_linked:
found_add = n.inputs[use_idx].links[0].from_socket
found_mul = None
elif n.blend_type == 'MULTIPLY':
use_idx = 1
if n.inputs[1].is_linked:
tn = n.inputs[1].links[0].from_node
if tn == nn:
use_idx = 2
if n.inputs[use_idx].is_linked:
found_mul = n.inputs[use_idx].links[0].from_socket
n = nn
return found_mul, found_add
# Lookup the directory name of other area via dock link
def get_other_area_name(op, bg_scene, dock_idx):
dock_conns = swld.build_dock_connections(bg_scene)
this_dir = os.path.split(bpy.data.filepath)[0]
this_area_name = os.path.basename(this_dir)
wld_area = next((area for area in dock_conns[0] if area[0].name == this_area_name), None)
if wld_area is None:
op.report({'ERROR_INVALID_CONTEXT'}, 'Unable to resolve area in world')
return None
if dock_idx not in range(len(wld_area[1])):
op.report({'ERROR_INVALID_CONTEXT'}, 'Dock %d is out of this area\'s range [0,%d]' %
(dock_idx, len(wld_area[1])))
return None
dock_obj = wld_area[1][dock_idx]
if dock_obj.name not in dock_conns[1]:
op.report({'ERROR_INVALID_CONTEXT'}, 'Unable to find sister dock for %s' % dock_obj.name)
return None
other_wld_area = dock_conns[1][dock_obj.name][2].parent
if other_wld_area is None:
op.report({'ERROR_INVALID_CONTEXT'}, '%s does not have a parent area' % dock_obj.name)
return None
return other_wld_area.name
# Shared lightmap render procedure
def render_lightmaps(context):
if context.scene is not None:
area_data = context.scene.hecl_srea_data
# Resolution
pixel_size = int(area_data.lightmap_resolution)
# Mmm Cycles
context.scene.render.engine = 'CYCLES'
context.scene.render.bake.margin = pixel_size // 256
# Iterate materials and setup cycles
for mat in bpy.data.materials:
if mat.use_nodes:
# Set bake target node active
if 'CYCLES_OUT' in mat.node_tree.nodes:
mat.node_tree.nodes.active = mat.node_tree.nodes['CYCLES_OUT']
elif mat.hecl_lightmap and not mat.library:
image_out_node = mat.node_tree.nodes.new('ShaderNodeTexImage')
mat.node_tree.nodes.active = image_out_node
image_out_node.name = 'CYCLES_OUT'
image_out_node.image = make_or_load_cycles_image(mat, area_data)
else:
image_out_node = mat.node_tree.nodes.new('ShaderNodeTexImage')
mat.node_tree.nodes.active = image_out_node
image_out_node.name = 'CYCLES_OUT'
if 'FAKE' in bpy.data.images:
image_out_node.image = bpy.data.images['FAKE']
else:
fake_img = bpy.data.images.new('FAKE', 1, 1)
image_out_node.image = fake_img
# Iterate mesh objects and set UV 0 as the active UV layer
for obj in context.scene.objects:
if obj.type == 'MESH':
if not len(obj.data.uv_layers):
continue
# Make correct UV layer active
obj.data.uv_layers.active_index = 0
# Make lightmaps
bpy.ops.object.bake('INVOKE_DEFAULT', type='DIFFUSE', pass_filter={'AO', 'DIRECT', 'INDIRECT'})
# Lightmap render operator
class SREARenderLightmaps(bpy.types.Operator):
bl_idname = "scene.hecl_area_render_lightmaps"
bl_label = "HECL Render New Lightmaps"
bl_description = "Bake new lightmaps for HECL runtime"
@classmethod
def poll(cls, context):
return context.scene is not None
def execute(self, context):
if not context.selected_objects:
for obj in context.scene.objects:
if obj.type == 'MESH' and not obj.library:
obj.select_set(True)
context.view_layer.objects.active = obj
render_lightmaps(context)
return {'FINISHED'}
def shadeless_material(idx):
name = 'SHADELESS_MAT_%d' % idx
if name in bpy.data.materials:
return bpy.data.materials[name]
mat = bpy.data.materials.new(name)
r = idx % 256
g = (idx % 65536) // 256
b = idx // 65536
mat.diffuse_color = Color((r / 255.0, g / 255.0, b / 255.0))
return mat
look_forward = Quaternion((1.0, 0.0, 0.0), math.radians(90.0))
look_backward = Quaternion((0.0, 0.0, 1.0), math.radians(180.0)) @ Quaternion((1.0, 0.0, 0.0), math.radians(90.0))
look_up = Quaternion((1.0, 0.0, 0.0), math.radians(180.0))
look_down = Quaternion((1.0, 0.0, 0.0), math.radians(0.0))
look_left = Quaternion((0.0, 0.0, 1.0), math.radians(90.0)) @ Quaternion((1.0, 0.0, 0.0), math.radians(90.0))
look_right = Quaternion((0.0, 0.0, 1.0), math.radians(-90.0)) @ Quaternion((1.0, 0.0, 0.0), math.radians(90.0))
look_list = (look_forward, look_backward, look_up, look_down, look_left, look_right)
# Render PVS for location
def render_pvs(pathOut, location):
bpy.context.scene.render.resolution_x = 256
bpy.context.scene.render.resolution_y = 256
bpy.context.scene.render.resolution_percentage = 100
bpy.context.scene.render.use_antialiasing = False
bpy.context.scene.render.use_textures = False
bpy.context.scene.render.use_shadows = False
bpy.context.scene.render.use_sss = False
bpy.context.scene.render.use_envmaps = False
bpy.context.scene.render.use_raytrace = False
bpy.context.scene.display_settings.display_device = 'None'
bpy.context.scene.render.image_settings.file_format = 'PNG'
bpy.context.scene.world.horizon_color = Color((1.0, 1.0, 1.0))
bpy.context.scene.world.zenith_color = Color((1.0, 1.0, 1.0))
cam = bpy.data.cameras.new('CUBIC_CAM')
cam_obj = bpy.data.objects.new('CUBIC_CAM', cam)
bpy.context.scene.collection.objects.link(cam_obj)
bpy.context.scene.camera = cam_obj
cam.lens_unit = 'FOV'
cam.angle = math.radians(90.0)
mat_idx = 0
for obj in bpy.context.scene.objects:
if obj.type == 'MESH':
if obj.name == 'CMESH':
continue
mat = shadeless_material(mat_idx)
for slot in obj.material_slots:
slot.material = mat
mat_idx += 1
cam_obj.location = location
cam_obj.rotation_mode = 'QUATERNION'
for i in range(6):
cam_obj.rotation_quaternion = look_list[i]
bpy.context.scene.render.filepath = '%s%d' % (pathOut, i)
bpy.ops.render.render(write_still=True)
bpy.context.scene.camera = None
#bpy.context.scene.objects.unlink(cam_obj)
bpy.data.objects.remove(cam_obj)
bpy.data.cameras.remove(cam)
# Render PVS for light
def render_pvs_light(pathOut, lightName):
if lightName not in bpy.context.scene.objects:
raise RuntimeError('Unable to find light %s' % lightName)
render_pvs(pathOut, bpy.context.scene.objects[lightName].location)
# Cook
def cook(writebuffunc, platform, endianchar):
print('COOKING SREA')
# Panel draw
def draw(layout, context):
area_data = context.scene.hecl_srea_data
layout.label(text="Lighting:", icon='LIGHT')
light_row = layout.row(align=True)
light_row.prop_enum(area_data, 'lightmap_mode', 'NONE')
light_row.prop_enum(area_data, 'lightmap_mode', 'ORIGINAL')
light_row.prop_enum(area_data, 'lightmap_mode', 'CYCLES')
layout.prop(area_data, 'lightmap_resolution', text="Resolution")
layout.popover("CYCLES_PT_sampling_presets", text=bpy.types.CYCLES_PT_sampling_presets.bl_label)
layout.prop(context.scene.render.bake, "use_clear", text="Clear Before Baking")
layout.prop(area_data, 'adjacent_area', text='Adjacent Dock Index', icon='MOD_OPACITY')
layout.operator("scene.hecl_area_render_lightmaps", text="Bake Cycles Lightmaps", icon='RENDER_STILL')
layout.operator("image.save_dirty", text="Save Lightmaps", icon='FILE_TICK')
# Load scene callback
@persistent
def scene_loaded(dummy):
preview_update(None, bpy.context)
# Registration
def register():
bpy.utils.register_class(SREAData)
bpy.utils.register_class(SREARenderLightmaps)
bpy.types.Scene.hecl_srea_data = bpy.props.PointerProperty(type=SREAData)
bpy.types.Material.hecl_lightmap = bpy.props.StringProperty(name='HECL: Lightmap Base Name')
bpy.app.handlers.load_post.append(scene_loaded)
def unregister():
bpy.utils.unregister_class(SREAData)
bpy.utils.unregister_class(SREARenderLightmaps)

View File

@ -0,0 +1,79 @@
import bpy, struct
from mathutils import Vector
def build_dock_connections(scene):
areas = []
docks = []
for obj in sorted(scene.objects, key=lambda x: x.name):
if obj.type == 'MESH' and obj.parent is None:
dock_list = []
for ch in obj.children:
if ch.type == 'MESH':
docks.append((len(areas), len(dock_list), ch))
dock_list.append(ch)
areas.append((obj, dock_list))
dock_dict = dict()
for dockA in docks:
mtxA = dockA[2].matrix_world
locA = Vector((mtxA[0][3], mtxA[1][3], mtxA[2][3]))
match = False
for dockB in docks:
if dockA == dockB:
continue
mtxB = dockB[2].matrix_world
locB = Vector((mtxB[0][3], mtxB[1][3], mtxB[2][3]))
if (locA - locB).magnitude < 0.1:
dock_dict[dockA[2].name] = dockB
match = True
break
#if not match:
# raise RuntimeError('No dock match for %s' % dockA[2].name)
return (areas, dock_dict)
# Cook
def cook(writebuf):
areas, dock_conns = build_dock_connections(bpy.context.scene)
writebuf(struct.pack('I', len(areas)))
for area in areas:
obj = area[0]
dock_list = area[1]
writebuf(struct.pack('I', len(obj.name)))
writebuf(obj.name.encode())
pt = Vector(obj.bound_box[0])
writebuf(struct.pack('fff', pt[0], pt[1], pt[2]))
pt = Vector(obj.bound_box[6])
writebuf(struct.pack('fff', pt[0], pt[1], pt[2]))
wmtx = obj.matrix_world
writebuf(struct.pack('ffffffffffffffff',
wmtx[0][0], wmtx[0][1], wmtx[0][2], wmtx[0][3],
wmtx[1][0], wmtx[1][1], wmtx[1][2], wmtx[1][3],
wmtx[2][0], wmtx[2][1], wmtx[2][2], wmtx[2][3],
wmtx[3][0], wmtx[3][1], wmtx[3][2], wmtx[3][3]))
wmtx_inv = wmtx.inverted()
writebuf(struct.pack('I', len(dock_list)))
for ch in dock_list:
if len(ch.data.vertices) < 4:
raise RuntimeError('Not enough vertices in dock %s' % ch.name)
wmtx = wmtx_inv @ ch.matrix_world
for vi in range(4):
v = wmtx @ ch.data.vertices[vi].co
writebuf(struct.pack('fff', v[0], v[1], v[2]))
if ch.name in dock_conns:
conn_dock = dock_conns[ch.name]
writebuf(struct.pack('I', conn_dock[0]))
writebuf(struct.pack('I', conn_dock[1]))
else:
writebuf(struct.pack('I', 0xffffffff))
writebuf(struct.pack('I', 0xffffffff))
# Panel draw
def draw(layout, context):
pass

View File

@ -0,0 +1,590 @@
import bpy, sys, os, re, struct, traceback
ARGS_PATTERN = re.compile(r'''(?:"([^"]+)"|'([^']+)'|(\S+))''')
# Background mode seems to require quit() in some 2.80 builds
def _quitblender():
bpy.ops.wm.quit_blender()
quit()
MIN_BLENDER_MAJOR = 2
MIN_BLENDER_MINOR = 83
# Extract pipe file descriptors from arguments
print('HECL Blender Launch', sys.argv)
if '--' not in sys.argv:
_quitblender()
args = sys.argv[sys.argv.index('--')+1:]
readfd = int(args[0])
writefd = int(args[1])
verbosity_level = int(args[2])
err_path = ""
if sys.platform == "win32":
import msvcrt
readfd = msvcrt.open_osfhandle(readfd, os.O_RDONLY | os.O_BINARY)
writefd = msvcrt.open_osfhandle(writefd, os.O_WRONLY | os.O_BINARY)
err_path = "/Temp"
if 'TEMP' in os.environ:
err_path = os.environ['TEMP']
else:
err_path = "/tmp"
if 'TMPDIR' in os.environ:
err_path = os.environ['TMPDIR']
err_path += "/hecl_%016X.derp" % os.getpid()
def readpipestr():
read_bytes = os.read(readfd, 4)
if len(read_bytes) != 4:
print('HECL connection lost or desynchronized')
_quitblender()
read_len = struct.unpack('I', read_bytes)[0]
return os.read(readfd, read_len)
def writepipestr(linebytes):
#print('LINE', linebytes)
os.write(writefd, struct.pack('I', len(linebytes)))
os.write(writefd, linebytes)
def writepipebuf(linebytes):
#print('BUF', linebytes)
os.write(writefd, linebytes)
def quitblender():
writepipestr(b'QUITTING')
_quitblender()
class PathHasher:
def hashpath32(self, path):
writepipestr(path.encode())
read_str = readpipestr()
return int(read_str[0:8], 16)
# Ensure Blender 2.83+ is being used
if bpy.app.version < (MIN_BLENDER_MAJOR, MIN_BLENDER_MINOR, 0):
writepipestr(b'INVALIDBLENDERVER')
_quitblender()
# If there's a third argument, use it as the .zip path containing the addon
did_install = False
if len(args) >= 4 and args[3] != 'SKIPINSTALL':
bpy.ops.preferences.addon_install(overwrite=True, target='DEFAULT', filepath=args[3])
bpy.ops.preferences.addon_refresh()
did_install = True
# Make addon available to commands
if bpy.context.preferences.addons.find('hecl') == -1:
try:
bpy.ops.preferences.addon_enable(module='hecl')
bpy.ops.wm.save_userpref()
except:
pass
try:
import hecl
except:
writepipestr(b'NOADDON')
_quitblender()
# Quit if just installed
if did_install:
writepipestr(b'ADDONINSTALLED')
_quitblender()
# Intro handshake
writepipestr(b'READY')
ackbytes = readpipestr()
if ackbytes != b'ACK':
quitblender()
# Count brackets
def count_brackets(linestr):
bracket_count = 0
for ch in linestr:
if ch in {'[','{','('}:
bracket_count += 1
elif ch in {']','}',')'}:
bracket_count -= 1
return bracket_count
# Read line of space-separated/quoted arguments
def read_cmdargs():
cmdline = readpipestr()
if cmdline == b'':
print('HECL connection lost')
_quitblender()
cmdargs = []
for match in ARGS_PATTERN.finditer(cmdline.decode()):
cmdargs.append(match.group(match.lastindex))
return cmdargs
# Complete sequences of statements compiled/executed here
def exec_compbuf(compbuf, globals):
if verbosity_level >= 3:
print(compbuf)
try:
co = compile(compbuf, '<HECL>', 'exec')
exec(co, globals)
except Exception as e:
trace_prefix = 'Error processing:\n'
trace_prefix += compbuf
raise RuntimeError(trace_prefix) from e
# Command loop for writing animation key data to blender
def animin_loop(globals):
writepipestr(b'ANIMREADY')
while True:
crv_type = struct.unpack('b', os.read(readfd, 1))
if crv_type[0] < 0:
writepipestr(b'ANIMDONE')
return
elif crv_type[0] == 0:
crvs = globals['rotCurves']
elif crv_type[0] == 1:
crvs = globals['transCurves']
elif crv_type[0] == 2:
crvs = globals['scaleCurves']
key_info = struct.unpack('ii', os.read(readfd, 8))
crv = crvs[key_info[0]]
crv.keyframe_points.add(count=key_info[1])
if crv_type[0] == 1:
for k in range(key_info[1]):
key_data = struct.unpack('if', os.read(readfd, 8))
pt = crv.keyframe_points[k]
pt.interpolation = 'LINEAR'
pt.co = (key_data[0], key_data[1])
else:
for k in range(key_info[1]):
key_data = struct.unpack('if', os.read(readfd, 8))
pt = crv.keyframe_points[k]
pt.interpolation = 'LINEAR'
pt.co = (key_data[0], key_data[1])
def writelight(obj):
wmtx = obj.matrix_world
writepipebuf(struct.pack('ffffffffffffffff',
wmtx[0][0], wmtx[0][1], wmtx[0][2], wmtx[0][3],
wmtx[1][0], wmtx[1][1], wmtx[1][2], wmtx[1][3],
wmtx[2][0], wmtx[2][1], wmtx[2][2], wmtx[2][3],
wmtx[3][0], wmtx[3][1], wmtx[3][2], wmtx[3][3]))
writepipebuf(struct.pack('fff', obj.data.color[0], obj.data.color[1], obj.data.color[2]))
type = 2
spotCutoff = 0.0
hasFalloff = False
castShadow = False
if obj.data.type == 'POINT':
type = 2
hasFalloff = True
castShadow = obj.data.use_shadow
elif obj.data.type == 'SPOT':
type = 3
hasFalloff = True
spotCutoff = obj.data.spot_size
castShadow = obj.data.use_shadow
elif obj.data.type == 'SUN':
type = 1
castShadow = obj.data.use_shadow
constant = 1.0
linear = 0.0
quadratic = 0.0
if hasFalloff:
if obj.data.falloff_type == 'INVERSE_COEFFICIENTS':
constant = obj.data.constant_coefficient
linear = obj.data.linear_coefficient
quadratic = obj.data.quadratic_coefficient
layer = 0
if 'retro_layer' in obj.data.keys():
layer = obj.data['retro_layer']
writepipebuf(struct.pack('IIfffffb', layer, type, obj.data.energy, spotCutoff, constant, linear, quadratic,
castShadow))
writepipestr(obj.name.encode())
# Command loop for reading data from blender
def dataout_loop():
writepipestr(b'READY')
while True:
cmdargs = read_cmdargs()
print(cmdargs)
if cmdargs[0] == 'DATAEND':
writepipestr(b'DONE')
return
elif cmdargs[0] == 'MESHLIST':
meshCount = 0
for meshobj in bpy.data.objects:
if meshobj.type == 'MESH' and not meshobj.data.library:
meshCount += 1
writepipebuf(struct.pack('I', meshCount))
for meshobj in bpy.data.objects:
if meshobj.type == 'MESH' and not meshobj.data.library:
writepipestr(meshobj.name.encode())
elif cmdargs[0] == 'LIGHTLIST':
lightCount = 0
for obj in bpy.context.scene.objects:
if obj.type == 'LIGHT' and not obj.data.library:
lightCount += 1
writepipebuf(struct.pack('I', lightCount))
for obj in bpy.context.scene.objects:
if obj.type == 'LIGHT' and not obj.data.library:
writepipestr(obj.name.encode())
elif cmdargs[0] == 'MESHAABB':
writepipestr(b'OK')
hecl.mesh_aabb(writepipebuf)
elif cmdargs[0] == 'MESHCOMPILE':
meshName = bpy.context.scene.hecl_mesh_obj
if meshName not in bpy.data.objects:
writepipestr(('mesh %s not found' % meshName).encode())
continue
writepipestr(b'OK')
hecl.hmdl.cook(writepipebuf, bpy.data.objects[meshName])
elif cmdargs[0] == 'ARMATURECOMPILE':
armName = bpy.context.scene.hecl_arm_obj
if armName not in bpy.data.objects:
writepipestr(('armature %s not found' % armName).encode())
continue
writepipestr(b'OK')
hecl.armature.cook(writepipebuf, bpy.data.objects[armName].data)
elif cmdargs[0] == 'MESHCOMPILENAME':
meshName = cmdargs[1]
useLuv = int(cmdargs[2])
if meshName not in bpy.data.objects:
writepipestr(('mesh %s not found' % meshName).encode())
continue
writepipestr(b'OK')
hecl.hmdl.cook(writepipebuf, bpy.data.objects[meshName], useLuv)
elif cmdargs[0] == 'MESHCOMPILENAMECOLLISION':
meshName = cmdargs[1]
if meshName not in bpy.data.objects:
writepipestr(('mesh %s not found' % meshName).encode())
continue
writepipestr(b'OK')
hecl.hmdl.cookcol(writepipebuf, bpy.data.objects[meshName])
elif cmdargs[0] == 'MESHCOMPILECOLLISIONALL':
writepipestr(b'OK')
colCount = 0
for obj in bpy.context.scene.objects:
if obj.type == 'MESH' and not obj.data.library:
colCount += 1
writepipebuf(struct.pack('I', colCount))
for obj in bpy.context.scene.objects:
if obj.type == 'MESH' and not obj.data.library:
hecl.hmdl.cookcol(writepipebuf, obj)
elif cmdargs[0] == 'MESHCOMPILEPATH':
meshName = bpy.context.scene.hecl_path_obj
if meshName not in bpy.data.objects:
writepipestr(('mesh %s not found' % meshName).encode())
continue
writepipestr(b'OK')
hecl.path.cook(writepipebuf, bpy.data.objects[meshName])
elif cmdargs[0] == 'WORLDCOMPILE':
writepipestr(b'OK')
hecl.swld.cook(writepipebuf)
elif cmdargs[0] == 'FRAMECOMPILE':
version = int(cmdargs[1])
if version != 0 and version != 1:
writepipestr(b'bad version')
continue
writepipestr(b'OK')
buffer = hecl.frme.cook(writepipebuf, version, PathHasher())
writepipestr(b'FRAMEDONE')
writepipebuf(struct.pack('I', len(buffer)))
writepipebuf(buffer)
elif cmdargs[0] == 'LIGHTCOMPILEALL':
writepipestr(b'OK')
lampCount = 0
firstSpot = None
for obj in bpy.context.scene.objects:
if obj.type == 'LIGHT':
lampCount += 1
if firstSpot is None and obj.data.type == 'SPOT':
firstSpot = obj
# Ambient
world = bpy.context.scene.world
ambient_energy = 0.0
ambient_color = None
if world.use_nodes and 'Background' in world.node_tree.nodes:
bg_node = world.node_tree.nodes['Background']
ambient_energy = bg_node.inputs[1].default_value
ambient_color = bg_node.inputs[0].default_value
if ambient_energy:
lampCount += 1
writepipebuf(struct.pack('I', lampCount))
if firstSpot is not None:
writelight(firstSpot)
if ambient_energy:
writepipebuf(struct.pack('ffffffffffffffff',
1.0, 0.0, 0.0, 0.0,
0.0, 1.0, 0.0, 0.0,
0.0, 0.0, 1.0, 0.0,
0.0, 0.0, 0.0, 1.0))
writepipebuf(struct.pack('fff', ambient_color[0], ambient_color[1], ambient_color[2]))
writepipebuf(struct.pack('IIfffffb', 0, 0, ambient_energy, 0.0, 1.0, 0.0, 0.0, False))
writepipestr(b'AMBIENT')
# Lamp objects
for obj in bpy.context.scene.objects:
if obj != firstSpot and obj.type == 'LIGHT':
writelight(obj)
elif cmdargs[0] == 'GETTEXTURES':
writepipestr(b'OK')
img_count = 0
for img in bpy.data.images:
if img.type == 'IMAGE':
img_count += 1
writepipebuf(struct.pack('I', img_count))
for img in bpy.data.images:
if img.type == 'IMAGE':
path = os.path.normpath(bpy.path.abspath(img.filepath))
writepipebuf(struct.pack('I', len(path)))
writepipebuf(path.encode())
elif cmdargs[0] == 'ACTORCOMPILE':
writepipestr(b'OK')
hecl.sact.cook(writepipebuf)
elif cmdargs[0] == 'ACTORCOMPILECHARACTERONLY':
writepipestr(b'OK')
hecl.sact.cook_character_only(writepipebuf)
elif cmdargs[0] == 'ACTIONCOMPILECHANNELSONLY':
actionName = cmdargs[1]
writepipestr(b'OK')
hecl.sact.cook_action_channels_only(writepipebuf, actionName)
elif cmdargs[0] == 'GETSUBTYPENAMES':
writepipestr(b'OK')
hecl.sact.get_subtype_names(writepipebuf)
elif cmdargs[0] == 'GETSUBTYPEOVERLAYNAMES':
subtypeName = cmdargs[1]
writepipestr(b'OK')
hecl.sact.get_subtype_overlay_names(writepipebuf, subtypeName)
elif cmdargs[0] == 'GETATTACHMENTNAMES':
writepipestr(b'OK')
hecl.sact.get_attachment_names(writepipebuf)
elif cmdargs[0] == 'GETACTIONNAMES':
writepipestr(b'OK')
hecl.sact.get_action_names(writepipebuf)
elif cmdargs[0] == 'GETBONEMATRICES':
armName = cmdargs[1]
if armName not in bpy.data.objects:
writepipestr(('armature %s not found' % armName).encode())
continue
armObj = bpy.data.objects[armName]
if armObj.type != 'ARMATURE':
writepipestr(('object %s not an ARMATURE' % armName).encode())
continue
writepipestr(b'OK')
writepipebuf(struct.pack('I', len(armObj.data.bones)))
for bone in armObj.data.bones:
writepipebuf(struct.pack('I', len(bone.name)))
writepipebuf(bone.name.encode())
for r in bone.matrix_local.to_3x3():
for c in r:
writepipebuf(struct.pack('f', c))
elif cmdargs[0] == 'RENDERPVS':
pathOut = cmdargs[1]
locX = float(cmdargs[2])
locY = float(cmdargs[3])
locZ = float(cmdargs[4])
hecl.srea.render_pvs(pathOut, (locX, locY, locZ))
writepipestr(b'OK')
elif cmdargs[0] == 'RENDERPVSLIGHT':
pathOut = cmdargs[1]
lightName = cmdargs[2]
hecl.srea.render_pvs_light(pathOut, lightName)
writepipestr(b'OK')
elif cmdargs[0] == 'MAPAREACOMPILE':
if 'MAP' not in bpy.data.objects:
writepipestr(('"MAP" object not in .blend').encode())
continue
map_obj = bpy.data.objects['MAP']
if map_obj.type != 'MESH':
writepipestr(('object "MAP" not a MESH').encode())
continue
writepipestr(b'OK')
hecl.mapa.cook(writepipebuf, map_obj)
elif cmdargs[0] == 'MAPUNIVERSECOMPILE':
writepipestr(b'OK')
hecl.mapu.cook(writepipebuf)
loaded_blend = None
# Main exception handling
try:
# Command loop
while True:
cmdargs = read_cmdargs()
print(cmdargs)
if cmdargs[0] == 'QUIT':
quitblender()
elif cmdargs[0] == 'OPEN':
if 'FINISHED' in bpy.ops.wm.open_mainfile(filepath=cmdargs[1]):
if bpy.ops.object.mode_set.poll():
bpy.ops.object.mode_set(mode = 'OBJECT')
loaded_blend = cmdargs[1]
writepipestr(b'FINISHED')
else:
writepipestr(b'CANCELLED')
elif cmdargs[0] == 'CREATE':
if len(cmdargs) >= 4:
bpy.ops.wm.open_mainfile(filepath=cmdargs[3])
else:
bpy.ops.wm.read_homefile(use_empty=True)
bpy.context.scene.world = bpy.data.worlds.new('World')
loaded_blend = cmdargs[1]
bpy.context.preferences.filepaths.save_version = 0
if 'FINISHED' in bpy.ops.wm.save_as_mainfile(filepath=cmdargs[1]):
bpy.ops.file.hecl_patching_load()
bpy.context.scene.hecl_type = cmdargs[2]
writepipestr(b'FINISHED')
else:
writepipestr(b'CANCELLED')
elif cmdargs[0] == 'GETTYPE':
writepipestr(bpy.context.scene.hecl_type.encode())
elif cmdargs[0] == 'GETMESHRIGGED':
meshName = bpy.context.scene.hecl_mesh_obj
if meshName not in bpy.data.objects:
writepipestr(b'FALSE')
else:
if len(bpy.data.objects[meshName].vertex_groups):
writepipestr(b'TRUE')
else:
writepipestr(b'FALSE')
elif cmdargs[0] == 'SAVE':
bpy.context.preferences.filepaths.save_version = 0
print('SAVING %s' % loaded_blend)
if loaded_blend:
if 'FINISHED' in bpy.ops.wm.save_as_mainfile(filepath=loaded_blend, check_existing=False, compress=True):
writepipestr(b'FINISHED')
else:
writepipestr(b'CANCELLED')
elif cmdargs[0] == 'PYBEGIN':
writepipestr(b'READY')
globals = {'hecl':hecl}
compbuf = str()
bracket_count = 0
while True:
try:
line = readpipestr()
# ANIM check
if line == b'PYANIM':
# Ensure remaining block gets executed
if len(compbuf):
exec_compbuf(compbuf, globals)
compbuf = str()
animin_loop(globals)
continue
# End check
elif line == b'PYEND':
# Ensure remaining block gets executed
if len(compbuf):
exec_compbuf(compbuf, globals)
compbuf = str()
writepipestr(b'DONE')
break
# Syntax filter
linestr = line.decode().rstrip()
if not len(linestr) or linestr.lstrip()[0] == '#':
writepipestr(b'OK')
continue
leading_spaces = len(linestr) - len(linestr.lstrip())
# Block lines always get appended right away
if linestr.endswith(':') or leading_spaces or bracket_count:
if len(compbuf):
compbuf += '\n'
compbuf += linestr
bracket_count += count_brackets(linestr)
writepipestr(b'OK')
continue
# Complete non-block statement in compbuf
if len(compbuf):
exec_compbuf(compbuf, globals)
# Establish new compbuf
compbuf = linestr
bracket_count += count_brackets(linestr)
except Exception as e:
writepipestr(b'EXCEPTION')
raise
break
writepipestr(b'OK')
elif cmdargs[0] == 'PYEND':
writepipestr(b'ERROR')
elif cmdargs[0] == 'DATABEGIN':
try:
dataout_loop()
except Exception as e:
writepipestr(b'EXCEPTION')
raise
elif cmdargs[0] == 'DATAEND':
writepipestr(b'ERROR')
else:
hecl.command(cmdargs, writepipestr, writepipebuf)
except Exception:
fout = open(err_path, 'w')
traceback.print_exc(file=fout)
fout.close()
raise

View File

@ -0,0 +1,18 @@
import sys, os
import zipfile
def zipdir(path, ziph):
# ziph is zipfile handle
for root, dirs, files in os.walk(path):
for file in files:
ziph.write(os.path.join(root, file))
package_path = 'hecl'
target_zip = sys.argv[1]
zf = zipfile.ZipFile(target_zip, mode='w', compression=zipfile.ZIP_DEFLATED)
#print('GENERATING', target_zip)
if os.path.isdir(package_path):
zipdir(package_path, zf)
zf.close()

3
hecl/bootstrap.sh Executable file
View File

@ -0,0 +1,3 @@
#!/bin/bash
git submodule update --init --recursive

View File

@ -0,0 +1,33 @@
if(NOT WINDOWS_STORE)
add_executable(hecl main.cpp
ToolBase.hpp
ToolPackage.hpp
ToolExtract.hpp
ToolInit.hpp
ToolHelp.hpp
ToolCook.hpp
ToolImage.hpp
ToolSpec.hpp
../DataSpecRegistry.hpp.in)
if(COMMAND add_sanitizers)
add_sanitizers(hecl)
endif()
if(NOT WIN32)
list(APPEND PLAT_LIBS pthread)
endif()
if(APPLE)
find_library(CF_LIBRARY CoreFoundation)
list(APPEND PLAT_LIBS ${CF_LIBRARY})
endif()
target_link_libraries(hecl PUBLIC ${DATA_SPEC_LIBS} hecl-full)
if(TARGET nod)
target_link_libraries(hecl PUBLIC nod)
target_compile_definitions(hecl PUBLIC HECL_HAS_NOD=1)
endif()
endif()

235
hecl/driver/ToolBase.hpp Normal file
View File

@ -0,0 +1,235 @@
#pragma once
#include <string>
#include <vector>
#include <list>
#include <cstdio>
#include <cstring>
#ifndef _WIN32
#include <unistd.h>
#include <termios.h>
#else
#include <conio.h>
#endif
#include "hecl/Database.hpp"
#include "logvisor/logvisor.hpp"
extern logvisor::Module LogModule;
struct ToolPassInfo {
hecl::SystemString pname;
hecl::SystemString cwd;
std::vector<hecl::SystemString> args;
std::vector<hecl::SystemChar> flags;
hecl::SystemString output;
hecl::Database::Project* project = nullptr;
unsigned verbosityLevel = 0;
bool force = false;
bool yes = false;
bool gui = false;
};
#define RED "\033[0;31m"
#define GREEN "\033[0;32m"
#define YELLOW "\033[0;33m"
#define BLUE "\033[0;34m"
#define MAGENTA "\033[0;35m"
#define CYAN "\033[0;36m"
#define BOLD "\033[1m"
#define NORMAL "\033[0m"
#define HIDE_CURSOR "\033[?25l"
#define SHOW_CURSOR "\033[?25h"
#define WRAP_INDENT 4
extern bool XTERM_COLOR;
class ToolBase {
protected:
const ToolPassInfo& m_info;
bool m_good = false;
bool continuePrompt() {
if (!m_info.yes) {
if (XTERM_COLOR)
fmt::print(FMT_STRING(_SYS_STR("\n" BLUE BOLD "Continue?" NORMAL " (Y/n) ")));
else
fmt::print(FMT_STRING(_SYS_STR("\nContinue? (Y/n) ")));
fflush(stdout);
int ch;
#ifndef _WIN32
struct termios tioOld, tioNew;
tcgetattr(0, &tioOld);
tioNew = tioOld;
tioNew.c_lflag &= ~ICANON;
tcsetattr(0, TCSANOW, &tioNew);
while ((ch = getchar()))
#else
while ((ch = getch()))
#endif
{
if (ch == 'n' || ch == 'N') {
fmt::print(FMT_STRING(_SYS_STR("\n")));
return false;
}
if (ch == 'y' || ch == 'Y' || ch == '\r' || ch == '\n')
break;
}
#ifndef _WIN32
tcsetattr(0, TCSANOW, &tioOld);
#endif
}
fmt::print(FMT_STRING(_SYS_STR("\n")));
return true;
}
public:
explicit ToolBase(const ToolPassInfo& info) : m_info(info) {
hecl::VerbosityLevel = info.verbosityLevel;
hecl::GuiMode = info.gui;
}
virtual ~ToolBase() = default;
virtual hecl::SystemStringView toolName() const = 0;
virtual int run() = 0;
virtual void cancel() {}
explicit operator bool() const { return m_good; }
};
class HelpOutput {
public:
using HelpFunc = void (*)(HelpOutput&);
private:
FILE* m_sout = nullptr;
HelpFunc m_helpFunc;
int m_lineWidth;
hecl::SystemString m_wrapBuffer;
void _wrapBuf(hecl::SystemString& string) {
int counter;
hecl::SystemString::iterator it = string.begin();
while (it != string.end()) {
std::ptrdiff_t v = it - string.begin();
/* copy string until the end of the line is reached */
for (counter = WRAP_INDENT; counter < m_lineWidth; ++counter) {
if (it >= string.end())
return;
if (*it == _SYS_STR('\n')) {
counter = WRAP_INDENT;
++it;
}
if (counter == WRAP_INDENT) {
for (int i = 0; i < WRAP_INDENT; ++i)
it = string.insert(it, _SYS_STR(' ')) + 1;
}
if (it >= string.end())
return;
if (*it != _SYS_STR('\n'))
++it;
}
/* check for whitespace */
if (isspace(*it)) {
*it = _SYS_STR('\n');
counter = WRAP_INDENT;
++it;
} else {
/* check for nearest whitespace back in string */
for (hecl::SystemString::iterator k = it; k != string.begin(); --k) {
if (isspace(*k)) {
counter = WRAP_INDENT;
if (k - string.begin() < v)
k = string.insert(it, _SYS_STR('\n'));
else
*k = _SYS_STR('\n');
it = k + 1;
break;
}
}
}
}
}
public:
explicit HelpOutput(HelpFunc helpFunc)
: m_helpFunc(helpFunc), m_lineWidth(hecl::GuiMode ? 120 : hecl::ConsoleWidth()) {}
void go() {
#if _WIN32
m_sout = stdout;
m_helpFunc(*this);
#else
m_sout = popen("less -R", "w");
if (m_sout) {
m_helpFunc(*this);
pclose(m_sout);
} else {
m_sout = stdout;
m_helpFunc(*this);
}
#endif
}
void print(const hecl::SystemChar* str) { fmt::print(m_sout, FMT_STRING(_SYS_STR("{}")), str); }
void printBold(const hecl::SystemChar* str) {
if (XTERM_COLOR)
fmt::print(m_sout, FMT_STRING(_SYS_STR("" BOLD "{}" NORMAL "")), str);
else
fmt::print(m_sout, FMT_STRING(_SYS_STR("{}")), str);
}
void secHead(const hecl::SystemChar* headName) {
if (XTERM_COLOR)
fmt::print(m_sout, FMT_STRING(_SYS_STR("" BOLD "{}" NORMAL "\n")), headName);
else
fmt::print(m_sout, FMT_STRING(_SYS_STR("{}\n")), headName);
}
void optionHead(const hecl::SystemChar* flag, const hecl::SystemChar* synopsis) {
if (XTERM_COLOR)
fmt::print(m_sout, FMT_STRING(_SYS_STR("" BOLD "{}" NORMAL " ({})\n")), flag, synopsis);
else
fmt::print(m_sout, FMT_STRING(_SYS_STR("{} ({})\n")), flag, synopsis);
}
void beginWrap() { m_wrapBuffer.clear(); }
void wrap(const hecl::SystemChar* str) { m_wrapBuffer += str; }
void wrapBold(const hecl::SystemChar* str) {
if (XTERM_COLOR)
m_wrapBuffer += _SYS_STR("" BOLD "");
m_wrapBuffer += str;
if (XTERM_COLOR)
m_wrapBuffer += _SYS_STR("" NORMAL "");
}
void endWrap() {
_wrapBuf(m_wrapBuffer);
m_wrapBuffer += _SYS_STR('\n');
fmt::print(m_sout, FMT_STRING(_SYS_STR("{}")), m_wrapBuffer);
m_wrapBuffer.clear();
}
};
static hecl::SystemString MakePathArgAbsolute(const hecl::SystemString& arg, const hecl::SystemString& cwd) {
#if _WIN32
if (arg.size() >= 2 && iswalpha(arg[0]) && arg[1] == _SYS_STR(':'))
return arg;
if (arg[0] == _SYS_STR('\\') || arg[0] == _SYS_STR('/'))
return arg;
return cwd + _SYS_STR('\\') + arg;
#else
if (arg[0] == _SYS_STR('/') || arg[0] == _SYS_STR('\\'))
return arg;
if (cwd.back() == _SYS_STR('/') || cwd.back() == _SYS_STR('\\'))
return cwd + arg;
return cwd + _SYS_STR('/') + arg;
#endif
}

160
hecl/driver/ToolCook.hpp Normal file
View File

@ -0,0 +1,160 @@
#pragma once
#include "ToolBase.hpp"
#include <cstdio>
#include "hecl/ClientProcess.hpp"
class ToolCook final : public ToolBase {
std::vector<hecl::ProjectPath> m_selectedItems;
std::unique_ptr<hecl::Database::Project> m_fallbackProj;
hecl::Database::Project* m_useProj;
const hecl::Database::DataSpecEntry* m_spec = nullptr;
bool m_recursive = false;
bool m_fast = false;
public:
explicit ToolCook(const ToolPassInfo& info) : ToolBase(info), m_useProj(info.project) {
/* Check for recursive flag */
for (hecl::SystemChar arg : info.flags)
if (arg == _SYS_STR('r'))
m_recursive = true;
/* Scan args */
if (info.args.size()) {
/* See if project path is supplied via args and use that over the getcwd one */
m_selectedItems.reserve(info.args.size());
for (const hecl::SystemString& arg : info.args) {
if (arg.empty())
continue;
else if (arg == _SYS_STR("--fast")) {
m_fast = true;
continue;
} else if (arg.size() >= 8 && !arg.compare(0, 7, _SYS_STR("--spec="))) {
hecl::SystemString specName(arg.begin() + 7, arg.end());
for (const hecl::Database::DataSpecEntry* spec : hecl::Database::DATA_SPEC_REGISTRY) {
if (!hecl::StrCaseCmp(spec->m_name.data(), specName.c_str())) {
m_spec = spec;
break;
}
}
if (!m_spec)
LogModule.report(logvisor::Fatal, FMT_STRING(_SYS_STR("unable to find data spec '{}'")), specName);
continue;
} else if (arg.size() >= 2 && arg[0] == _SYS_STR('-') && arg[1] == _SYS_STR('-'))
continue;
hecl::SystemString subPath;
hecl::ProjectRootPath root = hecl::SearchForProject(MakePathArgAbsolute(arg, info.cwd), subPath);
if (root) {
if (!m_fallbackProj) {
m_fallbackProj.reset(new hecl::Database::Project(root));
m_useProj = m_fallbackProj.get();
} else if (m_fallbackProj->getProjectRootPath() != root)
LogModule.report(logvisor::Fatal,
FMT_STRING(_SYS_STR("hecl cook can only process multiple items in the same project; ")
_SYS_STR("'{}' and '{}' are different projects")),
m_fallbackProj->getProjectRootPath().getAbsolutePath(),
root.getAbsolutePath());
m_selectedItems.emplace_back(*m_useProj, subPath);
}
}
}
if (!m_useProj)
LogModule.report(logvisor::Fatal,
FMT_STRING("hecl cook must be ran within a project directory or "
"provided a path within a project"));
/* Default case: recursive at root */
if (m_selectedItems.empty()) {
m_selectedItems.reserve(1);
m_selectedItems.push_back({hecl::ProjectPath(*m_useProj, _SYS_STR(""))});
m_recursive = true;
}
}
static void Help(HelpOutput& help) {
help.secHead(_SYS_STR("NAME"));
help.beginWrap();
help.wrap(_SYS_STR("hecl-cook - Cook objects within the project database\n"));
help.endWrap();
help.secHead(_SYS_STR("SYNOPSIS"));
help.beginWrap();
help.wrap(_SYS_STR("hecl cook [-rf] [--fast] [--spec=<spec>] [<pathspec>...]\n"));
help.endWrap();
help.secHead(_SYS_STR("DESCRIPTION"));
help.beginWrap();
help.wrap(_SYS_STR("This command initiates a cooking pass on the project database. Cooking ")
_SYS_STR("is analogous to compiling in software development. The resulting object buffers ")
_SYS_STR("are cached within the project database. HECL performs the following ")
_SYS_STR("tasks for each object during the cook process:\n\n"));
help.wrapBold(_SYS_STR("- Object Gather: "));
help.wrap(_SYS_STR("Files added with "));
help.wrapBold(_SYS_STR("hecl add"));
help.wrap(_SYS_STR(" are queried for their dependent files (e.g. "));
help.wrapBold(_SYS_STR(".blend"));
help.wrap(_SYS_STR(" files return any linked "));
help.wrapBold(_SYS_STR(".png"));
help.wrap(_SYS_STR(" images). If the dependent files are unable to be found, the cook process aborts.\n\n"));
help.wrapBold(_SYS_STR("- Modtime Comparison: "));
help.wrap(_SYS_STR("Files that have previously finished a cook pass are inspected for their time of ")
_SYS_STR("last modification. If the file hasn't changed since its previous cook-pass, the ") _SYS_STR(
"process is skipped. If the file has been moved or deleted, the object is automatically ")
_SYS_STR("removed from the project database.\n\n"));
help.wrapBold(_SYS_STR("- Cook: "));
help.wrap(_SYS_STR("A type-specific procedure compiles the file's contents into an efficient format ")
_SYS_STR("for use by the runtime. A data-buffer is provided to HECL.\n\n"));
help.wrapBold(_SYS_STR("- Hash and Compress: "));
help.wrap(_SYS_STR("The data-buffer is hashed and compressed before being cached in the object database.\n\n"));
help.endWrap();
help.secHead(_SYS_STR("OPTIONS"));
help.optionHead(_SYS_STR("<pathspec>..."), _SYS_STR("input file(s)"));
help.beginWrap();
help.wrap(_SYS_STR("Specifies working file(s) containing production data to be cooked by HECL. ")
_SYS_STR("Glob-strings may be specified (e.g. "));
help.wrapBold(_SYS_STR("*.blend"));
help.wrap(_SYS_STR(") to automatically cook all matching current-directory files in the project database. ")
_SYS_STR("If no path specified, all files in the project database are cooked.\n"));
help.endWrap();
help.optionHead(_SYS_STR("-r"), _SYS_STR("recursion"));
help.beginWrap();
help.wrap(_SYS_STR("Enables recursive file-matching for cooking entire directories of working files.\n"));
help.endWrap();
help.optionHead(_SYS_STR("-f"), _SYS_STR("force"));
help.beginWrap();
help.wrap(_SYS_STR("Forces cooking of all matched files, ignoring timestamp differences.\n"));
help.endWrap();
help.optionHead(_SYS_STR("--fast"), _SYS_STR("fast cook"));
help.beginWrap();
help.wrap(_SYS_STR("Performs draft-optimization cooking for supported data types.\n"));
help.endWrap();
help.optionHead(_SYS_STR("--spec=<spec>"), _SYS_STR("data specification"));
help.beginWrap();
help.wrap(_SYS_STR("Specifies a DataSpec to use when cooking. ")
_SYS_STR("This build of hecl supports the following values of <spec>:\n"));
for (const hecl::Database::DataSpecEntry* spec : hecl::Database::DATA_SPEC_REGISTRY) {
if (!spec->m_factory)
continue;
help.wrap(_SYS_STR(" "));
help.wrapBold(spec->m_name.data());
help.wrap(_SYS_STR("\n"));
}
}
hecl::SystemStringView toolName() const override { return _SYS_STR("cook"sv); }
int run() override {
hecl::MultiProgressPrinter printer(true);
hecl::ClientProcess cp(&printer);
for (const hecl::ProjectPath& path : m_selectedItems)
m_useProj->cookPath(path, printer, m_recursive, m_info.force, m_fast, m_spec, &cp);
cp.waitUntilComplete();
return 0;
}
void cancel() override { m_useProj->interruptCook(); }
};

166
hecl/driver/ToolExtract.hpp Normal file
View File

@ -0,0 +1,166 @@
#pragma once
#include "ToolBase.hpp"
#include <cstdio>
#if _WIN32
#include <conio.h>
#else
#include <termios.h>
#endif
#include "hecl/MultiProgressPrinter.hpp"
class ToolExtract final : public ToolBase {
hecl::Database::IDataSpec::ExtractPassInfo m_einfo;
struct SpecExtractPass {
const hecl::Database::DataSpecEntry* m_entry;
std::unique_ptr<hecl::Database::IDataSpec> m_instance;
SpecExtractPass(const hecl::Database::DataSpecEntry* entry, std::unique_ptr<hecl::Database::IDataSpec>&& instance)
: m_entry(entry), m_instance(std::move(instance)) {}
SpecExtractPass(const SpecExtractPass& other) = delete;
SpecExtractPass(SpecExtractPass&& other) = default;
};
std::vector<SpecExtractPass> m_specPasses;
std::vector<hecl::Database::IDataSpec::ExtractReport> m_reps;
std::unique_ptr<hecl::Database::Project> m_fallbackProj;
hecl::Database::Project* m_useProj = nullptr;
public:
explicit ToolExtract(const ToolPassInfo& info) : ToolBase(info) {
if (!m_info.args.size())
LogModule.report(logvisor::Fatal, FMT_STRING("hecl extract needs a source path as its first argument"));
if (!info.project) {
hecl::SystemString rootDir;
if (info.output.empty()) {
/* Get name from input file and init project there */
hecl::SystemString baseFile = info.args.front();
size_t slashPos = baseFile.rfind(_SYS_STR('/'));
if (slashPos == hecl::SystemString::npos)
slashPos = baseFile.rfind(_SYS_STR('\\'));
if (slashPos != hecl::SystemString::npos)
baseFile.assign(baseFile.begin() + slashPos + 1, baseFile.end());
size_t dotPos = baseFile.rfind(_SYS_STR('.'));
if (dotPos != hecl::SystemString::npos)
baseFile.assign(baseFile.begin(), baseFile.begin() + dotPos);
if (baseFile.empty())
LogModule.report(logvisor::Fatal, FMT_STRING("hecl extract must be ran within a project directory"));
rootDir = info.cwd + baseFile;
} else {
if (hecl::PathRelative(info.output.c_str()))
rootDir = info.cwd + info.output;
else
rootDir = info.output;
}
size_t ErrorRef = logvisor::ErrorCount;
hecl::ProjectRootPath newProjRoot(rootDir);
newProjRoot.makeDir();
m_fallbackProj.reset(new hecl::Database::Project(newProjRoot));
if (logvisor::ErrorCount > ErrorRef)
LogModule.report(logvisor::Fatal, FMT_STRING(_SYS_STR("unable to init project at '{}'")), rootDir);
LogModule.report(logvisor::Info, FMT_STRING(_SYS_STR("initialized project at '{}/.hecl'")), rootDir);
m_useProj = m_fallbackProj.get();
} else
m_useProj = info.project;
m_einfo.srcpath = m_info.args.front();
m_einfo.force = info.force;
m_einfo.extractArgs.reserve(info.args.size());
auto it = info.args.cbegin();
++it;
for (; it != info.args.cend(); ++it)
m_einfo.extractArgs.push_back(*it);
m_specPasses.reserve(hecl::Database::DATA_SPEC_REGISTRY.size());
for (const hecl::Database::DataSpecEntry* entry : hecl::Database::DATA_SPEC_REGISTRY) {
if (entry->m_factory) {
auto ds = entry->m_factory(*m_useProj, hecl::Database::DataSpecTool::Extract);
if (ds && ds->canExtract(m_einfo, m_reps))
m_specPasses.emplace_back(entry, std::move(ds));
}
}
}
static void Help(HelpOutput& help) {
help.secHead(_SYS_STR("NAME"));
help.beginWrap();
help.wrap(_SYS_STR("hecl-extract - Extract objects from supported package/image formats\n"));
help.endWrap();
help.secHead(_SYS_STR("SYNOPSIS"));
help.beginWrap();
help.wrap(_SYS_STR("hecl extract <packagefile> [<subnode>...]\n"));
help.endWrap();
help.secHead(_SYS_STR("DESCRIPTION"));
help.beginWrap();
help.wrap(_SYS_STR("This command recursively extracts all or part of a dataspec-supported ")
_SYS_STR("package format. Each object is decoded to a working format and added to the project.\n\n"));
help.endWrap();
help.secHead(_SYS_STR("OPTIONS"));
help.optionHead(_SYS_STR("<packagefile>[/<subnode>...]"), _SYS_STR("input file"));
help.beginWrap();
help.wrap(_SYS_STR("Specifies the package file or disc image to source data from. ")
_SYS_STR("An optional subnode specifies a named hierarchical-node specific ")
_SYS_STR("to the game architecture (levels/areas)."));
help.endWrap();
}
hecl::SystemStringView toolName() const override { return _SYS_STR("extract"sv); }
static void _recursivePrint(int level, hecl::Database::IDataSpec::ExtractReport& rep) {
for (int l = 0; l < level; ++l)
fmt::print(FMT_STRING(_SYS_STR(" ")));
if (XTERM_COLOR)
fmt::print(FMT_STRING(_SYS_STR("" BOLD "{}" NORMAL "")), rep.name);
else
fmt::print(FMT_STRING(_SYS_STR("{}")), rep.name);
if (rep.desc.size())
fmt::print(FMT_STRING(_SYS_STR(" [{}]")), rep.desc);
fmt::print(FMT_STRING(_SYS_STR("\n")));
for (hecl::Database::IDataSpec::ExtractReport& child : rep.childOpts)
_recursivePrint(level + 1, child);
}
int run() override {
if (m_specPasses.empty()) {
if (XTERM_COLOR)
fmt::print(FMT_STRING(_SYS_STR("" RED BOLD "NOTHING TO EXTRACT" NORMAL "\n")));
else
fmt::print(FMT_STRING(_SYS_STR("NOTHING TO EXTRACT\n")));
return 1;
}
if (XTERM_COLOR)
fmt::print(FMT_STRING(_SYS_STR("" GREEN BOLD "ABOUT TO EXTRACT:" NORMAL "\n")));
else
fmt::print(FMT_STRING(_SYS_STR("ABOUT TO EXTRACT:\n")));
for (hecl::Database::IDataSpec::ExtractReport& rep : m_reps) {
_recursivePrint(0, rep);
fmt::print(FMT_STRING(_SYS_STR("\n")));
}
fflush(stdout);
if (continuePrompt()) {
for (SpecExtractPass& ds : m_specPasses) {
if (XTERM_COLOR)
fmt::print(FMT_STRING(_SYS_STR("" MAGENTA BOLD "Using DataSpec {}:" NORMAL "\n")), ds.m_entry->m_name);
else
fmt::print(FMT_STRING(_SYS_STR("Using DataSpec {}:\n")), ds.m_entry->m_name);
ds.m_instance->doExtract(m_einfo, {true});
fmt::print(FMT_STRING(_SYS_STR("\n\n")));
}
}
return 0;
}
};

80
hecl/driver/ToolHelp.hpp Normal file
View File

@ -0,0 +1,80 @@
#pragma once
#include "ToolBase.hpp"
#include <cstdio>
#include <functional>
class ToolHelp final : public ToolBase {
public:
explicit ToolHelp(const ToolPassInfo& info) : ToolBase(info) {
if (m_info.args.empty()) {
LogModule.report(logvisor::Error, FMT_STRING("help requires a tool name argument"));
return;
}
m_good = true;
}
~ToolHelp() override = default;
static void Help(HelpOutput& help) {
/* clang-format off */
help.printBold(
_SYS_STR(" ___________ \n")
_SYS_STR(" ,.-'\"...........``~., \n")
_SYS_STR(" ,.-\".......................\"-., \n")
_SYS_STR(" ,/..................................\":, \n")
_SYS_STR(" .,?........................................, \n")
_SYS_STR(" /...........................................,}\n")
_SYS_STR(" ./........................................,:`^`..}\n")
_SYS_STR(" ./.......................................,:\"...../\n")
_SYS_STR(" ?.....__..................................:`...../\n")
_SYS_STR(" /__.(...\"~-,_...........................,:`....../\n")
_SYS_STR(" /(_....\"~,_....\"~,_.....................,:`...._/ \n")
_SYS_STR(" {.._$;_....\"=,_.....\"-,_......,.-~-,},.~\";/....} \n")
_SYS_STR(" ((...*~_......\"=-._...\";,,./`........../\"..../ \n")
_SYS_STR(" ,,,___.`~,......\"~.,....................`......}....../ \n")
_SYS_STR("............(....`=-,,...`.........................(...;_,,-\" \n")
_SYS_STR("............/.`~,......`-.................................../ \n")
_SYS_STR(".............`~.*-,.....................................|,./...,__ \n")
_SYS_STR(",,_..........}.>-._...................................|.......`=~-, \n")
_SYS_STR(".....`=~-,__......`,................................. \n")
_SYS_STR("...................`=~-,,.,........................... \n")
_SYS_STR(".........................`:,,..........................`\n")
_SYS_STR("...........................`=-,...............,%%`>--==`` \n")
_SYS_STR(".................................._.........._,-%%...` \n")
_SYS_STR("...................................,\n"));
/* clang-format on */
}
static void ShowHelp(const hecl::SystemString& toolName) {
/* Select tool's help-text streamer */
HelpOutput::HelpFunc helpFunc = nullptr;
if (toolName == _SYS_STR("init"))
helpFunc = ToolInit::Help;
else if (toolName == _SYS_STR("spec"))
helpFunc = ToolSpec::Help;
else if (toolName == _SYS_STR("extract"))
helpFunc = ToolExtract::Help;
else if (toolName == _SYS_STR("cook"))
helpFunc = ToolCook::Help;
else if (toolName == _SYS_STR("package") || toolName == _SYS_STR("pack"))
helpFunc = ToolPackage::Help;
else if (toolName == _SYS_STR("help"))
helpFunc = ToolHelp::Help;
else {
LogModule.report(logvisor::Error, FMT_STRING(_SYS_STR("unrecognized tool '{}' - can't help")), toolName);
return;
}
HelpOutput ho(helpFunc);
ho.go();
}
hecl::SystemStringView toolName() const override { return _SYS_STR("help"sv); }
int run() override {
ShowHelp(m_info.args.front());
return 0;
}
};

137
hecl/driver/ToolImage.hpp Normal file
View File

@ -0,0 +1,137 @@
#pragma once
#if HECL_HAS_NOD
#include <vector>
#include <string>
#include "ToolBase.hpp"
#include <cstdio>
#include "nod/DiscGCN.hpp"
#include "nod/DiscWii.hpp"
#include "athena/FileReader.hpp"
class ToolImage final : public ToolBase {
std::unique_ptr<hecl::Database::Project> m_fallbackProj;
hecl::Database::Project* m_useProj;
public:
explicit ToolImage(const ToolPassInfo& info) : ToolBase(info), m_useProj(info.project) {
if (!info.project)
LogModule.report(logvisor::Fatal, FMT_STRING("hecl image must be ran within a project directory"));
/* Scan args */
if (info.args.size()) {
/* See if project path is supplied via args and use that over the getcwd one */
for (const hecl::SystemString& arg : info.args) {
if (arg.empty())
continue;
hecl::SystemString subPath;
hecl::ProjectRootPath root = hecl::SearchForProject(MakePathArgAbsolute(arg, info.cwd), subPath);
if (root) {
if (!m_fallbackProj) {
m_fallbackProj.reset(new hecl::Database::Project(root));
m_useProj = m_fallbackProj.get();
break;
}
}
}
}
if (!m_useProj)
LogModule.report(logvisor::Fatal,
FMT_STRING("hecl image must be ran within a project directory or "
"provided a path within a project"));
}
~ToolImage() override = default;
static void Help(HelpOutput& help) {
help.secHead(_SYS_STR("NAME"));
help.beginWrap();
help.wrap(_SYS_STR("hecl-image - Generate GameCube/Wii disc image from packaged files\n"));
help.endWrap();
help.secHead(_SYS_STR("SYNOPSIS"));
help.beginWrap();
help.wrap(_SYS_STR("hecl image [<input-dir>]\n"));
help.endWrap();
help.secHead(_SYS_STR("DESCRIPTION"));
help.beginWrap();
help.wrap(_SYS_STR("This command uses the current contents of `out` to generate a GameCube or ")
_SYS_STR("Wii disc image. `hecl package` must have been run previously to be effective.\n"));
help.endWrap();
help.secHead(_SYS_STR("OPTIONS"));
help.optionHead(_SYS_STR("<input-dir>"), _SYS_STR("input directory"));
help.beginWrap();
help.wrap(_SYS_STR("Specifies a project subdirectory to root the resulting image from. ")
_SYS_STR("Project must contain an out/sys and out/files directory to succeed.\n"));
help.endWrap();
}
hecl::SystemStringView toolName() const override { return _SYS_STR("image"sv); }
int run() override {
if (XTERM_COLOR)
fmt::print(FMT_STRING(_SYS_STR("" GREEN BOLD "ABOUT TO IMAGE:" NORMAL "\n")));
else
fmt::print(FMT_STRING(_SYS_STR("ABOUT TO IMAGE:\n")));
fmt::print(FMT_STRING(_SYS_STR(" {}\n")), m_useProj->getProjectRootPath().getAbsolutePath());
fflush(stdout);
if (continuePrompt()) {
hecl::ProjectPath outPath(m_useProj->getProjectWorkingPath(), _SYS_STR("out"));
if (!outPath.isDirectory()) {
LogModule.report(logvisor::Error, FMT_STRING(_SYS_STR("{} is not a directory")), outPath.getAbsolutePath());
return 1;
}
hecl::ProjectPath bootBinPath(outPath, _SYS_STR("sys/boot.bin"));
if (!bootBinPath.isFile()) {
LogModule.report(logvisor::Error, FMT_STRING(_SYS_STR("{} is not a file")), bootBinPath.getAbsolutePath());
return 1;
}
athena::io::FileReader r(bootBinPath.getAbsolutePath());
if (r.hasError()) {
LogModule.report(logvisor::Error, FMT_STRING(_SYS_STR("unable to open {}")), bootBinPath.getAbsolutePath());
return 1;
}
std::string id = r.readString(6);
r.close();
hecl::SystemStringConv idView(id);
hecl::SystemString fileOut = hecl::SystemString(outPath.getAbsolutePath()) + _SYS_STR('/') + idView.c_str();
hecl::MultiProgressPrinter printer(true);
auto progFunc = [&printer](float totalProg, nod::SystemStringView fileName, size_t fileBytesXfered) {
printer.print(fileName.data(), nullptr, totalProg);
};
if (id[0] == 'G') {
fileOut += _SYS_STR(".gcm");
if (nod::DiscBuilderGCN::CalculateTotalSizeRequired(outPath.getAbsolutePath()) == UINT64_MAX)
return 1;
LogModule.report(logvisor::Info, FMT_STRING(_SYS_STR("Generating {} as GameCube image")), fileOut);
nod::DiscBuilderGCN db(fileOut, progFunc);
if (db.buildFromDirectory(outPath.getAbsolutePath()) != nod::EBuildResult::Success)
return 1;
} else {
fileOut += _SYS_STR(".iso");
bool dualLayer;
if (nod::DiscBuilderWii::CalculateTotalSizeRequired(outPath.getAbsolutePath(), dualLayer) == UINT64_MAX)
return 1;
LogModule.report(logvisor::Info, FMT_STRING(_SYS_STR("Generating {} as {}-layer Wii image")), fileOut,
dualLayer ? _SYS_STR("dual") : _SYS_STR("single"));
nod::DiscBuilderWii db(fileOut, dualLayer, progFunc);
if (db.buildFromDirectory(outPath.getAbsolutePath()) != nod::EBuildResult::Success)
return 1;
}
}
return 0;
}
};
#endif

77
hecl/driver/ToolInit.hpp Normal file
View File

@ -0,0 +1,77 @@
#pragma once
#include "ToolBase.hpp"
#include <cstdio>
class ToolInit final : public ToolBase {
const hecl::SystemString* m_dir = nullptr;
public:
explicit ToolInit(const ToolPassInfo& info) : ToolBase(info) {
hecl::Sstat theStat;
const hecl::SystemString* dir;
if (info.args.size())
dir = &info.args.front();
else
dir = &info.cwd;
if (hecl::Stat(dir->c_str(), &theStat)) {
hecl::MakeDir(dir->c_str());
if (hecl::Stat(dir->c_str(), &theStat)) {
LogModule.report(logvisor::Fatal, FMT_STRING(_SYS_STR("unable to stat '{}'")), *dir);
return;
}
}
if (!S_ISDIR(theStat.st_mode)) {
LogModule.report(logvisor::Fatal, FMT_STRING(_SYS_STR("'{}' is not a directory")), *dir);
return;
}
hecl::SystemString testPath = *dir + _SYS_STR("/.hecl/beacon");
if (!hecl::Stat(testPath.c_str(), &theStat)) {
LogModule.report(logvisor::Fatal, FMT_STRING(_SYS_STR("project already exists at '{}'")), *dir);
return;
}
m_dir = dir;
}
int run() override {
if (!m_dir)
return 1;
size_t ErrorRef = logvisor::ErrorCount;
hecl::Database::Project proj((hecl::ProjectRootPath(*m_dir)));
if (logvisor::ErrorCount > ErrorRef)
return 1;
LogModule.report(logvisor::Info, FMT_STRING(_SYS_STR("initialized project at '{}/.hecl'")), *m_dir);
return 0;
}
static void Help(HelpOutput& help) {
help.secHead(_SYS_STR("NAME"));
help.beginWrap();
help.wrap(_SYS_STR("hecl-init - Initialize a brand-new project database\n"));
help.endWrap();
help.secHead(_SYS_STR("SYNOPSIS"));
help.beginWrap();
help.wrap(_SYS_STR("hecl init [<dir>]\n"));
help.endWrap();
help.secHead(_SYS_STR("DESCRIPTION"));
help.beginWrap();
help.wrap(_SYS_STR("Creates a "));
help.wrapBold(_SYS_STR(".hecl"));
help.wrap(_SYS_STR(" directory within the selected directory with an initialized database index. ")
_SYS_STR("This constitutes an empty HECL project, ready for making stuff!!\n"));
help.endWrap();
help.secHead(_SYS_STR("OPTIONS"));
help.optionHead(_SYS_STR("<dir>"), _SYS_STR("group directory path"));
help.beginWrap();
help.wrap(_SYS_STR("Directory to create new project database in. If not specified, current directory is used.\n"));
help.endWrap();
}
hecl::SystemStringView toolName() const override { return _SYS_STR("init"sv); }
};

View File

@ -0,0 +1,34 @@
#pragma once
#include "ToolBase.hpp"
#include <cstdio>
class ToolInstallAddon final : public ToolBase {
public:
explicit ToolInstallAddon(const ToolPassInfo& info) : ToolBase(info) {}
int run() override {
hecl::blender::SharedBlenderToken.getBlenderConnection();
return 0;
}
static void Help(HelpOutput& help) {
help.secHead(_SYS_STR("NAME"));
help.beginWrap();
help.wrap(_SYS_STR("hecl-installaddon - Installs embedded Blender addon into local Blender\n"));
help.endWrap();
help.secHead(_SYS_STR("SYNOPSIS"));
help.beginWrap();
help.wrap(_SYS_STR("hecl installaddon\n"));
help.endWrap();
help.secHead(_SYS_STR("DESCRIPTION"));
help.beginWrap();
help.wrap(_SYS_STR("Installs the hecl Blender addon into Blender. The path to the blender executable ")
_SYS_STR("can be overridden by setting the BLENDER_BIN environment variable."));
help.endWrap();
}
hecl::SystemStringView toolName() const override { return _SYS_STR("installaddon"sv); }
};

180
hecl/driver/ToolPackage.hpp Normal file
View File

@ -0,0 +1,180 @@
#pragma once
#include <vector>
#include <string>
#include "ToolBase.hpp"
#include <cstdio>
class ToolPackage final : public ToolBase {
std::vector<hecl::ProjectPath> m_selectedItems;
std::unique_ptr<hecl::Database::Project> m_fallbackProj;
hecl::Database::Project* m_useProj;
const hecl::Database::DataSpecEntry* m_spec = nullptr;
bool m_fast = false;
void AddSelectedItem(const hecl::ProjectPath& path) {
for (const hecl::ProjectPath& item : m_selectedItems)
if (item == path)
return;
m_selectedItems.push_back(path);
}
void CheckFile(const hecl::ProjectPath& path) {
auto lastComp = path.getLastComponent();
if (hecl::StringUtils::BeginsWith(lastComp, _SYS_STR("!world_")) &&
hecl::StringUtils::EndsWith(lastComp, _SYS_STR(".blend")))
AddSelectedItem(path);
}
void FindSelectedItems(const hecl::ProjectPath& path, bool checkGeneral) {
if (path.isFile()) {
CheckFile(path);
return;
}
size_t origSize = m_selectedItems.size();
hecl::DirectoryEnumerator dEnum(path.getAbsolutePath(), hecl::DirectoryEnumerator::Mode::DirsThenFilesSorted, false,
false, true);
for (const auto& ent : dEnum) {
hecl::ProjectPath childPath(path, ent.m_name);
if (ent.m_isDir)
FindSelectedItems(childPath, checkGeneral && childPath.getPathComponents().size() <= 2);
else
CheckFile(childPath);
}
/* Directory with 2 components not "Shared" or macOS app bundle
* and no nested !world.blend files == General PAK */
if (checkGeneral && origSize == m_selectedItems.size()) {
auto pathComps = path.getPathComponents();
if (pathComps.size() == 2 && pathComps[0] != _SYS_STR("out") && pathComps[1] != _SYS_STR("Shared") &&
pathComps[0].find(_SYS_STR(".app")) == hecl::SystemString::npos)
AddSelectedItem(path);
}
}
public:
explicit ToolPackage(const ToolPassInfo& info) : ToolBase(info), m_useProj(info.project) {
if (!info.project)
LogModule.report(logvisor::Fatal, FMT_STRING("hecl package must be ran within a project directory"));
/* Scan args */
if (info.args.size()) {
/* See if project path is supplied via args and use that over the getcwd one */
m_selectedItems.reserve(info.args.size());
for (const hecl::SystemString& arg : info.args) {
if (arg.empty())
continue;
else if (arg == _SYS_STR("--fast")) {
m_fast = true;
continue;
} else if (arg.size() >= 8 && !arg.compare(0, 7, _SYS_STR("--spec="))) {
hecl::SystemString specName(arg.begin() + 7, arg.end());
for (const hecl::Database::DataSpecEntry* spec : hecl::Database::DATA_SPEC_REGISTRY) {
if (!hecl::StrCaseCmp(spec->m_name.data(), specName.c_str())) {
m_spec = spec;
break;
}
}
if (!m_spec)
LogModule.report(logvisor::Fatal, FMT_STRING(_SYS_STR("unable to find data spec '{}'")), specName);
continue;
} else if (arg.size() >= 2 && arg[0] == _SYS_STR('-') && arg[1] == _SYS_STR('-'))
continue;
hecl::SystemString subPath;
hecl::ProjectRootPath root = hecl::SearchForProject(MakePathArgAbsolute(arg, info.cwd), subPath);
if (root) {
if (!m_fallbackProj) {
m_fallbackProj.reset(new hecl::Database::Project(root));
m_useProj = m_fallbackProj.get();
} else if (m_fallbackProj->getProjectRootPath() != root)
LogModule.report(logvisor::Fatal,
FMT_STRING(_SYS_STR("hecl package can only process multiple items in the same project; ")
_SYS_STR("'{}' and '{}' are different projects")),
m_fallbackProj->getProjectRootPath().getAbsolutePath(),
root.getAbsolutePath());
FindSelectedItems({*m_useProj, subPath}, true);
}
}
}
if (!m_useProj)
LogModule.report(logvisor::Fatal,
FMT_STRING("hecl package must be ran within a project directory or "
"provided a path within a project"));
/* Default case: recursive at root */
if (m_selectedItems.empty())
FindSelectedItems({*m_useProj, _SYS_STR("")}, true);
}
static void Help(HelpOutput& help) {
help.secHead(_SYS_STR("NAME"));
help.beginWrap();
help.wrap(_SYS_STR("hecl-pack\n") _SYS_STR("hecl-package - Package objects within the project database\n"));
help.endWrap();
help.secHead(_SYS_STR("SYNOPSIS"));
help.beginWrap();
help.wrap(_SYS_STR("hecl package [--spec=<spec>] [<input-dir>]\n"));
help.endWrap();
help.secHead(_SYS_STR("DESCRIPTION"));
help.beginWrap();
help.wrap(_SYS_STR("This command initiates a packaging pass on the project database. Packaging ")
_SYS_STR("is analogous to linking in software development. All objects necessary to ") _SYS_STR(
"generate a complete package are gathered, grouped, and indexed within a .upak file.\n"));
help.endWrap();
help.secHead(_SYS_STR("OPTIONS"));
help.optionHead(_SYS_STR("--spec=<spec>"), _SYS_STR("data specification"));
help.beginWrap();
help.wrap(_SYS_STR("Specifies a DataSpec to use when cooking and generating the package. ")
_SYS_STR("This build of hecl supports the following values of <spec>:\n"));
for (const hecl::Database::DataSpecEntry* spec : hecl::Database::DATA_SPEC_REGISTRY) {
if (!spec->m_factory)
continue;
help.wrap(_SYS_STR(" "));
help.wrapBold(spec->m_name.data());
help.wrap(_SYS_STR("\n"));
}
help.endWrap();
help.secHead(_SYS_STR("OPTIONS"));
help.optionHead(_SYS_STR("<input-dir>"), _SYS_STR("input directory"));
help.beginWrap();
help.wrap(_SYS_STR("Specifies a project subdirectory to root the resulting package from. ")
_SYS_STR("If any dependent files fall outside this subdirectory, they will be implicitly ")
_SYS_STR("gathered and packaged.\n"));
help.endWrap();
}
hecl::SystemStringView toolName() const override { return _SYS_STR("package"sv); }
int run() override {
if (XTERM_COLOR)
fmt::print(FMT_STRING(_SYS_STR("" GREEN BOLD "ABOUT TO PACKAGE:" NORMAL "\n")));
else
fmt::print(FMT_STRING(_SYS_STR("ABOUT TO PACKAGE:\n")));
for (auto& item : m_selectedItems)
fmt::print(FMT_STRING(_SYS_STR(" {}\n")), item.getRelativePath());
fflush(stdout);
if (continuePrompt()) {
hecl::MultiProgressPrinter printer(true);
hecl::ClientProcess cp(&printer);
for (const hecl::ProjectPath& path : m_selectedItems) {
if (!m_useProj->packagePath(path, printer, m_fast, m_spec, &cp))
LogModule.report(logvisor::Error, FMT_STRING(_SYS_STR("Unable to package {}")), path.getAbsolutePath());
}
cp.waitUntilComplete();
}
return 0;
}
void cancel() override { m_useProj->interruptCook(); }
};

131
hecl/driver/ToolSpec.hpp Normal file
View File

@ -0,0 +1,131 @@
#pragma once
#include "ToolBase.hpp"
#include <cstdio>
#include <map>
class ToolSpec final : public ToolBase {
enum Mode { MLIST = 0, MENABLE, MDISABLE } mode = MLIST;
public:
explicit ToolSpec(const ToolPassInfo& info) : ToolBase(info) {
if (info.args.empty())
return;
if (!info.project)
LogModule.report(logvisor::Fatal, FMT_STRING("hecl spec must be ran within a project directory"));
const auto& specs = info.project->getDataSpecs();
hecl::SystemString firstArg = info.args.front();
hecl::ToLower(firstArg);
if (firstArg == _SYS_STR("enable"))
mode = MENABLE;
else if (firstArg == _SYS_STR("disable"))
mode = MDISABLE;
else
return;
if (info.args.size() < 2)
LogModule.report(logvisor::Fatal, FMT_STRING("Speclist argument required"));
auto it = info.args.begin();
++it;
for (; it != info.args.end(); ++it) {
bool found = false;
for (auto& spec : specs) {
if (!it->compare(spec.spec.m_name)) {
found = true;
break;
}
}
if (!found)
LogModule.report(logvisor::Fatal, FMT_STRING(_SYS_STR("'{}' is not found in the dataspec registry")), *it);
}
}
static void Help(HelpOutput& help) {
help.secHead(_SYS_STR("NAME"));
help.beginWrap();
help.wrap(_SYS_STR("hecl-spec - Configure target data options\n"));
help.endWrap();
help.secHead(_SYS_STR("SYNOPSIS"));
help.beginWrap();
help.wrap(_SYS_STR("hecl spec [enable|disable] [<specname>...]\n"));
help.endWrap();
help.secHead(_SYS_STR("DESCRIPTION"));
help.beginWrap();
help.wrap(
_SYS_STR("This command configures the HECL project with the user's preferred target DataSpecs.\n\n")
_SYS_STR("Providing enable/disable argument will bulk-set the enable status of the provided spec(s)")
_SYS_STR("list. If enable/disable is not provided, a list of supported DataSpecs is printed.\n\n"));
help.endWrap();
help.secHead(_SYS_STR("OPTIONS"));
help.optionHead(_SYS_STR("<specname>..."), _SYS_STR("DataSpec name(s)"));
help.beginWrap();
help.wrap(_SYS_STR("Specifies platform-names to enable/disable"));
help.endWrap();
}
hecl::SystemStringView toolName() const override { return _SYS_STR("spec"sv); }
int run() override {
if (!m_info.project) {
for (const hecl::Database::DataSpecEntry* spec : hecl::Database::DATA_SPEC_REGISTRY) {
if (XTERM_COLOR)
fmt::print(FMT_STRING(_SYS_STR("" BOLD CYAN "{}" NORMAL "\n")), spec->m_name);
else
fmt::print(FMT_STRING(_SYS_STR("{}\n")), spec->m_name);
fmt::print(FMT_STRING(_SYS_STR(" {}\n")), spec->m_desc);
}
return 0;
}
const auto& specs = m_info.project->getDataSpecs();
if (mode == MLIST) {
for (auto& spec : specs) {
if (XTERM_COLOR)
fmt::print(FMT_STRING(_SYS_STR("" BOLD CYAN "{}" NORMAL "")), spec.spec.m_name);
else
fmt::print(FMT_STRING(_SYS_STR("{}")), spec.spec.m_name);
if (spec.active) {
if (XTERM_COLOR)
fmt::print(FMT_STRING(_SYS_STR(" " BOLD GREEN "[ENABLED]" NORMAL "")));
else
fmt::print(FMT_STRING(_SYS_STR(" [ENABLED]")));
}
fmt::print(FMT_STRING(_SYS_STR("\n {}\n")), spec.spec.m_desc);
}
return 0;
}
std::vector<hecl::SystemString> opSpecs;
auto it = m_info.args.begin();
++it;
for (; it != m_info.args.end(); ++it) {
hecl::SystemString itName = *it;
hecl::ToLower(itName);
for (auto& spec : specs) {
hecl::SystemString compName(spec.spec.m_name);
hecl::ToLower(compName);
if (itName == compName) {
opSpecs.emplace_back(spec.spec.m_name);
break;
}
}
}
if (opSpecs.size()) {
if (mode == MENABLE)
m_info.project->enableDataSpecs(opSpecs);
else if (mode == MDISABLE)
m_info.project->disableDataSpecs(opSpecs);
}
return 0;
}
};

378
hecl/driver/main.cpp Normal file
View File

@ -0,0 +1,378 @@
#if _WIN32
#ifndef NOMINMAX
#define NOMINMAX
#endif
#define WIN_PAUSE 0
#include <objbase.h>
#endif
#include <clocale>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cstdarg>
#include <signal.h>
#include <regex>
#include <list>
#include "hecl/Database.hpp"
#include "hecl/Blender/Connection.hpp"
#include "hecl/Runtime.hpp"
#include "logvisor/logvisor.hpp"
#include "../version.h"
logvisor::Module LogModule("hecl::Driver");
#include "ToolBase.hpp"
#include "ToolInit.hpp"
#include "ToolSpec.hpp"
#include "ToolExtract.hpp"
#include "ToolCook.hpp"
#include "ToolPackage.hpp"
#include "ToolImage.hpp"
#include "ToolInstallAddon.hpp"
#include "ToolHelp.hpp"
/* Static reference to dataspec additions
* (used by MSVC to definitively link DataSpecs) */
#include "DataSpecRegistry.hpp"
bool XTERM_COLOR = false;
/*
#define HECL_GIT 1234567
#define HECL_GIT_S "1234567"
#define HECL_BRANCH master
#define HECL_BRANCH_S "master"
*/
/* Main usage message */
static void printHelp(const hecl::SystemChar* pname) {
if (XTERM_COLOR)
fmt::print(FMT_STRING(_SYS_STR("" BOLD "HECL" NORMAL "")));
else
fmt::print(FMT_STRING(_SYS_STR("HECL")));
#if HECL_HAS_NOD
#define TOOL_LIST "extract|init|cook|package|image|installaddon|help"
#else
#define TOOL_LIST "extract|init|cook|package|installaddon|help"
#endif
#if HECL_GIT
fmt::print(FMT_STRING(_SYS_STR(" Commit " HECL_GIT_S " " HECL_BRANCH_S "\nUsage: {} " TOOL_LIST "\n")), pname);
#elif HECL_VER
fmt::print(FMT_STRING(_SYS_STR(" Version " HECL_VER_S "\nUsage: {} " TOOL_LIST "\n")), pname);
#else
fmt::print(FMT_STRING(_SYS_STR("\nUsage: {} " TOOL_LIST "\n")), pname);
#endif
}
/* Regex patterns */
static const hecl::SystemRegex regOPEN(_SYS_STR("-o([^\"]*|\\S*)"), std::regex::ECMAScript | std::regex::optimize);
static ToolBase* ToolPtr = nullptr;
/* SIGINT will gracefully close blender connections and delete blends in progress */
static void SIGINTHandler(int sig) {
if (ToolPtr)
ToolPtr->cancel();
hecl::blender::Connection::Shutdown();
logvisor::KillProcessTree();
exit(1);
}
static logvisor::Module AthenaLog("Athena");
static void AthenaExc(athena::error::Level level, const char* file, const char*, int line, fmt::string_view fmt,
fmt::format_args args) {
AthenaLog.vreport(logvisor::Level(level), fmt, args);
}
hecl::SystemString ExeDir;
#if _WIN32
static ToolPassInfo CreateInfo(int argc, const wchar_t** argv) {
#else
static ToolPassInfo CreateInfo(int argc, const char** argv) {
#endif
hecl::SystemChar cwdbuf[1024];
ToolPassInfo info;
info.pname = argv[0];
if (hecl::Getcwd(cwdbuf, static_cast<int>(std::size(cwdbuf)))) {
info.cwd = cwdbuf;
if (info.cwd.size() && info.cwd.back() != _SYS_STR('/') && info.cwd.back() != _SYS_STR('\\')) {
#if _WIN32
info.cwd += _SYS_STR('\\');
#else
info.cwd += _SYS_STR('/');
#endif
}
if (hecl::PathRelative(argv[0])) {
ExeDir = hecl::SystemString(cwdbuf) + _SYS_STR('/');
}
hecl::SystemString Argv0(argv[0]);
hecl::SystemString::size_type lastIdx = Argv0.find_last_of(_SYS_STR("/\\"));
if (lastIdx != hecl::SystemString::npos) {
ExeDir.insert(ExeDir.end(), Argv0.begin(), Argv0.begin() + lastIdx);
}
}
/* Concatenate args */
std::vector<hecl::SystemString> args;
args.reserve(argc - 2);
for (int i = 2; i < argc; ++i) {
args.emplace_back(argv[i]);
}
if (!args.empty()) {
/* Extract output argument */
for (auto it = args.cbegin(); it != args.cend();) {
const hecl::SystemString& arg = *it;
hecl::SystemRegexMatch oMatch;
if (std::regex_search(arg, oMatch, regOPEN)) {
const hecl::SystemString& token = oMatch[1].str();
if (token.size()) {
if (info.output.empty()) {
info.output = oMatch[1].str();
}
it = args.erase(it);
} else {
it = args.erase(it);
if (it == args.end()) {
break;
}
if (info.output.empty()) {
info.output = *it;
}
it = args.erase(it);
}
continue;
}
++it;
}
/* Iterate flags */
bool threadArg = false;
for (auto it = args.cbegin(); it != args.cend();) {
const hecl::SystemString& arg = *it;
if (threadArg) {
threadArg = false;
hecl::CpuCountOverride = int(hecl::StrToUl(arg.c_str(), nullptr, 0));
it = args.erase(it);
continue;
}
if (arg.size() < 2 || arg[0] != _SYS_STR('-') || arg[1] == _SYS_STR('-')) {
++it;
continue;
}
for (auto chit = arg.cbegin() + 1; chit != arg.cend(); ++chit) {
if (*chit == _SYS_STR('v'))
++info.verbosityLevel;
else if (*chit == _SYS_STR('f'))
info.force = true;
else if (*chit == _SYS_STR('y'))
info.yes = true;
else if (*chit == _SYS_STR('g'))
info.gui = true;
else if (*chit == _SYS_STR('j')) {
++chit;
if (*chit)
hecl::CpuCountOverride = int(hecl::StrToUl(&*chit, nullptr, 0));
else
threadArg = true;
break;
} else
info.flags.push_back(*chit);
}
it = args.erase(it);
}
/* Gather remaining args */
info.args.reserve(args.size());
for (const hecl::SystemString& arg : args)
info.args.push_back(arg);
}
return info;
}
static std::unique_ptr<hecl::Database::Project> FindProject(hecl::SystemStringView cwd) {
const hecl::ProjectRootPath rootPath = hecl::SearchForProject(cwd);
if (!rootPath) {
return nullptr;
}
const size_t ErrorRef = logvisor::ErrorCount;
auto newProj = std::make_unique<hecl::Database::Project>(rootPath);
if (logvisor::ErrorCount > ErrorRef) {
#if WIN_PAUSE
system("PAUSE");
#endif
return nullptr;
}
return newProj;
}
static std::unique_ptr<ToolBase> MakeSelectedTool(hecl::SystemString toolName, ToolPassInfo& info) {
hecl::SystemString toolNameLower = toolName;
hecl::ToLower(toolNameLower);
if (toolNameLower == _SYS_STR("init")) {
return std::make_unique<ToolInit>(info);
}
if (toolNameLower == _SYS_STR("spec")) {
return std::make_unique<ToolSpec>(info);
}
if (toolNameLower == _SYS_STR("extract")) {
return std::make_unique<ToolExtract>(info);
}
if (toolNameLower == _SYS_STR("cook")) {
return std::make_unique<ToolCook>(info);
}
if (toolNameLower == _SYS_STR("package") || toolNameLower == _SYS_STR("pack")) {
return std::make_unique<ToolPackage>(info);
}
#if HECL_HAS_NOD
if (toolNameLower == _SYS_STR("image")) {
return std::make_unique<ToolImage>(info);
}
#endif
if (toolNameLower == _SYS_STR("installaddon")) {
return std::make_unique<ToolInstallAddon>(info);
}
if (toolNameLower == _SYS_STR("help")) {
return std::make_unique<ToolHelp>(info);
}
auto fp = hecl::FopenUnique(toolName.c_str(), _SYS_STR("rb"));
if (fp == nullptr) {
LogModule.report(logvisor::Error, FMT_STRING(_SYS_STR("unrecognized tool '{}'")), toolNameLower);
return nullptr;
}
fp.reset();
/* Shortcut-case: implicit extract */
info.args.insert(info.args.begin(), std::move(toolName));
return std::make_unique<ToolExtract>(info);
}
#if _WIN32
int wmain(int argc, const wchar_t** argv)
#else
/* SIGWINCH should do nothing */
static void SIGWINCHHandler(int sig) {}
int main(int argc, const char** argv)
#endif
{
if (argc > 1 && !hecl::StrCmp(argv[1], _SYS_STR("--dlpackage"))) {
fmt::print(FMT_STRING("{}\n"), URDE_DLPACKAGE);
return 100;
}
#if _WIN32
CoInitializeEx(nullptr, COINIT_MULTITHREADED | COINIT_DISABLE_OLE1DDE);
#else
std::setlocale(LC_ALL, "en-US.UTF-8");
#endif
/* Xterm check */
#if _WIN32
const char* conemuANSI = getenv("ConEmuANSI");
if (conemuANSI && !strcmp(conemuANSI, "ON"))
XTERM_COLOR = true;
#else
const char* term = getenv("TERM");
if (term && !strncmp(term, "xterm", 5))
XTERM_COLOR = true;
signal(SIGWINCH, SIGWINCHHandler);
#endif
signal(SIGINT, SIGINTHandler);
logvisor::RegisterStandardExceptions();
logvisor::RegisterConsoleLogger();
atSetExceptionHandler(AthenaExc);
#if SENTRY_ENABLED
hecl::Runtime::FileStoreManager fileMgr{_SYS_STR("sentry-native-hecl")};
hecl::SystemUTF8Conv cacheDir{fileMgr.getStoreRoot()};
logvisor::RegisterSentry("hecl", URDE_WC_DESCRIBE, cacheDir.c_str());
#endif
/* Basic usage check */
if (argc == 1) {
printHelp(argv[0]);
#if WIN_PAUSE
system("PAUSE");
#endif
return 0;
} else if (argc == 0) {
printHelp(_SYS_STR("hecl"));
#if WIN_PAUSE
system("PAUSE");
#endif
return 0;
}
/* Prepare DataSpecs */
HECLRegisterDataSpecs();
/* Assemble common tool pass info */
ToolPassInfo info = CreateInfo(argc, argv);
/* Attempt to find hecl project */
auto project = FindProject(info.cwd);
if (project != nullptr) {
info.project = project.get();
}
/* Construct selected tool */
const size_t MakeToolErrorRef = logvisor::ErrorCount;
auto tool = MakeSelectedTool(argv[1], info);
if (logvisor::ErrorCount > MakeToolErrorRef) {
#if WIN_PAUSE
system("PAUSE");
#endif
return 1;
}
if (info.verbosityLevel) {
LogModule.report(logvisor::Info, FMT_STRING(_SYS_STR("Constructed tool '{}' {}\n")), tool->toolName(),
info.verbosityLevel);
}
/* Run tool */
const size_t RunToolErrorRef = logvisor::ErrorCount;
ToolPtr = tool.get();
const int retval = tool->run();
ToolPtr = nullptr;
if (logvisor::ErrorCount > RunToolErrorRef) {
hecl::blender::Connection::Shutdown();
#if WIN_PAUSE
system("PAUSE");
#endif
return 1;
}
hecl::blender::Connection::Shutdown();
#if WIN_PAUSE
system("PAUSE");
#endif
return retval;
}

5
hecl/extern/CMakeLists.txt vendored Normal file
View File

@ -0,0 +1,5 @@
add_subdirectory(boo)
add_subdirectory(libSquish)
add_subdirectory(athena)
add_subdirectory(libpng)
add_subdirectory(libjpeg-turbo)

1
hecl/extern/athena vendored Submodule

@ -0,0 +1 @@
Subproject commit fc3e9a51d2c8cf449c05b9eeb1da251ea389ea53

1
hecl/extern/boo vendored Submodule

@ -0,0 +1 @@
Subproject commit 500775f3db012f7516ff081cb0f18d4a266299f1

1
hecl/extern/libSquish vendored Submodule

@ -0,0 +1 @@
Subproject commit fd5e6f4fe294bcdde24e591fcef37dd5d740e060

1
hecl/extern/libjpeg-turbo vendored Submodule

@ -0,0 +1 @@
Subproject commit 3d2da99c69d000c6351f6d2390b694f800e5ef4d

115
hecl/extern/libpng/ANNOUNCE vendored Normal file
View File

@ -0,0 +1,115 @@
Libpng 1.6.19 - November 12, 2015
This is a public release of libpng, intended for use in production codes.
Files available for download:
Source files with LF line endings (for Unix/Linux) and with a
"configure" script
libpng-1.6.19.tar.xz (LZMA-compressed, recommended)
libpng-1.6.19.tar.gz
Source files with CRLF line endings (for Windows), without the
"configure" script
lpng1619.7z (LZMA-compressed, recommended)
lpng1619.zip
Other information:
libpng-1.6.19-README.txt
libpng-1.6.19-LICENSE.txt
libpng-1.6.19-*.asc (armored detached GPG signatures)
Changes since the last public release (1.6.18):
Updated obsolete information about the simplified API macros in the
manual pages (Bug report by Arc Riley).
Avoid potentially dereferencing NULL info_ptr in png_info_init_3().
Rearranged png.h to put the major sections in the same order as
in libpng17.
Eliminated unused PNG_COST_SHIFT, PNG_WEIGHT_SHIFT, PNG_COST_FACTOR, and
PNG_WEIGHT_FACTOR macros.
Suppressed some warnings from the Borland C++ 5.5.1/5.82 compiler
(Bug report by Viktor Szakats). Several warnings remain and are
unavoidable, where we test for overflow.
Fixed potential leak of png_pixels in contrib/pngminus/pnm2png.c
Fixed uninitialized variable in contrib/gregbook/rpng2-x.c
Moved config.h.in~ from the "libpng_autotools_files" list to the
"libpng_autotools_extra" list in autogen.sh because it was causing a
false positive for missing files (bug report by Robert C. Seacord).
Removed unreachable "break" statements in png.c, pngread.c, and pngrtran.c
to suppress clang warnings (Bug report by Viktor Szakats).
Fixed some bad links in the man page.
Changed "n bit" to "n-bit" in comments.
Added signed/unsigned 16-bit safety net. This removes the dubious
0x8000 flag definitions on 16-bit systems. They aren't supported
yet the defs *probably* work, however it seems much safer to do this
and be advised if anyone, contrary to advice, is building libpng 1.6
on a 16-bit system. It also adds back various switch default clauses
for GCC; GCC errors out if they are not present (with an appropriately
high level of warnings).
Safely convert num_bytes to a png_byte in png_set_sig_bytes() (Robert
Seacord).
Fixed the recently reported 1's complement security issue by replacing
the value that is illegal in the PNG spec, in both signed and unsigned
values, with 0. Illegal unsigned values (anything greater than or equal
to 0x80000000) can still pass through, but since these are not illegal
in ANSI-C (unlike 0x80000000 in the signed case) the checking that
occurs later can catch them (John Bowler).
Fixed png_save_int_32 when int is not 2's complement (John Bowler).
Updated libpng16 with all the recent test changes from libpng17,
including changes to pngvalid.c to ensure that the original,
distributed, version of contrib/visupng/cexcept.h can be used
(John Bowler).
pngvalid contains the correction to the use of SAVE/STORE_
UNKNOWN_CHUNKS; a bug revealed by changes in libpng 1.7. More
tests contain the --strict option to detect warnings and the
pngvalid-standard test has been corrected so that it does not
turn on progressive-read. There is a separate test which does
that. (John Bowler)
Also made some signed/unsigned fixes.
Make pngstest error limits version specific. Splitting the machine
generated error structs out to a file allows the values to be updated
without changing pngstest.c itself. Since libpng 1.6 and 1.7 have
slightly different error limits this simplifies maintenance. The
makepngs.sh script has also been updated to more accurately reflect
current problems in libpng 1.7 (John Bowler).
Incorporated new test PNG files into make check. tests/pngstest-*
are changed so that the new test files are divided into 8 groups by
gamma and alpha channel. These tests have considerably better code
and pixel-value coverage than contrib/pngsuite; however,coverage is
still incomplete (John Bowler).
Removed the '--strict' in 1.6 because of the double-gamma-correction
warning, updated pngstest-errors.h for the errors detected with the
new contrib/testspngs PNG test files (John Bowler).
Worked around rgb-to-gray issues in libpng 1.6. The previous
attempts to ignore the errors in the code aren't quite enough to
deal with the 'channel selection' encoding added to libpng 1.7; abort.
Fixed 'pow' macros in pngvalid.c. It is legal for 'pow' to be a
macro, therefore the argument list cannot contain preprocessing
directives. Make sure pow is a function where this happens. This is
a minimal safe fix, the issue only arises in non-performance-critical
code (bug report by Curtis Leach, fix by John Bowler).
Added sPLT support to pngtest.c
Prevent setting or writing over-length PLTE chunk (Cosmin Truta).
Silently truncate over-length PLTE chunk while reading.
Libpng incorrectly calculated the output rowbytes when the application
decreased either the number of channels or the bit depth (or both) in
a user transform. This was safe; libpng overallocated buffer space
(potentially by quite a lot; up to 4 times the amount required) but,
from 1.5.4 on, resulted in a png_error (John Bowler).
Fixed some inconsequential cut-and-paste typos in png_set_cHRM_XYZ_fixed().
Clarified COPYRIGHT information to state explicitly that versions
are derived from previous versions.
Removed much of the long list of previous versions from png.h and
libpng.3.
Send comments/corrections/commendations to png-mng-implement at lists.sf.net
(subscription required; visit
https://lists.sourceforge.net/lists/listinfo/png-mng-implement
to subscribe)
or to glennrp at users.sourceforge.net
Glenn R-P

5424
hecl/extern/libpng/CHANGES vendored Normal file

File diff suppressed because it is too large Load Diff

53
hecl/extern/libpng/CMakeLists.txt vendored Normal file
View File

@ -0,0 +1,53 @@
if(NOT WIN32 AND NOT APPLE AND NOT NX) # remove WIN32 when specter/freetype is gone
find_library(PNG_LIB NAMES png libpng)
endif()
if(NOT PNG_LIB)
message(STATUS "Using HECL's built-in libpng")
if("${CMAKE_SYSTEM_PROCESSOR}" MATCHES "arm(64)?")
set(INTRINSICS
arm/arm_init.c
arm/filter_neon.S
arm/filter_neon_intrinsics.c
arm/palette_neon_intrinsics.c)
elseif(${CMAKE_SYSTEM_PROCESSOR} STREQUAL x86_64)
set(INTRINSICS
intel/filter_sse2_intrinsics.c
intel/intel_init.c)
endif()
add_library(png
png.h
pngconf.h
pngdebug.h
pnginfo.h
pngpriv.h
pngstruct.h
pnglibconf.h
png.c
pngerror.c
pngget.c
pngmem.c
pngpread.c
pngread.c
pngrio.c
pngrtran.c
pngrutil.c
pngset.c
pngtrans.c
pngwio.c
pngwrite.c
pngwtran.c
pngwutil.c
${INTRINSICS})
if(APPLE)
target_compile_options(png PRIVATE -Wno-implicit-fallthrough)
endif()
target_link_libraries(png PUBLIC ${ZLIB_LIBRARIES})
target_include_directories(png PUBLIC ${ZLIB_INCLUDE_DIR})
set(PNG_LIBRARIES png CACHE PATH "PNG libraries" FORCE)
set(PNG_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR} CACHE PATH "PNG include path" FORCE)
else()
set(PNG_LIBRARIES ${PNG_LIB} CACHE PATH "PNG libraries" FORCE)
find_path(PNG_INCLUDE_DIR png.h)
set(PNG_INCLUDE_DIR ${PNG_INCLUDE_DIR} CACHE PATH "PNG include path" FORCE)
endif()

183
hecl/extern/libpng/README vendored Normal file
View File

@ -0,0 +1,183 @@
README for libpng version 1.6.37 - April 14, 2019
=================================================
See the note about version numbers near the top of png.h.
See INSTALL for instructions on how to install libpng.
Libpng comes in several distribution formats. Get libpng-*.tar.gz or
libpng-*.tar.xz or if you want UNIX-style line endings in the text
files, or lpng*.7z or lpng*.zip if you want DOS-style line endings.
Version 0.89 was the first official release of libpng. Don't let the
fact that it's the first release fool you. The libpng library has been
in extensive use and testing since mid-1995. By late 1997 it had
finally gotten to the stage where there hadn't been significant
changes to the API in some time, and people have a bad feeling about
libraries with versions < 1.0. Version 1.0.0 was released in
March 1998.
****
Note that some of the changes to the png_info structure render this
version of the library binary incompatible with libpng-0.89 or
earlier versions if you are using a shared library. The type of the
"filler" parameter for png_set_filler() has changed from png_byte to
png_uint_32, which will affect shared-library applications that use
this function.
To avoid problems with changes to the internals of the png info_struct,
new APIs have been made available in 0.95 to avoid direct application
access to info_ptr. These functions are the png_set_<chunk> and
png_get_<chunk> functions. These functions should be used when
accessing/storing the info_struct data, rather than manipulating it
directly, to avoid such problems in the future.
It is important to note that the APIs did not make current programs
that access the info struct directly incompatible with the new
library, through libpng-1.2.x. In libpng-1.4.x, which was meant to
be a transitional release, members of the png_struct and the
info_struct can still be accessed, but the compiler will issue a
warning about deprecated usage. Since libpng-1.5.0, direct access
to these structs is not allowed, and the definitions of the structs
reside in private pngstruct.h and pnginfo.h header files that are not
accessible to applications. It is strongly suggested that new
programs use the new APIs (as shown in example.c and pngtest.c), and
older programs be converted to the new format, to facilitate upgrades
in the future.
****
Additions since 0.90 include the ability to compile libpng as a
Windows DLL, and new APIs for accessing data in the info struct.
Experimental functions include the ability to set weighting and cost
factors for row filter selection, direct reads of integers from buffers
on big-endian processors that support misaligned data access, faster
methods of doing alpha composition, and more accurate 16->8 bit color
conversion.
The additions since 0.89 include the ability to read from a PNG stream
which has had some (or all) of the signature bytes read by the calling
application. This also allows the reading of embedded PNG streams that
do not have the PNG file signature. As well, it is now possible to set
the library action on the detection of chunk CRC errors. It is possible
to set different actions based on whether the CRC error occurred in a
critical or an ancillary chunk.
For a detailed description on using libpng, read libpng-manual.txt.
For examples of libpng in a program, see example.c and pngtest.c. For
usage information and restrictions (what little they are) on libpng,
see png.h. For a description on using zlib (the compression library
used by libpng) and zlib's restrictions, see zlib.h
I have included a general makefile, as well as several machine and
compiler specific ones, but you may have to modify one for your own
needs.
You should use zlib 1.0.4 or later to run this, but it MAY work with
versions as old as zlib 0.95. Even so, there are bugs in older zlib
versions which can cause the output of invalid compression streams for
some images.
You should also note that zlib is a compression library that is useful
for more things than just PNG files. You can use zlib as a drop-in
replacement for fread() and fwrite(), if you are so inclined.
zlib should be available at the same place that libpng is, or at
https://zlib.net.
You may also want a copy of the PNG specification. It is available
as an RFC, a W3C Recommendation, and an ISO/IEC Standard. You can find
these at http://www.libpng.org/pub/png/pngdocs.html .
This code is currently being archived at libpng.sourceforge.io in the
[DOWNLOAD] area, and at http://libpng.download/src .
This release, based in a large way on Glenn's, Guy's and Andreas'
earlier work, was created and will be supported by myself and the PNG
development group.
Send comments/corrections/commendations to png-mng-implement at
lists.sourceforge.net (subscription required; visit
https://lists.sourceforge.net/lists/listinfo/png-mng-implement
to subscribe).
Send general questions about the PNG specification to png-mng-misc
at lists.sourceforge.net (subscription required; visit
https://lists.sourceforge.net/lists/listinfo/png-mng-misc to
subscribe).
Files in this distribution:
ANNOUNCE => Announcement of this version, with recent changes
AUTHORS => List of contributing authors
CHANGES => Description of changes between libpng versions
KNOWNBUG => List of known bugs and deficiencies
LICENSE => License to use and redistribute libpng
README => This file
TODO => Things not implemented in the current library
TRADEMARK => Trademark information
example.c => Example code for using libpng functions
libpng.3 => manual page for libpng (includes libpng-manual.txt)
libpng-manual.txt => Description of libpng and its functions
libpngpf.3 => manual page for libpng's private functions
png.5 => manual page for the PNG format
png.c => Basic interface functions common to library
png.h => Library function and interface declarations (public)
pngpriv.h => Library function and interface declarations (private)
pngconf.h => System specific library configuration (public)
pngstruct.h => png_struct declaration (private)
pnginfo.h => png_info struct declaration (private)
pngdebug.h => debugging macros (private)
pngerror.c => Error/warning message I/O functions
pngget.c => Functions for retrieving info from struct
pngmem.c => Memory handling functions
pngbar.png => PNG logo, 88x31
pngnow.png => PNG logo, 98x31
pngpread.c => Progressive reading functions
pngread.c => Read data/helper high-level functions
pngrio.c => Lowest-level data read I/O functions
pngrtran.c => Read data transformation functions
pngrutil.c => Read data utility functions
pngset.c => Functions for storing data into the info_struct
pngtest.c => Library test program
pngtest.png => Library test sample image
pngtrans.c => Common data transformation functions
pngwio.c => Lowest-level write I/O functions
pngwrite.c => High-level write functions
pngwtran.c => Write data transformations
pngwutil.c => Write utility functions
arm => Contains optimized code for the ARM platform
powerpc => Contains optimized code for the PowerPC platform
contrib => Contributions
arm-neon => Optimized code for ARM-NEON platform
powerpc-vsx => Optimized code for POWERPC-VSX platform
examples => Example programs
gregbook => source code for PNG reading and writing, from
Greg Roelofs' "PNG: The Definitive Guide",
O'Reilly, 1999
libtests => Test programs
mips-msa => Optimized code for MIPS-MSA platform
pngminim => Minimal decoder, encoder, and progressive decoder
programs demonstrating use of pngusr.dfa
pngminus => Simple pnm2png and png2pnm programs
pngsuite => Test images
testpngs
tools => Various tools
visupng => Contains a MSVC workspace for VisualPng
intel => Optimized code for INTEL-SSE2 platform
mips => Optimized code for MIPS platform
projects => Contains project files and workspaces for
building a DLL
owatcom => Contains a WATCOM project for building libpng
visualc71 => Contains a Microsoft Visual C++ (MSVC)
workspace for building libpng and zlib
vstudio => Contains a Microsoft Visual C++ (MSVC)
workspace for building libpng and zlib
scripts => Directory containing scripts for building libpng:
(see scripts/README.txt for the list of scripts)
Good luck, and happy coding!
* Cosmin Truta (current maintainer, since 2018)
* Glenn Randers-Pehrson (former maintainer, 1998-2018)
* Andreas Eric Dilger (former maintainer, 1996-1997)
* Guy Eric Schalnat (original author and former maintainer, 1995-1996)
(formerly of Group 42, Inc.)

29
hecl/extern/libpng/TODO vendored Normal file
View File

@ -0,0 +1,29 @@
/*
TODO - list of things to do for libpng:
Final bug fixes.
Better C++ wrapper/full C++ implementation?
Fix problem with C++ and EXTERN "C".
cHRM transformation.
Remove setjmp/longjmp usage in favor of returning error codes.
Palette creation.
Add "grayscale->palette" transformation and "palette->grayscale" detection.
Improved dithering.
Multi-lingual error and warning message support.
Complete sRGB transformation (presently it simply uses gamma=0.45455).
Make profile checking optional via a png_set_something() call.
Man pages for function calls.
Better documentation.
Better filter selection
(counting huffman bits/precompression? filter inertia? filter costs?).
Histogram creation.
Text conversion between different code pages (Latin-1 -> Mac and DOS).
Avoid building gamma tables whenever possible.
Use greater precision when changing to linear gamma for compositing against
background and doing rgb-to-gray transformation.
Investigate pre-incremented loop counters and other loop constructions.
Add interpolated method of handling interlacing.
Switch to the simpler zlib (zlib/libpng) license if legally possible.
Extend pngvalid.c to validate more of the libpng transformations.
*/

136
hecl/extern/libpng/arm/arm_init.c vendored Normal file
View File

@ -0,0 +1,136 @@
/* arm_init.c - NEON optimised filter functions
*
* Copyright (c) 2018 Cosmin Truta
* Copyright (c) 2014,2016 Glenn Randers-Pehrson
* Written by Mans Rullgard, 2011.
*
* This code is released under the libpng license.
* For conditions of distribution and use, see the disclaimer
* and license in png.h
*/
/* Below, after checking __linux__, various non-C90 POSIX 1003.1 functions are
* called.
*/
#define _POSIX_SOURCE 1
#include "../pngpriv.h"
#ifdef PNG_READ_SUPPORTED
#if PNG_ARM_NEON_OPT > 0
#ifdef PNG_ARM_NEON_CHECK_SUPPORTED /* Do run-time checks */
/* WARNING: it is strongly recommended that you do not build libpng with
* run-time checks for CPU features if at all possible. In the case of the ARM
* NEON instructions there is no processor-specific way of detecting the
* presence of the required support, therefore run-time detection is extremely
* OS specific.
*
* You may set the macro PNG_ARM_NEON_FILE to the file name of file containing
* a fragment of C source code which defines the png_have_neon function. There
* are a number of implementations in contrib/arm-neon, but the only one that
* has partial support is contrib/arm-neon/linux.c - a generic Linux
* implementation which reads /proc/cpufino.
*/
#ifndef PNG_ARM_NEON_FILE
# ifdef __linux__
# define PNG_ARM_NEON_FILE "contrib/arm-neon/linux.c"
# endif
#endif
#ifdef PNG_ARM_NEON_FILE
#include <signal.h> /* for sig_atomic_t */
static int png_have_neon(png_structp png_ptr);
#include PNG_ARM_NEON_FILE
#else /* PNG_ARM_NEON_FILE */
# error "PNG_ARM_NEON_FILE undefined: no support for run-time ARM NEON checks"
#endif /* PNG_ARM_NEON_FILE */
#endif /* PNG_ARM_NEON_CHECK_SUPPORTED */
#ifndef PNG_ALIGNED_MEMORY_SUPPORTED
# error "ALIGNED_MEMORY is required; set: -DPNG_ALIGNED_MEMORY_SUPPORTED"
#endif
void
png_init_filter_functions_neon(png_structp pp, unsigned int bpp)
{
/* The switch statement is compiled in for ARM_NEON_API, the call to
* png_have_neon is compiled in for ARM_NEON_CHECK. If both are defined
* the check is only performed if the API has not set the NEON option on
* or off explicitly. In this case the check controls what happens.
*
* If the CHECK is not compiled in and the option is UNSET the behavior prior
* to 1.6.7 was to use the NEON code - this was a bug caused by having the
* wrong order of the 'ON' and 'default' cases. UNSET now defaults to OFF,
* as documented in png.h
*/
png_debug(1, "in png_init_filter_functions_neon");
#ifdef PNG_ARM_NEON_API_SUPPORTED
switch ((pp->options >> PNG_ARM_NEON) & 3)
{
case PNG_OPTION_UNSET:
/* Allow the run-time check to execute if it has been enabled -
* thus both API and CHECK can be turned on. If it isn't supported
* this case will fall through to the 'default' below, which just
* returns.
*/
#endif /* PNG_ARM_NEON_API_SUPPORTED */
#ifdef PNG_ARM_NEON_CHECK_SUPPORTED
{
static volatile sig_atomic_t no_neon = -1; /* not checked */
if (no_neon < 0)
no_neon = !png_have_neon(pp);
if (no_neon)
return;
}
#ifdef PNG_ARM_NEON_API_SUPPORTED
break;
#endif
#endif /* PNG_ARM_NEON_CHECK_SUPPORTED */
#ifdef PNG_ARM_NEON_API_SUPPORTED
default: /* OFF or INVALID */
return;
case PNG_OPTION_ON:
/* Option turned on */
break;
}
#endif
/* IMPORTANT: any new external functions used here must be declared using
* PNG_INTERNAL_FUNCTION in ../pngpriv.h. This is required so that the
* 'prefix' option to configure works:
*
* ./configure --with-libpng-prefix=foobar_
*
* Verify you have got this right by running the above command, doing a build
* and examining pngprefix.h; it must contain a #define for every external
* function you add. (Notice that this happens automatically for the
* initialization function.)
*/
pp->read_filter[PNG_FILTER_VALUE_UP-1] = png_read_filter_row_up_neon;
if (bpp == 3)
{
pp->read_filter[PNG_FILTER_VALUE_SUB-1] = png_read_filter_row_sub3_neon;
pp->read_filter[PNG_FILTER_VALUE_AVG-1] = png_read_filter_row_avg3_neon;
pp->read_filter[PNG_FILTER_VALUE_PAETH-1] =
png_read_filter_row_paeth3_neon;
}
else if (bpp == 4)
{
pp->read_filter[PNG_FILTER_VALUE_SUB-1] = png_read_filter_row_sub4_neon;
pp->read_filter[PNG_FILTER_VALUE_AVG-1] = png_read_filter_row_avg4_neon;
pp->read_filter[PNG_FILTER_VALUE_PAETH-1] =
png_read_filter_row_paeth4_neon;
}
}
#endif /* PNG_ARM_NEON_OPT > 0 */
#endif /* READ */

253
hecl/extern/libpng/arm/filter_neon.S vendored Normal file
View File

@ -0,0 +1,253 @@
/* filter_neon.S - NEON optimised filter functions
*
* Copyright (c) 2018 Cosmin Truta
* Copyright (c) 2014,2017 Glenn Randers-Pehrson
* Written by Mans Rullgard, 2011.
*
* This code is released under the libpng license.
* For conditions of distribution and use, see the disclaimer
* and license in png.h
*/
/* This is required to get the symbol renames, which are #defines, and the
* definitions (or not) of PNG_ARM_NEON_OPT and PNG_ARM_NEON_IMPLEMENTATION.
*/
#define PNG_VERSION_INFO_ONLY
#include "../pngpriv.h"
#if (defined(__linux__) || defined(__FreeBSD__)) && defined(__ELF__)
.section .note.GNU-stack,"",%progbits /* mark stack as non-executable */
#endif
#ifdef PNG_READ_SUPPORTED
/* Assembler NEON support - only works for 32-bit ARM (i.e. it does not work for
* ARM64). The code in arm/filter_neon_intrinsics.c supports ARM64, however it
* only works if -mfpu=neon is specified on the GCC command line. See pngpriv.h
* for the logic which sets PNG_USE_ARM_NEON_ASM:
*/
#if PNG_ARM_NEON_IMPLEMENTATION == 2 /* hand-coded assembler */
#if PNG_ARM_NEON_OPT > 0
#ifdef __ELF__
# define ELF
#else
# define ELF @
#endif
.arch armv7-a
.fpu neon
.macro func name, export=0
.macro endfunc
ELF .size \name, . - \name
.endfunc
.purgem endfunc
.endm
.text
/* Explicitly specifying alignment here because some versions of
* GAS don't align code correctly. This is harmless in correctly
* written versions of GAS.
*/
.align 2
.if \export
.global \name
.endif
ELF .type \name, STT_FUNC
.func \name
\name:
.endm
func png_read_filter_row_sub4_neon, export=1
ldr r3, [r0, #4] @ rowbytes
vmov.i8 d3, #0
1:
vld4.32 {d4[],d5[],d6[],d7[]}, [r1,:128]
vadd.u8 d0, d3, d4
vadd.u8 d1, d0, d5
vadd.u8 d2, d1, d6
vadd.u8 d3, d2, d7
vst4.32 {d0[0],d1[0],d2[0],d3[0]},[r1,:128]!
subs r3, r3, #16
bgt 1b
bx lr
endfunc
func png_read_filter_row_sub3_neon, export=1
ldr r3, [r0, #4] @ rowbytes
vmov.i8 d3, #0
mov r0, r1
mov r2, #3
mov r12, #12
vld1.8 {q11}, [r0], r12
1:
vext.8 d5, d22, d23, #3
vadd.u8 d0, d3, d22
vext.8 d6, d22, d23, #6
vadd.u8 d1, d0, d5
vext.8 d7, d23, d23, #1
vld1.8 {q11}, [r0], r12
vst1.32 {d0[0]}, [r1,:32], r2
vadd.u8 d2, d1, d6
vst1.32 {d1[0]}, [r1], r2
vadd.u8 d3, d2, d7
vst1.32 {d2[0]}, [r1], r2
vst1.32 {d3[0]}, [r1], r2
subs r3, r3, #12
bgt 1b
bx lr
endfunc
func png_read_filter_row_up_neon, export=1
ldr r3, [r0, #4] @ rowbytes
1:
vld1.8 {q0}, [r1,:128]
vld1.8 {q1}, [r2,:128]!
vadd.u8 q0, q0, q1
vst1.8 {q0}, [r1,:128]!
subs r3, r3, #16
bgt 1b
bx lr
endfunc
func png_read_filter_row_avg4_neon, export=1
ldr r12, [r0, #4] @ rowbytes
vmov.i8 d3, #0
1:
vld4.32 {d4[],d5[],d6[],d7[]}, [r1,:128]
vld4.32 {d16[],d17[],d18[],d19[]},[r2,:128]!
vhadd.u8 d0, d3, d16
vadd.u8 d0, d0, d4
vhadd.u8 d1, d0, d17
vadd.u8 d1, d1, d5
vhadd.u8 d2, d1, d18
vadd.u8 d2, d2, d6
vhadd.u8 d3, d2, d19
vadd.u8 d3, d3, d7
vst4.32 {d0[0],d1[0],d2[0],d3[0]},[r1,:128]!
subs r12, r12, #16
bgt 1b
bx lr
endfunc
func png_read_filter_row_avg3_neon, export=1
push {r4,lr}
ldr r12, [r0, #4] @ rowbytes
vmov.i8 d3, #0
mov r0, r1
mov r4, #3
mov lr, #12
vld1.8 {q11}, [r0], lr
1:
vld1.8 {q10}, [r2], lr
vext.8 d5, d22, d23, #3
vhadd.u8 d0, d3, d20
vext.8 d17, d20, d21, #3
vadd.u8 d0, d0, d22
vext.8 d6, d22, d23, #6
vhadd.u8 d1, d0, d17
vext.8 d18, d20, d21, #6
vadd.u8 d1, d1, d5
vext.8 d7, d23, d23, #1
vld1.8 {q11}, [r0], lr
vst1.32 {d0[0]}, [r1,:32], r4
vhadd.u8 d2, d1, d18
vst1.32 {d1[0]}, [r1], r4
vext.8 d19, d21, d21, #1
vadd.u8 d2, d2, d6
vhadd.u8 d3, d2, d19
vst1.32 {d2[0]}, [r1], r4
vadd.u8 d3, d3, d7
vst1.32 {d3[0]}, [r1], r4
subs r12, r12, #12
bgt 1b
pop {r4,pc}
endfunc
.macro paeth rx, ra, rb, rc
vaddl.u8 q12, \ra, \rb @ a + b
vaddl.u8 q15, \rc, \rc @ 2*c
vabdl.u8 q13, \rb, \rc @ pa
vabdl.u8 q14, \ra, \rc @ pb
vabd.u16 q15, q12, q15 @ pc
vcle.u16 q12, q13, q14 @ pa <= pb
vcle.u16 q13, q13, q15 @ pa <= pc
vcle.u16 q14, q14, q15 @ pb <= pc
vand q12, q12, q13 @ pa <= pb && pa <= pc
vmovn.u16 d28, q14
vmovn.u16 \rx, q12
vbsl d28, \rb, \rc
vbsl \rx, \ra, d28
.endm
func png_read_filter_row_paeth4_neon, export=1
ldr r12, [r0, #4] @ rowbytes
vmov.i8 d3, #0
vmov.i8 d20, #0
1:
vld4.32 {d4[],d5[],d6[],d7[]}, [r1,:128]
vld4.32 {d16[],d17[],d18[],d19[]},[r2,:128]!
paeth d0, d3, d16, d20
vadd.u8 d0, d0, d4
paeth d1, d0, d17, d16
vadd.u8 d1, d1, d5
paeth d2, d1, d18, d17
vadd.u8 d2, d2, d6
paeth d3, d2, d19, d18
vmov d20, d19
vadd.u8 d3, d3, d7
vst4.32 {d0[0],d1[0],d2[0],d3[0]},[r1,:128]!
subs r12, r12, #16
bgt 1b
bx lr
endfunc
func png_read_filter_row_paeth3_neon, export=1
push {r4,lr}
ldr r12, [r0, #4] @ rowbytes
vmov.i8 d3, #0
vmov.i8 d4, #0
mov r0, r1
mov r4, #3
mov lr, #12
vld1.8 {q11}, [r0], lr
1:
vld1.8 {q10}, [r2], lr
paeth d0, d3, d20, d4
vext.8 d5, d22, d23, #3
vadd.u8 d0, d0, d22
vext.8 d17, d20, d21, #3
paeth d1, d0, d17, d20
vst1.32 {d0[0]}, [r1,:32], r4
vext.8 d6, d22, d23, #6
vadd.u8 d1, d1, d5
vext.8 d18, d20, d21, #6
paeth d2, d1, d18, d17
vext.8 d7, d23, d23, #1
vld1.8 {q11}, [r0], lr
vst1.32 {d1[0]}, [r1], r4
vadd.u8 d2, d2, d6
vext.8 d19, d21, d21, #1
paeth d3, d2, d19, d18
vst1.32 {d2[0]}, [r1], r4
vmov d4, d19
vadd.u8 d3, d3, d7
vst1.32 {d3[0]}, [r1], r4
subs r12, r12, #12
bgt 1b
pop {r4,pc}
endfunc
#endif /* PNG_ARM_NEON_OPT > 0 */
#endif /* PNG_ARM_NEON_IMPLEMENTATION == 2 (assembler) */
#endif /* READ */

View File

@ -0,0 +1,402 @@
/* filter_neon_intrinsics.c - NEON optimised filter functions
*
* Copyright (c) 2018 Cosmin Truta
* Copyright (c) 2014,2016 Glenn Randers-Pehrson
* Written by James Yu <james.yu at linaro.org>, October 2013.
* Based on filter_neon.S, written by Mans Rullgard, 2011.
*
* This code is released under the libpng license.
* For conditions of distribution and use, see the disclaimer
* and license in png.h
*/
#include "../pngpriv.h"
#ifdef PNG_READ_SUPPORTED
/* This code requires -mfpu=neon on the command line: */
#if PNG_ARM_NEON_IMPLEMENTATION == 1 /* intrinsics code from pngpriv.h */
#if defined(_MSC_VER) && defined(_M_ARM64)
# include <arm64_neon.h>
#else
# include <arm_neon.h>
#endif
/* libpng row pointers are not necessarily aligned to any particular boundary,
* however this code will only work with appropriate alignment. arm/arm_init.c
* checks for this (and will not compile unless it is done). This code uses
* variants of png_aligncast to avoid compiler warnings.
*/
#define png_ptr(type,pointer) png_aligncast(type *,pointer)
#define png_ptrc(type,pointer) png_aligncastconst(const type *,pointer)
/* The following relies on a variable 'temp_pointer' being declared with type
* 'type'. This is written this way just to hide the GCC strict aliasing
* warning; note that the code is safe because there never is an alias between
* the input and output pointers.
*
* When compiling with MSVC ARM64, the png_ldr macro can't be passed directly
* to vst4_lane_u32, because of an internal compiler error inside MSVC.
* To avoid this compiler bug, we use a temporary variable (vdest_val) to store
* the result of png_ldr.
*/
#define png_ldr(type,pointer)\
(temp_pointer = png_ptr(type,pointer), *temp_pointer)
#if PNG_ARM_NEON_OPT > 0
void
png_read_filter_row_up_neon(png_row_infop row_info, png_bytep row,
png_const_bytep prev_row)
{
png_bytep rp = row;
png_bytep rp_stop = row + row_info->rowbytes;
png_const_bytep pp = prev_row;
png_debug(1, "in png_read_filter_row_up_neon");
for (; rp < rp_stop; rp += 16, pp += 16)
{
uint8x16_t qrp, qpp;
qrp = vld1q_u8(rp);
qpp = vld1q_u8(pp);
qrp = vaddq_u8(qrp, qpp);
vst1q_u8(rp, qrp);
}
}
void
png_read_filter_row_sub3_neon(png_row_infop row_info, png_bytep row,
png_const_bytep prev_row)
{
png_bytep rp = row;
png_bytep rp_stop = row + row_info->rowbytes;
uint8x16_t vtmp = vld1q_u8(rp);
uint8x8x2_t *vrpt = png_ptr(uint8x8x2_t, &vtmp);
uint8x8x2_t vrp = *vrpt;
uint8x8x4_t vdest;
vdest.val[3] = vdup_n_u8(0);
png_debug(1, "in png_read_filter_row_sub3_neon");
for (; rp < rp_stop;)
{
uint8x8_t vtmp1, vtmp2;
uint32x2_t *temp_pointer;
vtmp1 = vext_u8(vrp.val[0], vrp.val[1], 3);
vdest.val[0] = vadd_u8(vdest.val[3], vrp.val[0]);
vtmp2 = vext_u8(vrp.val[0], vrp.val[1], 6);
vdest.val[1] = vadd_u8(vdest.val[0], vtmp1);
vtmp1 = vext_u8(vrp.val[1], vrp.val[1], 1);
vdest.val[2] = vadd_u8(vdest.val[1], vtmp2);
vdest.val[3] = vadd_u8(vdest.val[2], vtmp1);
vtmp = vld1q_u8(rp + 12);
vrpt = png_ptr(uint8x8x2_t, &vtmp);
vrp = *vrpt;
vst1_lane_u32(png_ptr(uint32_t,rp), png_ldr(uint32x2_t,&vdest.val[0]), 0);
rp += 3;
vst1_lane_u32(png_ptr(uint32_t,rp), png_ldr(uint32x2_t,&vdest.val[1]), 0);
rp += 3;
vst1_lane_u32(png_ptr(uint32_t,rp), png_ldr(uint32x2_t,&vdest.val[2]), 0);
rp += 3;
vst1_lane_u32(png_ptr(uint32_t,rp), png_ldr(uint32x2_t,&vdest.val[3]), 0);
rp += 3;
}
PNG_UNUSED(prev_row)
}
void
png_read_filter_row_sub4_neon(png_row_infop row_info, png_bytep row,
png_const_bytep prev_row)
{
png_bytep rp = row;
png_bytep rp_stop = row + row_info->rowbytes;
uint8x8x4_t vdest;
vdest.val[3] = vdup_n_u8(0);
png_debug(1, "in png_read_filter_row_sub4_neon");
for (; rp < rp_stop; rp += 16)
{
uint32x2x4_t vtmp = vld4_u32(png_ptr(uint32_t,rp));
uint8x8x4_t *vrpt = png_ptr(uint8x8x4_t,&vtmp);
uint8x8x4_t vrp = *vrpt;
uint32x2x4_t *temp_pointer;
uint32x2x4_t vdest_val;
vdest.val[0] = vadd_u8(vdest.val[3], vrp.val[0]);
vdest.val[1] = vadd_u8(vdest.val[0], vrp.val[1]);
vdest.val[2] = vadd_u8(vdest.val[1], vrp.val[2]);
vdest.val[3] = vadd_u8(vdest.val[2], vrp.val[3]);
vdest_val = png_ldr(uint32x2x4_t, &vdest);
vst4_lane_u32(png_ptr(uint32_t,rp), vdest_val, 0);
}
PNG_UNUSED(prev_row)
}
void
png_read_filter_row_avg3_neon(png_row_infop row_info, png_bytep row,
png_const_bytep prev_row)
{
png_bytep rp = row;
png_const_bytep pp = prev_row;
png_bytep rp_stop = row + row_info->rowbytes;
uint8x16_t vtmp;
uint8x8x2_t *vrpt;
uint8x8x2_t vrp;
uint8x8x4_t vdest;
vdest.val[3] = vdup_n_u8(0);
vtmp = vld1q_u8(rp);
vrpt = png_ptr(uint8x8x2_t,&vtmp);
vrp = *vrpt;
png_debug(1, "in png_read_filter_row_avg3_neon");
for (; rp < rp_stop; pp += 12)
{
uint8x8_t vtmp1, vtmp2, vtmp3;
uint8x8x2_t *vppt;
uint8x8x2_t vpp;
uint32x2_t *temp_pointer;
vtmp = vld1q_u8(pp);
vppt = png_ptr(uint8x8x2_t,&vtmp);
vpp = *vppt;
vtmp1 = vext_u8(vrp.val[0], vrp.val[1], 3);
vdest.val[0] = vhadd_u8(vdest.val[3], vpp.val[0]);
vdest.val[0] = vadd_u8(vdest.val[0], vrp.val[0]);
vtmp2 = vext_u8(vpp.val[0], vpp.val[1], 3);
vtmp3 = vext_u8(vrp.val[0], vrp.val[1], 6);
vdest.val[1] = vhadd_u8(vdest.val[0], vtmp2);
vdest.val[1] = vadd_u8(vdest.val[1], vtmp1);
vtmp2 = vext_u8(vpp.val[0], vpp.val[1], 6);
vtmp1 = vext_u8(vrp.val[1], vrp.val[1], 1);
vtmp = vld1q_u8(rp + 12);
vrpt = png_ptr(uint8x8x2_t,&vtmp);
vrp = *vrpt;
vdest.val[2] = vhadd_u8(vdest.val[1], vtmp2);
vdest.val[2] = vadd_u8(vdest.val[2], vtmp3);
vtmp2 = vext_u8(vpp.val[1], vpp.val[1], 1);
vdest.val[3] = vhadd_u8(vdest.val[2], vtmp2);
vdest.val[3] = vadd_u8(vdest.val[3], vtmp1);
vst1_lane_u32(png_ptr(uint32_t,rp), png_ldr(uint32x2_t,&vdest.val[0]), 0);
rp += 3;
vst1_lane_u32(png_ptr(uint32_t,rp), png_ldr(uint32x2_t,&vdest.val[1]), 0);
rp += 3;
vst1_lane_u32(png_ptr(uint32_t,rp), png_ldr(uint32x2_t,&vdest.val[2]), 0);
rp += 3;
vst1_lane_u32(png_ptr(uint32_t,rp), png_ldr(uint32x2_t,&vdest.val[3]), 0);
rp += 3;
}
}
void
png_read_filter_row_avg4_neon(png_row_infop row_info, png_bytep row,
png_const_bytep prev_row)
{
png_bytep rp = row;
png_bytep rp_stop = row + row_info->rowbytes;
png_const_bytep pp = prev_row;
uint8x8x4_t vdest;
vdest.val[3] = vdup_n_u8(0);
png_debug(1, "in png_read_filter_row_avg4_neon");
for (; rp < rp_stop; rp += 16, pp += 16)
{
uint32x2x4_t vtmp;
uint8x8x4_t *vrpt, *vppt;
uint8x8x4_t vrp, vpp;
uint32x2x4_t *temp_pointer;
uint32x2x4_t vdest_val;
vtmp = vld4_u32(png_ptr(uint32_t,rp));
vrpt = png_ptr(uint8x8x4_t,&vtmp);
vrp = *vrpt;
vtmp = vld4_u32(png_ptrc(uint32_t,pp));
vppt = png_ptr(uint8x8x4_t,&vtmp);
vpp = *vppt;
vdest.val[0] = vhadd_u8(vdest.val[3], vpp.val[0]);
vdest.val[0] = vadd_u8(vdest.val[0], vrp.val[0]);
vdest.val[1] = vhadd_u8(vdest.val[0], vpp.val[1]);
vdest.val[1] = vadd_u8(vdest.val[1], vrp.val[1]);
vdest.val[2] = vhadd_u8(vdest.val[1], vpp.val[2]);
vdest.val[2] = vadd_u8(vdest.val[2], vrp.val[2]);
vdest.val[3] = vhadd_u8(vdest.val[2], vpp.val[3]);
vdest.val[3] = vadd_u8(vdest.val[3], vrp.val[3]);
vdest_val = png_ldr(uint32x2x4_t, &vdest);
vst4_lane_u32(png_ptr(uint32_t,rp), vdest_val, 0);
}
}
static uint8x8_t
paeth(uint8x8_t a, uint8x8_t b, uint8x8_t c)
{
uint8x8_t d, e;
uint16x8_t p1, pa, pb, pc;
p1 = vaddl_u8(a, b); /* a + b */
pc = vaddl_u8(c, c); /* c * 2 */
pa = vabdl_u8(b, c); /* pa */
pb = vabdl_u8(a, c); /* pb */
pc = vabdq_u16(p1, pc); /* pc */
p1 = vcleq_u16(pa, pb); /* pa <= pb */
pa = vcleq_u16(pa, pc); /* pa <= pc */
pb = vcleq_u16(pb, pc); /* pb <= pc */
p1 = vandq_u16(p1, pa); /* pa <= pb && pa <= pc */
d = vmovn_u16(pb);
e = vmovn_u16(p1);
d = vbsl_u8(d, b, c);
e = vbsl_u8(e, a, d);
return e;
}
void
png_read_filter_row_paeth3_neon(png_row_infop row_info, png_bytep row,
png_const_bytep prev_row)
{
png_bytep rp = row;
png_const_bytep pp = prev_row;
png_bytep rp_stop = row + row_info->rowbytes;
uint8x16_t vtmp;
uint8x8x2_t *vrpt;
uint8x8x2_t vrp;
uint8x8_t vlast = vdup_n_u8(0);
uint8x8x4_t vdest;
vdest.val[3] = vdup_n_u8(0);
vtmp = vld1q_u8(rp);
vrpt = png_ptr(uint8x8x2_t,&vtmp);
vrp = *vrpt;
png_debug(1, "in png_read_filter_row_paeth3_neon");
for (; rp < rp_stop; pp += 12)
{
uint8x8x2_t *vppt;
uint8x8x2_t vpp;
uint8x8_t vtmp1, vtmp2, vtmp3;
uint32x2_t *temp_pointer;
vtmp = vld1q_u8(pp);
vppt = png_ptr(uint8x8x2_t,&vtmp);
vpp = *vppt;
vdest.val[0] = paeth(vdest.val[3], vpp.val[0], vlast);
vdest.val[0] = vadd_u8(vdest.val[0], vrp.val[0]);
vtmp1 = vext_u8(vrp.val[0], vrp.val[1], 3);
vtmp2 = vext_u8(vpp.val[0], vpp.val[1], 3);
vdest.val[1] = paeth(vdest.val[0], vtmp2, vpp.val[0]);
vdest.val[1] = vadd_u8(vdest.val[1], vtmp1);
vtmp1 = vext_u8(vrp.val[0], vrp.val[1], 6);
vtmp3 = vext_u8(vpp.val[0], vpp.val[1], 6);
vdest.val[2] = paeth(vdest.val[1], vtmp3, vtmp2);
vdest.val[2] = vadd_u8(vdest.val[2], vtmp1);
vtmp1 = vext_u8(vrp.val[1], vrp.val[1], 1);
vtmp2 = vext_u8(vpp.val[1], vpp.val[1], 1);
vtmp = vld1q_u8(rp + 12);
vrpt = png_ptr(uint8x8x2_t,&vtmp);
vrp = *vrpt;
vdest.val[3] = paeth(vdest.val[2], vtmp2, vtmp3);
vdest.val[3] = vadd_u8(vdest.val[3], vtmp1);
vlast = vtmp2;
vst1_lane_u32(png_ptr(uint32_t,rp), png_ldr(uint32x2_t,&vdest.val[0]), 0);
rp += 3;
vst1_lane_u32(png_ptr(uint32_t,rp), png_ldr(uint32x2_t,&vdest.val[1]), 0);
rp += 3;
vst1_lane_u32(png_ptr(uint32_t,rp), png_ldr(uint32x2_t,&vdest.val[2]), 0);
rp += 3;
vst1_lane_u32(png_ptr(uint32_t,rp), png_ldr(uint32x2_t,&vdest.val[3]), 0);
rp += 3;
}
}
void
png_read_filter_row_paeth4_neon(png_row_infop row_info, png_bytep row,
png_const_bytep prev_row)
{
png_bytep rp = row;
png_bytep rp_stop = row + row_info->rowbytes;
png_const_bytep pp = prev_row;
uint8x8_t vlast = vdup_n_u8(0);
uint8x8x4_t vdest;
vdest.val[3] = vdup_n_u8(0);
png_debug(1, "in png_read_filter_row_paeth4_neon");
for (; rp < rp_stop; rp += 16, pp += 16)
{
uint32x2x4_t vtmp;
uint8x8x4_t *vrpt, *vppt;
uint8x8x4_t vrp, vpp;
uint32x2x4_t *temp_pointer;
uint32x2x4_t vdest_val;
vtmp = vld4_u32(png_ptr(uint32_t,rp));
vrpt = png_ptr(uint8x8x4_t,&vtmp);
vrp = *vrpt;
vtmp = vld4_u32(png_ptrc(uint32_t,pp));
vppt = png_ptr(uint8x8x4_t,&vtmp);
vpp = *vppt;
vdest.val[0] = paeth(vdest.val[3], vpp.val[0], vlast);
vdest.val[0] = vadd_u8(vdest.val[0], vrp.val[0]);
vdest.val[1] = paeth(vdest.val[0], vpp.val[1], vpp.val[0]);
vdest.val[1] = vadd_u8(vdest.val[1], vrp.val[1]);
vdest.val[2] = paeth(vdest.val[1], vpp.val[2], vpp.val[1]);
vdest.val[2] = vadd_u8(vdest.val[2], vrp.val[2]);
vdest.val[3] = paeth(vdest.val[2], vpp.val[3], vpp.val[2]);
vdest.val[3] = vadd_u8(vdest.val[3], vrp.val[3]);
vlast = vpp.val[3];
vdest_val = png_ldr(uint32x2x4_t, &vdest);
vst4_lane_u32(png_ptr(uint32_t,rp), vdest_val, 0);
}
}
#endif /* PNG_ARM_NEON_OPT > 0 */
#endif /* PNG_ARM_NEON_IMPLEMENTATION == 1 (intrinsics) */
#endif /* READ */

View File

@ -0,0 +1,149 @@
/* palette_neon_intrinsics.c - NEON optimised palette expansion functions
*
* Copyright (c) 2018-2019 Cosmin Truta
* Copyright (c) 2017-2018 Arm Holdings. All rights reserved.
* Written by Richard Townsend <Richard.Townsend@arm.com>, February 2017.
*
* This code is released under the libpng license.
* For conditions of distribution and use, see the disclaimer
* and license in png.h
*/
#include "../pngpriv.h"
#if PNG_ARM_NEON_IMPLEMENTATION == 1
#if defined(_MSC_VER) && defined(_M_ARM64)
# include <arm64_neon.h>
#else
# include <arm_neon.h>
#endif
/* Build an RGBA8 palette from the separate RGB and alpha palettes. */
void
png_riffle_palette_neon(png_structrp png_ptr)
{
png_const_colorp palette = png_ptr->palette;
png_bytep riffled_palette = png_ptr->riffled_palette;
png_const_bytep trans_alpha = png_ptr->trans_alpha;
int num_trans = png_ptr->num_trans;
int i;
png_debug(1, "in png_riffle_palette_neon");
/* Initially black, opaque. */
uint8x16x4_t w = {{
vdupq_n_u8(0x00),
vdupq_n_u8(0x00),
vdupq_n_u8(0x00),
vdupq_n_u8(0xff),
}};
/* First, riffle the RGB colours into an RGBA8 palette.
* The alpha component is set to opaque for now.
*/
for (i = 0; i < 256; i += 16)
{
uint8x16x3_t v = vld3q_u8((png_const_bytep)(palette + i));
w.val[0] = v.val[0];
w.val[1] = v.val[1];
w.val[2] = v.val[2];
vst4q_u8(riffled_palette + (i << 2), w);
}
/* Fix up the missing transparency values. */
for (i = 0; i < num_trans; i++)
riffled_palette[(i << 2) + 3] = trans_alpha[i];
}
/* Expands a palettized row into RGBA8. */
int
png_do_expand_palette_rgba8_neon(png_structrp png_ptr, png_row_infop row_info,
png_const_bytep row, png_bytepp ssp, png_bytepp ddp)
{
png_uint_32 row_width = row_info->width;
const png_uint_32 *riffled_palette =
(const png_uint_32 *)png_ptr->riffled_palette;
const png_int_32 pixels_per_chunk = 4;
int i;
png_debug(1, "in png_do_expand_palette_rgba8_neon");
if (row_width < pixels_per_chunk)
return 0;
/* This function originally gets the last byte of the output row.
* The NEON part writes forward from a given position, so we have
* to seek this back by 4 pixels x 4 bytes.
*/
*ddp = *ddp - ((pixels_per_chunk * sizeof(png_uint_32)) - 1);
for (i = 0; i < row_width; i += pixels_per_chunk)
{
uint32x4_t cur;
png_bytep sp = *ssp - i, dp = *ddp - (i << 2);
cur = vld1q_dup_u32 (riffled_palette + *(sp - 3));
cur = vld1q_lane_u32(riffled_palette + *(sp - 2), cur, 1);
cur = vld1q_lane_u32(riffled_palette + *(sp - 1), cur, 2);
cur = vld1q_lane_u32(riffled_palette + *(sp - 0), cur, 3);
vst1q_u32((void *)dp, cur);
}
if (i != row_width)
{
/* Remove the amount that wasn't processed. */
i -= pixels_per_chunk;
}
/* Decrement output pointers. */
*ssp = *ssp - i;
*ddp = *ddp - (i << 2);
return i;
}
/* Expands a palettized row into RGB8. */
int
png_do_expand_palette_rgb8_neon(png_structrp png_ptr, png_row_infop row_info,
png_const_bytep row, png_bytepp ssp, png_bytepp ddp)
{
png_uint_32 row_width = row_info->width;
png_const_bytep palette = (png_const_bytep)png_ptr->palette;
const png_uint_32 pixels_per_chunk = 8;
int i;
png_debug(1, "in png_do_expand_palette_rgb8_neon");
if (row_width <= pixels_per_chunk)
return 0;
/* Seeking this back by 8 pixels x 3 bytes. */
*ddp = *ddp - ((pixels_per_chunk * sizeof(png_color)) - 1);
for (i = 0; i < row_width; i += pixels_per_chunk)
{
uint8x8x3_t cur;
png_bytep sp = *ssp - i, dp = *ddp - ((i << 1) + i);
cur = vld3_dup_u8(palette + sizeof(png_color) * (*(sp - 7)));
cur = vld3_lane_u8(palette + sizeof(png_color) * (*(sp - 6)), cur, 1);
cur = vld3_lane_u8(palette + sizeof(png_color) * (*(sp - 5)), cur, 2);
cur = vld3_lane_u8(palette + sizeof(png_color) * (*(sp - 4)), cur, 3);
cur = vld3_lane_u8(palette + sizeof(png_color) * (*(sp - 3)), cur, 4);
cur = vld3_lane_u8(palette + sizeof(png_color) * (*(sp - 2)), cur, 5);
cur = vld3_lane_u8(palette + sizeof(png_color) * (*(sp - 1)), cur, 6);
cur = vld3_lane_u8(palette + sizeof(png_color) * (*(sp - 0)), cur, 7);
vst3_u8((void *)dp, cur);
}
if (i != row_width)
{
/* Remove the amount that wasn't processed. */
i -= pixels_per_chunk;
}
/* Decrement output pointers. */
*ssp = *ssp - i;
*ddp = *ddp - ((i << 1) + i);
return i;
}
#endif /* PNG_ARM_NEON_IMPLEMENTATION */

View File

@ -0,0 +1,391 @@
/* filter_sse2_intrinsics.c - SSE2 optimized filter functions
*
* Copyright (c) 2018 Cosmin Truta
* Copyright (c) 2016-2017 Glenn Randers-Pehrson
* Written by Mike Klein and Matt Sarett
* Derived from arm/filter_neon_intrinsics.c
*
* This code is released under the libpng license.
* For conditions of distribution and use, see the disclaimer
* and license in png.h
*/
#include "../pngpriv.h"
#ifdef PNG_READ_SUPPORTED
#if PNG_INTEL_SSE_IMPLEMENTATION > 0
#include <immintrin.h>
/* Functions in this file look at most 3 pixels (a,b,c) to predict the 4th (d).
* They're positioned like this:
* prev: c b
* row: a d
* The Sub filter predicts d=a, Avg d=(a+b)/2, and Paeth predicts d to be
* whichever of a, b, or c is closest to p=a+b-c.
*/
static __m128i load4(const void* p) {
int tmp;
memcpy(&tmp, p, sizeof(tmp));
return _mm_cvtsi32_si128(tmp);
}
static void store4(void* p, __m128i v) {
int tmp = _mm_cvtsi128_si32(v);
memcpy(p, &tmp, sizeof(int));
}
static __m128i load3(const void* p) {
png_uint_32 tmp = 0;
memcpy(&tmp, p, 3);
return _mm_cvtsi32_si128(tmp);
}
static void store3(void* p, __m128i v) {
int tmp = _mm_cvtsi128_si32(v);
memcpy(p, &tmp, 3);
}
void png_read_filter_row_sub3_sse2(png_row_infop row_info, png_bytep row,
png_const_bytep prev)
{
/* The Sub filter predicts each pixel as the previous pixel, a.
* There is no pixel to the left of the first pixel. It's encoded directly.
* That works with our main loop if we just say that left pixel was zero.
*/
size_t rb;
__m128i a, d = _mm_setzero_si128();
png_debug(1, "in png_read_filter_row_sub3_sse2");
rb = row_info->rowbytes;
while (rb >= 4) {
a = d; d = load4(row);
d = _mm_add_epi8(d, a);
store3(row, d);
row += 3;
rb -= 3;
}
if (rb > 0) {
a = d; d = load3(row);
d = _mm_add_epi8(d, a);
store3(row, d);
row += 3;
rb -= 3;
}
PNG_UNUSED(prev)
}
void png_read_filter_row_sub4_sse2(png_row_infop row_info, png_bytep row,
png_const_bytep prev)
{
/* The Sub filter predicts each pixel as the previous pixel, a.
* There is no pixel to the left of the first pixel. It's encoded directly.
* That works with our main loop if we just say that left pixel was zero.
*/
size_t rb;
__m128i a, d = _mm_setzero_si128();
png_debug(1, "in png_read_filter_row_sub4_sse2");
rb = row_info->rowbytes+4;
while (rb > 4) {
a = d; d = load4(row);
d = _mm_add_epi8(d, a);
store4(row, d);
row += 4;
rb -= 4;
}
PNG_UNUSED(prev)
}
void png_read_filter_row_avg3_sse2(png_row_infop row_info, png_bytep row,
png_const_bytep prev)
{
/* The Avg filter predicts each pixel as the (truncated) average of a and b.
* There's no pixel to the left of the first pixel. Luckily, it's
* predicted to be half of the pixel above it. So again, this works
* perfectly with our loop if we make sure a starts at zero.
*/
size_t rb;
const __m128i zero = _mm_setzero_si128();
__m128i b;
__m128i a, d = zero;
png_debug(1, "in png_read_filter_row_avg3_sse2");
rb = row_info->rowbytes;
while (rb >= 4) {
__m128i avg;
b = load4(prev);
a = d; d = load4(row );
/* PNG requires a truncating average, so we can't just use _mm_avg_epu8 */
avg = _mm_avg_epu8(a,b);
/* ...but we can fix it up by subtracting off 1 if it rounded up. */
avg = _mm_sub_epi8(avg, _mm_and_si128(_mm_xor_si128(a,b),
_mm_set1_epi8(1)));
d = _mm_add_epi8(d, avg);
store3(row, d);
prev += 3;
row += 3;
rb -= 3;
}
if (rb > 0) {
__m128i avg;
b = load3(prev);
a = d; d = load3(row );
/* PNG requires a truncating average, so we can't just use _mm_avg_epu8 */
avg = _mm_avg_epu8(a,b);
/* ...but we can fix it up by subtracting off 1 if it rounded up. */
avg = _mm_sub_epi8(avg, _mm_and_si128(_mm_xor_si128(a,b),
_mm_set1_epi8(1)));
d = _mm_add_epi8(d, avg);
store3(row, d);
prev += 3;
row += 3;
rb -= 3;
}
}
void png_read_filter_row_avg4_sse2(png_row_infop row_info, png_bytep row,
png_const_bytep prev)
{
/* The Avg filter predicts each pixel as the (truncated) average of a and b.
* There's no pixel to the left of the first pixel. Luckily, it's
* predicted to be half of the pixel above it. So again, this works
* perfectly with our loop if we make sure a starts at zero.
*/
size_t rb;
const __m128i zero = _mm_setzero_si128();
__m128i b;
__m128i a, d = zero;
png_debug(1, "in png_read_filter_row_avg4_sse2");
rb = row_info->rowbytes+4;
while (rb > 4) {
__m128i avg;
b = load4(prev);
a = d; d = load4(row );
/* PNG requires a truncating average, so we can't just use _mm_avg_epu8 */
avg = _mm_avg_epu8(a,b);
/* ...but we can fix it up by subtracting off 1 if it rounded up. */
avg = _mm_sub_epi8(avg, _mm_and_si128(_mm_xor_si128(a,b),
_mm_set1_epi8(1)));
d = _mm_add_epi8(d, avg);
store4(row, d);
prev += 4;
row += 4;
rb -= 4;
}
}
/* Returns |x| for 16-bit lanes. */
static __m128i abs_i16(__m128i x) {
#if PNG_INTEL_SSE_IMPLEMENTATION >= 2
return _mm_abs_epi16(x);
#else
/* Read this all as, return x<0 ? -x : x.
* To negate two's complement, you flip all the bits then add 1.
*/
__m128i is_negative = _mm_cmplt_epi16(x, _mm_setzero_si128());
/* Flip negative lanes. */
x = _mm_xor_si128(x, is_negative);
/* +1 to negative lanes, else +0. */
x = _mm_sub_epi16(x, is_negative);
return x;
#endif
}
/* Bytewise c ? t : e. */
static __m128i if_then_else(__m128i c, __m128i t, __m128i e) {
#if PNG_INTEL_SSE_IMPLEMENTATION >= 3
return _mm_blendv_epi8(e,t,c);
#else
return _mm_or_si128(_mm_and_si128(c, t), _mm_andnot_si128(c, e));
#endif
}
void png_read_filter_row_paeth3_sse2(png_row_infop row_info, png_bytep row,
png_const_bytep prev)
{
/* Paeth tries to predict pixel d using the pixel to the left of it, a,
* and two pixels from the previous row, b and c:
* prev: c b
* row: a d
* The Paeth function predicts d to be whichever of a, b, or c is nearest to
* p=a+b-c.
*
* The first pixel has no left context, and so uses an Up filter, p = b.
* This works naturally with our main loop's p = a+b-c if we force a and c
* to zero.
* Here we zero b and d, which become c and a respectively at the start of
* the loop.
*/
size_t rb;
const __m128i zero = _mm_setzero_si128();
__m128i c, b = zero,
a, d = zero;
png_debug(1, "in png_read_filter_row_paeth3_sse2");
rb = row_info->rowbytes;
while (rb >= 4) {
/* It's easiest to do this math (particularly, deal with pc) with 16-bit
* intermediates.
*/
__m128i pa,pb,pc,smallest,nearest;
c = b; b = _mm_unpacklo_epi8(load4(prev), zero);
a = d; d = _mm_unpacklo_epi8(load4(row ), zero);
/* (p-a) == (a+b-c - a) == (b-c) */
pa = _mm_sub_epi16(b,c);
/* (p-b) == (a+b-c - b) == (a-c) */
pb = _mm_sub_epi16(a,c);
/* (p-c) == (a+b-c - c) == (a+b-c-c) == (b-c)+(a-c) */
pc = _mm_add_epi16(pa,pb);
pa = abs_i16(pa); /* |p-a| */
pb = abs_i16(pb); /* |p-b| */
pc = abs_i16(pc); /* |p-c| */
smallest = _mm_min_epi16(pc, _mm_min_epi16(pa, pb));
/* Paeth breaks ties favoring a over b over c. */
nearest = if_then_else(_mm_cmpeq_epi16(smallest, pa), a,
if_then_else(_mm_cmpeq_epi16(smallest, pb), b,
c));
/* Note `_epi8`: we need addition to wrap modulo 255. */
d = _mm_add_epi8(d, nearest);
store3(row, _mm_packus_epi16(d,d));
prev += 3;
row += 3;
rb -= 3;
}
if (rb > 0) {
/* It's easiest to do this math (particularly, deal with pc) with 16-bit
* intermediates.
*/
__m128i pa,pb,pc,smallest,nearest;
c = b; b = _mm_unpacklo_epi8(load3(prev), zero);
a = d; d = _mm_unpacklo_epi8(load3(row ), zero);
/* (p-a) == (a+b-c - a) == (b-c) */
pa = _mm_sub_epi16(b,c);
/* (p-b) == (a+b-c - b) == (a-c) */
pb = _mm_sub_epi16(a,c);
/* (p-c) == (a+b-c - c) == (a+b-c-c) == (b-c)+(a-c) */
pc = _mm_add_epi16(pa,pb);
pa = abs_i16(pa); /* |p-a| */
pb = abs_i16(pb); /* |p-b| */
pc = abs_i16(pc); /* |p-c| */
smallest = _mm_min_epi16(pc, _mm_min_epi16(pa, pb));
/* Paeth breaks ties favoring a over b over c. */
nearest = if_then_else(_mm_cmpeq_epi16(smallest, pa), a,
if_then_else(_mm_cmpeq_epi16(smallest, pb), b,
c));
/* Note `_epi8`: we need addition to wrap modulo 255. */
d = _mm_add_epi8(d, nearest);
store3(row, _mm_packus_epi16(d,d));
prev += 3;
row += 3;
rb -= 3;
}
}
void png_read_filter_row_paeth4_sse2(png_row_infop row_info, png_bytep row,
png_const_bytep prev)
{
/* Paeth tries to predict pixel d using the pixel to the left of it, a,
* and two pixels from the previous row, b and c:
* prev: c b
* row: a d
* The Paeth function predicts d to be whichever of a, b, or c is nearest to
* p=a+b-c.
*
* The first pixel has no left context, and so uses an Up filter, p = b.
* This works naturally with our main loop's p = a+b-c if we force a and c
* to zero.
* Here we zero b and d, which become c and a respectively at the start of
* the loop.
*/
size_t rb;
const __m128i zero = _mm_setzero_si128();
__m128i pa,pb,pc,smallest,nearest;
__m128i c, b = zero,
a, d = zero;
png_debug(1, "in png_read_filter_row_paeth4_sse2");
rb = row_info->rowbytes+4;
while (rb > 4) {
/* It's easiest to do this math (particularly, deal with pc) with 16-bit
* intermediates.
*/
c = b; b = _mm_unpacklo_epi8(load4(prev), zero);
a = d; d = _mm_unpacklo_epi8(load4(row ), zero);
/* (p-a) == (a+b-c - a) == (b-c) */
pa = _mm_sub_epi16(b,c);
/* (p-b) == (a+b-c - b) == (a-c) */
pb = _mm_sub_epi16(a,c);
/* (p-c) == (a+b-c - c) == (a+b-c-c) == (b-c)+(a-c) */
pc = _mm_add_epi16(pa,pb);
pa = abs_i16(pa); /* |p-a| */
pb = abs_i16(pb); /* |p-b| */
pc = abs_i16(pc); /* |p-c| */
smallest = _mm_min_epi16(pc, _mm_min_epi16(pa, pb));
/* Paeth breaks ties favoring a over b over c. */
nearest = if_then_else(_mm_cmpeq_epi16(smallest, pa), a,
if_then_else(_mm_cmpeq_epi16(smallest, pb), b,
c));
/* Note `_epi8`: we need addition to wrap modulo 255. */
d = _mm_add_epi8(d, nearest);
store4(row, _mm_packus_epi16(d,d));
prev += 4;
row += 4;
rb -= 4;
}
}
#endif /* PNG_INTEL_SSE_IMPLEMENTATION > 0 */
#endif /* READ */

52
hecl/extern/libpng/intel/intel_init.c vendored Normal file
View File

@ -0,0 +1,52 @@
/* intel_init.c - SSE2 optimized filter functions
*
* Copyright (c) 2018 Cosmin Truta
* Copyright (c) 2016-2017 Glenn Randers-Pehrson
* Written by Mike Klein and Matt Sarett, Google, Inc.
* Derived from arm/arm_init.c
*
* This code is released under the libpng license.
* For conditions of distribution and use, see the disclaimer
* and license in png.h
*/
#include "../pngpriv.h"
#ifdef PNG_READ_SUPPORTED
#if PNG_INTEL_SSE_IMPLEMENTATION > 0
void
png_init_filter_functions_sse2(png_structp pp, unsigned int bpp)
{
/* The techniques used to implement each of these filters in SSE operate on
* one pixel at a time.
* So they generally speed up 3bpp images about 3x, 4bpp images about 4x.
* They can scale up to 6 and 8 bpp images and down to 2 bpp images,
* but they'd not likely have any benefit for 1bpp images.
* Most of these can be implemented using only MMX and 64-bit registers,
* but they end up a bit slower than using the equally-ubiquitous SSE2.
*/
png_debug(1, "in png_init_filter_functions_sse2");
if (bpp == 3)
{
pp->read_filter[PNG_FILTER_VALUE_SUB-1] = png_read_filter_row_sub3_sse2;
pp->read_filter[PNG_FILTER_VALUE_AVG-1] = png_read_filter_row_avg3_sse2;
pp->read_filter[PNG_FILTER_VALUE_PAETH-1] =
png_read_filter_row_paeth3_sse2;
}
else if (bpp == 4)
{
pp->read_filter[PNG_FILTER_VALUE_SUB-1] = png_read_filter_row_sub4_sse2;
pp->read_filter[PNG_FILTER_VALUE_AVG-1] = png_read_filter_row_avg4_sse2;
pp->read_filter[PNG_FILTER_VALUE_PAETH-1] =
png_read_filter_row_paeth4_sse2;
}
/* No need optimize PNG_FILTER_VALUE_UP. The compiler should
* autovectorize.
*/
}
#endif /* PNG_INTEL_SSE_IMPLEMENTATION > 0 */
#endif /* PNG_READ_SUPPORTED */

4607
hecl/extern/libpng/png.c vendored Normal file

File diff suppressed because it is too large Load Diff

3247
hecl/extern/libpng/png.h vendored Normal file

File diff suppressed because it is too large Load Diff

623
hecl/extern/libpng/pngconf.h vendored Normal file
View File

@ -0,0 +1,623 @@
/* pngconf.h - machine-configurable file for libpng
*
* libpng version 1.6.37
*
* Copyright (c) 2018-2019 Cosmin Truta
* Copyright (c) 1998-2002,2004,2006-2016,2018 Glenn Randers-Pehrson
* Copyright (c) 1996-1997 Andreas Dilger
* Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc.
*
* This code is released under the libpng license.
* For conditions of distribution and use, see the disclaimer
* and license in png.h
*
* Any machine specific code is near the front of this file, so if you
* are configuring libpng for a machine, you may want to read the section
* starting here down to where it starts to typedef png_color, png_text,
* and png_info.
*/
#ifndef PNGCONF_H
#define PNGCONF_H
#ifndef PNG_BUILDING_SYMBOL_TABLE /* else includes may cause problems */
/* From libpng 1.6.0 libpng requires an ANSI X3.159-1989 ("ISOC90") compliant C
* compiler for correct compilation. The following header files are required by
* the standard. If your compiler doesn't provide these header files, or they
* do not match the standard, you will need to provide/improve them.
*/
#include <limits.h>
#include <stddef.h>
/* Library header files. These header files are all defined by ISOC90; libpng
* expects conformant implementations, however, an ISOC90 conformant system need
* not provide these header files if the functionality cannot be implemented.
* In this case it will be necessary to disable the relevant parts of libpng in
* the build of pnglibconf.h.
*
* Prior to 1.6.0 string.h was included here; the API changes in 1.6.0 to not
* include this unnecessary header file.
*/
#ifdef PNG_STDIO_SUPPORTED
/* Required for the definition of FILE: */
# include <stdio.h>
#endif
#ifdef PNG_SETJMP_SUPPORTED
/* Required for the definition of jmp_buf and the declaration of longjmp: */
# include <setjmp.h>
#endif
#ifdef PNG_CONVERT_tIME_SUPPORTED
/* Required for struct tm: */
# include <time.h>
#endif
#endif /* PNG_BUILDING_SYMBOL_TABLE */
/* Prior to 1.6.0, it was possible to turn off 'const' in declarations,
* using PNG_NO_CONST. This is no longer supported.
*/
#define PNG_CONST const /* backward compatibility only */
/* This controls optimization of the reading of 16-bit and 32-bit
* values from PNG files. It can be set on a per-app-file basis: it
* just changes whether a macro is used when the function is called.
* The library builder sets the default; if read functions are not
* built into the library the macro implementation is forced on.
*/
#ifndef PNG_READ_INT_FUNCTIONS_SUPPORTED
# define PNG_USE_READ_MACROS
#endif
#if !defined(PNG_NO_USE_READ_MACROS) && !defined(PNG_USE_READ_MACROS)
# if PNG_DEFAULT_READ_MACROS
# define PNG_USE_READ_MACROS
# endif
#endif
/* COMPILER SPECIFIC OPTIONS.
*
* These options are provided so that a variety of difficult compilers
* can be used. Some are fixed at build time (e.g. PNG_API_RULE
* below) but still have compiler specific implementations, others
* may be changed on a per-file basis when compiling against libpng.
*/
/* The PNGARG macro was used in versions of libpng prior to 1.6.0 to protect
* against legacy (pre ISOC90) compilers that did not understand function
* prototypes. It is not required for modern C compilers.
*/
#ifndef PNGARG
# define PNGARG(arglist) arglist
#endif
/* Function calling conventions.
* =============================
* Normally it is not necessary to specify to the compiler how to call
* a function - it just does it - however on x86 systems derived from
* Microsoft and Borland C compilers ('IBM PC', 'DOS', 'Windows' systems
* and some others) there are multiple ways to call a function and the
* default can be changed on the compiler command line. For this reason
* libpng specifies the calling convention of every exported function and
* every function called via a user supplied function pointer. This is
* done in this file by defining the following macros:
*
* PNGAPI Calling convention for exported functions.
* PNGCBAPI Calling convention for user provided (callback) functions.
* PNGCAPI Calling convention used by the ANSI-C library (required
* for longjmp callbacks and sometimes used internally to
* specify the calling convention for zlib).
*
* These macros should never be overridden. If it is necessary to
* change calling convention in a private build this can be done
* by setting PNG_API_RULE (which defaults to 0) to one of the values
* below to select the correct 'API' variants.
*
* PNG_API_RULE=0 Use PNGCAPI - the 'C' calling convention - throughout.
* This is correct in every known environment.
* PNG_API_RULE=1 Use the operating system convention for PNGAPI and
* the 'C' calling convention (from PNGCAPI) for
* callbacks (PNGCBAPI). This is no longer required
* in any known environment - if it has to be used
* please post an explanation of the problem to the
* libpng mailing list.
*
* These cases only differ if the operating system does not use the C
* calling convention, at present this just means the above cases
* (x86 DOS/Windows systems) and, even then, this does not apply to
* Cygwin running on those systems.
*
* Note that the value must be defined in pnglibconf.h so that what
* the application uses to call the library matches the conventions
* set when building the library.
*/
/* Symbol export
* =============
* When building a shared library it is almost always necessary to tell
* the compiler which symbols to export. The png.h macro 'PNG_EXPORT'
* is used to mark the symbols. On some systems these symbols can be
* extracted at link time and need no special processing by the compiler,
* on other systems the symbols are flagged by the compiler and just
* the declaration requires a special tag applied (unfortunately) in a
* compiler dependent way. Some systems can do either.
*
* A small number of older systems also require a symbol from a DLL to
* be flagged to the program that calls it. This is a problem because
* we do not know in the header file included by application code that
* the symbol will come from a shared library, as opposed to a statically
* linked one. For this reason the application must tell us by setting
* the magic flag PNG_USE_DLL to turn on the special processing before
* it includes png.h.
*
* Four additional macros are used to make this happen:
*
* PNG_IMPEXP The magic (if any) to cause a symbol to be exported from
* the build or imported if PNG_USE_DLL is set - compiler
* and system specific.
*
* PNG_EXPORT_TYPE(type) A macro that pre or appends PNG_IMPEXP to
* 'type', compiler specific.
*
* PNG_DLL_EXPORT Set to the magic to use during a libpng build to
* make a symbol exported from the DLL. Not used in the
* public header files; see pngpriv.h for how it is used
* in the libpng build.
*
* PNG_DLL_IMPORT Set to the magic to force the libpng symbols to come
* from a DLL - used to define PNG_IMPEXP when
* PNG_USE_DLL is set.
*/
/* System specific discovery.
* ==========================
* This code is used at build time to find PNG_IMPEXP, the API settings
* and PNG_EXPORT_TYPE(), it may also set a macro to indicate the DLL
* import processing is possible. On Windows systems it also sets
* compiler-specific macros to the values required to change the calling
* conventions of the various functions.
*/
#if defined(_Windows) || defined(_WINDOWS) || defined(WIN32) ||\
defined(_WIN32) || defined(__WIN32__) || defined(__CYGWIN__)
/* Windows system (DOS doesn't support DLLs). Includes builds under Cygwin or
* MinGW on any architecture currently supported by Windows. Also includes
* Watcom builds but these need special treatment because they are not
* compatible with GCC or Visual C because of different calling conventions.
*/
# if PNG_API_RULE == 2
/* If this line results in an error, either because __watcall is not
* understood or because of a redefine just below you cannot use *this*
* build of the library with the compiler you are using. *This* build was
* build using Watcom and applications must also be built using Watcom!
*/
# define PNGCAPI __watcall
# endif
# if defined(__GNUC__) || (defined(_MSC_VER) && (_MSC_VER >= 800))
# define PNGCAPI __cdecl
# if PNG_API_RULE == 1
/* If this line results in an error __stdcall is not understood and
* PNG_API_RULE should not have been set to '1'.
*/
# define PNGAPI __stdcall
# endif
# else
/* An older compiler, or one not detected (erroneously) above,
* if necessary override on the command line to get the correct
* variants for the compiler.
*/
# ifndef PNGCAPI
# define PNGCAPI _cdecl
# endif
# if PNG_API_RULE == 1 && !defined(PNGAPI)
# define PNGAPI _stdcall
# endif
# endif /* compiler/api */
/* NOTE: PNGCBAPI always defaults to PNGCAPI. */
# if defined(PNGAPI) && !defined(PNG_USER_PRIVATEBUILD)
# error "PNG_USER_PRIVATEBUILD must be defined if PNGAPI is changed"
# endif
# if (defined(_MSC_VER) && _MSC_VER < 800) ||\
(defined(__BORLANDC__) && __BORLANDC__ < 0x500)
/* older Borland and MSC
* compilers used '__export' and required this to be after
* the type.
*/
# ifndef PNG_EXPORT_TYPE
# define PNG_EXPORT_TYPE(type) type PNG_IMPEXP
# endif
# define PNG_DLL_EXPORT __export
# else /* newer compiler */
# define PNG_DLL_EXPORT __declspec(dllexport)
# ifndef PNG_DLL_IMPORT
# define PNG_DLL_IMPORT __declspec(dllimport)
# endif
# endif /* compiler */
#else /* !Windows */
# if (defined(__IBMC__) || defined(__IBMCPP__)) && defined(__OS2__)
# define PNGAPI _System
# else /* !Windows/x86 && !OS/2 */
/* Use the defaults, or define PNG*API on the command line (but
* this will have to be done for every compile!)
*/
# endif /* other system, !OS/2 */
#endif /* !Windows/x86 */
/* Now do all the defaulting . */
#ifndef PNGCAPI
# define PNGCAPI
#endif
#ifndef PNGCBAPI
# define PNGCBAPI PNGCAPI
#endif
#ifndef PNGAPI
# define PNGAPI PNGCAPI
#endif
/* PNG_IMPEXP may be set on the compilation system command line or (if not set)
* then in an internal header file when building the library, otherwise (when
* using the library) it is set here.
*/
#ifndef PNG_IMPEXP
# if defined(PNG_USE_DLL) && defined(PNG_DLL_IMPORT)
/* This forces use of a DLL, disallowing static linking */
# define PNG_IMPEXP PNG_DLL_IMPORT
# endif
# ifndef PNG_IMPEXP
# define PNG_IMPEXP
# endif
#endif
/* In 1.5.2 the definition of PNG_FUNCTION has been changed to always treat
* 'attributes' as a storage class - the attributes go at the start of the
* function definition, and attributes are always appended regardless of the
* compiler. This considerably simplifies these macros but may cause problems
* if any compilers both need function attributes and fail to handle them as
* a storage class (this is unlikely.)
*/
#ifndef PNG_FUNCTION
# define PNG_FUNCTION(type, name, args, attributes) attributes type name args
#endif
#ifndef PNG_EXPORT_TYPE
# define PNG_EXPORT_TYPE(type) PNG_IMPEXP type
#endif
/* The ordinal value is only relevant when preprocessing png.h for symbol
* table entries, so we discard it here. See the .dfn files in the
* scripts directory.
*/
#ifndef PNG_EXPORTA
# define PNG_EXPORTA(ordinal, type, name, args, attributes) \
PNG_FUNCTION(PNG_EXPORT_TYPE(type), (PNGAPI name), PNGARG(args), \
PNG_LINKAGE_API attributes)
#endif
/* ANSI-C (C90) does not permit a macro to be invoked with an empty argument,
* so make something non-empty to satisfy the requirement:
*/
#define PNG_EMPTY /*empty list*/
#define PNG_EXPORT(ordinal, type, name, args) \
PNG_EXPORTA(ordinal, type, name, args, PNG_EMPTY)
/* Use PNG_REMOVED to comment out a removed interface. */
#ifndef PNG_REMOVED
# define PNG_REMOVED(ordinal, type, name, args, attributes)
#endif
#ifndef PNG_CALLBACK
# define PNG_CALLBACK(type, name, args) type (PNGCBAPI name) PNGARG(args)
#endif
/* Support for compiler specific function attributes. These are used
* so that where compiler support is available incorrect use of API
* functions in png.h will generate compiler warnings.
*
* Added at libpng-1.2.41.
*/
#ifndef PNG_NO_PEDANTIC_WARNINGS
# ifndef PNG_PEDANTIC_WARNINGS_SUPPORTED
# define PNG_PEDANTIC_WARNINGS_SUPPORTED
# endif
#endif
#ifdef PNG_PEDANTIC_WARNINGS_SUPPORTED
/* Support for compiler specific function attributes. These are used
* so that where compiler support is available, incorrect use of API
* functions in png.h will generate compiler warnings. Added at libpng
* version 1.2.41. Disabling these removes the warnings but may also produce
* less efficient code.
*/
# if defined(__clang__) && defined(__has_attribute)
/* Clang defines both __clang__ and __GNUC__. Check __clang__ first. */
# if !defined(PNG_USE_RESULT) && __has_attribute(__warn_unused_result__)
# define PNG_USE_RESULT __attribute__((__warn_unused_result__))
# endif
# if !defined(PNG_NORETURN) && __has_attribute(__noreturn__)
# define PNG_NORETURN __attribute__((__noreturn__))
# endif
# if !defined(PNG_ALLOCATED) && __has_attribute(__malloc__)
# define PNG_ALLOCATED __attribute__((__malloc__))
# endif
# if !defined(PNG_DEPRECATED) && __has_attribute(__deprecated__)
# define PNG_DEPRECATED __attribute__((__deprecated__))
# endif
# if !defined(PNG_PRIVATE)
# ifdef __has_extension
# if __has_extension(attribute_unavailable_with_message)
# define PNG_PRIVATE __attribute__((__unavailable__(\
"This function is not exported by libpng.")))
# endif
# endif
# endif
# ifndef PNG_RESTRICT
# define PNG_RESTRICT __restrict
# endif
# elif defined(__GNUC__)
# ifndef PNG_USE_RESULT
# define PNG_USE_RESULT __attribute__((__warn_unused_result__))
# endif
# ifndef PNG_NORETURN
# define PNG_NORETURN __attribute__((__noreturn__))
# endif
# if __GNUC__ >= 3
# ifndef PNG_ALLOCATED
# define PNG_ALLOCATED __attribute__((__malloc__))
# endif
# ifndef PNG_DEPRECATED
# define PNG_DEPRECATED __attribute__((__deprecated__))
# endif
# ifndef PNG_PRIVATE
# if 0 /* Doesn't work so we use deprecated instead*/
# define PNG_PRIVATE \
__attribute__((warning("This function is not exported by libpng.")))
# else
# define PNG_PRIVATE \
__attribute__((__deprecated__))
# endif
# endif
# if ((__GNUC__ > 3) || !defined(__GNUC_MINOR__) || (__GNUC_MINOR__ >= 1))
# ifndef PNG_RESTRICT
# define PNG_RESTRICT __restrict
# endif
# endif /* __GNUC__.__GNUC_MINOR__ > 3.0 */
# endif /* __GNUC__ >= 3 */
# elif defined(_MSC_VER) && (_MSC_VER >= 1300)
# ifndef PNG_USE_RESULT
# define PNG_USE_RESULT /* not supported */
# endif
# ifndef PNG_NORETURN
# define PNG_NORETURN __declspec(noreturn)
# endif
# ifndef PNG_ALLOCATED
# if (_MSC_VER >= 1400)
# define PNG_ALLOCATED __declspec(restrict)
# endif
# endif
# ifndef PNG_DEPRECATED
# define PNG_DEPRECATED __declspec(deprecated)
# endif
# ifndef PNG_PRIVATE
# define PNG_PRIVATE __declspec(deprecated)
# endif
# ifndef PNG_RESTRICT
# if (_MSC_VER >= 1400)
# define PNG_RESTRICT __restrict
# endif
# endif
# elif defined(__WATCOMC__)
# ifndef PNG_RESTRICT
# define PNG_RESTRICT __restrict
# endif
# endif
#endif /* PNG_PEDANTIC_WARNINGS */
#ifndef PNG_DEPRECATED
# define PNG_DEPRECATED /* Use of this function is deprecated */
#endif
#ifndef PNG_USE_RESULT
# define PNG_USE_RESULT /* The result of this function must be checked */
#endif
#ifndef PNG_NORETURN
# define PNG_NORETURN /* This function does not return */
#endif
#ifndef PNG_ALLOCATED
# define PNG_ALLOCATED /* The result of the function is new memory */
#endif
#ifndef PNG_PRIVATE
# define PNG_PRIVATE /* This is a private libpng function */
#endif
#ifndef PNG_RESTRICT
# define PNG_RESTRICT /* The C99 "restrict" feature */
#endif
#ifndef PNG_FP_EXPORT /* A floating point API. */
# ifdef PNG_FLOATING_POINT_SUPPORTED
# define PNG_FP_EXPORT(ordinal, type, name, args)\
PNG_EXPORT(ordinal, type, name, args);
# else /* No floating point APIs */
# define PNG_FP_EXPORT(ordinal, type, name, args)
# endif
#endif
#ifndef PNG_FIXED_EXPORT /* A fixed point API. */
# ifdef PNG_FIXED_POINT_SUPPORTED
# define PNG_FIXED_EXPORT(ordinal, type, name, args)\
PNG_EXPORT(ordinal, type, name, args);
# else /* No fixed point APIs */
# define PNG_FIXED_EXPORT(ordinal, type, name, args)
# endif
#endif
#ifndef PNG_BUILDING_SYMBOL_TABLE
/* Some typedefs to get us started. These should be safe on most of the common
* platforms.
*
* png_uint_32 and png_int_32 may, currently, be larger than required to hold a
* 32-bit value however this is not normally advisable.
*
* png_uint_16 and png_int_16 should always be two bytes in size - this is
* verified at library build time.
*
* png_byte must always be one byte in size.
*
* The checks below use constants from limits.h, as defined by the ISOC90
* standard.
*/
#if CHAR_BIT == 8 && UCHAR_MAX == 255
typedef unsigned char png_byte;
#else
# error "libpng requires 8-bit bytes"
#endif
#if INT_MIN == -32768 && INT_MAX == 32767
typedef int png_int_16;
#elif SHRT_MIN == -32768 && SHRT_MAX == 32767
typedef short png_int_16;
#else
# error "libpng requires a signed 16-bit type"
#endif
#if UINT_MAX == 65535
typedef unsigned int png_uint_16;
#elif USHRT_MAX == 65535
typedef unsigned short png_uint_16;
#else
# error "libpng requires an unsigned 16-bit type"
#endif
#if INT_MIN < -2147483646 && INT_MAX > 2147483646
typedef int png_int_32;
#elif LONG_MIN < -2147483646 && LONG_MAX > 2147483646
typedef long int png_int_32;
#else
# error "libpng requires a signed 32-bit (or more) type"
#endif
#if UINT_MAX > 4294967294U
typedef unsigned int png_uint_32;
#elif ULONG_MAX > 4294967294U
typedef unsigned long int png_uint_32;
#else
# error "libpng requires an unsigned 32-bit (or more) type"
#endif
/* Prior to 1.6.0, it was possible to disable the use of size_t and ptrdiff_t.
* From 1.6.0 onwards, an ISO C90 compiler, as well as a standard-compliant
* behavior of sizeof and ptrdiff_t are required.
* The legacy typedefs are provided here for backwards compatibility.
*/
typedef size_t png_size_t;
typedef ptrdiff_t png_ptrdiff_t;
/* libpng needs to know the maximum value of 'size_t' and this controls the
* definition of png_alloc_size_t, below. This maximum value of size_t limits
* but does not control the maximum allocations the library makes - there is
* direct application control of this through png_set_user_limits().
*/
#ifndef PNG_SMALL_SIZE_T
/* Compiler specific tests for systems where size_t is known to be less than
* 32 bits (some of these systems may no longer work because of the lack of
* 'far' support; see above.)
*/
# if (defined(__TURBOC__) && !defined(__FLAT__)) ||\
(defined(_MSC_VER) && defined(MAXSEG_64K))
# define PNG_SMALL_SIZE_T
# endif
#endif
/* png_alloc_size_t is guaranteed to be no smaller than size_t, and no smaller
* than png_uint_32. Casts from size_t or png_uint_32 to png_alloc_size_t are
* not necessary; in fact, it is recommended not to use them at all, so that
* the compiler can complain when something turns out to be problematic.
*
* Casts in the other direction (from png_alloc_size_t to size_t or
* png_uint_32) should be explicitly applied; however, we do not expect to
* encounter practical situations that require such conversions.
*
* PNG_SMALL_SIZE_T must be defined if the maximum value of size_t is less than
* 4294967295 - i.e. less than the maximum value of png_uint_32.
*/
#ifdef PNG_SMALL_SIZE_T
typedef png_uint_32 png_alloc_size_t;
#else
typedef size_t png_alloc_size_t;
#endif
/* Prior to 1.6.0 libpng offered limited support for Microsoft C compiler
* implementations of Intel CPU specific support of user-mode segmented address
* spaces, where 16-bit pointers address more than 65536 bytes of memory using
* separate 'segment' registers. The implementation requires two different
* types of pointer (only one of which includes the segment value.)
*
* If required this support is available in version 1.2 of libpng and may be
* available in versions through 1.5, although the correctness of the code has
* not been verified recently.
*/
/* Typedef for floating-point numbers that are converted to fixed-point with a
* multiple of 100,000, e.g., gamma
*/
typedef png_int_32 png_fixed_point;
/* Add typedefs for pointers */
typedef void * png_voidp;
typedef const void * png_const_voidp;
typedef png_byte * png_bytep;
typedef const png_byte * png_const_bytep;
typedef png_uint_32 * png_uint_32p;
typedef const png_uint_32 * png_const_uint_32p;
typedef png_int_32 * png_int_32p;
typedef const png_int_32 * png_const_int_32p;
typedef png_uint_16 * png_uint_16p;
typedef const png_uint_16 * png_const_uint_16p;
typedef png_int_16 * png_int_16p;
typedef const png_int_16 * png_const_int_16p;
typedef char * png_charp;
typedef const char * png_const_charp;
typedef png_fixed_point * png_fixed_point_p;
typedef const png_fixed_point * png_const_fixed_point_p;
typedef size_t * png_size_tp;
typedef const size_t * png_const_size_tp;
#ifdef PNG_STDIO_SUPPORTED
typedef FILE * png_FILE_p;
#endif
#ifdef PNG_FLOATING_POINT_SUPPORTED
typedef double * png_doublep;
typedef const double * png_const_doublep;
#endif
/* Pointers to pointers; i.e. arrays */
typedef png_byte * * png_bytepp;
typedef png_uint_32 * * png_uint_32pp;
typedef png_int_32 * * png_int_32pp;
typedef png_uint_16 * * png_uint_16pp;
typedef png_int_16 * * png_int_16pp;
typedef const char * * png_const_charpp;
typedef char * * png_charpp;
typedef png_fixed_point * * png_fixed_point_pp;
#ifdef PNG_FLOATING_POINT_SUPPORTED
typedef double * * png_doublepp;
#endif
/* Pointers to pointers to pointers; i.e., pointer to array */
typedef char * * * png_charppp;
#endif /* PNG_BUILDING_SYMBOL_TABLE */
#endif /* PNGCONF_H */

153
hecl/extern/libpng/pngdebug.h vendored Normal file
View File

@ -0,0 +1,153 @@
/* pngdebug.h - Debugging macros for libpng, also used in pngtest.c
*
* Copyright (c) 2018 Cosmin Truta
* Copyright (c) 1998-2002,2004,2006-2013 Glenn Randers-Pehrson
* Copyright (c) 1996-1997 Andreas Dilger
* Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc.
*
* This code is released under the libpng license.
* For conditions of distribution and use, see the disclaimer
* and license in png.h
*/
/* Define PNG_DEBUG at compile time for debugging information. Higher
* numbers for PNG_DEBUG mean more debugging information. This has
* only been added since version 0.95 so it is not implemented throughout
* libpng yet, but more support will be added as needed.
*
* png_debug[1-2]?(level, message ,arg{0-2})
* Expands to a statement (either a simple expression or a compound
* do..while(0) statement) that outputs a message with parameter
* substitution if PNG_DEBUG is defined to 2 or more. If PNG_DEBUG
* is undefined, 0 or 1 every png_debug expands to a simple expression
* (actually ((void)0)).
*
* level: level of detail of message, starting at 0. A level 'n'
* message is preceded by 'n' 3-space indentations (not implemented
* on Microsoft compilers unless PNG_DEBUG_FILE is also
* defined, to allow debug DLL compilation with no standard IO).
* message: a printf(3) style text string. A trailing '\n' is added
* to the message.
* arg: 0 to 2 arguments for printf(3) style substitution in message.
*/
#ifndef PNGDEBUG_H
#define PNGDEBUG_H
/* These settings control the formatting of messages in png.c and pngerror.c */
/* Moved to pngdebug.h at 1.5.0 */
# ifndef PNG_LITERAL_SHARP
# define PNG_LITERAL_SHARP 0x23
# endif
# ifndef PNG_LITERAL_LEFT_SQUARE_BRACKET
# define PNG_LITERAL_LEFT_SQUARE_BRACKET 0x5b
# endif
# ifndef PNG_LITERAL_RIGHT_SQUARE_BRACKET
# define PNG_LITERAL_RIGHT_SQUARE_BRACKET 0x5d
# endif
# ifndef PNG_STRING_NEWLINE
# define PNG_STRING_NEWLINE "\n"
# endif
#ifdef PNG_DEBUG
# if (PNG_DEBUG > 0)
# if !defined(PNG_DEBUG_FILE) && defined(_MSC_VER)
# include <crtdbg.h>
# if (PNG_DEBUG > 1)
# ifndef _DEBUG
# define _DEBUG
# endif
# ifndef png_debug
# define png_debug(l,m) _RPT0(_CRT_WARN,m PNG_STRING_NEWLINE)
# endif
# ifndef png_debug1
# define png_debug1(l,m,p1) _RPT1(_CRT_WARN,m PNG_STRING_NEWLINE,p1)
# endif
# ifndef png_debug2
# define png_debug2(l,m,p1,p2) \
_RPT2(_CRT_WARN,m PNG_STRING_NEWLINE,p1,p2)
# endif
# endif
# else /* PNG_DEBUG_FILE || !_MSC_VER */
# ifndef PNG_STDIO_SUPPORTED
# include <stdio.h> /* not included yet */
# endif
# ifndef PNG_DEBUG_FILE
# define PNG_DEBUG_FILE stderr
# endif /* PNG_DEBUG_FILE */
# if (PNG_DEBUG > 1)
# ifdef __STDC__
# ifndef png_debug
# define png_debug(l,m) \
do { \
int num_tabs=l; \
fprintf(PNG_DEBUG_FILE,"%s" m PNG_STRING_NEWLINE,(num_tabs==1 ? " " : \
(num_tabs==2 ? " " : (num_tabs>2 ? " " : "")))); \
} while (0)
# endif
# ifndef png_debug1
# define png_debug1(l,m,p1) \
do { \
int num_tabs=l; \
fprintf(PNG_DEBUG_FILE,"%s" m PNG_STRING_NEWLINE,(num_tabs==1 ? " " : \
(num_tabs==2 ? " " : (num_tabs>2 ? " " : ""))),p1); \
} while (0)
# endif
# ifndef png_debug2
# define png_debug2(l,m,p1,p2) \
do { \
int num_tabs=l; \
fprintf(PNG_DEBUG_FILE,"%s" m PNG_STRING_NEWLINE,(num_tabs==1 ? " " : \
(num_tabs==2 ? " " : (num_tabs>2 ? " " : ""))),p1,p2);\
} while (0)
# endif
# else /* __STDC __ */
# ifndef png_debug
# define png_debug(l,m) \
do { \
int num_tabs=l; \
char format[256]; \
snprintf(format,256,"%s%s%s",(num_tabs==1 ? "\t" : \
(num_tabs==2 ? "\t\t":(num_tabs>2 ? "\t\t\t":""))), \
m,PNG_STRING_NEWLINE); \
fprintf(PNG_DEBUG_FILE,format); \
} while (0)
# endif
# ifndef png_debug1
# define png_debug1(l,m,p1) \
do { \
int num_tabs=l; \
char format[256]; \
snprintf(format,256,"%s%s%s",(num_tabs==1 ? "\t" : \
(num_tabs==2 ? "\t\t":(num_tabs>2 ? "\t\t\t":""))), \
m,PNG_STRING_NEWLINE); \
fprintf(PNG_DEBUG_FILE,format,p1); \
} while (0)
# endif
# ifndef png_debug2
# define png_debug2(l,m,p1,p2) \
do { \
int num_tabs=l; \
char format[256]; \
snprintf(format,256,"%s%s%s",(num_tabs==1 ? "\t" : \
(num_tabs==2 ? "\t\t":(num_tabs>2 ? "\t\t\t":""))), \
m,PNG_STRING_NEWLINE); \
fprintf(PNG_DEBUG_FILE,format,p1,p2); \
} while (0)
# endif
# endif /* __STDC __ */
# endif /* (PNG_DEBUG > 1) */
# endif /* _MSC_VER */
# endif /* (PNG_DEBUG > 0) */
#endif /* PNG_DEBUG */
#ifndef png_debug
# define png_debug(l, m) ((void)0)
#endif
#ifndef png_debug1
# define png_debug1(l, m, p1) ((void)0)
#endif
#ifndef png_debug2
# define png_debug2(l, m, p1, p2) ((void)0)
#endif
#endif /* PNGDEBUG_H */

963
hecl/extern/libpng/pngerror.c vendored Normal file
View File

@ -0,0 +1,963 @@
/* pngerror.c - stub functions for i/o and memory allocation
*
* Copyright (c) 2018 Cosmin Truta
* Copyright (c) 1998-2002,2004,2006-2017 Glenn Randers-Pehrson
* Copyright (c) 1996-1997 Andreas Dilger
* Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc.
*
* This code is released under the libpng license.
* For conditions of distribution and use, see the disclaimer
* and license in png.h
*
* This file provides a location for all error handling. Users who
* need special error handling are expected to write replacement functions
* and use png_set_error_fn() to use those functions. See the instructions
* at each function.
*/
#include "pngpriv.h"
#if defined(PNG_READ_SUPPORTED) || defined(PNG_WRITE_SUPPORTED)
static PNG_FUNCTION(void, png_default_error,PNGARG((png_const_structrp png_ptr,
png_const_charp error_message)),PNG_NORETURN);
#ifdef PNG_WARNINGS_SUPPORTED
static void /* PRIVATE */
png_default_warning PNGARG((png_const_structrp png_ptr,
png_const_charp warning_message));
#endif /* WARNINGS */
/* This function is called whenever there is a fatal error. This function
* should not be changed. If there is a need to handle errors differently,
* you should supply a replacement error function and use png_set_error_fn()
* to replace the error function at run-time.
*/
#ifdef PNG_ERROR_TEXT_SUPPORTED
PNG_FUNCTION(void,PNGAPI
png_error,(png_const_structrp png_ptr, png_const_charp error_message),
PNG_NORETURN)
{
#ifdef PNG_ERROR_NUMBERS_SUPPORTED
char msg[16];
if (png_ptr != NULL)
{
if ((png_ptr->flags &
(PNG_FLAG_STRIP_ERROR_NUMBERS|PNG_FLAG_STRIP_ERROR_TEXT)) != 0)
{
if (*error_message == PNG_LITERAL_SHARP)
{
/* Strip "#nnnn " from beginning of error message. */
int offset;
for (offset = 1; offset<15; offset++)
if (error_message[offset] == ' ')
break;
if ((png_ptr->flags & PNG_FLAG_STRIP_ERROR_TEXT) != 0)
{
int i;
for (i = 0; i < offset - 1; i++)
msg[i] = error_message[i + 1];
msg[i - 1] = '\0';
error_message = msg;
}
else
error_message += offset;
}
else
{
if ((png_ptr->flags & PNG_FLAG_STRIP_ERROR_TEXT) != 0)
{
msg[0] = '0';
msg[1] = '\0';
error_message = msg;
}
}
}
}
#endif
if (png_ptr != NULL && png_ptr->error_fn != NULL)
(*(png_ptr->error_fn))(png_constcast(png_structrp,png_ptr),
error_message);
/* If the custom handler doesn't exist, or if it returns,
use the default handler, which will not return. */
png_default_error(png_ptr, error_message);
}
#else
PNG_FUNCTION(void,PNGAPI
png_err,(png_const_structrp png_ptr),PNG_NORETURN)
{
/* Prior to 1.5.2 the error_fn received a NULL pointer, expressed
* erroneously as '\0', instead of the empty string "". This was
* apparently an error, introduced in libpng-1.2.20, and png_default_error
* will crash in this case.
*/
if (png_ptr != NULL && png_ptr->error_fn != NULL)
(*(png_ptr->error_fn))(png_constcast(png_structrp,png_ptr), "");
/* If the custom handler doesn't exist, or if it returns,
use the default handler, which will not return. */
png_default_error(png_ptr, "");
}
#endif /* ERROR_TEXT */
/* Utility to safely appends strings to a buffer. This never errors out so
* error checking is not required in the caller.
*/
size_t
png_safecat(png_charp buffer, size_t bufsize, size_t pos,
png_const_charp string)
{
if (buffer != NULL && pos < bufsize)
{
if (string != NULL)
while (*string != '\0' && pos < bufsize-1)
buffer[pos++] = *string++;
buffer[pos] = '\0';
}
return pos;
}
#if defined(PNG_WARNINGS_SUPPORTED) || defined(PNG_TIME_RFC1123_SUPPORTED)
/* Utility to dump an unsigned value into a buffer, given a start pointer and
* and end pointer (which should point just *beyond* the end of the buffer!)
* Returns the pointer to the start of the formatted string.
*/
png_charp
png_format_number(png_const_charp start, png_charp end, int format,
png_alloc_size_t number)
{
int count = 0; /* number of digits output */
int mincount = 1; /* minimum number required */
int output = 0; /* digit output (for the fixed point format) */
*--end = '\0';
/* This is written so that the loop always runs at least once, even with
* number zero.
*/
while (end > start && (number != 0 || count < mincount))
{
static const char digits[] = "0123456789ABCDEF";
switch (format)
{
case PNG_NUMBER_FORMAT_fixed:
/* Needs five digits (the fraction) */
mincount = 5;
if (output != 0 || number % 10 != 0)
{
*--end = digits[number % 10];
output = 1;
}
number /= 10;
break;
case PNG_NUMBER_FORMAT_02u:
/* Expects at least 2 digits. */
mincount = 2;
/* FALLTHROUGH */
case PNG_NUMBER_FORMAT_u:
*--end = digits[number % 10];
number /= 10;
break;
case PNG_NUMBER_FORMAT_02x:
/* This format expects at least two digits */
mincount = 2;
/* FALLTHROUGH */
case PNG_NUMBER_FORMAT_x:
*--end = digits[number & 0xf];
number >>= 4;
break;
default: /* an error */
number = 0;
break;
}
/* Keep track of the number of digits added */
++count;
/* Float a fixed number here: */
if ((format == PNG_NUMBER_FORMAT_fixed) && (count == 5) && (end > start))
{
/* End of the fraction, but maybe nothing was output? In that case
* drop the decimal point. If the number is a true zero handle that
* here.
*/
if (output != 0)
*--end = '.';
else if (number == 0) /* and !output */
*--end = '0';
}
}
return end;
}
#endif
#ifdef PNG_WARNINGS_SUPPORTED
/* This function is called whenever there is a non-fatal error. This function
* should not be changed. If there is a need to handle warnings differently,
* you should supply a replacement warning function and use
* png_set_error_fn() to replace the warning function at run-time.
*/
void PNGAPI
png_warning(png_const_structrp png_ptr, png_const_charp warning_message)
{
int offset = 0;
if (png_ptr != NULL)
{
#ifdef PNG_ERROR_NUMBERS_SUPPORTED
if ((png_ptr->flags &
(PNG_FLAG_STRIP_ERROR_NUMBERS|PNG_FLAG_STRIP_ERROR_TEXT)) != 0)
#endif
{
if (*warning_message == PNG_LITERAL_SHARP)
{
for (offset = 1; offset < 15; offset++)
if (warning_message[offset] == ' ')
break;
}
}
}
if (png_ptr != NULL && png_ptr->warning_fn != NULL)
(*(png_ptr->warning_fn))(png_constcast(png_structrp,png_ptr),
warning_message + offset);
else
png_default_warning(png_ptr, warning_message + offset);
}
/* These functions support 'formatted' warning messages with up to
* PNG_WARNING_PARAMETER_COUNT parameters. In the format string the parameter
* is introduced by @<number>, where 'number' starts at 1. This follows the
* standard established by X/Open for internationalizable error messages.
*/
void
png_warning_parameter(png_warning_parameters p, int number,
png_const_charp string)
{
if (number > 0 && number <= PNG_WARNING_PARAMETER_COUNT)
(void)png_safecat(p[number-1], (sizeof p[number-1]), 0, string);
}
void
png_warning_parameter_unsigned(png_warning_parameters p, int number, int format,
png_alloc_size_t value)
{
char buffer[PNG_NUMBER_BUFFER_SIZE];
png_warning_parameter(p, number, PNG_FORMAT_NUMBER(buffer, format, value));
}
void
png_warning_parameter_signed(png_warning_parameters p, int number, int format,
png_int_32 value)
{
png_alloc_size_t u;
png_charp str;
char buffer[PNG_NUMBER_BUFFER_SIZE];
/* Avoid overflow by doing the negate in a png_alloc_size_t: */
u = (png_alloc_size_t)value;
if (value < 0)
u = ~u + 1;
str = PNG_FORMAT_NUMBER(buffer, format, u);
if (value < 0 && str > buffer)
*--str = '-';
png_warning_parameter(p, number, str);
}
void
png_formatted_warning(png_const_structrp png_ptr, png_warning_parameters p,
png_const_charp message)
{
/* The internal buffer is just 192 bytes - enough for all our messages,
* overflow doesn't happen because this code checks! If someone figures
* out how to send us a message longer than 192 bytes, all that will
* happen is that the message will be truncated appropriately.
*/
size_t i = 0; /* Index in the msg[] buffer: */
char msg[192];
/* Each iteration through the following loop writes at most one character
* to msg[i++] then returns here to validate that there is still space for
* the trailing '\0'. It may (in the case of a parameter) read more than
* one character from message[]; it must check for '\0' and continue to the
* test if it finds the end of string.
*/
while (i<(sizeof msg)-1 && *message != '\0')
{
/* '@' at end of string is now just printed (previously it was skipped);
* it is an error in the calling code to terminate the string with @.
*/
if (p != NULL && *message == '@' && message[1] != '\0')
{
int parameter_char = *++message; /* Consume the '@' */
static const char valid_parameters[] = "123456789";
int parameter = 0;
/* Search for the parameter digit, the index in the string is the
* parameter to use.
*/
while (valid_parameters[parameter] != parameter_char &&
valid_parameters[parameter] != '\0')
++parameter;
/* If the parameter digit is out of range it will just get printed. */
if (parameter < PNG_WARNING_PARAMETER_COUNT)
{
/* Append this parameter */
png_const_charp parm = p[parameter];
png_const_charp pend = p[parameter] + (sizeof p[parameter]);
/* No need to copy the trailing '\0' here, but there is no guarantee
* that parm[] has been initialized, so there is no guarantee of a
* trailing '\0':
*/
while (i<(sizeof msg)-1 && *parm != '\0' && parm < pend)
msg[i++] = *parm++;
/* Consume the parameter digit too: */
++message;
continue;
}
/* else not a parameter and there is a character after the @ sign; just
* copy that. This is known not to be '\0' because of the test above.
*/
}
/* At this point *message can't be '\0', even in the bad parameter case
* above where there is a lone '@' at the end of the message string.
*/
msg[i++] = *message++;
}
/* i is always less than (sizeof msg), so: */
msg[i] = '\0';
/* And this is the formatted message. It may be larger than
* PNG_MAX_ERROR_TEXT, but that is only used for 'chunk' errors and these
* are not (currently) formatted.
*/
png_warning(png_ptr, msg);
}
#endif /* WARNINGS */
#ifdef PNG_BENIGN_ERRORS_SUPPORTED
void PNGAPI
png_benign_error(png_const_structrp png_ptr, png_const_charp error_message)
{
if ((png_ptr->flags & PNG_FLAG_BENIGN_ERRORS_WARN) != 0)
{
# ifdef PNG_READ_SUPPORTED
if ((png_ptr->mode & PNG_IS_READ_STRUCT) != 0 &&
png_ptr->chunk_name != 0)
png_chunk_warning(png_ptr, error_message);
else
# endif
png_warning(png_ptr, error_message);
}
else
{
# ifdef PNG_READ_SUPPORTED
if ((png_ptr->mode & PNG_IS_READ_STRUCT) != 0 &&
png_ptr->chunk_name != 0)
png_chunk_error(png_ptr, error_message);
else
# endif
png_error(png_ptr, error_message);
}
# ifndef PNG_ERROR_TEXT_SUPPORTED
PNG_UNUSED(error_message)
# endif
}
void /* PRIVATE */
png_app_warning(png_const_structrp png_ptr, png_const_charp error_message)
{
if ((png_ptr->flags & PNG_FLAG_APP_WARNINGS_WARN) != 0)
png_warning(png_ptr, error_message);
else
png_error(png_ptr, error_message);
# ifndef PNG_ERROR_TEXT_SUPPORTED
PNG_UNUSED(error_message)
# endif
}
void /* PRIVATE */
png_app_error(png_const_structrp png_ptr, png_const_charp error_message)
{
if ((png_ptr->flags & PNG_FLAG_APP_ERRORS_WARN) != 0)
png_warning(png_ptr, error_message);
else
png_error(png_ptr, error_message);
# ifndef PNG_ERROR_TEXT_SUPPORTED
PNG_UNUSED(error_message)
# endif
}
#endif /* BENIGN_ERRORS */
#define PNG_MAX_ERROR_TEXT 196 /* Currently limited by profile_error in png.c */
#if defined(PNG_WARNINGS_SUPPORTED) || \
(defined(PNG_READ_SUPPORTED) && defined(PNG_ERROR_TEXT_SUPPORTED))
/* These utilities are used internally to build an error message that relates
* to the current chunk. The chunk name comes from png_ptr->chunk_name,
* which is used to prefix the message. The message is limited in length
* to 63 bytes. The name characters are output as hex digits wrapped in []
* if the character is invalid.
*/
#define isnonalpha(c) ((c) < 65 || (c) > 122 || ((c) > 90 && (c) < 97))
static const char png_digit[16] = {
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
'A', 'B', 'C', 'D', 'E', 'F'
};
static void /* PRIVATE */
png_format_buffer(png_const_structrp png_ptr, png_charp buffer, png_const_charp
error_message)
{
png_uint_32 chunk_name = png_ptr->chunk_name;
int iout = 0, ishift = 24;
while (ishift >= 0)
{
int c = (int)(chunk_name >> ishift) & 0xff;
ishift -= 8;
if (isnonalpha(c) != 0)
{
buffer[iout++] = PNG_LITERAL_LEFT_SQUARE_BRACKET;
buffer[iout++] = png_digit[(c & 0xf0) >> 4];
buffer[iout++] = png_digit[c & 0x0f];
buffer[iout++] = PNG_LITERAL_RIGHT_SQUARE_BRACKET;
}
else
{
buffer[iout++] = (char)c;
}
}
if (error_message == NULL)
buffer[iout] = '\0';
else
{
int iin = 0;
buffer[iout++] = ':';
buffer[iout++] = ' ';
while (iin < PNG_MAX_ERROR_TEXT-1 && error_message[iin] != '\0')
buffer[iout++] = error_message[iin++];
/* iin < PNG_MAX_ERROR_TEXT, so the following is safe: */
buffer[iout] = '\0';
}
}
#endif /* WARNINGS || ERROR_TEXT */
#if defined(PNG_READ_SUPPORTED) && defined(PNG_ERROR_TEXT_SUPPORTED)
PNG_FUNCTION(void,PNGAPI
png_chunk_error,(png_const_structrp png_ptr, png_const_charp error_message),
PNG_NORETURN)
{
char msg[18+PNG_MAX_ERROR_TEXT];
if (png_ptr == NULL)
png_error(png_ptr, error_message);
else
{
png_format_buffer(png_ptr, msg, error_message);
png_error(png_ptr, msg);
}
}
#endif /* READ && ERROR_TEXT */
#ifdef PNG_WARNINGS_SUPPORTED
void PNGAPI
png_chunk_warning(png_const_structrp png_ptr, png_const_charp warning_message)
{
char msg[18+PNG_MAX_ERROR_TEXT];
if (png_ptr == NULL)
png_warning(png_ptr, warning_message);
else
{
png_format_buffer(png_ptr, msg, warning_message);
png_warning(png_ptr, msg);
}
}
#endif /* WARNINGS */
#ifdef PNG_READ_SUPPORTED
#ifdef PNG_BENIGN_ERRORS_SUPPORTED
void PNGAPI
png_chunk_benign_error(png_const_structrp png_ptr, png_const_charp
error_message)
{
if ((png_ptr->flags & PNG_FLAG_BENIGN_ERRORS_WARN) != 0)
png_chunk_warning(png_ptr, error_message);
else
png_chunk_error(png_ptr, error_message);
# ifndef PNG_ERROR_TEXT_SUPPORTED
PNG_UNUSED(error_message)
# endif
}
#endif
#endif /* READ */
void /* PRIVATE */
png_chunk_report(png_const_structrp png_ptr, png_const_charp message, int error)
{
# ifndef PNG_WARNINGS_SUPPORTED
PNG_UNUSED(message)
# endif
/* This is always supported, but for just read or just write it
* unconditionally does the right thing.
*/
# if defined(PNG_READ_SUPPORTED) && defined(PNG_WRITE_SUPPORTED)
if ((png_ptr->mode & PNG_IS_READ_STRUCT) != 0)
# endif
# ifdef PNG_READ_SUPPORTED
{
if (error < PNG_CHUNK_ERROR)
png_chunk_warning(png_ptr, message);
else
png_chunk_benign_error(png_ptr, message);
}
# endif
# if defined(PNG_READ_SUPPORTED) && defined(PNG_WRITE_SUPPORTED)
else if ((png_ptr->mode & PNG_IS_READ_STRUCT) == 0)
# endif
# ifdef PNG_WRITE_SUPPORTED
{
if (error < PNG_CHUNK_WRITE_ERROR)
png_app_warning(png_ptr, message);
else
png_app_error(png_ptr, message);
}
# endif
}
#ifdef PNG_ERROR_TEXT_SUPPORTED
#ifdef PNG_FLOATING_POINT_SUPPORTED
PNG_FUNCTION(void,
png_fixed_error,(png_const_structrp png_ptr, png_const_charp name),PNG_NORETURN)
{
# define fixed_message "fixed point overflow in "
# define fixed_message_ln ((sizeof fixed_message)-1)
unsigned int iin;
char msg[fixed_message_ln+PNG_MAX_ERROR_TEXT];
memcpy(msg, fixed_message, fixed_message_ln);
iin = 0;
if (name != NULL)
while (iin < (PNG_MAX_ERROR_TEXT-1) && name[iin] != 0)
{
msg[fixed_message_ln + iin] = name[iin];
++iin;
}
msg[fixed_message_ln + iin] = 0;
png_error(png_ptr, msg);
}
#endif
#endif
#ifdef PNG_SETJMP_SUPPORTED
/* This API only exists if ANSI-C style error handling is used,
* otherwise it is necessary for png_default_error to be overridden.
*/
jmp_buf* PNGAPI
png_set_longjmp_fn(png_structrp png_ptr, png_longjmp_ptr longjmp_fn,
size_t jmp_buf_size)
{
/* From libpng 1.6.0 the app gets one chance to set a 'jmpbuf_size' value
* and it must not change after that. Libpng doesn't care how big the
* buffer is, just that it doesn't change.
*
* If the buffer size is no *larger* than the size of jmp_buf when libpng is
* compiled a built in jmp_buf is returned; this preserves the pre-1.6.0
* semantics that this call will not fail. If the size is larger, however,
* the buffer is allocated and this may fail, causing the function to return
* NULL.
*/
if (png_ptr == NULL)
return NULL;
if (png_ptr->jmp_buf_ptr == NULL)
{
png_ptr->jmp_buf_size = 0; /* not allocated */
if (jmp_buf_size <= (sizeof png_ptr->jmp_buf_local))
png_ptr->jmp_buf_ptr = &png_ptr->jmp_buf_local;
else
{
png_ptr->jmp_buf_ptr = png_voidcast(jmp_buf *,
png_malloc_warn(png_ptr, jmp_buf_size));
if (png_ptr->jmp_buf_ptr == NULL)
return NULL; /* new NULL return on OOM */
png_ptr->jmp_buf_size = jmp_buf_size;
}
}
else /* Already allocated: check the size */
{
size_t size = png_ptr->jmp_buf_size;
if (size == 0)
{
size = (sizeof png_ptr->jmp_buf_local);
if (png_ptr->jmp_buf_ptr != &png_ptr->jmp_buf_local)
{
/* This is an internal error in libpng: somehow we have been left
* with a stack allocated jmp_buf when the application regained
* control. It's always possible to fix this up, but for the moment
* this is a png_error because that makes it easy to detect.
*/
png_error(png_ptr, "Libpng jmp_buf still allocated");
/* png_ptr->jmp_buf_ptr = &png_ptr->jmp_buf_local; */
}
}
if (size != jmp_buf_size)
{
png_warning(png_ptr, "Application jmp_buf size changed");
return NULL; /* caller will probably crash: no choice here */
}
}
/* Finally fill in the function, now we have a satisfactory buffer. It is
* valid to change the function on every call.
*/
png_ptr->longjmp_fn = longjmp_fn;
return png_ptr->jmp_buf_ptr;
}
void /* PRIVATE */
png_free_jmpbuf(png_structrp png_ptr)
{
if (png_ptr != NULL)
{
jmp_buf *jb = png_ptr->jmp_buf_ptr;
/* A size of 0 is used to indicate a local, stack, allocation of the
* pointer; used here and in png.c
*/
if (jb != NULL && png_ptr->jmp_buf_size > 0)
{
/* This stuff is so that a failure to free the error control structure
* does not leave libpng in a state with no valid error handling: the
* free always succeeds, if there is an error it gets ignored.
*/
if (jb != &png_ptr->jmp_buf_local)
{
/* Make an internal, libpng, jmp_buf to return here */
jmp_buf free_jmp_buf;
if (!setjmp(free_jmp_buf))
{
png_ptr->jmp_buf_ptr = &free_jmp_buf; /* come back here */
png_ptr->jmp_buf_size = 0; /* stack allocation */
png_ptr->longjmp_fn = longjmp;
png_free(png_ptr, jb); /* Return to setjmp on error */
}
}
}
/* *Always* cancel everything out: */
png_ptr->jmp_buf_size = 0;
png_ptr->jmp_buf_ptr = NULL;
png_ptr->longjmp_fn = 0;
}
}
#endif
/* This is the default error handling function. Note that replacements for
* this function MUST NOT RETURN, or the program will likely crash. This
* function is used by default, or if the program supplies NULL for the
* error function pointer in png_set_error_fn().
*/
static PNG_FUNCTION(void /* PRIVATE */,
png_default_error,(png_const_structrp png_ptr, png_const_charp error_message),
PNG_NORETURN)
{
#ifdef PNG_CONSOLE_IO_SUPPORTED
#ifdef PNG_ERROR_NUMBERS_SUPPORTED
/* Check on NULL only added in 1.5.4 */
if (error_message != NULL && *error_message == PNG_LITERAL_SHARP)
{
/* Strip "#nnnn " from beginning of error message. */
int offset;
char error_number[16];
for (offset = 0; offset<15; offset++)
{
error_number[offset] = error_message[offset + 1];
if (error_message[offset] == ' ')
break;
}
if ((offset > 1) && (offset < 15))
{
error_number[offset - 1] = '\0';
fprintf(stderr, "libpng error no. %s: %s",
error_number, error_message + offset + 1);
fprintf(stderr, PNG_STRING_NEWLINE);
}
else
{
fprintf(stderr, "libpng error: %s, offset=%d",
error_message, offset);
fprintf(stderr, PNG_STRING_NEWLINE);
}
}
else
#endif
{
fprintf(stderr, "libpng error: %s", error_message ? error_message :
"undefined");
fprintf(stderr, PNG_STRING_NEWLINE);
}
#else
PNG_UNUSED(error_message) /* Make compiler happy */
#endif
png_longjmp(png_ptr, 1);
}
PNG_FUNCTION(void,PNGAPI
png_longjmp,(png_const_structrp png_ptr, int val),PNG_NORETURN)
{
#ifdef PNG_SETJMP_SUPPORTED
if (png_ptr != NULL && png_ptr->longjmp_fn != NULL &&
png_ptr->jmp_buf_ptr != NULL)
png_ptr->longjmp_fn(*png_ptr->jmp_buf_ptr, val);
#else
PNG_UNUSED(png_ptr)
PNG_UNUSED(val)
#endif
/* If control reaches this point, png_longjmp() must not return. The only
* choice is to terminate the whole process (or maybe the thread); to do
* this the ANSI-C abort() function is used unless a different method is
* implemented by overriding the default configuration setting for
* PNG_ABORT().
*/
PNG_ABORT();
}
#ifdef PNG_WARNINGS_SUPPORTED
/* This function is called when there is a warning, but the library thinks
* it can continue anyway. Replacement functions don't have to do anything
* here if you don't want them to. In the default configuration, png_ptr is
* not used, but it is passed in case it may be useful.
*/
static void /* PRIVATE */
png_default_warning(png_const_structrp png_ptr, png_const_charp warning_message)
{
#ifdef PNG_CONSOLE_IO_SUPPORTED
# ifdef PNG_ERROR_NUMBERS_SUPPORTED
if (*warning_message == PNG_LITERAL_SHARP)
{
int offset;
char warning_number[16];
for (offset = 0; offset < 15; offset++)
{
warning_number[offset] = warning_message[offset + 1];
if (warning_message[offset] == ' ')
break;
}
if ((offset > 1) && (offset < 15))
{
warning_number[offset + 1] = '\0';
fprintf(stderr, "libpng warning no. %s: %s",
warning_number, warning_message + offset);
fprintf(stderr, PNG_STRING_NEWLINE);
}
else
{
fprintf(stderr, "libpng warning: %s",
warning_message);
fprintf(stderr, PNG_STRING_NEWLINE);
}
}
else
# endif
{
fprintf(stderr, "libpng warning: %s", warning_message);
fprintf(stderr, PNG_STRING_NEWLINE);
}
#else
PNG_UNUSED(warning_message) /* Make compiler happy */
#endif
PNG_UNUSED(png_ptr) /* Make compiler happy */
}
#endif /* WARNINGS */
/* This function is called when the application wants to use another method
* of handling errors and warnings. Note that the error function MUST NOT
* return to the calling routine or serious problems will occur. The return
* method used in the default routine calls longjmp(png_ptr->jmp_buf_ptr, 1)
*/
void PNGAPI
png_set_error_fn(png_structrp png_ptr, png_voidp error_ptr,
png_error_ptr error_fn, png_error_ptr warning_fn)
{
if (png_ptr == NULL)
return;
png_ptr->error_ptr = error_ptr;
png_ptr->error_fn = error_fn;
#ifdef PNG_WARNINGS_SUPPORTED
png_ptr->warning_fn = warning_fn;
#else
PNG_UNUSED(warning_fn)
#endif
}
/* This function returns a pointer to the error_ptr associated with the user
* functions. The application should free any memory associated with this
* pointer before png_write_destroy and png_read_destroy are called.
*/
png_voidp PNGAPI
png_get_error_ptr(png_const_structrp png_ptr)
{
if (png_ptr == NULL)
return NULL;
return ((png_voidp)png_ptr->error_ptr);
}
#ifdef PNG_ERROR_NUMBERS_SUPPORTED
void PNGAPI
png_set_strip_error_numbers(png_structrp png_ptr, png_uint_32 strip_mode)
{
if (png_ptr != NULL)
{
png_ptr->flags &=
((~(PNG_FLAG_STRIP_ERROR_NUMBERS |
PNG_FLAG_STRIP_ERROR_TEXT))&strip_mode);
}
}
#endif
#if defined(PNG_SIMPLIFIED_READ_SUPPORTED) ||\
defined(PNG_SIMPLIFIED_WRITE_SUPPORTED)
/* Currently the above both depend on SETJMP_SUPPORTED, however it would be
* possible to implement without setjmp support just so long as there is some
* way to handle the error return here:
*/
PNG_FUNCTION(void /* PRIVATE */, (PNGCBAPI
png_safe_error),(png_structp png_nonconst_ptr, png_const_charp error_message),
PNG_NORETURN)
{
png_const_structrp png_ptr = png_nonconst_ptr;
png_imagep image = png_voidcast(png_imagep, png_ptr->error_ptr);
/* An error is always logged here, overwriting anything (typically a warning)
* that is already there:
*/
if (image != NULL)
{
png_safecat(image->message, (sizeof image->message), 0, error_message);
image->warning_or_error |= PNG_IMAGE_ERROR;
/* Retrieve the jmp_buf from within the png_control, making this work for
* C++ compilation too is pretty tricky: C++ wants a pointer to the first
* element of a jmp_buf, but C doesn't tell us the type of that.
*/
if (image->opaque != NULL && image->opaque->error_buf != NULL)
longjmp(png_control_jmp_buf(image->opaque), 1);
/* Missing longjmp buffer, the following is to help debugging: */
{
size_t pos = png_safecat(image->message, (sizeof image->message), 0,
"bad longjmp: ");
png_safecat(image->message, (sizeof image->message), pos,
error_message);
}
}
/* Here on an internal programming error. */
abort();
}
#ifdef PNG_WARNINGS_SUPPORTED
void /* PRIVATE */ PNGCBAPI
png_safe_warning(png_structp png_nonconst_ptr, png_const_charp warning_message)
{
png_const_structrp png_ptr = png_nonconst_ptr;
png_imagep image = png_voidcast(png_imagep, png_ptr->error_ptr);
/* A warning is only logged if there is no prior warning or error. */
if (image->warning_or_error == 0)
{
png_safecat(image->message, (sizeof image->message), 0, warning_message);
image->warning_or_error |= PNG_IMAGE_WARNING;
}
}
#endif
int /* PRIVATE */
png_safe_execute(png_imagep image_in, int (*function)(png_voidp), png_voidp arg)
{
volatile png_imagep image = image_in;
volatile int result;
volatile png_voidp saved_error_buf;
jmp_buf safe_jmpbuf;
/* Safely execute function(arg) with png_error returning to this function. */
saved_error_buf = image->opaque->error_buf;
result = setjmp(safe_jmpbuf) == 0;
if (result != 0)
{
image->opaque->error_buf = safe_jmpbuf;
result = function(arg);
}
image->opaque->error_buf = saved_error_buf;
/* And do the cleanup prior to any failure return. */
if (result == 0)
png_image_free(image);
return result;
}
#endif /* SIMPLIFIED READ || SIMPLIFIED_WRITE */
#endif /* READ || WRITE */

1249
hecl/extern/libpng/pngget.c vendored Normal file

File diff suppressed because it is too large Load Diff

267
hecl/extern/libpng/pnginfo.h vendored Normal file
View File

@ -0,0 +1,267 @@
/* pnginfo.h - header file for PNG reference library
*
* Copyright (c) 2018 Cosmin Truta
* Copyright (c) 1998-2002,2004,2006-2013,2018 Glenn Randers-Pehrson
* Copyright (c) 1996-1997 Andreas Dilger
* Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc.
*
* This code is released under the libpng license.
* For conditions of distribution and use, see the disclaimer
* and license in png.h
*/
/* png_info is a structure that holds the information in a PNG file so
* that the application can find out the characteristics of the image.
* If you are reading the file, this structure will tell you what is
* in the PNG file. If you are writing the file, fill in the information
* you want to put into the PNG file, using png_set_*() functions, then
* call png_write_info().
*
* The names chosen should be very close to the PNG specification, so
* consult that document for information about the meaning of each field.
*
* With libpng < 0.95, it was only possible to directly set and read the
* the values in the png_info_struct, which meant that the contents and
* order of the values had to remain fixed. With libpng 0.95 and later,
* however, there are now functions that abstract the contents of
* png_info_struct from the application, so this makes it easier to use
* libpng with dynamic libraries, and even makes it possible to use
* libraries that don't have all of the libpng ancillary chunk-handing
* functionality. In libpng-1.5.0 this was moved into a separate private
* file that is not visible to applications.
*
* The following members may have allocated storage attached that should be
* cleaned up before the structure is discarded: palette, trans, text,
* pcal_purpose, pcal_units, pcal_params, hist, iccp_name, iccp_profile,
* splt_palettes, scal_unit, row_pointers, and unknowns. By default, these
* are automatically freed when the info structure is deallocated, if they were
* allocated internally by libpng. This behavior can be changed by means
* of the png_data_freer() function.
*
* More allocation details: all the chunk-reading functions that
* change these members go through the corresponding png_set_*
* functions. A function to clear these members is available: see
* png_free_data(). The png_set_* functions do not depend on being
* able to point info structure members to any of the storage they are
* passed (they make their own copies), EXCEPT that the png_set_text
* functions use the same storage passed to them in the text_ptr or
* itxt_ptr structure argument, and the png_set_rows and png_set_unknowns
* functions do not make their own copies.
*/
#ifndef PNGINFO_H
#define PNGINFO_H
struct png_info_def
{
/* The following are necessary for every PNG file */
png_uint_32 width; /* width of image in pixels (from IHDR) */
png_uint_32 height; /* height of image in pixels (from IHDR) */
png_uint_32 valid; /* valid chunk data (see PNG_INFO_ below) */
size_t rowbytes; /* bytes needed to hold an untransformed row */
png_colorp palette; /* array of color values (valid & PNG_INFO_PLTE) */
png_uint_16 num_palette; /* number of color entries in "palette" (PLTE) */
png_uint_16 num_trans; /* number of transparent palette color (tRNS) */
png_byte bit_depth; /* 1, 2, 4, 8, or 16 bits/channel (from IHDR) */
png_byte color_type; /* see PNG_COLOR_TYPE_ below (from IHDR) */
/* The following three should have been named *_method not *_type */
png_byte compression_type; /* must be PNG_COMPRESSION_TYPE_BASE (IHDR) */
png_byte filter_type; /* must be PNG_FILTER_TYPE_BASE (from IHDR) */
png_byte interlace_type; /* One of PNG_INTERLACE_NONE, PNG_INTERLACE_ADAM7 */
/* The following are set by png_set_IHDR, called from the application on
* write, but the are never actually used by the write code.
*/
png_byte channels; /* number of data channels per pixel (1, 2, 3, 4) */
png_byte pixel_depth; /* number of bits per pixel */
png_byte spare_byte; /* to align the data, and for future use */
#ifdef PNG_READ_SUPPORTED
/* This is never set during write */
png_byte signature[8]; /* magic bytes read by libpng from start of file */
#endif
/* The rest of the data is optional. If you are reading, check the
* valid field to see if the information in these are valid. If you
* are writing, set the valid field to those chunks you want written,
* and initialize the appropriate fields below.
*/
#if defined(PNG_COLORSPACE_SUPPORTED) || defined(PNG_GAMMA_SUPPORTED)
/* png_colorspace only contains 'flags' if neither GAMMA or COLORSPACE are
* defined. When COLORSPACE is switched on all the colorspace-defining
* chunks should be enabled, when GAMMA is switched on all the gamma-defining
* chunks should be enabled. If this is not done it becomes possible to read
* inconsistent PNG files and assign a probably incorrect interpretation to
* the information. (In other words, by carefully choosing which chunks to
* recognize the system configuration can select an interpretation for PNG
* files containing ambiguous data and this will result in inconsistent
* behavior between different libpng builds!)
*/
png_colorspace colorspace;
#endif
#ifdef PNG_iCCP_SUPPORTED
/* iCCP chunk data. */
png_charp iccp_name; /* profile name */
png_bytep iccp_profile; /* International Color Consortium profile data */
png_uint_32 iccp_proflen; /* ICC profile data length */
#endif
#ifdef PNG_TEXT_SUPPORTED
/* The tEXt, and zTXt chunks contain human-readable textual data in
* uncompressed, compressed, and optionally compressed forms, respectively.
* The data in "text" is an array of pointers to uncompressed,
* null-terminated C strings. Each chunk has a keyword that describes the
* textual data contained in that chunk. Keywords are not required to be
* unique, and the text string may be empty. Any number of text chunks may
* be in an image.
*/
int num_text; /* number of comments read or comments to write */
int max_text; /* current size of text array */
png_textp text; /* array of comments read or comments to write */
#endif /* TEXT */
#ifdef PNG_tIME_SUPPORTED
/* The tIME chunk holds the last time the displayed image data was
* modified. See the png_time struct for the contents of this struct.
*/
png_time mod_time;
#endif
#ifdef PNG_sBIT_SUPPORTED
/* The sBIT chunk specifies the number of significant high-order bits
* in the pixel data. Values are in the range [1, bit_depth], and are
* only specified for the channels in the pixel data. The contents of
* the low-order bits is not specified. Data is valid if
* (valid & PNG_INFO_sBIT) is non-zero.
*/
png_color_8 sig_bit; /* significant bits in color channels */
#endif
#if defined(PNG_tRNS_SUPPORTED) || defined(PNG_READ_EXPAND_SUPPORTED) || \
defined(PNG_READ_BACKGROUND_SUPPORTED)
/* The tRNS chunk supplies transparency data for paletted images and
* other image types that don't need a full alpha channel. There are
* "num_trans" transparency values for a paletted image, stored in the
* same order as the palette colors, starting from index 0. Values
* for the data are in the range [0, 255], ranging from fully transparent
* to fully opaque, respectively. For non-paletted images, there is a
* single color specified that should be treated as fully transparent.
* Data is valid if (valid & PNG_INFO_tRNS) is non-zero.
*/
png_bytep trans_alpha; /* alpha values for paletted image */
png_color_16 trans_color; /* transparent color for non-palette image */
#endif
#if defined(PNG_bKGD_SUPPORTED) || defined(PNG_READ_BACKGROUND_SUPPORTED)
/* The bKGD chunk gives the suggested image background color if the
* display program does not have its own background color and the image
* is needs to composited onto a background before display. The colors
* in "background" are normally in the same color space/depth as the
* pixel data. Data is valid if (valid & PNG_INFO_bKGD) is non-zero.
*/
png_color_16 background;
#endif
#ifdef PNG_oFFs_SUPPORTED
/* The oFFs chunk gives the offset in "offset_unit_type" units rightwards
* and downwards from the top-left corner of the display, page, or other
* application-specific co-ordinate space. See the PNG_OFFSET_ defines
* below for the unit types. Valid if (valid & PNG_INFO_oFFs) non-zero.
*/
png_int_32 x_offset; /* x offset on page */
png_int_32 y_offset; /* y offset on page */
png_byte offset_unit_type; /* offset units type */
#endif
#ifdef PNG_pHYs_SUPPORTED
/* The pHYs chunk gives the physical pixel density of the image for
* display or printing in "phys_unit_type" units (see PNG_RESOLUTION_
* defines below). Data is valid if (valid & PNG_INFO_pHYs) is non-zero.
*/
png_uint_32 x_pixels_per_unit; /* horizontal pixel density */
png_uint_32 y_pixels_per_unit; /* vertical pixel density */
png_byte phys_unit_type; /* resolution type (see PNG_RESOLUTION_ below) */
#endif
#ifdef PNG_eXIf_SUPPORTED
int num_exif; /* Added at libpng-1.6.31 */
png_bytep exif;
# ifdef PNG_READ_eXIf_SUPPORTED
png_bytep eXIf_buf; /* Added at libpng-1.6.32 */
# endif
#endif
#ifdef PNG_hIST_SUPPORTED
/* The hIST chunk contains the relative frequency or importance of the
* various palette entries, so that a viewer can intelligently select a
* reduced-color palette, if required. Data is an array of "num_palette"
* values in the range [0,65535]. Data valid if (valid & PNG_INFO_hIST)
* is non-zero.
*/
png_uint_16p hist;
#endif
#ifdef PNG_pCAL_SUPPORTED
/* The pCAL chunk describes a transformation between the stored pixel
* values and original physical data values used to create the image.
* The integer range [0, 2^bit_depth - 1] maps to the floating-point
* range given by [pcal_X0, pcal_X1], and are further transformed by a
* (possibly non-linear) transformation function given by "pcal_type"
* and "pcal_params" into "pcal_units". Please see the PNG_EQUATION_
* defines below, and the PNG-Group's PNG extensions document for a
* complete description of the transformations and how they should be
* implemented, and for a description of the ASCII parameter strings.
* Data values are valid if (valid & PNG_INFO_pCAL) non-zero.
*/
png_charp pcal_purpose; /* pCAL chunk description string */
png_int_32 pcal_X0; /* minimum value */
png_int_32 pcal_X1; /* maximum value */
png_charp pcal_units; /* Latin-1 string giving physical units */
png_charpp pcal_params; /* ASCII strings containing parameter values */
png_byte pcal_type; /* equation type (see PNG_EQUATION_ below) */
png_byte pcal_nparams; /* number of parameters given in pcal_params */
#endif
/* New members added in libpng-1.0.6 */
png_uint_32 free_me; /* flags items libpng is responsible for freeing */
#ifdef PNG_STORE_UNKNOWN_CHUNKS_SUPPORTED
/* Storage for unknown chunks that the library doesn't recognize. */
png_unknown_chunkp unknown_chunks;
/* The type of this field is limited by the type of
* png_struct::user_chunk_cache_max, else overflow can occur.
*/
int unknown_chunks_num;
#endif
#ifdef PNG_sPLT_SUPPORTED
/* Data on sPLT chunks (there may be more than one). */
png_sPLT_tp splt_palettes;
int splt_palettes_num; /* Match type returned by png_get API */
#endif
#ifdef PNG_sCAL_SUPPORTED
/* The sCAL chunk describes the actual physical dimensions of the
* subject matter of the graphic. The chunk contains a unit specification
* a byte value, and two ASCII strings representing floating-point
* values. The values are width and height corresponding to one pixel
* in the image. Data values are valid if (valid & PNG_INFO_sCAL) is
* non-zero.
*/
png_byte scal_unit; /* unit of physical scale */
png_charp scal_s_width; /* string containing height */
png_charp scal_s_height; /* string containing width */
#endif
#ifdef PNG_INFO_IMAGE_SUPPORTED
/* Memory has been allocated if (valid & PNG_ALLOCATED_INFO_ROWS)
non-zero */
/* Data valid if (valid & PNG_INFO_IDAT) non-zero */
png_bytepp row_pointers; /* the image bits */
#endif
};
#endif /* PNGINFO_H */

219
hecl/extern/libpng/pnglibconf.h vendored Normal file
View File

@ -0,0 +1,219 @@
/* pnglibconf.h - library build configuration */
/* libpng version 1.6.37 */
/* Copyright (c) 2018-2019 Cosmin Truta */
/* Copyright (c) 1998-2002,2004,2006-2018 Glenn Randers-Pehrson */
/* This code is released under the libpng license. */
/* For conditions of distribution and use, see the disclaimer */
/* and license in png.h */
/* pnglibconf.h */
/* Machine generated file: DO NOT EDIT */
/* Derived from: scripts/pnglibconf.dfa */
#ifndef PNGLCONF_H
#define PNGLCONF_H
/* options */
#define PNG_16BIT_SUPPORTED
#define PNG_ALIGNED_MEMORY_SUPPORTED
/*#undef PNG_ARM_NEON_API_SUPPORTED*/
/*#undef PNG_ARM_NEON_CHECK_SUPPORTED*/
#define PNG_BENIGN_ERRORS_SUPPORTED
#define PNG_BENIGN_READ_ERRORS_SUPPORTED
/*#undef PNG_BENIGN_WRITE_ERRORS_SUPPORTED*/
#define PNG_BUILD_GRAYSCALE_PALETTE_SUPPORTED
#define PNG_CHECK_FOR_INVALID_INDEX_SUPPORTED
#define PNG_COLORSPACE_SUPPORTED
#define PNG_CONSOLE_IO_SUPPORTED
#define PNG_CONVERT_tIME_SUPPORTED
#define PNG_EASY_ACCESS_SUPPORTED
/*#undef PNG_ERROR_NUMBERS_SUPPORTED*/
#define PNG_ERROR_TEXT_SUPPORTED
#define PNG_FIXED_POINT_SUPPORTED
#define PNG_FLOATING_ARITHMETIC_SUPPORTED
#define PNG_FLOATING_POINT_SUPPORTED
#define PNG_FORMAT_AFIRST_SUPPORTED
#define PNG_FORMAT_BGR_SUPPORTED
#define PNG_GAMMA_SUPPORTED
#define PNG_GET_PALETTE_MAX_SUPPORTED
#define PNG_HANDLE_AS_UNKNOWN_SUPPORTED
#define PNG_INCH_CONVERSIONS_SUPPORTED
#define PNG_INFO_IMAGE_SUPPORTED
#define PNG_IO_STATE_SUPPORTED
#define PNG_MNG_FEATURES_SUPPORTED
#define PNG_POINTER_INDEXING_SUPPORTED
/*#undef PNG_POWERPC_VSX_API_SUPPORTED*/
/*#undef PNG_POWERPC_VSX_CHECK_SUPPORTED*/
#define PNG_PROGRESSIVE_READ_SUPPORTED
#define PNG_READ_16BIT_SUPPORTED
#define PNG_READ_ALPHA_MODE_SUPPORTED
#define PNG_READ_ANCILLARY_CHUNKS_SUPPORTED
#define PNG_READ_BACKGROUND_SUPPORTED
#define PNG_READ_BGR_SUPPORTED
#define PNG_READ_CHECK_FOR_INVALID_INDEX_SUPPORTED
#define PNG_READ_COMPOSITE_NODIV_SUPPORTED
#define PNG_READ_COMPRESSED_TEXT_SUPPORTED
#define PNG_READ_EXPAND_16_SUPPORTED
#define PNG_READ_EXPAND_SUPPORTED
#define PNG_READ_FILLER_SUPPORTED
#define PNG_READ_GAMMA_SUPPORTED
#define PNG_READ_GET_PALETTE_MAX_SUPPORTED
#define PNG_READ_GRAY_TO_RGB_SUPPORTED
#define PNG_READ_INTERLACING_SUPPORTED
#define PNG_READ_INT_FUNCTIONS_SUPPORTED
#define PNG_READ_INVERT_ALPHA_SUPPORTED
#define PNG_READ_INVERT_SUPPORTED
#define PNG_READ_OPT_PLTE_SUPPORTED
#define PNG_READ_PACKSWAP_SUPPORTED
#define PNG_READ_PACK_SUPPORTED
#define PNG_READ_QUANTIZE_SUPPORTED
#define PNG_READ_RGB_TO_GRAY_SUPPORTED
#define PNG_READ_SCALE_16_TO_8_SUPPORTED
#define PNG_READ_SHIFT_SUPPORTED
#define PNG_READ_STRIP_16_TO_8_SUPPORTED
#define PNG_READ_STRIP_ALPHA_SUPPORTED
#define PNG_READ_SUPPORTED
#define PNG_READ_SWAP_ALPHA_SUPPORTED
#define PNG_READ_SWAP_SUPPORTED
#define PNG_READ_TEXT_SUPPORTED
#define PNG_READ_TRANSFORMS_SUPPORTED
#define PNG_READ_UNKNOWN_CHUNKS_SUPPORTED
#define PNG_READ_USER_CHUNKS_SUPPORTED
#define PNG_READ_USER_TRANSFORM_SUPPORTED
#define PNG_READ_bKGD_SUPPORTED
#define PNG_READ_cHRM_SUPPORTED
#define PNG_READ_eXIf_SUPPORTED
#define PNG_READ_gAMA_SUPPORTED
#define PNG_READ_hIST_SUPPORTED
#define PNG_READ_iCCP_SUPPORTED
#define PNG_READ_iTXt_SUPPORTED
#define PNG_READ_oFFs_SUPPORTED
#define PNG_READ_pCAL_SUPPORTED
#define PNG_READ_pHYs_SUPPORTED
#define PNG_READ_sBIT_SUPPORTED
#define PNG_READ_sCAL_SUPPORTED
#define PNG_READ_sPLT_SUPPORTED
#define PNG_READ_sRGB_SUPPORTED
#define PNG_READ_tEXt_SUPPORTED
#define PNG_READ_tIME_SUPPORTED
#define PNG_READ_tRNS_SUPPORTED
#define PNG_READ_zTXt_SUPPORTED
#define PNG_SAVE_INT_32_SUPPORTED
#define PNG_SAVE_UNKNOWN_CHUNKS_SUPPORTED
#define PNG_SEQUENTIAL_READ_SUPPORTED
#define PNG_SETJMP_SUPPORTED
#define PNG_SET_OPTION_SUPPORTED
#define PNG_SET_UNKNOWN_CHUNKS_SUPPORTED
#define PNG_SET_USER_LIMITS_SUPPORTED
#define PNG_SIMPLIFIED_READ_AFIRST_SUPPORTED
#define PNG_SIMPLIFIED_READ_BGR_SUPPORTED
#define PNG_SIMPLIFIED_READ_SUPPORTED
#define PNG_SIMPLIFIED_WRITE_AFIRST_SUPPORTED
#define PNG_SIMPLIFIED_WRITE_BGR_SUPPORTED
#define PNG_SIMPLIFIED_WRITE_STDIO_SUPPORTED
#define PNG_SIMPLIFIED_WRITE_SUPPORTED
#define PNG_STDIO_SUPPORTED
#define PNG_STORE_UNKNOWN_CHUNKS_SUPPORTED
#define PNG_TEXT_SUPPORTED
#define PNG_TIME_RFC1123_SUPPORTED
#define PNG_UNKNOWN_CHUNKS_SUPPORTED
#define PNG_USER_CHUNKS_SUPPORTED
#define PNG_USER_LIMITS_SUPPORTED
#define PNG_USER_MEM_SUPPORTED
#define PNG_USER_TRANSFORM_INFO_SUPPORTED
#define PNG_USER_TRANSFORM_PTR_SUPPORTED
#define PNG_WARNINGS_SUPPORTED
#define PNG_WRITE_16BIT_SUPPORTED
#define PNG_WRITE_ANCILLARY_CHUNKS_SUPPORTED
#define PNG_WRITE_BGR_SUPPORTED
#define PNG_WRITE_CHECK_FOR_INVALID_INDEX_SUPPORTED
#define PNG_WRITE_COMPRESSED_TEXT_SUPPORTED
#define PNG_WRITE_CUSTOMIZE_COMPRESSION_SUPPORTED
#define PNG_WRITE_CUSTOMIZE_ZTXT_COMPRESSION_SUPPORTED
#define PNG_WRITE_FILLER_SUPPORTED
#define PNG_WRITE_FILTER_SUPPORTED
#define PNG_WRITE_FLUSH_SUPPORTED
#define PNG_WRITE_GET_PALETTE_MAX_SUPPORTED
#define PNG_WRITE_INTERLACING_SUPPORTED
#define PNG_WRITE_INT_FUNCTIONS_SUPPORTED
#define PNG_WRITE_INVERT_ALPHA_SUPPORTED
#define PNG_WRITE_INVERT_SUPPORTED
#define PNG_WRITE_OPTIMIZE_CMF_SUPPORTED
#define PNG_WRITE_PACKSWAP_SUPPORTED
#define PNG_WRITE_PACK_SUPPORTED
#define PNG_WRITE_SHIFT_SUPPORTED
#define PNG_WRITE_SUPPORTED
#define PNG_WRITE_SWAP_ALPHA_SUPPORTED
#define PNG_WRITE_SWAP_SUPPORTED
#define PNG_WRITE_TEXT_SUPPORTED
#define PNG_WRITE_TRANSFORMS_SUPPORTED
#define PNG_WRITE_UNKNOWN_CHUNKS_SUPPORTED
#define PNG_WRITE_USER_TRANSFORM_SUPPORTED
#define PNG_WRITE_WEIGHTED_FILTER_SUPPORTED
#define PNG_WRITE_bKGD_SUPPORTED
#define PNG_WRITE_cHRM_SUPPORTED
#define PNG_WRITE_eXIf_SUPPORTED
#define PNG_WRITE_gAMA_SUPPORTED
#define PNG_WRITE_hIST_SUPPORTED
#define PNG_WRITE_iCCP_SUPPORTED
#define PNG_WRITE_iTXt_SUPPORTED
#define PNG_WRITE_oFFs_SUPPORTED
#define PNG_WRITE_pCAL_SUPPORTED
#define PNG_WRITE_pHYs_SUPPORTED
#define PNG_WRITE_sBIT_SUPPORTED
#define PNG_WRITE_sCAL_SUPPORTED
#define PNG_WRITE_sPLT_SUPPORTED
#define PNG_WRITE_sRGB_SUPPORTED
#define PNG_WRITE_tEXt_SUPPORTED
#define PNG_WRITE_tIME_SUPPORTED
#define PNG_WRITE_tRNS_SUPPORTED
#define PNG_WRITE_zTXt_SUPPORTED
#define PNG_bKGD_SUPPORTED
#define PNG_cHRM_SUPPORTED
#define PNG_eXIf_SUPPORTED
#define PNG_gAMA_SUPPORTED
#define PNG_hIST_SUPPORTED
#define PNG_iCCP_SUPPORTED
#define PNG_iTXt_SUPPORTED
#define PNG_oFFs_SUPPORTED
#define PNG_pCAL_SUPPORTED
#define PNG_pHYs_SUPPORTED
#define PNG_sBIT_SUPPORTED
#define PNG_sCAL_SUPPORTED
#define PNG_sPLT_SUPPORTED
#define PNG_sRGB_SUPPORTED
#define PNG_tEXt_SUPPORTED
#define PNG_tIME_SUPPORTED
#define PNG_tRNS_SUPPORTED
#define PNG_zTXt_SUPPORTED
/* end of options */
/* settings */
#define PNG_API_RULE 0
#define PNG_DEFAULT_READ_MACROS 1
#define PNG_GAMMA_THRESHOLD_FIXED 5000
#define PNG_IDAT_READ_SIZE PNG_ZBUF_SIZE
#define PNG_INFLATE_BUF_SIZE 1024
#define PNG_LINKAGE_API extern
#define PNG_LINKAGE_CALLBACK extern
#define PNG_LINKAGE_DATA extern
#define PNG_LINKAGE_FUNCTION extern
#define PNG_MAX_GAMMA_8 11
#define PNG_QUANTIZE_BLUE_BITS 5
#define PNG_QUANTIZE_GREEN_BITS 5
#define PNG_QUANTIZE_RED_BITS 5
#define PNG_TEXT_Z_DEFAULT_COMPRESSION (-1)
#define PNG_TEXT_Z_DEFAULT_STRATEGY 0
#define PNG_USER_CHUNK_CACHE_MAX 1000
#define PNG_USER_CHUNK_MALLOC_MAX 8000000
#define PNG_USER_HEIGHT_MAX 1000000
#define PNG_USER_WIDTH_MAX 1000000
#define PNG_ZBUF_SIZE 8192
#define PNG_ZLIB_VERNUM 0 /* unknown */
#define PNG_Z_DEFAULT_COMPRESSION (-1)
#define PNG_Z_DEFAULT_NOFILTER_STRATEGY 0
#define PNG_Z_DEFAULT_STRATEGY 1
#define PNG_sCAL_PRECISION 5
#define PNG_sRGB_PROFILE_CHECKS 2
/* end of settings */
#endif /* PNGLCONF_H */

284
hecl/extern/libpng/pngmem.c vendored Normal file
View File

@ -0,0 +1,284 @@
/* pngmem.c - stub functions for memory allocation
*
* Copyright (c) 2018 Cosmin Truta
* Copyright (c) 1998-2002,2004,2006-2014,2016 Glenn Randers-Pehrson
* Copyright (c) 1996-1997 Andreas Dilger
* Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc.
*
* This code is released under the libpng license.
* For conditions of distribution and use, see the disclaimer
* and license in png.h
*
* This file provides a location for all memory allocation. Users who
* need special memory handling are expected to supply replacement
* functions for png_malloc() and png_free(), and to use
* png_create_read_struct_2() and png_create_write_struct_2() to
* identify the replacement functions.
*/
#include "pngpriv.h"
#if defined(PNG_READ_SUPPORTED) || defined(PNG_WRITE_SUPPORTED)
/* Free a png_struct */
void /* PRIVATE */
png_destroy_png_struct(png_structrp png_ptr)
{
if (png_ptr != NULL)
{
/* png_free might call png_error and may certainly call
* png_get_mem_ptr, so fake a temporary png_struct to support this.
*/
png_struct dummy_struct = *png_ptr;
memset(png_ptr, 0, (sizeof *png_ptr));
png_free(&dummy_struct, png_ptr);
# ifdef PNG_SETJMP_SUPPORTED
/* We may have a jmp_buf left to deallocate. */
png_free_jmpbuf(&dummy_struct);
# endif
}
}
/* Allocate memory. For reasonable files, size should never exceed
* 64K. However, zlib may allocate more than 64K if you don't tell
* it not to. See zconf.h and png.h for more information. zlib does
* need to allocate exactly 64K, so whatever you call here must
* have the ability to do that.
*/
PNG_FUNCTION(png_voidp,PNGAPI
png_calloc,(png_const_structrp png_ptr, png_alloc_size_t size),PNG_ALLOCATED)
{
png_voidp ret;
ret = png_malloc(png_ptr, size);
if (ret != NULL)
memset(ret, 0, size);
return ret;
}
/* png_malloc_base, an internal function added at libpng 1.6.0, does the work of
* allocating memory, taking into account limits and PNG_USER_MEM_SUPPORTED.
* Checking and error handling must happen outside this routine; it returns NULL
* if the allocation cannot be done (for any reason.)
*/
PNG_FUNCTION(png_voidp /* PRIVATE */,
png_malloc_base,(png_const_structrp png_ptr, png_alloc_size_t size),
PNG_ALLOCATED)
{
/* Moved to png_malloc_base from png_malloc_default in 1.6.0; the DOS
* allocators have also been removed in 1.6.0, so any 16-bit system now has
* to implement a user memory handler. This checks to be sure it isn't
* called with big numbers.
*/
#ifndef PNG_USER_MEM_SUPPORTED
PNG_UNUSED(png_ptr)
#endif
/* Some compilers complain that this is always true. However, it
* can be false when integer overflow happens.
*/
if (size > 0 && size <= PNG_SIZE_MAX
# ifdef PNG_MAX_MALLOC_64K
&& size <= 65536U
# endif
)
{
#ifdef PNG_USER_MEM_SUPPORTED
if (png_ptr != NULL && png_ptr->malloc_fn != NULL)
return png_ptr->malloc_fn(png_constcast(png_structrp,png_ptr), size);
else
#endif
return malloc((size_t)size); /* checked for truncation above */
}
else
return NULL;
}
#if defined(PNG_TEXT_SUPPORTED) || defined(PNG_sPLT_SUPPORTED) ||\
defined(PNG_STORE_UNKNOWN_CHUNKS_SUPPORTED)
/* This is really here only to work round a spurious warning in GCC 4.6 and 4.7
* that arises because of the checks in png_realloc_array that are repeated in
* png_malloc_array.
*/
static png_voidp
png_malloc_array_checked(png_const_structrp png_ptr, int nelements,
size_t element_size)
{
png_alloc_size_t req = (png_alloc_size_t)nelements; /* known to be > 0 */
if (req <= PNG_SIZE_MAX/element_size)
return png_malloc_base(png_ptr, req * element_size);
/* The failure case when the request is too large */
return NULL;
}
PNG_FUNCTION(png_voidp /* PRIVATE */,
png_malloc_array,(png_const_structrp png_ptr, int nelements,
size_t element_size),PNG_ALLOCATED)
{
if (nelements <= 0 || element_size == 0)
png_error(png_ptr, "internal error: array alloc");
return png_malloc_array_checked(png_ptr, nelements, element_size);
}
PNG_FUNCTION(png_voidp /* PRIVATE */,
png_realloc_array,(png_const_structrp png_ptr, png_const_voidp old_array,
int old_elements, int add_elements, size_t element_size),PNG_ALLOCATED)
{
/* These are internal errors: */
if (add_elements <= 0 || element_size == 0 || old_elements < 0 ||
(old_array == NULL && old_elements > 0))
png_error(png_ptr, "internal error: array realloc");
/* Check for overflow on the elements count (so the caller does not have to
* check.)
*/
if (add_elements <= INT_MAX - old_elements)
{
png_voidp new_array = png_malloc_array_checked(png_ptr,
old_elements+add_elements, element_size);
if (new_array != NULL)
{
/* Because png_malloc_array worked the size calculations below cannot
* overflow.
*/
if (old_elements > 0)
memcpy(new_array, old_array, element_size*(unsigned)old_elements);
memset((char*)new_array + element_size*(unsigned)old_elements, 0,
element_size*(unsigned)add_elements);
return new_array;
}
}
return NULL; /* error */
}
#endif /* TEXT || sPLT || STORE_UNKNOWN_CHUNKS */
/* Various functions that have different error handling are derived from this.
* png_malloc always exists, but if PNG_USER_MEM_SUPPORTED is defined a separate
* function png_malloc_default is also provided.
*/
PNG_FUNCTION(png_voidp,PNGAPI
png_malloc,(png_const_structrp png_ptr, png_alloc_size_t size),PNG_ALLOCATED)
{
png_voidp ret;
if (png_ptr == NULL)
return NULL;
ret = png_malloc_base(png_ptr, size);
if (ret == NULL)
png_error(png_ptr, "Out of memory"); /* 'm' means png_malloc */
return ret;
}
#ifdef PNG_USER_MEM_SUPPORTED
PNG_FUNCTION(png_voidp,PNGAPI
png_malloc_default,(png_const_structrp png_ptr, png_alloc_size_t size),
PNG_ALLOCATED PNG_DEPRECATED)
{
png_voidp ret;
if (png_ptr == NULL)
return NULL;
/* Passing 'NULL' here bypasses the application provided memory handler. */
ret = png_malloc_base(NULL/*use malloc*/, size);
if (ret == NULL)
png_error(png_ptr, "Out of Memory"); /* 'M' means png_malloc_default */
return ret;
}
#endif /* USER_MEM */
/* This function was added at libpng version 1.2.3. The png_malloc_warn()
* function will issue a png_warning and return NULL instead of issuing a
* png_error, if it fails to allocate the requested memory.
*/
PNG_FUNCTION(png_voidp,PNGAPI
png_malloc_warn,(png_const_structrp png_ptr, png_alloc_size_t size),
PNG_ALLOCATED)
{
if (png_ptr != NULL)
{
png_voidp ret = png_malloc_base(png_ptr, size);
if (ret != NULL)
return ret;
png_warning(png_ptr, "Out of memory");
}
return NULL;
}
/* Free a pointer allocated by png_malloc(). If ptr is NULL, return
* without taking any action.
*/
void PNGAPI
png_free(png_const_structrp png_ptr, png_voidp ptr)
{
if (png_ptr == NULL || ptr == NULL)
return;
#ifdef PNG_USER_MEM_SUPPORTED
if (png_ptr->free_fn != NULL)
png_ptr->free_fn(png_constcast(png_structrp,png_ptr), ptr);
else
png_free_default(png_ptr, ptr);
}
PNG_FUNCTION(void,PNGAPI
png_free_default,(png_const_structrp png_ptr, png_voidp ptr),PNG_DEPRECATED)
{
if (png_ptr == NULL || ptr == NULL)
return;
#endif /* USER_MEM */
free(ptr);
}
#ifdef PNG_USER_MEM_SUPPORTED
/* This function is called when the application wants to use another method
* of allocating and freeing memory.
*/
void PNGAPI
png_set_mem_fn(png_structrp png_ptr, png_voidp mem_ptr, png_malloc_ptr
malloc_fn, png_free_ptr free_fn)
{
if (png_ptr != NULL)
{
png_ptr->mem_ptr = mem_ptr;
png_ptr->malloc_fn = malloc_fn;
png_ptr->free_fn = free_fn;
}
}
/* This function returns a pointer to the mem_ptr associated with the user
* functions. The application should free any memory associated with this
* pointer before png_write_destroy and png_read_destroy are called.
*/
png_voidp PNGAPI
png_get_mem_ptr(png_const_structrp png_ptr)
{
if (png_ptr == NULL)
return NULL;
return png_ptr->mem_ptr;
}
#endif /* USER_MEM */
#endif /* READ || WRITE */

1096
hecl/extern/libpng/pngpread.c vendored Normal file

File diff suppressed because it is too large Load Diff

2152
hecl/extern/libpng/pngpriv.h vendored Normal file

File diff suppressed because it is too large Load Diff

4225
hecl/extern/libpng/pngread.c vendored Normal file

File diff suppressed because it is too large Load Diff

120
hecl/extern/libpng/pngrio.c vendored Normal file
View File

@ -0,0 +1,120 @@
/* pngrio.c - functions for data input
*
* Copyright (c) 2018 Cosmin Truta
* Copyright (c) 1998-2002,2004,2006-2016,2018 Glenn Randers-Pehrson
* Copyright (c) 1996-1997 Andreas Dilger
* Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc.
*
* This code is released under the libpng license.
* For conditions of distribution and use, see the disclaimer
* and license in png.h
*
* This file provides a location for all input. Users who need
* special handling are expected to write a function that has the same
* arguments as this and performs a similar function, but that possibly
* has a different input method. Note that you shouldn't change this
* function, but rather write a replacement function and then make
* libpng use it at run time with png_set_read_fn(...).
*/
#include "pngpriv.h"
#ifdef PNG_READ_SUPPORTED
/* Read the data from whatever input you are using. The default routine
* reads from a file pointer. Note that this routine sometimes gets called
* with very small lengths, so you should implement some kind of simple
* buffering if you are using unbuffered reads. This should never be asked
* to read more than 64K on a 16-bit machine.
*/
void /* PRIVATE */
png_read_data(png_structrp png_ptr, png_bytep data, size_t length)
{
png_debug1(4, "reading %d bytes", (int)length);
if (png_ptr->read_data_fn != NULL)
(*(png_ptr->read_data_fn))(png_ptr, data, length);
else
png_error(png_ptr, "Call to NULL read function");
}
#ifdef PNG_STDIO_SUPPORTED
/* This is the function that does the actual reading of data. If you are
* not reading from a standard C stream, you should create a replacement
* read_data function and use it at run time with png_set_read_fn(), rather
* than changing the library.
*/
void PNGCBAPI
png_default_read_data(png_structp png_ptr, png_bytep data, size_t length)
{
size_t check;
if (png_ptr == NULL)
return;
/* fread() returns 0 on error, so it is OK to store this in a size_t
* instead of an int, which is what fread() actually returns.
*/
check = fread(data, 1, length, png_voidcast(png_FILE_p, png_ptr->io_ptr));
if (check != length)
png_error(png_ptr, "Read Error");
}
#endif
/* This function allows the application to supply a new input function
* for libpng if standard C streams aren't being used.
*
* This function takes as its arguments:
*
* png_ptr - pointer to a png input data structure
*
* io_ptr - pointer to user supplied structure containing info about
* the input functions. May be NULL.
*
* read_data_fn - pointer to a new input function that takes as its
* arguments a pointer to a png_struct, a pointer to
* a location where input data can be stored, and a 32-bit
* unsigned int that is the number of bytes to be read.
* To exit and output any fatal error messages the new write
* function should call png_error(png_ptr, "Error msg").
* May be NULL, in which case libpng's default function will
* be used.
*/
void PNGAPI
png_set_read_fn(png_structrp png_ptr, png_voidp io_ptr,
png_rw_ptr read_data_fn)
{
if (png_ptr == NULL)
return;
png_ptr->io_ptr = io_ptr;
#ifdef PNG_STDIO_SUPPORTED
if (read_data_fn != NULL)
png_ptr->read_data_fn = read_data_fn;
else
png_ptr->read_data_fn = png_default_read_data;
#else
png_ptr->read_data_fn = read_data_fn;
#endif
#ifdef PNG_WRITE_SUPPORTED
/* It is an error to write to a read device */
if (png_ptr->write_data_fn != NULL)
{
png_ptr->write_data_fn = NULL;
png_warning(png_ptr,
"Can't set both read_data_fn and write_data_fn in the"
" same structure");
}
#endif
#ifdef PNG_WRITE_FLUSH_SUPPORTED
png_ptr->output_flush_fn = NULL;
#endif
}
#endif /* READ */

5044
hecl/extern/libpng/pngrtran.c vendored Normal file

File diff suppressed because it is too large Load Diff

4681
hecl/extern/libpng/pngrutil.c vendored Normal file

File diff suppressed because it is too large Load Diff

1802
hecl/extern/libpng/pngset.c vendored Normal file

File diff suppressed because it is too large Load Diff

489
hecl/extern/libpng/pngstruct.h vendored Normal file
View File

@ -0,0 +1,489 @@
/* pngstruct.h - header file for PNG reference library
*
* Copyright (c) 2018-2019 Cosmin Truta
* Copyright (c) 1998-2002,2004,2006-2018 Glenn Randers-Pehrson
* Copyright (c) 1996-1997 Andreas Dilger
* Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc.
*
* This code is released under the libpng license.
* For conditions of distribution and use, see the disclaimer
* and license in png.h
*/
/* The structure that holds the information to read and write PNG files.
* The only people who need to care about what is inside of this are the
* people who will be modifying the library for their own special needs.
* It should NOT be accessed directly by an application.
*/
#ifndef PNGSTRUCT_H
#define PNGSTRUCT_H
/* zlib.h defines the structure z_stream, an instance of which is included
* in this structure and is required for decompressing the LZ compressed
* data in PNG files.
*/
#ifndef ZLIB_CONST
/* We must ensure that zlib uses 'const' in declarations. */
# define ZLIB_CONST
#endif
#include "zlib.h"
#ifdef const
/* zlib.h sometimes #defines const to nothing, undo this. */
# undef const
#endif
/* zlib.h has mediocre z_const use before 1.2.6, this stuff is for compatibility
* with older builds.
*/
#if ZLIB_VERNUM < 0x1260
# define PNGZ_MSG_CAST(s) png_constcast(char*,s)
# define PNGZ_INPUT_CAST(b) png_constcast(png_bytep,b)
#else
# define PNGZ_MSG_CAST(s) (s)
# define PNGZ_INPUT_CAST(b) (b)
#endif
/* zlib.h declares a magic type 'uInt' that limits the amount of data that zlib
* can handle at once. This type need be no larger than 16 bits (so maximum of
* 65535), this define allows us to discover how big it is, but limited by the
* maximum for size_t. The value can be overridden in a library build
* (pngusr.h, or set it in CPPFLAGS) and it works to set it to a considerably
* lower value (e.g. 255 works). A lower value may help memory usage (slightly)
* and may even improve performance on some systems (and degrade it on others.)
*/
#ifndef ZLIB_IO_MAX
# define ZLIB_IO_MAX ((uInt)-1)
#endif
#ifdef PNG_WRITE_SUPPORTED
/* The type of a compression buffer list used by the write code. */
typedef struct png_compression_buffer
{
struct png_compression_buffer *next;
png_byte output[1]; /* actually zbuf_size */
} png_compression_buffer, *png_compression_bufferp;
#define PNG_COMPRESSION_BUFFER_SIZE(pp)\
(offsetof(png_compression_buffer, output) + (pp)->zbuffer_size)
#endif
/* Colorspace support; structures used in png_struct, png_info and in internal
* functions to hold and communicate information about the color space.
*
* PNG_COLORSPACE_SUPPORTED is only required if the application will perform
* colorspace corrections, otherwise all the colorspace information can be
* skipped and the size of libpng can be reduced (significantly) by compiling
* out the colorspace support.
*/
#ifdef PNG_COLORSPACE_SUPPORTED
/* The chromaticities of the red, green and blue colorants and the chromaticity
* of the corresponding white point (i.e. of rgb(1.0,1.0,1.0)).
*/
typedef struct png_xy
{
png_fixed_point redx, redy;
png_fixed_point greenx, greeny;
png_fixed_point bluex, bluey;
png_fixed_point whitex, whitey;
} png_xy;
/* The same data as above but encoded as CIE XYZ values. When this data comes
* from chromaticities the sum of the Y values is assumed to be 1.0
*/
typedef struct png_XYZ
{
png_fixed_point red_X, red_Y, red_Z;
png_fixed_point green_X, green_Y, green_Z;
png_fixed_point blue_X, blue_Y, blue_Z;
} png_XYZ;
#endif /* COLORSPACE */
#if defined(PNG_COLORSPACE_SUPPORTED) || defined(PNG_GAMMA_SUPPORTED)
/* A colorspace is all the above plus, potentially, profile information;
* however at present libpng does not use the profile internally so it is only
* stored in the png_info struct (if iCCP is supported.) The rendering intent
* is retained here and is checked.
*
* The file gamma encoding information is also stored here and gamma correction
* is done by libpng, whereas color correction must currently be done by the
* application.
*/
typedef struct png_colorspace
{
#ifdef PNG_GAMMA_SUPPORTED
png_fixed_point gamma; /* File gamma */
#endif
#ifdef PNG_COLORSPACE_SUPPORTED
png_xy end_points_xy; /* End points as chromaticities */
png_XYZ end_points_XYZ; /* End points as CIE XYZ colorant values */
png_uint_16 rendering_intent; /* Rendering intent of a profile */
#endif
/* Flags are always defined to simplify the code. */
png_uint_16 flags; /* As defined below */
} png_colorspace, * PNG_RESTRICT png_colorspacerp;
typedef const png_colorspace * PNG_RESTRICT png_const_colorspacerp;
/* General flags for the 'flags' field */
#define PNG_COLORSPACE_HAVE_GAMMA 0x0001
#define PNG_COLORSPACE_HAVE_ENDPOINTS 0x0002
#define PNG_COLORSPACE_HAVE_INTENT 0x0004
#define PNG_COLORSPACE_FROM_gAMA 0x0008
#define PNG_COLORSPACE_FROM_cHRM 0x0010
#define PNG_COLORSPACE_FROM_sRGB 0x0020
#define PNG_COLORSPACE_ENDPOINTS_MATCH_sRGB 0x0040
#define PNG_COLORSPACE_MATCHES_sRGB 0x0080 /* exact match on profile */
#define PNG_COLORSPACE_INVALID 0x8000
#define PNG_COLORSPACE_CANCEL(flags) (0xffff ^ (flags))
#endif /* COLORSPACE || GAMMA */
struct png_struct_def
{
#ifdef PNG_SETJMP_SUPPORTED
jmp_buf jmp_buf_local; /* New name in 1.6.0 for jmp_buf in png_struct */
png_longjmp_ptr longjmp_fn;/* setjmp non-local goto function. */
jmp_buf *jmp_buf_ptr; /* passed to longjmp_fn */
size_t jmp_buf_size; /* size of the above, if allocated */
#endif
png_error_ptr error_fn; /* function for printing errors and aborting */
#ifdef PNG_WARNINGS_SUPPORTED
png_error_ptr warning_fn; /* function for printing warnings */
#endif
png_voidp error_ptr; /* user supplied struct for error functions */
png_rw_ptr write_data_fn; /* function for writing output data */
png_rw_ptr read_data_fn; /* function for reading input data */
png_voidp io_ptr; /* ptr to application struct for I/O functions */
#ifdef PNG_READ_USER_TRANSFORM_SUPPORTED
png_user_transform_ptr read_user_transform_fn; /* user read transform */
#endif
#ifdef PNG_WRITE_USER_TRANSFORM_SUPPORTED
png_user_transform_ptr write_user_transform_fn; /* user write transform */
#endif
/* These were added in libpng-1.0.2 */
#ifdef PNG_USER_TRANSFORM_PTR_SUPPORTED
#if defined(PNG_READ_USER_TRANSFORM_SUPPORTED) || \
defined(PNG_WRITE_USER_TRANSFORM_SUPPORTED)
png_voidp user_transform_ptr; /* user supplied struct for user transform */
png_byte user_transform_depth; /* bit depth of user transformed pixels */
png_byte user_transform_channels; /* channels in user transformed pixels */
#endif
#endif
png_uint_32 mode; /* tells us where we are in the PNG file */
png_uint_32 flags; /* flags indicating various things to libpng */
png_uint_32 transformations; /* which transformations to perform */
png_uint_32 zowner; /* ID (chunk type) of zstream owner, 0 if none */
z_stream zstream; /* decompression structure */
#ifdef PNG_WRITE_SUPPORTED
png_compression_bufferp zbuffer_list; /* Created on demand during write */
uInt zbuffer_size; /* size of the actual buffer */
int zlib_level; /* holds zlib compression level */
int zlib_method; /* holds zlib compression method */
int zlib_window_bits; /* holds zlib compression window bits */
int zlib_mem_level; /* holds zlib compression memory level */
int zlib_strategy; /* holds zlib compression strategy */
#endif
/* Added at libpng 1.5.4 */
#ifdef PNG_WRITE_CUSTOMIZE_ZTXT_COMPRESSION_SUPPORTED
int zlib_text_level; /* holds zlib compression level */
int zlib_text_method; /* holds zlib compression method */
int zlib_text_window_bits; /* holds zlib compression window bits */
int zlib_text_mem_level; /* holds zlib compression memory level */
int zlib_text_strategy; /* holds zlib compression strategy */
#endif
/* End of material added at libpng 1.5.4 */
/* Added at libpng 1.6.0 */
#ifdef PNG_WRITE_SUPPORTED
int zlib_set_level; /* Actual values set into the zstream on write */
int zlib_set_method;
int zlib_set_window_bits;
int zlib_set_mem_level;
int zlib_set_strategy;
#endif
png_uint_32 width; /* width of image in pixels */
png_uint_32 height; /* height of image in pixels */
png_uint_32 num_rows; /* number of rows in current pass */
png_uint_32 usr_width; /* width of row at start of write */
size_t rowbytes; /* size of row in bytes */
png_uint_32 iwidth; /* width of current interlaced row in pixels */
png_uint_32 row_number; /* current row in interlace pass */
png_uint_32 chunk_name; /* PNG_CHUNK() id of current chunk */
png_bytep prev_row; /* buffer to save previous (unfiltered) row.
* While reading this is a pointer into
* big_prev_row; while writing it is separately
* allocated if needed.
*/
png_bytep row_buf; /* buffer to save current (unfiltered) row.
* While reading, this is a pointer into
* big_row_buf; while writing it is separately
* allocated.
*/
#ifdef PNG_WRITE_FILTER_SUPPORTED
png_bytep try_row; /* buffer to save trial row when filtering */
png_bytep tst_row; /* buffer to save best trial row when filtering */
#endif
size_t info_rowbytes; /* Added in 1.5.4: cache of updated row bytes */
png_uint_32 idat_size; /* current IDAT size for read */
png_uint_32 crc; /* current chunk CRC value */
png_colorp palette; /* palette from the input file */
png_uint_16 num_palette; /* number of color entries in palette */
/* Added at libpng-1.5.10 */
#ifdef PNG_CHECK_FOR_INVALID_INDEX_SUPPORTED
int num_palette_max; /* maximum palette index found in IDAT */
#endif
png_uint_16 num_trans; /* number of transparency values */
png_byte compression; /* file compression type (always 0) */
png_byte filter; /* file filter type (always 0) */
png_byte interlaced; /* PNG_INTERLACE_NONE, PNG_INTERLACE_ADAM7 */
png_byte pass; /* current interlace pass (0 - 6) */
png_byte do_filter; /* row filter flags (see PNG_FILTER_ in png.h ) */
png_byte color_type; /* color type of file */
png_byte bit_depth; /* bit depth of file */
png_byte usr_bit_depth; /* bit depth of users row: write only */
png_byte pixel_depth; /* number of bits per pixel */
png_byte channels; /* number of channels in file */
#ifdef PNG_WRITE_SUPPORTED
png_byte usr_channels; /* channels at start of write: write only */
#endif
png_byte sig_bytes; /* magic bytes read/written from start of file */
png_byte maximum_pixel_depth;
/* pixel depth used for the row buffers */
png_byte transformed_pixel_depth;
/* pixel depth after read/write transforms */
#if ZLIB_VERNUM >= 0x1240
png_byte zstream_start; /* at start of an input zlib stream */
#endif /* Zlib >= 1.2.4 */
#if defined(PNG_READ_FILLER_SUPPORTED) || defined(PNG_WRITE_FILLER_SUPPORTED)
png_uint_16 filler; /* filler bytes for pixel expansion */
#endif
#if defined(PNG_bKGD_SUPPORTED) || defined(PNG_READ_BACKGROUND_SUPPORTED) ||\
defined(PNG_READ_ALPHA_MODE_SUPPORTED)
png_byte background_gamma_type;
png_fixed_point background_gamma;
png_color_16 background; /* background color in screen gamma space */
#ifdef PNG_READ_GAMMA_SUPPORTED
png_color_16 background_1; /* background normalized to gamma 1.0 */
#endif
#endif /* bKGD */
#ifdef PNG_WRITE_FLUSH_SUPPORTED
png_flush_ptr output_flush_fn; /* Function for flushing output */
png_uint_32 flush_dist; /* how many rows apart to flush, 0 - no flush */
png_uint_32 flush_rows; /* number of rows written since last flush */
#endif
#ifdef PNG_READ_GAMMA_SUPPORTED
int gamma_shift; /* number of "insignificant" bits in 16-bit gamma */
png_fixed_point screen_gamma; /* screen gamma value (display_exponent) */
png_bytep gamma_table; /* gamma table for 8-bit depth files */
png_uint_16pp gamma_16_table; /* gamma table for 16-bit depth files */
#if defined(PNG_READ_BACKGROUND_SUPPORTED) || \
defined(PNG_READ_ALPHA_MODE_SUPPORTED) || \
defined(PNG_READ_RGB_TO_GRAY_SUPPORTED)
png_bytep gamma_from_1; /* converts from 1.0 to screen */
png_bytep gamma_to_1; /* converts from file to 1.0 */
png_uint_16pp gamma_16_from_1; /* converts from 1.0 to screen */
png_uint_16pp gamma_16_to_1; /* converts from file to 1.0 */
#endif /* READ_BACKGROUND || READ_ALPHA_MODE || RGB_TO_GRAY */
#endif
#if defined(PNG_READ_GAMMA_SUPPORTED) || defined(PNG_sBIT_SUPPORTED)
png_color_8 sig_bit; /* significant bits in each available channel */
#endif
#if defined(PNG_READ_SHIFT_SUPPORTED) || defined(PNG_WRITE_SHIFT_SUPPORTED)
png_color_8 shift; /* shift for significant bit transformation */
#endif
#if defined(PNG_tRNS_SUPPORTED) || defined(PNG_READ_BACKGROUND_SUPPORTED) \
|| defined(PNG_READ_EXPAND_SUPPORTED) || defined(PNG_READ_BACKGROUND_SUPPORTED)
png_bytep trans_alpha; /* alpha values for paletted files */
png_color_16 trans_color; /* transparent color for non-paletted files */
#endif
png_read_status_ptr read_row_fn; /* called after each row is decoded */
png_write_status_ptr write_row_fn; /* called after each row is encoded */
#ifdef PNG_PROGRESSIVE_READ_SUPPORTED
png_progressive_info_ptr info_fn; /* called after header data fully read */
png_progressive_row_ptr row_fn; /* called after a prog. row is decoded */
png_progressive_end_ptr end_fn; /* called after image is complete */
png_bytep save_buffer_ptr; /* current location in save_buffer */
png_bytep save_buffer; /* buffer for previously read data */
png_bytep current_buffer_ptr; /* current location in current_buffer */
png_bytep current_buffer; /* buffer for recently used data */
png_uint_32 push_length; /* size of current input chunk */
png_uint_32 skip_length; /* bytes to skip in input data */
size_t save_buffer_size; /* amount of data now in save_buffer */
size_t save_buffer_max; /* total size of save_buffer */
size_t buffer_size; /* total amount of available input data */
size_t current_buffer_size; /* amount of data now in current_buffer */
int process_mode; /* what push library is currently doing */
int cur_palette; /* current push library palette index */
#endif /* PROGRESSIVE_READ */
#if defined(__TURBOC__) && !defined(_Windows) && !defined(__FLAT__)
/* For the Borland special 64K segment handler */
png_bytepp offset_table_ptr;
png_bytep offset_table;
png_uint_16 offset_table_number;
png_uint_16 offset_table_count;
png_uint_16 offset_table_count_free;
#endif
#ifdef PNG_READ_QUANTIZE_SUPPORTED
png_bytep palette_lookup; /* lookup table for quantizing */
png_bytep quantize_index; /* index translation for palette files */
#endif
/* Options */
#ifdef PNG_SET_OPTION_SUPPORTED
png_uint_32 options; /* On/off state (up to 16 options) */
#endif
#if PNG_LIBPNG_VER < 10700
/* To do: remove this from libpng-1.7 */
#ifdef PNG_TIME_RFC1123_SUPPORTED
char time_buffer[29]; /* String to hold RFC 1123 time text */
#endif
#endif
/* New members added in libpng-1.0.6 */
png_uint_32 free_me; /* flags items libpng is responsible for freeing */
#ifdef PNG_USER_CHUNKS_SUPPORTED
png_voidp user_chunk_ptr;
#ifdef PNG_READ_USER_CHUNKS_SUPPORTED
png_user_chunk_ptr read_user_chunk_fn; /* user read chunk handler */
#endif
#endif
#ifdef PNG_SET_UNKNOWN_CHUNKS_SUPPORTED
int unknown_default; /* As PNG_HANDLE_* */
unsigned int num_chunk_list; /* Number of entries in the list */
png_bytep chunk_list; /* List of png_byte[5]; the textual chunk name
* followed by a PNG_HANDLE_* byte */
#endif
/* New members added in libpng-1.0.3 */
#ifdef PNG_READ_RGB_TO_GRAY_SUPPORTED
png_byte rgb_to_gray_status;
/* Added in libpng 1.5.5 to record setting of coefficients: */
png_byte rgb_to_gray_coefficients_set;
/* These were changed from png_byte in libpng-1.0.6 */
png_uint_16 rgb_to_gray_red_coeff;
png_uint_16 rgb_to_gray_green_coeff;
/* deleted in 1.5.5: rgb_to_gray_blue_coeff; */
#endif
/* New member added in libpng-1.6.36 */
#if defined(PNG_READ_EXPAND_SUPPORTED) && \
defined(PNG_ARM_NEON_IMPLEMENTATION)
png_bytep riffled_palette; /* buffer for accelerated palette expansion */
#endif
/* New member added in libpng-1.0.4 (renamed in 1.0.9) */
#if defined(PNG_MNG_FEATURES_SUPPORTED)
/* Changed from png_byte to png_uint_32 at version 1.2.0 */
png_uint_32 mng_features_permitted;
#endif
/* New member added in libpng-1.0.9, ifdef'ed out in 1.0.12, enabled in 1.2.0 */
#ifdef PNG_MNG_FEATURES_SUPPORTED
png_byte filter_type;
#endif
/* New members added in libpng-1.2.0 */
/* New members added in libpng-1.0.2 but first enabled by default in 1.2.0 */
#ifdef PNG_USER_MEM_SUPPORTED
png_voidp mem_ptr; /* user supplied struct for mem functions */
png_malloc_ptr malloc_fn; /* function for allocating memory */
png_free_ptr free_fn; /* function for freeing memory */
#endif
/* New member added in libpng-1.0.13 and 1.2.0 */
png_bytep big_row_buf; /* buffer to save current (unfiltered) row */
#ifdef PNG_READ_QUANTIZE_SUPPORTED
/* The following three members were added at version 1.0.14 and 1.2.4 */
png_bytep quantize_sort; /* working sort array */
png_bytep index_to_palette; /* where the original index currently is
in the palette */
png_bytep palette_to_index; /* which original index points to this
palette color */
#endif
/* New members added in libpng-1.0.16 and 1.2.6 */
png_byte compression_type;
#ifdef PNG_USER_LIMITS_SUPPORTED
png_uint_32 user_width_max;
png_uint_32 user_height_max;
/* Added in libpng-1.4.0: Total number of sPLT, text, and unknown
* chunks that can be stored (0 means unlimited).
*/
png_uint_32 user_chunk_cache_max;
/* Total memory that a zTXt, sPLT, iTXt, iCCP, or unknown chunk
* can occupy when decompressed. 0 means unlimited.
*/
png_alloc_size_t user_chunk_malloc_max;
#endif
/* New member added in libpng-1.0.25 and 1.2.17 */
#ifdef PNG_READ_UNKNOWN_CHUNKS_SUPPORTED
/* Temporary storage for unknown chunk that the library doesn't recognize,
* used while reading the chunk.
*/
png_unknown_chunk unknown_chunk;
#endif
/* New member added in libpng-1.2.26 */
size_t old_big_row_buf_size;
#ifdef PNG_READ_SUPPORTED
/* New member added in libpng-1.2.30 */
png_bytep read_buffer; /* buffer for reading chunk data */
png_alloc_size_t read_buffer_size; /* current size of the buffer */
#endif
#ifdef PNG_SEQUENTIAL_READ_SUPPORTED
uInt IDAT_read_size; /* limit on read buffer size for IDAT */
#endif
#ifdef PNG_IO_STATE_SUPPORTED
/* New member added in libpng-1.4.0 */
png_uint_32 io_state;
#endif
/* New member added in libpng-1.5.6 */
png_bytep big_prev_row;
/* New member added in libpng-1.5.7 */
void (*read_filter[PNG_FILTER_VALUE_LAST-1])(png_row_infop row_info,
png_bytep row, png_const_bytep prev_row);
#ifdef PNG_READ_SUPPORTED
#if defined(PNG_COLORSPACE_SUPPORTED) || defined(PNG_GAMMA_SUPPORTED)
png_colorspace colorspace;
#endif
#endif
};
#endif /* PNGSTRUCT_H */

2158
hecl/extern/libpng/pngtest.c vendored Normal file

File diff suppressed because it is too large Load Diff

864
hecl/extern/libpng/pngtrans.c vendored Normal file
View File

@ -0,0 +1,864 @@
/* pngtrans.c - transforms the data in a row (used by both readers and writers)
*
* Copyright (c) 2018 Cosmin Truta
* Copyright (c) 1998-2002,2004,2006-2018 Glenn Randers-Pehrson
* Copyright (c) 1996-1997 Andreas Dilger
* Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc.
*
* This code is released under the libpng license.
* For conditions of distribution and use, see the disclaimer
* and license in png.h
*/
#include "pngpriv.h"
#if defined(PNG_READ_SUPPORTED) || defined(PNG_WRITE_SUPPORTED)
#if defined(PNG_READ_BGR_SUPPORTED) || defined(PNG_WRITE_BGR_SUPPORTED)
/* Turn on BGR-to-RGB mapping */
void PNGAPI
png_set_bgr(png_structrp png_ptr)
{
png_debug(1, "in png_set_bgr");
if (png_ptr == NULL)
return;
png_ptr->transformations |= PNG_BGR;
}
#endif
#if defined(PNG_READ_SWAP_SUPPORTED) || defined(PNG_WRITE_SWAP_SUPPORTED)
/* Turn on 16-bit byte swapping */
void PNGAPI
png_set_swap(png_structrp png_ptr)
{
png_debug(1, "in png_set_swap");
if (png_ptr == NULL)
return;
if (png_ptr->bit_depth == 16)
png_ptr->transformations |= PNG_SWAP_BYTES;
}
#endif
#if defined(PNG_READ_PACK_SUPPORTED) || defined(PNG_WRITE_PACK_SUPPORTED)
/* Turn on pixel packing */
void PNGAPI
png_set_packing(png_structrp png_ptr)
{
png_debug(1, "in png_set_packing");
if (png_ptr == NULL)
return;
if (png_ptr->bit_depth < 8)
{
png_ptr->transformations |= PNG_PACK;
# ifdef PNG_WRITE_SUPPORTED
png_ptr->usr_bit_depth = 8;
# endif
}
}
#endif
#if defined(PNG_READ_PACKSWAP_SUPPORTED)||defined(PNG_WRITE_PACKSWAP_SUPPORTED)
/* Turn on packed pixel swapping */
void PNGAPI
png_set_packswap(png_structrp png_ptr)
{
png_debug(1, "in png_set_packswap");
if (png_ptr == NULL)
return;
if (png_ptr->bit_depth < 8)
png_ptr->transformations |= PNG_PACKSWAP;
}
#endif
#if defined(PNG_READ_SHIFT_SUPPORTED) || defined(PNG_WRITE_SHIFT_SUPPORTED)
void PNGAPI
png_set_shift(png_structrp png_ptr, png_const_color_8p true_bits)
{
png_debug(1, "in png_set_shift");
if (png_ptr == NULL)
return;
png_ptr->transformations |= PNG_SHIFT;
png_ptr->shift = *true_bits;
}
#endif
#if defined(PNG_READ_INTERLACING_SUPPORTED) || \
defined(PNG_WRITE_INTERLACING_SUPPORTED)
int PNGAPI
png_set_interlace_handling(png_structrp png_ptr)
{
png_debug(1, "in png_set_interlace handling");
if (png_ptr != 0 && png_ptr->interlaced != 0)
{
png_ptr->transformations |= PNG_INTERLACE;
return (7);
}
return (1);
}
#endif
#if defined(PNG_READ_FILLER_SUPPORTED) || defined(PNG_WRITE_FILLER_SUPPORTED)
/* Add a filler byte on read, or remove a filler or alpha byte on write.
* The filler type has changed in v0.95 to allow future 2-byte fillers
* for 48-bit input data, as well as to avoid problems with some compilers
* that don't like bytes as parameters.
*/
void PNGAPI
png_set_filler(png_structrp png_ptr, png_uint_32 filler, int filler_loc)
{
png_debug(1, "in png_set_filler");
if (png_ptr == NULL)
return;
/* In libpng 1.6 it is possible to determine whether this is a read or write
* operation and therefore to do more checking here for a valid call.
*/
if ((png_ptr->mode & PNG_IS_READ_STRUCT) != 0)
{
# ifdef PNG_READ_FILLER_SUPPORTED
/* On read png_set_filler is always valid, regardless of the base PNG
* format, because other transformations can give a format where the
* filler code can execute (basically an 8 or 16-bit component RGB or G
* format.)
*
* NOTE: usr_channels is not used by the read code! (This has led to
* confusion in the past.) The filler is only used in the read code.
*/
png_ptr->filler = (png_uint_16)filler;
# else
png_app_error(png_ptr, "png_set_filler not supported on read");
PNG_UNUSED(filler) /* not used in the write case */
return;
# endif
}
else /* write */
{
# ifdef PNG_WRITE_FILLER_SUPPORTED
/* On write the usr_channels parameter must be set correctly at the
* start to record the number of channels in the app-supplied data.
*/
switch (png_ptr->color_type)
{
case PNG_COLOR_TYPE_RGB:
png_ptr->usr_channels = 4;
break;
case PNG_COLOR_TYPE_GRAY:
if (png_ptr->bit_depth >= 8)
{
png_ptr->usr_channels = 2;
break;
}
else
{
/* There simply isn't any code in libpng to strip out bits
* from bytes when the components are less than a byte in
* size!
*/
png_app_error(png_ptr,
"png_set_filler is invalid for"
" low bit depth gray output");
return;
}
default:
png_app_error(png_ptr,
"png_set_filler: inappropriate color type");
return;
}
# else
png_app_error(png_ptr, "png_set_filler not supported on write");
return;
# endif
}
/* Here on success - libpng supports the operation, set the transformation
* and the flag to say where the filler channel is.
*/
png_ptr->transformations |= PNG_FILLER;
if (filler_loc == PNG_FILLER_AFTER)
png_ptr->flags |= PNG_FLAG_FILLER_AFTER;
else
png_ptr->flags &= ~PNG_FLAG_FILLER_AFTER;
}
/* Added to libpng-1.2.7 */
void PNGAPI
png_set_add_alpha(png_structrp png_ptr, png_uint_32 filler, int filler_loc)
{
png_debug(1, "in png_set_add_alpha");
if (png_ptr == NULL)
return;
png_set_filler(png_ptr, filler, filler_loc);
/* The above may fail to do anything. */
if ((png_ptr->transformations & PNG_FILLER) != 0)
png_ptr->transformations |= PNG_ADD_ALPHA;
}
#endif
#if defined(PNG_READ_SWAP_ALPHA_SUPPORTED) || \
defined(PNG_WRITE_SWAP_ALPHA_SUPPORTED)
void PNGAPI
png_set_swap_alpha(png_structrp png_ptr)
{
png_debug(1, "in png_set_swap_alpha");
if (png_ptr == NULL)
return;
png_ptr->transformations |= PNG_SWAP_ALPHA;
}
#endif
#if defined(PNG_READ_INVERT_ALPHA_SUPPORTED) || \
defined(PNG_WRITE_INVERT_ALPHA_SUPPORTED)
void PNGAPI
png_set_invert_alpha(png_structrp png_ptr)
{
png_debug(1, "in png_set_invert_alpha");
if (png_ptr == NULL)
return;
png_ptr->transformations |= PNG_INVERT_ALPHA;
}
#endif
#if defined(PNG_READ_INVERT_SUPPORTED) || defined(PNG_WRITE_INVERT_SUPPORTED)
void PNGAPI
png_set_invert_mono(png_structrp png_ptr)
{
png_debug(1, "in png_set_invert_mono");
if (png_ptr == NULL)
return;
png_ptr->transformations |= PNG_INVERT_MONO;
}
/* Invert monochrome grayscale data */
void /* PRIVATE */
png_do_invert(png_row_infop row_info, png_bytep row)
{
png_debug(1, "in png_do_invert");
/* This test removed from libpng version 1.0.13 and 1.2.0:
* if (row_info->bit_depth == 1 &&
*/
if (row_info->color_type == PNG_COLOR_TYPE_GRAY)
{
png_bytep rp = row;
size_t i;
size_t istop = row_info->rowbytes;
for (i = 0; i < istop; i++)
{
*rp = (png_byte)(~(*rp));
rp++;
}
}
else if (row_info->color_type == PNG_COLOR_TYPE_GRAY_ALPHA &&
row_info->bit_depth == 8)
{
png_bytep rp = row;
size_t i;
size_t istop = row_info->rowbytes;
for (i = 0; i < istop; i += 2)
{
*rp = (png_byte)(~(*rp));
rp += 2;
}
}
#ifdef PNG_16BIT_SUPPORTED
else if (row_info->color_type == PNG_COLOR_TYPE_GRAY_ALPHA &&
row_info->bit_depth == 16)
{
png_bytep rp = row;
size_t i;
size_t istop = row_info->rowbytes;
for (i = 0; i < istop; i += 4)
{
*rp = (png_byte)(~(*rp));
*(rp + 1) = (png_byte)(~(*(rp + 1)));
rp += 4;
}
}
#endif
}
#endif
#ifdef PNG_16BIT_SUPPORTED
#if defined(PNG_READ_SWAP_SUPPORTED) || defined(PNG_WRITE_SWAP_SUPPORTED)
/* Swaps byte order on 16-bit depth images */
void /* PRIVATE */
png_do_swap(png_row_infop row_info, png_bytep row)
{
png_debug(1, "in png_do_swap");
if (row_info->bit_depth == 16)
{
png_bytep rp = row;
png_uint_32 i;
png_uint_32 istop= row_info->width * row_info->channels;
for (i = 0; i < istop; i++, rp += 2)
{
#ifdef PNG_BUILTIN_BSWAP16_SUPPORTED
/* Feature added to libpng-1.6.11 for testing purposes, not
* enabled by default.
*/
*(png_uint_16*)rp = __builtin_bswap16(*(png_uint_16*)rp);
#else
png_byte t = *rp;
*rp = *(rp + 1);
*(rp + 1) = t;
#endif
}
}
}
#endif
#endif
#if defined(PNG_READ_PACKSWAP_SUPPORTED)||defined(PNG_WRITE_PACKSWAP_SUPPORTED)
static const png_byte onebppswaptable[256] = {
0x00, 0x80, 0x40, 0xC0, 0x20, 0xA0, 0x60, 0xE0,
0x10, 0x90, 0x50, 0xD0, 0x30, 0xB0, 0x70, 0xF0,
0x08, 0x88, 0x48, 0xC8, 0x28, 0xA8, 0x68, 0xE8,
0x18, 0x98, 0x58, 0xD8, 0x38, 0xB8, 0x78, 0xF8,
0x04, 0x84, 0x44, 0xC4, 0x24, 0xA4, 0x64, 0xE4,
0x14, 0x94, 0x54, 0xD4, 0x34, 0xB4, 0x74, 0xF4,
0x0C, 0x8C, 0x4C, 0xCC, 0x2C, 0xAC, 0x6C, 0xEC,
0x1C, 0x9C, 0x5C, 0xDC, 0x3C, 0xBC, 0x7C, 0xFC,
0x02, 0x82, 0x42, 0xC2, 0x22, 0xA2, 0x62, 0xE2,
0x12, 0x92, 0x52, 0xD2, 0x32, 0xB2, 0x72, 0xF2,
0x0A, 0x8A, 0x4A, 0xCA, 0x2A, 0xAA, 0x6A, 0xEA,
0x1A, 0x9A, 0x5A, 0xDA, 0x3A, 0xBA, 0x7A, 0xFA,
0x06, 0x86, 0x46, 0xC6, 0x26, 0xA6, 0x66, 0xE6,
0x16, 0x96, 0x56, 0xD6, 0x36, 0xB6, 0x76, 0xF6,
0x0E, 0x8E, 0x4E, 0xCE, 0x2E, 0xAE, 0x6E, 0xEE,
0x1E, 0x9E, 0x5E, 0xDE, 0x3E, 0xBE, 0x7E, 0xFE,
0x01, 0x81, 0x41, 0xC1, 0x21, 0xA1, 0x61, 0xE1,
0x11, 0x91, 0x51, 0xD1, 0x31, 0xB1, 0x71, 0xF1,
0x09, 0x89, 0x49, 0xC9, 0x29, 0xA9, 0x69, 0xE9,
0x19, 0x99, 0x59, 0xD9, 0x39, 0xB9, 0x79, 0xF9,
0x05, 0x85, 0x45, 0xC5, 0x25, 0xA5, 0x65, 0xE5,
0x15, 0x95, 0x55, 0xD5, 0x35, 0xB5, 0x75, 0xF5,
0x0D, 0x8D, 0x4D, 0xCD, 0x2D, 0xAD, 0x6D, 0xED,
0x1D, 0x9D, 0x5D, 0xDD, 0x3D, 0xBD, 0x7D, 0xFD,
0x03, 0x83, 0x43, 0xC3, 0x23, 0xA3, 0x63, 0xE3,
0x13, 0x93, 0x53, 0xD3, 0x33, 0xB3, 0x73, 0xF3,
0x0B, 0x8B, 0x4B, 0xCB, 0x2B, 0xAB, 0x6B, 0xEB,
0x1B, 0x9B, 0x5B, 0xDB, 0x3B, 0xBB, 0x7B, 0xFB,
0x07, 0x87, 0x47, 0xC7, 0x27, 0xA7, 0x67, 0xE7,
0x17, 0x97, 0x57, 0xD7, 0x37, 0xB7, 0x77, 0xF7,
0x0F, 0x8F, 0x4F, 0xCF, 0x2F, 0xAF, 0x6F, 0xEF,
0x1F, 0x9F, 0x5F, 0xDF, 0x3F, 0xBF, 0x7F, 0xFF
};
static const png_byte twobppswaptable[256] = {
0x00, 0x40, 0x80, 0xC0, 0x10, 0x50, 0x90, 0xD0,
0x20, 0x60, 0xA0, 0xE0, 0x30, 0x70, 0xB0, 0xF0,
0x04, 0x44, 0x84, 0xC4, 0x14, 0x54, 0x94, 0xD4,
0x24, 0x64, 0xA4, 0xE4, 0x34, 0x74, 0xB4, 0xF4,
0x08, 0x48, 0x88, 0xC8, 0x18, 0x58, 0x98, 0xD8,
0x28, 0x68, 0xA8, 0xE8, 0x38, 0x78, 0xB8, 0xF8,
0x0C, 0x4C, 0x8C, 0xCC, 0x1C, 0x5C, 0x9C, 0xDC,
0x2C, 0x6C, 0xAC, 0xEC, 0x3C, 0x7C, 0xBC, 0xFC,
0x01, 0x41, 0x81, 0xC1, 0x11, 0x51, 0x91, 0xD1,
0x21, 0x61, 0xA1, 0xE1, 0x31, 0x71, 0xB1, 0xF1,
0x05, 0x45, 0x85, 0xC5, 0x15, 0x55, 0x95, 0xD5,
0x25, 0x65, 0xA5, 0xE5, 0x35, 0x75, 0xB5, 0xF5,
0x09, 0x49, 0x89, 0xC9, 0x19, 0x59, 0x99, 0xD9,
0x29, 0x69, 0xA9, 0xE9, 0x39, 0x79, 0xB9, 0xF9,
0x0D, 0x4D, 0x8D, 0xCD, 0x1D, 0x5D, 0x9D, 0xDD,
0x2D, 0x6D, 0xAD, 0xED, 0x3D, 0x7D, 0xBD, 0xFD,
0x02, 0x42, 0x82, 0xC2, 0x12, 0x52, 0x92, 0xD2,
0x22, 0x62, 0xA2, 0xE2, 0x32, 0x72, 0xB2, 0xF2,
0x06, 0x46, 0x86, 0xC6, 0x16, 0x56, 0x96, 0xD6,
0x26, 0x66, 0xA6, 0xE6, 0x36, 0x76, 0xB6, 0xF6,
0x0A, 0x4A, 0x8A, 0xCA, 0x1A, 0x5A, 0x9A, 0xDA,
0x2A, 0x6A, 0xAA, 0xEA, 0x3A, 0x7A, 0xBA, 0xFA,
0x0E, 0x4E, 0x8E, 0xCE, 0x1E, 0x5E, 0x9E, 0xDE,
0x2E, 0x6E, 0xAE, 0xEE, 0x3E, 0x7E, 0xBE, 0xFE,
0x03, 0x43, 0x83, 0xC3, 0x13, 0x53, 0x93, 0xD3,
0x23, 0x63, 0xA3, 0xE3, 0x33, 0x73, 0xB3, 0xF3,
0x07, 0x47, 0x87, 0xC7, 0x17, 0x57, 0x97, 0xD7,
0x27, 0x67, 0xA7, 0xE7, 0x37, 0x77, 0xB7, 0xF7,
0x0B, 0x4B, 0x8B, 0xCB, 0x1B, 0x5B, 0x9B, 0xDB,
0x2B, 0x6B, 0xAB, 0xEB, 0x3B, 0x7B, 0xBB, 0xFB,
0x0F, 0x4F, 0x8F, 0xCF, 0x1F, 0x5F, 0x9F, 0xDF,
0x2F, 0x6F, 0xAF, 0xEF, 0x3F, 0x7F, 0xBF, 0xFF
};
static const png_byte fourbppswaptable[256] = {
0x00, 0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70,
0x80, 0x90, 0xA0, 0xB0, 0xC0, 0xD0, 0xE0, 0xF0,
0x01, 0x11, 0x21, 0x31, 0x41, 0x51, 0x61, 0x71,
0x81, 0x91, 0xA1, 0xB1, 0xC1, 0xD1, 0xE1, 0xF1,
0x02, 0x12, 0x22, 0x32, 0x42, 0x52, 0x62, 0x72,
0x82, 0x92, 0xA2, 0xB2, 0xC2, 0xD2, 0xE2, 0xF2,
0x03, 0x13, 0x23, 0x33, 0x43, 0x53, 0x63, 0x73,
0x83, 0x93, 0xA3, 0xB3, 0xC3, 0xD3, 0xE3, 0xF3,
0x04, 0x14, 0x24, 0x34, 0x44, 0x54, 0x64, 0x74,
0x84, 0x94, 0xA4, 0xB4, 0xC4, 0xD4, 0xE4, 0xF4,
0x05, 0x15, 0x25, 0x35, 0x45, 0x55, 0x65, 0x75,
0x85, 0x95, 0xA5, 0xB5, 0xC5, 0xD5, 0xE5, 0xF5,
0x06, 0x16, 0x26, 0x36, 0x46, 0x56, 0x66, 0x76,
0x86, 0x96, 0xA6, 0xB6, 0xC6, 0xD6, 0xE6, 0xF6,
0x07, 0x17, 0x27, 0x37, 0x47, 0x57, 0x67, 0x77,
0x87, 0x97, 0xA7, 0xB7, 0xC7, 0xD7, 0xE7, 0xF7,
0x08, 0x18, 0x28, 0x38, 0x48, 0x58, 0x68, 0x78,
0x88, 0x98, 0xA8, 0xB8, 0xC8, 0xD8, 0xE8, 0xF8,
0x09, 0x19, 0x29, 0x39, 0x49, 0x59, 0x69, 0x79,
0x89, 0x99, 0xA9, 0xB9, 0xC9, 0xD9, 0xE9, 0xF9,
0x0A, 0x1A, 0x2A, 0x3A, 0x4A, 0x5A, 0x6A, 0x7A,
0x8A, 0x9A, 0xAA, 0xBA, 0xCA, 0xDA, 0xEA, 0xFA,
0x0B, 0x1B, 0x2B, 0x3B, 0x4B, 0x5B, 0x6B, 0x7B,
0x8B, 0x9B, 0xAB, 0xBB, 0xCB, 0xDB, 0xEB, 0xFB,
0x0C, 0x1C, 0x2C, 0x3C, 0x4C, 0x5C, 0x6C, 0x7C,
0x8C, 0x9C, 0xAC, 0xBC, 0xCC, 0xDC, 0xEC, 0xFC,
0x0D, 0x1D, 0x2D, 0x3D, 0x4D, 0x5D, 0x6D, 0x7D,
0x8D, 0x9D, 0xAD, 0xBD, 0xCD, 0xDD, 0xED, 0xFD,
0x0E, 0x1E, 0x2E, 0x3E, 0x4E, 0x5E, 0x6E, 0x7E,
0x8E, 0x9E, 0xAE, 0xBE, 0xCE, 0xDE, 0xEE, 0xFE,
0x0F, 0x1F, 0x2F, 0x3F, 0x4F, 0x5F, 0x6F, 0x7F,
0x8F, 0x9F, 0xAF, 0xBF, 0xCF, 0xDF, 0xEF, 0xFF
};
/* Swaps pixel packing order within bytes */
void /* PRIVATE */
png_do_packswap(png_row_infop row_info, png_bytep row)
{
png_debug(1, "in png_do_packswap");
if (row_info->bit_depth < 8)
{
png_bytep rp;
png_const_bytep end, table;
end = row + row_info->rowbytes;
if (row_info->bit_depth == 1)
table = onebppswaptable;
else if (row_info->bit_depth == 2)
table = twobppswaptable;
else if (row_info->bit_depth == 4)
table = fourbppswaptable;
else
return;
for (rp = row; rp < end; rp++)
*rp = table[*rp];
}
}
#endif /* PACKSWAP || WRITE_PACKSWAP */
#if defined(PNG_WRITE_FILLER_SUPPORTED) || \
defined(PNG_READ_STRIP_ALPHA_SUPPORTED)
/* Remove a channel - this used to be 'png_do_strip_filler' but it used a
* somewhat weird combination of flags to determine what to do. All the calls
* to png_do_strip_filler are changed in 1.5.2 to call this instead with the
* correct arguments.
*
* The routine isn't general - the channel must be the channel at the start or
* end (not in the middle) of each pixel.
*/
void /* PRIVATE */
png_do_strip_channel(png_row_infop row_info, png_bytep row, int at_start)
{
png_bytep sp = row; /* source pointer */
png_bytep dp = row; /* destination pointer */
png_bytep ep = row + row_info->rowbytes; /* One beyond end of row */
/* At the start sp will point to the first byte to copy and dp to where
* it is copied to. ep always points just beyond the end of the row, so
* the loop simply copies (channels-1) channels until sp reaches ep.
*
* at_start: 0 -- convert AG, XG, ARGB, XRGB, AAGG, XXGG, etc.
* nonzero -- convert GA, GX, RGBA, RGBX, GGAA, RRGGBBXX, etc.
*/
/* GA, GX, XG cases */
if (row_info->channels == 2)
{
if (row_info->bit_depth == 8)
{
if (at_start != 0) /* Skip initial filler */
++sp;
else /* Skip initial channel and, for sp, the filler */
{
sp += 2; ++dp;
}
/* For a 1 pixel wide image there is nothing to do */
while (sp < ep)
{
*dp++ = *sp; sp += 2;
}
row_info->pixel_depth = 8;
}
else if (row_info->bit_depth == 16)
{
if (at_start != 0) /* Skip initial filler */
sp += 2;
else /* Skip initial channel and, for sp, the filler */
{
sp += 4; dp += 2;
}
while (sp < ep)
{
*dp++ = *sp++; *dp++ = *sp; sp += 3;
}
row_info->pixel_depth = 16;
}
else
return; /* bad bit depth */
row_info->channels = 1;
/* Finally fix the color type if it records an alpha channel */
if (row_info->color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
row_info->color_type = PNG_COLOR_TYPE_GRAY;
}
/* RGBA, RGBX, XRGB cases */
else if (row_info->channels == 4)
{
if (row_info->bit_depth == 8)
{
if (at_start != 0) /* Skip initial filler */
++sp;
else /* Skip initial channels and, for sp, the filler */
{
sp += 4; dp += 3;
}
/* Note that the loop adds 3 to dp and 4 to sp each time. */
while (sp < ep)
{
*dp++ = *sp++; *dp++ = *sp++; *dp++ = *sp; sp += 2;
}
row_info->pixel_depth = 24;
}
else if (row_info->bit_depth == 16)
{
if (at_start != 0) /* Skip initial filler */
sp += 2;
else /* Skip initial channels and, for sp, the filler */
{
sp += 8; dp += 6;
}
while (sp < ep)
{
/* Copy 6 bytes, skip 2 */
*dp++ = *sp++; *dp++ = *sp++;
*dp++ = *sp++; *dp++ = *sp++;
*dp++ = *sp++; *dp++ = *sp; sp += 3;
}
row_info->pixel_depth = 48;
}
else
return; /* bad bit depth */
row_info->channels = 3;
/* Finally fix the color type if it records an alpha channel */
if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA)
row_info->color_type = PNG_COLOR_TYPE_RGB;
}
else
return; /* The filler channel has gone already */
/* Fix the rowbytes value. */
row_info->rowbytes = (size_t)(dp-row);
}
#endif
#if defined(PNG_READ_BGR_SUPPORTED) || defined(PNG_WRITE_BGR_SUPPORTED)
/* Swaps red and blue bytes within a pixel */
void /* PRIVATE */
png_do_bgr(png_row_infop row_info, png_bytep row)
{
png_debug(1, "in png_do_bgr");
if ((row_info->color_type & PNG_COLOR_MASK_COLOR) != 0)
{
png_uint_32 row_width = row_info->width;
if (row_info->bit_depth == 8)
{
if (row_info->color_type == PNG_COLOR_TYPE_RGB)
{
png_bytep rp;
png_uint_32 i;
for (i = 0, rp = row; i < row_width; i++, rp += 3)
{
png_byte save = *rp;
*rp = *(rp + 2);
*(rp + 2) = save;
}
}
else if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA)
{
png_bytep rp;
png_uint_32 i;
for (i = 0, rp = row; i < row_width; i++, rp += 4)
{
png_byte save = *rp;
*rp = *(rp + 2);
*(rp + 2) = save;
}
}
}
#ifdef PNG_16BIT_SUPPORTED
else if (row_info->bit_depth == 16)
{
if (row_info->color_type == PNG_COLOR_TYPE_RGB)
{
png_bytep rp;
png_uint_32 i;
for (i = 0, rp = row; i < row_width; i++, rp += 6)
{
png_byte save = *rp;
*rp = *(rp + 4);
*(rp + 4) = save;
save = *(rp + 1);
*(rp + 1) = *(rp + 5);
*(rp + 5) = save;
}
}
else if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA)
{
png_bytep rp;
png_uint_32 i;
for (i = 0, rp = row; i < row_width; i++, rp += 8)
{
png_byte save = *rp;
*rp = *(rp + 4);
*(rp + 4) = save;
save = *(rp + 1);
*(rp + 1) = *(rp + 5);
*(rp + 5) = save;
}
}
}
#endif
}
}
#endif /* READ_BGR || WRITE_BGR */
#if defined(PNG_READ_CHECK_FOR_INVALID_INDEX_SUPPORTED) || \
defined(PNG_WRITE_CHECK_FOR_INVALID_INDEX_SUPPORTED)
/* Added at libpng-1.5.10 */
void /* PRIVATE */
png_do_check_palette_indexes(png_structrp png_ptr, png_row_infop row_info)
{
if (png_ptr->num_palette < (1 << row_info->bit_depth) &&
png_ptr->num_palette > 0) /* num_palette can be 0 in MNG files */
{
/* Calculations moved outside switch in an attempt to stop different
* compiler warnings. 'padding' is in *bits* within the last byte, it is
* an 'int' because pixel_depth becomes an 'int' in the expression below,
* and this calculation is used because it avoids warnings that other
* forms produced on either GCC or MSVC.
*/
int padding = PNG_PADBITS(row_info->pixel_depth, row_info->width);
png_bytep rp = png_ptr->row_buf + row_info->rowbytes - 1;
switch (row_info->bit_depth)
{
case 1:
{
/* in this case, all bytes must be 0 so we don't need
* to unpack the pixels except for the rightmost one.
*/
for (; rp > png_ptr->row_buf; rp--)
{
if ((*rp >> padding) != 0)
png_ptr->num_palette_max = 1;
padding = 0;
}
break;
}
case 2:
{
for (; rp > png_ptr->row_buf; rp--)
{
int i = ((*rp >> padding) & 0x03);
if (i > png_ptr->num_palette_max)
png_ptr->num_palette_max = i;
i = (((*rp >> padding) >> 2) & 0x03);
if (i > png_ptr->num_palette_max)
png_ptr->num_palette_max = i;
i = (((*rp >> padding) >> 4) & 0x03);
if (i > png_ptr->num_palette_max)
png_ptr->num_palette_max = i;
i = (((*rp >> padding) >> 6) & 0x03);
if (i > png_ptr->num_palette_max)
png_ptr->num_palette_max = i;
padding = 0;
}
break;
}
case 4:
{
for (; rp > png_ptr->row_buf; rp--)
{
int i = ((*rp >> padding) & 0x0f);
if (i > png_ptr->num_palette_max)
png_ptr->num_palette_max = i;
i = (((*rp >> padding) >> 4) & 0x0f);
if (i > png_ptr->num_palette_max)
png_ptr->num_palette_max = i;
padding = 0;
}
break;
}
case 8:
{
for (; rp > png_ptr->row_buf; rp--)
{
if (*rp > png_ptr->num_palette_max)
png_ptr->num_palette_max = (int) *rp;
}
break;
}
default:
break;
}
}
}
#endif /* CHECK_FOR_INVALID_INDEX */
#if defined(PNG_READ_USER_TRANSFORM_SUPPORTED) || \
defined(PNG_WRITE_USER_TRANSFORM_SUPPORTED)
#ifdef PNG_USER_TRANSFORM_PTR_SUPPORTED
void PNGAPI
png_set_user_transform_info(png_structrp png_ptr, png_voidp
user_transform_ptr, int user_transform_depth, int user_transform_channels)
{
png_debug(1, "in png_set_user_transform_info");
if (png_ptr == NULL)
return;
#ifdef PNG_READ_USER_TRANSFORM_SUPPORTED
if ((png_ptr->mode & PNG_IS_READ_STRUCT) != 0 &&
(png_ptr->flags & PNG_FLAG_ROW_INIT) != 0)
{
png_app_error(png_ptr,
"info change after png_start_read_image or png_read_update_info");
return;
}
#endif
png_ptr->user_transform_ptr = user_transform_ptr;
png_ptr->user_transform_depth = (png_byte)user_transform_depth;
png_ptr->user_transform_channels = (png_byte)user_transform_channels;
}
#endif
/* This function returns a pointer to the user_transform_ptr associated with
* the user transform functions. The application should free any memory
* associated with this pointer before png_write_destroy and png_read_destroy
* are called.
*/
#ifdef PNG_USER_TRANSFORM_PTR_SUPPORTED
png_voidp PNGAPI
png_get_user_transform_ptr(png_const_structrp png_ptr)
{
if (png_ptr == NULL)
return (NULL);
return png_ptr->user_transform_ptr;
}
#endif
#ifdef PNG_USER_TRANSFORM_INFO_SUPPORTED
png_uint_32 PNGAPI
png_get_current_row_number(png_const_structrp png_ptr)
{
/* See the comments in png.h - this is the sub-image row when reading an
* interlaced image.
*/
if (png_ptr != NULL)
return png_ptr->row_number;
return PNG_UINT_32_MAX; /* help the app not to fail silently */
}
png_byte PNGAPI
png_get_current_pass_number(png_const_structrp png_ptr)
{
if (png_ptr != NULL)
return png_ptr->pass;
return 8; /* invalid */
}
#endif /* USER_TRANSFORM_INFO */
#endif /* READ_USER_TRANSFORM || WRITE_USER_TRANSFORM */
#endif /* READ || WRITE */

168
hecl/extern/libpng/pngwio.c vendored Normal file
View File

@ -0,0 +1,168 @@
/* pngwio.c - functions for data output
*
* Copyright (c) 2018 Cosmin Truta
* Copyright (c) 1998-2002,2004,2006-2014,2016,2018 Glenn Randers-Pehrson
* Copyright (c) 1996-1997 Andreas Dilger
* Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc.
*
* This code is released under the libpng license.
* For conditions of distribution and use, see the disclaimer
* and license in png.h
*
* This file provides a location for all output. Users who need
* special handling are expected to write functions that have the same
* arguments as these and perform similar functions, but that possibly
* use different output methods. Note that you shouldn't change these
* functions, but rather write replacement functions and then change
* them at run time with png_set_write_fn(...).
*/
#include "pngpriv.h"
#ifdef PNG_WRITE_SUPPORTED
/* Write the data to whatever output you are using. The default routine
* writes to a file pointer. Note that this routine sometimes gets called
* with very small lengths, so you should implement some kind of simple
* buffering if you are using unbuffered writes. This should never be asked
* to write more than 64K on a 16-bit machine.
*/
void /* PRIVATE */
png_write_data(png_structrp png_ptr, png_const_bytep data, size_t length)
{
/* NOTE: write_data_fn must not change the buffer! */
if (png_ptr->write_data_fn != NULL )
(*(png_ptr->write_data_fn))(png_ptr, png_constcast(png_bytep,data),
length);
else
png_error(png_ptr, "Call to NULL write function");
}
#ifdef PNG_STDIO_SUPPORTED
/* This is the function that does the actual writing of data. If you are
* not writing to a standard C stream, you should create a replacement
* write_data function and use it at run time with png_set_write_fn(), rather
* than changing the library.
*/
void PNGCBAPI
png_default_write_data(png_structp png_ptr, png_bytep data, size_t length)
{
size_t check;
if (png_ptr == NULL)
return;
check = fwrite(data, 1, length, (png_FILE_p)(png_ptr->io_ptr));
if (check != length)
png_error(png_ptr, "Write Error");
}
#endif
/* This function is called to output any data pending writing (normally
* to disk). After png_flush is called, there should be no data pending
* writing in any buffers.
*/
#ifdef PNG_WRITE_FLUSH_SUPPORTED
void /* PRIVATE */
png_flush(png_structrp png_ptr)
{
if (png_ptr->output_flush_fn != NULL)
(*(png_ptr->output_flush_fn))(png_ptr);
}
# ifdef PNG_STDIO_SUPPORTED
void PNGCBAPI
png_default_flush(png_structp png_ptr)
{
png_FILE_p io_ptr;
if (png_ptr == NULL)
return;
io_ptr = png_voidcast(png_FILE_p, (png_ptr->io_ptr));
fflush(io_ptr);
}
# endif
#endif
/* This function allows the application to supply new output functions for
* libpng if standard C streams aren't being used.
*
* This function takes as its arguments:
* png_ptr - pointer to a png output data structure
* io_ptr - pointer to user supplied structure containing info about
* the output functions. May be NULL.
* write_data_fn - pointer to a new output function that takes as its
* arguments a pointer to a png_struct, a pointer to
* data to be written, and a 32-bit unsigned int that is
* the number of bytes to be written. The new write
* function should call png_error(png_ptr, "Error msg")
* to exit and output any fatal error messages. May be
* NULL, in which case libpng's default function will
* be used.
* flush_data_fn - pointer to a new flush function that takes as its
* arguments a pointer to a png_struct. After a call to
* the flush function, there should be no data in any buffers
* or pending transmission. If the output method doesn't do
* any buffering of output, a function prototype must still be
* supplied although it doesn't have to do anything. If
* PNG_WRITE_FLUSH_SUPPORTED is not defined at libpng compile
* time, output_flush_fn will be ignored, although it must be
* supplied for compatibility. May be NULL, in which case
* libpng's default function will be used, if
* PNG_WRITE_FLUSH_SUPPORTED is defined. This is not
* a good idea if io_ptr does not point to a standard
* *FILE structure.
*/
void PNGAPI
png_set_write_fn(png_structrp png_ptr, png_voidp io_ptr,
png_rw_ptr write_data_fn, png_flush_ptr output_flush_fn)
{
if (png_ptr == NULL)
return;
png_ptr->io_ptr = io_ptr;
#ifdef PNG_STDIO_SUPPORTED
if (write_data_fn != NULL)
png_ptr->write_data_fn = write_data_fn;
else
png_ptr->write_data_fn = png_default_write_data;
#else
png_ptr->write_data_fn = write_data_fn;
#endif
#ifdef PNG_WRITE_FLUSH_SUPPORTED
# ifdef PNG_STDIO_SUPPORTED
if (output_flush_fn != NULL)
png_ptr->output_flush_fn = output_flush_fn;
else
png_ptr->output_flush_fn = png_default_flush;
# else
png_ptr->output_flush_fn = output_flush_fn;
# endif
#else
PNG_UNUSED(output_flush_fn)
#endif /* WRITE_FLUSH */
#ifdef PNG_READ_SUPPORTED
/* It is an error to read while writing a png file */
if (png_ptr->read_data_fn != NULL)
{
png_ptr->read_data_fn = NULL;
png_warning(png_ptr,
"Can't set both read_data_fn and write_data_fn in the"
" same structure");
}
#endif
}
#endif /* WRITE */

2395
hecl/extern/libpng/pngwrite.c vendored Normal file

File diff suppressed because it is too large Load Diff

575
hecl/extern/libpng/pngwtran.c vendored Normal file
View File

@ -0,0 +1,575 @@
/* pngwtran.c - transforms the data in a row for PNG writers
*
* Copyright (c) 2018 Cosmin Truta
* Copyright (c) 1998-2002,2004,2006-2016,2018 Glenn Randers-Pehrson
* Copyright (c) 1996-1997 Andreas Dilger
* Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc.
*
* This code is released under the libpng license.
* For conditions of distribution and use, see the disclaimer
* and license in png.h
*/
#include "pngpriv.h"
#ifdef PNG_WRITE_SUPPORTED
#ifdef PNG_WRITE_TRANSFORMS_SUPPORTED
#ifdef PNG_WRITE_PACK_SUPPORTED
/* Pack pixels into bytes. Pass the true bit depth in bit_depth. The
* row_info bit depth should be 8 (one pixel per byte). The channels
* should be 1 (this only happens on grayscale and paletted images).
*/
static void
png_do_pack(png_row_infop row_info, png_bytep row, png_uint_32 bit_depth)
{
png_debug(1, "in png_do_pack");
if (row_info->bit_depth == 8 &&
row_info->channels == 1)
{
switch ((int)bit_depth)
{
case 1:
{
png_bytep sp, dp;
int mask, v;
png_uint_32 i;
png_uint_32 row_width = row_info->width;
sp = row;
dp = row;
mask = 0x80;
v = 0;
for (i = 0; i < row_width; i++)
{
if (*sp != 0)
v |= mask;
sp++;
if (mask > 1)
mask >>= 1;
else
{
mask = 0x80;
*dp = (png_byte)v;
dp++;
v = 0;
}
}
if (mask != 0x80)
*dp = (png_byte)v;
break;
}
case 2:
{
png_bytep sp, dp;
unsigned int shift;
int v;
png_uint_32 i;
png_uint_32 row_width = row_info->width;
sp = row;
dp = row;
shift = 6;
v = 0;
for (i = 0; i < row_width; i++)
{
png_byte value;
value = (png_byte)(*sp & 0x03);
v |= (value << shift);
if (shift == 0)
{
shift = 6;
*dp = (png_byte)v;
dp++;
v = 0;
}
else
shift -= 2;
sp++;
}
if (shift != 6)
*dp = (png_byte)v;
break;
}
case 4:
{
png_bytep sp, dp;
unsigned int shift;
int v;
png_uint_32 i;
png_uint_32 row_width = row_info->width;
sp = row;
dp = row;
shift = 4;
v = 0;
for (i = 0; i < row_width; i++)
{
png_byte value;
value = (png_byte)(*sp & 0x0f);
v |= (value << shift);
if (shift == 0)
{
shift = 4;
*dp = (png_byte)v;
dp++;
v = 0;
}
else
shift -= 4;
sp++;
}
if (shift != 4)
*dp = (png_byte)v;
break;
}
default:
break;
}
row_info->bit_depth = (png_byte)bit_depth;
row_info->pixel_depth = (png_byte)(bit_depth * row_info->channels);
row_info->rowbytes = PNG_ROWBYTES(row_info->pixel_depth,
row_info->width);
}
}
#endif
#ifdef PNG_WRITE_SHIFT_SUPPORTED
/* Shift pixel values to take advantage of whole range. Pass the
* true number of bits in bit_depth. The row should be packed
* according to row_info->bit_depth. Thus, if you had a row of
* bit depth 4, but the pixels only had values from 0 to 7, you
* would pass 3 as bit_depth, and this routine would translate the
* data to 0 to 15.
*/
static void
png_do_shift(png_row_infop row_info, png_bytep row,
png_const_color_8p bit_depth)
{
png_debug(1, "in png_do_shift");
if (row_info->color_type != PNG_COLOR_TYPE_PALETTE)
{
int shift_start[4], shift_dec[4];
unsigned int channels = 0;
if ((row_info->color_type & PNG_COLOR_MASK_COLOR) != 0)
{
shift_start[channels] = row_info->bit_depth - bit_depth->red;
shift_dec[channels] = bit_depth->red;
channels++;
shift_start[channels] = row_info->bit_depth - bit_depth->green;
shift_dec[channels] = bit_depth->green;
channels++;
shift_start[channels] = row_info->bit_depth - bit_depth->blue;
shift_dec[channels] = bit_depth->blue;
channels++;
}
else
{
shift_start[channels] = row_info->bit_depth - bit_depth->gray;
shift_dec[channels] = bit_depth->gray;
channels++;
}
if ((row_info->color_type & PNG_COLOR_MASK_ALPHA) != 0)
{
shift_start[channels] = row_info->bit_depth - bit_depth->alpha;
shift_dec[channels] = bit_depth->alpha;
channels++;
}
/* With low row depths, could only be grayscale, so one channel */
if (row_info->bit_depth < 8)
{
png_bytep bp = row;
size_t i;
unsigned int mask;
size_t row_bytes = row_info->rowbytes;
if (bit_depth->gray == 1 && row_info->bit_depth == 2)
mask = 0x55;
else if (row_info->bit_depth == 4 && bit_depth->gray == 3)
mask = 0x11;
else
mask = 0xff;
for (i = 0; i < row_bytes; i++, bp++)
{
int j;
unsigned int v, out;
v = *bp;
out = 0;
for (j = shift_start[0]; j > -shift_dec[0]; j -= shift_dec[0])
{
if (j > 0)
out |= v << j;
else
out |= (v >> (-j)) & mask;
}
*bp = (png_byte)(out & 0xff);
}
}
else if (row_info->bit_depth == 8)
{
png_bytep bp = row;
png_uint_32 i;
png_uint_32 istop = channels * row_info->width;
for (i = 0; i < istop; i++, bp++)
{
unsigned int c = i%channels;
int j;
unsigned int v, out;
v = *bp;
out = 0;
for (j = shift_start[c]; j > -shift_dec[c]; j -= shift_dec[c])
{
if (j > 0)
out |= v << j;
else
out |= v >> (-j);
}
*bp = (png_byte)(out & 0xff);
}
}
else
{
png_bytep bp;
png_uint_32 i;
png_uint_32 istop = channels * row_info->width;
for (bp = row, i = 0; i < istop; i++)
{
unsigned int c = i%channels;
int j;
unsigned int value, v;
v = png_get_uint_16(bp);
value = 0;
for (j = shift_start[c]; j > -shift_dec[c]; j -= shift_dec[c])
{
if (j > 0)
value |= v << j;
else
value |= v >> (-j);
}
*bp++ = (png_byte)((value >> 8) & 0xff);
*bp++ = (png_byte)(value & 0xff);
}
}
}
}
#endif
#ifdef PNG_WRITE_SWAP_ALPHA_SUPPORTED
static void
png_do_write_swap_alpha(png_row_infop row_info, png_bytep row)
{
png_debug(1, "in png_do_write_swap_alpha");
{
if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA)
{
if (row_info->bit_depth == 8)
{
/* This converts from ARGB to RGBA */
png_bytep sp, dp;
png_uint_32 i;
png_uint_32 row_width = row_info->width;
for (i = 0, sp = dp = row; i < row_width; i++)
{
png_byte save = *(sp++);
*(dp++) = *(sp++);
*(dp++) = *(sp++);
*(dp++) = *(sp++);
*(dp++) = save;
}
}
#ifdef PNG_WRITE_16BIT_SUPPORTED
else
{
/* This converts from AARRGGBB to RRGGBBAA */
png_bytep sp, dp;
png_uint_32 i;
png_uint_32 row_width = row_info->width;
for (i = 0, sp = dp = row; i < row_width; i++)
{
png_byte save[2];
save[0] = *(sp++);
save[1] = *(sp++);
*(dp++) = *(sp++);
*(dp++) = *(sp++);
*(dp++) = *(sp++);
*(dp++) = *(sp++);
*(dp++) = *(sp++);
*(dp++) = *(sp++);
*(dp++) = save[0];
*(dp++) = save[1];
}
}
#endif /* WRITE_16BIT */
}
else if (row_info->color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
{
if (row_info->bit_depth == 8)
{
/* This converts from AG to GA */
png_bytep sp, dp;
png_uint_32 i;
png_uint_32 row_width = row_info->width;
for (i = 0, sp = dp = row; i < row_width; i++)
{
png_byte save = *(sp++);
*(dp++) = *(sp++);
*(dp++) = save;
}
}
#ifdef PNG_WRITE_16BIT_SUPPORTED
else
{
/* This converts from AAGG to GGAA */
png_bytep sp, dp;
png_uint_32 i;
png_uint_32 row_width = row_info->width;
for (i = 0, sp = dp = row; i < row_width; i++)
{
png_byte save[2];
save[0] = *(sp++);
save[1] = *(sp++);
*(dp++) = *(sp++);
*(dp++) = *(sp++);
*(dp++) = save[0];
*(dp++) = save[1];
}
}
#endif /* WRITE_16BIT */
}
}
}
#endif
#ifdef PNG_WRITE_INVERT_ALPHA_SUPPORTED
static void
png_do_write_invert_alpha(png_row_infop row_info, png_bytep row)
{
png_debug(1, "in png_do_write_invert_alpha");
{
if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA)
{
if (row_info->bit_depth == 8)
{
/* This inverts the alpha channel in RGBA */
png_bytep sp, dp;
png_uint_32 i;
png_uint_32 row_width = row_info->width;
for (i = 0, sp = dp = row; i < row_width; i++)
{
/* Does nothing
*(dp++) = *(sp++);
*(dp++) = *(sp++);
*(dp++) = *(sp++);
*/
sp+=3; dp = sp;
*dp = (png_byte)(255 - *(sp++));
}
}
#ifdef PNG_WRITE_16BIT_SUPPORTED
else
{
/* This inverts the alpha channel in RRGGBBAA */
png_bytep sp, dp;
png_uint_32 i;
png_uint_32 row_width = row_info->width;
for (i = 0, sp = dp = row; i < row_width; i++)
{
/* Does nothing
*(dp++) = *(sp++);
*(dp++) = *(sp++);
*(dp++) = *(sp++);
*(dp++) = *(sp++);
*(dp++) = *(sp++);
*(dp++) = *(sp++);
*/
sp+=6; dp = sp;
*(dp++) = (png_byte)(255 - *(sp++));
*dp = (png_byte)(255 - *(sp++));
}
}
#endif /* WRITE_16BIT */
}
else if (row_info->color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
{
if (row_info->bit_depth == 8)
{
/* This inverts the alpha channel in GA */
png_bytep sp, dp;
png_uint_32 i;
png_uint_32 row_width = row_info->width;
for (i = 0, sp = dp = row; i < row_width; i++)
{
*(dp++) = *(sp++);
*(dp++) = (png_byte)(255 - *(sp++));
}
}
#ifdef PNG_WRITE_16BIT_SUPPORTED
else
{
/* This inverts the alpha channel in GGAA */
png_bytep sp, dp;
png_uint_32 i;
png_uint_32 row_width = row_info->width;
for (i = 0, sp = dp = row; i < row_width; i++)
{
/* Does nothing
*(dp++) = *(sp++);
*(dp++) = *(sp++);
*/
sp+=2; dp = sp;
*(dp++) = (png_byte)(255 - *(sp++));
*dp = (png_byte)(255 - *(sp++));
}
}
#endif /* WRITE_16BIT */
}
}
}
#endif
/* Transform the data according to the user's wishes. The order of
* transformations is significant.
*/
void /* PRIVATE */
png_do_write_transformations(png_structrp png_ptr, png_row_infop row_info)
{
png_debug(1, "in png_do_write_transformations");
if (png_ptr == NULL)
return;
#ifdef PNG_WRITE_USER_TRANSFORM_SUPPORTED
if ((png_ptr->transformations & PNG_USER_TRANSFORM) != 0)
if (png_ptr->write_user_transform_fn != NULL)
(*(png_ptr->write_user_transform_fn)) /* User write transform
function */
(png_ptr, /* png_ptr */
row_info, /* row_info: */
/* png_uint_32 width; width of row */
/* size_t rowbytes; number of bytes in row */
/* png_byte color_type; color type of pixels */
/* png_byte bit_depth; bit depth of samples */
/* png_byte channels; number of channels (1-4) */
/* png_byte pixel_depth; bits per pixel (depth*channels) */
png_ptr->row_buf + 1); /* start of pixel data for row */
#endif
#ifdef PNG_WRITE_FILLER_SUPPORTED
if ((png_ptr->transformations & PNG_FILLER) != 0)
png_do_strip_channel(row_info, png_ptr->row_buf + 1,
!(png_ptr->flags & PNG_FLAG_FILLER_AFTER));
#endif
#ifdef PNG_WRITE_PACKSWAP_SUPPORTED
if ((png_ptr->transformations & PNG_PACKSWAP) != 0)
png_do_packswap(row_info, png_ptr->row_buf + 1);
#endif
#ifdef PNG_WRITE_PACK_SUPPORTED
if ((png_ptr->transformations & PNG_PACK) != 0)
png_do_pack(row_info, png_ptr->row_buf + 1,
(png_uint_32)png_ptr->bit_depth);
#endif
#ifdef PNG_WRITE_SWAP_SUPPORTED
# ifdef PNG_16BIT_SUPPORTED
if ((png_ptr->transformations & PNG_SWAP_BYTES) != 0)
png_do_swap(row_info, png_ptr->row_buf + 1);
# endif
#endif
#ifdef PNG_WRITE_SHIFT_SUPPORTED
if ((png_ptr->transformations & PNG_SHIFT) != 0)
png_do_shift(row_info, png_ptr->row_buf + 1,
&(png_ptr->shift));
#endif
#ifdef PNG_WRITE_SWAP_ALPHA_SUPPORTED
if ((png_ptr->transformations & PNG_SWAP_ALPHA) != 0)
png_do_write_swap_alpha(row_info, png_ptr->row_buf + 1);
#endif
#ifdef PNG_WRITE_INVERT_ALPHA_SUPPORTED
if ((png_ptr->transformations & PNG_INVERT_ALPHA) != 0)
png_do_write_invert_alpha(row_info, png_ptr->row_buf + 1);
#endif
#ifdef PNG_WRITE_BGR_SUPPORTED
if ((png_ptr->transformations & PNG_BGR) != 0)
png_do_bgr(row_info, png_ptr->row_buf + 1);
#endif
#ifdef PNG_WRITE_INVERT_SUPPORTED
if ((png_ptr->transformations & PNG_INVERT_MONO) != 0)
png_do_invert(row_info, png_ptr->row_buf + 1);
#endif
}
#endif /* WRITE_TRANSFORMS */
#endif /* WRITE */

2781
hecl/extern/libpng/pngwutil.c vendored Normal file

File diff suppressed because it is too large Load Diff

29
hecl/extra/hecl_autocomplete.sh Executable file
View File

@ -0,0 +1,29 @@
#!/bin/bash
_hecl ()
{
local word=${COMP_WORDS[COMP_CWORD]}
local filecmds=(init spec extract add remove group cook clean package)
if [ $COMP_CWORD == 1 ]
then
COMPREPLY=($(compgen -W "${filecmds[*]} help" "${word}"))
return 0
elif [ $COMP_CWORD == 2 ]
then
case ${COMP_WORDS[1]} in
init|extract|add|remove|group|cook|clean|package)
COMPREPLY=($(compgen -f -- "${word}"))
;;
spec)
COMPREPLY=($(compgen -W "enable disable" "${word}"))
;;
help)
COMPREPLY=($(compgen -W "${filecmds[*]}" "${word}"))
;;
esac
fi
}
complete -F _hecl hecl

View File

@ -0,0 +1,16 @@
/* CMake-curated application reps header */
#define STAGE_SPECIALIZATIONS(T, P) \
T<P, hecl::PipelineStage::Vertex>, \
T<P, hecl::PipelineStage::Fragment>, \
T<P, hecl::PipelineStage::Geometry>, \
T<P, hecl::PipelineStage::Control>, \
T<P, hecl::PipelineStage::Evaluation>,
@HECL_APPLICATION_REPS_INCLUDES_LOCAL@
#define HECL_APPLICATION_PIPELINE_REPS_UNIVERSAL \
@HECL_APPLICATION_PIPELINE_REPS_UNIVERSAL_LOCAL@
#define HECL_APPLICATION_STAGE_REPS(P, S) \
@HECL_APPLICATION_STAGE_REPS_LOCAL@

View File

@ -0,0 +1,299 @@
#pragma once
#include <cstddef>
#include <cstdint>
#include <string_view>
#include <vector>
#include <boo/graphicsdev/IGraphicsDataFactory.hpp>
#include "hecl.hpp"
#include "../extern/boo/xxhash/xxhash.h"
namespace hecl::Backend {
struct ExtensionSlot;
using namespace std::literals;
enum class TexCoordSource : uint8_t {
Invalid = 0xff,
Position = 0,
Normal = 1,
Tex0 = 2,
Tex1 = 3,
Tex2 = 4,
Tex3 = 5,
Tex4 = 6,
Tex5 = 7,
Tex6 = 8,
Tex7 = 9,
};
enum class BlendFactor : uint8_t {
Zero,
One,
SrcColor,
InvSrcColor,
DstColor,
InvDstColor,
SrcAlpha,
InvSrcAlpha,
DstAlpha,
InvDstAlpha,
SrcColor1,
InvSrcColor1,
Original = 0xff
};
constexpr std::string_view BlendFactorToDefine(BlendFactor factor, BlendFactor defaultFactor) {
switch (factor) {
case BlendFactor::Zero:
return "ZERO"sv;
case BlendFactor::One:
return "ONE"sv;
case BlendFactor::SrcColor:
return "SRCCOLOR"sv;
case BlendFactor::InvSrcColor:
return "INVSRCCOLOR"sv;
case BlendFactor::DstColor:
return "DSTCOLOR"sv;
case BlendFactor::InvDstColor:
return "INVDSTCOLOR"sv;
case BlendFactor::SrcAlpha:
return "SRCALPHA"sv;
case BlendFactor::InvSrcAlpha:
return "INVSRCALPHA"sv;
case BlendFactor::DstAlpha:
return "DSTALPHA"sv;
case BlendFactor::InvDstAlpha:
return "INVDSTALPHA"sv;
case BlendFactor::SrcColor1:
return "SRCCOLOR1"sv;
case BlendFactor::InvSrcColor1:
return "INVSRCCOLOR1"sv;
default:
return BlendFactorToDefine(defaultFactor, BlendFactor::Zero);
}
}
enum class ZTest : uint8_t { None, LEqual, Greater, Equal, GEqual, Original = 0xff };
enum class CullMode : uint8_t { None, Backface, Frontface, Original = 0xff };
struct TextureInfo {
TexCoordSource src;
uint8_t mtxIdx;
bool normalize;
};
enum class ReflectionType { None, Simple, Indirect };
/**
* @brief Hash subclass for identifying shaders and their metadata
*/
class ShaderTag : public Hash {
union {
uint64_t m_meta = 0;
struct {
uint8_t m_colorCount;
uint8_t m_uvCount;
uint8_t m_weightCount;
uint8_t m_skinSlotCount;
uint8_t m_primitiveType;
uint8_t m_reflectionType;
bool m_depthTest : 1;
bool m_depthWrite : 1;
bool m_backfaceCulling : 1;
bool m_alphaTest : 1;
};
};
public:
ShaderTag() = default;
ShaderTag(std::string_view source, uint8_t c, uint8_t u, uint8_t w, uint8_t s, boo::Primitive pt,
Backend::ReflectionType reflectionType, bool depthTest, bool depthWrite, bool backfaceCulling,
bool alphaTest)
: Hash(source) {
m_colorCount = c;
m_uvCount = u;
m_weightCount = w;
m_skinSlotCount = s;
m_primitiveType = uint8_t(pt);
m_reflectionType = uint8_t(reflectionType);
m_depthTest = depthTest;
m_depthWrite = depthWrite;
m_backfaceCulling = backfaceCulling;
m_alphaTest = alphaTest;
hash ^= m_meta;
}
ShaderTag(uint64_t hashin, uint8_t c, uint8_t u, uint8_t w, uint8_t s, boo::Primitive pt,
Backend::ReflectionType reflectionType, bool depthTest, bool depthWrite, bool backfaceCulling,
bool alphaTest)
: Hash(hashin) {
m_colorCount = c;
m_uvCount = u;
m_weightCount = w;
m_skinSlotCount = s;
m_primitiveType = uint8_t(pt);
m_reflectionType = uint8_t(reflectionType);
m_depthTest = depthTest;
m_depthWrite = depthWrite;
m_backfaceCulling = backfaceCulling;
m_alphaTest = alphaTest;
hash ^= m_meta;
}
ShaderTag(uint64_t comphashin, uint64_t meta) : Hash(comphashin), m_meta(meta) {}
ShaderTag(const ShaderTag& other) : Hash(other), m_meta(other.m_meta) {}
uint8_t getColorCount() const { return m_colorCount; }
uint8_t getUvCount() const { return m_uvCount; }
uint8_t getWeightCount() const { return m_weightCount; }
uint8_t getSkinSlotCount() const { return m_skinSlotCount; }
boo::Primitive getPrimType() const { return boo::Primitive(m_primitiveType); }
Backend::ReflectionType getReflectionType() const { return Backend::ReflectionType(m_reflectionType); }
bool getDepthTest() const { return m_depthTest; }
bool getDepthWrite() const { return m_depthWrite; }
bool getBackfaceCulling() const { return m_backfaceCulling; }
bool getAlphaTest() const { return m_alphaTest; }
uint64_t getMetaData() const { return m_meta; }
std::vector<boo::VertexElementDescriptor> vertexFormat() const {
std::vector<boo::VertexElementDescriptor> ret;
size_t elemCount = 2 + m_colorCount + m_uvCount + m_weightCount;
ret.resize(elemCount);
ret[0].semantic = boo::VertexSemantic::Position3;
ret[1].semantic = boo::VertexSemantic::Normal3;
size_t e = 2;
for (size_t i = 0; i < m_colorCount; ++i, ++e) {
ret[e].semantic = boo::VertexSemantic::ColorUNorm;
ret[e].semanticIdx = i;
}
for (size_t i = 0; i < m_uvCount; ++i, ++e) {
ret[e].semantic = boo::VertexSemantic::UV2;
ret[e].semanticIdx = i;
}
for (size_t i = 0; i < m_weightCount; ++i, ++e) {
ret[e].semantic = boo::VertexSemantic::Weight;
ret[e].semanticIdx = i;
}
return ret;
}
boo::AdditionalPipelineInfo additionalInfo(const ExtensionSlot& ext,
std::pair<BlendFactor, BlendFactor> blendFactors) const;
};
struct Function {
std::string_view m_source;
std::string_view m_entry;
Function() = default;
Function(std::string_view source, std::string_view entry) : m_source(source), m_entry(entry) {}
};
struct ExtensionSlot {
const char* shaderMacro = nullptr;
size_t texCount = 0;
const Backend::TextureInfo* texs = nullptr;
Backend::BlendFactor srcFactor = Backend::BlendFactor::Original;
Backend::BlendFactor dstFactor = Backend::BlendFactor::Original;
Backend::ZTest depthTest = Backend::ZTest::Original;
Backend::CullMode cullMode = Backend::CullMode::Backface;
bool noDepthWrite = false;
bool noColorWrite = false;
bool noAlphaWrite = false;
bool noAlphaOverwrite = false;
bool noReflection = false;
bool forceAlphaTest = false;
bool diffuseOnly = false;
constexpr ExtensionSlot(size_t texCount = 0, const Backend::TextureInfo* texs = nullptr,
Backend::BlendFactor srcFactor = Backend::BlendFactor::Original,
Backend::BlendFactor dstFactor = Backend::BlendFactor::Original,
Backend::ZTest depthTest = Backend::ZTest::Original,
Backend::CullMode cullMode = Backend::CullMode::Backface, bool noDepthWrite = false,
bool noColorWrite = false, bool noAlphaWrite = false, bool noAlphaOverwrite = false,
bool noReflection = false, bool forceAlphaTest = false, bool diffuseOnly = false) noexcept
: texCount(texCount)
, texs(texs)
, srcFactor(srcFactor)
, dstFactor(dstFactor)
, depthTest(depthTest)
, cullMode(cullMode)
, noDepthWrite(noDepthWrite)
, noColorWrite(noColorWrite)
, noAlphaWrite(noAlphaWrite)
, noAlphaOverwrite(noAlphaOverwrite)
, noReflection(noReflection)
, forceAlphaTest(forceAlphaTest)
, diffuseOnly(diffuseOnly) {}
mutable uint64_t m_hash = 0;
void calculateHash() const {
XXH64_state_t st;
XXH64_reset(&st, 0);
XXH64_update(&st, shaderMacro, strlen(shaderMacro));
for (size_t i = 0; i < texCount; ++i) {
const Backend::TextureInfo& tinfo = texs[i];
XXH64_update(&st, &tinfo, sizeof(tinfo));
}
XXH64_update(&st, &srcFactor, offsetof(ExtensionSlot, m_hash) - offsetof(ExtensionSlot, srcFactor));
m_hash = XXH64_digest(&st);
}
uint64_t hash() const {
if (m_hash == 0)
calculateHash();
return m_hash;
}
};
inline boo::AdditionalPipelineInfo ShaderTag::additionalInfo(const ExtensionSlot& ext,
std::pair<BlendFactor, BlendFactor> blendFactors) const {
boo::ZTest zTest;
switch (ext.depthTest) {
case hecl::Backend::ZTest::Original:
default:
zTest = getDepthTest() ? boo::ZTest::LEqual : boo::ZTest::None;
break;
case hecl::Backend::ZTest::None:
zTest = boo::ZTest::None;
break;
case hecl::Backend::ZTest::LEqual:
zTest = boo::ZTest::LEqual;
break;
case hecl::Backend::ZTest::Greater:
zTest = boo::ZTest::Greater;
break;
case hecl::Backend::ZTest::Equal:
zTest = boo::ZTest::Equal;
break;
case hecl::Backend::ZTest::GEqual:
zTest = boo::ZTest::GEqual;
break;
}
BlendFactor srcFactor = m_alphaTest ? BlendFactor::One : blendFactors.first;
BlendFactor dstFactor = m_alphaTest ? BlendFactor::Zero : blendFactors.second;
return {boo::BlendFactor((ext.srcFactor == BlendFactor::Original) ? srcFactor : ext.srcFactor),
boo::BlendFactor((ext.dstFactor == BlendFactor::Original) ? dstFactor : ext.dstFactor),
getPrimType(),
zTest,
ext.noDepthWrite ? false : getDepthWrite(),
!ext.noColorWrite,
!ext.noAlphaWrite,
(ext.cullMode == hecl::Backend::CullMode::Original)
? (getBackfaceCulling() ? boo::CullMode::Backface : boo::CullMode::None)
: boo::CullMode(ext.cullMode),
!ext.noAlphaOverwrite};
}
} // namespace hecl::Backend
namespace std {
template <>
struct hash<hecl::Backend::ShaderTag> {
size_t operator()(const hecl::Backend::ShaderTag& val) const noexcept { return val.valSizeT(); }
};
} // namespace std

View File

@ -0,0 +1,588 @@
//===- llvm/ADT/BitVector.h - Bit vectors -----------------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file implements the BitVector class.
//
//===----------------------------------------------------------------------===//
#pragma once
#include "MathExtras.hpp"
#include <algorithm>
#include <cassert>
#include <climits>
#include <cstdint>
#include <cstdlib>
#include <cstring>
namespace hecl {
namespace llvm {
class BitVector {
typedef unsigned long BitWord;
enum { BITWORD_SIZE = (unsigned)sizeof(BitWord) * CHAR_BIT };
static_assert(BITWORD_SIZE == 64 || BITWORD_SIZE == 32, "Unsupported word size");
BitWord* Bits; // Actual bits.
unsigned Size; // Size of bitvector in bits.
unsigned Capacity; // Number of BitWords allocated in the Bits array.
public:
typedef unsigned size_type;
// Encapsulation of a single bit.
class reference {
friend class BitVector;
BitWord* WordRef;
unsigned BitPos;
reference(); // Undefined
public:
reference(BitVector& b, unsigned Idx) {
WordRef = &b.Bits[Idx / BITWORD_SIZE];
BitPos = Idx % BITWORD_SIZE;
}
reference(const reference&) = default;
reference& operator=(reference t) {
*this = bool(t);
return *this;
}
reference& operator=(bool t) {
if (t)
*WordRef |= BitWord(1) << BitPos;
else
*WordRef &= ~(BitWord(1) << BitPos);
return *this;
}
explicit operator bool() const { return ((*WordRef) & (BitWord(1) << BitPos)) != 0; }
};
/// BitVector default ctor - Creates an empty bitvector.
BitVector() : Size(0), Capacity(0) { Bits = nullptr; }
/// BitVector ctor - Creates a bitvector of specified number of bits. All
/// bits are initialized to the specified value.
explicit BitVector(unsigned s, bool t = false) : Size(s) {
Capacity = NumBitWords(s);
Bits = (BitWord*)std::malloc(Capacity * sizeof(BitWord));
init_words(Bits, Capacity, t);
if (t)
clear_unused_bits();
}
/// BitVector copy ctor.
BitVector(const BitVector& RHS) : Size(RHS.size()) {
if (Size == 0) {
Bits = nullptr;
Capacity = 0;
return;
}
Capacity = NumBitWords(RHS.size());
Bits = (BitWord*)std::malloc(Capacity * sizeof(BitWord));
std::memcpy(Bits, RHS.Bits, Capacity * sizeof(BitWord));
}
BitVector(BitVector&& RHS) : Bits(RHS.Bits), Size(RHS.Size), Capacity(RHS.Capacity) {
RHS.Bits = nullptr;
RHS.Size = RHS.Capacity = 0;
}
~BitVector() { std::free(Bits); }
/// empty - Tests whether there are no bits in this bitvector.
bool empty() const { return Size == 0; }
/// size - Returns the number of bits in this bitvector.
size_type size() const { return Size; }
/// count - Returns the number of bits which are set.
size_type count() const {
unsigned NumBits = 0;
for (unsigned i = 0; i < NumBitWords(size()); ++i)
NumBits += countPopulation(Bits[i]);
return NumBits;
}
/// any - Returns true if any bit is set.
bool any() const {
for (unsigned i = 0; i < NumBitWords(size()); ++i)
if (Bits[i] != 0)
return true;
return false;
}
/// all - Returns true if all bits are set.
bool all() const {
for (unsigned i = 0; i < Size / BITWORD_SIZE; ++i)
if (Bits[i] != ~0UL)
return false;
// If bits remain check that they are ones. The unused bits are always zero.
if (unsigned Remainder = Size % BITWORD_SIZE)
return Bits[Size / BITWORD_SIZE] == (1UL << Remainder) - 1;
return true;
}
/// none - Returns true if none of the bits are set.
bool none() const { return !any(); }
/// find_first - Returns the index of the first set bit, -1 if none
/// of the bits are set.
int find_first() const {
for (unsigned i = 0; i < NumBitWords(size()); ++i)
if (Bits[i] != 0)
return i * BITWORD_SIZE + countTrailingZeros(Bits[i]);
return -1;
}
/// find_next - Returns the index of the next set bit following the
/// "Prev" bit. Returns -1 if the next set bit is not found.
int find_next(unsigned Prev) const {
++Prev;
if (Prev >= Size)
return -1;
unsigned WordPos = Prev / BITWORD_SIZE;
unsigned BitPos = Prev % BITWORD_SIZE;
BitWord Copy = Bits[WordPos];
// Mask off previous bits.
Copy &= ~0UL << BitPos;
if (Copy != 0)
return WordPos * BITWORD_SIZE + countTrailingZeros(Copy);
// Check subsequent words.
for (unsigned i = WordPos + 1; i < NumBitWords(size()); ++i)
if (Bits[i] != 0)
return i * BITWORD_SIZE + countTrailingZeros(Bits[i]);
return -1;
}
/// find_first_contiguous - Returns the index of the first contiguous
/// set of bits of "Length", -1 if no contiguous bits found.
int find_first_contiguous(unsigned Length, unsigned BucketSz) const {
for (int idx = find_first(); idx != -1; idx = find_next(idx)) {
if (idx + Length > size())
return -1;
bool good = true;
for (unsigned i = 0; i < Length; ++i) {
int ThisIdx = idx + i;
if (!test(ThisIdx)) {
good = false;
idx = ThisIdx;
break;
}
}
if (good) {
unsigned space = BucketSz - (idx % BucketSz);
if (space >= Length)
return idx;
}
}
return -1;
}
/// clear - Clear all bits.
void clear() { Size = 0; }
/// resize - Grow or shrink the bitvector.
void resize(unsigned N, bool t = false) {
if (N > Capacity * BITWORD_SIZE) {
unsigned OldCapacity = Capacity;
grow(N);
init_words(&Bits[OldCapacity], (Capacity - OldCapacity), t);
}
// Set any old unused bits that are now included in the BitVector. This
// may set bits that are not included in the new vector, but we will clear
// them back out below.
if (N > Size)
set_unused_bits(t);
// Update the size, and clear out any bits that are now unused
unsigned OldSize = Size;
Size = N;
if (t || N < OldSize)
clear_unused_bits();
}
void reserve(unsigned N) {
if (N > Capacity * BITWORD_SIZE)
grow(N);
}
// Set, reset, flip
BitVector& set() {
init_words(Bits, Capacity, true);
clear_unused_bits();
return *this;
}
BitVector& set(unsigned Idx) {
assert(Bits && "Bits never allocated");
Bits[Idx / BITWORD_SIZE] |= BitWord(1) << (Idx % BITWORD_SIZE);
return *this;
}
/// set - Efficiently set a range of bits in [I, E)
BitVector& set(unsigned I, unsigned E) {
assert(I <= E && "Attempted to set backwards range!");
assert(E <= size() && "Attempted to set out-of-bounds range!");
if (I == E)
return *this;
if (I / BITWORD_SIZE == E / BITWORD_SIZE) {
BitWord EMask = 1UL << (E % BITWORD_SIZE);
BitWord IMask = 1UL << (I % BITWORD_SIZE);
BitWord Mask = EMask - IMask;
Bits[I / BITWORD_SIZE] |= Mask;
return *this;
}
BitWord PrefixMask = ~0UL << (I % BITWORD_SIZE);
Bits[I / BITWORD_SIZE] |= PrefixMask;
I = alignTo(I, BITWORD_SIZE);
for (; I + BITWORD_SIZE <= E; I += BITWORD_SIZE)
Bits[I / BITWORD_SIZE] = ~0UL;
BitWord PostfixMask = (1UL << (E % BITWORD_SIZE)) - 1;
if (I < E)
Bits[I / BITWORD_SIZE] |= PostfixMask;
return *this;
}
BitVector& reset() {
init_words(Bits, Capacity, false);
return *this;
}
BitVector& reset(unsigned Idx) {
Bits[Idx / BITWORD_SIZE] &= ~(BitWord(1) << (Idx % BITWORD_SIZE));
return *this;
}
/// reset - Efficiently reset a range of bits in [I, E)
BitVector& reset(unsigned I, unsigned E) {
assert(I <= E && "Attempted to reset backwards range!");
assert(E <= size() && "Attempted to reset out-of-bounds range!");
if (I == E)
return *this;
if (I / BITWORD_SIZE == E / BITWORD_SIZE) {
BitWord EMask = 1UL << (E % BITWORD_SIZE);
BitWord IMask = 1UL << (I % BITWORD_SIZE);
BitWord Mask = EMask - IMask;
Bits[I / BITWORD_SIZE] &= ~Mask;
return *this;
}
BitWord PrefixMask = ~0UL << (I % BITWORD_SIZE);
Bits[I / BITWORD_SIZE] &= ~PrefixMask;
I = alignTo(I, BITWORD_SIZE);
for (; I + BITWORD_SIZE <= E; I += BITWORD_SIZE)
Bits[I / BITWORD_SIZE] = 0UL;
BitWord PostfixMask = (1UL << (E % BITWORD_SIZE)) - 1;
if (I < E)
Bits[I / BITWORD_SIZE] &= ~PostfixMask;
return *this;
}
BitVector& flip() {
for (unsigned i = 0; i < NumBitWords(size()); ++i)
Bits[i] = ~Bits[i];
clear_unused_bits();
return *this;
}
BitVector& flip(unsigned Idx) {
Bits[Idx / BITWORD_SIZE] ^= BitWord(1) << (Idx % BITWORD_SIZE);
return *this;
}
// Indexing.
reference operator[](unsigned Idx) {
assert(Idx < Size && "Out-of-bounds Bit access.");
return reference(*this, Idx);
}
bool operator[](unsigned Idx) const {
assert(Idx < Size && "Out-of-bounds Bit access.");
BitWord Mask = BitWord(1) << (Idx % BITWORD_SIZE);
return (Bits[Idx / BITWORD_SIZE] & Mask) != 0;
}
bool test(unsigned Idx) const { return (*this)[Idx]; }
/// Test if any common bits are set.
bool anyCommon(const BitVector& RHS) const {
unsigned ThisWords = NumBitWords(size());
unsigned RHSWords = NumBitWords(RHS.size());
for (unsigned i = 0, e = std::min(ThisWords, RHSWords); i != e; ++i)
if (Bits[i] & RHS.Bits[i])
return true;
return false;
}
// Comparison operators.
bool operator==(const BitVector& RHS) const {
unsigned ThisWords = NumBitWords(size());
unsigned RHSWords = NumBitWords(RHS.size());
unsigned i;
for (i = 0; i != std::min(ThisWords, RHSWords); ++i)
if (Bits[i] != RHS.Bits[i])
return false;
// Verify that any extra words are all zeros.
if (i != ThisWords) {
for (; i != ThisWords; ++i)
if (Bits[i])
return false;
} else if (i != RHSWords) {
for (; i != RHSWords; ++i)
if (RHS.Bits[i])
return false;
}
return true;
}
bool operator!=(const BitVector& RHS) const { return !(*this == RHS); }
/// Intersection, union, disjoint union.
BitVector& operator&=(const BitVector& RHS) {
unsigned ThisWords = NumBitWords(size());
unsigned RHSWords = NumBitWords(RHS.size());
unsigned i;
for (i = 0; i != std::min(ThisWords, RHSWords); ++i)
Bits[i] &= RHS.Bits[i];
// Any bits that are just in this bitvector become zero, because they aren't
// in the RHS bit vector. Any words only in RHS are ignored because they
// are already zero in the LHS.
for (; i != ThisWords; ++i)
Bits[i] = 0;
return *this;
}
/// reset - Reset bits that are set in RHS. Same as *this &= ~RHS.
BitVector& reset(const BitVector& RHS) {
unsigned ThisWords = NumBitWords(size());
unsigned RHSWords = NumBitWords(RHS.size());
unsigned i;
for (i = 0; i != std::min(ThisWords, RHSWords); ++i)
Bits[i] &= ~RHS.Bits[i];
return *this;
}
/// test - Check if (This - RHS) is zero.
/// This is the same as reset(RHS) and any().
bool test(const BitVector& RHS) const {
unsigned ThisWords = NumBitWords(size());
unsigned RHSWords = NumBitWords(RHS.size());
unsigned i;
for (i = 0; i != std::min(ThisWords, RHSWords); ++i)
if ((Bits[i] & ~RHS.Bits[i]) != 0)
return true;
for (; i != ThisWords; ++i)
if (Bits[i] != 0)
return true;
return false;
}
BitVector& operator|=(const BitVector& RHS) {
if (size() < RHS.size())
resize(RHS.size());
for (size_t i = 0, e = NumBitWords(RHS.size()); i != e; ++i)
Bits[i] |= RHS.Bits[i];
return *this;
}
BitVector& operator^=(const BitVector& RHS) {
if (size() < RHS.size())
resize(RHS.size());
for (size_t i = 0, e = NumBitWords(RHS.size()); i != e; ++i)
Bits[i] ^= RHS.Bits[i];
return *this;
}
// Assignment operator.
const BitVector& operator=(const BitVector& RHS) {
if (this == &RHS)
return *this;
Size = RHS.size();
unsigned RHSWords = NumBitWords(Size);
if (Size <= Capacity * BITWORD_SIZE) {
if (Size)
std::memcpy(Bits, RHS.Bits, RHSWords * sizeof(BitWord));
clear_unused_bits();
return *this;
}
// Grow the bitvector to have enough elements.
Capacity = RHSWords;
assert(Capacity > 0 && "negative capacity?");
BitWord* NewBits = (BitWord*)std::malloc(Capacity * sizeof(BitWord));
std::memcpy(NewBits, RHS.Bits, Capacity * sizeof(BitWord));
// Destroy the old bits.
std::free(Bits);
Bits = NewBits;
return *this;
}
const BitVector& operator=(BitVector&& RHS) {
if (this == &RHS)
return *this;
std::free(Bits);
Bits = RHS.Bits;
Size = RHS.Size;
Capacity = RHS.Capacity;
RHS.Bits = nullptr;
RHS.Size = RHS.Capacity = 0;
return *this;
}
void swap(BitVector& RHS) {
std::swap(Bits, RHS.Bits);
std::swap(Size, RHS.Size);
std::swap(Capacity, RHS.Capacity);
}
//===--------------------------------------------------------------------===//
// Portable bit mask operations.
//===--------------------------------------------------------------------===//
//
// These methods all operate on arrays of uint32_t, each holding 32 bits. The
// fixed word size makes it easier to work with literal bit vector constants
// in portable code.
//
// The LSB in each word is the lowest numbered bit. The size of a portable
// bit mask is always a whole multiple of 32 bits. If no bit mask size is
// given, the bit mask is assumed to cover the entire BitVector.
/// setBitsInMask - Add '1' bits from Mask to this vector. Don't resize.
/// This computes "*this |= Mask".
void setBitsInMask(const uint32_t* Mask, unsigned MaskWords = ~0u) { applyMask<true, false>(Mask, MaskWords); }
/// clearBitsInMask - Clear any bits in this vector that are set in Mask.
/// Don't resize. This computes "*this &= ~Mask".
void clearBitsInMask(const uint32_t* Mask, unsigned MaskWords = ~0u) { applyMask<false, false>(Mask, MaskWords); }
/// setBitsNotInMask - Add a bit to this vector for every '0' bit in Mask.
/// Don't resize. This computes "*this |= ~Mask".
void setBitsNotInMask(const uint32_t* Mask, unsigned MaskWords = ~0u) { applyMask<true, true>(Mask, MaskWords); }
/// clearBitsNotInMask - Clear a bit in this vector for every '0' bit in Mask.
/// Don't resize. This computes "*this &= Mask".
void clearBitsNotInMask(const uint32_t* Mask, unsigned MaskWords = ~0u) { applyMask<false, true>(Mask, MaskWords); }
private:
unsigned NumBitWords(unsigned S) const { return (S + BITWORD_SIZE - 1) / BITWORD_SIZE; }
// Set the unused bits in the high words.
void set_unused_bits(bool t = true) {
// Set high words first.
unsigned UsedWords = NumBitWords(Size);
if (Capacity > UsedWords)
init_words(&Bits[UsedWords], (Capacity - UsedWords), t);
// Then set any stray high bits of the last used word.
unsigned ExtraBits = Size % BITWORD_SIZE;
if (ExtraBits) {
BitWord ExtraBitMask = ~0UL << ExtraBits;
if (t)
Bits[UsedWords - 1] |= ExtraBitMask;
else
Bits[UsedWords - 1] &= ~ExtraBitMask;
}
}
// Clear the unused bits in the high words.
void clear_unused_bits() { set_unused_bits(false); }
void grow(unsigned NewSize) {
Capacity = std::max(NumBitWords(NewSize), Capacity * 2);
assert(Capacity > 0 && "realloc-ing zero space");
Bits = (BitWord*)std::realloc(Bits, Capacity * sizeof(BitWord));
clear_unused_bits();
}
void init_words(BitWord* B, unsigned NumWords, bool t) { memset(B, 0 - (int)t, NumWords * sizeof(BitWord)); }
template <bool AddBits, bool InvertMask>
void applyMask(const uint32_t* Mask, unsigned MaskWords) {
static_assert(BITWORD_SIZE % 32 == 0, "Unsupported BitWord size.");
MaskWords = std::min(MaskWords, (size() + 31) / 32);
const unsigned Scale = BITWORD_SIZE / 32;
unsigned i;
for (i = 0; MaskWords >= Scale; ++i, MaskWords -= Scale) {
BitWord BW = Bits[i];
// This inner loop should unroll completely when BITWORD_SIZE > 32.
for (unsigned b = 0; b != BITWORD_SIZE; b += 32) {
uint32_t M = *Mask++;
if (InvertMask)
M = ~M;
if (AddBits)
BW |= BitWord(M) << b;
else
BW &= ~(BitWord(M) << b);
}
Bits[i] = BW;
}
for (unsigned b = 0; MaskWords; b += 32, --MaskWords) {
uint32_t M = *Mask++;
if (InvertMask)
M = ~M;
if (AddBits)
Bits[i] |= BitWord(M) << b;
else
Bits[i] &= ~(BitWord(M) << b);
}
if (AddBits)
clear_unused_bits();
}
public:
/// Return the size (in bytes) of the bit vector.
size_t getMemorySize() const { return Capacity * sizeof(BitWord); }
};
static inline size_t capacity_in_bytes(const BitVector& X) { return X.getMemorySize(); }
} // end namespace llvm
} // end namespace hecl
namespace std {
/// Implement std::swap in terms of BitVector swap.
inline void swap(hecl::llvm::BitVector& LHS, hecl::llvm::BitVector& RHS) { LHS.swap(RHS); }
} // end namespace std

View File

@ -0,0 +1,938 @@
#pragma once
#if _WIN32
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN 1
#endif
#ifndef NOMINMAX
#define NOMINMAX
#endif
#include <windows.h>
#else
#include <unistd.h>
#endif
#include <array>
#include <atomic>
#include <cstddef>
#include <cstdint>
#include <cstdio>
#include <functional>
#include <memory>
#include <optional>
#include <ostream>
#include <string>
#include <unordered_map>
#include <utility>
#include <variant>
#include <vector>
#include "hecl/hecl.hpp"
#include "hecl/Backend.hpp"
#include "hecl/HMDLMeta.hpp"
#include "hecl/TypedVariant.hpp"
#include <athena/Types.hpp>
#include <fmt/ostream.h>
#include <logvisor/logvisor.hpp>
namespace hecl::blender {
using namespace std::literals;
class Connection;
class HMDLBuffers;
extern logvisor::Module BlenderLog;
struct PoolSkinIndex {
std::size_t m_poolSz = 0;
std::unique_ptr<uint32_t[]> m_poolToSkinIndex;
void allocate(std::size_t poolSz) {
m_poolSz = poolSz;
if (poolSz)
m_poolToSkinIndex.reset(new uint32_t[poolSz]);
}
};
enum class ANIMCurveType { Rotate, Translate, Scale };
class ANIMOutStream {
Connection* m_parent;
unsigned m_curCount = 0;
unsigned m_totalCount = 0;
bool m_inCurve = false;
public:
using CurveType = ANIMCurveType;
ANIMOutStream(Connection* parent);
~ANIMOutStream();
void changeCurve(CurveType type, unsigned crvIdx, unsigned keyCount);
void write(unsigned frame, float val);
};
class PyOutStream : public std::ostream {
friend class Connection;
Connection* m_parent;
struct StreamBuf : std::streambuf {
PyOutStream& m_parent;
std::string m_lineBuf;
bool m_deleteOnError;
StreamBuf(PyOutStream& parent, bool deleteOnError) : m_parent(parent), m_deleteOnError(deleteOnError) {}
StreamBuf(const StreamBuf& other) = delete;
StreamBuf(StreamBuf&& other) = default;
bool sendLine(std::string_view line);
int_type overflow(int_type ch) override;
std::streamsize xsputn(const char_type* __s, std::streamsize __n) override;
} m_sbuf;
PyOutStream(Connection* parent, bool deleteOnError);
public:
PyOutStream(const PyOutStream& other) = delete;
PyOutStream(PyOutStream&& other) : std::ostream(&m_sbuf), m_parent(other.m_parent), m_sbuf(std::move(other.m_sbuf)) {
other.m_parent = nullptr;
}
~PyOutStream() override { close(); }
void close();
template <typename S, typename... Args, typename Char = fmt::char_t<S>>
void format(const S& format, Args&&... args);
void linkBlend(std::string_view target, std::string_view objName, bool link = true);
void linkArmature(std::string_view target, std::string_view armName);
void linkMesh(std::string_view target, std::string_view meshName);
void linkBackground(std::string_view target, std::string_view sceneName = {});
void AABBToBMesh(const atVec3f& min, const atVec3f& max);
void centerView();
ANIMOutStream beginANIMCurve() { return ANIMOutStream(m_parent); }
Connection& getConnection() { return *m_parent; }
};
/* Vector types with integrated stream reading constructor */
struct Vector2f {
atVec2f val;
Vector2f() = default;
void read(Connection& conn);
Vector2f(Connection& conn) { read(conn); }
operator const atVec2f&() const { return val; }
bool operator==(const Vector2f& other) const {
return val.simd[0] == other.val.simd[0] &&
val.simd[1] == other.val.simd[1];
}
};
struct Vector3f {
atVec3f val;
Vector3f() = default;
void read(Connection& conn);
Vector3f(Connection& conn) { read(conn); }
operator const atVec3f&() const { return val; }
bool operator==(const Vector3f& other) const {
return val.simd[0] == other.val.simd[0] &&
val.simd[1] == other.val.simd[1] &&
val.simd[2] == other.val.simd[2];
}
};
struct Vector4f {
atVec4f val;
Vector4f() = default;
void read(Connection& conn);
Vector4f(Connection& conn) { read(conn); }
operator const atVec4f&() const { return val; }
bool operator==(const Vector4f& other) const {
return val.simd[0] == other.val.simd[0] &&
val.simd[1] == other.val.simd[1] &&
val.simd[2] == other.val.simd[2] &&
val.simd[3] == other.val.simd[3];
}
};
struct Matrix3f {
std::array<atVec3f, 3> m;
atVec3f& operator[](std::size_t idx) { return m[idx]; }
const atVec3f& operator[](std::size_t idx) const { return m[idx]; }
};
struct Matrix4f {
std::array<atVec4f, 4> val;
Matrix4f() = default;
Matrix4f(Connection& conn) { read(conn); }
void read(Connection& conn);
const atVec4f& operator[](std::size_t idx) const { return val[idx]; }
};
struct Index {
uint32_t val;
Index() = default;
void read(Connection& conn);
Index(Connection& conn) { read(conn); }
operator const uint32_t&() const { return val; }
};
struct Float {
float val;
Float() = default;
void read(Connection& conn);
Float(Connection& conn) { read(conn); }
operator const float&() const { return val; }
};
struct Boolean {
bool val;
Boolean() = default;
void read(Connection& conn);
Boolean(Connection& conn) { read(conn); }
operator const bool&() const { return val; }
};
atVec3f MtxVecMul4RM(const Matrix4f& mtx, const Vector3f& vec);
atVec3f MtxVecMul3RM(const Matrix4f& mtx, const Vector3f& vec);
/** Intermediate material representation */
struct Material {
enum class ShaderType : uint32_t {
Invalid = 0,
RetroShader = 'RSHD',
RetroDynamicShader = 'RDYN',
RetroDynamicAlphaShader = 'RDAL',
RetroDynamicCharacterShader = 'RCHR',
};
enum class ChunkType : uint32_t {
Invalid = 0,
TexturePass = 'PASS',
ColorPass = 'CLR ',
};
enum class PassType : uint32_t {
Invalid = 0,
Lightmap = 'LMAP',
Diffuse = 'DIFF',
Emissive = 'EMIS',
Specular = 'SPEC',
ExtendedSpecular = 'ESPC',
Reflection = 'REFL',
IndirectTex = 'INDR',
Alpha = 'ALPH',
};
static constexpr std::string_view PassTypeToString(PassType tp) {
switch (tp) {
case PassType::Lightmap: return "lightmap"sv;
case PassType::Diffuse: return "diffuse"sv;
case PassType::Emissive: return "emissive"sv;
case PassType::Specular: return "specular"sv;
case PassType::ExtendedSpecular: return "extendedSpecular"sv;
case PassType::Reflection: return "reflection"sv;
case PassType::Alpha: return "alpha"sv;
default:
assert(false && "Unknown pass type");
return ""sv;
}
}
enum class UVAnimType : uint8_t {
MvInvNoTranslation,
MvInv,
Scroll,
Rotation,
HStrip,
VStrip,
Model,
CylinderEnvironment,
Eight,
Invalid = UINT8_MAX
};
using TexCoordSource = hecl::Backend::TexCoordSource;
struct PASS : hecl::TypedRecord<ChunkType::TexturePass> {
PassType type;
ProjectPath tex;
TexCoordSource source;
UVAnimType uvAnimType;
std::array<float, 9> uvAnimParms = {};
bool alpha;
explicit PASS(Connection& conn);
bool operator==(const PASS& other) const {
return type == other.type &&
tex == other.tex &&
source == other.source &&
uvAnimType == other.uvAnimType &&
uvAnimParms == other.uvAnimParms &&
alpha == other.alpha;
}
void hash(XXH64_state_t* st) const {
XXH64_update(st, &type, sizeof(type));
XXH64_update(st, &source, sizeof(source));
XXH64_update(st, &uvAnimType, sizeof(uvAnimType));
XXH64_update(st, &alpha, sizeof(alpha));
}
};
struct CLR : hecl::TypedRecord<ChunkType::ColorPass> {
PassType type;
Vector4f color;
explicit CLR(Connection& conn);
bool operator==(const CLR& other) const {
return type == other.type && color == other.color;
}
void hash(XXH64_state_t* st) const {
XXH64_update(st, &type, sizeof(type));
}
};
using Chunk = hecl::TypedVariant<PASS, CLR>;
enum class BlendMode : uint32_t {
Opaque = 0,
Alpha = 1,
Additive = 2
};
std::string name;
uint32_t passIndex;
ShaderType shaderType;
std::vector<Chunk> chunks;
std::unordered_map<std::string, int32_t> iprops;
BlendMode blendMode = BlendMode::Opaque;
explicit Material(Connection& conn);
bool operator==(const Material& other) const {
return chunks == other.chunks && iprops == other.iprops && blendMode == other.blendMode;
}
};
/** Intermediate mesh representation prepared by blender from a single mesh object */
struct Mesh {
static constexpr std::size_t MaxColorLayers = 4;
static constexpr std::size_t MaxUVLayers = 8;
static constexpr std::size_t MaxSkinEntries = 16;
HMDLTopology topology;
/* Object transform in scene */
Matrix4f sceneXf;
/* Cumulative AABB */
Vector3f aabbMin;
Vector3f aabbMax;
std::vector<std::vector<Material>> materialSets;
/* Vertex buffer data */
std::vector<Vector3f> pos;
std::vector<Vector3f> norm;
uint32_t colorLayerCount = 0;
std::vector<Vector3f> color;
uint32_t uvLayerCount = 0;
std::vector<Vector2f> uv;
uint32_t luvLayerCount = 0;
std::vector<Vector2f> luv;
/* Skinning data */
std::vector<std::string> boneNames;
struct SkinBind {
uint32_t vg_idx = UINT32_MAX;
float weight = 0.f;
SkinBind() = default;
explicit SkinBind(Connection& conn);
bool valid() const { return vg_idx != UINT32_MAX; }
bool operator==(const SkinBind& other) const { return vg_idx == other.vg_idx && weight == other.weight; }
};
std::vector<std::array<SkinBind, MaxSkinEntries>> skins;
std::vector<std::size_t> contiguousSkinVertCounts;
static std::size_t countSkinBinds(const std::array<SkinBind, MaxSkinEntries>& arr) {
std::size_t ret = 0;
for (const auto& b : arr)
if (b.valid())
++ret;
else
break;
return ret;
}
void normalizeSkinBinds();
/** Islands of the same material/skinBank are represented here */
struct Surface {
Vector3f centroid;
uint32_t materialIdx;
Vector3f aabbMin;
Vector3f aabbMax;
Vector3f reflectionNormal;
uint32_t skinBankIdx;
/** Vertex indexing data (all primitives joined as degenerate tri-strip) */
struct Vert {
uint32_t iPos = 0xffffffff;
uint32_t iNorm = 0xffffffff;
std::array<uint32_t, 4> iColor = {0xffffffff};
std::array<uint32_t, 8> iUv = {0xffffffff};
uint32_t iSkin = 0xffffffff;
uint32_t iBankSkin = 0xffffffff;
bool operator==(const Vert& other) const;
};
std::vector<Vert> verts;
};
std::vector<Surface> surfaces;
std::unordered_map<std::string, std::string> customProps;
struct SkinBanks {
struct Bank {
std::vector<uint32_t> m_skinIdxs;
std::vector<uint32_t> m_boneIdxs;
void addSkins(const Mesh& parent, const std::vector<uint32_t>& skinIdxs);
};
std::vector<Bank> banks;
std::vector<Bank>::iterator addSkinBank(int skinSlotCount);
uint32_t addSurface(const Mesh& mesh, const Surface& surf, int skinSlotCount);
} skinBanks;
Mesh(Connection& conn, HMDLTopology topology, int skinSlotCount, bool useLuvs = false);
Mesh getContiguousSkinningVersion() const;
/** Prepares mesh representation for indexed access on modern APIs.
* Mesh must remain resident for accessing reference members
*/
HMDLBuffers getHMDLBuffers(bool absoluteCoords, PoolSkinIndex& poolSkinIndex) const;
};
/** Intermediate collision mesh representation prepared by blender from a single mesh object */
struct ColMesh {
/** HECL source and metadata of each material */
struct Material {
std::string name;
bool unknown;
bool surfaceStone;
bool surfaceMetal;
bool surfaceGrass;
bool surfaceIce;
bool pillar;
bool surfaceMetalGrating;
bool surfacePhazon;
bool surfaceDirt;
bool surfaceLava;
bool surfaceSPMetal;
bool surfaceLavaStone;
bool surfaceSnow;
bool surfaceMudSlow;
bool surfaceFabric;
bool halfPipe;
bool surfaceMud;
bool surfaceGlass;
bool unused3;
bool unused4;
bool surfaceShield;
bool surfaceSand;
bool surfaceMothOrSeedOrganics;
bool surfaceWeb;
bool projPassthrough;
bool solid;
bool noPlatformCollision;
bool camPassthrough;
bool surfaceWood;
bool surfaceOrganic;
bool noEdgeCollision;
bool surfaceRubber;
bool seeThrough;
bool scanPassthrough;
bool aiPassthrough;
bool ceiling;
bool wall;
bool floor;
bool aiBlock;
bool jumpNotAllowed;
bool spiderBall;
bool screwAttackWallJump;
Material(Connection& conn);
};
std::vector<Material> materials;
std::vector<Vector3f> verts;
struct Edge {
std::array<uint32_t, 2> verts;
bool seam;
Edge(Connection& conn);
};
std::vector<Edge> edges;
struct Triangle {
std::array<uint32_t, 3> edges;
uint32_t matIdx;
bool flip;
Triangle(Connection& conn);
};
std::vector<Triangle> trianges;
ColMesh(Connection& conn);
};
/** Intermediate world representation */
struct World {
struct Area {
ProjectPath path;
std::array<Vector3f, 2> aabb;
Matrix4f transform;
struct Dock {
std::array<Vector3f, 4> verts;
Index targetArea;
Index targetDock;
Dock(Connection& conn);
};
std::vector<Dock> docks;
Area(Connection& conn);
};
std::vector<Area> areas;
World(Connection& conn);
};
/** Intermediate lamp representation */
struct Light {
/* Object transform in scene */
Matrix4f sceneXf;
Vector3f color;
uint32_t layer;
enum class Type : uint32_t { Ambient, Directional, Custom, Spot } type;
float energy;
float spotCutoff;
float constant;
float linear;
float quadratic;
bool shadow;
std::string name;
Light(Connection& conn);
};
/** Intermediate MapArea representation */
struct MapArea {
uint32_t visType;
std::vector<Vector3f> verts;
std::vector<uint32_t> indices;
struct Surface {
Vector3f normal;
Vector3f centerOfMass;
uint32_t start;
uint32_t count;
std::vector<std::pair<uint32_t, uint32_t>> borders;
Surface(Connection& conn);
};
std::vector<Surface> surfaces;
struct POI {
uint32_t type;
uint32_t visMode;
uint32_t objid;
Matrix4f xf;
POI(Connection& conn);
};
std::vector<POI> pois;
MapArea(Connection& conn);
};
/** Intermediate MapUniverse representation */
struct MapUniverse {
hecl::ProjectPath hexagonPath;
struct World {
std::string name;
Matrix4f xf;
std::vector<Matrix4f> hexagons;
Vector4f color;
hecl::ProjectPath worldPath;
World(Connection& conn);
};
std::vector<World> worlds;
MapUniverse(Connection& conn);
};
/** Intermediate bone representation used in Armature */
struct Bone {
std::string name;
Vector3f origin;
int32_t parent = -1;
std::vector<int32_t> children;
Bone(Connection& conn);
};
/** Intermediate armature representation used in Actor */
struct Armature {
std::vector<Bone> bones;
const Bone* lookupBone(const char* name) const;
const Bone* getParent(const Bone* bone) const;
const Bone* getChild(const Bone* bone, std::size_t child) const;
const Bone* getRoot() const;
Armature(Connection& conn);
};
/** Intermediate action representation used in Actor */
struct Action {
std::string name;
std::string animId;
float interval;
bool additive;
bool looping;
std::vector<int32_t> frames;
struct Channel {
std::string boneName;
uint32_t attrMask;
struct Key {
Vector4f rotation;
Vector3f position;
Vector3f scale;
Key(Connection& conn, uint32_t attrMask);
};
std::vector<Key> keys;
Channel(Connection& conn);
};
std::vector<Channel> channels;
std::vector<std::pair<Vector3f, Vector3f>> subtypeAABBs;
Action(Connection& conn);
};
/** Intermediate actor representation prepared by blender from a single HECL actor blend */
struct Actor {
struct ActorArmature {
std::string name;
ProjectPath path;
std::optional<Armature> armature;
ActorArmature(Connection& conn);
};
std::vector<ActorArmature> armatures;
struct Subtype {
std::string name;
std::string cskrId;
ProjectPath mesh;
int32_t armature = -1;
struct OverlayMesh {
std::string name;
std::string cskrId;
ProjectPath mesh;
OverlayMesh(Connection& conn);
};
std::vector<OverlayMesh> overlayMeshes;
Subtype(Connection& conn);
};
std::vector<Subtype> subtypes;
struct Attachment {
std::string name;
std::string cskrId;
ProjectPath mesh;
int32_t armature = -1;
Attachment(Connection& conn);
};
std::vector<Attachment> attachments;
std::vector<Action> actions;
Actor(Connection& conn);
};
/** Intermediate pathfinding representation prepared by blender */
struct PathMesh {
std::vector<uint8_t> data;
PathMesh(Connection& conn);
};
class DataStream {
friend class Connection;
Connection* m_parent;
DataStream(Connection* parent);
public:
DataStream(const DataStream& other) = delete;
DataStream(DataStream&& other) : m_parent(other.m_parent) { other.m_parent = nullptr; }
~DataStream() { close(); }
void close();
std::vector<std::string> getMeshList();
std::vector<std::string> getLightList();
std::pair<atVec3f, atVec3f> getMeshAABB();
static const char* MeshOutputModeString(HMDLTopology topology);
/** Compile mesh by context (MESH blends only) */
Mesh compileMesh(HMDLTopology topology, int skinSlotCount = 10);
/** Compile mesh by name (AREA blends only) */
Mesh compileMesh(std::string_view name, HMDLTopology topology, int skinSlotCount = 10, bool useLuv = false);
/** Compile collision mesh by name (AREA blends only) */
ColMesh compileColMesh(std::string_view name);
/** Compile all meshes as collision meshes (CMESH blends only) */
std::vector<ColMesh> compileColMeshes();
/** Compile world intermediate (WORLD blends only) */
World compileWorld();
/** Gather all lights in scene (AREA blends only) */
std::vector<Light> compileLights();
/** Get PathMesh from scene (PATH blends only) */
PathMesh compilePathMesh();
/** Compile GUI into FRME data (FRAME blends only) */
std::vector<uint8_t> compileGuiFrame(int version);
/** Gather all texture paths in scene */
std::vector<ProjectPath> getTextures();
Actor compileActor();
Actor compileActorCharacterOnly();
Armature compileArmature();
Action compileActionChannelsOnly(std::string_view name);
std::vector<std::pair<std::string, std::string>> getSubtypeNames();
std::vector<std::pair<std::string, std::string>> getActionNames();
std::vector<std::pair<std::string, std::string>> getSubtypeOverlayNames(std::string_view name);
std::vector<std::pair<std::string, std::string>> getAttachmentNames();
std::unordered_map<std::string, Matrix3f> getBoneMatrices(std::string_view name);
bool renderPvs(std::string_view path, const atVec3f& location);
bool renderPvsLight(std::string_view path, std::string_view lightName);
MapArea compileMapArea();
MapUniverse compileMapUniverse();
};
class Connection {
friend class ANIMOutStream;
friend class DataStream;
friend class PyOutStream;
friend struct Action;
friend struct Actor;
friend struct Armature;
friend struct Bone;
friend struct Boolean;
friend struct ColMesh;
friend struct Float;
friend struct Index;
friend struct Light;
friend struct MapArea;
friend struct MapUniverse;
friend struct Material;
friend struct Matrix3f;
friend struct Matrix4f;
friend struct Mesh;
friend struct PathMesh;
friend struct PyOutStream::StreamBuf;
friend struct Vector2f;
friend struct Vector3f;
friend struct Vector4f;
friend struct World;
friend class MeshOptimizer;
std::atomic_bool m_lock = {false};
bool m_pyStreamActive = false;
bool m_dataStreamActive = false;
bool m_blenderQuit = false;
#if _WIN32
PROCESS_INFORMATION m_pinfo = {};
std::thread m_consoleThread;
bool m_consoleThreadRunning = true;
#else
pid_t m_blenderProc = 0;
#endif
std::array<int, 2> m_readpipe{};
std::array<int, 2> m_writepipe{};
BlendType m_loadedType = BlendType::None;
bool m_loadedRigged = false;
ProjectPath m_loadedBlend;
hecl::SystemString m_errPath;
uint32_t _readStr(char* buf, uint32_t bufSz);
uint32_t _writeStr(const char* str, uint32_t len, int wpipe);
uint32_t _writeStr(const char* str, uint32_t len) { return _writeStr(str, len, m_writepipe[1]); }
uint32_t _writeStr(std::string_view view) { return _writeStr(view.data(), view.size()); }
std::size_t _readBuf(void* buf, std::size_t len);
std::size_t _writeBuf(const void* buf, std::size_t len);
std::string _readStdString() {
uint32_t bufSz;
_readBuf(&bufSz, 4);
std::string ret(bufSz, ' ');
_readBuf(&ret[0], bufSz);
return ret;
}
template<typename T, std::enable_if_t<std::disjunction_v<std::is_arithmetic<T>, std::is_enum<T>>, int> = 0>
void _readValue(T& v) { _readBuf(&v, sizeof(T)); }
template<typename T>
void _readItems(T enumerator) {
uint32_t nItems;
_readBuf(&nItems, 4);
for (uint32_t i = 0; i < nItems; ++i)
enumerator(*this);
}
template<typename T, typename... Args, std::enable_if_t<
!std::disjunction_v<std::is_arithmetic<T>, std::is_enum<T>, std::is_same<T, std::string>>, int> = 0>
void _readVector(std::vector<T>& container, Args&&... args) {
uint32_t nItems;
_readBuf(&nItems, 4);
container.clear();
container.reserve(nItems);
for (uint32_t i = 0; i < nItems; ++i)
container.emplace_back(*this, std::forward<Args>(args)...);
}
template<typename T, std::enable_if_t<std::disjunction_v<std::is_arithmetic<T>, std::is_enum<T>>, int> = 0>
void _readVector(std::vector<T>& container) {
uint32_t nItems;
_readBuf(&nItems, 4);
container.clear();
container.resize(nItems);
_readBuf(&container[0], sizeof(T) * nItems);
}
void _readVector(std::vector<std::string>& container) {
uint32_t nItems;
_readBuf(&nItems, 4);
container.clear();
container.reserve(nItems);
for (uint32_t i = 0; i < nItems; ++i) {
uint32_t strSize;
_readBuf(&strSize, 4);
_readBuf(&container.emplace_back(strSize, ' ')[0], strSize);
}
}
template<typename T, typename F>
void _readVectorFunc(std::vector<T>& container, F func) {
uint32_t nItems;
_readBuf(&nItems, 4);
container.clear();
container.reserve(nItems);
for (uint32_t i = 0; i < nItems; ++i)
func();
}
ProjectPath _readPath();
bool _isStatus(const char* status) {
char readBuf[16];
_readStr(readBuf, 16);
return std::strcmp(readBuf, status) == 0;
}
bool _isOk() { return _isStatus("OK"); }
bool _isFinished() { return _isStatus("FINISHED"); }
bool _isTrue() { return _isStatus("TRUE"); }
void _checkStatus(std::string_view action, std::string_view status) {
char readBuf[16];
_readStr(readBuf, 16);
if (status != readBuf)
BlenderLog.report(logvisor::Fatal, FMT_STRING("{}: {}: {}"), m_loadedBlend.getRelativePathUTF8(), action, readBuf);
}
void _checkReady(std::string_view action) { _checkStatus(action, "READY"sv); }
void _checkDone(std::string_view action) { _checkStatus(action, "DONE"sv); }
void _checkOk(std::string_view action) { _checkStatus(action, "OK"sv); }
void _checkAnimReady(std::string_view action) { _checkStatus(action, "ANIMREADY"sv); }
void _checkAnimDone(std::string_view action) { _checkStatus(action, "ANIMDONE"sv); }
void _closePipe();
void _blenderDied();
public:
Connection(int verbosityLevel = 1);
~Connection();
Connection(const Connection&) = delete;
Connection& operator=(const Connection&) = delete;
Connection(Connection&&) = delete;
Connection& operator=(Connection&&) = delete;
bool createBlend(const ProjectPath& path, BlendType type);
BlendType getBlendType() const { return m_loadedType; }
const ProjectPath& getBlendPath() const { return m_loadedBlend; }
bool getRigged() const { return m_loadedRigged; }
bool openBlend(const ProjectPath& path, bool force = false);
bool saveBlend();
void deleteBlend();
PyOutStream beginPythonOut(bool deleteOnError = false) {
bool expect = false;
if (!m_lock.compare_exchange_strong(expect, true))
BlenderLog.report(logvisor::Fatal, FMT_STRING("lock already held for blender::Connection::beginPythonOut()"));
return PyOutStream(this, deleteOnError);
}
DataStream beginData() {
bool expect = false;
if (!m_lock.compare_exchange_strong(expect, true))
BlenderLog.report(logvisor::Fatal, FMT_STRING("lock already held for blender::Connection::beginDataIn()"));
return DataStream(this);
}
void quitBlender();
void closeStream() {
if (m_lock)
deleteBlend();
}
static Connection& SharedConnection();
static void Shutdown();
};
template <typename S, typename... Args, typename Char>
void PyOutStream::format(const S& format, Args&&... args) {
if (!m_parent || !m_parent->m_lock)
BlenderLog.report(logvisor::Fatal, FMT_STRING("lock not held for PyOutStream::format()"));
fmt::print(*this, format, std::forward<Args>(args)...);
}
class HMDLBuffers {
public:
struct Surface;
private:
friend struct Mesh;
HMDLBuffers(HMDLMeta&& meta, std::size_t vboSz, const std::vector<atUint32>& iboData, std::vector<Surface>&& surfaces,
const Mesh::SkinBanks& skinBanks);
public:
HMDLMeta m_meta;
std::size_t m_vboSz;
std::unique_ptr<uint8_t[]> m_vboData;
std::size_t m_iboSz;
std::unique_ptr<uint8_t[]> m_iboData;
struct Surface {
Surface(const Mesh::Surface& origSurf, atUint32 start, atUint32 count)
: m_origSurf(origSurf), m_start(start), m_count(count) {}
const Mesh::Surface& m_origSurf;
atUint32 m_start;
atUint32 m_count;
};
std::vector<Surface> m_surfaces;
const Mesh::SkinBanks& m_skinBanks;
};
} // namespace hecl::blender
namespace std {
template <>
struct hash<hecl::blender::Vector2f> {
std::size_t operator()(const hecl::blender::Vector2f& val) const noexcept {
std::size_t h = std::hash<float>()(val.val.simd[0]);
hecl::hash_combine_impl(h, std::hash<float>()(val.val.simd[1]));
return h;
}
};
template <>
struct hash<hecl::blender::Vector3f> {
std::size_t operator()(const hecl::blender::Vector3f& val) const noexcept {
std::size_t h = std::hash<float>()(val.val.simd[0]);
hecl::hash_combine_impl(h, std::hash<float>()(val.val.simd[1]));
hecl::hash_combine_impl(h, std::hash<float>()(val.val.simd[2]));
return h;
}
};
template <>
struct hash<hecl::blender::Vector4f> {
std::size_t operator()(const hecl::blender::Vector4f& val) const noexcept {
std::size_t h = std::hash<float>()(val.val.simd[0]);
hecl::hash_combine_impl(h, std::hash<float>()(val.val.simd[1]));
hecl::hash_combine_impl(h, std::hash<float>()(val.val.simd[2]));
hecl::hash_combine_impl(h, std::hash<float>()(val.val.simd[3]));
return h;
}
};
template <>
struct hash<array<hecl::blender::Mesh::SkinBind, 16>> {
std::size_t operator()(const array<hecl::blender::Mesh::SkinBind, 16>& val) const noexcept {
std::size_t h = 0;
for (const auto& bind : val) {
if (!bind.valid())
break;
hecl::hash_combine_impl(h, std::hash<float>()(bind.vg_idx));
hecl::hash_combine_impl(h, std::hash<float>()(bind.weight));
}
return h;
}
};
}

View File

@ -0,0 +1,76 @@
#pragma once
#include <functional>
#include <vector>
#include "hecl/FourCC.hpp"
#include "hecl/SystemChar.hpp"
#include <athena/DNA.hpp>
namespace athena::io {
class MemoryReader;
}
namespace hecl::blender {
enum class BlendType;
struct SDNABlock : public athena::io::DNA<athena::Endian::Little> {
AT_DECL_DNA
DNAFourCC magic;
DNAFourCC nameMagic;
Value<atUint32> numNames;
Vector<String<-1>, AT_DNA_COUNT(numNames)> names;
Align<4> align1;
DNAFourCC typeMagic;
Value<atUint32> numTypes;
Vector<String<-1>, AT_DNA_COUNT(numTypes)> types;
Align<4> align2;
DNAFourCC tlenMagic;
Vector<Value<atUint16>, AT_DNA_COUNT(numTypes)> tlens;
Align<4> align3;
DNAFourCC strcMagic;
Value<atUint32> numStrcs;
struct SDNAStruct : public athena::io::DNA<athena::Endian::Little> {
AT_DECL_DNA
Value<atUint16> type;
Value<atUint16> numFields;
struct SDNAField : public athena::io::DNA<athena::Endian::Little> {
AT_DECL_DNA
Value<atUint16> type;
Value<atUint16> name;
atUint32 offset;
};
Vector<SDNAField, AT_DNA_COUNT(numFields)> fields;
void computeOffsets(const SDNABlock& block);
const SDNAField* lookupField(const SDNABlock& block, const char* n) const;
};
Vector<SDNAStruct, AT_DNA_COUNT(numStrcs)> strcs;
const SDNAStruct* lookupStruct(const char* n, atUint32& idx) const;
};
struct FileBlock : public athena::io::DNA<athena::Endian::Little> {
AT_DECL_DNA
DNAFourCC type;
Value<atUint32> size;
Value<atUint64> ptr;
Value<atUint32> sdnaIdx;
Value<atUint32> count;
};
class SDNARead {
std::vector<uint8_t> m_data;
SDNABlock m_sdnaBlock;
public:
explicit SDNARead(SystemStringView path);
explicit operator bool() const { return !m_data.empty(); }
const SDNABlock& sdnaBlock() const { return m_sdnaBlock; }
void enumerate(const std::function<bool(const FileBlock& block, athena::io::MemoryReader& r)>& func) const;
};
BlendType GetBlendType(SystemStringView path);
} // namespace hecl::blender

View File

@ -0,0 +1,23 @@
#pragma once
#include <memory>
namespace hecl::blender {
class Connection;
class Token {
std::unique_ptr<Connection> m_conn;
public:
Connection& getBlenderConnection();
void shutdown();
Token() = default;
~Token();
Token(const Token&) = delete;
Token& operator=(const Token&) = delete;
Token(Token&&) = default;
Token& operator=(Token&&) = default;
};
} // namespace hecl::blender

320
hecl/include/hecl/CVar.hpp Normal file
View File

@ -0,0 +1,320 @@
#pragma once
#include <functional>
#include <string>
#include <vector>
#include <athena/DNAYaml.hpp>
#include <athena/Global.hpp>
#include <athena/Types.hpp>
namespace hecl {
namespace DNACVAR {
enum class EType : atUint8 { Boolean, Signed, Unsigned, Real, Literal, Vec2f, Vec2d, Vec3f, Vec3d, Vec4f, Vec4d };
enum class EFlags {
None = 0,
System = (1 << 0),
Game = (1 << 1),
Editor = (1 << 2),
Gui = (1 << 3),
Cheat = (1 << 4),
Hidden = (1 << 5),
ReadOnly = (1 << 6),
Archive = (1 << 7),
InternalArchivable = (1 << 8),
Modified = (1 << 9),
ModifyRestart = (1 << 10), //!< If this bit is set, any modification will inform the user that a restart is required
Color = (1 << 11), //!< If this bit is set, Vec3f and Vec4f will be displayed in the console with a colored square
NoDeveloper = (1 << 12), //!< Not even developer mode can modify this
Any = -1
};
ENABLE_BITWISE_ENUM(EFlags)
class CVar : public athena::io::DNA<athena::Endian::Big> {
public:
AT_DECL_DNA
String<-1> m_name;
String<-1> m_value;
};
struct CVarContainer : public athena::io::DNA<athena::Endian::Big> {
AT_DECL_DNA
Value<atUint32> magic = 'CVAR';
Value<atUint32> cvarCount;
Vector<CVar, AT_DNA_COUNT(cvarCount)> cvars;
};
} // namespace DNACVAR
class CVarManager;
class CVar : protected DNACVAR::CVar {
friend class CVarManager;
Delete _d;
public:
typedef std::function<void(CVar*)> ListenerFunc;
using EType = DNACVAR::EType;
using EFlags = DNACVAR::EFlags;
CVar(std::string_view name, std::string_view value, std::string_view help, EFlags flags);
CVar(std::string_view name, const atVec2f& value, std::string_view help, EFlags flags);
CVar(std::string_view name, const atVec2d& value, std::string_view help, EFlags flags);
CVar(std::string_view name, const atVec3f& value, std::string_view help, EFlags flags);
CVar(std::string_view name, const atVec3d& value, std::string_view help, EFlags flags);
CVar(std::string_view name, const atVec4f& value, std::string_view help, EFlags flags);
CVar(std::string_view name, const atVec4d& value, std::string_view help, EFlags flags);
CVar(std::string_view name, double value, std::string_view help, EFlags flags);
CVar(std::string_view name, bool value, std::string_view help, EFlags flags);
CVar(std::string_view name, int32_t value, std::string_view help, EFlags flags);
CVar(std::string_view name, uint32_t value, std::string_view help, EFlags flags);
std::string_view name() const { return m_name; }
std::string_view rawHelp() const { return m_help; }
std::string help() const;
std::string value() const { return m_value; }
template <typename T>
inline bool toValue(T& value) const;
atVec2f toVec2f(bool* isValid = nullptr) const;
atVec2d toVec2d(bool* isValid = nullptr) const;
atVec3f toVec3f(bool* isValid = nullptr) const;
atVec3d toVec3d(bool* isValid = nullptr) const;
atVec4f toVec4f(bool* isValid = nullptr) const;
atVec4d toVec4d(bool* isValid = nullptr) const;
double toReal(bool* isValid = nullptr) const;
bool toBoolean(bool* isValid = nullptr) const;
int32_t toSigned(bool* isValid = nullptr) const;
uint32_t toUnsigned(bool* isValid = nullptr) const;
std::wstring toWideLiteral(bool* isValid = nullptr) const;
std::string toLiteral(bool* isValid = nullptr) const;
template <typename T>
inline bool fromValue(T value) {
return false;
}
bool fromVec2f(const atVec2f& val);
bool fromVec2d(const atVec2d& val);
bool fromVec3f(const atVec3f& val);
bool fromVec3d(const atVec3d& val);
bool fromVec4f(const atVec4f& val);
bool fromVec4d(const atVec4d& val);
bool fromReal(double val);
bool fromBoolean(bool val);
bool fromInteger(int32_t val);
bool fromInteger(uint32_t val);
bool fromLiteral(std::string_view val);
bool fromLiteral(std::wstring_view val);
bool fromLiteralToType(std::string_view val);
bool fromLiteralToType(std::wstring_view val);
bool isVec2f() const { return m_type == EType::Vec2f; }
bool isVec2d() const { return m_type == EType::Vec2d; }
bool isVec3f() const { return m_type == EType::Vec3f; }
bool isVec3d() const { return m_type == EType::Vec3d; }
bool isVec4f() const { return m_type == EType::Vec4f; }
bool isVec4d() const { return m_type == EType::Vec4d; }
bool isFloat() const { return m_type == EType::Real; }
bool isBoolean() const { return m_type == EType::Boolean; }
bool isInteger() const { return m_type == EType::Signed || m_type == EType::Unsigned; }
bool isLiteral() const { return m_type == EType::Literal; }
bool isModified() const;
bool modificationRequiresRestart() const;
bool isReadOnly() const;
bool isCheat() const;
bool isHidden() const;
bool isArchive() const;
bool isInternalArchivable() const;
bool isNoDeveloper() const;
bool isColor() const;
bool wasDeserialized() const;
bool hasDefaultValue() const;
EType type() const { return m_type; }
EFlags flags() const { return (m_unlocked ? m_oldFlags : m_flags); }
/*!
* \brief Unlocks the CVar for writing if it is ReadOnly.
* <b>Handle with care!!!</b> if you use unlock(), make sure
* you lock the cvar using lock()
* \see lock
*/
void unlock();
/*!
* \brief Locks the CVar to prevent writing if it is ReadOnly.
* Unlike its partner function unlock, lock is harmless
* \see unlock
*/
void lock();
void addListener(ListenerFunc func) { m_listeners.push_back(std::move(func)); }
bool isValidInput(std::string_view input) const;
bool isValidInput(std::wstring_view input) const;
private:
CVar(std::string_view name, std::string_view help, EType type) : m_help(help), m_type(type) { m_name = name; }
void dispatch();
void clearModified();
void setModified();
std::string m_help;
EType m_type;
std::string m_defaultValue;
EFlags m_flags = EFlags::None;
EFlags m_oldFlags = EFlags::None;
bool m_unlocked = false;
bool m_wasDeserialized = false;
std::vector<ListenerFunc> m_listeners;
bool safeToModify(EType type) const;
void init(EFlags flags, bool removeColor = true);
};
template <>
inline bool CVar::toValue(atVec2f& value) const {
bool isValid = false;
value = toVec2f(&isValid);
return isValid;
}
template <>
inline bool CVar::toValue(atVec2d& value) const {
bool isValid = false;
value = toVec2d(&isValid);
return isValid;
}
template <>
inline bool CVar::toValue(atVec3f& value) const {
bool isValid = false;
value = toVec3f(&isValid);
return isValid;
}
template <>
inline bool CVar::toValue(atVec3d& value) const {
bool isValid = false;
value = toVec3d(&isValid);
return isValid;
}
template <>
inline bool CVar::toValue(atVec4f& value) const {
bool isValid = false;
value = toVec4f(&isValid);
return isValid;
}
template <>
inline bool CVar::toValue(atVec4d& value) const {
bool isValid = false;
value = toVec4d(&isValid);
return isValid;
}
template <>
inline bool CVar::toValue(double& value) const {
bool isValid = false;
value = toReal(&isValid);
return isValid;
}
template <>
inline bool CVar::toValue(float& value) const {
bool isValid = false;
value = static_cast<float>(toReal(&isValid));
return isValid;
}
template <>
inline bool CVar::toValue(bool& value) const {
bool isValid = false;
value = toBoolean(&isValid);
return isValid;
}
template <>
inline bool CVar::toValue(int32_t& value) const {
bool isValid = false;
value = toSigned(&isValid);
return isValid;
}
template <>
inline bool CVar::toValue(uint32_t& value) const {
bool isValid = false;
value = toUnsigned(&isValid);
return isValid;
}
template <>
inline bool CVar::toValue(std::wstring& value) const {
bool isValid = false;
value = toWideLiteral(&isValid);
return isValid;
}
template <>
inline bool CVar::toValue(std::string& value) const {
bool isValid = false;
value = toLiteral(&isValid);
return isValid;
}
template <>
inline bool CVar::fromValue(const atVec2f& val) {
return fromVec2f(val);
}
template <>
inline bool CVar::fromValue(const atVec2d& val) {
return fromVec2d(val);
}
template <>
inline bool CVar::fromValue(const atVec3f& val) {
return fromVec3f(val);
}
template <>
inline bool CVar::fromValue(const atVec3d& val) {
return fromVec3d(val);
}
template <>
inline bool CVar::fromValue(const atVec4f& val) {
return fromVec4f(val);
}
template <>
inline bool CVar::fromValue(const atVec4d& val) {
return fromVec4d(val);
}
template <>
inline bool CVar::fromValue(float val) {
return fromReal(val);
}
template <>
inline bool CVar::fromValue(double val) {
return fromReal(val);
}
template <>
inline bool CVar::fromValue(bool val) {
return fromBoolean(val);
}
template <>
inline bool CVar::fromValue(int32_t val) {
return fromInteger(val);
}
template <>
inline bool CVar::fromValue(uint32_t val) {
return fromInteger(val);
}
template <>
inline bool CVar::fromValue(std::string_view val) {
return fromLiteral(val);
}
template <>
inline bool CVar::fromValue(std::wstring_view val) {
return fromLiteral(val);
}
class CVarUnlocker {
CVar* m_cvar;
public:
CVarUnlocker(CVar* cvar) : m_cvar(cvar) {
if (m_cvar)
m_cvar->unlock();
}
~CVarUnlocker() {
if (m_cvar)
m_cvar->lock();
}
};
} // namespace hecl

View File

@ -0,0 +1,82 @@
#pragma once
#include <algorithm>
#include <cstdint>
#include <string>
#include "hecl/CVarManager.hpp"
#undef min
#undef max
namespace hecl {
using namespace std::literals;
#if defined(__APPLE__)
#define DEFAULT_GRAPHICS_API "Metal"sv
#else
#define DEFAULT_GRAPHICS_API "Vulkan"sv
#endif
struct CVarCommons {
CVarManager& m_mgr;
CVar* m_fullscreen = nullptr;
CVar* m_graphicsApi = nullptr;
CVar* m_drawSamples = nullptr;
CVar* m_texAnisotropy = nullptr;
CVar* m_deepColor = nullptr;
CVar* m_variableDt = nullptr;
CVar* m_debugOverlayPlayerInfo = nullptr;
CVar* m_debugOverlayWorldInfo = nullptr;
CVar* m_debugOverlayAreaInfo = nullptr;
CVar* m_debugOverlayShowFrameCounter = nullptr;
CVar* m_debugOverlayShowFramerate = nullptr;
CVar* m_debugOverlayShowInGameTime = nullptr;
CVar* m_debugOverlayShowResourceStats = nullptr;
CVar* m_debugOverlayShowRandomStats = nullptr;
CVar* m_debugOverlayShowRoomTimer = nullptr;
CVar* m_debugToolDrawAiPath = nullptr;
CVar* m_debugToolDrawLighting = nullptr;
CVar* m_debugToolDrawCollisionActors = nullptr;
CVar* m_debugToolDrawMazePath = nullptr;
CVar* m_debugToolDrawPlatformCollision = nullptr;
CVar* m_logFile = nullptr;
CVarCommons(CVarManager& manager);
bool getFullscreen() const { return m_fullscreen->toBoolean(); }
void setFullscreen(bool b) { m_fullscreen->fromBoolean(b); }
std::string getGraphicsApi() const { return m_graphicsApi->toLiteral(); }
void setGraphicsApi(std::string_view api) { m_graphicsApi->fromLiteral(api); }
uint32_t getSamples() const { return std::max(1u, m_drawSamples->toUnsigned()); }
void setSamples(uint32_t v) { m_drawSamples->fromInteger(std::max(uint32_t(1), v)); }
uint32_t getAnisotropy() const { return std::max(1u, uint32_t(m_texAnisotropy->toUnsigned())); }
void setAnisotropy(uint32_t v) { m_texAnisotropy->fromInteger(std::max(1u, v)); }
bool getDeepColor() const { return m_deepColor->toBoolean(); }
void setDeepColor(bool b) { m_deepColor->fromBoolean(b); }
bool getVariableFrameTime() const { return m_variableDt->toBoolean(); }
void setVariableFrameTime(bool b) { m_variableDt->fromBoolean(b); }
std::string getLogFile() const { return m_logFile->toLiteral(); };
void setLogFile(std::string_view log) { m_logFile->fromLiteral(log); }
void serialize() { m_mgr.serialize(); }
static CVarCommons* instance();
};
} // namespace hecl

View File

@ -0,0 +1,120 @@
#pragma once
#include <memory>
#include <string>
#include <unordered_map>
#include <vector>
#include "hecl/CVar.hpp"
#include "hecl/SystemChar.hpp"
namespace hecl {
namespace Runtime {
class FileStoreManager;
}
extern CVar* com_developer;
extern CVar* com_configfile;
extern CVar* com_enableCheats;
extern CVar* com_cubemaps;
class CVarManager final {
using CVarContainer = DNACVAR::CVarContainer;
template <typename T>
CVar* _newCVar(std::string_view name, std::string_view help, const T& value, CVar::EFlags flags) {
if (CVar* ret = registerCVar(std::make_unique<CVar>(name, value, help, flags))) {
deserialize(ret);
return ret;
}
return nullptr;
}
hecl::Runtime::FileStoreManager& m_store;
bool m_useBinary;
static CVarManager* m_instance;
public:
CVarManager() = delete;
CVarManager(const CVarManager&) = delete;
CVarManager& operator=(const CVarManager&) = delete;
CVarManager& operator=(const CVarManager&&) = delete;
CVarManager(hecl::Runtime::FileStoreManager& store, bool useBinary = false);
~CVarManager();
CVar* newCVar(std::string_view name, std::string_view help, const atVec2f& value, CVar::EFlags flags) {
return _newCVar<atVec2f>(name, help, value, flags);
}
CVar* newCVar(std::string_view name, std::string_view help, const atVec2d& value, CVar::EFlags flags) {
return _newCVar<atVec2d>(name, help, value, flags);
}
CVar* newCVar(std::string_view name, std::string_view help, const atVec3f& value, CVar::EFlags flags) {
return _newCVar<atVec3f>(name, help, value, flags);
}
CVar* newCVar(std::string_view name, std::string_view help, const atVec3d& value, CVar::EFlags flags) {
return _newCVar<atVec3d>(name, help, value, flags);
}
CVar* newCVar(std::string_view name, std::string_view help, const atVec4f& value, CVar::EFlags flags) {
return _newCVar<atVec4f>(name, help, value, flags);
}
CVar* newCVar(std::string_view name, std::string_view help, const atVec4d& value, CVar::EFlags flags) {
return _newCVar<atVec4d>(name, help, value, flags);
}
CVar* newCVar(std::string_view name, std::string_view help, std::string_view value, CVar::EFlags flags) {
return _newCVar<std::string_view>(name, help, value, flags);
}
CVar* newCVar(std::string_view name, std::string_view help, bool value, CVar::EFlags flags) {
return _newCVar<bool>(name, help, value, flags);
}
// Float and double are internally identical, all floating point values are stored as `double`
CVar* newCVar(std::string_view name, std::string_view help, float value, CVar::EFlags flags) {
return _newCVar<double>(name, help, static_cast<double>(value), flags);
}
CVar* newCVar(std::string_view name, std::string_view help, double value, CVar::EFlags flags) {
return _newCVar<double>(name, help, value, flags);
}
// Integer CVars can be seamlessly converted between either type, the distinction is to make usage absolutely clear
CVar* newCVar(std::string_view name, std::string_view help, int32_t value, CVar::EFlags flags) {
return _newCVar<int32_t>(name, help, value, flags);
}
CVar* newCVar(std::string_view name, std::string_view help, uint32_t value, CVar::EFlags flags) {
return _newCVar<uint32_t>(name, help, value, flags);
}
CVar* registerCVar(std::unique_ptr<CVar>&& cvar);
CVar* findCVar(std::string_view name);
template <class... _Args>
CVar* findOrMakeCVar(std::string_view name, _Args&&... args) {
if (CVar* cv = findCVar(name))
return cv;
return newCVar(name, std::forward<_Args>(args)...);
}
std::vector<CVar*> archivedCVars() const;
std::vector<CVar*> cvars(CVar::EFlags filter = CVar::EFlags::Any) const;
void deserialize(CVar* cvar);
void serialize();
static CVarManager* instance();
void proc();
void list(class Console* con, const std::vector<std::string>& args);
void setCVar(class Console* con, const std::vector<std::string>& args);
void getCVar(class Console* con, const std::vector<std::string>& args);
void setDeveloperMode(bool v, bool setDeserialized = false);
void setCheatsEnabled(bool v, bool setDeserialized = false);
bool restartRequired() const;
void parseCommandLine(const std::vector<SystemString>& args);
private:
bool suppressDeveloper();
void restoreDeveloper(bool oldDeveloper);
std::unordered_map<std::string, std::unique_ptr<CVar>> m_cvars;
std::unordered_map<std::string, std::string> m_deferedCVars;
};
} // namespace hecl

View File

@ -0,0 +1,110 @@
#pragma once
#include <condition_variable>
#include <cstddef>
#include <functional>
#include <list>
#include <memory>
#include <mutex>
#include <thread>
#include "hecl/Blender/Token.hpp"
#include "hecl/hecl.hpp"
#include "hecl/SystemChar.hpp"
#include <boo/ThreadLocalPtr.hpp>
namespace hecl::Database {
class IDataSpec;
}
namespace hecl {
class MultiProgressPrinter;
extern int CpuCountOverride;
void SetCpuCountOverride(int argc, const SystemChar** argv);
class ClientProcess {
std::mutex m_mutex;
std::condition_variable m_cv;
std::condition_variable m_initCv;
std::condition_variable m_waitCv;
const MultiProgressPrinter* m_progPrinter;
int m_completedCooks = 0;
int m_addedCooks = 0;
public:
struct Transaction {
ClientProcess& m_parent;
enum class Type { Buffer, Cook, Lambda } m_type;
bool m_complete = false;
virtual void run(blender::Token& btok) = 0;
Transaction(ClientProcess& parent, Type tp) : m_parent(parent), m_type(tp) {}
};
struct BufferTransaction final : Transaction {
ProjectPath m_path;
void* m_targetBuf;
size_t m_maxLen;
size_t m_offset;
void run(blender::Token& btok) override;
BufferTransaction(ClientProcess& parent, const ProjectPath& path, void* target, size_t maxLen, size_t offset)
: Transaction(parent, Type::Buffer), m_path(path), m_targetBuf(target), m_maxLen(maxLen), m_offset(offset) {}
};
struct CookTransaction final : Transaction {
ProjectPath m_path;
Database::IDataSpec* m_dataSpec;
bool m_returnResult = false;
bool m_force;
bool m_fast;
void run(blender::Token& btok) override;
CookTransaction(ClientProcess& parent, const ProjectPath& path, bool force, bool fast, Database::IDataSpec* spec)
: Transaction(parent, Type::Cook), m_path(path), m_dataSpec(spec), m_force(force), m_fast(fast) {}
};
struct LambdaTransaction final : Transaction {
std::function<void(blender::Token&)> m_func;
void run(blender::Token& btok) override;
LambdaTransaction(ClientProcess& parent, std::function<void(blender::Token&)>&& func)
: Transaction(parent, Type::Lambda), m_func(std::move(func)) {}
};
private:
std::list<std::shared_ptr<Transaction>> m_pendingQueue;
std::list<std::shared_ptr<Transaction>> m_completedQueue;
int m_inProgress = 0;
bool m_running = true;
struct Worker {
ClientProcess& m_proc;
int m_idx;
std::thread m_thr;
blender::Token m_blendTok;
bool m_didInit = false;
Worker(ClientProcess& proc, int idx);
void proc();
};
std::vector<Worker> m_workers;
static ThreadLocalPtr<ClientProcess::Worker> ThreadWorker;
public:
ClientProcess(const MultiProgressPrinter* progPrinter = nullptr);
~ClientProcess() { shutdown(); }
std::shared_ptr<const BufferTransaction> addBufferTransaction(const hecl::ProjectPath& path, void* target,
size_t maxLen, size_t offset);
std::shared_ptr<const CookTransaction> addCookTransaction(const hecl::ProjectPath& path, bool force, bool fast,
Database::IDataSpec* spec);
std::shared_ptr<const LambdaTransaction> addLambdaTransaction(std::function<void(blender::Token&)>&& func);
bool syncCook(const hecl::ProjectPath& path, Database::IDataSpec* spec, blender::Token& btok, bool force, bool fast);
void swapCompletedQueue(std::list<std::shared_ptr<Transaction>>& queue);
void waitUntilComplete();
void shutdown();
bool isBusy() const { return m_pendingQueue.size() || m_inProgress; }
static int GetThreadWorkerIdx() {
Worker* w = ThreadWorker.get();
if (w)
return w->m_idx;
return -1;
}
};
} // namespace hecl

View File

@ -0,0 +1,96 @@
#pragma once
#include <cstddef>
#include <memory>
#include <utility>
#include <boo/graphicsdev/D3D.hpp>
#include <boo/graphicsdev/GL.hpp>
#include <boo/graphicsdev/IGraphicsDataFactory.hpp>
#include <boo/graphicsdev/Metal.hpp>
#include <boo/graphicsdev/Vulkan.hpp>
namespace hecl {
namespace PlatformType {
using PlatformEnum = boo::IGraphicsDataFactory::Platform;
struct Null {};
struct OpenGL {
static constexpr PlatformEnum Enum = PlatformEnum::OpenGL;
static constexpr char Name[] = "OpenGL";
#if BOO_HAS_GL
using Context = boo::GLDataFactory::Context;
#endif
};
struct D3D11 {
static constexpr PlatformEnum Enum = PlatformEnum::D3D11;
static constexpr char Name[] = "D3D11";
#if _WIN32
using Context = boo::D3D11DataFactory::Context;
#endif
};
struct Metal {
static constexpr PlatformEnum Enum = PlatformEnum::Metal;
static constexpr char Name[] = "Metal";
#if BOO_HAS_METAL
using Context = boo::MetalDataFactory::Context;
#endif
};
struct Vulkan {
static constexpr PlatformEnum Enum = PlatformEnum::Vulkan;
static constexpr char Name[] = "Vulkan";
#if BOO_HAS_VULKAN
using Context = boo::VulkanDataFactory::Context;
#endif
};
struct NX {
static constexpr PlatformEnum Enum = PlatformEnum::NX;
static constexpr char Name[] = "NX";
#if BOO_HAS_NX
using Context = boo::NXDataFactory::Context;
#endif
};
} // namespace PlatformType
namespace PipelineStage {
using StageEnum = boo::PipelineStage;
struct Null {
static constexpr StageEnum Enum = StageEnum::Null;
static constexpr char Name[] = "Null";
};
struct Vertex {
static constexpr StageEnum Enum = StageEnum::Vertex;
static constexpr char Name[] = "Vertex";
};
struct Fragment {
static constexpr StageEnum Enum = StageEnum::Fragment;
static constexpr char Name[] = "Fragment";
};
struct Geometry {
static constexpr StageEnum Enum = StageEnum::Geometry;
static constexpr char Name[] = "Geometry";
};
struct Control {
static constexpr StageEnum Enum = StageEnum::Control;
static constexpr char Name[] = "Control";
};
struct Evaluation {
static constexpr StageEnum Enum = StageEnum::Evaluation;
static constexpr char Name[] = "Evaluation";
};
} // namespace PipelineStage
#ifdef _LIBCPP_VERSION
using StageBinaryData = std::shared_ptr<uint8_t>;
inline StageBinaryData MakeStageBinaryData(size_t sz) {
return StageBinaryData(new uint8_t[sz], std::default_delete<uint8_t[]>{});
}
#else
using StageBinaryData = std::shared_ptr<uint8_t[]>;
inline StageBinaryData MakeStageBinaryData(size_t sz) { return StageBinaryData(new uint8_t[sz]); }
#endif
template <typename P, typename S>
std::pair<StageBinaryData, size_t> CompileShader(std::string_view text);
} // namespace hecl

View File

@ -0,0 +1,113 @@
#pragma once
#include <functional>
#include <string>
#include <unordered_map>
#include <vector>
#include <boo/System.hpp>
#include <logvisor/logvisor.hpp>
namespace boo {
class IWindow;
enum class EModifierKey;
enum class ESpecialKey;
struct IGraphicsCommandQueue;
} // namespace boo
namespace hecl {
class CVarManager;
class CVar;
struct SConsoleCommand {
enum class ECommandFlags { Normal = 0, Cheat = (1 << 0), Developer = (1 << 1) };
std::string m_displayName;
std::string m_helpString;
std::string m_usage;
std::function<void(class Console*, const std::vector<std::string>&)> m_func;
ECommandFlags m_flags;
};
ENABLE_BITWISE_ENUM(SConsoleCommand::ECommandFlags)
class Console {
friend class LogVisorAdapter;
struct LogVisorAdapter : logvisor::ILogger {
Console* m_con;
LogVisorAdapter(Console* con) : logvisor::ILogger(log_typeid(LogVisorAdapter)), m_con(con) {}
~LogVisorAdapter() override = default;
void report(const char* modName, logvisor::Level severity, fmt::string_view format, fmt::format_args args) override;
void report(const char* modName, logvisor::Level severity, fmt::wstring_view format,
fmt::wformat_args args) override;
void reportSource(const char* modName, logvisor::Level severity, const char* file, unsigned linenum,
fmt::string_view format, fmt::format_args args) override;
void reportSource(const char* modName, logvisor::Level severity, const char* file, unsigned linenum,
fmt::wstring_view format, fmt::wformat_args args) override;
};
public:
static Console* m_instance;
enum class Level {
Info, /**< Non-error informative message */
Warning, /**< Non-error warning message */
Error, /**< Recoverable error message */
Fatal /**< Non-recoverable error message (Kept for compatibility with logvisor) */
};
enum class State { Closed, Closing, Opened, Opening };
private:
CVarManager* m_cvarMgr = nullptr;
boo::IWindow* m_window = nullptr;
std::unordered_map<std::string, SConsoleCommand> m_commands;
std::vector<std::pair<std::string, Level>> m_log;
int m_logOffset = 0;
std::string m_commandString;
std::vector<std::string> m_commandHistory;
int m_cursorPosition = -1;
int m_currentCommand = -1;
size_t m_maxLines = 0;
bool m_overwrite : 1;
bool m_cursorAtEnd : 1;
State m_state = State::Closed;
CVar* m_conSpeed;
CVar* m_conHeight;
float m_cachedConSpeed;
float m_cachedConHeight;
bool m_showCursor = true;
float m_cursorTime = 0.f;
public:
Console(CVarManager*);
void registerCommand(std::string_view name, std::string_view helpText, std::string_view usage,
std::function<void(Console*, const std::vector<std::string>&)>&& func,
SConsoleCommand::ECommandFlags cmdFlags = SConsoleCommand::ECommandFlags::Normal);
void unregisterCommand(std::string_view name);
void executeString(const std::string& strToExec);
void help(Console* con, const std::vector<std::string>& args);
void listCommands(Console* con, const std::vector<std::string>& args);
bool commandExists(std::string_view cmd) const;
void vreport(Level level, fmt::string_view format, fmt::format_args args);
template <typename S, typename... Args, typename Char = fmt::char_t<S>>
void report(Level level, const S& format, Args&&... args) {
vreport(level, fmt::to_string_view<Char>(format),
fmt::basic_format_args<fmt::buffer_context<Char>>(
fmt::make_args_checked<Args...>(format, args...)));
}
void init(boo::IWindow* ctx);
void proc();
void draw(boo::IGraphicsCommandQueue* gfxQ);
void handleCharCode(unsigned long chr, boo::EModifierKey mod, bool repeat);
void handleSpecialKeyDown(boo::ESpecialKey sp, boo::EModifierKey mod, bool repeat);
void handleSpecialKeyUp(boo::ESpecialKey sp, boo::EModifierKey mod);
void dumpLog();
static Console* instance();
static void RegisterLogger(Console* con);
bool isOpen() const { return m_state == State::Opened; }
};
} // namespace hecl

View File

@ -0,0 +1,442 @@
#pragma once
#include <cassert>
#include <cstddef>
#include <cstdint>
#include <functional>
#include <memory>
#include <string>
#include <unordered_map>
#include <vector>
#include "hecl/hecl.hpp"
#include <logvisor/logvisor.hpp>
namespace hecl {
class ClientProcess;
namespace Database {
class Project;
extern logvisor::Module LogModule;
/**
* @brief Nodegraph class for gathering dependency-resolved objects for packaging
*/
class PackageDepsgraph {
public:
struct Node {
enum class Type { Data, Group } type;
ProjectPath path;
ProjectPath cookedPath;
class ObjectBase* projectObj;
Node* sub;
Node* next;
};
private:
friend class Project;
std::vector<Node> m_nodes;
public:
const Node* getRootNode() const { return &m_nodes[0]; }
};
/**
* @brief Subclassed by dataspec entries to manage per-game aspects of the data pipeline
*
* The DataSpec class manages interfaces for unpackaging, cooking, and packaging
* of data for interacting with a specific system/game-engine.
*/
class IDataSpec {
const DataSpecEntry* m_specEntry;
public:
IDataSpec(const DataSpecEntry* specEntry) : m_specEntry(specEntry) {}
virtual ~IDataSpec() = default;
using FCookProgress = std::function<void(const SystemChar*)>;
/**
* @brief Extract Pass Info
*
* An extract pass iterates through a source package or image and
* reverses the cooking process by emitting editable resources
*/
struct ExtractPassInfo {
SystemString srcpath;
std::vector<SystemString> extractArgs;
bool force;
};
/**
* @brief Extract Report Representation
*
* Constructed by canExtract() to advise the user of the content about
* to be extracted
*/
struct ExtractReport {
SystemString name;
SystemString desc;
std::vector<ExtractReport> childOpts;
};
virtual void setThreadProject() {}
virtual bool canExtract([[maybe_unused]] const ExtractPassInfo& info,
[[maybe_unused]] std::vector<ExtractReport>& reps) {
LogModule.report(logvisor::Error, FMT_STRING("not implemented"));
return false;
}
virtual void doExtract([[maybe_unused]] const ExtractPassInfo& info,
[[maybe_unused]] const MultiProgressPrinter& progress) {}
virtual bool canCook([[maybe_unused]] const ProjectPath& path, [[maybe_unused]] blender::Token& btok) {
LogModule.report(logvisor::Error, FMT_STRING("not implemented"));
return false;
}
virtual const DataSpecEntry* overrideDataSpec([[maybe_unused]] const ProjectPath& path,
const DataSpecEntry* oldEntry) const {
return oldEntry;
}
virtual void doCook([[maybe_unused]] const ProjectPath& path, [[maybe_unused]] const ProjectPath& cookedPath,
[[maybe_unused]] bool fast, [[maybe_unused]] blender::Token& btok,
[[maybe_unused]] FCookProgress progress) {}
virtual bool canPackage([[maybe_unused]] const ProjectPath& path) {
return false;
}
virtual void doPackage([[maybe_unused]] const ProjectPath& path, [[maybe_unused]] const DataSpecEntry* entry,
[[maybe_unused]] bool fast, [[maybe_unused]] blender::Token& btok,
[[maybe_unused]] const MultiProgressPrinter& progress,
[[maybe_unused]] ClientProcess* cp = nullptr) {}
virtual void interruptCook() {}
const DataSpecEntry* getDataSpecEntry() const { return m_specEntry; }
};
/**
* @brief Pre-emptive indication of what the constructed DataSpec is used for
*/
enum class DataSpecTool { Extract, Cook, Package };
extern std::vector<const struct DataSpecEntry*> DATA_SPEC_REGISTRY;
/**
* @brief IDataSpec registry entry
*
* Auto-registers with data spec registry
*/
struct DataSpecEntry {
SystemStringView m_name;
SystemStringView m_desc;
SystemStringView m_pakExt;
std::function<std::unique_ptr<IDataSpec>(Project&, DataSpecTool)> m_factory;
DataSpecEntry(SystemStringView name, SystemStringView desc, SystemStringView pakExt,
std::function<std::unique_ptr<IDataSpec>(Project& project, DataSpecTool)>&& factory)
: m_name(name), m_desc(desc), m_pakExt(pakExt), m_factory(std::move(factory)) {}
};
/**
* @brief Base object to subclass for integrating with key project operations
*
* All project objects are provided with IDataObject pointers to their database
* entries. Subclasses register themselves with a type registry so instances
* are automatically constructed when performing operations like cooking and packaging.
*
* DO NOT CONSTRUCT THIS OR SUBCLASSES DIRECTLY!!
*/
class ObjectBase {
friend class Project;
SystemString m_path;
protected:
/**
* @brief Byte-order of target system
*/
enum class DataEndianness {
None,
Big, /**< Big-endian (PowerPC) */
Little /**< Little-endian (Intel) */
};
/**
* @brief Data-formats of target system
*/
enum class DataPlatform {
None,
Generic, /**< Scanline textures and 3-way shader bundle (GLSL, HLSL, SPIR-V) */
Revolution, /**< Tiled textures and GX register buffers */
Cafe /**< Swizzled textures and R700 shader objects */
};
using FDataAppender = std::function<void(const void* data, size_t len)>;
/**
* @brief Optional private method implemented by subclasses to cook objects
* @param dataAppender subclass calls this function zero or more times to provide cooked-data linearly
* @param endianness byte-order to target
* @param platform data-formats to target
* @return true if cook succeeded
*
* This method is called during IProject::cookPath().
* Part of the cooking process may include embedding database-refs to dependencies.
* This method should store the 64-bit value provided by IDataObject::id() when doing this.
*/
virtual bool cookObject([[maybe_unused]] FDataAppender dataAppender, [[maybe_unused]] DataEndianness endianness,
[[maybe_unused]] DataPlatform platform) {
return true;
}
using FDepAdder = std::function<void(ObjectBase*)>;
/**
* @brief Optional private method implemented by CProjectObject subclasses to resolve dependencies
* @param depAdder subclass calls this function zero or more times to register each dependency
*
* This method is called during IProject::packagePath().
* Dependencies registered via this method will eventually have this method called on themselves
* as well. This is a non-recursive operation, no need for subclasses to implement recursion-control.
*/
virtual void gatherDeps([[maybe_unused]] FDepAdder depAdder) {}
/**
* @brief Get a packagable FourCC representation of the object's type
* @return FourCC of the type
*/
virtual FourCC getType() const { return FourCC("NULL"); }
public:
ObjectBase(SystemStringView path) : m_path(path) {}
SystemStringView getPath() const { return m_path; }
};
/**
* @brief Main project interface
*
* Projects are intermediate working directories used for staging
* resources in their ideal editor-formats. This interface exposes all
* primary operations to perform on a given project.
*/
class Project {
public:
struct ProjectDataSpec {
const DataSpecEntry& spec;
ProjectPath cookedPath;
bool active;
};
private:
ProjectRootPath m_rootPath;
ProjectPath m_workRoot;
ProjectPath m_dotPath;
ProjectPath m_cookedRoot;
std::vector<ProjectDataSpec> m_compiledSpecs;
std::unordered_map<uint64_t, ProjectPath> m_bridgePathCache;
std::vector<std::unique_ptr<IDataSpec>> m_cookSpecs;
std::unique_ptr<IDataSpec> m_lastPackageSpec;
bool m_valid = false;
public:
Project(const ProjectRootPath& rootPath);
explicit operator bool() const { return m_valid; }
/**
* @brief Configuration file handle
*
* Holds a path to a line-delimited textual configuration file;
* opening a locked handle for read/write transactions
*/
class ConfigFile {
SystemString m_filepath;
std::vector<std::string> m_lines;
UniqueFilePtr m_lockedFile;
public:
ConfigFile(const Project& project, SystemStringView name, SystemStringView subdir = _SYS_STR("/.hecl/"));
std::vector<std::string>& lockAndRead();
void addLine(std::string_view line);
void removeLine(std::string_view refLine);
bool checkForLine(std::string_view refLine) const;
void unlockAndDiscard();
bool unlockAndCommit();
};
ConfigFile m_specs;
ConfigFile m_paths;
ConfigFile m_groups;
/**
* @brief A rough description of how 'expensive' a given cook operation is
*
* This is used to provide pretty colors during the cook operation
*/
enum class Cost { None, Light, Medium, Heavy };
/**
* @brief Get the path of the project's root-directory
* @return project root path
*
* Self explanatory
*/
const ProjectRootPath& getProjectRootPath() const { return m_rootPath; }
/**
* @brief Get the path of project's working directory
* @return project working path
*/
const ProjectPath& getProjectWorkingPath() const { return m_workRoot; }
/**
* @brief Get the path of project's cooked directory for a specific DataSpec
* @param spec DataSpec to retrieve path for
* @return project cooked path
*
* The cooked path matches the directory layout of the working directory
*/
const ProjectPath& getProjectCookedPath(const DataSpecEntry& spec) const;
/**
* @brief Add given file(s) to the database
* @param paths files or patterns within project
* @return true on success
*
* This method blocks while object hashing takes place
*/
bool addPaths(const std::vector<ProjectPath>& paths);
/**
* @brief Remove a given file or file-pattern from the database
* @param paths file(s) or pattern(s) within project
* @param recursive traverse into matched subdirectories
* @return true on success
*
* This method will not delete actual working files from the project
* directory. It will delete associated cooked objects though.
*/
bool removePaths(const std::vector<ProjectPath>& paths, bool recursive = false);
/**
* @brief Register a working sub-directory as a Dependency Group
* @param path directory to register as Dependency Group
* @return true on success
*
* Dependency Groups are used at runtime to stage burst load-transactions.
* They may only be added to directories and will automatically claim
* subdirectories as well.
*
* Cooked objects in dependency groups will be packaged contiguously
* and automatically duplicated if shared with other dependency groups.
* This contiguous storage makes for optimal loading from slow block-devices
* like optical drives.
*/
bool addGroup(const ProjectPath& path);
/**
* @brief Unregister a working sub-directory as a dependency group
* @param path directory to unregister as Dependency Group
* @return true on success
*/
bool removeGroup(const ProjectPath& path);
/**
* @brief Re-reads the data store holding user's spec preferences
*
* Call periodically in a long-term use of the hecl::Database::Project class.
* Install filesystem event-hooks if possible.
*/
void rescanDataSpecs();
/**
* @brief Return map populated with dataspecs targetable by this project interface
* @return Platform map with name-string keys and enable-status values
*/
const std::vector<ProjectDataSpec>& getDataSpecs() const { return m_compiledSpecs; }
/**
* @brief Enable persistent user preference for particular spec string(s)
* @param specs String(s) representing unique spec(s) from getDataSpecs
* @return true on success
*/
bool enableDataSpecs(const std::vector<SystemString>& specs);
/**
* @brief Disable persistent user preference for particular spec string(s)
* @param specs String(s) representing unique spec(s) from getDataSpecs
* @return true on success
*/
bool disableDataSpecs(const std::vector<SystemString>& specs);
/**
* @brief Begin cook process for specified directory
* @param path directory of intermediates to cook
* @param feedbackCb a callback to run reporting cook-progress
* @param recursive traverse subdirectories to cook as well
* @param fast enables faster (draft) extraction for supported data types
* @param spec if non-null, cook using a manually-selected dataspec
* @param cp if non-null, cook asynchronously via the ClientProcess
* @return true on success
*
* Object cooking is generally an expensive process for large projects.
* This method blocks execution during the procedure, with periodic
* feedback delivered via feedbackCb.
*/
bool cookPath(const ProjectPath& path, const MultiProgressPrinter& feedbackCb, bool recursive = false,
bool force = false, bool fast = false, const DataSpecEntry* spec = nullptr,
ClientProcess* cp = nullptr);
/**
* @brief Begin package process for specified !world.blend or directory
* @param path Path to !world.blend or directory
* @param feedbackCb a callback to run reporting cook-progress
* @param fast enables faster (draft) extraction for supported data types
* @param spec if non-null, cook using a manually-selected dataspec
* @param cp if non-null, cook asynchronously via the ClientProcess
*/
bool packagePath(const ProjectPath& path, const MultiProgressPrinter& feedbackCb, bool fast = false,
const DataSpecEntry* spec = nullptr, ClientProcess* cp = nullptr);
/**
* @brief Interrupts a cook in progress (call from SIGINT handler)
*
* Database corruption is bad! HECL spreads its data objects through
* the filesystem; this ensures that open objects are cleanly
* finalized or discarded before stopping.
*
* Note that this method returns immediately; the resumed cookPath()
* call will return as quickly as possible.
*/
void interruptCook();
/**
* @brief Delete cooked objects for directory
* @param path directory of intermediates to clean
* @param recursive traverse subdirectories to clean as well
* @return true on success
*
* Developers understand how useful 'clean' is. While ideally not required,
* it's useful for verifying that a rebuild from ground-up is doable.
*/
bool cleanPath(const ProjectPath& path, bool recursive = false);
/**
* @brief Constructs a full depsgraph of the project-subpath provided
* @param path Subpath of project to root depsgraph at
* @return Populated depsgraph ready to traverse
*/
PackageDepsgraph buildPackageDepsgraph(const ProjectPath& path);
/** Add ProjectPath to bridge cache */
void addBridgePathToCache(uint64_t id, const ProjectPath& path);
/** Clear all ProjectPaths in bridge cache */
void clearBridgePathCache();
/** Lookup ProjectPath from bridge cache */
const ProjectPath* lookupBridgePath(uint64_t id) const;
};
} // namespace Database
} // namespace hecl

View File

@ -0,0 +1,103 @@
#pragma once
#include <cstdint>
#include <cstddef>
#include <string>
#include <athena/DNA.hpp>
#include <logvisor/logvisor.hpp>
namespace hecl {
/**
* @brief FourCC representation used within HECL's database
*
* FourCCs are efficient, mnemonic four-char-sequences used to represent types
* while fitting comfortably in a 32-bit word. HECL uses a four-char array
* to remain endian-independent.
*/
class FourCC {
protected:
union {
char fcc[4];
uint32_t num = 0;
};
public:
// Sentinel FourCC
constexpr FourCC() noexcept = default;
constexpr FourCC(const FourCC& other) noexcept = default;
constexpr FourCC(FourCC&& other) noexcept = default;
constexpr FourCC(const char* name) noexcept : fcc{name[0], name[1], name[2], name[3]} {}
constexpr FourCC(uint32_t n) noexcept : num(n) {}
constexpr FourCC& operator=(const FourCC&) noexcept = default;
constexpr FourCC& operator=(FourCC&&) noexcept = default;
constexpr bool operator==(const FourCC& other) const noexcept { return num == other.num; }
constexpr bool operator!=(const FourCC& other) const noexcept { return !operator==(other); }
constexpr bool operator==(const char* other) const noexcept {
return other[0] == fcc[0] && other[1] == fcc[1] && other[2] == fcc[2] && other[3] == fcc[3];
}
constexpr bool operator!=(const char* other) const noexcept { return !operator==(other); }
constexpr bool operator==(int32_t other) const noexcept { return num == uint32_t(other); }
constexpr bool operator!=(int32_t other) const noexcept { return !operator==(other); }
constexpr bool operator==(uint32_t other) const noexcept { return num == other; }
constexpr bool operator!=(uint32_t other) const noexcept { return !operator==(other); }
std::string toString() const { return std::string(std::begin(fcc), std::end(fcc)); }
constexpr std::string_view toStringView() const { return std::string_view(fcc, std::size(fcc)); }
constexpr uint32_t toUint32() const noexcept { return num; }
constexpr const char* getChars() const noexcept { return fcc; }
constexpr char* getChars() noexcept { return fcc; }
constexpr bool IsValid() const noexcept { return num != 0; }
};
#define FOURCC(chars) FourCC(SBIG(chars))
using BigDNA = athena::io::DNA<athena::Endian::Big>;
/** FourCC with DNA read/write */
class DNAFourCC final : public BigDNA, public FourCC {
public:
constexpr DNAFourCC() noexcept : FourCC() {}
constexpr DNAFourCC(const FourCC& other) noexcept : FourCC(other) {}
constexpr DNAFourCC(const char* name) noexcept : FourCC(name) {}
constexpr DNAFourCC(uint32_t n) noexcept : FourCC(n) {}
DNAFourCC(athena::io::IStreamReader& r) { read(r); }
AT_DECL_EXPLICIT_DNA_YAML
};
template <>
inline void DNAFourCC::Enumerate<BigDNA::Read>(Read::StreamT& r) {
r.readUBytesToBuf(fcc, std::size(fcc));
}
template <>
inline void DNAFourCC::Enumerate<BigDNA::Write>(Write::StreamT& w) {
w.writeBytes(fcc, std::size(fcc));
}
template <>
inline void DNAFourCC::Enumerate<BigDNA::ReadYaml>(ReadYaml::StreamT& r) {
const std::string rs = r.readString();
rs.copy(fcc, std::size(fcc));
}
template <>
inline void DNAFourCC::Enumerate<BigDNA::WriteYaml>(WriteYaml::StreamT& w) {
w.writeString(std::string_view{fcc, std::size(fcc)});
}
template <>
inline void DNAFourCC::Enumerate<BigDNA::BinarySize>(BinarySize::StreamT& s) {
s += std::size(fcc);
}
} // namespace hecl
namespace std {
template <>
struct hash<hecl::FourCC> {
size_t operator()(const hecl::FourCC& val) const noexcept { return val.toUint32(); }
};
} // namespace std
FMT_CUSTOM_FORMATTER(hecl::FourCC, "{:c}{:c}{:c}{:c}", obj.getChars()[0], obj.getChars()[1], obj.getChars()[2],
obj.getChars()[3])
FMT_CUSTOM_FORMATTER(hecl::DNAFourCC, "{:c}{:c}{:c}{:c}", obj.getChars()[0], obj.getChars()[1], obj.getChars()[2],
obj.getChars()[3])

View File

@ -0,0 +1,27 @@
#pragma once
#include <athena/DNA.hpp>
namespace hecl {
enum class HMDLTopology : atUint32 {
Triangles,
TriStrips,
};
#define HECL_HMDL_META_SZ 32
struct HMDLMeta : athena::io::DNA<athena::Endian::Big> {
AT_DECL_DNA
Value<atUint32> magic = 'TACO';
Value<HMDLTopology> topology;
Value<atUint32> vertStride;
Value<atUint32> vertCount;
Value<atUint32> indexCount;
Value<atUint32> colorCount;
Value<atUint32> uvCount;
Value<atUint16> weightCount;
Value<atUint16> bankCount;
};
} // namespace hecl

View File

@ -0,0 +1,821 @@
//===-- llvm/Support/MathExtras.h - Useful math functions -------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file contains some functions that are useful for math stuff.
//
//===----------------------------------------------------------------------===//
#pragma once
/// \macro LLVM_GNUC_PREREQ
/// \brief Extend the default __GNUC_PREREQ even if glibc's features.h isn't
/// available.
#ifndef LLVM_GNUC_PREREQ
#if defined(__GNUC__) && defined(__GNUC_MINOR__) && defined(__GNUC_PATCHLEVEL__)
#define LLVM_GNUC_PREREQ(maj, min, patch) \
((__GNUC__ << 20) + (__GNUC_MINOR__ << 10) + __GNUC_PATCHLEVEL__ >= ((maj) << 20) + ((min) << 10) + (patch))
#elif defined(__GNUC__) && defined(__GNUC_MINOR__)
#define LLVM_GNUC_PREREQ(maj, min, patch) ((__GNUC__ << 20) + (__GNUC_MINOR__ << 10) >= ((maj) << 20) + ((min) << 10))
#else
#define LLVM_GNUC_PREREQ(maj, min, patch) 0
#endif
#endif
#ifndef __has_builtin
#define __has_builtin(x) 0
#endif
#include "hecl.hpp"
#include <algorithm>
#include <cassert>
#include <cstring>
#include <type_traits>
#include <limits>
#ifdef _MSC_VER
#include <intrin.h>
#endif
#ifdef __ANDROID_NDK__
#include <android/api-level.h>
#endif
namespace hecl {
namespace llvm {
/// \brief The behavior an operation has on an input of 0.
enum ZeroBehavior {
/// \brief The returned value is undefined.
ZB_Undefined,
/// \brief The returned value is numeric_limits<T>::max()
ZB_Max,
/// \brief The returned value is numeric_limits<T>::digits
ZB_Width
};
namespace detail {
template <typename T, std::size_t SizeOfT>
struct TrailingZerosCounter {
static std::size_t count(T Val, ZeroBehavior) {
if (!Val)
return std::numeric_limits<T>::digits;
if (Val & 0x1)
return 0;
// Bisection method.
std::size_t ZeroBits = 0;
T Shift = std::numeric_limits<T>::digits >> 1;
T Mask = std::numeric_limits<T>::max() >> Shift;
while (Shift) {
if ((Val & Mask) == 0) {
Val >>= Shift;
ZeroBits |= Shift;
}
Shift >>= 1;
Mask >>= Shift;
}
return ZeroBits;
}
};
#if __GNUC__ >= 4 || defined(_MSC_VER)
template <typename T>
struct TrailingZerosCounter<T, 4> {
static std::size_t count(T Val, ZeroBehavior ZB) {
if (ZB != ZB_Undefined && Val == 0)
return 32;
#if __has_builtin(__builtin_ctz) || LLVM_GNUC_PREREQ(4, 0, 0)
return __builtin_ctz(Val);
#elif defined(_MSC_VER)
unsigned long Index;
_BitScanForward(&Index, Val);
return Index;
#endif
}
};
#if !defined(_MSC_VER) || defined(_M_X64)
template <typename T>
struct TrailingZerosCounter<T, 8> {
static std::size_t count(T Val, ZeroBehavior ZB) {
if (ZB != ZB_Undefined && Val == 0)
return 64;
#if __has_builtin(__builtin_ctzll) || LLVM_GNUC_PREREQ(4, 0, 0)
return __builtin_ctzll(Val);
#elif defined(_MSC_VER)
unsigned long Index;
_BitScanForward64(&Index, Val);
return Index;
#endif
}
};
#endif
#endif
} // namespace detail
/// \brief Count number of 0's from the least significant bit to the most
/// stopping at the first 1.
///
/// Only unsigned integral types are allowed.
///
/// \param ZB the behavior on an input of 0. Only ZB_Width and ZB_Undefined are
/// valid arguments.
template <typename T>
std::size_t countTrailingZeros(T Val, ZeroBehavior ZB = ZB_Width) {
static_assert(std::numeric_limits<T>::is_integer && !std::numeric_limits<T>::is_signed,
"Only unsigned integral types are allowed.");
return detail::TrailingZerosCounter<T, sizeof(T)>::count(Val, ZB);
}
namespace detail {
template <typename T, std::size_t SizeOfT>
struct LeadingZerosCounter {
static std::size_t count(T Val, ZeroBehavior) {
if (!Val)
return std::numeric_limits<T>::digits;
// Bisection method.
std::size_t ZeroBits = 0;
for (T Shift = std::numeric_limits<T>::digits >> 1; Shift; Shift >>= 1) {
T Tmp = Val >> Shift;
if (Tmp)
Val = Tmp;
else
ZeroBits |= Shift;
}
return ZeroBits;
}
};
#if __GNUC__ >= 4 || defined(_MSC_VER)
template <typename T>
struct LeadingZerosCounter<T, 4> {
static std::size_t count(T Val, ZeroBehavior ZB) {
if (ZB != ZB_Undefined && Val == 0)
return 32;
#if __has_builtin(__builtin_clz) || LLVM_GNUC_PREREQ(4, 0, 0)
return __builtin_clz(Val);
#elif defined(_MSC_VER)
unsigned long Index;
_BitScanReverse(&Index, Val);
return Index ^ 31;
#endif
}
};
#if !defined(_MSC_VER) || defined(_M_X64)
template <typename T>
struct LeadingZerosCounter<T, 8> {
static std::size_t count(T Val, ZeroBehavior ZB) {
if (ZB != ZB_Undefined && Val == 0)
return 64;
#if __has_builtin(__builtin_clzll) || LLVM_GNUC_PREREQ(4, 0, 0)
return __builtin_clzll(Val);
#elif defined(_MSC_VER)
unsigned long Index;
_BitScanReverse64(&Index, Val);
return Index ^ 63;
#endif
}
};
#endif
#endif
} // namespace detail
/// \brief Count number of 0's from the most significant bit to the least
/// stopping at the first 1.
///
/// Only unsigned integral types are allowed.
///
/// \param ZB the behavior on an input of 0. Only ZB_Width and ZB_Undefined are
/// valid arguments.
template <typename T>
std::size_t countLeadingZeros(T Val, ZeroBehavior ZB = ZB_Width) {
static_assert(std::numeric_limits<T>::is_integer && !std::numeric_limits<T>::is_signed,
"Only unsigned integral types are allowed.");
return detail::LeadingZerosCounter<T, sizeof(T)>::count(Val, ZB);
}
/// \brief Get the index of the first set bit starting from the least
/// significant bit.
///
/// Only unsigned integral types are allowed.
///
/// \param ZB the behavior on an input of 0. Only ZB_Max and ZB_Undefined are
/// valid arguments.
template <typename T>
T findFirstSet(T Val, ZeroBehavior ZB = ZB_Max) {
if (ZB == ZB_Max && Val == 0)
return std::numeric_limits<T>::max();
return countTrailingZeros(Val, ZB_Undefined);
}
/// \brief Get the index of the last set bit starting from the least
/// significant bit.
///
/// Only unsigned integral types are allowed.
///
/// \param ZB the behavior on an input of 0. Only ZB_Max and ZB_Undefined are
/// valid arguments.
template <typename T>
T findLastSet(T Val, ZeroBehavior ZB = ZB_Max) {
if (ZB == ZB_Max && Val == 0)
return std::numeric_limits<T>::max();
// Use ^ instead of - because both gcc and llvm can remove the associated ^
// in the __builtin_clz intrinsic on x86.
return countLeadingZeros(Val, ZB_Undefined) ^ (std::numeric_limits<T>::digits - 1);
}
/// \brief Macro compressed bit reversal table for 256 bits.
///
/// http://graphics.stanford.edu/~seander/bithacks.html#BitReverseTable
static const unsigned char BitReverseTable256[256] = {
#define R2(n) n, n + 2 * 64, n + 1 * 64, n + 3 * 64
#define R4(n) R2(n), R2(n + 2 * 16), R2(n + 1 * 16), R2(n + 3 * 16)
#define R6(n) R4(n), R4(n + 2 * 4), R4(n + 1 * 4), R4(n + 3 * 4)
R6(0), R6(2), R6(1), R6(3)
#undef R2
#undef R4
#undef R6
};
/// \brief Reverse the bits in \p Val.
template <typename T>
T reverseBits(T Val) {
unsigned char in[sizeof(Val)];
unsigned char out[sizeof(Val)];
std::memcpy(in, &Val, sizeof(Val));
for (unsigned i = 0; i < sizeof(Val); ++i)
out[(sizeof(Val) - i) - 1] = BitReverseTable256[in[i]];
std::memcpy(&Val, out, sizeof(Val));
return Val;
}
// NOTE: The following support functions use the _32/_64 extensions instead of
// type overloading so that signed and unsigned integers can be used without
// ambiguity.
/// Hi_32 - This function returns the high 32 bits of a 64 bit value.
constexpr inline uint32_t Hi_32(uint64_t Value) { return static_cast<uint32_t>(Value >> 32); }
/// Lo_32 - This function returns the low 32 bits of a 64 bit value.
constexpr inline uint32_t Lo_32(uint64_t Value) { return static_cast<uint32_t>(Value); }
/// Make_64 - This functions makes a 64-bit integer from a high / low pair of
/// 32-bit integers.
constexpr inline uint64_t Make_64(uint32_t High, uint32_t Low) { return ((uint64_t)High << 32) | (uint64_t)Low; }
/// isInt - Checks if an integer fits into the given bit width.
template <unsigned N>
constexpr inline bool isInt(int64_t x) {
return N >= 64 || (-(INT64_C(1) << (N - 1)) <= x && x < (INT64_C(1) << (N - 1)));
}
// Template specializations to get better code for common cases.
template <>
constexpr inline bool isInt<8>(int64_t x) {
return static_cast<int8_t>(x) == x;
}
template <>
constexpr inline bool isInt<16>(int64_t x) {
return static_cast<int16_t>(x) == x;
}
template <>
constexpr inline bool isInt<32>(int64_t x) {
return static_cast<int32_t>(x) == x;
}
/// isShiftedInt<N,S> - Checks if a signed integer is an N bit number shifted
/// left by S.
template <unsigned N, unsigned S>
constexpr inline bool isShiftedInt(int64_t x) {
static_assert(N > 0, "isShiftedInt<0> doesn't make sense (refers to a 0-bit number.");
static_assert(N + S <= 64, "isShiftedInt<N, S> with N + S > 64 is too wide.");
return isInt<N + S>(x) && (x % (UINT64_C(1) << S) == 0);
}
/// isUInt - Checks if an unsigned integer fits into the given bit width.
///
/// This is written as two functions rather than as simply
///
/// return N >= 64 || X < (UINT64_C(1) << N);
///
/// to keep MSVC from (incorrectly) warning on isUInt<64> that we're shifting
/// left too many places.
template <unsigned N>
constexpr inline typename std::enable_if<(N < 64), bool>::type isUInt(uint64_t X) {
static_assert(N > 0, "isUInt<0> doesn't make sense");
return X < (UINT64_C(1) << (N));
}
template <unsigned N>
constexpr inline typename std::enable_if<N >= 64, bool>::type isUInt(uint64_t X) {
return true;
}
// Template specializations to get better code for common cases.
template <>
constexpr inline bool isUInt<8>(uint64_t x) {
return static_cast<uint8_t>(x) == x;
}
template <>
constexpr inline bool isUInt<16>(uint64_t x) {
return static_cast<uint16_t>(x) == x;
}
template <>
constexpr inline bool isUInt<32>(uint64_t x) {
return static_cast<uint32_t>(x) == x;
}
/// Checks if a unsigned integer is an N bit number shifted left by S.
template <unsigned N, unsigned S>
constexpr inline bool isShiftedUInt(uint64_t x) {
static_assert(N > 0, "isShiftedUInt<0> doesn't make sense (refers to a 0-bit number)");
static_assert(N + S <= 64, "isShiftedUInt<N, S> with N + S > 64 is too wide.");
// Per the two static_asserts above, S must be strictly less than 64. So
// 1 << S is not undefined behavior.
return isUInt<N + S>(x) && (x % (UINT64_C(1) << S) == 0);
}
/// Gets the maximum value for a N-bit unsigned integer.
inline uint64_t maxUIntN(uint64_t N) {
assert(N > 0 && N <= 64 && "integer width out of range");
// uint64_t(1) << 64 is undefined behavior, so we can't do
// (uint64_t(1) << N) - 1
// without checking first that N != 64. But this works and doesn't have a
// branch.
return UINT64_MAX >> (64 - N);
}
/// Gets the minimum value for a N-bit signed integer.
inline int64_t minIntN(int64_t N) {
assert(N > 0 && N <= 64 && "integer width out of range");
return -(UINT64_C(1) << (N - 1));
}
/// Gets the maximum value for a N-bit signed integer.
inline int64_t maxIntN(int64_t N) {
assert(N > 0 && N <= 64 && "integer width out of range");
// This relies on two's complement wraparound when N == 64, so we convert to
// int64_t only at the very end to avoid UB.
return (UINT64_C(1) << (N - 1)) - 1;
}
/// isUIntN - Checks if an unsigned integer fits into the given (dynamic)
/// bit width.
inline bool isUIntN(unsigned N, uint64_t x) { return N >= 64 || x <= maxUIntN(N); }
/// isIntN - Checks if an signed integer fits into the given (dynamic)
/// bit width.
inline bool isIntN(unsigned N, int64_t x) { return N >= 64 || (minIntN(N) <= x && x <= maxIntN(N)); }
/// isMask_32 - This function returns true if the argument is a non-empty
/// sequence of ones starting at the least significant bit with the remainder
/// zero (32 bit version). Ex. isMask_32(0x0000FFFFU) == true.
constexpr inline bool isMask_32(uint32_t Value) { return Value && ((Value + 1) & Value) == 0; }
/// isMask_64 - This function returns true if the argument is a non-empty
/// sequence of ones starting at the least significant bit with the remainder
/// zero (64 bit version).
constexpr inline bool isMask_64(uint64_t Value) { return Value && ((Value + 1) & Value) == 0; }
/// isShiftedMask_32 - This function returns true if the argument contains a
/// non-empty sequence of ones with the remainder zero (32 bit version.)
/// Ex. isShiftedMask_32(0x0000FF00U) == true.
constexpr inline bool isShiftedMask_32(uint32_t Value) { return Value && isMask_32((Value - 1) | Value); }
/// isShiftedMask_64 - This function returns true if the argument contains a
/// non-empty sequence of ones with the remainder zero (64 bit version.)
constexpr inline bool isShiftedMask_64(uint64_t Value) { return Value && isMask_64((Value - 1) | Value); }
/// isPowerOf2_32 - This function returns true if the argument is a power of
/// two > 0. Ex. isPowerOf2_32(0x00100000U) == true (32 bit edition.)
constexpr inline bool isPowerOf2_32(uint32_t Value) { return Value && !(Value & (Value - 1)); }
/// isPowerOf2_64 - This function returns true if the argument is a power of two
/// > 0 (64 bit edition.)
constexpr inline bool isPowerOf2_64(uint64_t Value) { return Value && !(Value & (Value - int64_t(1L))); }
/// ByteSwap_16 - This function returns a byte-swapped representation of the
/// 16-bit argument, Value.
inline uint16_t ByteSwap_16(uint16_t Value) { return hecl::bswap16(Value); }
/// ByteSwap_32 - This function returns a byte-swapped representation of the
/// 32-bit argument, Value.
inline uint32_t ByteSwap_32(uint32_t Value) { return hecl::bswap32(Value); }
/// ByteSwap_64 - This function returns a byte-swapped representation of the
/// 64-bit argument, Value.
inline uint64_t ByteSwap_64(uint64_t Value) { return hecl::bswap64(Value); }
/// \brief Count the number of ones from the most significant bit to the first
/// zero bit.
///
/// Ex. CountLeadingOnes(0xFF0FFF00) == 8.
/// Only unsigned integral types are allowed.
///
/// \param ZB the behavior on an input of all ones. Only ZB_Width and
/// ZB_Undefined are valid arguments.
template <typename T>
std::size_t countLeadingOnes(T Value, ZeroBehavior ZB = ZB_Width) {
static_assert(std::numeric_limits<T>::is_integer && !std::numeric_limits<T>::is_signed,
"Only unsigned integral types are allowed.");
return countLeadingZeros(~Value, ZB);
}
/// \brief Count the number of ones from the least significant bit to the first
/// zero bit.
///
/// Ex. countTrailingOnes(0x00FF00FF) == 8.
/// Only unsigned integral types are allowed.
///
/// \param ZB the behavior on an input of all ones. Only ZB_Width and
/// ZB_Undefined are valid arguments.
template <typename T>
std::size_t countTrailingOnes(T Value, ZeroBehavior ZB = ZB_Width) {
static_assert(std::numeric_limits<T>::is_integer && !std::numeric_limits<T>::is_signed,
"Only unsigned integral types are allowed.");
return countTrailingZeros(~Value, ZB);
}
namespace detail {
template <typename T, std::size_t SizeOfT>
struct PopulationCounter {
static unsigned count(T Value) {
// Generic version, forward to 32 bits.
static_assert(SizeOfT <= 4, "Not implemented!");
#if __GNUC__ >= 4
return __builtin_popcount(Value);
#else
uint32_t v = Value;
v = v - ((v >> 1) & 0x55555555);
v = (v & 0x33333333) + ((v >> 2) & 0x33333333);
return ((v + (v >> 4) & 0xF0F0F0F) * 0x1010101) >> 24;
#endif
}
};
template <typename T>
struct PopulationCounter<T, 8> {
static unsigned count(T Value) {
#if __GNUC__ >= 4
return __builtin_popcountll(Value);
#else
uint64_t v = Value;
v = v - ((v >> 1) & 0x5555555555555555ULL);
v = (v & 0x3333333333333333ULL) + ((v >> 2) & 0x3333333333333333ULL);
v = (v + (v >> 4)) & 0x0F0F0F0F0F0F0F0FULL;
return unsigned((uint64_t)(v * 0x0101010101010101ULL) >> 56);
#endif
}
};
} // namespace detail
/// \brief Count the number of set bits in a value.
/// Ex. countPopulation(0xF000F000) = 8
/// Returns 0 if the word is zero.
template <typename T>
inline unsigned countPopulation(T Value) {
static_assert(std::numeric_limits<T>::is_integer && !std::numeric_limits<T>::is_signed,
"Only unsigned integral types are allowed.");
return detail::PopulationCounter<T, sizeof(T)>::count(Value);
}
/// Log2 - This function returns the log base 2 of the specified value
inline double Log2(double Value) {
#if defined(__ANDROID_API__) && __ANDROID_API__ < 18
return __builtin_log(Value) / __builtin_log(2.0);
#else
return log2(Value);
#endif
}
/// Log2_32 - This function returns the floor log base 2 of the specified value,
/// -1 if the value is zero. (32 bit edition.)
/// Ex. Log2_32(32) == 5, Log2_32(1) == 0, Log2_32(0) == -1, Log2_32(6) == 2
inline unsigned Log2_32(uint32_t Value) { return 31 - countLeadingZeros(Value); }
/// Log2_64 - This function returns the floor log base 2 of the specified value,
/// -1 if the value is zero. (64 bit edition.)
inline unsigned Log2_64(uint64_t Value) { return 63 - countLeadingZeros(Value); }
/// Log2_32_Ceil - This function returns the ceil log base 2 of the specified
/// value, 32 if the value is zero. (32 bit edition).
/// Ex. Log2_32_Ceil(32) == 5, Log2_32_Ceil(1) == 0, Log2_32_Ceil(6) == 3
inline unsigned Log2_32_Ceil(uint32_t Value) { return 32 - countLeadingZeros(Value - 1); }
/// Log2_64_Ceil - This function returns the ceil log base 2 of the specified
/// value, 64 if the value is zero. (64 bit edition.)
inline unsigned Log2_64_Ceil(uint64_t Value) { return 64 - countLeadingZeros(Value - 1); }
/// GreatestCommonDivisor64 - Return the greatest common divisor of the two
/// values using Euclid's algorithm.
inline uint64_t GreatestCommonDivisor64(uint64_t A, uint64_t B) {
while (B) {
uint64_t T = B;
B = A % B;
A = T;
}
return A;
}
/// BitsToDouble - This function takes a 64-bit integer and returns the bit
/// equivalent double.
inline double BitsToDouble(uint64_t Bits) {
union {
uint64_t L;
double D;
} T;
T.L = Bits;
return T.D;
}
/// BitsToFloat - This function takes a 32-bit integer and returns the bit
/// equivalent float.
inline float BitsToFloat(uint32_t Bits) {
union {
uint32_t I;
float F;
} T;
T.I = Bits;
return T.F;
}
/// DoubleToBits - This function takes a double and returns the bit
/// equivalent 64-bit integer. Note that copying doubles around
/// changes the bits of NaNs on some hosts, notably x86, so this
/// routine cannot be used if these bits are needed.
inline uint64_t DoubleToBits(double Double) {
union {
uint64_t L;
double D;
} T;
T.D = Double;
return T.L;
}
/// FloatToBits - This function takes a float and returns the bit
/// equivalent 32-bit integer. Note that copying floats around
/// changes the bits of NaNs on some hosts, notably x86, so this
/// routine cannot be used if these bits are needed.
inline uint32_t FloatToBits(float Float) {
union {
uint32_t I;
float F;
} T;
T.F = Float;
return T.I;
}
/// MinAlign - A and B are either alignments or offsets. Return the minimum
/// alignment that may be assumed after adding the two together.
constexpr inline uint64_t MinAlign(uint64_t A, uint64_t B) {
// The largest power of 2 that divides both A and B.
//
// Replace "-Value" by "1+~Value" in the following commented code to avoid
// MSVC warning C4146
// return (A | B) & -(A | B);
return (A | B) & (1 + ~(A | B));
}
/// \brief Aligns \c Addr to \c Alignment bytes, rounding up.
///
/// Alignment should be a power of two. This method rounds up, so
/// alignAddr(7, 4) == 8 and alignAddr(8, 4) == 8.
inline uintptr_t alignAddr(const void* Addr, size_t Alignment) {
assert(Alignment && isPowerOf2_64((uint64_t)Alignment) && "Alignment is not a power of two!");
assert((uintptr_t)Addr + Alignment - 1 >= (uintptr_t)Addr);
return (((uintptr_t)Addr + Alignment - 1) & ~(uintptr_t)(Alignment - 1));
}
/// \brief Returns the necessary adjustment for aligning \c Ptr to \c Alignment
/// bytes, rounding up.
inline size_t alignmentAdjustment(const void* Ptr, size_t Alignment) {
return alignAddr(Ptr, Alignment) - (uintptr_t)Ptr;
}
/// NextPowerOf2 - Returns the next power of two (in 64-bits)
/// that is strictly greater than A. Returns zero on overflow.
inline uint64_t NextPowerOf2(uint64_t A) {
A |= (A >> 1);
A |= (A >> 2);
A |= (A >> 4);
A |= (A >> 8);
A |= (A >> 16);
A |= (A >> 32);
return A + 1;
}
/// Returns the power of two which is less than or equal to the given value.
/// Essentially, it is a floor operation across the domain of powers of two.
inline uint64_t PowerOf2Floor(uint64_t A) {
if (!A)
return 0;
return 1ull << (63 - countLeadingZeros(A, ZB_Undefined));
}
/// Returns the power of two which is greater than or equal to the given value.
/// Essentially, it is a ceil operation across the domain of powers of two.
inline uint64_t PowerOf2Ceil(uint64_t A) {
if (!A)
return 0;
return NextPowerOf2(A - 1);
}
/// Returns the next integer (mod 2**64) that is greater than or equal to
/// \p Value and is a multiple of \p Align. \p Align must be non-zero.
///
/// If non-zero \p Skew is specified, the return value will be a minimal
/// integer that is greater than or equal to \p Value and equal to
/// \p Align * N + \p Skew for some integer N. If \p Skew is larger than
/// \p Align, its value is adjusted to '\p Skew mod \p Align'.
///
/// Examples:
/// \code
/// alignTo(5, 8) = 8
/// alignTo(17, 8) = 24
/// alignTo(~0LL, 8) = 0
/// alignTo(321, 255) = 510
///
/// alignTo(5, 8, 7) = 7
/// alignTo(17, 8, 1) = 17
/// alignTo(~0LL, 8, 3) = 3
/// alignTo(321, 255, 42) = 552
/// \endcode
inline uint64_t alignTo(uint64_t Value, uint64_t Align, uint64_t Skew = 0) {
assert(Align != 0u && "Align can't be 0.");
Skew %= Align;
return (Value + Align - 1 - Skew) / Align * Align + Skew;
}
/// Returns the next integer (mod 2**64) that is greater than or equal to
/// \p Value and is a multiple of \c Align. \c Align must be non-zero.
template <uint64_t Align>
constexpr inline uint64_t alignTo(uint64_t Value) {
static_assert(Align != 0u, "Align must be non-zero");
return (Value + Align - 1) / Align * Align;
}
/// \c alignTo for contexts where a constant expression is required.
/// \sa alignTo
///
/// \todo FIXME: remove when \c constexpr becomes really \c constexpr
template <uint64_t Align>
struct AlignTo {
static_assert(Align != 0u, "Align must be non-zero");
template <uint64_t Value>
struct from_value {
static const uint64_t value = (Value + Align - 1) / Align * Align;
};
};
/// Returns the largest uint64_t less than or equal to \p Value and is
/// \p Skew mod \p Align. \p Align must be non-zero
inline uint64_t alignDown(uint64_t Value, uint64_t Align, uint64_t Skew = 0) {
assert(Align != 0u && "Align can't be 0.");
Skew %= Align;
return (Value - Skew) / Align * Align + Skew;
}
/// Returns the offset to the next integer (mod 2**64) that is greater than
/// or equal to \p Value and is a multiple of \p Align. \p Align must be
/// non-zero.
inline uint64_t OffsetToAlignment(uint64_t Value, uint64_t Align) { return alignTo(Value, Align) - Value; }
/// Sign-extend the number in the bottom B bits of X to a 32-bit integer.
/// Requires 0 < B <= 32.
template <unsigned B>
constexpr inline int32_t SignExtend32(uint32_t X) {
static_assert(B > 0, "Bit width can't be 0.");
static_assert(B <= 32, "Bit width out of range.");
return int32_t(X << (32 - B)) >> (32 - B);
}
/// Sign-extend the number in the bottom B bits of X to a 32-bit integer.
/// Requires 0 < B < 32.
inline int32_t SignExtend32(uint32_t X, unsigned B) {
assert(B > 0 && "Bit width can't be 0.");
assert(B <= 32 && "Bit width out of range.");
return int32_t(X << (32 - B)) >> (32 - B);
}
/// Sign-extend the number in the bottom B bits of X to a 64-bit integer.
/// Requires 0 < B < 64.
template <unsigned B>
constexpr inline int64_t SignExtend64(uint64_t x) {
static_assert(B > 0, "Bit width can't be 0.");
static_assert(B <= 64, "Bit width out of range.");
return int64_t(x << (64 - B)) >> (64 - B);
}
/// Sign-extend the number in the bottom B bits of X to a 64-bit integer.
/// Requires 0 < B < 64.
inline int64_t SignExtend64(uint64_t X, unsigned B) {
assert(B > 0 && "Bit width can't be 0.");
assert(B <= 64 && "Bit width out of range.");
return int64_t(X << (64 - B)) >> (64 - B);
}
/// Subtract two unsigned integers, X and Y, of type T and return the absolute
/// value of the result.
template <typename T>
typename std::enable_if<std::is_unsigned<T>::value, T>::type AbsoluteDifference(T X, T Y) {
return std::max(X, Y) - std::min(X, Y);
}
/// Add two unsigned integers, X and Y, of type T. Clamp the result to the
/// maximum representable value of T on overflow. ResultOverflowed indicates if
/// the result is larger than the maximum representable value of type T.
template <typename T>
typename std::enable_if<std::is_unsigned<T>::value, T>::type SaturatingAdd(T X, T Y, bool* ResultOverflowed = nullptr) {
bool Dummy;
bool& Overflowed = ResultOverflowed ? *ResultOverflowed : Dummy;
// Hacker's Delight, p. 29
T Z = X + Y;
Overflowed = (Z < X || Z < Y);
if (Overflowed)
return std::numeric_limits<T>::max();
else
return Z;
}
/// Multiply two unsigned integers, X and Y, of type T. Clamp the result to the
/// maximum representable value of T on overflow. ResultOverflowed indicates if
/// the result is larger than the maximum representable value of type T.
template <typename T>
typename std::enable_if<std::is_unsigned<T>::value, T>::type SaturatingMultiply(T X, T Y,
bool* ResultOverflowed = nullptr) {
bool Dummy;
bool& Overflowed = ResultOverflowed ? *ResultOverflowed : Dummy;
// Hacker's Delight, p. 30 has a different algorithm, but we don't use that
// because it fails for uint16_t (where multiplication can have undefined
// behavior due to promotion to int), and requires a division in addition
// to the multiplication.
Overflowed = false;
// Log2(Z) would be either Log2Z or Log2Z + 1.
// Special case: if X or Y is 0, Log2_64 gives -1, and Log2Z
// will necessarily be less than Log2Max as desired.
int Log2Z = Log2_64(X) + Log2_64(Y);
const T Max = std::numeric_limits<T>::max();
int Log2Max = Log2_64(Max);
if (Log2Z < Log2Max) {
return X * Y;
}
if (Log2Z > Log2Max) {
Overflowed = true;
return Max;
}
// We're going to use the top bit, and maybe overflow one
// bit past it. Multiply all but the bottom bit then add
// that on at the end.
T Z = (X >> 1) * Y;
if (Z & ~(Max >> 1)) {
Overflowed = true;
return Max;
}
Z <<= 1;
if (X & 1)
return SaturatingAdd(Z, Y, ResultOverflowed);
return Z;
}
/// Multiply two unsigned integers, X and Y, and add the unsigned integer, A to
/// the product. Clamp the result to the maximum representable value of T on
/// overflow. ResultOverflowed indicates if the result is larger than the
/// maximum representable value of type T.
template <typename T>
typename std::enable_if<std::is_unsigned<T>::value, T>::type SaturatingMultiplyAdd(T X, T Y, T A,
bool* ResultOverflowed = nullptr) {
bool Dummy;
bool& Overflowed = ResultOverflowed ? *ResultOverflowed : Dummy;
T Product = SaturatingMultiply(X, Y, &Overflowed);
if (Overflowed)
return Product;
return SaturatingAdd(A, Product, &Overflowed);
}
/// Use this rather than HUGE_VALF; the latter causes warnings on MSVC.
extern const float huge_valf;
} // namespace llvm
} // namespace hecl

Some files were not shown because too many files have changed in this diff Show More