Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • gamedev/fggl
  • onuralpsezer/fggl
2 results
Show changes
Showing
with 9522 additions and 1578 deletions
......@@ -23,10 +23,10 @@
namespace fggl::gui {
Button::Button( math::vec2 pos, math::vec2 size) : Widget(pos, size), m_label(pos, size), m_hover(false), m_active(false) {}
Button::Button(math::vec2 pos, math::vec2 size) : Widget(pos, size), m_label(pos, size), m_active(false) {}
void Button::render(gfx::Paint &paint) {
gfx::Path2D path{ topLeft() };
gfx::Path2D path{topLeft()};
draw_button(path, topLeft(), size(), m_hover, m_active);
paint.fill(path);
......@@ -35,41 +35,37 @@ namespace fggl::gui {
void Button::activate() {
m_active = !m_active;
if ( m_active ) {
for( auto& callback : m_callbacks ) {
if (m_active) {
for (auto &callback : m_callbacks) {
callback();
m_active = false;
}
m_active = false;
}
}
void Button::onEnter() {
m_hover = true;
}
void Button::onExit() {
m_hover = false;
}
void Button::addCallback(Callback cb) {
m_callbacks.push_back( cb );
m_callbacks.push_back(cb);
}
void Button::label(const std::string &value) {
m_label.text(value);
}
std::string Button::label() const {
void Button::layout() {
m_label.size(topLeft(), size());
}
auto Button::label() const -> std::string {
return m_label.text();
}
void Label::layout() {
if ( m_font == nullptr ) {
if (m_font == nullptr) {
return;
}
math::vec2 size;
for (const auto& letter : m_value) {
for (const auto &letter : m_value) {
auto metrics = m_font->metrics(letter);
size.x += (metrics.advance << 6);
size.y = std::max(size.y, metrics.size.y);
......
......@@ -16,7 +16,6 @@
// Created by webpigeon on 20/11/2021.
//
#include <fggl/ecs3/ecs.hpp>
#include <fggl/input/input.hpp>
#include <fggl/gfx/camera.hpp>
......@@ -24,156 +23,160 @@
namespace fggl::input {
void process_arcball(fggl::ecs3::World &ecs, const Input &input, fggl::ecs::entity_t cam) {
// see https://asliceofrendering.com/camera/2019/11/30/ArcballCamera/
auto *camTransform = ecs.get<fggl::math::Transform>(cam);
auto *camComp = ecs.get<fggl::gfx::Camera>(cam);
auto &mouse = input.mouse;
void process_arcball(entity::EntityManager &ecs, const Input &input, entity::EntityID cam) {
// see https://asliceofrendering.com/camera/2019/11/30/ArcballCamera/
auto &camTransform = ecs.get<fggl::math::Transform>(cam);
auto &camComp = ecs.get<fggl::gfx::Camera>(cam);
const auto &mouse = input.mouse;
glm::vec4 position(camTransform.origin(), 1.0F);
glm::vec4 pivot(camComp.target, 1.0F);
glm::mat4 view = glm::lookAt(camTransform.origin(), camComp.target, camTransform.up());
glm::vec3 viewDir = -glm::transpose(view)[2];
glm::vec3 rightDir = glm::transpose(view)[0];
float deltaAngleX = (2 * M_PI);
float deltaAngleY = (M_PI);
float xAngle = (-mouse.axisDelta(fggl::input::MouseAxis::X)) * deltaAngleX;
float yAngle = (-mouse.axisDelta(fggl::input::MouseAxis::Y)) * deltaAngleY;
// rotate the camera around the pivot on the first axis
glm::mat4x4 rotationMatrixX(1.0F);
rotationMatrixX = glm::rotate(rotationMatrixX, xAngle, fggl::math::UP);
position = (rotationMatrixX * (position - pivot)) + pivot;
// rotate the camera around the pivot on the second axis
glm::mat4x4 rotationMatrixY(1.0F);
rotationMatrixY = glm::rotate(rotationMatrixY, yAngle, rightDir);
glm::vec3 finalPos = (rotationMatrixY * (position - pivot)) + pivot;
camTransform.origin(finalPos);
}
glm::vec4 position(camTransform->origin(), 1.0f);
glm::vec4 pivot(camComp->target, 1.0f);
glm::mat4 view = glm::lookAt(camTransform->origin(), camComp->target, camTransform->up());
glm::vec3 viewDir = -glm::transpose(view)[2];
glm::vec3 rightDir = glm::transpose(view)[0];
void process_scroll(entity::EntityManager &ecs,
const Input &input,
entity::EntityID cam,
float minZoom,
float maxZoom) {
auto &camTransform = ecs.get<fggl::math::Transform>(cam);
auto &camComp = ecs.get<fggl::gfx::Camera>(cam);
float deltaAngleX = (2 * M_PI);
float deltaAngleY = (M_PI);
float xAngle = (-mouse.axisDelta(fggl::input::MouseAxis::X)) * deltaAngleX;
float yAngle = (-mouse.axisDelta(fggl::input::MouseAxis::Y)) * deltaAngleY;
const glm::vec3 dir = (camTransform.origin() - camComp.target);
const glm::vec3 forward = glm::normalize(dir);
// rotate the camera around the pivot on the first axis
glm::mat4x4 rotationMatrixX(1.0f);
rotationMatrixX = glm::rotate(rotationMatrixX, xAngle, fggl::math::UP);
position = (rotationMatrixX * (position - pivot)) + pivot;
glm::vec3 motion(0.0F);
float delta = input.mouse.axis(fggl::input::MouseAxis::SCROLL_Y);
if ((glm::length(dir) < maxZoom && delta < 0.0F) || (glm::length(dir) > minZoom && delta > 0.0F)) {
motion -= (forward * delta);
camTransform.origin(camTransform.origin() + motion);
}
}
// rotate the camera aroud the pivot on the second axis
glm::mat4x4 rotationMatrixY(1.0f);
rotationMatrixY = glm::rotate(rotationMatrixY, yAngle, rightDir);
glm::vec3 finalPos = (rotationMatrixY * (position - pivot)) + pivot;
void process_freecam(entity::EntityManager &ecs, const Input &input, entity::EntityID cam) {
float rotationValue = 0.0F;
glm::vec3 translation(0.0F);
camTransform->origin(finalPos);
}
const auto &keyboard = input.keyboard;
auto &settings = ecs.get<FreeCamKeys>(cam);
void process_scroll(fggl::ecs3::World &ecs, const Input &input, fggl::ecs::entity_t cam, float minZoom, float maxZoom){
auto* camTransform = ecs.get<fggl::math::Transform>(cam);
auto* camComp = ecs.get<fggl::gfx::Camera>(cam);
// calculate rotation (user input)
if (keyboard.down(settings.rotate_cw)) {
rotationValue = ROT_SPEED;
} else if (keyboard.down(settings.rotate_ccw)) {
rotationValue = -ROT_SPEED;
}
const glm::vec3 dir = ( camTransform->origin() - camComp->target );
const glm::vec3 forward = glm::normalize( dir );
// calculate movement (user input)
if (keyboard.down(settings.forward)) {
translation -= fggl::math::RIGHT;
}
glm::vec3 motion(0.0F);
float delta = input.mouse.axis( fggl::input::MouseAxis::SCROLL_Y );
if ( (glm::length( dir ) < maxZoom && delta < 0.0f) || (glm::length( dir ) > minZoom && delta > 0.0f) ) {
motion -= (forward * delta);
camTransform->origin(camTransform->origin() + motion);
if (keyboard.down(settings.backward)) {
translation += fggl::math::RIGHT;
}
if (keyboard.down(settings.right)) {
translation += fggl::math::FORWARD;
}
if (keyboard.down(settings.left)) {
translation -= fggl::math::FORWARD;
}
// apply rotation/movement
auto camTransform = ecs.get<fggl::math::Transform>(cam);
auto camComp = ecs.get<fggl::gfx::Camera>(cam);
glm::vec4 position(camTransform.origin(), 1.0F);
glm::vec4 pivot(camComp.target, 1.0F);
// apply movement
if (translation != glm::vec3(0.0F)) {
const auto rotation = (position - pivot);
const float angle = atan2f(rotation.x, rotation.z);
const auto rotationMat = glm::rotate(MAT_IDENTITY, angle, fggl::math::UP);
auto deltaMove = (rotationMat * glm::vec4(translation, 1.0F)) * PAN_SPEED;
deltaMove.w = 0.0F;
position += deltaMove;
pivot += deltaMove;
}
// apply rotation
if (rotationValue != 0.0F) {
glm::mat4 rotation = glm::rotate(MAT_IDENTITY, rotationValue, fggl::math::UP);
position = (rotation * (position - pivot)) + pivot;
}
camTransform.origin(position);
camComp.target = pivot;
}
void process_freecam(fggl::ecs3::World &ecs, const Input &input, fggl::ecs::entity_t cam) {
float rotationValue = 0.0f;
glm::vec3 translation(0.0f);
void process_edgescroll(entity::EntityManager &ecs, const Input &input, entity::EntityID cam) {
glm::vec3 translation(0.0F);
const auto &mouse = input.mouse;
// calculate movement (user input)
if (mouse.axis(MouseAxis::Y) < 0.9F) {
translation -= fggl::math::RIGHT;
}
if (mouse.axis(MouseAxis::Y) > -0.9F) {
translation += fggl::math::RIGHT;
}
if (mouse.axis(MouseAxis::X) > -0.9F) {
translation += fggl::math::FORWARD;
}
auto &keyboard = input.keyboard;
auto* settings = ecs.get<FreeCamKeys>(cam);
// calculate rotation (user input)
if (keyboard.down(settings->rotate_cw)) {
rotationValue = ROT_SPEED;
} else if (keyboard.down(settings->rotate_ccw)) {
rotationValue = -ROT_SPEED;
}
// calculate movement (user input)
if (keyboard.down(settings->forward)) {
translation -= fggl::math::RIGHT;
}
if (keyboard.down(settings->backward)) {
translation += fggl::math::RIGHT;
}
if (keyboard.down(settings->right)) {
translation += fggl::math::FORWARD;
}
if (keyboard.down(settings->left)) {
translation -= fggl::math::FORWARD;
}
// apply rotation/movement
auto camTransform = ecs.get<fggl::math::Transform>(cam);
auto camComp = ecs.get<fggl::gfx::Camera>(cam);
glm::vec4 position(camTransform->origin(), 1.0f);
glm::vec4 pivot(camComp->target, 1.0f);
// apply movement
if (translation != glm::vec3(0.0f)) {
const auto rotation = (position - pivot);
const float angle = atan2f(rotation.x, rotation.z);
const auto rotationMat = glm::rotate(MAT_IDENTITY, angle, fggl::math::UP);
auto deltaMove = (rotationMat * glm::vec4(translation, 1.0f)) * PAN_SPEED;
deltaMove.w = 0.0f;
position += deltaMove;
pivot += deltaMove;
}
// apply rotation
if (rotationValue != 0.0f) {
glm::mat4 rotation = glm::rotate(MAT_IDENTITY, rotationValue, fggl::math::UP);
position = (rotation * (position - pivot)) + pivot;
}
camTransform->origin(position);
camComp->target = pivot;
}
void process_edgescroll(fggl::ecs3::World &ecs, const Input &input, fggl::ecs::entity_t cam) {
glm::vec3 translation(0.0f);
auto &mouse = input.mouse;
// calculate movement (user input)
if (mouse.axis(MouseAxis::Y) < 0.9f) {
translation -= fggl::math::RIGHT;
}
if (mouse.axis(MouseAxis::Y) > -0.9f) {
translation += fggl::math::RIGHT;
}
if (mouse.axis(MouseAxis::X) > -0.9f) {
translation += fggl::math::FORWARD;
}
if (mouse.axis(MouseAxis::X) < 0.9f) {
translation -= fggl::math::FORWARD;
}
// apply rotation/movement
auto camTransform = ecs.get<fggl::math::Transform>(cam);
auto camComp = ecs.get<fggl::gfx::Camera>(cam);
glm::vec4 position(camTransform->origin(), 1.0f);
glm::vec4 pivot(camComp->target, 1.0f);
// apply movement
if (translation != glm::vec3(0.0f)) {
const auto rotation = (position - pivot);
const float angle = atan2f(rotation.x, rotation.z);
const auto rotationMat = glm::rotate(MAT_IDENTITY, angle, fggl::math::UP);
auto deltaMove = (rotationMat * glm::vec4(translation, 1.0f)) * PAN_SPEED;
deltaMove.w = 0.0f;
position += deltaMove;
pivot += deltaMove;
}
// move camera
camTransform->origin(position);
camComp->target = pivot;
}
if (mouse.axis(MouseAxis::X) < 0.9F) {
translation -= fggl::math::FORWARD;
}
// apply rotation/movement
auto &camTransform = ecs.get<fggl::math::Transform>(cam);
auto &camComp = ecs.get<fggl::gfx::Camera>(cam);
glm::vec4 position(camTransform.origin(), 1.0F);
glm::vec4 pivot(camComp.target, 1.0F);
// apply movement
if (translation != glm::vec3(0.0F)) {
const auto rotation = (position - pivot);
const float angle = atan2f(rotation.x, rotation.z);
const auto rotationMat = glm::rotate(MAT_IDENTITY, angle, fggl::math::UP);
auto deltaMove = (rotationMat * glm::vec4(translation, 1.0F)) * PAN_SPEED;
deltaMove.w = 0.0F;
position += deltaMove;
pivot += deltaMove;
}
// move camera
camTransform.origin(position);
camComp.target = pivot;
}
}
......@@ -16,12 +16,11 @@
namespace fggl::input {
void MouseState::operator=(const MouseState& rhs) {
for ( int i=0; i<4; i++ ) {
axis[i] = rhs.axis[i];
}
buttons = rhs.buttons;
}
void MouseState::operator=(const MouseState &rhs) {
for (int i = 0; i < 4; i++) {
axis[i] = rhs.axis[i];
}
buttons = rhs.buttons;
}
} // namespace fggl::input
# math
find_package( glm CONFIG REQUIRED )
target_link_libraries( fggl PUBLIC glm::glm )
find_package(glm CONFIG REQUIRED)
target_link_libraries(fggl PUBLIC glm::glm)
target_sources(fggl
PRIVATE
shapes.cpp
triangulation.cpp
)
shapes.cpp
triangulation.cpp
)
......@@ -19,21 +19,17 @@ namespace fggl::math::phs3d {
void AABB::emtpy() {
min = {FLT_MAX, FLT_MAX, FLT_MAX};
max = { - FLT_MAX, - FLT_MAX, -FLT_MAX};
max = {-FLT_MAX, -FLT_MAX, -FLT_MAX};
}
void AABB::add(const math::vec3 &p) {
if ( p.x < min.x ) min.x = p.x;
if ( p.x > max.x ) max.x = p.x;
if ( p.y < min.y ) min.y = p.y;
if ( p.y > min.y ) max.y = p.y;
if ( p.z < min.z ) min.z = p.z;
if ( p.z > max.z ) max.z = p.z;
void AABB::add(const math::vec3 &point) {
min = minElm(min, point);
max = maxElm(max, point);
}
AABB AABB::fromPoints(const std::vector<math::vec3> &points) {
auto AABB::fromPoints(const std::vector<math::vec3> &points) -> AABB {
AABB box;
for (const auto& point : points) {
for (const auto &point : points) {
box.add(point);
}
return box;
......@@ -44,9 +40,9 @@ namespace fggl::math::phs3d {
min = max = m[3]; // should be the translation component of the matrix
// this feels like something that should be vectorizable...
for ( int i = 0; i < 3; i++) {
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
if ( m[i][j] > 0.0f ) {
if (m[i][j] > 0.0F) {
min[j] += m[i][j] * other.min[j];
max[j] += m[i][j] * other.max[j];
} else {
......@@ -57,7 +53,7 @@ namespace fggl::math::phs3d {
}
}
Plane Plane::fromPoints(const math::vec3 p1, const math::vec3 p2, const math::vec3 p3) {
auto Plane::fromPoints(const math::vec3 p1, const math::vec3 p2, const math::vec3 p3) -> Plane {
const auto e3 = p2 - p1;
const auto e1 = p3 - p2;
auto normal = glm::normalize(glm::cross(e3, e1));
......@@ -65,13 +61,13 @@ namespace fggl::math::phs3d {
return {normal, d};
}
static math::vec3 bestFitNormal(const std::vector<math::vec3>& points) {
static auto bestFitNormal(const std::vector<math::vec3> &points) -> math::vec3 {
assert(!points.empty());
math::vec3 result;
math::vec3 p = points.back();
for ( std::size_t i = 0; i < points.size(); ++i ){
for (std::size_t i = 0; i < points.size(); ++i) {
math::vec3 c = points[i];
result.x += (p.z + c.z) * (p.y - c.y);
......@@ -84,13 +80,13 @@ namespace fggl::math::phs3d {
return glm::normalize(result);
};
static float bestFitD(const std::vector<math::vec3>& points, glm::vec3 normal) {
static auto bestFitD(const std::vector<math::vec3> &points, glm::vec3 normal) -> float {
math::vec3 sum;
for (auto& point : points) {
for (const auto &point : points) {
sum += point;
}
sum *= 1.0F/points.size();
return glm::dot( sum, normal );
sum *= 1.0F / points.size();
return glm::dot(sum, normal);
}
const char X = 0;
......@@ -102,11 +98,11 @@ namespace fggl::math::phs3d {
const char b;
};
static bary_axis baryCalcAxis(const math::vec3& normal) {
if ( (fabs(normal.x) >= fabs(normal.y)) && (fabs(normal.x) >= fabs(normal.z))) {
static auto baryCalcAxis(const math::vec3 &normal) -> bary_axis {
if ((fabs(normal.x) >= fabs(normal.y)) && (fabs(normal.x) >= fabs(normal.z))) {
// discard x
return {Y, Z};
} else if ( fabs(normal.y) >= fabs(normal.z) ) {
} else if (fabs(normal.y) >= fabs(normal.z)) {
// discard y
return {Z, X};
} else {
......@@ -115,7 +111,7 @@ namespace fggl::math::phs3d {
}
}
bool Triangle::CartToBarycentric(const math::vec3& cart, Barycentric& outVal) {
auto Triangle::CartToBarycentric(const math::vec3 &cart, Barycentric &outVal) -> bool {
// everything is const because I'm paying the compiler is smarter than me...
const auto d1 = v[1] - v[0];
......@@ -137,20 +133,20 @@ namespace fggl::math::phs3d {
const float v3 = cart[ax.b] - v[0][ax.b];
const float v4 = cart[ax.b] - v[2][ax.b];
const float denom = v1*u2 - v2*u1;
if ( denom == 0.0f) {
const float denom = v1 * u2 - v2 * u1;
if (denom == 0.0F) {
return false;
}
// finally, we can work it out
const float oneOverDenom = 1.0f / denom;
outVal.b[0] = (v4*u2 - v2*u4) * oneOverDenom;
outVal.b[1] = (v1*u3 - v3*u1) * oneOverDenom;
outVal.b[2] = 1.0f - outVal.b[0] - outVal.b[1];
const float oneOverDenom = 1.0F / denom;
outVal.b[0] = (v4 * u2 - v2 * u4) * oneOverDenom;
outVal.b[1] = (v1 * u3 - v3 * u1) * oneOverDenom;
outVal.b[2] = 1.0F - outVal.b[0] - outVal.b[1];
return true;
}
bool Triangle::CartToBarycentric2(const math::vec3& cart, Barycentric& outVal) {
auto Triangle::CartToBarycentric2(const math::vec3 &cart, Barycentric &outVal) -> bool {
const auto e1 = v[2] - v[1];
const auto e2 = v[0] - v[2];
const auto e3 = v[1] - v[0];
......@@ -161,7 +157,7 @@ namespace fggl::math::phs3d {
const auto normal = glm::normalize(glm::cross(e1, e2));
const auto denom = glm::dot(glm::cross(e1, e2), normal);
assert( denom != 0.0f);
assert(denom != 0.0F);
outVal.b[0] = glm::dot(glm::cross(e1, d3), normal) / denom;
outVal.b[1] = glm::dot(glm::cross(e2, d1), normal) / denom;
......
......@@ -13,14 +13,13 @@
*/
#include "fggl/math/triangulation.hpp"
#include <iostream>
namespace fggl::math {
/**
* Fast Triangulation for convex polygons.
*/
void fan_triangulation(const PolygonVertex& polygon, data::Mesh2D &mesh) {
void fan_triangulation(const PolygonVertex &polygon, data::Mesh2D &mesh) {
assert(polygon.size() >= 3);
// add the first two points to the mesh
......@@ -29,7 +28,7 @@ namespace fggl::math {
// deal with the indices
const auto nTris = polygon.size() - 2;
for (auto i = 0u; i < nTris; i++) {
for (auto i = 0U; i < nTris; i++) {
mesh.add_index(firstIdx);
mesh.add_index(prevIdx);
......
target_sources(fggl
PRIVATE
null.cpp
)
\ No newline at end of file
/*
* This file is part of FGGL.
*
* FGGL is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any
* later version.
*
* FGGL is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License along with FGGL.
* If not, see <https://www.gnu.org/licenses/>.
*/
//
// Created by webpigeon on 20/08/22.
//
#include "fggl/phys/null.hpp"
namespace fggl::phys {
auto NullPhysics::factory(modules::ServiceName serviceName, modules::Services &serviceManager) -> bool {
if (serviceName == phys::PhysicsProvider::service) {
serviceManager.bind<phys::PhysicsProvider, NullPhysicsProvider>();
return true;
}
return false;
}
}
\ No newline at end of file
if ( CMAKE_SYSTEM_NAME MATCHES "Linux" )
if (CMAKE_SYSTEM_NAME MATCHES "Linux")
add_subdirectory(linux)
else()
else ()
add_subdirectory(fallback)
endif()
\ No newline at end of file
endif ()
\ No newline at end of file
......@@ -16,22 +16,24 @@
// Created by webpigeon on 26/06/22.
//
#define FGGL_PLATFORM_PATHS fallback
#include <cstdlib>
#include "fggl/platform/fallback/paths.hpp"
namespace fggl::platform {
inline static std::filesystem::path get_user_path(const char* env, const char* fallback) {
const char* path = std::getenv(env);
inline static std::filesystem::path get_user_path(const char *env, const char *fallback) {
const char *path = std::getenv(env);
if (path != nullptr) {
return {path};
}
return std::filesystem::current_path() / fallback;
}
EnginePaths calc_engine_paths(const char* base) {
return EnginePaths {
EnginePaths calc_engine_paths(const char *base) {
return EnginePaths{
get_user_path(ENV_USER_CONFIG, DEFAULT_USER_CONFIG) / base,
get_user_path(ENV_USER_DATA, DEFAULT_USER_DATA) / base,
std::filesystem::temp_directory_path() / base
......@@ -40,13 +42,13 @@ namespace fggl::platform {
std::filesystem::path locate_data(const EnginePaths &paths, const std::filesystem::path &relPath) {
auto userPath = paths.userData / relPath;
if ( std::filesystem::exists(userPath) ) {
if (std::filesystem::exists(userPath)) {
return userPath;
}
// if debug mode, try CWD as well.
auto debugPath = std::filesystem::current_path() / "data" / relPath;
if ( std::filesystem::exists(debugPath) ) {
if (std::filesystem::exists(debugPath)) {
return debugPath;
}
......
#find_package(Fontconfig)
#target_link_libraries(fggl PRIVATE Fontconfig::Fontconfig)
target_sources( fggl
PRIVATE
# fonts.cpp
target_sources(fggl
PRIVATE
# fonts.cpp
paths.cpp
)
\ No newline at end of file
)
\ No newline at end of file
......@@ -21,14 +21,14 @@
namespace fggl::platform::Linux {
void get_font() {
/* FcConfig *config = FcInitLoadConfigAndFonts();
FcPattern* pat = FcPatternCreate();
FcObjectSet* os = FcObjectSetBuild(FC_FAMILY, FC_STYLE, FC_WEIGHT, FC_SLANT, FC_PIXEL_SIZE, FC_SIZE, nullptr);
FcFontSet* fs = FcFontList(config, pat, os);
/* FcConfig *config = FcInitLoadConfigAndFonts();
FcPattern* pat = FcPatternCreate();
FcObjectSet* os = FcObjectSetBuild(FC_FAMILY, FC_STYLE, FC_WEIGHT, FC_SLANT, FC_PIXEL_SIZE, FC_SIZE, nullptr);
FcFontSet* fs = FcFontList(config, pat, os);
if ( fs ) {
FcFontSetDestroy(fs);
}*/
if ( fs ) {
FcFontSetDestroy(fs);
}*/
}
} // namespace fggl::platform::linux
\ No newline at end of file
......@@ -18,53 +18,58 @@
#include <cstdlib>
#define FGGL_PLATFORM_PATHS linux
#include "fggl/platform/linux/paths.hpp"
#include "fggl/debug/logging.hpp"
namespace fggl::platform {
inline static std::filesystem::path get_user_path(const char* env, const char* fallback) {
const char* path = std::getenv(env);
if (path != nullptr) {
return {path};
namespace {
inline auto get_user_path(const char *env, const char *fallback) -> std::filesystem::path {
const char *path = std::getenv(env);
if (path != nullptr) {
return {path};
}
return {fallback};
}
return {fallback};
}
static std::vector<std::filesystem::path> get_path_list(const char* env) {
const char* pathList = std::getenv(env);
std::vector<std::filesystem::path> paths;
if (pathList) {
std::string pathListStr(pathList);
std::string::size_type pos = 0;
while (pos < pathListStr.size()) {
std::string::size_type nextPos = pathListStr.find(':', pos);
if (nextPos == std::string::npos) {
nextPos = pathListStr.size();
auto get_path_list(const char *env, const char *folderName) -> std::vector<std::filesystem::path> {
const char *pathList = std::getenv(env);
std::vector<std::filesystem::path> paths;
if (pathList) {
std::string pathListStr(pathList);
std::string::size_type pos = 0;
while (pos < pathListStr.size()) {
std::string::size_type nextPos = pathListStr.find(':', pos);
if (nextPos == std::string::npos) {
nextPos = pathListStr.size();
}
std::string path = pathListStr.substr(pos, nextPos - pos);
paths.push_back(std::filesystem::path(path) / folderName);
pos = nextPos + 1;
}
std::string path = pathListStr.substr(pos, nextPos - pos);
paths.push_back(std::filesystem::path(path));
pos = nextPos + 1;
}
return paths;
}
return paths;
}
EnginePaths calc_engine_paths(const char* base) {
auto dataDirs = get_path_list(ENV_DATA_DIRS);
if ( dataDirs.empty() ) {
for ( const auto& defaultDir : DEFAULT_DATA_DIRS ) {
dataDirs.push_back(std::filesystem::path(defaultDir) / base );
auto calc_engine_paths(const char *base) -> EnginePaths {
auto dataDirs = get_path_list(ENV_DATA_DIRS, base);
if (dataDirs.empty()) {
for (const auto &defaultDir : DEFAULT_DATA_DIRS) {
dataDirs.push_back(std::filesystem::path(defaultDir) / base);
}
}
auto configDirs = get_path_list(ENV_CONFIG_DIRS);
if ( configDirs.empty() ) {
for ( const auto& defaultDir : DEFAULT_CONFIG_DIRS ) {
configDirs.push_back(std::filesystem::path(defaultDir) / base );
auto configDirs = get_path_list(ENV_CONFIG_DIRS, base);
if (configDirs.empty()) {
for (const auto &defaultDir : DEFAULT_CONFIG_DIRS) {
configDirs.push_back(std::filesystem::path(defaultDir) / base);
}
}
return EnginePaths {
return EnginePaths{
get_user_path(ENV_USER_CONFIG, DEFAULT_USER_CONFIG) / base,
get_user_path(ENV_USER_DATA, DEFAULT_USER_DATA) / base,
get_user_path(ENV_USER_CACHE, DEFAULT_USER_CACHE) / base,
......@@ -73,40 +78,42 @@ namespace fggl::platform {
};
}
std::filesystem::path locate_data(const EnginePaths &paths, const std::filesystem::path &relPath) {
auto locate_data(const EnginePaths &paths, const std::filesystem::path &relPath) -> std::filesystem::path {
auto userPath = paths.userData / relPath;
if ( std::filesystem::exists(userPath) ) {
if (std::filesystem::exists(userPath)) {
return userPath;
}
// check system paths
for ( const auto& path : paths.dataDirs ) {
for (const auto &path : paths.dataDirs) {
auto fullPath = path / relPath;
if ( std::filesystem::exists(fullPath) ) {
debug::trace("Checking data path: {}, exists: {}", fullPath.c_str(), std::filesystem::exists(fullPath));
if (std::filesystem::exists(fullPath)) {
return fullPath;
}
}
// if debug mode, try CWD as well.
auto debugPath = std::filesystem::current_path() / "data" / relPath;
if ( std::filesystem::exists(debugPath) ) {
if (std::filesystem::exists(debugPath)) {
debug::trace("Checking debug path: {}, exists: {}", debugPath.c_str(), std::filesystem::exists(debugPath));
return debugPath;
}
// if the file existed, it shoudl exist in the user space
// if the file existed, it should exist in the user space
return userPath;
}
std::filesystem::path locate_config(const EnginePaths &paths, const std::filesystem::path &relPath) {
auto locate_config(const EnginePaths &paths, const std::filesystem::path &relPath) -> std::filesystem::path {
auto userPath = paths.userConfig / relPath;
if ( std::filesystem::exists(userPath) ) {
if (std::filesystem::exists(userPath)) {
return userPath;
}
// check system paths
for ( const auto& path : paths.configDirs ) {
for (const auto &path : paths.configDirs) {
auto fullPath = path / relPath;
if ( std::filesystem::exists(fullPath) ) {
if (std::filesystem::exists(fullPath)) {
return fullPath;
}
}
......@@ -115,16 +122,16 @@ namespace fggl::platform {
return userPath;
}
std::filesystem::path locate_cache(const EnginePaths &paths, const std::filesystem::path &relPath) {
auto locate_cache(const EnginePaths &paths, const std::filesystem::path &relPath) -> std::filesystem::path {
auto userPath = paths.userCache / relPath;
if ( std::filesystem::exists(userPath) ) {
if (std::filesystem::exists(userPath)) {
return userPath;
}
// check system paths
for ( const auto& path : paths.configDirs ) {
for (const auto &path : paths.configDirs) {
auto fullPath = path / relPath;
if ( std::filesystem::exists(fullPath) ) {
if (std::filesystem::exists(fullPath)) {
return fullPath;
}
}
......
......@@ -17,7 +17,6 @@
//
#include "fggl/scenes/game.hpp"
#include "fggl/util/service.hpp"
#if __has_include("fggl/phys/bullet/bullet.hpp")
#include "fggl/phys/bullet/bullet.hpp"
......@@ -25,6 +24,20 @@
namespace fggl::scenes {
GameBase::GameBase(fggl::App& app) : AppState(app) {
m_input = app.service<input::Input>();
}
void GameBase::update(float /*dt*/) {
// detect the user quitting
if (m_input != nullptr) {
bool escapePressed = m_input->keyboard.pressed(glfwGetKeyScancode(GLFW_KEY_ESCAPE));
if (escapePressed) {
m_owner.change_state(m_previous);
}
}
}
Game::Game(fggl::App &app) : AppState(app) {
m_input = app.service<input::Input>();
}
......@@ -33,11 +46,11 @@ namespace fggl::scenes {
fggl::AppState::activate();
// setup the scene
m_world = std::make_unique<ecs3::World>(*m_owner.registry());
m_world = std::make_unique<entity::EntityManager>();
#ifdef FGGL_MODULE_BULLET
// FIXME this ties bullet to the game state - which shouldn't be the case
m_phys = std::make_unique<fggl::phys::bullet::BulletPhysicsEngine>(m_world.get());
// FIXME this ties bullet to the game state - which shouldn't be the case
m_phys = std::make_unique<fggl::phys::bullet::BulletPhysicsEngine>(m_world.get());
#endif
}
......@@ -46,26 +59,31 @@ namespace fggl::scenes {
m_world.reset();
}
void Game::update() {
assert( m_world && "called game update, but there was no world - was activate called?" );
void Game::update(float /*dt*/) {
assert(m_world && "called game update, but there was no world - was activate called?");
if ( m_input != nullptr ) {
if (m_input != nullptr) {
bool escapePressed = m_input->keyboard.pressed(glfwGetKeyScancode(GLFW_KEY_ESCAPE));
if (escapePressed) {
m_owner.change_state(m_previous);
}
if ( m_input->keyboard.pressed(glfwGetKeyScancode(GLFW_KEY_F2)) ) {
m_debug = !m_debug;
}
}
if ( m_phys != nullptr ) {
if (m_phys != nullptr) {
m_phys->step();
}
m_world->reapEntities();
// debug render toggle
//m_world->reapEntities();
}
void Game::render(fggl::gfx::Graphics &gfx) {
if ( m_world != nullptr ) {
gfx.drawScene( *m_world );
if (m_world != nullptr) {
gfx.drawScene(*m_world, m_debug);
}
}
......
......@@ -13,91 +13,74 @@
*/
#include <fggl/scenes/menu.hpp>
#include <fggl/util/service.hpp>
#include <fggl/gui/gui.hpp>
#include <spdlog/spdlog.h>
namespace fggl::scenes {
using fggl::input::MouseButton;
using fggl::input::MouseAxis;
using fggl::input::MouseButton;
using fggl::input::MouseAxis;
constexpr float SCREEN_WIDTH = 1920.0F;
constexpr float SCREEN_HEIGHT = 1080.0F;
BasicMenu::BasicMenu(fggl::App &app) : AppState(app), m_inputs(app.service<input::Input>()), m_active(), m_cursorPos(), m_hover(nullptr) {
}
BasicMenu::BasicMenu(fggl::App& app) : AppState(app), m_inputs(nullptr), m_active(), m_hover(nullptr) {
m_inputs = app.service<input::Input>();
}
void BasicMenu::update() {
if ( m_inputs != nullptr ) {
m_cursorPos.x = m_inputs->mouse.axis( MouseAxis::X );
m_cursorPos.y = m_inputs->mouse.axis( MouseAxis::Y );
void BasicMenu::update(float /*dt*/) {
if (m_inputs != nullptr) {
m_cursorPos.x = m_inputs->mouse.axis(MouseAxis::X);
m_cursorPos.y = m_inputs->mouse.axis(MouseAxis::Y);
// in canvas space
math::vec2 projected;
projected.x = math::rescale_ndc(m_cursorPos.x, 0, 1920.f);
//projected.y = math::rescale_ndc(m_cursorPos.y, 1080.0f, 0);
projected.y = math::rescale_ndc(m_cursorPos.y, 0, 1080.0f);
auto* hoverWidget = m_canvas.getChildAt(projected);
if ( hoverWidget != m_hover ){
if ( m_hover != nullptr ) {
m_hover->onExit();
}
m_hover = hoverWidget;
if ( m_hover != nullptr ){
m_hover->onEnter();
}
}
projected.x = math::rescale_ndc(m_cursorPos.x, 0, SCREEN_WIDTH);
projected.y = math::rescale_ndc(m_cursorPos.y, 0, SCREEN_HEIGHT);
m_canvas.onMouseOver(projected);
if ( m_inputs->mouse.pressed( MouseButton::LEFT ) ) {
spdlog::info("clicky clicky: ({}, {})", projected.x, projected.y);
auto widget = m_canvas.getChildAt(projected);
if (m_inputs->mouse.pressed(MouseButton::LEFT)) {
auto* widget = m_canvas.getChildAt(projected);
if (widget != nullptr) {
widget->activate();
spdlog::info("ooo! there is a thing there!");
}
}
}
}
}
}
}
void BasicMenu::render(gfx::Graphics& gfx) {
void BasicMenu::render(gfx::Graphics &gfx) {
// render the 2D scene (we don't have a 3D scene to worry about in menus)
gfx::Paint paint;
m_canvas.render( paint );
m_canvas.render(paint);
gfx.draw2D(paint);
}
}
void BasicMenu::activate() {
void BasicMenu::activate() {
}
}
void BasicMenu::deactivate() {
void BasicMenu::deactivate() {
}
}
void BasicMenu::add(const std::string& name, callback cb) {
m_items[name] = cb;
void BasicMenu::add(const std::string &name, const Callback& callback) {
m_items[name] = callback;
const math::vec2 btnSize{ 150.0f, 30.0f };
const math::vec2 btnSize{150.0F, 30.0F};
const float spacing = 5;
const float padX = 50.0f;
const float padY = 50.0f;
const float padX = 50.0F;
const float padY = 50.0F;
// figure out the position based off the old logic
// FIXME should be the container's job
math::vec2 pos { 1920.0f - ( padX + btnSize.x ), padY };
auto btnIdx = m_items.size() - 1;
pos.y += (btnIdx * (btnSize.y + spacing));
math::vec2 pos{SCREEN_WIDTH - (padX + btnSize.x), padY};
pos.y += ( (m_items.size()-1.0F) * (btnSize.y + spacing));
// build the button
auto btn = std::make_unique<gui::Button>(pos, btnSize);
btn->label(name);
btn->addCallback(cb);
btn->addCallback(callback);
m_canvas.add(std::move(btn));
}
}
};
......@@ -146,7 +146,7 @@ extern unsigned int stbcc_get_unique_id(stbcc_grid *g, int x, int y);
#include <string.h> // memset
#if !defined(STBCC_GRID_COUNT_X_LOG2) || !defined(STBCC_GRID_COUNT_Y_LOG2)
#error "You must define STBCC_GRID_COUNT_X_LOG2 and STBCC_GRID_COUNT_Y_LOG2 to define the max grid supported."
#error "You must define STBCC_GRID_COUNT_X_LOG2 and STBCC_GRID_COUNT_Y_LOG2 to define the max grid supported."
#endif
#define STBCC__GRID_COUNT_X (1 << STBCC_GRID_COUNT_X_LOG2)
......@@ -155,19 +155,19 @@ extern unsigned int stbcc_get_unique_id(stbcc_grid *g, int x, int y);
#define STBCC__MAP_STRIDE (1 << (STBCC_GRID_COUNT_X_LOG2-3))
#ifndef STBCC_CLUSTER_SIZE_X_LOG2
#define STBCC_CLUSTER_SIZE_X_LOG2 (STBCC_GRID_COUNT_X_LOG2/2) // log2(sqrt(2^N)) = 1/2 * log2(2^N)) = 1/2 * N
#if STBCC_CLUSTER_SIZE_X_LOG2 > 6
#undef STBCC_CLUSTER_SIZE_X_LOG2
#define STBCC_CLUSTER_SIZE_X_LOG2 6
#endif
#define STBCC_CLUSTER_SIZE_X_LOG2 (STBCC_GRID_COUNT_X_LOG2/2) // log2(sqrt(2^N)) = 1/2 * log2(2^N)) = 1/2 * N
#if STBCC_CLUSTER_SIZE_X_LOG2 > 6
#undef STBCC_CLUSTER_SIZE_X_LOG2
#define STBCC_CLUSTER_SIZE_X_LOG2 6
#endif
#endif
#ifndef STBCC_CLUSTER_SIZE_Y_LOG2
#define STBCC_CLUSTER_SIZE_Y_LOG2 (STBCC_GRID_COUNT_Y_LOG2/2)
#if STBCC_CLUSTER_SIZE_Y_LOG2 > 6
#undef STBCC_CLUSTER_SIZE_Y_LOG2
#define STBCC_CLUSTER_SIZE_Y_LOG2 6
#endif
#define STBCC_CLUSTER_SIZE_Y_LOG2 (STBCC_GRID_COUNT_Y_LOG2/2)
#if STBCC_CLUSTER_SIZE_Y_LOG2 > 6
#undef STBCC_CLUSTER_SIZE_Y_LOG2
#define STBCC_CLUSTER_SIZE_Y_LOG2 6
#endif
#endif
#define STBCC__CLUSTER_SIZE_X (1 << STBCC_CLUSTER_SIZE_X_LOG2)
......@@ -180,7 +180,7 @@ extern unsigned int stbcc_get_unique_id(stbcc_grid *g, int x, int y);
#define STBCC__CLUSTER_COUNT_Y (1 << STBCC__CLUSTER_COUNT_Y_LOG2)
#if STBCC__CLUSTER_SIZE_X >= STBCC__GRID_COUNT_X || STBCC__CLUSTER_SIZE_Y >= STBCC__GRID_COUNT_Y
#error "STBCC_CLUSTER_SIZE_X/Y_LOG2 must be smaller than STBCC_GRID_COUNT_X/Y_LOG2"
#error "STBCC_CLUSTER_SIZE_X/Y_LOG2 must be smaller than STBCC_GRID_COUNT_X/Y_LOG2"
#endif
// worst case # of clumps per cluster
......@@ -222,16 +222,16 @@ typedef unsigned char stbcc__verify_max_exits[STBCC__MAX_EXITS_PER_CLUMP <= 256]
typedef struct
{
unsigned short clump_index:12;
signed short cluster_dx:2;
signed short cluster_dy:2;
signed short cluster_dx:2;
signed short cluster_dy:2;
} stbcc__relative_clumpid;
typedef union
{
struct {
unsigned int clump_index:12;
unsigned int cluster_x:10;
unsigned int cluster_y:10;
unsigned int clump_index:12;
unsigned int cluster_x:10;
unsigned int cluster_y:10;
} f;
unsigned int c;
} stbcc__global_clumpid;
......@@ -280,11 +280,11 @@ int stbcc_query_grid_node_connection(stbcc_grid *g, int x1, int y1, int x2, int
int cy2 = STBCC__CLUSTER_Y_FOR_COORD_Y(y2);
assert(!g->in_batched_update);
if (c1 == STBCC__NULL_CLUMPID || c2 == STBCC__NULL_CLUMPID)
return 0;
return 0;
label1 = g->cluster[cy1][cx1].clump[c1].global_label;
label2 = g->cluster[cy2][cx2].clump[c2].global_label;
if (label1.c == label2.c)
return 1;
return 1;
return 0;
}
......@@ -324,7 +324,7 @@ static stbcc__global_clumpid stbcc__clump_find(stbcc_grid *g, stbcc__global_clum
stbcc__clump *c = &g->cluster[n.f.cluster_y][n.f.cluster_x].clump[n.f.clump_index];
if (c->global_label.c == n.c)
return n;
return n;
q = stbcc__clump_find(g, c->global_label);
c->global_label = q;
......@@ -346,7 +346,7 @@ static void stbcc__clump_union(stbcc_grid *g, stbcc__unpacked_clumpid m, int x,
stbcc__global_clumpid np = stbcc__clump_find(g, nc->global_label);
if (mp.c == np.c)
return;
return;
g->cluster[mp.f.cluster_y][mp.f.cluster_x].clump[mp.f.clump_index].global_label = np;
}
......@@ -356,51 +356,51 @@ static void stbcc__build_connected_components_for_clumps(stbcc_grid *g)
int i,j,k,h;
for (j=0; j < STBCC__CLUSTER_COUNT_Y; ++j) {
for (i=0; i < STBCC__CLUSTER_COUNT_X; ++i) {
stbcc__cluster *cluster = &g->cluster[j][i];
for (k=0; k < (int) cluster->num_edge_clumps; ++k) {
stbcc__global_clumpid m;
m.f.clump_index = k;
m.f.cluster_x = i;
m.f.cluster_y = j;
assert((int) m.f.clump_index == k && (int) m.f.cluster_x == i && (int) m.f.cluster_y == j);
cluster->clump[k].global_label = m;
}
}
for (i=0; i < STBCC__CLUSTER_COUNT_X; ++i) {
stbcc__cluster *cluster = &g->cluster[j][i];
for (k=0; k < (int) cluster->num_edge_clumps; ++k) {
stbcc__global_clumpid m;
m.f.clump_index = k;
m.f.cluster_x = i;
m.f.cluster_y = j;
assert((int) m.f.clump_index == k && (int) m.f.cluster_x == i && (int) m.f.cluster_y == j);
cluster->clump[k].global_label = m;
}
}
}
for (j=0; j < STBCC__CLUSTER_COUNT_Y; ++j) {
for (i=0; i < STBCC__CLUSTER_COUNT_X; ++i) {
stbcc__cluster *cluster = &g->cluster[j][i];
for (k=0; k < (int) cluster->num_edge_clumps; ++k) {
stbcc__clump *clump = &cluster->clump[k];
stbcc__unpacked_clumpid m;
stbcc__relative_clumpid *adj;
m.clump_index = k;
m.cluster_x = i;
m.cluster_y = j;
adj = &cluster->adjacency_storage[clump->adjacent_clump_list_index];
for (h=0; h < clump->num_adjacent; ++h) {
unsigned int clump_index = adj[h].clump_index;
unsigned int x = adj[h].cluster_dx + i;
unsigned int y = adj[h].cluster_dy + j;
stbcc__clump_union(g, m, x, y, clump_index);
}
}
}
for (i=0; i < STBCC__CLUSTER_COUNT_X; ++i) {
stbcc__cluster *cluster = &g->cluster[j][i];
for (k=0; k < (int) cluster->num_edge_clumps; ++k) {
stbcc__clump *clump = &cluster->clump[k];
stbcc__unpacked_clumpid m;
stbcc__relative_clumpid *adj;
m.clump_index = k;
m.cluster_x = i;
m.cluster_y = j;
adj = &cluster->adjacency_storage[clump->adjacent_clump_list_index];
for (h=0; h < clump->num_adjacent; ++h) {
unsigned int clump_index = adj[h].clump_index;
unsigned int x = adj[h].cluster_dx + i;
unsigned int y = adj[h].cluster_dy + j;
stbcc__clump_union(g, m, x, y, clump_index);
}
}
}
}
for (j=0; j < STBCC__CLUSTER_COUNT_Y; ++j) {
for (i=0; i < STBCC__CLUSTER_COUNT_X; ++i) {
stbcc__cluster *cluster = &g->cluster[j][i];
for (k=0; k < (int) cluster->num_edge_clumps; ++k) {
stbcc__global_clumpid m;
m.f.clump_index = k;
m.f.cluster_x = i;
m.f.cluster_y = j;
stbcc__clump_find(g, m);
}
}
for (i=0; i < STBCC__CLUSTER_COUNT_X; ++i) {
stbcc__cluster *cluster = &g->cluster[j][i];
for (k=0; k < (int) cluster->num_edge_clumps; ++k) {
stbcc__global_clumpid m;
m.f.clump_index = k;
m.f.cluster_x = i;
m.f.cluster_y = j;
stbcc__clump_find(g, m);
}
}
}
}
......@@ -422,57 +422,57 @@ static void stbcc__build_all_connections_for_cluster(stbcc_grid *g, int cx, int
total = 0;
for (m=0; m < 4; ++m) {
switch (m) {
case 0:
dx = 1, dy = 0;
step_x = 0, step_y = 1;
i = STBCC__CLUSTER_SIZE_X-1;
j = 0;
n = STBCC__CLUSTER_SIZE_Y;
break;
case 1:
dx = -1, dy = 0;
i = 0;
j = 0;
step_x = 0;
step_y = 1;
n = STBCC__CLUSTER_SIZE_Y;
break;
case 2:
dy = -1, dx = 0;
i = 0;
j = 0;
step_x = 1;
step_y = 0;
n = STBCC__CLUSTER_SIZE_X;
break;
case 3:
dy = 1, dx = 0;
i = 0;
j = STBCC__CLUSTER_SIZE_Y-1;
step_x = 1;
step_y = 0;
n = STBCC__CLUSTER_SIZE_X;
break;
}
if (cx+dx < 0 || cx+dx >= g->cw || cy+dy < 0 || cy+dy >= g->ch)
continue;
memset(connected, 0, sizeof(connected));
for (k=0; k < n; ++k) {
if (STBCC__MAP_OPEN(g, x+i, y+j) && STBCC__MAP_OPEN(g, x+i+dx, y+j+dy)) {
stbcc__clumpid src = g->clump_for_node[y+j][x+i];
stbcc__clumpid dest = g->clump_for_node[y+j+dy][x+i+dx];
if (0 == (connected[src][dest>>3] & (1 << (dest & 7)))) {
connected[src][dest>>3] |= 1 << (dest & 7);
++num_adj[src];
++total;
}
}
i += step_x;
j += step_y;
}
switch (m) {
case 0:
dx = 1, dy = 0;
step_x = 0, step_y = 1;
i = STBCC__CLUSTER_SIZE_X-1;
j = 0;
n = STBCC__CLUSTER_SIZE_Y;
break;
case 1:
dx = -1, dy = 0;
i = 0;
j = 0;
step_x = 0;
step_y = 1;
n = STBCC__CLUSTER_SIZE_Y;
break;
case 2:
dy = -1, dx = 0;
i = 0;
j = 0;
step_x = 1;
step_y = 0;
n = STBCC__CLUSTER_SIZE_X;
break;
case 3:
dy = 1, dx = 0;
i = 0;
j = STBCC__CLUSTER_SIZE_Y-1;
step_x = 1;
step_y = 0;
n = STBCC__CLUSTER_SIZE_X;
break;
}
if (cx+dx < 0 || cx+dx >= g->cw || cy+dy < 0 || cy+dy >= g->ch)
continue;
memset(connected, 0, sizeof(connected));
for (k=0; k < n; ++k) {
if (STBCC__MAP_OPEN(g, x+i, y+j) && STBCC__MAP_OPEN(g, x+i+dx, y+j+dy)) {
stbcc__clumpid src = g->clump_for_node[y+j][x+i];
stbcc__clumpid dest = g->clump_for_node[y+j+dy][x+i+dx];
if (0 == (connected[src][dest>>3] & (1 << (dest & 7)))) {
connected[src][dest>>3] |= 1 << (dest & 7);
++num_adj[src];
++total;
}
}
i += step_x;
j += step_y;
}
}
assert(total <= STBCC__CLUSTER_ADJACENCY_COUNT);
......@@ -487,24 +487,24 @@ static void stbcc__build_all_connections_for_cluster(stbcc_grid *g, int cx, int
// ignoring edge-vs-non-edge with 'num_adj[i]*2' was faster than
// 'num_adj[i]+extra' with the divide
if (total + (cluster->num_edge_clumps<<2) <= STBCC__CLUSTER_ADJACENCY_COUNT)
extra = 4;
extra = 4;
else if (total + (cluster->num_edge_clumps<<1) <= STBCC__CLUSTER_ADJACENCY_COUNT)
extra = 2;
extra = 2;
else if (total + (cluster->num_edge_clumps<<0) <= STBCC__CLUSTER_ADJACENCY_COUNT)
extra = 1;
extra = 1;
else
extra = 0;
extra = 0;
total = 0;
for (i=0; i < (int) cluster->num_edge_clumps; ++i) {
int alloc = num_adj[i]+extra;
if (alloc > STBCC__MAX_EXITS_PER_CLUSTER)
alloc = STBCC__MAX_EXITS_PER_CLUSTER;
assert(total < 256); // must fit in byte
cluster->clump[i].adjacent_clump_list_index = (unsigned char) total;
cluster->clump[i].max_adjacent = alloc;
cluster->clump[i].num_adjacent = 0;
total += alloc;
int alloc = num_adj[i]+extra;
if (alloc > STBCC__MAX_EXITS_PER_CLUSTER)
alloc = STBCC__MAX_EXITS_PER_CLUSTER;
assert(total < 256); // must fit in byte
cluster->clump[i].adjacent_clump_list_index = (unsigned char) total;
cluster->clump[i].max_adjacent = alloc;
cluster->clump[i].num_adjacent = 0;
total += alloc;
}
assert(total <= STBCC__CLUSTER_ADJACENCY_COUNT);
......@@ -519,9 +519,9 @@ static void stbcc__build_all_connections_for_cluster(stbcc_grid *g, int cx, int
static void stbcc__add_connections_to_adjacent_cluster_with_rebuild(stbcc_grid *g, int cx, int cy, int dx, int dy)
{
if (cx >= 0 && cx < g->cw && cy >= 0 && cy < g->ch) {
stbcc__add_connections_to_adjacent_cluster(g, cx, cy, dx, dy);
if (g->cluster[cy][cx].rebuild_adjacency)
stbcc__build_all_connections_for_cluster(g, cx, cy);
stbcc__add_connections_to_adjacent_cluster(g, cx, cy, dx, dy);
if (g->cluster[cy][cx].rebuild_adjacency)
stbcc__build_all_connections_for_cluster(g, cx, cy);
}
}
......@@ -530,11 +530,11 @@ void stbcc_update_grid(stbcc_grid *g, int x, int y, int solid)
int cx,cy;
if (!solid) {
if (STBCC__MAP_OPEN(g,x,y))
return;
if (STBCC__MAP_OPEN(g,x,y))
return;
} else {
if (!STBCC__MAP_OPEN(g,x,y))
return;
if (!STBCC__MAP_OPEN(g,x,y))
return;
}
cx = STBCC__CLUSTER_X_FOR_COORD_X(x);
......@@ -546,9 +546,9 @@ void stbcc_update_grid(stbcc_grid *g, int x, int y, int solid)
stbcc__remove_connections_to_adjacent_cluster(g, cx, cy+1, 0,-1);
if (!solid)
STBCC__MAP_BYTE(g,x,y) |= STBCC__MAP_BYTE_MASK(x,y);
STBCC__MAP_BYTE(g,x,y) |= STBCC__MAP_BYTE_MASK(x,y);
else
STBCC__MAP_BYTE(g,x,y) &= ~STBCC__MAP_BYTE_MASK(x,y);
STBCC__MAP_BYTE(g,x,y) &= ~STBCC__MAP_BYTE_MASK(x,y);
stbcc__build_clumps_for_cluster(g, cx, cy);
stbcc__build_all_connections_for_cluster(g, cx, cy);
......@@ -559,11 +559,11 @@ void stbcc_update_grid(stbcc_grid *g, int x, int y, int solid)
stbcc__add_connections_to_adjacent_cluster_with_rebuild(g, cx, cy+1, 0,-1);
if (!g->in_batched_update)
stbcc__build_connected_components_for_clumps(g);
#if 0
stbcc__build_connected_components_for_clumps(g);
#if 0
else
g->cluster_dirty[cy][cx] = 1;
#endif
g->cluster_dirty[cy][cx] = 1;
#endif
}
void stbcc_update_batch_begin(stbcc_grid *g)
......@@ -597,35 +597,35 @@ void stbcc_init_grid(stbcc_grid *g, unsigned char *map, int w, int h)
g->ch = h >> STBCC_CLUSTER_SIZE_Y_LOG2;
g->in_batched_update = 0;
#if 0
#if 0
for (j=0; j < STBCC__CLUSTER_COUNT_Y; ++j)
for (i=0; i < STBCC__CLUSTER_COUNT_X; ++i)
g->cluster_dirty[j][i] = 0;
#endif
for (i=0; i < STBCC__CLUSTER_COUNT_X; ++i)
g->cluster_dirty[j][i] = 0;
#endif
for (j=0; j < h; ++j) {
for (i=0; i < w; i += 8) {
unsigned char c = 0;
for (k=0; k < 8; ++k)
if (map[j*w + (i+k)] == 0)
c |= (1 << k);
g->map[j][i>>3] = c;
}
for (i=0; i < w; i += 8) {
unsigned char c = 0;
for (k=0; k < 8; ++k)
if (map[j*w + (i+k)] == 0)
c |= (1 << k);
g->map[j][i>>3] = c;
}
}
for (j=0; j < g->ch; ++j)
for (i=0; i < g->cw; ++i)
stbcc__build_clumps_for_cluster(g, i, j);
for (i=0; i < g->cw; ++i)
stbcc__build_clumps_for_cluster(g, i, j);
for (j=0; j < g->ch; ++j)
for (i=0; i < g->cw; ++i)
stbcc__build_all_connections_for_cluster(g, i, j);
for (i=0; i < g->cw; ++i)
stbcc__build_all_connections_for_cluster(g, i, j);
stbcc__build_connected_components_for_clumps(g);
for (j=0; j < g->h; ++j)
for (i=0; i < g->w; ++i)
assert(g->clump_for_node[j][i] <= STBCC__NULL_CLUMPID);
for (i=0; i < g->w; ++i)
assert(g->clump_for_node[j][i] <= STBCC__NULL_CLUMPID);
}
......@@ -657,12 +657,12 @@ static void stbcc__add_clump_connection(stbcc_grid *g, int x1, int y1, int x2, i
clump = &cluster->clump[c1];
assert(clump->num_adjacent <= clump->max_adjacent);
if (clump->num_adjacent == clump->max_adjacent)
g->cluster[cy1][cx1].rebuild_adjacency = 1;
g->cluster[cy1][cx1].rebuild_adjacency = 1;
else {
stbcc__relative_clumpid *adj = &cluster->adjacency_storage[clump->adjacent_clump_list_index];
assert(clump->num_adjacent < STBCC__MAX_EXITS_PER_CLUMP);
assert(clump->adjacent_clump_list_index + clump->num_adjacent <= STBCC__CLUSTER_ADJACENCY_COUNT);
adj[clump->num_adjacent++] = rc;
stbcc__relative_clumpid *adj = &cluster->adjacency_storage[clump->adjacent_clump_list_index];
assert(clump->num_adjacent < STBCC__MAX_EXITS_PER_CLUMP);
assert(clump->adjacent_clump_list_index + clump->num_adjacent <= STBCC__CLUSTER_ADJACENCY_COUNT);
adj[clump->num_adjacent++] = rc;
}
}
......@@ -697,15 +697,15 @@ static void stbcc__remove_clump_connection(stbcc_grid *g, int x1, int y1, int x2
adj = &cluster->adjacency_storage[clump->adjacent_clump_list_index];
for (i=0; i < clump->num_adjacent; ++i)
if (rc.clump_index == adj[i].clump_index &&
rc.cluster_dx == adj[i].cluster_dx &&
rc.cluster_dy == adj[i].cluster_dy)
break;
if (rc.clump_index == adj[i].clump_index &&
rc.cluster_dx == adj[i].cluster_dx &&
rc.cluster_dy == adj[i].cluster_dy)
break;
if (i < clump->num_adjacent)
adj[i] = adj[--clump->num_adjacent];
adj[i] = adj[--clump->num_adjacent];
else
assert(0);
assert(0);
}
static void stbcc__add_connections_to_adjacent_cluster(stbcc_grid *g, int cx, int cy, int dx, int dy)
......@@ -716,59 +716,59 @@ static void stbcc__add_connections_to_adjacent_cluster(stbcc_grid *g, int cx, in
int step_x, step_y=0, i, j, k, n;
if (cx < 0 || cx >= g->cw || cy < 0 || cy >= g->ch)
return;
return;
if (cx+dx < 0 || cx+dx >= g->cw || cy+dy < 0 || cy+dy >= g->ch)
return;
return;
if (g->cluster[cy][cx].rebuild_adjacency)
return;
return;
assert(abs(dx) + abs(dy) == 1);
if (dx == 1) {
i = STBCC__CLUSTER_SIZE_X-1;
j = 0;
step_x = 0;
step_y = 1;
n = STBCC__CLUSTER_SIZE_Y;
i = STBCC__CLUSTER_SIZE_X-1;
j = 0;
step_x = 0;
step_y = 1;
n = STBCC__CLUSTER_SIZE_Y;
} else if (dx == -1) {
i = 0;
j = 0;
step_x = 0;
step_y = 1;
n = STBCC__CLUSTER_SIZE_Y;
i = 0;
j = 0;
step_x = 0;
step_y = 1;
n = STBCC__CLUSTER_SIZE_Y;
} else if (dy == -1) {
i = 0;
j = 0;
step_x = 1;
step_y = 0;
n = STBCC__CLUSTER_SIZE_X;
i = 0;
j = 0;
step_x = 1;
step_y = 0;
n = STBCC__CLUSTER_SIZE_X;
} else if (dy == 1) {
i = 0;
j = STBCC__CLUSTER_SIZE_Y-1;
step_x = 1;
step_y = 0;
n = STBCC__CLUSTER_SIZE_X;
i = 0;
j = STBCC__CLUSTER_SIZE_Y-1;
step_x = 1;
step_y = 0;
n = STBCC__CLUSTER_SIZE_X;
} else {
assert(0);
return;
assert(0);
return;
}
for (k=0; k < n; ++k) {
if (STBCC__MAP_OPEN(g, x+i, y+j) && STBCC__MAP_OPEN(g, x+i+dx, y+j+dy)) {
stbcc__clumpid src = g->clump_for_node[y+j][x+i];
stbcc__clumpid dest = g->clump_for_node[y+j+dy][x+i+dx];
if (0 == (connected[src][dest>>3] & (1 << (dest & 7)))) {
assert((dest>>3) < sizeof(connected));
connected[src][dest>>3] |= 1 << (dest & 7);
stbcc__add_clump_connection(g, x+i, y+j, x+i+dx, y+j+dy);
if (g->cluster[cy][cx].rebuild_adjacency)
break;
}
}
i += step_x;
j += step_y;
if (STBCC__MAP_OPEN(g, x+i, y+j) && STBCC__MAP_OPEN(g, x+i+dx, y+j+dy)) {
stbcc__clumpid src = g->clump_for_node[y+j][x+i];
stbcc__clumpid dest = g->clump_for_node[y+j+dy][x+i+dx];
if (0 == (connected[src][dest>>3] & (1 << (dest & 7)))) {
assert((dest>>3) < sizeof(connected));
connected[src][dest>>3] |= 1 << (dest & 7);
stbcc__add_clump_connection(g, x+i, y+j, x+i+dx, y+j+dy);
if (g->cluster[cy][cx].rebuild_adjacency)
break;
}
}
i += step_x;
j += step_y;
}
}
......@@ -780,53 +780,53 @@ static void stbcc__remove_connections_to_adjacent_cluster(stbcc_grid *g, int cx,
int step_x, step_y=0, i, j, k, n;
if (cx < 0 || cx >= g->cw || cy < 0 || cy >= g->ch)
return;
return;
if (cx+dx < 0 || cx+dx >= g->cw || cy+dy < 0 || cy+dy >= g->ch)
return;
return;
assert(abs(dx) + abs(dy) == 1);
if (dx == 1) {
i = STBCC__CLUSTER_SIZE_X-1;
j = 0;
step_x = 0;
step_y = 1;
n = STBCC__CLUSTER_SIZE_Y;
i = STBCC__CLUSTER_SIZE_X-1;
j = 0;
step_x = 0;
step_y = 1;
n = STBCC__CLUSTER_SIZE_Y;
} else if (dx == -1) {
i = 0;
j = 0;
step_x = 0;
step_y = 1;
n = STBCC__CLUSTER_SIZE_Y;
i = 0;
j = 0;
step_x = 0;
step_y = 1;
n = STBCC__CLUSTER_SIZE_Y;
} else if (dy == -1) {
i = 0;
j = 0;
step_x = 1;
step_y = 0;
n = STBCC__CLUSTER_SIZE_X;
i = 0;
j = 0;
step_x = 1;
step_y = 0;
n = STBCC__CLUSTER_SIZE_X;
} else if (dy == 1) {
i = 0;
j = STBCC__CLUSTER_SIZE_Y-1;
step_x = 1;
step_y = 0;
n = STBCC__CLUSTER_SIZE_X;
i = 0;
j = STBCC__CLUSTER_SIZE_Y-1;
step_x = 1;
step_y = 0;
n = STBCC__CLUSTER_SIZE_X;
} else {
assert(0);
return;
assert(0);
return;
}
for (k=0; k < n; ++k) {
if (STBCC__MAP_OPEN(g, x+i, y+j) && STBCC__MAP_OPEN(g, x+i+dx, y+j+dy)) {
stbcc__clumpid src = g->clump_for_node[y+j][x+i];
stbcc__clumpid dest = g->clump_for_node[y+j+dy][x+i+dx];
if (0 == (disconnected[src][dest>>3] & (1 << (dest & 7)))) {
disconnected[src][dest>>3] |= 1 << (dest & 7);
stbcc__remove_clump_connection(g, x+i, y+j, x+i+dx, y+j+dy);
}
}
i += step_x;
j += step_y;
if (STBCC__MAP_OPEN(g, x+i, y+j) && STBCC__MAP_OPEN(g, x+i+dx, y+j+dy)) {
stbcc__clumpid src = g->clump_for_node[y+j][x+i];
stbcc__clumpid dest = g->clump_for_node[y+j+dy][x+i+dx];
if (0 == (disconnected[src][dest>>3] & (1 << (dest & 7)))) {
disconnected[src][dest>>3] |= 1 << (dest & 7);
stbcc__remove_clump_connection(g, x+i, y+j, x+i+dx, y+j+dy);
}
}
i += step_x;
j += step_y;
}
}
......@@ -835,7 +835,7 @@ static stbcc__tinypoint stbcc__incluster_find(stbcc__cluster_build_info *cbi, in
stbcc__tinypoint p,q;
p = cbi->parent[y][x];
if (p.x == x && p.y == y)
return p;
return p;
q = stbcc__incluster_find(cbi, p.x, p.y);
cbi->parent[y][x] = q;
return q;
......@@ -847,7 +847,7 @@ static void stbcc__incluster_union(stbcc__cluster_build_info *cbi, int x1, int y
stbcc__tinypoint q = stbcc__incluster_find(cbi, x2,y2);
if (p.x == q.x && p.y == q.y)
return;
return;
cbi->parent[p.y][p.x] = q;
}
......@@ -871,23 +871,23 @@ static void stbcc__build_clumps_for_cluster(stbcc_grid *g, int cx, int cy)
// set initial disjoint set forest state
for (j=0; j < STBCC__CLUSTER_SIZE_Y; ++j) {
for (i=0; i < STBCC__CLUSTER_SIZE_X; ++i) {
cbi.parent[j][i].x = i;
cbi.parent[j][i].y = j;
}
for (i=0; i < STBCC__CLUSTER_SIZE_X; ++i) {
cbi.parent[j][i].x = i;
cbi.parent[j][i].y = j;
}
}
// join all sets that are connected
for (j=0; j < STBCC__CLUSTER_SIZE_Y; ++j) {
// check down only if not on bottom row
if (j < STBCC__CLUSTER_SIZE_Y-1)
for (i=0; i < STBCC__CLUSTER_SIZE_X; ++i)
if (STBCC__MAP_OPEN(g,x+i,y+j) && STBCC__MAP_OPEN(g,x+i ,y+j+1))
stbcc__incluster_union(&cbi, i,j, i,j+1);
// check right for everything but rightmost column
for (i=0; i < STBCC__CLUSTER_SIZE_X-1; ++i)
if (STBCC__MAP_OPEN(g,x+i,y+j) && STBCC__MAP_OPEN(g,x+i+1,y+j ))
stbcc__incluster_union(&cbi, i,j, i+1,j);
// check down only if not on bottom row
if (j < STBCC__CLUSTER_SIZE_Y-1)
for (i=0; i < STBCC__CLUSTER_SIZE_X; ++i)
if (STBCC__MAP_OPEN(g,x+i,y+j) && STBCC__MAP_OPEN(g,x+i ,y+j+1))
stbcc__incluster_union(&cbi, i,j, i,j+1);
// check right for everything but rightmost column
for (i=0; i < STBCC__CLUSTER_SIZE_X-1; ++i)
if (STBCC__MAP_OPEN(g,x+i,y+j) && STBCC__MAP_OPEN(g,x+i+1,y+j ))
stbcc__incluster_union(&cbi, i,j, i+1,j);
}
// label all non-empty clumps along edges so that all edge clumps are first
......@@ -897,57 +897,57 @@ static void stbcc__build_clumps_for_cluster(stbcc_grid *g, int cx, int cy)
// first put solid labels on all the edges; these will get overwritten if they're open
for (j=0; j < STBCC__CLUSTER_SIZE_Y; ++j)
cbi.label[j][0] = cbi.label[j][STBCC__CLUSTER_SIZE_X-1] = STBCC__NULL_CLUMPID;
cbi.label[j][0] = cbi.label[j][STBCC__CLUSTER_SIZE_X-1] = STBCC__NULL_CLUMPID;
for (i=0; i < STBCC__CLUSTER_SIZE_X; ++i)
cbi.label[0][i] = cbi.label[STBCC__CLUSTER_SIZE_Y-1][i] = STBCC__NULL_CLUMPID;
cbi.label[0][i] = cbi.label[STBCC__CLUSTER_SIZE_Y-1][i] = STBCC__NULL_CLUMPID;
for (j=0; j < STBCC__CLUSTER_SIZE_Y; ++j) {
i = 0;
if (STBCC__MAP_OPEN(g, x+i, y+j)) {
stbcc__tinypoint p = stbcc__incluster_find(&cbi, i,j);
if (p.x == i && p.y == j)
// if this is the leader, give it a label
cbi.label[j][i] = label++;
else if (!(p.x == 0 || p.x == STBCC__CLUSTER_SIZE_X-1 || p.y == 0 || p.y == STBCC__CLUSTER_SIZE_Y-1)) {
// if leader is in interior, promote this edge node to leader and label
stbcc__switch_root(&cbi, i, j, p);
cbi.label[j][i] = label++;
}
// else if leader is on edge, do nothing (it'll get labelled when we reach it)
}
i = STBCC__CLUSTER_SIZE_X-1;
if (STBCC__MAP_OPEN(g, x+i, y+j)) {
stbcc__tinypoint p = stbcc__incluster_find(&cbi, i,j);
if (p.x == i && p.y == j)
cbi.label[j][i] = label++;
else if (!(p.x == 0 || p.x == STBCC__CLUSTER_SIZE_X-1 || p.y == 0 || p.y == STBCC__CLUSTER_SIZE_Y-1)) {
stbcc__switch_root(&cbi, i, j, p);
cbi.label[j][i] = label++;
}
}
i = 0;
if (STBCC__MAP_OPEN(g, x+i, y+j)) {
stbcc__tinypoint p = stbcc__incluster_find(&cbi, i,j);
if (p.x == i && p.y == j)
// if this is the leader, give it a label
cbi.label[j][i] = label++;
else if (!(p.x == 0 || p.x == STBCC__CLUSTER_SIZE_X-1 || p.y == 0 || p.y == STBCC__CLUSTER_SIZE_Y-1)) {
// if leader is in interior, promote this edge node to leader and label
stbcc__switch_root(&cbi, i, j, p);
cbi.label[j][i] = label++;
}
// else if leader is on edge, do nothing (it'll get labelled when we reach it)
}
i = STBCC__CLUSTER_SIZE_X-1;
if (STBCC__MAP_OPEN(g, x+i, y+j)) {
stbcc__tinypoint p = stbcc__incluster_find(&cbi, i,j);
if (p.x == i && p.y == j)
cbi.label[j][i] = label++;
else if (!(p.x == 0 || p.x == STBCC__CLUSTER_SIZE_X-1 || p.y == 0 || p.y == STBCC__CLUSTER_SIZE_Y-1)) {
stbcc__switch_root(&cbi, i, j, p);
cbi.label[j][i] = label++;
}
}
}
for (i=1; i < STBCC__CLUSTER_SIZE_Y-1; ++i) {
j = 0;
if (STBCC__MAP_OPEN(g, x+i, y+j)) {
stbcc__tinypoint p = stbcc__incluster_find(&cbi, i,j);
if (p.x == i && p.y == j)
cbi.label[j][i] = label++;
else if (!(p.x == 0 || p.x == STBCC__CLUSTER_SIZE_X-1 || p.y == 0 || p.y == STBCC__CLUSTER_SIZE_Y-1)) {
stbcc__switch_root(&cbi, i, j, p);
cbi.label[j][i] = label++;
}
}
j = STBCC__CLUSTER_SIZE_Y-1;
if (STBCC__MAP_OPEN(g, x+i, y+j)) {
stbcc__tinypoint p = stbcc__incluster_find(&cbi, i,j);
if (p.x == i && p.y == j)
cbi.label[j][i] = label++;
else if (!(p.x == 0 || p.x == STBCC__CLUSTER_SIZE_X-1 || p.y == 0 || p.y == STBCC__CLUSTER_SIZE_Y-1)) {
stbcc__switch_root(&cbi, i, j, p);
cbi.label[j][i] = label++;
}
}
j = 0;
if (STBCC__MAP_OPEN(g, x+i, y+j)) {
stbcc__tinypoint p = stbcc__incluster_find(&cbi, i,j);
if (p.x == i && p.y == j)
cbi.label[j][i] = label++;
else if (!(p.x == 0 || p.x == STBCC__CLUSTER_SIZE_X-1 || p.y == 0 || p.y == STBCC__CLUSTER_SIZE_Y-1)) {
stbcc__switch_root(&cbi, i, j, p);
cbi.label[j][i] = label++;
}
}
j = STBCC__CLUSTER_SIZE_Y-1;
if (STBCC__MAP_OPEN(g, x+i, y+j)) {
stbcc__tinypoint p = stbcc__incluster_find(&cbi, i,j);
if (p.x == i && p.y == j)
cbi.label[j][i] = label++;
else if (!(p.x == 0 || p.x == STBCC__CLUSTER_SIZE_X-1 || p.y == 0 || p.y == STBCC__CLUSTER_SIZE_Y-1)) {
stbcc__switch_root(&cbi, i, j, p);
cbi.label[j][i] = label++;
}
}
}
c = &g->cluster[cy][cx];
......@@ -955,51 +955,51 @@ static void stbcc__build_clumps_for_cluster(stbcc_grid *g, int cx, int cy)
// label any internal clusters
for (j=1; j < STBCC__CLUSTER_SIZE_Y-1; ++j) {
for (i=1; i < STBCC__CLUSTER_SIZE_X-1; ++i) {
stbcc__tinypoint p = cbi.parent[j][i];
if (p.x == i && p.y == j) {
if (STBCC__MAP_OPEN(g,x+i,y+j))
cbi.label[j][i] = label++;
else
cbi.label[j][i] = STBCC__NULL_CLUMPID;
}
}
for (i=1; i < STBCC__CLUSTER_SIZE_X-1; ++i) {
stbcc__tinypoint p = cbi.parent[j][i];
if (p.x == i && p.y == j) {
if (STBCC__MAP_OPEN(g,x+i,y+j))
cbi.label[j][i] = label++;
else
cbi.label[j][i] = STBCC__NULL_CLUMPID;
}
}
}
// label all other nodes
for (j=0; j < STBCC__CLUSTER_SIZE_Y; ++j) {
for (i=0; i < STBCC__CLUSTER_SIZE_X; ++i) {
stbcc__tinypoint p = stbcc__incluster_find(&cbi, i,j);
if (p.x != i || p.y != j) {
if (STBCC__MAP_OPEN(g,x+i,y+j))
cbi.label[j][i] = cbi.label[p.y][p.x];
}
if (STBCC__MAP_OPEN(g,x+i,y+j))
assert(cbi.label[j][i] != STBCC__NULL_CLUMPID);
}
for (i=0; i < STBCC__CLUSTER_SIZE_X; ++i) {
stbcc__tinypoint p = stbcc__incluster_find(&cbi, i,j);
if (p.x != i || p.y != j) {
if (STBCC__MAP_OPEN(g,x+i,y+j))
cbi.label[j][i] = cbi.label[p.y][p.x];
}
if (STBCC__MAP_OPEN(g,x+i,y+j))
assert(cbi.label[j][i] != STBCC__NULL_CLUMPID);
}
}
c->num_clumps = label;
for (i=0; i < label; ++i) {
c->clump[i].num_adjacent = 0;
c->clump[i].max_adjacent = 0;
c->clump[i].num_adjacent = 0;
c->clump[i].max_adjacent = 0;
}
for (j=0; j < STBCC__CLUSTER_SIZE_Y; ++j)
for (i=0; i < STBCC__CLUSTER_SIZE_X; ++i) {
g->clump_for_node[y+j][x+i] = cbi.label[j][i]; // @OPTIMIZE: remove cbi.label entirely
assert(g->clump_for_node[y+j][x+i] <= STBCC__NULL_CLUMPID);
}
for (i=0; i < STBCC__CLUSTER_SIZE_X; ++i) {
g->clump_for_node[y+j][x+i] = cbi.label[j][i]; // @OPTIMIZE: remove cbi.label entirely
assert(g->clump_for_node[y+j][x+i] <= STBCC__NULL_CLUMPID);
}
// set the global label for all interior clumps since they can't have connections,
// so we don't have to do this on the global pass (brings from O(N) to O(N^0.75))
for (i=(int) c->num_edge_clumps; i < (int) c->num_clumps; ++i) {
stbcc__global_clumpid gc;
gc.f.cluster_x = cx;
gc.f.cluster_y = cy;
gc.f.clump_index = i;
c->clump[i].global_label = gc;
stbcc__global_clumpid gc;
gc.f.cluster_x = cx;
gc.f.cluster_y = cy;
gc.f.clump_index = i;
c->clump[i].global_label = gc;
}
c->rebuild_adjacency = 1; // flag that it has no valid adjacency data
......
......@@ -93,10 +93,10 @@ extern "C" {
extern int stb_div_trunc(int value_to_be_divided, int value_to_divide_by);
extern int stb_div_floor(int value_to_be_divided, int value_to_divide_by);
extern int stb_div_eucl (int value_to_be_divided, int value_to_divide_by);
extern int stb_div_eucl(int value_to_be_divided, int value_to_divide_by);
extern int stb_mod_trunc(int value_to_be_divided, int value_to_divide_by);
extern int stb_mod_floor(int value_to_be_divided, int value_to_divide_by);
extern int stb_mod_eucl (int value_to_be_divided, int value_to_divide_by);
extern int stb_mod_eucl(int value_to_be_divided, int value_to_divide_by);
#ifdef __cplusplus
}
......@@ -105,9 +105,9 @@ extern int stb_mod_eucl (int value_to_be_divided, int value_to_divide_by);
#ifdef STB_DIVIDE_IMPLEMENTATION
#if defined(__STDC_VERSION) && __STDC_VERSION__ >= 19901
#ifndef C_INTEGER_DIVISION_TRUNCATES
#define C_INTEGER_DIVISION_TRUNCATES
#endif
#ifndef C_INTEGER_DIVISION_TRUNCATES
#define C_INTEGER_DIVISION_TRUNCATES
#endif
#endif
#ifndef INT_MIN
......@@ -117,150 +117,150 @@ extern int stb_mod_eucl (int value_to_be_divided, int value_to_divide_by);
// the following macros are designed to allow testing
// other platforms by simulating them
#ifndef STB_DIVIDE_TEST_FLOOR
#define stb__div(a,b) ((a)/(b))
#define stb__mod(a,b) ((a)%(b))
#define stb__div(a,b) ((a)/(b))
#define stb__mod(a,b) ((a)%(b))
#else
// implement floor-style divide on trunc platform
#ifndef C_INTEGER_DIVISION_TRUNCATES
#error "floor test requires truncating division"
#endif
#undef C_INTEGER_DIVISION_TRUNCATES
#ifndef C_INTEGER_DIVISION_TRUNCATES
#error "floor test requires truncating division"
#endif
#undef C_INTEGER_DIVISION_TRUNCATES
int stb__div(int v1, int v2)
{
int q = v1/v2, r = v1%v2;
if ((r > 0 && v2 < 0) || (r < 0 && v2 > 0))
return q-1;
else
return q;
int q = v1/v2, r = v1%v2;
if ((r > 0 && v2 < 0) || (r < 0 && v2 > 0))
return q-1;
else
return q;
}
int stb__mod(int v1, int v2)
{
int r = v1%v2;
if ((r > 0 && v2 < 0) || (r < 0 && v2 > 0))
return r+v2;
else
return r;
int r = v1%v2;
if ((r > 0 && v2 < 0) || (r < 0 && v2 > 0))
return r+v2;
else
return r;
}
#endif
int stb_div_trunc(int v1, int v2)
{
#ifdef C_INTEGER_DIVISION_TRUNCATES
#ifdef C_INTEGER_DIVISION_TRUNCATES
return v1/v2;
#else
#else
if (v1 >= 0 && v2 <= 0)
return -stb__div(-v1,v2); // both negative to avoid overflow
return -stb__div(-v1,v2); // both negative to avoid overflow
if (v1 <= 0 && v2 >= 0)
if (v1 != INT_MIN)
return -stb__div(v1,-v2); // both negative to avoid overflow
else
return -stb__div(v1+v2,-v2)-1; // push v1 away from wrap point
if (v1 != INT_MIN)
return -stb__div(v1,-v2); // both negative to avoid overflow
else
return -stb__div(v1+v2,-v2)-1; // push v1 away from wrap point
else
return v1/v2; // same sign, so expect truncation
#endif
return v1/v2; // same sign, so expect truncation
#endif
}
int stb_div_floor(int v1, int v2)
{
#ifdef C_INTEGER_DIVISION_FLOORS
#ifdef C_INTEGER_DIVISION_FLOORS
return v1/v2;
#else
#else
if (v1 >= 0 && v2 < 0) {
if (v2 + 1 >= INT_MIN + v1) // check if increasing v1's magnitude overflows
return -stb__div((v2+1)-v1,v2); // nope, so just compute it
else
return -stb__div(-v1,v2) + ((-v1)%v2 ? -1 : 0);
if (v2 + 1 >= INT_MIN + v1) // check if increasing v1's magnitude overflows
return -stb__div((v2+1)-v1,v2); // nope, so just compute it
else
return -stb__div(-v1,v2) + ((-v1)%v2 ? -1 : 0);
}
if (v1 < 0 && v2 >= 0) {
if (v1 != INT_MIN) {
if (v1 + 1 >= INT_MIN + v2) // check if increasing v1's magnitude overflows
return -stb__div((v1+1)-v2,-v2); // nope, so just compute it
else
return -stb__div(-v1,v2) + (stb__mod(v1,-v2) ? -1 : 0);
} else // it must be possible to compute -(v1+v2) without overflowing
return -stb__div(-(v1+v2),v2) + (stb__mod(-(v1+v2),v2) ? -2 : -1);
if (v1 != INT_MIN) {
if (v1 + 1 >= INT_MIN + v2) // check if increasing v1's magnitude overflows
return -stb__div((v1+1)-v2,-v2); // nope, so just compute it
else
return -stb__div(-v1,v2) + (stb__mod(v1,-v2) ? -1 : 0);
} else // it must be possible to compute -(v1+v2) without overflowing
return -stb__div(-(v1+v2),v2) + (stb__mod(-(v1+v2),v2) ? -2 : -1);
} else
return v1/v2; // same sign, so expect truncation
#endif
return v1/v2; // same sign, so expect truncation
#endif
}
int stb_div_eucl(int v1, int v2)
{
int q,r;
#ifdef C_INTEGER_DIVISION_TRUNCATES
#ifdef C_INTEGER_DIVISION_TRUNCATES
q = v1/v2;
r = v1%v2;
#else
#else
// handle every quadrant separately, since we can't rely on q and r flor
if (v1 >= 0)
if (v2 >= 0)
return stb__div(v1,v2);
else if (v2 != INT_MIN)
q = -stb__div(v1,-v2), r = stb__mod(v1,-v2);
else
q = 0, r = v1;
if (v2 >= 0)
return stb__div(v1,v2);
else if (v2 != INT_MIN)
q = -stb__div(v1,-v2), r = stb__mod(v1,-v2);
else
q = 0, r = v1;
else if (v1 != INT_MIN)
if (v2 >= 0)
q = -stb__div(-v1,v2), r = -stb__mod(-v1,v2);
else if (v2 != INT_MIN)
q = stb__div(-v1,-v2), r = -stb__mod(-v1,-v2);
else // if v2 is INT_MIN, then we can't use -v2, but we can't divide by v2
q = 1, r = v1-q*v2;
if (v2 >= 0)
q = -stb__div(-v1,v2), r = -stb__mod(-v1,v2);
else if (v2 != INT_MIN)
q = stb__div(-v1,-v2), r = -stb__mod(-v1,-v2);
else // if v2 is INT_MIN, then we can't use -v2, but we can't divide by v2
q = 1, r = v1-q*v2;
else // if v1 is INT_MIN, we have to move away from overflow place
if (v2 >= 0)
q = -stb__div(-(v1+v2),v2)-1, r = -stb__mod(-(v1+v2),v2);
else if (v2 != INT_MIN)
q = stb__div(-(v1-v2),-v2)+1, r = -stb__mod(-(v1-v2),-v2);
else // for INT_MIN / INT_MIN, we need to be extra-careful to avoid overflow
q = 1, r = 0;
#endif
if (v2 >= 0)
q = -stb__div(-(v1+v2),v2)-1, r = -stb__mod(-(v1+v2),v2);
else if (v2 != INT_MIN)
q = stb__div(-(v1-v2),-v2)+1, r = -stb__mod(-(v1-v2),-v2);
else // for INT_MIN / INT_MIN, we need to be extra-careful to avoid overflow
q = 1, r = 0;
#endif
if (r >= 0)
return q;
return q;
else
return q + (v2 > 0 ? -1 : 1);
return q + (v2 > 0 ? -1 : 1);
}
int stb_mod_trunc(int v1, int v2)
{
#ifdef C_INTEGER_DIVISION_TRUNCATES
#ifdef C_INTEGER_DIVISION_TRUNCATES
return v1%v2;
#else
#else
if (v1 >= 0) { // modulus result should always be positive
int r = stb__mod(v1,v2);
if (r >= 0)
return r;
else
return r - (v2 < 0 ? v2 : -v2);
int r = stb__mod(v1,v2);
if (r >= 0)
return r;
else
return r - (v2 < 0 ? v2 : -v2);
} else { // modulus result should always be negative
int r = stb__mod(v1,v2);
if (r <= 0)
return r;
else
return r + (v2 < 0 ? v2 : -v2);
int r = stb__mod(v1,v2);
if (r <= 0)
return r;
else
return r + (v2 < 0 ? v2 : -v2);
}
#endif
#endif
}
int stb_mod_floor(int v1, int v2)
{
#ifdef C_INTEGER_DIVISION_FLOORS
#ifdef C_INTEGER_DIVISION_FLOORS
return v1%v2;
#else
#else
if (v2 >= 0) { // result should always be positive
int r = stb__mod(v1,v2);
if (r >= 0)
return r;
else
return r + v2;
int r = stb__mod(v1,v2);
if (r >= 0)
return r;
else
return r + v2;
} else { // result should always be negative
int r = stb__mod(v1,v2);
if (r <= 0)
return r;
else
return r + v2;
int r = stb__mod(v1,v2);
if (r <= 0)
return r;
else
return r + v2;
}
#endif
#endif
}
int stb_mod_eucl(int v1, int v2)
......@@ -268,9 +268,9 @@ int stb_mod_eucl(int v1, int v2)
int r = stb__mod(v1,v2);
if (r >= 0)
return r;
return r;
else
return r - (v2 < 0 ? v2 : -v2); // negative abs() [to avoid overflow]
return r - (v2 < 0 ? v2 : -v2); // negative abs() [to avoid overflow]
}
#ifdef STB_DIVIDE_TEST
......@@ -284,28 +284,28 @@ int err=0;
void stbdiv_check(int q, int r, int a, int b, char *type, int dir)
{
if ((dir > 0 && r < 0) || (dir < 0 && r > 0)) {
fprintf(stderr, "FAILED: %s(%d,%d) remainder %d in wrong direction\n", type,a,b,r);
err++;
fprintf(stderr, "FAILED: %s(%d,%d) remainder %d in wrong direction\n", type,a,b,r);
err++;
} else
if (b != INT_MIN) // can't compute abs(), but if b==INT_MIN all remainders are valid
if (r <= -abs(b) || r >= abs(b)) {
fprintf(stderr, "FAILED: %s(%d,%d) remainder %d out of range\n", type,a,b,r);
err++;
}
#ifdef STB_DIVIDE_TEST_64
if (b != INT_MIN) // can't compute abs(), but if b==INT_MIN all remainders are valid
if (r <= -abs(b) || r >= abs(b)) {
fprintf(stderr, "FAILED: %s(%d,%d) remainder %d out of range\n", type,a,b,r);
err++;
}
#ifdef STB_DIVIDE_TEST_64
{
STB_DIVIDE_TEST_64 q64 = q, r64=r, a64=a, b64=b;
if (q64*b64+r64 != a64) {
fprintf(stderr, "FAILED: %s(%d,%d) remainder %d doesn't match quotient %d\n", type,a,b,r,q);
err++;
}
STB_DIVIDE_TEST_64 q64 = q, r64=r, a64=a, b64=b;
if (q64*b64+r64 != a64) {
fprintf(stderr, "FAILED: %s(%d,%d) remainder %d doesn't match quotient %d\n", type,a,b,r,q);
err++;
}
}
#else
#else
if (q*b+r != a) {
fprintf(stderr, "FAILED: %s(%d,%d) remainder %d doesn't match quotient %d\n", type,a,b,r,q);
err++;
fprintf(stderr, "FAILED: %s(%d,%d) remainder %d doesn't match quotient %d\n", type,a,b,r,q);
err++;
}
#endif
#endif
}
void test(int a, int b)
......
......@@ -256,21 +256,19 @@ STB_HEXWAVE_DEF void hexwave_generate_samples(float *output, int num_samples, He
// oscillator_freq: frequency of the oscillator divided by the sample rate
// private:
typedef struct
{
int reflect;
float peak_time;
float zero_wait;
float half_height;
typedef struct {
int reflect;
float peak_time;
float zero_wait;
float half_height;
} HexWaveParameters;
struct HexWave
{
float t, prev_dt;
HexWaveParameters current, pending;
int have_pending;
float buffer[STB_HEXWAVE_MAX_BLEP_LENGTH];
};
struct HexWave {
float t, prev_dt;
HexWaveParameters current, pending;
int have_pending;
float buffer[STB_HEXWAVE_MAX_BLEP_LENGTH];
};
#endif
#ifdef STB_HEXWAVE_IMPLEMENTATION
......@@ -320,14 +318,14 @@ static void hex_add_oversampled_bleplike(float *output, float time_since_transit
int slot = (int) (time_since_transition * hexblep.oversample);
if (slot >= hexblep.oversample)
slot = hexblep.oversample-1; // clamp in case the floats overshoot
slot = hexblep.oversample-1; // clamp in case the floats overshoot
d1 = &data[ slot *bw];
d2 = &data[(slot+1)*bw];
lerpweight = time_since_transition * hexblep.oversample - slot;
for (i=0; i < bw; ++i)
output[i] += scale * (d1[i] + (d2[i]-d1[i])*lerpweight);
output[i] += scale * (d1[i] + (d2[i]-d1[i])*lerpweight);
}
static void hex_blep (float *output, float time_since_transition, float scale)
......@@ -362,52 +360,52 @@ static void hexwave_generate_linesegs(hexvert vert[9], HexWave *hex, float dt)
vert[3].v = hex->current.half_height;
if (hex->current.reflect) {
for (j=4; j <= 7; ++j) {
vert[j].t = 1 - vert[7-j].t;
vert[j].v = - vert[7-j].v;
}
for (j=4; j <= 7; ++j) {
vert[j].t = 1 - vert[7-j].t;
vert[j].v = - vert[7-j].v;
}
} else {
for (j=4; j <= 7; ++j) {
vert[j].t = 0.5f + vert[j-4].t;
vert[j].v = - vert[j-4].v;
}
for (j=4; j <= 7; ++j) {
vert[j].t = 0.5f + vert[j-4].t;
vert[j].v = - vert[j-4].v;
}
}
vert[8].t = 1;
vert[8].v = 0;
for (j=0; j < 8; ++j) {
if (vert[j+1].t <= vert[j].t + min_len) {
// if change takes place over less than a fraction of a sample treat as discontinuity
//
// otherwise the slope computation can blow up to arbitrarily large and we
// try to generate a huge BLAMP and the result is wrong.
//
// why does this happen if the math is right? i believe if done perfectly,
// the two BLAMPs on either side of the slope would cancel out, but our
// BLAMPs have only limited sub-sample precision and limited integration
// accuracy. or maybe it's just the math blowing up w/ floating point precision
// limits as we try to make x * (1/x) cancel out
//
// min_len verified artifact-free even near nyquist with only oversample=4
vert[j+1].t = vert[j].t;
}
if (vert[j+1].t <= vert[j].t + min_len) {
// if change takes place over less than a fraction of a sample treat as discontinuity
//
// otherwise the slope computation can blow up to arbitrarily large and we
// try to generate a huge BLAMP and the result is wrong.
//
// why does this happen if the math is right? i believe if done perfectly,
// the two BLAMPs on either side of the slope would cancel out, but our
// BLAMPs have only limited sub-sample precision and limited integration
// accuracy. or maybe it's just the math blowing up w/ floating point precision
// limits as we try to make x * (1/x) cancel out
//
// min_len verified artifact-free even near nyquist with only oversample=4
vert[j+1].t = vert[j].t;
}
}
if (vert[8].t != 1.0f) {
// if the above fixup moved the endpoint away from 1.0, move it back,
// along with any other vertices that got moved to the same time
float t = vert[8].t;
for (j=5; j <= 8; ++j)
if (vert[j].t == t)
vert[j].t = 1.0f;
// if the above fixup moved the endpoint away from 1.0, move it back,
// along with any other vertices that got moved to the same time
float t = vert[8].t;
for (j=5; j <= 8; ++j)
if (vert[j].t == t)
vert[j].t = 1.0f;
}
// compute the exact slopes from the final fixed-up positions
for (j=0; j < 8; ++j)
if (vert[j+1].t == vert[j].t)
vert[j].s = 0;
else
vert[j].s = (vert[j+1].v - vert[j].v) / (vert[j+1].t - vert[j].t);
if (vert[j+1].t == vert[j].t)
vert[j].s = 0;
else
vert[j].s = (vert[j+1].v - vert[j].v) / (vert[j+1].t - vert[j].t);
// wraparound at end
vert[8].t = 1;
......@@ -429,21 +427,21 @@ STB_HEXWAVE_DEF void hexwave_generate_samples(float *output, int num_samples, He
// all sample times are biased by halfw to leave room for BLEP/BLAMP to go back in time
if (num_samples <= 0)
return;
return;
// convert parameters to times and slopes
hexwave_generate_linesegs(vert, hex, dt);
if (hex->prev_dt != dt) {
// if frequency changes, add a fixup at the derivative discontinuity starting at now
float slope;
for (j=1; j < 6; ++j)
if (t < vert[j].t)
break;
slope = vert[j].s;
if (slope != 0)
hex_blamp(output, 0, (dt - hex->prev_dt)*slope);
hex->prev_dt = dt;
// if frequency changes, add a fixup at the derivative discontinuity starting at now
float slope;
for (j=1; j < 6; ++j)
if (t < vert[j].t)
break;
slope = vert[j].s;
if (slope != 0)
hex_blamp(output, 0, (dt - hex->prev_dt)*slope);
hex->prev_dt = dt;
}
// copy the buffered data from last call and clear the rest of the output array
......@@ -451,93 +449,93 @@ STB_HEXWAVE_DEF void hexwave_generate_samples(float *output, int num_samples, He
memset(temp_output, 0, 2*hexblep.width*sizeof(float));
if (num_samples >= hexblep.width) {
memcpy(output, hex->buffer, buffered_length);
memcpy(output, hex->buffer, buffered_length);
} else {
// if the output is shorter than hexblep.width, we do all synthesis to temp_output
memcpy(temp_output, hex->buffer, buffered_length);
// if the output is shorter than hexblep.width, we do all synthesis to temp_output
memcpy(temp_output, hex->buffer, buffered_length);
}
for (pass=0; pass < 2; ++pass) {
int i0,i1;
float *out;
// we want to simulate having one buffer that is num_output + hexblep.width
// samples long, without putting that requirement on the user, and without
// allocating a temp buffer that's as long as the whole thing. so we use two
// overlapping buffers, one the user's buffer and one a fixed-length temp
// buffer.
if (pass == 0) {
if (num_samples < hexblep.width)
continue;
// run as far as we can without overwriting the end of the user's buffer
out = output;
i0 = 0;
i1 = num_samples - hexblep.width;
} else {
// generate the rest into a temp buffer
out = temp_output;
i0 = 0;
if (num_samples >= hexblep.width)
i1 = hexblep.width;
else
i1 = num_samples;
}
// determine current segment
for (j=0; j < 8; ++j)
if (t < vert[j+1].t)
break;
i = i0;
for(;;) {
while (t < vert[j+1].t) {
if (i == i1)
goto done;
out[i+halfw] += vert[j].v + vert[j].s*(t - vert[j].t);
t += dt;
++i;
}
// transition from lineseg starting at j to lineseg starting at j+1
if (vert[j].t == vert[j+1].t)
hex_blep(out+i, recip_dt*(t-vert[j+1].t), (vert[j+1].v - vert[j].v));
hex_blamp(out+i, recip_dt*(t-vert[j+1].t), dt*(vert[j+1].s - vert[j].s));
++j;
if (j == 8) {
// change to different waveform if there's a change pending
j = 0;
t -= 1.0; // t was >= 1.f if j==8
if (hex->have_pending) {
float prev_s0 = vert[j].s;
float prev_v0 = vert[j].v;
hex->current = hex->pending;
hex->have_pending = 0;
hexwave_generate_linesegs(vert, hex, dt);
// the following never occurs with this oscillator, but it makes
// the code work in more general cases
if (vert[j].v != prev_v0)
hex_blep (out+i, recip_dt*t, (vert[j].v - prev_v0));
if (vert[j].s != prev_s0)
hex_blamp(out+i, recip_dt*t, dt*(vert[j].s - prev_s0));
}
}
}
done:
;
int i0,i1;
float *out;
// we want to simulate having one buffer that is num_output + hexblep.width
// samples long, without putting that requirement on the user, and without
// allocating a temp buffer that's as long as the whole thing. so we use two
// overlapping buffers, one the user's buffer and one a fixed-length temp
// buffer.
if (pass == 0) {
if (num_samples < hexblep.width)
continue;
// run as far as we can without overwriting the end of the user's buffer
out = output;
i0 = 0;
i1 = num_samples - hexblep.width;
} else {
// generate the rest into a temp buffer
out = temp_output;
i0 = 0;
if (num_samples >= hexblep.width)
i1 = hexblep.width;
else
i1 = num_samples;
}
// determine current segment
for (j=0; j < 8; ++j)
if (t < vert[j+1].t)
break;
i = i0;
for(;;) {
while (t < vert[j+1].t) {
if (i == i1)
goto done;
out[i+halfw] += vert[j].v + vert[j].s*(t - vert[j].t);
t += dt;
++i;
}
// transition from lineseg starting at j to lineseg starting at j+1
if (vert[j].t == vert[j+1].t)
hex_blep(out+i, recip_dt*(t-vert[j+1].t), (vert[j+1].v - vert[j].v));
hex_blamp(out+i, recip_dt*(t-vert[j+1].t), dt*(vert[j+1].s - vert[j].s));
++j;
if (j == 8) {
// change to different waveform if there's a change pending
j = 0;
t -= 1.0; // t was >= 1.f if j==8
if (hex->have_pending) {
float prev_s0 = vert[j].s;
float prev_v0 = vert[j].v;
hex->current = hex->pending;
hex->have_pending = 0;
hexwave_generate_linesegs(vert, hex, dt);
// the following never occurs with this oscillator, but it makes
// the code work in more general cases
if (vert[j].v != prev_v0)
hex_blep (out+i, recip_dt*t, (vert[j].v - prev_v0));
if (vert[j].s != prev_s0)
hex_blamp(out+i, recip_dt*t, dt*(vert[j].s - prev_s0));
}
}
}
done:
;
}
// at this point, we've written output[] and temp_output[]
if (num_samples >= hexblep.width) {
// the first half of temp[] overlaps the end of output, the second half will be the new start overlap
for (i=0; i < hexblep.width; ++i)
output[num_samples-hexblep.width + i] += temp_output[i];
memcpy(hex->buffer, temp_output+hexblep.width, buffered_length);
// the first half of temp[] overlaps the end of output, the second half will be the new start overlap
for (i=0; i < hexblep.width; ++i)
output[num_samples-hexblep.width + i] += temp_output[i];
memcpy(hex->buffer, temp_output+hexblep.width, buffered_length);
} else {
for (i=0; i < num_samples; ++i)
output[i] = temp_output[i];
memcpy(hex->buffer, temp_output+num_samples, buffered_length);
for (i=0; i < num_samples; ++i)
output[i] = temp_output[i];
memcpy(hex->buffer, temp_output+num_samples, buffered_length);
}
hex->t = t;
......@@ -545,15 +543,15 @@ STB_HEXWAVE_DEF void hexwave_generate_samples(float *output, int num_samples, He
STB_HEXWAVE_DEF void hexwave_shutdown(float *user_buffer)
{
#ifndef STB_HEXWAVE_NO_ALLOCATION
#ifndef STB_HEXWAVE_NO_ALLOCATION
if (user_buffer != 0) {
free(hexblep.blep);
free(hexblep.blamp);
free(hexblep.blep);
free(hexblep.blamp);
}
#endif
#endif
}
// buffer should be NULL or must be 4*(width*(oversample+1)*2 +
// buffer should be NULL or must be 4*(width*(oversample+1)*2 +
STB_HEXWAVE_DEF void hexwave_init(int width, int oversample, float *user_buffer)
{
int halfwidth = width/2;
......@@ -572,57 +570,57 @@ STB_HEXWAVE_DEF void hexwave_init(int width, int oversample, float *user_buffer)
int i,j;
if (width > STB_HEXWAVE_MAX_BLEP_LENGTH)
width = STB_HEXWAVE_MAX_BLEP_LENGTH;
width = STB_HEXWAVE_MAX_BLEP_LENGTH;
if (user_buffer == 0) {
#ifndef STB_HEXWAVE_NO_ALLOCATION
blep_buffer = (float *) malloc(sizeof(float)*blep_buffer_count);
blamp_buffer = (float *) malloc(sizeof(float)*blep_buffer_count);
#endif
#ifndef STB_HEXWAVE_NO_ALLOCATION
blep_buffer = (float *) malloc(sizeof(float)*blep_buffer_count);
blamp_buffer = (float *) malloc(sizeof(float)*blep_buffer_count);
#endif
} else {
blep_buffer = ramp+n;
blamp_buffer = blep_buffer + blep_buffer_count;
blep_buffer = ramp+n;
blamp_buffer = blep_buffer + blep_buffer_count;
}
// compute BLEP and BLAMP by integerating windowed sinc
for (i=0; i < n; ++i) {
for (j=0; j < 16; ++j) {
float sinc_t = 3.141592f* (i-half) / oversample;
float sinc = (i==half) ? 1.0f : (float) sin(sinc_t) / (sinc_t);
float wt = 2.0f*3.1415926f * i / (n-1);
float window = (float) (0.355768 - 0.487396*cos(wt) + 0.144232*cos(2*wt) - 0.012604*cos(3*wt)); // Nuttall
double value = window * sinc;
integrate_impulse += value/16;
integrate_step += integrate_impulse/16;
}
step[i] = (float) integrate_impulse;
ramp[i] = (float) integrate_step;
for (j=0; j < 16; ++j) {
float sinc_t = 3.141592f* (i-half) / oversample;
float sinc = (i==half) ? 1.0f : (float) sin(sinc_t) / (sinc_t);
float wt = 2.0f*3.1415926f * i / (n-1);
float window = (float) (0.355768 - 0.487396*cos(wt) + 0.144232*cos(2*wt) - 0.012604*cos(3*wt)); // Nuttall
double value = window * sinc;
integrate_impulse += value/16;
integrate_step += integrate_impulse/16;
}
step[i] = (float) integrate_impulse;
ramp[i] = (float) integrate_step;
}
// renormalize
for (i=0; i < n; ++i) {
step[i] = step[i] * (float) (1.0 / step[n-1]); // step needs to reach to 1.0
ramp[i] = ramp[i] * (float) (halfwidth / ramp[n-1]); // ramp needs to become a slope of 1.0 after oversampling
step[i] = step[i] * (float) (1.0 / step[n-1]); // step needs to reach to 1.0
ramp[i] = ramp[i] * (float) (halfwidth / ramp[n-1]); // ramp needs to become a slope of 1.0 after oversampling
}
// deinterleave to allow efficient interpolation e.g. w/SIMD
for (j=0; j <= oversample; ++j) {
for (i=0; i < width; ++i) {
blep_buffer [j*width+i] = step[j+i*oversample];
blamp_buffer[j*width+i] = ramp[j+i*oversample];
}
for (i=0; i < width; ++i) {
blep_buffer [j*width+i] = step[j+i*oversample];
blamp_buffer[j*width+i] = ramp[j+i*oversample];
}
}
// subtract out the naive waveform; note we can't do this to the raw data
// above, because we want the discontinuity to be in a different locations
// for j=0 and j=oversample (which exists to provide something to interpolate against)
for (j=0; j <= oversample; ++j) {
// subtract step
for (i=halfwidth; i < width; ++i)
blep_buffer [j*width+i] -= 1.0f;
// subtract ramp
for (i=halfwidth; i < width; ++i)
blamp_buffer[j*width+i] -= (j+i*oversample-half)*(1.0f/oversample);
// subtract step
for (i=halfwidth; i < width; ++i)
blep_buffer [j*width+i] -= 1.0f;
// subtract ramp
for (i=halfwidth; i < width; ++i)
blamp_buffer[j*width+i] -= (j+i*oversample-half)*(1.0f/oversample);
}
hexblep.blep = blep_buffer;
......@@ -630,10 +628,10 @@ STB_HEXWAVE_DEF void hexwave_init(int width, int oversample, float *user_buffer)
hexblep.width = width;
hexblep.oversample = oversample;
#ifndef STB_HEXWAVE_NO_ALLOCATION
#ifndef STB_HEXWAVE_NO_ALLOCATION
if (user_buffer == 0)
free(buffers);
#endif
free(buffers);
#endif
}
#endif // STB_HEXWAVE_IMPLEMENTATION
......
source diff could not be displayed: it is too large. Options to address this: view the blob.
......@@ -173,11 +173,11 @@ STBIWDEF int stbi_write_force_png_filter;
#endif
#ifndef STBI_WRITE_NO_STDIO
STBIWDEF int stbi_write_png(char const *filename, int w, int h, int comp, const void *data, int stride_in_bytes);
STBIWDEF int stbi_write_bmp(char const *filename, int w, int h, int comp, const void *data);
STBIWDEF int stbi_write_tga(char const *filename, int w, int h, int comp, const void *data);
STBIWDEF int stbi_write_png(char const *filename, int w, int h, int comp, const void *data, int stride_in_bytes);
STBIWDEF int stbi_write_bmp(char const *filename, int w, int h, int comp, const void *data);
STBIWDEF int stbi_write_tga(char const *filename, int w, int h, int comp, const void *data);
STBIWDEF int stbi_write_hdr(char const *filename, int w, int h, int comp, const float *data);
STBIWDEF int stbi_write_jpg(char const *filename, int x, int y, int comp, const void *data, int quality);
STBIWDEF int stbi_write_jpg(char const *filename, int x, int y, int comp, const void *data, int quality);
#ifdef STBIW_WINDOWS_UTF8
STBIWDEF int stbiw_convert_wchar_to_utf8(char *buffer, size_t bufferlen, const wchar_t* input);
......@@ -186,11 +186,23 @@ STBIWDEF int stbiw_convert_wchar_to_utf8(char *buffer, size_t bufferlen, const w
typedef void stbi_write_func(void *context, void *data, int size);
STBIWDEF int stbi_write_png_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data, int stride_in_bytes);
STBIWDEF int stbi_write_bmp_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data);
STBIWDEF int stbi_write_tga_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data);
STBIWDEF int stbi_write_png_to_func(stbi_write_func *func,
void *context,
int w,
int h,
int comp,
const void *data,
int stride_in_bytes);
STBIWDEF int stbi_write_bmp_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data);
STBIWDEF int stbi_write_tga_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data);
STBIWDEF int stbi_write_hdr_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const float *data);
STBIWDEF int stbi_write_jpg_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void *data, int quality);
STBIWDEF int stbi_write_jpg_to_func(stbi_write_func *func,
void *context,
int x,
int y,
int comp,
const void *data,
int quality);
STBIWDEF void stbi_flip_vertically_on_write(int flip_boolean);
......@@ -199,12 +211,12 @@ STBIWDEF void stbi_flip_vertically_on_write(int flip_boolean);
#ifdef STB_IMAGE_WRITE_IMPLEMENTATION
#ifdef _WIN32
#ifndef _CRT_SECURE_NO_WARNINGS
#define _CRT_SECURE_NO_WARNINGS
#endif
#ifndef _CRT_NONSTDC_NO_DEPRECATE
#define _CRT_NONSTDC_NO_DEPRECATE
#endif
#ifndef _CRT_SECURE_NO_WARNINGS
#define _CRT_SECURE_NO_WARNINGS
#endif
#ifndef _CRT_NONSTDC_NO_DEPRECATE
#define _CRT_NONSTDC_NO_DEPRECATE
#endif
#endif
#ifndef STBI_WRITE_NO_STDIO
......@@ -308,21 +320,21 @@ static FILE *stbiw__fopen(char const *filename, char const *mode)
wchar_t wMode[64];
wchar_t wFilename[1024];
if (0 == MultiByteToWideChar(65001 /* UTF8 */, 0, filename, -1, wFilename, sizeof(wFilename)/sizeof(*wFilename)))
return 0;
return 0;
if (0 == MultiByteToWideChar(65001 /* UTF8 */, 0, mode, -1, wMode, sizeof(wMode)/sizeof(*wMode)))
return 0;
return 0;
#if defined(_MSC_VER) && _MSC_VER >= 1400
if (0 != _wfopen_s(&f, wFilename, wMode))
f = 0;
f = 0;
#else
f = _wfopen(wFilename, wMode);
#endif
#elif defined(_MSC_VER) && _MSC_VER >= 1400
if (0 != fopen_s(&f, filename, mode))
f=0;
f=0;
#else
f = fopen(filename, mode);
#endif
......@@ -349,29 +361,29 @@ typedef int stb_image_write_test[sizeof(stbiw_uint32)==4 ? 1 : -1];
static void stbiw__writefv(stbi__write_context *s, const char *fmt, va_list v)
{
while (*fmt) {
switch (*fmt++) {
case ' ': break;
case '1': { unsigned char x = STBIW_UCHAR(va_arg(v, int));
s->func(s->context,&x,1);
break; }
case '2': { int x = va_arg(v,int);
unsigned char b[2];
b[0] = STBIW_UCHAR(x);
b[1] = STBIW_UCHAR(x>>8);
s->func(s->context,b,2);
break; }
case '4': { stbiw_uint32 x = va_arg(v,int);
unsigned char b[4];
b[0]=STBIW_UCHAR(x);
b[1]=STBIW_UCHAR(x>>8);
b[2]=STBIW_UCHAR(x>>16);
b[3]=STBIW_UCHAR(x>>24);
s->func(s->context,b,4);
break; }
default:
STBIW_ASSERT(0);
return;
}
switch (*fmt++) {
case ' ': break;
case '1': { unsigned char x = STBIW_UCHAR(va_arg(v, int));
s->func(s->context,&x,1);
break; }
case '2': { int x = va_arg(v,int);
unsigned char b[2];
b[0] = STBIW_UCHAR(x);
b[1] = STBIW_UCHAR(x>>8);
s->func(s->context,b,2);
break; }
case '4': { stbiw_uint32 x = va_arg(v,int);
unsigned char b[4];
b[0]=STBIW_UCHAR(x);
b[1]=STBIW_UCHAR(x>>8);
b[2]=STBIW_UCHAR(x>>16);
b[3]=STBIW_UCHAR(x>>24);
s->func(s->context,b,4);
break; }
default:
STBIW_ASSERT(0);
return;
}
}
}
......@@ -386,8 +398,8 @@ static void stbiw__writef(stbi__write_context *s, const char *fmt, ...)
static void stbiw__write_flush(stbi__write_context *s)
{
if (s->buf_used) {
s->func(s->context, &s->buffer, s->buf_used);
s->buf_used = 0;
s->func(s->context, &s->buffer, s->buf_used);
s->buf_used = 0;
}
}
......@@ -399,7 +411,7 @@ static void stbiw__putc(stbi__write_context *s, unsigned char c)
static void stbiw__write1(stbi__write_context *s, unsigned char a)
{
if ((size_t)s->buf_used + 1 > sizeof(s->buffer))
stbiw__write_flush(s);
stbiw__write_flush(s);
s->buffer[s->buf_used++] = a;
}
......@@ -407,7 +419,7 @@ static void stbiw__write3(stbi__write_context *s, unsigned char a, unsigned char
{
int n;
if ((size_t)s->buf_used + 3 > sizeof(s->buffer))
stbiw__write_flush(s);
stbiw__write_flush(s);
n = s->buf_used;
s->buf_used = n+3;
s->buffer[n+0] = a;
......@@ -421,31 +433,31 @@ static void stbiw__write_pixel(stbi__write_context *s, int rgb_dir, int comp, in
int k;
if (write_alpha < 0)
stbiw__write1(s, d[comp - 1]);
stbiw__write1(s, d[comp - 1]);
switch (comp) {
case 2: // 2 pixels = mono + alpha, alpha is written separately, so same as 1-channel case
case 1:
if (expand_mono)
stbiw__write3(s, d[0], d[0], d[0]); // monochrome bmp
else
stbiw__write1(s, d[0]); // monochrome TGA
break;
case 4:
if (!write_alpha) {
// composite against pink background
for (k = 0; k < 3; ++k)
px[k] = bg[k] + ((d[k] - bg[k]) * d[3]) / 255;
stbiw__write3(s, px[1 - rgb_dir], px[1], px[1 + rgb_dir]);
break;
}
/* FALLTHROUGH */
case 3:
stbiw__write3(s, d[1 - rgb_dir], d[1], d[1 + rgb_dir]);
break;
case 2: // 2 pixels = mono + alpha, alpha is written separately, so same as 1-channel case
case 1:
if (expand_mono)
stbiw__write3(s, d[0], d[0], d[0]); // monochrome bmp
else
stbiw__write1(s, d[0]); // monochrome TGA
break;
case 4:
if (!write_alpha) {
// composite against pink background
for (k = 0; k < 3; ++k)
px[k] = bg[k] + ((d[k] - bg[k]) * d[3]) / 255;
stbiw__write3(s, px[1 - rgb_dir], px[1], px[1 + rgb_dir]);
break;
}
/* FALLTHROUGH */
case 3:
stbiw__write3(s, d[1 - rgb_dir], d[1], d[1 + rgb_dir]);
break;
}
if (write_alpha > 0)
stbiw__write1(s, d[comp - 1]);
stbiw__write1(s, d[comp - 1]);
}
static void stbiw__write_pixels(stbi__write_context *s, int rgb_dir, int vdir, int x, int y, int comp, void *data, int write_alpha, int scanline_pad, int expand_mono)
......@@ -454,58 +466,58 @@ static void stbiw__write_pixels(stbi__write_context *s, int rgb_dir, int vdir, i
int i,j, j_end;
if (y <= 0)
return;
return;
if (stbi__flip_vertically_on_write)
vdir *= -1;
vdir *= -1;
if (vdir < 0) {
j_end = -1; j = y-1;
j_end = -1; j = y-1;
} else {
j_end = y; j = 0;
j_end = y; j = 0;
}
for (; j != j_end; j += vdir) {
for (i=0; i < x; ++i) {
unsigned char *d = (unsigned char *) data + (j*x+i)*comp;
stbiw__write_pixel(s, rgb_dir, comp, write_alpha, expand_mono, d);
}
stbiw__write_flush(s);
s->func(s->context, &zero, scanline_pad);
for (i=0; i < x; ++i) {
unsigned char *d = (unsigned char *) data + (j*x+i)*comp;
stbiw__write_pixel(s, rgb_dir, comp, write_alpha, expand_mono, d);
}
stbiw__write_flush(s);
s->func(s->context, &zero, scanline_pad);
}
}
static int stbiw__outfile(stbi__write_context *s, int rgb_dir, int vdir, int x, int y, int comp, int expand_mono, void *data, int alpha, int pad, const char *fmt, ...)
{
if (y < 0 || x < 0) {
return 0;
return 0;
} else {
va_list v;
va_start(v, fmt);
stbiw__writefv(s, fmt, v);
va_end(v);
stbiw__write_pixels(s,rgb_dir,vdir,x,y,comp,data,alpha,pad, expand_mono);
return 1;
va_list v;
va_start(v, fmt);
stbiw__writefv(s, fmt, v);
va_end(v);
stbiw__write_pixels(s,rgb_dir,vdir,x,y,comp,data,alpha,pad, expand_mono);
return 1;
}
}
static int stbi_write_bmp_core(stbi__write_context *s, int x, int y, int comp, const void *data)
{
if (comp != 4) {
// write RGB bitmap
int pad = (-x*3) & 3;
return stbiw__outfile(s,-1,-1,x,y,comp,1,(void *) data,0,pad,
"11 4 22 4" "4 44 22 444444",
'B', 'M', 14+40+(x*3+pad)*y, 0,0, 14+40, // file header
40, x,y, 1,24, 0,0,0,0,0,0); // bitmap header
// write RGB bitmap
int pad = (-x*3) & 3;
return stbiw__outfile(s,-1,-1,x,y,comp,1,(void *) data,0,pad,
"11 4 22 4" "4 44 22 444444",
'B', 'M', 14+40+(x*3+pad)*y, 0,0, 14+40, // file header
40, x,y, 1,24, 0,0,0,0,0,0); // bitmap header
} else {
// RGBA bitmaps need a v4 header
// use BI_BITFIELDS mode with 32bpp and alpha mask
// (straight BI_RGB with alpha mask doesn't work in most readers)
return stbiw__outfile(s,-1,-1,x,y,comp,1,(void *)data,1,0,
"11 4 22 4" "4 44 22 444444 4444 4 444 444 444 444",
'B', 'M', 14+108+x*y*4, 0, 0, 14+108, // file header
108, x,y, 1,32, 3,0,0,0,0,0, 0xff0000,0xff00,0xff,0xff000000u, 0, 0,0,0, 0,0,0, 0,0,0, 0,0,0); // bitmap V4 header
// RGBA bitmaps need a v4 header
// use BI_BITFIELDS mode with 32bpp and alpha mask
// (straight BI_RGB with alpha mask doesn't work in most readers)
return stbiw__outfile(s,-1,-1,x,y,comp,1,(void *)data,1,0,
"11 4 22 4" "4 44 22 444444 4444 4 444 444 444 444",
'B', 'M', 14+108+x*y*4, 0, 0, 14+108, // file header
108, x,y, 1,32, 3,0,0,0,0,0, 0xff0000,0xff00,0xff,0xff000000u, 0, 0,0,0, 0,0,0, 0,0,0, 0,0,0); // bitmap V4 header
}
}
......@@ -521,11 +533,11 @@ STBIWDEF int stbi_write_bmp(char const *filename, int x, int y, int comp, const
{
stbi__write_context s = { 0 };
if (stbi__start_write_file(&s,filename)) {
int r = stbi_write_bmp_core(&s, x, y, comp, data);
stbi__end_write_file(&s);
return r;
int r = stbi_write_bmp_core(&s, x, y, comp, data);
stbi__end_write_file(&s);
return r;
} else
return 0;
return 0;
}
#endif //!STBI_WRITE_NO_STDIO
......@@ -536,74 +548,74 @@ static int stbi_write_tga_core(stbi__write_context *s, int x, int y, int comp, v
int format = colorbytes < 2 ? 3 : 2; // 3 color channels (RGB/RGBA) = 2, 1 color channel (Y/YA) = 3
if (y < 0 || x < 0)
return 0;
return 0;
if (!stbi_write_tga_with_rle) {
return stbiw__outfile(s, -1, -1, x, y, comp, 0, (void *) data, has_alpha, 0,
"111 221 2222 11", 0, 0, format, 0, 0, 0, 0, 0, x, y, (colorbytes + has_alpha) * 8, has_alpha * 8);
return stbiw__outfile(s, -1, -1, x, y, comp, 0, (void *) data, has_alpha, 0,
"111 221 2222 11", 0, 0, format, 0, 0, 0, 0, 0, x, y, (colorbytes + has_alpha) * 8, has_alpha * 8);
} else {
int i,j,k;
int jend, jdir;
stbiw__writef(s, "111 221 2222 11", 0,0,format+8, 0,0,0, 0,0,x,y, (colorbytes + has_alpha) * 8, has_alpha * 8);
if (stbi__flip_vertically_on_write) {
j = 0;
jend = y;
jdir = 1;
} else {
j = y-1;
jend = -1;
jdir = -1;
}
for (; j != jend; j += jdir) {
unsigned char *row = (unsigned char *) data + j * x * comp;
int len;
for (i = 0; i < x; i += len) {
unsigned char *begin = row + i * comp;
int diff = 1;
len = 1;
if (i < x - 1) {
++len;
diff = memcmp(begin, row + (i + 1) * comp, comp);
if (diff) {
const unsigned char *prev = begin;
for (k = i + 2; k < x && len < 128; ++k) {
if (memcmp(prev, row + k * comp, comp)) {
prev += comp;
++len;
} else {
--len;
break;
}
}
} else {
for (k = i + 2; k < x && len < 128; ++k) {
if (!memcmp(begin, row + k * comp, comp)) {
++len;
} else {
break;
}
}
}
}
if (diff) {
unsigned char header = STBIW_UCHAR(len - 1);
stbiw__write1(s, header);
for (k = 0; k < len; ++k) {
stbiw__write_pixel(s, -1, comp, has_alpha, 0, begin + k * comp);
}
} else {
unsigned char header = STBIW_UCHAR(len - 129);
stbiw__write1(s, header);
stbiw__write_pixel(s, -1, comp, has_alpha, 0, begin);
}
}
}
stbiw__write_flush(s);
int i,j,k;
int jend, jdir;
stbiw__writef(s, "111 221 2222 11", 0,0,format+8, 0,0,0, 0,0,x,y, (colorbytes + has_alpha) * 8, has_alpha * 8);
if (stbi__flip_vertically_on_write) {
j = 0;
jend = y;
jdir = 1;
} else {
j = y-1;
jend = -1;
jdir = -1;
}
for (; j != jend; j += jdir) {
unsigned char *row = (unsigned char *) data + j * x * comp;
int len;
for (i = 0; i < x; i += len) {
unsigned char *begin = row + i * comp;
int diff = 1;
len = 1;
if (i < x - 1) {
++len;
diff = memcmp(begin, row + (i + 1) * comp, comp);
if (diff) {
const unsigned char *prev = begin;
for (k = i + 2; k < x && len < 128; ++k) {
if (memcmp(prev, row + k * comp, comp)) {
prev += comp;
++len;
} else {
--len;
break;
}
}
} else {
for (k = i + 2; k < x && len < 128; ++k) {
if (!memcmp(begin, row + k * comp, comp)) {
++len;
} else {
break;
}
}
}
}
if (diff) {
unsigned char header = STBIW_UCHAR(len - 1);
stbiw__write1(s, header);
for (k = 0; k < len; ++k) {
stbiw__write_pixel(s, -1, comp, has_alpha, 0, begin + k * comp);
}
} else {
unsigned char header = STBIW_UCHAR(len - 129);
stbiw__write1(s, header);
stbiw__write_pixel(s, -1, comp, has_alpha, 0, begin);
}
}
}
stbiw__write_flush(s);
}
return 1;
}
......@@ -620,11 +632,11 @@ STBIWDEF int stbi_write_tga(char const *filename, int x, int y, int comp, const
{
stbi__write_context s = { 0 };
if (stbi__start_write_file(&s,filename)) {
int r = stbi_write_tga_core(&s, x, y, comp, (void *) data);
stbi__end_write_file(&s);
return r;
int r = stbi_write_tga_core(&s, x, y, comp, (void *) data);
stbi__end_write_file(&s);
return r;
} else
return 0;
return 0;
}
#endif
......@@ -642,14 +654,14 @@ static void stbiw__linear_to_rgbe(unsigned char *rgbe, float *linear)
float maxcomp = stbiw__max(linear[0], stbiw__max(linear[1], linear[2]));
if (maxcomp < 1e-32f) {
rgbe[0] = rgbe[1] = rgbe[2] = rgbe[3] = 0;
rgbe[0] = rgbe[1] = rgbe[2] = rgbe[3] = 0;
} else {
float normalize = (float) frexp(maxcomp, &exponent) * 256.0f/maxcomp;
float normalize = (float) frexp(maxcomp, &exponent) * 256.0f/maxcomp;
rgbe[0] = (unsigned char)(linear[0] * normalize);
rgbe[1] = (unsigned char)(linear[1] * normalize);
rgbe[2] = (unsigned char)(linear[2] * normalize);
rgbe[3] = (unsigned char)(exponent + 128);
rgbe[0] = (unsigned char)(linear[0] * normalize);
rgbe[1] = (unsigned char)(linear[1] * normalize);
rgbe[2] = (unsigned char)(linear[2] * normalize);
rgbe[3] = (unsigned char)(exponent + 128);
}
}
......@@ -681,106 +693,106 @@ static void stbiw__write_hdr_scanline(stbi__write_context *s, int width, int nco
/* skip RLE for images too small or large */
if (width < 8 || width >= 32768) {
for (x=0; x < width; x++) {
switch (ncomp) {
case 4: /* fallthrough */
case 3: linear[2] = scanline[x*ncomp + 2];
linear[1] = scanline[x*ncomp + 1];
linear[0] = scanline[x*ncomp + 0];
break;
default:
linear[0] = linear[1] = linear[2] = scanline[x*ncomp + 0];
break;
}
stbiw__linear_to_rgbe(rgbe, linear);
s->func(s->context, rgbe, 4);
}
for (x=0; x < width; x++) {
switch (ncomp) {
case 4: /* fallthrough */
case 3: linear[2] = scanline[x*ncomp + 2];
linear[1] = scanline[x*ncomp + 1];
linear[0] = scanline[x*ncomp + 0];
break;
default:
linear[0] = linear[1] = linear[2] = scanline[x*ncomp + 0];
break;
}
stbiw__linear_to_rgbe(rgbe, linear);
s->func(s->context, rgbe, 4);
}
} else {
int c,r;
/* encode into scratch buffer */
for (x=0; x < width; x++) {
switch(ncomp) {
case 4: /* fallthrough */
case 3: linear[2] = scanline[x*ncomp + 2];
linear[1] = scanline[x*ncomp + 1];
linear[0] = scanline[x*ncomp + 0];
break;
default:
linear[0] = linear[1] = linear[2] = scanline[x*ncomp + 0];
break;
}
stbiw__linear_to_rgbe(rgbe, linear);
scratch[x + width*0] = rgbe[0];
scratch[x + width*1] = rgbe[1];
scratch[x + width*2] = rgbe[2];
scratch[x + width*3] = rgbe[3];
}
s->func(s->context, scanlineheader, 4);
/* RLE each component separately */
for (c=0; c < 4; c++) {
unsigned char *comp = &scratch[width*c];
x = 0;
while (x < width) {
// find first run
r = x;
while (r+2 < width) {
if (comp[r] == comp[r+1] && comp[r] == comp[r+2])
break;
++r;
}
if (r+2 >= width)
r = width;
// dump up to first run
while (x < r) {
int len = r-x;
if (len > 128) len = 128;
stbiw__write_dump_data(s, len, &comp[x]);
x += len;
}
// if there's a run, output it
if (r+2 < width) { // same test as what we break out of in search loop, so only true if we break'd
// find next byte after run
while (r < width && comp[r] == comp[x])
++r;
// output run up to r
while (x < r) {
int len = r-x;
if (len > 127) len = 127;
stbiw__write_run_data(s, len, comp[x]);
x += len;
}
}
}
}
int c,r;
/* encode into scratch buffer */
for (x=0; x < width; x++) {
switch(ncomp) {
case 4: /* fallthrough */
case 3: linear[2] = scanline[x*ncomp + 2];
linear[1] = scanline[x*ncomp + 1];
linear[0] = scanline[x*ncomp + 0];
break;
default:
linear[0] = linear[1] = linear[2] = scanline[x*ncomp + 0];
break;
}
stbiw__linear_to_rgbe(rgbe, linear);
scratch[x + width*0] = rgbe[0];
scratch[x + width*1] = rgbe[1];
scratch[x + width*2] = rgbe[2];
scratch[x + width*3] = rgbe[3];
}
s->func(s->context, scanlineheader, 4);
/* RLE each component separately */
for (c=0; c < 4; c++) {
unsigned char *comp = &scratch[width*c];
x = 0;
while (x < width) {
// find first run
r = x;
while (r+2 < width) {
if (comp[r] == comp[r+1] && comp[r] == comp[r+2])
break;
++r;
}
if (r+2 >= width)
r = width;
// dump up to first run
while (x < r) {
int len = r-x;
if (len > 128) len = 128;
stbiw__write_dump_data(s, len, &comp[x]);
x += len;
}
// if there's a run, output it
if (r+2 < width) { // same test as what we break out of in search loop, so only true if we break'd
// find next byte after run
while (r < width && comp[r] == comp[x])
++r;
// output run up to r
while (x < r) {
int len = r-x;
if (len > 127) len = 127;
stbiw__write_run_data(s, len, comp[x]);
x += len;
}
}
}
}
}
}
static int stbi_write_hdr_core(stbi__write_context *s, int x, int y, int comp, float *data)
{
if (y <= 0 || x <= 0 || data == NULL)
return 0;
return 0;
else {
// Each component is stored separately. Allocate scratch space for full output scanline.
unsigned char *scratch = (unsigned char *) STBIW_MALLOC(x*4);
int i, len;
char buffer[128];
char header[] = "#?RADIANCE\n# Written by stb_image_write.h\nFORMAT=32-bit_rle_rgbe\n";
s->func(s->context, header, sizeof(header)-1);
// Each component is stored separately. Allocate scratch space for full output scanline.
unsigned char *scratch = (unsigned char *) STBIW_MALLOC(x*4);
int i, len;
char buffer[128];
char header[] = "#?RADIANCE\n# Written by stb_image_write.h\nFORMAT=32-bit_rle_rgbe\n";
s->func(s->context, header, sizeof(header)-1);
#ifdef __STDC_LIB_EXT1__
len = sprintf_s(buffer, sizeof(buffer), "EXPOSURE= 1.0000000000000\n\n-Y %d +X %d\n", y, x);
len = sprintf_s(buffer, sizeof(buffer), "EXPOSURE= 1.0000000000000\n\n-Y %d +X %d\n", y, x);
#else
len = sprintf(buffer, "EXPOSURE= 1.0000000000000\n\n-Y %d +X %d\n", y, x);
len = sprintf(buffer, "EXPOSURE= 1.0000000000000\n\n-Y %d +X %d\n", y, x);
#endif
s->func(s->context, buffer, len);
s->func(s->context, buffer, len);
for(i=0; i < y; i++)
stbiw__write_hdr_scanline(s, x, comp, scratch, data + comp*x*(stbi__flip_vertically_on_write ? y-1-i : i));
STBIW_FREE(scratch);
return 1;
for(i=0; i < y; i++)
stbiw__write_hdr_scanline(s, x, comp, scratch, data + comp*x*(stbi__flip_vertically_on_write ? y-1-i : i));
STBIW_FREE(scratch);
return 1;
}
}
......@@ -795,11 +807,11 @@ STBIWDEF int stbi_write_hdr(char const *filename, int x, int y, int comp, const
{
stbi__write_context s = { 0 };
if (stbi__start_write_file(&s,filename)) {
int r = stbi_write_hdr_core(&s, x, y, comp, (float *) data);
stbi__end_write_file(&s);
return r;
int r = stbi_write_hdr_core(&s, x, y, comp, (float *) data);
stbi__end_write_file(&s);
return r;
} else
return 0;
return 0;
}
#endif // STBI_WRITE_NO_STDIO
......@@ -829,9 +841,9 @@ static void *stbiw__sbgrowf(void **arr, int increment, int itemsize)
void *p = STBIW_REALLOC_SIZED(*arr ? stbiw__sbraw(*arr) : 0, *arr ? (stbiw__sbm(*arr)*itemsize + sizeof(int)*2) : 0, itemsize * m + sizeof(int)*2);
STBIW_ASSERT(p);
if (p) {
if (!*arr) ((int *) p)[1] = 0;
*arr = (void *) ((int *) p + 2);
stbiw__sbm(*arr) = m;
if (!*arr) ((int *) p)[1] = 0;
*arr = (void *) ((int *) p + 2);
stbiw__sbm(*arr) = m;
}
return *arr;
}
......@@ -839,9 +851,9 @@ static void *stbiw__sbgrowf(void **arr, int increment, int itemsize)
static unsigned char *stbiw__zlib_flushf(unsigned char *data, unsigned int *bitbuffer, int *bitcount)
{
while (*bitcount >= 8) {
stbiw__sbpush(data, STBIW_UCHAR(*bitbuffer));
*bitbuffer >>= 8;
*bitcount -= 8;
stbiw__sbpush(data, STBIW_UCHAR(*bitbuffer));
*bitbuffer >>= 8;
*bitcount -= 8;
}
return data;
}
......@@ -850,8 +862,8 @@ static int stbiw__zlib_bitrev(int code, int codebits)
{
int res=0;
while (codebits--) {
res = (res << 1) | (code & 1);
code >>= 1;
res = (res << 1) | (code & 1);
code >>= 1;
}
return res;
}
......@@ -860,7 +872,7 @@ static unsigned int stbiw__zlib_countm(unsigned char *a, unsigned char *b, int l
{
int i;
for (i=0; i < limit && i < 258; ++i)
if (a[i] != b[i]) break;
if (a[i] != b[i]) break;
return i;
}
......@@ -878,7 +890,7 @@ static unsigned int stbiw__zhash(unsigned char *data)
#define stbiw__zlib_flush() (out = stbiw__zlib_flushf(out, &bitbuf, &bitcount))
#define stbiw__zlib_add(code,codebits) \
(bitbuf |= (code) << bitcount, bitcount += (codebits), stbiw__zlib_flush())
(bitbuf |= (code) << bitcount, bitcount += (codebits), stbiw__zlib_flush())
#define stbiw__zlib_huffa(b,c) stbiw__zlib_add(stbiw__zlib_bitrev(b,c),c)
// default huffman tables
#define stbiw__zlib_huff1(n) stbiw__zlib_huffa(0x30 + (n), 8)
......@@ -907,7 +919,7 @@ STBIWDEF unsigned char * stbi_zlib_compress(unsigned char *data, int data_len, i
unsigned char *out = NULL;
unsigned char ***hash_table = (unsigned char***) STBIW_MALLOC(stbiw__ZHASH * sizeof(unsigned char**));
if (hash_table == NULL)
return NULL;
return NULL;
if (quality < 5) quality = 5;
stbiw__sbpush(out, 0x78); // DEFLATE 32K window
......@@ -916,103 +928,103 @@ STBIWDEF unsigned char * stbi_zlib_compress(unsigned char *data, int data_len, i
stbiw__zlib_add(1,2); // BTYPE = 1 -- fixed huffman
for (i=0; i < stbiw__ZHASH; ++i)
hash_table[i] = NULL;
hash_table[i] = NULL;
i=0;
while (i < data_len-3) {
// hash next 3 bytes of data to be compressed
int h = stbiw__zhash(data+i)&(stbiw__ZHASH-1), best=3;
unsigned char *bestloc = 0;
unsigned char **hlist = hash_table[h];
int n = stbiw__sbcount(hlist);
for (j=0; j < n; ++j) {
if (hlist[j]-data > i-32768) { // if entry lies within window
int d = stbiw__zlib_countm(hlist[j], data+i, data_len-i);
if (d >= best) { best=d; bestloc=hlist[j]; }
}
}
// when hash table entry is too long, delete half the entries
if (hash_table[h] && stbiw__sbn(hash_table[h]) == 2*quality) {
STBIW_MEMMOVE(hash_table[h], hash_table[h]+quality, sizeof(hash_table[h][0])*quality);
stbiw__sbn(hash_table[h]) = quality;
}
stbiw__sbpush(hash_table[h],data+i);
if (bestloc) {
// "lazy matching" - check match at *next* byte, and if it's better, do cur byte as literal
h = stbiw__zhash(data+i+1)&(stbiw__ZHASH-1);
hlist = hash_table[h];
n = stbiw__sbcount(hlist);
for (j=0; j < n; ++j) {
if (hlist[j]-data > i-32767) {
int e = stbiw__zlib_countm(hlist[j], data+i+1, data_len-i-1);
if (e > best) { // if next match is better, bail on current match
bestloc = NULL;
break;
}
}
}
}
if (bestloc) {
int d = (int) (data+i - bestloc); // distance back
STBIW_ASSERT(d <= 32767 && best <= 258);
for (j=0; best > lengthc[j+1]-1; ++j);
stbiw__zlib_huff(j+257);
if (lengtheb[j]) stbiw__zlib_add(best - lengthc[j], lengtheb[j]);
for (j=0; d > distc[j+1]-1; ++j);
stbiw__zlib_add(stbiw__zlib_bitrev(j,5),5);
if (disteb[j]) stbiw__zlib_add(d - distc[j], disteb[j]);
i += best;
} else {
stbiw__zlib_huffb(data[i]);
++i;
}
// hash next 3 bytes of data to be compressed
int h = stbiw__zhash(data+i)&(stbiw__ZHASH-1), best=3;
unsigned char *bestloc = 0;
unsigned char **hlist = hash_table[h];
int n = stbiw__sbcount(hlist);
for (j=0; j < n; ++j) {
if (hlist[j]-data > i-32768) { // if entry lies within window
int d = stbiw__zlib_countm(hlist[j], data+i, data_len-i);
if (d >= best) { best=d; bestloc=hlist[j]; }
}
}
// when hash table entry is too long, delete half the entries
if (hash_table[h] && stbiw__sbn(hash_table[h]) == 2*quality) {
STBIW_MEMMOVE(hash_table[h], hash_table[h]+quality, sizeof(hash_table[h][0])*quality);
stbiw__sbn(hash_table[h]) = quality;
}
stbiw__sbpush(hash_table[h],data+i);
if (bestloc) {
// "lazy matching" - check match at *next* byte, and if it's better, do cur byte as literal
h = stbiw__zhash(data+i+1)&(stbiw__ZHASH-1);
hlist = hash_table[h];
n = stbiw__sbcount(hlist);
for (j=0; j < n; ++j) {
if (hlist[j]-data > i-32767) {
int e = stbiw__zlib_countm(hlist[j], data+i+1, data_len-i-1);
if (e > best) { // if next match is better, bail on current match
bestloc = NULL;
break;
}
}
}
}
if (bestloc) {
int d = (int) (data+i - bestloc); // distance back
STBIW_ASSERT(d <= 32767 && best <= 258);
for (j=0; best > lengthc[j+1]-1; ++j);
stbiw__zlib_huff(j+257);
if (lengtheb[j]) stbiw__zlib_add(best - lengthc[j], lengtheb[j]);
for (j=0; d > distc[j+1]-1; ++j);
stbiw__zlib_add(stbiw__zlib_bitrev(j,5),5);
if (disteb[j]) stbiw__zlib_add(d - distc[j], disteb[j]);
i += best;
} else {
stbiw__zlib_huffb(data[i]);
++i;
}
}
// write out final bytes
for (;i < data_len; ++i)
stbiw__zlib_huffb(data[i]);
stbiw__zlib_huffb(data[i]);
stbiw__zlib_huff(256); // end of block
// pad with 0 bits to byte boundary
while (bitcount)
stbiw__zlib_add(0,1);
stbiw__zlib_add(0,1);
for (i=0; i < stbiw__ZHASH; ++i)
(void) stbiw__sbfree(hash_table[i]);
(void) stbiw__sbfree(hash_table[i]);
STBIW_FREE(hash_table);
// store uncompressed instead if compression was worse
if (stbiw__sbn(out) > data_len + 2 + ((data_len+32766)/32767)*5) {
stbiw__sbn(out) = 2; // truncate to DEFLATE 32K window and FLEVEL = 1
for (j = 0; j < data_len;) {
int blocklen = data_len - j;
if (blocklen > 32767) blocklen = 32767;
stbiw__sbpush(out, data_len - j == blocklen); // BFINAL = ?, BTYPE = 0 -- no compression
stbiw__sbpush(out, STBIW_UCHAR(blocklen)); // LEN
stbiw__sbpush(out, STBIW_UCHAR(blocklen >> 8));
stbiw__sbpush(out, STBIW_UCHAR(~blocklen)); // NLEN
stbiw__sbpush(out, STBIW_UCHAR(~blocklen >> 8));
memcpy(out+stbiw__sbn(out), data+j, blocklen);
stbiw__sbn(out) += blocklen;
j += blocklen;
}
stbiw__sbn(out) = 2; // truncate to DEFLATE 32K window and FLEVEL = 1
for (j = 0; j < data_len;) {
int blocklen = data_len - j;
if (blocklen > 32767) blocklen = 32767;
stbiw__sbpush(out, data_len - j == blocklen); // BFINAL = ?, BTYPE = 0 -- no compression
stbiw__sbpush(out, STBIW_UCHAR(blocklen)); // LEN
stbiw__sbpush(out, STBIW_UCHAR(blocklen >> 8));
stbiw__sbpush(out, STBIW_UCHAR(~blocklen)); // NLEN
stbiw__sbpush(out, STBIW_UCHAR(~blocklen >> 8));
memcpy(out+stbiw__sbn(out), data+j, blocklen);
stbiw__sbn(out) += blocklen;
j += blocklen;
}
}
{
// compute adler32 on input
unsigned int s1=1, s2=0;
int blocklen = (int) (data_len % 5552);
j=0;
while (j < data_len) {
for (i=0; i < blocklen; ++i) { s1 += data[j+i]; s2 += s1; }
s1 %= 65521; s2 %= 65521;
j += blocklen;
blocklen = 5552;
}
stbiw__sbpush(out, STBIW_UCHAR(s2 >> 8));
stbiw__sbpush(out, STBIW_UCHAR(s2));
stbiw__sbpush(out, STBIW_UCHAR(s1 >> 8));
stbiw__sbpush(out, STBIW_UCHAR(s1));
// compute adler32 on input
unsigned int s1=1, s2=0;
int blocklen = (int) (data_len % 5552);
j=0;
while (j < data_len) {
for (i=0; i < blocklen; ++i) { s1 += data[j+i]; s2 += s1; }
s1 %= 65521; s2 %= 65521;
j += blocklen;
blocklen = 5552;
}
stbiw__sbpush(out, STBIW_UCHAR(s2 >> 8));
stbiw__sbpush(out, STBIW_UCHAR(s2));
stbiw__sbpush(out, STBIW_UCHAR(s1 >> 8));
stbiw__sbpush(out, STBIW_UCHAR(s1));
}
*out_len = stbiw__sbn(out);
// make returned pointer freeable
......@@ -1024,48 +1036,48 @@ STBIWDEF unsigned char * stbi_zlib_compress(unsigned char *data, int data_len, i
static unsigned int stbiw__crc32(unsigned char *buffer, int len)
{
#ifdef STBIW_CRC32
return STBIW_CRC32(buffer, len);
return STBIW_CRC32(buffer, len);
#else
static unsigned int crc_table[256] =
{
0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA, 0x076DC419, 0x706AF48F, 0xE963A535, 0x9E6495A3,
0x0eDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988, 0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91,
0x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE, 0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7,
0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC, 0x14015C4F, 0x63066CD9, 0xFA0F3D63, 0x8D080DF5,
0x3B6E20C8, 0x4C69105E, 0xD56041E4, 0xA2677172, 0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B,
0x35B5A8FA, 0x42B2986C, 0xDBBBC9D6, 0xACBCF940, 0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59,
0x26D930AC, 0x51DE003A, 0xC8D75180, 0xBFD06116, 0x21B4F4B5, 0x56B3C423, 0xCFBA9599, 0xB8BDA50F,
0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924, 0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D,
0x76DC4190, 0x01DB7106, 0x98D220BC, 0xEFD5102A, 0x71B18589, 0x06B6B51F, 0x9FBFE4A5, 0xE8B8D433,
0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818, 0x7F6A0DBB, 0x086D3D2D, 0x91646C97, 0xE6635C01,
0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E, 0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457,
0x65B0D9C6, 0x12B7E950, 0x8BBEB8EA, 0xFCB9887C, 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65,
0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2, 0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB,
0x4369E96A, 0x346ED9FC, 0xAD678846, 0xDA60B8D0, 0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9,
0x5005713C, 0x270241AA, 0xBE0B1010, 0xC90C2086, 0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F,
0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4, 0x59B33D17, 0x2EB40D81, 0xB7BD5C3B, 0xC0BA6CAD,
0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A, 0xEAD54739, 0x9DD277AF, 0x04DB2615, 0x73DC1683,
0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8, 0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1,
0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE, 0xF762575D, 0x806567CB, 0x196C3671, 0x6E6B06E7,
0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC, 0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5,
0xD6D6A3E8, 0xA1D1937E, 0x38D8C2C4, 0x4FDFF252, 0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B,
0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60, 0xDF60EFC3, 0xA867DF55, 0x316E8EEF, 0x4669BE79,
0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236, 0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F,
0xC5BA3BBE, 0xB2BD0B28, 0x2BB45A92, 0x5CB36A04, 0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D,
0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A, 0x9C0906A9, 0xEB0E363F, 0x72076785, 0x05005713,
0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38, 0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21,
0x86D3D2D4, 0xF1D4E242, 0x68DDB3F8, 0x1FDA836E, 0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777,
0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C, 0x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45,
0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2, 0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB,
0xAED16A4A, 0xD9D65ADC, 0x40DF0B66, 0x37D83BF0, 0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9,
0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6, 0xBAD03605, 0xCDD70693, 0x54DE5729, 0x23D967BF,
0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94, 0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D
0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA, 0x076DC419, 0x706AF48F, 0xE963A535, 0x9E6495A3,
0x0eDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988, 0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91,
0x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE, 0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7,
0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC, 0x14015C4F, 0x63066CD9, 0xFA0F3D63, 0x8D080DF5,
0x3B6E20C8, 0x4C69105E, 0xD56041E4, 0xA2677172, 0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B,
0x35B5A8FA, 0x42B2986C, 0xDBBBC9D6, 0xACBCF940, 0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59,
0x26D930AC, 0x51DE003A, 0xC8D75180, 0xBFD06116, 0x21B4F4B5, 0x56B3C423, 0xCFBA9599, 0xB8BDA50F,
0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924, 0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D,
0x76DC4190, 0x01DB7106, 0x98D220BC, 0xEFD5102A, 0x71B18589, 0x06B6B51F, 0x9FBFE4A5, 0xE8B8D433,
0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818, 0x7F6A0DBB, 0x086D3D2D, 0x91646C97, 0xE6635C01,
0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E, 0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457,
0x65B0D9C6, 0x12B7E950, 0x8BBEB8EA, 0xFCB9887C, 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65,
0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2, 0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB,
0x4369E96A, 0x346ED9FC, 0xAD678846, 0xDA60B8D0, 0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9,
0x5005713C, 0x270241AA, 0xBE0B1010, 0xC90C2086, 0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F,
0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4, 0x59B33D17, 0x2EB40D81, 0xB7BD5C3B, 0xC0BA6CAD,
0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A, 0xEAD54739, 0x9DD277AF, 0x04DB2615, 0x73DC1683,
0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8, 0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1,
0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE, 0xF762575D, 0x806567CB, 0x196C3671, 0x6E6B06E7,
0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC, 0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5,
0xD6D6A3E8, 0xA1D1937E, 0x38D8C2C4, 0x4FDFF252, 0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B,
0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60, 0xDF60EFC3, 0xA867DF55, 0x316E8EEF, 0x4669BE79,
0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236, 0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F,
0xC5BA3BBE, 0xB2BD0B28, 0x2BB45A92, 0x5CB36A04, 0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D,
0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A, 0x9C0906A9, 0xEB0E363F, 0x72076785, 0x05005713,
0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38, 0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21,
0x86D3D2D4, 0xF1D4E242, 0x68DDB3F8, 0x1FDA836E, 0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777,
0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C, 0x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45,
0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2, 0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB,
0xAED16A4A, 0xD9D65ADC, 0x40DF0B66, 0x37D83BF0, 0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9,
0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6, 0xBAD03605, 0xCDD70693, 0x54DE5729, 0x23D967BF,
0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94, 0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D
};
unsigned int crc = ~0u;
int i;
for (i=0; i < len; ++i)
crc = (crc >> 8) ^ crc_table[buffer[i] ^ (crc & 0xff)];
crc = (crc >> 8) ^ crc_table[buffer[i] ^ (crc & 0xff)];
return ~crc;
#endif
}
......@@ -1100,28 +1112,28 @@ static void stbiw__encode_png_line(unsigned char *pixels, int stride_bytes, int
int signed_stride = stbi__flip_vertically_on_write ? -stride_bytes : stride_bytes;
if (type==0) {
memcpy(line_buffer, z, width*n);
return;
memcpy(line_buffer, z, width*n);
return;
}
// first loop isn't optimized since it's just one pixel
for (i = 0; i < n; ++i) {
switch (type) {
case 1: line_buffer[i] = z[i]; break;
case 2: line_buffer[i] = z[i] - z[i-signed_stride]; break;
case 3: line_buffer[i] = z[i] - (z[i-signed_stride]>>1); break;
case 4: line_buffer[i] = (signed char) (z[i] - stbiw__paeth(0,z[i-signed_stride],0)); break;
case 5: line_buffer[i] = z[i]; break;
case 6: line_buffer[i] = z[i]; break;
}
switch (type) {
case 1: line_buffer[i] = z[i]; break;
case 2: line_buffer[i] = z[i] - z[i-signed_stride]; break;
case 3: line_buffer[i] = z[i] - (z[i-signed_stride]>>1); break;
case 4: line_buffer[i] = (signed char) (z[i] - stbiw__paeth(0,z[i-signed_stride],0)); break;
case 5: line_buffer[i] = z[i]; break;
case 6: line_buffer[i] = z[i]; break;
}
}
switch (type) {
case 1: for (i=n; i < width*n; ++i) line_buffer[i] = z[i] - z[i-n]; break;
case 2: for (i=n; i < width*n; ++i) line_buffer[i] = z[i] - z[i-signed_stride]; break;
case 3: for (i=n; i < width*n; ++i) line_buffer[i] = z[i] - ((z[i-n] + z[i-signed_stride])>>1); break;
case 4: for (i=n; i < width*n; ++i) line_buffer[i] = z[i] - stbiw__paeth(z[i-n], z[i-signed_stride], z[i-signed_stride-n]); break;
case 5: for (i=n; i < width*n; ++i) line_buffer[i] = z[i] - (z[i-n]>>1); break;
case 6: for (i=n; i < width*n; ++i) line_buffer[i] = z[i] - stbiw__paeth(z[i-n], 0,0); break;
case 1: for (i=n; i < width*n; ++i) line_buffer[i] = z[i] - z[i-n]; break;
case 2: for (i=n; i < width*n; ++i) line_buffer[i] = z[i] - z[i-signed_stride]; break;
case 3: for (i=n; i < width*n; ++i) line_buffer[i] = z[i] - ((z[i-n] + z[i-signed_stride])>>1); break;
case 4: for (i=n; i < width*n; ++i) line_buffer[i] = z[i] - stbiw__paeth(z[i-n], z[i-signed_stride], z[i-signed_stride-n]); break;
case 5: for (i=n; i < width*n; ++i) line_buffer[i] = z[i] - (z[i-n]>>1); break;
case 6: for (i=n; i < width*n; ++i) line_buffer[i] = z[i] - stbiw__paeth(z[i-n], 0,0); break;
}
}
......@@ -1135,42 +1147,42 @@ STBIWDEF unsigned char *stbi_write_png_to_mem(const unsigned char *pixels, int s
int j,zlen;
if (stride_bytes == 0)
stride_bytes = x * n;
stride_bytes = x * n;
if (force_filter >= 5) {
force_filter = -1;
force_filter = -1;
}
filt = (unsigned char *) STBIW_MALLOC((x*n+1) * y); if (!filt) return 0;
line_buffer = (signed char *) STBIW_MALLOC(x * n); if (!line_buffer) { STBIW_FREE(filt); return 0; }
for (j=0; j < y; ++j) {
int filter_type;
if (force_filter > -1) {
filter_type = force_filter;
stbiw__encode_png_line((unsigned char*)(pixels), stride_bytes, x, y, j, n, force_filter, line_buffer);
} else { // Estimate the best filter by running through all of them:
int best_filter = 0, best_filter_val = 0x7fffffff, est, i;
for (filter_type = 0; filter_type < 5; filter_type++) {
stbiw__encode_png_line((unsigned char*)(pixels), stride_bytes, x, y, j, n, filter_type, line_buffer);
// Estimate the entropy of the line using this filter; the less, the better.
est = 0;
for (i = 0; i < x*n; ++i) {
est += abs((signed char) line_buffer[i]);
}
if (est < best_filter_val) {
best_filter_val = est;
best_filter = filter_type;
}
}
if (filter_type != best_filter) { // If the last iteration already got us the best filter, don't redo it
stbiw__encode_png_line((unsigned char*)(pixels), stride_bytes, x, y, j, n, best_filter, line_buffer);
filter_type = best_filter;
}
}
// when we get here, filter_type contains the filter type, and line_buffer contains the data
filt[j*(x*n+1)] = (unsigned char) filter_type;
STBIW_MEMMOVE(filt+j*(x*n+1)+1, line_buffer, x*n);
int filter_type;
if (force_filter > -1) {
filter_type = force_filter;
stbiw__encode_png_line((unsigned char*)(pixels), stride_bytes, x, y, j, n, force_filter, line_buffer);
} else { // Estimate the best filter by running through all of them:
int best_filter = 0, best_filter_val = 0x7fffffff, est, i;
for (filter_type = 0; filter_type < 5; filter_type++) {
stbiw__encode_png_line((unsigned char*)(pixels), stride_bytes, x, y, j, n, filter_type, line_buffer);
// Estimate the entropy of the line using this filter; the less, the better.
est = 0;
for (i = 0; i < x*n; ++i) {
est += abs((signed char) line_buffer[i]);
}
if (est < best_filter_val) {
best_filter_val = est;
best_filter = filter_type;
}
}
if (filter_type != best_filter) { // If the last iteration already got us the best filter, don't redo it
stbiw__encode_png_line((unsigned char*)(pixels), stride_bytes, x, y, j, n, best_filter, line_buffer);
filter_type = best_filter;
}
}
// when we get here, filter_type contains the filter type, and line_buffer contains the data
filt[j*(x*n+1)] = (unsigned char) filter_type;
STBIW_MEMMOVE(filt+j*(x*n+1)+1, line_buffer, x*n);
}
STBIW_FREE(line_buffer);
zlib = stbi_zlib_compress(filt, y*( x*n+1), &zlen, stbi_write_png_compression_level);
......@@ -1248,20 +1260,20 @@ STBIWDEF int stbi_write_png_to_func(stbi_write_func *func, void *context, int x,
*/
static const unsigned char stbiw__jpg_ZigZag[] = { 0,1,5,6,14,15,27,28,2,4,7,13,16,26,29,42,3,8,12,17,25,30,41,43,9,11,18,
24,31,40,44,53,10,19,23,32,39,45,52,54,20,22,33,38,46,51,55,60,21,34,37,47,50,56,59,61,35,36,48,49,57,58,62,63 };
24,31,40,44,53,10,19,23,32,39,45,52,54,20,22,33,38,46,51,55,60,21,34,37,47,50,56,59,61,35,36,48,49,57,58,62,63 };
static void stbiw__jpg_writeBits(stbi__write_context *s, int *bitBufP, int *bitCntP, const unsigned short *bs) {
int bitBuf = *bitBufP, bitCnt = *bitCntP;
bitCnt += bs[1];
bitBuf |= bs[0] << (24 - bitCnt);
while(bitCnt >= 8) {
unsigned char c = (bitBuf >> 16) & 255;
stbiw__putc(s, c);
if(c == 255) {
stbiw__putc(s, 0);
}
bitBuf <<= 8;
bitCnt -= 8;
unsigned char c = (bitBuf >> 16) & 255;
stbiw__putc(s, c);
if(c == 255) {
stbiw__putc(s, 0);
}
bitBuf <<= 8;
bitCnt -= 8;
}
*bitBufP = bitBuf;
*bitCntP = bitCnt;
......@@ -1320,7 +1332,7 @@ static void stbiw__jpg_calcBits(int val, unsigned short bits[2]) {
val = val < 0 ? val-1 : val;
bits[1] = 1;
while(tmp1 >>= 1) {
++bits[1];
++bits[1];
}
bits[0] = val & ((1<<bits[1])-1);
}
......@@ -1333,34 +1345,34 @@ static int stbiw__jpg_processDU(stbi__write_context *s, int *bitBuf, int *bitCnt
// DCT rows
for(dataOff=0, n=du_stride*8; dataOff<n; dataOff+=du_stride) {
stbiw__jpg_DCT(&CDU[dataOff], &CDU[dataOff+1], &CDU[dataOff+2], &CDU[dataOff+3], &CDU[dataOff+4], &CDU[dataOff+5], &CDU[dataOff+6], &CDU[dataOff+7]);
stbiw__jpg_DCT(&CDU[dataOff], &CDU[dataOff+1], &CDU[dataOff+2], &CDU[dataOff+3], &CDU[dataOff+4], &CDU[dataOff+5], &CDU[dataOff+6], &CDU[dataOff+7]);
}
// DCT columns
for(dataOff=0; dataOff<8; ++dataOff) {
stbiw__jpg_DCT(&CDU[dataOff], &CDU[dataOff+du_stride], &CDU[dataOff+du_stride*2], &CDU[dataOff+du_stride*3], &CDU[dataOff+du_stride*4],
&CDU[dataOff+du_stride*5], &CDU[dataOff+du_stride*6], &CDU[dataOff+du_stride*7]);
stbiw__jpg_DCT(&CDU[dataOff], &CDU[dataOff+du_stride], &CDU[dataOff+du_stride*2], &CDU[dataOff+du_stride*3], &CDU[dataOff+du_stride*4],
&CDU[dataOff+du_stride*5], &CDU[dataOff+du_stride*6], &CDU[dataOff+du_stride*7]);
}
// Quantize/descale/zigzag the coefficients
for(y = 0, j=0; y < 8; ++y) {
for(x = 0; x < 8; ++x,++j) {
float v;
i = y*du_stride+x;
v = CDU[i]*fdtbl[j];
// DU[stbiw__jpg_ZigZag[j]] = (int)(v < 0 ? ceilf(v - 0.5f) : floorf(v + 0.5f));
// ceilf() and floorf() are C99, not C89, but I /think/ they're not needed here anyway?
DU[stbiw__jpg_ZigZag[j]] = (int)(v < 0 ? v - 0.5f : v + 0.5f);
}
for(x = 0; x < 8; ++x,++j) {
float v;
i = y*du_stride+x;
v = CDU[i]*fdtbl[j];
// DU[stbiw__jpg_ZigZag[j]] = (int)(v < 0 ? ceilf(v - 0.5f) : floorf(v + 0.5f));
// ceilf() and floorf() are C99, not C89, but I /think/ they're not needed here anyway?
DU[stbiw__jpg_ZigZag[j]] = (int)(v < 0 ? v - 0.5f : v + 0.5f);
}
}
// Encode DC
diff = DU[0] - DC;
if (diff == 0) {
stbiw__jpg_writeBits(s, bitBuf, bitCnt, HTDC[0]);
stbiw__jpg_writeBits(s, bitBuf, bitCnt, HTDC[0]);
} else {
unsigned short bits[2];
stbiw__jpg_calcBits(diff, bits);
stbiw__jpg_writeBits(s, bitBuf, bitCnt, HTDC[bits[1]]);
stbiw__jpg_writeBits(s, bitBuf, bitCnt, bits);
unsigned short bits[2];
stbiw__jpg_calcBits(diff, bits);
stbiw__jpg_writeBits(s, bitBuf, bitCnt, HTDC[bits[1]]);
stbiw__jpg_writeBits(s, bitBuf, bitCnt, bits);
}
// Encode ACs
end0pos = 63;
......@@ -1368,29 +1380,29 @@ static int stbiw__jpg_processDU(stbi__write_context *s, int *bitBuf, int *bitCnt
}
// end0pos = first element in reverse order !=0
if(end0pos == 0) {
stbiw__jpg_writeBits(s, bitBuf, bitCnt, EOB);
return DU[0];
stbiw__jpg_writeBits(s, bitBuf, bitCnt, EOB);
return DU[0];
}
for(i = 1; i <= end0pos; ++i) {
int startpos = i;
int nrzeroes;
unsigned short bits[2];
for (; DU[i]==0 && i<=end0pos; ++i) {
}
nrzeroes = i-startpos;
if ( nrzeroes >= 16 ) {
int lng = nrzeroes>>4;
int nrmarker;
for (nrmarker=1; nrmarker <= lng; ++nrmarker)
stbiw__jpg_writeBits(s, bitBuf, bitCnt, M16zeroes);
nrzeroes &= 15;
}
stbiw__jpg_calcBits(DU[i], bits);
stbiw__jpg_writeBits(s, bitBuf, bitCnt, HTAC[(nrzeroes<<4)+bits[1]]);
stbiw__jpg_writeBits(s, bitBuf, bitCnt, bits);
int startpos = i;
int nrzeroes;
unsigned short bits[2];
for (; DU[i]==0 && i<=end0pos; ++i) {
}
nrzeroes = i-startpos;
if ( nrzeroes >= 16 ) {
int lng = nrzeroes>>4;
int nrmarker;
for (nrmarker=1; nrmarker <= lng; ++nrmarker)
stbiw__jpg_writeBits(s, bitBuf, bitCnt, M16zeroes);
nrzeroes &= 15;
}
stbiw__jpg_calcBits(DU[i], bits);
stbiw__jpg_writeBits(s, bitBuf, bitCnt, HTAC[(nrzeroes<<4)+bits[1]]);
stbiw__jpg_writeBits(s, bitBuf, bitCnt, bits);
}
if(end0pos != 63) {
stbiw__jpg_writeBits(s, bitBuf, bitCnt, EOB);
stbiw__jpg_writeBits(s, bitBuf, bitCnt, EOB);
}
return DU[0];
}
......@@ -1401,78 +1413,78 @@ static int stbi_write_jpg_core(stbi__write_context *s, int width, int height, in
static const unsigned char std_dc_luminance_values[] = {0,1,2,3,4,5,6,7,8,9,10,11};
static const unsigned char std_ac_luminance_nrcodes[] = {0,0,2,1,3,3,2,4,3,5,5,4,4,0,0,1,0x7d};
static const unsigned char std_ac_luminance_values[] = {
0x01,0x02,0x03,0x00,0x04,0x11,0x05,0x12,0x21,0x31,0x41,0x06,0x13,0x51,0x61,0x07,0x22,0x71,0x14,0x32,0x81,0x91,0xa1,0x08,
0x23,0x42,0xb1,0xc1,0x15,0x52,0xd1,0xf0,0x24,0x33,0x62,0x72,0x82,0x09,0x0a,0x16,0x17,0x18,0x19,0x1a,0x25,0x26,0x27,0x28,
0x29,0x2a,0x34,0x35,0x36,0x37,0x38,0x39,0x3a,0x43,0x44,0x45,0x46,0x47,0x48,0x49,0x4a,0x53,0x54,0x55,0x56,0x57,0x58,0x59,
0x5a,0x63,0x64,0x65,0x66,0x67,0x68,0x69,0x6a,0x73,0x74,0x75,0x76,0x77,0x78,0x79,0x7a,0x83,0x84,0x85,0x86,0x87,0x88,0x89,
0x8a,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9a,0xa2,0xa3,0xa4,0xa5,0xa6,0xa7,0xa8,0xa9,0xaa,0xb2,0xb3,0xb4,0xb5,0xb6,
0xb7,0xb8,0xb9,0xba,0xc2,0xc3,0xc4,0xc5,0xc6,0xc7,0xc8,0xc9,0xca,0xd2,0xd3,0xd4,0xd5,0xd6,0xd7,0xd8,0xd9,0xda,0xe1,0xe2,
0xe3,0xe4,0xe5,0xe6,0xe7,0xe8,0xe9,0xea,0xf1,0xf2,0xf3,0xf4,0xf5,0xf6,0xf7,0xf8,0xf9,0xfa
0x01,0x02,0x03,0x00,0x04,0x11,0x05,0x12,0x21,0x31,0x41,0x06,0x13,0x51,0x61,0x07,0x22,0x71,0x14,0x32,0x81,0x91,0xa1,0x08,
0x23,0x42,0xb1,0xc1,0x15,0x52,0xd1,0xf0,0x24,0x33,0x62,0x72,0x82,0x09,0x0a,0x16,0x17,0x18,0x19,0x1a,0x25,0x26,0x27,0x28,
0x29,0x2a,0x34,0x35,0x36,0x37,0x38,0x39,0x3a,0x43,0x44,0x45,0x46,0x47,0x48,0x49,0x4a,0x53,0x54,0x55,0x56,0x57,0x58,0x59,
0x5a,0x63,0x64,0x65,0x66,0x67,0x68,0x69,0x6a,0x73,0x74,0x75,0x76,0x77,0x78,0x79,0x7a,0x83,0x84,0x85,0x86,0x87,0x88,0x89,
0x8a,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9a,0xa2,0xa3,0xa4,0xa5,0xa6,0xa7,0xa8,0xa9,0xaa,0xb2,0xb3,0xb4,0xb5,0xb6,
0xb7,0xb8,0xb9,0xba,0xc2,0xc3,0xc4,0xc5,0xc6,0xc7,0xc8,0xc9,0xca,0xd2,0xd3,0xd4,0xd5,0xd6,0xd7,0xd8,0xd9,0xda,0xe1,0xe2,
0xe3,0xe4,0xe5,0xe6,0xe7,0xe8,0xe9,0xea,0xf1,0xf2,0xf3,0xf4,0xf5,0xf6,0xf7,0xf8,0xf9,0xfa
};
static const unsigned char std_dc_chrominance_nrcodes[] = {0,0,3,1,1,1,1,1,1,1,1,1,0,0,0,0,0};
static const unsigned char std_dc_chrominance_values[] = {0,1,2,3,4,5,6,7,8,9,10,11};
static const unsigned char std_ac_chrominance_nrcodes[] = {0,0,2,1,2,4,4,3,4,7,5,4,4,0,1,2,0x77};
static const unsigned char std_ac_chrominance_values[] = {
0x00,0x01,0x02,0x03,0x11,0x04,0x05,0x21,0x31,0x06,0x12,0x41,0x51,0x07,0x61,0x71,0x13,0x22,0x32,0x81,0x08,0x14,0x42,0x91,
0xa1,0xb1,0xc1,0x09,0x23,0x33,0x52,0xf0,0x15,0x62,0x72,0xd1,0x0a,0x16,0x24,0x34,0xe1,0x25,0xf1,0x17,0x18,0x19,0x1a,0x26,
0x27,0x28,0x29,0x2a,0x35,0x36,0x37,0x38,0x39,0x3a,0x43,0x44,0x45,0x46,0x47,0x48,0x49,0x4a,0x53,0x54,0x55,0x56,0x57,0x58,
0x59,0x5a,0x63,0x64,0x65,0x66,0x67,0x68,0x69,0x6a,0x73,0x74,0x75,0x76,0x77,0x78,0x79,0x7a,0x82,0x83,0x84,0x85,0x86,0x87,
0x88,0x89,0x8a,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9a,0xa2,0xa3,0xa4,0xa5,0xa6,0xa7,0xa8,0xa9,0xaa,0xb2,0xb3,0xb4,
0xb5,0xb6,0xb7,0xb8,0xb9,0xba,0xc2,0xc3,0xc4,0xc5,0xc6,0xc7,0xc8,0xc9,0xca,0xd2,0xd3,0xd4,0xd5,0xd6,0xd7,0xd8,0xd9,0xda,
0xe2,0xe3,0xe4,0xe5,0xe6,0xe7,0xe8,0xe9,0xea,0xf2,0xf3,0xf4,0xf5,0xf6,0xf7,0xf8,0xf9,0xfa
0x00,0x01,0x02,0x03,0x11,0x04,0x05,0x21,0x31,0x06,0x12,0x41,0x51,0x07,0x61,0x71,0x13,0x22,0x32,0x81,0x08,0x14,0x42,0x91,
0xa1,0xb1,0xc1,0x09,0x23,0x33,0x52,0xf0,0x15,0x62,0x72,0xd1,0x0a,0x16,0x24,0x34,0xe1,0x25,0xf1,0x17,0x18,0x19,0x1a,0x26,
0x27,0x28,0x29,0x2a,0x35,0x36,0x37,0x38,0x39,0x3a,0x43,0x44,0x45,0x46,0x47,0x48,0x49,0x4a,0x53,0x54,0x55,0x56,0x57,0x58,
0x59,0x5a,0x63,0x64,0x65,0x66,0x67,0x68,0x69,0x6a,0x73,0x74,0x75,0x76,0x77,0x78,0x79,0x7a,0x82,0x83,0x84,0x85,0x86,0x87,
0x88,0x89,0x8a,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9a,0xa2,0xa3,0xa4,0xa5,0xa6,0xa7,0xa8,0xa9,0xaa,0xb2,0xb3,0xb4,
0xb5,0xb6,0xb7,0xb8,0xb9,0xba,0xc2,0xc3,0xc4,0xc5,0xc6,0xc7,0xc8,0xc9,0xca,0xd2,0xd3,0xd4,0xd5,0xd6,0xd7,0xd8,0xd9,0xda,
0xe2,0xe3,0xe4,0xe5,0xe6,0xe7,0xe8,0xe9,0xea,0xf2,0xf3,0xf4,0xf5,0xf6,0xf7,0xf8,0xf9,0xfa
};
// Huffman tables
static const unsigned short YDC_HT[256][2] = { {0,2},{2,3},{3,3},{4,3},{5,3},{6,3},{14,4},{30,5},{62,6},{126,7},{254,8},{510,9}};
static const unsigned short UVDC_HT[256][2] = { {0,2},{1,2},{2,2},{6,3},{14,4},{30,5},{62,6},{126,7},{254,8},{510,9},{1022,10},{2046,11}};
static const unsigned short YAC_HT[256][2] = {
{10,4},{0,2},{1,2},{4,3},{11,4},{26,5},{120,7},{248,8},{1014,10},{65410,16},{65411,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},
{12,4},{27,5},{121,7},{502,9},{2038,11},{65412,16},{65413,16},{65414,16},{65415,16},{65416,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},
{28,5},{249,8},{1015,10},{4084,12},{65417,16},{65418,16},{65419,16},{65420,16},{65421,16},{65422,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},
{58,6},{503,9},{4085,12},{65423,16},{65424,16},{65425,16},{65426,16},{65427,16},{65428,16},{65429,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},
{59,6},{1016,10},{65430,16},{65431,16},{65432,16},{65433,16},{65434,16},{65435,16},{65436,16},{65437,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},
{122,7},{2039,11},{65438,16},{65439,16},{65440,16},{65441,16},{65442,16},{65443,16},{65444,16},{65445,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},
{123,7},{4086,12},{65446,16},{65447,16},{65448,16},{65449,16},{65450,16},{65451,16},{65452,16},{65453,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},
{250,8},{4087,12},{65454,16},{65455,16},{65456,16},{65457,16},{65458,16},{65459,16},{65460,16},{65461,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},
{504,9},{32704,15},{65462,16},{65463,16},{65464,16},{65465,16},{65466,16},{65467,16},{65468,16},{65469,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},
{505,9},{65470,16},{65471,16},{65472,16},{65473,16},{65474,16},{65475,16},{65476,16},{65477,16},{65478,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},
{506,9},{65479,16},{65480,16},{65481,16},{65482,16},{65483,16},{65484,16},{65485,16},{65486,16},{65487,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},
{1017,10},{65488,16},{65489,16},{65490,16},{65491,16},{65492,16},{65493,16},{65494,16},{65495,16},{65496,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},
{1018,10},{65497,16},{65498,16},{65499,16},{65500,16},{65501,16},{65502,16},{65503,16},{65504,16},{65505,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},
{2040,11},{65506,16},{65507,16},{65508,16},{65509,16},{65510,16},{65511,16},{65512,16},{65513,16},{65514,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},
{65515,16},{65516,16},{65517,16},{65518,16},{65519,16},{65520,16},{65521,16},{65522,16},{65523,16},{65524,16},{0,0},{0,0},{0,0},{0,0},{0,0},
{2041,11},{65525,16},{65526,16},{65527,16},{65528,16},{65529,16},{65530,16},{65531,16},{65532,16},{65533,16},{65534,16},{0,0},{0,0},{0,0},{0,0},{0,0}
{10,4},{0,2},{1,2},{4,3},{11,4},{26,5},{120,7},{248,8},{1014,10},{65410,16},{65411,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},
{12,4},{27,5},{121,7},{502,9},{2038,11},{65412,16},{65413,16},{65414,16},{65415,16},{65416,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},
{28,5},{249,8},{1015,10},{4084,12},{65417,16},{65418,16},{65419,16},{65420,16},{65421,16},{65422,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},
{58,6},{503,9},{4085,12},{65423,16},{65424,16},{65425,16},{65426,16},{65427,16},{65428,16},{65429,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},
{59,6},{1016,10},{65430,16},{65431,16},{65432,16},{65433,16},{65434,16},{65435,16},{65436,16},{65437,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},
{122,7},{2039,11},{65438,16},{65439,16},{65440,16},{65441,16},{65442,16},{65443,16},{65444,16},{65445,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},
{123,7},{4086,12},{65446,16},{65447,16},{65448,16},{65449,16},{65450,16},{65451,16},{65452,16},{65453,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},
{250,8},{4087,12},{65454,16},{65455,16},{65456,16},{65457,16},{65458,16},{65459,16},{65460,16},{65461,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},
{504,9},{32704,15},{65462,16},{65463,16},{65464,16},{65465,16},{65466,16},{65467,16},{65468,16},{65469,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},
{505,9},{65470,16},{65471,16},{65472,16},{65473,16},{65474,16},{65475,16},{65476,16},{65477,16},{65478,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},
{506,9},{65479,16},{65480,16},{65481,16},{65482,16},{65483,16},{65484,16},{65485,16},{65486,16},{65487,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},
{1017,10},{65488,16},{65489,16},{65490,16},{65491,16},{65492,16},{65493,16},{65494,16},{65495,16},{65496,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},
{1018,10},{65497,16},{65498,16},{65499,16},{65500,16},{65501,16},{65502,16},{65503,16},{65504,16},{65505,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},
{2040,11},{65506,16},{65507,16},{65508,16},{65509,16},{65510,16},{65511,16},{65512,16},{65513,16},{65514,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},
{65515,16},{65516,16},{65517,16},{65518,16},{65519,16},{65520,16},{65521,16},{65522,16},{65523,16},{65524,16},{0,0},{0,0},{0,0},{0,0},{0,0},
{2041,11},{65525,16},{65526,16},{65527,16},{65528,16},{65529,16},{65530,16},{65531,16},{65532,16},{65533,16},{65534,16},{0,0},{0,0},{0,0},{0,0},{0,0}
};
static const unsigned short UVAC_HT[256][2] = {
{0,2},{1,2},{4,3},{10,4},{24,5},{25,5},{56,6},{120,7},{500,9},{1014,10},{4084,12},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},
{11,4},{57,6},{246,8},{501,9},{2038,11},{4085,12},{65416,16},{65417,16},{65418,16},{65419,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},
{26,5},{247,8},{1015,10},{4086,12},{32706,15},{65420,16},{65421,16},{65422,16},{65423,16},{65424,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},
{27,5},{248,8},{1016,10},{4087,12},{65425,16},{65426,16},{65427,16},{65428,16},{65429,16},{65430,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},
{58,6},{502,9},{65431,16},{65432,16},{65433,16},{65434,16},{65435,16},{65436,16},{65437,16},{65438,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},
{59,6},{1017,10},{65439,16},{65440,16},{65441,16},{65442,16},{65443,16},{65444,16},{65445,16},{65446,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},
{121,7},{2039,11},{65447,16},{65448,16},{65449,16},{65450,16},{65451,16},{65452,16},{65453,16},{65454,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},
{122,7},{2040,11},{65455,16},{65456,16},{65457,16},{65458,16},{65459,16},{65460,16},{65461,16},{65462,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},
{249,8},{65463,16},{65464,16},{65465,16},{65466,16},{65467,16},{65468,16},{65469,16},{65470,16},{65471,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},
{503,9},{65472,16},{65473,16},{65474,16},{65475,16},{65476,16},{65477,16},{65478,16},{65479,16},{65480,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},
{504,9},{65481,16},{65482,16},{65483,16},{65484,16},{65485,16},{65486,16},{65487,16},{65488,16},{65489,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},
{505,9},{65490,16},{65491,16},{65492,16},{65493,16},{65494,16},{65495,16},{65496,16},{65497,16},{65498,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},
{506,9},{65499,16},{65500,16},{65501,16},{65502,16},{65503,16},{65504,16},{65505,16},{65506,16},{65507,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},
{2041,11},{65508,16},{65509,16},{65510,16},{65511,16},{65512,16},{65513,16},{65514,16},{65515,16},{65516,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},
{16352,14},{65517,16},{65518,16},{65519,16},{65520,16},{65521,16},{65522,16},{65523,16},{65524,16},{65525,16},{0,0},{0,0},{0,0},{0,0},{0,0},
{1018,10},{32707,15},{65526,16},{65527,16},{65528,16},{65529,16},{65530,16},{65531,16},{65532,16},{65533,16},{65534,16},{0,0},{0,0},{0,0},{0,0},{0,0}
{0,2},{1,2},{4,3},{10,4},{24,5},{25,5},{56,6},{120,7},{500,9},{1014,10},{4084,12},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},
{11,4},{57,6},{246,8},{501,9},{2038,11},{4085,12},{65416,16},{65417,16},{65418,16},{65419,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},
{26,5},{247,8},{1015,10},{4086,12},{32706,15},{65420,16},{65421,16},{65422,16},{65423,16},{65424,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},
{27,5},{248,8},{1016,10},{4087,12},{65425,16},{65426,16},{65427,16},{65428,16},{65429,16},{65430,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},
{58,6},{502,9},{65431,16},{65432,16},{65433,16},{65434,16},{65435,16},{65436,16},{65437,16},{65438,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},
{59,6},{1017,10},{65439,16},{65440,16},{65441,16},{65442,16},{65443,16},{65444,16},{65445,16},{65446,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},
{121,7},{2039,11},{65447,16},{65448,16},{65449,16},{65450,16},{65451,16},{65452,16},{65453,16},{65454,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},
{122,7},{2040,11},{65455,16},{65456,16},{65457,16},{65458,16},{65459,16},{65460,16},{65461,16},{65462,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},
{249,8},{65463,16},{65464,16},{65465,16},{65466,16},{65467,16},{65468,16},{65469,16},{65470,16},{65471,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},
{503,9},{65472,16},{65473,16},{65474,16},{65475,16},{65476,16},{65477,16},{65478,16},{65479,16},{65480,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},
{504,9},{65481,16},{65482,16},{65483,16},{65484,16},{65485,16},{65486,16},{65487,16},{65488,16},{65489,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},
{505,9},{65490,16},{65491,16},{65492,16},{65493,16},{65494,16},{65495,16},{65496,16},{65497,16},{65498,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},
{506,9},{65499,16},{65500,16},{65501,16},{65502,16},{65503,16},{65504,16},{65505,16},{65506,16},{65507,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},
{2041,11},{65508,16},{65509,16},{65510,16},{65511,16},{65512,16},{65513,16},{65514,16},{65515,16},{65516,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},
{16352,14},{65517,16},{65518,16},{65519,16},{65520,16},{65521,16},{65522,16},{65523,16},{65524,16},{65525,16},{0,0},{0,0},{0,0},{0,0},{0,0},
{1018,10},{32707,15},{65526,16},{65527,16},{65528,16},{65529,16},{65530,16},{65531,16},{65532,16},{65533,16},{65534,16},{0,0},{0,0},{0,0},{0,0},{0,0}
};
static const int YQT[] = {16,11,10,16,24,40,51,61,12,12,14,19,26,58,60,55,14,13,16,24,40,57,69,56,14,17,22,29,51,87,80,62,18,22,
37,56,68,109,103,77,24,35,55,64,81,104,113,92,49,64,78,87,103,121,120,101,72,92,95,98,112,100,103,99};
37,56,68,109,103,77,24,35,55,64,81,104,113,92,49,64,78,87,103,121,120,101,72,92,95,98,112,100,103,99};
static const int UVQT[] = {17,18,24,47,99,99,99,99,18,21,26,66,99,99,99,99,24,26,56,99,99,99,99,99,47,66,99,99,99,99,99,99,
99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99};
99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99};
static const float aasf[] = { 1.0f * 2.828427125f, 1.387039845f * 2.828427125f, 1.306562965f * 2.828427125f, 1.175875602f * 2.828427125f,
1.0f * 2.828427125f, 0.785694958f * 2.828427125f, 0.541196100f * 2.828427125f, 0.275899379f * 2.828427125f };
1.0f * 2.828427125f, 0.785694958f * 2.828427125f, 0.541196100f * 2.828427125f, 0.275899379f * 2.828427125f };
int row, col, i, k, subsample;
float fdtbl_Y[64], fdtbl_UV[64];
unsigned char YTable[64], UVTable[64];
if(!data || !width || !height || comp > 4 || comp < 1) {
return 0;
return 0;
}
quality = quality ? quality : 90;
......@@ -1481,120 +1493,120 @@ static int stbi_write_jpg_core(stbi__write_context *s, int width, int height, in
quality = quality < 50 ? 5000 / quality : 200 - quality * 2;
for(i = 0; i < 64; ++i) {
int uvti, yti = (YQT[i]*quality+50)/100;
YTable[stbiw__jpg_ZigZag[i]] = (unsigned char) (yti < 1 ? 1 : yti > 255 ? 255 : yti);
uvti = (UVQT[i]*quality+50)/100;
UVTable[stbiw__jpg_ZigZag[i]] = (unsigned char) (uvti < 1 ? 1 : uvti > 255 ? 255 : uvti);
int uvti, yti = (YQT[i]*quality+50)/100;
YTable[stbiw__jpg_ZigZag[i]] = (unsigned char) (yti < 1 ? 1 : yti > 255 ? 255 : yti);
uvti = (UVQT[i]*quality+50)/100;
UVTable[stbiw__jpg_ZigZag[i]] = (unsigned char) (uvti < 1 ? 1 : uvti > 255 ? 255 : uvti);
}
for(row = 0, k = 0; row < 8; ++row) {
for(col = 0; col < 8; ++col, ++k) {
fdtbl_Y[k] = 1 / (YTable [stbiw__jpg_ZigZag[k]] * aasf[row] * aasf[col]);
fdtbl_UV[k] = 1 / (UVTable[stbiw__jpg_ZigZag[k]] * aasf[row] * aasf[col]);
}
for(col = 0; col < 8; ++col, ++k) {
fdtbl_Y[k] = 1 / (YTable [stbiw__jpg_ZigZag[k]] * aasf[row] * aasf[col]);
fdtbl_UV[k] = 1 / (UVTable[stbiw__jpg_ZigZag[k]] * aasf[row] * aasf[col]);
}
}
// Write Headers
{
static const unsigned char head0[] = { 0xFF,0xD8,0xFF,0xE0,0,0x10,'J','F','I','F',0,1,1,0,0,1,0,1,0,0,0xFF,0xDB,0,0x84,0 };
static const unsigned char head2[] = { 0xFF,0xDA,0,0xC,3,1,0,2,0x11,3,0x11,0,0x3F,0 };
const unsigned char head1[] = { 0xFF,0xC0,0,0x11,8,(unsigned char)(height>>8),STBIW_UCHAR(height),(unsigned char)(width>>8),STBIW_UCHAR(width),
3,1,(unsigned char)(subsample?0x22:0x11),0,2,0x11,1,3,0x11,1,0xFF,0xC4,0x01,0xA2,0 };
s->func(s->context, (void*)head0, sizeof(head0));
s->func(s->context, (void*)YTable, sizeof(YTable));
stbiw__putc(s, 1);
s->func(s->context, UVTable, sizeof(UVTable));
s->func(s->context, (void*)head1, sizeof(head1));
s->func(s->context, (void*)(std_dc_luminance_nrcodes+1), sizeof(std_dc_luminance_nrcodes)-1);
s->func(s->context, (void*)std_dc_luminance_values, sizeof(std_dc_luminance_values));
stbiw__putc(s, 0x10); // HTYACinfo
s->func(s->context, (void*)(std_ac_luminance_nrcodes+1), sizeof(std_ac_luminance_nrcodes)-1);
s->func(s->context, (void*)std_ac_luminance_values, sizeof(std_ac_luminance_values));
stbiw__putc(s, 1); // HTUDCinfo
s->func(s->context, (void*)(std_dc_chrominance_nrcodes+1), sizeof(std_dc_chrominance_nrcodes)-1);
s->func(s->context, (void*)std_dc_chrominance_values, sizeof(std_dc_chrominance_values));
stbiw__putc(s, 0x11); // HTUACinfo
s->func(s->context, (void*)(std_ac_chrominance_nrcodes+1), sizeof(std_ac_chrominance_nrcodes)-1);
s->func(s->context, (void*)std_ac_chrominance_values, sizeof(std_ac_chrominance_values));
s->func(s->context, (void*)head2, sizeof(head2));
static const unsigned char head0[] = { 0xFF,0xD8,0xFF,0xE0,0,0x10,'J','F','I','F',0,1,1,0,0,1,0,1,0,0,0xFF,0xDB,0,0x84,0 };
static const unsigned char head2[] = { 0xFF,0xDA,0,0xC,3,1,0,2,0x11,3,0x11,0,0x3F,0 };
const unsigned char head1[] = { 0xFF,0xC0,0,0x11,8,(unsigned char)(height>>8),STBIW_UCHAR(height),(unsigned char)(width>>8),STBIW_UCHAR(width),
3,1,(unsigned char)(subsample?0x22:0x11),0,2,0x11,1,3,0x11,1,0xFF,0xC4,0x01,0xA2,0 };
s->func(s->context, (void*)head0, sizeof(head0));
s->func(s->context, (void*)YTable, sizeof(YTable));
stbiw__putc(s, 1);
s->func(s->context, UVTable, sizeof(UVTable));
s->func(s->context, (void*)head1, sizeof(head1));
s->func(s->context, (void*)(std_dc_luminance_nrcodes+1), sizeof(std_dc_luminance_nrcodes)-1);
s->func(s->context, (void*)std_dc_luminance_values, sizeof(std_dc_luminance_values));
stbiw__putc(s, 0x10); // HTYACinfo
s->func(s->context, (void*)(std_ac_luminance_nrcodes+1), sizeof(std_ac_luminance_nrcodes)-1);
s->func(s->context, (void*)std_ac_luminance_values, sizeof(std_ac_luminance_values));
stbiw__putc(s, 1); // HTUDCinfo
s->func(s->context, (void*)(std_dc_chrominance_nrcodes+1), sizeof(std_dc_chrominance_nrcodes)-1);
s->func(s->context, (void*)std_dc_chrominance_values, sizeof(std_dc_chrominance_values));
stbiw__putc(s, 0x11); // HTUACinfo
s->func(s->context, (void*)(std_ac_chrominance_nrcodes+1), sizeof(std_ac_chrominance_nrcodes)-1);
s->func(s->context, (void*)std_ac_chrominance_values, sizeof(std_ac_chrominance_values));
s->func(s->context, (void*)head2, sizeof(head2));
}
// Encode 8x8 macroblocks
{
static const unsigned short fillBits[] = {0x7F, 7};
int DCY=0, DCU=0, DCV=0;
int bitBuf=0, bitCnt=0;
// comp == 2 is grey+alpha (alpha is ignored)
int ofsG = comp > 2 ? 1 : 0, ofsB = comp > 2 ? 2 : 0;
const unsigned char *dataR = (const unsigned char *)data;
const unsigned char *dataG = dataR + ofsG;
const unsigned char *dataB = dataR + ofsB;
int x, y, pos;
if(subsample) {
for(y = 0; y < height; y += 16) {
for(x = 0; x < width; x += 16) {
float Y[256], U[256], V[256];
for(row = y, pos = 0; row < y+16; ++row) {
// row >= height => use last input row
int clamped_row = (row < height) ? row : height - 1;
int base_p = (stbi__flip_vertically_on_write ? (height-1-clamped_row) : clamped_row)*width*comp;
for(col = x; col < x+16; ++col, ++pos) {
// if col >= width => use pixel from last input column
int p = base_p + ((col < width) ? col : (width-1))*comp;
float r = dataR[p], g = dataG[p], b = dataB[p];
Y[pos]= +0.29900f*r + 0.58700f*g + 0.11400f*b - 128;
U[pos]= -0.16874f*r - 0.33126f*g + 0.50000f*b;
V[pos]= +0.50000f*r - 0.41869f*g - 0.08131f*b;
}
}
DCY = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, Y+0, 16, fdtbl_Y, DCY, YDC_HT, YAC_HT);
DCY = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, Y+8, 16, fdtbl_Y, DCY, YDC_HT, YAC_HT);
DCY = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, Y+128, 16, fdtbl_Y, DCY, YDC_HT, YAC_HT);
DCY = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, Y+136, 16, fdtbl_Y, DCY, YDC_HT, YAC_HT);
// subsample U,V
{
float subU[64], subV[64];
int yy, xx;
for(yy = 0, pos = 0; yy < 8; ++yy) {
for(xx = 0; xx < 8; ++xx, ++pos) {
int j = yy*32+xx*2;
subU[pos] = (U[j+0] + U[j+1] + U[j+16] + U[j+17]) * 0.25f;
subV[pos] = (V[j+0] + V[j+1] + V[j+16] + V[j+17]) * 0.25f;
}
}
DCU = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, subU, 8, fdtbl_UV, DCU, UVDC_HT, UVAC_HT);
DCV = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, subV, 8, fdtbl_UV, DCV, UVDC_HT, UVAC_HT);
}
}
}
} else {
for(y = 0; y < height; y += 8) {
for(x = 0; x < width; x += 8) {
float Y[64], U[64], V[64];
for(row = y, pos = 0; row < y+8; ++row) {
// row >= height => use last input row
int clamped_row = (row < height) ? row : height - 1;
int base_p = (stbi__flip_vertically_on_write ? (height-1-clamped_row) : clamped_row)*width*comp;
for(col = x; col < x+8; ++col, ++pos) {
// if col >= width => use pixel from last input column
int p = base_p + ((col < width) ? col : (width-1))*comp;
float r = dataR[p], g = dataG[p], b = dataB[p];
Y[pos]= +0.29900f*r + 0.58700f*g + 0.11400f*b - 128;
U[pos]= -0.16874f*r - 0.33126f*g + 0.50000f*b;
V[pos]= +0.50000f*r - 0.41869f*g - 0.08131f*b;
}
}
DCY = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, Y, 8, fdtbl_Y, DCY, YDC_HT, YAC_HT);
DCU = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, U, 8, fdtbl_UV, DCU, UVDC_HT, UVAC_HT);
DCV = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, V, 8, fdtbl_UV, DCV, UVDC_HT, UVAC_HT);
}
}
}
// Do the bit alignment of the EOI marker
stbiw__jpg_writeBits(s, &bitBuf, &bitCnt, fillBits);
static const unsigned short fillBits[] = {0x7F, 7};
int DCY=0, DCU=0, DCV=0;
int bitBuf=0, bitCnt=0;
// comp == 2 is grey+alpha (alpha is ignored)
int ofsG = comp > 2 ? 1 : 0, ofsB = comp > 2 ? 2 : 0;
const unsigned char *dataR = (const unsigned char *)data;
const unsigned char *dataG = dataR + ofsG;
const unsigned char *dataB = dataR + ofsB;
int x, y, pos;
if(subsample) {
for(y = 0; y < height; y += 16) {
for(x = 0; x < width; x += 16) {
float Y[256], U[256], V[256];
for(row = y, pos = 0; row < y+16; ++row) {
// row >= height => use last input row
int clamped_row = (row < height) ? row : height - 1;
int base_p = (stbi__flip_vertically_on_write ? (height-1-clamped_row) : clamped_row)*width*comp;
for(col = x; col < x+16; ++col, ++pos) {
// if col >= width => use pixel from last input column
int p = base_p + ((col < width) ? col : (width-1))*comp;
float r = dataR[p], g = dataG[p], b = dataB[p];
Y[pos]= +0.29900f*r + 0.58700f*g + 0.11400f*b - 128;
U[pos]= -0.16874f*r - 0.33126f*g + 0.50000f*b;
V[pos]= +0.50000f*r - 0.41869f*g - 0.08131f*b;
}
}
DCY = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, Y+0, 16, fdtbl_Y, DCY, YDC_HT, YAC_HT);
DCY = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, Y+8, 16, fdtbl_Y, DCY, YDC_HT, YAC_HT);
DCY = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, Y+128, 16, fdtbl_Y, DCY, YDC_HT, YAC_HT);
DCY = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, Y+136, 16, fdtbl_Y, DCY, YDC_HT, YAC_HT);
// subsample U,V
{
float subU[64], subV[64];
int yy, xx;
for(yy = 0, pos = 0; yy < 8; ++yy) {
for(xx = 0; xx < 8; ++xx, ++pos) {
int j = yy*32+xx*2;
subU[pos] = (U[j+0] + U[j+1] + U[j+16] + U[j+17]) * 0.25f;
subV[pos] = (V[j+0] + V[j+1] + V[j+16] + V[j+17]) * 0.25f;
}
}
DCU = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, subU, 8, fdtbl_UV, DCU, UVDC_HT, UVAC_HT);
DCV = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, subV, 8, fdtbl_UV, DCV, UVDC_HT, UVAC_HT);
}
}
}
} else {
for(y = 0; y < height; y += 8) {
for(x = 0; x < width; x += 8) {
float Y[64], U[64], V[64];
for(row = y, pos = 0; row < y+8; ++row) {
// row >= height => use last input row
int clamped_row = (row < height) ? row : height - 1;
int base_p = (stbi__flip_vertically_on_write ? (height-1-clamped_row) : clamped_row)*width*comp;
for(col = x; col < x+8; ++col, ++pos) {
// if col >= width => use pixel from last input column
int p = base_p + ((col < width) ? col : (width-1))*comp;
float r = dataR[p], g = dataG[p], b = dataB[p];
Y[pos]= +0.29900f*r + 0.58700f*g + 0.11400f*b - 128;
U[pos]= -0.16874f*r - 0.33126f*g + 0.50000f*b;
V[pos]= +0.50000f*r - 0.41869f*g - 0.08131f*b;
}
}
DCY = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, Y, 8, fdtbl_Y, DCY, YDC_HT, YAC_HT);
DCU = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, U, 8, fdtbl_UV, DCU, UVDC_HT, UVAC_HT);
DCV = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, V, 8, fdtbl_UV, DCV, UVDC_HT, UVAC_HT);
}
}
}
// Do the bit alignment of the EOI marker
stbiw__jpg_writeBits(s, &bitBuf, &bitCnt, fillBits);
}
// EOI
......@@ -1617,11 +1629,11 @@ STBIWDEF int stbi_write_jpg(char const *filename, int x, int y, int comp, const
{
stbi__write_context s = { 0 };
if (stbi__start_write_file(&s,filename)) {
int r = stbi_write_jpg_core(&s, x, y, comp, data, quality);
stbi__end_write_file(&s);
return r;
int r = stbi_write_jpg_core(&s, x, y, comp, data, quality);
stbi__end_write_file(&s);
return r;
} else
return 0;
return 0;
}
#endif
......