diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..b7dd315 --- /dev/null +++ b/.clang-format @@ -0,0 +1,193 @@ +--- +Language: Cpp +BasedOnStyle: '' + +# Basic indentation and spacing rules: +IndentWidth: 4 +ContinuationIndentWidth: 4 +TabWidth: 4 +UseCRLF: false +UseTab: Never +ColumnLimit: 120 +MaxEmptyLinesToKeep: 3 + +IndentCaseLabels: true +IndentCaseBlocks: false +IndentGotoLabels: true +IndentPPDirectives: None +IndentExternBlock: AfterExternBlock +IndentWrappedFunctionNames: true + +AlignConsecutiveAssignments: + # The column at which the = starts is constant among a block of definitions. + Enabled: true + AcrossEmptyLines: false + AcrossComments: true + AlignCompound: true + PadOperators: true +AlignConsecutiveBitFields: + Enabled: true + AcrossEmptyLines: false + AcrossComments: true +AlignConsecutiveDeclarations: + # The column at which the name starts is constant among a block of declarations / definitions. + Enabled: true + AcrossEmptyLines: false + AcrossComments: true +AlignConsecutiveMacros: + Enabled: true + AcrossEmptyLines: false + AcrossComments: true + +# configure braces and parens: +BraceWrapping: + AfterEnum: false # The { is on the same line as the struct, union or enum keyword. + AfterStruct: false # The { is on the same line as the struct, union or enum keyword. + AfterUnion: false # The { is on the same line as the struct, union or enum keyword. + AfterFunction: false # The { is on the same line as the ). + + AfterCaseLabel: false + AfterControlStatement: Never + + AfterExternBlock: false + BeforeElse: false + BeforeWhile: false + IndentBraces: false + SplitEmptyFunction: true + SplitEmptyRecord: true + +AllowShortFunctionsOnASingleLine: None # never collapse empty functions +AllowShortBlocksOnASingleLine: Never # never make single-line blocks +AllowShortIfStatementsOnASingleLine: Never # never make single-line ifs +AllowShortLoopsOnASingleLine: true # allow single-line loops +AllowShortEnumsOnASingleLine: true # allow enum { FOO = 10 }; + +# configure general code: + +# The = has at least one space to the left and one space to the right. +SpaceBeforeAssignmentOperators: true + +# configure pointers: + +PointerAlignment: Right # The * is at the function name and has at least one space from the type before it. +QualifierAlignment: Right # The const is to the left of the type (unless the pointer is also a constant). +SpaceAroundPointerQualifiers: After + +# include sorting: + +SortIncludes: CaseInsensitive +IncludeBlocks: Regroup +IncludeCategories: + # External headers + - Regex: '^\<(' + Priority: 3 + SortPriority: 0 + CaseSensitive: false + + # System headers + - Regex: '^\<(std.*|inttypes)' + Priority: 2 + SortPriority: 0 + CaseSensitive: false + + # Assume project header + - Regex: '^\".*' + Priority: 1 + SortPriority: 0 + CaseSensitive: false + +# all other options + +# all arguments on one or on a separate line, use closing parens on separate line +BinPackArguments: false +BinPackParameters: false +AlignAfterOpenBracket: BlockIndent +AllowAllArgumentsOnNextLine: false + +BitFieldColonSpacing: Both # int bf : 2; + +AlignEscapedNewlines: Right + +AllowShortCaseLabelsOnASingleLine: true # don't split up "case x: return value;" + +BreakBeforeTernaryOperators: true + +# default values: + +# AlignArrayOfStructures: None +# AlignOperands: Align +# AlignTrailingComments: true +# AllowAllParametersOfDeclarationOnNextLine: true + +# AlwaysBreakAfterDefinitionReturnType: None +# AlwaysBreakAfterReturnType: None +# AlwaysBreakBeforeMultilineStrings: false +# BreakBeforeBinaryOperators: None +# BreakBeforeBraces: Attach +# BreakStringLiterals: true +# CommentPragmas: '^ IWYU pragma:' + +# ExperimentalAutoDetectBinPacking: false +# IncludeIsMainRegex: '(Test)?$' +# IncludeIsMainSourceRegex: '' +# InsertBraces: false +# InsertTrailingCommas: None +# KeepEmptyLinesAtTheStartOfBlocks: true +# ReflowComments: true +# RequiresClausePosition: OwnLine +# SeparateDefinitionBlocks: Leave + +# SpaceAfterCStyleCast: false +# SpaceAfterLogicalNot: false +# SpaceBeforeCaseColon: false +# SpaceBeforeParens: ControlStatements +# SpaceBeforeParensOptions: +# AfterControlStatements: true +# AfterForeachMacros: true +# AfterFunctionDefinitionName: false +# AfterFunctionDeclarationName: false +# AfterIfMacros: true +# AfterOverloadedOperator: false +# AfterRequiresInClause: false +# AfterRequiresInExpression: false +# BeforeNonEmptyParentheses: false +# SpaceBeforeRangeBasedForLoopColon: true +# SpaceInEmptyBlock: false +# SpaceInEmptyParentheses: false +# SpacesBeforeTrailingComments: 1 +# SpacesInAngles: Never +# SpacesInConditionalStatement: false +# SpacesInContainerLiterals: true +# SpacesInCStyleCastParentheses: false +# SpacesInLineCommentPrefix: +# Minimum: 1 +# Maximum: -1 +# SpacesInParentheses: false +# SpacesInSquareBrackets: false +# SpaceBeforeSquareBrackets: false + +# Configure some special macros: +AttributeMacros: + - NORETURN + - PURE + - COLD + - HOT + - FORCEINLINE + - SECTION +WhitespaceSensitiveMacros: + - comptime_stringify +# ForEachMacros: +# - foreach +# - Q_FOREACH +# - BOOST_FOREACH +# IfMacros: +# - KJ_IF_MAYBE +# StatementMacros: +# - Q_UNUSED +# - QT_REQUIRE_VERSION +# StatementAttributeLikeMacros: +# - FALLTHROUGH +# MacroBlockBegin: '' +# MacroBlockEnd: '' +--- + diff --git a/CMakeLists.txt b/CMakeLists.txt index da3a8f9..0c7ef7d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -10,6 +10,7 @@ elseif(DEFINED PICO_SDK_PATH) project(pax_graphics C CXX) message(STATUS "Building PAX Graphics for Pi Pico SDK.") + add_definitions(-DPAX_STANDALONE=0) add_definitions(-DPAX_COMPILE_MCR=0) add_definitions(-DPAX_PI_PICO=1) include(${CMAKE_CURRENT_LIST_DIR}/standalone.cmake) diff --git a/LICENSE b/LICENSE index a59be73..9bf3408 100644 --- a/LICENSE +++ b/LICENSE @@ -1,23 +1,21 @@ -/* - MIT License +MIT License - Copyright (c) 2021-2023 Julian Scheffers +Copyright (c) 2021-2023 Julian Scheffers - Permission is hereby granted, free of charge, to any person obtaining a copy - of this software and associated documentation files (the "Software"), to deal - in the Software without restriction, including without limitation the rights - to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - copies of the Software, and to permit persons to whom the Software is - furnished to do so, subject to the following conditions: +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: - The above copyright notice and this permission notice shall be included in all - copies or substantial portions of the Software. +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - SOFTWARE. -*/ +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/precalc.py b/precalc.py index 07e694e..bdf5f12 100755 --- a/precalc.py +++ b/precalc.py @@ -3,42 +3,48 @@ import math def precalcCircle(fd_c, fd_h, resolution, id): - delta = math.pi * 2 / resolution - - # Precalculate points. - fd_h.write("// Circle: " + str(resolution) + " segments\n") - fd_h.write("extern const pax_vec2f pax_precalc_" + id + "[" + str(resolution + 1) + "];\n") - fd_c.write("// Circle: " + str(resolution) + " segments\n") - fd_c.write("const pax_vec2f pax_precalc_" + id + "[" + str(resolution + 1) + "] = {\n") - for i in range(resolution + 1): - angle = i * delta - fd_c.write("\t{" + str(math.cos(angle)) + ", " + str(math.sin(angle)) + "},\n") - fd_c.write("};\n\n") - - # Precalculate UVs. - fd_h.write("// Circle UVs: " + str(resolution) + " segments\n") - fd_h.write("extern const pax_trif pax_precalc_uv_" + id + "[" + str(resolution - 1) + "];\n\n") - fd_c.write("// Circle UVs: " + str(resolution) + " segments\n") - fd_c.write("const pax_trif pax_precalc_uv_" + id + "[" + str(resolution - 1) + "] = {\n") - for i in range(resolution - 1): - a1 = i * delta - a2 = (i + 1) * delta - fd_c.write("\t{1, 0.5, ") - fd_c.write(str(math.cos(a1)/2+0.5) + ", " + str(math.sin(a1)/2+0.5) + ", ") - fd_c.write(str(math.cos(a2)/2+0.5) + ", " + str(math.sin(a2)/2+0.5)) - fd_c.write("},\n") - fd_c.write("};\n\n") + delta = math.pi * 2 / resolution + + # Precalculate points. + fd_h.write("// Circle: " + str(resolution) + " segments\n") + fd_h.write("extern const pax_vec2f pax_precalc_" + id + "[" + str(resolution + 1) + "];\n") + fd_c.write("// Circle: " + str(resolution) + " segments\n") + fd_c.write("const pax_vec2f pax_precalc_" + id + "[" + str(resolution + 1) + "] = {\n") + for i in range(resolution + 1): + angle = i * delta + fd_c.write("\t{" + str(math.cos(angle)) + ", " + str(math.sin(angle)) + "},\n") + fd_c.write("};\n\n") + + # Precalculate UVs. + fd_h.write("// Circle UVs: " + str(resolution) + " segments\n") + fd_h.write("extern const pax_trif pax_precalc_uv_" + id + "[" + str(resolution - 1) + "];\n\n") + fd_c.write("// Circle UVs: " + str(resolution) + " segments\n") + fd_c.write("const pax_trif pax_precalc_uv_" + id + "[" + str(resolution - 1) + "] = {\n") + for i in range(resolution - 1): + a1 = i * delta + a2 = (i + 1) * delta + fd_c.write("\t{1, 0.5, ") + fd_c.write(str(math.cos(a1)/2+0.5) + ", " + str(math.sin(a1)/2+0.5) + ", ") + fd_c.write(str(math.cos(a2)/2+0.5) + ", " + str(math.sin(a2)/2+0.5)) + fd_c.write("},\n") + fd_c.write("};\n\n") if __name__ == "__main__": - out_c = open("src/helpers/pax_precalculated.c", "w") - out_c.write("// WARNING: This is a generated file, do not edit it!\n") - out_c.write("// This file contains precalculated math operations.\n\n") - out_c.write("#include \"pax_internal.h\"\n\n") - out_h = open("src/helpers/pax_precalculated.h", "w") - out_h.write("// WARNING: This is a generated file, do not edit it!\n") - out_h.write("// This file contains precalculated math operations.\n\n") - out_h.write("#include \"pax_types.h\"\n\n") - precalcCircle(out_c, out_h, 4, "circle_4") - precalcCircle(out_c, out_h, 16, "circle_16") - precalcCircle(out_c, out_h, 24, "circle_24") - precalcCircle(out_c, out_h, 32, "circle_32") + out_c = open("src/helpers/pax_precalculated.c", "w") + out_c.write("// WARNING: This is a generated file, do not edit it!\n") + out_c.write("// This file contains precalculated math operations.\n") + out_c.write("// clang-format off\n\n") + out_c.write("#include \"pax_internal.h\"\n\n") + out_h = open("src/helpers/pax_precalculated.h", "w") + out_h.write("// WARNING: This is a generated file, do not edit it!\n") + out_h.write("// This file contains precalculated math operations.\n") + out_c.write("// clang-format off\n\n") + out_h.write("#include \"pax_types.h\"\n\n") + precalcCircle(out_c, out_h, 4, "circle_4") + precalcCircle(out_c, out_h, 16, "circle_16") + precalcCircle(out_c, out_h, 24, "circle_24") + precalcCircle(out_c, out_h, 32, "circle_32") + out_c.flush() + out_c.close() + out_h.flush() + out_h.close() diff --git a/sources.cmake b/sources.cmake index 44b1e75..4bbd7c6 100644 --- a/sources.cmake +++ b/sources.cmake @@ -37,6 +37,6 @@ set(PAX_INCLUDE_C set(PAX_SRCS_CXX ) -# C include directories. +# C++ include directories. set(PAX_INCLUDE_CXX ) diff --git a/src/helpers/pax_dh_generic_rect.hpp b/src/helpers/pax_dh_generic_rect.hpp index ad07310..c675ea0 100644 --- a/src/helpers/pax_dh_generic_rect.hpp +++ b/src/helpers/pax_dh_generic_rect.hpp @@ -1,29 +1,8 @@ -/* - MIT License - Copyright (c) 2021-2023 Julian Scheffers +// SPDX-License-Identifier: MIT - Permission is hereby granted, free of charge, to any person obtaining a copy - of this software and associated documentation files (the "Software"), to deal - in the Software without restriction, including without limitation the rights - to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - copies of the Software, and to permit persons to whom the Software is - furnished to do so, subject to the following conditions: - - The above copyright notice and this permission notice shall be included in all - copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - SOFTWARE. -*/ - -#include "pax_internal.h" #include "pax_fixpt.hpp" +#include "pax_internal.h" #ifdef PDHG_NAME @@ -53,291 +32,334 @@ #ifdef PDHG_STATIC static #endif -void PDHG_NAME( + void + PDHG_NAME( #ifdef PDHG_MCR - bool odd_scanline, + bool odd_scanline, +#endif + pax_buf_t *buf, + pax_col_t color, +#ifdef PDHG_SHADED + pax_shader_t const *shader, +#endif + float _x, + float _y, + float _width, + float _height +#ifdef PDHG_NORMAL_UV + , + float _u0, + float _v0, + float _u1, + float _v1, + float _u2, + float _v2, + float _u3, + float _v3 #endif - pax_buf_t *buf, pax_col_t color, +#ifdef PDHG_RESTRICT_UV + , + float _u0, + float _v0, + float _u1, + float _v1 +#endif + ) { + + // Float to fixed point. + fixpt_t x = _x; + fixpt_t y = _y; + fixpt_t width = _width; + fixpt_t height = _height; +#ifdef PDHG_NORMAL_UV + fixpt_t u0 = _u0; + fixpt_t v0 = _v0; + fixpt_t u1 = _u1; + fixpt_t v1 = _v1; + fixpt_t u2 = _u2; + fixpt_t v2 = _v2; + fixpt_t u3 = _u3; + fixpt_t v3 = _v3; +#endif +#ifdef PDHG_RESTRICT_UV + fixpt_t u0 = _u0; + fixpt_t v0 = _v0; + fixpt_t u1 = _u1; + fixpt_t v1 = _v1; +#endif + #ifdef PDHG_SHADED - const pax_shader_t *shader, + // Get shader context. + pax_shader_ctx_t shader_ctx = pax_get_shader_ctx(buf, color, shader); + if (shader_ctx.skip) + return; + // Get pixel setter. + pax_col_conv_t buf2col = PAX_IS_PALETTE(buf->type) ? pax_col_conv_dummy : buf->buf2col; +#else // PDHG_SHADED + // Get pixel setter. + pax_index_setter_t setter = pax_get_setter(buf, &color, NULL); + if (!setter) + return; +#endif // PDHG_SHADED + + // Fix width and height. + if (width < 0) { + x += width; + width = -width; +#ifdef PDHG_NORMAL_UV + PAX_SWAP(fixpt_t, u0, u1) + PAX_SWAP(fixpt_t, v0, v1) + PAX_SWAP(fixpt_t, u2, u3) + PAX_SWAP(fixpt_t, v2, v3) +#endif +#ifdef PDHG_RESTRICT_UV + PAX_SWAP(fixpt_t, u0, u1) +#endif + } + if (height < 0) { + y += height; + height = -height; +#ifdef PDHG_NORMAL_UV + PAX_SWAP(fixpt_t, u0, u3) + PAX_SWAP(fixpt_t, v0, v3) + PAX_SWAP(fixpt_t, u1, u2) + PAX_SWAP(fixpt_t, v1, v2) +#endif +#ifdef PDHG_RESTRICT_UV + PAX_SWAP(fixpt_t, v0, v1) +#endif + } + + // Clip rect in inside of buffer. + if (x < buf->clip.x) { +#ifdef PDHG_NORMAL_UV + fixpt_t part = (buf->clip.x - x) / width; + u0 = u0 + (u1 - u0) * part; + v0 = v0 + (v1 - v0) * part; + u3 = u3 + (u2 - u3) * part; + v3 = v3 + (v2 - v3) * part; +#endif +#ifdef PDHG_RESTRICT_UV + fixpt_t part = (buf->clip.x - x) / width; + u0 = u0 + (u1 - u0) * part; +#endif + + width -= buf->clip.x - x; + x = buf->clip.x; + } + if (x + width > buf->clip.x + buf->clip.w) { +#ifdef PDHG_NORMAL_UV + fixpt_t part = (buf->clip.x + buf->clip.w - x) / width; + u1 = u0 + (u1 - u0) * part; + v1 = v0 + (v1 - v0) * part; + u2 = u3 + (u2 - u3) * part; + v2 = v3 + (v2 - v3) * part; +#endif +#ifdef PDHG_RESTRICT_UV + fixpt_t part = (buf->clip.x + buf->clip.w - x) / width; + u1 = u0 + (u1 - u0) * part; +#endif + + width = buf->clip.x + buf->clip.w - x; + } + if (y < buf->clip.y) { +#ifdef PDHG_NORMAL_UV + fixpt_t part = (buf->clip.y - y) / height; + u0 = u0 + (u3 - u0) * part; + v0 = v0 + (v3 - v0) * part; + u1 = u1 + (u2 - u1) * part; + v1 = v1 + (v2 - v1) * part; +#endif +#ifdef PDHG_RESTRICT_UV + fixpt_t part = (buf->clip.y - y) / height; + v0 = v0 + (v1 - v0) * part; +#endif + + height -= buf->clip.y - y; + y = buf->clip.y; + } + if (y + height > buf->clip.y + buf->clip.h) { +#ifdef PDHG_NORMAL_UV + fixpt_t part = (buf->clip.y + buf->clip.h - y) / height; + u3 = u0 + (u3 - u0) * part; + v3 = v0 + (v3 - v0) * part; + u2 = u1 + (u2 - u1) * part; + v2 = v1 + (v2 - v1) * part; +#endif +#ifdef PDHG_RESTRICT_UV + fixpt_t part = (buf->clip.y + buf->clip.h - y) / height; + v1 = v0 + (v1 - v0) * part; +#endif + + height = buf->clip.y + buf->clip.h - y; + } + +#if defined(PDHG_NORMAL_UV) || defined(PDHG_RESTRICT_UV) + // Adjust UVs to match pixel co-ordinates. + fixpt_t min_x = (int)(x + 0.5) + 0.5; + fixpt_t max_x = (int)(x + width - 0.5) + 0.5; + fixpt_t min_y = (int)(y + 0.5) + 0.5; + fixpt_t max_y = (int)(y + height - 0.5) + 0.5; +#endif + +#ifdef PDHG_NORMAL_UV + // Adjust X part. + { // Adjust UV0 and UV1. + fixpt_t new_u0 = u0 + (u1 - u0) / width * (min_x - x); + fixpt_t new_u1 = u0 + (u1 - u0) / width * (max_x - x); + fixpt_t new_v0 = v0 + (v1 - v0) / width * (min_x - x); + fixpt_t new_v1 = v0 + (v1 - v0) / width * (max_x - x); + u0 = new_u0; + u1 = new_u1; + v0 = new_v0; + v1 = new_v1; + } + { // Adjust UV3 and UV2. + fixpt_t new_u3 = u3 + (u2 - u3) / width * (min_x - x); + fixpt_t new_u2 = u3 + (u2 - u3) / width * (max_x - x); + fixpt_t new_v3 = v3 + (v2 - v3) / width * (min_x - x); + fixpt_t new_v2 = v3 + (v2 - v3) / width * (max_x - x); + u3 = new_u3; + u2 = new_u2; + v3 = new_v3; + v2 = new_v2; + } + // Adjust Y part. + { // Adjust UV1 and UV2. + fixpt_t new_u1 = u1 + (u2 - u1) / height * (min_y - y); + fixpt_t new_u2 = u1 + (u2 - u1) / height * (max_y - y); + fixpt_t new_v1 = v1 + (v2 - v1) / height * (min_y - y); + fixpt_t new_v2 = v1 + (v2 - v1) / height * (max_y - y); + u1 = new_u1; + u2 = new_u2; + v1 = new_v1; + v2 = new_v2; + } + { // Adjust UV0 and UV3. + fixpt_t new_u0 = u0 + (u3 - u0) / height * (min_y - y); + fixpt_t new_u3 = u0 + (u3 - u0) / height * (max_y - y); + fixpt_t new_v0 = v0 + (v3 - v0) / height * (min_y - y); + fixpt_t new_v3 = v0 + (v3 - v0) / height * (max_y - y); + u0 = new_u0; + u3 = new_u3; + v0 = new_v0; + v3 = new_v3; + } + + // Find UV deltas. + fixpt_t u0_u3_du = (u3 - u0) / (height - 1); + fixpt_t v0_v3_dv = (v3 - v0) / (height - 1); + fixpt_t u1_u2_du = (u2 - u1) / (height - 1); + fixpt_t v1_v2_dv = (v2 - v1) / (height - 1); + + fixpt_t u_a = u0, v_a = v0; + fixpt_t u_b = u1, v_b = v1; +#endif // PDHG_NORMAL_UV + +#ifdef PDHG_RESTRICT_UV + { // Adjust the X part. + fixpt_t new_u0 = u0 + (u1 - u0) / width * (min_x - x); + fixpt_t new_u1 = u0 + (u1 - u0) / width * (max_x - x); + u0 = new_u0; + u1 = new_u1; + } + { // Adjust the Y part. + fixpt_t new_v0 = v0 + (v1 - v0) / height * (min_y - y); + fixpt_t new_v1 = v0 + (v1 - v0) / height * (max_y - y); + v0 = new_v0; + v1 = new_v1; + } + + // Find UV deltas. + fixpt_t u0_u1_du = (u1 - u0) / (max_x - min_x); + fixpt_t v0_v1_dv = (v1 - v0) / (max_y - min_y); + + fixpt_t v = v0; +#endif // PDHG_RESTRICT_UV + + int c_y = y + 0.5; +#ifdef PDHG_MCR + // Snap c_y to the correct line. + if ((c_y & 1) != odd_scanline) { + c_y++; +#ifdef PDHG_NORMAL_UV + u_a += u0_u3_du; + v_a += v0_v3_dv; + u_b += u1_u2_du; + v_b += v1_v2_dv; +#endif +#ifdef PDHG_RESTRICT_UV + v += v0_v1_dv; +#endif + } #endif - float _x, float _y, float _width, float _height + + // Pixel time. + int delta = c_y * buf->width; + for (; c_y <= y + height - 0.5; c_y += PDHG_INCREMENT) { #ifdef PDHG_NORMAL_UV - , float _u0, float _v0, float _u1, float _v1, float _u2, float _v2, float _u3, float _v3 + fixpt_t ua_ub_du = (u_b - u_a) / (max_x - min_x); + fixpt_t va_vb_dv = (v_b - v_a) / (max_x - min_x); + fixpt_t u = u_a, v = v_a; #endif #ifdef PDHG_RESTRICT_UV - , float _u0, float _v0, float _u1, float _v1 -#endif - ) { - - // Float to fixed point. - fixpt_t x = _x; - fixpt_t y = _y; - fixpt_t width = _width; - fixpt_t height = _height; + fixpt_t u = u0; +#endif + for (int c_x = x + 0.5; c_x <= x + width - 0.5; c_x++) { #ifdef PDHG_NORMAL_UV - fixpt_t u0 = _u0; - fixpt_t v0 = _v0; - fixpt_t u1 = _u1; - fixpt_t v1 = _v1; - fixpt_t u2 = _u2; - fixpt_t v2 = _v2; - fixpt_t u3 = _u3; - fixpt_t v3 = _v3; + pax_col_t result = (shader_ctx.callback)( + color, + shader_ctx.do_getter ? buf2col(buf, buf->getter(buf, c_x + delta)) : 0, + c_x, + c_y, + u, + v, + shader_ctx.callback_args + ); + pax_set_index_conv(buf, result, c_x + delta); + u += ua_ub_du; + v += va_vb_dv; #endif #ifdef PDHG_RESTRICT_UV - fixpt_t u0 = _u0; - fixpt_t v0 = _v0; - fixpt_t u1 = _u1; - fixpt_t v1 = _v1; -#endif - - #ifdef PDHG_SHADED - // Get shader context. - pax_shader_ctx_t shader_ctx = pax_get_shader_ctx(buf, color, shader); - if (shader_ctx.skip) return; - // Get pixel setter. - pax_col_conv_t buf2col = PAX_IS_PALETTE(buf->type) ? pax_col_conv_dummy : buf->buf2col; - #else // PDHG_SHADED - // Get pixel setter. - pax_index_setter_t setter = pax_get_setter(buf, &color, NULL); - if (!setter) return; - #endif // PDHG_SHADED - - // Fix width and height. - if (width < 0) { - x += width; - width = -width; - #ifdef PDHG_NORMAL_UV - PAX_SWAP(fixpt_t, u0, u1) - PAX_SWAP(fixpt_t, v0, v1) - PAX_SWAP(fixpt_t, u2, u3) - PAX_SWAP(fixpt_t, v2, v3) - #endif - #ifdef PDHG_RESTRICT_UV - PAX_SWAP(fixpt_t, u0, u1) - #endif - } - if (height < 0) { - y += height; - height = -height; - #ifdef PDHG_NORMAL_UV - PAX_SWAP(fixpt_t, u0, u3) - PAX_SWAP(fixpt_t, v0, v3) - PAX_SWAP(fixpt_t, u1, u2) - PAX_SWAP(fixpt_t, v1, v2) - #endif - #ifdef PDHG_RESTRICT_UV - PAX_SWAP(fixpt_t, v0, v1) - #endif - } - - // Clip rect in inside of buffer. - if (x < buf->clip.x) { - #ifdef PDHG_NORMAL_UV - fixpt_t part = (buf->clip.x - x) / width; - u0 = u0 + (u1 - u0) * part; - v0 = v0 + (v1 - v0) * part; - u3 = u3 + (u2 - u3) * part; - v3 = v3 + (v2 - v3) * part; - #endif - #ifdef PDHG_RESTRICT_UV - fixpt_t part = (buf->clip.x - x) / width; - u0 = u0 + (u1 - u0) * part; - #endif - - width -= buf->clip.x - x; - x = buf->clip.x; - } - if (x + width > buf->clip.x + buf->clip.w) { - #ifdef PDHG_NORMAL_UV - fixpt_t part = (buf->clip.x + buf->clip.w - x) / width; - u1 = u0 + (u1 - u0) * part; - v1 = v0 + (v1 - v0) * part; - u2 = u3 + (u2 - u3) * part; - v2 = v3 + (v2 - v3) * part; - #endif - #ifdef PDHG_RESTRICT_UV - fixpt_t part = (buf->clip.x + buf->clip.w - x) / width; - u1 = u0 + (u1 - u0) * part; - #endif - - width = buf->clip.x + buf->clip.w - x; - } - if (y < buf->clip.y) { - #ifdef PDHG_NORMAL_UV - fixpt_t part = (buf->clip.y - y) / height; - u0 = u0 + (u3 - u0) * part; - v0 = v0 + (v3 - v0) * part; - u1 = u1 + (u2 - u1) * part; - v1 = v1 + (v2 - v1) * part; - #endif - #ifdef PDHG_RESTRICT_UV - fixpt_t part = (buf->clip.y - y) / height; - v0 = v0 + (v1 - v0) * part; - #endif - - height -= buf->clip.y - y; - y = buf->clip.y; - } - if (y + height > buf->clip.y + buf->clip.h) { - #ifdef PDHG_NORMAL_UV - fixpt_t part = (buf->clip.y + buf->clip.h - y) / height; - u3 = u0 + (u3 - u0) * part; - v3 = v0 + (v3 - v0) * part; - u2 = u1 + (u2 - u1) * part; - v2 = v1 + (v2 - v1) * part; - #endif - #ifdef PDHG_RESTRICT_UV - fixpt_t part = (buf->clip.y + buf->clip.h - y) / height; - v1 = v0 + (v1 - v0) * part; - #endif - - height = buf->clip.y + buf->clip.h - y; - } - - #if defined(PDHG_NORMAL_UV) || defined(PDHG_RESTRICT_UV) - // Adjust UVs to match pixel co-ordinates. - fixpt_t min_x = (int) (x + 0.5) + 0.5; - fixpt_t max_x = (int) (x + width - 0.5) + 0.5; - fixpt_t min_y = (int) (y + 0.5) + 0.5; - fixpt_t max_y = (int) (y + height - 0.5) + 0.5; - #endif - - #ifdef PDHG_NORMAL_UV - // Adjust X part. - { // Adjust UV0 and UV1. - fixpt_t new_u0 = u0 + (u1 - u0) / width * (min_x - x); - fixpt_t new_u1 = u0 + (u1 - u0) / width * (max_x - x); - fixpt_t new_v0 = v0 + (v1 - v0) / width * (min_x - x); - fixpt_t new_v1 = v0 + (v1 - v0) / width * (max_x - x); - u0 = new_u0; - u1 = new_u1; - v0 = new_v0; - v1 = new_v1; - } - { // Adjust UV3 and UV2. - fixpt_t new_u3 = u3 + (u2 - u3) / width * (min_x - x); - fixpt_t new_u2 = u3 + (u2 - u3) / width * (max_x - x); - fixpt_t new_v3 = v3 + (v2 - v3) / width * (min_x - x); - fixpt_t new_v2 = v3 + (v2 - v3) / width * (max_x - x); - u3 = new_u3; - u2 = new_u2; - v3 = new_v3; - v2 = new_v2; - } - // Adjust Y part. - { // Adjust UV1 and UV2. - fixpt_t new_u1 = u1 + (u2 - u1) / height * (min_y - y); - fixpt_t new_u2 = u1 + (u2 - u1) / height * (max_y - y); - fixpt_t new_v1 = v1 + (v2 - v1) / height * (min_y - y); - fixpt_t new_v2 = v1 + (v2 - v1) / height * (max_y - y); - u1 = new_u1; - u2 = new_u2; - v1 = new_v1; - v2 = new_v2; - } - { // Adjust UV0 and UV3. - fixpt_t new_u0 = u0 + (u3 - u0) / height * (min_y - y); - fixpt_t new_u3 = u0 + (u3 - u0) / height * (max_y - y); - fixpt_t new_v0 = v0 + (v3 - v0) / height * (min_y - y); - fixpt_t new_v3 = v0 + (v3 - v0) / height * (max_y - y); - u0 = new_u0; - u3 = new_u3; - v0 = new_v0; - v3 = new_v3; - } - - // Find UV deltas. - fixpt_t u0_u3_du = (u3 - u0) / (height - 1); - fixpt_t v0_v3_dv = (v3 - v0) / (height - 1); - fixpt_t u1_u2_du = (u2 - u1) / (height - 1); - fixpt_t v1_v2_dv = (v2 - v1) / (height - 1); - - fixpt_t u_a = u0, v_a = v0; - fixpt_t u_b = u1, v_b = v1; - #endif // PDHG_NORMAL_UV - - #ifdef PDHG_RESTRICT_UV - { // Adjust the X part. - fixpt_t new_u0 = u0 + (u1 - u0) / width * (min_x - x); - fixpt_t new_u1 = u0 + (u1 - u0) / width * (max_x - x); - u0 = new_u0; - u1 = new_u1; - } - { // Adjust the Y part. - fixpt_t new_v0 = v0 + (v1 - v0) / height * (min_y - y); - fixpt_t new_v1 = v0 + (v1 - v0) / height * (max_y - y); - v0 = new_v0; - v1 = new_v1; - } - - // Find UV deltas. - fixpt_t u0_u1_du = (u1 - u0) / (max_x - min_x); - fixpt_t v0_v1_dv = (v1 - v0) / (max_y - min_y); - - fixpt_t v = v0; - #endif // PDHG_RESTRICT_UV - - int c_y = y + 0.5; - #ifdef PDHG_MCR - // Snap c_y to the correct line. - if ((c_y & 1) != odd_scanline) { - c_y ++; - #ifdef PDHG_NORMAL_UV - u_a += u0_u3_du; - v_a += v0_v3_dv; - u_b += u1_u2_du; - v_b += v1_v2_dv; - #endif - #ifdef PDHG_RESTRICT_UV - v += v0_v1_dv; - #endif - } - #endif - - // Pixel time. - int delta = c_y * buf->width; - for (; c_y <= y + height - 0.5; c_y += PDHG_INCREMENT) { - #ifdef PDHG_NORMAL_UV - fixpt_t ua_ub_du = (u_b - u_a) / (max_x - min_x); - fixpt_t va_vb_dv = (v_b - v_a) / (max_x - min_x); - fixpt_t u = u_a, v = v_a; - #endif - #ifdef PDHG_RESTRICT_UV - fixpt_t u = u0; - #endif - for (int c_x = x + 0.5; c_x <= x + width - 0.5; c_x ++) { - #ifdef PDHG_NORMAL_UV - pax_col_t result = (shader_ctx.callback)(color, shader_ctx.do_getter ? buf2col(buf, buf->getter(buf, c_x+delta)) : 0, c_x, c_y, u, v, shader_ctx.callback_args); - pax_set_index_conv(buf, result, c_x+delta); - u += ua_ub_du; - v += va_vb_dv; - #endif - #ifdef PDHG_RESTRICT_UV - pax_col_t result = (shader_ctx.callback)(color, shader_ctx.do_getter ? buf2col(buf, buf->getter(buf, c_x+delta)) : 0, c_x, c_y, u, v, shader_ctx.callback_args); - pax_set_index_conv(buf, result, c_x+delta); - u += u0_u1_du; - #endif - #ifdef PDHG_IGNORE_UV - pax_col_t result = (shader_ctx.callback)(color, shader_ctx.do_getter ? buf2col(buf, buf->getter(buf, c_x+delta)) : 0, c_x, c_y, 0, 0, shader_ctx.callback_args); - pax_set_index_conv(buf, result, c_x+delta); - #endif - #ifndef PDHG_SHADED - setter(buf, color, c_x+delta); - #endif - } - #ifdef PDHG_NORMAL_UV - u_a += PDHG_INCREMENT*u0_u3_du; - v_a += PDHG_INCREMENT*v0_v3_dv; - u_b += PDHG_INCREMENT*u1_u2_du; - v_b += PDHG_INCREMENT*v1_v2_dv; - #endif - #ifdef PDHG_RESTRICT_UV - v += PDHG_INCREMENT*v0_v1_dv; - #endif - delta += PDHG_INCREMENT*buf->width; - } + pax_col_t result = (shader_ctx.callback)( + color, + shader_ctx.do_getter ? buf2col(buf, buf->getter(buf, c_x + delta)) : 0, + c_x, + c_y, + u, + v, + shader_ctx.callback_args + ); + pax_set_index_conv(buf, result, c_x + delta); + u += u0_u1_du; +#endif +#ifdef PDHG_IGNORE_UV + pax_col_t result = (shader_ctx.callback)( + color, + shader_ctx.do_getter ? buf2col(buf, buf->getter(buf, c_x + delta)) : 0, + c_x, + c_y, + 0, + 0, + shader_ctx.callback_args + ); + pax_set_index_conv(buf, result, c_x + delta); +#endif +#ifndef PDHG_SHADED + setter(buf, color, c_x + delta); +#endif + } +#ifdef PDHG_NORMAL_UV + u_a += PDHG_INCREMENT * u0_u3_du; + v_a += PDHG_INCREMENT * v0_v3_dv; + u_b += PDHG_INCREMENT * u1_u2_du; + v_b += PDHG_INCREMENT * v1_v2_dv; +#endif +#ifdef PDHG_RESTRICT_UV + v += PDHG_INCREMENT * v0_v1_dv; +#endif + delta += PDHG_INCREMENT * buf->width; + } } #endif // PDHG_NAME diff --git a/src/helpers/pax_dh_generic_tri.hpp b/src/helpers/pax_dh_generic_tri.hpp index d89ba42..eba5f58 100644 --- a/src/helpers/pax_dh_generic_tri.hpp +++ b/src/helpers/pax_dh_generic_tri.hpp @@ -1,29 +1,8 @@ -/* - MIT License - - Copyright (c) 2021-2023 Julian Scheffers - - Permission is hereby granted, free of charge, to any person obtaining a copy - of this software and associated documentation files (the "Software"), to deal - in the Software without restriction, including without limitation the rights - to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - copies of the Software, and to permit persons to whom the Software is - furnished to do so, subject to the following conditions: - - The above copyright notice and this permission notice shall be included in all - copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - SOFTWARE. -*/ -#include "pax_internal.h" +// SPDX-License-Identifier: MIT + #include "pax_fixpt.hpp" +#include "pax_internal.h" #ifdef PDHG_NAME @@ -65,156 +44,189 @@ // Assumes y0 < y1. static void PDHG_TRAPEZOID_NAME( #ifdef PDHG_MCR - bool odd_scanline, + bool odd_scanline, #endif - pax_buf_t *buf, pax_col_t color, + pax_buf_t *buf, + pax_col_t color, #ifdef PDHG_SHADED - const pax_shader_t *shader, + pax_shader_t const *shader, #endif - fixpt_t x0a, fixpt_t x0b, fixpt_t y0, fixpt_t x1a, fixpt_t x1b, fixpt_t y1 + fixpt_t x0a, + fixpt_t x0b, + fixpt_t y0, + fixpt_t x1a, + fixpt_t x1b, + fixpt_t y1 #ifdef PDHG_NORMAL_UV - , fixpt_t u0a, fixpt_t v0a, fixpt_t u0b, fixpt_t v0b, fixpt_t u1a, fixpt_t v1a, fixpt_t u1b, fixpt_t v1b + , + fixpt_t u0a, + fixpt_t v0a, + fixpt_t u0b, + fixpt_t v0b, + fixpt_t u1a, + fixpt_t v1a, + fixpt_t u1b, + fixpt_t v1b #endif - ) { - - #ifdef PDHG_SHADED - // Get shader context. - pax_shader_ctx_t shader_ctx = pax_get_shader_ctx(buf, color, shader); - if (shader_ctx.skip) return; - pax_col_conv_t buf2col = PAX_IS_PALETTE(buf->type) ? pax_col_conv_dummy : buf->buf2col; - #else - // Get pixel setter. - pax_index_setter_t setter = pax_get_setter(buf, &color, NULL); - if (!setter) return; - #endif - - // Determine vertical bounds. - int iy0 = y0 + 0.5; - int iy1 = y1 + 0.5; - if (iy0 >= iy1) return; - - // Sort points by X. - if (x0a > x0b || x1a > x1b) { - PAX_SWAP(fixpt_t, x0a, x0b); - PAX_SWAP(fixpt_t, x1a, x1b); - #ifdef PDHG_NORMAL_UV - PAX_SWAP(fixpt_t, u0a, u0b); - PAX_SWAP(fixpt_t, u1a, u1b); - PAX_SWAP(fixpt_t, v0a, v0b); - PAX_SWAP(fixpt_t, v1a, v1b); - #endif - } - - // Clip: Y axis. - if (iy0 < buf->clip.y) { - iy0 = buf->clip.y; - } - if (iy0 >= buf->clip.y + buf->clip.h) { - return; - } - if (iy1 < buf->clip.y) { - return; - } - if (iy1 >= buf->clip.y + buf->clip.h) { - iy1 = buf->clip.y + buf->clip.h - 1; - } - - #ifdef PDHG_MCR - // Snap y to correct scanline. - if ((iy0 & 1) != odd_scanline) { - iy0 ++; - } - #endif - - // Determine X deltas. - fixpt_t x0a_x1a_dx = (x1a - x0a) / (y1 - y0); - fixpt_t x0b_x1b_dx = (x1b - x0b) / (y1 - y0); - - #ifdef PDHG_NORMAL_UV - // Determine UV deltas. - fixpt_t u0a_u1a_du = (u1a - u0a) / (y1 - y0); - fixpt_t u0b_u1b_du = (u1b - u0b) / (y1 - y0); - fixpt_t v0a_v1a_dv = (v1a - v0a) / (y1 - y0); - fixpt_t v0b_v1b_dv = (v1b - v0b) / (y1 - y0); - #endif - - // Initial X interpolation. - fixpt_t coeff = (iy0 + 0.5f) - y0; - fixpt_t x_a = x0a + x0a_x1a_dx * coeff; - fixpt_t x_b = x0b + x0b_x1b_dx * coeff; - - #ifdef PDHG_NORMAL_UV - // Initial UV interpolation. - fixpt_t u_a = u0a + u0a_u1a_du * coeff; - fixpt_t u_b = u0b + u0b_u1b_du * coeff; - fixpt_t v_a = v0a + v0a_v1a_dv * coeff; - fixpt_t v_b = v0b + v0b_v1b_dv * coeff; - #endif - - // Vertical drawing loop. - int delta = buf->width * iy0; - for (int y = iy0; y < iy1; y += PDHG_INCREMENT) { - int ixa = x_a + 0.5, ixb = x_b + 0.5; - - // Clip: X axis. - if (ixa < buf->clip.x) { - ixa = buf->clip.x; - } - if (ixa > buf->clip.x + buf->clip.w) { - ixa = buf->clip.x + buf->clip.w; - } - if (ixb < buf->clip.x) { - ixb = buf->clip.x; - } - if (ixb > buf->clip.x + buf->clip.w) { - ixb = buf->clip.x + buf->clip.w; - } - - #ifdef PDHG_NORMAL_UV - // Determine UV deltas. - fixpt_t du = (u_b - u_a) / (x_b - x_a); - fixpt_t dv = (v_b - v_a) / (x_b - x_a); - - // Interpolate UV. - coeff = (ixa + 0.5) - x_a; - fixpt_t u = u_a + du * coeff; - fixpt_t v = v_a + dv * coeff; - #endif - - // Horizontal drawing loop. - for (int x = ixa; x < ixb; x ++) { - #ifdef PDHG_NORMAL_UV - // Apply the shader, - pax_col_t result = (shader_ctx.callback)(color, shader_ctx.do_getter ? buf2col(buf, buf->getter(buf, x+delta)) : 0, x, y, u, v, shader_ctx.callback_args); - // And simply merge colors accordingly. - pax_set_index_conv(buf, result, x+delta); - u += du; - v += dv; - #elif defined(PDHG_SHADED) - // Apply the shader, - pax_col_t result = (shader_ctx.callback)(color, shader_ctx.do_getter ? buf2col(buf, buf->getter(buf, x+delta)) : 0, x, y, 0, 0, shader_ctx.callback_args); - // And simply merge colors accordingly. - pax_set_index_conv(buf, result, x+delta); - #else - // And simply merge colors accordingly. - setter(buf, color, x+delta); - #endif - } - - #ifdef PDHG_NORMAL_UV - // Interpolate UVs. - u_a += PDHG_INCREMENT*u0a_u1a_du; - u_b += PDHG_INCREMENT*u0b_u1b_du; - v_a += PDHG_INCREMENT*v0a_v1a_dv; - v_b += PDHG_INCREMENT*v0b_v1b_dv; - #endif - - // Interpolate X. - x_a += PDHG_INCREMENT*x0a_x1a_dx; - x_b += PDHG_INCREMENT*x0b_x1b_dx; - - delta += PDHG_INCREMENT*buf->width; - } +) { + +#ifdef PDHG_SHADED + // Get shader context. + pax_shader_ctx_t shader_ctx = pax_get_shader_ctx(buf, color, shader); + if (shader_ctx.skip) + return; + pax_col_conv_t buf2col = PAX_IS_PALETTE(buf->type) ? pax_col_conv_dummy : buf->buf2col; +#else + // Get pixel setter. + pax_index_setter_t setter = pax_get_setter(buf, &color, NULL); + if (!setter) + return; +#endif + + // Determine vertical bounds. + int iy0 = y0 + 0.5; + int iy1 = y1 + 0.5; + if (iy0 >= iy1) + return; + + // Sort points by X. + if (x0a > x0b || x1a > x1b) { + PAX_SWAP(fixpt_t, x0a, x0b); + PAX_SWAP(fixpt_t, x1a, x1b); +#ifdef PDHG_NORMAL_UV + PAX_SWAP(fixpt_t, u0a, u0b); + PAX_SWAP(fixpt_t, u1a, u1b); + PAX_SWAP(fixpt_t, v0a, v0b); + PAX_SWAP(fixpt_t, v1a, v1b); +#endif + } + + // Clip: Y axis. + if (iy0 < buf->clip.y) { + iy0 = buf->clip.y; + } + if (iy0 >= buf->clip.y + buf->clip.h) { + return; + } + if (iy1 < buf->clip.y) { + return; + } + if (iy1 >= buf->clip.y + buf->clip.h) { + iy1 = buf->clip.y + buf->clip.h - 1; + } + +#ifdef PDHG_MCR + // Snap y to correct scanline. + if ((iy0 & 1) != odd_scanline) { + iy0++; + } +#endif + + // Determine X deltas. + fixpt_t x0a_x1a_dx = (x1a - x0a) / (y1 - y0); + fixpt_t x0b_x1b_dx = (x1b - x0b) / (y1 - y0); + +#ifdef PDHG_NORMAL_UV + // Determine UV deltas. + fixpt_t u0a_u1a_du = (u1a - u0a) / (y1 - y0); + fixpt_t u0b_u1b_du = (u1b - u0b) / (y1 - y0); + fixpt_t v0a_v1a_dv = (v1a - v0a) / (y1 - y0); + fixpt_t v0b_v1b_dv = (v1b - v0b) / (y1 - y0); +#endif + + // Initial X interpolation. + fixpt_t coeff = (iy0 + 0.5f) - y0; + fixpt_t x_a = x0a + x0a_x1a_dx * coeff; + fixpt_t x_b = x0b + x0b_x1b_dx * coeff; + +#ifdef PDHG_NORMAL_UV + // Initial UV interpolation. + fixpt_t u_a = u0a + u0a_u1a_du * coeff; + fixpt_t u_b = u0b + u0b_u1b_du * coeff; + fixpt_t v_a = v0a + v0a_v1a_dv * coeff; + fixpt_t v_b = v0b + v0b_v1b_dv * coeff; +#endif + + // Vertical drawing loop. + int delta = buf->width * iy0; + for (int y = iy0; y < iy1; y += PDHG_INCREMENT) { + int ixa = x_a + 0.5, ixb = x_b + 0.5; + + // Clip: X axis. + if (ixa < buf->clip.x) { + ixa = buf->clip.x; + } + if (ixa > buf->clip.x + buf->clip.w) { + ixa = buf->clip.x + buf->clip.w; + } + if (ixb < buf->clip.x) { + ixb = buf->clip.x; + } + if (ixb > buf->clip.x + buf->clip.w) { + ixb = buf->clip.x + buf->clip.w; + } + +#ifdef PDHG_NORMAL_UV + // Determine UV deltas. + fixpt_t du = (u_b - u_a) / (x_b - x_a); + fixpt_t dv = (v_b - v_a) / (x_b - x_a); + + // Interpolate UV. + coeff = (ixa + 0.5) - x_a; + fixpt_t u = u_a + du * coeff; + fixpt_t v = v_a + dv * coeff; +#endif + + // Horizontal drawing loop. + for (int x = ixa; x < ixb; x++) { +#ifdef PDHG_NORMAL_UV + // Apply the shader, + pax_col_t result = (shader_ctx.callback)( + color, + shader_ctx.do_getter ? buf2col(buf, buf->getter(buf, x + delta)) : 0, + x, + y, + u, + v, + shader_ctx.callback_args + ); + // And simply merge colors accordingly. + pax_set_index_conv(buf, result, x + delta); + u += du; + v += dv; +#elif defined(PDHG_SHADED) + // Apply the shader, + pax_col_t result = (shader_ctx.callback)( + color, + shader_ctx.do_getter ? buf2col(buf, buf->getter(buf, x + delta)) : 0, + x, + y, + 0, + 0, + shader_ctx.callback_args + ); + // And simply merge colors accordingly. + pax_set_index_conv(buf, result, x + delta); +#else + // And simply merge colors accordingly. + setter(buf, color, x + delta); +#endif + } + +#ifdef PDHG_NORMAL_UV + // Interpolate UVs. + u_a += PDHG_INCREMENT * u0a_u1a_du; + u_b += PDHG_INCREMENT * u0b_u1b_du; + v_a += PDHG_INCREMENT * v0a_v1a_dv; + v_b += PDHG_INCREMENT * v0b_v1b_dv; +#endif + + // Interpolate X. + x_a += PDHG_INCREMENT * x0a_x1a_dx; + x_b += PDHG_INCREMENT * x0b_x1b_dx; + + delta += PDHG_INCREMENT * buf->width; + } } @@ -223,97 +235,138 @@ static void PDHG_TRAPEZOID_NAME( #ifdef PDHG_STATIC static #endif -void PDHG_NAME ( + void + PDHG_NAME( +#ifdef PDHG_MCR + bool odd_scanline, +#endif + pax_buf_t *buf, + pax_col_t color, +#ifdef PDHG_SHADED + pax_shader_t const *shader, +#endif + float _x0, + float _y0, + float _x1, + float _y1, + float _x2, + float _y2 +#ifdef PDHG_NORMAL_UV + , + float _u0, + float _v0, + float _u1, + float _v1, + float _u2, + float _v2 +#endif + ) { + + fixpt_t x0 = _x0; + fixpt_t y0 = _y0; + fixpt_t x1 = _x1; + fixpt_t y1 = _y1; + fixpt_t x2 = _x2; + fixpt_t y2 = _y2; +#ifdef PDHG_NORMAL_UV + fixpt_t u0 = _u0; + fixpt_t v0 = _v0; + fixpt_t u1 = _u1; + fixpt_t v1 = _v1; + fixpt_t u2 = _u2; + fixpt_t v2 = _v2; +#endif + + // Sort points by height. + if (y1 < y0) { + PAX_SWAP(fixpt_t, x0, x1) + PAX_SWAP(fixpt_t, y0, y1) +#ifdef PDHG_NORMAL_UV + PAX_SWAP(fixpt_t, u0, u1) + PAX_SWAP(fixpt_t, v0, v1) +#endif + } + if (y2 < y0) { + PAX_SWAP(fixpt_t, x0, x2) + PAX_SWAP(fixpt_t, y0, y2) +#ifdef PDHG_NORMAL_UV + PAX_SWAP(fixpt_t, u0, u2) + PAX_SWAP(fixpt_t, v0, v2) +#endif + } + if (y2 < y1) { + PAX_SWAP(fixpt_t, x1, x2) + PAX_SWAP(fixpt_t, y1, y2) +#ifdef PDHG_NORMAL_UV + PAX_SWAP(fixpt_t, u1, u2) + PAX_SWAP(fixpt_t, v1, v2) +#endif + } + + // Interpolate coordinates. + fixpt_t coeff = (y1 - y0) / (y2 - y0); + fixpt_t x1b = x0 + (x2 - x0) * coeff; +#ifdef PDHG_NORMAL_UV + fixpt_t u1b = u0 + (u2 - u0) * coeff; + fixpt_t v1b = v0 + (v2 - v0) * coeff; +#endif + + // Top half. + PDHG_TRAPEZOID_NAME( +#ifdef PDHG_MCR + odd_scanline, +#endif + buf, + color, +#ifdef PDHG_SHADED + shader, +#endif + x0, + x0, + y0, + x1, + x1b, + y1 +#ifdef PDHG_NORMAL_UV + , + u0, + v0, + u0, + v0, + u1, + v1, + u1b, + v1b +#endif + ); + // Bottom half. + PDHG_TRAPEZOID_NAME( #ifdef PDHG_MCR - bool odd_scanline, + odd_scanline, #endif - pax_buf_t *buf, pax_col_t color, + buf, + color, #ifdef PDHG_SHADED - const pax_shader_t *shader, + shader, #endif - float _x0, float _y0, float _x1, float _y1, float _x2, float _y2 + x1, + x1b, + y1, + x2, + x2, + y2 #ifdef PDHG_NORMAL_UV - , float _u0, float _v0, float _u1, float _v1, float _u2, float _v2 + , + u1, + v1, + u1b, + v1b, + u2, + v2, + u2, + v2 #endif - ) { - - fixpt_t x0 = _x0; - fixpt_t y0 = _y0; - fixpt_t x1 = _x1; - fixpt_t y1 = _y1; - fixpt_t x2 = _x2; - fixpt_t y2 = _y2; - #ifdef PDHG_NORMAL_UV - fixpt_t u0 = _u0; - fixpt_t v0 = _v0; - fixpt_t u1 = _u1; - fixpt_t v1 = _v1; - fixpt_t u2 = _u2; - fixpt_t v2 = _v2; - #endif - - // Sort points by height. - if (y1 < y0) { - PAX_SWAP(fixpt_t, x0, x1) - PAX_SWAP(fixpt_t, y0, y1) - #ifdef PDHG_NORMAL_UV - PAX_SWAP(fixpt_t, u0, u1) - PAX_SWAP(fixpt_t, v0, v1) - #endif - } - if (y2 < y0) { - PAX_SWAP(fixpt_t, x0, x2) - PAX_SWAP(fixpt_t, y0, y2) - #ifdef PDHG_NORMAL_UV - PAX_SWAP(fixpt_t, u0, u2) - PAX_SWAP(fixpt_t, v0, v2) - #endif - } - if (y2 < y1) { - PAX_SWAP(fixpt_t, x1, x2) - PAX_SWAP(fixpt_t, y1, y2) - #ifdef PDHG_NORMAL_UV - PAX_SWAP(fixpt_t, u1, u2) - PAX_SWAP(fixpt_t, v1, v2) - #endif - } - - // Interpolate coordinates. - fixpt_t coeff = (y1 - y0) / (y2 - y0); - fixpt_t x1b = x0 + (x2 - x0) * coeff; - #ifdef PDHG_NORMAL_UV - fixpt_t u1b = u0 + (u2 - u0) * coeff; - fixpt_t v1b = v0 + (v2 - v0) * coeff; - #endif - - // Top half. - PDHG_TRAPEZOID_NAME( - #ifdef PDHG_MCR - odd_scanline, - #endif - buf, color, - #ifdef PDHG_SHADED - shader, - #endif - x0, x0, y0, x1, x1b, y1 - #ifdef PDHG_NORMAL_UV - , u0, v0, u0, v0, u1, v1, u1b, v1b - #endif - ); - // Bottom half. - PDHG_TRAPEZOID_NAME( - #ifdef PDHG_MCR - odd_scanline, - #endif - buf, color, - #ifdef PDHG_SHADED - shader, - #endif - x1, x1b, y1, x2, x2, y2 - #ifdef PDHG_NORMAL_UV - , u1, v1, u1b, v1b, u2, v2, u2, v2 - #endif - ); + ); } diff --git a/src/helpers/pax_dh_mcr_shaded.cpp b/src/helpers/pax_dh_mcr_shaded.cpp index 93bb44f..7c4f11a 100644 --- a/src/helpers/pax_dh_mcr_shaded.cpp +++ b/src/helpers/pax_dh_mcr_shaded.cpp @@ -1,28 +1,8 @@ -/* - MIT License - - Copyright (c) 2021-2023 Julian Scheffers - - Permission is hereby granted, free of charge, to any person obtaining a copy - of this software and associated documentation files (the "Software"), to deal - in the Software without restriction, including without limitation the rights - to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - copies of the Software, and to permit persons to whom the Software is - furnished to do so, subject to the following conditions: - - The above copyright notice and this permission notice shall be included in all - copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - SOFTWARE. -*/ + +// SPDX-License-Identifier: MIT #include "pax_internal.h" + #include /* ======== SHADED DRAWING ======= */ @@ -46,109 +26,128 @@ // Multi-core method for shaded triangles. // If odd_scanline is true, the odd (counted from 0) lines are drawn, otherwise the even lines are drawn. -void paxmcr_tri_shaded(bool odd_scanline, pax_buf_t *buf, pax_col_t color, const pax_shader_t *shader, - float x0, float y0, float x1, float y1, float x2, float y2, - float u0, float v0, float u1, float v1, float u2, float v2) { - if (shader->promise_callback) { - uint32_t promises = (*(pax_promise_func_t) shader->promise_callback)(buf, color, shader->callback_args); - if (promises & PAX_PROMISE_INVISIBLE) return; - if (promises & PAX_PROMISE_IGNORE_UVS) { - paxmcr_tri_shaded1(odd_scanline, buf, color, shader, x0, y0, x1, y1, x2, y2); - return; - } - } - paxmcr_tri_shaded0(odd_scanline, buf, color, shader, x0, y0, x1, y1, x2, y2, u0, v0, u1, v1, u2, v2); +void paxmcr_tri_shaded( + bool odd_scanline, + pax_buf_t *buf, + pax_col_t color, + pax_shader_t const *shader, + float x0, + float y0, + float x1, + float y1, + float x2, + float y2, + float u0, + float v0, + float u1, + float v1, + float u2, + float v2 +) { + if (shader->promise_callback) { + uint32_t promises = (*(pax_promise_func_t)shader->promise_callback)(buf, color, shader->callback_args); + if (promises & PAX_PROMISE_INVISIBLE) + return; + if (promises & PAX_PROMISE_IGNORE_UVS) { + paxmcr_tri_shaded1(odd_scanline, buf, color, shader, x0, y0, x1, y1, x2, y2); + return; + } + } + paxmcr_tri_shaded0(odd_scanline, buf, color, shader, x0, y0, x1, y1, x2, y2, u0, v0, u1, v1, u2, v2); } // Multi-core optimisation which maps a buffer directly onto another. // If odd_scanline is true, the odd (counted from 0) lines are drawn, otherwise the even lines are drawn. -void paxmcr_overlay_buffer(bool odd_scanline, pax_buf_t *base, pax_buf_t *top, int x, int y, int width, int height, bool assume_opaque) { - int tex_x = 0, tex_y = 0; - - // Perform clipping. - if (x < base->clip.x) { - tex_x = base->clip.x - x; - width -= tex_x; - x = base->clip.x; - } - if (x + width > base->clip.x + base->clip.w) { - width = base->clip.x + base->clip.w - x; - } - if (y < base->clip.y) { - tex_y = base->clip.y - y; - height -= tex_y; - y = base->clip.y; - } - if (y + height > base->clip.y + base->clip.h) { - height = base->clip.y + base->clip.h - y; - } - - bool equal = top->type == base->type; - if (equal && x == 0 && y == 0 && width == base->width && height == base->height && base->reverse_endianness == top->reverse_endianness) { - // When copying one buffer onto another as a background, - // and the types are the same, perform a memcpy() instead. - // memcpy(base->buf, top->buf, (PAX_GET_BPP(base->type) * width * height + 7) >> 3); - // return; - } - // Fix Y co-ordinates. - if ((y & 1) != odd_scanline) { - y ++; - tex_y ++; - } - - // Check alpha channel presence. - if (!PAX_IS_ALPHA(top->type)) { - assume_opaque = true; - } - - // Now, let us MAP. - int top_delta = tex_y * top->width; - int base_delta = y * base->width; - if (assume_opaque) { - if (equal) { - // Equal types and alpha. - for (int c_y = odd_scanline; c_y < height; c_y += 2) { - for (int c_x = 0; c_x < width; c_x++) { - pax_col_t col = top->getter(top, tex_x+top_delta); - base->setter(base, col, x+base_delta); - tex_x ++; - x ++; - } - tex_x -= width; - x -= width; - top_delta += 2*top->width; - base_delta += 2*base->width; - } - } else { - // Not equal types, but no alpha. - for (int c_y = odd_scanline; c_y < height; c_y += 2) { - for (int c_x = 0; c_x < width; c_x++) { - pax_col_t col = top->buf2col(top, top->getter(top, tex_x+top_delta)); - base->setter(base, base->col2buf(base, col), x+base_delta); - tex_x ++; - x ++; - } - tex_x -= width; - x -= width; - top_delta += 2*top->width; - base_delta += 2*base->width; - } - } - } else { - // With alpha. - for (int c_y = odd_scanline; c_y < height; c_y += 2) { - for (int c_x = 0; c_x < width; c_x++) { - pax_col_t col = top->buf2col(top, top->getter(top, tex_x+top_delta)); - pax_merge_index(base, col, x+base_delta); - tex_x ++; - x ++; - } - tex_x -= width; - x -= width; - top_delta += 2*top->width; - base_delta += 2*base->width; - } - } +void paxmcr_overlay_buffer( + bool odd_scanline, pax_buf_t *base, pax_buf_t *top, int x, int y, int width, int height, bool assume_opaque +) { + int tex_x = 0, tex_y = 0; + + // Perform clipping. + if (x < base->clip.x) { + tex_x = base->clip.x - x; + width -= tex_x; + x = base->clip.x; + } + if (x + width > base->clip.x + base->clip.w) { + width = base->clip.x + base->clip.w - x; + } + if (y < base->clip.y) { + tex_y = base->clip.y - y; + height -= tex_y; + y = base->clip.y; + } + if (y + height > base->clip.y + base->clip.h) { + height = base->clip.y + base->clip.h - y; + } + + bool equal = top->type == base->type; + if (equal && x == 0 && y == 0 && width == base->width && height == base->height && + base->reverse_endianness == top->reverse_endianness) { + // When copying one buffer onto another as a background, + // and the types are the same, perform a memcpy() instead. + // memcpy(base->buf, top->buf, (PAX_GET_BPP(base->type) * width * height + 7) >> 3); + // return; + } + // Fix Y co-ordinates. + if ((y & 1) != odd_scanline) { + y++; + tex_y++; + } + + // Check alpha channel presence. + if (!PAX_IS_ALPHA(top->type)) { + assume_opaque = true; + } + + // Now, let us MAP. + int top_delta = tex_y * top->width; + int base_delta = y * base->width; + if (assume_opaque) { + if (equal) { + // Equal types and alpha. + for (int c_y = odd_scanline; c_y < height; c_y += 2) { + for (int c_x = 0; c_x < width; c_x++) { + pax_col_t col = top->getter(top, tex_x + top_delta); + base->setter(base, col, x + base_delta); + tex_x++; + x++; + } + tex_x -= width; + x -= width; + top_delta += 2 * top->width; + base_delta += 2 * base->width; + } + } else { + // Not equal types, but no alpha. + for (int c_y = odd_scanline; c_y < height; c_y += 2) { + for (int c_x = 0; c_x < width; c_x++) { + pax_col_t col = top->buf2col(top, top->getter(top, tex_x + top_delta)); + base->setter(base, base->col2buf(base, col), x + base_delta); + tex_x++; + x++; + } + tex_x -= width; + x -= width; + top_delta += 2 * top->width; + base_delta += 2 * base->width; + } + } + } else { + // With alpha. + for (int c_y = odd_scanline; c_y < height; c_y += 2) { + for (int c_x = 0; c_x < width; c_x++) { + pax_col_t col = top->buf2col(top, top->getter(top, tex_x + top_delta)); + pax_merge_index(base, col, x + base_delta); + tex_x++; + x++; + } + tex_x -= width; + x -= width; + top_delta += 2 * top->width; + base_delta += 2 * base->width; + } + } } // Multi-core optimisation which does not have UVs. @@ -179,34 +178,58 @@ void paxmcr_overlay_buffer(bool odd_scanline, pax_buf_t *base, pax_buf_t *top, i // Multi-core method for shaded rects. // If odd_scanline is true, the odd (counted from 0) lines are drawn, otherwise the even lines are drawn. -void paxmcr_rect_shaded(bool odd_scanline, pax_buf_t *buf, pax_col_t color, const pax_shader_t *shader, - float x, float y, float width, float height, - float u0, float v0, float u1, float v1, float u2, float v2, float u3, float v3) { - - pax_promise_func_t fn = (pax_promise_func_t) shader->promise_callback; - uint32_t promise = fn ? fn(buf, color, shader->callback_args) : 0; - - if (promise & PAX_PROMISE_IGNORE_UVS) { - // Ignore UVs. - paxmcr_rect_shaded2(odd_scanline, buf, color, shader, x, y, width, height); - return; - } - - bool is_default_uv = u0 == 0 && v0 == 0 && u1 == 1 && v1 == 0 && u2 == 1 && v2 == 1 && u3 == 0 && v3 == 1; - - if ((shader->callback == pax_shader_texture || shader->callback == pax_shader_texture_aa) && color == 0xffffffff) { - // Use a more direct copying of textures. - pax_buf_t *top = (pax_buf_t *) shader->callback_args; - if (is_default_uv && (int) (width + 0.5) == top->width && (int) (height + 0.5) == top->height) { - paxmcr_overlay_buffer(odd_scanline, buf, top, x + 0.5, y + 0.5, width + 0.5, height + 0.5, shader->alpha_promise_255); - return; - } - } else if (is_default_uv || (v0 == v1 && v2 == v3 && u0 == u3 && u1 == u2)) { - // Make some assumptions about UVs. - paxmcr_rect_shaded1(odd_scanline, buf, color, shader, x, y, width, height, u0, v0, u2, v2); - return; - } - - // Use the more expensive generic implementation. - paxmcr_rect_shaded0(odd_scanline, buf, color, shader, x, y, width, height, u0, v0, u1, v1, u2, v2, u3, v3); +void paxmcr_rect_shaded( + bool odd_scanline, + pax_buf_t *buf, + pax_col_t color, + pax_shader_t const *shader, + float x, + float y, + float width, + float height, + float u0, + float v0, + float u1, + float v1, + float u2, + float v2, + float u3, + float v3 +) { + + pax_promise_func_t fn = (pax_promise_func_t)shader->promise_callback; + uint32_t promise = fn ? fn(buf, color, shader->callback_args) : 0; + + if (promise & PAX_PROMISE_IGNORE_UVS) { + // Ignore UVs. + paxmcr_rect_shaded2(odd_scanline, buf, color, shader, x, y, width, height); + return; + } + + bool is_default_uv = u0 == 0 && v0 == 0 && u1 == 1 && v1 == 0 && u2 == 1 && v2 == 1 && u3 == 0 && v3 == 1; + + if ((shader->callback == pax_shader_texture || shader->callback == pax_shader_texture_aa) && color == 0xffffffff) { + // Use a more direct copying of textures. + pax_buf_t *top = (pax_buf_t *)shader->callback_args; + if (is_default_uv && (int)(width + 0.5) == top->width && (int)(height + 0.5) == top->height) { + paxmcr_overlay_buffer( + odd_scanline, + buf, + top, + x + 0.5, + y + 0.5, + width + 0.5, + height + 0.5, + shader->alpha_promise_255 + ); + return; + } + } else if (is_default_uv || (v0 == v1 && v2 == v3 && u0 == u3 && u1 == u2)) { + // Make some assumptions about UVs. + paxmcr_rect_shaded1(odd_scanline, buf, color, shader, x, y, width, height, u0, v0, u2, v2); + return; + } + + // Use the more expensive generic implementation. + paxmcr_rect_shaded0(odd_scanline, buf, color, shader, x, y, width, height, u0, v0, u1, v1, u2, v2, u3, v3); } diff --git a/src/helpers/pax_dh_mcr_unshaded.cpp b/src/helpers/pax_dh_mcr_unshaded.cpp index 3176d16..df1c2a3 100644 --- a/src/helpers/pax_dh_mcr_unshaded.cpp +++ b/src/helpers/pax_dh_mcr_unshaded.cpp @@ -1,26 +1,5 @@ -/* - MIT License - Copyright (c) 2021-2023 Julian Scheffers - - Permission is hereby granted, free of charge, to any person obtaining a copy - of this software and associated documentation files (the "Software"), to deal - in the Software without restriction, including without limitation the rights - to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - copies of the Software, and to permit persons to whom the Software is - furnished to do so, subject to the following conditions: - - The above copyright notice and this permission notice shall be included in all - copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - SOFTWARE. -*/ +// SPDX-License-Identifier: MIT #include "pax_internal.h" diff --git a/src/helpers/pax_dh_shaded.cpp b/src/helpers/pax_dh_shaded.cpp index cb63080..8be48d3 100644 --- a/src/helpers/pax_dh_shaded.cpp +++ b/src/helpers/pax_dh_shaded.cpp @@ -1,28 +1,8 @@ -/* - MIT License - - Copyright (c) 2021-2023 Julian Scheffers - - Permission is hereby granted, free of charge, to any person obtaining a copy - of this software and associated documentation files (the "Software"), to deal - in the Software without restriction, including without limitation the rights - to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - copies of the Software, and to permit persons to whom the Software is - furnished to do so, subject to the following conditions: - - The above copyright notice and this permission notice shall be included in all - copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - SOFTWARE. -*/ + +// SPDX-License-Identifier: MIT #include "pax_internal.h" + #include /* ======== SHADED DRAWING ======= */ @@ -42,105 +22,121 @@ #include "pax_dh_generic_tri.hpp" // Internal method for shaded triangles. -void pax_tri_shaded(pax_buf_t *buf, pax_col_t color, const pax_shader_t *shader, - float x0, float y0, float x1, float y1, float x2, float y2, - float u0, float v0, float u1, float v1, float u2, float v2) { - if (shader->promise_callback) { - uint32_t promises = (*(pax_promise_func_t) shader->promise_callback)(buf, color, shader->callback_args); - if (promises & PAX_PROMISE_INVISIBLE) return; - if (promises & PAX_PROMISE_IGNORE_UVS) { - pax_tri_shaded1(buf, color, shader, x0, y0, x1, y1, x2, y2); - return; - } - } - pax_tri_shaded0(buf, color, shader, x0, y0, x1, y1, x2, y2, u0, v0, u1, v1, u2, v2); +void pax_tri_shaded( + pax_buf_t *buf, + pax_col_t color, + pax_shader_t const *shader, + float x0, + float y0, + float x1, + float y1, + float x2, + float y2, + float u0, + float v0, + float u1, + float v1, + float u2, + float v2 +) { + if (shader->promise_callback) { + uint32_t promises = (*(pax_promise_func_t)shader->promise_callback)(buf, color, shader->callback_args); + if (promises & PAX_PROMISE_INVISIBLE) + return; + if (promises & PAX_PROMISE_IGNORE_UVS) { + pax_tri_shaded1(buf, color, shader, x0, y0, x1, y1, x2, y2); + return; + } + } + pax_tri_shaded0(buf, color, shader, x0, y0, x1, y1, x2, y2, u0, v0, u1, v1, u2, v2); } // Optimisation which maps a buffer directly onto another. // If assume_opaque is true, the overlay is done without transparency. void pax_overlay_buffer(pax_buf_t *base, pax_buf_t *top, int x, int y, int width, int height, bool assume_opaque) { - int tex_x = 0, tex_y = 0; - - // Perform clipping. - if (x < base->clip.x) { - tex_x = base->clip.x - x; - width -= tex_x; - x = base->clip.x; - } - if (x + width > base->clip.x + base->clip.w) { - width = base->clip.x + base->clip.w - x; - } - if (y < base->clip.y) { - tex_y = base->clip.y - y; - height -= tex_y; - y = base->clip.y; - } - if (y + height > base->clip.y + base->clip.h) { - height = base->clip.y + base->clip.h - y; - } - - bool equal = top->type == base->type; - - if (equal && x == 0 && y == 0 && width == base->width && height == base->height && base->reverse_endianness == top->reverse_endianness) { - // When copying one buffer onto another as a background, - // and the types are the same, perform a memcpy() instead. - memcpy(base->buf, top->buf, (PAX_GET_BPP(base->type) * width * height + 7) >> 3); - return; - } - - // Check alpha channel presence. - if (!PAX_IS_ALPHA(top->type)) { - assume_opaque = true; - } - - // Now, let us MAP. - int top_delta = tex_y * top->width; - int base_delta = y * base->width; - if (assume_opaque) { - if (equal) { - // Equal types and alpha. - for (int c_y = 0; c_y < height; c_y++) { - for (int c_x = 0; c_x < width; c_x++) { - pax_col_t col = top->getter(top, tex_x+top_delta); - base->setter(base, col, x+base_delta); - tex_x ++; - x ++; - } - tex_x -= width; - x -= width; - top_delta += top->width; - base_delta += base->width; - } - } else { - // Not equal types, but no alpha. - for (int c_y = 0; c_y < height; c_y++) { - for (int c_x = 0; c_x < width; c_x++) { - pax_col_t col = top->buf2col(top, top->getter(top, tex_x+top_delta)); - base->setter(base, base->col2buf(base, col), x+base_delta); - tex_x ++; - x ++; - } - tex_x -= width; - x -= width; - top_delta += top->width; - base_delta += base->width; - } - } - } else { - // With alpha. - for (int c_y = 0; c_y < height; c_y++) { - for (int c_x = 0; c_x < width; c_x++) { - pax_col_t col = top->buf2col(top, top->getter(top, tex_x+top_delta)); - pax_merge_index(base, col, x+base_delta); - tex_x ++; - x ++; - } - tex_x -= width; - x -= width; - top_delta += top->width; - base_delta += base->width; - } - } + int tex_x = 0, tex_y = 0; + + // Perform clipping. + if (x < base->clip.x) { + tex_x = base->clip.x - x; + width -= tex_x; + x = base->clip.x; + } + if (x + width > base->clip.x + base->clip.w) { + width = base->clip.x + base->clip.w - x; + } + if (y < base->clip.y) { + tex_y = base->clip.y - y; + height -= tex_y; + y = base->clip.y; + } + if (y + height > base->clip.y + base->clip.h) { + height = base->clip.y + base->clip.h - y; + } + + bool equal = top->type == base->type; + + if (equal && x == 0 && y == 0 && width == base->width && height == base->height && + base->reverse_endianness == top->reverse_endianness) { + // When copying one buffer onto another as a background, + // and the types are the same, perform a memcpy() instead. + memcpy(base->buf, top->buf, (PAX_GET_BPP(base->type) * width * height + 7) >> 3); + return; + } + + // Check alpha channel presence. + if (!PAX_IS_ALPHA(top->type)) { + assume_opaque = true; + } + + // Now, let us MAP. + int top_delta = tex_y * top->width; + int base_delta = y * base->width; + if (assume_opaque) { + if (equal) { + // Equal types and alpha. + for (int c_y = 0; c_y < height; c_y++) { + for (int c_x = 0; c_x < width; c_x++) { + pax_col_t col = top->getter(top, tex_x + top_delta); + base->setter(base, col, x + base_delta); + tex_x++; + x++; + } + tex_x -= width; + x -= width; + top_delta += top->width; + base_delta += base->width; + } + } else { + // Not equal types, but no alpha. + for (int c_y = 0; c_y < height; c_y++) { + for (int c_x = 0; c_x < width; c_x++) { + pax_col_t col = top->buf2col(top, top->getter(top, tex_x + top_delta)); + base->setter(base, base->col2buf(base, col), x + base_delta); + tex_x++; + x++; + } + tex_x -= width; + x -= width; + top_delta += top->width; + base_delta += base->width; + } + } + } else { + // With alpha. + for (int c_y = 0; c_y < height; c_y++) { + for (int c_x = 0; c_x < width; c_x++) { + pax_col_t col = top->buf2col(top, top->getter(top, tex_x + top_delta)); + pax_merge_index(base, col, x + base_delta); + tex_x++; + x++; + } + tex_x -= width; + x -= width; + top_delta += top->width; + base_delta += base->width; + } + } } // Optimisation which has no UVs. @@ -164,172 +160,229 @@ void pax_overlay_buffer(pax_buf_t *base, pax_buf_t *top, int x, int y, int width #include "pax_dh_generic_rect.hpp" // Internal method for shaded rects. -void pax_rect_shaded(pax_buf_t *buf, pax_col_t color, const pax_shader_t *shader, - float x, float y, float width, float height, - float u0, float v0, float u1, float v1, float u2, float v2, float u3, float v3) { - - pax_promise_func_t fn = (pax_promise_func_t) shader->promise_callback; - uint32_t promise = fn ? fn(buf, color, shader->callback_args) : 0; - - if (promise & PAX_PROMISE_IGNORE_UVS) { - // Ignore UVs. - pax_rect_shaded2(buf, color, shader, x, y, width, height); - return; - } - - bool is_default_uv = u0 == 0 && v0 == 0 && u1 == 1 && v1 == 0 && u2 == 1 && v2 == 1 && u3 == 0 && v3 == 1; - - if ((shader->callback == pax_shader_texture || shader->callback == pax_shader_texture_aa) && color == 0xffffffff) { - // Use a more direct copying of textures. - pax_buf_t *top = (pax_buf_t *) shader->callback_args; - if (is_default_uv && (int) (width + 0.5) == top->width && (int) (height + 0.5) == top->height) { - pax_overlay_buffer(buf, top, x + 0.5, y + 0.5, width + 0.5, height + 0.5, shader->alpha_promise_255); - return; - } - } else if (is_default_uv || (v0 == v1 && v2 == v3 && u0 == u3 && u1 == u2)) { - // Make some assumptions about UVs. - pax_rect_shaded1(buf, color, shader, x, y, width, height, u0, v0, u2, v2); - return; - } - - // Use the more expensive generic implementation. - pax_rect_shaded0(buf, color, shader, x, y, width, height, u0, v0, u1, v1, u2, v2, u3, v3); +void pax_rect_shaded( + pax_buf_t *buf, + pax_col_t color, + pax_shader_t const *shader, + float x, + float y, + float width, + float height, + float u0, + float v0, + float u1, + float v1, + float u2, + float v2, + float u3, + float v3 +) { + + pax_promise_func_t fn = (pax_promise_func_t)shader->promise_callback; + uint32_t promise = fn ? fn(buf, color, shader->callback_args) : 0; + + if (promise & PAX_PROMISE_IGNORE_UVS) { + // Ignore UVs. + pax_rect_shaded2(buf, color, shader, x, y, width, height); + return; + } + + bool is_default_uv = u0 == 0 && v0 == 0 && u1 == 1 && v1 == 0 && u2 == 1 && v2 == 1 && u3 == 0 && v3 == 1; + + if ((shader->callback == pax_shader_texture || shader->callback == pax_shader_texture_aa) && color == 0xffffffff) { + // Use a more direct copying of textures. + pax_buf_t *top = (pax_buf_t *)shader->callback_args; + if (is_default_uv && (int)(width + 0.5) == top->width && (int)(height + 0.5) == top->height) { + pax_overlay_buffer(buf, top, x + 0.5, y + 0.5, width + 0.5, height + 0.5, shader->alpha_promise_255); + return; + } + } else if (is_default_uv || (v0 == v1 && v2 == v3 && u0 == u3 && u1 == u2)) { + // Make some assumptions about UVs. + pax_rect_shaded1(buf, color, shader, x, y, width, height, u0, v0, u2, v2); + return; + } + + // Use the more expensive generic implementation. + pax_rect_shaded0(buf, color, shader, x, y, width, height, u0, v0, u1, v1, u2, v2, u3, v3); } // Internal method for line drawing. -void pax_line_shaded(pax_buf_t *buf, pax_col_t color, const pax_shader_t *shader, float u0, float v0, float u1, float v1, float x0, float y0, float x1, float y1) { - - pax_shader_ctx_t shader_ctx = pax_get_shader_ctx(buf, color, shader); - if (shader_ctx.skip) return; - pax_col_conv_t buf2col = PAX_IS_PALETTE(buf->type) ? pax_col_conv_dummy : buf->buf2col; - - // Sort points vertially. - if (y0 > y1) { - PAX_SWAP(float, x0, x1) - PAX_SWAP(float, y0, y1) - PAX_SWAP(float, u0, u1) - PAX_SWAP(float, v0, v1) - } - - // Determine whether the line might fall within the clip rect. - if (!buf->clip.w || !buf->clip.h) return; - if (y1 < buf->clip.y || y0 > buf->clip.y + buf->clip.h - 1) return; - if (x0 == x1 && (x0 < buf->clip.x || x0 > buf->clip.x + buf->clip.w - 1)) return; - if (x0 < buf->clip.x && x1 < buf->clip.x) return; - if (x0 > buf->clip.x + buf->clip.w - 1 && x1 > buf->clip.x + buf->clip.w - 1) return; - - // Clip top. - if (y0 < buf->clip.y) { - float coeff = (buf->clip.y - y0) / (y1 - y0); - u0 = u0 + (u1 - u0) * coeff; - v0 = v0 + (v1 - v0) * coeff; - x0 = x0 + (x1 - x0) * coeff; - y0 = buf->clip.y; - } - // Clip bottom. - if (y1 > buf->clip.y + buf->clip.h - 1) { - float coeff = (buf->clip.y + buf->clip.h - 1 - y0) / (y1 - y0); - u1 = u0 + (u1 - u0) * coeff; - v1 = v0 + (v1 - v0) * coeff; - x1 = x0 + (x1 - x0) * coeff; - y1 = buf->clip.y + buf->clip.h - 1; - } - // Clip left. - if (x1 < buf->clip.x) { - float coeff = (buf->clip.x - x0) / (x1 - x0); - u1 = u0 + (u1 - u0) * coeff; - v1 = v0 + (v1 - v0) * coeff; - y1 = y0 + (y1 - y0) * coeff; - x1 = buf->clip.x; - - } else if (x0 < buf->clip.x) { - float coeff = (buf->clip.x - x0) / (x1 - x0); - u0 = u0 + (u1 - u0) * coeff; - v0 = v0 + (v1 - v0) * coeff; - y0 = y0 + (y1 - y0) * coeff; - x0 = buf->clip.x; - } - // Clip right. - if (x1 > buf->clip.x + buf->clip.w - 1) { - float coeff = (buf->clip.x + buf->clip.w - 1 - x0) / (x1 - x0); - u1 = u0 + (u1 - u0) * coeff; - v1 = v0 + (v1 - v0) * coeff; - y1 = y0 + (y1 - y0) * coeff; - x1 = buf->clip.x + buf->clip.w - 1; - - } else if (x0 > buf->clip.x + buf->clip.w - 1) { - float coeff = (buf->clip.x + buf->clip.w - 1 - x0) / (x1 - x0); - u0 = u0 + (u1 - u0) * coeff; - v0 = v0 + (v1 - v0) * coeff; - y0 = y0 + (y1 - y0) * coeff; - x0 = buf->clip.x + buf->clip.w - 1; - } - - // Determine whether the line is "steep" (dx*dx > dy*dy). - float dx = x1 - x0; - float dy = y1 - y0; - bool is_steep = fabsf(dx) < fabsf(dy); - int nIter; - - // Determine the number of iterations. - nIter = ceilf(fabsf(is_steep ? dy : dx)); - if (nIter < 1) nIter = 1; - - // Adjust dx and dy. - dx /= nIter; - dy /= nIter; - - if (y0 == y1) { - // Horizontal line. - int index = (int) y0 * buf->width; - if (dx < 0) { - PAX_SWAP(float, x0, x1); - PAX_SWAP(float, u0, u1) - PAX_SWAP(float, v0, v1) - } - nIter = (int) x1 - (int) x0 + 1; - float u = u0, v = v0; - float du = (u1 - u0) / nIter; - float dv = (v1 - v0) / nIter; - for (int i = x0; i <= x1; i++) { - pax_col_t result = (shader_ctx.callback)(color, shader_ctx.do_getter ? buf2col(buf, buf->getter(buf, index + i)) : 0, i, y0, u, v, shader_ctx.callback_args); - pax_set_index_conv(buf, result, index + i); - u += du; - v += dv; - } - } else if (x0 == x1) { - // Vertical line. - int index = x0 + (int) y0 * buf->width; - nIter = (int) y1 - (int) y0 + 1; - float u = u0, v = v0; - float du = (u1 - u0) / nIter; - float dv = (v1 - v0) / nIter; - for (int i = y0; i <= y1; i++, index += buf->width) { - pax_col_t result = (shader_ctx.callback)(color, shader_ctx.do_getter ? buf2col(buf, buf->getter(buf, index)) : 0, x0, i, u, v, shader_ctx.callback_args); - pax_set_index_conv(buf, result, index); - u += du; - v += dv; - } - } else { - // Any other line. - float du = (u1 - u0) / nIter; - float dv = (v1 - v0) / nIter; - int_fast32_t x = x0 * 0x10000; - int_fast32_t y = y0 * 0x10000; - int_fast32_t idx = dx * 0x10000; - int_fast32_t idy = dy * 0x10000; - int_fast32_t u = u0 * 0x10000; - int_fast32_t v = v0 * 0x10000; - int_fast32_t idu = du * 0x10000; - int_fast32_t idv = dv * 0x10000; - for (int i = 0; i <= nIter; i++) { - size_t delta = (x>>16)+(y>>16)*buf->width; - pax_col_t result = (shader_ctx.callback)(color, shader_ctx.do_getter ? buf2col(buf, buf->getter(buf, delta)) : 0, x>>16, y>>16, u/(float)0x10000, v/(float)0x10000, shader_ctx.callback_args); - pax_set_index_conv(buf, result, delta); - x += idx; - y += idy; - u += idu; - v += idv; - } - } +void pax_line_shaded( + pax_buf_t *buf, + pax_col_t color, + pax_shader_t const *shader, + float u0, + float v0, + float u1, + float v1, + float x0, + float y0, + float x1, + float y1 +) { + + pax_shader_ctx_t shader_ctx = pax_get_shader_ctx(buf, color, shader); + if (shader_ctx.skip) + return; + pax_col_conv_t buf2col = PAX_IS_PALETTE(buf->type) ? pax_col_conv_dummy : buf->buf2col; + + // Sort points vertially. + if (y0 > y1) { + PAX_SWAP(float, x0, x1) + PAX_SWAP(float, y0, y1) + PAX_SWAP(float, u0, u1) + PAX_SWAP(float, v0, v1) + } + + // Determine whether the line might fall within the clip rect. + if (!buf->clip.w || !buf->clip.h) + return; + if (y1 < buf->clip.y || y0 > buf->clip.y + buf->clip.h - 1) + return; + if (x0 == x1 && (x0 < buf->clip.x || x0 > buf->clip.x + buf->clip.w - 1)) + return; + if (x0 < buf->clip.x && x1 < buf->clip.x) + return; + if (x0 > buf->clip.x + buf->clip.w - 1 && x1 > buf->clip.x + buf->clip.w - 1) + return; + + // Clip top. + if (y0 < buf->clip.y) { + float coeff = (buf->clip.y - y0) / (y1 - y0); + u0 = u0 + (u1 - u0) * coeff; + v0 = v0 + (v1 - v0) * coeff; + x0 = x0 + (x1 - x0) * coeff; + y0 = buf->clip.y; + } + // Clip bottom. + if (y1 > buf->clip.y + buf->clip.h - 1) { + float coeff = (buf->clip.y + buf->clip.h - 1 - y0) / (y1 - y0); + u1 = u0 + (u1 - u0) * coeff; + v1 = v0 + (v1 - v0) * coeff; + x1 = x0 + (x1 - x0) * coeff; + y1 = buf->clip.y + buf->clip.h - 1; + } + // Clip left. + if (x1 < buf->clip.x) { + float coeff = (buf->clip.x - x0) / (x1 - x0); + u1 = u0 + (u1 - u0) * coeff; + v1 = v0 + (v1 - v0) * coeff; + y1 = y0 + (y1 - y0) * coeff; + x1 = buf->clip.x; + + } else if (x0 < buf->clip.x) { + float coeff = (buf->clip.x - x0) / (x1 - x0); + u0 = u0 + (u1 - u0) * coeff; + v0 = v0 + (v1 - v0) * coeff; + y0 = y0 + (y1 - y0) * coeff; + x0 = buf->clip.x; + } + // Clip right. + if (x1 > buf->clip.x + buf->clip.w - 1) { + float coeff = (buf->clip.x + buf->clip.w - 1 - x0) / (x1 - x0); + u1 = u0 + (u1 - u0) * coeff; + v1 = v0 + (v1 - v0) * coeff; + y1 = y0 + (y1 - y0) * coeff; + x1 = buf->clip.x + buf->clip.w - 1; + + } else if (x0 > buf->clip.x + buf->clip.w - 1) { + float coeff = (buf->clip.x + buf->clip.w - 1 - x0) / (x1 - x0); + u0 = u0 + (u1 - u0) * coeff; + v0 = v0 + (v1 - v0) * coeff; + y0 = y0 + (y1 - y0) * coeff; + x0 = buf->clip.x + buf->clip.w - 1; + } + + // Determine whether the line is "steep" (dx*dx > dy*dy). + float dx = x1 - x0; + float dy = y1 - y0; + bool is_steep = fabsf(dx) < fabsf(dy); + int nIter; + + // Determine the number of iterations. + nIter = ceilf(fabsf(is_steep ? dy : dx)); + if (nIter < 1) + nIter = 1; + + // Adjust dx and dy. + dx /= nIter; + dy /= nIter; + + if (y0 == y1) { + // Horizontal line. + int index = (int)y0 * buf->width; + if (dx < 0) { + PAX_SWAP(float, x0, x1); + PAX_SWAP(float, u0, u1) + PAX_SWAP(float, v0, v1) + } + nIter = (int)x1 - (int)x0 + 1; + float u = u0, v = v0; + float du = (u1 - u0) / nIter; + float dv = (v1 - v0) / nIter; + for (int i = x0; i <= x1; i++) { + pax_col_t result = (shader_ctx.callback)( + color, + shader_ctx.do_getter ? buf2col(buf, buf->getter(buf, index + i)) : 0, + i, + y0, + u, + v, + shader_ctx.callback_args + ); + pax_set_index_conv(buf, result, index + i); + u += du; + v += dv; + } + } else if (x0 == x1) { + // Vertical line. + int index = x0 + (int)y0 * buf->width; + nIter = (int)y1 - (int)y0 + 1; + float u = u0, v = v0; + float du = (u1 - u0) / nIter; + float dv = (v1 - v0) / nIter; + for (int i = y0; i <= y1; i++, index += buf->width) { + pax_col_t result = (shader_ctx.callback)( + color, + shader_ctx.do_getter ? buf2col(buf, buf->getter(buf, index)) : 0, + x0, + i, + u, + v, + shader_ctx.callback_args + ); + pax_set_index_conv(buf, result, index); + u += du; + v += dv; + } + } else { + // Any other line. + float du = (u1 - u0) / nIter; + float dv = (v1 - v0) / nIter; + int_fast32_t x = x0 * 0x10000; + int_fast32_t y = y0 * 0x10000; + int_fast32_t idx = dx * 0x10000; + int_fast32_t idy = dy * 0x10000; + int_fast32_t u = u0 * 0x10000; + int_fast32_t v = v0 * 0x10000; + int_fast32_t idu = du * 0x10000; + int_fast32_t idv = dv * 0x10000; + for (int i = 0; i <= nIter; i++) { + size_t delta = (x >> 16) + (y >> 16) * buf->width; + pax_col_t result = (shader_ctx.callback)( + color, + shader_ctx.do_getter ? buf2col(buf, buf->getter(buf, delta)) : 0, + x >> 16, + y >> 16, + u / (float)0x10000, + v / (float)0x10000, + shader_ctx.callback_args + ); + pax_set_index_conv(buf, result, delta); + x += idx; + y += idy; + u += idu; + v += idv; + } + } } diff --git a/src/helpers/pax_dh_unshaded.cpp b/src/helpers/pax_dh_unshaded.cpp index 4d787e5..5d9b9b7 100644 --- a/src/helpers/pax_dh_unshaded.cpp +++ b/src/helpers/pax_dh_unshaded.cpp @@ -1,26 +1,5 @@ -/* - MIT License - - Copyright (c) 2021-2023 Julian Scheffers - - Permission is hereby granted, free of charge, to any person obtaining a copy - of this software and associated documentation files (the "Software"), to deal - in the Software without restriction, including without limitation the rights - to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - copies of the Software, and to permit persons to whom the Software is - furnished to do so, subject to the following conditions: - - The above copyright notice and this permission notice shall be included in all - copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - SOFTWARE. -*/ + +// SPDX-License-Identifier: MIT #include "pax_internal.h" @@ -36,93 +15,101 @@ // Internal method for line drawing. void pax_line_unshaded(pax_buf_t *buf, pax_col_t color, float x0, float y0, float x1, float y1) { - - pax_index_setter_t setter = pax_get_setter(buf, &color, NULL); - if (!setter) return; - - if (y1 < y0) { - PAX_SWAP(float, x0, x1) - PAX_SWAP(float, y0, y1) - } - - // Clip: left. - if (x0 < x1 && x0 < buf->clip.x) { - if (x1 < buf->clip.x) return; - // Adjust X0 against left clip. - y0 = y0 + (y1 - y0) * (buf->clip.x - x0) / (x1 - x0); - x0 = buf->clip.x; - } else if (x1 < x0 && x1 < buf->clip.x) { - if (x0 < buf->clip.x) return; - // Adjust X1 against left clip. - y1 = y1 + (y0 - y1) * (buf->clip.x - x1) / (x0 - x1); - x1 = buf->clip.x; - } - - // Clip: right. - if (x1 > x0 && x1 > buf->clip.x + buf->clip.w - 1) { - if (x0 > buf->clip.x + buf->clip.w) return; - // Adjust X1 against right of clip. - y1 = y0 + (y1 - y0) * (buf->clip.x + buf->clip.w - 1 - x0) / (x1 - x0); - x1 = buf->clip.x + buf->clip.w - 1; - } else if (x0 > x1 && x0 > buf->clip.x + buf->clip.w - 1) { - if (x1 > buf->clip.x + buf->clip.w) return; - // Adjust X0 against right of clip. - y0 = y1 + (y0 - y1) * (buf->clip.x + buf->clip.w - 1 - x1) / (x0 - x1); - x0 = buf->clip.x + buf->clip.w - 1; - } - - // Clip: top. - if (y0 < buf->clip.y) { - if (y1 < buf->clip.y) return; - // Adjust Y0 against top of clip. - x0 = x0 + (x1 - x0) * (buf->clip.y - y0) / (y1 - y0); - y0 = buf->clip.y; - } - - // Clip: bottom. - if (y1 > buf->clip.y + buf->clip.h - 1) { - if (y0 > buf->clip.y + buf->clip.h - 1) return; - // Adjust Y1 against bottom of clip. - x1 = x1 + (x1 - x0) * (buf->clip.y + buf->clip.h - 1 - y1) / (y1 - y0); - y1 = buf->clip.y + buf->clip.h - 1; - } - - // Determine whether the line is "steep" (dx*dx > dy*dy). - float dx = x1 - x0; - float dy = y1 - y0; - bool is_steep = fabsf(dx) < fabsf(dy); - int nIter; - - // Determine the number of iterations. - nIter = ceilf(fabsf(is_steep ? dy : dx)); - if (nIter < 1) nIter = 1; - - // Adjust dx and dy. - dx /= nIter; - dy /= nIter; - - if (y0 == y1) { - int index = (int) y0 * buf->width; - if (dx < 0) { - PAX_SWAP(float, x0, x1); - } - for (int i = x0; i <= x1; i++) { - setter(buf, color, index + i); - } - } else if (x0 == x1) { - int index = x0 + (int) y0 * buf->width; - for (int i = y0; i <= y1; i++, index += buf->width) { - setter(buf, color, index); - } - } else { - int_fast32_t x = x0 * 0x10000; - int_fast32_t y = y0 * 0x10000; - int_fast32_t idx = dx * 0x10000; - int_fast32_t idy = dy * 0x10000; - for (int i = 0; i <= nIter; i++) { - setter(buf, color, (x>>16)+(y>>16)*buf->width); - x += idx; - y += idy; - } - } + + pax_index_setter_t setter = pax_get_setter(buf, &color, NULL); + if (!setter) + return; + + if (y1 < y0) { + PAX_SWAP(float, x0, x1) + PAX_SWAP(float, y0, y1) + } + + // Clip: left. + if (x0 < x1 && x0 < buf->clip.x) { + if (x1 < buf->clip.x) + return; + // Adjust X0 against left clip. + y0 = y0 + (y1 - y0) * (buf->clip.x - x0) / (x1 - x0); + x0 = buf->clip.x; + } else if (x1 < x0 && x1 < buf->clip.x) { + if (x0 < buf->clip.x) + return; + // Adjust X1 against left clip. + y1 = y1 + (y0 - y1) * (buf->clip.x - x1) / (x0 - x1); + x1 = buf->clip.x; + } + + // Clip: right. + if (x1 > x0 && x1 > buf->clip.x + buf->clip.w - 1) { + if (x0 > buf->clip.x + buf->clip.w) + return; + // Adjust X1 against right of clip. + y1 = y0 + (y1 - y0) * (buf->clip.x + buf->clip.w - 1 - x0) / (x1 - x0); + x1 = buf->clip.x + buf->clip.w - 1; + } else if (x0 > x1 && x0 > buf->clip.x + buf->clip.w - 1) { + if (x1 > buf->clip.x + buf->clip.w) + return; + // Adjust X0 against right of clip. + y0 = y1 + (y0 - y1) * (buf->clip.x + buf->clip.w - 1 - x1) / (x0 - x1); + x0 = buf->clip.x + buf->clip.w - 1; + } + + // Clip: top. + if (y0 < buf->clip.y) { + if (y1 < buf->clip.y) + return; + // Adjust Y0 against top of clip. + x0 = x0 + (x1 - x0) * (buf->clip.y - y0) / (y1 - y0); + y0 = buf->clip.y; + } + + // Clip: bottom. + if (y1 > buf->clip.y + buf->clip.h - 1) { + if (y0 > buf->clip.y + buf->clip.h - 1) + return; + // Adjust Y1 against bottom of clip. + x1 = x1 + (x1 - x0) * (buf->clip.y + buf->clip.h - 1 - y1) / (y1 - y0); + y1 = buf->clip.y + buf->clip.h - 1; + } + + // Determine whether the line is "steep" (dx*dx > dy*dy). + float dx = x1 - x0; + float dy = y1 - y0; + bool is_steep = fabsf(dx) < fabsf(dy); + int nIter; + + // Determine the number of iterations. + nIter = ceilf(fabsf(is_steep ? dy : dx)); + if (nIter < 1) + nIter = 1; + + // Adjust dx and dy. + dx /= nIter; + dy /= nIter; + + if (y0 == y1) { + int index = (int)y0 * buf->width; + if (dx < 0) { + PAX_SWAP(float, x0, x1); + } + for (int i = x0; i <= x1; i++) { + setter(buf, color, index + i); + } + } else if (x0 == x1) { + int index = x0 + (int)y0 * buf->width; + for (int i = y0; i <= y1; i++, index += buf->width) { + setter(buf, color, index); + } + } else { + int_fast32_t x = x0 * 0x10000; + int_fast32_t y = y0 * 0x10000; + int_fast32_t idx = dx * 0x10000; + int_fast32_t idy = dy * 0x10000; + for (int i = 0; i <= nIter; i++) { + setter(buf, color, (x >> 16) + (y >> 16) * buf->width); + x += idx; + y += idy; + } + } } diff --git a/src/helpers/pax_mcr_dummy.c b/src/helpers/pax_mcr_dummy.c index 0f0e662..aa52544 100644 --- a/src/helpers/pax_mcr_dummy.c +++ b/src/helpers/pax_mcr_dummy.c @@ -1,26 +1,5 @@ -/* - MIT License - - Copyright (c) 2021-2023 Julian Scheffers - - Permission is hereby granted, free of charge, to any person obtaining a copy - of this software and associated documentation files (the "Software"), to deal - in the Software without restriction, including without limitation the rights - to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - copies of the Software, and to permit persons to whom the Software is - furnished to do so, subject to the following conditions: - - The above copyright notice and this permission notice shall be included in all - copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - SOFTWARE. -*/ + +// SPDX-License-Identifier: MIT #include "../pax_internal.h" @@ -30,17 +9,17 @@ // If multi-core rendering is enabled, wait for the other core. void pax_join() { - // No MCR, no wait. + // No MCR, no wait. } // Enable multi-core rendering. void pax_enable_multicore(int core) { - PAX_LOGE(TAG, "Failed to enable MCR: MCR not compiled, please define PAX_COMPILE_MCR."); + PAX_LOGE(TAG, "Failed to enable MCR: MCR not compiled, please define PAX_COMPILE_MCR."); } // Disable multi-core rendering. void pax_disable_multicore() { - PAX_LOGE(TAG, "No need to disable MCR: MCR not compiled, please define PAX_COMPILE_MCR."); + PAX_LOGE(TAG, "No need to disable MCR: MCR not compiled, please define PAX_COMPILE_MCR."); } #endif diff --git a/src/helpers/pax_mcr_esp32.c b/src/helpers/pax_mcr_esp32.c index 5651103..e37b7a5 100644 --- a/src/helpers/pax_mcr_esp32.c +++ b/src/helpers/pax_mcr_esp32.c @@ -1,26 +1,5 @@ -/* - MIT License - - Copyright (c) 2021-2023 Julian Scheffers - - Permission is hereby granted, free of charge, to any person obtaining a copy - of this software and associated documentation files (the "Software"), to deal - in the Software without restriction, including without limitation the rights - to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - copies of the Software, and to permit persons to whom the Software is - furnished to do so, subject to the following conditions: - - The above copyright notice and this permission notice shall be included in all - copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - SOFTWARE. -*/ + +// SPDX-License-Identifier: MIT #include "pax_internal.h" @@ -28,179 +7,198 @@ /* ===== MULTI-CORE RENDERING ==== */ -#include -#include -#include - #include -#include #include +#include +#include +#include +#include // Whether or not the multicore task is currently busy. -extern bool multicore_busy; +extern bool multicore_busy; // The task handle for the main core. -extern TaskHandle_t main_handle; +extern TaskHandle_t main_handle; // The task handle for the other core. -extern TaskHandle_t multicore_handle; +extern TaskHandle_t multicore_handle; // The render queue for the other core. -extern QueueHandle_t queue_handle; +extern QueueHandle_t queue_handle; -static const char *TAG = "pax-mcr"; +static char const *TAG = "pax-mcr"; // The scheduler for multicore rendering. void paxmcr_add_task(pax_task_t *task) { - // Snedt it. - esp_err_t res = xQueueSend(queue_handle, task, pdMS_TO_TICKS(10)); - if (res != pdTRUE) { - PAX_LOGE(TAG, "No space in queue after 10ms!"); - PAX_LOGW(TAG, "Reverting to disabling MCR."); - pax_disable_multicore(); - } + // Snedt it. + esp_err_t res = xQueueSend(queue_handle, task, pdMS_TO_TICKS(10)); + if (res != pdTRUE) { + PAX_LOGE(TAG, "No space in queue after 10ms!"); + PAX_LOGW(TAG, "Reverting to disabling MCR."); + pax_disable_multicore(); + } } // The actual task for multicore rendering. static void pax_multicore_task_function(void *args) { - const char *TAG = "pax-mcr-worker"; - PAX_LOGI(TAG, "MCR worker started."); - - // Up the frequency. - esp_pm_lock_handle_t pm_lock; - esp_pm_lock_create(ESP_PM_CPU_FREQ_MAX, 0, NULL, &pm_lock); - esp_pm_lock_acquire(pm_lock); - - multicore_busy = false; - pax_task_t tsk; - while (pax_do_multicore) { - // Wait for a task. - esp_err_t res = xQueueReceive(queue_handle, &tsk, pdMS_TO_TICKS(100)); - if (res != pdTRUE) continue; - multicore_busy = true; - // TODO: Sanity check on tasks? - if (tsk.type == PAX_TASK_STOP) { - break; - } - - // Now, we actually DRAW.will end itself - if (tsk.use_shader) { - if (tsk.type == PAX_TASK_RECT) { - paxmcr_rect_shaded( - true, tsk.buffer, - tsk.color, &tsk.shader, - tsk.shape[0], tsk.shape[1], - tsk.shape[2], tsk.shape[3], - tsk.quad_uvs.x0, tsk.quad_uvs.y0, - tsk.quad_uvs.x1, tsk.quad_uvs.y1, - tsk.quad_uvs.x2, tsk.quad_uvs.y2, - tsk.quad_uvs.x3, tsk.quad_uvs.y3 - ); - } else if (tsk.type == PAX_TASK_TRI) { - paxmcr_tri_shaded( - true, tsk.buffer, - tsk.color, &tsk.shader, - tsk.shape[0], tsk.shape[1], - tsk.shape[2], tsk.shape[3], - tsk.shape[4], tsk.shape[5], - tsk.tri_uvs.x0, tsk.tri_uvs.y0, - tsk.tri_uvs.x1, tsk.tri_uvs.y1, - tsk.tri_uvs.x2, tsk.tri_uvs.y2 - ); - } - } else { - if (tsk.type == PAX_TASK_RECT) { - paxmcr_rect_unshaded( - true, tsk.buffer, - tsk.color, - tsk.shape[0], tsk.shape[1], - tsk.shape[2], tsk.shape[3] - ); - } else if (tsk.type == PAX_TASK_TRI) { - paxmcr_tri_unshaded( - true, tsk.buffer, - tsk.color, - tsk.shape[0], tsk.shape[1], - tsk.shape[2], tsk.shape[3], - tsk.shape[4], tsk.shape[5] - ); - } - } - } - - // Cleaning. - esp_pm_lock_release(pm_lock); - esp_pm_lock_delete(pm_lock); - - PAX_LOGI(TAG, "MCR worker stopped."); - vTaskDelete(NULL); + char const *TAG = "pax-mcr-worker"; + PAX_LOGI(TAG, "MCR worker started."); + + // Up the frequency. + esp_pm_lock_handle_t pm_lock; + esp_pm_lock_create(ESP_PM_CPU_FREQ_MAX, 0, NULL, &pm_lock); + esp_pm_lock_acquire(pm_lock); + + multicore_busy = false; + pax_task_t tsk; + while (pax_do_multicore) { + // Wait for a task. + esp_err_t res = xQueueReceive(queue_handle, &tsk, pdMS_TO_TICKS(100)); + if (res != pdTRUE) + continue; + multicore_busy = true; + // TODO: Sanity check on tasks? + if (tsk.type == PAX_TASK_STOP) { + break; + } + + // Now, we actually DRAW.will end itself + if (tsk.use_shader) { + if (tsk.type == PAX_TASK_RECT) { + paxmcr_rect_shaded( + true, + tsk.buffer, + tsk.color, + &tsk.shader, + tsk.shape[0], + tsk.shape[1], + tsk.shape[2], + tsk.shape[3], + tsk.quad_uvs.x0, + tsk.quad_uvs.y0, + tsk.quad_uvs.x1, + tsk.quad_uvs.y1, + tsk.quad_uvs.x2, + tsk.quad_uvs.y2, + tsk.quad_uvs.x3, + tsk.quad_uvs.y3 + ); + } else if (tsk.type == PAX_TASK_TRI) { + paxmcr_tri_shaded( + true, + tsk.buffer, + tsk.color, + &tsk.shader, + tsk.shape[0], + tsk.shape[1], + tsk.shape[2], + tsk.shape[3], + tsk.shape[4], + tsk.shape[5], + tsk.tri_uvs.x0, + tsk.tri_uvs.y0, + tsk.tri_uvs.x1, + tsk.tri_uvs.y1, + tsk.tri_uvs.x2, + tsk.tri_uvs.y2 + ); + } + } else { + if (tsk.type == PAX_TASK_RECT) { + paxmcr_rect_unshaded( + true, + tsk.buffer, + tsk.color, + tsk.shape[0], + tsk.shape[1], + tsk.shape[2], + tsk.shape[3] + ); + } else if (tsk.type == PAX_TASK_TRI) { + paxmcr_tri_unshaded( + true, + tsk.buffer, + tsk.color, + tsk.shape[0], + tsk.shape[1], + tsk.shape[2], + tsk.shape[3], + tsk.shape[4], + tsk.shape[5] + ); + } + } + } + + // Cleaning. + esp_pm_lock_release(pm_lock); + esp_pm_lock_delete(pm_lock); + + PAX_LOGI(TAG, "MCR worker stopped."); + vTaskDelete(NULL); } // If multi-core rendering is enabled, wait for the other core. PAX_PERF_CRITICAL_ATTR void pax_join() { - while ((multicore_handle && eTaskGetState(multicore_handle) == eRunning) || (queue_handle && uxQueueMessagesWaiting(queue_handle))) { - // Wait for the other core. - taskYIELD(); - } + while ((multicore_handle && eTaskGetState(multicore_handle) == eRunning) || + (queue_handle && uxQueueMessagesWaiting(queue_handle))) { + // Wait for the other core. + taskYIELD(); + } } // Enable multi-core rendering. void pax_enable_multicore(int core) { - if (pax_do_multicore) { - PAX_LOGW(TAG, "No need to enable MCR: MCR was already enabled."); - return; - } - - // Figure out who we are so the worker can wake us up. - main_handle = xTaskGetCurrentTaskHandle(); - - // Create a queue for the rendering tasks. - if (!queue_handle) { - queue_handle = xQueueCreate(PAX_QUEUE_SIZE, sizeof(pax_task_t)); - if (!queue_handle) { - PAX_LOGE(TAG, "Failed to enable MCR: Queue creation error."); - return; - } - } - - // Create a task to do said rendering. - pax_do_multicore = true; - int result = xTaskCreatePinnedToCore( - pax_multicore_task_function, - "pax_mcr_worker", 4096, NULL, 2, - &multicore_handle, core - ); - if (result != pdPASS) { - multicore_handle = NULL; - PAX_LOGE(TAG, "Failed to enable MCR: Task creation error %s (%x).", esp_err_to_name(result), result); - pax_do_multicore = false; - } else { - PAX_LOGI(TAG, "Successfully enabled MCR."); - } + if (pax_do_multicore) { + PAX_LOGW(TAG, "No need to enable MCR: MCR was already enabled."); + return; + } + + // Figure out who we are so the worker can wake us up. + main_handle = xTaskGetCurrentTaskHandle(); + + // Create a queue for the rendering tasks. + if (!queue_handle) { + queue_handle = xQueueCreate(PAX_QUEUE_SIZE, sizeof(pax_task_t)); + if (!queue_handle) { + PAX_LOGE(TAG, "Failed to enable MCR: Queue creation error."); + return; + } + } + + // Create a task to do said rendering. + pax_do_multicore = true; + int result = + xTaskCreatePinnedToCore(pax_multicore_task_function, "pax_mcr_worker", 4096, NULL, 2, &multicore_handle, core); + if (result != pdPASS) { + multicore_handle = NULL; + PAX_LOGE(TAG, "Failed to enable MCR: Task creation error %s (%x).", esp_err_to_name(result), result); + pax_do_multicore = false; + } else { + PAX_LOGI(TAG, "Successfully enabled MCR."); + } } // Disable multi-core rendering. void pax_disable_multicore() { - if (!pax_do_multicore) { - PAX_LOGW(TAG, "No need to disable MCR: MCR was not enabled."); - return; - } - PAX_LOGI(TAG, "Disabling MCR..."); - - // Notify that multicore is disabled. - pax_do_multicore = false; - pax_task_t stopper = { - .type = PAX_TASK_STOP - }; - paxmcr_add_task(&stopper); - - // The task, realising multicore is now disabled, exit when finished. - pax_join(); - - vQueueDelete(queue_handle); - queue_handle = NULL; - - PAX_LOGI(TAG, "MCR successfully disabled."); - multicore_handle = NULL; + if (!pax_do_multicore) { + PAX_LOGW(TAG, "No need to disable MCR: MCR was not enabled."); + return; + } + PAX_LOGI(TAG, "Disabling MCR..."); + + // Notify that multicore is disabled. + pax_do_multicore = false; + pax_task_t stopper = {.type = PAX_TASK_STOP}; + paxmcr_add_task(&stopper); + + // The task, realising multicore is now disabled, exit when finished. + pax_join(); + + vQueueDelete(queue_handle); + queue_handle = NULL; + + PAX_LOGI(TAG, "MCR successfully disabled."); + multicore_handle = NULL; } #endif diff --git a/src/helpers/pax_mcr_pthread.c b/src/helpers/pax_mcr_pthread.c index 21d6953..b241108 100644 --- a/src/helpers/pax_mcr_pthread.c +++ b/src/helpers/pax_mcr_pthread.c @@ -1,38 +1,17 @@ -/* - MIT License - - Copyright (c) 2021-2023 Julian Scheffers - - Permission is hereby granted, free of charge, to any person obtaining a copy - of this software and associated documentation files (the "Software"), to deal - in the Software without restriction, including without limitation the rights - to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - copies of the Software, and to permit persons to whom the Software is - furnished to do so, subject to the following conditions: - - The above copyright notice and this permission notice shall be included in all - copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - SOFTWARE. -*/ + +// SPDX-License-Identifier: MIT #include "pax_internal.h" -#ifdef PAX_STANDALONE +#if PAX_STANDALONE && PAX_COMPILE_MCR /* ===== MULTI-CORE RENDERING ==== */ -#include #include +#include #include -#include #include +#include // Whether or not the multicore task is currently busy. extern bool multicore_busy; @@ -45,172 +24,194 @@ extern ptq_queue_t queue_handle; // The scheduler for multicore rendering. void paxmcr_add_task(pax_task_t *task) { - // Snedt it. - if (!ptq_send_block(queue_handle, task, NULL)) { - PAX_LOGE(TAG, "No space in queue!"); - PAX_LOGW(TAG, "Reverting to disabling MCR."); - pax_disable_multicore(); - } + // Snedt it. + if (!ptq_send_block(queue_handle, task, NULL)) { + PAX_LOGE(TAG, "No space in queue!"); + PAX_LOGW(TAG, "Reverting to disabling MCR."); + pax_disable_multicore(); + } } // The actual task for multicore rendering. static void *pax_multicore_task_function(void *args) { - const char *TAG = "pax-mcr-worker"; - - multicore_busy = false; - pax_task_t tsk; - if (!pax_do_multicore) { - PAX_LOGE(TAG, "Multicore set to disabled before worker started."); - } else { - PAX_LOGI(TAG, "MCR worker started."); - } - - while (pax_do_multicore) { - // Wait for a task. - if (!ptq_receive_block(queue_handle, &tsk, &multicore_mutex)) { - PAX_LOGE(TAG, "Error while receiving from queue!"); - PAX_LOGW(TAG, "Reverting to disabling MCR."); - pax_do_multicore = false; - break; - } - - // TODO: Sanity check on tasks? - if (tsk.type == PAX_TASK_STOP) { - PAX_LOGI(TAG, "Received stop command."); - break; - } - - - // Now, we actually DRAW.will end itself - if (tsk.use_shader) { - if (tsk.type == PAX_TASK_RECT) { - paxmcr_rect_shaded( - true, tsk.buffer, - tsk.color, &tsk.shader, - tsk.shape[0], tsk.shape[1], - tsk.shape[2], tsk.shape[3], - tsk.quad_uvs.x0, tsk.quad_uvs.y0, - tsk.quad_uvs.x1, tsk.quad_uvs.y1, - tsk.quad_uvs.x2, tsk.quad_uvs.y2, - tsk.quad_uvs.x3, tsk.quad_uvs.y3 - ); - } else if (tsk.type == PAX_TASK_TRI) { - paxmcr_tri_shaded( - true, tsk.buffer, - tsk.color, &tsk.shader, - tsk.shape[0], tsk.shape[1], - tsk.shape[2], tsk.shape[3], - tsk.shape[4], tsk.shape[5], - tsk.tri_uvs.x0, tsk.tri_uvs.y0, - tsk.tri_uvs.x1, tsk.tri_uvs.y1, - tsk.tri_uvs.x2, tsk.tri_uvs.y2 - ); - } - } else { - if (tsk.type == PAX_TASK_RECT) { - paxmcr_rect_unshaded( - true, tsk.buffer, - tsk.color, - tsk.shape[0], tsk.shape[1], - tsk.shape[2], tsk.shape[3] - ); - } else if (tsk.type == PAX_TASK_TRI) { - paxmcr_tri_unshaded( - true, tsk.buffer, - tsk.color, - tsk.shape[0], tsk.shape[1], - tsk.shape[2], tsk.shape[3], - tsk.shape[4], tsk.shape[5] - ); - } - } - - // Release the sync mutex. - pthread_mutex_unlock(&multicore_mutex); - } - - PAX_LOGI(TAG, "MCR worker stopped."); - return NULL; + char const *TAG = "pax-mcr-worker"; + + multicore_busy = false; + pax_task_t tsk; + if (!pax_do_multicore) { + PAX_LOGE(TAG, "Multicore set to disabled before worker started."); + } else { + PAX_LOGI(TAG, "MCR worker started."); + } + + while (pax_do_multicore) { + // Wait for a task. + if (!ptq_receive_block(queue_handle, &tsk, &multicore_mutex)) { + PAX_LOGE(TAG, "Error while receiving from queue!"); + PAX_LOGW(TAG, "Reverting to disabling MCR."); + pax_do_multicore = false; + break; + } + + // TODO: Sanity check on tasks? + if (tsk.type == PAX_TASK_STOP) { + PAX_LOGI(TAG, "Received stop command."); + break; + } + + + // Now, we actually DRAW.will end itself + if (tsk.use_shader) { + if (tsk.type == PAX_TASK_RECT) { + paxmcr_rect_shaded( + true, + tsk.buffer, + tsk.color, + &tsk.shader, + tsk.shape[0], + tsk.shape[1], + tsk.shape[2], + tsk.shape[3], + tsk.quad_uvs.x0, + tsk.quad_uvs.y0, + tsk.quad_uvs.x1, + tsk.quad_uvs.y1, + tsk.quad_uvs.x2, + tsk.quad_uvs.y2, + tsk.quad_uvs.x3, + tsk.quad_uvs.y3 + ); + } else if (tsk.type == PAX_TASK_TRI) { + paxmcr_tri_shaded( + true, + tsk.buffer, + tsk.color, + &tsk.shader, + tsk.shape[0], + tsk.shape[1], + tsk.shape[2], + tsk.shape[3], + tsk.shape[4], + tsk.shape[5], + tsk.tri_uvs.x0, + tsk.tri_uvs.y0, + tsk.tri_uvs.x1, + tsk.tri_uvs.y1, + tsk.tri_uvs.x2, + tsk.tri_uvs.y2 + ); + } + } else { + if (tsk.type == PAX_TASK_RECT) { + paxmcr_rect_unshaded( + true, + tsk.buffer, + tsk.color, + tsk.shape[0], + tsk.shape[1], + tsk.shape[2], + tsk.shape[3] + ); + } else if (tsk.type == PAX_TASK_TRI) { + paxmcr_tri_unshaded( + true, + tsk.buffer, + tsk.color, + tsk.shape[0], + tsk.shape[1], + tsk.shape[2], + tsk.shape[3], + tsk.shape[4], + tsk.shape[5] + ); + } + } + + // Release the sync mutex. + pthread_mutex_unlock(&multicore_mutex); + } + + PAX_LOGI(TAG, "MCR worker stopped."); + return NULL; } // If multi-core rendering is enabled, wait for the other core. void pax_join() { - if (pax_do_multicore) { - // Await queue to be empty. - ptq_join(queue_handle, &multicore_mutex); - pthread_mutex_unlock(&multicore_mutex); - } + if (pax_do_multicore) { + // Await queue to be empty. + ptq_join(queue_handle, &multicore_mutex); + pthread_mutex_unlock(&multicore_mutex); + } } // Enable multi-core rendering. void pax_enable_multicore(int core) { - if (pax_do_multicore) { - PAX_LOGW(TAG, "No need to enable MCR: MCR was already enabled."); - return; - } - - // Enable log mutex. - int res = pthread_mutex_init(&pax_log_mutex, NULL); - if (res) { - PAX_LOGE(TAG, "Failed to enable MCR: Log mutex creation error."); - return; - } - pax_log_use_mutex = true; - - // Mark MCR as enabled. - pax_do_multicore = true; - - // Create sync mutex. - res = pthread_mutex_init(&multicore_mutex, NULL); - if (res) { - PAX_LOGE(TAG, "Failed to enable MCR: Sync mutex creation error."); - return; - } - - // Create queue. - queue_handle = ptq_create_max(sizeof(pax_task_t), PAX_QUEUE_SIZE); - if (!queue_handle) { - PAX_LOGE(TAG, "Failed to enable MCR: Queue creation error."); - pax_do_multicore = false; - return; - } - - // Create worker thread. - res = pthread_create(&multicore_handle, NULL, pax_multicore_task_function, NULL); - - if (res) { - PAX_LOGE(TAG, "Failed to enable MCR: Task creation error %d.", res); - pax_do_multicore = false; - } else { - PAX_LOGI(TAG, "Successfully enabled MCR."); - } + if (pax_do_multicore) { + PAX_LOGW(TAG, "No need to enable MCR: MCR was already enabled."); + return; + } + + // Enable log mutex. + int res = pthread_mutex_init(&pax_log_mutex, NULL); + if (res) { + PAX_LOGE(TAG, "Failed to enable MCR: Log mutex creation error."); + return; + } + pax_log_use_mutex = true; + + // Mark MCR as enabled. + pax_do_multicore = true; + + // Create sync mutex. + res = pthread_mutex_init(&multicore_mutex, NULL); + if (res) { + PAX_LOGE(TAG, "Failed to enable MCR: Sync mutex creation error."); + return; + } + + // Create queue. + queue_handle = ptq_create_max(sizeof(pax_task_t), PAX_QUEUE_SIZE); + if (!queue_handle) { + PAX_LOGE(TAG, "Failed to enable MCR: Queue creation error."); + pax_do_multicore = false; + return; + } + + // Create worker thread. + res = pthread_create(&multicore_handle, NULL, pax_multicore_task_function, NULL); + + if (res) { + PAX_LOGE(TAG, "Failed to enable MCR: Task creation error %d.", res); + pax_do_multicore = false; + } else { + PAX_LOGI(TAG, "Successfully enabled MCR."); + } } // Disable multi-core rendering. void pax_disable_multicore() { - if (!pax_do_multicore) { - PAX_LOGW(TAG, "No need to disable MCR: MCR was not enabled."); - return; - } - PAX_LOGI(TAG, "Disabling MCR..."); - - // Notify that multicore is disabled. - pax_do_multicore = false; - pax_task_t stopper = { - .type = PAX_TASK_STOP, - }; - paxmcr_add_task(&stopper); - - // The task, realising multicore is now disabled, exit when finished. - pax_join(); - - // Clean up queue. - ptq_destroy(queue_handle); - - // Disable log mutex. - pax_log_use_mutex = false; - pthread_mutex_destroy(&pax_log_mutex); - + if (!pax_do_multicore) { + PAX_LOGW(TAG, "No need to disable MCR: MCR was not enabled."); + return; + } + PAX_LOGI(TAG, "Disabling MCR..."); + + // Notify that multicore is disabled. + pax_do_multicore = false; + pax_task_t stopper = { + .type = PAX_TASK_STOP, + }; + paxmcr_add_task(&stopper); + + // The task, realising multicore is now disabled, exit when finished. + pax_join(); + + // Clean up queue. + ptq_destroy(queue_handle); + + // Disable log mutex. + pax_log_use_mutex = false; + pthread_mutex_destroy(&pax_log_mutex); } #endif diff --git a/src/helpers/pax_precalculated.c b/src/helpers/pax_precalculated.c index 86fe181..5122be0 100644 --- a/src/helpers/pax_precalculated.c +++ b/src/helpers/pax_precalculated.c @@ -4,186 +4,185 @@ #include "pax_internal.h" // Circle: 4 segments -const pax_vec2f pax_precalc_circle_4[5] = { - {1.0, 0.0}, - {6.123233995736766e-17, 1.0}, - {-1.0, 1.2246467991473532e-16}, - {-1.8369701987210297e-16, -1.0}, - {1.0, -2.4492935982947064e-16}, +pax_vec2f const pax_precalc_circle_4[5] = { + {1.0, 0.0}, + {6.123233995736766e-17, 1.0}, + {-1.0, 1.2246467991473532e-16}, + {-1.8369701987210297e-16, -1.0}, + {1.0, -2.4492935982947064e-16}, }; // Circle UVs: 4 segments -const pax_trif pax_precalc_uv_circle_4[3] = { - {1, 0.5, 1.0, 0.5, 0.5, 1.0}, - {1, 0.5, 0.5, 1.0, 0.0, 0.5000000000000001}, - {1, 0.5, 0.0, 0.5000000000000001, 0.4999999999999999, 0.0}, +pax_trif const pax_precalc_uv_circle_4[3] = { + {1, 0.5, 1.0, 0.5, 0.5, 1.0}, + {1, 0.5, 0.5, 1.0, 0.0, 0.5000000000000001}, + {1, 0.5, 0.0, 0.5000000000000001, 0.4999999999999999, 0.0}, }; // Circle: 16 segments -const pax_vec2f pax_precalc_circle_16[17] = { - {1.0, 0.0}, - {0.9238795325112867, 0.3826834323650898}, - {0.7071067811865476, 0.7071067811865475}, - {0.38268343236508984, 0.9238795325112867}, - {6.123233995736766e-17, 1.0}, - {-0.3826834323650897, 0.9238795325112867}, - {-0.7071067811865475, 0.7071067811865476}, - {-0.9238795325112867, 0.3826834323650899}, - {-1.0, 1.2246467991473532e-16}, - {-0.9238795325112868, -0.38268343236508967}, - {-0.7071067811865477, -0.7071067811865475}, - {-0.38268343236509034, -0.9238795325112865}, - {-1.8369701987210297e-16, -1.0}, - {0.38268343236509, -0.9238795325112866}, - {0.7071067811865474, -0.7071067811865477}, - {0.9238795325112865, -0.3826834323650904}, - {1.0, -2.4492935982947064e-16}, +pax_vec2f const pax_precalc_circle_16[17] = { + {1.0, 0.0}, + {0.9238795325112867, 0.3826834323650898}, + {0.7071067811865476, 0.7071067811865475}, + {0.38268343236508984, 0.9238795325112867}, + {6.123233995736766e-17, 1.0}, + {-0.3826834323650897, 0.9238795325112867}, + {-0.7071067811865475, 0.7071067811865476}, + {-0.9238795325112867, 0.3826834323650899}, + {-1.0, 1.2246467991473532e-16}, + {-0.9238795325112868, -0.38268343236508967}, + {-0.7071067811865477, -0.7071067811865475}, + {-0.38268343236509034, -0.9238795325112865}, + {-1.8369701987210297e-16, -1.0}, + {0.38268343236509, -0.9238795325112866}, + {0.7071067811865474, -0.7071067811865477}, + {0.9238795325112865, -0.3826834323650904}, + {1.0, -2.4492935982947064e-16}, }; // Circle UVs: 16 segments -const pax_trif pax_precalc_uv_circle_16[15] = { - {1, 0.5, 1.0, 0.5, 0.9619397662556434, 0.6913417161825449}, - {1, 0.5, 0.9619397662556434, 0.6913417161825449, 0.8535533905932737, 0.8535533905932737}, - {1, 0.5, 0.8535533905932737, 0.8535533905932737, 0.6913417161825449, 0.9619397662556434}, - {1, 0.5, 0.6913417161825449, 0.9619397662556434, 0.5, 1.0}, - {1, 0.5, 0.5, 1.0, 0.30865828381745514, 0.9619397662556434}, - {1, 0.5, 0.30865828381745514, 0.9619397662556434, 0.14644660940672627, 0.8535533905932737}, - {1, 0.5, 0.14644660940672627, 0.8535533905932737, 0.03806023374435663, 0.6913417161825449}, - {1, 0.5, 0.03806023374435663, 0.6913417161825449, 0.0, 0.5000000000000001}, - {1, 0.5, 0.0, 0.5000000000000001, 0.038060233744356575, 0.3086582838174552}, - {1, 0.5, 0.038060233744356575, 0.3086582838174552, 0.14644660940672616, 0.14644660940672627}, - {1, 0.5, 0.14644660940672616, 0.14644660940672627, 0.30865828381745486, 0.03806023374435674}, - {1, 0.5, 0.30865828381745486, 0.03806023374435674, 0.4999999999999999, 0.0}, - {1, 0.5, 0.4999999999999999, 0.0, 0.691341716182545, 0.038060233744356686}, - {1, 0.5, 0.691341716182545, 0.038060233744356686, 0.8535533905932737, 0.14644660940672616}, - {1, 0.5, 0.8535533905932737, 0.14644660940672616, 0.9619397662556433, 0.3086582838174548}, +pax_trif const pax_precalc_uv_circle_16[15] = { + {1, 0.5, 1.0, 0.5, 0.9619397662556434, 0.6913417161825449}, + {1, 0.5, 0.9619397662556434, 0.6913417161825449, 0.8535533905932737, 0.8535533905932737}, + {1, 0.5, 0.8535533905932737, 0.8535533905932737, 0.6913417161825449, 0.9619397662556434}, + {1, 0.5, 0.6913417161825449, 0.9619397662556434, 0.5, 1.0}, + {1, 0.5, 0.5, 1.0, 0.30865828381745514, 0.9619397662556434}, + {1, 0.5, 0.30865828381745514, 0.9619397662556434, 0.14644660940672627, 0.8535533905932737}, + {1, 0.5, 0.14644660940672627, 0.8535533905932737, 0.03806023374435663, 0.6913417161825449}, + {1, 0.5, 0.03806023374435663, 0.6913417161825449, 0.0, 0.5000000000000001}, + {1, 0.5, 0.0, 0.5000000000000001, 0.038060233744356575, 0.3086582838174552}, + {1, 0.5, 0.038060233744356575, 0.3086582838174552, 0.14644660940672616, 0.14644660940672627}, + {1, 0.5, 0.14644660940672616, 0.14644660940672627, 0.30865828381745486, 0.03806023374435674}, + {1, 0.5, 0.30865828381745486, 0.03806023374435674, 0.4999999999999999, 0.0}, + {1, 0.5, 0.4999999999999999, 0.0, 0.691341716182545, 0.038060233744356686}, + {1, 0.5, 0.691341716182545, 0.038060233744356686, 0.8535533905932737, 0.14644660940672616}, + {1, 0.5, 0.8535533905932737, 0.14644660940672616, 0.9619397662556433, 0.3086582838174548}, }; // Circle: 24 segments -const pax_vec2f pax_precalc_circle_24[25] = { - {1.0, 0.0}, - {0.9659258262890683, 0.25881904510252074}, - {0.8660254037844387, 0.49999999999999994}, - {0.7071067811865476, 0.7071067811865475}, - {0.5000000000000001, 0.8660254037844386}, - {0.25881904510252096, 0.9659258262890682}, - {6.123233995736766e-17, 1.0}, - {-0.25881904510252063, 0.9659258262890683}, - {-0.4999999999999998, 0.8660254037844387}, - {-0.7071067811865475, 0.7071067811865476}, - {-0.8660254037844385, 0.5000000000000003}, - {-0.9659258262890682, 0.258819045102521}, - {-1.0, 1.2246467991473532e-16}, - {-0.9659258262890684, -0.25881904510252035}, - {-0.8660254037844388, -0.4999999999999997}, - {-0.7071067811865479, -0.7071067811865471}, - {-0.5000000000000004, -0.8660254037844384}, - {-0.2588190451025215, -0.9659258262890681}, - {-1.8369701987210297e-16, -1.0}, - {0.2588190451025203, -0.9659258262890684}, - {0.49999999999999933, -0.866025403784439}, - {0.7071067811865474, -0.7071067811865477}, - {0.8660254037844384, -0.5000000000000004}, - {0.9659258262890681, -0.25881904510252157}, - {1.0, -2.4492935982947064e-16}, +pax_vec2f const pax_precalc_circle_24[25] = { + {1.0, 0.0}, + {0.9659258262890683, 0.25881904510252074}, + {0.8660254037844387, 0.49999999999999994}, + {0.7071067811865476, 0.7071067811865475}, + {0.5000000000000001, 0.8660254037844386}, + {0.25881904510252096, 0.9659258262890682}, + {6.123233995736766e-17, 1.0}, + {-0.25881904510252063, 0.9659258262890683}, + {-0.4999999999999998, 0.8660254037844387}, + {-0.7071067811865475, 0.7071067811865476}, + {-0.8660254037844385, 0.5000000000000003}, + {-0.9659258262890682, 0.258819045102521}, + {-1.0, 1.2246467991473532e-16}, + {-0.9659258262890684, -0.25881904510252035}, + {-0.8660254037844388, -0.4999999999999997}, + {-0.7071067811865479, -0.7071067811865471}, + {-0.5000000000000004, -0.8660254037844384}, + {-0.2588190451025215, -0.9659258262890681}, + {-1.8369701987210297e-16, -1.0}, + {0.2588190451025203, -0.9659258262890684}, + {0.49999999999999933, -0.866025403784439}, + {0.7071067811865474, -0.7071067811865477}, + {0.8660254037844384, -0.5000000000000004}, + {0.9659258262890681, -0.25881904510252157}, + {1.0, -2.4492935982947064e-16}, }; // Circle UVs: 24 segments -const pax_trif pax_precalc_uv_circle_24[23] = { - {1, 0.5, 1.0, 0.5, 0.9829629131445341, 0.6294095225512604}, - {1, 0.5, 0.9829629131445341, 0.6294095225512604, 0.9330127018922194, 0.75}, - {1, 0.5, 0.9330127018922194, 0.75, 0.8535533905932737, 0.8535533905932737}, - {1, 0.5, 0.8535533905932737, 0.8535533905932737, 0.75, 0.9330127018922193}, - {1, 0.5, 0.75, 0.9330127018922193, 0.6294095225512605, 0.9829629131445341}, - {1, 0.5, 0.6294095225512605, 0.9829629131445341, 0.5, 1.0}, - {1, 0.5, 0.5, 1.0, 0.3705904774487397, 0.9829629131445341}, - {1, 0.5, 0.3705904774487397, 0.9829629131445341, 0.2500000000000001, 0.9330127018922194}, - {1, 0.5, 0.2500000000000001, 0.9330127018922194, 0.14644660940672627, 0.8535533905932737}, - {1, 0.5, 0.14644660940672627, 0.8535533905932737, 0.06698729810778076, 0.7500000000000002}, - {1, 0.5, 0.06698729810778076, 0.7500000000000002, 0.0170370868554659, 0.6294095225512605}, - {1, 0.5, 0.0170370868554659, 0.6294095225512605, 0.0, 0.5000000000000001}, - {1, 0.5, 0.0, 0.5000000000000001, 0.01703708685546579, 0.37059047744873985}, - {1, 0.5, 0.01703708685546579, 0.37059047744873985, 0.06698729810778059, 0.2500000000000001}, - {1, 0.5, 0.06698729810778059, 0.2500000000000001, 0.14644660940672605, 0.14644660940672644}, - {1, 0.5, 0.14644660940672605, 0.14644660940672644, 0.24999999999999978, 0.06698729810778081}, - {1, 0.5, 0.24999999999999978, 0.06698729810778081, 0.37059047744873924, 0.017037086855465955}, - {1, 0.5, 0.37059047744873924, 0.017037086855465955, 0.4999999999999999, 0.0}, - {1, 0.5, 0.4999999999999999, 0.0, 0.6294095225512601, 0.01703708685546579}, - {1, 0.5, 0.6294095225512601, 0.01703708685546579, 0.7499999999999997, 0.06698729810778048}, - {1, 0.5, 0.7499999999999997, 0.06698729810778048, 0.8535533905932737, 0.14644660940672616}, - {1, 0.5, 0.8535533905932737, 0.14644660940672616, 0.9330127018922192, 0.24999999999999978}, - {1, 0.5, 0.9330127018922192, 0.24999999999999978, 0.9829629131445341, 0.3705904774487392}, +pax_trif const pax_precalc_uv_circle_24[23] = { + {1, 0.5, 1.0, 0.5, 0.9829629131445341, 0.6294095225512604}, + {1, 0.5, 0.9829629131445341, 0.6294095225512604, 0.9330127018922194, 0.75}, + {1, 0.5, 0.9330127018922194, 0.75, 0.8535533905932737, 0.8535533905932737}, + {1, 0.5, 0.8535533905932737, 0.8535533905932737, 0.75, 0.9330127018922193}, + {1, 0.5, 0.75, 0.9330127018922193, 0.6294095225512605, 0.9829629131445341}, + {1, 0.5, 0.6294095225512605, 0.9829629131445341, 0.5, 1.0}, + {1, 0.5, 0.5, 1.0, 0.3705904774487397, 0.9829629131445341}, + {1, 0.5, 0.3705904774487397, 0.9829629131445341, 0.2500000000000001, 0.9330127018922194}, + {1, 0.5, 0.2500000000000001, 0.9330127018922194, 0.14644660940672627, 0.8535533905932737}, + {1, 0.5, 0.14644660940672627, 0.8535533905932737, 0.06698729810778076, 0.7500000000000002}, + {1, 0.5, 0.06698729810778076, 0.7500000000000002, 0.0170370868554659, 0.6294095225512605}, + {1, 0.5, 0.0170370868554659, 0.6294095225512605, 0.0, 0.5000000000000001}, + {1, 0.5, 0.0, 0.5000000000000001, 0.01703708685546579, 0.37059047744873985}, + {1, 0.5, 0.01703708685546579, 0.37059047744873985, 0.06698729810778059, 0.2500000000000001}, + {1, 0.5, 0.06698729810778059, 0.2500000000000001, 0.14644660940672605, 0.14644660940672644}, + {1, 0.5, 0.14644660940672605, 0.14644660940672644, 0.24999999999999978, 0.06698729810778081}, + {1, 0.5, 0.24999999999999978, 0.06698729810778081, 0.37059047744873924, 0.017037086855465955}, + {1, 0.5, 0.37059047744873924, 0.017037086855465955, 0.4999999999999999, 0.0}, + {1, 0.5, 0.4999999999999999, 0.0, 0.6294095225512601, 0.01703708685546579}, + {1, 0.5, 0.6294095225512601, 0.01703708685546579, 0.7499999999999997, 0.06698729810778048}, + {1, 0.5, 0.7499999999999997, 0.06698729810778048, 0.8535533905932737, 0.14644660940672616}, + {1, 0.5, 0.8535533905932737, 0.14644660940672616, 0.9330127018922192, 0.24999999999999978}, + {1, 0.5, 0.9330127018922192, 0.24999999999999978, 0.9829629131445341, 0.3705904774487392}, }; // Circle: 32 segments -const pax_vec2f pax_precalc_circle_32[33] = { - {1.0, 0.0}, - {0.9807852804032304, 0.19509032201612825}, - {0.9238795325112867, 0.3826834323650898}, - {0.8314696123025452, 0.5555702330196022}, - {0.7071067811865476, 0.7071067811865475}, - {0.5555702330196023, 0.8314696123025452}, - {0.38268343236508984, 0.9238795325112867}, - {0.19509032201612833, 0.9807852804032304}, - {6.123233995736766e-17, 1.0}, - {-0.1950903220161282, 0.9807852804032304}, - {-0.3826834323650897, 0.9238795325112867}, - {-0.555570233019602, 0.8314696123025455}, - {-0.7071067811865475, 0.7071067811865476}, - {-0.8314696123025453, 0.5555702330196022}, - {-0.9238795325112867, 0.3826834323650899}, - {-0.9807852804032304, 0.1950903220161286}, - {-1.0, 1.2246467991473532e-16}, - {-0.9807852804032304, -0.19509032201612836}, - {-0.9238795325112868, -0.38268343236508967}, - {-0.8314696123025455, -0.555570233019602}, - {-0.7071067811865477, -0.7071067811865475}, - {-0.5555702330196022, -0.8314696123025452}, - {-0.38268343236509034, -0.9238795325112865}, - {-0.19509032201612866, -0.9807852804032303}, - {-1.8369701987210297e-16, -1.0}, - {0.1950903220161283, -0.9807852804032304}, - {0.38268343236509, -0.9238795325112866}, - {0.5555702330196018, -0.8314696123025455}, - {0.7071067811865474, -0.7071067811865477}, - {0.8314696123025452, -0.5555702330196022}, - {0.9238795325112865, -0.3826834323650904}, - {0.9807852804032303, -0.19509032201612872}, - {1.0, -2.4492935982947064e-16}, +pax_vec2f const pax_precalc_circle_32[33] = { + {1.0, 0.0}, + {0.9807852804032304, 0.19509032201612825}, + {0.9238795325112867, 0.3826834323650898}, + {0.8314696123025452, 0.5555702330196022}, + {0.7071067811865476, 0.7071067811865475}, + {0.5555702330196023, 0.8314696123025452}, + {0.38268343236508984, 0.9238795325112867}, + {0.19509032201612833, 0.9807852804032304}, + {6.123233995736766e-17, 1.0}, + {-0.1950903220161282, 0.9807852804032304}, + {-0.3826834323650897, 0.9238795325112867}, + {-0.555570233019602, 0.8314696123025455}, + {-0.7071067811865475, 0.7071067811865476}, + {-0.8314696123025453, 0.5555702330196022}, + {-0.9238795325112867, 0.3826834323650899}, + {-0.9807852804032304, 0.1950903220161286}, + {-1.0, 1.2246467991473532e-16}, + {-0.9807852804032304, -0.19509032201612836}, + {-0.9238795325112868, -0.38268343236508967}, + {-0.8314696123025455, -0.555570233019602}, + {-0.7071067811865477, -0.7071067811865475}, + {-0.5555702330196022, -0.8314696123025452}, + {-0.38268343236509034, -0.9238795325112865}, + {-0.19509032201612866, -0.9807852804032303}, + {-1.8369701987210297e-16, -1.0}, + {0.1950903220161283, -0.9807852804032304}, + {0.38268343236509, -0.9238795325112866}, + {0.5555702330196018, -0.8314696123025455}, + {0.7071067811865474, -0.7071067811865477}, + {0.8314696123025452, -0.5555702330196022}, + {0.9238795325112865, -0.3826834323650904}, + {0.9807852804032303, -0.19509032201612872}, + {1.0, -2.4492935982947064e-16}, }; // Circle UVs: 32 segments -const pax_trif pax_precalc_uv_circle_32[31] = { - {1, 0.5, 1.0, 0.5, 0.9903926402016152, 0.5975451610080641}, - {1, 0.5, 0.9903926402016152, 0.5975451610080641, 0.9619397662556434, 0.6913417161825449}, - {1, 0.5, 0.9619397662556434, 0.6913417161825449, 0.9157348061512727, 0.7777851165098011}, - {1, 0.5, 0.9157348061512727, 0.7777851165098011, 0.8535533905932737, 0.8535533905932737}, - {1, 0.5, 0.8535533905932737, 0.8535533905932737, 0.7777851165098011, 0.9157348061512727}, - {1, 0.5, 0.7777851165098011, 0.9157348061512727, 0.6913417161825449, 0.9619397662556434}, - {1, 0.5, 0.6913417161825449, 0.9619397662556434, 0.5975451610080642, 0.9903926402016152}, - {1, 0.5, 0.5975451610080642, 0.9903926402016152, 0.5, 1.0}, - {1, 0.5, 0.5, 1.0, 0.4024548389919359, 0.9903926402016152}, - {1, 0.5, 0.4024548389919359, 0.9903926402016152, 0.30865828381745514, 0.9619397662556434}, - {1, 0.5, 0.30865828381745514, 0.9619397662556434, 0.22221488349019902, 0.9157348061512727}, - {1, 0.5, 0.22221488349019902, 0.9157348061512727, 0.14644660940672627, 0.8535533905932737}, - {1, 0.5, 0.14644660940672627, 0.8535533905932737, 0.08426519384872733, 0.7777851165098011}, - {1, 0.5, 0.08426519384872733, 0.7777851165098011, 0.03806023374435663, 0.6913417161825449}, - {1, 0.5, 0.03806023374435663, 0.6913417161825449, 0.009607359798384785, 0.5975451610080643}, - {1, 0.5, 0.009607359798384785, 0.5975451610080643, 0.0, 0.5000000000000001}, - {1, 0.5, 0.0, 0.5000000000000001, 0.009607359798384785, 0.4024548389919358}, - {1, 0.5, 0.009607359798384785, 0.4024548389919358, 0.038060233744356575, 0.3086582838174552}, - {1, 0.5, 0.038060233744356575, 0.3086582838174552, 0.08426519384872727, 0.22221488349019902}, - {1, 0.5, 0.08426519384872727, 0.22221488349019902, 0.14644660940672616, 0.14644660940672627}, - {1, 0.5, 0.14644660940672616, 0.14644660940672627, 0.2222148834901989, 0.08426519384872738}, - {1, 0.5, 0.2222148834901989, 0.08426519384872738, 0.30865828381745486, 0.03806023374435674}, - {1, 0.5, 0.30865828381745486, 0.03806023374435674, 0.4024548389919357, 0.00960735979838484}, - {1, 0.5, 0.4024548389919357, 0.00960735979838484, 0.4999999999999999, 0.0}, - {1, 0.5, 0.4999999999999999, 0.0, 0.5975451610080642, 0.009607359798384785}, - {1, 0.5, 0.5975451610080642, 0.009607359798384785, 0.691341716182545, 0.038060233744356686}, - {1, 0.5, 0.691341716182545, 0.038060233744356686, 0.7777851165098009, 0.08426519384872727}, - {1, 0.5, 0.7777851165098009, 0.08426519384872727, 0.8535533905932737, 0.14644660940672616}, - {1, 0.5, 0.8535533905932737, 0.14644660940672616, 0.9157348061512727, 0.2222148834901989}, - {1, 0.5, 0.9157348061512727, 0.2222148834901989, 0.9619397662556433, 0.3086582838174548}, - {1, 0.5, 0.9619397662556433, 0.3086582838174548, 0.9903926402016152, 0.4024548389919356}, +pax_trif const pax_precalc_uv_circle_32[31] = { + {1, 0.5, 1.0, 0.5, 0.9903926402016152, 0.5975451610080641}, + {1, 0.5, 0.9903926402016152, 0.5975451610080641, 0.9619397662556434, 0.6913417161825449}, + {1, 0.5, 0.9619397662556434, 0.6913417161825449, 0.9157348061512727, 0.7777851165098011}, + {1, 0.5, 0.9157348061512727, 0.7777851165098011, 0.8535533905932737, 0.8535533905932737}, + {1, 0.5, 0.8535533905932737, 0.8535533905932737, 0.7777851165098011, 0.9157348061512727}, + {1, 0.5, 0.7777851165098011, 0.9157348061512727, 0.6913417161825449, 0.9619397662556434}, + {1, 0.5, 0.6913417161825449, 0.9619397662556434, 0.5975451610080642, 0.9903926402016152}, + {1, 0.5, 0.5975451610080642, 0.9903926402016152, 0.5, 1.0}, + {1, 0.5, 0.5, 1.0, 0.4024548389919359, 0.9903926402016152}, + {1, 0.5, 0.4024548389919359, 0.9903926402016152, 0.30865828381745514, 0.9619397662556434}, + {1, 0.5, 0.30865828381745514, 0.9619397662556434, 0.22221488349019902, 0.9157348061512727}, + {1, 0.5, 0.22221488349019902, 0.9157348061512727, 0.14644660940672627, 0.8535533905932737}, + {1, 0.5, 0.14644660940672627, 0.8535533905932737, 0.08426519384872733, 0.7777851165098011}, + {1, 0.5, 0.08426519384872733, 0.7777851165098011, 0.03806023374435663, 0.6913417161825449}, + {1, 0.5, 0.03806023374435663, 0.6913417161825449, 0.009607359798384785, 0.5975451610080643}, + {1, 0.5, 0.009607359798384785, 0.5975451610080643, 0.0, 0.5000000000000001}, + {1, 0.5, 0.0, 0.5000000000000001, 0.009607359798384785, 0.4024548389919358}, + {1, 0.5, 0.009607359798384785, 0.4024548389919358, 0.038060233744356575, 0.3086582838174552}, + {1, 0.5, 0.038060233744356575, 0.3086582838174552, 0.08426519384872727, 0.22221488349019902}, + {1, 0.5, 0.08426519384872727, 0.22221488349019902, 0.14644660940672616, 0.14644660940672627}, + {1, 0.5, 0.14644660940672616, 0.14644660940672627, 0.2222148834901989, 0.08426519384872738}, + {1, 0.5, 0.2222148834901989, 0.08426519384872738, 0.30865828381745486, 0.03806023374435674}, + {1, 0.5, 0.30865828381745486, 0.03806023374435674, 0.4024548389919357, 0.00960735979838484}, + {1, 0.5, 0.4024548389919357, 0.00960735979838484, 0.4999999999999999, 0.0}, + {1, 0.5, 0.4999999999999999, 0.0, 0.5975451610080642, 0.009607359798384785}, + {1, 0.5, 0.5975451610080642, 0.009607359798384785, 0.691341716182545, 0.038060233744356686}, + {1, 0.5, 0.691341716182545, 0.038060233744356686, 0.7777851165098009, 0.08426519384872727}, + {1, 0.5, 0.7777851165098009, 0.08426519384872727, 0.8535533905932737, 0.14644660940672616}, + {1, 0.5, 0.8535533905932737, 0.14644660940672616, 0.9157348061512727, 0.2222148834901989}, + {1, 0.5, 0.9157348061512727, 0.2222148834901989, 0.9619397662556433, 0.3086582838174548}, + {1, 0.5, 0.9619397662556433, 0.3086582838174548, 0.9903926402016152, 0.4024548389919356}, }; - diff --git a/src/pax_config.h b/src/pax_config.h index 88e998c..c2bfc5e 100644 --- a/src/pax_config.h +++ b/src/pax_config.h @@ -1,82 +1,61 @@ -/* - MIT License - Copyright (c) 2021-2023 Julian Scheffers - - Permission is hereby granted, free of charge, to any person obtaining a copy - of this software and associated documentation files (the "Software"), to deal - in the Software without restriction, including without limitation the rights - to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - copies of the Software, and to permit persons to whom the Software is - furnished to do so, subject to the following conditions: - - The above copyright notice and this permission notice shall be included in all - copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - SOFTWARE. -*/ +// SPDX-License-Identifier: MIT #ifndef PAX_CONFIG_H #define PAX_CONFIG_H -#include -#include #include +#include +#include #ifndef PAX_DO_BICUBIC // Perform bicubic interpolation in text and images. - #define PAX_DO_BICUBIC false +#define PAX_DO_BICUBIC false #endif #ifndef PAX_AUTOREPORT // Log errors instead of just setting pax_last_error. - #define PAX_AUTOREPORT true +#define PAX_AUTOREPORT true #endif #ifndef PAX_COMPILE_BEZIER // Compile in bezier curves. - #define PAX_COMPILE_BEZIER true +#define PAX_COMPILE_BEZIER true #endif #ifndef PAX_USE_EXPENSIVE_BEZIER // Use the more expensive, but more accurate algorithm in pax_draw_bezier. - #define PAX_USE_EXPENSIVE_BEZIER false +#define PAX_USE_EXPENSIVE_BEZIER false #endif #ifndef PAX_COMPILE_TRIANGULATE // Compile in triangulation (filling the outline of a shape). - #define PAX_COMPILE_TRIANGULATE true +#define PAX_COMPILE_TRIANGULATE true #endif #ifndef PAX_COMPILE_FONT_INDEX // Compile in all fonts and the fonts index. - #define PAX_COMPILE_FONT_INDEX false +#define PAX_COMPILE_FONT_INDEX false #endif #ifndef PAX_COMPILE_MCR // Compile in multi-core rendering. - #define PAX_COMPILE_MCR true +#define PAX_COMPILE_MCR true #endif #ifndef PAX_COMPILE_ORIENTATION // Compile in buffer orientation settings. - #define PAX_COMPILE_ORIENTATION true +#define PAX_COMPILE_ORIENTATION true #endif #ifndef PAX_QUEUE_SIZE // Queue size to use for multi-core rendering. - #define PAX_QUEUE_SIZE 32 +#define PAX_QUEUE_SIZE 32 #endif #ifndef PAX_USE_FIXED_POINT // Whether to use fixed-point arithmetic internally. - #define PAX_USE_FIXED_POINT true +#define PAX_USE_FIXED_POINT true #endif -#endif //PAX_CONFIG_H +#endif // PAX_CONFIG_H diff --git a/src/pax_fixpt.hpp b/src/pax_fixpt.hpp index 6827fcb..1c4d7d4 100644 --- a/src/pax_fixpt.hpp +++ b/src/pax_fixpt.hpp @@ -1,26 +1,5 @@ -/* - MIT License - - Copyright (c) 2021-2023 Julian Scheffers - - Permission is hereby granted, free of charge, to any person obtaining a copy - of this software and associated documentation files (the "Software"), to deal - in the Software without restriction, including without limitation the rights - to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - copies of the Software, and to permit persons to whom the Software is - furnished to do so, subject to the following conditions: - - The above copyright notice and this permission notice shall be included in all - copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - SOFTWARE. -*/ + +// SPDX-License-Identifier: MIT #ifndef FIXPT_HPP #define FIXPT_HPP @@ -29,8 +8,13 @@ #if !PAX_USE_FIXED_POINT typedef float fixpt_t; -static inline constexpr fixpt_t operator ""_fix(unsigned long long in) { return in; } -static inline constexpr fixpt_t operator ""_fix(long double in) { return in; } + +static inline constexpr fixpt_t operator""_fix(unsigned long long in) { + return in; +} +static inline constexpr fixpt_t operator""_fix(long double in) { + return in; +} #else #define PAX_FIXPT_INT_BITS 12 @@ -38,182 +22,394 @@ static inline constexpr fixpt_t operator ""_fix(long double in) { return in; } #define PAX_FIXPT_MUL 0x00100000 class fixpt_t { - public: - int32_t raw_value; - - private: - static constexpr int32_t _div(int32_t a, int32_t b) { - return b ? (((int64_t) a << PAX_FIXPT_FRAC_BITS) / (int64_t) b) : (a > 0 ? INT32_MAX : INT32_MIN); - } - static constexpr int32_t _mul(int32_t a, int32_t b) { - return ((int64_t) a * (int64_t) b) >> PAX_FIXPT_FRAC_BITS; - } - template - static constexpr int32_t _from(T in) { - return in * PAX_FIXPT_MUL; - } - template - static constexpr T _to(int32_t in) { - return (T) (in / (T) PAX_FIXPT_MUL); - } - constexpr fixpt_t(int32_t val, bool dummy): raw_value(val) {} - - public: - // Create from raw value (instead of conversion). - static constexpr fixpt_t from_raw(int32_t raw) { - return fixpt_t(raw, false); - } - - // Default (0). - constexpr fixpt_t(): raw_value(0) {} - // Keep default copy constructor. - constexpr fixpt_t(const fixpt_t &in) = default; - // Keep default move constructor. - constexpr fixpt_t(fixpt_t &&in) = default; - // Create from integer. - constexpr fixpt_t(int in): raw_value(_from(in)) {} - // Create from float. - constexpr fixpt_t(float in): raw_value(_from(in)) {} - // Create from float. - constexpr fixpt_t(double in): raw_value(_from(in)) {} - - // Keep default assignment operator. - fixpt_t &operator=(const fixpt_t &) = default; - // Keep default move assignment operator. - fixpt_t &operator=(fixpt_t &&) = default; - // Set from integer. - fixpt_t &operator=(int in) { raw_value = _from(in); return *this; } - // Set from float. - fixpt_t &operator=(float in) { raw_value = _from(in); return *this; } - // Set from float. - fixpt_t &operator=(double in) { raw_value = _from(in); return *this; } - - /* ==== Implicit conversion operators ==== */ - constexpr operator bool() const { return raw_value; } - constexpr operator char() const { return _to(raw_value); } - constexpr operator short() const { return _to(raw_value); } - constexpr operator int() const { return _to(raw_value); } - constexpr operator long() const { return _to(raw_value); } - constexpr operator long long() const { return _to(raw_value); } - constexpr operator float() const { return _to(raw_value); } - constexpr operator double() const { return _to(raw_value); } - constexpr operator long double() const { return _to(raw_value); } - - /* ==== Comparison operators ==== */ - template - constexpr bool operator==(T other) const { return raw_value == fixpt_t(other).raw_value; } - template - constexpr bool operator!=(T other) const { return raw_value != fixpt_t(other).raw_value; } - template - constexpr bool operator>=(T other) const { return raw_value >= fixpt_t(other).raw_value; } - template - constexpr bool operator<=(T other) const { return raw_value <= fixpt_t(other).raw_value; } - template - constexpr bool operator>(T other) const { return raw_value > fixpt_t(other).raw_value; } - template - constexpr bool operator<(T other) const { return raw_value < fixpt_t(other).raw_value; } - - /* ==== Binary math operators ==== */ - constexpr fixpt_t operator+(fixpt_t other) const { return from_raw(raw_value + other.raw_value); } - constexpr fixpt_t operator-(fixpt_t other) const { return from_raw(raw_value - other.raw_value); } - constexpr fixpt_t operator*(fixpt_t other) const { return from_raw(_mul(raw_value, other.raw_value)); } - constexpr fixpt_t operator/(fixpt_t other) const { return from_raw(_div(raw_value, other.raw_value)); } - constexpr fixpt_t operator<<(int other) const { return from_raw(raw_value << other); } - constexpr fixpt_t operator>>(int other) const { return from_raw(raw_value >> other); } - - /* ==== Unary math operators ==== */ - constexpr bool operator!() const { return !raw_value; } - constexpr fixpt_t operator+() const { return *this; } - constexpr fixpt_t operator-() const { return from_raw(-raw_value); } - fixpt_t &operator++() { raw_value += PAX_FIXPT_MUL; return *this; } - fixpt_t &operator--() { raw_value -= PAX_FIXPT_MUL; return *this; } - fixpt_t operator++(int) { auto pre = *this; raw_value += PAX_FIXPT_MUL; return pre; } - fixpt_t operator--(int) { auto pre = *this; raw_value -= PAX_FIXPT_MUL; return pre; } - - /* ==== Assignment math operators ==== */ - fixpt_t &operator+=(fixpt_t other) { raw_value = raw_value + other.raw_value; return *this; } - fixpt_t &operator-=(fixpt_t other) { raw_value = raw_value - other.raw_value; return *this; } - fixpt_t &operator*=(fixpt_t other) { raw_value = _mul(raw_value, other.raw_value); return *this; } - fixpt_t &operator/=(fixpt_t other) { raw_value = _div(raw_value, other.raw_value); return *this; } - fixpt_t &operator<<=(int other) { raw_value = raw_value << other; return *this; } - fixpt_t &operator>>=(int other) { raw_value = raw_value >> other; return *this; } + public: + int32_t raw_value; + + private: + static constexpr int32_t _div(int32_t a, int32_t b) { + return b ? (((int64_t)a << PAX_FIXPT_FRAC_BITS) / (int64_t)b) : (a > 0 ? INT32_MAX : INT32_MIN); + } + static constexpr int32_t _mul(int32_t a, int32_t b) { + return ((int64_t)a * (int64_t)b) >> PAX_FIXPT_FRAC_BITS; + } + template static constexpr int32_t _from(T in) { + return in * PAX_FIXPT_MUL; + } + template static constexpr T _to(int32_t in) { + return (T)(in / (T)PAX_FIXPT_MUL); + } + constexpr fixpt_t(int32_t val, bool dummy) : raw_value(val) { + } + + public: + // Create from raw value (instead of conversion). + static constexpr fixpt_t from_raw(int32_t raw) { + return fixpt_t(raw, false); + } + + // Default (0). + constexpr fixpt_t() : raw_value(0) { + } + // Keep default copy constructor. + constexpr fixpt_t(fixpt_t const &in) = default; + // Keep default move constructor. + constexpr fixpt_t(fixpt_t &&in) = default; + // Create from integer. + constexpr fixpt_t(int in) : raw_value(_from(in)) { + } + // Create from float. + constexpr fixpt_t(float in) : raw_value(_from(in)) { + } + // Create from float. + constexpr fixpt_t(double in) : raw_value(_from(in)) { + } + + // Keep default assignment operator. + fixpt_t &operator=(fixpt_t const &) = default; + // Keep default move assignment operator. + fixpt_t &operator=(fixpt_t &&) = default; + // Set from integer. + fixpt_t &operator=(int in) { + raw_value = _from(in); + return *this; + } + // Set from float. + fixpt_t &operator=(float in) { + raw_value = _from(in); + return *this; + } + // Set from float. + fixpt_t &operator=(double in) { + raw_value = _from(in); + return *this; + } + + /* ==== Implicit conversion operators ==== */ + constexpr operator bool() const { + return raw_value; + } + constexpr operator char() const { + return _to(raw_value); + } + constexpr operator short() const { + return _to(raw_value); + } + constexpr operator int() const { + return _to(raw_value); + } + constexpr operator long() const { + return _to(raw_value); + } + constexpr operator long long() const { + return _to(raw_value); + } + constexpr operator float() const { + return _to(raw_value); + } + constexpr operator double() const { + return _to(raw_value); + } + constexpr operator long double() const { + return _to(raw_value); + } + + /* ==== Comparison operators ==== */ + template constexpr bool operator==(T other) const { + return raw_value == fixpt_t(other).raw_value; + } + template constexpr bool operator!=(T other) const { + return raw_value != fixpt_t(other).raw_value; + } + template constexpr bool operator>=(T other) const { + return raw_value >= fixpt_t(other).raw_value; + } + template constexpr bool operator<=(T other) const { + return raw_value <= fixpt_t(other).raw_value; + } + template constexpr bool operator>(T other) const { + return raw_value > fixpt_t(other).raw_value; + } + template constexpr bool operator<(T other) const { + return raw_value < fixpt_t(other).raw_value; + } + + /* ==== Binary math operators ==== */ + constexpr fixpt_t operator+(fixpt_t other) const { + return from_raw(raw_value + other.raw_value); + } + constexpr fixpt_t operator-(fixpt_t other) const { + return from_raw(raw_value - other.raw_value); + } + constexpr fixpt_t operator*(fixpt_t other) const { + return from_raw(_mul(raw_value, other.raw_value)); + } + constexpr fixpt_t operator/(fixpt_t other) const { + return from_raw(_div(raw_value, other.raw_value)); + } + constexpr fixpt_t operator<<(int other) const { + return from_raw(raw_value << other); + } + constexpr fixpt_t operator>>(int other) const { + return from_raw(raw_value >> other); + } + + /* ==== Unary math operators ==== */ + constexpr bool operator!() const { + return !raw_value; + } + constexpr fixpt_t operator+() const { + return *this; + } + constexpr fixpt_t operator-() const { + return from_raw(-raw_value); + } + fixpt_t &operator++() { + raw_value += PAX_FIXPT_MUL; + return *this; + } + fixpt_t &operator--() { + raw_value -= PAX_FIXPT_MUL; + return *this; + } + fixpt_t operator++(int) { + auto pre = *this; + raw_value += PAX_FIXPT_MUL; + return pre; + } + fixpt_t operator--(int) { + auto pre = *this; + raw_value -= PAX_FIXPT_MUL; + return pre; + } + + /* ==== Assignment math operators ==== */ + fixpt_t &operator+=(fixpt_t other) { + raw_value = raw_value + other.raw_value; + return *this; + } + fixpt_t &operator-=(fixpt_t other) { + raw_value = raw_value - other.raw_value; + return *this; + } + fixpt_t &operator*=(fixpt_t other) { + raw_value = _mul(raw_value, other.raw_value); + return *this; + } + fixpt_t &operator/=(fixpt_t other) { + raw_value = _div(raw_value, other.raw_value); + return *this; + } + fixpt_t &operator<<=(int other) { + raw_value = raw_value << other; + return *this; + } + fixpt_t &operator>>=(int other) { + raw_value = raw_value >> other; + return *this; + } }; /* ==== Miscellaneous math functions ==== */ -static inline constexpr fixpt_t abs(fixpt_t in) { return fixpt_t::from_raw(abs(in.raw_value)); } +static inline constexpr fixpt_t abs(fixpt_t in) { + return fixpt_t::from_raw(abs(in.raw_value)); +} /* ==== Literal operator ==== */ -static inline constexpr fixpt_t operator ""_fix(unsigned long long in) { return fixpt_t((int) in); } -static inline constexpr fixpt_t operator ""_fix(long double in) { return fixpt_t((double) in); } +static inline constexpr fixpt_t operator""_fix(unsigned long long in) { + return fixpt_t((int)in); +} +static inline constexpr fixpt_t operator""_fix(long double in) { + return fixpt_t((double)in); +} /* ==== Fixed point and integer ==== */ -static inline constexpr fixpt_t operator+(int a, fixpt_t b) { return fixpt_t(a) + fixpt_t(b); } -static inline constexpr fixpt_t operator-(int a, fixpt_t b) { return fixpt_t(a) - fixpt_t(b); } -static inline constexpr fixpt_t operator*(int a, fixpt_t b) { return fixpt_t(a) * fixpt_t(b); } -static inline constexpr fixpt_t operator/(int a, fixpt_t b) { return fixpt_t(a) / fixpt_t(b); } -static inline constexpr fixpt_t operator==(int a, fixpt_t b) { return fixpt_t(a) == fixpt_t(b); } -static inline constexpr fixpt_t operator!=(int a, fixpt_t b) { return fixpt_t(a) != fixpt_t(b); } -static inline constexpr fixpt_t operator<=(int a, fixpt_t b) { return fixpt_t(a) <= fixpt_t(b); } -static inline constexpr fixpt_t operator>=(int a, fixpt_t b) { return fixpt_t(a) >= fixpt_t(b); } -static inline constexpr fixpt_t operator<(int a, fixpt_t b) { return fixpt_t(a) < fixpt_t(b); } -static inline constexpr fixpt_t operator>(int a, fixpt_t b) { return fixpt_t(a) > fixpt_t(b); } - -static inline constexpr fixpt_t operator+(fixpt_t a, int b) { return fixpt_t(a) + fixpt_t(b); } -static inline constexpr fixpt_t operator-(fixpt_t a, int b) { return fixpt_t(a) - fixpt_t(b); } -static inline constexpr fixpt_t operator*(fixpt_t a, int b) { return fixpt_t(a) * fixpt_t(b); } -static inline constexpr fixpt_t operator/(fixpt_t a, int b) { return fixpt_t(a) / fixpt_t(b); } -static inline constexpr fixpt_t operator==(fixpt_t a, int b) { return fixpt_t(a) == fixpt_t(b); } -static inline constexpr fixpt_t operator!=(fixpt_t a, int b) { return fixpt_t(a) != fixpt_t(b); } -static inline constexpr fixpt_t operator<=(fixpt_t a, int b) { return fixpt_t(a) <= fixpt_t(b); } -static inline constexpr fixpt_t operator>=(fixpt_t a, int b) { return fixpt_t(a) >= fixpt_t(b); } -static inline constexpr fixpt_t operator<(fixpt_t a, int b) { return fixpt_t(a) < fixpt_t(b); } -static inline constexpr fixpt_t operator>(fixpt_t a, int b) { return fixpt_t(a) > fixpt_t(b); } +static inline constexpr fixpt_t operator+(int a, fixpt_t b) { + return fixpt_t(a) + fixpt_t(b); +} +static inline constexpr fixpt_t operator-(int a, fixpt_t b) { + return fixpt_t(a) - fixpt_t(b); +} +static inline constexpr fixpt_t operator*(int a, fixpt_t b) { + return fixpt_t(a) * fixpt_t(b); +} +static inline constexpr fixpt_t operator/(int a, fixpt_t b) { + return fixpt_t(a) / fixpt_t(b); +} +static inline constexpr fixpt_t operator==(int a, fixpt_t b) { + return fixpt_t(a) == fixpt_t(b); +} +static inline constexpr fixpt_t operator!=(int a, fixpt_t b) { + return fixpt_t(a) != fixpt_t(b); +} +static inline constexpr fixpt_t operator<=(int a, fixpt_t b) { + return fixpt_t(a) <= fixpt_t(b); +} +static inline constexpr fixpt_t operator>=(int a, fixpt_t b) { + return fixpt_t(a) >= fixpt_t(b); +} +static inline constexpr fixpt_t operator<(int a, fixpt_t b) { + return fixpt_t(a) < fixpt_t(b); +} +static inline constexpr fixpt_t operator>(int a, fixpt_t b) { + return fixpt_t(a) > fixpt_t(b); +} + +static inline constexpr fixpt_t operator+(fixpt_t a, int b) { + return fixpt_t(a) + fixpt_t(b); +} +static inline constexpr fixpt_t operator-(fixpt_t a, int b) { + return fixpt_t(a) - fixpt_t(b); +} +static inline constexpr fixpt_t operator*(fixpt_t a, int b) { + return fixpt_t(a) * fixpt_t(b); +} +static inline constexpr fixpt_t operator/(fixpt_t a, int b) { + return fixpt_t(a) / fixpt_t(b); +} +static inline constexpr fixpt_t operator==(fixpt_t a, int b) { + return fixpt_t(a) == fixpt_t(b); +} +static inline constexpr fixpt_t operator!=(fixpt_t a, int b) { + return fixpt_t(a) != fixpt_t(b); +} +static inline constexpr fixpt_t operator<=(fixpt_t a, int b) { + return fixpt_t(a) <= fixpt_t(b); +} +static inline constexpr fixpt_t operator>=(fixpt_t a, int b) { + return fixpt_t(a) >= fixpt_t(b); +} +static inline constexpr fixpt_t operator<(fixpt_t a, int b) { + return fixpt_t(a) < fixpt_t(b); +} +static inline constexpr fixpt_t operator>(fixpt_t a, int b) { + return fixpt_t(a) > fixpt_t(b); +} /* ==== Fixed point and float ==== */ -static inline constexpr fixpt_t operator+(float a, fixpt_t b) { return fixpt_t(a) + fixpt_t(b); } -static inline constexpr fixpt_t operator-(float a, fixpt_t b) { return fixpt_t(a) - fixpt_t(b); } -static inline constexpr fixpt_t operator*(float a, fixpt_t b) { return fixpt_t(a) * fixpt_t(b); } -static inline constexpr fixpt_t operator/(float a, fixpt_t b) { return fixpt_t(a) / fixpt_t(b); } -static inline constexpr fixpt_t operator==(float a, fixpt_t b) { return fixpt_t(a) == fixpt_t(b); } -static inline constexpr fixpt_t operator!=(float a, fixpt_t b) { return fixpt_t(a) != fixpt_t(b); } -static inline constexpr fixpt_t operator<=(float a, fixpt_t b) { return fixpt_t(a) <= fixpt_t(b); } -static inline constexpr fixpt_t operator>=(float a, fixpt_t b) { return fixpt_t(a) >= fixpt_t(b); } -static inline constexpr fixpt_t operator<(float a, fixpt_t b) { return fixpt_t(a) < fixpt_t(b); } -static inline constexpr fixpt_t operator>(float a, fixpt_t b) { return fixpt_t(a) > fixpt_t(b); } - -static inline constexpr fixpt_t operator+(fixpt_t a, float b) { return fixpt_t(a) + fixpt_t(b); } -static inline constexpr fixpt_t operator-(fixpt_t a, float b) { return fixpt_t(a) - fixpt_t(b); } -static inline constexpr fixpt_t operator*(fixpt_t a, float b) { return fixpt_t(a) * fixpt_t(b); } -static inline constexpr fixpt_t operator/(fixpt_t a, float b) { return fixpt_t(a) / fixpt_t(b); } -static inline constexpr fixpt_t operator==(fixpt_t a, float b) { return fixpt_t(a) == fixpt_t(b); } -static inline constexpr fixpt_t operator!=(fixpt_t a, float b) { return fixpt_t(a) != fixpt_t(b); } -static inline constexpr fixpt_t operator<=(fixpt_t a, float b) { return fixpt_t(a) <= fixpt_t(b); } -static inline constexpr fixpt_t operator>=(fixpt_t a, float b) { return fixpt_t(a) >= fixpt_t(b); } -static inline constexpr fixpt_t operator<(fixpt_t a, float b) { return fixpt_t(a) < fixpt_t(b); } -static inline constexpr fixpt_t operator>(fixpt_t a, float b) { return fixpt_t(a) > fixpt_t(b); } +static inline constexpr fixpt_t operator+(float a, fixpt_t b) { + return fixpt_t(a) + fixpt_t(b); +} +static inline constexpr fixpt_t operator-(float a, fixpt_t b) { + return fixpt_t(a) - fixpt_t(b); +} +static inline constexpr fixpt_t operator*(float a, fixpt_t b) { + return fixpt_t(a) * fixpt_t(b); +} +static inline constexpr fixpt_t operator/(float a, fixpt_t b) { + return fixpt_t(a) / fixpt_t(b); +} +static inline constexpr fixpt_t operator==(float a, fixpt_t b) { + return fixpt_t(a) == fixpt_t(b); +} +static inline constexpr fixpt_t operator!=(float a, fixpt_t b) { + return fixpt_t(a) != fixpt_t(b); +} +static inline constexpr fixpt_t operator<=(float a, fixpt_t b) { + return fixpt_t(a) <= fixpt_t(b); +} +static inline constexpr fixpt_t operator>=(float a, fixpt_t b) { + return fixpt_t(a) >= fixpt_t(b); +} +static inline constexpr fixpt_t operator<(float a, fixpt_t b) { + return fixpt_t(a) < fixpt_t(b); +} +static inline constexpr fixpt_t operator>(float a, fixpt_t b) { + return fixpt_t(a) > fixpt_t(b); +} + +static inline constexpr fixpt_t operator+(fixpt_t a, float b) { + return fixpt_t(a) + fixpt_t(b); +} +static inline constexpr fixpt_t operator-(fixpt_t a, float b) { + return fixpt_t(a) - fixpt_t(b); +} +static inline constexpr fixpt_t operator*(fixpt_t a, float b) { + return fixpt_t(a) * fixpt_t(b); +} +static inline constexpr fixpt_t operator/(fixpt_t a, float b) { + return fixpt_t(a) / fixpt_t(b); +} +static inline constexpr fixpt_t operator==(fixpt_t a, float b) { + return fixpt_t(a) == fixpt_t(b); +} +static inline constexpr fixpt_t operator!=(fixpt_t a, float b) { + return fixpt_t(a) != fixpt_t(b); +} +static inline constexpr fixpt_t operator<=(fixpt_t a, float b) { + return fixpt_t(a) <= fixpt_t(b); +} +static inline constexpr fixpt_t operator>=(fixpt_t a, float b) { + return fixpt_t(a) >= fixpt_t(b); +} +static inline constexpr fixpt_t operator<(fixpt_t a, float b) { + return fixpt_t(a) < fixpt_t(b); +} +static inline constexpr fixpt_t operator>(fixpt_t a, float b) { + return fixpt_t(a) > fixpt_t(b); +} /* ==== Fixed point and float ==== */ -static inline constexpr fixpt_t operator+(double a, fixpt_t b) { return fixpt_t(a) + fixpt_t(b); } -static inline constexpr fixpt_t operator-(double a, fixpt_t b) { return fixpt_t(a) - fixpt_t(b); } -static inline constexpr fixpt_t operator*(double a, fixpt_t b) { return fixpt_t(a) * fixpt_t(b); } -static inline constexpr fixpt_t operator/(double a, fixpt_t b) { return fixpt_t(a) / fixpt_t(b); } -static inline constexpr fixpt_t operator==(double a, fixpt_t b) { return fixpt_t(a) == fixpt_t(b); } -static inline constexpr fixpt_t operator!=(double a, fixpt_t b) { return fixpt_t(a) != fixpt_t(b); } -static inline constexpr fixpt_t operator<=(double a, fixpt_t b) { return fixpt_t(a) <= fixpt_t(b); } -static inline constexpr fixpt_t operator>=(double a, fixpt_t b) { return fixpt_t(a) >= fixpt_t(b); } -static inline constexpr fixpt_t operator<(double a, fixpt_t b) { return fixpt_t(a) < fixpt_t(b); } -static inline constexpr fixpt_t operator>(double a, fixpt_t b) { return fixpt_t(a) > fixpt_t(b); } - -static inline constexpr fixpt_t operator+(fixpt_t a, double b) { return fixpt_t(a) + fixpt_t(b); } -static inline constexpr fixpt_t operator-(fixpt_t a, double b) { return fixpt_t(a) - fixpt_t(b); } -static inline constexpr fixpt_t operator*(fixpt_t a, double b) { return fixpt_t(a) * fixpt_t(b); } -static inline constexpr fixpt_t operator/(fixpt_t a, double b) { return fixpt_t(a) / fixpt_t(b); } -static inline constexpr fixpt_t operator==(fixpt_t a, double b) { return fixpt_t(a) == fixpt_t(b); } -static inline constexpr fixpt_t operator!=(fixpt_t a, double b) { return fixpt_t(a) != fixpt_t(b); } -static inline constexpr fixpt_t operator<=(fixpt_t a, double b) { return fixpt_t(a) <= fixpt_t(b); } -static inline constexpr fixpt_t operator>=(fixpt_t a, double b) { return fixpt_t(a) >= fixpt_t(b); } -static inline constexpr fixpt_t operator<(fixpt_t a, double b) { return fixpt_t(a) < fixpt_t(b); } -static inline constexpr fixpt_t operator>(fixpt_t a, double b) { return fixpt_t(a) > fixpt_t(b); } +static inline constexpr fixpt_t operator+(double a, fixpt_t b) { + return fixpt_t(a) + fixpt_t(b); +} +static inline constexpr fixpt_t operator-(double a, fixpt_t b) { + return fixpt_t(a) - fixpt_t(b); +} +static inline constexpr fixpt_t operator*(double a, fixpt_t b) { + return fixpt_t(a) * fixpt_t(b); +} +static inline constexpr fixpt_t operator/(double a, fixpt_t b) { + return fixpt_t(a) / fixpt_t(b); +} +static inline constexpr fixpt_t operator==(double a, fixpt_t b) { + return fixpt_t(a) == fixpt_t(b); +} +static inline constexpr fixpt_t operator!=(double a, fixpt_t b) { + return fixpt_t(a) != fixpt_t(b); +} +static inline constexpr fixpt_t operator<=(double a, fixpt_t b) { + return fixpt_t(a) <= fixpt_t(b); +} +static inline constexpr fixpt_t operator>=(double a, fixpt_t b) { + return fixpt_t(a) >= fixpt_t(b); +} +static inline constexpr fixpt_t operator<(double a, fixpt_t b) { + return fixpt_t(a) < fixpt_t(b); +} +static inline constexpr fixpt_t operator>(double a, fixpt_t b) { + return fixpt_t(a) > fixpt_t(b); +} + +static inline constexpr fixpt_t operator+(fixpt_t a, double b) { + return fixpt_t(a) + fixpt_t(b); +} +static inline constexpr fixpt_t operator-(fixpt_t a, double b) { + return fixpt_t(a) - fixpt_t(b); +} +static inline constexpr fixpt_t operator*(fixpt_t a, double b) { + return fixpt_t(a) * fixpt_t(b); +} +static inline constexpr fixpt_t operator/(fixpt_t a, double b) { + return fixpt_t(a) / fixpt_t(b); +} +static inline constexpr fixpt_t operator==(fixpt_t a, double b) { + return fixpt_t(a) == fixpt_t(b); +} +static inline constexpr fixpt_t operator!=(fixpt_t a, double b) { + return fixpt_t(a) != fixpt_t(b); +} +static inline constexpr fixpt_t operator<=(fixpt_t a, double b) { + return fixpt_t(a) <= fixpt_t(b); +} +static inline constexpr fixpt_t operator>=(fixpt_t a, double b) { + return fixpt_t(a) >= fixpt_t(b); +} +static inline constexpr fixpt_t operator<(fixpt_t a, double b) { + return fixpt_t(a) < fixpt_t(b); +} +static inline constexpr fixpt_t operator>(fixpt_t a, double b) { + return fixpt_t(a) > fixpt_t(b); +} #endif // !PAX_USE_FIXED_POINT #endif // FIXPT_HPP diff --git a/src/pax_fonts.c b/src/pax_fonts.c index 524e1ae..2916941 100644 --- a/src/pax_fonts.c +++ b/src/pax_fonts.c @@ -1,38 +1,19 @@ -/* - MIT License - - Copyright (c) 2021-2023 Julian Scheffers - - Permission is hereby granted, free of charge, to any person obtaining a copy - of this software and associated documentation files (the "Software"), to deal - in the Software without restriction, including without limitation the rights - to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - copies of the Software, and to permit persons to whom the Software is - furnished to do so, subject to the following conditions: - - The above copyright notice and this permission notice shall be included in all - copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - SOFTWARE. -*/ + +// SPDX-License-Identifier: MIT #include "pax_fonts.h" + #include "pax_internal.h" + #include -extern const pax_font_range_t pax_font_sky_ranges[]; -extern const pax_font_range_t permanentmarker_ranges[]; -extern const pax_font_range_t sairaregular_ranges[]; -extern const pax_font_range_t sairacondensed_ranges[]; +extern pax_font_range_t const pax_font_sky_ranges[]; +extern pax_font_range_t const permanentmarker_ranges[]; +extern pax_font_range_t const sairaregular_ranges[]; +extern pax_font_range_t const sairacondensed_ranges[]; // Font ROMs. -extern const uint8_t font_bitmap_raw_7x9[]; +extern uint8_t const font_bitmap_raw_7x9[]; // ¯\_(ツ)_/¯ // 0 1 2 3 4 5 6 @@ -47,140 +28,154 @@ extern const uint8_t font_bitmap_raw_7x9[]; // 8 . . . . . . . -static const uint8_t funny_thingy[] = { - 0x00, - 0x0a, - 0x0a, - 0x40, - 0x40, - 0x20, - 0x10, - 0x0e, +static uint8_t const funny_thingy[] = { + 0x00, + 0x0a, + 0x0a, + 0x40, + 0x40, + 0x20, + 0x10, + 0x0e, }; -static const uint8_t unfunny_thingy[] = { - 0x00, - 0x3e, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, +static uint8_t const unfunny_thingy[] = { + 0x00, + 0x3e, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, }; -static const pax_font_range_t font_7x9_ranges[] = { - { // Ascii range. - .type = PAX_FONT_TYPE_BITMAP_MONO, - .start = 0x00000, - .end = 0x00080, - .bitmap_mono = { - .glyphs = font_bitmap_raw_7x9, - .width = 7, - .height = 9, - .bpp = 1, - }, - }, { // Test range. - .type = PAX_FONT_TYPE_BITMAP_MONO, - .start = 0x030c4, - .end = 0x030c4, - .bitmap_mono = { - .glyphs = funny_thingy, - .width = 7, - .height = 9, - .bpp = 1, - }, - }, { // Macron range. - .type = PAX_FONT_TYPE_BITMAP_MONO, - .start = 0x000af, - .end = 0x000af, - .bitmap_mono = { - .glyphs = unfunny_thingy, - .width = 7, - .height = 9, - .bpp = 1, - }, - } +static pax_font_range_t const font_7x9_ranges[] = { + { + // Ascii range. + .type = PAX_FONT_TYPE_BITMAP_MONO, + .start = 0x00000, + .end = 0x00080, + .bitmap_mono = + { + .glyphs = font_bitmap_raw_7x9, + .width = 7, + .height = 9, + .bpp = 1, + }, + }, + { + // Test range. + .type = PAX_FONT_TYPE_BITMAP_MONO, + .start = 0x030c4, + .end = 0x030c4, + .bitmap_mono = + { + .glyphs = funny_thingy, + .width = 7, + .height = 9, + .bpp = 1, + }, + }, + { + // Macron range. + .type = PAX_FONT_TYPE_BITMAP_MONO, + .start = 0x000af, + .end = 0x000af, + .bitmap_mono = + { + .glyphs = unfunny_thingy, + .width = 7, + .height = 9, + .bpp = 1, + }, + } }; -const pax_font_t *pax_fonts_index[] = { +pax_font_t const *pax_fonts_index[] = { #if PAX_COMPILE_FONT_INDEX - &PRIVATE_pax_font_sky, - &PRIVATE_pax_font_sky_mono, - &PRIVATE_pax_font_marker, - &PRIVATE_pax_font_saira_condensed, - &PRIVATE_pax_font_saira_regular, + &PRIVATE_pax_font_sky, + &PRIVATE_pax_font_sky_mono, + &PRIVATE_pax_font_marker, + &PRIVATE_pax_font_saira_condensed, + &PRIVATE_pax_font_saira_regular, #endif }; -const size_t pax_n_fonts = sizeof(pax_fonts_index) / sizeof(const pax_font_t *); - -const pax_font_t PRIVATE_pax_font_sky = { // Sky - .name = "Sky", - .n_ranges = 6, - .ranges = pax_font_sky_ranges, - .default_size = 9, - .recommend_aa = false, +size_t const pax_n_fonts = sizeof(pax_fonts_index) / sizeof(pax_font_t const *); + +pax_font_t const PRIVATE_pax_font_sky = { + // Sky + .name = "Sky", + .n_ranges = 6, + .ranges = pax_font_sky_ranges, + .default_size = 9, + .recommend_aa = false, }; -const pax_font_t PRIVATE_pax_font_sky_mono = { // Sky mono - .name = "Sky Mono", - .n_ranges = 3, - .ranges = font_7x9_ranges, - .default_size = 9, - .recommend_aa = false, +pax_font_t const PRIVATE_pax_font_sky_mono = { + // Sky mono + .name = "Sky Mono", + .n_ranges = 3, + .ranges = font_7x9_ranges, + .default_size = 9, + .recommend_aa = false, }; -const pax_font_t PRIVATE_pax_font_marker = { // PermanentMarker - .name = "Permanent Marker", - .n_ranges = 3, - .ranges = permanentmarker_ranges, - .default_size = 22, - .recommend_aa = true, +pax_font_t const PRIVATE_pax_font_marker = { + // PermanentMarker + .name = "Permanent Marker", + .n_ranges = 3, + .ranges = permanentmarker_ranges, + .default_size = 22, + .recommend_aa = true, }; -const pax_font_t PRIVATE_pax_font_saira_condensed = { // Saira condensed - .name = "Saira Condensed", - .n_ranges = 3, - .ranges = sairacondensed_ranges, - .default_size = 45, - .recommend_aa = true, +pax_font_t const PRIVATE_pax_font_saira_condensed = { + // Saira condensed + .name = "Saira Condensed", + .n_ranges = 3, + .ranges = sairacondensed_ranges, + .default_size = 45, + .recommend_aa = true, }; -const pax_font_t PRIVATE_pax_font_saira_regular = { // Saira regular - .name = "Saira Regular", - .n_ranges = 27, - .ranges = sairaregular_ranges, - .default_size = 18, - .recommend_aa = true, +pax_font_t const PRIVATE_pax_font_saira_regular = { + // Saira regular + .name = "Saira Regular", + .n_ranges = 27, + .ranges = sairaregular_ranges, + .default_size = 18, + .recommend_aa = true, }; #if PAX_COMPILE_FONT_INDEX static char lower(char in) { - if (in >= 'A' && in <= 'Z') { - return in + 'a' - 'A'; - } else { - return in; - } + if (in >= 'A' && in <= 'Z') { + return in + 'a' - 'A'; + } else { + return in; + } } -static bool casecmp(const char *restrict a, const char *restrict b) { - while (*a || *b) { - if (lower(*a) != lower(*b)) return false; - a++; b++; - } - return true; +static bool casecmp(char const *restrict a, char const *restrict b) { + while (*a || *b) { + if (lower(*a) != lower(*b)) + return false; + a++; + b++; + } + return true; } // Finds the built-in font with the given name. -const pax_font_t *pax_get_font(const char *name) { - for (size_t i = 0; i < PAX_N_FONTS; i++) { - if (!casecmp(pax_fonts_index[i]->name, name)) { - return pax_fonts_index[i]; - } - } - return NULL; +pax_font_t const *pax_get_font(char const *name) { + for (size_t i = 0; i < PAX_N_FONTS; i++) { + if (!casecmp(pax_fonts_index[i]->name, name)) { + return pax_fonts_index[i]; + } + } + return NULL; } #else -const pax_font_t *pax_get_font(const char *name) { - // Not compiled in, so ignore this. - PAX_ERROR1("pax_get_font", PAX_ERR_UNSUPPORTED, NULL); +pax_font_t const *pax_get_font(char const *name) { + // Not compiled in, so ignore this. + PAX_ERROR1("pax_get_font", PAX_ERR_UNSUPPORTED, NULL); } #endif - diff --git a/src/pax_fonts.h b/src/pax_fonts.h index 2401646..0df67af 100644 --- a/src/pax_fonts.h +++ b/src/pax_fonts.h @@ -1,26 +1,5 @@ -/* - MIT License - Copyright (c) 2021-2023 Julian Scheffers - - Permission is hereby granted, free of charge, to any person obtaining a copy - of this software and associated documentation files (the "Software"), to deal - in the Software without restriction, including without limitation the rights - to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - copies of the Software, and to permit persons to whom the Software is - furnished to do so, subject to the following conditions: - - The above copyright notice and this permission notice shall be included in all - copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - SOFTWARE. -*/ +// SPDX-License-Identifier: MIT #ifndef PAX_FONTS_H #define PAX_FONTS_H @@ -33,11 +12,11 @@ extern "C" { /* ============ INDEX ============ */ -extern const pax_font_t PRIVATE_pax_font_sky; -extern const pax_font_t PRIVATE_pax_font_sky_mono; -extern const pax_font_t PRIVATE_pax_font_marker; -extern const pax_font_t PRIVATE_pax_font_saira_condensed; -extern const pax_font_t PRIVATE_pax_font_saira_regular; +extern pax_font_t const PRIVATE_pax_font_sky; +extern pax_font_t const PRIVATE_pax_font_sky_mono; +extern pax_font_t const PRIVATE_pax_font_marker; +extern pax_font_t const PRIVATE_pax_font_saira_condensed; +extern pax_font_t const PRIVATE_pax_font_saira_regular; #define pax_font_sky (&PRIVATE_pax_font_sky) #define pax_font_sky_mono (&PRIVATE_pax_font_sky_mono) @@ -46,20 +25,20 @@ extern const pax_font_t PRIVATE_pax_font_saira_regular; #define pax_font_saira_regular (&PRIVATE_pax_font_saira_regular) // The number of built-in fonts. -#define PAX_N_FONTS pax_n_fonts +#define PAX_N_FONTS pax_n_fonts // The default font ("sky", variable pitch). #define PAX_FONT_DEFAULT pax_font_sky // A comprehensive index of built-in fonts. -extern const pax_font_t *pax_fonts_index[]; -extern const size_t pax_n_fonts; +extern pax_font_t const *pax_fonts_index[]; +extern size_t const pax_n_fonts; /* ========== FUNCTIONS ========== */ // Finds the built-in font with the given name. -const pax_font_t *pax_get_font(const char *name); +pax_font_t const *pax_get_font(char const *name); #ifdef __cplusplus } #endif //__cplusplus -#endif //PAX_FONTS_H +#endif // PAX_FONTS_H diff --git a/src/pax_gfx.c b/src/pax_gfx.c index 3d6567e..64459b7 100644 --- a/src/pax_gfx.c +++ b/src/pax_gfx.c @@ -1,83 +1,62 @@ -/* - MIT License - Copyright (c) 2021-2023 Julian Scheffers +// SPDX-License-Identifier: MIT - Permission is hereby granted, free of charge, to any person obtaining a copy - of this software and associated documentation files (the "Software"), to deal - in the Software without restriction, including without limitation the rights - to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - copies of the Software, and to permit persons to whom the Software is - furnished to do so, subject to the following conditions: - - The above copyright notice and this permission notice shall be included in all - copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - SOFTWARE. -*/ - -static const char *TAG = "pax_gfx"; +static char const *TAG = "pax_gfx"; #include "pax_internal.h" #include "pax_shaders.h" #include -#include #include +#include #ifdef PAX_ESP_IDF #include #endif // The last error reported. -pax_err_t pax_last_error = PAX_OK; +pax_err_t pax_last_error = PAX_OK; // Whether multi-core rendering is enabled. // You should not modify this variable. -bool pax_do_multicore = false; +bool pax_do_multicore = false; #if PAX_COMPILE_MCR // Whether or not the multicore task is currently busy. -bool multicore_busy = false; +bool multicore_busy = false; #ifdef PAX_ESP_IDF #include -#include #include +#include // The task handle for the main core. -TaskHandle_t main_handle = NULL; +TaskHandle_t main_handle = NULL; // The task handle for the other core. -TaskHandle_t multicore_handle = NULL; +TaskHandle_t multicore_handle = NULL; // The render queue for the other core. -QueueHandle_t queue_handle = NULL; +QueueHandle_t queue_handle = NULL; #elif defined(PAX_STANDALONE) #include -#include #include +#include // The thread for multicore helper stuff. pthread_t multicore_handle; // The mutex used to determine IDLE. -pthread_mutex_t multicore_mutex = PTHREAD_MUTEX_INITIALIZER; +pthread_mutex_t multicore_mutex = PTHREAD_MUTEX_INITIALIZER; // The render queue for the multicore helper. -ptq_queue_t queue_handle = NULL; +ptq_queue_t queue_handle = NULL; #endif #endif #if defined(PAX_STANDALONE) && PAX_COMPILE_MCR -pthread_mutex_t pax_log_mutex = PTHREAD_MUTEX_INITIALIZER; -bool pax_log_use_mutex = false; +pthread_mutex_t pax_log_mutex = PTHREAD_MUTEX_INITIALIZER; +bool pax_log_use_mutex = false; #endif @@ -85,64 +64,67 @@ bool pax_log_use_mutex = false; /* ============ DEBUG ============ */ // Print error to console. -void pax_report_error(const char *where, pax_err_t errno) { - // Ignore the "Error: Success" cases. - if (errno == PAX_OK) return; - - // // Number of silenced messages. - // static uint64_t silenced = 0; - // // Last spam message time in microseconds. - // static uint64_t last_spam = 0; - // // Spam silencing delay in microseconds. - // static const uint64_t spam_delay = 2 * 1000 * 1000; - - // // Check whether the message might potentially be spam. - // bool spam_potential = - // errno == PAX_ERR_NOBUF - // || !strcmp(where, "pax_get_pixel") - // || !strcmp(where, "pax_set_pixel"); - - // if (spam_potential) { - // // If so, check time. - // uint64_t now = esp_timer_get_time() + spam_delay; - - // if (now < last_spam + spam_delay) { - // // It gets blocked. - // silenced ++; - // return; - // } else if (silenced) { - // // It goes through, report silenced count. - // PAX_LOGE(TAG, "%llu silenced errors", silenced); - // silenced = 0; - // } - - // last_spam = now; - // } - - // Log the error. - PAX_LOGE(TAG, "@ %s: %s", where, pax_desc_err(errno)); +void pax_report_error(char const *where, pax_err_t errno) { + // Ignore the "Error: Success" cases. + if (errno == PAX_OK) + return; + + // // Number of silenced messages. + // static uint64_t silenced = 0; + // // Last spam message time in microseconds. + // static uint64_t last_spam = 0; + // // Spam silencing delay in microseconds. + // static const uint64_t spam_delay = 2 * 1000 * 1000; + + // // Check whether the message might potentially be spam. + // bool spam_potential = + // errno == PAX_ERR_NOBUF + // || !strcmp(where, "pax_get_pixel") + // || !strcmp(where, "pax_set_pixel"); + + // if (spam_potential) { + // // If so, check time. + // uint64_t now = esp_timer_get_time() + spam_delay; + + // if (now < last_spam + spam_delay) { + // // It gets blocked. + // silenced ++; + // return; + // } else if (silenced) { + // // It goes through, report silenced count. + // PAX_LOGE(TAG, "%llu silenced errors", silenced); + // silenced = 0; + // } + + // last_spam = now; + // } + + // Log the error. + PAX_LOGE(TAG, "@ %s: %s", where, pax_desc_err(errno)); } // Describe error. -const char *pax_desc_err(pax_err_t error) { - const char *unknown = "Unknown error"; - const char *desc[] = { - "Success", - "No framebuffer", - "No memory", - "Invalid parameters", - "Infinite parameters", - "Out of bounds", - "Matrix stack underflow", - "Out of data", - "Image decoding error", - "Unsupported operation", - "Corrupted buffer", - "Image encoding error", - }; - size_t n_desc = sizeof(desc) / sizeof(char *); - if (error > 0 || -error > n_desc) return unknown; - else return desc[-error]; +char const *pax_desc_err(pax_err_t error) { + char const *unknown = "Unknown error"; + char const *desc[] = { + "Success", + "No framebuffer", + "No memory", + "Invalid parameters", + "Infinite parameters", + "Out of bounds", + "Matrix stack underflow", + "Out of data", + "Image decoding error", + "Unsupported operation", + "Corrupted buffer", + "Image encoding error", + }; + size_t n_desc = sizeof(desc) / sizeof(char *); + if (error > 0 || -error > n_desc) + return unknown; + else + return desc[-error]; } @@ -150,50 +132,42 @@ const char *pax_desc_err(pax_err_t error) { /* ======= DRAWING HELPERS ======= */ // A wrapper callback to support V0 shader callbacks. -static pax_col_t pax_shader_wrapper_for_v0(pax_col_t tint, pax_col_t existing, int x, int y, float u, float v, void *args0) { - pax_shader_t *args = args0; - pax_shader_func_v0_t v0 = args->callback; - return pax_col_merge(existing, v0(tint, x, y, u, v, args->callback_args)); +static pax_col_t + pax_shader_wrapper_for_v0(pax_col_t tint, pax_col_t existing, int x, int y, float u, float v, void *args0) { + pax_shader_t *args = args0; + pax_shader_func_v0_t v0 = args->callback; + return pax_col_merge(existing, v0(tint, x, y, u, v, args->callback_args)); } // Gets the correct callback function for the shader. -pax_shader_ctx_t pax_get_shader_ctx(pax_buf_t *buf, pax_col_t color, const pax_shader_t *shader) { - if (shader->schema_version != ~shader->schema_complement) { - // TODO: Bad. - } - if (shader->schema_version == 0) { - // Use the old version. - return (pax_shader_ctx_t) { - .callback = pax_shader_wrapper_for_v0, - .callback_args = (void *) shader, - .do_getter = true, - .skip = false, - }; - } - - // Use the new version. - return (pax_shader_ctx_t) { - .callback = shader->callback, - .callback_args = shader->callback_args, - .do_getter = true, - .skip = false, - }; +pax_shader_ctx_t pax_get_shader_ctx(pax_buf_t *buf, pax_col_t color, pax_shader_t const *shader) { + if (shader->schema_version != ~shader->schema_complement) { + // TODO: Bad. + } + if (shader->schema_version == 0) { + // Use the old version. + return (pax_shader_ctx_t){ + .callback = pax_shader_wrapper_for_v0, + .callback_args = (void *)shader, + .do_getter = true, + .skip = false, + }; + } + + // Use the new version. + return (pax_shader_ctx_t){ + .callback = shader->callback, + .callback_args = shader->callback_args, + .do_getter = true, + .skip = false, + }; } // Dummy UVs used for quad UVs where NULL is provided. -static const pax_quadf dummy_quad_uvs = { - .x0 = 0, .y0 = 0, - .x1 = 1, .y1 = 0, - .x2 = 1, .y2 = 1, - .x3 = 0, .y3 = 1 -}; +static pax_quadf const dummy_quad_uvs = {.x0 = 0, .y0 = 0, .x1 = 1, .y1 = 0, .x2 = 1, .y2 = 1, .x3 = 0, .y3 = 1}; // Dummy UVs used for tri UVs where NULL is provided. -static const pax_trif dummy_tri_uvs = { - .x0 = 0, .y0 = 0, - .x1 = 1, .y1 = 0, - .x2 = 0, .y2 = 1 -}; +static pax_trif const dummy_tri_uvs = {.x0 = 0, .y0 = 0, .x1 = 1, .y1 = 0, .x2 = 0, .y2 = 1}; @@ -202,400 +176,438 @@ static const pax_trif dummy_tri_uvs = { // Create a new buffer. // If mem is NULL, a new area is allocated. void pax_buf_init(pax_buf_t *buf, void *mem, int width, int height, pax_buf_type_t type) { - bool use_alloc = !mem; - if (use_alloc) { - // Allocate the right amount of bytes. - mem = malloc(PAX_BUF_CALC_SIZE(width, height, type)); - if (!mem) PAX_ERROR("pax_buf_init", PAX_ERR_NOMEM); - } - *buf = (pax_buf_t) { - // Buffer size information. - .type = type, - .buf = mem, - .width = width, - .height = height, - .bpp = PAX_GET_BPP(type), - // Defaults. - .stack_2d = { - .parent = NULL, - .value = matrix_2d_identity() - }, - // Memory management information. - .do_free = use_alloc, - .do_free_pal = false, - .reverse_endianness = false, - .pallette = NULL - }; - // Bit of workaround because 16BPP screens almost always require this option. - if (PAX_GET_BPP(type) == 16) buf->reverse_endianness = true; - // Update getters and setters. - pax_get_col_conv(buf, &buf->col2buf, &buf->buf2col); - pax_get_setters(buf, &buf->getter, &buf->setter); - if (use_alloc) { - // Funny. - pax_background(buf, 0); - pax_draw_text(buf, PAX_IS_PALETTE(type) ? 1 : 0xffffffff, pax_font_sky, 9, 5, 5, "Julian Wuz Here"); - } - // The clip rectangle is disabled by default. - pax_noclip(buf); - PAX_SUCCESS(); + bool use_alloc = !mem; + if (use_alloc) { + // Allocate the right amount of bytes. + mem = malloc(PAX_BUF_CALC_SIZE(width, height, type)); + if (!mem) + PAX_ERROR("pax_buf_init", PAX_ERR_NOMEM); + } + *buf = (pax_buf_t){// Buffer size information. + .type = type, + .buf = mem, + .width = width, + .height = height, + .bpp = PAX_GET_BPP(type), + // Defaults. + .stack_2d = {.parent = NULL, .value = matrix_2d_identity()}, + // Memory management information. + .do_free = use_alloc, + .do_free_pal = false, + .reverse_endianness = false, + .pallette = NULL + }; + // Bit of workaround because 16BPP screens almost always require this option. + if (PAX_GET_BPP(type) == 16) + buf->reverse_endianness = true; + // Update getters and setters. + pax_get_col_conv(buf, &buf->col2buf, &buf->buf2col); + pax_get_setters(buf, &buf->getter, &buf->setter); + if (use_alloc) { + // Funny. + pax_background(buf, 0); + pax_draw_text(buf, PAX_IS_PALETTE(type) ? 1 : 0xffffffff, pax_font_sky, 9, 5, 5, "Julian Wuz Here"); + } + // The clip rectangle is disabled by default. + pax_noclip(buf); + PAX_SUCCESS(); } // Enable/disable the reversing of endianness for `buf`. // Some displays might require a feature like this one. void pax_buf_reversed(pax_buf_t *buf, bool reversed_endianness) { - PAX_BUF_CHECK("pax_buf_reversed"); - - // Update endianness flag. - buf->reverse_endianness = reversed_endianness; - // Update getters and setters. - pax_get_col_conv(buf, &buf->col2buf, &buf->buf2col); - pax_get_setters(buf, &buf->getter, &buf->setter); - - PAX_SUCCESS(); + PAX_BUF_CHECK("pax_buf_reversed"); + + // Update endianness flag. + buf->reverse_endianness = reversed_endianness; + // Update getters and setters. + pax_get_col_conv(buf, &buf->col2buf, &buf->buf2col); + pax_get_setters(buf, &buf->getter, &buf->setter); + + PAX_SUCCESS(); } // Destroy the buffer, freeing its memory. void pax_buf_destroy(pax_buf_t *buf) { - PAX_BUF_CHECK("pax_buf_destroy"); - - // Recursively unlink the matrix stack. - matrix_stack_2d_t *current = buf->stack_2d.parent; - while (current) { - matrix_stack_2d_t *next = current->parent; - free(current); - current = next; - } - - // Free allocated memory. - if (buf->do_free) { - free(buf->buf); - } - if (buf->pallette && buf->do_free_pal) { - free(buf->pallette); - } - - // A safety mechanism to prevent use-after-free on the user's behalf. - buf->buf = NULL; - buf->type = 0; - - PAX_SUCCESS(); + PAX_BUF_CHECK("pax_buf_destroy"); + + // Recursively unlink the matrix stack. + matrix_stack_2d_t *current = buf->stack_2d.parent; + while (current) { + matrix_stack_2d_t *next = current->parent; + free(current); + current = next; + } + + // Free allocated memory. + if (buf->do_free) { + free(buf->buf); + } + if (buf->pallette && buf->do_free_pal) { + free(buf->pallette); + } + + // A safety mechanism to prevent use-after-free on the user's behalf. + buf->buf = NULL; + buf->type = 0; + + PAX_SUCCESS(); } // WARNING: This is a beta feature and it does not work! -// +// // Convert the buffer to the given new format. // If dest is equal to src, src will be converted. void pax_buf_convert(pax_buf_t *dst, pax_buf_t *src, pax_buf_type_t type) { - if (!(src) || !(src)->buf) PAX_ERROR("pax_buf_convert (src)", PAX_ERR_NOBUF); - if (!(dst) || !(dst)->buf) PAX_ERROR("pax_buf_convert (dst)", PAX_ERR_NOBUF); - - pax_buf_t dummy; - bool use_dummy = dst == src; - if (use_dummy) { - dummy = *src; - dst = &dummy; - } - - // We can't go using realloc on an unknown buffer. - if (!dst->do_free) PAX_ERROR("pax_buf_convert", PAX_ERR_PARAM); - // Src and dst must match in size. - if (src->width != dst->width || src->height != dst->height) { - PAX_LOGE(TAG, "size mismatch: %dx%d vs %dx%d", src->width, src->height, dst->width, dst->height); - PAX_ERROR("pax_buf_convert", PAX_ERR_BOUNDS); - } - - // Update destination buffer type. - dst->bpp = PAX_GET_BPP(type); - dst->type = type; - // Update getters and setters. - pax_get_col_conv(dst, &dst->col2buf, &dst->buf2col); - pax_get_setters(dst, &dst->getter, &dst->setter); - // Compute new size requirement. - size_t new_pixels = dst->width * dst->height; - size_t new_size = (new_pixels * dst->bpp + 7) / 8; - - // Row buffer to prevent corruption in <8bpp. - pax_col_t *tmp = malloc(sizeof(pax_col_t) * dst->width); - if (!tmp) PAX_ERROR("pax_buf_convert", PAX_ERR_NOMEM); - - if (dst->bpp > src->bpp) { - PAX_LOGI(TAG, "Expanding buffer."); - - // Resize the memory for DST beforehand. - void *mem = realloc(dst->buf, new_size); - if (!mem) { free(tmp); PAX_ERROR("pax_buf_convert", PAX_ERR_NOMEM); } - dst->buf = mem; - if (use_dummy) src->buf = mem; - } - - // Convert pixel data. - for (int y = 0; y < dst->height; y++) { - int offs = y*dst->width; - - // Extract from SRC. - for (int x = 0; x < dst->width; x++) { - tmp[x] = pax_get_index_conv(src, offs+x); - } - // Store into DST. - for (int x = 0; x < dst->width; x++) { - pax_set_index_conv(dst, tmp[x], offs+x); - } - } - free(tmp); - - if (dst->bpp < src->bpp) { - PAX_LOGI(TAG, "Shrinking buffer."); - - // Resize the memory for DST afterwards. - void *mem = realloc(dst->buf, new_size); - if (!dst->buf) PAX_ERROR("pax_buf_convert", PAX_ERR_NOMEM); - dst->buf = mem; - } - - if (use_dummy) { - *src = dummy; - } + if (!(src) || !(src)->buf) + PAX_ERROR("pax_buf_convert (src)", PAX_ERR_NOBUF); + if (!(dst) || !(dst)->buf) + PAX_ERROR("pax_buf_convert (dst)", PAX_ERR_NOBUF); + + pax_buf_t dummy; + bool use_dummy = dst == src; + if (use_dummy) { + dummy = *src; + dst = &dummy; + } + + // We can't go using realloc on an unknown buffer. + if (!dst->do_free) + PAX_ERROR("pax_buf_convert", PAX_ERR_PARAM); + // Src and dst must match in size. + if (src->width != dst->width || src->height != dst->height) { + PAX_LOGE(TAG, "size mismatch: %dx%d vs %dx%d", src->width, src->height, dst->width, dst->height); + PAX_ERROR("pax_buf_convert", PAX_ERR_BOUNDS); + } + + // Update destination buffer type. + dst->bpp = PAX_GET_BPP(type); + dst->type = type; + // Update getters and setters. + pax_get_col_conv(dst, &dst->col2buf, &dst->buf2col); + pax_get_setters(dst, &dst->getter, &dst->setter); + // Compute new size requirement. + size_t new_pixels = dst->width * dst->height; + size_t new_size = (new_pixels * dst->bpp + 7) / 8; + + // Row buffer to prevent corruption in <8bpp. + pax_col_t *tmp = malloc(sizeof(pax_col_t) * dst->width); + if (!tmp) + PAX_ERROR("pax_buf_convert", PAX_ERR_NOMEM); + + if (dst->bpp > src->bpp) { + PAX_LOGI(TAG, "Expanding buffer."); + + // Resize the memory for DST beforehand. + void *mem = realloc(dst->buf, new_size); + if (!mem) { + free(tmp); + PAX_ERROR("pax_buf_convert", PAX_ERR_NOMEM); + } + dst->buf = mem; + if (use_dummy) + src->buf = mem; + } + + // Convert pixel data. + for (int y = 0; y < dst->height; y++) { + int offs = y * dst->width; + + // Extract from SRC. + for (int x = 0; x < dst->width; x++) { + tmp[x] = pax_get_index_conv(src, offs + x); + } + // Store into DST. + for (int x = 0; x < dst->width; x++) { + pax_set_index_conv(dst, tmp[x], offs + x); + } + } + free(tmp); + + if (dst->bpp < src->bpp) { + PAX_LOGI(TAG, "Shrinking buffer."); + + // Resize the memory for DST afterwards. + void *mem = realloc(dst->buf, new_size); + if (!dst->buf) + PAX_ERROR("pax_buf_convert", PAX_ERR_NOMEM); + dst->buf = mem; + } + + if (use_dummy) { + *src = dummy; + } } // Retrieve the width of the buffer. -int pax_buf_get_width(const pax_buf_t *buf) { return buf->width; } +int pax_buf_get_width(pax_buf_t const *buf) { + return buf->width; +} // Retrieve the height of the buffer. -int pax_buf_get_height(const pax_buf_t *buf) { return buf->height; } +int pax_buf_get_height(pax_buf_t const *buf) { + return buf->height; +} // Retrieve the width of the buffer. -float pax_buf_get_widthf(const pax_buf_t *buf) { return (float) buf->width;} +float pax_buf_get_widthf(pax_buf_t const *buf) { + return (float)buf->width; +} // Retrieve the height of the buffer. -float pax_buf_get_heightf(const pax_buf_t *buf) { return (float) buf->height; } +float pax_buf_get_heightf(pax_buf_t const *buf) { + return (float)buf->height; +} // Retrieve the type of the buffer. -pax_buf_type_t pax_buf_get_type(const pax_buf_t *buf) { return buf->type; } +pax_buf_type_t pax_buf_get_type(pax_buf_t const *buf) { + return buf->type; +} // Get a const pointer to the image data. -const void *pax_buf_get_pixels(const pax_buf_t *buf) { return buf->buf; } +void const *pax_buf_get_pixels(pax_buf_t const *buf) { + return buf->buf; +} // Get a non-const pointer to the image data. -void *pax_buf_get_pixels_rw(pax_buf_t *buf) { return buf->buf; } +void *pax_buf_get_pixels_rw(pax_buf_t *buf) { + return buf->buf; +} // Get the byte size of the image data. -size_t pax_buf_get_size(const pax_buf_t *buf) { return PAX_BUF_CALC_SIZE(buf->width, buf->height, buf->type); } +size_t pax_buf_get_size(pax_buf_t const *buf) { + return PAX_BUF_CALC_SIZE(buf->width, buf->height, buf->type); +} // Set orientation of the buffer. void pax_buf_set_orientation(pax_buf_t *buf, pax_orientation_t x) { - buf->orientation = x & 7; + buf->orientation = x & 7; } // Get orientation of the buffer. -pax_orientation_t pax_buf_get_orientation(const pax_buf_t *buf) { - return buf->orientation; +pax_orientation_t pax_buf_get_orientation(pax_buf_t const *buf) { + return buf->orientation; } // Scroll the buffer, filling with a placeholder color. void pax_buf_scroll(pax_buf_t *buf, pax_col_t placeholder, int x, int y) { - #if PAX_COMPILE_MCR - pax_join(); - #endif - - #if PAX_COMPILE_ORIENTATION - { - int x0 = x, y0 = y; - // Fix direction of scrolling. - switch (buf->orientation & 3) { - default: - case 0: break; - case 1: y = -x0; x = y0; break; - case 2: x = -x0; y = -y0; break; - case 3: y = x0; x = -y0; break; - } - if (buf->orientation & 4) { - x = -x; - } - } - #endif - - // Edge case: Scrolls too far. - if (x >= buf->width || x <= -buf->width || y >= buf->height || y <= -buf->height) { - pax_background(buf, placeholder); - return; - } - - // Pixel index offset for the copy. - ssize_t off = x + y * buf->width; - // Number of pixels that must be copied. - size_t count = buf->width * buf->height - labs(off); - - // Bit index version of the offset. - ssize_t bit_off = PAX_GET_BPP(buf->type) * off; - // Number of bits to copy. - size_t bit_count = PAX_GET_BPP(buf->type) * count; - - if ((bit_off & 7) == 0) { - // If bit offset lines up to a byte, use memmove. - ssize_t byte_off = bit_off / 8; - size_t byte_count = bit_count / 8; - - if (byte_off > 0) { - memmove(buf->buf_8bpp + byte_off, buf->buf_8bpp, byte_count); - } else { - memmove(buf->buf_8bpp, buf->buf_8bpp - byte_off, byte_count); - } - - } else { - // If it does not, an expensive copy must be performed. - if (off > 0) { - for (ssize_t i = count - 1; i >= 0; i--) { - pax_col_t value = buf->getter(buf, off + i); - buf->setter(buf, value, i); - } - } else { - for (ssize_t i = 0; i < count; i++) { - pax_col_t value = buf->getter(buf, i); - buf->setter(buf, value, off + i); - } - } - } - - #if PAX_COMPILE_ORIENTATION - // Ignore orientation for a moment. - int rot = buf->orientation; - buf->orientation = 0; - #endif - - // Fill the edges. - if (x > 0) { - pax_simple_rect(buf, placeholder, 0, y, x, buf->height - y); - } else if (x < 0) { - pax_simple_rect(buf, placeholder, buf->width, y, x, buf->height - y); - } - if (y > 0) { - pax_simple_rect(buf, placeholder, 0, 0, buf->width, y); - } else if (y < 0) { - pax_simple_rect(buf, placeholder, 0, buf->height, buf->width, y); - } - - #if PAX_COMPILE_ORIENTATION - // Restore previous orientation. - buf->orientation = rot; - #endif +#if PAX_COMPILE_MCR + pax_join(); +#endif + +#if PAX_COMPILE_ORIENTATION + { + int x0 = x, y0 = y; + // Fix direction of scrolling. + switch (buf->orientation & 3) { + default: + case 0: break; + case 1: + y = -x0; + x = y0; + break; + case 2: + x = -x0; + y = -y0; + break; + case 3: + y = x0; + x = -y0; + break; + } + if (buf->orientation & 4) { + x = -x; + } + } +#endif + + // Edge case: Scrolls too far. + if (x >= buf->width || x <= -buf->width || y >= buf->height || y <= -buf->height) { + pax_background(buf, placeholder); + return; + } + + // Pixel index offset for the copy. + ssize_t off = x + y * buf->width; + // Number of pixels that must be copied. + size_t count = buf->width * buf->height - labs(off); + + // Bit index version of the offset. + ssize_t bit_off = PAX_GET_BPP(buf->type) * off; + // Number of bits to copy. + size_t bit_count = PAX_GET_BPP(buf->type) * count; + + if ((bit_off & 7) == 0) { + // If bit offset lines up to a byte, use memmove. + ssize_t byte_off = bit_off / 8; + size_t byte_count = bit_count / 8; + + if (byte_off > 0) { + memmove(buf->buf_8bpp + byte_off, buf->buf_8bpp, byte_count); + } else { + memmove(buf->buf_8bpp, buf->buf_8bpp - byte_off, byte_count); + } + + } else { + // If it does not, an expensive copy must be performed. + if (off > 0) { + for (ssize_t i = count - 1; i >= 0; i--) { + pax_col_t value = buf->getter(buf, off + i); + buf->setter(buf, value, i); + } + } else { + for (ssize_t i = 0; i < count; i++) { + pax_col_t value = buf->getter(buf, i); + buf->setter(buf, value, off + i); + } + } + } + +#if PAX_COMPILE_ORIENTATION + // Ignore orientation for a moment. + int rot = buf->orientation; + buf->orientation = 0; +#endif + + // Fill the edges. + if (x > 0) { + pax_simple_rect(buf, placeholder, 0, y, x, buf->height - y); + } else if (x < 0) { + pax_simple_rect(buf, placeholder, buf->width, y, x, buf->height - y); + } + if (y > 0) { + pax_simple_rect(buf, placeholder, 0, 0, buf->width, y); + } else if (y < 0) { + pax_simple_rect(buf, placeholder, 0, buf->height, buf->width, y); + } + +#if PAX_COMPILE_ORIENTATION + // Restore previous orientation. + buf->orientation = rot; +#endif } // Clip the buffer to the desired rectangle. void pax_clip(pax_buf_t *buf, int x, int y, int width, int height) { - // Make width and height positive. - if (width < 0) { - x += width; - width = -width; - } - if (height < 0) { - y += height; - height = -height; - } - // Clip the entire rectangle to be at most the buffer's size. - if (x < 0) { - width += x; - x = 0; - } - if (y < 0) { - height += y; - y = 0; - } - if (x + width > buf->width) { - width = buf->width - x; - } - if (y + height > buf->height) { - height = buf->height - y; - } - // Apply the clip. - buf->clip = (pax_recti) { - .x = x, - .y = y, - .w = width, - .h = height - }; + // Make width and height positive. + if (width < 0) { + x += width; + width = -width; + } + if (height < 0) { + y += height; + height = -height; + } + // Clip the entire rectangle to be at most the buffer's size. + if (x < 0) { + width += x; + x = 0; + } + if (y < 0) { + height += y; + y = 0; + } + if (x + width > buf->width) { + width = buf->width - x; + } + if (y + height > buf->height) { + height = buf->height - y; + } + // Apply the clip. + buf->clip = (pax_recti){.x = x, .y = y, .w = width, .h = height}; } // Get the current clip rectangle. -pax_recti pax_get_clip(const pax_buf_t *buf) { - return buf->clip; +pax_recti pax_get_clip(pax_buf_t const *buf) { + return buf->clip; } // Clip the buffer to it's full size. void pax_noclip(pax_buf_t *buf) { - buf->clip = (pax_recti) { - .x = 0, - .y = 0, - .w = buf->width, - .h = buf->height - }; + buf->clip = (pax_recti){.x = 0, .y = 0, .w = buf->width, .h = buf->height}; } // Check whether the buffer is dirty. -bool pax_is_dirty(const pax_buf_t *buf) { - PAX_BUF_CHECK1("pax_is_dirty", 0); - return buf->dirty_x0 < buf->dirty_x1; +bool pax_is_dirty(pax_buf_t const *buf) { + PAX_BUF_CHECK1("pax_is_dirty", 0); + return buf->dirty_x0 < buf->dirty_x1; } // Get a copy of the dirty rectangle. -pax_recti pax_get_dirty(const pax_buf_t *buf) { - return (pax_recti) { - buf->dirty_x0, - buf->dirty_y0, - buf->dirty_x1 - buf->dirty_x0 + 1, - buf->dirty_y1 - buf->dirty_y0 + 1, - }; +pax_recti pax_get_dirty(pax_buf_t const *buf) { + return (pax_recti){ + buf->dirty_x0, + buf->dirty_y0, + buf->dirty_x1 - buf->dirty_x0 + 1, + buf->dirty_y1 - buf->dirty_y0 + 1, + }; } // Mark the entire buffer as clean. void pax_mark_clean(pax_buf_t *buf) { - PAX_BUF_CHECK("pax_mark_clean"); - buf->dirty_x0 = buf->width; - buf->dirty_y0 = buf->height; - buf->dirty_x1 = -1; - buf->dirty_y1 = -1; - PAX_SUCCESS(); + PAX_BUF_CHECK("pax_mark_clean"); + buf->dirty_x0 = buf->width; + buf->dirty_y0 = buf->height; + buf->dirty_x1 = -1; + buf->dirty_y1 = -1; + PAX_SUCCESS(); } // Mark the entire buffer as dirty. void pax_mark_dirty0(pax_buf_t *buf) { - PAX_BUF_CHECK("pax_mark_dirty0"); - buf->dirty_x0 = 0; - buf->dirty_y0 = 0; - buf->dirty_x1 = buf->width - 1; - buf->dirty_y1 = buf->height - 1; - PAX_SUCCESS(); + PAX_BUF_CHECK("pax_mark_dirty0"); + buf->dirty_x0 = 0; + buf->dirty_y0 = 0; + buf->dirty_x1 = buf->width - 1; + buf->dirty_y1 = buf->height - 1; + PAX_SUCCESS(); } // Mark a single point as dirty. void pax_mark_dirty1(pax_buf_t *buf, int x, int y) { - PAX_BUF_CHECK("pax_mark_dirty1"); - - if (x < 0) x = 0; - if (y < 0) y = 0; - if (x >= buf->width) y = buf->width - 1; - if (y >= buf->height) y = buf->height - 1; - - if (x < buf->dirty_x0) buf->dirty_x0 = x; - if (x > buf->dirty_x1) buf->dirty_x1 = x; - if (y < buf->dirty_y0) buf->dirty_y0 = y; - if (y > buf->dirty_y1) buf->dirty_y1 = y; - - PAX_SUCCESS(); + PAX_BUF_CHECK("pax_mark_dirty1"); + + if (x < 0) + x = 0; + if (y < 0) + y = 0; + if (x >= buf->width) + y = buf->width - 1; + if (y >= buf->height) + y = buf->height - 1; + + if (x < buf->dirty_x0) + buf->dirty_x0 = x; + if (x > buf->dirty_x1) + buf->dirty_x1 = x; + if (y < buf->dirty_y0) + buf->dirty_y0 = y; + if (y > buf->dirty_y1) + buf->dirty_y1 = y; + + PAX_SUCCESS(); } // Mark a rectangle as dirty. void pax_mark_dirty2(pax_buf_t *buf, int x, int y, int width, int height) { - PAX_BUF_CHECK("pax_mark_dirty2"); - - if (x < buf->dirty_x0) buf->dirty_x0 = x; - if (x + width - 1 > buf->dirty_x1) buf->dirty_x1 = x + width - 1; - if (y < buf->dirty_y0) buf->dirty_y0 = y; - if (y + height - 1 > buf->dirty_y1) buf->dirty_y1 = y + height - 1; - - if (buf->dirty_x0 < 0) buf->dirty_x0 = 0; - if (buf->dirty_y0 < 0) buf->dirty_y0 = 0; - if (buf->dirty_x0 >= buf->width) buf->dirty_x0 = buf->width - 1; - if (buf->dirty_y0 >= buf->height) buf->dirty_y0 = buf->height - 1; - - PAX_SUCCESS(); + PAX_BUF_CHECK("pax_mark_dirty2"); + + if (x < buf->dirty_x0) + buf->dirty_x0 = x; + if (x + width - 1 > buf->dirty_x1) + buf->dirty_x1 = x + width - 1; + if (y < buf->dirty_y0) + buf->dirty_y0 = y; + if (y + height - 1 > buf->dirty_y1) + buf->dirty_y1 = y + height - 1; + + if (buf->dirty_x0 < 0) + buf->dirty_x0 = 0; + if (buf->dirty_y0 < 0) + buf->dirty_y0 = 0; + if (buf->dirty_x0 >= buf->width) + buf->dirty_x0 = buf->width - 1; + if (buf->dirty_y0 >= buf->height) + buf->dirty_y0 = buf->height - 1; + + PAX_SUCCESS(); } @@ -604,216 +616,230 @@ void pax_mark_dirty2(pax_buf_t *buf, int x, int y, int width, int height) { // 8-bit + 8-bit fractional (0x00ff=1) division. static inline uint16_t pax_frac_div16(uint16_t a, uint8_t b) { - return (a << 8) / (b + (b >> 7)); + return (a << 8) / (b + (b >> 7)); } // Internal method for AHSV to ARGB. // Ranges are 0xff, 0x5ff, 0xff, 0xff. pax_col_t PRIVATE_pax_col_hsv(uint8_t a, uint16_t h, uint8_t s, uint8_t v) { - uint8_t phase = h >> 8; - // Parts of HSV. - uint8_t up, down, other; - other = ~s; - if (h & 0x100) { - // Down goes away. - up = 0xff; - down = pax_lerp(s, 0xff, ~h & 0xff); - } else { - // Up comes in. - up = pax_lerp(s, 0xff, h & 0xff); - down = 0xff; - } - // Apply brightness. - up = pax_lerp(v, 0, up); - down = pax_lerp(v, 0, down); - other = pax_lerp(v, 0, other); - // Apply to RGB. - uint8_t r, g, b; - switch (phase >> 1) { - default /* case 0 */: - // From R to G. - r = down; g = up; b = other; - break; - case 1: - // From G to B. - r = other; g = down; b = up; - break; - case 2: - // From B to R. - r = up; g = other; b = down; - break; - } - // Merge. - return (a << 24) | (r << 16) | (g << 8) | b; + uint8_t phase = h >> 8; + // Parts of HSV. + uint8_t up, down, other; + other = ~s; + if (h & 0x100) { + // Down goes away. + up = 0xff; + down = pax_lerp(s, 0xff, ~h & 0xff); + } else { + // Up comes in. + up = pax_lerp(s, 0xff, h & 0xff); + down = 0xff; + } + // Apply brightness. + up = pax_lerp(v, 0, up); + down = pax_lerp(v, 0, down); + other = pax_lerp(v, 0, other); + // Apply to RGB. + uint8_t r, g, b; + switch (phase >> 1) { + default /* case 0 */: + // From R to G. + r = down; + g = up; + b = other; + break; + case 1: + // From G to B. + r = other; + g = down; + b = up; + break; + case 2: + // From B to R. + r = up; + g = other; + b = down; + break; + } + // Merge. + return (a << 24) | (r << 16) | (g << 8) | b; } // Internal method for RGB to HSV. // Ranges are 0x5ff, 0xff, 0xff. void PRIVATE_pax_undo_col_hsv(pax_col_t in, uint16_t *h, uint8_t *s, uint8_t *v) { - // Split the RGB. - uint8_t r = in >> 16; - uint8_t g = in >> 8; - uint8_t b = in; - - // Edge case: Equal brightness. - if (r == g && g == b) { - *h = 0; *s = 0; *v = r; - return; - } - - // Sort levels. - uint8_t high = r, middle = g, low = b; - if (high < middle) { uint8_t tmp = high; high = middle; middle = tmp; } - if (middle < low) { uint8_t tmp = middle; middle = low; low = tmp; } - if (high < middle) { uint8_t tmp = high; high = middle; middle = tmp; } - - // Factor out brightness. - *v = high; - middle = middle * 255 / high; - low = low * 255 / high; - r = r * 255 / high; - g = g * 255 / high; - b = b * 255 / high; - high = 255; - - // Factor out saturation. - *s = ~low; - - // How I inverted the function (where 1.0=0xff): - - // middle = lerp(s, 1, X) - // middle = 1 + s * (X - 1) - // middle = 1 + s * X - s * 1 - - // middle - 1 + s * 1 = s * X - // s * X = middle - 1 + s * 1 - - // X = (middle - 1 + s * 1) / s - // X = (middle - 1) / s + 1 - - // This is it, written in code. - // Here, `x` is either `~h` or `h` in a 9-bit context, - // From the interpolation of `up` and `down` in hsv. - uint16_t x = pax_frac_div16(middle - 0xff + *s, *s); - - // Reason about hue. - uint16_t l_h; - if (r == high) { - if (g == middle) { - // R = down, [G = up], h < 0x100 - l_h = 0x000 | x; - } else { - // [B = down], R = up, h > 0x100 - l_h = 0x500 | (255-x); - } - } else if (g == high) { - if (b == middle) { - // G = down, [B = up], h < 0x100 - l_h = 0x200 | x; - } else { - // [R = down], G = up, h > 0x100 - l_h = 0x100 | (255-x); - } - } else /* b == high */ { - if (r == middle) { - // B = down, [R = up], h < 0x100 - l_h = 0x400 | x; - } else { - // [G = down], B = up, h > 0x100 - l_h = 0x300 | (255-x); - } - } - - *h = l_h; + // Split the RGB. + uint8_t r = in >> 16; + uint8_t g = in >> 8; + uint8_t b = in; + + // Edge case: Equal brightness. + if (r == g && g == b) { + *h = 0; + *s = 0; + *v = r; + return; + } + + // Sort levels. + uint8_t high = r, middle = g, low = b; + if (high < middle) { + uint8_t tmp = high; + high = middle; + middle = tmp; + } + if (middle < low) { + uint8_t tmp = middle; + middle = low; + low = tmp; + } + if (high < middle) { + uint8_t tmp = high; + high = middle; + middle = tmp; + } + + // Factor out brightness. + *v = high; + middle = middle * 255 / high; + low = low * 255 / high; + r = r * 255 / high; + g = g * 255 / high; + b = b * 255 / high; + high = 255; + + // Factor out saturation. + *s = ~low; + + // How I inverted the function (where 1.0=0xff): + + // middle = lerp(s, 1, X) + // middle = 1 + s * (X - 1) + // middle = 1 + s * X - s * 1 + + // middle - 1 + s * 1 = s * X + // s * X = middle - 1 + s * 1 + + // X = (middle - 1 + s * 1) / s + // X = (middle - 1) / s + 1 + + // This is it, written in code. + // Here, `x` is either `~h` or `h` in a 9-bit context, + // From the interpolation of `up` and `down` in hsv. + uint16_t x = pax_frac_div16(middle - 0xff + *s, *s); + + // Reason about hue. + uint16_t l_h; + if (r == high) { + if (g == middle) { + // R = down, [G = up], h < 0x100 + l_h = 0x000 | x; + } else { + // [B = down], R = up, h > 0x100 + l_h = 0x500 | (255 - x); + } + } else if (g == high) { + if (b == middle) { + // G = down, [B = up], h < 0x100 + l_h = 0x200 | x; + } else { + // [R = down], G = up, h > 0x100 + l_h = 0x100 | (255 - x); + } + } else /* b == high */ { + if (r == middle) { + // B = down, [R = up], h < 0x100 + l_h = 0x400 | x; + } else { + // [G = down], B = up, h > 0x100 + l_h = 0x300 | (255 - x); + } + } + + *h = l_h; } // Converts HSV to ARGB, ranges are 0-255. pax_col_t pax_col_hsv(uint8_t h, uint8_t s, uint8_t v) { - return PRIVATE_pax_col_hsv(255, h * 6, s, v); + return PRIVATE_pax_col_hsv(255, h * 6, s, v); } // Converts AHSV to ARGB, ranges are 0-255. pax_col_t pax_col_ahsv(uint8_t a, uint8_t h, uint8_t s, uint8_t v) { - return PRIVATE_pax_col_hsv(a, h * 6, s, v); + return PRIVATE_pax_col_hsv(a, h * 6, s, v); } // Converts HSV to ARGB, ranges are 0-359. pax_col_t pax_col_hsv_alt(uint16_t h, uint8_t s, uint8_t v) { - return PRIVATE_pax_col_hsv(255, h%360*6*255/359, s, v); + return PRIVATE_pax_col_hsv(255, h % 360 * 6 * 255 / 359, s, v); } // Converts AHSV to ARGB. pax_col_t pax_col_ahsv_alt(uint8_t a, uint16_t h, uint8_t s, uint8_t v) { - return PRIVATE_pax_col_hsv(a, h%360*6*255/359, s, v); + return PRIVATE_pax_col_hsv(a, h % 360 * 6 * 255 / 359, s, v); } // Converts ARGB into AHSV, ranges are 0-255. void pax_undo_ahsv(pax_col_t in, uint8_t *a, uint8_t *h, uint8_t *s, uint8_t *v) { - *a = in >>24; - uint16_t l_h; - PRIVATE_pax_undo_col_hsv(in, &l_h, s, v); - *h = (l_h + 3) / 6; + *a = in >> 24; + uint16_t l_h; + PRIVATE_pax_undo_col_hsv(in, &l_h, s, v); + *h = (l_h + 3) / 6; } // Converts RGB into HSV, ranges are 0-255. void pax_undo_hsv(pax_col_t in, uint8_t *h, uint8_t *s, uint8_t *v) { - uint16_t l_h; - PRIVATE_pax_undo_col_hsv(in, &l_h, s, v); - *h = (l_h + 3) / 6; + uint16_t l_h; + PRIVATE_pax_undo_col_hsv(in, &l_h, s, v); + *h = (l_h + 3) / 6; } // Converts ARGB into AHSV, ranges are 0-255, 0-359, 0-99, 0-99. void pax_undo_ahsv_alt(pax_col_t in, uint8_t *a, uint16_t *h, uint8_t *s, uint8_t *v) { - *a = in >>24; - uint16_t l_h; - PRIVATE_pax_undo_col_hsv(in, &l_h, s, v); - *h = (l_h + 3) * 359 / 255 / 6; - *s = *s * 100 / 255; - *v = *v * 100 / 255; + *a = in >> 24; + uint16_t l_h; + PRIVATE_pax_undo_col_hsv(in, &l_h, s, v); + *h = (l_h + 3) * 359 / 255 / 6; + *s = *s * 100 / 255; + *v = *v * 100 / 255; } // Converts RGB into HSV, ranges are 0-359, 0-99, 0-99. void pax_undo_hsv_alt(pax_col_t in, uint16_t *h, uint8_t *s, uint8_t *v) { - uint16_t l_h; - PRIVATE_pax_undo_col_hsv(in, &l_h, s, v); - *h = (l_h + 3) * 359 / 255 / 6; - *s = *s * 100 / 255; - *v = *v * 100 / 255; + uint16_t l_h; + PRIVATE_pax_undo_col_hsv(in, &l_h, s, v); + *h = (l_h + 3) * 359 / 255 / 6; + *s = *s * 100 / 255; + *v = *v * 100 / 255; } // Linearly interpolates between from and to, including alpha. pax_col_t pax_col_lerp(uint8_t part, pax_col_t from, pax_col_t to) { - return (pax_lerp(part, from >> 24, to >> 24) << 24) - | (pax_lerp(part, from >> 16, to >> 16) << 16) - | (pax_lerp(part, from >> 8, to >> 8) << 8) - | pax_lerp(part, from, to); + return (pax_lerp(part, from >> 24, to >> 24) << 24) | (pax_lerp(part, from >> 16, to >> 16) << 16) | + (pax_lerp(part, from >> 8, to >> 8) << 8) | pax_lerp(part, from, to); } // Merges the two colors, based on alpha. pax_col_t pax_col_merge(pax_col_t base, pax_col_t top) { - // It is not more optimal to add exceptions for full or zero alpha due to linearity. - - // Otherwise, do a full alpha blend. - uint8_t part = top >> 24; - return (pax_lerp(part, base >> 24, 255) << 24) - | (pax_lerp(part, base >> 16, top >> 16) << 16) - | (pax_lerp(part, base >> 8, top >> 8) << 8) - | pax_lerp(part, base, top); + // It is not more optimal to add exceptions for full or zero alpha due to linearity. + + // Otherwise, do a full alpha blend. + uint8_t part = top >> 24; + return (pax_lerp(part, base >> 24, 255) << 24) | (pax_lerp(part, base >> 16, top >> 16) << 16) | + (pax_lerp(part, base >> 8, top >> 8) << 8) | pax_lerp(part, base, top); } // Tints the color, commonly used for textures. pax_col_t pax_col_tint(pax_col_t col, pax_col_t tint) { - // It is not more optimal to add exceptions for full or zero alpha due to linearity. - - // Otherwise, do a full tint. - return (pax_lerp(tint >> 24, 0, col >> 24) << 24) - | (pax_lerp(tint >> 16, 0, col >> 16) << 16) - | (pax_lerp(tint >> 8, 0, col >> 8) << 8) - | pax_lerp(tint, 0, col); + // It is not more optimal to add exceptions for full or zero alpha due to linearity. + + // Otherwise, do a full tint. + return (pax_lerp(tint >> 24, 0, col >> 24) << 24) | (pax_lerp(tint >> 16, 0, col >> 16) << 16) | + (pax_lerp(tint >> 8, 0, col >> 8) << 8) | pax_lerp(tint, 0, col); } @@ -822,118 +848,124 @@ pax_col_t pax_col_tint(pax_col_t col, pax_col_t tint) { // Set a pixel, merging with alpha. void pax_merge_pixel(pax_buf_t *buf, pax_col_t color, int x, int y) { - PAX_BUF_CHECK("pax_merge_pixel"); - - #if PAX_COMPILE_ORIENTATION - pax_vec2i tmp = pax_orient_det_vec2i(buf, (pax_vec2i) {x, y}); - x = tmp.x; y = tmp.y; - #endif - - if (x < 0 || x >= buf->width || y < 0 || y >= buf->height) { - // Out of bounds error. - pax_last_error = PAX_ERR_BOUNDS; - return; - } - - PAX_SUCCESS(); - - int index = x + y * buf->width; - if (PAX_IS_PALETTE(buf->type)) { - // Palette colors don't have conversion. - if (color & 0xff000000) buf->setter(buf, color, index); - } else if (color >= 0xff000000) { - // Opaque colors don't need alpha blending. - buf->setter(buf, buf->col2buf(buf, color), index); - } else if (color & 0xff000000) { - // Non-transparent colors will be blended normally. - pax_col_t base = buf->buf2col(buf, buf->getter(buf, index)); - buf->setter(buf, buf->col2buf(buf, pax_col_merge(base, color)), index); - } + PAX_BUF_CHECK("pax_merge_pixel"); + +#if PAX_COMPILE_ORIENTATION + pax_vec2i tmp = pax_orient_det_vec2i(buf, (pax_vec2i){x, y}); + x = tmp.x; + y = tmp.y; +#endif + + if (x < 0 || x >= buf->width || y < 0 || y >= buf->height) { + // Out of bounds error. + pax_last_error = PAX_ERR_BOUNDS; + return; + } + + PAX_SUCCESS(); + + int index = x + y * buf->width; + if (PAX_IS_PALETTE(buf->type)) { + // Palette colors don't have conversion. + if (color & 0xff000000) + buf->setter(buf, color, index); + } else if (color >= 0xff000000) { + // Opaque colors don't need alpha blending. + buf->setter(buf, buf->col2buf(buf, color), index); + } else if (color & 0xff000000) { + // Non-transparent colors will be blended normally. + pax_col_t base = buf->buf2col(buf, buf->getter(buf, index)); + buf->setter(buf, buf->col2buf(buf, pax_col_merge(base, color)), index); + } } // Set a pixel. void pax_set_pixel(pax_buf_t *buf, pax_col_t color, int x, int y) { - PAX_BUF_CHECK("pax_set_pixel"); - - #if PAX_COMPILE_ORIENTATION - pax_vec2i tmp = pax_orient_det_vec2i(buf, (pax_vec2i) {x, y}); - x = tmp.x; y = tmp.y; - #endif - - if (x < 0 || x >= buf->width || y < 0 || y >= buf->height) { - // Out of bounds error. - pax_last_error = PAX_ERR_BOUNDS; - return; - } - - PAX_SUCCESS(); - - int index = x + y * buf->width; - if (PAX_IS_PALETTE(buf->type)) { - // Palette colors don't have conversion. - buf->setter(buf, color, index); - } else { - // But all other colors do have a conversion. - buf->setter(buf, buf->col2buf(buf, color), index); - } + PAX_BUF_CHECK("pax_set_pixel"); + +#if PAX_COMPILE_ORIENTATION + pax_vec2i tmp = pax_orient_det_vec2i(buf, (pax_vec2i){x, y}); + x = tmp.x; + y = tmp.y; +#endif + + if (x < 0 || x >= buf->width || y < 0 || y >= buf->height) { + // Out of bounds error. + pax_last_error = PAX_ERR_BOUNDS; + return; + } + + PAX_SUCCESS(); + + int index = x + y * buf->width; + if (PAX_IS_PALETTE(buf->type)) { + // Palette colors don't have conversion. + buf->setter(buf, color, index); + } else { + // But all other colors do have a conversion. + buf->setter(buf, buf->col2buf(buf, color), index); + } } // Get a pixel. -pax_col_t pax_get_pixel(const pax_buf_t *buf, int x, int y) { - PAX_BUF_CHECK1("pax_get_pixel", 0); - - #if PAX_COMPILE_ORIENTATION - pax_vec2i tmp = pax_orient_det_vec2i(buf, (pax_vec2i) {x, y}); - x = tmp.x; y = tmp.y; - #endif - - if (x < 0 || x >= buf->width || y < 0 || y >= buf->height) { - // Out of bounds error. - pax_last_error = PAX_ERR_BOUNDS; - return 0; - } - PAX_SUCCESS(); - return buf->buf2col(buf, buf->getter(buf, x + y * buf->width)); +pax_col_t pax_get_pixel(pax_buf_t const *buf, int x, int y) { + PAX_BUF_CHECK1("pax_get_pixel", 0); + +#if PAX_COMPILE_ORIENTATION + pax_vec2i tmp = pax_orient_det_vec2i(buf, (pax_vec2i){x, y}); + x = tmp.x; + y = tmp.y; +#endif + + if (x < 0 || x >= buf->width || y < 0 || y >= buf->height) { + // Out of bounds error. + pax_last_error = PAX_ERR_BOUNDS; + return 0; + } + PAX_SUCCESS(); + return buf->buf2col(buf, buf->getter(buf, x + y * buf->width)); } // Set a pixel without color conversion. void pax_set_pixel_raw(pax_buf_t *buf, pax_col_t color, int x, int y) { - PAX_BUF_CHECK("pax_set_pixel"); - - #if PAX_COMPILE_ORIENTATION - pax_vec2i tmp = pax_orient_det_vec2i(buf, (pax_vec2i) {x, y}); - x = tmp.x; y = tmp.y; - #endif - - if (x < 0 || x >= buf->width || y < 0 || y >= buf->height) { - // Out of bounds error. - pax_last_error = PAX_ERR_BOUNDS; - return; - } - - PAX_SUCCESS(); - - int index = x + y * buf->width; - // Don't do any color conversion. - buf->setter(buf, color, index); + PAX_BUF_CHECK("pax_set_pixel"); + +#if PAX_COMPILE_ORIENTATION + pax_vec2i tmp = pax_orient_det_vec2i(buf, (pax_vec2i){x, y}); + x = tmp.x; + y = tmp.y; +#endif + + if (x < 0 || x >= buf->width || y < 0 || y >= buf->height) { + // Out of bounds error. + pax_last_error = PAX_ERR_BOUNDS; + return; + } + + PAX_SUCCESS(); + + int index = x + y * buf->width; + // Don't do any color conversion. + buf->setter(buf, color, index); } // Get a pixel without color conversion. -pax_col_t pax_get_pixel_raw(const pax_buf_t *buf, int x, int y) { - PAX_BUF_CHECK1("pax_get_pixel", 0); - - #if PAX_COMPILE_ORIENTATION - pax_vec2i tmp = pax_orient_det_vec2i(buf, (pax_vec2i) {x, y}); - x = tmp.x; y = tmp.y; - #endif - - if (x < 0 || x >= buf->width || y < 0 || y >= buf->height) { - // Out of bounds error. - pax_last_error = PAX_ERR_BOUNDS; - return 0; - } - PAX_SUCCESS(); - return buf->getter(buf, x + y * buf->width); +pax_col_t pax_get_pixel_raw(pax_buf_t const *buf, int x, int y) { + PAX_BUF_CHECK1("pax_get_pixel", 0); + +#if PAX_COMPILE_ORIENTATION + pax_vec2i tmp = pax_orient_det_vec2i(buf, (pax_vec2i){x, y}); + x = tmp.x; + y = tmp.y; +#endif + + if (x < 0 || x >= buf->width || y < 0 || y >= buf->height) { + // Out of bounds error. + pax_last_error = PAX_ERR_BOUNDS; + return 0; + } + PAX_SUCCESS(); + return buf->getter(buf, x + y * buf->width); } @@ -941,518 +973,648 @@ pax_col_t pax_get_pixel_raw(const pax_buf_t *buf, int x, int y) { /* ========= DRAWING: 2D ========= */ // Draws an image at the image's normal size. -void pax_draw_image(pax_buf_t *buf, const pax_buf_t *image, float x, float y) { - if (!image || !image->buf) PAX_ERROR("pax_draw_image", PAX_ERR_CORRUPT); - pax_draw_image_sized(buf, image, x, y, image->width, image->height); +void pax_draw_image(pax_buf_t *buf, pax_buf_t const *image, float x, float y) { + if (!image || !image->buf) + PAX_ERROR("pax_draw_image", PAX_ERR_CORRUPT); + pax_draw_image_sized(buf, image, x, y, image->width, image->height); } // Draw an image with a prespecified size. -void pax_draw_image_sized(pax_buf_t *buf, const pax_buf_t *image, float x, float y, float width, float height) { - if (!image || !image->buf) PAX_ERROR("pax_draw_image", PAX_ERR_CORRUPT); - if (PAX_IS_ALPHA(image->type)) { - pax_shade_rect(buf, -1, &PAX_SHADER_TEXTURE(image), NULL, x, y, width, height); - } else { - pax_shade_rect(buf, -1, &PAX_SHADER_TEXTURE_OP(image), NULL, x, y, width, height); - } +void pax_draw_image_sized(pax_buf_t *buf, pax_buf_t const *image, float x, float y, float width, float height) { + if (!image || !image->buf) + PAX_ERROR("pax_draw_image", PAX_ERR_CORRUPT); + if (PAX_IS_ALPHA(image->type)) { + pax_shade_rect(buf, -1, &PAX_SHADER_TEXTURE(image), NULL, x, y, width, height); + } else { + pax_shade_rect(buf, -1, &PAX_SHADER_TEXTURE_OP(image), NULL, x, y, width, height); + } } // Draws an image at the image's normal size. // Assumes the image is completely opaque, any transparent parts are drawn opaque. -void pax_draw_image_op(pax_buf_t *buf, const pax_buf_t *image, float x, float y) { - if (!image || !image->buf) PAX_ERROR("pax_draw_image_op", PAX_ERR_CORRUPT); - pax_draw_image_sized_op(buf, image, x, y, image->width, image->height); +void pax_draw_image_op(pax_buf_t *buf, pax_buf_t const *image, float x, float y) { + if (!image || !image->buf) + PAX_ERROR("pax_draw_image_op", PAX_ERR_CORRUPT); + pax_draw_image_sized_op(buf, image, x, y, image->width, image->height); } // Draw an image with a prespecified size. // Assumes the image is completely opaque, any transparent parts are drawn opaque. -void pax_draw_image_sized_op(pax_buf_t *buf, const pax_buf_t *image, float x, float y, float width, float height) { - if (!image || !image->buf) PAX_ERROR("pax_draw_image", PAX_ERR_CORRUPT); - pax_shade_rect(buf, -1, &PAX_SHADER_TEXTURE_OP(image), NULL, x, y, width, height); +void pax_draw_image_sized_op(pax_buf_t *buf, pax_buf_t const *image, float x, float y, float width, float height) { + if (!image || !image->buf) + PAX_ERROR("pax_draw_image", PAX_ERR_CORRUPT); + pax_shade_rect(buf, -1, &PAX_SHADER_TEXTURE_OP(image), NULL, x, y, width, height); } // Draw a rectangle with a shader. // If uvs is NULL, a default will be used (0,0; 1,0; 1,1; 0,1). -void pax_shade_rect(pax_buf_t *buf, pax_col_t color, const pax_shader_t *shader, - const pax_quadf *uvs, float x, float y, float width, float height) { - if (!shader) { - // If shader is NULL, simplify this. - pax_draw_rect(buf, color, x, y, width, height); - return; - } - - if (!uvs) { - // Apply default UVs. - uvs = &dummy_quad_uvs; - } - - // Split UVs into two triangles. - pax_trif uv0 = { - .x0 = uvs->x0, .y0 = uvs->y0, - .x1 = uvs->x1, .y1 = uvs->y1, - .x2 = uvs->x2, .y2 = uvs->y2 - }; - pax_trif uv1 = { - .x0 = uvs->x0, .y0 = uvs->y0, - .x1 = uvs->x3, .y1 = uvs->y3, - .x2 = uvs->x2, .y2 = uvs->y2 - }; - - if (matrix_2d_is_identity2(buf->stack_2d.value)) { - // We don't need to use triangles here. - matrix_2d_transform(buf->stack_2d.value, &x, &y); - width *= buf->stack_2d.value.a0; - height *= buf->stack_2d.value.b1; - - // Perform rotation. - #if PAX_COMPILE_ORIENTATION - pax_rectf tmp = pax_orient_det_rectf(buf, (pax_rectf) {x, y, width, height}); - x=tmp.x; - y=tmp.y; - width=tmp.w; - height=tmp.h; - - pax_quadf uvs_rotated; - if (buf->orientation & 1) { - uvs_rotated = (pax_quadf) { - uvs->x0, uvs->y0, - uvs->x3, uvs->y3, - uvs->x2, uvs->y2, - uvs->x1, uvs->y1, - }; - uvs = &uvs_rotated; - } - #endif - - pax_mark_dirty2(buf, x - 0.5, y - 0.5, width + 1, height + 1); - #if PAX_COMPILE_MCR - if (pax_do_multicore) { - // Assign worker task. - pax_task_t task = { - .buffer = buf, - .type = PAX_TASK_RECT, - .color = color, - .use_shader = shader, - .quad_uvs = *uvs, - .shape = { x, y, width, height }, - .shape_len = 4 - }; - if (shader) task.shader = *shader; - paxmcr_add_task(&task); - // Draw our part. - paxmcr_rect_shaded( - false, - buf, color, shader, x, y, width, height, - uvs->x0, uvs->y0, uvs->x1, uvs->y1, - uvs->x2, uvs->y2, uvs->x3, uvs->y3 - ); - } else - #endif - { - pax_rect_shaded( - buf, color, shader, x, y, width, height, - uvs->x0, uvs->y0, uvs->x1, uvs->y1, - uvs->x2, uvs->y2, uvs->x3, uvs->y3 - ); - } - } else { - // We still need triangles. - pax_shade_tri(buf, color, shader, &uv0, x, y, x + width, y, x + width, y + height); - pax_shade_tri(buf, color, shader, &uv1, x, y, x, y + height, x + width, y + height); - } +void pax_shade_rect( + pax_buf_t *buf, + pax_col_t color, + pax_shader_t const *shader, + pax_quadf const *uvs, + float x, + float y, + float width, + float height +) { + if (!shader) { + // If shader is NULL, simplify this. + pax_draw_rect(buf, color, x, y, width, height); + return; + } + + if (!uvs) { + // Apply default UVs. + uvs = &dummy_quad_uvs; + } + + // Split UVs into two triangles. + pax_trif uv0 = {.x0 = uvs->x0, .y0 = uvs->y0, .x1 = uvs->x1, .y1 = uvs->y1, .x2 = uvs->x2, .y2 = uvs->y2}; + pax_trif uv1 = {.x0 = uvs->x0, .y0 = uvs->y0, .x1 = uvs->x3, .y1 = uvs->y3, .x2 = uvs->x2, .y2 = uvs->y2}; + + if (matrix_2d_is_identity2(buf->stack_2d.value)) { + // We don't need to use triangles here. + matrix_2d_transform(buf->stack_2d.value, &x, &y); + width *= buf->stack_2d.value.a0; + height *= buf->stack_2d.value.b1; + +// Perform rotation. +#if PAX_COMPILE_ORIENTATION + pax_rectf tmp = pax_orient_det_rectf(buf, (pax_rectf){x, y, width, height}); + x = tmp.x; + y = tmp.y; + width = tmp.w; + height = tmp.h; + + pax_quadf uvs_rotated; + if (buf->orientation & 1) { + uvs_rotated = (pax_quadf){ + uvs->x0, + uvs->y0, + uvs->x3, + uvs->y3, + uvs->x2, + uvs->y2, + uvs->x1, + uvs->y1, + }; + uvs = &uvs_rotated; + } +#endif + + pax_mark_dirty2(buf, x - 0.5, y - 0.5, width + 1, height + 1); +#if PAX_COMPILE_MCR + if (pax_do_multicore) { + // Assign worker task. + pax_task_t task = { + .buffer = buf, + .type = PAX_TASK_RECT, + .color = color, + .use_shader = shader, + .quad_uvs = *uvs, + .shape = {x, y, width, height}, + .shape_len = 4 + }; + if (shader) + task.shader = *shader; + paxmcr_add_task(&task); + // Draw our part. + paxmcr_rect_shaded( + false, + buf, + color, + shader, + x, + y, + width, + height, + uvs->x0, + uvs->y0, + uvs->x1, + uvs->y1, + uvs->x2, + uvs->y2, + uvs->x3, + uvs->y3 + ); + } else +#endif + { + pax_rect_shaded( + buf, + color, + shader, + x, + y, + width, + height, + uvs->x0, + uvs->y0, + uvs->x1, + uvs->y1, + uvs->x2, + uvs->y2, + uvs->x3, + uvs->y3 + ); + } + } else { + // We still need triangles. + pax_shade_tri(buf, color, shader, &uv0, x, y, x + width, y, x + width, y + height); + pax_shade_tri(buf, color, shader, &uv1, x, y, x, y + height, x + width, y + height); + } } // Draw a line with a shader. // Beta feature: UVs are not currently available. -void pax_shade_line(pax_buf_t *buf, pax_col_t color, const pax_shader_t *shader, - const pax_linef *uvs, float x0, float y0, float x1, float y1) { - PAX_BUF_CHECK("pax_shade_line"); - - if (!shader) { - pax_draw_line(buf, color, x0, y0, x1, y1); - return; - } - - float u0, v0, u1, v1; - - if (uvs) { - u0 = uvs->x0; - v0 = uvs->y0; - u1 = uvs->x1; - v1 = uvs->y1; - } else { - u0 = 0; - v0 = 0; - u1 = 1; - v1 = 0; - } - - // Apply transforms. - matrix_2d_transform(buf->stack_2d.value, &x0, &y0); - matrix_2d_transform(buf->stack_2d.value, &x1, &y1); - - if (!isfinite(x0) || !isfinite(y0) || !isfinite(x1) || !isfinite(y1)) { - // We can't draw to infinity. - pax_last_error = PAX_ERR_INF; - return; - } - - // Rotate points. - pax_vec1_t tmp = pax_orient_det_vec2f(buf, (pax_vec2f) {x0, y0}); - x0 = tmp.x; y0 = tmp.y; - tmp = pax_orient_det_vec2f(buf, (pax_vec2f) {x1, y1}); - x1 = tmp.x; y1 = tmp.y; - - // If any point is outside clip now, we don't draw a line. - if (y0 < buf->clip.y || y1 > buf->clip.y + buf->clip.h - 1) goto noneed; - - pax_mark_dirty1(buf, x0, y0); - pax_mark_dirty1(buf, x1, y1); - #if PAX_COMPILE_MCR - // Because a line isn't drawn in alternating scanlines, we need to sync up with the worker. - pax_join(); - #endif - pax_line_shaded(buf, color, shader, u0, v0, u1, v1, x0, y0, x1, y1); - - // This label is used if there's no need to try to draw a line. - noneed: - PAX_SUCCESS(); +void pax_shade_line( + pax_buf_t *buf, + pax_col_t color, + pax_shader_t const *shader, + pax_linef const *uvs, + float x0, + float y0, + float x1, + float y1 +) { + PAX_BUF_CHECK("pax_shade_line"); + + if (!shader) { + pax_draw_line(buf, color, x0, y0, x1, y1); + return; + } + + float u0, v0, u1, v1; + + if (uvs) { + u0 = uvs->x0; + v0 = uvs->y0; + u1 = uvs->x1; + v1 = uvs->y1; + } else { + u0 = 0; + v0 = 0; + u1 = 1; + v1 = 0; + } + + // Apply transforms. + matrix_2d_transform(buf->stack_2d.value, &x0, &y0); + matrix_2d_transform(buf->stack_2d.value, &x1, &y1); + + if (!isfinite(x0) || !isfinite(y0) || !isfinite(x1) || !isfinite(y1)) { + // We can't draw to infinity. + pax_last_error = PAX_ERR_INF; + return; + } + + // Rotate points. + pax_vec1_t tmp = pax_orient_det_vec2f(buf, (pax_vec2f){x0, y0}); + x0 = tmp.x; + y0 = tmp.y; + tmp = pax_orient_det_vec2f(buf, (pax_vec2f){x1, y1}); + x1 = tmp.x; + y1 = tmp.y; + + // If any point is outside clip now, we don't draw a line. + if (y0 < buf->clip.y || y1 > buf->clip.y + buf->clip.h - 1) + goto noneed; + + pax_mark_dirty1(buf, x0, y0); + pax_mark_dirty1(buf, x1, y1); +#if PAX_COMPILE_MCR + // Because a line isn't drawn in alternating scanlines, we need to sync up with the worker. + pax_join(); +#endif + pax_line_shaded(buf, color, shader, u0, v0, u1, v1, x0, y0, x1, y1); + +// This label is used if there's no need to try to draw a line. +noneed: + PAX_SUCCESS(); } // Draw a triangle with a shader. // If uvs is NULL, a default will be used (0,0; 1,0; 0,1). -void pax_shade_tri(pax_buf_t *buf, pax_col_t color, const pax_shader_t *shader, - const pax_trif *uvs, float x0, float y0, float x1, float y1, float x2, float y2) { - if (!shader) { - // If shader is NULL, simplify this. - pax_draw_tri(buf, color, x0, y0, x1, y1, x2, y2); - return; - } - - PAX_BUF_CHECK("pax_shade_tri"); - matrix_2d_transform(buf->stack_2d.value, &x0, &y0); - matrix_2d_transform(buf->stack_2d.value, &x1, &y1); - matrix_2d_transform(buf->stack_2d.value, &x2, &y2); - - if (!isfinite(x0) || !isfinite(y0) || !isfinite(x1) || !isfinite(y1) || !isfinite(x2) || !isfinite(y2)) { - // We can't draw to infinity. - pax_last_error = PAX_ERR_INF; - return; - } - - // Rotate points. - pax_vec1_t tmp = pax_orient_det_vec2f(buf, (pax_vec2f) {x0, y0}); - x0 = tmp.x; y0 = tmp.y; - tmp = pax_orient_det_vec2f(buf, (pax_vec2f) {x1, y1}); - x1 = tmp.x; y1 = tmp.y; - tmp = pax_orient_det_vec2f(buf, (pax_vec2f) {x2, y2}); - x2 = tmp.x; y2 = tmp.y; - - if (!uvs) { - // Apply default UVs. - uvs = &dummy_tri_uvs; - } - - if ((y2 == y0 && y1 == y0) || (x2 == x0 && x1 == x0)) { - // We can't draw a flat triangle. - PAX_SUCCESS(); - return; - } - - // Mark each corner of the triangle as dirty. - pax_mark_dirty1(buf, x0 - 0.5, y0 - 0.5); - pax_mark_dirty1(buf, x1 - 0.5, y1 - 0.5); - pax_mark_dirty1(buf, x2 - 0.5, y2 - 0.5); - pax_mark_dirty1(buf, x0 + 0.5, y0 + 0.5); - pax_mark_dirty1(buf, x1 + 0.5, y1 + 0.5); - pax_mark_dirty1(buf, x2 + 0.5, y2 + 0.5); - - #if PAX_COMPILE_MCR - if (pax_do_multicore) { - // Assign worker task. - pax_task_t task = { - .buffer = buf, - .type = PAX_TASK_TRI, - .color = color, - .use_shader = shader, - .tri_uvs = *uvs, - .shape = { x0, y0, x1, y1, x2, y2 }, - .shape_len = 6 - }; - if (shader) task.shader = *shader; - paxmcr_add_task(&task); - // Draw our part. - paxmcr_tri_shaded( - false, buf, color, shader, - x0, y0, x1, y1, x2, y2, - uvs->x0, uvs->y0, uvs->x1, uvs->y1, uvs->x2, uvs->y2 - ); - } else - #endif - { - pax_tri_shaded( - buf, color, shader, - x0, y0, x1, y1, x2, y2, - uvs->x0, uvs->y0, uvs->x1, uvs->y1, uvs->x2, uvs->y2 - ); - } - - PAX_SUCCESS(); +void pax_shade_tri( + pax_buf_t *buf, + pax_col_t color, + pax_shader_t const *shader, + pax_trif const *uvs, + float x0, + float y0, + float x1, + float y1, + float x2, + float y2 +) { + if (!shader) { + // If shader is NULL, simplify this. + pax_draw_tri(buf, color, x0, y0, x1, y1, x2, y2); + return; + } + + PAX_BUF_CHECK("pax_shade_tri"); + matrix_2d_transform(buf->stack_2d.value, &x0, &y0); + matrix_2d_transform(buf->stack_2d.value, &x1, &y1); + matrix_2d_transform(buf->stack_2d.value, &x2, &y2); + + if (!isfinite(x0) || !isfinite(y0) || !isfinite(x1) || !isfinite(y1) || !isfinite(x2) || !isfinite(y2)) { + // We can't draw to infinity. + pax_last_error = PAX_ERR_INF; + return; + } + + // Rotate points. + pax_vec1_t tmp = pax_orient_det_vec2f(buf, (pax_vec2f){x0, y0}); + x0 = tmp.x; + y0 = tmp.y; + tmp = pax_orient_det_vec2f(buf, (pax_vec2f){x1, y1}); + x1 = tmp.x; + y1 = tmp.y; + tmp = pax_orient_det_vec2f(buf, (pax_vec2f){x2, y2}); + x2 = tmp.x; + y2 = tmp.y; + + if (!uvs) { + // Apply default UVs. + uvs = &dummy_tri_uvs; + } + + if ((y2 == y0 && y1 == y0) || (x2 == x0 && x1 == x0)) { + // We can't draw a flat triangle. + PAX_SUCCESS(); + return; + } + + // Mark each corner of the triangle as dirty. + pax_mark_dirty1(buf, x0 - 0.5, y0 - 0.5); + pax_mark_dirty1(buf, x1 - 0.5, y1 - 0.5); + pax_mark_dirty1(buf, x2 - 0.5, y2 - 0.5); + pax_mark_dirty1(buf, x0 + 0.5, y0 + 0.5); + pax_mark_dirty1(buf, x1 + 0.5, y1 + 0.5); + pax_mark_dirty1(buf, x2 + 0.5, y2 + 0.5); + +#if PAX_COMPILE_MCR + if (pax_do_multicore) { + // Assign worker task. + pax_task_t task = { + .buffer = buf, + .type = PAX_TASK_TRI, + .color = color, + .use_shader = shader, + .tri_uvs = *uvs, + .shape = {x0, y0, x1, y1, x2, y2}, + .shape_len = 6 + }; + if (shader) + task.shader = *shader; + paxmcr_add_task(&task); + // Draw our part. + paxmcr_tri_shaded( + false, + buf, + color, + shader, + x0, + y0, + x1, + y1, + x2, + y2, + uvs->x0, + uvs->y0, + uvs->x1, + uvs->y1, + uvs->x2, + uvs->y2 + ); + } else +#endif + { + pax_tri_shaded( + buf, + color, + shader, + x0, + y0, + x1, + y1, + x2, + y2, + uvs->x0, + uvs->y0, + uvs->x1, + uvs->y1, + uvs->x2, + uvs->y2 + ); + } + + PAX_SUCCESS(); } // Draw an arc with a shader, angles in radians. // If uvs is NULL, a default will be used (0,0; 1,0; 1,1; 0,1). -void pax_shade_arc(pax_buf_t *buf, pax_col_t color, const pax_shader_t *shader, - const pax_quadf *uvs, float x, float y, float r, float a0, float a1) { - if (!shader) { - // If shader is NULL, simplify this. - pax_draw_arc(buf, color, x, y, r, a0, a1); - return; - } - - PAX_BUF_CHECK("pax_draw_arc"); - if (!uvs) { - // Assign default UVs. - uvs = &dummy_quad_uvs; - } - - // Simplify the angles slightly. - float a2 = fmodf(a0, M_PI * 2); - a1 += a2 - a0; - a0 = a2; - if (a1 < a0) PAX_SWAP(float, a0, a1); - if (a1 - a0 > M_PI * 2) { - a1 = M_PI * 2; - a0 = 0; - } - - // Pick an appropriate number of divisions. - int n_div; - matrix_2d_t matrix = buf->stack_2d.value; - float c_r = r * sqrtf(matrix.a0*matrix.a0 + matrix.b0*matrix.b0) * sqrtf(matrix.a1*matrix.a1 + matrix.b1*matrix.b1); - if (c_r > 30) n_div = (a1 - a0) / M_PI * 32 + 1; - else n_div = (a1 - a0) / M_PI * 16 + 1; - - // Get the sine and cosine of one division, used for rotation in the loop. - float div_angle = (a1 - a0) / n_div; - float c_sin = sinf(div_angle); - float c_cos = cosf(div_angle); - - // Start with a unit vector according to a0. - float x0 = cosf(a0); - float y0 = sinf(a0); - - // Prepare some UVs to apply to the triangle. - pax_trif tri_uvs; - tri_uvs.x0 = (uvs->x0 + uvs->x1 + uvs->x2 + uvs->x3) * 0.25; - tri_uvs.y0 = (uvs->y0 + uvs->y1 + uvs->y2 + uvs->y3) * 0.25; - - tri_uvs.x1 = pax_flerp4(x0, y0, uvs->x0, uvs->x1, uvs->x3, uvs->x2); - tri_uvs.y1 = pax_flerp4(x0, y0, uvs->y0, uvs->y1, uvs->y3, uvs->y2); - - // Draw it as a series of triangles, rotating with what is essentially matrix multiplication. - for (int i = 0; i < n_div; i++) { - // Perform the rotation. - float x1 = x0 * c_cos - y0 * c_sin; - float y1 = x0 * c_sin + y0 * c_cos; - // And UV interpolation. - tri_uvs.x2 = pax_flerp4(x1, y1, uvs->x0, uvs->x1, uvs->x3, uvs->x2); - tri_uvs.y2 = pax_flerp4(x1, y1, uvs->y0, uvs->y1, uvs->y3, uvs->y2); - // We subtract y0 and y1 from y because our up is -y. - pax_shade_tri(buf, color, shader, &tri_uvs, x, y, x + x0 * r, y - y0 * r, x + x1 * r, y - y1 * r); - // Assign the newly rotated vectors. - x0 = x1; - y0 = y1; - tri_uvs.x1 = tri_uvs.x2; - tri_uvs.y1 = tri_uvs.y2; - } - - PAX_SUCCESS(); +void pax_shade_arc( + pax_buf_t *buf, + pax_col_t color, + pax_shader_t const *shader, + pax_quadf const *uvs, + float x, + float y, + float r, + float a0, + float a1 +) { + if (!shader) { + // If shader is NULL, simplify this. + pax_draw_arc(buf, color, x, y, r, a0, a1); + return; + } + + PAX_BUF_CHECK("pax_draw_arc"); + if (!uvs) { + // Assign default UVs. + uvs = &dummy_quad_uvs; + } + + // Simplify the angles slightly. + float a2 = fmodf(a0, M_PI * 2); + a1 += a2 - a0; + a0 = a2; + if (a1 < a0) + PAX_SWAP(float, a0, a1); + if (a1 - a0 > M_PI * 2) { + a1 = M_PI * 2; + a0 = 0; + } + + // Pick an appropriate number of divisions. + int n_div; + matrix_2d_t matrix = buf->stack_2d.value; + float c_r = + r * sqrtf(matrix.a0 * matrix.a0 + matrix.b0 * matrix.b0) * sqrtf(matrix.a1 * matrix.a1 + matrix.b1 * matrix.b1); + if (c_r > 30) + n_div = (a1 - a0) / M_PI * 32 + 1; + else + n_div = (a1 - a0) / M_PI * 16 + 1; + + // Get the sine and cosine of one division, used for rotation in the loop. + float div_angle = (a1 - a0) / n_div; + float c_sin = sinf(div_angle); + float c_cos = cosf(div_angle); + + // Start with a unit vector according to a0. + float x0 = cosf(a0); + float y0 = sinf(a0); + + // Prepare some UVs to apply to the triangle. + pax_trif tri_uvs; + tri_uvs.x0 = (uvs->x0 + uvs->x1 + uvs->x2 + uvs->x3) * 0.25; + tri_uvs.y0 = (uvs->y0 + uvs->y1 + uvs->y2 + uvs->y3) * 0.25; + + tri_uvs.x1 = pax_flerp4(x0, y0, uvs->x0, uvs->x1, uvs->x3, uvs->x2); + tri_uvs.y1 = pax_flerp4(x0, y0, uvs->y0, uvs->y1, uvs->y3, uvs->y2); + + // Draw it as a series of triangles, rotating with what is essentially matrix multiplication. + for (int i = 0; i < n_div; i++) { + // Perform the rotation. + float x1 = x0 * c_cos - y0 * c_sin; + float y1 = x0 * c_sin + y0 * c_cos; + // And UV interpolation. + tri_uvs.x2 = pax_flerp4(x1, y1, uvs->x0, uvs->x1, uvs->x3, uvs->x2); + tri_uvs.y2 = pax_flerp4(x1, y1, uvs->y0, uvs->y1, uvs->y3, uvs->y2); + // We subtract y0 and y1 from y because our up is -y. + pax_shade_tri(buf, color, shader, &tri_uvs, x, y, x + x0 * r, y - y0 * r, x + x1 * r, y - y1 * r); + // Assign the newly rotated vectors. + x0 = x1; + y0 = y1; + tri_uvs.x1 = tri_uvs.x2; + tri_uvs.y1 = tri_uvs.y2; + } + + PAX_SUCCESS(); } // Draw a circle with a shader. // If uvs is NULL, a default will be used (0,0; 1,0; 1,1; 0,1). -void pax_shade_circle(pax_buf_t *buf, pax_col_t color, const pax_shader_t *shader, - const pax_quadf *uvs, float x, float y, float r) { - // Use precalcualted circles for speed because the user can't tell anyway. - const pax_vec2f *preset; - const pax_trif *uv_set; - size_t size; - - // Pick a suitable number of subdivisions. - matrix_2d_t matrix = buf->stack_2d.value; - float c_r = r * sqrtf(matrix.a0*matrix.a0 + matrix.b0*matrix.b0) * sqrtf(matrix.a1*matrix.a1 + matrix.b1*matrix.b1); - - if (c_r > 30) { - preset = pax_precalc_circle_24; - uv_set = pax_precalc_uv_circle_24; - size = 24; - } else if (c_r > 7) { - preset = pax_precalc_circle_16; - uv_set = pax_precalc_uv_circle_16; - size = 16; - } else { - preset = pax_precalc_circle_4; - uv_set = pax_precalc_uv_circle_4; - size = 4; - } - - // Use the builtin matrix stuff to our advantage. - pax_push_2d(buf); - pax_apply_2d(buf, matrix_2d_translate(x, y)); - pax_apply_2d(buf, matrix_2d_scale(r, r)); - if (uvs) { - // UV interpolation required. - pax_trif uv_res; - uv_res.x0 = (uvs->x1 + uvs->x2) * 0.5; - uv_res.y0 = (uvs->y1 + uvs->y2) * 0.5; - uv_res.x1 = pax_flerp4(preset[1].x, -preset[1].y, uvs->x0, uvs->x1, uvs->x3, uvs->x2); - uv_res.y1 = pax_flerp4(preset[1].x, -preset[1].y, uvs->y0, uvs->y1, uvs->y3, uvs->y2); - for (size_t i = 0; i < size - 1; i++) { - uv_res.x2 = pax_flerp4(preset[i+1].x, -preset[i+1].y, uvs->x0, uvs->x1, uvs->x3, uvs->x2); - uv_res.y2 = pax_flerp4(preset[i+1].x, -preset[i+1].y, uvs->y0, uvs->y1, uvs->y3, uvs->y2); - pax_shade_tri(buf, color, shader, &uv_res, preset[0].x, preset[0].y, preset[i].x, preset[i].y, preset[i+1].x, preset[i+1].y); - uv_res.x1 = uv_res.x2; - uv_res.y1 = uv_res.y2; - } - - } else { - // No UV interpolation needed. - for (size_t i = 0; i < size - 1; i++) { - pax_shade_tri(buf, color, shader, &uv_set[i], preset[0].x, preset[0].y, preset[i].x, preset[i].y, preset[i+1].x, preset[i+1].y); - } - } - pax_pop_2d(buf); +void pax_shade_circle( + pax_buf_t *buf, pax_col_t color, pax_shader_t const *shader, pax_quadf const *uvs, float x, float y, float r +) { + // Use precalcualted circles for speed because the user can't tell anyway. + pax_vec2f const *preset; + pax_trif const *uv_set; + size_t size; + + // Pick a suitable number of subdivisions. + matrix_2d_t matrix = buf->stack_2d.value; + float c_r = + r * sqrtf(matrix.a0 * matrix.a0 + matrix.b0 * matrix.b0) * sqrtf(matrix.a1 * matrix.a1 + matrix.b1 * matrix.b1); + + if (c_r > 30) { + preset = pax_precalc_circle_24; + uv_set = pax_precalc_uv_circle_24; + size = 24; + } else if (c_r > 7) { + preset = pax_precalc_circle_16; + uv_set = pax_precalc_uv_circle_16; + size = 16; + } else { + preset = pax_precalc_circle_4; + uv_set = pax_precalc_uv_circle_4; + size = 4; + } + + // Use the builtin matrix stuff to our advantage. + pax_push_2d(buf); + pax_apply_2d(buf, matrix_2d_translate(x, y)); + pax_apply_2d(buf, matrix_2d_scale(r, r)); + if (uvs) { + // UV interpolation required. + pax_trif uv_res; + uv_res.x0 = (uvs->x1 + uvs->x2) * 0.5; + uv_res.y0 = (uvs->y1 + uvs->y2) * 0.5; + uv_res.x1 = pax_flerp4(preset[1].x, -preset[1].y, uvs->x0, uvs->x1, uvs->x3, uvs->x2); + uv_res.y1 = pax_flerp4(preset[1].x, -preset[1].y, uvs->y0, uvs->y1, uvs->y3, uvs->y2); + for (size_t i = 0; i < size - 1; i++) { + uv_res.x2 = pax_flerp4(preset[i + 1].x, -preset[i + 1].y, uvs->x0, uvs->x1, uvs->x3, uvs->x2); + uv_res.y2 = pax_flerp4(preset[i + 1].x, -preset[i + 1].y, uvs->y0, uvs->y1, uvs->y3, uvs->y2); + pax_shade_tri( + buf, + color, + shader, + &uv_res, + preset[0].x, + preset[0].y, + preset[i].x, + preset[i].y, + preset[i + 1].x, + preset[i + 1].y + ); + uv_res.x1 = uv_res.x2; + uv_res.y1 = uv_res.y2; + } + + } else { + // No UV interpolation needed. + for (size_t i = 0; i < size - 1; i++) { + pax_shade_tri( + buf, + color, + shader, + &uv_set[i], + preset[0].x, + preset[0].y, + preset[i].x, + preset[i].y, + preset[i + 1].x, + preset[i + 1].y + ); + } + } + pax_pop_2d(buf); } // Draw a rectangle. void pax_draw_rect(pax_buf_t *buf, pax_col_t color, float x, float y, float width, float height) { - PAX_BUF_CHECK("pax_draw_rect"); - if (!pax_do_draw_col(buf, color)) return; - if (matrix_2d_is_identity2(buf->stack_2d.value)) { - // This can be simplified significantly. - matrix_2d_transform(buf->stack_2d.value, &x, &y); - width *= buf->stack_2d.value.a0; - height *= buf->stack_2d.value.b1; - pax_simple_rect(buf, color, x, y, width, height); - } else { - // We need to go full quad. - float x0 = x, y0 = y; - float x1 = x + width, y1 = y; - float x2 = x + width, y2 = y + height; - float x3 = x, y3 = y + height; - // Transform all points. - matrix_2d_transform(buf->stack_2d.value, &x0, &y0); - matrix_2d_transform(buf->stack_2d.value, &x1, &y1); - matrix_2d_transform(buf->stack_2d.value, &x2, &y2); - matrix_2d_transform(buf->stack_2d.value, &x3, &y3); - // Draw the triangle components. - pax_simple_tri(buf, color, x0, y0, x1, y1, x2, y2); - pax_simple_tri(buf, color, x0, y0, x3, y3, x2, y2); - } + PAX_BUF_CHECK("pax_draw_rect"); + if (!pax_do_draw_col(buf, color)) + return; + if (matrix_2d_is_identity2(buf->stack_2d.value)) { + // This can be simplified significantly. + matrix_2d_transform(buf->stack_2d.value, &x, &y); + width *= buf->stack_2d.value.a0; + height *= buf->stack_2d.value.b1; + pax_simple_rect(buf, color, x, y, width, height); + } else { + // We need to go full quad. + float x0 = x, y0 = y; + float x1 = x + width, y1 = y; + float x2 = x + width, y2 = y + height; + float x3 = x, y3 = y + height; + // Transform all points. + matrix_2d_transform(buf->stack_2d.value, &x0, &y0); + matrix_2d_transform(buf->stack_2d.value, &x1, &y1); + matrix_2d_transform(buf->stack_2d.value, &x2, &y2); + matrix_2d_transform(buf->stack_2d.value, &x3, &y3); + // Draw the triangle components. + pax_simple_tri(buf, color, x0, y0, x1, y1, x2, y2); + pax_simple_tri(buf, color, x0, y0, x3, y3, x2, y2); + } } // Draw a line. void pax_draw_line(pax_buf_t *buf, pax_col_t color, float x0, float y0, float x1, float y1) { - PAX_BUF_CHECK("pax_draw_line"); - if (!pax_do_draw_col(buf, color)) return; - // Apply transforms. - matrix_2d_transform(buf->stack_2d.value, &x0, &y0); - matrix_2d_transform(buf->stack_2d.value, &x1, &y1); - // Draw the line. - pax_simple_line(buf, color, x0, y0, x1, y1); + PAX_BUF_CHECK("pax_draw_line"); + if (!pax_do_draw_col(buf, color)) + return; + // Apply transforms. + matrix_2d_transform(buf->stack_2d.value, &x0, &y0); + matrix_2d_transform(buf->stack_2d.value, &x1, &y1); + // Draw the line. + pax_simple_line(buf, color, x0, y0, x1, y1); } // Draw a triangle. void pax_draw_tri(pax_buf_t *buf, pax_col_t color, float x0, float y0, float x1, float y1, float x2, float y2) { - PAX_BUF_CHECK("pax_draw_tri"); - if (!pax_do_draw_col(buf, color)) return; - // Apply the transforms. - matrix_2d_transform(buf->stack_2d.value, &x0, &y0); - matrix_2d_transform(buf->stack_2d.value, &x1, &y1); - matrix_2d_transform(buf->stack_2d.value, &x2, &y2); - // Draw the triangle. - pax_simple_tri(buf, color, x0, y0, x1, y1, x2, y2); + PAX_BUF_CHECK("pax_draw_tri"); + if (!pax_do_draw_col(buf, color)) + return; + // Apply the transforms. + matrix_2d_transform(buf->stack_2d.value, &x0, &y0); + matrix_2d_transform(buf->stack_2d.value, &x1, &y1); + matrix_2d_transform(buf->stack_2d.value, &x2, &y2); + // Draw the triangle. + pax_simple_tri(buf, color, x0, y0, x1, y1, x2, y2); } // Draw na arc, angles in radians. -void pax_draw_arc(pax_buf_t *buf, pax_col_t color, float x, float y, float r, float a0, float a1) { - PAX_BUF_CHECK("pax_draw_arc"); - if (!pax_do_draw_col(buf, color)) return; - - // Simplify the angles slightly. - float a2 = fmodf(a0, M_PI * 2); - a1 += a2 - a0; - a0 = a2; - if (a1 < a0) PAX_SWAP(float, a0, a1); - if (a1 - a0 > M_PI * 2) { - a1 = M_PI * 2; - a0 = 0; - } - - // Pick an appropriate number of divisions. - int n_div; - matrix_2d_t matrix = buf->stack_2d.value; - float c_r = r * sqrtf(matrix.a0*matrix.a0 + matrix.b0*matrix.b0) * sqrtf(matrix.a1*matrix.a1 + matrix.b1*matrix.b1); - if (c_r > 30) n_div = (a1 - a0) / M_PI * 32 + 1; - else n_div = (a1 - a0) / M_PI * 16 + 1; - - // Get the sine and cosine of one division, used for rotation in the loop. - float div_angle = (a1 - a0) / n_div; - float c_sin = sinf(div_angle); - float c_cos = cosf(div_angle); - - // Start with a unit vector according to a0. - float x0 = cosf(a0); - float y0 = sinf(a0); - - // Draw it as a series of triangles, rotating with what is essentially matrix multiplication. - for (int i = 0; i < n_div; i++) { - // Perform the rotation. - float x1 = x0 * c_cos - y0 * c_sin; - float y1 = x0 * c_sin + y0 * c_cos; - // We subtract y0 and y1 from y because our up is -y. - pax_draw_tri(buf, color, x, y, x + x0 * r, y - y0 * r, x + x1 * r, y - y1 * r); - // Assign the newly rotated vectors. - x0 = x1; - y0 = y1; - } - - PAX_SUCCESS(); +void pax_draw_arc(pax_buf_t *buf, pax_col_t color, float x, float y, float r, float a0, float a1) { + PAX_BUF_CHECK("pax_draw_arc"); + if (!pax_do_draw_col(buf, color)) + return; + + // Simplify the angles slightly. + float a2 = fmodf(a0, M_PI * 2); + a1 += a2 - a0; + a0 = a2; + if (a1 < a0) + PAX_SWAP(float, a0, a1); + if (a1 - a0 > M_PI * 2) { + a1 = M_PI * 2; + a0 = 0; + } + + // Pick an appropriate number of divisions. + int n_div; + matrix_2d_t matrix = buf->stack_2d.value; + float c_r = + r * sqrtf(matrix.a0 * matrix.a0 + matrix.b0 * matrix.b0) * sqrtf(matrix.a1 * matrix.a1 + matrix.b1 * matrix.b1); + if (c_r > 30) + n_div = (a1 - a0) / M_PI * 32 + 1; + else + n_div = (a1 - a0) / M_PI * 16 + 1; + + // Get the sine and cosine of one division, used for rotation in the loop. + float div_angle = (a1 - a0) / n_div; + float c_sin = sinf(div_angle); + float c_cos = cosf(div_angle); + + // Start with a unit vector according to a0. + float x0 = cosf(a0); + float y0 = sinf(a0); + + // Draw it as a series of triangles, rotating with what is essentially matrix multiplication. + for (int i = 0; i < n_div; i++) { + // Perform the rotation. + float x1 = x0 * c_cos - y0 * c_sin; + float y1 = x0 * c_sin + y0 * c_cos; + // We subtract y0 and y1 from y because our up is -y. + pax_draw_tri(buf, color, x, y, x + x0 * r, y - y0 * r, x + x1 * r, y - y1 * r); + // Assign the newly rotated vectors. + x0 = x1; + y0 = y1; + } + + PAX_SUCCESS(); } // Draw a circle. -void pax_draw_circle(pax_buf_t *buf, pax_col_t color, float x, float y, float r) { - PAX_BUF_CHECK("pax_draw_circle"); - if (!pax_do_draw_col(buf, color)) return; - - // Use precalcualted circles for speed because the user can't tell anyway. - const pax_vec2f *preset; - size_t size; - - // Pick a suitable number of subdivisions. - matrix_2d_t matrix = buf->stack_2d.value; - float c_r = r * sqrtf(matrix.a0*matrix.a0 + matrix.b0*matrix.b0) * sqrtf(matrix.a1*matrix.a1 + matrix.b1*matrix.b1); - - if (c_r > 30) { - preset = pax_precalc_circle_24; - size = 24; - } else if (c_r > 7) { - preset = pax_precalc_circle_16; - size = 16; - } else { - preset = pax_precalc_circle_4; - size = 4; - } - - // Use the builtin matrix stuff to our advantage. - pax_push_2d(buf); - pax_apply_2d(buf, matrix_2d_translate(x, y)); - pax_apply_2d(buf, matrix_2d_scale(r, r)); - // Plot all the triangles in the ROM. - for (size_t i = 0; i < size - 1; i++) { - pax_draw_tri(buf, color, preset[0].x, preset[0].y, preset[i].x, preset[i].y, preset[i+1].x, preset[i+1].y); - } - pax_pop_2d(buf); +void pax_draw_circle(pax_buf_t *buf, pax_col_t color, float x, float y, float r) { + PAX_BUF_CHECK("pax_draw_circle"); + if (!pax_do_draw_col(buf, color)) + return; + + // Use precalcualted circles for speed because the user can't tell anyway. + pax_vec2f const *preset; + size_t size; + + // Pick a suitable number of subdivisions. + matrix_2d_t matrix = buf->stack_2d.value; + float c_r = + r * sqrtf(matrix.a0 * matrix.a0 + matrix.b0 * matrix.b0) * sqrtf(matrix.a1 * matrix.a1 + matrix.b1 * matrix.b1); + + if (c_r > 30) { + preset = pax_precalc_circle_24; + size = 24; + } else if (c_r > 7) { + preset = pax_precalc_circle_16; + size = 16; + } else { + preset = pax_precalc_circle_4; + size = 4; + } + + // Use the builtin matrix stuff to our advantage. + pax_push_2d(buf); + pax_apply_2d(buf, matrix_2d_translate(x, y)); + pax_apply_2d(buf, matrix_2d_scale(r, r)); + // Plot all the triangles in the ROM. + for (size_t i = 0; i < size - 1; i++) { + pax_draw_tri(buf, color, preset[0].x, preset[0].y, preset[i].x, preset[i].y, preset[i + 1].x, preset[i + 1].y); + } + pax_pop_2d(buf); } @@ -1461,227 +1623,245 @@ void pax_draw_circle(pax_buf_t *buf, pax_col_t color, float x, float y, float // Fill the background. PAX_PERF_CRITICAL_ATTR void pax_background(pax_buf_t *buf, pax_col_t color) { - PAX_BUF_CHECK("pax_background"); - - #if PAX_COMPILE_MCR - pax_join(); - #endif - - uint32_t value; - if (PAX_IS_PALETTE(buf->type)) { - if (color > buf->pallette_size) value = 0; - else value = color; - } else { - value = buf->col2buf(buf, color); - } - - if (value == 0) { - memset(buf->buf, 0, PAX_BUF_CALC_SIZE(buf->width, buf->height, buf->type)); - } else if (buf->bpp == 16) { - if (buf->reverse_endianness) { - value = pax_rev_endian_16(value); - } - // Fill 16bpp parts. - for (size_t i = 0; i < buf->width * buf->height; i++) { - buf->buf_16bpp[i] = value; - } - } else if (buf->bpp == 32) { - if (buf->reverse_endianness) { - value = pax_rev_endian_32(value); - } - // Fill 32bpp parts. - for (size_t i = 0; i < buf->width * buf->height; i++) { - buf->buf_32bpp[i] = value; - } - } else { - // Fill <=8bpp parts. - if (buf->bpp == 1) value = -value; - else if (buf->bpp == 2) value = value * 0x55; - else if (buf->bpp == 4) value = value * 0x11; - size_t limit = (7 + buf->width * buf->height * buf->bpp) / 8; - for (size_t i = 0; i < limit; i++) { - buf->buf_8bpp[i] = value; - } - } - - pax_mark_dirty0(buf); - PAX_SUCCESS(); + PAX_BUF_CHECK("pax_background"); + +#if PAX_COMPILE_MCR + pax_join(); +#endif + + uint32_t value; + if (PAX_IS_PALETTE(buf->type)) { + if (color > buf->pallette_size) + value = 0; + else + value = color; + } else { + value = buf->col2buf(buf, color); + } + + if (value == 0) { + memset(buf->buf, 0, PAX_BUF_CALC_SIZE(buf->width, buf->height, buf->type)); + } else if (buf->bpp == 16) { + if (buf->reverse_endianness) { + value = pax_rev_endian_16(value); + } + // Fill 16bpp parts. + for (size_t i = 0; i < buf->width * buf->height; i++) { + buf->buf_16bpp[i] = value; + } + } else if (buf->bpp == 32) { + if (buf->reverse_endianness) { + value = pax_rev_endian_32(value); + } + // Fill 32bpp parts. + for (size_t i = 0; i < buf->width * buf->height; i++) { + buf->buf_32bpp[i] = value; + } + } else { + // Fill <=8bpp parts. + if (buf->bpp == 1) + value = -value; + else if (buf->bpp == 2) + value = value * 0x55; + else if (buf->bpp == 4) + value = value * 0x11; + size_t limit = (7 + buf->width * buf->height * buf->bpp) / 8; + for (size_t i = 0; i < limit; i++) { + buf->buf_8bpp[i] = value; + } + } + + pax_mark_dirty0(buf); + PAX_SUCCESS(); } // Draw a rectangle, ignoring matrix transform. void pax_simple_rect(pax_buf_t *buf, pax_col_t color, float x, float y, float width, float height) { - PAX_BUF_CHECK("pax_simple_rect"); - if (!pax_do_draw_col(buf, color)) return; - - #if PAX_COMPILE_ORIENTATION - // Do rotation. - pax_rectf tmp = pax_orient_det_rectf(buf, (pax_rectf) {x,y,width,height}); - x=tmp.x; - y=tmp.y; - width=tmp.w; - height=tmp.h; - #endif - - // Mark dirty area. - pax_mark_dirty2(buf, x - 0.5, y - 0.5, width + 1, height + 1); - #if PAX_COMPILE_MCR - if (pax_do_multicore) { - // Assign worker task. - pax_task_t task = { - .buffer = buf, - .type = PAX_TASK_RECT, - .color = color, - .use_shader = false, - .shape = { x, y, width, height }, - .shape_len = 4 - }; - paxmcr_add_task(&task); - // Draw our part. - paxmcr_rect_unshaded(false, buf, color, x, y, width, height); - } else - #endif - { - pax_rect_unshaded(buf, color, x, y, width, height); - } - - PAX_SUCCESS(); + PAX_BUF_CHECK("pax_simple_rect"); + if (!pax_do_draw_col(buf, color)) + return; + +#if PAX_COMPILE_ORIENTATION + // Do rotation. + pax_rectf tmp = pax_orient_det_rectf(buf, (pax_rectf){x, y, width, height}); + x = tmp.x; + y = tmp.y; + width = tmp.w; + height = tmp.h; +#endif + + // Mark dirty area. + pax_mark_dirty2(buf, x - 0.5, y - 0.5, width + 1, height + 1); +#if PAX_COMPILE_MCR + if (pax_do_multicore) { + // Assign worker task. + pax_task_t task = { + .buffer = buf, + .type = PAX_TASK_RECT, + .color = color, + .use_shader = false, + .shape = {x, y, width, height}, + .shape_len = 4 + }; + paxmcr_add_task(&task); + // Draw our part. + paxmcr_rect_unshaded(false, buf, color, x, y, width, height); + } else +#endif + { + pax_rect_unshaded(buf, color, x, y, width, height); + } + + PAX_SUCCESS(); } // Draw a line, ignoring matrix transform. void pax_simple_line(pax_buf_t *buf, pax_col_t color, float x0, float y0, float x1, float y1) { - PAX_BUF_CHECK("pax_simple_line"); - if (!pax_do_draw_col(buf, color)) return; - - if (!isfinite(x0) || !isfinite(y0) || !isfinite(x1) || !isfinite(y1)) { - // We can't draw to infinity. - pax_last_error = PAX_ERR_INF; - return; - } - - #if PAX_COMPILE_ORIENTATION - // Rotate points. - pax_vec1_t tmp = pax_orient_det_vec2f(buf, (pax_vec2f) {x0, y0}); - x0 = tmp.x; y0 = tmp.y; - tmp = pax_orient_det_vec2f(buf, (pax_vec2f) {x1, y1}); - x1 = tmp.x; y1 = tmp.y; - #endif - - pax_mark_dirty1(buf, x0, y0); - pax_mark_dirty1(buf, x1, y1); - #if PAX_COMPILE_MCR - // Because a line isn't drawn in alternating scanlines, we need to sync up with the worker. - pax_join(); - #endif - pax_line_unshaded(buf, color, x0, y0, x1, y1); - - PAX_SUCCESS(); + PAX_BUF_CHECK("pax_simple_line"); + if (!pax_do_draw_col(buf, color)) + return; + + if (!isfinite(x0) || !isfinite(y0) || !isfinite(x1) || !isfinite(y1)) { + // We can't draw to infinity. + pax_last_error = PAX_ERR_INF; + return; + } + +#if PAX_COMPILE_ORIENTATION + // Rotate points. + pax_vec1_t tmp = pax_orient_det_vec2f(buf, (pax_vec2f){x0, y0}); + x0 = tmp.x; + y0 = tmp.y; + tmp = pax_orient_det_vec2f(buf, (pax_vec2f){x1, y1}); + x1 = tmp.x; + y1 = tmp.y; +#endif + + pax_mark_dirty1(buf, x0, y0); + pax_mark_dirty1(buf, x1, y1); +#if PAX_COMPILE_MCR + // Because a line isn't drawn in alternating scanlines, we need to sync up with the worker. + pax_join(); +#endif + pax_line_unshaded(buf, color, x0, y0, x1, y1); + + PAX_SUCCESS(); } // Draw a triangle, ignoring matrix transform. void pax_simple_tri(pax_buf_t *buf, pax_col_t color, float x0, float y0, float x1, float y1, float x2, float y2) { - PAX_BUF_CHECK("pax_simple_tri"); - if (!pax_do_draw_col(buf, color)) return; - - if (!isfinite(x0) || !isfinite(y0) || !isfinite(x1) || !isfinite(y1) || !isfinite(x2) || !isfinite(y2)) { - // We can't draw to infinity. - pax_last_error = PAX_ERR_INF; - return; - } - - if ((y2 == y0 && y1 == y0) || (x2 == x0 && x1 == x0)) { - // We can't draw a flat triangle. - PAX_SUCCESS(); - return; - } - - #if PAX_COMPILE_ORIENTATION - // Rotate points. - pax_vec1_t tmp = pax_orient_det_vec2f(buf, (pax_vec2f) {x0, y0}); - x0 = tmp.x; y0 = tmp.y; - tmp = pax_orient_det_vec2f(buf, (pax_vec2f) {x1, y1}); - x1 = tmp.x; y1 = tmp.y; - tmp = pax_orient_det_vec2f(buf, (pax_vec2f) {x2, y2}); - x2 = tmp.x; y2 = tmp.y; - #endif - - // Mark all points as dirty - pax_mark_dirty1(buf, x0 - 0.5, y0 - 0.5); - pax_mark_dirty1(buf, x1 - 0.5, y1 - 0.5); - pax_mark_dirty1(buf, x2 - 0.5, y2 - 0.5); - pax_mark_dirty1(buf, x0 + 0.5, y0 + 0.5); - pax_mark_dirty1(buf, x1 + 0.5, y1 + 0.5); - pax_mark_dirty1(buf, x2 + 0.5, y2 + 0.5); - - #if PAX_COMPILE_MCR - if (pax_do_multicore) { - // Add worker task. - pax_task_t task = { - .buffer = buf, - .type = PAX_TASK_TRI, - .color = color, - .use_shader = false, - .shape = { x0, y0, x1, y1, x2, y2 }, - .shape_len = 6 - }; - paxmcr_add_task(&task); - // Draw our part. - paxmcr_tri_unshaded(false, buf, color, x0, y0, x1, y1, x2, y2); - } else - #endif - { - pax_tri_unshaded(buf, color, x0, y0, x1, y1, x2, y2); - } - - PAX_SUCCESS(); + PAX_BUF_CHECK("pax_simple_tri"); + if (!pax_do_draw_col(buf, color)) + return; + + if (!isfinite(x0) || !isfinite(y0) || !isfinite(x1) || !isfinite(y1) || !isfinite(x2) || !isfinite(y2)) { + // We can't draw to infinity. + pax_last_error = PAX_ERR_INF; + return; + } + + if ((y2 == y0 && y1 == y0) || (x2 == x0 && x1 == x0)) { + // We can't draw a flat triangle. + PAX_SUCCESS(); + return; + } + +#if PAX_COMPILE_ORIENTATION + // Rotate points. + pax_vec1_t tmp = pax_orient_det_vec2f(buf, (pax_vec2f){x0, y0}); + x0 = tmp.x; + y0 = tmp.y; + tmp = pax_orient_det_vec2f(buf, (pax_vec2f){x1, y1}); + x1 = tmp.x; + y1 = tmp.y; + tmp = pax_orient_det_vec2f(buf, (pax_vec2f){x2, y2}); + x2 = tmp.x; + y2 = tmp.y; +#endif + + // Mark all points as dirty + pax_mark_dirty1(buf, x0 - 0.5, y0 - 0.5); + pax_mark_dirty1(buf, x1 - 0.5, y1 - 0.5); + pax_mark_dirty1(buf, x2 - 0.5, y2 - 0.5); + pax_mark_dirty1(buf, x0 + 0.5, y0 + 0.5); + pax_mark_dirty1(buf, x1 + 0.5, y1 + 0.5); + pax_mark_dirty1(buf, x2 + 0.5, y2 + 0.5); + +#if PAX_COMPILE_MCR + if (pax_do_multicore) { + // Add worker task. + pax_task_t task = { + .buffer = buf, + .type = PAX_TASK_TRI, + .color = color, + .use_shader = false, + .shape = {x0, y0, x1, y1, x2, y2}, + .shape_len = 6 + }; + paxmcr_add_task(&task); + // Draw our part. + paxmcr_tri_unshaded(false, buf, color, x0, y0, x1, y1, x2, y2); + } else +#endif + { + pax_tri_unshaded(buf, color, x0, y0, x1, y1, x2, y2); + } + + PAX_SUCCESS(); } // Draw a arc, ignoring matrix transform. // Angles in radians. void pax_simple_arc(pax_buf_t *buf, pax_col_t color, float x, float y, float r, float a0, float a1) { - PAX_BUF_CHECK("pax_simple_arc"); - if (!pax_do_draw_col(buf, color)) return; - - // Simplify the angles slightly. - float a2 = fmodf(a0, M_PI * 2); - a1 += a2 - a0; - a0 = a2; - if (a1 < a0) PAX_SWAP(float, a0, a1); - if (a1 - a0 > M_PI * 2) { - a1 = M_PI * 2; - a0 = 0; - } - - // Pick an appropriate number of divisions. - int n_div; - if (r > 30) n_div = (a1 - a0) / M_PI * 32 + 1; - if (r > 20) n_div = (a1 - a0) / M_PI * 16 + 1; - else n_div = (a1 - a0) / M_PI * 8 + 1; - - // Get the sine and cosine of one division, used for rotation in the loop. - float div_angle = (a1 - a0) / n_div; - float c_sin = sinf(div_angle); - float c_cos = cosf(div_angle); - - // Start with a unit vector according to a0. - float x0 = cosf(a0); - float y0 = sinf(a0); - - // Draw it as a series of triangles, rotating with what is essentially matrix multiplication. - for (int i = 0; i < n_div; i++) { - // Perform the rotation. - float x1 = x0 * c_cos - y0 * c_sin; - float y1 = x0 * c_sin + y0 * c_cos; - // We subtract y0 and y1 from y because our up is -y. - pax_simple_tri(buf, color, x, y, x + x0 * r, y - y0 * r, x + x1 * r, y - y1 * r); - // Assign them yes. - x0 = x1; - y0 = y1; - } - - PAX_SUCCESS(); + PAX_BUF_CHECK("pax_simple_arc"); + if (!pax_do_draw_col(buf, color)) + return; + + // Simplify the angles slightly. + float a2 = fmodf(a0, M_PI * 2); + a1 += a2 - a0; + a0 = a2; + if (a1 < a0) + PAX_SWAP(float, a0, a1); + if (a1 - a0 > M_PI * 2) { + a1 = M_PI * 2; + a0 = 0; + } + + // Pick an appropriate number of divisions. + int n_div; + if (r > 30) + n_div = (a1 - a0) / M_PI * 32 + 1; + if (r > 20) + n_div = (a1 - a0) / M_PI * 16 + 1; + else + n_div = (a1 - a0) / M_PI * 8 + 1; + + // Get the sine and cosine of one division, used for rotation in the loop. + float div_angle = (a1 - a0) / n_div; + float c_sin = sinf(div_angle); + float c_cos = cosf(div_angle); + + // Start with a unit vector according to a0. + float x0 = cosf(a0); + float y0 = sinf(a0); + + // Draw it as a series of triangles, rotating with what is essentially matrix multiplication. + for (int i = 0; i < n_div; i++) { + // Perform the rotation. + float x1 = x0 * c_cos - y0 * c_sin; + float y1 = x0 * c_sin + y0 * c_cos; + // We subtract y0 and y1 from y because our up is -y. + pax_simple_tri(buf, color, x, y, x + x0 * r, y - y0 * r, x + x1 * r, y - y1 * r); + // Assign them yes. + x0 = x1; + y0 = y1; + } + + PAX_SUCCESS(); } // Draw a circle, ignoring matrix transform. void pax_simple_circle(pax_buf_t *buf, pax_col_t color, float x, float y, float r) { - pax_simple_arc(buf, color, x, y, r, 0, M_PI * 2); + pax_simple_arc(buf, color, x, y, r, 0, M_PI * 2); } diff --git a/src/pax_gfx.h b/src/pax_gfx.h index 7149877..bb5f164 100644 --- a/src/pax_gfx.h +++ b/src/pax_gfx.h @@ -1,36 +1,15 @@ -/* - MIT License - - Copyright (c) 2021-2023 Julian Scheffers - - Permission is hereby granted, free of charge, to any person obtaining a copy - of this software and associated documentation files (the "Software"), to deal - in the Software without restriction, including without limitation the rights - to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - copies of the Software, and to permit persons to whom the Software is - furnished to do so, subject to the following conditions: - - The above copyright notice and this permission notice shall be included in all - copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - SOFTWARE. -*/ + +// SPDX-License-Identifier: MIT #ifndef PAX_GFX_H #define PAX_GFX_H -#include "pax_types.h" #include "pax_fonts.h" -#include "pax_text.h" -#include "pax_shapes.h" -#include "pax_shaders.h" #include "pax_orientation.h" +#include "pax_shaders.h" +#include "pax_shapes.h" +#include "pax_text.h" +#include "pax_types.h" #ifdef __cplusplus extern "C" { @@ -42,98 +21,97 @@ extern "C" { extern pax_err_t pax_last_error; // Describe error. -const char *pax_desc_err (pax_err_t error); +char const *pax_desc_err(pax_err_t error); /* ===== MULTI-CORE RENDERING ==== */ // If multi-core rendering is enabled, wait for the other core. -void pax_join (); +void pax_join(); // Enable multi-core rendering. // You can specify the core number to use, though this may be irrelevant on some platforms. -void pax_enable_multicore (int core); +void pax_enable_multicore(int core); // Disable multi-core rendering. -void pax_disable_multicore (); +void pax_disable_multicore(); /* ============ BUFFER =========== */ // Get the bits per pixel for the given buffer type. -#define PAX_GET_BPP(type) ((type) & 0xff) +#define PAX_GET_BPP(type) ((type) & 0xff) // Reflects whether the buffer type is greyscale. -#define PAX_IS_GREY(type) (((type) & 0xf0000000) == 0x10000000) +#define PAX_IS_GREY(type) (((type) & 0xf0000000) == 0x10000000) // Reflects whether the buffer type is paletted. -#define PAX_IS_PALETTE(type) (((type) & 0xf0000000) == 0x20000000) +#define PAX_IS_PALETTE(type) (((type) & 0xf0000000) == 0x20000000) // Reflects whether the buffer type is color. -#define PAX_IS_COLOR(type) (((type) & 0xf0000000) == 0x00000000) +#define PAX_IS_COLOR(type) (((type) & 0xf0000000) == 0x00000000) // Whether the buffer type potentially has alpha. -#define PAX_IS_ALPHA(type) (((type) & 0x00f00000) || PAX_IS_PALETTE(type)) +#define PAX_IS_ALPHA(type) (((type) & 0x00f00000) || PAX_IS_PALETTE(type)) // Determine how much capacity a certain buffer initialisation needs. -#define PAX_BUF_CALC_SIZE(width, height, type) \ - ((PAX_GET_BPP(type) * (width) * (height) + 7) >> 3) +#define PAX_BUF_CALC_SIZE(width, height, type) ((PAX_GET_BPP(type) * (width) * (height) + 7) >> 3) // Create a new buffer. // If mem is NULL, a new area is allocated. -void pax_buf_init (pax_buf_t *buf, void *mem, int width, int height, pax_buf_type_t type); +void pax_buf_init(pax_buf_t *buf, void *mem, int width, int height, pax_buf_type_t type); // Enable/disable the reversing of endianness for `buf`. // Some displays might require a feature like this one. -void pax_buf_reversed (pax_buf_t *buf, bool reversed_endianness); +void pax_buf_reversed(pax_buf_t *buf, bool reversed_endianness); // Destroy the buffer, freeing its memory. -void pax_buf_destroy (pax_buf_t *buf); +void pax_buf_destroy(pax_buf_t *buf); // WARNING: This is a beta feature and it does not work! -// +// // Convert the buffer to the given new format. // If dest is NULL or equal to src, src will be converted. -void pax_buf_convert (pax_buf_t *dst, pax_buf_t *src, pax_buf_type_t type); +void pax_buf_convert(pax_buf_t *dst, pax_buf_t *src, pax_buf_type_t type); // Retrieve the width of the buffer. -int pax_buf_get_width (const pax_buf_t *buf); +int pax_buf_get_width(pax_buf_t const *buf); // Retrieve the height of the buffer. -int pax_buf_get_height (const pax_buf_t *buf); +int pax_buf_get_height(pax_buf_t const *buf); // Retrieve the width of the buffer. -float pax_buf_get_widthf (const pax_buf_t *buf); +float pax_buf_get_widthf(pax_buf_t const *buf); // Retrieve the height of the buffer. -float pax_buf_get_heightf (const pax_buf_t *buf); +float pax_buf_get_heightf(pax_buf_t const *buf); // Retrieve the type of the buffer. -pax_buf_type_t pax_buf_get_type (const pax_buf_t *buf); +pax_buf_type_t pax_buf_get_type(pax_buf_t const *buf); // Get a const pointer to the image data. // See <../docs/pixelformat.md> for the format. -const void *pax_buf_get_pixels (const pax_buf_t *buf); +void const *pax_buf_get_pixels(pax_buf_t const *buf); // Get a non-const pointer to the image data. // See <../docs/pixelformat.md> for the format. -void *pax_buf_get_pixels_rw (pax_buf_t *buf); +void *pax_buf_get_pixels_rw(pax_buf_t *buf); // Get the byte size of the image data. -size_t pax_buf_get_size (const pax_buf_t *buf); +size_t pax_buf_get_size(pax_buf_t const *buf); // Set orientation of the buffer. -void pax_buf_set_orientation (pax_buf_t *buf, pax_orientation_t x); +void pax_buf_set_orientation(pax_buf_t *buf, pax_orientation_t x); // Get orientation of the buffer. -pax_orientation_t pax_buf_get_orientation (const pax_buf_t *buf); +pax_orientation_t pax_buf_get_orientation(pax_buf_t const *buf); // Scroll the buffer, filling with a placeholder color. -void pax_buf_scroll (pax_buf_t *buf, pax_col_t placeholder, int x, int y); +void pax_buf_scroll(pax_buf_t *buf, pax_col_t placeholder, int x, int y); // Clip the buffer to the desired rectangle. -void pax_clip (pax_buf_t *buf, int x, int y, int width, int height); +void pax_clip(pax_buf_t *buf, int x, int y, int width, int height); // Get the current clip rectangle. -pax_recti pax_get_clip (const pax_buf_t *buf); +pax_recti pax_get_clip(pax_buf_t const *buf); // Clip the buffer to it's full size. -void pax_noclip (pax_buf_t *buf); +void pax_noclip(pax_buf_t *buf); // Check whether the buffer is dirty. -bool pax_is_dirty (const pax_buf_t *buf); +bool pax_is_dirty(pax_buf_t const *buf); // Get a copy of the dirty rectangle. -pax_recti pax_get_dirty (const pax_buf_t *buf); +pax_recti pax_get_dirty(pax_buf_t const *buf); // Mark the entire buffer as clean. -void pax_mark_clean (pax_buf_t *buf); +void pax_mark_clean(pax_buf_t *buf); // Mark the entire buffer as dirty. -void pax_mark_dirty0 (pax_buf_t *buf); +void pax_mark_dirty0(pax_buf_t *buf); // Mark a single point as dirty. -void pax_mark_dirty1 (pax_buf_t *buf, int x, int y); +void pax_mark_dirty1(pax_buf_t *buf, int x, int y); // Mark a rectangle as dirty. -void pax_mark_dirty2 (pax_buf_t *buf, int x, int y, int width, int height); +void pax_mark_dirty2(pax_buf_t *buf, int x, int y, int width, int height); @@ -141,70 +119,70 @@ void pax_mark_dirty2 (pax_buf_t *buf, int x, int y, int width, int // Multiplicatively decreases alpha based on a float. static inline pax_col_t pax_col_reduce_alpha(pax_col_t in, float coeff) { - return ((pax_col_t) (((in & 0xff000000) * coeff)) & 0xff000000) | (in & 0x00ffffff); + return ((pax_col_t)(((in & 0xff000000) * coeff)) & 0xff000000) | (in & 0x00ffffff); } // Combines RGB. static inline pax_col_t pax_col_rgb(uint8_t r, uint8_t g, uint8_t b) { - return 0xff000000 | (r << 16) | (g << 8) | b; + return 0xff000000 | (r << 16) | (g << 8) | b; } // Combines ARGB. static inline pax_col_t pax_col_argb(uint8_t a, uint8_t r, uint8_t g, uint8_t b) { - return (a << 24) | (r << 16) | (g << 8) | b; + return (a << 24) | (r << 16) | (g << 8) | b; } // Converts HSV to RGB, ranges are 0-255. -pax_col_t pax_col_hsv (uint8_t h, uint8_t s, uint8_t v); +pax_col_t pax_col_hsv(uint8_t h, uint8_t s, uint8_t v); // Converts AHSV to ARGB, ranges are 0-255. -pax_col_t pax_col_ahsv (uint8_t a, uint8_t h, uint8_t s, uint8_t v); +pax_col_t pax_col_ahsv(uint8_t a, uint8_t h, uint8_t s, uint8_t v); // Converts HSV to RGB, ranges are 0-359, 0-99, 0-99. -pax_col_t pax_col_hsv_alt (uint16_t h, uint8_t s, uint8_t v); +pax_col_t pax_col_hsv_alt(uint16_t h, uint8_t s, uint8_t v); // Converts AHSV to ARGB, ranges are 0-255, 0-359, 0-99, 0-99. -pax_col_t pax_col_ahsv_alt (uint8_t a, uint16_t h, uint8_t s, uint8_t v); +pax_col_t pax_col_ahsv_alt(uint8_t a, uint16_t h, uint8_t s, uint8_t v); // Converts ARGB into AHSV, ranges are 0-255. -void pax_undo_ahsv (pax_col_t in, uint8_t *a, uint8_t *h, uint8_t *s, uint8_t *v); +void pax_undo_ahsv(pax_col_t in, uint8_t *a, uint8_t *h, uint8_t *s, uint8_t *v); // Converts RGB into HSV, ranges are 0-255. -void pax_undo_hsv (pax_col_t in, uint8_t *h, uint8_t *s, uint8_t *v); +void pax_undo_hsv(pax_col_t in, uint8_t *h, uint8_t *s, uint8_t *v); // Converts ARGB into AHSV, ranges are 0-255, 0-359, 0-99, 0-99. void pax_undo_ahsv_alt(pax_col_t in, uint8_t *a, uint16_t *h, uint8_t *s, uint8_t *v); // Converts RGB into HSV, ranges are 0-359, 0-99, 0-99. -void pax_undo_hsv_alt (pax_col_t in, uint16_t *h, uint8_t *s, uint8_t *v); +void pax_undo_hsv_alt(pax_col_t in, uint16_t *h, uint8_t *s, uint8_t *v); // Linearly interpolates between from and to, including alpha. -pax_col_t pax_col_lerp (uint8_t part, pax_col_t from, pax_col_t to); +pax_col_t pax_col_lerp(uint8_t part, pax_col_t from, pax_col_t to); // Merges the two colors, based on alpha. -pax_col_t pax_col_merge (pax_col_t base, pax_col_t top); +pax_col_t pax_col_merge(pax_col_t base, pax_col_t top); // Tints the color, commonly used for textures. -pax_col_t pax_col_tint (pax_col_t col, pax_col_t tint); +pax_col_t pax_col_tint(pax_col_t col, pax_col_t tint); /* ============ MATRIX =========== */ // Apply the given matrix to the stack. -void pax_apply_2d (pax_buf_t *buf, matrix_2d_t a); +void pax_apply_2d(pax_buf_t *buf, matrix_2d_t a); // Push the current matrix up the stack. -void pax_push_2d (pax_buf_t *buf); +void pax_push_2d(pax_buf_t *buf); // Pop the top matrix off the stack. -void pax_pop_2d (pax_buf_t *buf); +void pax_pop_2d(pax_buf_t *buf); // Reset the matrix stack. // If full is true, the entire stack gets cleared instead of just the top. -void pax_reset_2d (pax_buf_t *buf, bool full); +void pax_reset_2d(pax_buf_t *buf, bool full); /* ======== DRAWING: PIXEL ======= */ // Set a pixel, merging with alpha. -void pax_merge_pixel (pax_buf_t *buf, pax_col_t color, int x, int y); +void pax_merge_pixel(pax_buf_t *buf, pax_col_t color, int x, int y); // Set a pixel. -void pax_set_pixel (pax_buf_t *buf, pax_col_t color, int x, int y); +void pax_set_pixel(pax_buf_t *buf, pax_col_t color, int x, int y); // Get a pixel (does palette lookup if applicable). -pax_col_t pax_get_pixel (const pax_buf_t *buf, int x, int y); +pax_col_t pax_get_pixel(pax_buf_t const *buf, int x, int y); // Set a pixel without color conversion. -void pax_set_pixel_raw (pax_buf_t *buf, pax_col_t color, int x, int y); +void pax_set_pixel_raw(pax_buf_t *buf, pax_col_t color, int x, int y); // Get a pixel without color conversion. -pax_col_t pax_get_pixel_raw (const pax_buf_t *buf, int x, int y); +pax_col_t pax_get_pixel_raw(pax_buf_t const *buf, int x, int y); @@ -212,62 +190,103 @@ pax_col_t pax_get_pixel_raw (const pax_buf_t *buf, int x, int y); // Draw a rectangle with a shader. // If uvs is NULL, a default will be used (0,0; 1,0; 1,1; 0,1). -void pax_shade_rect (pax_buf_t *buf, pax_col_t color, const pax_shader_t *shader, const pax_quadf *uvs, float x, float y, float width, float height); +void pax_shade_rect( + pax_buf_t *buf, + pax_col_t color, + pax_shader_t const *shader, + pax_quadf const *uvs, + float x, + float y, + float width, + float height +); // Draw a line with a shader. // If uvs is NULL, a default will be used (0,0; 1,0). -void pax_shade_line (pax_buf_t *buf, pax_col_t color, const pax_shader_t *shader, const pax_linef *uvs, float x0, float y0, float x1, float y1); +void pax_shade_line( + pax_buf_t *buf, + pax_col_t color, + pax_shader_t const *shader, + pax_linef const *uvs, + float x0, + float y0, + float x1, + float y1 +); // Draw a triangle with a shader. // If uvs is NULL, a default will be used (0,0; 1,0; 0,1). -void pax_shade_tri (pax_buf_t *buf, pax_col_t color, const pax_shader_t *shader, const pax_trif *uvs, float x0, float y0, float x1, float y1, float x2, float y2); +void pax_shade_tri( + pax_buf_t *buf, + pax_col_t color, + pax_shader_t const *shader, + pax_trif const *uvs, + float x0, + float y0, + float x1, + float y1, + float x2, + float y2 +); // Draw an arc with a shader, angles in radians. // If uvs is NULL, a default will be used (0,0; 1,0; 1,1; 0,1). -void pax_shade_arc (pax_buf_t *buf, pax_col_t color, const pax_shader_t *shader, const pax_quadf *uvs, float x, float y, float r, float a0, float a1); +void pax_shade_arc( + pax_buf_t *buf, + pax_col_t color, + pax_shader_t const *shader, + pax_quadf const *uvs, + float x, + float y, + float r, + float a0, + float a1 +); // Draw a circle with a shader. // If uvs is NULL, a default will be used (0,0; 1,0; 1,1; 0,1). -void pax_shade_circle (pax_buf_t *buf, pax_col_t color, const pax_shader_t *shader, const pax_quadf *uvs, float x, float y, float r); +void pax_shade_circle( + pax_buf_t *buf, pax_col_t color, pax_shader_t const *shader, pax_quadf const *uvs, float x, float y, float r +); // Draws an image at the image's normal size. -void pax_draw_image (pax_buf_t *buf, const pax_buf_t *image, float x, float y); +void pax_draw_image(pax_buf_t *buf, pax_buf_t const *image, float x, float y); // Draw an image with a prespecified size. -void pax_draw_image_sized (pax_buf_t *buf, const pax_buf_t *image, float x, float y, float width, float height); +void pax_draw_image_sized(pax_buf_t *buf, pax_buf_t const *image, float x, float y, float width, float height); // Draws an image at the image's normal size. // Assumes the image is completely opaque, any transparent parts are drawn opaque. -void pax_draw_image_op (pax_buf_t *buf, const pax_buf_t *image, float x, float y); +void pax_draw_image_op(pax_buf_t *buf, pax_buf_t const *image, float x, float y); // Draw an image with a prespecified size. // Assumes the image is completely opaque, any transparent parts are drawn opaque. -void pax_draw_image_sized_op (pax_buf_t *buf, const pax_buf_t *image, float x, float y, float width, float height); +void pax_draw_image_sized_op(pax_buf_t *buf, pax_buf_t const *image, float x, float y, float width, float height); // Draw a rectangle. -void pax_draw_rect (pax_buf_t *buf, pax_col_t color, float x, float y, float width, float height); +void pax_draw_rect(pax_buf_t *buf, pax_col_t color, float x, float y, float width, float height); // Draw a line. -void pax_draw_line (pax_buf_t *buf, pax_col_t color, float x0, float y0, float x1, float y1); +void pax_draw_line(pax_buf_t *buf, pax_col_t color, float x0, float y0, float x1, float y1); // Draw a triangle. -void pax_draw_tri (pax_buf_t *buf, pax_col_t color, float x0, float y0, float x1, float y1, float x2, float y2); +void pax_draw_tri(pax_buf_t *buf, pax_col_t color, float x0, float y0, float x1, float y1, float x2, float y2); // Draw an arc, angles in radians. -void pax_draw_arc (pax_buf_t *buf, pax_col_t color, float x, float y, float r, float a0, float a1); +void pax_draw_arc(pax_buf_t *buf, pax_col_t color, float x, float y, float r, float a0, float a1); // Draw a circle. -void pax_draw_circle (pax_buf_t *buf, pax_col_t color, float x, float y, float r); +void pax_draw_circle(pax_buf_t *buf, pax_col_t color, float x, float y, float r); /* ======= DRAWING: SIMPLE ======= */ // Fill the background. -void pax_background (pax_buf_t *buf, pax_col_t color); +void pax_background(pax_buf_t *buf, pax_col_t color); // Draw a rectangle, ignoring matrix transform. -void pax_simple_rect (pax_buf_t *buf, pax_col_t color, float x, float y, float width, float height); +void pax_simple_rect(pax_buf_t *buf, pax_col_t color, float x, float y, float width, float height); // Draw a line, ignoring matrix transform. -void pax_simple_line (pax_buf_t *buf, pax_col_t color, float x0, float y0, float x1, float y1); +void pax_simple_line(pax_buf_t *buf, pax_col_t color, float x0, float y0, float x1, float y1); // Draw a triangle, ignoring matrix transform. -void pax_simple_tri (pax_buf_t *buf, pax_col_t color, float x0, float y0, float x1, float y1, float x2, float y2); +void pax_simple_tri(pax_buf_t *buf, pax_col_t color, float x0, float y0, float x1, float y1, float x2, float y2); // Draw na arc, ignoring matrix transform. // Angles in radians. -void pax_simple_arc (pax_buf_t *buf, pax_col_t color, float x, float y, float r, float a0, float a1); +void pax_simple_arc(pax_buf_t *buf, pax_col_t color, float x, float y, float r, float a0, float a1); // Draw a circle, ignoring matrix transform. -void pax_simple_circle (pax_buf_t *buf, pax_col_t color, float x, float y, float r); +void pax_simple_circle(pax_buf_t *buf, pax_col_t color, float x, float y, float r); @@ -275,4 +294,4 @@ void pax_simple_circle (pax_buf_t *buf, pax_col_t color, float x, } // extern "C" #endif //__cplusplus -#endif //PAX_GFX_H +#endif // PAX_GFX_H diff --git a/src/pax_internal.h b/src/pax_internal.h index 8fff0e8..ff31030 100644 --- a/src/pax_internal.h +++ b/src/pax_internal.h @@ -1,35 +1,14 @@ -/* - MIT License - - Copyright (c) 2021-2023 Julian Scheffers - - Permission is hereby granted, free of charge, to any person obtaining a copy - of this software and associated documentation files (the "Software"), to deal - in the Software without restriction, including without limitation the rights - to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - copies of the Software, and to permit persons to whom the Software is - furnished to do so, subject to the following conditions: - - The above copyright notice and this permission notice shall be included in all - copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - SOFTWARE. -*/ + +// SPDX-License-Identifier: MIT #ifndef PAX_INTERNAL_H #define PAX_INTERNAL_H -#include "pax_gfx.h" #include "helpers/pax_precalculated.h" +#include "pax_gfx.h" -#include #include +#include #include #ifdef __cplusplus @@ -45,10 +24,10 @@ extern "C" { #ifdef __cplusplus } #endif -#include -#include #include #include +#include +#include #ifdef __cplusplus extern "C" { #endif @@ -81,25 +60,27 @@ extern "C" { #if PAX_COMPILE_MCR extern pthread_mutex_t pax_log_mutex; -extern bool pax_log_use_mutex; - -#define PRIVATE_PAX_LOG_HELPER(file, prefix, tag, ...) do {\ - if (pax_log_use_mutex) {\ - pthread_mutex_lock(&pax_log_mutex);\ - }\ - fprintf(file, prefix "%s: ", (tag));\ - fprintf(file, __VA_ARGS__);\ - fputs("\033[0m\r\n", file);\ - if (pax_log_use_mutex) {\ - pthread_mutex_unlock(&pax_log_mutex);\ - }\ - } while(0) +extern bool pax_log_use_mutex; + +#define PRIVATE_PAX_LOG_HELPER(file, prefix, tag, ...) \ + do { \ + if (pax_log_use_mutex) { \ + pthread_mutex_lock(&pax_log_mutex); \ + } \ + fprintf(file, prefix "%s: ", (tag)); \ + fprintf(file, __VA_ARGS__); \ + fputs("\033[0m\r\n", file); \ + if (pax_log_use_mutex) { \ + pthread_mutex_unlock(&pax_log_mutex); \ + } \ + } while (0) #else -#define PRIVATE_PAX_LOG_HELPER(file, prefix, tag, ...) do {\ - fprintf(file, prefix "%s: ", (tag));\ - fprintf(file, __VA_ARGS__);\ - fputs("\033[0m\r\n", file);\ - } while(0) +#define PRIVATE_PAX_LOG_HELPER(file, prefix, tag, ...) \ + do { \ + fprintf(file, prefix "%s: ", (tag)); \ + fprintf(file, __VA_ARGS__); \ + fputs("\033[0m\r\n", file); \ + } while (0) #endif #define PAX_LOGE(tag, ...) PRIVATE_PAX_LOG_HELPER(stderr, "\033[91mError ", tag, __VA_ARGS__) @@ -109,18 +90,22 @@ extern bool pax_log_use_mutex; #ifdef PAX_ENABLE_DEBUG_LOGS #define PAX_LOGD(tag, ...) PRIVATE_PAX_LOG_HELPER(stdout, "\033[94mDebug ", tag, __VA_ARGS__) #else -#define PAX_LOGD(...) do;while(0) +#define PAX_LOGD(...) \ + do \ + ; \ + while (0) #endif #else #define PAX_PERF_CRITICAL_ATTR __attribute__((hot)) -#define PRIVATE_PAX_LOG_HELPER(file, prefix, tag, ...) do {\ - fprintf(file, prefix "%s: ", (tag));\ - fprintf(file, __VA_ARGS__);\ - fputs("\033[0m\r\n", file);\ - } while(0) +#define PRIVATE_PAX_LOG_HELPER(file, prefix, tag, ...) \ + do { \ + fprintf(file, prefix "%s: ", (tag)); \ + fprintf(file, __VA_ARGS__); \ + fputs("\033[0m\r\n", file); \ + } while (0) #define PAX_LOGE(tag, ...) PRIVATE_PAX_LOG_HELPER(stderr, "\033[91mError ", tag, __VA_ARGS__) #define PAX_LOGI(tag, ...) PRIVATE_PAX_LOG_HELPER(stdout, "\033[32mInfo ", tag, __VA_ARGS__) @@ -141,54 +126,86 @@ extern bool pax_do_multicore; // Macros for errors. #ifdef PAX_AUTOREPORT -#define PAX_ERROR(where, errno) { pax_report_error(where, errno); pax_last_error = errno; return; } -#define PAX_ERROR1(where, errno, retval) { pax_report_error(where, errno); pax_last_error = errno; return retval; } +#define PAX_ERROR(where, errno) \ + { \ + pax_report_error(where, errno); \ + pax_last_error = errno; \ + return; \ + } +#define PAX_ERROR1(where, errno, retval) \ + { \ + pax_report_error(where, errno); \ + pax_last_error = errno; \ + return retval; \ + } #else -#define PAX_ERROR(where, errno) { pax_last_error = errno; return; } -#define PAX_ERROR1(where, errno, retval) { pax_last_error = errno; return retval; } +#define PAX_ERROR(where, errno) \ + { \ + pax_last_error = errno; \ + return; \ + } +#define PAX_ERROR1(where, errno, retval) \ + { \ + pax_last_error = errno; \ + return retval; \ + } #endif -#define PAX_SUCCESS() { pax_last_error = PAX_OK; } +#define PAX_SUCCESS() \ + { pax_last_error = PAX_OK; } // Buffer sanity check. -#define PAX_BUF_CHECK(where) { if (!(buf) || !(buf)->buf) PAX_ERROR(where, PAX_ERR_NOBUF); } +#define PAX_BUF_CHECK(where) \ + { \ + if (!(buf) || !(buf)->buf) \ + PAX_ERROR(where, PAX_ERR_NOBUF); \ + } // Buffer sanity check. -#define PAX_BUF_CHECK1(where, retval) { if (!(buf) || !(buf)->buf) PAX_ERROR1(where, PAX_ERR_NOBUF, retval); } +#define PAX_BUF_CHECK1(where, retval) \ + { \ + if (!(buf) || !(buf)->buf) \ + PAX_ERROR1(where, PAX_ERR_NOBUF, retval); \ + } // Swap two variables. -#define PAX_SWAP(type, a, b) { type tmp = a; a = b; b = tmp; } +#define PAX_SWAP(type, a, b) \ + { \ + type tmp = a; \ + a = b; \ + b = tmp; \ + } // Prints a simple error report of an error code. -void pax_report_error(const char *where, pax_err_t errno); +void pax_report_error(char const *where, pax_err_t errno); /* ===== GETTERS AND SETTERS ===== */ // Gets the index getters and setters for the given buffer. -void pax_get_setters(const pax_buf_t *buf, pax_index_getter_t *getter, pax_index_setter_t *setter); +void pax_get_setters(pax_buf_t const *buf, pax_index_getter_t *getter, pax_index_setter_t *setter); // Gets the most efficient index setter for the occasion. // Also converts the color, if applicable. // Returns NULL when setting is not required. -pax_index_setter_t pax_get_setter(const pax_buf_t *buf, pax_col_t *col, const pax_shader_t *shader); +pax_index_setter_t pax_get_setter(pax_buf_t const *buf, pax_col_t *col, pax_shader_t const *shader); // Gets a raw value from a 1BPP buffer. -pax_col_t pax_index_getter_1bpp(const pax_buf_t *buf, int index); +pax_col_t pax_index_getter_1bpp(pax_buf_t const *buf, int index); // Gets a raw value from a 2BPP buffer. -pax_col_t pax_index_getter_2bpp(const pax_buf_t *buf, int index); +pax_col_t pax_index_getter_2bpp(pax_buf_t const *buf, int index); // Gets a raw value from a 4BPP buffer. -pax_col_t pax_index_getter_4bpp(const pax_buf_t *buf, int index); +pax_col_t pax_index_getter_4bpp(pax_buf_t const *buf, int index); // Gets a raw value from a 8BPP buffer. -pax_col_t pax_index_getter_8bpp(const pax_buf_t *buf, int index); +pax_col_t pax_index_getter_8bpp(pax_buf_t const *buf, int index); // Gets a raw value from a 16BPP buffer. -pax_col_t pax_index_getter_16bpp(const pax_buf_t *buf, int index); +pax_col_t pax_index_getter_16bpp(pax_buf_t const *buf, int index); // Gets a raw value from a 32BPP buffer. -pax_col_t pax_index_getter_32bpp(const pax_buf_t *buf, int index); +pax_col_t pax_index_getter_32bpp(pax_buf_t const *buf, int index); // Gets a raw value from a 16BPP buffer, reversed endianness. -pax_col_t pax_index_getter_16bpp_rev(const pax_buf_t *buf, int index); +pax_col_t pax_index_getter_16bpp_rev(pax_buf_t const *buf, int index); // Gets a raw value from a 32BPP buffer, reversed endianness. -pax_col_t pax_index_getter_32bpp_rev(const pax_buf_t *buf, int index); +pax_col_t pax_index_getter_32bpp_rev(pax_buf_t const *buf, int index); // Sets a raw value from a 1BPP buffer. void pax_index_setter_1bpp(pax_buf_t *buf, pax_col_t color, int index); @@ -209,11 +226,11 @@ void pax_index_setter_32bpp_rev(pax_buf_t *buf, pax_col_t color, int index); // Gets based on index instead of coordinates. // Does no bounds checking nor color conversion. -pax_col_t pax_get_index(const pax_buf_t *buf, int index); +pax_col_t pax_get_index(pax_buf_t const *buf, int index); // Gets based on index instead of coordinates. // Does no bounds checking. -pax_col_t pax_get_index_conv(const pax_buf_t *buf, int index); +pax_col_t pax_get_index_conv(pax_buf_t const *buf, int index); // Sets based on index instead of coordinates. // Does no bounds checking nor color conversion. @@ -232,66 +249,66 @@ void pax_merge_index(pax_buf_t *buf, pax_col_t col, int index); /* ======= COLOR CONVERSION ====== */ // Get the correct color conversion methods for the buffer type. -void pax_get_col_conv(const pax_buf_t *buf, pax_col_conv_t *col2buf, pax_col_conv_t *buf2col); +void pax_get_col_conv(pax_buf_t const *buf, pax_col_conv_t *col2buf, pax_col_conv_t *buf2col); // Dummy color converter, returns color input directly. -pax_col_t pax_col_conv_dummy(const pax_buf_t *buf, pax_col_t color); +pax_col_t pax_col_conv_dummy(pax_buf_t const *buf, pax_col_t color); // Truncates input to 1 bit. -pax_col_t pax_trunc_to_1(const pax_buf_t *buf, pax_col_t color); +pax_col_t pax_trunc_to_1(pax_buf_t const *buf, pax_col_t color); // Truncates input to 2 bit. -pax_col_t pax_trunc_to_2(const pax_buf_t *buf, pax_col_t color); +pax_col_t pax_trunc_to_2(pax_buf_t const *buf, pax_col_t color); // Truncates input to 4 bit. -pax_col_t pax_trunc_to_4(const pax_buf_t *buf, pax_col_t color); +pax_col_t pax_trunc_to_4(pax_buf_t const *buf, pax_col_t color); // Truncates input to 8 bit. -pax_col_t pax_trunc_to_8(const pax_buf_t *buf, pax_col_t color); +pax_col_t pax_trunc_to_8(pax_buf_t const *buf, pax_col_t color); // Truncates input to 16 bit. -pax_col_t pax_trunc_to_16(const pax_buf_t *buf, pax_col_t color); +pax_col_t pax_trunc_to_16(pax_buf_t const *buf, pax_col_t color); // Converts ARGB to 1-bit greyscale (AKA black/white). -pax_col_t pax_col_to_1_grey(const pax_buf_t *buf, pax_col_t color); +pax_col_t pax_col_to_1_grey(pax_buf_t const *buf, pax_col_t color); // Converts ARGB to 2-bit greyscale. -pax_col_t pax_col_to_2_grey(const pax_buf_t *buf, pax_col_t color); +pax_col_t pax_col_to_2_grey(pax_buf_t const *buf, pax_col_t color); // Converts ARGB to 4-bit greyscale. -pax_col_t pax_col_to_4_grey(const pax_buf_t *buf, pax_col_t color); +pax_col_t pax_col_to_4_grey(pax_buf_t const *buf, pax_col_t color); // Converts ARGB to 8-bit greyscale. -pax_col_t pax_col_to_8_grey(const pax_buf_t *buf, pax_col_t color); +pax_col_t pax_col_to_8_grey(pax_buf_t const *buf, pax_col_t color); // Converts ARGB to 3, 3, 2 bit RGB. -pax_col_t pax_col_to_332_rgb(const pax_buf_t *buf, pax_col_t color); +pax_col_t pax_col_to_332_rgb(pax_buf_t const *buf, pax_col_t color); // Converts ARGB to 5, 6, 5 bit RGB. -pax_col_t pax_col_to_565_rgb(const pax_buf_t *buf, pax_col_t color); +pax_col_t pax_col_to_565_rgb(pax_buf_t const *buf, pax_col_t color); // Converts ARGB to 1 bit per channel ARGB. -pax_col_t pax_col_to_1111_argb(const pax_buf_t *buf, pax_col_t color); +pax_col_t pax_col_to_1111_argb(pax_buf_t const *buf, pax_col_t color); // Converts ARGB to 2 bit per channel ARGB. -pax_col_t pax_col_to_2222_argb(const pax_buf_t *buf, pax_col_t color); +pax_col_t pax_col_to_2222_argb(pax_buf_t const *buf, pax_col_t color); // Converts ARGB to 4 bit per channel ARGB. -pax_col_t pax_col_to_4444_argb(const pax_buf_t *buf, pax_col_t color); +pax_col_t pax_col_to_4444_argb(pax_buf_t const *buf, pax_col_t color); // Performs a palette lookup based on the input. -pax_col_t pax_pal_lookup(const pax_buf_t *buf, pax_col_t color); +pax_col_t pax_pal_lookup(pax_buf_t const *buf, pax_col_t color); // Converts 1-bit greyscale (AKA black/white) to ARGB. -pax_col_t pax_1_grey_to_col(const pax_buf_t *buf, pax_col_t color); +pax_col_t pax_1_grey_to_col(pax_buf_t const *buf, pax_col_t color); // Converts 2-bit greyscale to ARGB. -pax_col_t pax_2_grey_to_col(const pax_buf_t *buf, pax_col_t color); +pax_col_t pax_2_grey_to_col(pax_buf_t const *buf, pax_col_t color); // Converts 4-bit greyscale to ARGB. -pax_col_t pax_4_grey_to_col(const pax_buf_t *buf, pax_col_t color); +pax_col_t pax_4_grey_to_col(pax_buf_t const *buf, pax_col_t color); // Converts 8-bit greyscale to ARGB. -pax_col_t pax_8_grey_to_col(const pax_buf_t *buf, pax_col_t color); +pax_col_t pax_8_grey_to_col(pax_buf_t const *buf, pax_col_t color); // Converts 3, 3, 2 bit RGB to ARGB. -pax_col_t pax_332_rgb_to_col(const pax_buf_t *buf, pax_col_t color); +pax_col_t pax_332_rgb_to_col(pax_buf_t const *buf, pax_col_t color); // Converts 5, 6, 5 bit RGB to ARGB. -pax_col_t pax_565_rgb_to_col(const pax_buf_t *buf, pax_col_t color); +pax_col_t pax_565_rgb_to_col(pax_buf_t const *buf, pax_col_t color); // Converts 1 bit per channel ARGB to ARGB. -pax_col_t pax_1111_argb_to_col(const pax_buf_t *buf, pax_col_t color); +pax_col_t pax_1111_argb_to_col(pax_buf_t const *buf, pax_col_t color); // Converts 2 bit per channel ARGB to ARGB. -pax_col_t pax_2222_argb_to_col(const pax_buf_t *buf, pax_col_t color); +pax_col_t pax_2222_argb_to_col(pax_buf_t const *buf, pax_col_t color); // Converts 4 bit per channel ARGB to ARGB. -pax_col_t pax_4444_argb_to_col(const pax_buf_t *buf, pax_col_t color); +pax_col_t pax_4444_argb_to_col(pax_buf_t const *buf, pax_col_t color); @@ -300,38 +317,38 @@ pax_col_t pax_4444_argb_to_col(const pax_buf_t *buf, pax_col_t color); // Determine whether or not to draw given a color. // Non-palette buffers: Draw if alpha > 0. // Palette buffers: Draw if color in bounds. -static inline bool pax_do_draw_col(const pax_buf_t *buf, pax_col_t col) { - if (PAX_IS_PALETTE(buf->type)) { - return col < buf->palette_size; - } else { - return col & 0xff000000; - } +static inline bool pax_do_draw_col(pax_buf_t const *buf, pax_col_t col) { + if (PAX_IS_PALETTE(buf->type)) { + return col < buf->palette_size; + } else { + return col & 0xff000000; + } } // A linear interpolation based only on ints. static inline uint8_t pax_lerp(uint8_t part, uint8_t from, uint8_t to) { - // This funny line converts part from 0-255 to 0-256. - // Then, it applies an integer multiplication and the result is shifted right by 8. - return from + (( (to - from) * (part + (part >> 7)) ) >> 8); + // This funny line converts part from 0-255 to 0-256. + // Then, it applies an integer multiplication and the result is shifted right by 8. + return from + (((to - from) * (part + (part >> 7))) >> 8); } // UV interpolation helper for the circle methods. static inline float pax_flerp4(float x, float y, float e0, float e1, float e2, float e3) { - x = x * 0.5 + 0.5; - y = y * -0.5 + 0.5; - float a = e0 + (e1 - e0) * x; - float b = e2 + (e3 - e2) * x; - return a + (b - a) * y; + x = x * 0.5 + 0.5; + y = y * -0.5 + 0.5; + float a = e0 + (e1 - e0) * x; + float b = e2 + (e3 - e2) * x; + return a + (b - a) * y; } // Reverse endianness for 16-bit things. static inline uint16_t pax_rev_endian_16(uint16_t in) { - return (in >> 8) | (in << 8); + return (in >> 8) | (in << 8); } // Reverse endianness for 32-bit things. static inline uint32_t pax_rev_endian_32(uint32_t in) { - return (in >> 24) | ((in >> 8) & 0x0000ff00) | ((in << 8) & 0x00ff0000) | (in << 24); + return (in >> 24) | ((in >> 8) & 0x0000ff00) | ((in << 8) & 0x00ff0000) | (in << 24); } @@ -339,7 +356,7 @@ static inline uint32_t pax_rev_endian_32(uint32_t in) { /* ======= DRAWING HELPERS ======= */ // Gets the correct callback function for the shader. -pax_shader_ctx_t pax_get_shader_ctx(pax_buf_t *buf, pax_col_t color, const pax_shader_t *shader); +pax_shader_ctx_t pax_get_shader_ctx(pax_buf_t *buf, pax_col_t color, pax_shader_t const *shader); // The scheduler for multicore rendering. void paxmcr_add_task(pax_task_t *task); @@ -347,63 +364,135 @@ void paxmcr_add_task(pax_task_t *task); // Multi-core method for shaded triangles. // Assumes points are sorted by Y. // If odd_scanline is true, the odd (counted from 0) lines are drawn, otherwise the even lines are drawn. -void paxmcr_tri_shaded(bool odd_scanline, pax_buf_t *buf, pax_col_t color, const pax_shader_t *shader, - float x0, float y0, float x1, float y1, float x2, float y2, - float u0, float v0, float u1, float v1, float u2, float v2); +void paxmcr_tri_shaded( + bool odd_scanline, + pax_buf_t *buf, + pax_col_t color, + pax_shader_t const *shader, + float x0, + float y0, + float x1, + float y1, + float x2, + float y2, + float u0, + float v0, + float u1, + float v1, + float u2, + float v2 +); // Multi-core optimisation which maps a buffer directly onto another. // If odd_scanline is true, the odd (counted from 0) lines are drawn, otherwise the even lines are drawn. -void paxmcr_overlay_buffer(bool odd_scanline, pax_buf_t *base, pax_buf_t *top, int x, int y, int width, int height, bool assume_opaque); +void paxmcr_overlay_buffer( + bool odd_scanline, pax_buf_t *base, pax_buf_t *top, int x, int y, int width, int height, bool assume_opaque +); // Multi-core method for shaded rects. // If odd_scanline is true, the odd (counted from 0) lines are drawn, otherwise the even lines are drawn. -void paxmcr_rect_shaded(bool odd_scanline, pax_buf_t *buf, pax_col_t color, const pax_shader_t *shader, - float x, float y, float width, float height, - float u0, float v0, float u1, float v1, float u2, float v2, float u3, float v3); +void paxmcr_rect_shaded( + bool odd_scanline, + pax_buf_t *buf, + pax_col_t color, + pax_shader_t const *shader, + float x, + float y, + float width, + float height, + float u0, + float v0, + float u1, + float v1, + float u2, + float v2, + float u3, + float v3 +); // Multi-core method for unshaded triangles. // Assumes points are sorted by Y. // If odd_scanline is true, the odd (counted from 0) lines are drawn, otherwise the even lines are drawn. -void paxmcr_tri_unshaded(bool odd_scanline, pax_buf_t *buf, pax_col_t color, - float x0, float y0, float x1, float y1, float x2, float y2); +void paxmcr_tri_unshaded( + bool odd_scanline, pax_buf_t *buf, pax_col_t color, float x0, float y0, float x1, float y1, float x2, float y2 +); // Multi-core method for rectangle drawing. // If odd_scanline is true, the odd (counted from 0) lines are drawn, otherwise the even lines are drawn. -void paxmcr_rect_unshaded(bool odd_scanline, pax_buf_t *buf, pax_col_t color, - float x, float y, float width, float height); +void paxmcr_rect_unshaded( + bool odd_scanline, pax_buf_t *buf, pax_col_t color, float x, float y, float width, float height +); // Internal method for shaded triangles. // Assumes points are sorted by Y. -void pax_tri_shaded(pax_buf_t *buf, pax_col_t color, const pax_shader_t *shader, - float x0, float y0, float x1, float y1, float x2, float y2, - float u0, float v0, float u1, float v1, float u2, float v2); +void pax_tri_shaded( + pax_buf_t *buf, + pax_col_t color, + pax_shader_t const *shader, + float x0, + float y0, + float x1, + float y1, + float x2, + float y2, + float u0, + float v0, + float u1, + float v1, + float u2, + float v2 +); // Optimisation which maps a buffer directly onto another. // If assume_opaque is true, the overlay is done without transparency. void pax_overlay_buffer(pax_buf_t *base, pax_buf_t *top, int x, int y, int width, int height, bool assume_opaque); // Internal method for shaded rects. -void pax_rect_shaded(pax_buf_t *buf, pax_col_t color, const pax_shader_t *shader, - float x, float y, float width, float height, - float u0, float v0, float u1, float v1, float u2, float v2, float u3, float v3); +void pax_rect_shaded( + pax_buf_t *buf, + pax_col_t color, + pax_shader_t const *shader, + float x, + float y, + float width, + float height, + float u0, + float v0, + float u1, + float v1, + float u2, + float v2, + float u3, + float v3 +); // Internal method for line drawing. -void pax_line_shaded(pax_buf_t *buf, pax_col_t color, const pax_shader_t *shader, float u0, float v0, float u1, float v1, float x0, float y0, float x1, float y1); +void pax_line_shaded( + pax_buf_t *buf, + pax_col_t color, + pax_shader_t const *shader, + float u0, + float v0, + float u1, + float v1, + float x0, + float y0, + float x1, + float y1 +); // Internal method for unshaded triangles. // Assumes points are sorted by Y. -void pax_tri_unshaded(pax_buf_t *buf, pax_col_t color, - float x0, float y0, float x1, float y1, float x2, float y2); +void pax_tri_unshaded(pax_buf_t *buf, pax_col_t color, float x0, float y0, float x1, float y1, float x2, float y2); // Internal method for rectangle drawing. -void pax_rect_unshaded(pax_buf_t *buf, pax_col_t color, - float x, float y, float width, float height); +void pax_rect_unshaded(pax_buf_t *buf, pax_col_t color, float x, float y, float width, float height); // Internal method for line drawing. void pax_line_unshaded(pax_buf_t *buf, pax_col_t color, float x0, float y0, float x1, float y1); @@ -414,4 +503,4 @@ void pax_line_unshaded(pax_buf_t *buf, pax_col_t color, float x0, float y0, floa } #endif //__cplusplus -#endif //PAX_INTERNAL_H +#endif // PAX_INTERNAL_H diff --git a/src/pax_matrix.c b/src/pax_matrix.c index 207ffb4..f48a496 100644 --- a/src/pax_matrix.c +++ b/src/pax_matrix.c @@ -1,26 +1,5 @@ -/* - MIT License - Copyright (c) 2021-2023 Julian Scheffers - - Permission is hereby granted, free of charge, to any person obtaining a copy - of this software and associated documentation files (the "Software"), to deal - in the Software without restriction, including without limitation the rights - to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - copies of the Software, and to permit persons to whom the Software is - furnished to do so, subject to the following conditions: - - The above copyright notice and this permission notice shall be included in all - copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - SOFTWARE. -*/ +// SPDX-License-Identifier: MIT #include "pax_internal.h" @@ -29,85 +8,85 @@ // 2D rotation matrix: represents a 2D shearing. matrix_2d_t matrix_2d_rotate(float angle) { - float cos_res = cosf(-angle); - float sin_res = sinf(-angle); - return (matrix_2d_t) { .arr = { - cos_res, -sin_res, 0, - sin_res, cos_res, 0 - }}; + float cos_res = cosf(-angle); + float sin_res = sinf(-angle); + return (matrix_2d_t){.arr = {cos_res, -sin_res, 0, sin_res, cos_res, 0}}; } // 2D matrix: applies the transformation that b represents on to a. matrix_2d_t matrix_2d_multiply(matrix_2d_t a, matrix_2d_t b) { - // [a b c] [p q r] [ap+bs aq+bt ar+bu+c] - // [d e f]*[s t u]=[dp+es dq+et dr+eu+f] - // [0 0 1] [0 0 1] [0 0 1 ] - return (matrix_2d_t) { .arr = { - a.a0*b.a0 + a.a1*b.b0, a.a0*b.a1 + a.a1*b.b1, a.a0*b.a2 + a.a1*b.b2 + a.a2, - a.b0*b.a0 + a.b1*b.b0, a.b0*b.a1 + a.b1*b.b1, a.b0*b.a2 + a.b1*b.b2 + a.b2 - }}; + // [a b c] [p q r] [ap+bs aq+bt ar+bu+c] + // [d e f]*[s t u]=[dp+es dq+et dr+eu+f] + // [0 0 1] [0 0 1] [0 0 1 ] + return (matrix_2d_t + ){.arr = { + a.a0 * b.a0 + a.a1 * b.b0, + a.a0 * b.a1 + a.a1 * b.b1, + a.a0 * b.a2 + a.a1 * b.b2 + a.a2, + a.b0 * b.a0 + a.b1 * b.b0, + a.b0 * b.a1 + a.b1 * b.b1, + a.b0 * b.a2 + a.b1 * b.b2 + a.b2 + }}; } // 2D matrix: applies the transformation that a represents on to a point. void matrix_2d_transform(matrix_2d_t a, float *x, float *y) { - // [a b c] [x] [a] [b] [c] [ax+by+c] - // [d e f]*[y]=x[d]+y[e]+[f]=[dx+ey+f] - // [0 0 1] [1] [0] [0] [1] [1 ] - float c_x = *x, c_y = *y; - *x = a.a0*c_x + a.a1*c_y + a.a2; - *y = a.b0*c_x + a.b1*c_y + a.b2; + // [a b c] [x] [a] [b] [c] [ax+by+c] + // [d e f]*[y]=x[d]+y[e]+[f]=[dx+ey+f] + // [0 0 1] [1] [0] [0] [1] [1 ] + float c_x = *x, c_y = *y; + *x = a.a0 * c_x + a.a1 * c_y + a.a2; + *y = a.b0 * c_x + a.b1 * c_y + a.b2; } // 2D vector: unifies a given vector (it's magnitude will be 1). // Does not work for vectors with all zero. pax_vec2f vec1_unify(pax_vec2f vec) { - float magnitude = sqrtf(vec.x*vec.x + vec.y*vec.y); - return (pax_vec2f) { - .x = vec.x / magnitude, - .y = vec.y / magnitude - }; + float magnitude = sqrtf(vec.x * vec.x + vec.y * vec.y); + return (pax_vec2f){.x = vec.x / magnitude, .y = vec.y / magnitude}; } // Apply the given matrix to the stack. void pax_apply_2d(pax_buf_t *buf, matrix_2d_t a) { - PAX_BUF_CHECK("pax_apply_2d"); - buf->stack_2d.value = matrix_2d_multiply(buf->stack_2d.value, a); - PAX_SUCCESS(); + PAX_BUF_CHECK("pax_apply_2d"); + buf->stack_2d.value = matrix_2d_multiply(buf->stack_2d.value, a); + PAX_SUCCESS(); } // Push the current matrix up the stack. void pax_push_2d(pax_buf_t *buf) { - PAX_BUF_CHECK("pax_push_2d"); - matrix_stack_2d_t *parent = malloc(sizeof(matrix_stack_2d_t)); - if (!parent) PAX_ERROR("pax_push_2d", PAX_ERR_NOMEM); - *parent = buf->stack_2d; - buf->stack_2d.parent = parent; - PAX_SUCCESS(); + PAX_BUF_CHECK("pax_push_2d"); + matrix_stack_2d_t *parent = malloc(sizeof(matrix_stack_2d_t)); + if (!parent) + PAX_ERROR("pax_push_2d", PAX_ERR_NOMEM); + *parent = buf->stack_2d; + buf->stack_2d.parent = parent; + PAX_SUCCESS(); } // Pop the top matrix off the stack. void pax_pop_2d(pax_buf_t *buf) { - PAX_BUF_CHECK("pax_pop_2d"); - matrix_stack_2d_t *parent = buf->stack_2d.parent; - if (!parent) PAX_ERROR("pax_pop_2d", PAX_ERR_UNDERFLOW); - buf->stack_2d = *parent; - free(parent); - PAX_SUCCESS(); + PAX_BUF_CHECK("pax_pop_2d"); + matrix_stack_2d_t *parent = buf->stack_2d.parent; + if (!parent) + PAX_ERROR("pax_pop_2d", PAX_ERR_UNDERFLOW); + buf->stack_2d = *parent; + free(parent); + PAX_SUCCESS(); } // Reset the matrix stack. // If full is true, the entire stack gets cleared. // Else, only the top element gets cleared. void pax_reset_2d(pax_buf_t *buf, bool full) { - if (full) { - matrix_stack_2d_t *current = buf->stack_2d.parent; - while (current) { - matrix_stack_2d_t *next = current->parent; - free(current); - current = next; - } - buf->stack_2d.parent = NULL; - } - buf->stack_2d.value = matrix_2d_identity(); + if (full) { + matrix_stack_2d_t *current = buf->stack_2d.parent; + while (current) { + matrix_stack_2d_t *next = current->parent; + free(current); + current = next; + } + buf->stack_2d.parent = NULL; + } + buf->stack_2d.value = matrix_2d_identity(); } - diff --git a/src/pax_matrix.h b/src/pax_matrix.h index 8926025..7da6efd 100644 --- a/src/pax_matrix.h +++ b/src/pax_matrix.h @@ -1,34 +1,14 @@ -/* - MIT License - - Copyright (c) 2021-2023 Julian Scheffers - - Permission is hereby granted, free of charge, to any person obtaining a copy - of this software and associated documentation files (the "Software"), to deal - in the Software without restriction, including without limitation the rights - to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - copies of the Software, and to permit persons to whom the Software is - furnished to do so, subject to the following conditions: - - The above copyright notice and this permission notice shall be included in all - copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - SOFTWARE. -*/ + +// SPDX-License-Identifier: MIT #pragma once -#include -#include #include +#include #include +#include + #ifdef __cplusplus #define PAX_CXX_Vecf_union union #else @@ -43,12 +23,12 @@ typedef PAX_CXX_Vecf_union struct_pax_4vec2f pax_vec4_t; typedef PAX_CXX_Vecf_union struct_pax_2vec2f pax_line_t; typedef PAX_CXX_Vecf_union struct_pax_3vec2f pax_tri_t; typedef PAX_CXX_Vecf_union struct_pax_4vec2f pax_quad_t; -typedef PAX_CXX_Vecf_union struct_pax_rectf pax_rect_t; +typedef PAX_CXX_Vecf_union struct_pax_rectf pax_rect_t; /* Integer vectors. */ typedef PAX_CXX_Vecf_union struct_pax_1vec2i pax_vec2i; typedef PAX_CXX_Vecf_union struct_pax_1vec2i pax_1vec2i; -typedef PAX_CXX_Vecf_union struct_pax_recti pax_recti; +typedef PAX_CXX_Vecf_union struct_pax_recti pax_recti; /* Float vectors. */ typedef PAX_CXX_Vecf_union struct_pax_1vec2f pax_vec2f; @@ -59,12 +39,12 @@ typedef PAX_CXX_Vecf_union struct_pax_4vec2f pax_4vec2f; typedef PAX_CXX_Vecf_union struct_pax_2vec2f pax_linef; typedef PAX_CXX_Vecf_union struct_pax_3vec2f pax_trif; typedef PAX_CXX_Vecf_union struct_pax_4vec2f pax_quadf; -typedef PAX_CXX_Vecf_union struct_pax_rectf pax_rectf; +typedef PAX_CXX_Vecf_union struct_pax_rectf pax_rectf; /* Shape things. */ /* Matrix stuff. */ -typedef union matrix_2d matrix_2d_t; +typedef union matrix_2d matrix_2d_t; typedef struct matrix_stack_2d matrix_stack_2d_t; #ifdef __cplusplus @@ -73,465 +53,551 @@ typedef struct matrix_stack_2d matrix_stack_2d_t; namespace pax { -typedef union struct_pax_1vec2f Vec2f; -typedef union struct_pax_2vec2f BiVec2f; -typedef union struct_pax_3vec2f TriVec2f; -typedef union struct_pax_4vec2f QuadVec2f; -typedef union struct_pax_1vec2f Pointf; -typedef union struct_pax_2vec2f Linef; -typedef union struct_pax_3vec2f Trif; -typedef union struct_pax_4vec2f Quadf; -typedef union struct_pax_rectf Rectf; +typedef union struct_pax_1vec2f Vec2f; +typedef union struct_pax_2vec2f BiVec2f; +typedef union struct_pax_3vec2f TriVec2f; +typedef union struct_pax_4vec2f QuadVec2f; +typedef union struct_pax_1vec2f Pointf; +typedef union struct_pax_2vec2f Linef; +typedef union struct_pax_3vec2f Trif; +typedef union struct_pax_4vec2f Quadf; +typedef union struct_pax_rectf Rectf; -typedef union struct_pax_1vec2i Vec2i; -typedef union struct_pax_recti Recti; +typedef union struct_pax_1vec2i Vec2i; +typedef union struct_pax_recti Recti; -typedef union matrix_2d Matrix2f; +typedef union matrix_2d Matrix2f; typedef struct matrix_stack_2d Matrix2fStack; } // namespace pax -#define PAX_CXX_Vec2f_INDEX() \ - pax::Vec2f &operator[](int index) { \ - return ((pax::Vec2f*) arr)[index]; \ - } \ - const pax::Vec2f &operator[](int index) const { \ - return ((const pax::Vec2f*) arr)[index]; \ - } - -#define PAX_CXX_Vecf_AVERAGE() \ - pax::Vec2f average() const { \ - pax::Vec2f avg(0, 0); \ - const size_t _size = sizeof(arr) / 2 / sizeof(float); \ - for (size_t i = 0; i < _size; i++) { \ - avg += arr[i]; \ - } \ - return avg / _size; \ - } - -#define PAX_CXX_Vecf_OPERATOR(_type, _oper) \ - _type operator _oper(_type rhs) const { \ - _type out; \ - const size_t _size = sizeof(arr) / 2 / sizeof(float); \ - for (size_t i = 0; i < _size; i++) { \ - out.arr[i] = arr[i] _oper rhs.arr[i]; \ - } \ - return out; \ - } \ - _type operator _oper(float rhs) const { \ - _type out; \ - const size_t _size = sizeof(arr) / 2 / sizeof(float); \ - for (size_t i = 0; i < _size; i++) { \ - out.arr[i] = arr[i] _oper rhs; \ - } \ - return out; \ - } - -#define PAX_CXX_Vecf_OPERATOR_ASSIGN(_type, _oper) \ - _type &operator _oper(_type rhs) { \ - const size_t _size = sizeof(arr) / 2 / sizeof(float); \ - for (size_t i = 0; i < _size; i++) { \ - arr[i] _oper rhs.arr[i]; \ - } \ - return *this; \ - } \ - _type &operator _oper(float rhs) { \ - const size_t _size = sizeof(arr) / 2 / sizeof(float); \ - for (size_t i = 0; i < _size; i++) { \ - arr[i] _oper rhs; \ - } \ - return *this; \ - } - -#define PAX_CXX_Vecf_OPERATORS(_type) \ - _type round() const { \ - _type out; \ - const size_t _size = sizeof(arr) / sizeof(float); \ - for (size_t i = 0; i < _size; i++) { \ - out.arr[i] = (int) (arr[i] + 0.5); \ - } \ - return out; \ - } \ - bool operator==(_type rhs) const { \ - const size_t _size = sizeof(arr) / 2 / sizeof(float); \ - for (size_t i = 0; i < _size; i++) { \ - if (arr[i] != rhs.arr[i]) return false; \ - } \ - return true; \ - } \ - bool operator!=(_type rhs) const { \ - const size_t _size = sizeof(arr) / 2 / sizeof(float); \ - for (size_t i = 0; i < _size; i++) { \ - if (arr[i] == rhs.arr[i]) return false; \ - } \ - return true; \ - } \ - PAX_CXX_Vecf_OPERATOR(_type, +) \ - PAX_CXX_Vecf_OPERATOR(_type, -) \ - PAX_CXX_Vecf_OPERATOR(_type, *) \ - PAX_CXX_Vecf_OPERATOR(_type, /) \ - PAX_CXX_Vecf_OPERATOR_ASSIGN(_type, +=) \ - PAX_CXX_Vecf_OPERATOR_ASSIGN(_type, -=) \ - PAX_CXX_Vecf_OPERATOR_ASSIGN(_type, *=) \ - PAX_CXX_Vecf_OPERATOR_ASSIGN(_type, /=) - -#define PAX_CXX_Vec2i_INDEX() \ - pax::Vec2i &operator[](int index) { \ - return ((pax::Vec2i*) arr)[index]; \ - } \ - const pax::Vec2i &operator[](int index) const { \ - return ((const pax::Vec2i*) arr)[index]; \ - } - -#define PAX_CXX_Veci_AVERAGE() \ - pax::Vec2i average() const { \ - int64_t avgX, avgY; \ - const size_t _size = sizeof(arr) / 2 / sizeof(int); \ - for (size_t i = 0; i < _size; i++) { \ - avgX += arr[i].x; \ - avgY += arr[i].y; \ - } \ - return {(avgX + _size/2) / _size, (avgY + _size/2) / _size}; \ - } - -#define PAX_CXX_Veci_OPERATOR(_type, _oper) \ - _type operator _oper(_type rhs) const { \ - _type out; \ - const size_t _size = sizeof(arr) / 2 / sizeof(int); \ - for (size_t i = 0; i < _size; i++) { \ - out.arr[i] = arr[i] _oper rhs.arr[i]; \ - } \ - return out; \ - } \ - _type operator _oper(int rhs) const { \ - _type out; \ - const size_t _size = sizeof(arr) / 2 / sizeof(int); \ - for (size_t i = 0; i < _size; i++) { \ - out.arr[i] = arr[i] _oper rhs; \ - } \ - return out; \ - } - -#define PAX_CXX_Veci_OPERATOR_ASSIGN(_type, _oper) \ - _type &operator _oper(_type rhs) { \ - const size_t _size = sizeof(arr) / 2 / sizeof(int); \ - for (size_t i = 0; i < _size; i++) { \ - arr[i] _oper rhs.arr[i]; \ - } \ - return *this; \ - } \ - _type &operator _oper(int rhs) { \ - const size_t _size = sizeof(arr) / 2 / sizeof(int); \ - for (size_t i = 0; i < _size; i++) { \ - arr[i] _oper rhs; \ - } \ - return *this; \ - } - -#define PAX_CXX_Veci_OPERATORS(_type) \ - _type round() const { \ - return *this; \ - } \ - bool operator==(_type rhs) const { \ - const size_t _size = sizeof(arr) / 2 / sizeof(int); \ - for (size_t i = 0; i < _size; i++) { \ - if (arr[i] != rhs.arr[i]) return false; \ - } \ - return true; \ - } \ - bool operator!=(_type rhs) const { \ - const size_t _size = sizeof(arr) / 2 / sizeof(int); \ - for (size_t i = 0; i < _size; i++) { \ - if (arr[i] == rhs.arr[i]) return false; \ - } \ - return true; \ - } \ - PAX_CXX_Veci_OPERATOR(_type, +) \ - PAX_CXX_Veci_OPERATOR(_type, -) \ - PAX_CXX_Veci_OPERATOR(_type, *) \ - PAX_CXX_Veci_OPERATOR(_type, /) \ - PAX_CXX_Veci_OPERATOR_ASSIGN(_type, +=) \ - PAX_CXX_Veci_OPERATOR_ASSIGN(_type, -=) \ - PAX_CXX_Veci_OPERATOR_ASSIGN(_type, *=) \ - PAX_CXX_Veci_OPERATOR_ASSIGN(_type, /=) +#define PAX_CXX_Vec2f_INDEX() \ + pax::Vec2f &operator[](int index) { \ + return ((pax::Vec2f *)arr)[index]; \ + } \ + const pax::Vec2f &operator[](int index) const { \ + return ((const pax::Vec2f *)arr)[index]; \ + } + +#define PAX_CXX_Vecf_AVERAGE() \ + pax::Vec2f average() const { \ + pax::Vec2f avg(0, 0); \ + const size_t _size = sizeof(arr) / 2 / sizeof(float); \ + for (size_t i = 0; i < _size; i++) { \ + avg += arr[i]; \ + } \ + return avg / _size; \ + } + +#define PAX_CXX_Vecf_OPERATOR(_type, _oper) \ + _type operator _oper(_type rhs) const { \ + _type out; \ + const size_t _size = sizeof(arr) / 2 / sizeof(float); \ + for (size_t i = 0; i < _size; i++) { \ + out.arr[i] = arr[i] _oper rhs.arr[i]; \ + } \ + return out; \ + } \ + _type operator _oper(float rhs) const { \ + _type out; \ + const size_t _size = sizeof(arr) / 2 / sizeof(float); \ + for (size_t i = 0; i < _size; i++) { \ + out.arr[i] = arr[i] _oper rhs; \ + } \ + return out; \ + } + +#define PAX_CXX_Vecf_OPERATOR_ASSIGN(_type, _oper) \ + _type &operator _oper(_type rhs) { \ + const size_t _size = sizeof(arr) / 2 / sizeof(float); \ + for (size_t i = 0; i < _size; i++) { \ + arr[i] _oper rhs.arr[i]; \ + } \ + return *this; \ + } \ + _type &operator _oper(float rhs) { \ + const size_t _size = sizeof(arr) / 2 / sizeof(float); \ + for (size_t i = 0; i < _size; i++) { \ + arr[i] _oper rhs; \ + } \ + return *this; \ + } + +#define PAX_CXX_Vecf_OPERATORS(_type) \ + _type round() const { \ + _type out; \ + const size_t _size = sizeof(arr) / sizeof(float); \ + for (size_t i = 0; i < _size; i++) { \ + out.arr[i] = (int)(arr[i] + 0.5); \ + } \ + return out; \ + } \ + bool operator==(_type rhs) const { \ + const size_t _size = sizeof(arr) / 2 / sizeof(float); \ + for (size_t i = 0; i < _size; i++) { \ + if (arr[i] != rhs.arr[i]) \ + return false; \ + } \ + return true; \ + } \ + bool operator!=(_type rhs) const { \ + const size_t _size = sizeof(arr) / 2 / sizeof(float); \ + for (size_t i = 0; i < _size; i++) { \ + if (arr[i] == rhs.arr[i]) \ + return false; \ + } \ + return true; \ + } \ + PAX_CXX_Vecf_OPERATOR(_type, +) PAX_CXX_Vecf_OPERATOR(_type, -) PAX_CXX_Vecf_OPERATOR(_type, *) \ + PAX_CXX_Vecf_OPERATOR(_type, /) PAX_CXX_Vecf_OPERATOR_ASSIGN(_type, +=) \ + PAX_CXX_Vecf_OPERATOR_ASSIGN(_type, -=) PAX_CXX_Vecf_OPERATOR_ASSIGN(_type, *=) \ + PAX_CXX_Vecf_OPERATOR_ASSIGN(_type, /=) + +#define PAX_CXX_Vec2i_INDEX() \ + pax::Vec2i &operator[](int index) { \ + return ((pax::Vec2i *)arr)[index]; \ + } \ + const pax::Vec2i &operator[](int index) const { \ + return ((const pax::Vec2i *)arr)[index]; \ + } + +#define PAX_CXX_Veci_AVERAGE() \ + pax::Vec2i average() const { \ + int64_t avgX, avgY; \ + const size_t _size = sizeof(arr) / 2 / sizeof(int); \ + for (size_t i = 0; i < _size; i++) { \ + avgX += arr[i].x; \ + avgY += arr[i].y; \ + } \ + return {(avgX + _size / 2) / _size, (avgY + _size / 2) / _size}; \ + } + +#define PAX_CXX_Veci_OPERATOR(_type, _oper) \ + _type operator _oper(_type rhs) const { \ + _type out; \ + const size_t _size = sizeof(arr) / 2 / sizeof(int); \ + for (size_t i = 0; i < _size; i++) { \ + out.arr[i] = arr[i] _oper rhs.arr[i]; \ + } \ + return out; \ + } \ + _type operator _oper(int rhs) const { \ + _type out; \ + const size_t _size = sizeof(arr) / 2 / sizeof(int); \ + for (size_t i = 0; i < _size; i++) { \ + out.arr[i] = arr[i] _oper rhs; \ + } \ + return out; \ + } + +#define PAX_CXX_Veci_OPERATOR_ASSIGN(_type, _oper) \ + _type &operator _oper(_type rhs) { \ + const size_t _size = sizeof(arr) / 2 / sizeof(int); \ + for (size_t i = 0; i < _size; i++) { \ + arr[i] _oper rhs.arr[i]; \ + } \ + return *this; \ + } \ + _type &operator _oper(int rhs) { \ + const size_t _size = sizeof(arr) / 2 / sizeof(int); \ + for (size_t i = 0; i < _size; i++) { \ + arr[i] _oper rhs; \ + } \ + return *this; \ + } + +#define PAX_CXX_Veci_OPERATORS(_type) \ + _type round() const { \ + return *this; \ + } \ + bool operator==(_type rhs) const { \ + const size_t _size = sizeof(arr) / 2 / sizeof(int); \ + for (size_t i = 0; i < _size; i++) { \ + if (arr[i] != rhs.arr[i]) \ + return false; \ + } \ + return true; \ + } \ + bool operator!=(_type rhs) const { \ + const size_t _size = sizeof(arr) / 2 / sizeof(int); \ + for (size_t i = 0; i < _size; i++) { \ + if (arr[i] == rhs.arr[i]) \ + return false; \ + } \ + return true; \ + } \ + PAX_CXX_Veci_OPERATOR(_type, +) PAX_CXX_Veci_OPERATOR(_type, -) PAX_CXX_Veci_OPERATOR(_type, *) \ + PAX_CXX_Veci_OPERATOR(_type, /) PAX_CXX_Veci_OPERATOR_ASSIGN(_type, +=) \ + PAX_CXX_Veci_OPERATOR_ASSIGN(_type, -=) PAX_CXX_Veci_OPERATOR_ASSIGN(_type, *=) \ + PAX_CXX_Veci_OPERATOR_ASSIGN(_type, /=) #endif //__cplusplus -PAX_CXX_Vecf_union struct_pax_1vec2i { +PAX_CXX_Vecf_union struct_pax_1vec2i { #ifdef __cplusplus - struct { + struct { #endif - // Single point. - int x, y; + // Single point. + int x, y; #ifdef __cplusplus - }; - int arr[2]; + }; + int arr[2]; #endif - -#ifdef __cplusplus - // Initialise to zero. - struct_pax_1vec2i() {x=y=0;} - // Initialise with value. - struct_pax_1vec2i(int _x, int _y) {x=_x; y=_y;} - // Initialise from initialiser list. - struct_pax_1vec2i(std::initializer_list list) { assert(list.size()==sizeof(arr)/sizeof(float)); std::copy(list.begin(), list.end(), arr); } - // Initialise as copy. - inline struct_pax_1vec2i(const pax::Vec2f &other); - - PAX_CXX_Veci_OPERATORS(pax::Vec2i) + +#ifdef __cplusplus + // Initialise to zero. + struct_pax_1vec2i() { + x = y = 0; + } + // Initialise with value. + struct_pax_1vec2i(int _x, int _y) { + x = _x; + y = _y; + } + // Initialise from initialiser list. + struct_pax_1vec2i(std::initializer_list list) { + assert(list.size() == sizeof(arr) / sizeof(float)); + std::copy(list.begin(), list.end(), arr); + } + // Initialise as copy. + inline struct_pax_1vec2i(pax::Vec2f const &other); + + PAX_CXX_Veci_OPERATORS(pax::Vec2i) #endif //__cplusplus }; -PAX_CXX_Vecf_union struct_pax_1vec2f { +PAX_CXX_Vecf_union struct_pax_1vec2f { #ifdef __cplusplus - struct { + struct { #endif - // Single point. - float x, y; + // Single point. + float x, y; #ifdef __cplusplus - }; - float arr[2]; + }; + float arr[2]; #endif - -#ifdef __cplusplus - // Initialise to zero. - struct_pax_1vec2f() {x=y=0;} - // Initialise with value. - struct_pax_1vec2f(float _x, float _y) {x=_x; y=_y;} - // Initialise from initialiser list. - struct_pax_1vec2f(std::initializer_list list) { assert(list.size()==sizeof(arr)/sizeof(float)); std::copy(list.begin(), list.end(), arr); } - - PAX_CXX_Vecf_OPERATORS(pax::Vec2f) - - // Unifies a this vector (it's magnitude will be 1). - // Does not work for vectors with all zero. - pax::Vec2f &unify(); - // Calculate magnitude of vector. - float magnitude(); - // Calculate magnitude squared of vector. - float squareMagnitude(); + +#ifdef __cplusplus + // Initialise to zero. + struct_pax_1vec2f() { + x = y = 0; + } + // Initialise with value. + struct_pax_1vec2f(float _x, float _y) { + x = _x; + y = _y; + } + // Initialise from initialiser list. + struct_pax_1vec2f(std::initializer_list list) { + assert(list.size() == sizeof(arr) / sizeof(float)); + std::copy(list.begin(), list.end(), arr); + } + + PAX_CXX_Vecf_OPERATORS(pax::Vec2f) + + // Unifies a this vector (it's magnitude will be 1). + // Does not work for vectors with all zero. + pax::Vec2f & + unify(); + // Calculate magnitude of vector. + float magnitude(); + // Calculate magnitude squared of vector. + float squareMagnitude(); #endif //__cplusplus }; #ifdef __cplusplus -pax::Vec2i::struct_pax_1vec2i(const pax::Vec2f &other) { - x = (int) other.x; - y = (int) other.y; +pax::Vec2i::struct_pax_1vec2i(pax::Vec2f const &other) { + x = (int)other.x; + y = (int)other.y; } #endif -PAX_CXX_Vecf_union struct_pax_2vec2f { +PAX_CXX_Vecf_union struct_pax_2vec2f { #ifdef __cplusplus - struct { + struct { #endif - // Line points. - float x0, y0, x1, y1; + // Line points. + float x0, y0, x1, y1; #ifdef __cplusplus - }; - float arr[4]; + }; + float arr[4]; #endif - -#ifdef __cplusplus - // Initialise to zero. - struct_pax_2vec2f() {x0=y0=x1=y1=0;} - // Initialise with value. - struct_pax_2vec2f(float _x0, float _y0, float _x1, float _y1) {x0=_x0; y0=_y0; x1=_x1; y1=_y1;} - // Initialise from initialiser list. - struct_pax_2vec2f(std::initializer_list list) { assert(list.size()==sizeof(arr)/sizeof(float)); std::copy(list.begin(), list.end(), arr); } - - // Operator [] - PAX_CXX_Vec2f_INDEX() - // Function average() - PAX_CXX_Vecf_AVERAGE() - // Operators + - * / += -= *= /= = - PAX_CXX_Vecf_OPERATORS(pax::BiVec2f) + +#ifdef __cplusplus + // Initialise to zero. + struct_pax_2vec2f() { + x0 = y0 = x1 = y1 = 0; + } + // Initialise with value. + struct_pax_2vec2f(float _x0, float _y0, float _x1, float _y1) { + x0 = _x0; + y0 = _y0; + x1 = _x1; + y1 = _y1; + } + // Initialise from initialiser list. + struct_pax_2vec2f(std::initializer_list list) { + assert(list.size() == sizeof(arr) / sizeof(float)); + std::copy(list.begin(), list.end(), arr); + } + + // Operator [] + PAX_CXX_Vec2f_INDEX() + // Function average() + PAX_CXX_Vecf_AVERAGE() + // Operators + - * / += -= *= /= = + PAX_CXX_Vecf_OPERATORS(pax::BiVec2f) #endif //__cplusplus }; -PAX_CXX_Vecf_union struct_pax_3vec2f { +PAX_CXX_Vecf_union struct_pax_3vec2f { #ifdef __cplusplus - struct { + struct { #endif - // Triangle points. - float x0, y0, x1, y1, x2, y2; + // Triangle points. + float x0, y0, x1, y1, x2, y2; #ifdef __cplusplus - }; - float arr[6]; + }; + float arr[6]; #endif - -#ifdef __cplusplus - // Initialise to zero. - struct_pax_3vec2f() {x0=y0=x1=y1=x2=y2=0;} - // Initialise with value. - struct_pax_3vec2f(float _x0, float _y0, float _x1, float _y1, float _x2, float _y2) {x0=_x0; y0=_y0; x1=_x1; y1=_y1; x2=_x2; y2=_y2;} - // Initialise from initialiser list. - struct_pax_3vec2f(std::initializer_list list) { assert(list.size()==sizeof(arr)/sizeof(float)); std::copy(list.begin(), list.end(), arr); } - - // Operator [] - PAX_CXX_Vec2f_INDEX() - // Operators + - * / += -= *= /= = - PAX_CXX_Vecf_OPERATORS(pax::TriVec2f) + +#ifdef __cplusplus + // Initialise to zero. + struct_pax_3vec2f() { + x0 = y0 = x1 = y1 = x2 = y2 = 0; + } + // Initialise with value. + struct_pax_3vec2f(float _x0, float _y0, float _x1, float _y1, float _x2, float _y2) { + x0 = _x0; + y0 = _y0; + x1 = _x1; + y1 = _y1; + x2 = _x2; + y2 = _y2; + } + // Initialise from initialiser list. + struct_pax_3vec2f(std::initializer_list list) { + assert(list.size() == sizeof(arr) / sizeof(float)); + std::copy(list.begin(), list.end(), arr); + } + + // Operator [] + PAX_CXX_Vec2f_INDEX() + // Operators + - * / += -= *= /= = + PAX_CXX_Vecf_OPERATORS(pax::TriVec2f) #endif //__cplusplus }; -PAX_CXX_Vecf_union struct_pax_4vec2f { +PAX_CXX_Vecf_union struct_pax_4vec2f { #ifdef __cplusplus - struct { + struct { #endif - // Quad points. - float x0, y0, x1, y1, x2, y2, x3, y3; + // Quad points. + float x0, y0, x1, y1, x2, y2, x3, y3; #ifdef __cplusplus - }; - float arr[8]; + }; + float arr[8]; #endif - -#ifdef __cplusplus - // Initialise to zero. - struct_pax_4vec2f() {x0=y0=x1=y1=x2=y2=x3=y3=0;} - // Initialise with value. - struct_pax_4vec2f(float _x0, float _y0, float _x1, float _y1, float _x2, float _y2, float _x3, float _y3) {x0=_x0; y0=_y0; x1=_x1; y1=_y1; x2=_x2; y2=_y2; x3=_x3; y3=_y3;} - // Initialise from initialiser list. - struct_pax_4vec2f(std::initializer_list list) { assert(list.size()==sizeof(arr)/sizeof(float)); std::copy(list.begin(), list.end(), arr); } - - // Operator [] - PAX_CXX_Vec2f_INDEX() - // Operators + - * / += -= *= /= = - PAX_CXX_Vecf_OPERATORS(pax::QuadVec2f) + +#ifdef __cplusplus + // Initialise to zero. + struct_pax_4vec2f() { + x0 = y0 = x1 = y1 = x2 = y2 = x3 = y3 = 0; + } + // Initialise with value. + struct_pax_4vec2f(float _x0, float _y0, float _x1, float _y1, float _x2, float _y2, float _x3, float _y3) { + x0 = _x0; + y0 = _y0; + x1 = _x1; + y1 = _y1; + x2 = _x2; + y2 = _y2; + x3 = _x3; + y3 = _y3; + } + // Initialise from initialiser list. + struct_pax_4vec2f(std::initializer_list list) { + assert(list.size() == sizeof(arr) / sizeof(float)); + std::copy(list.begin(), list.end(), arr); + } + + // Operator [] + PAX_CXX_Vec2f_INDEX() + // Operators + - * / += -= *= /= = + PAX_CXX_Vecf_OPERATORS(pax::QuadVec2f) #endif //__cplusplus }; -PAX_CXX_Vecf_union struct_pax_recti { +PAX_CXX_Vecf_union struct_pax_recti { #ifdef __cplusplus - struct { + struct { #endif - // Rectangle points. - int x, y, w, h; + // Rectangle points. + int x, y, w, h; #ifdef __cplusplus - }; - int arr[4]; + }; + int arr[4]; #endif - -#ifdef __cplusplus - // Initialise to zero. - struct_pax_recti() {x=y=w=h=0;} - // Initialise with value. - struct_pax_recti(int _x, int _y, int _w, int _h) {x=_x; y=_y; w=_w; h=_h;} - // Initialise from initialiser list. - struct_pax_recti(std::initializer_list list) { assert(list.size()==sizeof(arr)/sizeof(int)); std::copy(list.begin(), list.end(), arr); } - // Initialise from initialiser list. - struct_pax_recti(std::initializer_list list) { assert(list.size()==2); position()=list.begin()[0]; size()=list.begin()[1]; } - - // Operator [] - PAX_CXX_Vec2i_INDEX() - - // Comparator. - bool operator==(const pax::Recti &other) const { - return x == other.x && y == other.y && w == other.w && h == other.h; - } - // Comparator. - bool operator!=(const pax::Recti &other) const { - return !operator==(other); - } - - // Get average position, i.e. center, of the rectangle. - pax::Vec2i average() const { - return pax::Vec2f(x+w/2.0f, y+h/2.0f); - } - // Get X/Y component. - pax::Vec2i &position() { - return *(pax::Vec2i*) &x; - } - // Get X/Y component. - const pax::Vec2i &position() const { - return *(pax::Vec2i*) &x; - } - // Get width/height component. - pax::Vec2i &size() { - return *(pax::Vec2i*) &w; - } - // Get width/height component. - const pax::Vec2i &size() const { - return *(pax::Vec2i*) &w; - } - // Create an equivalent quad. - pax::Quadf toQuad() const { - return pax::Quadf(x, y, x+w, y, x+w, y+h, x, y+h); - } - // Get a copy which gaurantees nonnegative dimensions. - pax::Recti fixSize() const { - pax::Recti out = *this; - if (out.w < 0) { out.x += out.w; out.w = -out.w; } - if (out.h < 0) { out.y += out.h; out.h = -out.h; } - return out; - } + +#ifdef __cplusplus + // Initialise to zero. + struct_pax_recti() { + x = y = w = h = 0; + } + // Initialise with value. + struct_pax_recti(int _x, int _y, int _w, int _h) { + x = _x; + y = _y; + w = _w; + h = _h; + } + // Initialise from initialiser list. + struct_pax_recti(std::initializer_list list) { + assert(list.size() == sizeof(arr) / sizeof(int)); + std::copy(list.begin(), list.end(), arr); + } + // Initialise from initialiser list. + struct_pax_recti(std::initializer_list list) { + assert(list.size() == 2); + position() = list.begin()[0]; + size() = list.begin()[1]; + } + + // Operator [] + PAX_CXX_Vec2i_INDEX() + + // Comparator. + bool + operator==(pax::Recti const &other) const { + return x == other.x && y == other.y && w == other.w && h == other.h; + } + // Comparator. + bool operator!=(pax::Recti const &other) const { + return !operator==(other); + } + + // Get average position, i.e. center, of the rectangle. + pax::Vec2i average() const { + return pax::Vec2f(x + w / 2.0f, y + h / 2.0f); + } + // Get X/Y component. + pax::Vec2i &position() { + return *(pax::Vec2i *)&x; + } + // Get X/Y component. + pax::Vec2i const &position() const { + return *(pax::Vec2i *)&x; + } + // Get width/height component. + pax::Vec2i &size() { + return *(pax::Vec2i *)&w; + } + // Get width/height component. + pax::Vec2i const &size() const { + return *(pax::Vec2i *)&w; + } + // Create an equivalent quad. + pax::Quadf toQuad() const { + return pax::Quadf(x, y, x + w, y, x + w, y + h, x, y + h); + } + // Get a copy which gaurantees nonnegative dimensions. + pax::Recti fixSize() const { + pax::Recti out = *this; + if (out.w < 0) { + out.x += out.w; + out.w = -out.w; + } + if (out.h < 0) { + out.y += out.h; + out.h = -out.h; + } + return out; + } #endif //__cplusplus }; -PAX_CXX_Vecf_union struct_pax_rectf { +PAX_CXX_Vecf_union struct_pax_rectf { #ifdef __cplusplus - struct { + struct { #endif - // Rectangle points. - float x, y, w, h; + // Rectangle points. + float x, y, w, h; #ifdef __cplusplus - }; - float arr[4]; + }; + float arr[4]; #endif - -#ifdef __cplusplus - // Initialise to zero. - struct_pax_rectf() {x=y=w=h=0;} - // Initialise with value. - struct_pax_rectf(float _x, float _y, float _w, float _h) {x=_x; y=_y; w=_w; h=_h;} - // Initialise from initialiser list. - struct_pax_rectf(std::initializer_list list) { assert(list.size()==sizeof(arr)/sizeof(float)); std::copy(list.begin(), list.end(), arr); } - // Initialise from initialiser list. - struct_pax_rectf(std::initializer_list list) { assert(list.size()==2); position()=list.begin()[0]; size()=list.begin()[1]; } - - // Operator [] - PAX_CXX_Vec2f_INDEX() - - // Comparator. - bool operator==(const pax::Rectf &other) const { - return x == other.x && y == other.y && w == other.w && h == other.h; - } - // Comparator. - bool operator!=(const pax::Rectf &other) const { - return !operator==(other); - } - - pax::Rectf round() const { - return pax::Rectf{ - (float) (int) (x+0.5), - (float) (int) (y+0.5), - (float) (int) (w+0.5), - (float) (int) (h+0.5) - }; - } - - // Get average position, i.e. center, of the rectangle. - pax::Vec2f average() { - return pax::Vec2f(x+w/2.0f, y+h/2.0f); - } - // Get X/Y component. - pax::Vec2f &position() { - return *(pax::Vec2f*) &x; - } - // Get width/height component. - pax::Vec2f &size() { - return *(pax::Vec2f*) &w; - } - // Create an equivalent quad. - pax::Quadf toQuad() { - return pax::Quadf(x, y, x+w, y, x+w, y+h, x, y+h); - } - // Get a copy which gaurantees nonnegative dimensions. - pax::Rectf fixSize() { - pax::Rectf out = *this; - if (out.w < 0) { out.x += out.w; out.w = -out.w; } - if (out.h < 0) { out.y += out.h; out.h = -out.h; } - return out; - } + +#ifdef __cplusplus + // Initialise to zero. + struct_pax_rectf() { + x = y = w = h = 0; + } + // Initialise with value. + struct_pax_rectf(float _x, float _y, float _w, float _h) { + x = _x; + y = _y; + w = _w; + h = _h; + } + // Initialise from initialiser list. + struct_pax_rectf(std::initializer_list list) { + assert(list.size() == sizeof(arr) / sizeof(float)); + std::copy(list.begin(), list.end(), arr); + } + // Initialise from initialiser list. + struct_pax_rectf(std::initializer_list list) { + assert(list.size() == 2); + position() = list.begin()[0]; + size() = list.begin()[1]; + } + + // Operator [] + PAX_CXX_Vec2f_INDEX() + + // Comparator. + bool + operator==(pax::Rectf const &other) const { + return x == other.x && y == other.y && w == other.w && h == other.h; + } + // Comparator. + bool operator!=(pax::Rectf const &other) const { + return !operator==(other); + } + + pax::Rectf round() const { + return pax::Rectf{(float)(int)(x + 0.5), (float)(int)(y + 0.5), (float)(int)(w + 0.5), (float)(int)(h + 0.5)}; + } + + // Get average position, i.e. center, of the rectangle. + pax::Vec2f average() { + return pax::Vec2f(x + w / 2.0f, y + h / 2.0f); + } + // Get X/Y component. + pax::Vec2f &position() { + return *(pax::Vec2f *)&x; + } + // Get width/height component. + pax::Vec2f &size() { + return *(pax::Vec2f *)&w; + } + // Create an equivalent quad. + pax::Quadf toQuad() { + return pax::Quadf(x, y, x + w, y, x + w, y + h, x, y + h); + } + // Get a copy which gaurantees nonnegative dimensions. + pax::Rectf fixSize() { + pax::Rectf out = *this; + if (out.w < 0) { + out.x += out.w; + out.w = -out.w; + } + if (out.h < 0) { + out.y += out.h; + out.h = -out.h; + } + return out; + } #endif //__cplusplus }; @@ -542,49 +608,65 @@ PAX_CXX_Vecf_union struct_pax_rectf { // b0, b1, b2, // 0, 0, 1 union matrix_2d { - // Named members of the matrix. - struct { - float a0, a1, a2; - float b0, b1, b2; - }; - // Array members of the matrix. - float arr[6]; - -#ifdef __cplusplus - // Initialise to identity. - matrix_2d() {a0=1; a1=a2=b0=0; b1=1; b2=0;} - // Initialise with value. - matrix_2d(float _a0, float _a1, float _a2, float _b0, float _b1, float _b2) {a0=_a0; a1=_a1; a2=_a2; b0=_b0; b1=_b1; b2=_b2;} - // Initialise from initialiser list. - matrix_2d(std::initializer_list list) { assert(list.size()==sizeof(arr)/sizeof(float)); std::copy(list.begin(), list.end(), arr); } - - // Comparator. - bool operator==(const matrix_2d &other) const { - for (auto i = 0; i < 6; i++) { - if (arr[i] != other.arr[i]) return false; - } - return true; - } - // Comparator. - bool operator!=(const matrix_2d &other) const { - return !operator==(other); - } - - // 2D identity matrix: represents no transformation. - static matrix_2d identity(); - // 2D scale matrix: represents a 2D scaling. - static matrix_2d scale(float x, float y); - // 2D translation matrix: represents a 2D movement of the camera. - static matrix_2d translate(float x, float y); - // 2D shear matrix: represents a 2D shearing. - static matrix_2d shear(float x, float y); - // 2D rotation matrix: represents a 2D rotation. - static matrix_2d rotate(float angle); - - // Matrix multiplication. Note that A*B != B*A in matrices. - matrix_2d operator*(matrix_2d rhs); - // Matrix multiplication. Note that A*B != B*A in matrices. - matrix_2d operator*(const matrix_2d &rhs); + // Named members of the matrix. + struct { + float a0, a1, a2; + float b0, b1, b2; + }; + // Array members of the matrix. + float arr[6]; + +#ifdef __cplusplus + // Initialise to identity. + matrix_2d() { + a0 = 1; + a1 = a2 = b0 = 0; + b1 = 1; + b2 = 0; + } + // Initialise with value. + matrix_2d(float _a0, float _a1, float _a2, float _b0, float _b1, float _b2) { + a0 = _a0; + a1 = _a1; + a2 = _a2; + b0 = _b0; + b1 = _b1; + b2 = _b2; + } + // Initialise from initialiser list. + matrix_2d(std::initializer_list list) { + assert(list.size() == sizeof(arr) / sizeof(float)); + std::copy(list.begin(), list.end(), arr); + } + + // Comparator. + bool operator==(matrix_2d const &other) const { + for (auto i = 0; i < 6; i++) { + if (arr[i] != other.arr[i]) + return false; + } + return true; + } + // Comparator. + bool operator!=(matrix_2d const &other) const { + return !operator==(other); + } + + // 2D identity matrix: represents no transformation. + static matrix_2d identity(); + // 2D scale matrix: represents a 2D scaling. + static matrix_2d scale(float x, float y); + // 2D translation matrix: represents a 2D movement of the camera. + static matrix_2d translate(float x, float y); + // 2D shear matrix: represents a 2D shearing. + static matrix_2d shear(float x, float y); + // 2D rotation matrix: represents a 2D rotation. + static matrix_2d rotate(float angle); + + // Matrix multiplication. Note that A*B != B*A in matrices. + matrix_2d operator*(matrix_2d rhs); + // Matrix multiplication. Note that A*B != B*A in matrices. + matrix_2d operator*(matrix_2d const &rhs); #endif //__cplusplus }; @@ -596,61 +678,60 @@ extern "C" { // Check whether the matrix exactly equals the identity matrix. static inline bool matrix_2d_is_identity(matrix_2d_t m) { - return m.a0 == 1 && m.a1 == 0 && m.a2 == 0 - && m.b0 == 0 && m.b1 == 1 && m.b2 == 0; + return m.a0 == 1 && m.a1 == 0 && m.a2 == 0 && m.b0 == 0 && m.b1 == 1 && m.b2 == 0; } // Check whether the matrix represents no more than a translation. static inline bool matrix_2d_is_identity1(matrix_2d_t m) { - return m.a0 == 1 && m.a1 == 0 && m.b0 == 0 && m.b1 == 1; + return m.a0 == 1 && m.a1 == 0 && m.b0 == 0 && m.b1 == 1; } // Check whether the matrix represents no more than a translation and/or scale. static inline bool matrix_2d_is_identity2(matrix_2d_t m) { - return m.a1 == 0 && m.b0 == 0; + return m.a1 == 0 && m.b0 == 0; } // 2D identity matrix: represents no transformation. static inline matrix_2d_t matrix_2d_identity() { #ifdef __cplusplus - return pax::Matrix2f(1, 0, 0, 0, 1, 0); + return pax::Matrix2f(1, 0, 0, 0, 1, 0); #else - return (matrix_2d_t) {.arr = {1, 0, 0, 0, 1, 0}}; + return (matrix_2d_t){.arr = {1, 0, 0, 0, 1, 0}}; #endif } // 2D scale matrix: represents a 2D scaling. static inline matrix_2d_t matrix_2d_scale(float x, float y) { #ifdef __cplusplus - return pax::Matrix2f(x, 0, 0, 0, y, 0); + return pax::Matrix2f(x, 0, 0, 0, y, 0); #else - return (matrix_2d_t) {.arr = {x, 0, 0, 0, y, 0}}; + return (matrix_2d_t){.arr = {x, 0, 0, 0, y, 0}}; #endif } // 2D translation matrix: represents a 2D movement of the camera. static inline matrix_2d_t matrix_2d_translate(float x, float y) { #ifdef __cplusplus - return pax::Matrix2f(1, 0, x, 0, 1, y); + return pax::Matrix2f(1, 0, x, 0, 1, y); #else - return (matrix_2d_t) {.arr = {1, 0, x, 0, 1, y}}; + return (matrix_2d_t){.arr = {1, 0, x, 0, 1, y}}; #endif } // 2D shear matrix: represents a 2D shearing. static inline matrix_2d_t matrix_2d_shear(float x, float y) { #ifdef __cplusplus - return pax::Matrix2f(1, y, 0, x, 1, 0); + return pax::Matrix2f(1, y, 0, x, 1, 0); #else - return (matrix_2d_t) {.arr = {1, y, 0, x, 1, 0}}; + return (matrix_2d_t){.arr = {1, y, 0, x, 1, 0}}; #endif } // 2D rotation matrix: represents a 2D rotation. -matrix_2d_t matrix_2d_rotate (float angle); +matrix_2d_t matrix_2d_rotate(float angle); // 2D matrix: applies the transformation that b represents on to a. -matrix_2d_t matrix_2d_multiply (matrix_2d_t a, matrix_2d_t b); +matrix_2d_t matrix_2d_multiply(matrix_2d_t a, matrix_2d_t b); // 2D matrix: applies the transformation that a represents on to a point. -void matrix_2d_transform (matrix_2d_t a, float *x, float *y); +void matrix_2d_transform(matrix_2d_t a, float *x, float *y); // 2D vector: unifies a given vector (it's magnitude will be 1). // Does not work for vectors with all zero. -pax_vec2f vec1_unify (pax_vec2f vec); +pax_vec2f vec1_unify(pax_vec2f vec); #ifdef __cplusplus } // extern "C" @@ -663,49 +744,50 @@ pax_vec2f vec1_unify (pax_vec2f vec); // Unifies a this vector (it's magnitude will be 1). // Does not work for vectors with all zero. inline pax::Vec2f &pax::Vec2f::unify() { - float mag = magnitude(); - x /= mag; y /= mag; - return *this; + float mag = magnitude(); + x /= mag; + y /= mag; + return *this; } // Calculate magnitude of vector. inline float pax::Vec2f::magnitude() { - return sqrtf(x*x+y*y); + return sqrtf(x * x + y * y); } // Calculate magnitude squared of vector. inline float pax::Vec2f::squareMagnitude() { - return x*x+y*y; + return x * x + y * y; } // 2D identity matrix: represents no transformation. inline pax::Matrix2f pax::Matrix2f::identity() { - return matrix_2d_identity(); + return matrix_2d_identity(); } // 2D scale matrix: represents a 2D scaling. inline pax::Matrix2f pax::Matrix2f::scale(float x, float y) { - return matrix_2d_scale(x, y); + return matrix_2d_scale(x, y); } // 2D translation matrix: represents a 2D movement of the camera. inline pax::Matrix2f pax::Matrix2f::translate(float x, float y) { - return matrix_2d_translate(x, y); + return matrix_2d_translate(x, y); } // 2D shear matrix: represents a 2D shearing. inline pax::Matrix2f pax::Matrix2f::shear(float x, float y) { - return matrix_2d_shear(x, y); + return matrix_2d_shear(x, y); } // 2D rotation matrix: represents a 2D rotation. inline pax::Matrix2f pax::Matrix2f::rotate(float angle) { - return matrix_2d_rotate(angle); + return matrix_2d_rotate(angle); } // Matrix multiplication. Note that A*B != B*A in matrices. inline pax::Matrix2f pax::Matrix2f::operator*(pax::Matrix2f rhs) { - return matrix_2d_multiply(*this, rhs); + return matrix_2d_multiply(*this, rhs); } // Matrix multiplication. Note that A*B != B*A in matrices. -inline pax::Matrix2f pax::Matrix2f::operator*(const pax::Matrix2f &rhs) { - return matrix_2d_multiply(*this, rhs); +inline pax::Matrix2f pax::Matrix2f::operator*(pax::Matrix2f const &rhs) { + return matrix_2d_multiply(*this, rhs); } #endif //__cplusplus \ No newline at end of file diff --git a/src/pax_orientation.c b/src/pax_orientation.c index b524a4f..010b233 100644 --- a/src/pax_orientation.c +++ b/src/pax_orientation.c @@ -1,346 +1,325 @@ -/* - MIT License - - Copyright (c) 2021-2023 Julian Scheffers - - Permission is hereby granted, free of charge, to any person obtaining a copy - of this software and associated documentation files (the "Software"), to deal - in the Software without restriction, including without limitation the rights - to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - copies of the Software, and to permit persons to whom the Software is - furnished to do so, subject to the following conditions: - - The above copyright notice and this permission notice shall be included in all - copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - SOFTWARE. -*/ + +// SPDX-License-Identifier: MIT #include "pax_orientation.h" // Transforms the co-ordinates as 1x counter-clockwise rotation. -static inline pax_vec2f pax_orient_ccw1_vec2f(const pax_buf_t *buf, pax_vec2f vec) __attribute__((pure)); -static inline pax_vec2f pax_orient_ccw1_vec2f(const pax_buf_t *buf, pax_vec2f vec) { - return (pax_vec2f) { - vec.y, - buf->height - vec.x, - }; +static inline pax_vec2f pax_orient_ccw1_vec2f(pax_buf_t const *buf, pax_vec2f vec) __attribute__((pure)); +static inline pax_vec2f pax_orient_ccw1_vec2f(pax_buf_t const *buf, pax_vec2f vec) { + return (pax_vec2f){ + vec.y, + buf->height - vec.x, + }; } // Transforms the co-ordinates as 2x counter-clockwise rotation. -static inline pax_vec2f pax_orient_ccw2_vec2f(const pax_buf_t *buf, pax_vec2f vec) __attribute__((pure)); -static inline pax_vec2f pax_orient_ccw2_vec2f(const pax_buf_t *buf, pax_vec2f vec) { - return (pax_vec2f) { - buf->width - vec.x, - buf->height - vec.y, - }; +static inline pax_vec2f pax_orient_ccw2_vec2f(pax_buf_t const *buf, pax_vec2f vec) __attribute__((pure)); +static inline pax_vec2f pax_orient_ccw2_vec2f(pax_buf_t const *buf, pax_vec2f vec) { + return (pax_vec2f){ + buf->width - vec.x, + buf->height - vec.y, + }; } // Transforms the co-ordinates as 2x counter-clockwise rotation. -static inline pax_vec2f pax_orient_ccw3_vec2f(const pax_buf_t *buf, pax_vec2f vec) __attribute__((pure)); -static inline pax_vec2f pax_orient_ccw3_vec2f(const pax_buf_t *buf, pax_vec2f vec) { - return (pax_vec2f) { - buf->width - vec.y, - vec.x, - }; +static inline pax_vec2f pax_orient_ccw3_vec2f(pax_buf_t const *buf, pax_vec2f vec) __attribute__((pure)); +static inline pax_vec2f pax_orient_ccw3_vec2f(pax_buf_t const *buf, pax_vec2f vec) { + return (pax_vec2f){ + buf->width - vec.y, + vec.x, + }; } // Transforms the co-ordinates as flip horizontally. -static inline pax_vec2f pax_orient_flip_vec2f(const pax_buf_t *buf, pax_vec2f vec) __attribute__((pure)); -static inline pax_vec2f pax_orient_flip_vec2f(const pax_buf_t *buf, pax_vec2f vec) { - return (pax_vec2f) { - buf->width - vec.x, - vec.y, - }; +static inline pax_vec2f pax_orient_flip_vec2f(pax_buf_t const *buf, pax_vec2f vec) __attribute__((pure)); +static inline pax_vec2f pax_orient_flip_vec2f(pax_buf_t const *buf, pax_vec2f vec) { + return (pax_vec2f){ + buf->width - vec.x, + vec.y, + }; } // Transforms the co-ordinates as 1x counter-clockwise rotation and flip horizontally. -static inline pax_vec2f pax_orient_ccw1_flip_vec2f(const pax_buf_t *buf, pax_vec2f vec) __attribute__((pure)); -static inline pax_vec2f pax_orient_ccw1_flip_vec2f(const pax_buf_t *buf, pax_vec2f vec) { - return (pax_vec2f) { - buf->width - vec.y, - buf->height - vec.x, - }; +static inline pax_vec2f pax_orient_ccw1_flip_vec2f(pax_buf_t const *buf, pax_vec2f vec) __attribute__((pure)); +static inline pax_vec2f pax_orient_ccw1_flip_vec2f(pax_buf_t const *buf, pax_vec2f vec) { + return (pax_vec2f){ + buf->width - vec.y, + buf->height - vec.x, + }; } // Transforms the co-ordinates as 2x counter-clockwise rotation and flip horizontally. -static inline pax_vec2f pax_orient_ccw2_flip_vec2f(const pax_buf_t *buf, pax_vec2f vec) __attribute__((pure)); -static inline pax_vec2f pax_orient_ccw2_flip_vec2f(const pax_buf_t *buf, pax_vec2f vec) { - return (pax_vec2f) { - vec.x, - buf->height - vec.y, - }; +static inline pax_vec2f pax_orient_ccw2_flip_vec2f(pax_buf_t const *buf, pax_vec2f vec) __attribute__((pure)); +static inline pax_vec2f pax_orient_ccw2_flip_vec2f(pax_buf_t const *buf, pax_vec2f vec) { + return (pax_vec2f){ + vec.x, + buf->height - vec.y, + }; } // Transforms the co-ordinates as 2x counter-clockwise rotation and flip horizontally. -static inline pax_vec2f pax_orient_ccw3_flip_vec2f(const pax_buf_t *buf, pax_vec2f vec) __attribute__((pure)); -static inline pax_vec2f pax_orient_ccw3_flip_vec2f(const pax_buf_t *buf, pax_vec2f vec) { - return (pax_vec2f) { - vec.y, - vec.x, - }; +static inline pax_vec2f pax_orient_ccw3_flip_vec2f(pax_buf_t const *buf, pax_vec2f vec) __attribute__((pure)); +static inline pax_vec2f pax_orient_ccw3_flip_vec2f(pax_buf_t const *buf, pax_vec2f vec) { + return (pax_vec2f){ + vec.y, + vec.x, + }; } // Detects orientation and transforms co-ordinates accordingly. -pax_vec2f pax_orient_det_vec2f(const pax_buf_t *buf, pax_vec2f vec) { - #if PAX_COMPILE_ORIENTATION - switch (buf->orientation) { - default: - case PAX_O_UPRIGHT: return vec; - case PAX_O_ROT_CCW: return pax_orient_ccw1_vec2f(buf, vec); - case PAX_O_ROT_HALF: return pax_orient_ccw2_vec2f(buf, vec); - case PAX_O_ROT_CW: return pax_orient_ccw3_vec2f(buf, vec); - case PAX_O_FLIP_H: return pax_orient_flip_vec2f(buf, vec); - case PAX_O_ROT_CCW_FLIP_H: return pax_orient_ccw1_flip_vec2f(buf, vec); - case PAX_O_ROT_HALF_FLIP_H: return pax_orient_ccw2_flip_vec2f(buf, vec); - case PAX_O_ROT_CW_FLIP_H: return pax_orient_ccw3_flip_vec2f(buf, vec); - } - #else - return vec; - #endif +pax_vec2f pax_orient_det_vec2f(pax_buf_t const *buf, pax_vec2f vec) { +#if PAX_COMPILE_ORIENTATION + switch (buf->orientation) { + default: + case PAX_O_UPRIGHT: return vec; + case PAX_O_ROT_CCW: return pax_orient_ccw1_vec2f(buf, vec); + case PAX_O_ROT_HALF: return pax_orient_ccw2_vec2f(buf, vec); + case PAX_O_ROT_CW: return pax_orient_ccw3_vec2f(buf, vec); + case PAX_O_FLIP_H: return pax_orient_flip_vec2f(buf, vec); + case PAX_O_ROT_CCW_FLIP_H: return pax_orient_ccw1_flip_vec2f(buf, vec); + case PAX_O_ROT_HALF_FLIP_H: return pax_orient_ccw2_flip_vec2f(buf, vec); + case PAX_O_ROT_CW_FLIP_H: return pax_orient_ccw3_flip_vec2f(buf, vec); + } +#else + return vec; +#endif } // Detects orientation and transforms co-ordinates accordingly. -pax_vec2f pax_unorient_det_vec2f(const pax_buf_t *buf, pax_vec2f vec) { - #if PAX_COMPILE_ORIENTATION - switch (buf->orientation) { - default: - case PAX_O_UPRIGHT: return vec; - case PAX_O_ROT_CCW: return pax_orient_ccw3_vec2f(buf, vec); - case PAX_O_ROT_HALF: return pax_orient_ccw2_vec2f(buf, vec); - case PAX_O_ROT_CW: return pax_orient_ccw1_vec2f(buf, vec); - case PAX_O_FLIP_H: return pax_orient_flip_vec2f(buf, vec); - case PAX_O_ROT_CCW_FLIP_H: return pax_orient_ccw1_flip_vec2f(buf, vec); - case PAX_O_ROT_HALF_FLIP_H: return pax_orient_ccw2_flip_vec2f(buf, vec); - case PAX_O_ROT_CW_FLIP_H: return pax_orient_ccw3_flip_vec2f(buf, vec); - } - #else - return vec; - #endif +pax_vec2f pax_unorient_det_vec2f(pax_buf_t const *buf, pax_vec2f vec) { +#if PAX_COMPILE_ORIENTATION + switch (buf->orientation) { + default: + case PAX_O_UPRIGHT: return vec; + case PAX_O_ROT_CCW: return pax_orient_ccw3_vec2f(buf, vec); + case PAX_O_ROT_HALF: return pax_orient_ccw2_vec2f(buf, vec); + case PAX_O_ROT_CW: return pax_orient_ccw1_vec2f(buf, vec); + case PAX_O_FLIP_H: return pax_orient_flip_vec2f(buf, vec); + case PAX_O_ROT_CCW_FLIP_H: return pax_orient_ccw1_flip_vec2f(buf, vec); + case PAX_O_ROT_HALF_FLIP_H: return pax_orient_ccw2_flip_vec2f(buf, vec); + case PAX_O_ROT_CW_FLIP_H: return pax_orient_ccw3_flip_vec2f(buf, vec); + } +#else + return vec; +#endif } // Transforms the co-ordinates as 1x counter-clockwise rotation. -static inline pax_rectf pax_orient_ccw1_rectf(const pax_buf_t *buf, pax_rectf vec) __attribute__((pure)); -static inline pax_rectf pax_orient_ccw1_rectf(const pax_buf_t *buf, pax_rectf vec) { - return (pax_rectf) { - vec.y, - buf->height - vec.x, - vec.h, - -vec.w, - }; +static inline pax_rectf pax_orient_ccw1_rectf(pax_buf_t const *buf, pax_rectf vec) __attribute__((pure)); +static inline pax_rectf pax_orient_ccw1_rectf(pax_buf_t const *buf, pax_rectf vec) { + return (pax_rectf){ + vec.y, + buf->height - vec.x, + vec.h, + -vec.w, + }; } // Transforms the co-ordinates as 2x counter-clockwise rotation. -static inline pax_rectf pax_orient_ccw2_rectf(const pax_buf_t *buf, pax_rectf vec) __attribute__((pure)); -static inline pax_rectf pax_orient_ccw2_rectf(const pax_buf_t *buf, pax_rectf vec) { - return (pax_rectf) { - buf->width - vec.x, - buf->height - vec.y, - -vec.w, - -vec.h, - }; +static inline pax_rectf pax_orient_ccw2_rectf(pax_buf_t const *buf, pax_rectf vec) __attribute__((pure)); +static inline pax_rectf pax_orient_ccw2_rectf(pax_buf_t const *buf, pax_rectf vec) { + return (pax_rectf){ + buf->width - vec.x, + buf->height - vec.y, + -vec.w, + -vec.h, + }; } // Transforms the co-ordinates as 2x counter-clockwise rotation. -static inline pax_rectf pax_orient_ccw3_rectf(const pax_buf_t *buf, pax_rectf vec) __attribute__((pure)); -static inline pax_rectf pax_orient_ccw3_rectf(const pax_buf_t *buf, pax_rectf vec) { - return (pax_rectf) { - buf->width - vec.y, - vec.x, - -vec.h, - vec.w, - }; +static inline pax_rectf pax_orient_ccw3_rectf(pax_buf_t const *buf, pax_rectf vec) __attribute__((pure)); +static inline pax_rectf pax_orient_ccw3_rectf(pax_buf_t const *buf, pax_rectf vec) { + return (pax_rectf){ + buf->width - vec.y, + vec.x, + -vec.h, + vec.w, + }; } // Transforms the co-ordinates as flip horizontally. -static inline pax_rectf pax_orient_flip_rectf(const pax_buf_t *buf, pax_rectf vec) __attribute__((pure)); -static inline pax_rectf pax_orient_flip_rectf(const pax_buf_t *buf, pax_rectf vec) { - return (pax_rectf) { - buf->width - vec.x, - buf->height - vec.y, - -vec.w, - vec.h, - }; +static inline pax_rectf pax_orient_flip_rectf(pax_buf_t const *buf, pax_rectf vec) __attribute__((pure)); +static inline pax_rectf pax_orient_flip_rectf(pax_buf_t const *buf, pax_rectf vec) { + return (pax_rectf){ + buf->width - vec.x, + buf->height - vec.y, + -vec.w, + vec.h, + }; } // Transforms the co-ordinates as 1x counter-clockwise rotation and flip horizontally. -static inline pax_rectf pax_orient_ccw1_flip_rectf(const pax_buf_t *buf, pax_rectf vec) __attribute__((pure)); -static inline pax_rectf pax_orient_ccw1_flip_rectf(const pax_buf_t *buf, pax_rectf vec) { - return (pax_rectf) { - buf->width - vec.y, - buf->height - vec.x, - -vec.h, - -vec.w, - }; +static inline pax_rectf pax_orient_ccw1_flip_rectf(pax_buf_t const *buf, pax_rectf vec) __attribute__((pure)); +static inline pax_rectf pax_orient_ccw1_flip_rectf(pax_buf_t const *buf, pax_rectf vec) { + return (pax_rectf){ + buf->width - vec.y, + buf->height - vec.x, + -vec.h, + -vec.w, + }; } // Transforms the co-ordinates as 2x counter-clockwise rotation and flip horizontally. -static inline pax_rectf pax_orient_ccw2_flip_rectf(const pax_buf_t *buf, pax_rectf vec) __attribute__((pure)); -static inline pax_rectf pax_orient_ccw2_flip_rectf(const pax_buf_t *buf, pax_rectf vec) { - return (pax_rectf) { - vec.x, - buf->height - vec.y, - vec.w, - -vec.h, - }; +static inline pax_rectf pax_orient_ccw2_flip_rectf(pax_buf_t const *buf, pax_rectf vec) __attribute__((pure)); +static inline pax_rectf pax_orient_ccw2_flip_rectf(pax_buf_t const *buf, pax_rectf vec) { + return (pax_rectf){ + vec.x, + buf->height - vec.y, + vec.w, + -vec.h, + }; } // Transforms the co-ordinates as 2x counter-clockwise rotation and flip horizontally. -static inline pax_rectf pax_orient_ccw3_flip_rectf(const pax_buf_t *buf, pax_rectf vec) __attribute__((pure)); -static inline pax_rectf pax_orient_ccw3_flip_rectf(const pax_buf_t *buf, pax_rectf vec) { - return (pax_rectf) { - vec.y, - vec.x, - vec.h, - vec.w, - }; +static inline pax_rectf pax_orient_ccw3_flip_rectf(pax_buf_t const *buf, pax_rectf vec) __attribute__((pure)); +static inline pax_rectf pax_orient_ccw3_flip_rectf(pax_buf_t const *buf, pax_rectf vec) { + return (pax_rectf){ + vec.y, + vec.x, + vec.h, + vec.w, + }; } // Detects orientation and transforms co-ordinates accordingly. -pax_rectf pax_orient_det_rectf(const pax_buf_t *buf, pax_rectf vec) { - #if PAX_COMPILE_ORIENTATION - switch (buf->orientation) { - default: - case PAX_O_UPRIGHT: return vec; - case PAX_O_ROT_CCW: return pax_orient_ccw1_rectf(buf, vec); - case PAX_O_ROT_HALF: return pax_orient_ccw2_rectf(buf, vec); - case PAX_O_ROT_CW: return pax_orient_ccw3_rectf(buf, vec); - case PAX_O_FLIP_H: return pax_orient_flip_rectf(buf, vec); - case PAX_O_ROT_CCW_FLIP_H: return pax_orient_ccw1_flip_rectf(buf, vec); - case PAX_O_ROT_HALF_FLIP_H: return pax_orient_ccw2_flip_rectf(buf, vec); - case PAX_O_ROT_CW_FLIP_H: return pax_orient_ccw3_flip_rectf(buf, vec); - } - #else - return vec; - #endif +pax_rectf pax_orient_det_rectf(pax_buf_t const *buf, pax_rectf vec) { +#if PAX_COMPILE_ORIENTATION + switch (buf->orientation) { + default: + case PAX_O_UPRIGHT: return vec; + case PAX_O_ROT_CCW: return pax_orient_ccw1_rectf(buf, vec); + case PAX_O_ROT_HALF: return pax_orient_ccw2_rectf(buf, vec); + case PAX_O_ROT_CW: return pax_orient_ccw3_rectf(buf, vec); + case PAX_O_FLIP_H: return pax_orient_flip_rectf(buf, vec); + case PAX_O_ROT_CCW_FLIP_H: return pax_orient_ccw1_flip_rectf(buf, vec); + case PAX_O_ROT_HALF_FLIP_H: return pax_orient_ccw2_flip_rectf(buf, vec); + case PAX_O_ROT_CW_FLIP_H: return pax_orient_ccw3_flip_rectf(buf, vec); + } +#else + return vec; +#endif } // Detects orientation and transforms co-ordinates accordingly. -pax_rectf pax_unorient_det_rectf(const pax_buf_t *buf, pax_rectf vec) { - #if PAX_COMPILE_ORIENTATION - switch (buf->orientation) { - default: - case PAX_O_UPRIGHT: return vec; - case PAX_O_ROT_CCW: return pax_orient_ccw3_rectf(buf, vec); - case PAX_O_ROT_HALF: return pax_orient_ccw2_rectf(buf, vec); - case PAX_O_ROT_CW: return pax_orient_ccw1_rectf(buf, vec); - case PAX_O_FLIP_H: return pax_orient_flip_rectf(buf, vec); - case PAX_O_ROT_CCW_FLIP_H: return pax_orient_ccw1_flip_rectf(buf, vec); - case PAX_O_ROT_HALF_FLIP_H: return pax_orient_ccw2_flip_rectf(buf, vec); - case PAX_O_ROT_CW_FLIP_H: return pax_orient_ccw3_flip_rectf(buf, vec); - } - #else - return vec; - #endif +pax_rectf pax_unorient_det_rectf(pax_buf_t const *buf, pax_rectf vec) { +#if PAX_COMPILE_ORIENTATION + switch (buf->orientation) { + default: + case PAX_O_UPRIGHT: return vec; + case PAX_O_ROT_CCW: return pax_orient_ccw3_rectf(buf, vec); + case PAX_O_ROT_HALF: return pax_orient_ccw2_rectf(buf, vec); + case PAX_O_ROT_CW: return pax_orient_ccw1_rectf(buf, vec); + case PAX_O_FLIP_H: return pax_orient_flip_rectf(buf, vec); + case PAX_O_ROT_CCW_FLIP_H: return pax_orient_ccw1_flip_rectf(buf, vec); + case PAX_O_ROT_HALF_FLIP_H: return pax_orient_ccw2_flip_rectf(buf, vec); + case PAX_O_ROT_CW_FLIP_H: return pax_orient_ccw3_flip_rectf(buf, vec); + } +#else + return vec; +#endif } // Transforms the co-ordinates as 1x counter-clockwise rotation. -static inline pax_vec2i pax_orient_ccw1_vec2i(const pax_buf_t *buf, pax_vec2i vec) __attribute__((pure)); -static inline pax_vec2i pax_orient_ccw1_vec2i(const pax_buf_t *buf, pax_vec2i vec) { - return (pax_vec2i) { - vec.y, - buf->height - 1 - vec.x, - }; +static inline pax_vec2i pax_orient_ccw1_vec2i(pax_buf_t const *buf, pax_vec2i vec) __attribute__((pure)); +static inline pax_vec2i pax_orient_ccw1_vec2i(pax_buf_t const *buf, pax_vec2i vec) { + return (pax_vec2i){ + vec.y, + buf->height - 1 - vec.x, + }; } // Transforms the co-ordinates as 2x counter-clockwise rotation. -static inline pax_vec2i pax_orient_ccw2_vec2i(const pax_buf_t *buf, pax_vec2i vec) __attribute__((pure)); -static inline pax_vec2i pax_orient_ccw2_vec2i(const pax_buf_t *buf, pax_vec2i vec) { - return (pax_vec2i) { - buf->width - 1 - vec.x, - buf->height - 1 - vec.y, - }; +static inline pax_vec2i pax_orient_ccw2_vec2i(pax_buf_t const *buf, pax_vec2i vec) __attribute__((pure)); +static inline pax_vec2i pax_orient_ccw2_vec2i(pax_buf_t const *buf, pax_vec2i vec) { + return (pax_vec2i){ + buf->width - 1 - vec.x, + buf->height - 1 - vec.y, + }; } // Transforms the co-ordinates as 2x counter-clockwise rotation. -static inline pax_vec2i pax_orient_ccw3_vec2i(const pax_buf_t *buf, pax_vec2i vec) __attribute__((pure)); -static inline pax_vec2i pax_orient_ccw3_vec2i(const pax_buf_t *buf, pax_vec2i vec) { - return (pax_vec2i) { - buf->width - 1 - vec.y, - vec.x, - }; +static inline pax_vec2i pax_orient_ccw3_vec2i(pax_buf_t const *buf, pax_vec2i vec) __attribute__((pure)); +static inline pax_vec2i pax_orient_ccw3_vec2i(pax_buf_t const *buf, pax_vec2i vec) { + return (pax_vec2i){ + buf->width - 1 - vec.y, + vec.x, + }; } // Transforms the co-ordinates as 1x counter-clockwise rotation. -static inline pax_vec2i pax_orient_flip_vec2i(const pax_buf_t *buf, pax_vec2i vec) __attribute__((pure)); -static inline pax_vec2i pax_orient_flip_vec2i(const pax_buf_t *buf, pax_vec2i vec) { - return (pax_vec2i) { - buf->width - 1 - vec.x, - vec.y, - }; +static inline pax_vec2i pax_orient_flip_vec2i(pax_buf_t const *buf, pax_vec2i vec) __attribute__((pure)); +static inline pax_vec2i pax_orient_flip_vec2i(pax_buf_t const *buf, pax_vec2i vec) { + return (pax_vec2i){ + buf->width - 1 - vec.x, + vec.y, + }; } // Transforms the co-ordinates as 1x counter-clockwise rotation. -static inline pax_vec2i pax_orient_ccw1_flip_vec2i(const pax_buf_t *buf, pax_vec2i vec) __attribute__((pure)); -static inline pax_vec2i pax_orient_ccw1_flip_vec2i(const pax_buf_t *buf, pax_vec2i vec) { - return (pax_vec2i) { - buf->width - 1 - vec.y, - buf->height - 1 - vec.x, - }; +static inline pax_vec2i pax_orient_ccw1_flip_vec2i(pax_buf_t const *buf, pax_vec2i vec) __attribute__((pure)); +static inline pax_vec2i pax_orient_ccw1_flip_vec2i(pax_buf_t const *buf, pax_vec2i vec) { + return (pax_vec2i){ + buf->width - 1 - vec.y, + buf->height - 1 - vec.x, + }; } // Transforms the co-ordinates as 2x counter-clockwise rotation. -static inline pax_vec2i pax_orient_ccw2_flip_vec2i(const pax_buf_t *buf, pax_vec2i vec) __attribute__((pure)); -static inline pax_vec2i pax_orient_ccw2_flip_vec2i(const pax_buf_t *buf, pax_vec2i vec) { - return (pax_vec2i) { - vec.x, - buf->height - 1 - vec.y, - }; +static inline pax_vec2i pax_orient_ccw2_flip_vec2i(pax_buf_t const *buf, pax_vec2i vec) __attribute__((pure)); +static inline pax_vec2i pax_orient_ccw2_flip_vec2i(pax_buf_t const *buf, pax_vec2i vec) { + return (pax_vec2i){ + vec.x, + buf->height - 1 - vec.y, + }; } // Transforms the co-ordinates as 2x counter-clockwise rotation. -static inline pax_vec2i pax_orient_ccw3_flip_vec2i(const pax_buf_t *buf, pax_vec2i vec) __attribute__((pure)); -static inline pax_vec2i pax_orient_ccw3_flip_vec2i(const pax_buf_t *buf, pax_vec2i vec) { - return (pax_vec2i) { - vec.y, - vec.x, - }; +static inline pax_vec2i pax_orient_ccw3_flip_vec2i(pax_buf_t const *buf, pax_vec2i vec) __attribute__((pure)); +static inline pax_vec2i pax_orient_ccw3_flip_vec2i(pax_buf_t const *buf, pax_vec2i vec) { + return (pax_vec2i){ + vec.y, + vec.x, + }; } // Detects orientation and transforms co-ordinates accordingly. -pax_vec2i pax_orient_det_vec2i(const pax_buf_t *buf, pax_vec2i vec) { - #if PAX_COMPILE_ORIENTATION - switch (buf->orientation) { - default: - case PAX_O_UPRIGHT: return vec; - case PAX_O_ROT_CCW: return pax_orient_ccw1_vec2i(buf, vec); - case PAX_O_ROT_HALF: return pax_orient_ccw2_vec2i(buf, vec); - case PAX_O_ROT_CW: return pax_orient_ccw3_vec2i(buf, vec); - case PAX_O_FLIP_H: return pax_orient_flip_vec2i(buf, vec); - case PAX_O_ROT_CCW_FLIP_H: return pax_orient_ccw1_flip_vec2i(buf, vec); - case PAX_O_ROT_HALF_FLIP_H: return pax_orient_ccw2_flip_vec2i(buf, vec); - case PAX_O_ROT_CW_FLIP_H: return pax_orient_ccw3_flip_vec2i(buf, vec); - } - #else - return vec; - #endif +pax_vec2i pax_orient_det_vec2i(pax_buf_t const *buf, pax_vec2i vec) { +#if PAX_COMPILE_ORIENTATION + switch (buf->orientation) { + default: + case PAX_O_UPRIGHT: return vec; + case PAX_O_ROT_CCW: return pax_orient_ccw1_vec2i(buf, vec); + case PAX_O_ROT_HALF: return pax_orient_ccw2_vec2i(buf, vec); + case PAX_O_ROT_CW: return pax_orient_ccw3_vec2i(buf, vec); + case PAX_O_FLIP_H: return pax_orient_flip_vec2i(buf, vec); + case PAX_O_ROT_CCW_FLIP_H: return pax_orient_ccw1_flip_vec2i(buf, vec); + case PAX_O_ROT_HALF_FLIP_H: return pax_orient_ccw2_flip_vec2i(buf, vec); + case PAX_O_ROT_CW_FLIP_H: return pax_orient_ccw3_flip_vec2i(buf, vec); + } +#else + return vec; +#endif } // Detects orientation and transforms co-ordinates accordingly. -pax_vec2i pax_unorient_det_vec2i(const pax_buf_t *buf, pax_vec2i vec) { - #if PAX_COMPILE_ORIENTATION - switch (buf->orientation) { - default: - case PAX_O_UPRIGHT: return vec; - case PAX_O_ROT_CCW: return pax_orient_ccw3_vec2i(buf, vec); - case PAX_O_ROT_HALF: return pax_orient_ccw2_vec2i(buf, vec); - case PAX_O_ROT_CW: return pax_orient_ccw1_vec2i(buf, vec); - case PAX_O_FLIP_H: return pax_orient_flip_vec2i(buf, vec); - case PAX_O_ROT_CCW_FLIP_H: return pax_orient_ccw1_flip_vec2i(buf, vec); - case PAX_O_ROT_HALF_FLIP_H: return pax_orient_ccw2_flip_vec2i(buf, vec); - case PAX_O_ROT_CW_FLIP_H: return pax_orient_ccw3_flip_vec2i(buf, vec); - } - #else - return vec; - #endif +pax_vec2i pax_unorient_det_vec2i(pax_buf_t const *buf, pax_vec2i vec) { +#if PAX_COMPILE_ORIENTATION + switch (buf->orientation) { + default: + case PAX_O_UPRIGHT: return vec; + case PAX_O_ROT_CCW: return pax_orient_ccw3_vec2i(buf, vec); + case PAX_O_ROT_HALF: return pax_orient_ccw2_vec2i(buf, vec); + case PAX_O_ROT_CW: return pax_orient_ccw1_vec2i(buf, vec); + case PAX_O_FLIP_H: return pax_orient_flip_vec2i(buf, vec); + case PAX_O_ROT_CCW_FLIP_H: return pax_orient_ccw1_flip_vec2i(buf, vec); + case PAX_O_ROT_HALF_FLIP_H: return pax_orient_ccw2_flip_vec2i(buf, vec); + case PAX_O_ROT_CW_FLIP_H: return pax_orient_ccw3_flip_vec2i(buf, vec); + } +#else + return vec; +#endif } diff --git a/src/pax_orientation.h b/src/pax_orientation.h index 5f68384..3b00487 100644 --- a/src/pax_orientation.h +++ b/src/pax_orientation.h @@ -1,26 +1,5 @@ -/* - MIT License - - Copyright (c) 2021-2023 Julian Scheffers - - Permission is hereby granted, free of charge, to any person obtaining a copy - of this software and associated documentation files (the "Software"), to deal - in the Software without restriction, including without limitation the rights - to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - copies of the Software, and to permit persons to whom the Software is - furnished to do so, subject to the following conditions: - - The above copyright notice and this permission notice shall be included in all - copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - SOFTWARE. -*/ + +// SPDX-License-Identifier: MIT #ifndef PAX_ORIENTATION_H #define PAX_ORIENTATION_H @@ -36,109 +15,110 @@ extern "C" { // Flip the orientation horizontally. static inline pax_orientation_t pax_orient_flip_h(pax_orientation_t x) __attribute__((const)); static inline pax_orientation_t pax_orient_flip_h(pax_orientation_t x) { - if ((unsigned int) x >= 8) return PAX_O_FLIP_H; - return (pax_orientation_t) (4 ^ (int) x); + if ((unsigned int)x >= 8) + return PAX_O_FLIP_H; + return (pax_orientation_t)(4 ^ (int)x); } // Flip the orientation vertically. static inline pax_orientation_t pax_orient_flip_v(pax_orientation_t x) __attribute__((const)); static inline pax_orientation_t pax_orient_flip_v(pax_orientation_t x) { - switch (x) { - default: - case PAX_O_UPRIGHT: return PAX_O_ROT_HALF_FLIP_H; - case PAX_O_ROT_CCW: return PAX_O_ROT_CW_FLIP_H; - case PAX_O_ROT_HALF: return PAX_O_FLIP_H; - case PAX_O_ROT_CW: return PAX_O_ROT_CCW_FLIP_H; - case PAX_O_FLIP_H: return PAX_O_ROT_HALF; - case PAX_O_ROT_CCW_FLIP_H: return PAX_O_ROT_CW; - case PAX_O_ROT_HALF_FLIP_H: return PAX_O_UPRIGHT; - case PAX_O_ROT_CW_FLIP_H: return PAX_O_ROT_CCW; - } + switch (x) { + default: + case PAX_O_UPRIGHT: return PAX_O_ROT_HALF_FLIP_H; + case PAX_O_ROT_CCW: return PAX_O_ROT_CW_FLIP_H; + case PAX_O_ROT_HALF: return PAX_O_FLIP_H; + case PAX_O_ROT_CW: return PAX_O_ROT_CCW_FLIP_H; + case PAX_O_FLIP_H: return PAX_O_ROT_HALF; + case PAX_O_ROT_CCW_FLIP_H: return PAX_O_ROT_CW; + case PAX_O_ROT_HALF_FLIP_H: return PAX_O_UPRIGHT; + case PAX_O_ROT_CW_FLIP_H: return PAX_O_ROT_CCW; + } } // Rotate the orientation a quarter turn counter-clockwise. static inline pax_orientation_t pax_orient_rot_ccw(pax_orientation_t x) __attribute__((const)); static inline pax_orientation_t pax_orient_rot_ccw(pax_orientation_t x) { - switch (x) { - default: - case PAX_O_UPRIGHT: return PAX_O_ROT_CCW; - case PAX_O_ROT_CCW: return PAX_O_ROT_HALF; - case PAX_O_ROT_HALF: return PAX_O_ROT_CW; - case PAX_O_ROT_CW: return PAX_O_UPRIGHT; - case PAX_O_FLIP_H: return PAX_O_ROT_CW_FLIP_H; - case PAX_O_ROT_CCW_FLIP_H: return PAX_O_FLIP_H; - case PAX_O_ROT_HALF_FLIP_H: return PAX_O_ROT_CCW_FLIP_H; - case PAX_O_ROT_CW_FLIP_H: return PAX_O_ROT_HALF_FLIP_H; - } + switch (x) { + default: + case PAX_O_UPRIGHT: return PAX_O_ROT_CCW; + case PAX_O_ROT_CCW: return PAX_O_ROT_HALF; + case PAX_O_ROT_HALF: return PAX_O_ROT_CW; + case PAX_O_ROT_CW: return PAX_O_UPRIGHT; + case PAX_O_FLIP_H: return PAX_O_ROT_CW_FLIP_H; + case PAX_O_ROT_CCW_FLIP_H: return PAX_O_FLIP_H; + case PAX_O_ROT_HALF_FLIP_H: return PAX_O_ROT_CCW_FLIP_H; + case PAX_O_ROT_CW_FLIP_H: return PAX_O_ROT_HALF_FLIP_H; + } } // Rotate the orientation a quarter turn clockwise. static inline pax_orientation_t pax_orient_rot_cw(pax_orientation_t x) __attribute__((const)); static inline pax_orientation_t pax_orient_rot_cw(pax_orientation_t x) { - switch (x) { - default: - case PAX_O_UPRIGHT: return PAX_O_ROT_CW; - case PAX_O_ROT_CCW: return PAX_O_UPRIGHT; - case PAX_O_ROT_HALF: return PAX_O_ROT_CCW; - case PAX_O_ROT_CW: return PAX_O_ROT_HALF; - case PAX_O_FLIP_H: return PAX_O_ROT_CCW_FLIP_H; - case PAX_O_ROT_CCW_FLIP_H: return PAX_O_ROT_HALF_FLIP_H; - case PAX_O_ROT_HALF_FLIP_H: return PAX_O_ROT_CW_FLIP_H; - case PAX_O_ROT_CW_FLIP_H: return PAX_O_FLIP_H; - } + switch (x) { + default: + case PAX_O_UPRIGHT: return PAX_O_ROT_CW; + case PAX_O_ROT_CCW: return PAX_O_UPRIGHT; + case PAX_O_ROT_HALF: return PAX_O_ROT_CCW; + case PAX_O_ROT_CW: return PAX_O_ROT_HALF; + case PAX_O_FLIP_H: return PAX_O_ROT_CCW_FLIP_H; + case PAX_O_ROT_CCW_FLIP_H: return PAX_O_ROT_HALF_FLIP_H; + case PAX_O_ROT_HALF_FLIP_H: return PAX_O_ROT_CW_FLIP_H; + case PAX_O_ROT_CW_FLIP_H: return PAX_O_FLIP_H; + } } // Rotate the orientation by a half turn. static inline pax_orientation_t pax_orient_rot_half(pax_orientation_t x) __attribute__((const)); static inline pax_orientation_t pax_orient_rot_half(pax_orientation_t x) { - switch (x) { - default: - case PAX_O_UPRIGHT: return PAX_O_ROT_HALF; - case PAX_O_ROT_CCW: return PAX_O_ROT_CW; - case PAX_O_ROT_HALF: return PAX_O_UPRIGHT; - case PAX_O_ROT_CW: return PAX_O_ROT_CCW; - case PAX_O_FLIP_H: return PAX_O_ROT_HALF_FLIP_H; - case PAX_O_ROT_CCW_FLIP_H: return PAX_O_ROT_CW_FLIP_H; - case PAX_O_ROT_HALF_FLIP_H: return PAX_O_FLIP_H; - case PAX_O_ROT_CW_FLIP_H: return PAX_O_ROT_CCW_FLIP_H; - } + switch (x) { + default: + case PAX_O_UPRIGHT: return PAX_O_ROT_HALF; + case PAX_O_ROT_CCW: return PAX_O_ROT_CW; + case PAX_O_ROT_HALF: return PAX_O_UPRIGHT; + case PAX_O_ROT_CW: return PAX_O_ROT_CCW; + case PAX_O_FLIP_H: return PAX_O_ROT_HALF_FLIP_H; + case PAX_O_ROT_CCW_FLIP_H: return PAX_O_ROT_CW_FLIP_H; + case PAX_O_ROT_HALF_FLIP_H: return PAX_O_FLIP_H; + case PAX_O_ROT_CW_FLIP_H: return PAX_O_ROT_CCW_FLIP_H; + } } // Get the inverse equivalent to the orientation. static inline pax_orientation_t pax_orient_inverse(pax_orientation_t x) __attribute__((const)); static inline pax_orientation_t pax_orient_inverse(pax_orientation_t x) { - switch (x) { - default: - case PAX_O_UPRIGHT: return PAX_O_UPRIGHT; - case PAX_O_ROT_CCW: return PAX_O_ROT_CW; - case PAX_O_ROT_HALF: return PAX_O_ROT_HALF; - case PAX_O_ROT_CW: return PAX_O_ROT_CCW; - case PAX_O_FLIP_H: return PAX_O_FLIP_H; - case PAX_O_ROT_CCW_FLIP_H: return PAX_O_ROT_CCW_FLIP_H; - case PAX_O_ROT_HALF_FLIP_H: return PAX_O_ROT_HALF_FLIP_H; - case PAX_O_ROT_CW_FLIP_H: return PAX_O_ROT_CW_FLIP_H; - } + switch (x) { + default: + case PAX_O_UPRIGHT: return PAX_O_UPRIGHT; + case PAX_O_ROT_CCW: return PAX_O_ROT_CW; + case PAX_O_ROT_HALF: return PAX_O_ROT_HALF; + case PAX_O_ROT_CW: return PAX_O_ROT_CCW; + case PAX_O_FLIP_H: return PAX_O_FLIP_H; + case PAX_O_ROT_CCW_FLIP_H: return PAX_O_ROT_CCW_FLIP_H; + case PAX_O_ROT_HALF_FLIP_H: return PAX_O_ROT_HALF_FLIP_H; + case PAX_O_ROT_CW_FLIP_H: return PAX_O_ROT_CW_FLIP_H; + } } // Detects orientation and transforms co-ordinates accordingly. -pax_vec2f pax_orient_det_vec2f(const pax_buf_t *buf, pax_vec2f vec) __attribute__((pure)); +pax_vec2f pax_orient_det_vec2f(pax_buf_t const *buf, pax_vec2f vec) __attribute__((pure)); // Detects orientation and transforms co-ordinates accordingly. -pax_vec2f pax_unorient_det_vec2f(const pax_buf_t *buf, pax_vec2f vec) __attribute__((pure)); +pax_vec2f pax_unorient_det_vec2f(pax_buf_t const *buf, pax_vec2f vec) __attribute__((pure)); // Detects orientation and transforms co-ordinates accordingly. -pax_rectf pax_orient_det_rectf(const pax_buf_t *buf, pax_rectf vec) __attribute__((pure)); +pax_rectf pax_orient_det_rectf(pax_buf_t const *buf, pax_rectf vec) __attribute__((pure)); // Detects orientation and transforms co-ordinates accordingly. -pax_rectf pax_unorient_det_rectf(const pax_buf_t *buf, pax_rectf vec) __attribute__((pure)); +pax_rectf pax_unorient_det_rectf(pax_buf_t const *buf, pax_rectf vec) __attribute__((pure)); // Detects orientation and transforms co-ordinates accordingly. -pax_vec2i pax_orient_det_vec2i(const pax_buf_t *buf, pax_vec2i vec) __attribute__((pure)); +pax_vec2i pax_orient_det_vec2i(pax_buf_t const *buf, pax_vec2i vec) __attribute__((pure)); // Detects orientation and transforms co-ordinates accordingly. -pax_vec2i pax_unorient_det_vec2i(const pax_buf_t *buf, pax_vec2i vec) __attribute__((pure)); +pax_vec2i pax_unorient_det_vec2i(pax_buf_t const *buf, pax_vec2i vec) __attribute__((pure)); #ifdef __cplusplus } // extern "C" #endif //__cplusplus -#endif //PAX_ORIENTATION_H +#endif // PAX_ORIENTATION_H diff --git a/src/pax_setters.c b/src/pax_setters.c index 53a7a66..dc54b53 100644 --- a/src/pax_setters.c +++ b/src/pax_setters.c @@ -1,537 +1,530 @@ +// SPDX-License-Identifier: MIT + #include "pax_internal.h" #include "pax_shaders.h" /* ===== GETTERS AND SETTERS ===== */ // Gets the index getters and setters for the given buffer. -void pax_get_setters(const pax_buf_t *buf, pax_index_getter_t *getter, pax_index_setter_t *setter) { - switch (buf->bpp) { - case (1): - *getter = pax_index_getter_1bpp; - *setter = pax_index_setter_1bpp; - break; - - case (2): - *getter = pax_index_getter_2bpp; - *setter = pax_index_setter_2bpp; - break; - - case (4): - *getter = pax_index_getter_4bpp; - *setter = pax_index_setter_4bpp; - break; - - case (8): - *getter = pax_index_getter_8bpp; - *setter = pax_index_setter_8bpp; - break; - - case (16): - if (buf->reverse_endianness) { - *getter = pax_index_getter_16bpp_rev; - *setter = pax_index_setter_16bpp_rev; - } else { - *getter = pax_index_getter_16bpp; - *setter = pax_index_setter_16bpp; - } - break; - - case (32): - if (buf->reverse_endianness) { - *getter = pax_index_getter_32bpp_rev; - *setter = pax_index_setter_32bpp_rev; - } else { - *getter = pax_index_getter_32bpp; - *setter = pax_index_setter_32bpp; - } - break; - } +void pax_get_setters(pax_buf_t const *buf, pax_index_getter_t *getter, pax_index_setter_t *setter) { + switch (buf->bpp) { + case (1): + *getter = pax_index_getter_1bpp; + *setter = pax_index_setter_1bpp; + break; + + case (2): + *getter = pax_index_getter_2bpp; + *setter = pax_index_setter_2bpp; + break; + + case (4): + *getter = pax_index_getter_4bpp; + *setter = pax_index_setter_4bpp; + break; + + case (8): + *getter = pax_index_getter_8bpp; + *setter = pax_index_setter_8bpp; + break; + + case (16): + if (buf->reverse_endianness) { + *getter = pax_index_getter_16bpp_rev; + *setter = pax_index_setter_16bpp_rev; + } else { + *getter = pax_index_getter_16bpp; + *setter = pax_index_setter_16bpp; + } + break; + + case (32): + if (buf->reverse_endianness) { + *getter = pax_index_getter_32bpp_rev; + *setter = pax_index_setter_32bpp_rev; + } else { + *getter = pax_index_getter_32bpp; + *setter = pax_index_setter_32bpp; + } + break; + } } // Gets a raw value from a 1BPP buffer. -pax_col_t pax_index_getter_1bpp(const pax_buf_t *buf, int index) { - return (buf->buf_8bpp[index >> 3] >> (index & 7)) & 1; +pax_col_t pax_index_getter_1bpp(pax_buf_t const *buf, int index) { + return (buf->buf_8bpp[index >> 3] >> (index & 7)) & 1; } // Gets a raw value from a 2BPP buffer. -pax_col_t pax_index_getter_2bpp(const pax_buf_t *buf, int index) { - return (buf->buf_8bpp[index >> 2] >> (index & 3) * 2) & 3; +pax_col_t pax_index_getter_2bpp(pax_buf_t const *buf, int index) { + return (buf->buf_8bpp[index >> 2] >> (index & 3) * 2) & 3; } // Gets a raw value from a 4BPP buffer. -pax_col_t pax_index_getter_4bpp(const pax_buf_t *buf, int index) { - return (buf->buf_8bpp[index >> 1] >> (index & 1) * 4) & 15; +pax_col_t pax_index_getter_4bpp(pax_buf_t const *buf, int index) { + return (buf->buf_8bpp[index >> 1] >> (index & 1) * 4) & 15; } // Gets a raw value from a 8BPP buffer. -pax_col_t pax_index_getter_8bpp(const pax_buf_t *buf, int index) { - return buf->buf_8bpp[index]; +pax_col_t pax_index_getter_8bpp(pax_buf_t const *buf, int index) { + return buf->buf_8bpp[index]; } // Gets a raw value from a 16BPP buffer. -pax_col_t pax_index_getter_16bpp(const pax_buf_t *buf, int index) { - return buf->buf_16bpp[index]; +pax_col_t pax_index_getter_16bpp(pax_buf_t const *buf, int index) { + return buf->buf_16bpp[index]; } // Gets a raw value from a 32BPP buffer. -pax_col_t pax_index_getter_32bpp(const pax_buf_t *buf, int index) { - return buf->buf_32bpp[index]; +pax_col_t pax_index_getter_32bpp(pax_buf_t const *buf, int index) { + return buf->buf_32bpp[index]; } // Gets a raw value from a 16BPP buffer, reversed endianness. -pax_col_t pax_index_getter_16bpp_rev(const pax_buf_t *buf, int index) { - return pax_rev_endian_16(buf->buf_16bpp[index]); +pax_col_t pax_index_getter_16bpp_rev(pax_buf_t const *buf, int index) { + return pax_rev_endian_16(buf->buf_16bpp[index]); } // Gets a raw value from a 32BPP buffer, reversed endianness. -pax_col_t pax_index_getter_32bpp_rev(const pax_buf_t *buf, int index) { - return pax_rev_endian_32(buf->buf_32bpp[index]); +pax_col_t pax_index_getter_32bpp_rev(pax_buf_t const *buf, int index) { + return pax_rev_endian_32(buf->buf_32bpp[index]); } // Sets a raw value from a 1BPP buffer. void pax_index_setter_1bpp(pax_buf_t *buf, pax_col_t color, int index) { - uint8_t *ptr = &buf->buf_8bpp[index >> 3]; - switch (index & 7) { - case (0): *ptr = (*ptr & 0xfe) | (color << 0); break; - case (1): *ptr = (*ptr & 0xfd) | (color << 1); break; - case (2): *ptr = (*ptr & 0xfb) | (color << 2); break; - case (3): *ptr = (*ptr & 0xf7) | (color << 3); break; - case (4): *ptr = (*ptr & 0xef) | (color << 4); break; - case (5): *ptr = (*ptr & 0xdf) | (color << 5); break; - case (6): *ptr = (*ptr & 0xbf) | (color << 6); break; - case (7): *ptr = (*ptr & 0x7f) | (color << 7); break; - } + uint8_t *ptr = &buf->buf_8bpp[index >> 3]; + switch (index & 7) { + case (0): *ptr = (*ptr & 0xfe) | (color << 0); break; + case (1): *ptr = (*ptr & 0xfd) | (color << 1); break; + case (2): *ptr = (*ptr & 0xfb) | (color << 2); break; + case (3): *ptr = (*ptr & 0xf7) | (color << 3); break; + case (4): *ptr = (*ptr & 0xef) | (color << 4); break; + case (5): *ptr = (*ptr & 0xdf) | (color << 5); break; + case (6): *ptr = (*ptr & 0xbf) | (color << 6); break; + case (7): *ptr = (*ptr & 0x7f) | (color << 7); break; + } } // Sets a raw value from a 2BPP buffer. void pax_index_setter_2bpp(pax_buf_t *buf, pax_col_t color, int index) { - uint8_t *ptr = &buf->buf_8bpp[index >> 2]; color &= 3; - switch (index & 3) { - case (0): *ptr = (*ptr & 0xfc) | (color << 0); break; - case (1): *ptr = (*ptr & 0xf3) | (color << 2); break; - case (2): *ptr = (*ptr & 0xcf) | (color << 4); break; - case (3): *ptr = (*ptr & 0x3f) | (color << 6); break; - } + uint8_t *ptr = &buf->buf_8bpp[index >> 2]; + color &= 3; + switch (index & 3) { + case (0): *ptr = (*ptr & 0xfc) | (color << 0); break; + case (1): *ptr = (*ptr & 0xf3) | (color << 2); break; + case (2): *ptr = (*ptr & 0xcf) | (color << 4); break; + case (3): *ptr = (*ptr & 0x3f) | (color << 6); break; + } } // Sets a raw value from a 4BPP buffer. void pax_index_setter_4bpp(pax_buf_t *buf, pax_col_t color, int index) { - uint8_t *ptr = &buf->buf_8bpp[index >> 1]; - if (index & 1) { - *ptr = (*ptr & 0x0f) | (color << 4); - } else { - *ptr = (*ptr & 0xf0) | color; - } + uint8_t *ptr = &buf->buf_8bpp[index >> 1]; + if (index & 1) { + *ptr = (*ptr & 0x0f) | (color << 4); + } else { + *ptr = (*ptr & 0xf0) | color; + } } // Sets a raw value from a 8BPP buffer. void pax_index_setter_8bpp(pax_buf_t *buf, pax_col_t color, int index) { - buf->buf_8bpp[index] = color; + buf->buf_8bpp[index] = color; } // Sets a raw value from a 16BPP buffer. void pax_index_setter_16bpp(pax_buf_t *buf, pax_col_t color, int index) { - buf->buf_16bpp[index] = color; + buf->buf_16bpp[index] = color; } // Sets a raw value from a 32BPP buffer. void pax_index_setter_32bpp(pax_buf_t *buf, pax_col_t color, int index) { - buf->buf_32bpp[index] = color; + buf->buf_32bpp[index] = color; } // Sets a raw value from a 16BPP buffer, reversed endianness. void pax_index_setter_16bpp_rev(pax_buf_t *buf, pax_col_t color, int index) { - buf->buf_16bpp[index] = pax_rev_endian_16(color); + buf->buf_16bpp[index] = pax_rev_endian_16(color); } // Sets a raw value from a 32BPP buffer, reversed endianness. void pax_index_setter_32bpp_rev(pax_buf_t *buf, pax_col_t color, int index) { - buf->buf_32bpp[index] = pax_rev_endian_32(color); + buf->buf_32bpp[index] = pax_rev_endian_32(color); } // Gets the most efficient index setter for the occasion. // Also converts the color, if applicable. // Returns NULL when setting is not required. -pax_index_setter_t pax_get_setter(const pax_buf_t *buf, pax_col_t *col_ptr, const pax_shader_t *shader) { - pax_col_t col = *col_ptr; - - if (PAX_IS_PALETTE(buf->type)) { - return pax_do_draw_col(buf, col) ? pax_set_index : NULL; - } - - if (shader && (shader->callback == pax_shader_texture || shader->callback == pax_shader_texture_aa)) { - // We can determine whether to factor in alpha based on buffer type. - if (PAX_IS_ALPHA(((pax_buf_t *)shader->callback_args)->type)) { - // If alpha needs factoring in, return the merging setter. - return col & 0xff000000 ? pax_merge_index : NULL; - } else { - // If alpha doesn't need factoring in, return the converting setter. - return col & 0xff000000 ? pax_set_index_conv : NULL; - } - - } else if (shader) { - // More generic shaders, including text. - if (!(col & 0xff000000) && shader->alpha_promise_0) { - // When a shader promises to have 0 alpha on 0 alpha tint, return NULL. - return NULL; - } else if ((col & 0xff000000) == 0xff000000 && shader->alpha_promise_255) { - // When a shader promises to have 255 alpha on 255 alpha tint, return converting setter. - return pax_set_index_conv; - } else { - // When no promises are made, fall back to the merging setter. - return pax_merge_index; - } - - } else if (!(col & 0xff000000)) { - // If no shader and alpha is 0, don't set. - return NULL; - - } else if ((col & 0xff000000) == 0xff000000) { - // If no shader and 255 alpha, convert color and return raw setter. - *col_ptr = buf->col2buf(buf, col); - return buf->setter; - - } else { - // If no shader and partial alpha, return merging setter. - return pax_merge_index; - - } +pax_index_setter_t pax_get_setter(pax_buf_t const *buf, pax_col_t *col_ptr, pax_shader_t const *shader) { + pax_col_t col = *col_ptr; + + if (PAX_IS_PALETTE(buf->type)) { + return pax_do_draw_col(buf, col) ? pax_set_index : NULL; + } + + if (shader && (shader->callback == pax_shader_texture || shader->callback == pax_shader_texture_aa)) { + // We can determine whether to factor in alpha based on buffer type. + if (PAX_IS_ALPHA(((pax_buf_t *)shader->callback_args)->type)) { + // If alpha needs factoring in, return the merging setter. + return col & 0xff000000 ? pax_merge_index : NULL; + } else { + // If alpha doesn't need factoring in, return the converting setter. + return col & 0xff000000 ? pax_set_index_conv : NULL; + } + + } else if (shader) { + // More generic shaders, including text. + if (!(col & 0xff000000) && shader->alpha_promise_0) { + // When a shader promises to have 0 alpha on 0 alpha tint, return NULL. + return NULL; + } else if ((col & 0xff000000) == 0xff000000 && shader->alpha_promise_255) { + // When a shader promises to have 255 alpha on 255 alpha tint, return converting setter. + return pax_set_index_conv; + } else { + // When no promises are made, fall back to the merging setter. + return pax_merge_index; + } + + } else if (!(col & 0xff000000)) { + // If no shader and alpha is 0, don't set. + return NULL; + + } else if ((col & 0xff000000) == 0xff000000) { + // If no shader and 255 alpha, convert color and return raw setter. + *col_ptr = buf->col2buf(buf, col); + return buf->setter; + + } else { + // If no shader and partial alpha, return merging setter. + return pax_merge_index; + } } // Gets based on index instead of coordinates. // Does no bounds checking nor color conversion. -pax_col_t pax_get_index(const pax_buf_t *buf, int index) { - return buf->getter(buf, index); +pax_col_t pax_get_index(pax_buf_t const *buf, int index) { + return buf->getter(buf, index); } // Gets based on index instead of coordinates. // Does no bounds checking. -pax_col_t pax_get_index_conv(const pax_buf_t *buf, int index) { - return buf->buf2col(buf, buf->getter(buf, index)); +pax_col_t pax_get_index_conv(pax_buf_t const *buf, int index) { + return buf->buf2col(buf, buf->getter(buf, index)); } // Sets based on index instead of coordinates. // Does no bounds checking nor color conversion. void pax_set_index(pax_buf_t *buf, pax_col_t color, int index) { - buf->setter(buf, color, index); + buf->setter(buf, color, index); } // Sets based on index instead of coordinates. // Does no bounds checking. void pax_set_index_conv(pax_buf_t *buf, pax_col_t col, int index) { - buf->setter(buf, buf->col2buf(buf, col), index); + buf->setter(buf, buf->col2buf(buf, col), index); } // Merges based on index instead of coordinates. Does no bounds checking. void pax_merge_index(pax_buf_t *buf, pax_col_t col, int index) { - pax_col_t base = buf->buf2col(buf, buf->getter(buf, index)); - pax_col_t res = buf->col2buf(buf, pax_col_merge(base, col)); - buf->setter(buf, res, index); + pax_col_t base = buf->buf2col(buf, buf->getter(buf, index)); + pax_col_t res = buf->col2buf(buf, pax_col_merge(base, col)); + buf->setter(buf, res, index); } - /* ======= COLOR CONVERSION ====== */ // Get the correct color conversion methods for the buffer type. -void pax_get_col_conv(const pax_buf_t *buf, pax_col_conv_t *col2buf, pax_col_conv_t *buf2col) { - switch (buf->type) { - case (PAX_BUF_1_PAL): - *col2buf = pax_trunc_to_1; - *buf2col = pax_pal_lookup; - break; - - case (PAX_BUF_2_PAL): - *col2buf = pax_trunc_to_2; - *buf2col = pax_pal_lookup; - break; - - case (PAX_BUF_4_PAL): - *col2buf = pax_trunc_to_4; - *buf2col = pax_pal_lookup; - break; - - case (PAX_BUF_8_PAL): - *col2buf = pax_trunc_to_8; - *buf2col = pax_pal_lookup; - break; - - case (PAX_BUF_16_PAL): - *col2buf = pax_trunc_to_16; - *buf2col = pax_pal_lookup; - break; - - - case (PAX_BUF_1_GREY): - *col2buf = pax_col_to_1_grey; - *buf2col = pax_1_grey_to_col; - break; - - case (PAX_BUF_2_GREY): - *col2buf = pax_col_to_2_grey; - *buf2col = pax_2_grey_to_col; - break; - - case (PAX_BUF_4_GREY): - *col2buf = pax_col_to_4_grey; - *buf2col = pax_4_grey_to_col; - break; - - case (PAX_BUF_8_GREY): - *col2buf = pax_col_to_8_grey; - *buf2col = pax_8_grey_to_col; - break; - - - case (PAX_BUF_8_332RGB): - *col2buf = pax_col_to_332_rgb; - *buf2col = pax_332_rgb_to_col; - break; - - case (PAX_BUF_16_565RGB): - *col2buf = pax_col_to_565_rgb; - *buf2col = pax_565_rgb_to_col; - break; - - - case (PAX_BUF_4_1111ARGB): - *col2buf = pax_col_to_1111_argb; - *buf2col = pax_1111_argb_to_col; - break; - - case (PAX_BUF_8_2222ARGB): - *col2buf = pax_col_to_2222_argb; - *buf2col = pax_2222_argb_to_col; - break; - - case (PAX_BUF_16_4444ARGB): - *col2buf = pax_col_to_4444_argb; - *buf2col = pax_4444_argb_to_col; - break; - - case (PAX_BUF_32_8888ARGB): - *col2buf = pax_col_conv_dummy; - *buf2col = pax_col_conv_dummy; - break; - - } +void pax_get_col_conv(pax_buf_t const *buf, pax_col_conv_t *col2buf, pax_col_conv_t *buf2col) { + switch (buf->type) { + case (PAX_BUF_1_PAL): + *col2buf = pax_trunc_to_1; + *buf2col = pax_pal_lookup; + break; + + case (PAX_BUF_2_PAL): + *col2buf = pax_trunc_to_2; + *buf2col = pax_pal_lookup; + break; + + case (PAX_BUF_4_PAL): + *col2buf = pax_trunc_to_4; + *buf2col = pax_pal_lookup; + break; + + case (PAX_BUF_8_PAL): + *col2buf = pax_trunc_to_8; + *buf2col = pax_pal_lookup; + break; + + case (PAX_BUF_16_PAL): + *col2buf = pax_trunc_to_16; + *buf2col = pax_pal_lookup; + break; + + + case (PAX_BUF_1_GREY): + *col2buf = pax_col_to_1_grey; + *buf2col = pax_1_grey_to_col; + break; + + case (PAX_BUF_2_GREY): + *col2buf = pax_col_to_2_grey; + *buf2col = pax_2_grey_to_col; + break; + + case (PAX_BUF_4_GREY): + *col2buf = pax_col_to_4_grey; + *buf2col = pax_4_grey_to_col; + break; + + case (PAX_BUF_8_GREY): + *col2buf = pax_col_to_8_grey; + *buf2col = pax_8_grey_to_col; + break; + + + case (PAX_BUF_8_332RGB): + *col2buf = pax_col_to_332_rgb; + *buf2col = pax_332_rgb_to_col; + break; + + case (PAX_BUF_16_565RGB): + *col2buf = pax_col_to_565_rgb; + *buf2col = pax_565_rgb_to_col; + break; + + + case (PAX_BUF_4_1111ARGB): + *col2buf = pax_col_to_1111_argb; + *buf2col = pax_1111_argb_to_col; + break; + + case (PAX_BUF_8_2222ARGB): + *col2buf = pax_col_to_2222_argb; + *buf2col = pax_2222_argb_to_col; + break; + + case (PAX_BUF_16_4444ARGB): + *col2buf = pax_col_to_4444_argb; + *buf2col = pax_4444_argb_to_col; + break; + + case (PAX_BUF_32_8888ARGB): + *col2buf = pax_col_conv_dummy; + *buf2col = pax_col_conv_dummy; + break; + } } // Dummy color converter, returns color input directly. -pax_col_t pax_col_conv_dummy(const pax_buf_t *buf, pax_col_t color) { - return color; +pax_col_t pax_col_conv_dummy(pax_buf_t const *buf, pax_col_t color) { + return color; } // Truncates input to 1 bit. -pax_col_t pax_trunc_to_1(const pax_buf_t *buf, pax_col_t color) { - return color & 1; +pax_col_t pax_trunc_to_1(pax_buf_t const *buf, pax_col_t color) { + return color & 1; } // Truncates input to 2 bit. -pax_col_t pax_trunc_to_2(const pax_buf_t *buf, pax_col_t color) { - return color & 3; +pax_col_t pax_trunc_to_2(pax_buf_t const *buf, pax_col_t color) { + return color & 3; } // Truncates input to 4 bit. -pax_col_t pax_trunc_to_4(const pax_buf_t *buf, pax_col_t color) { - return color & 15; +pax_col_t pax_trunc_to_4(pax_buf_t const *buf, pax_col_t color) { + return color & 15; } // Truncates input to 8 bit. -pax_col_t pax_trunc_to_8(const pax_buf_t *buf, pax_col_t color) { - return color & 255; +pax_col_t pax_trunc_to_8(pax_buf_t const *buf, pax_col_t color) { + return color & 255; } // Truncates input to 16 bit. -pax_col_t pax_trunc_to_16(const pax_buf_t *buf, pax_col_t color) { - return color & 65535; +pax_col_t pax_trunc_to_16(pax_buf_t const *buf, pax_col_t color) { + return color & 65535; } // Converts ARGB to 1-bit greyscale (AKA black/white). -pax_col_t pax_col_to_1_grey(const pax_buf_t *buf, pax_col_t color) { - uint_fast16_t total = (color & 0x0000ff) - + ((color & 0x00ff00) >> 8) - + ((color & 0xff0000) >> 16); - return total > 128*3; +pax_col_t pax_col_to_1_grey(pax_buf_t const *buf, pax_col_t color) { + uint_fast16_t total = (color & 0x0000ff) + ((color & 0x00ff00) >> 8) + ((color & 0xff0000) >> 16); + return total > 128 * 3; } // Converts ARGB to 2-bit greyscale. -pax_col_t pax_col_to_2_grey(const pax_buf_t *buf, pax_col_t color) { - uint_fast8_t total = ((color & 0x0000c0) >> 6) - + ((color & 0x00c000) >> 14) - + ((color & 0xc00000) >> 22); - return total / 3; +pax_col_t pax_col_to_2_grey(pax_buf_t const *buf, pax_col_t color) { + uint_fast8_t total = ((color & 0x0000c0) >> 6) + ((color & 0x00c000) >> 14) + ((color & 0xc00000) >> 22); + return total / 3; } // Converts ARGB to 4-bit greyscale. -pax_col_t pax_col_to_4_grey(const pax_buf_t *buf, pax_col_t color) { - uint_fast8_t total = ((color & 0x0000f0) >> 4) - + ((color & 0x00f000) >> 12) - + ((color & 0xf00000) >> 20); - return total / 3; +pax_col_t pax_col_to_4_grey(pax_buf_t const *buf, pax_col_t color) { + uint_fast8_t total = ((color & 0x0000f0) >> 4) + ((color & 0x00f000) >> 12) + ((color & 0xf00000) >> 20); + return total / 3; } // Converts ARGB to 8-bit greyscale. -pax_col_t pax_col_to_8_grey(const pax_buf_t *buf, pax_col_t color) { - uint_fast16_t total = (color & 0x0000ff) - + ((color & 0x00ff00) >> 8) - + ((color & 0xff0000) >> 16); - return total / 3; +pax_col_t pax_col_to_8_grey(pax_buf_t const *buf, pax_col_t color) { + uint_fast16_t total = (color & 0x0000ff) + ((color & 0x00ff00) >> 8) + ((color & 0xff0000) >> 16); + return total / 3; } // Converts ARGB to 3, 3, 2 bit RGB. -pax_col_t pax_col_to_332_rgb(const pax_buf_t *buf, pax_col_t color) { - // 8BPP 332-RGB - // From: Aaaa aaaa Rrrr rrrr Gggg gggg Bbbb bbbb - // To: RrrG ggBb - uint16_t value = ((color >> 16) & 0xe0) | ((color >> 11) & 0x1c) | ((color >> 6) & 0x03); - return value; +pax_col_t pax_col_to_332_rgb(pax_buf_t const *buf, pax_col_t color) { + // 8BPP 332-RGB + // From: Aaaa aaaa Rrrr rrrr Gggg gggg Bbbb bbbb + // To: RrrG ggBb + uint16_t value = ((color >> 16) & 0xe0) | ((color >> 11) & 0x1c) | ((color >> 6) & 0x03); + return value; } // Converts ARGB to 5, 6, 5 bit RGB. -pax_col_t pax_col_to_565_rgb(const pax_buf_t *buf, pax_col_t color) { - // 16BPP 565-RGB - // From: Aaaa aaaa Rrrr rrrr Gggg gggg Bbbb bbbb - // To: Rrrr rGgg gggB bbbb - return ((color >> 8) & 0xf800) | ((color >> 5) & 0x07e0) | ((color >> 3) & 0x001f); +pax_col_t pax_col_to_565_rgb(pax_buf_t const *buf, pax_col_t color) { + // 16BPP 565-RGB + // From: Aaaa aaaa Rrrr rrrr Gggg gggg Bbbb bbbb + // To: Rrrr rGgg gggB bbbb + return ((color >> 8) & 0xf800) | ((color >> 5) & 0x07e0) | ((color >> 3) & 0x001f); } // Converts ARGB to 1 bit per channel ARGB. -pax_col_t pax_col_to_1111_argb(const pax_buf_t *buf, pax_col_t color) { - // 4BPP 1111-ARGB - // From: Aaaa aaaa Rrrr rrrr Gggg gggg Bbbb bbbb - // To: ARGB - uint16_t value = ((color >> 28) & 0x8) | ((color >> 21) & 0x4) | ((color >> 14) & 0x2) | ((color >> 7) & 0x1); - return value; +pax_col_t pax_col_to_1111_argb(pax_buf_t const *buf, pax_col_t color) { + // 4BPP 1111-ARGB + // From: Aaaa aaaa Rrrr rrrr Gggg gggg Bbbb bbbb + // To: ARGB + uint16_t value = ((color >> 28) & 0x8) | ((color >> 21) & 0x4) | ((color >> 14) & 0x2) | ((color >> 7) & 0x1); + return value; } // Converts ARGB to 2 bit per channel ARGB. -pax_col_t pax_col_to_2222_argb(const pax_buf_t *buf, pax_col_t color) { - // 8BPP 2222-ARGB - // From: Aaaa aaaa Rrrr rrrr Gggg gggg Bbbb bbbb - // To: AaRr GgBb - uint16_t value = ((color >> 24) & 0xc0) | ((color >> 18) & 0x30) | ((color >> 12) & 0x0c) | ((color >> 6) & 0x03); - return value; +pax_col_t pax_col_to_2222_argb(pax_buf_t const *buf, pax_col_t color) { + // 8BPP 2222-ARGB + // From: Aaaa aaaa Rrrr rrrr Gggg gggg Bbbb bbbb + // To: AaRr GgBb + uint16_t value = ((color >> 24) & 0xc0) | ((color >> 18) & 0x30) | ((color >> 12) & 0x0c) | ((color >> 6) & 0x03); + return value; } // Converts ARGB to 4 bit per channel ARGB. -pax_col_t pax_col_to_4444_argb(const pax_buf_t *buf, pax_col_t color) { - // 16BPP 4444-ARGB - // From: Aaaa aaaa Rrrr rrrr Gggg gggg Bbbb bbbb - // To: Aaaa Rrrr Gggg Bbbb - return ((color >> 16) & 0xf000) | ((color >> 12) & 0x0f00) | ((color >> 8) & 0x00f0) | ((color >> 4) & 0x000f); +pax_col_t pax_col_to_4444_argb(pax_buf_t const *buf, pax_col_t color) { + // 16BPP 4444-ARGB + // From: Aaaa aaaa Rrrr rrrr Gggg gggg Bbbb bbbb + // To: Aaaa Rrrr Gggg Bbbb + return ((color >> 16) & 0xf000) | ((color >> 12) & 0x0f00) | ((color >> 8) & 0x00f0) | ((color >> 4) & 0x000f); } // Performs a palette lookup based on the input. -pax_col_t pax_pal_lookup(const pax_buf_t *buf, pax_col_t index) { - return (index >= buf->palette_size) - ? *buf->pallette - : buf->pallette[index]; +pax_col_t pax_pal_lookup(pax_buf_t const *buf, pax_col_t index) { + return (index >= buf->palette_size) ? *buf->pallette : buf->pallette[index]; } // Converts 1-bit greyscale (AKA black/white) to ARGB. -pax_col_t pax_1_grey_to_col(const pax_buf_t *buf, pax_col_t color) { - return color ? 0xffffffff : 0xff000000; +pax_col_t pax_1_grey_to_col(pax_buf_t const *buf, pax_col_t color) { + return color ? 0xffffffff : 0xff000000; } // Converts 2-bit greyscale to ARGB. -pax_col_t pax_2_grey_to_col(const pax_buf_t *buf, pax_col_t color) { - static const pax_col_t arr[] = { - 0xff000000, - 0xff555555, - 0xffaaaaaa, - 0xffffffff, - }; - return arr[color]; +pax_col_t pax_2_grey_to_col(pax_buf_t const *buf, pax_col_t color) { + static pax_col_t const arr[] = { + 0xff000000, + 0xff555555, + 0xffaaaaaa, + 0xffffffff, + }; + return arr[color]; } // Converts 4-bit greyscale to ARGB. -pax_col_t pax_4_grey_to_col(const pax_buf_t *buf, pax_col_t color) { - return 0xff000000 | (color * 0x00111111); +pax_col_t pax_4_grey_to_col(pax_buf_t const *buf, pax_col_t color) { + return 0xff000000 | (color * 0x00111111); } // Converts 8-bit greyscale to ARGB. -pax_col_t pax_8_grey_to_col(const pax_buf_t *buf, pax_col_t color) { - return 0xff000000 | (color * 0x00010101); +pax_col_t pax_8_grey_to_col(pax_buf_t const *buf, pax_col_t color) { + return 0xff000000 | (color * 0x00010101); } // Converts 3, 3, 2 bit RGB to ARGB. -pax_col_t pax_332_rgb_to_col(const pax_buf_t *buf, pax_col_t value) { - // 8BPP 332-RGB - // From: RrrG ggBb - // To: .... .... Rrr. .... Ggg. .... .... .... - // Add: .... .... ...R rrRr ...Gg gGg .... .... - // Add: .... .... .... .... .... .... BbBb BbBb - pax_col_t color = ((value << 16) & 0x00e00000) | ((value << 11) & 0x0000e000); - color |= (color >> 3) | ((color >> 6) & 0x000f0f00); - pax_col_t temp = (value & 0x03); - temp |= temp << 2; - color |= temp | (temp << 4); - return color | 0xff000000; +pax_col_t pax_332_rgb_to_col(pax_buf_t const *buf, pax_col_t value) { + // 8BPP 332-RGB + // From: RrrG ggBb + // To: .... .... Rrr. .... Ggg. .... .... .... + // Add: .... .... ...R rrRr ...Gg gGg .... .... + // Add: .... .... .... .... .... .... BbBb BbBb + pax_col_t color = ((value << 16) & 0x00e00000) | ((value << 11) & 0x0000e000); + color |= (color >> 3) | ((color >> 6) & 0x000f0f00); + pax_col_t temp = (value & 0x03); + temp |= temp << 2; + color |= temp | (temp << 4); + return color | 0xff000000; } // Converts 5, 6, 5 bit RGB to ARGB. -pax_col_t pax_565_rgb_to_col(const pax_buf_t *buf, pax_col_t value) { - // 16BPP 565-RGB - // From: Rrrr rGgg gggB bbbb - // To: .... .... Rrrr r... Gggg gg.. Bbbb b... - // Add: .... .... .... .Rrr .... ..Gg .... .Bbb - // Take the existing information. - pax_col_t color = ((value << 8) & 0x00f80000) | ((value << 5) & 0x0000fc00) | ((value << 3) & 0x000000f8); - // Now, fill in some missing bits. - color |= ((value << 3) & 0x00070000) | ((value >> 1) & 0x00000300) | ((value >> 2) & 0x00000007); - return color | 0xff000000; +pax_col_t pax_565_rgb_to_col(pax_buf_t const *buf, pax_col_t value) { + // 16BPP 565-RGB + // From: Rrrr rGgg gggB bbbb + // To: .... .... Rrrr r... Gggg gg.. Bbbb b... + // Add: .... .... .... .Rrr .... ..Gg .... .Bbb + // Take the existing information. + pax_col_t color = ((value << 8) & 0x00f80000) | ((value << 5) & 0x0000fc00) | ((value << 3) & 0x000000f8); + // Now, fill in some missing bits. + color |= ((value << 3) & 0x00070000) | ((value >> 1) & 0x00000300) | ((value >> 2) & 0x00000007); + return color | 0xff000000; } // Converts 1 bit per channel ARGB to ARGB. -pax_col_t pax_1111_argb_to_col(const pax_buf_t *buf, pax_col_t value) { - // 4BPP 1111-ARGB - // From: ARGB - // To: Aaaa aaaa Rrrr rrrr Gggg gggg Bbbb bbbb - pax_col_t color = ((value << 28) & 0x80000000) | ((value << 21) & 0x00800000) | ((value << 14) & 0x00008000) | ((value << 7) & 0x00000080); - color |= color >> 1; - color |= color >> 2; - color |= color >> 4; - return color; +pax_col_t pax_1111_argb_to_col(pax_buf_t const *buf, pax_col_t value) { + // 4BPP 1111-ARGB + // From: ARGB + // To: Aaaa aaaa Rrrr rrrr Gggg gggg Bbbb bbbb + pax_col_t color = ((value << 28) & 0x80000000) | ((value << 21) & 0x00800000) | ((value << 14) & 0x00008000) | + ((value << 7) & 0x00000080); + color |= color >> 1; + color |= color >> 2; + color |= color >> 4; + return color; } // Converts 2 bit per channel ARGB to ARGB. -pax_col_t pax_2222_argb_to_col(const pax_buf_t *buf, pax_col_t value) { - // 8BPP 2222-ARGB - // From: AaRr GgBb - // To: Aaaa aaaa Rrrr rrrr Gggg gggg Bbbb bbbb - pax_col_t color = ((value << 24) & 0xc0000000) | ((value << 18) & 0x00c00000) | ((value << 12) & 0x0000c000) | ((value << 6) & 0x000000c0); - color |= color >> 2; - color |= color >> 4; - return color; +pax_col_t pax_2222_argb_to_col(pax_buf_t const *buf, pax_col_t value) { + // 8BPP 2222-ARGB + // From: AaRr GgBb + // To: Aaaa aaaa Rrrr rrrr Gggg gggg Bbbb bbbb + pax_col_t color = ((value << 24) & 0xc0000000) | ((value << 18) & 0x00c00000) | ((value << 12) & 0x0000c000) | + ((value << 6) & 0x000000c0); + color |= color >> 2; + color |= color >> 4; + return color; } // Converts 4 bit per channel ARGB to ARGB. -pax_col_t pax_4444_argb_to_col(const pax_buf_t *buf, pax_col_t value) { - // 16BPP 4444-ARGB - // From: Aaaa Rrrr Gggg Bbbb - // To: Aaaa .... Rrrr .... Gggg .... Bbbb .... - // Add: .... Aaaa .... Rrrr .... Gggg .... Bbbb - pax_col_t color = ((value << 16) & 0xf0000000) | ((value << 12) & 0x00f00000) | ((value << 8) & 0x0000f000) | ((value << 4) & 0x000000f0); - // Now fill in some missing bits. - color |= color >> 4; - return color; +pax_col_t pax_4444_argb_to_col(pax_buf_t const *buf, pax_col_t value) { + // 16BPP 4444-ARGB + // From: Aaaa Rrrr Gggg Bbbb + // To: Aaaa .... Rrrr .... Gggg .... Bbbb .... + // Add: .... Aaaa .... Rrrr .... Gggg .... Bbbb + pax_col_t color = ((value << 16) & 0xf0000000) | ((value << 12) & 0x00f00000) | ((value << 8) & 0x0000f000) | + ((value << 4) & 0x000000f0); + // Now fill in some missing bits. + color |= color >> 4; + return color; } diff --git a/src/pax_shaders.c b/src/pax_shaders.c index dc529d4..85c65b8 100644 --- a/src/pax_shaders.c +++ b/src/pax_shaders.c @@ -1,33 +1,13 @@ -/* - MIT License - - Copyright (c) 2021-2023 Julian Scheffers - - Permission is hereby granted, free of charge, to any person obtaining a copy - of this software and associated documentation files (the "Software"), to deal - in the Software without restriction, including without limitation the rights - to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - copies of the Software, and to permit persons to whom the Software is - furnished to do so, subject to the following conditions: - - The above copyright notice and this permission notice shall be included in all - copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - SOFTWARE. -*/ + +// SPDX-License-Identifier: MIT #include "pax_shaders.h" + #include "pax_internal.h" static inline float pax_interp_cubic(float a) { - // Cubic interpolation: y = -2x³ + 3x² - return -2*a*a*a + 3*a*a; + // Cubic interpolation: y = -2x³ + 3x² + return -2 * a * a * a + 3 * a * a; } #if PAX_DO_BICUBIC @@ -39,322 +19,330 @@ static inline float pax_interp_cubic(float a) { // Texture shader for multi-bpp bitmap fonts on palette type buffers. pax_col_t pax_shader_font_bmp_hi_pal(pax_col_t base, pax_col_t existing, int x, int y, float u, float v, void *args0) { - pax_font_bmp_args_t *args = args0; - - // Extract texture coords. - int glyph_x = u; - int glyph_y = v; - if (glyph_x >= args->glyph_w) glyph_x = args->glyph_w - 1; - if (glyph_y >= args->glyph_h) glyph_y = args->glyph_h - 1; - - // Find byte index of pixel. - size_t glyph_index = - glyph_x / args->ppb - + args->glyph_y_mul * glyph_y; - - // Extract pixel data. - uint16_t val = - // Get bitmap at byte index. - (args->bitmap[glyph_index] - // Align the bits. - >> args->bpp * (glyph_x & (args->index_mask))) - // Mask out the ones we want. - & args->mask; - - // Compute alpha. - uint8_t alpha = val * 255 / (args->mask); - - // Pick the output value. - return alpha <= 127 ? existing : base; + pax_font_bmp_args_t *args = args0; + + // Extract texture coords. + int glyph_x = u; + int glyph_y = v; + if (glyph_x >= args->glyph_w) + glyph_x = args->glyph_w - 1; + if (glyph_y >= args->glyph_h) + glyph_y = args->glyph_h - 1; + + // Find byte index of pixel. + size_t glyph_index = glyph_x / args->ppb + args->glyph_y_mul * glyph_y; + + // Extract pixel data. + uint16_t val = + // Get bitmap at byte index. + (args->bitmap[glyph_index] + // Align the bits. + >> args->bpp * (glyph_x & (args->index_mask))) + // Mask out the ones we want. + & args->mask; + + // Compute alpha. + uint8_t alpha = val * 255 / (args->mask); + + // Pick the output value. + return alpha <= 127 ? existing : base; } // Texture shader for multi-bpp bitmap fonts. pax_col_t pax_shader_font_bmp_hi(pax_col_t base, pax_col_t existing, int x, int y, float u, float v, void *args0) { - pax_font_bmp_args_t *args = args0; - - // Extract texture coords. - int glyph_x = u; - int glyph_y = v; - if (glyph_x >= args->glyph_w) glyph_x = args->glyph_w - 1; - if (glyph_y >= args->glyph_h) glyph_y = args->glyph_h - 1; - - // Find byte index of pixel. - size_t glyph_index = - glyph_x / args->ppb - + args->glyph_y_mul * glyph_y; - - // Extract pixel data. - uint16_t val = - // Get bitmap at byte index. - (args->bitmap[glyph_index] - // Align the bits. - >> args->bpp * (glyph_x & (args->index_mask))) - // Mask out the ones we want. - & args->mask; - - // Compute alpha. - uint8_t alpha = val * 255 / (args->mask); - - // Interpolate the output color. - uint8_t coeff8 = pax_lerp(alpha, 0, base >> 24); - return pax_col_lerp(coeff8, existing | 0xff000000, base); + pax_font_bmp_args_t *args = args0; + + // Extract texture coords. + int glyph_x = u; + int glyph_y = v; + if (glyph_x >= args->glyph_w) + glyph_x = args->glyph_w - 1; + if (glyph_y >= args->glyph_h) + glyph_y = args->glyph_h - 1; + + // Find byte index of pixel. + size_t glyph_index = glyph_x / args->ppb + args->glyph_y_mul * glyph_y; + + // Extract pixel data. + uint16_t val = + // Get bitmap at byte index. + (args->bitmap[glyph_index] + // Align the bits. + >> args->bpp * (glyph_x & (args->index_mask))) + // Mask out the ones we want. + & args->mask; + + // Compute alpha. + uint8_t alpha = val * 255 / (args->mask); + + // Interpolate the output color. + uint8_t coeff8 = pax_lerp(alpha, 0, base >> 24); + return pax_col_lerp(coeff8, existing | 0xff000000, base); } // Texture shader for multi-bpp bitmap fonts with linear interpolation. pax_col_t pax_shader_font_bmp_hi_aa(pax_col_t base, pax_col_t existing, int x, int y, float u, float v, void *args0) { - pax_font_bmp_args_t *args = args0; - - // Correct UVs for the offset caused by filtering. - u -= 0.5; - v -= 0.5; - // Get texture coords, round down instead of round to 0. - int glyph_x = floorf(u); - int glyph_y = floorf(v); - // Get subpixel coords. - float dx = pax_interp_value(u - glyph_x); - float dy = pax_interp_value(v - glyph_y); - - // Coords out of bounds fix. - if (glyph_x >= args->glyph_w) glyph_x = args->glyph_w - 1; - if (glyph_y >= args->glyph_h) glyph_y = args->glyph_h - 1; - - // Find byte indicies for four neighbouring pixels. - size_t glyph_index_0 = glyph_x / args->ppb + args->glyph_y_mul * glyph_y; - glyph_y ++; - size_t glyph_index_2 = glyph_x / args->ppb + args->glyph_y_mul * glyph_y; - glyph_x ++; - size_t glyph_index_3 = glyph_x / args->ppb + args->glyph_y_mul * glyph_y; - glyph_y --; - size_t glyph_index_1 = glyph_x / args->ppb + args->glyph_y_mul * glyph_y; - glyph_x --; - - const uint8_t *arr = args->bitmap; - uint8_t glyph_bit_0 = 0; - uint8_t glyph_bit_1 = 0; - uint8_t glyph_bit_2 = 0; - uint8_t glyph_bit_3 = 0; - - // Top left bit. - if (glyph_x >= 0 && glyph_y >= 0) { - glyph_bit_0 = (arr[glyph_index_0] >> args->bpp * (glyph_x & (args->index_mask))) & args->mask; - } - - // Top right bit. - if (glyph_x < args->glyph_w - 1 && glyph_y >= 0) { - glyph_bit_1 = (arr[glyph_index_1] >> args->bpp * ((glyph_x + 1) & (args->index_mask))) & args->mask; - } - - // Bottom left bit. - if (glyph_x >= 0 && glyph_y < args->glyph_h - 1) { - glyph_bit_2 = (arr[glyph_index_2] >> args->bpp * (glyph_x & (args->index_mask))) & args->mask; - } - - // Bottom right bit. - if (glyph_x < args->glyph_w - 1 && glyph_y < args->glyph_h - 1) { - glyph_bit_3 = (arr[glyph_index_3] >> args->bpp * ((glyph_x + 1) & (args->index_mask))) & args->mask; - } - - // Turn them into floats. - float c0 = glyph_bit_0 / (float) args->mask; - float c1 = glyph_bit_1 / (float) args->mask; - float c2 = glyph_bit_2 / (float) args->mask; - float c3 = glyph_bit_3 / (float) args->mask; - - // First stage interpolation. - float c4 = c0 + (c1 - c0) * dx; - float c5 = c2 + (c3 - c2) * dx; - - // Second stage interpolation. - float coeff = c4 + (c5 - c4) * dy; - - // Interpolate the output color. - uint8_t coeff8 = coeff * (base >> 24); - return pax_col_lerp(coeff8, existing | 0xff000000, base); + pax_font_bmp_args_t *args = args0; + + // Correct UVs for the offset caused by filtering. + u -= 0.5; + v -= 0.5; + // Get texture coords, round down instead of round to 0. + int glyph_x = floorf(u); + int glyph_y = floorf(v); + // Get subpixel coords. + float dx = pax_interp_value(u - glyph_x); + float dy = pax_interp_value(v - glyph_y); + + // Coords out of bounds fix. + if (glyph_x >= args->glyph_w) + glyph_x = args->glyph_w - 1; + if (glyph_y >= args->glyph_h) + glyph_y = args->glyph_h - 1; + + // Find byte indicies for four neighbouring pixels. + size_t glyph_index_0 = glyph_x / args->ppb + args->glyph_y_mul * glyph_y; + glyph_y++; + size_t glyph_index_2 = glyph_x / args->ppb + args->glyph_y_mul * glyph_y; + glyph_x++; + size_t glyph_index_3 = glyph_x / args->ppb + args->glyph_y_mul * glyph_y; + glyph_y--; + size_t glyph_index_1 = glyph_x / args->ppb + args->glyph_y_mul * glyph_y; + glyph_x--; + + uint8_t const *arr = args->bitmap; + uint8_t glyph_bit_0 = 0; + uint8_t glyph_bit_1 = 0; + uint8_t glyph_bit_2 = 0; + uint8_t glyph_bit_3 = 0; + + // Top left bit. + if (glyph_x >= 0 && glyph_y >= 0) { + glyph_bit_0 = (arr[glyph_index_0] >> args->bpp * (glyph_x & (args->index_mask))) & args->mask; + } + + // Top right bit. + if (glyph_x < args->glyph_w - 1 && glyph_y >= 0) { + glyph_bit_1 = (arr[glyph_index_1] >> args->bpp * ((glyph_x + 1) & (args->index_mask))) & args->mask; + } + + // Bottom left bit. + if (glyph_x >= 0 && glyph_y < args->glyph_h - 1) { + glyph_bit_2 = (arr[glyph_index_2] >> args->bpp * (glyph_x & (args->index_mask))) & args->mask; + } + + // Bottom right bit. + if (glyph_x < args->glyph_w - 1 && glyph_y < args->glyph_h - 1) { + glyph_bit_3 = (arr[glyph_index_3] >> args->bpp * ((glyph_x + 1) & (args->index_mask))) & args->mask; + } + + // Turn them into floats. + float c0 = glyph_bit_0 / (float)args->mask; + float c1 = glyph_bit_1 / (float)args->mask; + float c2 = glyph_bit_2 / (float)args->mask; + float c3 = glyph_bit_3 / (float)args->mask; + + // First stage interpolation. + float c4 = c0 + (c1 - c0) * dx; + float c5 = c2 + (c3 - c2) * dx; + + // Second stage interpolation. + float coeff = c4 + (c5 - c4) * dy; + + // Interpolate the output color. + uint8_t coeff8 = coeff * (base >> 24); + return pax_col_lerp(coeff8, existing | 0xff000000, base); } // Texture shader for 1bpp bitmap fonts on palette type buffers. pax_col_t pax_shader_font_bmp_pal(pax_col_t tint, pax_col_t existing, int x, int y, float u, float v, void *args0) { - pax_font_bmp_args_t *args = args0; - - // Get texture coords. - int glyph_x = u; - int glyph_y = v; - // Coords out of bounds fix. - if (glyph_x >= args->glyph_w) glyph_x = args->glyph_w - 1; - if (glyph_y >= args->glyph_h) glyph_y = args->glyph_h - 1; - // Get byte index of pixel. - size_t glyph_index = glyph_x / 8 + args->glyph_y_mul * glyph_y; - - // Extract the pixel data. - bool pixdat = args->bitmap[glyph_index] & (1 << (glyph_x & 7)); - return pixdat ? tint : existing; + pax_font_bmp_args_t *args = args0; + + // Get texture coords. + int glyph_x = u; + int glyph_y = v; + // Coords out of bounds fix. + if (glyph_x >= args->glyph_w) + glyph_x = args->glyph_w - 1; + if (glyph_y >= args->glyph_h) + glyph_y = args->glyph_h - 1; + // Get byte index of pixel. + size_t glyph_index = glyph_x / 8 + args->glyph_y_mul * glyph_y; + + // Extract the pixel data. + bool pixdat = args->bitmap[glyph_index] & (1 << (glyph_x & 7)); + return pixdat ? tint : existing; } // Texture shader for 1bpp bitmap fonts. pax_col_t pax_shader_font_bmp(pax_col_t tint, pax_col_t existing, int x, int y, float u, float v, void *args0) { - pax_font_bmp_args_t *args = args0; - - // Get texture coords. - int glyph_x = u; - int glyph_y = v; - // Coords out of bounds fix. - if (glyph_x >= args->glyph_w) glyph_x = args->glyph_w - 1; - if (glyph_y >= args->glyph_h) glyph_y = args->glyph_h - 1; - // Get byte index of pixel. - size_t glyph_index = glyph_x / 8 + args->glyph_y_mul * glyph_y; - - // Extract the pixel data. - bool pixdat = args->bitmap[glyph_index] & (1 << (glyph_x & 7)); - return pixdat ? pax_col_merge(existing, tint) : existing; + pax_font_bmp_args_t *args = args0; + + // Get texture coords. + int glyph_x = u; + int glyph_y = v; + // Coords out of bounds fix. + if (glyph_x >= args->glyph_w) + glyph_x = args->glyph_w - 1; + if (glyph_y >= args->glyph_h) + glyph_y = args->glyph_h - 1; + // Get byte index of pixel. + size_t glyph_index = glyph_x / 8 + args->glyph_y_mul * glyph_y; + + // Extract the pixel data. + bool pixdat = args->bitmap[glyph_index] & (1 << (glyph_x & 7)); + return pixdat ? pax_col_merge(existing, tint) : existing; } // Texture shader for 1bpp bitmap fonts with linear interpolation. pax_col_t pax_shader_font_bmp_aa(pax_col_t base, pax_col_t existing, int x, int y, float u, float v, void *args0) { - pax_font_bmp_args_t *args = args0; - - // Correct UVs for the offset caused by filtering. - u -= 0.5; - v -= 0.5; - // Get texture coords, round down instead of round to 0. - int glyph_x = floorf(u); - int glyph_y = floorf(v); - // Get subpixel coords. - float dx = pax_interp_value(u - glyph_x); - float dy = pax_interp_value(v - glyph_y); - - // Coords out of bounds fix. - if (glyph_x >= args->glyph_w) glyph_x = args->glyph_w - 1; - if (glyph_y >= args->glyph_h) glyph_y = args->glyph_h - 1; - - // Find byte indicies for four neighbouring pixels. - size_t glyph_index_0 = glyph_x / 8 + args->glyph_y_mul * glyph_y; - glyph_y ++; - size_t glyph_index_2 = glyph_x / 8 + args->glyph_y_mul * glyph_y; - glyph_x ++; - size_t glyph_index_3 = glyph_x / 8 + args->glyph_y_mul * glyph_y; - glyph_y --; - size_t glyph_index_1 = glyph_x / 8 + args->glyph_y_mul * glyph_y; - glyph_x --; - - const uint8_t *arr = args->bitmap; - bool glyph_bit_0 = 0; - bool glyph_bit_1 = 0; - bool glyph_bit_2 = 0; - bool glyph_bit_3 = 0; - - // Top left bit. - if (glyph_x >= 0 && glyph_y >= 0) { - glyph_bit_0 = arr[glyph_index_0] & (1 << (glyph_x & 7)); - } - - // Top right bit. - if (glyph_x < args->glyph_w - 1 && glyph_y >= 0) { - glyph_bit_1 = arr[glyph_index_1] & (1 << ((glyph_x+1) & 7)); - } - - // Bottom left bit. - if (glyph_x >= 0 && glyph_y < args->glyph_h - 1) { - glyph_bit_2 = arr[glyph_index_2] & (1 << (glyph_x & 7)); - } - - // Bottom right bit. - if (glyph_x < args->glyph_w - 1 && glyph_y < args->glyph_h - 1) { - glyph_bit_3 = arr[glyph_index_3] & (1 << ((glyph_x+1) & 7)); - } - - // Simple conversion to float. - float c0 = glyph_bit_0; - float c1 = glyph_bit_1; - float c2 = glyph_bit_2; - float c3 = glyph_bit_3; - - // First stage interpolation. - float c4 = c0 + (c1 - c0) * dx; - float c5 = c2 + (c3 - c2) * dx; - - // Second stage interpolation. - float coeff = c4 + (c5 - c4) * dy; - - // Interpolate the output color. - uint8_t coeff8 = coeff * (base >> 24); - return pax_col_lerp(coeff8, existing | 0xff000000, base); + pax_font_bmp_args_t *args = args0; + + // Correct UVs for the offset caused by filtering. + u -= 0.5; + v -= 0.5; + // Get texture coords, round down instead of round to 0. + int glyph_x = floorf(u); + int glyph_y = floorf(v); + // Get subpixel coords. + float dx = pax_interp_value(u - glyph_x); + float dy = pax_interp_value(v - glyph_y); + + // Coords out of bounds fix. + if (glyph_x >= args->glyph_w) + glyph_x = args->glyph_w - 1; + if (glyph_y >= args->glyph_h) + glyph_y = args->glyph_h - 1; + + // Find byte indicies for four neighbouring pixels. + size_t glyph_index_0 = glyph_x / 8 + args->glyph_y_mul * glyph_y; + glyph_y++; + size_t glyph_index_2 = glyph_x / 8 + args->glyph_y_mul * glyph_y; + glyph_x++; + size_t glyph_index_3 = glyph_x / 8 + args->glyph_y_mul * glyph_y; + glyph_y--; + size_t glyph_index_1 = glyph_x / 8 + args->glyph_y_mul * glyph_y; + glyph_x--; + + uint8_t const *arr = args->bitmap; + bool glyph_bit_0 = 0; + bool glyph_bit_1 = 0; + bool glyph_bit_2 = 0; + bool glyph_bit_3 = 0; + + // Top left bit. + if (glyph_x >= 0 && glyph_y >= 0) { + glyph_bit_0 = arr[glyph_index_0] & (1 << (glyph_x & 7)); + } + + // Top right bit. + if (glyph_x < args->glyph_w - 1 && glyph_y >= 0) { + glyph_bit_1 = arr[glyph_index_1] & (1 << ((glyph_x + 1) & 7)); + } + + // Bottom left bit. + if (glyph_x >= 0 && glyph_y < args->glyph_h - 1) { + glyph_bit_2 = arr[glyph_index_2] & (1 << (glyph_x & 7)); + } + + // Bottom right bit. + if (glyph_x < args->glyph_w - 1 && glyph_y < args->glyph_h - 1) { + glyph_bit_3 = arr[glyph_index_3] & (1 << ((glyph_x + 1) & 7)); + } + + // Simple conversion to float. + float c0 = glyph_bit_0; + float c1 = glyph_bit_1; + float c2 = glyph_bit_2; + float c3 = glyph_bit_3; + + // First stage interpolation. + float c4 = c0 + (c1 - c0) * dx; + float c5 = c2 + (c3 - c2) * dx; + + // Second stage interpolation. + float coeff = c4 + (c5 - c4) * dy; + + // Interpolate the output color. + uint8_t coeff8 = coeff * (base >> 24); + return pax_col_lerp(coeff8, existing | 0xff000000, base); } // Texture shader without interpolation. pax_col_t pax_shader_texture(pax_col_t tint, pax_col_t existing, int x, int y, float u, float v, void *args) { - // Pointer cast to texture thingy. - const pax_buf_t *image = (const pax_buf_t *) args; - // Simply get a pixel. - pax_col_t color = pax_get_pixel(image, u*image->width, v*image->height); - // And return it. - if (tint != 0xffffffff) { - color = pax_col_tint(color, tint); - } - if ((color | 0x00ffffff) == 0xffffffff) { - return pax_col_merge(existing, color); - } else { - return color; - } + // Pointer cast to texture thingy. + pax_buf_t const *image = (pax_buf_t const *)args; + // Simply get a pixel. + pax_col_t color = pax_get_pixel(image, u * image->width, v * image->height); + // And return it. + if (tint != 0xffffffff) { + color = pax_col_tint(color, tint); + } + if ((color | 0x00ffffff) == 0xffffffff) { + return pax_col_merge(existing, color); + } else { + return color; + } } // Texture shader with interpolation. pax_col_t pax_shader_texture_aa(pax_col_t tint, pax_col_t existing, int x, int y, float u, float v, void *args) { - // Pointer cast to texture thingy. - const pax_buf_t *image = (const pax_buf_t *) args; - - // Remap UVs. - u *= image->width; - v *= image->height; - // Correct UVs for the offset caused by filtering. - u -= 0.5; - v -= 0.5; - - // Get texture coords, round down instead of round to 0. - int tex_x = floorf(u); - int tex_y = floorf(v); - // Get subpixel coords. - float dx = pax_interp_value(u - tex_x); - float dy = pax_interp_value(v - tex_y); - - // Get four pixels. - pax_col_union_t col0 = { .col = pax_get_pixel(image, tex_x, tex_y) }; - pax_col_union_t col1 = { .col = pax_get_pixel(image, tex_x+1, tex_y) }; - pax_col_union_t col2 = { .col = pax_get_pixel(image, tex_x+1, tex_y+1) }; - pax_col_union_t col3 = { .col = pax_get_pixel(image, tex_x, tex_y+1) }; - - // Compute interpolation coefficients. - uint_fast32_t coeffx = dx*256; - uint_fast32_t coeffy = dy*256; - uint_fast32_t coeff0 = (256-coeffx) * (256-coeffy); - uint_fast32_t coeff1 = coeffx * (256-coeffy); - uint_fast32_t coeff2 = coeffx * coeffy; - uint_fast32_t coeff3 = (256-coeffx) * coeffy; - - pax_col_union_t col_out; - - // Interpolate alpha. - bool do_alpha = (col0.a & col1.a & col2.a & col3.a) != 255; - if (do_alpha) { - col_out.a = (col0.a*coeff0 + col1.a*coeff1 + col2.a*coeff2 + col3.a*coeff3) >> 16; - } else { - col_out.a = 255; - } - // Interpolate RGB. - col_out.r = (col0.r*coeff0 + col1.r*coeff1 + col2.r*coeff2 + col3.r*coeff3) >> 16; - col_out.g = (col0.g*coeff0 + col1.g*coeff1 + col2.g*coeff2 + col3.g*coeff3) >> 16; - col_out.b = (col0.b*coeff0 + col1.b*coeff1 + col2.b*coeff2 + col3.b*coeff3) >> 16; - pax_col_t color = col_out.col; - - // And return it. - if (tint != 0xffffffff) { - color = pax_col_tint(color, tint); - } - if (do_alpha) { - return pax_col_merge(existing, color); - } else { - return color; - } + // Pointer cast to texture thingy. + pax_buf_t const *image = (pax_buf_t const *)args; + + // Remap UVs. + u *= image->width; + v *= image->height; + // Correct UVs for the offset caused by filtering. + u -= 0.5; + v -= 0.5; + + // Get texture coords, round down instead of round to 0. + int tex_x = floorf(u); + int tex_y = floorf(v); + // Get subpixel coords. + float dx = pax_interp_value(u - tex_x); + float dy = pax_interp_value(v - tex_y); + + // Get four pixels. + pax_col_union_t col0 = {.col = pax_get_pixel(image, tex_x, tex_y)}; + pax_col_union_t col1 = {.col = pax_get_pixel(image, tex_x + 1, tex_y)}; + pax_col_union_t col2 = {.col = pax_get_pixel(image, tex_x + 1, tex_y + 1)}; + pax_col_union_t col3 = {.col = pax_get_pixel(image, tex_x, tex_y + 1)}; + + // Compute interpolation coefficients. + uint_fast32_t coeffx = dx * 256; + uint_fast32_t coeffy = dy * 256; + uint_fast32_t coeff0 = (256 - coeffx) * (256 - coeffy); + uint_fast32_t coeff1 = coeffx * (256 - coeffy); + uint_fast32_t coeff2 = coeffx * coeffy; + uint_fast32_t coeff3 = (256 - coeffx) * coeffy; + + pax_col_union_t col_out; + + // Interpolate alpha. + bool do_alpha = (col0.a & col1.a & col2.a & col3.a) != 255; + if (do_alpha) { + col_out.a = (col0.a * coeff0 + col1.a * coeff1 + col2.a * coeff2 + col3.a * coeff3) >> 16; + } else { + col_out.a = 255; + } + // Interpolate RGB. + col_out.r = (col0.r * coeff0 + col1.r * coeff1 + col2.r * coeff2 + col3.r * coeff3) >> 16; + col_out.g = (col0.g * coeff0 + col1.g * coeff1 + col2.g * coeff2 + col3.g * coeff3) >> 16; + col_out.b = (col0.b * coeff0 + col1.b * coeff1 + col2.b * coeff2 + col3.b * coeff3) >> 16; + pax_col_t color = col_out.col; + + // And return it. + if (tint != 0xffffffff) { + color = pax_col_tint(color, tint); + } + if (do_alpha) { + return pax_col_merge(existing, color); + } else { + return color; + } } diff --git a/src/pax_shaders.h b/src/pax_shaders.h index f41b75a..608a742 100644 --- a/src/pax_shaders.h +++ b/src/pax_shaders.h @@ -1,33 +1,12 @@ -/* - MIT License - - Copyright (c) 2021-2023 Julian Scheffers - - Permission is hereby granted, free of charge, to any person obtaining a copy - of this software and associated documentation files (the "Software"), to deal - in the Software without restriction, including without limitation the rights - to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - copies of the Software, and to permit persons to whom the Software is - furnished to do so, subject to the following conditions: - - The above copyright notice and this permission notice shall be included in all - copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - SOFTWARE. -*/ + +// SPDX-License-Identifier: MIT #ifndef PAX_SHADERS_H #define PAX_SHADERS_H -#include "pax_types.h" #include "pax_fonts.h" #include "pax_gfx.h" +#include "pax_types.h" #ifdef __cplusplus extern "C" { @@ -37,30 +16,30 @@ extern "C" { // Data relevant to drawing bitmap fonts. typedef struct pax_font_bmp_args { - // The font to be drawn. - const pax_font_t *font; - // The range of the font to be drawn. - const pax_font_range_t *range; - // Whether or not to do antialiasing, used by the promise callback. - bool do_aa; - // The glyph to be drawn. - uint32_t glyph; - // The glyph data pointer. - const uint8_t *bitmap; - // The bytes per line of the glyph. - size_t glyph_y_mul; - // The width of the glyph's drawn region. - uint8_t glyph_w; - // The height of the glyph's drawn region. - uint8_t glyph_h; - // The glyph's Bits Per Pixel. - uint8_t bpp; - // The glyph's Pixels Per Byte. - uint8_t ppb; - // The bitmask representing the max value that can be stored in bpp bits. - uint8_t mask; - // The bitmask used for truncating sub-byte indices. - uint8_t index_mask; + // The font to be drawn. + pax_font_t const *font; + // The range of the font to be drawn. + pax_font_range_t const *range; + // Whether or not to do antialiasing, used by the promise callback. + bool do_aa; + // The glyph to be drawn. + uint32_t glyph; + // The glyph data pointer. + uint8_t const *bitmap; + // The bytes per line of the glyph. + size_t glyph_y_mul; + // The width of the glyph's drawn region. + uint8_t glyph_w; + // The height of the glyph's drawn region. + uint8_t glyph_h; + // The glyph's Bits Per Pixel. + uint8_t bpp; + // The glyph's Pixels Per Byte. + uint8_t ppb; + // The bitmask representing the max value that can be stored in bpp bits. + uint8_t mask; + // The bitmask used for truncating sub-byte indices. + uint8_t index_mask; } pax_font_bmp_args_t; // Texture shader for multi-bpp bitmap fonts on palette buffers. @@ -85,10 +64,20 @@ pax_col_t pax_shader_font_bmp_aa(pax_col_t tint, pax_col_t existing, int x, int // Create a shader_t of the given texture. // Texture format is pax_but_t*. -#define PAX_SHADER_TEXTURE(texture) (pax_shader_t) { .schema_version = 1, .schema_complement = (uint8_t) ~1, .renderer_id = PAX_RENDERER_ID_SWR, .promise_callback = NULL, .callback = (void *) pax_shader_texture_aa, .callback_args = (void *) (texture), .alpha_promise_0=true, .alpha_promise_255=false } +#define PAX_SHADER_TEXTURE(texture) \ + (pax_shader_t) { \ + .schema_version = 1, .schema_complement = (uint8_t)~1, .renderer_id = PAX_RENDERER_ID_SWR, \ + .promise_callback = NULL, .callback = (void *)pax_shader_texture_aa, .callback_args = (void *)(texture), \ + .alpha_promise_0 = true, .alpha_promise_255 = false \ + } // Create a shader_t of the given texture, assumes the texture is opaque. // Texture format is pax_but_t*. -#define PAX_SHADER_TEXTURE_OP(texture) (pax_shader_t) { .schema_version = 1, .schema_complement = (uint8_t) ~1, .renderer_id = PAX_RENDERER_ID_SWR, .promise_callback = NULL, .callback = (void *) pax_shader_texture_aa, .callback_args = (void *) (texture), .alpha_promise_0=true, .alpha_promise_255=true } +#define PAX_SHADER_TEXTURE_OP(texture) \ + (pax_shader_t) { \ + .schema_version = 1, .schema_complement = (uint8_t)~1, .renderer_id = PAX_RENDERER_ID_SWR, \ + .promise_callback = NULL, .callback = (void *)pax_shader_texture_aa, .callback_args = (void *)(texture), \ + .alpha_promise_0 = true, .alpha_promise_255 = true \ + } // Texture shader without interpolation. pax_col_t pax_shader_texture(pax_col_t tint, pax_col_t existing, int x, int y, float u, float v, void *args); // Texture shader with interpolation. @@ -98,4 +87,4 @@ pax_col_t pax_shader_texture_aa(pax_col_t tint, pax_col_t existing, int x, int y } #endif //__cplusplus -#endif //PAX_SHADERS_H +#endif // PAX_SHADERS_H diff --git a/src/pax_shapes.c b/src/pax_shapes.c index 864c8a8..0e03c92 100644 --- a/src/pax_shapes.c +++ b/src/pax_shapes.c @@ -1,85 +1,67 @@ -/* - MIT License - - Copyright (c) 2021-2023 Julian Scheffers - - Permission is hereby granted, free of charge, to any person obtaining a copy - of this software and associated documentation files (the "Software"), to deal - in the Software without restriction, including without limitation the rights - to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - copies of the Software, and to permit persons to whom the Software is - furnished to do so, subject to the following conditions: - - The above copyright notice and this permission notice shall be included in all - copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - SOFTWARE. -*/ + +// SPDX-License-Identifier: MIT #include "pax_shapes.h" + #include "pax_internal.h" -#include + #include +#include -static const char *TAG = "pax-shapes"; +static char const *TAG = "pax-shapes"; // This is only applicable during shape triangulation. typedef struct indexed_point { - union { - struct { - float x, y; - }; - pax_vec2f vector; - }; - size_t index; + union { + struct { + float x, y; + }; + pax_vec2f vector; + }; + size_t index; } indexed_point_t; // This is only applicable during bezier vectorisation. typedef struct bezier_point { - float x, y; - float part; + float x, y; + float part; } bezier_point_t; // This is only applicable during bezier vectorisation. typedef struct bezier_segment { - bezier_point_t *from; - bezier_point_t *to; + bezier_point_t *from; + bezier_point_t *to; } bezier_segment_t; // Calcultesa point on a bezier curve based on given control points. -static inline bezier_point_t pax_calc_bezier0(float part, float x0, float y0, float x1, float y1, float x2, float y2, float x3, float y3) { - // First set of interpolations. - float xa = x0 + (x1 - x0) * part; - float xb = x1 + (x2 - x1) * part; - float xc = x2 + (x3 - x2) * part; - // Second set of interpolations. - float xp = xa + (xb - xa) * part; - float xq = xb + (xc - xb) * part; - // Final interpolation. - float x = xp + (xq - xp) * part; - - // First set of interpolations. - float ya = y0 + (y1 - y0) * part; - float yb = y1 + (y2 - y1) * part; - float yc = y2 + (y3 - y2) * part; - // Second set of interpolations. - float yp = ya + (yb - ya) * part; - float yq = yb + (yc - yb) * part; - // Final interpolation. - float y = yp + (yq - yp) * part; - - return (bezier_point_t) { .x = x, .y = y, .part = part }; +static inline bezier_point_t + pax_calc_bezier0(float part, float x0, float y0, float x1, float y1, float x2, float y2, float x3, float y3) { + // First set of interpolations. + float xa = x0 + (x1 - x0) * part; + float xb = x1 + (x2 - x1) * part; + float xc = x2 + (x3 - x2) * part; + // Second set of interpolations. + float xp = xa + (xb - xa) * part; + float xq = xb + (xc - xb) * part; + // Final interpolation. + float x = xp + (xq - xp) * part; + + // First set of interpolations. + float ya = y0 + (y1 - y0) * part; + float yb = y1 + (y2 - y1) * part; + float yc = y2 + (y3 - y2) * part; + // Second set of interpolations. + float yp = ya + (yb - ya) * part; + float yq = yb + (yc - yb) * part; + // Final interpolation. + float y = yp + (yq - yp) * part; + + return (bezier_point_t){.x = x, .y = y, .part = part}; } // Macro for calculating a point on a given bezier curve. static inline bezier_point_t pax_calc_bezier(float part, pax_4vec2f ctl) { - return pax_calc_bezier0(part, ctl.x0, ctl.y0, ctl.x1, ctl.y1, ctl.x2, ctl.y2, ctl.x3, ctl.y3); + return pax_calc_bezier0(part, ctl.x0, ctl.y0, ctl.x1, ctl.y1, ctl.x2, ctl.y2, ctl.x3, ctl.y3); } /* @@ -87,32 +69,32 @@ static inline bezier_point_t pax_calc_bezier(float part, pax_4vec2f ctl) { // Sorts by absolute line length. // TODO: Convert to delta over length? static int bezier_bifurcate_comp(const void *e0, const void *e1) { - // Pointer conversion. - const bezier_segment_t *a = e0; - const bezier_segment_t *b = e1; - // Length of line A. - float x_a = a->to->x - a->from->x; - float y_a = a->to->y - a->from->y; - float len_a = x_a * x_a + y_a * y_a; - // Length of line B. - float x_b = b->to->x - b->from->x; - float y_b = b->to->y - b->from->y; - // Sort them. - float len_b = x_b * x_b + y_b * y_b; - if (len_a < len_b) return 1; - if (len_a > len_b) return -1; - return 0; + // Pointer conversion. + const bezier_segment_t *a = e0; + const bezier_segment_t *b = e1; + // Length of line A. + float x_a = a->to->x - a->from->x; + float y_a = a->to->y - a->from->y; + float len_a = x_a * x_a + y_a * y_a; + // Length of line B. + float x_b = b->to->x - b->from->x; + float y_b = b->to->y - b->from->y; + // Sort them. + float len_b = x_b * x_b + y_b * y_b; + if (len_a < len_b) return 1; + if (len_a > len_b) return -1; + return 0; } // Comparison function for two points. // Sorts by T value (part along curve). static int bezier_point_t_comp(const void *e0, const void *e1) { - // Pointer conversion. - const bezier_point_t *a = e0; - const bezier_point_t *b = e1; - // Comparing is the same as subtracting. - float delta = a->part - b->part; - return delta / fabs(delta); + // Pointer conversion. + const bezier_point_t *a = e0; + const bezier_point_t *b = e1; + // Comparing is the same as subtracting. + float delta = a->part - b->part; + return delta / fabs(delta); } */ @@ -125,157 +107,161 @@ static int bezier_point_t_comp(const void *e0, const void *e1) { // Convert a cubic bezier curve to line segments, with the given number of points. // From and to are from 0 to 1, but any value is accepted. void pax_vectorise_bezier_part(pax_vec2f *ptr, size_t max_points, pax_4vec2f control_points, float t_from, float t_to) { - if (max_points < 4) PAX_ERROR("pax_vectorise_bezier", PAX_ERR_PARAM); - if (!ptr) { - PAX_ERROR("pax_vectorise_bezier_part", PAX_ERR_PARAM); - return; - } - + if (max_points < 4) + PAX_ERROR("pax_vectorise_bezier", PAX_ERR_PARAM); + if (!ptr) { + PAX_ERROR("pax_vectorise_bezier_part", PAX_ERR_PARAM); + return; + } + #if PAX_USE_EXPENSIVE_BEZIER - - // Start with just three points: start, T=0.5 and end. - bezier_point_t *points = malloc(sizeof(bezier_point_t) * max_points); - if (!points) { - PAX_ERROR("pax_vectorise_bezier_part", PAX_ERR_NOMEM); - return; - } - points[0] = pax_calc_bezier(t_from, control_points); - points[1] = pax_calc_bezier((t_from + t_to) * 0.5, control_points); - points[2] = pax_calc_bezier(t_to, control_points); - size_t n_points = 3; - - // Turn the points into lines. - bezier_segment_t *lines = malloc(sizeof(bezier_segment_t) * (max_points - 1)); - if (!lines) { - free(points); - PAX_ERROR("pax_vectorise_bezier_part", PAX_ERR_NOMEM); - return; - } - lines[0] = (bezier_segment_t) { .from = &points[0], .to = &points[1] }; - lines[1] = (bezier_segment_t) { .from = &points[1], .to = &points[2] }; - size_t n_lines = 2; - - // Bifurcate the longest line segment continuously. - while (n_points < max_points) { - // Find the segment to bifurcate. - qsort(lines, n_lines, sizeof(bezier_segment_t), bezier_bifurcate_comp); - float new_part = (lines[0].from->part + lines[0].to->part) * 0.5; - // Create a point at the average T. - points[n_points] = pax_calc_bezier(new_part, control_points); - // Bifurcate the line. - lines[n_lines] = (bezier_segment_t) { .from = &points[n_points], .to = lines[0].to }; - lines[0].to = &points[n_points]; - // The incrementing. - n_points ++; - n_lines ++; - } - - // Sort points by T value. - qsort(points, n_points, sizeof(bezier_point_t), bezier_point_t_comp); - // Output our GARBAGE. - for (size_t i = 0; i < n_points; i++) { - ptr[i] = (pax_vec2f) { .x = points[i].x, .y = points[i].y }; - } - free(points); - free(lines); - + + // Start with just three points: start, T=0.5 and end. + bezier_point_t *points = malloc(sizeof(bezier_point_t) * max_points); + if (!points) { + PAX_ERROR("pax_vectorise_bezier_part", PAX_ERR_NOMEM); + return; + } + points[0] = pax_calc_bezier(t_from, control_points); + points[1] = pax_calc_bezier((t_from + t_to) * 0.5, control_points); + points[2] = pax_calc_bezier(t_to, control_points); + size_t n_points = 3; + + // Turn the points into lines. + bezier_segment_t *lines = malloc(sizeof(bezier_segment_t) * (max_points - 1)); + if (!lines) { + free(points); + PAX_ERROR("pax_vectorise_bezier_part", PAX_ERR_NOMEM); + return; + } + lines[0] = (bezier_segment_t){.from = &points[0], .to = &points[1]}; + lines[1] = (bezier_segment_t){.from = &points[1], .to = &points[2]}; + size_t n_lines = 2; + + // Bifurcate the longest line segment continuously. + while (n_points < max_points) { + // Find the segment to bifurcate. + qsort(lines, n_lines, sizeof(bezier_segment_t), bezier_bifurcate_comp); + float new_part = (lines[0].from->part + lines[0].to->part) * 0.5; + // Create a point at the average T. + points[n_points] = pax_calc_bezier(new_part, control_points); + // Bifurcate the line. + lines[n_lines] = (bezier_segment_t){.from = &points[n_points], .to = lines[0].to}; + lines[0].to = &points[n_points]; + // The incrementing. + n_points++; + n_lines++; + } + + // Sort points by T value. + qsort(points, n_points, sizeof(bezier_point_t), bezier_point_t_comp); + // Output our GARBAGE. + for (size_t i = 0; i < n_points; i++) { + ptr[i] = (pax_vec2f){.x = points[i].x, .y = points[i].y}; + } + free(points); + free(lines); + #else - - float delta = (t_to - t_from) / (max_points - 1); - float part = t_from; - for (size_t i = 0; i < max_points; i++) { - bezier_point_t point = pax_calc_bezier(part, control_points); - ptr[i] = (pax_vec2f) {point.x, point.y}; - part += delta; - } - + + float delta = (t_to - t_from) / (max_points - 1); + float part = t_from; + for (size_t i = 0; i < max_points; i++) { + bezier_point_t point = pax_calc_bezier(part, control_points); + ptr[i] = (pax_vec2f){point.x, point.y}; + part += delta; + } + #endif - - PAX_SUCCESS(); + + PAX_SUCCESS(); } // Convert a cubic bezier curve to line segments, with the given number of points. void pax_vectorise_bezier(pax_vec2f *output, size_t max_points, pax_4vec2f control_points) { - pax_vectorise_bezier_part(output, max_points, control_points, 0, 1); + pax_vectorise_bezier_part(output, max_points, control_points, 0, 1); } // Draw a cubic bezier curve. void pax_draw_bezier_part(pax_buf_t *buf, pax_col_t color, pax_4vec2f control_points, float from, float to) { - size_t n_points = 64; - if (to < from) { - PAX_SWAP(float, to, from); - } + size_t n_points = 64; + if (to < from) { + PAX_SWAP(float, to, from); + } #if PAX_USE_EXPENSIVE_BEZIER - // Vectorise the bezier curve first. - pax_vec2f *points; - pax_vectorise_bezier(&points, n_points, control_points); - if (!points) return; - - // Then draw it. - for (size_t i = 0; i < n_points - 1; i++) { - pax_draw_line(buf, color, points[i].x, points[i].y, points[i + 1].x, points[i + 1].y); - } - free(points); + // Vectorise the bezier curve first. + pax_vec2f *points; + pax_vectorise_bezier(&points, n_points, control_points); + if (!points) + return; + + // Then draw it. + for (size_t i = 0; i < n_points - 1; i++) { + pax_draw_line(buf, color, points[i].x, points[i].y, points[i + 1].x, points[i + 1].y); + } + free(points); #else - // Draw the curve in a more simple manner. - bezier_point_t last_point = pax_calc_bezier(from, control_points); - float delta = (to - from) / (n_points - 2); - float part = from; - for (size_t i = 0; i < n_points - 1; i++) { - bezier_point_t point = pax_calc_bezier(part, control_points); - pax_draw_line(buf, color, last_point.x, last_point.y, point.x, point.y); - last_point = point; - part += delta; - } + // Draw the curve in a more simple manner. + bezier_point_t last_point = pax_calc_bezier(from, control_points); + float delta = (to - from) / (n_points - 2); + float part = from; + for (size_t i = 0; i < n_points - 1; i++) { + bezier_point_t point = pax_calc_bezier(part, control_points); + pax_draw_line(buf, color, last_point.x, last_point.y, point.x, point.y); + last_point = point; + part += delta; + } #endif - - PAX_SUCCESS(); + + PAX_SUCCESS(); } // Draw a cubic bezier curve. void pax_draw_bezier(pax_buf_t *buf, pax_col_t color, pax_4vec2f control_points) { - pax_draw_bezier_part(buf, color, control_points, 0, 1); + pax_draw_bezier_part(buf, color, control_points, 0, 1); } #else static bool bezier_warning = false; static void pax_bezier_warn() { - if (!bezier_warning) { - PAX_LOGE(TAG, "Failed: Bezier curves not compiled, please define PAX_COMPILE_BEZIER."); - } + if (!bezier_warning) { + PAX_LOGE(TAG, "Failed: Bezier curves not compiled, please define PAX_COMPILE_BEZIER."); + } } // Convert a cubic bezier curve to line segments, with the given number of points. // From and to are from 0 to 1, but any value is accepted. -void pax_vectorise_bezier_part(pax_vec2f **output, pax_4vec2f control_points, size_t max_points, float t_from, float t_to) { - // Not compiled in, but keep the method for API compatibility. - pax_bezier_warn(); - PAX_ERROR("pax_vectorise_bezier_part", PAX_ERR_UNSUPPORTED); +void pax_vectorise_bezier_part( + pax_vec2f **output, pax_4vec2f control_points, size_t max_points, float t_from, float t_to +) { + // Not compiled in, but keep the method for API compatibility. + pax_bezier_warn(); + PAX_ERROR("pax_vectorise_bezier_part", PAX_ERR_UNSUPPORTED); } // Convert a cubic bezier curve to line segments, with the given number of points. void pax_vectorise_bezier(pax_vec2f **output, pax_4vec2f control_points, size_t max_points) { - // Not compiled in, but keep the method for API compatibility. - pax_bezier_warn(); - PAX_ERROR("pax_vectorise_bezier", PAX_ERR_UNSUPPORTED); + // Not compiled in, but keep the method for API compatibility. + pax_bezier_warn(); + PAX_ERROR("pax_vectorise_bezier", PAX_ERR_UNSUPPORTED); } // Draw a cubic bezier curve. void pax_draw_bezier_part(pax_buf_t *buf, pax_col_t color, pax_4vec2f control_points, float from, float to) { - // Not compiled in, but keep the method for API compatibility. - pax_bezier_warn(); - PAX_ERROR("pax_draw_bezier_part", PAX_ERR_UNSUPPORTED); + // Not compiled in, but keep the method for API compatibility. + pax_bezier_warn(); + PAX_ERROR("pax_draw_bezier_part", PAX_ERR_UNSUPPORTED); } // Draw a cubic bezier curve. void pax_draw_bezier(pax_buf_t *buf, pax_col_t color, pax_4vec2f control_points) { - // Not compiled in, but keep the method for API compatibility. - pax_bezier_warn(); - PAX_ERROR("pax_draw_bezier", PAX_ERR_UNSUPPORTED); + // Not compiled in, but keep the method for API compatibility. + pax_bezier_warn(); + PAX_ERROR("pax_draw_bezier", PAX_ERR_UNSUPPORTED); } -#endif //PAX_COMPILE_BEZIER +#endif // PAX_COMPILE_BEZIER @@ -283,49 +269,50 @@ void pax_draw_bezier(pax_buf_t *buf, pax_col_t color, pax_4vec2f control_points) // Vectorise an arc outline, angle in radians. void pax_vectorise_arc(pax_vec2f *ptr, size_t n_div, float x, float y, float r, float a0, float a1) { - // Allocate output array. - if (!ptr) { - PAX_ERROR("pax_vectorise_arc", PAX_ERR_PARAM); - } - - // Simplify the angles slightly. - float a2 = fmodf(a0, M_PI * 2); - a1 += a2 - a0; - a0 = a2; - if (a1 < a0) PAX_SWAP(float, a0, a1); - if (a1 - a0 > M_PI * 2) { - a1 = M_PI * 2; - a0 = 0; - } - - // Get the sine and cosine of one division, used for rotation in the loop. - float div_angle = (a1 - a0) / (n_div - 1); - float c_sin = sinf(div_angle); - float c_cos = cosf(div_angle); - - // Start with a unit vector according to a0. - float x0 = cosf(a0); - float y0 = sinf(a0); - - // Draw it as a series of triangles, rotating with what is essentially matrix multiplication. - for (int i = 0; i < n_div; i++) { - // Perform the rotation. - float x1 = x0 * c_cos - y0 * c_sin; - float y1 = x0 * c_sin + y0 * c_cos; - // Store to array. - // We subtract y0 and y1 from y because our up is -y. - ptr[i] = (pax_vec2f) { .x = x + x0 * r, .y = y - y0 * r }; - // Assign them yes. - x0 = x1; - y0 = y1; - } - - PAX_SUCCESS(); + // Allocate output array. + if (!ptr) { + PAX_ERROR("pax_vectorise_arc", PAX_ERR_PARAM); + } + + // Simplify the angles slightly. + float a2 = fmodf(a0, M_PI * 2); + a1 += a2 - a0; + a0 = a2; + if (a1 < a0) + PAX_SWAP(float, a0, a1); + if (a1 - a0 > M_PI * 2) { + a1 = M_PI * 2; + a0 = 0; + } + + // Get the sine and cosine of one division, used for rotation in the loop. + float div_angle = (a1 - a0) / (n_div - 1); + float c_sin = sinf(div_angle); + float c_cos = cosf(div_angle); + + // Start with a unit vector according to a0. + float x0 = cosf(a0); + float y0 = sinf(a0); + + // Draw it as a series of triangles, rotating with what is essentially matrix multiplication. + for (int i = 0; i < n_div; i++) { + // Perform the rotation. + float x1 = x0 * c_cos - y0 * c_sin; + float y1 = x0 * c_sin + y0 * c_cos; + // Store to array. + // We subtract y0 and y1 from y because our up is -y. + ptr[i] = (pax_vec2f){.x = x + x0 * r, .y = y - y0 * r}; + // Assign them yes. + x0 = x1; + y0 = y1; + } + + PAX_SUCCESS(); } // Vectorise a circle outline. void pax_vectorise_circle(pax_vec2f *output, size_t num_points, float x, float y, float r) { - pax_vectorise_arc(output, num_points, x, y, r, 0, M_PI * 2); + pax_vectorise_arc(output, num_points, x, y, r, 0, M_PI * 2); } @@ -334,228 +321,170 @@ void pax_vectorise_circle(pax_vec2f *output, size_t num_points, float x, float y // Draw a rounded rectangle. void pax_draw_round_rect(pax_buf_t *buf, pax_col_t color, float x, float y, float width, float height, float radius) { - // Clamp radius. - if (radius > width / 2) radius = width / 2; - if (radius > height / 2) radius = height / 2; - - // Draw corners. - pax_draw_arc( - buf, color, - x + radius, y + radius, - radius, M_PI*0.5, M_PI*1.0 - ); - pax_draw_arc( - buf, color, - x + width - radius, y + radius, - radius, M_PI*0.0, M_PI*0.5 - ); - pax_draw_arc( - buf, color, - x + width - radius, y + height - radius, - radius, M_PI*0.0, M_PI*-0.5 - ); - pax_draw_arc( - buf, color, - x + radius, y + height - radius, - radius, M_PI*-0.5, M_PI*-1.0 - ); - - // Draw infill. - pax_draw_rect( - buf, color, - x + radius, y, - width - 2*radius, radius - ); - pax_draw_rect( - buf, color, - x + radius, y + height - radius, - width - 2*radius, radius - ); - pax_draw_rect( - buf, color, - x , y + radius, - width, height - 2*radius - ); + // Clamp radius. + if (radius > width / 2) + radius = width / 2; + if (radius > height / 2) + radius = height / 2; + + // Draw corners. + pax_draw_arc(buf, color, x + radius, y + radius, radius, M_PI * 0.5, M_PI * 1.0); + pax_draw_arc(buf, color, x + width - radius, y + radius, radius, M_PI * 0.0, M_PI * 0.5); + pax_draw_arc(buf, color, x + width - radius, y + height - radius, radius, M_PI * 0.0, M_PI * -0.5); + pax_draw_arc(buf, color, x + radius, y + height - radius, radius, M_PI * -0.5, M_PI * -1.0); + + // Draw infill. + pax_draw_rect(buf, color, x + radius, y, width - 2 * radius, radius); + pax_draw_rect(buf, color, x + radius, y + height - radius, width - 2 * radius, radius); + pax_draw_rect(buf, color, x, y + radius, width, height - 2 * radius); } // Draw a hollow circle. void pax_draw_hollow_circle(pax_buf_t *buf, pax_col_t color, float x, float y, float radius0, float radius1) { - pax_draw_hollow_arc(buf, color, x, y, radius0, radius1, 0, M_PI*2); + pax_draw_hollow_arc(buf, color, x, y, radius0, radius1, 0, M_PI * 2); } // Draw a hollow arc. -void pax_draw_hollow_arc(pax_buf_t *buf, pax_col_t color, float x, float y, float radius0, float radius1, float a0, float a1) { - size_t n_points = 24; - pax_vec2f points[n_points]; - pax_vectorise_arc(points, n_points, 0, 0, 1, a0, a1); - for (size_t i = 0; i < n_points - 1; i++) { - pax_draw_tri( - buf, color, - x + points[i ].x * radius0, y + points[i ].y * radius0, - x + points[i ].x * radius1, y + points[i ].y * radius1, - x + points[i+1].x * radius1, y + points[i+1].y * radius1 - ); - pax_draw_tri( - buf, color, - x + points[i ].x * radius0, y + points[i ].y * radius0, - x + points[i+1].x * radius0, y + points[i+1].y * radius0, - x + points[i+1].x * radius1, y + points[i+1].y * radius1 - ); - } +void pax_draw_hollow_arc( + pax_buf_t *buf, pax_col_t color, float x, float y, float radius0, float radius1, float a0, float a1 +) { + size_t n_points = 24; + pax_vec2f points[n_points]; + pax_vectorise_arc(points, n_points, 0, 0, 1, a0, a1); + for (size_t i = 0; i < n_points - 1; i++) { + pax_draw_tri( + buf, + color, + x + points[i].x * radius0, + y + points[i].y * radius0, + x + points[i].x * radius1, + y + points[i].y * radius1, + x + points[i + 1].x * radius1, + y + points[i + 1].y * radius1 + ); + pax_draw_tri( + buf, + color, + x + points[i].x * radius0, + y + points[i].y * radius0, + x + points[i + 1].x * radius0, + y + points[i + 1].y * radius0, + x + points[i + 1].x * radius1, + y + points[i + 1].y * radius1 + ); + } } // Draw a hollow arc. -void pax_draw_round_hollow_arc(pax_buf_t *buf, pax_col_t color, float x, float y, float radius0, float radius1, float a0, float a1) { - pax_draw_hollow_arc(buf, color, x, y, radius0, radius1, a0, a1); - float radius = (radius0 + radius1) / 2; - float dradius = fabsf(radius1 - radius0) / 2; - float a2 = a0 - M_PI; - float a3 = a0; - float a4 = a1; - float a5 = a1 + M_PI; - if (a1 < a0) { - a2 += M_PI; - a3 += M_PI; - a4 += M_PI; - a5 += M_PI; - } - pax_draw_arc( - buf, color, - x + cos(a0) * radius, y - sinf(a0) * radius, - dradius, - a2, a3 - ); - pax_draw_arc( - buf, color, - x + cos(a1) * radius, y - sinf(a1) * radius, - dradius, - a4, a5 - ); +void pax_draw_round_hollow_arc( + pax_buf_t *buf, pax_col_t color, float x, float y, float radius0, float radius1, float a0, float a1 +) { + pax_draw_hollow_arc(buf, color, x, y, radius0, radius1, a0, a1); + float radius = (radius0 + radius1) / 2; + float dradius = fabsf(radius1 - radius0) / 2; + float a2 = a0 - M_PI; + float a3 = a0; + float a4 = a1; + float a5 = a1 + M_PI; + if (a1 < a0) { + a2 += M_PI; + a3 += M_PI; + a4 += M_PI; + a5 += M_PI; + } + pax_draw_arc(buf, color, x + cos(a0) * radius, y - sinf(a0) * radius, dradius, a2, a3); + pax_draw_arc(buf, color, x + cos(a1) * radius, y - sinf(a1) * radius, dradius, a4, a5); } // Outline a rounded rectangle. -void pax_outline_round_rect(pax_buf_t *buf, pax_col_t color, float x, float y, float width, float height, float radius) { - // Clamp radius. - if (radius > width / 2) radius = width / 2; - if (radius > height / 2) radius = height / 2; - - // Draw corners. - pax_outline_arc( - buf, color, - x + radius, y + radius, - radius, M_PI*0.5, M_PI*1.0 - ); - pax_outline_arc( - buf, color, - x + width - radius, y + radius, - radius, M_PI*0.0, M_PI*0.5 - ); - pax_outline_arc( - buf, color, - x + width - radius, y + height - radius, - radius, M_PI*0.0, M_PI*-0.5 - ); - pax_outline_arc( - buf, color, - x + radius, y + height - radius, - radius, M_PI*-0.5, M_PI*-1.0 - ); - - // Draw infill. - pax_draw_line( - buf, color, - x + radius, y, - x + width - radius, y - ); - pax_draw_line( - buf, color, - x + radius, y + height, - x + width - radius, y + height - ); - pax_draw_line( - buf, color, - x, y + radius, - x, y + height - radius - ); - pax_draw_line( - buf, color, - x + width, y + radius, - x + width, y + height - radius - ); +void pax_outline_round_rect( + pax_buf_t *buf, pax_col_t color, float x, float y, float width, float height, float radius +) { + // Clamp radius. + if (radius > width / 2) + radius = width / 2; + if (radius > height / 2) + radius = height / 2; + + // Draw corners. + pax_outline_arc(buf, color, x + radius, y + radius, radius, M_PI * 0.5, M_PI * 1.0); + pax_outline_arc(buf, color, x + width - radius, y + radius, radius, M_PI * 0.0, M_PI * 0.5); + pax_outline_arc(buf, color, x + width - radius, y + height - radius, radius, M_PI * 0.0, M_PI * -0.5); + pax_outline_arc(buf, color, x + radius, y + height - radius, radius, M_PI * -0.5, M_PI * -1.0); + + // Draw infill. + pax_draw_line(buf, color, x + radius, y, x + width - radius, y); + pax_draw_line(buf, color, x + radius, y + height, x + width - radius, y + height); + pax_draw_line(buf, color, x, y + radius, x, y + height - radius); + pax_draw_line(buf, color, x + width, y + radius, x + width, y + height - radius); } // Draw a hollow circle. void pax_outline_hollow_circle(pax_buf_t *buf, pax_col_t color, float x, float y, float radius0, float radius1) { - pax_outline_circle(buf, color, x, y, radius0); - pax_outline_circle(buf, color, x, y, radius1); + pax_outline_circle(buf, color, x, y, radius0); + pax_outline_circle(buf, color, x, y, radius1); } // Draw a hollow arc. -static void pax_outline_hollow_arc0(pax_buf_t *buf, pax_col_t color, float x, float y, float radius0, float radius1, float a0, float a1) { - size_t n_points = 24; - pax_vec2f points[n_points]; - pax_vectorise_arc(points, n_points, 0, 0, 1, a0, a1); - for (size_t i = 0; i < n_points - 1; i++) { - pax_draw_line( - buf, color, - x + points[i ].x * radius0, y + points[i ].y * radius0, - x + points[i+1].x * radius0, y + points[i+1].y * radius0 - ); - pax_draw_line( - buf, color, - x + points[i ].x * radius1, y + points[i ].y * radius1, - x + points[i+1].x * radius1, y + points[i+1].y * radius1 - ); - } +static void pax_outline_hollow_arc0( + pax_buf_t *buf, pax_col_t color, float x, float y, float radius0, float radius1, float a0, float a1 +) { + size_t n_points = 24; + pax_vec2f points[n_points]; + pax_vectorise_arc(points, n_points, 0, 0, 1, a0, a1); + for (size_t i = 0; i < n_points - 1; i++) { + pax_draw_line( + buf, + color, + x + points[i].x * radius0, + y + points[i].y * radius0, + x + points[i + 1].x * radius0, + y + points[i + 1].y * radius0 + ); + pax_draw_line( + buf, + color, + x + points[i].x * radius1, + y + points[i].y * radius1, + x + points[i + 1].x * radius1, + y + points[i + 1].y * radius1 + ); + } } // Draw a hollow arc. -void pax_outline_hollow_arc(pax_buf_t *buf, pax_col_t color, float x, float y, float radius0, float radius1, float a0, float a1) { - pax_outline_hollow_arc0(buf, color, x, y, radius0, radius1, a0, a1); - float sin0 = sinf(a0); - float cos0 = cosf(a0); - float sin1 = sinf(a1); - float cos1 = cosf(a1); - pax_draw_line( - buf, color, - x + cos0 * radius0, y - sin0 * radius0, - x + cos0 * radius1, y - sin0 * radius1 - ); - pax_draw_line( - buf, color, - x + cos1 * radius0, y - sin1 * radius0, - x + cos1 * radius1, y - sin1 * radius1 - ); +void pax_outline_hollow_arc( + pax_buf_t *buf, pax_col_t color, float x, float y, float radius0, float radius1, float a0, float a1 +) { + pax_outline_hollow_arc0(buf, color, x, y, radius0, radius1, a0, a1); + float sin0 = sinf(a0); + float cos0 = cosf(a0); + float sin1 = sinf(a1); + float cos1 = cosf(a1); + pax_draw_line(buf, color, x + cos0 * radius0, y - sin0 * radius0, x + cos0 * radius1, y - sin0 * radius1); + pax_draw_line(buf, color, x + cos1 * radius0, y - sin1 * radius0, x + cos1 * radius1, y - sin1 * radius1); } // Outline a hollow arc. -void pax_outline_round_hollow_arc(pax_buf_t *buf, pax_col_t color, float x, float y, float radius0, float radius1, float a0, float a1) { - pax_outline_hollow_arc0(buf, color, x, y, radius0, radius1, a0, a1); - float radius = (radius0 + radius1) / 2; - float dradius = fabsf(radius1 - radius0) / 2; - float a2 = a0 - M_PI; - float a3 = a0; - float a4 = a1; - float a5 = a1 + M_PI; - if (a1 < a0) { - a2 += M_PI; - a3 += M_PI; - a4 += M_PI; - a5 += M_PI; - } - pax_outline_arc( - buf, color, - x + cos(a0) * radius, y - sinf(a0) * radius, - dradius, - a2, a3 - ); - pax_outline_arc( - buf, color, - x + cos(a1) * radius, y - sinf(a1) * radius, - dradius, - a4, a5 - ); +void pax_outline_round_hollow_arc( + pax_buf_t *buf, pax_col_t color, float x, float y, float radius0, float radius1, float a0, float a1 +) { + pax_outline_hollow_arc0(buf, color, x, y, radius0, radius1, a0, a1); + float radius = (radius0 + radius1) / 2; + float dradius = fabsf(radius1 - radius0) / 2; + float a2 = a0 - M_PI; + float a3 = a0; + float a4 = a1; + float a5 = a1 + M_PI; + if (a1 < a0) { + a2 += M_PI; + a3 += M_PI; + a4 += M_PI; + a5 += M_PI; + } + pax_outline_arc(buf, color, x + cos(a0) * radius, y - sinf(a0) * radius, dradius, a2, a3); + pax_outline_arc(buf, color, x + cos(a1) * radius, y - sinf(a1) * radius, dradius, a4, a5); } @@ -564,196 +493,225 @@ void pax_outline_round_hollow_arc(pax_buf_t *buf, pax_col_t color, float x, floa // Draw a rectangle outline. void pax_outline_rect(pax_buf_t *buf, pax_col_t color, float x, float y, float width, float height) { - pax_push_2d(buf); - pax_apply_2d(buf, matrix_2d_translate(x, y)); - pax_draw_line(buf, color, 0, 0, width, 0); - pax_draw_line(buf, color, 0, height, width, height); - pax_draw_line(buf, color, 0, 0, 0, height); - pax_draw_line(buf, color, width, 0, width, height); - pax_pop_2d(buf); + pax_push_2d(buf); + pax_apply_2d(buf, matrix_2d_translate(x, y)); + pax_draw_line(buf, color, 0, 0, width, 0); + pax_draw_line(buf, color, 0, height, width, height); + pax_draw_line(buf, color, 0, 0, 0, height); + pax_draw_line(buf, color, width, 0, width, height); + pax_pop_2d(buf); } // Draw a triangle outline. void pax_outline_tri(pax_buf_t *buf, pax_col_t color, float x0, float y0, float x1, float y1, float x2, float y2) { - pax_draw_line(buf, color, x0, y0, x1, y1); - pax_draw_line(buf, color, x2, y2, x1, y1); - pax_draw_line(buf, color, x0, y0, x2, y2); + pax_draw_line(buf, color, x0, y0, x1, y1); + pax_draw_line(buf, color, x2, y2, x1, y1); + pax_draw_line(buf, color, x0, y0, x2, y2); } // Draw an arc outline, angle in radians. void pax_outline_arc(pax_buf_t *buf, pax_col_t color, float x, float y, float r, float a0, float a1) { - PAX_BUF_CHECK("pax_outline_arc"); - - // Simplify the angles slightly. - float a2 = fmodf(a0, M_PI * 2); - a1 += a2 - a0; - a0 = a2; - if (a1 < a0) PAX_SWAP(float, a0, a1); - if (a1 - a0 > M_PI * 2) { - a1 = M_PI * 2; - a0 = 0; - } - - // Pick an appropriate number of divisions. - int n_div; - matrix_2d_t matrix = buf->stack_2d.value; - float c_r = r * sqrtf(matrix.a0*matrix.a0 + matrix.b0*matrix.b0) * sqrtf(matrix.a1*matrix.a1 + matrix.b1*matrix.b1); - if (c_r > 30) n_div = (a1 - a0) / M_PI * 32 + 1; - else n_div = (a1 - a0) / M_PI * 16 + 1; - - // Get the sine and cosine of one division, used for rotation in the loop. - float div_angle = (a1 - a0) / n_div; - float c_sin = sinf(div_angle); - float c_cos = cosf(div_angle); - - // Start with a unit vector according to a0. - float x0 = cosf(a0); - float y0 = sinf(a0); - - // Draw it as a series of triangles, rotating with what is essentially matrix multiplication. - for (int i = 0; i < n_div; i++) { - // Perform the rotation. - float x1 = x0 * c_cos - y0 * c_sin; - float y1 = x0 * c_sin + y0 * c_cos; - // We subtract y0 and y1 from y because our up is -y. - pax_draw_line(buf, color, x + x0 * r, y - y0 * r, x + x1 * r, y - y1 * r); - // Assign them yes. - x0 = x1; - y0 = y1; - } - - PAX_SUCCESS(); + PAX_BUF_CHECK("pax_outline_arc"); + + // Simplify the angles slightly. + float a2 = fmodf(a0, M_PI * 2); + a1 += a2 - a0; + a0 = a2; + if (a1 < a0) + PAX_SWAP(float, a0, a1); + if (a1 - a0 > M_PI * 2) { + a1 = M_PI * 2; + a0 = 0; + } + + // Pick an appropriate number of divisions. + int n_div; + matrix_2d_t matrix = buf->stack_2d.value; + float c_r = + r * sqrtf(matrix.a0 * matrix.a0 + matrix.b0 * matrix.b0) * sqrtf(matrix.a1 * matrix.a1 + matrix.b1 * matrix.b1); + if (c_r > 30) + n_div = (a1 - a0) / M_PI * 32 + 1; + else + n_div = (a1 - a0) / M_PI * 16 + 1; + + // Get the sine and cosine of one division, used for rotation in the loop. + float div_angle = (a1 - a0) / n_div; + float c_sin = sinf(div_angle); + float c_cos = cosf(div_angle); + + // Start with a unit vector according to a0. + float x0 = cosf(a0); + float y0 = sinf(a0); + + // Draw it as a series of triangles, rotating with what is essentially matrix multiplication. + for (int i = 0; i < n_div; i++) { + // Perform the rotation. + float x1 = x0 * c_cos - y0 * c_sin; + float y1 = x0 * c_sin + y0 * c_cos; + // We subtract y0 and y1 from y because our up is -y. + pax_draw_line(buf, color, x + x0 * r, y - y0 * r, x + x1 * r, y - y1 * r); + // Assign them yes. + x0 = x1; + y0 = y1; + } + + PAX_SUCCESS(); } // Draw a circle outline. void pax_outline_circle(pax_buf_t *buf, pax_col_t color, float x, float y, float r) { - pax_outline_arc(buf, color, x, y, r, 0, M_PI * 2); + pax_outline_arc(buf, color, x, y, r, 0, M_PI * 2); } // Dummy UVs used for quad UVs where NULL is provided. -static const pax_quadf dummy_quad_uvs = { - .x0 = 0, .y0 = 0, - .x1 = 1, .y1 = 0, - .x2 = 1, .y2 = 1, - .x3 = 0, .y3 = 1 -}; +static pax_quadf const dummy_quad_uvs = {.x0 = 0, .y0 = 0, .x1 = 1, .y1 = 0, .x2 = 1, .y2 = 1, .x3 = 0, .y3 = 1}; // Dummy UVs used for tri UVs where NULL is provided. -static const pax_trif dummy_tri_uvs = { - .x0 = 0, .y0 = 0, - .x1 = 1, .y1 = 0, - .x2 = 0, .y2 = 1 -}; +static pax_trif const dummy_tri_uvs = {.x0 = 0, .y0 = 0, .x1 = 1, .y1 = 0, .x2 = 0, .y2 = 1}; // Draw a rectangle outline with a shader. // If uvs is NULL, a default will be used (0,0; 1,0; 1,1; 0,1). -void pax_shade_outline_rect(pax_buf_t *buf, pax_col_t color, const pax_shader_t *shader, const pax_quadf *uvs, float x, float y, float width, float height) { - pax_linef tmp; - if (uvs == NULL) { - uvs = &dummy_quad_uvs; - } - - pax_push_2d(buf); - pax_apply_2d(buf, matrix_2d_translate(x, y)); - - tmp = (pax_linef) { uvs->x0, uvs->y0, uvs->x1, uvs->y1}; - pax_shade_line(buf, color, shader, &tmp, 0, 0, width, 0); - - tmp = (pax_linef) { uvs->x1, uvs->y1, uvs->x2, uvs->y2}; - pax_shade_line(buf, color, shader, &tmp, width, 0, width, height); - - tmp = (pax_linef) { uvs->x2, uvs->y2, uvs->x3, uvs->y3}; - pax_shade_line(buf, color, shader, &tmp, width, height, 0, height); - - tmp = (pax_linef) { uvs->x3, uvs->y3, uvs->x0, uvs->y0}; - pax_shade_line(buf, color, shader, &tmp, 0, height, 0, 0); - - pax_pop_2d(buf); +void pax_shade_outline_rect( + pax_buf_t *buf, + pax_col_t color, + pax_shader_t const *shader, + pax_quadf const *uvs, + float x, + float y, + float width, + float height +) { + pax_linef tmp; + if (uvs == NULL) { + uvs = &dummy_quad_uvs; + } + + pax_push_2d(buf); + pax_apply_2d(buf, matrix_2d_translate(x, y)); + + tmp = (pax_linef){uvs->x0, uvs->y0, uvs->x1, uvs->y1}; + pax_shade_line(buf, color, shader, &tmp, 0, 0, width, 0); + + tmp = (pax_linef){uvs->x1, uvs->y1, uvs->x2, uvs->y2}; + pax_shade_line(buf, color, shader, &tmp, width, 0, width, height); + + tmp = (pax_linef){uvs->x2, uvs->y2, uvs->x3, uvs->y3}; + pax_shade_line(buf, color, shader, &tmp, width, height, 0, height); + + tmp = (pax_linef){uvs->x3, uvs->y3, uvs->x0, uvs->y0}; + pax_shade_line(buf, color, shader, &tmp, 0, height, 0, 0); + + pax_pop_2d(buf); } // Draw a triangle with a shader. // If uvs is NULL, a default will be used (0,0; 1,0; 1,1; 0,1). -void pax_shade_outline_tri(pax_buf_t *buf, pax_col_t color, const pax_shader_t *shader, - const pax_trif *uvs, float x0, float y0, float x1, float y1, float x2, float y2) { - pax_linef tmp; - if (uvs == NULL) { - uvs = &dummy_tri_uvs; - } - - tmp = (pax_linef) { uvs->x0, uvs->y0, uvs->x1, uvs->y1}; - pax_shade_line(buf, color, shader, &tmp, x0, y0, x1, y1); - - tmp = (pax_linef) { uvs->x1, uvs->y1, uvs->x2, uvs->y2}; - pax_shade_line(buf, color, shader, &tmp, x1, y1, x2, y2); - - tmp = (pax_linef) { uvs->x2, uvs->y2, uvs->x0, uvs->y0}; - pax_shade_line(buf, color, shader, &tmp, x2, y2, x0, y0); +void pax_shade_outline_tri( + pax_buf_t *buf, + pax_col_t color, + pax_shader_t const *shader, + pax_trif const *uvs, + float x0, + float y0, + float x1, + float y1, + float x2, + float y2 +) { + pax_linef tmp; + if (uvs == NULL) { + uvs = &dummy_tri_uvs; + } + + tmp = (pax_linef){uvs->x0, uvs->y0, uvs->x1, uvs->y1}; + pax_shade_line(buf, color, shader, &tmp, x0, y0, x1, y1); + + tmp = (pax_linef){uvs->x1, uvs->y1, uvs->x2, uvs->y2}; + pax_shade_line(buf, color, shader, &tmp, x1, y1, x2, y2); + + tmp = (pax_linef){uvs->x2, uvs->y2, uvs->x0, uvs->y0}; + pax_shade_line(buf, color, shader, &tmp, x2, y2, x0, y0); } // Draw an arc outline with a shader, angle in radians. // If uvs is NULL, a default will be used (0,0; 1,0; 1,1; 0,1). -void pax_shade_outline_arc(pax_buf_t *buf, pax_col_t color, const pax_shader_t *shader, - const pax_quadf *uvs, float x, float y, float r, float a0, float a1) { - PAX_BUF_CHECK("pax_shade_outline_arc"); - pax_linef tmp; - if (uvs == NULL) { - uvs = &dummy_quad_uvs; - } - - // Simplify the angles slightly. - float a2 = fmodf(a0, M_PI * 2); - a1 += a2 - a0; - a0 = a2; - if (a1 < a0) PAX_SWAP(float, a0, a1); - if (a1 - a0 > M_PI * 2) { - a1 = M_PI * 2; - a0 = 0; - } - - // Pick an appropriate number of divisions. - int n_div; - matrix_2d_t matrix = buf->stack_2d.value; - float c_r = r * sqrtf(matrix.a0*matrix.a0 + matrix.b0*matrix.b0) * sqrtf(matrix.a1*matrix.a1 + matrix.b1*matrix.b1); - if (c_r > 30) n_div = (a1 - a0) / M_PI * 32 + 1; - else n_div = (a1 - a0) / M_PI * 16 + 1; - - // Get the sine and cosine of one division, used for rotation in the loop. - float div_angle = (a1 - a0) / n_div; - float c_sin = sinf(div_angle); - float c_cos = cosf(div_angle); - - // Start with a unit vector according to a0. - float x0 = cosf(a0); - float y0 = sinf(a0); - // Transform UV coords. - tmp.x0 = pax_flerp4(x0, y0, uvs->x0, uvs->x1, uvs->x3, uvs->x2); - tmp.y0 = pax_flerp4(x0, y0, uvs->y0, uvs->y1, uvs->y3, uvs->y2); - - // Draw it as a series of triangles, rotating with what is essentially matrix multiplication. - for (int i = 0; i < n_div; i++) { - // Perform the rotation. - float x1 = x0 * c_cos - y0 * c_sin; - float y1 = x0 * c_sin + y0 * c_cos; - // Transform UV coords. - tmp.x1 = pax_flerp4(x1, y1, uvs->x0, uvs->x1, uvs->x3, uvs->x2); - tmp.y1 = pax_flerp4(x1, y1, uvs->y0, uvs->y1, uvs->y3, uvs->y2); - // We subtract y0 and y1 from y because our up is -y. - pax_shade_line(buf, color, shader, &tmp, x + x0 * r, y - y0 * r, x + x1 * r, y - y1 * r); - // Assign them yes. - x0 = x1; - y0 = y1; - tmp.x0 = tmp.x1; - tmp.y0 = tmp.y1; - } - - PAX_SUCCESS(); +void pax_shade_outline_arc( + pax_buf_t *buf, + pax_col_t color, + pax_shader_t const *shader, + pax_quadf const *uvs, + float x, + float y, + float r, + float a0, + float a1 +) { + PAX_BUF_CHECK("pax_shade_outline_arc"); + pax_linef tmp; + if (uvs == NULL) { + uvs = &dummy_quad_uvs; + } + + // Simplify the angles slightly. + float a2 = fmodf(a0, M_PI * 2); + a1 += a2 - a0; + a0 = a2; + if (a1 < a0) + PAX_SWAP(float, a0, a1); + if (a1 - a0 > M_PI * 2) { + a1 = M_PI * 2; + a0 = 0; + } + + // Pick an appropriate number of divisions. + int n_div; + matrix_2d_t matrix = buf->stack_2d.value; + float c_r = + r * sqrtf(matrix.a0 * matrix.a0 + matrix.b0 * matrix.b0) * sqrtf(matrix.a1 * matrix.a1 + matrix.b1 * matrix.b1); + if (c_r > 30) + n_div = (a1 - a0) / M_PI * 32 + 1; + else + n_div = (a1 - a0) / M_PI * 16 + 1; + + // Get the sine and cosine of one division, used for rotation in the loop. + float div_angle = (a1 - a0) / n_div; + float c_sin = sinf(div_angle); + float c_cos = cosf(div_angle); + + // Start with a unit vector according to a0. + float x0 = cosf(a0); + float y0 = sinf(a0); + // Transform UV coords. + tmp.x0 = pax_flerp4(x0, y0, uvs->x0, uvs->x1, uvs->x3, uvs->x2); + tmp.y0 = pax_flerp4(x0, y0, uvs->y0, uvs->y1, uvs->y3, uvs->y2); + + // Draw it as a series of triangles, rotating with what is essentially matrix multiplication. + for (int i = 0; i < n_div; i++) { + // Perform the rotation. + float x1 = x0 * c_cos - y0 * c_sin; + float y1 = x0 * c_sin + y0 * c_cos; + // Transform UV coords. + tmp.x1 = pax_flerp4(x1, y1, uvs->x0, uvs->x1, uvs->x3, uvs->x2); + tmp.y1 = pax_flerp4(x1, y1, uvs->y0, uvs->y1, uvs->y3, uvs->y2); + // We subtract y0 and y1 from y because our up is -y. + pax_shade_line(buf, color, shader, &tmp, x + x0 * r, y - y0 * r, x + x1 * r, y - y1 * r); + // Assign them yes. + x0 = x1; + y0 = y1; + tmp.x0 = tmp.x1; + tmp.y0 = tmp.y1; + } + + PAX_SUCCESS(); } // Draw a circle outline with a shader. // If uvs is NULL, a default will be used (0,0; 1,0; 1,1; 0,1). -void pax_shade_outline_circle(pax_buf_t *buf, pax_col_t color, const pax_shader_t *shader, const pax_quadf *uvs, float x, float y, float r) { - pax_shade_outline_arc(buf, color, shader, uvs, x, y, r, 0, M_PI * 2); +void pax_shade_outline_circle( + pax_buf_t *buf, pax_col_t color, pax_shader_t const *shader, pax_quadf const *uvs, float x, float y, float r +) { + pax_shade_outline_arc(buf, color, shader, uvs, x, y, r, 0, M_PI * 2); } @@ -763,119 +721,119 @@ void pax_shade_outline_circle(pax_buf_t *buf, pax_col_t color, const pax_shader_ // Partially outline a shape defined by a list of points. // From and to range from 0 to 1, outside this range is ignored. // Does not close the shape: this must be done manually. -void pax_outline_shape_part(pax_buf_t *buf, pax_col_t color, size_t num_points, const pax_vec2f *points, float from, float to) { - pax_outline_shape_part_cl(buf, color, num_points, points, false, from, to); +void pax_outline_shape_part( + pax_buf_t *buf, pax_col_t color, size_t num_points, pax_vec2f const *points, float from, float to +) { + pax_outline_shape_part_cl(buf, color, num_points, points, false, from, to); } // Outline a shape defined by a list of points. // Does not close the shape: this must be done manually. -void pax_outline_shape(pax_buf_t *buf, pax_col_t color, size_t num_points, const pax_vec2f *points) { - for (size_t i = 0; i < num_points - 1; i++) { - pax_draw_line(buf, color, points[i].x, points[i].y, points[i + 1].x, points[i + 1].y); - } +void pax_outline_shape(pax_buf_t *buf, pax_col_t color, size_t num_points, pax_vec2f const *points) { + for (size_t i = 0; i < num_points - 1; i++) { + pax_draw_line(buf, color, points[i].x, points[i].y, points[i + 1].x, points[i + 1].y); + } } // Partially outline a shape defined by a list of points. // From and to range from 0 to 1, outside this range is ignored. // Closes the shape: there is a line from the first to last point. -void pax_outline_shape_part_cl(pax_buf_t *buf, pax_col_t color, size_t num_points, const pax_vec2f *points, bool close, float from, float to) { - // El simplify. - if (to < from) { - PAX_SWAP(float, to, from); - } - if (from <= 0 && to >= 1) { - pax_outline_shape(buf, color, num_points, points); - return; - } - - // Calculate total distance. - float *dist = malloc(sizeof(float) * num_points); - float total_dist = 0; - float start_dist = 0; - if (!dist) { - PAX_ERROR("pax_outline_shape_part", PAX_ERR_NOMEM); - } - for (size_t i = 0; i < num_points - 1; i++) { - float dx = points[i + 1].x - points[i].x; - float dy = points[i + 1].y - points[i].y; - dist[i] = sqrtf(dx*dx + dy*dy); - total_dist += dist[i]; - } - // Count the returning line if the shape is closed. - if (close && num_points >= 2) { - float dx = points[num_points - 1].x - points[0].x; - float dy = points[num_points - 1].y - points[0].y; - dist[num_points-1] = sqrtf(dx*dx + dy*dy); - total_dist += dist[num_points-1]; - } - - // Do distance calculations. - start_dist = total_dist * from; - total_dist *= to; - - // Draw until the maximum length is reached. - for (size_t i = 0; i < num_points - !close; i++) { - size_t i1 = (i + 1) % num_points; - if (start_dist > dist[i]) { - // Skip the line segment. - } else if (start_dist > 0) { - float dx = points[i1].x - points[i].x; - float dy = points[i1].y - points[i].y; - float part0 = start_dist / dist[i]; - if (total_dist > dist[i]) { - // Draw the end of a segment. - pax_draw_line( - buf, color, - points[i].x + dx * part0, - points[i].y + dy * part0, - points[i1].x, - points[i1].y - ); - } else { - // Draw the middle of a segment. - float part1 = total_dist / dist[i]; - pax_draw_line( - buf, color, - points[i].x + dx * part0, - points[i].y + dy * part0, - points[i].x + dx * part1, - points[i].y + dy * part1 - ); - } - } else if (dist[i] < total_dist) { - // Draw the entire segment. - pax_draw_line(buf, color, points[i].x, points[i].y, points[i1].x, points[i1].y); - } else { - // Draw the start of a segment. - float dx = points[i1].x - points[i].x; - float dy = points[i1].y - points[i].y; - float part = total_dist / dist[i]; - pax_draw_line( - buf, color, - points[i].x, - points[i].y, - points[i].x + dx * part, - points[i].y + dy * part - ); - break; - } - total_dist -= dist[i]; - start_dist -= dist[i]; - } - - free(dist); - PAX_SUCCESS(); +void pax_outline_shape_part_cl( + pax_buf_t *buf, pax_col_t color, size_t num_points, pax_vec2f const *points, bool close, float from, float to +) { + // El simplify. + if (to < from) { + PAX_SWAP(float, to, from); + } + if (from <= 0 && to >= 1) { + pax_outline_shape(buf, color, num_points, points); + return; + } + + // Calculate total distance. + float *dist = malloc(sizeof(float) * num_points); + float total_dist = 0; + float start_dist = 0; + if (!dist) { + PAX_ERROR("pax_outline_shape_part", PAX_ERR_NOMEM); + } + for (size_t i = 0; i < num_points - 1; i++) { + float dx = points[i + 1].x - points[i].x; + float dy = points[i + 1].y - points[i].y; + dist[i] = sqrtf(dx * dx + dy * dy); + total_dist += dist[i]; + } + // Count the returning line if the shape is closed. + if (close && num_points >= 2) { + float dx = points[num_points - 1].x - points[0].x; + float dy = points[num_points - 1].y - points[0].y; + dist[num_points - 1] = sqrtf(dx * dx + dy * dy); + total_dist += dist[num_points - 1]; + } + + // Do distance calculations. + start_dist = total_dist * from; + total_dist *= to; + + // Draw until the maximum length is reached. + for (size_t i = 0; i < num_points - !close; i++) { + size_t i1 = (i + 1) % num_points; + if (start_dist > dist[i]) { + // Skip the line segment. + } else if (start_dist > 0) { + float dx = points[i1].x - points[i].x; + float dy = points[i1].y - points[i].y; + float part0 = start_dist / dist[i]; + if (total_dist > dist[i]) { + // Draw the end of a segment. + pax_draw_line( + buf, + color, + points[i].x + dx * part0, + points[i].y + dy * part0, + points[i1].x, + points[i1].y + ); + } else { + // Draw the middle of a segment. + float part1 = total_dist / dist[i]; + pax_draw_line( + buf, + color, + points[i].x + dx * part0, + points[i].y + dy * part0, + points[i].x + dx * part1, + points[i].y + dy * part1 + ); + } + } else if (dist[i] < total_dist) { + // Draw the entire segment. + pax_draw_line(buf, color, points[i].x, points[i].y, points[i1].x, points[i1].y); + } else { + // Draw the start of a segment. + float dx = points[i1].x - points[i].x; + float dy = points[i1].y - points[i].y; + float part = total_dist / dist[i]; + pax_draw_line(buf, color, points[i].x, points[i].y, points[i].x + dx * part, points[i].y + dy * part); + break; + } + total_dist -= dist[i]; + start_dist -= dist[i]; + } + + free(dist); + PAX_SUCCESS(); } // Outline a shape defined by a list of points. // Closes the shape: there is a line from the first to last point. -void pax_outline_shape_cl(pax_buf_t *buf, pax_col_t color, size_t num_points, const pax_vec2f *points, bool close) { - for (size_t i = 0; i < num_points - 1; i++) { - pax_draw_line(buf, color, points[i].x, points[i].y, points[i + 1].x, points[i + 1].y); - } - if (close && num_points >= 2) { - pax_draw_line(buf, color, points[0].x, points[0].y, points[num_points - 1].x, points[num_points - 1].y); - } +void pax_outline_shape_cl(pax_buf_t *buf, pax_col_t color, size_t num_points, pax_vec2f const *points, bool close) { + for (size_t i = 0; i < num_points - 1; i++) { + pax_draw_line(buf, color, points[i].x, points[i].y, points[i + 1].x, points[i + 1].y); + } + if (close && num_points >= 2) { + pax_draw_line(buf, color, points[0].x, points[0].y, points[num_points - 1].x, points[num_points - 1].y); + } } @@ -885,9 +843,9 @@ void pax_outline_shape_cl(pax_buf_t *buf, pax_col_t color, size_t num_points, co // Transforms a list of points using a given 2D matrix. // Overwrites the list's contents. void pax_transform_shape(size_t num_points, pax_vec2f *points, matrix_2d_t matrix) { - for (size_t i = 0; i < num_points; i++) { - matrix_2d_transform(matrix, &points[i].x, &points[i].y); - } + for (size_t i = 0; i < num_points; i++) { + matrix_2d_transform(matrix, &points[i].x, &points[i].y); + } } // Rounds a polygon with a uniform radius applied to all corners. @@ -895,19 +853,19 @@ void pax_transform_shape(size_t num_points, pax_vec2f *points, matrix_2d_t matri // Capable of dealing with self-intersecting shapes. // Returns the amount of points created. size_t pax_round_shape_uniform(pax_vec2f **output, size_t num_points, pax_vec2f *points, float radius) { - // Fill out an array with the same radius. - float *radii = malloc(sizeof(float) * num_points); - if (!radii) { - PAX_ERROR1("pax_round_shape_uniform", PAX_ERR_NOMEM, 0); - } - for (size_t i = 0; i < num_points; i++) { - radii[i] = radius; - } - - // Just use the more specific operation with simpler parameters. - size_t n_out = pax_round_shape(output, num_points, points, radii); - free(radii); - return n_out; + // Fill out an array with the same radius. + float *radii = malloc(sizeof(float) * num_points); + if (!radii) { + PAX_ERROR1("pax_round_shape_uniform", PAX_ERR_NOMEM, 0); + } + for (size_t i = 0; i < num_points; i++) { + radii[i] = radius; + } + + // Just use the more specific operation with simpler parameters. + size_t n_out = pax_round_shape(output, num_points, points, radii); + free(radii); + return n_out; } // Rounds a polygon with a specific radius per corner. @@ -915,7 +873,7 @@ size_t pax_round_shape_uniform(pax_vec2f **output, size_t num_points, pax_vec2f // Capable of dealing with self-intersecting shapes. // Returns the amount of points created. size_t pax_round_shape(pax_vec2f **output, size_t num_points, pax_vec2f *points, float *radii) { - return 0; + return 0; } @@ -926,123 +884,126 @@ size_t pax_round_shape(pax_vec2f **output, size_t num_points, pax_vec2f *points, // Determine whether the points go clockwise or counter-clockwise. // Does not work for less then 3 points. static bool is_clockwise(int num_points, indexed_point_t *points, int index, int num_test, float dy) { - float result = 0; - - // Simple but unoptimised loop. - for (int i = 0; i < num_test; i++) { - int index0 = i+index; - int index1 = (i+1) % num_test +index; - index0 %= num_points; - index1 %= num_points; - result += (points[index1].x - points[index0].x) * (points[index1].y + points[index0].y + dy); - } - - // Clockwise if result < 0. - return result < 0; + float result = 0; + + // Simple but unoptimised loop. + for (int i = 0; i < num_test; i++) { + int index0 = i + index; + int index1 = (i + 1) % num_test + index; + index0 %= num_points; + index1 %= num_points; + result += (points[index1].x - points[index0].x) * (points[index1].y + points[index0].y + dy); + } + + // Clockwise if result < 0. + return result < 0; } // Gets the slope of a line. // Returns +/- infinity for vertical lines. static inline float line_slope(pax_2vec2f line) { - return (line.y1 - line.y0) / (line.x1 - line.x0); + return (line.y1 - line.y0) / (line.x1 - line.x0); } // Creates a bounding rectangle for a line. static pax_rectf line_bounding_box(pax_2vec2f line) { - // Create a simple bounding box. - pax_rectf box = { - .x = line.x0, - .y = line.y0, - .w = line.x1 - line.x0, - .h = line.y1 - line.y0, - }; - - // Fix width/height so they are positive. - if (box.w < 0) { - box.x += box.w; - box.w = -box.w; - } - if (box.h < 0) { - box.y += box.h; - box.h = -box.h; - } - - return box; + // Create a simple bounding box. + pax_rectf box = { + .x = line.x0, + .y = line.y0, + .w = line.x1 - line.x0, + .h = line.y1 - line.y0, + }; + + // Fix width/height so they are positive. + if (box.w < 0) { + box.x += box.w; + box.w = -box.w; + } + if (box.h < 0) { + box.y += box.h; + box.h = -box.h; + } + + return box; } // Determines whether a point is in the bounding box, but not on it's edge. static inline bool bounding_box_contains(pax_rectf box, pax_vec2f point) { - if (box.w == 0 && box.h == 0) { - return point.x == box.x && point.x == box.y; - } else if (box.w == 0) { - return point.x >= box.x && point.y > box.y && point.x <= box.x + box.w && point.y < box.y + box.h; - } else if (box.h == 0) { - return point.x > box.x && point.y >= box.y && point.x < box.x + box.w && point.y <= box.y + box.h; - } else { - return point.x > box.x && point.y > box.y && point.x < box.x + box.w && point.y < box.y + box.h; - } + if (box.w == 0 && box.h == 0) { + return point.x == box.x && point.x == box.y; + } else if (box.w == 0) { + return point.x >= box.x && point.y > box.y && point.x <= box.x + box.w && point.y < box.y + box.h; + } else if (box.h == 0) { + return point.x > box.x && point.y >= box.y && point.x < box.x + box.w && point.y <= box.y + box.h; + } else { + return point.x > box.x && point.y > box.y && point.x < box.x + box.w && point.y < box.y + box.h; + } } // Tests whether lines A and B intersect. // Does not consider touching lines to intersect. static bool line_intersects_line(pax_2vec2f line_a, pax_2vec2f line_b, pax_vec2f *intersection) { - // If slopes are equal, then it will never intersect. - float rc_a = line_slope(line_a); - float rc_b = line_slope(line_b); - if (rc_a == rc_b || (isinf(rc_a) && isinf(rc_b))) return false; - - // Determine b in Y=a*X+b line formulas. - float dy_a = line_a.y0 - rc_a * line_a.x0; - float dy_b = line_b.y0 - rc_b * line_b.x0; - - // Determine bounding boxes. - pax_rectf box_a = line_bounding_box(line_a); - pax_rectf box_b = line_bounding_box(line_b); - - // Special cases for one of two lines is vertical. - if (isinf(rc_a)) { - float y = rc_b * line_a.x0 + dy_b; - if (y > box_a.y && y < box_a.y + box_a.h) { - if (intersection) { - *intersection = (pax_vec2f) {box_a.x, y}; - } - return true; - } - } - if (isinf(rc_b)) { - float y = rc_a * line_b.x0 + dy_a; - if (y > box_b.y && y < box_b.y + box_b.h) { - if (intersection) { - *intersection = (pax_vec2f) {box_b.x, y}; - } - return true; - } - } - - // Find the intersection point, assuming infinitely long lines. - float x = (dy_b - dy_a) / (rc_a - rc_b); - float y = x * rc_a + dy_a; - - // If this lies within both bounding boxes, the lines intersect. - bool intersects = bounding_box_contains(box_a, (pax_vec2f) {x, y}) && bounding_box_contains(box_b, (pax_vec2f) {x, y}); - if (intersects && intersection) { - *intersection = (pax_vec2f) {x, y}; - } - return intersects; + // If slopes are equal, then it will never intersect. + float rc_a = line_slope(line_a); + float rc_b = line_slope(line_b); + if (rc_a == rc_b || (isinf(rc_a) && isinf(rc_b))) + return false; + + // Determine b in Y=a*X+b line formulas. + float dy_a = line_a.y0 - rc_a * line_a.x0; + float dy_b = line_b.y0 - rc_b * line_b.x0; + + // Determine bounding boxes. + pax_rectf box_a = line_bounding_box(line_a); + pax_rectf box_b = line_bounding_box(line_b); + + // Special cases for one of two lines is vertical. + if (isinf(rc_a)) { + float y = rc_b * line_a.x0 + dy_b; + if (y > box_a.y && y < box_a.y + box_a.h) { + if (intersection) { + *intersection = (pax_vec2f){box_a.x, y}; + } + return true; + } + } + if (isinf(rc_b)) { + float y = rc_a * line_b.x0 + dy_a; + if (y > box_b.y && y < box_b.y + box_b.h) { + if (intersection) { + *intersection = (pax_vec2f){box_b.x, y}; + } + return true; + } + } + + // Find the intersection point, assuming infinitely long lines. + float x = (dy_b - dy_a) / (rc_a - rc_b); + float y = x * rc_a + dy_a; + + // If this lies within both bounding boxes, the lines intersect. + bool intersects = + bounding_box_contains(box_a, (pax_vec2f){x, y}) && bounding_box_contains(box_b, (pax_vec2f){x, y}); + if (intersects && intersection) { + *intersection = (pax_vec2f){x, y}; + } + return intersects; } // Tests whether a line intersects any of the lines in the dataset. // Intersection is NOT counted when only the end points touch. -static bool line_intersects_outline(size_t num_points, const pax_vec2f *raw_points, pax_vec2f start, pax_vec2f end) { - for (size_t i = 0; i < num_points; i++) { - size_t index1 = (i + 1) % num_points; - if (line_intersects_line( - (pax_2vec2f) {start.x, start.y, end.x, end.y}, - (pax_2vec2f) {raw_points[i].x, raw_points[i].y, raw_points[index1].x, raw_points[index1].y}, - NULL - )) return true; - } - return false; +static bool line_intersects_outline(size_t num_points, pax_vec2f const *raw_points, pax_vec2f start, pax_vec2f end) { + for (size_t i = 0; i < num_points; i++) { + size_t index1 = (i + 1) % num_points; + if (line_intersects_line( + (pax_2vec2f){start.x, start.y, end.x, end.y}, + (pax_2vec2f){raw_points[i].x, raw_points[i].y, raw_points[index1].x, raw_points[index1].y}, + NULL + )) + return true; + } + return false; } // Triangulates a shape based on an outline (any shape). @@ -1056,7 +1017,7 @@ static bool line_intersects_outline(size_t num_points, const pax_vec2f *raw_poin // Stores triangles as triple-index pairs in output, which is a dynamically allocated size_t array. // The number of triangles created is num_points - 2. size_t pax_triang_complete(size_t **output, pax_vec2f **additional_points, size_t num_points, pax_vec2f *points) { - return 0; + return 0; } // Triangulates a shape based on an outline (concave, non self-intersecting only). @@ -1066,130 +1027,138 @@ size_t pax_triang_complete(size_t **output, pax_vec2f **additional_points, size_ // // Stores triangles as triple-index pairs in output, which is a dynamically allocated size_t array. // Returns the number of triangles created. -size_t pax_triang_concave(size_t **output, size_t raw_num_points, const pax_vec2f *raw_points) { - // Cannot triangulate with less than 3 points. - if (raw_num_points < 3) { - *output = NULL; - return 0; - } - - // Find an annoying variable. - float dy = 0; - // Create another handy dandy points array which includes their original index. - size_t num_points = raw_num_points; - indexed_point_t *points = malloc(sizeof(indexed_point_t) * num_points); - if (points == NULL) { - *output = NULL; - return 0; - } - - for (size_t i = 0; i < num_points; i++) { - points[i] = (indexed_point_t) { - .vector = raw_points[i], - .index = i - }; - dy = fmaxf(dy, -points[i].y); - } - // The annoy extendsm. - dy *= 2; - dy += 2; - - // The number of triangles is always 2 less than the number of points. - size_t n_tris = num_points - 2; - int tri_index = 0; - size_t *tris = malloc(sizeof(size_t) * n_tris * 3); - if (!tris) { - PAX_LOGE(TAG, "Out of memory for triangulation!"); - *output = NULL; - return 0; - } - // Find the funny ordering. - bool clockwise = is_clockwise(num_points, points, 0, num_points, dy); - - // LOCATE all EARS conTINUousLY. - for (size_t i = 0; i < n_tris; i++) { - // LOOK for an EAR. - for (size_t i = 0; i < num_points; i++) { - // bool attempt = is_clockwise3(num_points, points, i); - bool attempt = is_clockwise(num_points, points, i, 3, dy); - - // It is an ear when the clockwisedness matches and the line does not intersect any of the source lines. - bool is_ear = clockwise == attempt && !line_intersects_outline(raw_num_points, raw_points, points[i].vector, points[(i+2)%num_points].vector); - if (is_ear) { - // We found an EAR, now we CONVERT IT. - tris[tri_index] = points[ i % num_points].index; - tri_index ++; - tris[tri_index] = points[(i+1) % num_points].index; - tri_index ++; - tris[tri_index] = points[(i+2) % num_points].index; - tri_index ++; - - // REMOVE the ear's CENTER POINT. - int remove = (i+1) % num_points; - int post = num_points - remove - 1; - // By means of MEMCPY. - memcpy(&points[remove], &points[remove + 1], sizeof(indexed_point_t) * post); - num_points --; - // Now, we CONTINUE FINIDIGN ERA. - break; - } - } - } - - // Did we find everything? - if (tri_index < n_tris*3) { - // No; abort. - PAX_LOGE(TAG, "Cannot handle shape for triangulation!"); - free(tris); - *output = NULL; - return 0; - - } else { - // Yes. - *output = tris; - return n_tris; - } +size_t pax_triang_concave(size_t **output, size_t raw_num_points, pax_vec2f const *raw_points) { + // Cannot triangulate with less than 3 points. + if (raw_num_points < 3) { + *output = NULL; + return 0; + } + + // Find an annoying variable. + float dy = 0; + // Create another handy dandy points array which includes their original index. + size_t num_points = raw_num_points; + indexed_point_t *points = malloc(sizeof(indexed_point_t) * num_points); + if (points == NULL) { + *output = NULL; + return 0; + } + + for (size_t i = 0; i < num_points; i++) { + points[i] = (indexed_point_t){.vector = raw_points[i], .index = i}; + dy = fmaxf(dy, -points[i].y); + } + // The annoy extendsm. + dy *= 2; + dy += 2; + + // The number of triangles is always 2 less than the number of points. + size_t n_tris = num_points - 2; + int tri_index = 0; + size_t *tris = malloc(sizeof(size_t) * n_tris * 3); + if (!tris) { + PAX_LOGE(TAG, "Out of memory for triangulation!"); + *output = NULL; + return 0; + } + // Find the funny ordering. + bool clockwise = is_clockwise(num_points, points, 0, num_points, dy); + + // LOCATE all EARS conTINUousLY. + for (size_t i = 0; i < n_tris; i++) { + // LOOK for an EAR. + for (size_t i = 0; i < num_points; i++) { + // bool attempt = is_clockwise3(num_points, points, i); + bool attempt = is_clockwise(num_points, points, i, 3, dy); + + // It is an ear when the clockwisedness matches and the line does not intersect any of the source lines. + bool is_ear = clockwise == attempt && !line_intersects_outline( + raw_num_points, + raw_points, + points[i].vector, + points[(i + 2) % num_points].vector + ); + if (is_ear) { + // We found an EAR, now we CONVERT IT. + tris[tri_index] = points[i % num_points].index; + tri_index++; + tris[tri_index] = points[(i + 1) % num_points].index; + tri_index++; + tris[tri_index] = points[(i + 2) % num_points].index; + tri_index++; + + // REMOVE the ear's CENTER POINT. + int remove = (i + 1) % num_points; + int post = num_points - remove - 1; + // By means of MEMCPY. + memcpy(&points[remove], &points[remove + 1], sizeof(indexed_point_t) * post); + num_points--; + // Now, we CONTINUE FINIDIGN ERA. + break; + } + } + } + + // Did we find everything? + if (tri_index < n_tris * 3) { + // No; abort. + PAX_LOGE(TAG, "Cannot handle shape for triangulation!"); + free(tris); + *output = NULL; + return 0; + + } else { + // Yes. + *output = tris; + return n_tris; + } } // Draws a shape which has been previously triangulated. // The number of triangles is num_points - 2. -void pax_draw_shape_triang(pax_buf_t *buf, pax_col_t color, size_t num_points, const pax_vec2f *points, size_t n_tris, const size_t *tris) { - // Then draw all triangles. - for (size_t i = 0, tri_index = 0; i < n_tris; i++) { - pax_draw_tri( - buf, color, - points[tris[tri_index ]].x, points[tris[tri_index ]].y, - points[tris[tri_index+1]].x, points[tris[tri_index+1]].y, - points[tris[tri_index+2]].x, points[tris[tri_index+2]].y - ); - tri_index += 3; - } +void pax_draw_shape_triang( + pax_buf_t *buf, pax_col_t color, size_t num_points, pax_vec2f const *points, size_t n_tris, size_t const *tris +) { + // Then draw all triangles. + for (size_t i = 0, tri_index = 0; i < n_tris; i++) { + pax_draw_tri( + buf, + color, + points[tris[tri_index]].x, + points[tris[tri_index]].y, + points[tris[tri_index + 1]].x, + points[tris[tri_index + 1]].y, + points[tris[tri_index + 2]].x, + points[tris[tri_index + 2]].y + ); + tri_index += 3; + } } // Draw a shape based on an outline. // Closes the shape: no need to have the last point overlap the first. -void pax_draw_shape(pax_buf_t *buf, pax_col_t color, size_t num_points, const pax_vec2f *points) { - // Simply outsource the triangulation. - size_t *tris = NULL; - size_t n_tris = pax_triang_concave(&tris, num_points, points); - if (!tris) { - return; - } - pax_draw_shape_triang(buf, color, num_points, points, n_tris, tris); - // And free el triangles. - free(tris); +void pax_draw_shape(pax_buf_t *buf, pax_col_t color, size_t num_points, pax_vec2f const *points) { + // Simply outsource the triangulation. + size_t *tris = NULL; + size_t n_tris = pax_triang_concave(&tris, num_points, points); + if (!tris) { + return; + } + pax_draw_shape_triang(buf, color, num_points, points, n_tris, tris); + // And free el triangles. + free(tris); } #else // Stub method because the real one isn't compiled in. size_t pax_triang_complete(size_t **output, pax_vec2f **additional_points, size_t num_points, pax_vec2f *points) { - PAX_ERROR1("pax_triang_complete", PAX_ERR_UNSUPPORTED, 0); + PAX_ERROR1("pax_triang_complete", PAX_ERR_UNSUPPORTED, 0); } // Stub method because the real one isn't compiled in. void pax_triang_concave(size_t **output, size_t num_points, pax_vec2f *points) { - PAX_ERROR("pax_triang_concave", PAX_ERR_UNSUPPORTED); + PAX_ERROR("pax_triang_concave", PAX_ERR_UNSUPPORTED); } // Stub method because the real one isn't compiled in. void pax_draw_shape(pax_buf_t *buf, pax_col_t color, size_t num_points, pax_vec2f *points) { - PAX_ERROR("pax_draw_shape", PAX_ERR_UNSUPPORTED); + PAX_ERROR("pax_draw_shape", PAX_ERR_UNSUPPORTED); } #endif diff --git a/src/pax_shapes.h b/src/pax_shapes.h index 0a1d361..6ddde10 100644 --- a/src/pax_shapes.h +++ b/src/pax_shapes.h @@ -1,26 +1,5 @@ -/* - MIT License - - Copyright (c) 2021-2023 Julian Scheffers - - Permission is hereby granted, free of charge, to any person obtaining a copy - of this software and associated documentation files (the "Software"), to deal - in the Software without restriction, including without limitation the rights - to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - copies of the Software, and to permit persons to whom the Software is - furnished to do so, subject to the following conditions: - - The above copyright notice and this permission notice shall be included in all - copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - SOFTWARE. -*/ + +// SPDX-License-Identifier: MIT #ifndef PAX_SHAPES_H #define PAX_SHAPES_H @@ -37,95 +16,139 @@ extern "C" { // From and to range from 0 to 1, but any value is accepted. void pax_vectorise_bezier_part(pax_vec2f *output, size_t num_points, pax_4vec2f control_points, float from, float to); // Convert a cubic bezier curve to line segments, with the given number of points. -void pax_vectorise_bezier (pax_vec2f *output, size_t num_points, pax_4vec2f control_points); +void pax_vectorise_bezier(pax_vec2f *output, size_t num_points, pax_4vec2f control_points); // Draw a cubic bezier curve. // From and to range from 0 to 1, but any value is accepted. -void pax_draw_bezier_part (pax_buf_t *buf, pax_col_t color, pax_4vec2f control_points, float from, float to); +void pax_draw_bezier_part(pax_buf_t *buf, pax_col_t color, pax_4vec2f control_points, float from, float to); // Draw a cubic bezier curve. -void pax_draw_bezier (pax_buf_t *buf, pax_col_t color, pax_4vec2f control_points); +void pax_draw_bezier(pax_buf_t *buf, pax_col_t color, pax_4vec2f control_points); /* ============= ARCS ============ */ // Vectorise an arc outline, angle in radians. -void pax_vectorise_arc (pax_vec2f *output, size_t num_points, float x, float y, float r, float a0, float a1); +void pax_vectorise_arc(pax_vec2f *output, size_t num_points, float x, float y, float r, float a0, float a1); // Vectorise a circle outline. -void pax_vectorise_circle (pax_vec2f *output, size_t num_points, float x, float y, float r); +void pax_vectorise_circle(pax_vec2f *output, size_t num_points, float x, float y, float r); /* ======= SPECIAL EDITIONS ====== */ // Draw a rounded rectangle. -void pax_draw_round_rect (pax_buf_t *buf, pax_col_t color, float x, float y, float width, float height, float radius); +void pax_draw_round_rect(pax_buf_t *buf, pax_col_t color, float x, float y, float width, float height, float radius); // Draw a hollow circle. -void pax_draw_hollow_circle (pax_buf_t *buf, pax_col_t color, float x, float y, float radius0, float radius1); +void pax_draw_hollow_circle(pax_buf_t *buf, pax_col_t color, float x, float y, float radius0, float radius1); // Draw a hollow arc. -void pax_draw_hollow_arc (pax_buf_t *buf, pax_col_t color, float x, float y, float radius0, float radius1, float a0, float a1); +void pax_draw_hollow_arc( + pax_buf_t *buf, pax_col_t color, float x, float y, float radius0, float radius1, float a0, float a1 +); // Draw a hollow arc. -void pax_draw_round_hollow_arc(pax_buf_t *buf, pax_col_t color, float x, float y, float radius0, float radius1, float a0, float a1); +void pax_draw_round_hollow_arc( + pax_buf_t *buf, pax_col_t color, float x, float y, float radius0, float radius1, float a0, float a1 +); // Outline a rounded rectangle. -void pax_outline_round_rect (pax_buf_t *buf, pax_col_t color, float x, float y, float width, float height, float radius); +void pax_outline_round_rect(pax_buf_t *buf, pax_col_t color, float x, float y, float width, float height, float radius); // Draw a hollow circle. -void pax_outline_hollow_circle (pax_buf_t *buf, pax_col_t color, float x, float y, float radius0, float radius1); +void pax_outline_hollow_circle(pax_buf_t *buf, pax_col_t color, float x, float y, float radius0, float radius1); // Draw a hollow arc. -void pax_outline_hollow_arc (pax_buf_t *buf, pax_col_t color, float x, float y, float radius0, float radius1, float a0, float a1); +void pax_outline_hollow_arc( + pax_buf_t *buf, pax_col_t color, float x, float y, float radius0, float radius1, float a0, float a1 +); // Outline a hollow arc. -void pax_outline_round_hollow_arc(pax_buf_t *buf, pax_col_t color, float x, float y, float radius0, float radius1, float a0, float a1); +void pax_outline_round_hollow_arc( + pax_buf_t *buf, pax_col_t color, float x, float y, float radius0, float radius1, float a0, float a1 +); /* ======= OUTLINE EDITIONS ====== */ // Draw a rectangle outline. -void pax_outline_rect (pax_buf_t *buf, pax_col_t color, float x, float y, float width, float height); +void pax_outline_rect(pax_buf_t *buf, pax_col_t color, float x, float y, float width, float height); // Draw a triangle. -void pax_outline_tri (pax_buf_t *buf, pax_col_t color, float x0, float y0, float x1, float y1, float x2, float y2); +void pax_outline_tri(pax_buf_t *buf, pax_col_t color, float x0, float y0, float x1, float y1, float x2, float y2); // Draw an arc outline, angle in radians. -void pax_outline_arc (pax_buf_t *buf, pax_col_t color, float x, float y, float r, float a0, float a1); +void pax_outline_arc(pax_buf_t *buf, pax_col_t color, float x, float y, float r, float a0, float a1); // Draw a circle outline. -void pax_outline_circle (pax_buf_t *buf, pax_col_t color, float x, float y, float r); +void pax_outline_circle(pax_buf_t *buf, pax_col_t color, float x, float y, float r); // Draw a rectangle outline with a shader. // If uvs is NULL, a default will be used (0,0; 1,0; 1,1; 0,1). -void pax_shade_outline_rect (pax_buf_t *buf, pax_col_t color, const pax_shader_t *shader, const pax_quadf *uvs, float x, float y, float width, float height); +void pax_shade_outline_rect( + pax_buf_t *buf, + pax_col_t color, + pax_shader_t const *shader, + pax_quadf const *uvs, + float x, + float y, + float width, + float height +); // Draw a triangle with a shader. // If uvs is NULL, a default will be used (0,0; 1,0; 1,1; 0,1). -void pax_shade_outline_tri (pax_buf_t *buf, pax_col_t color, const pax_shader_t *shader, const pax_trif *uvs, float x0, float y0, float x1, float y1, float x2, float y2); +void pax_shade_outline_tri( + pax_buf_t *buf, + pax_col_t color, + pax_shader_t const *shader, + pax_trif const *uvs, + float x0, + float y0, + float x1, + float y1, + float x2, + float y2 +); // Draw an arc outline with a shader, angle in radians. // If uvs is NULL, a default will be used (0,0; 1,0; 1,1; 0,1). -void pax_shade_outline_arc (pax_buf_t *buf, pax_col_t color, const pax_shader_t *shader, const pax_quadf *uvs, float x, float y, float r, float a0, float a1); +void pax_shade_outline_arc( + pax_buf_t *buf, + pax_col_t color, + pax_shader_t const *shader, + pax_quadf const *uvs, + float x, + float y, + float r, + float a0, + float a1 +); // Draw a circle outline with a shader. // If uvs is NULL, a default will be used (0,0; 1,0; 1,1; 0,1). -void pax_shade_outline_circle (pax_buf_t *buf, pax_col_t color, const pax_shader_t *shader, const pax_quadf *uvs, float x, float y, float r); +void pax_shade_outline_circle( + pax_buf_t *buf, pax_col_t color, pax_shader_t const *shader, pax_quadf const *uvs, float x, float y, float r +); /* =========== OUTLINES ========== */ // Partially outline a shape defined by a list of points. // From and to range from 0 to 1, outside this range is ignored. // Does not close the shape: this must be done manually. -void pax_outline_shape_part (pax_buf_t *buf, pax_col_t color, size_t num_points, const pax_vec2f *points, float from, float to); +void pax_outline_shape_part( + pax_buf_t *buf, pax_col_t color, size_t num_points, pax_vec2f const *points, float from, float to +); // Outline a shape defined by a list of points. // Does not close the shape: this must be done manually. -void pax_outline_shape (pax_buf_t *buf, pax_col_t color, size_t num_points, const pax_vec2f *points); +void pax_outline_shape(pax_buf_t *buf, pax_col_t color, size_t num_points, pax_vec2f const *points); // Partially outline a shape defined by a list of points. // From and to range from 0 to 1, outside this range is ignored. // When close is true, closes the shape; there is a line from the first to last point. -void pax_outline_shape_part_cl(pax_buf_t *buf, pax_col_t color, size_t num_points, const pax_vec2f *points, bool close, float from, float to); +void pax_outline_shape_part_cl( + pax_buf_t *buf, pax_col_t color, size_t num_points, pax_vec2f const *points, bool close, float from, float to +); // Outline a shape defined by a list of points. // When close is true, closes the shape; there is a line from the first to last point. -void pax_outline_shape_cl (pax_buf_t *buf, pax_col_t color, size_t num_points, const pax_vec2f *points, bool close); +void pax_outline_shape_cl(pax_buf_t *buf, pax_col_t color, size_t num_points, pax_vec2f const *points, bool close); /* ===== POLYGON MANIPULATION ==== */ // Transforms a list of points using a given 2D matrix. // Overwrites the list's contents. -void pax_transform_shape(size_t num_points, pax_vec2f *points, matrix_2d_t matrix); +void pax_transform_shape(size_t num_points, pax_vec2f *points, matrix_2d_t matrix); // WARNING: This is a beta feature and it does not work! -// +// // Rounds a polygon with a uniform radius applied to all corners. // Each corner can be rounded up to 50% of the edges it is part of. // Capable of dealing with self-intersecting shapes. // Returns the amount of points created. size_t pax_round_shape_uniform(pax_vec2f **output, size_t num_points, pax_vec2f *points, float radius); // WARNING: This is a beta feature and it does not work! -// +// // Rounds a polygon with a specific radius per corner. // Each corner can be rounded up to 50% of the edges it is part of. // Capable of dealing with self-intersecting shapes. @@ -135,7 +158,7 @@ size_t pax_round_shape(pax_vec2f **output, size_t num_points, pax_vec2f *points, /* ======== TRIANGULATION ======== */ // WARNING: This is a beta feature and it does not work! -// +// // Triangulates a shape based on an outline (any shape). // In effect, this creates triangles which completely fill the shape. // Closes the shape: no need to have the last point overlap the first. @@ -149,7 +172,7 @@ size_t pax_round_shape(pax_vec2f **output, size_t num_points, pax_vec2f *points, // Returns the number of additional points created. size_t pax_triang_complete(size_t **output, pax_vec2f **additional_points, size_t num_points, pax_vec2f *points); // WARNING: Does not work for self-intersecting polygons. -// +// // Triangulates a shape based on an outline (concave, non self-intersecting only). // In effect, this creates triangles which completely fill the shape. // Closes the shape: no need to have the last point overlap the first. @@ -157,19 +180,21 @@ size_t pax_triang_complete(size_t **output, pax_vec2f **additional_points, size_ // // Stores triangles as triple-index pairs in output, which is a dynamically allocated size_t array. // Returns the number of triangles created. -size_t pax_triang_concave (size_t **output, size_t num_points, const pax_vec2f *points); +size_t pax_triang_concave(size_t **output, size_t num_points, pax_vec2f const *points); // WARNING: Does not work for self-intersecting polygons. -// +// // Draw a shape based on an outline. // Closes the shape: no need to have the last point overlap the first. -void pax_draw_shape (pax_buf_t *buf, pax_col_t color, size_t num_points, const pax_vec2f *points); +void pax_draw_shape(pax_buf_t *buf, pax_col_t color, size_t num_points, pax_vec2f const *points); // Draws a shape which has been previously triangulated. // The number of triangles is num_points - 2. -void pax_draw_shape_triang (pax_buf_t *buf, pax_col_t color, size_t num_points, const pax_vec2f *points, size_t num_tris, const size_t *indices); +void pax_draw_shape_triang( + pax_buf_t *buf, pax_col_t color, size_t num_points, pax_vec2f const *points, size_t num_tris, size_t const *indices +); #ifdef __cplusplus } #endif //__cplusplus -#endif //PAX_SHAPES_H +#endif // PAX_SHAPES_H diff --git a/src/pax_text.c b/src/pax_text.c index 84ba73c..c073387 100644 --- a/src/pax_text.c +++ b/src/pax_text.c @@ -1,31 +1,10 @@ -/* - MIT License - Copyright (c) 2021-2023 Julian Scheffers +// SPDX-License-Identifier: MIT - Permission is hereby granted, free of charge, to any person obtaining a copy - of this software and associated documentation files (the "Software"), to deal - in the Software without restriction, including without limitation the rights - to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - copies of the Software, and to permit persons to whom the Software is - furnished to do so, subject to the following conditions: +static char const *TAG = "pax_text"; - The above copyright notice and this permission notice shall be included in all - copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - SOFTWARE. -*/ - -static const char *TAG = "pax_text"; - -#include "pax_internal.h" #include "pax_gfx.h" +#include "pax_internal.h" #include "pax_shaders.h" #include "string.h" @@ -33,36 +12,36 @@ static const char *TAG = "pax_text"; /* ===== PRIVATE TYPEDEFS ====== */ typedef struct { - /* ---- Generic context ---- */ - // Whether or not to render text. - // If false, only calculates size. - bool do_render; - // The buffer to render to, if any. - pax_buf_t *buf; - // The color to draw with. - pax_col_t color; - // The on-screen position of the text. - float x, y; - // The font to use. - const pax_font_t *font; - // The font size to use. - float font_size; - - /* ---- Rendering context ---- */ - // The glyph offset, if any. - const uint8_t *glyph_offs; - // The glyph bits per pixel, if any. - uint8_t glyph_bpp; - // The rendered size of the glyph. - uint8_t render_width, render_height; - // The counted size of the glyph. - uint8_t counted_width, counted_height; - // The rendering offset of the glyph. - uint8_t dx, dy; - // The vertical offset in bits per pixel. - uint8_t vertical_offs; - // Whether to do anti-aliasing and/or interpolation. - bool do_aa; + /* ---- Generic context ---- */ + // Whether or not to render text. + // If false, only calculates size. + bool do_render; + // The buffer to render to, if any. + pax_buf_t *buf; + // The color to draw with. + pax_col_t color; + // The on-screen position of the text. + float x, y; + // The font to use. + pax_font_t const *font; + // The font size to use. + float font_size; + + /* ---- Rendering context ---- */ + // The glyph offset, if any. + uint8_t const *glyph_offs; + // The glyph bits per pixel, if any. + uint8_t glyph_bpp; + // The rendered size of the glyph. + uint8_t render_width, render_height; + // The counted size of the glyph. + uint8_t counted_width, counted_height; + // The rendering offset of the glyph. + uint8_t dx, dy; + // The vertical offset in bits per pixel. + uint8_t vertical_offs; + // Whether to do anti-aliasing and/or interpolation. + bool do_aa; } pax_text_render_t; @@ -73,58 +52,58 @@ typedef struct { // Returns the new string pointer. // Sets the decoded UTF-8 using a pointer. // If the string terminates early or contains invalid unicode, U+FFFD is returned. -char *pax_utf8_getch(const char *cstr, uint32_t *out) { - char len, mask; - if (!*cstr) { - // Null pointer. - *out = 0xfffd; // Something something invalid UTF8. - return (char *) cstr; - } else if (*cstr <= 0x7f) { - // ASCII point. - *out = *cstr; - return (char *) cstr + 1; - } else if ((*cstr & 0xe0) == 0xc0) { - // Two byte point. - len = 2; - mask = 0x1f; - } else if ((*cstr & 0xf0) == 0xe0) { - // Three byte point. - len = 3; - mask = 0x0f; - } else if ((*cstr & 0xf8) == 0xf0) { - // Four byte point. - len = 4; - mask = 0x07; - } else { - // There are no points over four bytes long. - *out = 0xfffd; // Something something invalid UTF8. - return (char *) cstr; - } - - *out = 0; - for (int i = 0; i < len; i++) { - if (!*cstr) { - *out = 0xfffd; // Something something invalid UTF8. - return (char *) cstr; - } - *out <<= 6; - *out |= *cstr & mask; - mask = 0x3f; - cstr ++; - } - return (char *) cstr; +char *pax_utf8_getch(char const *cstr, uint32_t *out) { + char len, mask; + if (!*cstr) { + // Null pointer. + *out = 0xfffd; // Something something invalid UTF8. + return (char *)cstr; + } else if (*cstr <= 0x7f) { + // ASCII point. + *out = *cstr; + return (char *)cstr + 1; + } else if ((*cstr & 0xe0) == 0xc0) { + // Two byte point. + len = 2; + mask = 0x1f; + } else if ((*cstr & 0xf0) == 0xe0) { + // Three byte point. + len = 3; + mask = 0x0f; + } else if ((*cstr & 0xf8) == 0xf0) { + // Four byte point. + len = 4; + mask = 0x07; + } else { + // There are no points over four bytes long. + *out = 0xfffd; // Something something invalid UTF8. + return (char *)cstr; + } + + *out = 0; + for (int i = 0; i < len; i++) { + if (!*cstr) { + *out = 0xfffd; // Something something invalid UTF8. + return (char *)cstr; + } + *out <<= 6; + *out |= *cstr & mask; + mask = 0x3f; + cstr++; + } + return (char *)cstr; } // Returns how many UTF-8 characters a given c-string contains. -size_t pax_utf8_strlen(const char *cstr) { - const char *end = cstr + strlen(cstr); - uint32_t dummy = 0; - size_t len = 0; - while (cstr != end) { - len ++; - cstr = pax_utf8_getch(cstr, &dummy); - } - return 0; +size_t pax_utf8_strlen(char const *cstr) { + char const *end = cstr + strlen(cstr); + uint32_t dummy = 0; + size_t len = 0; + while (cstr != end) { + len++; + cstr = pax_utf8_getch(cstr, &dummy); + } + return 0; } @@ -132,404 +111,461 @@ size_t pax_utf8_strlen(const char *cstr) { /* ======= DRAWING: TEXT ======= */ static uint64_t text_promise_callback(pax_buf_t *buf, pax_col_t tint, void *args0) { - pax_font_bmp_args_t *args = args0; - return !(tint & 0xff000000) ? PAX_PROMISE_INVISIBLE : - (args->bpp == 1 && !args->do_aa) ? PAX_PROMISE_CUTOUT : 0; + pax_font_bmp_args_t *args = args0; + return !(tint & 0xff000000) ? PAX_PROMISE_INVISIBLE : (args->bpp == 1 && !args->do_aa) ? PAX_PROMISE_CUTOUT : 0; } // Pixel-aligned optimisation of pax_shade_rect, used for text. -static void pixel_aligned_render(pax_buf_t *buf, pax_col_t color, const pax_shader_t *shader, - const pax_quadf *uvs, float x, float y, float width, float height) { - // Offset and pixel-align co-ordinates. - x = (int) (0.5 + x + buf->stack_2d.value.a2); - y = (int) (0.5 + y + buf->stack_2d.value.b2); - pax_mark_dirty2(buf, x, y, width, height); - - #if PAX_COMPILE_ORIENTATION - pax_rectf tmp = pax_orient_det_rectf(buf, (pax_rectf) {x, y, width, height}); - x=tmp.x; - y=tmp.y; - width=tmp.w; - height=tmp.h; - - pax_quadf uvs_rotated; - if (buf->orientation & 1) { - uvs_rotated = (pax_quadf) { - uvs->x0, uvs->y0, - uvs->x3, uvs->y3, - uvs->x2, uvs->y2, - uvs->x1, uvs->y1, - }; - uvs = &uvs_rotated; - } - #endif - - // TODO: Re-work as integer UV rendererererrererer device. - #if PAX_COMPILE_MCR - if (pax_do_multicore) { - // Assign worker task. - pax_task_t task = { - .buffer = buf, - .type = PAX_TASK_RECT, - .color = color, - .shader = *shader, - .use_shader = true, - .quad_uvs = *uvs, - .shape = { x, y, width, height }, - .shape_len = 4 - }; - paxmcr_add_task(&task); - // Draw our part. - paxmcr_rect_shaded( - false, - buf, color, shader, x, y, width, height, - uvs->x0, uvs->y0, uvs->x1, uvs->y1, - uvs->x2, uvs->y2, uvs->x3, uvs->y3 - ); - } else - #endif - { - // Single core option. - pax_rect_shaded( - buf, color, shader, x, y, width, height, - uvs->x0, uvs->y0, uvs->x1, uvs->y1, - uvs->x2, uvs->y2, uvs->x3, uvs->y3 - ); - } +static void pixel_aligned_render( + pax_buf_t *buf, + pax_col_t color, + pax_shader_t const *shader, + pax_quadf const *uvs, + float x, + float y, + float width, + float height +) { + // Offset and pixel-align co-ordinates. + x = (int)(0.5 + x + buf->stack_2d.value.a2); + y = (int)(0.5 + y + buf->stack_2d.value.b2); + pax_mark_dirty2(buf, x, y, width, height); + +#if PAX_COMPILE_ORIENTATION + pax_rectf tmp = pax_orient_det_rectf(buf, (pax_rectf){x, y, width, height}); + x = tmp.x; + y = tmp.y; + width = tmp.w; + height = tmp.h; + + pax_quadf uvs_rotated; + if (buf->orientation & 1) { + uvs_rotated = (pax_quadf){ + uvs->x0, + uvs->y0, + uvs->x3, + uvs->y3, + uvs->x2, + uvs->y2, + uvs->x1, + uvs->y1, + }; + uvs = &uvs_rotated; + } +#endif + +// TODO: Re-work as integer UV rendererererrererer device. +#if PAX_COMPILE_MCR + if (pax_do_multicore) { + // Assign worker task. + pax_task_t task = { + .buffer = buf, + .type = PAX_TASK_RECT, + .color = color, + .shader = *shader, + .use_shader = true, + .quad_uvs = *uvs, + .shape = {x, y, width, height}, + .shape_len = 4 + }; + paxmcr_add_task(&task); + // Draw our part. + paxmcr_rect_shaded( + false, + buf, + color, + shader, + x, + y, + width, + height, + uvs->x0, + uvs->y0, + uvs->x1, + uvs->y1, + uvs->x2, + uvs->y2, + uvs->x3, + uvs->y3 + ); + } else +#endif + { + // Single core option. + pax_rect_shaded( + buf, + color, + shader, + x, + y, + width, + height, + uvs->x0, + uvs->y0, + uvs->x1, + uvs->y1, + uvs->x2, + uvs->y2, + uvs->x3, + uvs->y3 + ); + } } // Internal method for monospace bitmapped characters. -static pax_vec2f text_bitmap_mono(pax_text_render_t *ctx, const pax_font_range_t *range, uint32_t glyph) { - if (ctx->do_render && glyph != 0x20) { - // Set up shader. - pax_font_bmp_args_t args = { - .font = ctx->font, - .range = range, - .glyph = glyph, - .glyph_y_mul = (range->bitmap_mono.width * range->bitmap_mono.bpp + 7) / 8, - .glyph_w = range->bitmap_mono.width, - .glyph_h = range->bitmap_mono.height, - .bpp = range->bitmap_mono.bpp, - .ppb = 8 / range->bitmap_mono.bpp, - .do_aa = ctx->do_aa, - }; - args.mask = (1 << args.bpp) - 1; - args.index_mask = (1 << args.ppb) - 1; - - size_t glyph_len = args.glyph_y_mul * range->bitmap_mono.height; - size_t byte_index = glyph_len * (glyph - range->start); - args.bitmap = range->bitmap_mono.glyphs + byte_index; - - pax_shader_t shader = { - .schema_version = 1, - .schema_complement = ~1, - .renderer_id = PAX_RENDERER_ID_SWR, - .promise_callback = text_promise_callback, - .callback_args = &args, - .alpha_promise_0 = true, - .alpha_promise_255 = false, - }; - if (range->bitmap_mono.bpp == 1) { - // Single bit per pixel impl. - shader.callback = PAX_IS_PALETTE(ctx->buf->type) ? pax_shader_font_bmp_pal : pax_shader_font_bmp; - } else { - // Multi-bit per pixel impl. - shader.callback = PAX_IS_PALETTE(ctx->buf->type) ? pax_shader_font_bmp_hi_pal : (ctx->do_aa ? pax_shader_font_bmp_hi_aa : pax_shader_font_bmp_hi); - } - - // And UVs. - pax_quadf uvs = { - .x0 = 0, .y0 = 0, - .x1 = range->bitmap_mono.width, .y1 = 0, - .x2 = range->bitmap_mono.width, .y2 = range->bitmap_mono.height, - .x3 = 0, .y3 = range->bitmap_mono.height, - }; - - // Start drawing, boy! - if (matrix_2d_is_identity1(ctx->buf->stack_2d.value)) { - // Pixel-aligned instead of float optimisation/fix. - pixel_aligned_render( - ctx->buf, ctx->color, - &shader, &uvs, - 0, - 0, - range->bitmap_mono.width, - range->bitmap_mono.height - ); - } else { - pax_shade_rect( - ctx->buf, ctx->color, - &shader, &uvs, - 0, - 0, - range->bitmap_mono.width, - range->bitmap_mono.height - ); - } - pax_join(); - } - - // Size calculation is very simple. - return (pax_vec2f) { - .x = range->bitmap_mono.width, - .y = range->bitmap_mono.height - }; +static pax_vec2f text_bitmap_mono(pax_text_render_t *ctx, pax_font_range_t const *range, uint32_t glyph) { + if (ctx->do_render && glyph != 0x20) { + // Set up shader. + pax_font_bmp_args_t args = { + .font = ctx->font, + .range = range, + .glyph = glyph, + .glyph_y_mul = (range->bitmap_mono.width * range->bitmap_mono.bpp + 7) / 8, + .glyph_w = range->bitmap_mono.width, + .glyph_h = range->bitmap_mono.height, + .bpp = range->bitmap_mono.bpp, + .ppb = 8 / range->bitmap_mono.bpp, + .do_aa = ctx->do_aa, + }; + args.mask = (1 << args.bpp) - 1; + args.index_mask = (1 << args.ppb) - 1; + + size_t glyph_len = args.glyph_y_mul * range->bitmap_mono.height; + size_t byte_index = glyph_len * (glyph - range->start); + args.bitmap = range->bitmap_mono.glyphs + byte_index; + + pax_shader_t shader = { + .schema_version = 1, + .schema_complement = ~1, + .renderer_id = PAX_RENDERER_ID_SWR, + .promise_callback = text_promise_callback, + .callback_args = &args, + .alpha_promise_0 = true, + .alpha_promise_255 = false, + }; + if (range->bitmap_mono.bpp == 1) { + // Single bit per pixel impl. + shader.callback = PAX_IS_PALETTE(ctx->buf->type) ? pax_shader_font_bmp_pal : pax_shader_font_bmp; + } else { + // Multi-bit per pixel impl. + shader.callback = PAX_IS_PALETTE(ctx->buf->type) + ? pax_shader_font_bmp_hi_pal + : (ctx->do_aa ? pax_shader_font_bmp_hi_aa : pax_shader_font_bmp_hi); + } + + // And UVs. + pax_quadf uvs = { + .x0 = 0, + .y0 = 0, + .x1 = range->bitmap_mono.width, + .y1 = 0, + .x2 = range->bitmap_mono.width, + .y2 = range->bitmap_mono.height, + .x3 = 0, + .y3 = range->bitmap_mono.height, + }; + + // Start drawing, boy! + if (matrix_2d_is_identity1(ctx->buf->stack_2d.value)) { + // Pixel-aligned instead of float optimisation/fix. + pixel_aligned_render( + ctx->buf, + ctx->color, + &shader, + &uvs, + 0, + 0, + range->bitmap_mono.width, + range->bitmap_mono.height + ); + } else { + pax_shade_rect( + ctx->buf, + ctx->color, + &shader, + &uvs, + 0, + 0, + range->bitmap_mono.width, + range->bitmap_mono.height + ); + } + pax_join(); + } + + // Size calculation is very simple. + return (pax_vec2f){.x = range->bitmap_mono.width, .y = range->bitmap_mono.height}; } // Internal method for variable pitch bitmapped characters. -static pax_vec2f text_bitmap_var(pax_text_render_t *ctx, const pax_font_range_t *range, uint32_t glyph) { - size_t index = (glyph - range->start); - const pax_bmpv_t *dims = &range->bitmap_var.dims[index]; - if (ctx->do_render && glyph != 0x20) { - // Set up shader. - pax_font_bmp_args_t args = { - .font = ctx->font, - .range = range, - .glyph = glyph, - .glyph_y_mul = (dims->draw_w * range->bitmap_var.bpp + 7) / 8, - .glyph_w = dims->draw_w, - .glyph_h = dims->draw_h, - .bpp = range->bitmap_var.bpp, - .ppb = 8 / range->bitmap_var.bpp, - .do_aa = ctx->do_aa, - }; - args.mask = (1 << args.bpp) - 1; - args.index_mask = args.ppb - 1; - size_t byte_index = dims->index; - args.bitmap = range->bitmap_mono.glyphs + byte_index; - - pax_shader_t shader = { - .schema_version = 1, - .schema_complement = ~1, - .renderer_id = PAX_RENDERER_ID_SWR, - .promise_callback = text_promise_callback, - .callback_args = &args, - .alpha_promise_0 = true, - .alpha_promise_255 = false, - }; - if (range->bitmap_var.bpp == 1) { - // Single bit per pixel impl. - shader.callback = PAX_IS_PALETTE(ctx->buf->type) ? pax_shader_font_bmp_pal : pax_shader_font_bmp; - } else { - // Multi-bit per pixel impl. - shader.callback = PAX_IS_PALETTE(ctx->buf->type) ? pax_shader_font_bmp_hi_pal : (ctx->do_aa ? pax_shader_font_bmp_hi_aa : pax_shader_font_bmp_hi); - } - - // And UVs. - pax_quadf uvs = { - .x0 = 0, .y0 = 0, - .x1 = dims->draw_w, .y1 = 0, - .x2 = dims->draw_w, .y2 = dims->draw_h, - .x3 = 0, .y3 = dims->draw_h, - }; - - // Start drawing, boy! - if (dims->draw_w && dims->draw_h) { - if (matrix_2d_is_identity1(ctx->buf->stack_2d.value)) { - // Pixel-aligned instead of float optimisation/fix. - pixel_aligned_render( - ctx->buf, ctx->color, - &shader, &uvs, - dims->draw_x, - dims->draw_y, - dims->draw_w, - dims->draw_h - ); - } else { - pax_shade_rect( - ctx->buf, ctx->color, - &shader, &uvs, - dims->draw_x, - dims->draw_y, - dims->draw_w, - dims->draw_h - ); - } - pax_join(); - } - } - - // Size calculation is very simple. - return (pax_vec2f) { - .x = dims->measured_width, - .y = range->bitmap_var.height - }; +static pax_vec2f text_bitmap_var(pax_text_render_t *ctx, pax_font_range_t const *range, uint32_t glyph) { + size_t index = (glyph - range->start); + pax_bmpv_t const *dims = &range->bitmap_var.dims[index]; + if (ctx->do_render && glyph != 0x20) { + // Set up shader. + pax_font_bmp_args_t args = { + .font = ctx->font, + .range = range, + .glyph = glyph, + .glyph_y_mul = (dims->draw_w * range->bitmap_var.bpp + 7) / 8, + .glyph_w = dims->draw_w, + .glyph_h = dims->draw_h, + .bpp = range->bitmap_var.bpp, + .ppb = 8 / range->bitmap_var.bpp, + .do_aa = ctx->do_aa, + }; + args.mask = (1 << args.bpp) - 1; + args.index_mask = args.ppb - 1; + size_t byte_index = dims->index; + args.bitmap = range->bitmap_mono.glyphs + byte_index; + + pax_shader_t shader = { + .schema_version = 1, + .schema_complement = ~1, + .renderer_id = PAX_RENDERER_ID_SWR, + .promise_callback = text_promise_callback, + .callback_args = &args, + .alpha_promise_0 = true, + .alpha_promise_255 = false, + }; + if (range->bitmap_var.bpp == 1) { + // Single bit per pixel impl. + shader.callback = PAX_IS_PALETTE(ctx->buf->type) ? pax_shader_font_bmp_pal : pax_shader_font_bmp; + } else { + // Multi-bit per pixel impl. + shader.callback = PAX_IS_PALETTE(ctx->buf->type) + ? pax_shader_font_bmp_hi_pal + : (ctx->do_aa ? pax_shader_font_bmp_hi_aa : pax_shader_font_bmp_hi); + } + + // And UVs. + pax_quadf uvs = { + .x0 = 0, + .y0 = 0, + .x1 = dims->draw_w, + .y1 = 0, + .x2 = dims->draw_w, + .y2 = dims->draw_h, + .x3 = 0, + .y3 = dims->draw_h, + }; + + // Start drawing, boy! + if (dims->draw_w && dims->draw_h) { + if (matrix_2d_is_identity1(ctx->buf->stack_2d.value)) { + // Pixel-aligned instead of float optimisation/fix. + pixel_aligned_render( + ctx->buf, + ctx->color, + &shader, + &uvs, + dims->draw_x, + dims->draw_y, + dims->draw_w, + dims->draw_h + ); + } else { + pax_shade_rect( + ctx->buf, + ctx->color, + &shader, + &uvs, + dims->draw_x, + dims->draw_y, + dims->draw_w, + dims->draw_h + ); + } + pax_join(); + } + } + + // Size calculation is very simple. + return (pax_vec2f){.x = dims->measured_width, .y = range->bitmap_var.height}; } // Determines whether a character lies in a given range. -static inline bool text_range_includes(const pax_font_range_t *range, uint32_t c) { - return c >= range->start && c <= range->end; +static inline bool text_range_includes(pax_font_range_t const *range, uint32_t c) { + return c >= range->start && c <= range->end; } // Internal method for determining the font range to use. // Returns NULL if not in any range. -static const pax_font_range_t *text_get_range(const pax_font_t *font, uint32_t c) { - // Iterate over ranges. - for (size_t i = 0; i < font->n_ranges; i++) { - // Look for the first including the point. - if (text_range_includes(&font->ranges[i], c)) { - return &font->ranges[i]; - } - } - return NULL; +static pax_font_range_t const *text_get_range(pax_font_t const *font, uint32_t c) { + // Iterate over ranges. + for (size_t i = 0; i < font->n_ranges; i++) { + // Look for the first including the point. + if (text_range_includes(&font->ranges[i], c)) { + return &font->ranges[i]; + } + } + return NULL; } // Internal method for rendering text and calculating text size. -static pax_vec2f text_generic(pax_text_render_t *ctx, const char *text) { - // Sanity checks. - if (!text || !*text) { - return (pax_vec2f) { .x=0, .y=0, }; - } - // Render checks. - if (ctx->buf && !pax_do_draw_col(ctx->buf, ctx->color)) { - ctx->do_render = false; - } - - // Set up the fancy size crap. - float size_mul = ctx->font_size / ctx->font->default_size; - if (ctx->do_render) - pax_push_2d(ctx->buf); - if (ctx->do_render) - pax_apply_2d(ctx->buf, matrix_2d_scale(size_mul, size_mul)); - - float x = 0, y = 0; - float max_x = 0; - const pax_font_range_t *range = NULL; - const char *limit = text + strlen(text); - - // Simply loop over all characters. - while (text < limit) { - // Get a character. - uint32_t glyph = 0; - text = pax_utf8_getch(text, &glyph); - - // Is it a newline? - if (glyph == '\r') { - // Consume a '\n' if the next character is one. - char *peek = pax_utf8_getch(text, &glyph); - if (glyph == '\n') text = peek; - goto newline; - } else if (glyph == '\n') { - // Insert a newline. - newline: - if (x > max_x) max_x = x; - if (ctx->do_render) - pax_apply_2d(ctx->buf, matrix_2d_translate(-x, ctx->font->default_size)); - x = 0; - y += ctx->font->default_size; - } else { - if (glyph == 0xa0) { - // Non-breaking space is implicitly converted to space. - glyph = 0x20; - } - - // Try to find a range the glyph is in. - if (!range || !text_range_includes(range, glyph)) { - range = text_get_range(ctx->font, glyph); - } - - pax_vec2f dims = { .x=0, .y=0 }; - if (range) { - // Handle the character. - switch (range->type) { - case PAX_FONT_TYPE_BITMAP_MONO: - dims = text_bitmap_mono(ctx, range, glyph); - break; - case PAX_FONT_TYPE_BITMAP_VAR: - dims = text_bitmap_var(ctx, range, glyph); - break; - } - } else { - // Ignore it for now. - } - x += dims.x; - if (ctx->do_render) - pax_apply_2d(ctx->buf, matrix_2d_translate(dims.x, 0)); - } - } - - if (ctx->do_render) - pax_pop_2d(ctx->buf); - - if (x > max_x) max_x = x; - return (pax_vec2f) { - .x = size_mul * max_x, - .y = size_mul * (y + ctx->font->default_size) - }; +static pax_vec2f text_generic(pax_text_render_t *ctx, char const *text) { + // Sanity checks. + if (!text || !*text) { + return (pax_vec2f){ + .x = 0, + .y = 0, + }; + } + // Render checks. + if (ctx->buf && !pax_do_draw_col(ctx->buf, ctx->color)) { + ctx->do_render = false; + } + + // Set up the fancy size crap. + float size_mul = ctx->font_size / ctx->font->default_size; + if (ctx->do_render) + pax_push_2d(ctx->buf); + if (ctx->do_render) + pax_apply_2d(ctx->buf, matrix_2d_scale(size_mul, size_mul)); + + float x = 0, y = 0; + float max_x = 0; + pax_font_range_t const *range = NULL; + char const *limit = text + strlen(text); + + // Simply loop over all characters. + while (text < limit) { + // Get a character. + uint32_t glyph = 0; + text = pax_utf8_getch(text, &glyph); + + // Is it a newline? + if (glyph == '\r') { + // Consume a '\n' if the next character is one. + char *peek = pax_utf8_getch(text, &glyph); + if (glyph == '\n') + text = peek; + goto newline; + } else if (glyph == '\n') { + // Insert a newline. + newline: + if (x > max_x) + max_x = x; + if (ctx->do_render) + pax_apply_2d(ctx->buf, matrix_2d_translate(-x, ctx->font->default_size)); + x = 0; + y += ctx->font->default_size; + } else { + if (glyph == 0xa0) { + // Non-breaking space is implicitly converted to space. + glyph = 0x20; + } + + // Try to find a range the glyph is in. + if (!range || !text_range_includes(range, glyph)) { + range = text_get_range(ctx->font, glyph); + } + + pax_vec2f dims = {.x = 0, .y = 0}; + if (range) { + // Handle the character. + switch (range->type) { + case PAX_FONT_TYPE_BITMAP_MONO: dims = text_bitmap_mono(ctx, range, glyph); break; + case PAX_FONT_TYPE_BITMAP_VAR: dims = text_bitmap_var(ctx, range, glyph); break; + } + } else { + // Ignore it for now. + } + x += dims.x; + if (ctx->do_render) + pax_apply_2d(ctx->buf, matrix_2d_translate(dims.x, 0)); + } + } + + if (ctx->do_render) + pax_pop_2d(ctx->buf); + + if (x > max_x) + max_x = x; + return (pax_vec2f){.x = size_mul * max_x, .y = size_mul * (y + ctx->font->default_size)}; } // A single line of `pax_center_text`. -static pax_vec2f pax_center_line(pax_buf_t *buf, pax_col_t color, const pax_font_t *font, float font_size, float x, float y, const char *text) { - pax_vec2f dims = pax_text_size(font, font_size, text); - pax_draw_text(buf, color, font, font_size, x-dims.x/2.0f, y, text); - return dims; +static pax_vec2f pax_center_line( + pax_buf_t *buf, pax_col_t color, pax_font_t const *font, float font_size, float x, float y, char const *text +) { + pax_vec2f dims = pax_text_size(font, font_size, text); + pax_draw_text(buf, color, font, font_size, x - dims.x / 2.0f, y, text); + return dims; } // Draw a string with the given font and return it's size. // Text is center-aligned on every line. // Size is before matrix transformation. -pax_vec2f pax_center_text(pax_buf_t *buf, pax_col_t color, const pax_font_t *font, float font_size, float x, float y, const char *text) { - float width = 0, height = 0; - - // Allocate a buffer since we might go splitting up lines. - char *buffer = malloc(strlen(text) + 1); - - // Continuously look for newlines. - while (*text) { - // Look for newline characters. - char *cr = strchr(text, '\r'); - char *lf = strchr(text, '\n'); - // Determine which comes earlier. - char *found = cr && (!lf || cr < lf) ? cr : lf; - // Determine where to keep going afterwards. - char *next = cr && lf && (lf == cr + 1) ? lf+1 : found+1; - - if (!cr && !lf) { - // Nothing special left to do. - pax_vec2f dims = pax_center_line(buf, color, font, font_size, x, y+height, text); - if (dims.x > width) width = dims.x; - height += dims.y; - break; - - } else { - // Copy string segment into buffer. - memcpy(buffer, text, found-text); - buffer[found-text] = 0; - - // Draw this line individually. - pax_vec2f dims = pax_center_line(buf, color, font, font_size, x, y+height, buffer); - if (dims.x > width) width = dims.x; - height += dims.y; - - // Set text pointer to next line. - text = next; - } - } - - // Clean up and return total size. - free(buffer); - return (pax_vec2f) {width, height}; +pax_vec2f pax_center_text( + pax_buf_t *buf, pax_col_t color, pax_font_t const *font, float font_size, float x, float y, char const *text +) { + float width = 0, height = 0; + + // Allocate a buffer since we might go splitting up lines. + char *buffer = malloc(strlen(text) + 1); + + // Continuously look for newlines. + while (*text) { + // Look for newline characters. + char *cr = strchr(text, '\r'); + char *lf = strchr(text, '\n'); + // Determine which comes earlier. + char *found = cr && (!lf || cr < lf) ? cr : lf; + // Determine where to keep going afterwards. + char *next = cr && lf && (lf == cr + 1) ? lf + 1 : found + 1; + + if (!cr && !lf) { + // Nothing special left to do. + pax_vec2f dims = pax_center_line(buf, color, font, font_size, x, y + height, text); + if (dims.x > width) + width = dims.x; + height += dims.y; + break; + + } else { + // Copy string segment into buffer. + memcpy(buffer, text, found - text); + buffer[found - text] = 0; + + // Draw this line individually. + pax_vec2f dims = pax_center_line(buf, color, font, font_size, x, y + height, buffer); + if (dims.x > width) + width = dims.x; + height += dims.y; + + // Set text pointer to next line. + text = next; + } + } + + // Clean up and return total size. + free(buffer); + return (pax_vec2f){width, height}; } // Draw a string with the given font and return it's size. // Size is before matrix transformation. -pax_vec2f pax_draw_text(pax_buf_t *buf, pax_col_t color, const pax_font_t *font, float font_size, float x, float y, const char *text) { - if (!font) PAX_ERROR1("pax_draw_text(font: NULL)", PAX_ERR_PARAM, (pax_vec2f){0}); - pax_text_render_t ctx = { - .do_render = true, - .buf = buf, - .color = color, - .x = x, - .y = y, - .font = font, - .font_size = font_size, - .do_aa = font->recommend_aa, - }; - pax_push_2d (buf); - pax_apply_2d(buf, matrix_2d_translate(x, y)); - pax_vec2f dims = text_generic(&ctx, text); - pax_pop_2d (buf); - return dims; +pax_vec2f pax_draw_text( + pax_buf_t *buf, pax_col_t color, pax_font_t const *font, float font_size, float x, float y, char const *text +) { + if (!font) + PAX_ERROR1("pax_draw_text(font: NULL)", PAX_ERR_PARAM, (pax_vec2f){0}); + pax_text_render_t ctx = { + .do_render = true, + .buf = buf, + .color = color, + .x = x, + .y = y, + .font = font, + .font_size = font_size, + .do_aa = font->recommend_aa, + }; + pax_push_2d(buf); + pax_apply_2d(buf, matrix_2d_translate(x, y)); + pax_vec2f dims = text_generic(&ctx, text); + pax_pop_2d(buf); + return dims; } // DEPRECATION NOTICE: This function is subject to be removed @@ -537,508 +573,531 @@ pax_vec2f pax_draw_text(pax_buf_t *buf, pax_col_t color, const pax_font_t *font, // Size is before matrix transformation. // Font is scaled up with interpolation, overriding it's default. // If font is NULL, the default font (7x9) will be used. -pax_vec2f pax_draw_text_aa(pax_buf_t *buf, pax_col_t color, const pax_font_t *font, float font_size, float x, float y, const char *text) { - if (!font) PAX_ERROR1("pax_draw_text(font: NULL)", PAX_ERR_PARAM, (pax_vec2f){0}); - pax_text_render_t ctx = { - .do_render = true, - .buf = buf, - .color = color, - .x = x, - .y = y, - .font = font, - .font_size = font_size, - .do_aa = true, - }; - pax_push_2d (buf); - pax_apply_2d(buf, matrix_2d_translate(x, y)); - pax_vec2f dims = text_generic(&ctx, text); - pax_pop_2d (buf); - return dims; +pax_vec2f pax_draw_text_aa( + pax_buf_t *buf, pax_col_t color, pax_font_t const *font, float font_size, float x, float y, char const *text +) { + if (!font) + PAX_ERROR1("pax_draw_text(font: NULL)", PAX_ERR_PARAM, (pax_vec2f){0}); + pax_text_render_t ctx = { + .do_render = true, + .buf = buf, + .color = color, + .x = x, + .y = y, + .font = font, + .font_size = font_size, + .do_aa = true, + }; + pax_push_2d(buf); + pax_apply_2d(buf, matrix_2d_translate(x, y)); + pax_vec2f dims = text_generic(&ctx, text); + pax_pop_2d(buf); + return dims; } // DEPRECATION NOTICE: This function is subject to be removed // Draw a string with the given font and return it's size. // Size is before matrix transformation. // Font is scaled up without interpolation, overriding it's default. -pax_vec2f pax_draw_text_noaa(pax_buf_t *buf, pax_col_t color, const pax_font_t *font, float font_size, float x, float y, const char *text) { - if (!font) PAX_ERROR1("pax_draw_text(font: NULL)", PAX_ERR_PARAM, (pax_vec2f){0}); - pax_text_render_t ctx = { - .do_render = true, - .buf = buf, - .color = color, - .x = x, - .y = y, - .font = font, - .font_size = font_size, - .do_aa = false, - }; - pax_push_2d (buf); - pax_apply_2d(buf, matrix_2d_translate(x, y)); - pax_vec2f dims = text_generic(&ctx, text); - pax_pop_2d (buf); - return dims; +pax_vec2f pax_draw_text_noaa( + pax_buf_t *buf, pax_col_t color, pax_font_t const *font, float font_size, float x, float y, char const *text +) { + if (!font) + PAX_ERROR1("pax_draw_text(font: NULL)", PAX_ERR_PARAM, (pax_vec2f){0}); + pax_text_render_t ctx = { + .do_render = true, + .buf = buf, + .color = color, + .x = x, + .y = y, + .font = font, + .font_size = font_size, + .do_aa = false, + }; + pax_push_2d(buf); + pax_apply_2d(buf, matrix_2d_translate(x, y)); + pax_vec2f dims = text_generic(&ctx, text); + pax_pop_2d(buf); + return dims; } // Calculate the size of the string with the given font. // Size is before matrix transformation. // If font is NULL, the default font (7x9) will be used. -pax_vec2f pax_text_size(const pax_font_t *font, float font_size, const char *text) { - if (!font) PAX_ERROR1("pax_draw_text(font: NULL)", PAX_ERR_PARAM, (pax_vec2f){0}); - pax_text_render_t ctx = { - .do_render = false, - .font = font, - .font_size = font_size, - }; - return text_generic(&ctx, text); +pax_vec2f pax_text_size(pax_font_t const *font, float font_size, char const *text) { + if (!font) + PAX_ERROR1("pax_draw_text(font: NULL)", PAX_ERR_PARAM, (pax_vec2f){0}); + pax_text_render_t ctx = { + .do_render = false, + .font = font, + .font_size = font_size, + }; + return text_generic(&ctx, text); } #if 1 // Calculates the size of the region's raw data. -static size_t pax_calc_range_size(const pax_font_range_t *range, bool include_structs) { - size_t range_size = range->end - range->start + 1; - size_t size = include_structs ? sizeof(pax_font_range_t) : 0; - if (range->type == PAX_FONT_TYPE_BITMAP_MONO) { - // Based on array CALCULATION. - size_t bytes_per_line = (range->bitmap_mono.width * range->bitmap_mono.bpp + 7) / 8; - size += range_size * range->bitmap_mono.height * bytes_per_line; - return size; - - } else { - // More complex; based on last index. - pax_bmpv_t max_index = {.index = 0}; - - // Find glyph with highest index. - for (size_t i = 0; i < range_size; i++) { - size_t index = range->bitmap_var.dims[i].index; - if (index > max_index.index) max_index = range->bitmap_var.dims[i]; - } - - // Calculate length. - size_t bytes_per_line = (max_index.draw_w * range->bitmap_var.bpp + 7) / 8; - size += max_index.index + bytes_per_line * max_index.draw_h; - - if (include_structs) { - // Calculate size of pax_bmpv_t included. - size += sizeof(pax_bmpv_t) * range_size; - } - - return size; - } +static size_t pax_calc_range_size(pax_font_range_t const *range, bool include_structs) { + size_t range_size = range->end - range->start + 1; + size_t size = include_structs ? sizeof(pax_font_range_t) : 0; + if (range->type == PAX_FONT_TYPE_BITMAP_MONO) { + // Based on array CALCULATION. + size_t bytes_per_line = (range->bitmap_mono.width * range->bitmap_mono.bpp + 7) / 8; + size += range_size * range->bitmap_mono.height * bytes_per_line; + return size; + + } else { + // More complex; based on last index. + pax_bmpv_t max_index = {.index = 0}; + + // Find glyph with highest index. + for (size_t i = 0; i < range_size; i++) { + size_t index = range->bitmap_var.dims[i].index; + if (index > max_index.index) + max_index = range->bitmap_var.dims[i]; + } + + // Calculate length. + size_t bytes_per_line = (max_index.draw_w * range->bitmap_var.bpp + 7) / 8; + size += max_index.index + bytes_per_line * max_index.draw_h; + + if (include_structs) { + // Calculate size of pax_bmpv_t included. + size += sizeof(pax_bmpv_t) * range_size; + } + + return size; + } } // Calculates the size of the region's bitmap data. -static inline size_t pax_calc_range_bitmap_size(const pax_font_range_t *range) { - return pax_calc_range_size(range, false); +static inline size_t pax_calc_range_bitmap_size(pax_font_range_t const *range) { + return pax_calc_range_size(range, false); } // Calculates the size of the region's bitmap data and structs. -static inline size_t pax_calc_range_total_size(const pax_font_range_t *range) { - return pax_calc_range_size(range, true); +static inline size_t pax_calc_range_total_size(pax_font_range_t const *range) { + return pax_calc_range_size(range, true); } // Reads a number from the file (little-endian). static bool xreadnum(uint64_t *number, size_t bytes, FILE *fd) { - uint64_t out = 0; - size_t read = 0; - for (size_t i = 0; i < bytes; i++) { - uint8_t tmp = 0; - read += fread(&tmp, 1, 1, fd); - out |= tmp << (i*8); - } - *number = out; - return read == bytes; + uint64_t out = 0; + size_t read = 0; + for (size_t i = 0; i < bytes; i++) { + uint8_t tmp = 0; + read += fread(&tmp, 1, 1, fd); + out |= tmp << (i * 8); + } + *number = out; + return read == bytes; } // Writes a number to the file (little-endian). static bool xwritenum(uint64_t number, size_t bytes, FILE *fd) { - size_t written = 0; - for (size_t i = 0; i < bytes; i++) { - uint8_t tmp = number; - written += fwrite(&tmp, 1, 1, fd); - number >>= 8; - } - return written == bytes; + size_t written = 0; + for (size_t i = 0; i < bytes; i++) { + uint8_t tmp = number; + written += fwrite(&tmp, 1, 1, fd); + number >>= 8; + } + return written == bytes; } -static inline bool xfwrite(const void *restrict __ptr, size_t __size, size_t __n, FILE *restrict __s) { - return fwrite(__ptr, __size, __n, __s) == __n; +static inline bool xfwrite(void const *restrict __ptr, size_t __size, size_t __n, FILE *restrict __s) { + return fwrite(__ptr, __size, __n, __s) == __n; } static inline bool xfread(void *restrict __ptr, size_t __size, size_t __n, FILE *restrict __s) { - return fread(__ptr, __size, __n, __s) == __n; + return fread(__ptr, __size, __n, __s) == __n; } // Args: size_t *number, size_t bytes, FILE *fd -#define xreadnum_assert(...) \ - do { \ - if (!xreadnum(__VA_ARGS__)) goto fd_error; \ - } while(0) +#define xreadnum_assert(...) \ + do { \ + if (!xreadnum(__VA_ARGS__)) \ + goto fd_error; \ + } while (0) // Args: size_t number, size_t bytes, FILE *fd -#define xwritenum_assert(...) \ - do { \ - if (!xwritenum(__VA_ARGS__)) goto fd_error; \ - } while(0) +#define xwritenum_assert(...) \ + do { \ + if (!xwritenum(__VA_ARGS__)) \ + goto fd_error; \ + } while (0) // Args: void *ptr, size_t size, size_t n, FILE *fd -#define fread_assert(...) \ - do { \ - if (!xfread(__VA_ARGS__)) goto fd_error; \ - } while(0) +#define fread_assert(...) \ + do { \ + if (!xfread(__VA_ARGS__)) \ + goto fd_error; \ + } while (0) // Args: void *ptr, size_t size, size_t n, FILE *fd -#define fwrite_assert(...) \ - do { \ - if (!xfwrite(__VA_ARGS__)) goto fd_error; \ - } while(0) +#define fwrite_assert(...) \ + do { \ + if (!xfwrite(__VA_ARGS__)) \ + goto fd_error; \ + } while (0) #endif // Loads a font using a file descriptor. // Allocates the entire font in one go, such that only free(pax_font_t*) is required. pax_font_t *pax_load_font(FILE *fd) { - if (!fd) { - PAX_LOGE(TAG, "File pointer is NULL"); - pax_last_error = PAX_ERR_NODATA; - return NULL; - } - pax_font_t *out = NULL; - size_t out_addr; - uint64_t tmpint; - - /* ==== DETERMINE COMPATIBILITY ==== */ - // Validate magic. - char magic_temp[11] = {0,0,0,0,0,0,0,0,0,0,0}; - fread_assert(magic_temp, 1, 11, fd); - if (strcmp(magic_temp, "pax_font_t")) { - // Invalid magic. - PAX_LOGE(TAG, "Invalid magic in font file"); - pax_last_error = PAX_ERR_CORRUPT; - return NULL; - } - - // Validate loader version. - uint64_t font_version; - xreadnum_assert(&font_version, sizeof(uint16_t), fd); - if (font_version != PAX_FONT_LOADER_VERSION) { - // Different font loader version; unsupported. - PAX_LOGE(TAG, "Unsupported font version %hu (supported: %hu)", (uint16_t) font_version, PAX_FONT_LOADER_VERSION); - return NULL; - } - - /* ==== READ METADATA ==== */ - // Number of stored pax_bmpv_t. - uint64_t n_bmpv; - xreadnum_assert(&n_bmpv, sizeof(uint64_t), fd); - - // Size of the combined bitmaps. - uint64_t n_bitmap; - xreadnum_assert(&n_bitmap, sizeof(uint64_t), fd); - - // Size of the font name. - uint64_t n_name; - xreadnum_assert(&n_name, sizeof(uint64_t), fd); - - // Number of ranges in the font. - uint64_t n_ranges; - xreadnum_assert(&n_ranges, sizeof(uint64_t), fd); - - // Calculate required size. - size_t required_size = sizeof(pax_font_t) - + n_ranges * sizeof(pax_font_range_t) - + n_bmpv * sizeof(pax_bmpv_t) - + n_bitmap - + n_name + 1; - - // Validate required size. - if (required_size < PAX_FONT_LOADER_MINUMUM_SIZE) { - // The size is suspiciously small. - PAX_LOGE(TAG, "File corruption: Font size reported is too small (metadata; %zu < %zu)", required_size, PAX_FONT_LOADER_MINUMUM_SIZE); - pax_last_error = PAX_ERR_UNSUPPORTED; - return NULL; - } - - // Allocate memory. - out = malloc(required_size); - out_addr = (size_t) out; - if (!out) { - PAX_LOGE(TAG, "Out of memory for loading font (%zu required)", required_size); - pax_last_error = PAX_ERR_NOMEM; - return NULL; - } - - out->n_ranges = n_ranges; - - // Default point size. - xreadnum_assert(&tmpint, sizeof(uint16_t), fd); - out->default_size = tmpint; - - // Whether antialiassing is recommended. - xreadnum_assert(&tmpint, sizeof(bool), fd); - out->recommend_aa = !!tmpint; - - // Validate again whether the memory is enough. - size_t minimum_size = sizeof(pax_font_t) + out->n_ranges * sizeof(pax_font_range_t) + 3; - if (required_size < minimum_size) { - PAX_LOGE(TAG, "File corruption: Font size reported is too small (range metadata; %zu < %zu)", required_size, minimum_size); - pax_last_error = PAX_ERR_CORRUPT; - free(out); - return NULL; - } - - /* ==== READ RANGES ==== */ - // Calculate addresses. - size_t output_offset = sizeof(pax_font_t) + out->n_ranges * sizeof(pax_font_range_t); - out->ranges = (void *) (out_addr + sizeof(pax_font_t)); - - // Read range data. - size_t bitmap_blob_size = 0; - for (size_t i = 0; i < out->n_ranges; i++) { - pax_font_range_t *range = (pax_font_range_t *) &out->ranges[i]; - - // Range type. - xreadnum_assert(&tmpint, sizeof(uint8_t), fd); - range->type = tmpint; - - // Range start glyph. - xreadnum_assert(&tmpint, sizeof(uint32_t), fd); - range->start = tmpint; - - // Range end glyph. - xreadnum_assert(&tmpint, sizeof(uint32_t), fd); - range->end = tmpint; - - size_t range_size = range->end - range->start + 1; - - if (range->type == PAX_FONT_TYPE_BITMAP_MONO) { - // Glyph width. - xreadnum_assert(&tmpint, sizeof(uint8_t), fd); - range->bitmap_mono.width = tmpint; - - // Glyph height. - xreadnum_assert(&tmpint, sizeof(uint8_t), fd); - range->bitmap_mono.height = tmpint; - - // Glyph bits per pixel. - xreadnum_assert(&tmpint, sizeof(uint8_t), fd); - range->bitmap_mono.bpp = tmpint; - - } else if (range->type == PAX_FONT_TYPE_BITMAP_VAR) { - // Read later: Additional glyph dimensions. - // Calculate the address. - range->bitmap_var.dims = (void *) (out_addr + output_offset); - - // Glyph height. - xreadnum_assert(&tmpint, sizeof(uint8_t), fd); - range->bitmap_var.height = tmpint; - - // Glyph bits per pixel. - xreadnum_assert(&tmpint, sizeof(uint8_t), fd); - range->bitmap_var.bpp = tmpint; - - // Reassert size requirements. - minimum_size += range_size * sizeof(pax_bmpv_t); - output_offset += range_size * sizeof(pax_bmpv_t); - if (required_size < minimum_size) { - PAX_LOGE(TAG, "File corruption: Font size reported is too small (bitmap metadata; %zu < %zu)", required_size, minimum_size); - pax_last_error = PAX_ERR_CORRUPT; - free(out); - return NULL; - } - - // Additional glyph dimensions. - for (size_t x = 0; x < range_size; x++) { - pax_bmpv_t *bmpv = (pax_bmpv_t *) &range->bitmap_var.dims[x]; - - // Bitmap draw X offset. - xreadnum_assert(&tmpint, sizeof(uint8_t), fd); - bmpv->draw_x = tmpint; - - // Bitmap draw Y offset. - xreadnum_assert(&tmpint, sizeof(uint8_t), fd); - bmpv->draw_y = tmpint; - - // Bitmap drawn width. - xreadnum_assert(&tmpint, sizeof(uint8_t), fd); - bmpv->draw_w = tmpint; - - // Bitmap drawn height. - xreadnum_assert(&tmpint, sizeof(uint8_t), fd); - bmpv->draw_h = tmpint; - - // Bitmap measured width. - xreadnum_assert(&tmpint, sizeof(uint8_t), fd); - bmpv->measured_width = tmpint; - - // Bitmap index. - xreadnum_assert(&tmpint, sizeof(uint64_t), fd); - bmpv->index = tmpint; - } - - } else { - // Invalid type. - PAX_LOGE(TAG, "File corruption: Font type invalid (%u in range %zu)", range->type, i); - pax_last_error = PAX_ERR_CORRUPT; - free(out); - return NULL; - } - - // Tally up the bitmap blob size for later use. - bitmap_blob_size += pax_calc_range_bitmap_size(range); - - } - - /* ==== RAW BITMAP DATA ==== */ - fread_assert((void*) (out_addr + output_offset), 1, required_size - output_offset, fd); - for (size_t i = 0; i < out->n_ranges; i++) { - pax_font_range_t *range = (pax_font_range_t *) &out->ranges[i]; - - if (range->type == PAX_FONT_TYPE_BITMAP_MONO) { - // Calculate range glyphs address. - range->bitmap_mono.glyphs = (void *) (out_addr + output_offset); - output_offset += pax_calc_range_bitmap_size(range); - - } else { - // Calculate range glyphs address. - range->bitmap_var.glyphs = (void *) (out_addr + output_offset); - output_offset += pax_calc_range_bitmap_size(range); - } - } - - - return out; - - - fd_error: - pax_last_error = PAX_ERR_NODATA; - PAX_LOGE(TAG, "Error reading from file"); - if (out) free(out); - return NULL; + if (!fd) { + PAX_LOGE(TAG, "File pointer is NULL"); + pax_last_error = PAX_ERR_NODATA; + return NULL; + } + pax_font_t *out = NULL; + size_t out_addr; + uint64_t tmpint; + + /* ==== DETERMINE COMPATIBILITY ==== */ + // Validate magic. + char magic_temp[11] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + fread_assert(magic_temp, 1, 11, fd); + if (strcmp(magic_temp, "pax_font_t")) { + // Invalid magic. + PAX_LOGE(TAG, "Invalid magic in font file"); + pax_last_error = PAX_ERR_CORRUPT; + return NULL; + } + + // Validate loader version. + uint64_t font_version; + xreadnum_assert(&font_version, sizeof(uint16_t), fd); + if (font_version != PAX_FONT_LOADER_VERSION) { + // Different font loader version; unsupported. + PAX_LOGE(TAG, "Unsupported font version %hu (supported: %hu)", (uint16_t)font_version, PAX_FONT_LOADER_VERSION); + return NULL; + } + + /* ==== READ METADATA ==== */ + // Number of stored pax_bmpv_t. + uint64_t n_bmpv; + xreadnum_assert(&n_bmpv, sizeof(uint64_t), fd); + + // Size of the combined bitmaps. + uint64_t n_bitmap; + xreadnum_assert(&n_bitmap, sizeof(uint64_t), fd); + + // Size of the font name. + uint64_t n_name; + xreadnum_assert(&n_name, sizeof(uint64_t), fd); + + // Number of ranges in the font. + uint64_t n_ranges; + xreadnum_assert(&n_ranges, sizeof(uint64_t), fd); + + // Calculate required size. + size_t required_size = + sizeof(pax_font_t) + n_ranges * sizeof(pax_font_range_t) + n_bmpv * sizeof(pax_bmpv_t) + n_bitmap + n_name + 1; + + // Validate required size. + if (required_size < PAX_FONT_LOADER_MINUMUM_SIZE) { + // The size is suspiciously small. + PAX_LOGE( + TAG, + "File corruption: Font size reported is too small (metadata; %zu < %zu)", + required_size, + PAX_FONT_LOADER_MINUMUM_SIZE + ); + pax_last_error = PAX_ERR_UNSUPPORTED; + return NULL; + } + + // Allocate memory. + out = malloc(required_size); + out_addr = (size_t)out; + if (!out) { + PAX_LOGE(TAG, "Out of memory for loading font (%zu required)", required_size); + pax_last_error = PAX_ERR_NOMEM; + return NULL; + } + + out->n_ranges = n_ranges; + + // Default point size. + xreadnum_assert(&tmpint, sizeof(uint16_t), fd); + out->default_size = tmpint; + + // Whether antialiassing is recommended. + xreadnum_assert(&tmpint, sizeof(bool), fd); + out->recommend_aa = !!tmpint; + + // Validate again whether the memory is enough. + size_t minimum_size = sizeof(pax_font_t) + out->n_ranges * sizeof(pax_font_range_t) + 3; + if (required_size < minimum_size) { + PAX_LOGE( + TAG, + "File corruption: Font size reported is too small (range metadata; %zu < %zu)", + required_size, + minimum_size + ); + pax_last_error = PAX_ERR_CORRUPT; + free(out); + return NULL; + } + + /* ==== READ RANGES ==== */ + // Calculate addresses. + size_t output_offset = sizeof(pax_font_t) + out->n_ranges * sizeof(pax_font_range_t); + out->ranges = (void *)(out_addr + sizeof(pax_font_t)); + + // Read range data. + size_t bitmap_blob_size = 0; + for (size_t i = 0; i < out->n_ranges; i++) { + pax_font_range_t *range = (pax_font_range_t *)&out->ranges[i]; + + // Range type. + xreadnum_assert(&tmpint, sizeof(uint8_t), fd); + range->type = tmpint; + + // Range start glyph. + xreadnum_assert(&tmpint, sizeof(uint32_t), fd); + range->start = tmpint; + + // Range end glyph. + xreadnum_assert(&tmpint, sizeof(uint32_t), fd); + range->end = tmpint; + + size_t range_size = range->end - range->start + 1; + + if (range->type == PAX_FONT_TYPE_BITMAP_MONO) { + // Glyph width. + xreadnum_assert(&tmpint, sizeof(uint8_t), fd); + range->bitmap_mono.width = tmpint; + + // Glyph height. + xreadnum_assert(&tmpint, sizeof(uint8_t), fd); + range->bitmap_mono.height = tmpint; + + // Glyph bits per pixel. + xreadnum_assert(&tmpint, sizeof(uint8_t), fd); + range->bitmap_mono.bpp = tmpint; + + } else if (range->type == PAX_FONT_TYPE_BITMAP_VAR) { + // Read later: Additional glyph dimensions. + // Calculate the address. + range->bitmap_var.dims = (void *)(out_addr + output_offset); + + // Glyph height. + xreadnum_assert(&tmpint, sizeof(uint8_t), fd); + range->bitmap_var.height = tmpint; + + // Glyph bits per pixel. + xreadnum_assert(&tmpint, sizeof(uint8_t), fd); + range->bitmap_var.bpp = tmpint; + + // Reassert size requirements. + minimum_size += range_size * sizeof(pax_bmpv_t); + output_offset += range_size * sizeof(pax_bmpv_t); + if (required_size < minimum_size) { + PAX_LOGE( + TAG, + "File corruption: Font size reported is too small (bitmap metadata; %zu < %zu)", + required_size, + minimum_size + ); + pax_last_error = PAX_ERR_CORRUPT; + free(out); + return NULL; + } + + // Additional glyph dimensions. + for (size_t x = 0; x < range_size; x++) { + pax_bmpv_t *bmpv = (pax_bmpv_t *)&range->bitmap_var.dims[x]; + + // Bitmap draw X offset. + xreadnum_assert(&tmpint, sizeof(uint8_t), fd); + bmpv->draw_x = tmpint; + + // Bitmap draw Y offset. + xreadnum_assert(&tmpint, sizeof(uint8_t), fd); + bmpv->draw_y = tmpint; + + // Bitmap drawn width. + xreadnum_assert(&tmpint, sizeof(uint8_t), fd); + bmpv->draw_w = tmpint; + + // Bitmap drawn height. + xreadnum_assert(&tmpint, sizeof(uint8_t), fd); + bmpv->draw_h = tmpint; + + // Bitmap measured width. + xreadnum_assert(&tmpint, sizeof(uint8_t), fd); + bmpv->measured_width = tmpint; + + // Bitmap index. + xreadnum_assert(&tmpint, sizeof(uint64_t), fd); + bmpv->index = tmpint; + } + + } else { + // Invalid type. + PAX_LOGE(TAG, "File corruption: Font type invalid (%u in range %zu)", range->type, i); + pax_last_error = PAX_ERR_CORRUPT; + free(out); + return NULL; + } + + // Tally up the bitmap blob size for later use. + bitmap_blob_size += pax_calc_range_bitmap_size(range); + } + + /* ==== RAW BITMAP DATA ==== */ + fread_assert((void *)(out_addr + output_offset), 1, required_size - output_offset, fd); + for (size_t i = 0; i < out->n_ranges; i++) { + pax_font_range_t *range = (pax_font_range_t *)&out->ranges[i]; + + if (range->type == PAX_FONT_TYPE_BITMAP_MONO) { + // Calculate range glyphs address. + range->bitmap_mono.glyphs = (void *)(out_addr + output_offset); + output_offset += pax_calc_range_bitmap_size(range); + + } else { + // Calculate range glyphs address. + range->bitmap_var.glyphs = (void *)(out_addr + output_offset); + output_offset += pax_calc_range_bitmap_size(range); + } + } + + + return out; + + +fd_error: + pax_last_error = PAX_ERR_NODATA; + PAX_LOGE(TAG, "Error reading from file"); + if (out) + free(out); + return NULL; } // Stores a font to a file descriptor. // This is a memory intensive operation and might not succeed on embedded targets. -void pax_store_font(FILE *fd, const pax_font_t *font) { - if (!fd) { - PAX_LOGE(TAG, "File pointer is NULL"); - pax_last_error = PAX_ERR_NODATA; - return; - } - - /* ==== MAGIC BYTES ==== */ - fwrite_assert("pax_font_t", 1, 11, fd); - - /* ==== PLATFORM METADATA ==== */ - // Font loader version. - // Files written for another version are assumed incompatible. - xwritenum_assert(PAX_FONT_LOADER_VERSION, sizeof(uint16_t), fd); - - /* ==== DETERMINE TOTAL SIZE ==== */ - // Calculate total bitmap size. - size_t total_bitmap = 0; - for (size_t i = 0; i < font->n_ranges; i++) { - total_bitmap += pax_calc_range_bitmap_size(&font->ranges[i]); - } - // Calculate number of pax_bmpv_t stored. - size_t total_bmpv = 0; - for (size_t i = 0; i < font->n_ranges; i++) { - const pax_font_range_t *range = &font->ranges[i]; - - if (range->type == PAX_FONT_TYPE_BITMAP_VAR) { - total_bmpv += range->end - range->start + 1; - } - } - - /* ==== FONT METADATA ==== */ - // Total number of pax_bmpv_t. - xwritenum_assert(total_bmpv, sizeof(uint64_t), fd); - // Total size of the bitmap data. - xwritenum_assert(total_bitmap, sizeof(uint64_t), fd); - // Length excluding null terminator of the name. - xwritenum_assert(strlen(font->name), sizeof(uint64_t), fd); - // Number of ranges in the font. - xwritenum_assert(font->n_ranges, sizeof(uint64_t), fd); - // Default size of the font in pixels. - xwritenum_assert(font->default_size, sizeof(uint16_t), fd); - // Whether usage of antialiasing is recommended. - xwritenum_assert(font->recommend_aa, 1, fd); - - /* ==== RANGE DATA ==== */ - size_t raw_data_offs = 0; - for (size_t i = 0; i < font->n_ranges; i++) { - const pax_font_range_t *range = &font->ranges[i]; - size_t range_size = range->end - range->start + 1; - - // Range type. - xwritenum_assert(range->type, sizeof(uint8_t), fd); - // Range start. - xwritenum_assert(range->start, sizeof(uint32_t), fd); - // Range end. - xwritenum_assert(range->end, sizeof(uint32_t), fd); - - if (range->type == PAX_FONT_TYPE_BITMAP_MONO) { - // Range width. - xwritenum_assert(range->bitmap_mono.width, sizeof(uint8_t), fd); - // Range height. - xwritenum_assert(range->bitmap_mono.height, sizeof(uint8_t), fd); - // Range bit per pixel. - xwritenum_assert(range->bitmap_mono.bpp, sizeof(uint8_t), fd); - } else { - // Range height. - xwritenum_assert(range->bitmap_var.height, sizeof(uint8_t), fd); - // Range bit per pixel. - xwritenum_assert(range->bitmap_var.bpp, sizeof(uint8_t), fd); - - // Range bitmap dimensions. - for (size_t x = 0; x < range_size; x++) { - pax_bmpv_t bmpv = range->bitmap_var.dims[x]; - - // Bitmap draw X offset. - xwritenum_assert(bmpv.draw_x, sizeof(uint8_t), fd); - // Bitmap draw Y offset. - xwritenum_assert(bmpv.draw_y, sizeof(uint8_t), fd); - // Bitmap drawn width. - xwritenum_assert(bmpv.draw_w, sizeof(uint8_t), fd); - // Bitmap drawn height. - xwritenum_assert(bmpv.draw_h, sizeof(uint8_t), fd); - // Bitmap measured width. - xwritenum_assert(bmpv.measured_width, sizeof(uint8_t), fd); - // Bitmap data index. - xwritenum_assert(bmpv.index, sizeof(uint64_t), fd); - } - } - - raw_data_offs += pax_calc_range_bitmap_size(range); - } - - /* ==== RAW DATA ==== */ - // Write bitmap data. - for (size_t i = 0; i < font->n_ranges; i++) { - const pax_font_range_t *range = &font->ranges[i]; - const void *data; - // Determine size of raw data. - size_t length = pax_calc_range_bitmap_size(range); - - // Grab raw data. - if (range->type == PAX_FONT_TYPE_BITMAP_MONO) { - data = range->bitmap_mono.glyphs; - } else { - data = range->bitmap_var.glyphs; - } - - // Write to the data clump. - fwrite_assert(data, 1, length, fd); - } - - // Write font name. - fwrite_assert(font->name, 1, strlen(font->name) + 1, fd); - - return; - - - fd_error: - pax_last_error = PAX_ERR_UNKNOWN; - PAX_LOGE(TAG, "Error writing to file"); -} +void pax_store_font(FILE *fd, pax_font_t const *font) { + if (!fd) { + PAX_LOGE(TAG, "File pointer is NULL"); + pax_last_error = PAX_ERR_NODATA; + return; + } + + /* ==== MAGIC BYTES ==== */ + fwrite_assert("pax_font_t", 1, 11, fd); + + /* ==== PLATFORM METADATA ==== */ + // Font loader version. + // Files written for another version are assumed incompatible. + xwritenum_assert(PAX_FONT_LOADER_VERSION, sizeof(uint16_t), fd); + + /* ==== DETERMINE TOTAL SIZE ==== */ + // Calculate total bitmap size. + size_t total_bitmap = 0; + for (size_t i = 0; i < font->n_ranges; i++) { + total_bitmap += pax_calc_range_bitmap_size(&font->ranges[i]); + } + // Calculate number of pax_bmpv_t stored. + size_t total_bmpv = 0; + for (size_t i = 0; i < font->n_ranges; i++) { + pax_font_range_t const *range = &font->ranges[i]; + + if (range->type == PAX_FONT_TYPE_BITMAP_VAR) { + total_bmpv += range->end - range->start + 1; + } + } + + /* ==== FONT METADATA ==== */ + // Total number of pax_bmpv_t. + xwritenum_assert(total_bmpv, sizeof(uint64_t), fd); + // Total size of the bitmap data. + xwritenum_assert(total_bitmap, sizeof(uint64_t), fd); + // Length excluding null terminator of the name. + xwritenum_assert(strlen(font->name), sizeof(uint64_t), fd); + // Number of ranges in the font. + xwritenum_assert(font->n_ranges, sizeof(uint64_t), fd); + // Default size of the font in pixels. + xwritenum_assert(font->default_size, sizeof(uint16_t), fd); + // Whether usage of antialiasing is recommended. + xwritenum_assert(font->recommend_aa, 1, fd); + + /* ==== RANGE DATA ==== */ + size_t raw_data_offs = 0; + for (size_t i = 0; i < font->n_ranges; i++) { + pax_font_range_t const *range = &font->ranges[i]; + size_t range_size = range->end - range->start + 1; + // Range type. + xwritenum_assert(range->type, sizeof(uint8_t), fd); + // Range start. + xwritenum_assert(range->start, sizeof(uint32_t), fd); + // Range end. + xwritenum_assert(range->end, sizeof(uint32_t), fd); + + if (range->type == PAX_FONT_TYPE_BITMAP_MONO) { + // Range width. + xwritenum_assert(range->bitmap_mono.width, sizeof(uint8_t), fd); + // Range height. + xwritenum_assert(range->bitmap_mono.height, sizeof(uint8_t), fd); + // Range bit per pixel. + xwritenum_assert(range->bitmap_mono.bpp, sizeof(uint8_t), fd); + } else { + // Range height. + xwritenum_assert(range->bitmap_var.height, sizeof(uint8_t), fd); + // Range bit per pixel. + xwritenum_assert(range->bitmap_var.bpp, sizeof(uint8_t), fd); + + // Range bitmap dimensions. + for (size_t x = 0; x < range_size; x++) { + pax_bmpv_t bmpv = range->bitmap_var.dims[x]; + + // Bitmap draw X offset. + xwritenum_assert(bmpv.draw_x, sizeof(uint8_t), fd); + // Bitmap draw Y offset. + xwritenum_assert(bmpv.draw_y, sizeof(uint8_t), fd); + // Bitmap drawn width. + xwritenum_assert(bmpv.draw_w, sizeof(uint8_t), fd); + // Bitmap drawn height. + xwritenum_assert(bmpv.draw_h, sizeof(uint8_t), fd); + // Bitmap measured width. + xwritenum_assert(bmpv.measured_width, sizeof(uint8_t), fd); + // Bitmap data index. + xwritenum_assert(bmpv.index, sizeof(uint64_t), fd); + } + } + + raw_data_offs += pax_calc_range_bitmap_size(range); + } + + /* ==== RAW DATA ==== */ + // Write bitmap data. + for (size_t i = 0; i < font->n_ranges; i++) { + pax_font_range_t const *range = &font->ranges[i]; + void const *data; + // Determine size of raw data. + size_t length = pax_calc_range_bitmap_size(range); + + // Grab raw data. + if (range->type == PAX_FONT_TYPE_BITMAP_MONO) { + data = range->bitmap_mono.glyphs; + } else { + data = range->bitmap_var.glyphs; + } + + // Write to the data clump. + fwrite_assert(data, 1, length, fd); + } + + // Write font name. + fwrite_assert(font->name, 1, strlen(font->name) + 1, fd); + + return; + + +fd_error: + pax_last_error = PAX_ERR_UNKNOWN; + PAX_LOGE(TAG, "Error writing to file"); +} diff --git a/src/pax_text.h b/src/pax_text.h index 2327bbc..b162664 100644 --- a/src/pax_text.h +++ b/src/pax_text.h @@ -1,26 +1,5 @@ -/* - MIT License - Copyright (c) 2021-2023 Julian Scheffers - - Permission is hereby granted, free of charge, to any person obtaining a copy - of this software and associated documentation files (the "Software"), to deal - in the Software without restriction, including without limitation the rights - to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - copies of the Software, and to permit persons to whom the Software is - furnished to do so, subject to the following conditions: - - The above copyright notice and this permission notice shall be included in all - copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - SOFTWARE. -*/ +// SPDX-License-Identifier: MIT #ifndef PAX_TEXT_H #define PAX_TEXT_H @@ -30,6 +9,7 @@ extern "C" { #endif #include "pax_gfx.h" + #include /* ====== UTF-8 UTILITIES ====== */ @@ -38,41 +18,47 @@ extern "C" { // Returns the new string pointer. // Sets the decoded UTF-8 using a pointer. // If the string terminates early or contains invalid unicode, U+FFFD is returned. -char *pax_utf8_getch(const char *cstr, uint32_t *out); +char *pax_utf8_getch(char const *cstr, uint32_t *out); // Returns how many UTF-8 characters a given c-string contains. -size_t pax_utf8_strlen(const char *cstr); +size_t pax_utf8_strlen(char const *cstr); /* ======= DRAWING: TEXT ======= */ // Loads a font using a file descriptor. // Allocates the entire font in one go, such that only free(pax_font_t*) is required. -pax_font_t *pax_load_font (FILE *fd); +pax_font_t *pax_load_font(FILE *fd); // Stores a font to a file descriptor. // This is a memory intensive operation and might not succeed on embedded targets. -void pax_store_font (FILE *fd, const pax_font_t *font); +void pax_store_font(FILE *fd, pax_font_t const *font); // Draw a string with the given font and return it's size. // Text is center-aligned on every line. // Size is before matrix transformation. -pax_vec2f pax_center_text (pax_buf_t *buf, pax_col_t color, const pax_font_t *font, float font_size, float x, float y, const char *text); +pax_vec2f pax_center_text( + pax_buf_t *buf, pax_col_t color, pax_font_t const *font, float font_size, float x, float y, char const *text +); // Draw a string with the given font and return it's size. // Size is before matrix transformation. -pax_vec2f pax_draw_text (pax_buf_t *buf, pax_col_t color, const pax_font_t *font, float font_size, float x, float y, const char *text); +pax_vec2f pax_draw_text( + pax_buf_t *buf, pax_col_t color, pax_font_t const *font, float font_size, float x, float y, char const *text +); // DEPRECATION NOTICE: This function is subject to be removed // Draw a string with the given font and return it's size. // Size is before matrix transformation. // Font is scaled up without interpolation, overriding it's default. -__attribute__((deprecated)) -pax_vec2f pax_draw_text_noaa(pax_buf_t *buf, pax_col_t color, const pax_font_t *font, float font_size, float x, float y, const char *text); +__attribute__((deprecated)) pax_vec2f pax_draw_text_noaa( + pax_buf_t *buf, pax_col_t color, pax_font_t const *font, float font_size, float x, float y, char const *text +); // DEPRECATION NOTICE: This function is subject to be removed // Draw a string with the given font and return it's size. // Size is before matrix transformation. // Font is scaled up with interpolation, overriding it's default. -__attribute__((deprecated)) -pax_vec2f pax_draw_text_aa (pax_buf_t *buf, pax_col_t color, const pax_font_t *font, float font_size, float x, float y, const char *text); +__attribute__((deprecated)) pax_vec2f pax_draw_text_aa( + pax_buf_t *buf, pax_col_t color, pax_font_t const *font, float font_size, float x, float y, char const *text +); // Calculate the size of the string with the given font. // Size is before matrix transformation. -pax_vec2f pax_text_size (const pax_font_t *font, float font_size, const char *text); +pax_vec2f pax_text_size(pax_font_t const *font, float font_size, char const *text); #ifdef __cplusplus } // extern "C" diff --git a/src/pax_types.h b/src/pax_types.h index 2df96ec..a2669e1 100644 --- a/src/pax_types.h +++ b/src/pax_types.h @@ -1,32 +1,11 @@ -/* - MIT License - - Copyright (c) 2021-2023 Julian Scheffers - - Permission is hereby granted, free of charge, to any person obtaining a copy - of this software and associated documentation files (the "Software"), to deal - in the Software without restriction, including without limitation the rights - to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - copies of the Software, and to permit persons to whom the Software is - furnished to do so, subject to the following conditions: - - The above copyright notice and this permission notice shall be included in all - copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - SOFTWARE. -*/ + +// SPDX-License-Identifier: MIT #ifndef PAX_TYPES_H #define PAX_TYPES_H -#include "pax_matrix.h" #include "pax_config.h" +#include "pax_matrix.h" #include @@ -44,31 +23,31 @@ extern "C" { #define PAX_FONT_LOADER_VERSION 1 // The version of the shader schema. // Currently, schema version 0 is accepted and is interpreted as shaders as of v1.0.0. -#define PAX_SHADER_VERSION 1 +#define PAX_SHADER_VERSION 1 // The identifier used by shaders for software rendering. // A different ID is interpreted as belonging to an incompatble method of rendering. // IDs at and above 0x80 are distributed to third party renderers. -#define PAX_RENDERER_ID_SWR 0x00 +#define PAX_RENDERER_ID_SWR 0x00 // A human-readable representation of the current version number. -#define PAX_VERSION_STR "1.1.0" +#define PAX_VERSION_STR "1.1.0" // A numeric representation of the version, one decimal digit per version part (MAJOR.MINOR.PATCH). -#define PAX_VERSION_NUMBER 110 +#define PAX_VERSION_NUMBER 110 // Whether this is a prerelease version of PAX. #define PAX_VERSION_IS_SNAPSHOT false // The MAJOR part of the version (MAJOR.MINOR.PATCH). -#define PAX_VERSION_MAJOR 1 +#define PAX_VERSION_MAJOR 1 // The MINOR part of the version (MAJOR.MINOR.PATCH). -#define PAX_VERSION_MINOR 1 +#define PAX_VERSION_MINOR 1 // The PATCH part of the version (MAJOR.MINOR.PATCH). -#define PAX_VERSION_PATCH 0 +#define PAX_VERSION_PATCH 0 /* ========= ERROR DEFS ========== */ // Unknown error. -#define PAX_ERR_UNKNOWN 1 +#define PAX_ERR_UNKNOWN 1 // All is good. -#define PAX_OK 0 +#define PAX_OK 0 // Buffer pointer is null. #define PAX_ERR_NOBUF -1 // Out of memory. @@ -101,117 +80,117 @@ extern "C" { // The way pixel data is to be stored in a buffer. enum pax_buf_type { - PAX_BUF_1_PAL = 0x20000001, - PAX_BUF_2_PAL = 0x20000002, - PAX_BUF_4_PAL = 0x20000004, - PAX_BUF_8_PAL = 0x20000008, - PAX_BUF_16_PAL = 0x20000010, - - PAX_BUF_1_GREY = 0x10000001, - PAX_BUF_2_GREY = 0x10000002, - PAX_BUF_4_GREY = 0x10000004, - PAX_BUF_8_GREY = 0x10000008, - - PAX_BUF_8_332RGB = 0x00033208, - PAX_BUF_16_565RGB = 0x00056510, - - PAX_BUF_4_1111ARGB = 0x00111104, - PAX_BUF_8_2222ARGB = 0x00444408, - PAX_BUF_16_4444ARGB = 0x00444410, - PAX_BUF_32_8888ARGB = 0x00888820 + PAX_BUF_1_PAL = 0x20000001, + PAX_BUF_2_PAL = 0x20000002, + PAX_BUF_4_PAL = 0x20000004, + PAX_BUF_8_PAL = 0x20000008, + PAX_BUF_16_PAL = 0x20000010, + + PAX_BUF_1_GREY = 0x10000001, + PAX_BUF_2_GREY = 0x10000002, + PAX_BUF_4_GREY = 0x10000004, + PAX_BUF_8_GREY = 0x10000008, + + PAX_BUF_8_332RGB = 0x00033208, + PAX_BUF_16_565RGB = 0x00056510, + + PAX_BUF_4_1111ARGB = 0x00111104, + PAX_BUF_8_2222ARGB = 0x00444408, + PAX_BUF_16_4444ARGB = 0x00444410, + PAX_BUF_32_8888ARGB = 0x00888820 }; // Buffer orientation settings. enum pax_orientation { - // No change in orientation. - PAX_O_UPRIGHT, - // Counter-clockwise rotation. - PAX_O_ROT_CCW, - // Half turn rotation. - PAX_O_ROT_HALF, - // Clockwise rotation. - PAX_O_ROT_CW, - - // Flip horizontally. - PAX_O_FLIP_H, - // Counter-clockwise rotation then flip horizontally. - PAX_O_ROT_CCW_FLIP_H, - // Half turn rotation then flip horizontally. - PAX_O_ROT_HALF_FLIP_H, - // Clockwise rotation then flip horizontally. - PAX_O_ROT_CW_FLIP_H, + // No change in orientation. + PAX_O_UPRIGHT, + // Counter-clockwise rotation. + PAX_O_ROT_CCW, + // Half turn rotation. + PAX_O_ROT_HALF, + // Clockwise rotation. + PAX_O_ROT_CW, + + // Flip horizontally. + PAX_O_FLIP_H, + // Counter-clockwise rotation then flip horizontally. + PAX_O_ROT_CCW_FLIP_H, + // Half turn rotation then flip horizontally. + PAX_O_ROT_HALF_FLIP_H, + // Clockwise rotation then flip horizontally. + PAX_O_ROT_CW_FLIP_H, }; // Flip vertically. -#define PAX_O_FLIP_V PAX_O_ROT_HALF_FLIP_H +#define PAX_O_FLIP_V PAX_O_ROT_HALF_FLIP_H // Counter-clockwise rotation then flip vertically. -#define PAX_O_ROT_CCW_FLIP_V PAX_O_ROT_CW_FLIP_H +#define PAX_O_ROT_CCW_FLIP_V PAX_O_ROT_CW_FLIP_H // Half turn rotation then flip vertically. #define PAX_O_ROT_HALF_FLIP_V PAX_O_FLIP_H // Clockwise rotation then flip vertically. -#define PAX_O_ROT_CW_FLIP_V PAX_O_ROT_CCW_FLIP_H +#define PAX_O_ROT_CW_FLIP_V PAX_O_ROT_CCW_FLIP_H // Flip horizontally then counter-clockwise rotation. -#define PAX_O_FLIP_H_ROT_CCW PAX_O_ROT_CW_FLIP_H +#define PAX_O_FLIP_H_ROT_CCW PAX_O_ROT_CW_FLIP_H // Flip horizontally then half turn rotation. #define PAX_O_FLIP_H_ROT_HALF PAX_O_ROT_HALF_FLIP_H // Flip horizontally then clockwise rotation. -#define PAX_O_FLIP_H_ROT_CW PAX_O_ROT_CCW_FLIP_H +#define PAX_O_FLIP_H_ROT_CW PAX_O_ROT_CCW_FLIP_H // Flip vertically then counter-clockwise rotation. -#define PAX_O_FLIP_V_ROT_CCW PAX_O_ROT_CCW_FLIP_H +#define PAX_O_FLIP_V_ROT_CCW PAX_O_ROT_CCW_FLIP_H // Flip vertically then half turn rotation. #define PAX_O_FLIP_V_ROT_HALF PAX_O_FLIP_H // Flip vertically then clockwise rotation. -#define PAX_O_FLIP_V_ROT_CW PAX_O_ROT_CW_FLIP_H +#define PAX_O_FLIP_V_ROT_CW PAX_O_ROT_CW_FLIP_H // A way in which to perform word wrap. enum pax_word_wrap { - // Do not perform word wrap. - PAX_WW_NONE, - // Word wrap by the letter. - PAX_WW_LETTER, - // Word wrap by the word. - PAX_WW_WORD, - // Word wrap with inter-word justfication. - PAX_WW_JUSTIFY, + // Do not perform word wrap. + PAX_WW_NONE, + // Word wrap by the letter. + PAX_WW_LETTER, + // Word wrap by the word. + PAX_WW_WORD, + // Word wrap with inter-word justfication. + PAX_WW_JUSTIFY, }; // Type of task to do. // Things like text and arcs will decompose to rects and triangles. enum pax_task_type { - // Rectangle draw. - PAX_TASK_RECT, - // Triangle draw. - PAX_TASK_TRI, - // Stop MCR workder. - PAX_TASK_STOP, + // Rectangle draw. + PAX_TASK_RECT, + // Triangle draw. + PAX_TASK_TRI, + // Stop MCR workder. + PAX_TASK_STOP, }; // Distinguishes between ways to draw fonts. enum pax_font_type { - // For monospace bitmapped fonts. - PAX_FONT_TYPE_BITMAP_MONO, - // For variable pitch bitmapped fonts. - PAX_FONT_TYPE_BITMAP_VAR, + // For monospace bitmapped fonts. + PAX_FONT_TYPE_BITMAP_MONO, + // For variable pitch bitmapped fonts. + PAX_FONT_TYPE_BITMAP_VAR, }; -typedef enum pax_buf_type pax_buf_type_t; -typedef enum pax_orientation pax_orientation_t; -typedef enum pax_word_wrap pax_word_wrap_t; -typedef enum pax_task_type pax_task_type_t; -typedef enum pax_font_type pax_font_type_t; +typedef enum pax_buf_type pax_buf_type_t; +typedef enum pax_orientation pax_orientation_t; +typedef enum pax_word_wrap pax_word_wrap_t; +typedef enum pax_task_type pax_task_type_t; +typedef enum pax_font_type pax_font_type_t; // Promises that the shape will be fully opaque when drawn. -#define PAX_PROMISE_OPAQUE 0x01 +#define PAX_PROMISE_OPAQUE 0x01 // Promises that the shape will be fully transparent when drawn. -#define PAX_PROMISE_INVISIBLE 0x02 +#define PAX_PROMISE_INVISIBLE 0x02 // Promises that the shape will be drawn as a cutout (either fully opaque or fully transparent for a given pixel). -#define PAX_PROMISE_CUTOUT 0x03 +#define PAX_PROMISE_CUTOUT 0x03 // Promises that the shader does not need the UVs. -#define PAX_PROMISE_IGNORE_UVS 0x04 +#define PAX_PROMISE_IGNORE_UVS 0x04 // Promises that the shader ignores the existing color. -#define PAX_PROMISE_IGNORE_BASE 0x08 +#define PAX_PROMISE_IGNORE_BASE 0x08 struct matrix_stack_2d; @@ -226,42 +205,42 @@ struct pax_task; union pax_col_union; -typedef struct pax_buf pax_buf_t; -typedef struct pax_shader pax_shader_t; -typedef struct pax_task pax_task_t; -typedef struct pax_shader_ctx pax_shader_ctx_t; -typedef struct pax_bmpv pax_bmpv_t; -typedef struct pax_font pax_font_t; -typedef struct pax_font_range pax_font_range_t; +typedef struct pax_buf pax_buf_t; +typedef struct pax_shader pax_shader_t; +typedef struct pax_task pax_task_t; +typedef struct pax_shader_ctx pax_shader_ctx_t; +typedef struct pax_bmpv pax_bmpv_t; +typedef struct pax_font pax_font_t; +typedef struct pax_font_range pax_font_range_t; -typedef int32_t pax_err_t; -typedef uint32_t pax_col_t; -typedef union pax_col_union pax_col_union_t; +typedef int32_t pax_err_t; +typedef uint32_t pax_col_t; +typedef union pax_col_union pax_col_union_t; // Union for splitting ARGB. union pax_col_union { #if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ - struct { - uint8_t a, r, g, b; - }; + struct { + uint8_t a, r, g, b; + }; #else - struct { - uint8_t b, g, r, a; - }; + struct { + uint8_t b, g, r, a; + }; #endif - pax_col_t col; + pax_col_t col; }; // Helper for color conversion. // Used for both buffer type to ARGB and vice versa. // Buffer argument is mostly used for images with palette. -typedef pax_col_t (*pax_col_conv_t)(const pax_buf_t *buf, pax_col_t color); +typedef pax_col_t (*pax_col_conv_t)(pax_buf_t const *buf, pax_col_t color); // Helper for setting pixels in drawing routines. // Used to allow optimising away alpha in colors. typedef void (*pax_setter_t)(pax_buf_t *buf, pax_col_t color, int x, int y); // Helper for getting pixels in drawing routines. // Used to allow optimising away inline branching. -typedef pax_col_t (*pax_index_getter_t)(const pax_buf_t *buf, int index); +typedef pax_col_t (*pax_index_getter_t)(pax_buf_t const *buf, int index); // Helper for setting pixels in drawing routines. // Used to allow optimising away color conversion. typedef void (*pax_index_setter_t)(pax_buf_t *buf, pax_col_t color, int index); @@ -275,168 +254,168 @@ typedef pax_col_t (*pax_shader_func_v0_t)(pax_col_t tint, int x, int y, float u, // Function pointer for shader callback. // Tint is the color parameter to the pax_shade_xxx function. // It is assumed that color math is done by the shader based on the "existing" parameter. -typedef pax_col_t (*pax_shader_func_v1_t)(pax_col_t tint, pax_col_t existing, int x, int y, float u, float v, void *args); +typedef pax_col_t (*pax_shader_func_v1_t)( + pax_col_t tint, pax_col_t existing, int x, int y, float u, float v, void *args +); // A simple linked list data structure used to store matrices in a stack. struct matrix_stack_2d { - matrix_stack_2d_t *parent; - matrix_2d_t value; + matrix_stack_2d_t *parent; + matrix_2d_t value; }; // The main data structure in PAX. // Stores pixel data and matrix information among other things. struct pax_buf { - // Buffer type, color modes, etc. - pax_buf_type_t type; - // Whether to perform free on the buffer on deinit. - bool do_free; - // Whether to perform free on the palette on deinit. - bool do_free_pal; - // Whether to reverse the endianness of the buffer. - bool reverse_endianness; - union { - // Shorthand for 8bpp buffer. - uint8_t *buf_8bpp; - // Shorthand for 16bpp buffer. - uint16_t *buf_16bpp; - // Shorthand for 32bpp buffer. - uint32_t *buf_32bpp; - // Buffer pointer. - void *buf; - }; - // Bits per pixel. - int bpp; - - union { - // Pallette for buffers with a pallette type. - __attribute__((deprecated)) - pax_col_t *pallette; - // Pallette for buffers with a pallette type. - pax_col_t *palette; - }; - union { - // The number of colors in the pallette. - __attribute__((deprecated)) - size_t pallette_size; - // The number of colors in the pallette. - size_t palette_size; - }; - - // Width in pixels. - int width; - // Height in pixels. - int height; - - // Dirty x (top left). - int dirty_x0; - // Dirty y (top left). - int dirty_y0; - // Dirty x (bottom right). - int dirty_x1; - // Dirty y (bottom right). - int dirty_y1; - - // Color to buffer function to use. - pax_col_conv_t col2buf; - // Buffer to color function to use. - pax_col_conv_t buf2col; - - // Setter to use to write a pixel index. - pax_index_setter_t setter; - // Getter to use to read a pixel index. - pax_index_getter_t getter; - - // Clip rectangle. - // Shapes are only drawn inside the clip rectangle. - // This excludes PNG decoding functions. - pax_recti clip; - // Matrix stack. - // The top most entry is used to transform shapes. - matrix_stack_2d_t stack_2d; - - // Orientation setting. - pax_orientation_t orientation; + // Buffer type, color modes, etc. + pax_buf_type_t type; + // Whether to perform free on the buffer on deinit. + bool do_free; + // Whether to perform free on the palette on deinit. + bool do_free_pal; + // Whether to reverse the endianness of the buffer. + bool reverse_endianness; + union { + // Shorthand for 8bpp buffer. + uint8_t *buf_8bpp; + // Shorthand for 16bpp buffer. + uint16_t *buf_16bpp; + // Shorthand for 32bpp buffer. + uint32_t *buf_32bpp; + // Buffer pointer. + void *buf; + }; + // Bits per pixel. + int bpp; + + union { + // Pallette for buffers with a pallette type. + __attribute__((deprecated)) pax_col_t *pallette; + // Pallette for buffers with a pallette type. + pax_col_t *palette; + }; + union { + // The number of colors in the pallette. + __attribute__((deprecated)) size_t pallette_size; + // The number of colors in the pallette. + size_t palette_size; + }; + + // Width in pixels. + int width; + // Height in pixels. + int height; + + // Dirty x (top left). + int dirty_x0; + // Dirty y (top left). + int dirty_y0; + // Dirty x (bottom right). + int dirty_x1; + // Dirty y (bottom right). + int dirty_y1; + + // Color to buffer function to use. + pax_col_conv_t col2buf; + // Buffer to color function to use. + pax_col_conv_t buf2col; + + // Setter to use to write a pixel index. + pax_index_setter_t setter; + // Getter to use to read a pixel index. + pax_index_getter_t getter; + + // Clip rectangle. + // Shapes are only drawn inside the clip rectangle. + // This excludes PNG decoding functions. + pax_recti clip; + // Matrix stack. + // The top most entry is used to transform shapes. + matrix_stack_2d_t stack_2d; + + // Orientation setting. + pax_orientation_t orientation; }; // A shader definition, used by pax_shade_ methods. struct pax_shader { - // Version of the shader schema this was made for. - uint8_t schema_version; - // Bitwise inversion of schema version. - uint8_t schema_complement; - // Rendering type of this shader. - uint8_t renderer_id; - // Optional callback which is used to make contextual promises. - void *promise_callback; - // Callback which defines the colors to output. - void *callback; - // Shader arguments. - void *callback_args; - // Whether to promise that an alpha of 0 in tint will return a fully transparent. - bool alpha_promise_0; - // Whether to promise that an alpha of 255 in tint will return a fully opaque. - bool alpha_promise_255; + // Version of the shader schema this was made for. + uint8_t schema_version; + // Bitwise inversion of schema version. + uint8_t schema_complement; + // Rendering type of this shader. + uint8_t renderer_id; + // Optional callback which is used to make contextual promises. + void *promise_callback; + // Callback which defines the colors to output. + void *callback; + // Shader arguments. + void *callback_args; + // Whether to promise that an alpha of 0 in tint will return a fully transparent. + bool alpha_promise_0; + // Whether to promise that an alpha of 255 in tint will return a fully opaque. + bool alpha_promise_255; }; // Information relevant to each character of a variable pitch font. struct pax_bmpv { - // The position of the drawn portion. - int8_t draw_x, draw_y; - // The size of the drawn portion. - uint8_t draw_w, draw_h; - // The measured width of the glyph. - uint8_t measured_width; - // The index in the glyphs data for this glyph. - size_t index; + // The position of the drawn portion. + int8_t draw_x, draw_y; + // The size of the drawn portion. + uint8_t draw_w, draw_h; + // The measured width of the glyph. + uint8_t measured_width; + // The index in the glyphs data for this glyph. + size_t index; }; // Information relevant for the entirety of each font. struct pax_font { - // The searchable name of the font. - const char *name; - // The number of ranges included in the font. - size_t n_ranges; - // The ranges included in the font. - const pax_font_range_t *ranges; - // Default point size. - uint16_t default_size; - // Whether or not it is recommended to use antialiasing. - // Applies to pax_draw_text, but not it's variants. - bool recommend_aa; + // The searchable name of the font. + char const *name; + // The number of ranges included in the font. + size_t n_ranges; + // The ranges included in the font. + pax_font_range_t const *ranges; + // Default point size. + uint16_t default_size; + // Whether or not it is recommended to use antialiasing. + // Applies to pax_draw_text, but not it's variants. + bool recommend_aa; }; // Describes a range of glyphs in a font. struct pax_font_range { - // The type of font range. - pax_font_type_t type; - // First character in range. - uint32_t start; - // Last character in range. - uint32_t end; - union { - // Monospace, bitmapped fonts. - struct { - // The raw glyph bytes. - const uint8_t *glyphs; - // The width of all glyphs. - uint8_t width; - // The height of all glyphs. - uint8_t height; - // The Bits Per Pixel of all glyphs. - uint8_t bpp; - } bitmap_mono; - // Variable pitch, bitmapped fonts. - struct { - // The raw glyph bytes. - const uint8_t *glyphs; - // Additional dimensions defined per glyph. - const pax_bmpv_t *dims; - // The height of all glyphs. - uint8_t height; - // The Bits Per Pixel of all glyphs. - uint8_t bpp; - } bitmap_var; - }; + // The type of font range. + pax_font_type_t type; + // First character in range. + uint32_t start; + // Last character in range. + uint32_t end; + union { + // Monospace, bitmapped fonts. + struct { + // The raw glyph bytes. + uint8_t const *glyphs; + // The width of all glyphs. + uint8_t width; + // The height of all glyphs. + uint8_t height; + // The Bits Per Pixel of all glyphs. + uint8_t bpp; + } bitmap_mono; + // Variable pitch, bitmapped fonts. + struct { + // The raw glyph bytes. + uint8_t const *glyphs; + // Additional dimensions defined per glyph. + pax_bmpv_t const *dims; + // The height of all glyphs. + uint8_t height; + // The Bits Per Pixel of all glyphs. + uint8_t bpp; + } bitmap_var; + }; }; // A task to perform, used by multicore rendering. @@ -444,40 +423,40 @@ struct pax_font_range { // If you change the shader object's content (AKA the value that args points to), // You should run pax_join before making the change. struct pax_task { - // The buffer to apply this task to. - pax_buf_t *buffer; - // The type of thing to do. - pax_task_type_t type; - // Color to use. - pax_col_t color; - // Shader to use. - pax_shader_t shader; - // Whether to use a shader. - bool use_shader; - // UVs to use. - union { - // UVs to use for rects and arcs. - pax_quadf quad_uvs; - // UVs to use for triangle. - pax_trif tri_uvs; - }; - // Additional parameters. - // This is an array of floats for X, Y, and dimensions of shapes. - float shape[8]; - // Number of floats in the shape array. - size_t shape_len; + // The buffer to apply this task to. + pax_buf_t *buffer; + // The type of thing to do. + pax_task_type_t type; + // Color to use. + pax_col_t color; + // Shader to use. + pax_shader_t shader; + // Whether to use a shader. + bool use_shader; + // UVs to use. + union { + // UVs to use for rects and arcs. + pax_quadf quad_uvs; + // UVs to use for triangle. + pax_trif tri_uvs; + }; + // Additional parameters. + // This is an array of floats for X, Y, and dimensions of shapes. + float shape[8]; + // Number of floats in the shape array. + size_t shape_len; }; // Context used at drawing time for shaders. struct pax_shader_ctx { - // The callback internally used per pixel. - pax_shader_func_v1_t callback; - // The args to throw at the callback. - void *callback_args; - // Whether to skip drawing. - bool skip; - // Whether to do a get the pixel value for merging. - bool do_getter; + // The callback internally used per pixel. + pax_shader_func_v1_t callback; + // The args to throw at the callback. + void *callback_args; + // Whether to skip drawing. + bool skip; + // Whether to do a get the pixel value for merging. + bool do_getter; }; // The absolute minimum possible size a valid font can be in memory. @@ -487,4 +466,4 @@ struct pax_shader_ctx { } #endif //__cplusplus -#endif //PAX_TYPES_H +#endif // PAX_TYPES_H