diff --git a/source/core/CMakeLists.txt b/source/core/CMakeLists.txt index fa1d246..6deb473 100644 --- a/source/core/CMakeLists.txt +++ b/source/core/CMakeLists.txt @@ -41,6 +41,7 @@ SET (star_core_HEADERS StarIdMap.hpp StarImage.hpp StarImageProcessing.hpp + StarImageScaling.hpp StarInputEvent.hpp StarInterpolation.hpp StarRefPtr.hpp @@ -152,6 +153,7 @@ SET (star_core_SOURCES StarIODevice.cpp StarImage.cpp StarImageProcessing.cpp + StarImageScaling.cpp StarInputEvent.cpp StarJson.cpp StarJsonBuilder.cpp @@ -229,6 +231,10 @@ IF(STAR_PRECOMPILED_HEADERS) TARGET_PRECOMPILE_HEADERS (star_core PUBLIC StarPch.hpp) ENDIF() +IF(STAR_COMPILER STREQUAL "gnu") + SET_SOURCE_FILES_PROPERTIES(StarImageScaling.cpp PROPERTIES COMPILE_FLAGS "-O2") +ENDIF() + IF(STAR_USE_JEMALLOC AND JEMALLOC_IS_PREFIXED) SET_SOURCE_FILES_PROPERTIES(StarMemory.cpp PROPERTIES COMPILE_DEFINITIONS STAR_JEMALLOC_IS_PREFIXED diff --git a/source/core/StarImageProcessing.cpp b/source/core/StarImageProcessing.cpp index 96edc0b..1cfd847 100644 --- a/source/core/StarImageProcessing.cpp +++ b/source/core/StarImageProcessing.cpp @@ -1,4 +1,5 @@ #include "StarImageProcessing.hpp" +#include "StarImageScaling.hpp" #include "StarMatrix3.hpp" #include "StarInterpolation.hpp" #include "StarLexicalCast.hpp" @@ -10,104 +11,6 @@ namespace Star { -Image scaleNearest(Image const& srcImage, Vec2F const& scale) { - Vec2U srcSize = srcImage.size(); - Vec2U destSize = Vec2U::round(vmult(Vec2F(srcSize), scale)); - destSize[0] = max(destSize[0], 1u); - destSize[1] = max(destSize[1], 1u); - - Image destImage(destSize, srcImage.pixelFormat()); - - for (unsigned y = 0; y < destSize[1]; ++y) { - for (unsigned x = 0; x < destSize[0]; ++x) - destImage.set({x, y}, srcImage.clamp(Vec2I::round(vdiv(Vec2F(x, y), scale)))); - } - return destImage; -} - -#pragma GCC push_options -#pragma GCC optimize("-fno-unsafe-math-optimizations", "-ffloat-store") -Image scaleBilinear(Image const& srcImage, Vec2F const& scale) { - Vec2U srcSize = srcImage.size(); - Vec2U destSize = Vec2U::round(vmult(Vec2F(srcSize), scale)); - destSize[0] = max(destSize[0], 1u); - destSize[1] = max(destSize[1], 1u); - - Image destImage(destSize, srcImage.pixelFormat()); - - auto lerpVec = [](float const& offset, Vec4F f0, Vec4F f1) { - return f0 * (1 - offset) + f1 * (offset); - }; - - for (unsigned y = 0; y < destSize[1]; ++y) { - for (unsigned x = 0; x < destSize[0]; ++x) { - auto pos = vdiv(Vec2F(x, y), scale); - auto ipart = Vec2I::floor(pos); - auto fpart = pos - Vec2F(ipart); - - auto result = lerpVec(fpart[1], lerpVec(fpart[0], Vec4F(srcImage.clamp(ipart[0], ipart[1])), Vec4F(srcImage.clamp(ipart[0] + 1, ipart[1]))), lerpVec(fpart[0], - Vec4F(srcImage.clamp(ipart[0], ipart[1] + 1)), Vec4F(srcImage.clamp(ipart[0] + 1, ipart[1] + 1)))); - - destImage.set({x, y}, Vec4B(result)); - } - } - - return destImage; -} - -Image scaleBicubic(Image const& srcImage, Vec2F const& scale) { - Vec2U srcSize = srcImage.size(); - Vec2U destSize = Vec2U::round(vmult(Vec2F(srcSize), scale)); - destSize[0] = max(destSize[0], 1u); - destSize[1] = max(destSize[1], 1u); - - Image destImage(destSize, srcImage.pixelFormat()); - - for (unsigned y = 0; y < destSize[1]; ++y) { - for (unsigned x = 0; x < destSize[0]; ++x) { - auto pos = vdiv(Vec2F(x, y), scale); - auto ipart = Vec2I::floor(pos); - auto fpart = pos - Vec2F(ipart); - - Vec4F a = cubic4(fpart[0], - Vec4F(srcImage.clamp(ipart[0], ipart[1])), - Vec4F(srcImage.clamp(ipart[0] + 1, ipart[1])), - Vec4F(srcImage.clamp(ipart[0] + 2, ipart[1])), - Vec4F(srcImage.clamp(ipart[0] + 3, ipart[1]))); - - Vec4F b = cubic4(fpart[0], - Vec4F(srcImage.clamp(ipart[0], ipart[1] + 1)), - Vec4F(srcImage.clamp(ipart[0] + 1, ipart[1] + 1)), - Vec4F(srcImage.clamp(ipart[0] + 2, ipart[1] + 1)), - Vec4F(srcImage.clamp(ipart[0] + 3, ipart[1] + 1))); - - Vec4F c = cubic4(fpart[0], - Vec4F(srcImage.clamp(ipart[0], ipart[1] + 2)), - Vec4F(srcImage.clamp(ipart[0] + 1, ipart[1] + 2)), - Vec4F(srcImage.clamp(ipart[0] + 2, ipart[1] + 2)), - Vec4F(srcImage.clamp(ipart[0] + 3, ipart[1] + 2))); - - Vec4F d = cubic4(fpart[0], - Vec4F(srcImage.clamp(ipart[0], ipart[1] + 3)), - Vec4F(srcImage.clamp(ipart[0] + 1, ipart[1] + 3)), - Vec4F(srcImage.clamp(ipart[0] + 2, ipart[1] + 3)), - Vec4F(srcImage.clamp(ipart[0] + 3, ipart[1] + 3))); - - auto result = cubic4(fpart[1], a, b, c, d); - - destImage.set({x, y}, Vec4B( - clamp(result[0], 0.0f, 255.0f), - clamp(result[1], 0.0f, 255.0f), - clamp(result[2], 0.0f, 255.0f), - clamp(result[3], 0.0f, 255.0f) - )); - } - } - - return destImage; -} -#pragma GCC pop_options - StringList colorDirectivesFromConfig(JsonArray const& directives) { List result; diff --git a/source/core/StarImageProcessing.hpp b/source/core/StarImageProcessing.hpp index 8316d18..dfc9420 100644 --- a/source/core/StarImageProcessing.hpp +++ b/source/core/StarImageProcessing.hpp @@ -10,10 +10,6 @@ STAR_CLASS(Image); STAR_EXCEPTION(ImageOperationException, StarException); -Image scaleNearest(Image const& srcImage, Vec2F const& scale); -Image scaleBilinear(Image const& srcImage, Vec2F const& scale); -Image scaleBicubic(Image const& srcImage, Vec2F const& scale); - StringList colorDirectivesFromConfig(JsonArray const& directives); String paletteSwapDirectivesFromConfig(Json const& swaps); diff --git a/source/core/StarImageScaling.cpp b/source/core/StarImageScaling.cpp new file mode 100644 index 0000000..96837aa --- /dev/null +++ b/source/core/StarImageScaling.cpp @@ -0,0 +1,98 @@ +#include "StarImage.hpp" +#include "StarImageScaling.hpp" +#include "StarInterpolation.hpp" + +namespace Star { + +Image scaleNearest(Image const& srcImage, Vec2F const& scale) { + Vec2U srcSize = srcImage.size(); + Vec2U destSize = Vec2U::round(vmult(Vec2F(srcSize), scale)); + destSize[0] = max(destSize[0], 1u); + destSize[1] = max(destSize[1], 1u); + + Image destImage(destSize, srcImage.pixelFormat()); + + for (unsigned y = 0; y < destSize[1]; ++y) { + for (unsigned x = 0; x < destSize[0]; ++x) + destImage.set({x, y}, srcImage.clamp(Vec2I::round(vdiv(Vec2F(x, y), scale)))); + } + return destImage; +} + +Image scaleBilinear(Image const& srcImage, Vec2F const& scale) { + Vec2U srcSize = srcImage.size(); + Vec2U destSize = Vec2U::round(vmult(Vec2F(srcSize), scale)); + destSize[0] = max(destSize[0], 1u); + destSize[1] = max(destSize[1], 1u); + + Image destImage(destSize, srcImage.pixelFormat()); + + for (unsigned y = 0; y < destSize[1]; ++y) { + for (unsigned x = 0; x < destSize[0]; ++x) { + auto pos = vdiv(Vec2F(x, y), scale); + auto ipart = Vec2I::floor(pos); + auto fpart = pos - Vec2F(ipart); + + auto result = lerp(fpart[1], lerp(fpart[0], Vec4F(srcImage.clamp(ipart[0], ipart[1])), Vec4F(srcImage.clamp(ipart[0] + 1, ipart[1]))), lerp(fpart[0], + Vec4F(srcImage.clamp(ipart[0], ipart[1] + 1)), Vec4F(srcImage.clamp(ipart[0] + 1, ipart[1] + 1)))); + + destImage.set({x, y}, Vec4B(result)); + } + } + + return destImage; +} + +Image scaleBicubic(Image const& srcImage, Vec2F const& scale) { + Vec2U srcSize = srcImage.size(); + Vec2U destSize = Vec2U::round(vmult(Vec2F(srcSize), scale)); + destSize[0] = max(destSize[0], 1u); + destSize[1] = max(destSize[1], 1u); + + Image destImage(destSize, srcImage.pixelFormat()); + + for (unsigned y = 0; y < destSize[1]; ++y) { + for (unsigned x = 0; x < destSize[0]; ++x) { + auto pos = vdiv(Vec2F(x, y), scale); + auto ipart = Vec2I::floor(pos); + auto fpart = pos - Vec2F(ipart); + + Vec4F a = cubic4(fpart[0], + Vec4F(srcImage.clamp(ipart[0], ipart[1])), + Vec4F(srcImage.clamp(ipart[0] + 1, ipart[1])), + Vec4F(srcImage.clamp(ipart[0] + 2, ipart[1])), + Vec4F(srcImage.clamp(ipart[0] + 3, ipart[1]))); + + Vec4F b = cubic4(fpart[0], + Vec4F(srcImage.clamp(ipart[0], ipart[1] + 1)), + Vec4F(srcImage.clamp(ipart[0] + 1, ipart[1] + 1)), + Vec4F(srcImage.clamp(ipart[0] + 2, ipart[1] + 1)), + Vec4F(srcImage.clamp(ipart[0] + 3, ipart[1] + 1))); + + Vec4F c = cubic4(fpart[0], + Vec4F(srcImage.clamp(ipart[0], ipart[1] + 2)), + Vec4F(srcImage.clamp(ipart[0] + 1, ipart[1] + 2)), + Vec4F(srcImage.clamp(ipart[0] + 2, ipart[1] + 2)), + Vec4F(srcImage.clamp(ipart[0] + 3, ipart[1] + 2))); + + Vec4F d = cubic4(fpart[0], + Vec4F(srcImage.clamp(ipart[0], ipart[1] + 3)), + Vec4F(srcImage.clamp(ipart[0] + 1, ipart[1] + 3)), + Vec4F(srcImage.clamp(ipart[0] + 2, ipart[1] + 3)), + Vec4F(srcImage.clamp(ipart[0] + 3, ipart[1] + 3))); + + auto result = cubic4(fpart[1], a, b, c, d); + + destImage.set({x, y}, Vec4B( + clamp(result[0], 0.0f, 255.0f), + clamp(result[1], 0.0f, 255.0f), + clamp(result[2], 0.0f, 255.0f), + clamp(result[3], 0.0f, 255.0f) + )); + } + } + + return destImage; +} + +} \ No newline at end of file diff --git a/source/core/StarImageScaling.hpp b/source/core/StarImageScaling.hpp new file mode 100644 index 0000000..6a7271b --- /dev/null +++ b/source/core/StarImageScaling.hpp @@ -0,0 +1,8 @@ +namespace Star { + +STAR_CLASS(Image); +Image scaleNearest(Image const& srcImage, Vec2F const& scale); +Image scaleBilinear(Image const& srcImage, Vec2F const& scale); +Image scaleBicubic(Image const& srcImage, Vec2F const& scale); + +} \ No newline at end of file