├── .Doxyfile
├── .Doxyfile.md
├── .editorconfig
├── .github
└── workflows
│ └── pntr.yml
├── .gitignore
├── CMakeLists.txt
├── LICENSE.md
├── README.md
├── examples
├── CMakeLists.txt
├── pntr_examples.c
├── pntr_examples_alphamask.h
├── pntr_examples_alphamask.png
├── pntr_examples_fonts.h
├── pntr_examples_fonts.png
├── pntr_examples_image.h
├── pntr_examples_image.png
├── pntr_examples_resize.h
├── pntr_examples_resize.png
├── pntr_examples_rotate.h
├── pntr_examples_rotate.png
├── pntr_examples_shapes.h
├── pntr_examples_shapes.png
├── pntr_examples_sprite.h
├── pntr_examples_sprite.png
├── pntr_examples_thick.h
├── pntr_examples_thick.png
└── resources
│ ├── bmfont.png
│ ├── bunny.png
│ ├── doxygen-awesome.css
│ ├── logo-128x128.png
│ ├── logo-32x32.png
│ ├── logo-55x55.png
│ ├── readme.txt
│ ├── scarfy.png
│ ├── ttyfont-16x16.png
│ └── tuffy.ttf
├── extensions
├── pntr_cute_png.h
├── pntr_stb_image.h
└── pntr_stb_image_write.h
├── external
├── cute_png.h
├── font8x8_basic.h
├── stb_image.h
├── stb_image_write.h
├── stb_truetype.h
└── utf8.h
├── package.json
├── pntr.h
├── pntr_assert.h
└── test
├── CMakeLists.txt
├── pntr_test.c
├── resources
├── font-tty-8x8.png
├── font.png
├── image.png
├── logo-256x256.png
├── text.txt
└── tuffy.ttf
└── unit.h
/.Doxyfile.md:
--------------------------------------------------------------------------------
1 | # pntr: Image Manipulation Library
2 |
3 | *[pntr](https://github.com/robloach/pntr) is an header-only CPU graphics library for C99 or C++, with a focus on ease-of-use.*
4 |
5 | 
6 |
7 | ## Usage
8 |
9 | See the [pntr API](group__pntr.html) and [Examples](Examples.html) for how to use pntr.
10 |
11 | ``` c
12 | #define PNTR_PIXELFORMAT_RGBA
13 | #define PNTR_IMPLEMENTATION
14 | #include "pntr.h"
15 |
16 | int main() {
17 | pntr_image* image = pntr_new_image(200, 200);
18 | pntr_draw_circle_fill(image, 100, 100, 80, PNTR_RED);
19 | pntr_save_image(image, "output.png");
20 | pntr_unload_image(image);
21 |
22 | return 0;
23 | }
24 | ```
25 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*]
4 | indent_style = space
5 | charset = utf-8
6 | end_of_line = lf
7 | indent_size = 4
8 | insert_final_newline = true
9 | trim_trailing_whitespace = true
10 |
--------------------------------------------------------------------------------
/.github/workflows/pntr.yml:
--------------------------------------------------------------------------------
1 | name: CMake
2 |
3 | on:
4 | push:
5 | branches: [ "master" ]
6 | pull_request:
7 | branches: [ "master" ]
8 |
9 | env:
10 | # Customize the CMake build type here (Release, Debug, RelWithDebInfo, etc.)
11 | BUILD_TYPE: Debug
12 | DEFINES: -DPNTR_BUILD_EXAMPLES=false
13 |
14 | jobs:
15 | build:
16 | # The CMake configure and build commands are platform agnostic and should work equally well on Windows or Mac.
17 | # You can convert this to a matrix build if you need cross-platform coverage.
18 | # See: https://docs.github.com/en/free-pro-team@latest/actions/learn-github-actions/managing-complex-workflows#using-a-build-matrix
19 | runs-on: ubuntu-latest
20 |
21 | steps:
22 | - uses: actions/checkout@v3
23 |
24 | - name: Configure CMake
25 | # Configure CMake in a 'build' subdirectory. `CMAKE_BUILD_TYPE` is only required if you are using a single-configuration generator such as make.
26 | # See https://cmake.org/cmake/help/latest/variable/CMAKE_BUILD_TYPE.html?highlight=cmake_build_type
27 | run: cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} ${{env.DEFINES}}
28 |
29 | - name: Build
30 | # Build your program with the given configuration
31 | run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}}
32 |
33 | - name: Test
34 | working-directory: ${{github.workspace}}/build
35 | # Execute tests defined by the CMake configuration.
36 | # See https://cmake.org/cmake/help/latest/manual/ctest.1.html for more detail
37 | run: ctest -C ${{env.BUILD_TYPE}} -V
38 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .vscode
2 | build
3 | /web/pntr_demo*
4 | /docs
5 | Testing
6 | package-lock.json
7 | node_modules
8 |
--------------------------------------------------------------------------------
/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | cmake_minimum_required(VERSION 3.11)
2 | project(pntr
3 | DESCRIPTION "pntr: Header-only CPU graphics library for C99 and C++"
4 | HOMEPAGE_URL "https://github.com/robloach/pntr"
5 | VERSION 0.0.1
6 | LANGUAGES C
7 | )
8 |
9 | # pntr
10 | add_library(pntr INTERFACE)
11 | target_include_directories(pntr INTERFACE ${CMAKE_CURRENT_SOURCE_DIR})
12 |
13 | # Options
14 | if ("${CMAKE_SOURCE_DIR}" STREQUAL "${CMAKE_CURRENT_SOURCE_DIR}")
15 | set(PNTR_IS_MAIN TRUE)
16 | else()
17 | set(PNTR_IS_MAIN FALSE)
18 | endif()
19 |
20 | # Examples
21 | option(PNTR_BUILD_EXAMPLES "Examples" ${PNTR_IS_MAIN})
22 | if (PNTR_BUILD_EXAMPLES)
23 | add_subdirectory(examples)
24 | endif()
25 |
26 | # Tests
27 | option(PNTR_BUILD_TESTS "Build Tests" ${PNTR_IS_MAIN})
28 | if (PNTR_BUILD_TESTS)
29 | include(CTest)
30 | enable_testing()
31 | if (BUILD_TESTING)
32 | set(CTEST_CUSTOM_TESTS_IGNORE
33 | pkg-config--static
34 | )
35 | # Always print verbose output when tests fail if run using `make test`.
36 | list(APPEND CMAKE_CTEST_ARGUMENTS "--output-on-failure -V --debug")
37 | add_subdirectory(test)
38 | endif()
39 | endif()
40 |
41 | # Documentation
42 | option(PNTR_BUILD_DOCS "Build Documentation" OFF)
43 | if (PNTR_BUILD_EXAMPLES AND PNTR_BUILD_DOCS)
44 | find_package(Doxygen)
45 | if (DOXYGEN_FOUND)
46 | add_custom_target(pntr_doxygen ALL
47 | COMMAND ${DOXYGEN_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/.Doxyfile
48 | WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
49 | COMMENT "Generating API documentation with Doxygen"
50 | VERBATIM
51 | DEPENDS pntr_examples_output
52 | )
53 | else (DOXYGEN_FOUND)
54 | message("Doxygen is required to build documentation")
55 | endif (DOXYGEN_FOUND)
56 | endif()
57 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | # Zlib License
2 |
3 | Copyright (c) 2023 Rob Loach (@RobLoach, https://robloach.net)
4 |
5 | This software is provided "as-is", without any express or implied warranty. In no event
6 | will the authors be held liable for any damages arising from the use of this software.
7 |
8 | Permission is granted to anyone to use this software for any purpose, including commercial
9 | applications, and to alter it and redistribute it freely, subject to the following restrictions:
10 |
11 | 1. The origin of this software must not be misrepresented; you must not claim that you
12 | wrote the original software. If you use this software in a product, an acknowledgment
13 | in the product documentation would be appreciated but is not required.
14 |
15 | 2. Altered source versions must be plainly marked as such, and must not be misrepresented
16 | as being the original software.
17 |
18 | 3. This notice may not be removed or altered from any source distribution.
19 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # pntr
2 |
3 | Header-only CPU graphics library for [C99](https://en.wikipedia.org/wiki/C99_(C_standard_revision)) or C++, with a focus on ease-of-use.
4 |
5 | ## Usage
6 |
7 | ``` c
8 | #define PNTR_PIXELFORMAT_RGBA
9 | #define PNTR_IMPLEMENTATION
10 | #include "pntr.h"
11 |
12 | int main() {
13 | pntr_image* image = pntr_new_image(200, 200);
14 | pntr_draw_circle_fill(image, 100, 100, 80, PNTR_RED);
15 | pntr_save_image(image, "output.png");
16 | pntr_unload_image(image);
17 |
18 | return 0;
19 | }
20 | ```
21 |
22 | ## Examples
23 |
24 | [](examples/pntr_examples_image.h)
25 | [](examples/pntr_examples_shapes.h)
26 | [](examples/pntr_examples_fonts.h)
27 | [](examples/pntr_examples_sprite.h)
28 |
29 | ## Features
30 |
31 | - Draw rectangles, circles, ellipses, triangles, lines, arcs, etc
32 | - Clipping
33 | - Load and save images (PNG)
34 | - Draw sprites with scaling and rotation
35 | - Alpha-blending, alpha masks, invert, brightness, etc
36 | - Font rendering (TTF, BMF, TTY)
37 | - UTF-8
38 | - RGBA or ARGB pixel bit order
39 |
40 | ## Integrations
41 |
42 | | Name | Description |
43 | | ---- | ----------- |
44 | | [pntr_app](https://github.com/RobLoach/pntr_app) | Application wrapper for running the same pntr code in raylib, SDL, Web, libretro, or in a CLI |
45 | | [pntr_assetsys](https://github.com/RobLoach/pntr_assetsys) | Load pntr assets from *.zip* files with [assetsys.h](https://github.com/mattiasgustavsson/libs/blob/main/assetsys.h) |
46 | | [pntr_physfs](https://github.com/RobLoach/pntr_physfs) | Load pntr assets from *.zip* files with [PhysicsFS](https://github.com/icculus/physfs) |
47 | | [pntr_nuklear](https://github.com/RobLoach/pntr_nuklear) | [Nuklear](https://github.com/Immediate-Mode-UI/Nuklear) immediate-mode graphical user interface for pntr |
48 | | [pntr_aseprite](https://github.com/RobLoach/pntr_aseprite) | Use [Aseprite](https://github.com/RobLoach/pntr_aseprite) animated sprites in pntr |
49 | | [pntr_tiled](https://github.com/RobLoach/pntr_tiled) | Display [Tiled](https://www.mapeditor.org/) 2D level editor maps in pntr |
50 | | [pntr_portablegl](https://github.com/RobLoach/pntr_portablegl) | Use the OpenGL-esque software rendering library, [PortableGL](https://github.com/rswinkle/PortableGL), in pntr |
51 | | [pntr_doom](https://github.com/RobLoach/pntr_doom) | Play DOOM via [PureDOOM](https://github.com/Daivuk/PureDOOM/) rendered through pntr |
52 | | [pntr_pixelfont](https://github.com/RobLoach/pntr_pixelfont) | Additional pixel fonts outside of the default |
53 |
54 | ## API
55 |
56 | In order to use *pntr*, you must define `PNTR_IMPLEMENTATION` prior in one of your *.c* files prior to including `pntr.h`.
57 |
58 | ### Configuration
59 |
60 | You can modify how *pntr* functions based on the following defines:
61 |
62 | | Define | Description |
63 | | --- | --- |
64 | | `PNTR_IMPLEMENTATION` | Define this in one of your `.c` or `.cpp` files before including `pntr.h` |
65 | | `PNTR_PIXELFORMAT_RGBA` | Use the `RGBA` format |
66 | | `PNTR_PIXELFORMAT_ARGB` | Use the `ARGB` pixel format |
67 | | `PNTR_ENABLE_DEFAULT_FONT` | Enables the default 8x8 pixel font |
68 | | `PNTR_ENABLE_MATH` | Enables use of C's standard [`math.h`](https://en.cppreference.com/w/c/numeric/math) linked library, rather than using the built in math functions |
69 | | `PNTR_ENABLE_TTF` | Enables support for loading [TrueType fonts](https://en.wikipedia.org/wiki/TrueType_fonts) |
70 | | `PNTR_ENABLE_UTF8` | Enables [UTF-8](https://en.wikipedia.org/wiki/UTF-8) support for font loading and text rendering |
71 | | `PNTR_LOAD_FILE` | Callback to use when asked to load a file in `pntr_load_file()`. By default, will use `stdio.h`. |
72 | | `PNTR_LOAD_IMAGE_FROM_MEMORY` | Callback to use when loading an image from memory via `pntr_load_image_from_memory()`. By default, will use [stb_image](https://github.com/nothings/stb/blob/master/stb_image.h) |
73 | | `PNTR_SAVE_FILE` | Callback to use when saving a file via `pntr_save_file()`. By default, uses `stdio.h` |
74 | | `PNTR_SAVE_IMAGE_TO_MEMORY` | Callback to use when saving an image to memory via `pntr_save_image_to_memory()`. By default, will use [stb_image_write](https://github.com/nothings/stb/blob/master/stb_image_write.h) |
75 | | `PNTR_NO_ALPHABLEND` | Skips alpha blending when drawing pixels |
76 | | `PNTR_NO_STDIO` | Will disable the standard file loading/saving calls for `PNTR_LOAD_FILE` and `PNTR_SAVE_FILE` |
77 | | `PNTR_NO_SAVE_IMAGE` | Disables the default behavior of image saving |
78 | | `PNTR_NO_LOAD_IMAGE` | Disables the default behavior of image loading |
79 | | `PNTR_NO_CUTE_PNG_IMPLEMENTATION` | Skips defining `CUTE_PNG_IMPLEMENTATION`. Useful if you're using cute_png elsewhere |
80 | | `PNTR_NO_STB_TRUETYPE_IMPLEMENTATION` | Skips defining `STB_TRUETYPE_IMPLEMENTATION`. Useful if you're using stb_truetype elsewhere |
81 | | `PNTR_NO_STB_IMAGE_WRITE_IMPLEMENTATION` | Skips defining `STB_IMAGE_WRITE_IMPLEMENTATION`. useful if you're using stb_image_write elsewhere |
82 | | `PNTR_NO_STB_IMAGE_IMPLEMENTATION` | Skips defining `STB_IMAGE_IMPLEMENTATION`. useful if you're using stb_image_write elsewhere |
83 |
84 | ### Functions
85 |
86 | ``` c
87 | pntr_image* pntr_new_image(int width, int height);
88 | pntr_image* pntr_gen_image_color(int width, int height, pntr_color color);
89 | pntr_image* pntr_image_copy(pntr_image* image);
90 | pntr_image* pntr_image_from_image(pntr_image* image, int x, int y, int width, int height);
91 | pntr_image* pntr_image_subimage(pntr_image* image, int x, int y, int width, int height);
92 | pntr_rectangle pntr_image_get_clip(pntr_image* image);
93 | void pntr_image_set_clip(pntr_image* image, int x, int y, int width, int height);
94 | void pntr_image_reset_clip(pntr_image* image);
95 | void pntr_unload_image(pntr_image* image);
96 | void pntr_clear_background(pntr_image* image, pntr_color color);
97 |
98 | void pntr_draw_point(pntr_image* dst, int x, int y, pntr_color color);
99 | void pntr_draw_point_vec(pntr_image* dst, pntr_vector* point, pntr_color color);
100 | void pntr_draw_points(pntr_image* dst, pntr_vector* points, int pointsCount, pntr_color color);
101 | void pntr_draw_line(pntr_image* dst, int startPosX, int startPosY, int endPosX, int endPosY, pntr_color color);
102 | void pntr_draw_line_horizontal(pntr_image* dst, int posX, int posY, int width, pntr_color color);
103 | void pntr_draw_line_horizontal_thick(pntr_image* dst, int posX, int posY, int width, int thickness, pntr_color color);
104 | void pntr_draw_line_thick(pntr_image* dst, int startPosX, int startPosY, int endPosX, int endPosY, int thickness, pntr_color color);
105 | void pntr_draw_line_thick_vec(pntr_image* dst, pntr_vector start, pntr_vector end, int thickness, pntr_color color);
106 | void pntr_draw_line_vec(pntr_image* dst, pntr_vector start, pntr_vector end, pntr_color color);
107 | void pntr_draw_line_vertical(pntr_image* dst, int posX, int posY, int height, pntr_color color);
108 | void pntr_draw_line_vertical_thick(pntr_image* dst, int posX, int posY, int height, int thickness, pntr_color color);
109 | void pntr_draw_rectangle(pntr_image* dst, int posX, int posY, int width, int height, pntr_color color);
110 | void pntr_draw_rectangle_fill(pntr_image* dst, int posX, int posY, int width, int height, pntr_color color);
111 | void pntr_draw_rectangle_fill_rec(pntr_image* dst, pntr_rectangle rect, pntr_color color);
112 | void pntr_draw_rectangle_gradient(pntr_image* dst, int x, int y, int width, int height, pntr_color topLeft, pntr_color topRight, pntr_color bottomLeft, pntr_color bottomRight);
113 | void pntr_draw_rectangle_gradient_rec(pntr_image* dst, pntr_rectangle rect, pntr_color topLeft, pntr_color topRight, pntr_color bottomLeft, pntr_color bottomRight);
114 | void pntr_draw_rectangle_rec(pntr_image* dst, pntr_rectangle rec, pntr_color color);
115 | void pntr_draw_rectangle_thick(pntr_image* dst, int posX, int posY, int width, int height, int thickness, pntr_color color);
116 | void pntr_draw_rectangle_thick_rec(pntr_image* dst, pntr_rectangle rect, int thickness, pntr_color color);
117 | void pntr_draw_triangle(pntr_image* dst, int x1, int y1, int x2, int y2, int x3, int y3, pntr_color color);
118 | void pntr_draw_triangle_fill(pntr_image* dst, int x1, int y1, int x2, int y2, int x3, int y3, pntr_color color);
119 | void pntr_draw_triangle_fill_vec(pntr_image* dst, pntr_vector point1, pntr_vector point2, pntr_vector point3, pntr_color color);
120 | void pntr_draw_triangle_thick(pntr_image* dst, int x1, int y1, int x2, int y2, int x3, int y3, int thickness, pntr_color color);
121 | void pntr_draw_triangle_thick_vec(pntr_image* dst, pntr_vector point1, pntr_vector point2, pntr_vector point3, int thickness, pntr_color color);
122 | void pntr_draw_triangle_vec(pntr_image* dst, pntr_vector point1, pntr_vector point2, pntr_vector point3, pntr_color color);
123 | void pntr_draw_ellipse(pntr_image* dst, int centerX, int centerY, int radiusX, int radiusY, pntr_color color);
124 | void pntr_draw_ellipse_fill(pntr_image* dst, int centerX, int centerY, int radiusX, int radiusY, pntr_color color);
125 | void pntr_draw_ellipse_thick(pntr_image* dst, int centerX, int centerY, int radiusX, int radiusY, int thickness, pntr_color color);
126 | void pntr_draw_circle(pntr_image* dst, int centerX, int centerY, int radius, pntr_color color);
127 | void pntr_draw_circle_fill(pntr_image* dst, int centerX, int centerY, int radius, pntr_color color);
128 | void pntr_draw_circle_thick(pntr_image* dst, int centerX, int centerY, int radius, int thickness, pntr_color color);
129 | void pntr_draw_polygon(pntr_image* dst, pntr_vector* points, int numPoints, pntr_color color);
130 | void pntr_draw_polygon_fill(pntr_image* dst, pntr_vector* points, int numPoints, pntr_color color);
131 | void pntr_draw_polygon_thick(pntr_image* dst, pntr_vector* points, int numPoints, int thickness, pntr_color color);
132 | void pntr_draw_polyline(pntr_image* dst, pntr_vector* points, int numPoints, pntr_color color);
133 | void pntr_draw_polyline_thick(pntr_image* dst, pntr_vector* points, int numPoints, int thickness, pntr_color color);
134 | void pntr_draw_arc(pntr_image* dst, int centerX, int centerY, float radius, float startAngle, float endAngle, int segments, pntr_color color);
135 | void pntr_draw_arc_fill(pntr_image* dst, int centerX, int centerY, float radius, float startAngle, float endAngle, int segments, pntr_color color);
136 | void pntr_draw_arc_thick(pntr_image* dst, int centerX, int centerY, float radius, float startAngle, float endAngle, int segments, int thickness, pntr_color color);
137 | void pntr_draw_rectangle_rounded(pntr_image* dst, int x, int y, int width, int height, int topLeftRadius, int topRightRadius, int bottomLeftRadius, int bottomRightRadius, pntr_color color);
138 | void pntr_draw_rectangle_rounded_fill(pntr_image* dst, int x, int y, int width, int height, int cornerRadius, pntr_color color);
139 | void pntr_draw_rectangle_thick_rounded(pntr_image* dst, int x, int y, int width, int height, int topLeftRadius, int topRightRadius, int bottomLeftRadius, int bottomRightRadius, int thickness, pntr_color color);
140 | void pntr_draw_image(pntr_image* dst, pntr_image* src, int posX, int posY);
141 | void pntr_draw_image_rec(pntr_image* dst, pntr_image* src, pntr_rectangle srcRect, int posX, int posY);
142 | void pntr_draw_image_tint(pntr_image* dst, pntr_image* src, int posX, int posY, pntr_color tint);
143 | void pntr_draw_image_tint_rec(pntr_image* dst, pntr_image* src, pntr_rectangle srcRect, int posX, int posY, pntr_color tint);
144 | void pntr_draw_image_rotated(pntr_image* dst, pntr_image* src, int posX, int posY, float degrees, float offsetX, float offsetY, pntr_filter filter);
145 | void pntr_draw_image_rotated_rec(pntr_image* dst, pntr_image* src, pntr_rectangle srcRect, int posX, int posY, float degrees, float offsetX, float offsetY, pntr_filter filter);
146 | void pntr_draw_image_flipped(pntr_image* dst, pntr_image* src, int posX, int posY, bool flipHorizontal, bool flipVertical, bool flipDiagonal);
147 | void pntr_draw_image_flipped_rec(pntr_image* dst, pntr_image* src, pntr_rectangle srcRec, int posX, int posY, bool flipHorizontal, bool flipVertical, bool flipDiagonal);
148 | void pntr_draw_image_scaled(pntr_image* dst, pntr_image* src, int posX, int posY, float scaleX, float scaleY, float offsetX, float offsetY, pntr_filter filter);
149 | void pntr_draw_image_scaled_rec(pntr_image* dst, pntr_image* src, pntr_rectangle srcRect, int posX, int posY, float scaleX, float scaleY, float offsetX, float offsetY, pntr_filter filter);
150 | void pntr_draw_text(pntr_image* dst, pntr_font* font, const char* text, int posX, int posY, pntr_color tint);
151 | void pntr_draw_text_len(pntr_image* dst, pntr_font* font, const char* text, int textLength, int posX, int posY, pntr_color tint);
152 | void pntr_draw_text_wrapped(pntr_image* dst, pntr_font* font, const char* text, int posX, int posY, int maxWidth, pntr_color tint);
153 | void pntr_draw_text_ex(pntr_image* dst, pntr_font* font, int posX, int posY, pntr_color tint, const char* text, ...);
154 |
155 | pntr_color pntr_new_color(unsigned char r, unsigned char g, unsigned char b, unsigned char a);
156 | pntr_color pntr_get_color(unsigned int hexValue);
157 | unsigned char pntr_color_r(pntr_color color);
158 | unsigned char pntr_color_g(pntr_color color);
159 | unsigned char pntr_color_b(pntr_color color);
160 | unsigned char pntr_color_a(pntr_color color);
161 | void pntr_color_set_r(pntr_color* color, unsigned char r);
162 | void pntr_color_set_g(pntr_color* color, unsigned char g);
163 | void pntr_color_set_b(pntr_color* color, unsigned char b);
164 | void pntr_color_set_a(pntr_color* color, unsigned char a);
165 | pntr_color pntr_image_get_color(pntr_image* image, int x, int y);
166 | bool pntr_save_file(const char *fileName, const void *data, unsigned int bytesToWrite);
167 | void* pntr_image_to_pixelformat(pntr_image* image, unsigned int* dataSize, pntr_pixelformat pixelFormat);
168 | bool pntr_save_image(pntr_image* image, const char* fileName);
169 | unsigned char* pntr_save_image_to_memory(pntr_image* image, pntr_image_type type, unsigned int* dataSize);
170 | int pntr_get_pixel_data_size(int width, int height, pntr_pixelformat pixelFormat);
171 | pntr_image* pntr_load_image(const char* fileName);
172 | pntr_image* pntr_load_image_from_memory(pntr_image_type type, const unsigned char* fileData, unsigned int dataSize);
173 | pntr_image* pntr_image_from_pixelformat(const void* data, int width, int height, pntr_pixelformat pixelFormat);
174 | void* pntr_set_error(pntr_error error);
175 | const char* pntr_get_error(void);
176 | pntr_error pntr_get_error_code(void);
177 | pntr_image* pntr_image_resize(pntr_image* image, int newWidth, int newHeight, pntr_filter filter);
178 | pntr_image* pntr_image_scale(pntr_image* image, float scaleX, float scaleY, pntr_filter filter);
179 | void pntr_image_color_replace(pntr_image* image, pntr_color color, pntr_color replace);
180 | pntr_color pntr_color_tint(pntr_color color, pntr_color tint);
181 | void pntr_image_color_tint(pntr_image* image, pntr_color color);
182 | pntr_color pntr_color_fade(pntr_color color, float alpha);
183 | void pntr_image_color_fade(pntr_image* image, float alpha);
184 | pntr_color pntr_color_brightness(pntr_color color, float factor);
185 | pntr_color pntr_get_pixel_color(void* srcPtr, pntr_pixelformat srcPixelFormat);
186 | void pntr_set_pixel_color(void* dstPtr, pntr_pixelformat dstPixelFormat, pntr_color color);
187 | pntr_font* pntr_load_font_default(void);
188 | void pntr_unload_font(pntr_font* font);
189 | pntr_font* pntr_font_copy(pntr_font* font);
190 | pntr_font* pntr_font_scale(pntr_font* font, float scaleX, float scaleY, pntr_filter filter);
191 | pntr_font* pntr_load_font_bmf(const char* fileName, const char* characters);
192 | pntr_font* pntr_load_font_bmf_from_image(pntr_image* image, const char* characters);
193 | pntr_font* pntr_load_font_bmf_from_memory(const unsigned char* fileData, unsigned int dataSize, const char* characters);
194 | int pntr_measure_text(pntr_font* font, const char* text);
195 | pntr_vector pntr_measure_text_ex(pntr_font* font, const char* text, int textLength);
196 | pntr_image* pntr_gen_image_text(pntr_font* font, const char* text, pntr_color tint, pntr_color backgroundColor);
197 | pntr_font* pntr_load_font_tty(const char* fileName, int glyphWidth, int glyphHeight, const char* characters);
198 | pntr_font* pntr_load_font_tty_from_memory(const unsigned char* fileData, unsigned int dataSize, int glyphWidth, int glyphHeight, const char* characters);
199 | pntr_font* pntr_load_font_tty_from_image(pntr_image* image, int glyphWidth, int glyphHeight, const char* characters);
200 | unsigned char* pntr_load_file(const char *fileName, unsigned int *bytesRead);
201 | void pntr_unload_file(unsigned char* fileData);
202 | const char* pntr_load_file_text(const char *fileName);
203 | void pntr_unload_file_text(const char* text);
204 | pntr_font* pntr_load_font_ttf(const char* fileName, int fontSize);
205 | pntr_font* pntr_load_font_ttf_from_memory(const unsigned char* fileData, unsigned int dataSize, int fontSize);
206 | pntr_color pntr_color_invert(pntr_color color);
207 | void pntr_image_color_invert(pntr_image* image);
208 | pntr_color pntr_color_alpha_blend(pntr_color dst, pntr_color src);
209 | pntr_rectangle pntr_image_alpha_border(pntr_image* image, float threshold);
210 | bool pntr_image_crop(pntr_image* image, int x, int y, int width, int height);
211 | void pntr_image_alpha_crop(pntr_image* image, float threshold);
212 | void pntr_image_color_brightness(pntr_image* image, float factor);
213 | void pntr_image_flip(pntr_image* image, bool horizontal, bool vertical);
214 | pntr_color pntr_color_contrast(pntr_color color, float contrast);
215 | void pntr_image_color_contrast(pntr_image* image, float contrast);
216 | void pntr_image_alpha_mask(pntr_image* image, pntr_image* alphaMask, int posX, int posY);
217 | bool pntr_image_resize_canvas(pntr_image* image, int newWidth, int newHeight, int offsetX, int offsetY, pntr_color fill);
218 | pntr_image* pntr_image_rotate(pntr_image* image, float degrees, pntr_filter filter);
219 | pntr_image* pntr_gen_image_gradient(int width, int height, pntr_color topLeft, pntr_color topRight, pntr_color bottomLeft, pntr_color bottomRight);
220 | pntr_color pntr_color_bilinear_interpolate(pntr_color color00, pntr_color color01, pntr_color color10, pntr_color color11, float coordinateX, float coordinateY);
221 | void* pntr_load_memory(size_t size);
222 | void pntr_unload_memory(void* pointer);
223 | void* pntr_memory_copy(void* destination, void* source, size_t size);
224 | pntr_image_type pntr_get_file_image_type(const char* filePath);
225 | ```
226 |
227 | ## Development
228 |
229 | To build the tests and examples, use [CMake](https://cmake.org):
230 |
231 | ``` bash
232 | # Set up the build files.
233 | cmake -B build
234 |
235 | # Build the tests and examples.
236 | cmake --build build
237 |
238 | # Run the tests
239 | ctest --test-dir build -V
240 | ```
241 |
242 | ### Documentation
243 |
244 | To build the documentation, use [Doxygen](https://www.doxygen.nl)...
245 | ``` bash
246 | doxygen .Doxyfile
247 | ```
248 |
249 | To publish the documentation, [Node.js](https://nodejs.org)...
250 | ``` bash
251 | npm run docs
252 | ```
253 |
254 | ## Acknowledgements
255 |
256 | - [Logo](https://www.pixilart.com/art/bob-ross-9910c4da4b3a1c8) by [Ravenist](https://www.pixilart.com/ravenist), used with [permission](https://www.reddit.com/r/PixelArt/comments/fi2b1v/oc_felt_a_little_sad_so_i_watched_bob_ross_videos/j6ordqn/)
257 | - [cute_png.h](https://github.com/RandyGaul/cute_headers/blob/master/cute_png.h) by [Randy Gaul](https://github.com/RandyGaul)
258 | - [font8x8](https://github.com/dhepper/font8x8/) by [Daniel Hepper](https://github.com/dhepper) provides the [8x8 monochrome font](https://github.com/dhepper/font8x8/blob/master/font8x8_basic.h)
259 | - [stb_image.h](https://github.com/nothings/stb/blob/master/stb_image.h) by [Sean Barrett](https://github.com/nothings)
260 | - [stb_truetype.h](https://github.com/nothings/stb/blob/master/stb_truetype.h) by [Sean Barrett](https://github.com/nothings)
261 | - [utf8.h](https://github.com/sheredom/utf8.h) by [Neil Henning](https://github.com/sheredom)
262 | - [tester](https://github.com/zpl-c/tester) from [zpl](https://github.com/zpl-c) provides the unit testing framework
263 | - [raylib](https://github.com/raysan5/raylib) inspired some of the design patterns
264 |
265 | ## License
266 |
267 | Unless stated otherwise, all works are:
268 |
269 | - Copyright (c) 2023 [Rob Loach](https://robloach.net)
270 |
271 | ... and licensed under:
272 |
273 | - [zlib License](LICENSE.md)
274 |
--------------------------------------------------------------------------------
/examples/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | # Resources
2 | file(GLOB resources resources/*)
3 | set(examples_resources)
4 | list(APPEND examples_resources ${resources})
5 | file(COPY ${examples_resources} DESTINATION "resources/")
6 |
7 | # pntr_examples
8 | add_executable(pntr_examples
9 | pntr_examples.c
10 | )
11 | target_link_libraries(pntr_examples PUBLIC
12 | pntr
13 |
14 | # Math: Define PNTR_ENABLE_MATH to use the math library
15 | #m
16 | )
17 |
18 | # pntr_examples_output
19 | add_custom_target(pntr_examples_output
20 | COMMAND pntr_examples
21 | )
22 |
--------------------------------------------------------------------------------
/examples/pntr_examples.c:
--------------------------------------------------------------------------------
1 | /**
2 | * @page Examples Examples
3 | *
4 | * @brief These are a few examples of pntr in use.
5 | *
6 | * ## Alpha Mask
7 | * @include pntr_examples_alphamask.h
8 | * @image html pntr_examples_alphamask.png
9 | *
10 | * ## Fonts
11 | * @include pntr_examples_fonts.h
12 | * @image html pntr_examples_fonts.png
13 | *
14 | * ## Image
15 | * @include pntr_examples_image.h
16 | * @image html pntr_examples_image.png
17 | *
18 | * ## Resize
19 | * @include pntr_examples_resize.h
20 | * @image html pntr_examples_resize.png
21 | *
22 | * ## Rotate
23 | * @include pntr_examples_rotate.h
24 | * @image html pntr_examples_rotate.png
25 | *
26 | * ## Shapes
27 | * @include pntr_examples_shapes.h
28 | * @image html pntr_examples_shapes.png
29 | *
30 | * ## Sprite
31 | * @include pntr_examples_sprite.h
32 | * @image html pntr_examples_sprite.png
33 | *
34 | * ## Thick
35 | * @include pntr_examples_thick.h
36 | * @image html pntr_examples_thick.png
37 | */
38 |
39 | #ifndef _DOXYGEN_
40 |
41 | #define PNTR_ENABLE_DEFAULT_FONT
42 | #define PNTR_ENABLE_TTF
43 | #define PNTR_ENABLE_UTF8
44 | #define PNTR_ENABLE_VARGS
45 |
46 | // Math: Link the m library to use math.h
47 | //#define PNTR_ENABLE_MATH
48 |
49 | #define PNTR_IMPLEMENTATION
50 | #include "../pntr.h"
51 |
52 | #include "pntr_examples_alphamask.h"
53 | #include "pntr_examples_fonts.h"
54 | #include "pntr_examples_image.h"
55 | #include "pntr_examples_resize.h"
56 | #include "pntr_examples_rotate.h"
57 | #include "pntr_examples_shapes.h"
58 | #include "pntr_examples_sprite.h"
59 | #include "pntr_examples_thick.h"
60 |
61 | int main(int argc, char* argv[]) {
62 | pntr_examples_alphamask();
63 | pntr_examples_fonts();
64 | pntr_examples_image();
65 | pntr_examples_resize();
66 | pntr_examples_rotate();
67 | pntr_examples_shapes();
68 | pntr_examples_sprite();
69 | pntr_examples_thick();
70 |
71 | return 0;
72 | }
73 |
74 | #endif
75 |
--------------------------------------------------------------------------------
/examples/pntr_examples_alphamask.h:
--------------------------------------------------------------------------------
1 | void pntr_examples_alphamask() {
2 | // Load the Tuffy font, and find the size of the text output.
3 | pntr_font* font = pntr_load_font_ttf("resources/tuffy.ttf", 80);
4 | pntr_vector textSize = pntr_measure_text_ex(font, "Alpha Mask!", 0);
5 |
6 | // Create a background to use as the alphamask.
7 | pntr_image* output = pntr_gen_image_gradient(textSize.x, textSize.y, PNTR_RED, PNTR_DARKGREEN, PNTR_BLUE, PNTR_PURPLE);
8 |
9 | // Create an alpha mask of text
10 | pntr_image* imageText = pntr_gen_image_text(font, "Alpha Mask!", PNTR_WHITE, PNTR_BLANK);
11 |
12 | // Apply the alpha mask to the image.
13 | pntr_image_alpha_mask(output, imageText, 0, 0);
14 |
15 | pntr_save_image(output, "pntr_examples_alphamask.png");
16 |
17 | // Clean up
18 | pntr_unload_font(font);
19 | pntr_unload_image(imageText);
20 | pntr_unload_image(output);
21 | }
22 |
--------------------------------------------------------------------------------
/examples/pntr_examples_alphamask.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RobLoach/pntr/922aed03c4c53803466100596211da7549945da7/examples/pntr_examples_alphamask.png
--------------------------------------------------------------------------------
/examples/pntr_examples_fonts.h:
--------------------------------------------------------------------------------
1 | void pntr_examples_fonts() {
2 | pntr_image* canvas = pntr_gen_image_color(400, 225, PNTR_RAYWHITE);
3 |
4 | // Default Font
5 | pntr_font* defaultFont = pntr_load_font_default();
6 |
7 | pntr_draw_image(canvas, defaultFont->atlas, 0, 0);
8 |
9 | // Font Drawing
10 | pntr_draw_text(canvas, defaultFont, "Default Font Example", 10, 50, PNTR_BLACK);
11 |
12 | // BM Font
13 | pntr_font* bmFont = pntr_load_font_bmf("resources/bmfont.png", " abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789.,!?-+/");
14 | pntr_draw_rectangle_fill(canvas, 0, 90, 200, 20, PNTR_BLACK);
15 | pntr_draw_text(canvas, bmFont, "BM Font Example", 10, 90, PNTR_WHITE);
16 |
17 | // TTY Font
18 | pntr_font* ttyFont = pntr_load_font_tty("resources/ttyfont-16x16.png", 16, 16,
19 | "\x7f !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~");
20 | pntr_color background = pntr_get_color(0x0000caff);
21 | pntr_color border = pntr_get_color(0x8a8affff);
22 | pntr_draw_rectangle_fill(canvas, 0, 120, canvas->width, 60, border);
23 | pntr_draw_rectangle_fill(canvas, 0, 130, canvas->width, 40, background);
24 |
25 | const char* text = "*** TTY Font Example ***";
26 | int textWidth = pntr_measure_text(ttyFont, text);
27 | pntr_draw_text(canvas, ttyFont, text, canvas->width / 2 - textWidth / 2, 140, PNTR_WHITE);
28 |
29 | // TTF Font
30 | pntr_font* ttfFont = pntr_load_font_ttf("resources/tuffy.ttf", 28);
31 | const char* ttfText = "Привіт Світ!";
32 | pntr_vector textSize = pntr_measure_text_ex(ttfFont, ttfText, 0);
33 | pntr_draw_text(canvas, ttfFont, ttfText, 200, 20, PNTR_DARKPURPLE);
34 |
35 | // Resize the default font
36 | pntr_font* resizedFont = pntr_font_scale(defaultFont, 2.0f, 2.0f, PNTR_FILTER_NEARESTNEIGHBOR);
37 | pntr_draw_text(canvas, resizedFont, "Blue Text", 10, 65, PNTR_BLUE);
38 | pntr_unload_font(resizedFont);
39 |
40 | // Word-wrapped
41 | pntr_draw_rectangle(canvas, 225, 50, 150, 60, PNTR_BLUE);
42 | pntr_draw_text_wrapped(canvas, bmFont, "The quick brown fox jumps over the lazy dog.", 225, 50, 150, PNTR_RED);
43 |
44 | pntr_save_image(canvas, "pntr_examples_fonts.png");
45 |
46 | pntr_unload_font(defaultFont);
47 | pntr_unload_font(ttyFont);
48 | pntr_unload_font(bmFont);
49 | pntr_unload_font(ttfFont);
50 | pntr_unload_image(canvas);
51 | }
52 |
--------------------------------------------------------------------------------
/examples/pntr_examples_fonts.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RobLoach/pntr/922aed03c4c53803466100596211da7549945da7/examples/pntr_examples_fonts.png
--------------------------------------------------------------------------------
/examples/pntr_examples_image.h:
--------------------------------------------------------------------------------
1 | void pntr_examples_image() {
2 | pntr_image* canvas = pntr_gen_image_color(400, 225, PNTR_RAYWHITE);
3 | pntr_image* image;
4 | pntr_image* resized;
5 |
6 | // Load an image
7 | image = pntr_load_image("resources/logo-128x128.png");
8 |
9 | // Resize the image
10 | resized = pntr_image_resize(image, image->width / 2, image->height / 2, PNTR_FILTER_NEARESTNEIGHBOR);
11 |
12 | // Draw an image on the canvas
13 | pntr_draw_image(canvas, image, 30, 30);
14 |
15 | // Draw a section of an image
16 | pntr_rectangle rect;
17 | rect.x = 25;
18 | rect.y = 16;
19 | rect.width = 65;
20 | rect.height = 58;
21 | pntr_draw_image_rec(canvas, image, rect, 170, 30);
22 |
23 | // Draw the resized image
24 | pntr_draw_image(canvas, resized, 170, 100);
25 |
26 | // Draw the resized image
27 | pntr_draw_image_flipped(canvas, image, 240, 30, true, false, true);
28 |
29 | pntr_save_image(canvas, "pntr_examples_image.png");
30 |
31 | pntr_unload_image(image);
32 | pntr_unload_image(resized);
33 | pntr_unload_image(canvas);
34 | }
35 |
--------------------------------------------------------------------------------
/examples/pntr_examples_image.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RobLoach/pntr/922aed03c4c53803466100596211da7549945da7/examples/pntr_examples_image.png
--------------------------------------------------------------------------------
/examples/pntr_examples_resize.h:
--------------------------------------------------------------------------------
1 | void pntr_examples_resize() {
2 | float size = 0.5f;
3 | pntr_image* originalImage = pntr_load_image("resources/logo-128x128.png");
4 |
5 | pntr_image* canvas = pntr_gen_image_color(400, 225, PNTR_RAYWHITE);
6 |
7 | pntr_draw_image(canvas, originalImage, 10, 10);
8 |
9 | pntr_draw_image_scaled(canvas, originalImage,
10 | canvas->width / 4 - originalImage->width * size / 2.0f,
11 | canvas->height / 2 - originalImage->height * size / 2.0f,
12 | size, size, 0.0f, 0.0f, PNTR_FILTER_NEARESTNEIGHBOR);
13 |
14 | pntr_draw_image_scaled(canvas, originalImage,
15 | canvas->width / 2 + canvas->width / 4 - originalImage->width * size / 2.0f,
16 | canvas->height / 2 - originalImage->height * size / 2.0f,
17 | size, size, 0.0f, 0.0f, PNTR_FILTER_BILINEAR);
18 |
19 | pntr_draw_image_scaled(canvas, originalImage,
20 | canvas->width / 2 - originalImage->width * 0.75f / 2.0f,
21 | canvas->height / 2 - originalImage->height * 0.75f / 2.0f,
22 | 0.75f, 0.75, 0.0f, 0.0f, PNTR_FILTER_NEARESTNEIGHBOR);
23 |
24 | pntr_save_image(canvas, "pntr_examples_resize.png");
25 |
26 | pntr_unload_image(originalImage);
27 | pntr_unload_image(canvas);
28 | }
29 |
--------------------------------------------------------------------------------
/examples/pntr_examples_resize.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RobLoach/pntr/922aed03c4c53803466100596211da7549945da7/examples/pntr_examples_resize.png
--------------------------------------------------------------------------------
/examples/pntr_examples_rotate.h:
--------------------------------------------------------------------------------
1 | void pntr_examples_rotate() {
2 | pntr_image* canvas = pntr_gen_image_color(400, 225, PNTR_RAYWHITE);
3 |
4 | pntr_image* imageToRotate = pntr_load_image("resources/logo-128x128.png");
5 |
6 | // Draw the image rotated on screen using a nearest neighbor filter.
7 | pntr_draw_image_rotated(canvas, imageToRotate, 10, canvas->height / 2, 0.0f, 0, imageToRotate->height / 2.0f, PNTR_FILTER_NEARESTNEIGHBOR);
8 |
9 | // Draw the rotated image on the screen with a smooth filter.
10 | pntr_draw_image_rotated(canvas, imageToRotate, 140, canvas->height / 2, 32.0f, 0, imageToRotate->height / 2.0f, PNTR_FILTER_BILINEAR);
11 |
12 | // Draw the rotated image on the screen with a smooth filter.
13 | pntr_draw_image_rotated(canvas, imageToRotate, 280, canvas->height / 2, 180.0f, 0, imageToRotate->height / 2.0f, PNTR_FILTER_BILINEAR);
14 |
15 | pntr_save_image(canvas, "pntr_examples_rotate.png");
16 |
17 | pntr_unload_image(imageToRotate);
18 | pntr_unload_image(canvas);
19 | }
20 |
--------------------------------------------------------------------------------
/examples/pntr_examples_rotate.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RobLoach/pntr/922aed03c4c53803466100596211da7549945da7/examples/pntr_examples_rotate.png
--------------------------------------------------------------------------------
/examples/pntr_examples_shapes.h:
--------------------------------------------------------------------------------
1 | void pntr_examples_shapes() {
2 | pntr_image* canvas = pntr_gen_image_color(400, 225, PNTR_RAYWHITE);
3 |
4 | // Define some colors
5 | pntr_color lightGreen = PNTR_GREEN;
6 | lightGreen.rgba.a = 180;
7 | pntr_color lightBlue = PNTR_BLUE;
8 | lightBlue.rgba.a = 180;
9 |
10 | // Rectangles
11 | pntr_draw_rectangle_fill(canvas, 10, 30, 50, 50, PNTR_RED);
12 | pntr_draw_rectangle_fill(canvas, 20, 40, 50, 50, lightGreen);
13 | pntr_draw_rectangle_fill(canvas, 30, 50, 50, 50, lightBlue);
14 |
15 | pntr_draw_rectangle_thick(canvas, 10, 120, 50, 50, 5, PNTR_RED);
16 | pntr_draw_rectangle_thick(canvas, 20, 130, 50, 50, 5, PNTR_GREEN);
17 | pntr_draw_rectangle_thick(canvas, 30, 140, 50, 50, 5, PNTR_BLUE);
18 |
19 | // Circle
20 | pntr_draw_circle(canvas, 110, 60, 21, PNTR_RED);
21 | pntr_draw_circle_fill(canvas, 160, 60, 21, PNTR_BLUE);
22 |
23 | pntr_draw_ellipse(canvas, 110, 100, 20, 10, PNTR_GREEN);
24 | pntr_draw_ellipse_fill(canvas, 160, 100, 20, 10, PNTR_ORANGE);
25 |
26 | // Line
27 | pntr_draw_line(canvas, 200, 50, 250, 80, PNTR_DARKGREEN);
28 |
29 | // Triangle
30 | pntr_draw_triangle_fill(canvas,
31 | 250, 50,
32 | 300, 80,
33 | 350, 20, PNTR_PURPLE);
34 |
35 | // Rectangle Gradient
36 | pntr_draw_rectangle_gradient(canvas, 100, 120, 80, 80, PNTR_RED, PNTR_GREEN, PNTR_BLUE, PNTR_BLACK);
37 |
38 | // Polygon
39 | pntr_vector points[10];
40 | points[0] = PNTR_CLITERAL(pntr_vector) {210, 110};
41 | points[1] = PNTR_CLITERAL(pntr_vector) {215, 130};
42 | points[2] = PNTR_CLITERAL(pntr_vector) {240, 140};
43 | points[3] = PNTR_CLITERAL(pntr_vector) {200, 160};
44 | pntr_draw_polygon_fill(canvas, points, 4, PNTR_BLUE);
45 | pntr_draw_polygon(canvas, points, 4, PNTR_BLACK);
46 |
47 | // Arc
48 | int radius = 40;
49 | pntr_draw_arc_fill(canvas, 300, 120, radius, 90.0f, 180.0f, radius * 1.5f, PNTR_ORANGE);
50 | pntr_draw_arc(canvas, 300, 120, radius, 90.0f, 180.0f, radius *1.5f, PNTR_RED);
51 |
52 | // Polyline
53 | points[0] = PNTR_CLITERAL(pntr_vector) {240, 80};
54 | points[1] = PNTR_CLITERAL(pntr_vector) {260, 100};
55 | points[2] = PNTR_CLITERAL(pntr_vector) {220, 110};
56 | points[3] = PNTR_CLITERAL(pntr_vector) {240, 130};
57 | pntr_draw_polyline(canvas, points, 4, PNTR_PURPLE);
58 |
59 | // Line Curve
60 | pntr_draw_line_curve(canvas, points[0], points[1], points[2], points[3], 40, PNTR_DARKBLUE);
61 |
62 | pntr_save_image(canvas, "pntr_examples_shapes.png");
63 | pntr_unload_image(canvas);
64 | }
65 |
--------------------------------------------------------------------------------
/examples/pntr_examples_shapes.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RobLoach/pntr/922aed03c4c53803466100596211da7549945da7/examples/pntr_examples_shapes.png
--------------------------------------------------------------------------------
/examples/pntr_examples_sprite.h:
--------------------------------------------------------------------------------
1 | void pntr_examples_sprite() {
2 | pntr_image* canvas = pntr_gen_image_color(400, 225, PNTR_RAYWHITE);
3 |
4 | // Load the image
5 | pntr_image* scarfy = pntr_load_image("resources/scarfy.png");
6 |
7 | pntr_rectangle frameRec;
8 | frameRec.x = 0;
9 | frameRec.y = 0;
10 | frameRec.width = scarfy->width / 6;
11 | frameRec.height = scarfy->height;
12 |
13 | frameRec.x = 1 * (float)scarfy->width / 6;
14 | pntr_draw_image_rec(canvas, scarfy, frameRec, 10, canvas->height / 2 - frameRec.height / 2);
15 |
16 | frameRec.x = 3 * (float)scarfy->width / 6;
17 | pntr_draw_image_rec(canvas, scarfy, frameRec, 120, canvas->height / 2 - frameRec.height / 2);
18 |
19 | frameRec.x = 5 * (float)scarfy->width / 6;
20 | pntr_draw_image_rec(canvas, scarfy, frameRec, 250, canvas->height / 2 - frameRec.height / 2);
21 |
22 | pntr_save_image(canvas, "pntr_examples_sprite.png");
23 |
24 | pntr_unload_image(scarfy);
25 | pntr_unload_image(canvas);
26 | }
27 |
--------------------------------------------------------------------------------
/examples/pntr_examples_sprite.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RobLoach/pntr/922aed03c4c53803466100596211da7549945da7/examples/pntr_examples_sprite.png
--------------------------------------------------------------------------------
/examples/pntr_examples_thick.h:
--------------------------------------------------------------------------------
1 | void pntr_examples_thick() {
2 | pntr_image* canvas = pntr_gen_image_color(400, 225, PNTR_RAYWHITE);
3 |
4 | // Rectangles
5 | pntr_draw_rectangle_thick(canvas, 10, 120, 50, 50, 5, PNTR_RED);
6 | pntr_draw_rectangle_thick(canvas, 20, 130, 50, 50, 5, PNTR_GREEN);
7 | pntr_draw_rectangle_thick(canvas, 30, 140, 50, 50, 5, PNTR_BLUE);
8 |
9 | // Circle
10 | pntr_draw_circle_thick(canvas, 110, 60, 21, 10, PNTR_RED);
11 |
12 | pntr_draw_ellipse_thick(canvas, 110, 100, 20, 10, 8, PNTR_GREEN);
13 |
14 | // Line
15 | pntr_draw_line_thick(canvas, 150, 30, 230, 60, 8, PNTR_DARKGREEN);
16 |
17 | // Triangle
18 | pntr_draw_triangle_thick(canvas,
19 | 250, 50,
20 | 300, 80,
21 | 350, 20, 5, PNTR_PURPLE);
22 |
23 | // Polygon
24 | pntr_vector points[10];
25 | points[0] = PNTR_CLITERAL(pntr_vector) {210, 110};
26 | points[1] = PNTR_CLITERAL(pntr_vector) {215, 130};
27 | points[2] = PNTR_CLITERAL(pntr_vector) {240, 140};
28 | points[3] = PNTR_CLITERAL(pntr_vector) {200, 160};
29 | pntr_draw_polygon_thick(canvas, points, 4, 5, PNTR_BLACK);
30 |
31 | // Arc
32 | int radius = 80;
33 | pntr_draw_arc_thick(canvas, 350, 120, radius, 90.0f, 180.0f, radius * 1.5f, 12, PNTR_RED);
34 |
35 | // Polyline
36 | points[0] = PNTR_CLITERAL(pntr_vector) {240, 80};
37 | points[1] = PNTR_CLITERAL(pntr_vector) {260, 100};
38 | points[2] = PNTR_CLITERAL(pntr_vector) {220, 110};
39 | points[3] = PNTR_CLITERAL(pntr_vector) {240, 130};
40 | pntr_draw_polyline_thick(canvas, points, 4, 5, PNTR_PURPLE);
41 |
42 | // Line Curve
43 | pntr_draw_line_curve_thick(canvas, points[0], points[1], points[2], points[3], 40, 5, PNTR_DARKBLUE);
44 |
45 | // Vertical line
46 | pntr_draw_line_vertical_thick(canvas, 20, 20, 80, 12, PNTR_ORANGE);
47 |
48 | // Horizontal line
49 | pntr_draw_line_horizontal_thick(canvas, 40, 20, 80, 12, PNTR_DARKBROWN);
50 |
51 | pntr_save_image(canvas, "pntr_examples_thick.png");
52 | pntr_unload_image(canvas);
53 | }
54 |
--------------------------------------------------------------------------------
/examples/pntr_examples_thick.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RobLoach/pntr/922aed03c4c53803466100596211da7549945da7/examples/pntr_examples_thick.png
--------------------------------------------------------------------------------
/examples/resources/bmfont.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RobLoach/pntr/922aed03c4c53803466100596211da7549945da7/examples/resources/bmfont.png
--------------------------------------------------------------------------------
/examples/resources/bunny.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RobLoach/pntr/922aed03c4c53803466100596211da7549945da7/examples/resources/bunny.png
--------------------------------------------------------------------------------
/examples/resources/doxygen-awesome.css:
--------------------------------------------------------------------------------
1 | /* Use doxygen-awesome CSS https://jothepro.github.io/doxygen-awesome-css/ */
2 | @import url("https://jothepro.github.io/doxygen-awesome-css/doxygen-awesome.css");
3 |
--------------------------------------------------------------------------------
/examples/resources/logo-128x128.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RobLoach/pntr/922aed03c4c53803466100596211da7549945da7/examples/resources/logo-128x128.png
--------------------------------------------------------------------------------
/examples/resources/logo-32x32.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RobLoach/pntr/922aed03c4c53803466100596211da7549945da7/examples/resources/logo-32x32.png
--------------------------------------------------------------------------------
/examples/resources/logo-55x55.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RobLoach/pntr/922aed03c4c53803466100596211da7549945da7/examples/resources/logo-55x55.png
--------------------------------------------------------------------------------
/examples/resources/readme.txt:
--------------------------------------------------------------------------------
1 | The following are the attributions for pntr resources...
2 |
3 | scarfy.png
4 | By: Eiden Marsal (https://www.artstation.com/marshall_z)
5 | Source: https://github.com/raysan5/raylib/blob/master/examples/textures/resources/scarfy.png
6 | License: CC-BY-NC https://creativecommons.org/licenses/by-nc/4.0/legalcode
7 |
8 | logo*.png
9 | By: Ravenist (https://www.pixilart.com/ravenist)
10 | Source: https://www.pixilart.com/art/bob-ross-9910c4da4b3a1c8
11 |
12 | tuffy.ttf
13 | By: Thatcher Ulrich (http://tulrich.com)
14 | Source: https://www.dafont.com/tuffy.font
15 | License: CC0 https://creativecommons.org/public-domain/
16 |
17 | ttyfont-16x16
18 | Source: https://github.com/Grumbel/SDL_tty/blob/master/data/c64_16x16.png
19 |
20 | bunny.png
21 | Source: https://github.com/raysan5/raylib/blob/master/examples/textures/resources/wabbit_alpha.png
22 |
--------------------------------------------------------------------------------
/examples/resources/scarfy.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RobLoach/pntr/922aed03c4c53803466100596211da7549945da7/examples/resources/scarfy.png
--------------------------------------------------------------------------------
/examples/resources/ttyfont-16x16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RobLoach/pntr/922aed03c4c53803466100596211da7549945da7/examples/resources/ttyfont-16x16.png
--------------------------------------------------------------------------------
/examples/resources/tuffy.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RobLoach/pntr/922aed03c4c53803466100596211da7549945da7/examples/resources/tuffy.ttf
--------------------------------------------------------------------------------
/extensions/pntr_cute_png.h:
--------------------------------------------------------------------------------
1 | #ifndef PNTR_CUTE_PNG_H__
2 | #define PNTR_CUTE_PNG_H__
3 |
4 | /**
5 | * @defgroup pntr_cute_png pntr_cute_png
6 | * @{
7 | *
8 | * @brief [cute_png](https://github.com/RandyGaul/cute_headers/blob/master/cute_png.h) integration with pntr for loading and saving images.
9 | *
10 | * To use `cute_png.h` for saving and loading images, define `PNTR_CUTE_PNG` prior to including `pntr.h`:
11 | *
12 | * @code
13 | * #define PNTR_CUTE_PNG
14 | * #define PNTR_IMPLEMENTATION
15 | * #include "pntr.h"
16 | * @endcode
17 | *
18 | * @see https://github.com/RandyGaul/cute_headers/blob/master/cute_png.h
19 | * @see PNTR_CUTE_PNG
20 | * @see PNTR_LOAD_IMAGE_FROM_MEMORY
21 | * @see PNTR_SAVE_IMAGE_TO_MEMORY
22 | * @see PNTR_NO_LOAD_IMAGE
23 | * @see PNTR_NO_SAVE_IMAGE
24 | */
25 |
26 | /**
27 | * Load a pntr_image using cute_png.
28 | */
29 | pntr_image* pntr_cute_png_load_image_from_memory(pntr_image_type type, const unsigned char *fileData, unsigned int dataSize);
30 |
31 | /**
32 | * Save an image to memory using cutr_png.
33 | */
34 | unsigned char* pntr_cute_png_save_image_to_memory(pntr_image* image, pntr_image_type type, unsigned int* dataSize);
35 |
36 | /**
37 | * @}
38 | */
39 |
40 | #endif // PNTR_CUTE_PNG_H__
41 |
42 | #ifdef PNTR_IMPLEMENTATION
43 | #ifndef PNTR_CUTE_PNG_IMPLEMENTATION
44 | #define PNTR_CUTE_PNG_IMPLEMENTATION
45 |
46 | #ifndef PNTR_MEMCMP
47 | #include
48 | #define PNTR_MEMCMP(ptr1, ptr2, num) memcmp(ptr1, ptr2, num)
49 | #endif // PNTR_MEMCMP
50 |
51 | #ifndef PNTR_NO_CUTE_PNG_IMPLEMENTATION
52 | #define CUTE_PNG_IMPLEMENTATION
53 | #define CUTE_PNG_ALLOCA PNTR_MALLOC
54 | #define CUTE_PNG_ALLOC PNTR_MALLOC
55 | #define CUTE_PNG_FREE PNTR_FREE
56 | #define CUTE_PNG_CALLOC(num, size) PNTR_MALLOC((num) * (size))
57 | #define CUTE_PNG_REALLOC PNTR_REALLOC
58 | #define CUTE_PNG_MEMCMP PNTR_MEMCMP
59 | #define CUTE_PNG_MEMCPY PNTR_MEMCPY
60 | #define CUTE_PNG_MEMSET PNTR_MEMSET
61 | #define CUTE_PNG_FPRINTF (void)
62 | #define CUTE_PNG_ASSERT(condition) 0 // Skip assertions
63 | #define CUTE_PNG_SEEK_SET 0
64 | #define CUTE_PNG_SEEK_END 0
65 | #define CUTE_PNG_FILE void
66 | #define CUTE_PNG_FOPEN(filename, mode) (CUTE_PNG_FILE*)filename
67 | #define CUTE_PNG_FSEEK(stream, offset, origin) offset
68 | #define CUTE_PNG_FREAD(data, size, num, fp) (void)(data)
69 | #define CUTE_PNG_FTELL(fp) 0
70 | #define CUTE_PNG_FWRITE(data, size, num, fp) (void)(data)
71 | #define CUTE_PNG_FCLOSE (void)
72 | #define CUTE_PNG_FERROR(fp) 1
73 | #define CUTE_PNG_ATLAS_MUST_FIT 1
74 | #define CUTE_PNG_ATLAS_FLIP_Y_AXIS_FOR_UV 0
75 | #define CUTE_PNG_ATLAS_EMPTY_COLOR 0
76 | #endif // PNTR_NO_CUTE_PNG_IMPLEMENTATION
77 |
78 | #if defined(__GNUC__) || defined(__clang__)
79 | #pragma GCC diagnostic push
80 | #pragma GCC diagnostic ignored "-Wpragmas"
81 | #pragma GCC diagnostic ignored "-Wunknown-pragmas"
82 | #pragma GCC diagnostic ignored "-Wsign-conversion"
83 | #pragma GCC diagnostic ignored "-Wconversion"
84 | #pragma GCC diagnostic ignored "-Wunused-function"
85 | #pragma GCC diagnostic ignored "-Wunused-variable"
86 | #pragma GCC diagnostic ignored "-Wsign-compare"
87 | #pragma GCC diagnostic ignored "-Wunused-value"
88 | #endif // defined(__GNUC__) || defined(__clang__)
89 |
90 | #ifndef PNTR_CUTE_PNG_H
91 | #define PNTR_CUTE_PNG_H "../external/cute_png.h"
92 | #endif
93 | #include PNTR_CUTE_PNG_H
94 |
95 | #define PNTR_NO_CUTE_PNG_IMPLEMENTATION
96 | #ifdef CUTE_PNG_IMPLEMENTATION
97 | #undef CUTE_PNG_IMPLEMENTATION
98 | #endif
99 |
100 | #if defined(__GNUC__) || defined(__clang__)
101 | #pragma GCC diagnostic pop
102 | #endif // defined(__GNUC__) || defined(__clang__)
103 |
104 | pntr_image* pntr_cute_png_load_image_from_memory(pntr_image_type type, const unsigned char *fileData, unsigned int dataSize) {
105 | if (!(type == PNTR_IMAGE_TYPE_PNG || type == PNTR_IMAGE_TYPE_UNKNOWN)) {
106 | return (pntr_image*)pntr_set_error(PNTR_ERROR_NOT_SUPPORTED);
107 | }
108 |
109 | cp_image_t image = cp_load_png_mem(fileData, (int)dataSize);
110 | if (image.pix == NULL) {
111 | return (pntr_image*)pntr_set_error(PNTR_ERROR_FAILED_TO_OPEN);
112 | }
113 |
114 | pntr_image* output = pntr_image_from_pixelformat((const void*)image.pix, image.w, image.h, PNTR_PIXELFORMAT_RGBA8888);
115 | cp_free_png(&image);
116 |
117 | return output;
118 | }
119 |
120 | #ifndef PNTR_LOAD_IMAGE_FROM_MEMORY
121 | #define PNTR_LOAD_IMAGE_FROM_MEMORY pntr_cute_png_load_image_from_memory
122 | #endif
123 |
124 | unsigned char* pntr_cute_png_save_image_to_memory(pntr_image* image, pntr_image_type type, unsigned int* dataSize) {
125 | if (!(type == PNTR_IMAGE_TYPE_UNKNOWN || type == PNTR_IMAGE_TYPE_PNG)) {
126 | return (unsigned char*)pntr_set_error(PNTR_ERROR_NOT_SUPPORTED);
127 | }
128 |
129 | cp_image_t cpImage = PNTR_CLITERAL(cp_image_t) {
130 | .w = image->width,
131 | .h = image->height
132 | };
133 |
134 | cpImage.pix = (cp_pixel_t*)pntr_image_to_pixelformat(image, NULL, PNTR_PIXELFORMAT_RGBA8888);
135 | if (cpImage.pix == NULL) {
136 | return NULL;
137 | }
138 |
139 | cp_saved_png_t png = cp_save_png_to_memory(&cpImage);
140 | if (png.data == NULL) {
141 | cp_free_png(&cpImage);
142 | return (unsigned char*)pntr_set_error(PNTR_ERROR_FAILED_TO_WRITE);
143 | }
144 |
145 | // Export the datasize
146 | if (dataSize != NULL) {
147 | *dataSize = (unsigned int)png.size;
148 | }
149 |
150 | // Free up the temporary copy
151 | cp_free_png(&cpImage);
152 |
153 | return (unsigned char*)png.data;
154 | }
155 |
156 | #ifndef PNTR_SAVE_IMAGE_TO_MEMORY
157 | #define PNTR_SAVE_IMAGE_TO_MEMORY pntr_cute_png_save_image_to_memory
158 | #endif
159 |
160 | #endif // PNTR_CUTE_PNG_IMPLEMENTATION
161 | #endif // PNTR_IMPLEMENTATION
162 |
--------------------------------------------------------------------------------
/extensions/pntr_stb_image.h:
--------------------------------------------------------------------------------
1 | #ifndef PNTR_STB_IMAGE_H__
2 | #define PNTR_STB_IMAGE_H__
3 |
4 | /**
5 | * @defgroup pntr_stb_image pntr_stb_image
6 | * @{
7 | *
8 | * @brief [stb_image](https://github.com/nothings/stb/blob/master/stb_image.h) integration with pntr for loading images. This is the default behavior.
9 | *
10 | * To use *stb_image* for loading images, define `PNTR_STB_IMAGE` prior to including `pntr.h`. This is provided by default.
11 | *
12 | * @code
13 | * #define PNTR_STB_IMAGE
14 | * #define PNTR_IMPLEMENTATION
15 | * #include "pntr.h"
16 | * @endcode
17 | *
18 | * Image loading can be completely disabled with `PNTR_NO_LOAD_IMAGE`.
19 | *
20 | * @see https://github.com/nothings/stb/blob/master/stb_image.h
21 | * @see PNTR_STB_IMAGE
22 | * @see PNTR_LOAD_IMAGE_FROM_MEMORY
23 | * @see PNTR_NO_LOAD_IMAGE
24 | */
25 |
26 | /**
27 | * Load an image using stb_image.
28 | */
29 | pntr_image* pntr_stb_image_load_image_from_memory(pntr_image_type type, const unsigned char *fileData, unsigned int dataSize);
30 |
31 | /**
32 | * @}
33 | */
34 |
35 | #endif // PNTR_STB_IMAGE_H__
36 |
37 | #ifdef PNTR_IMPLEMENTATION
38 | #ifndef PNTR_STB_IMAGE_IMPLEMENTATION
39 | #define PNTR_STB_IMAGE_IMPLEMENTATION
40 |
41 | #ifndef PNTR_NO_STB_IMAGE_IMPLEMENTATION
42 | #define STB_IMAGE_IMPLEMENTATION
43 | #define STBI_MALLOC PNTR_MALLOC
44 | #define STBI_REALLOC PNTR_REALLOC
45 | #define STBI_FREE PNTR_FREE
46 | #define STBI_NO_HDR
47 | #define STBI_NO_FAILURE_STRINGS
48 | #define STBI_NO_STDIO
49 | #define STBI_NO_SIMD
50 | #define STBI_NO_THREAD_LOCALS
51 | #ifndef PNTR_ENABLE_JPEG
52 | #define STBI_NO_JPEG // JPG support in stb_image.
53 | #endif
54 | //#define STBI_NO_PNG
55 | #define STBI_NO_BMP
56 | #define STBI_NO_PSD
57 | #define STBI_NO_TGA
58 | #define STBI_NO_GIF
59 | #define STBI_NO_HDR
60 | #define STBI_NO_PIC
61 | #define STBI_NO_PNM
62 | //#define STBI_SUPPORT_ZLIB
63 | #define STBI_NO_LINEAR
64 |
65 | #define STBI_ASSERT(x)
66 | #endif // PNTR_NO_STB_IMAGE_IMPLEMENTATION
67 |
68 | #if defined(__GNUC__) || defined(__clang__)
69 | #pragma GCC diagnostic push
70 | #pragma GCC diagnostic ignored "-Wpragmas"
71 | #pragma GCC diagnostic ignored "-Wunknown-pragmas"
72 | #pragma GCC diagnostic ignored "-Wsign-conversion"
73 | #pragma GCC diagnostic ignored "-Wconversion"
74 | #pragma GCC diagnostic ignored "-Wunused-function"
75 | #pragma GCC diagnostic ignored "-Wunused-variable"
76 | #pragma GCC diagnostic ignored "-Wsign-compare"
77 | #pragma GCC diagnostic ignored "-Wunused-value"
78 | #endif // defined(__GNUC__) || defined(__clang__)
79 |
80 | #ifndef PNTR_STB_IMAGE_H
81 | #define PNTR_STB_IMAGE_H "../external/stb_image.h"
82 | #endif
83 | #include PNTR_STB_IMAGE_H
84 |
85 | #ifndef PNTR_NO_STB_IMAGE_IMPLEMENTATION
86 | #define PNTR_NO_STB_IMAGE_IMPLEMENTATION
87 | #endif
88 | #ifdef STB_IMAGE_IMPLEMENTATION
89 | #undef STB_IMAGE_IMPLEMENTATION
90 | #endif
91 |
92 | #if defined(__GNUC__) || defined(__clang__)
93 | #pragma GCC diagnostic pop
94 | #endif // defined(__GNUC__) || defined(__clang__)
95 |
96 | pntr_image* pntr_stb_image_load_image_from_memory(pntr_image_type type, const unsigned char *fileData, unsigned int dataSize) {
97 | // We don't need to use the type, as stb_image will figure that out for us.
98 | (void)type;
99 |
100 | int x, y, channels_in_file;
101 | int desired_channels = 4;
102 | stbi_uc *data = stbi_load_from_memory((stbi_uc const *)fileData, (int)dataSize, &x, &y, &channels_in_file, desired_channels);
103 | if (data == NULL) {
104 | return NULL;
105 | }
106 |
107 | pntr_image* output = pntr_image_from_pixelformat(data, x, y, PNTR_PIXELFORMAT_RGBA8888);
108 | stbi_image_free(data);
109 |
110 | return output;
111 | }
112 |
113 | #ifdef PNTR_LOAD_IMAGE_FROM_MEMORY
114 | #undef PNTR_LOAD_IMAGE_FROM_MEMORY
115 | #endif
116 |
117 | #define PNTR_LOAD_IMAGE_FROM_MEMORY pntr_stb_image_load_image_from_memory
118 |
119 | #endif // PNTR_STB_IMAGE_IMPLEMENTATION
120 | #endif // PNTR_IMPLEMENTATION
121 |
--------------------------------------------------------------------------------
/extensions/pntr_stb_image_write.h:
--------------------------------------------------------------------------------
1 | #ifndef PNTR_STB_IMAGE_WRITE_H__
2 | #define PNTR_STB_IMAGE_WRITE_H__
3 |
4 | /**
5 | * @defgroup pntr_stb_image_write pntr_stb_image_write
6 | * @{
7 | *
8 | * @brief [stb_image_write](https://github.com/nothings/stb/blob/master/stb_image_write.h) integration with pntr for saving images.
9 | *
10 | * To use *stb_image_write* for saving images, define `PNTR_STB_IMAGE` prior to including `pntr.h`. This is provided by default.
11 | *
12 | * @code
13 | * #define PNTR_STB_IMAGE
14 | * #define PNTR_IMPLEMENTATION
15 | * #include "pntr.h"
16 | * @endcode
17 | *
18 | * Image saving can be completely disabled with `PNTR_NO_SAVE_IMAGE`.
19 | *
20 | * @see https://github.com/nothings/stb/blob/master/stb_image_write.h
21 | * @see PNTR_STB_IMAGE
22 | * @see PNTR_SAVE_IMAGE_TO_MEMORY
23 | * @see PNTR_NO_SAVE_IMAGE
24 | */
25 |
26 | /**
27 | * Save an image using stb_image.
28 | */
29 | unsigned char* pntr_stb_image_save_image_to_memory(pntr_image* image, pntr_image_type type, unsigned int* dataSize);
30 |
31 | /**
32 | * @}
33 | */
34 |
35 | #endif // PNTR_STB_IMAGE_WRITE_H__
36 |
37 | #ifdef PNTR_IMPLEMENTATION
38 | #ifndef PNTR_STB_IMAGE_WRITE_IMPLEMENTATION
39 | #define PNTR_STB_IMAGE_WRITE_IMPLEMENTATION
40 |
41 | #ifndef PNTR_MEMMOVE
42 | #include
43 | /**
44 | * Copies the values of num bytes from the location pointed by source to the memory block pointed by destination.
45 | *
46 | * @note If not defined, will use `memmove()`.
47 | *
48 | * @param destination Pointer to the destination array where the content is to be copied, type-casted to a pointer of type `void*`.
49 | * @param source Pointer to the source of data to be copied, type-casted to a pointer of type `const void*`.
50 | * @param num Number of bytes to copy.
51 | */
52 | #define PNTR_MEMMOVE(destination, source, num) memmove((destination), (source), (num))
53 | #endif // PNTR_MEMMOVE
54 |
55 | #ifndef PNTR_NO_STB_IMAGE_WRITE_IMPLEMENTATION
56 | #define STB_IMAGE_WRITE_IMPLEMENTATION
57 | #define STBIW_MALLOC PNTR_MALLOC
58 | #define STBIW_REALLOC PNTR_REALLOC
59 | #define STBIW_FREE PNTR_FREE
60 | #define STBIW_MEMMOVE PNTR_MEMMOVE
61 | #define STBI_WRITE_NO_STDIO
62 | #define STBIW_ASSERT(x)
63 | #endif // PNTR_NO_STB_IMAGE_IMPLEMENTATION
64 |
65 | #if defined(__GNUC__) || defined(__clang__)
66 | #pragma GCC diagnostic push
67 | #pragma GCC diagnostic ignored "-Wpragmas"
68 | #pragma GCC diagnostic ignored "-Wunknown-pragmas"
69 | #pragma GCC diagnostic ignored "-Wsign-conversion"
70 | #pragma GCC diagnostic ignored "-Wconversion"
71 | #pragma GCC diagnostic ignored "-Wunused-function"
72 | #pragma GCC diagnostic ignored "-Wunused-variable"
73 | #pragma GCC diagnostic ignored "-Wsign-compare"
74 | #pragma GCC diagnostic ignored "-Wunused-value"
75 | #endif // defined(__GNUC__) || defined(__clang__)
76 |
77 | #ifndef PNTR_STB_IMAGE_WRITE_H
78 | #define PNTR_STB_IMAGE_WRITE_H "../external/stb_image_write.h"
79 | #endif
80 | #include PNTR_STB_IMAGE_WRITE_H
81 |
82 | #ifndef PNTR_NO_STB_IMAGE_WRITE_IMPLEMENTATION
83 | #define PNTR_NO_STB_IMAGE_WRITE_IMPLEMENTATION
84 | #endif
85 | #ifdef STB_IMAGE_WRITE_IMPLEMENTATION
86 | #undef STB_IMAGE_WRITE_IMPLEMENTATION
87 | #endif
88 |
89 | #if defined(__GNUC__) || defined(__clang__)
90 | #pragma GCC diagnostic pop
91 | #endif // defined(__GNUC__) || defined(__clang__)
92 |
93 | typedef struct pntr_stb_image_context {
94 | unsigned int dataSize;
95 | void* data;
96 | } pntr_stb_image_context;
97 |
98 | void pntr_stb_image_write_func(void *context, void *data, int size) {
99 | pntr_stb_image_context* ctx = (pntr_stb_image_context*)context;
100 | ctx->dataSize = (unsigned int)size;
101 | if (size == 0) {
102 | ctx->data = NULL;
103 | }
104 | else {
105 | // Copy the stb_image data, because stb_image will free() it afterwards.
106 | ctx->data = pntr_load_memory((size_t)size);
107 | pntr_memory_copy(ctx->data, data, (size_t)size);
108 | }
109 | }
110 |
111 | unsigned char* pntr_stb_image_save_image_to_memory(pntr_image* image, pntr_image_type type, unsigned int* dataSize) {
112 | const unsigned char* pixels = (const unsigned char*)pntr_image_to_pixelformat(image, NULL, PNTR_PIXELFORMAT_RGBA8888);
113 | if (pixels == NULL) {
114 | return NULL;
115 | }
116 |
117 | pntr_stb_image_context context;
118 | context.data = NULL;
119 | context.dataSize = 0;
120 |
121 | switch (type) {
122 | case PNTR_IMAGE_TYPE_UNKNOWN:
123 | case PNTR_IMAGE_TYPE_PNG: {
124 | int stride_bytes = pntr_get_pixel_data_size(image->width, 1, PNTR_PIXELFORMAT_RGBA8888);
125 | stbi_write_png_to_func(pntr_stb_image_write_func, &context, image->width, image->height, 4, pixels, stride_bytes);
126 | }
127 | break;
128 | case PNTR_IMAGE_TYPE_JPG: {
129 | stbi_write_jpg_to_func(pntr_stb_image_write_func, &context, image->width, image->height, 4, pixels, 100);
130 | }
131 | break;
132 | case PNTR_IMAGE_TYPE_BMP: {
133 | stbi_write_bmp_to_func(pntr_stb_image_write_func, &context, image->width, image->height, 4, pixels);
134 | }
135 | break;
136 | default:
137 | pntr_set_error(PNTR_ERROR_NOT_SUPPORTED);
138 | break;
139 | }
140 |
141 | pntr_unload_memory((void*)pixels);
142 |
143 | if (dataSize != NULL) {
144 | *dataSize = (unsigned int)context.dataSize;
145 | }
146 |
147 | return (unsigned char*)context.data;
148 | }
149 |
150 | #ifndef PNTR_SAVE_IMAGE_TO_MEMORY
151 | #define PNTR_SAVE_IMAGE_TO_MEMORY pntr_stb_image_save_image_to_memory
152 | #endif
153 |
154 | #endif // PNTR_STB_IMAGE_WRITE_IMPLEMENTATION
155 | #endif // PNTR_IMPLEMENTATION
156 |
--------------------------------------------------------------------------------
/external/font8x8_basic.h:
--------------------------------------------------------------------------------
1 | /**
2 | * 8x8 monochrome bitmap fonts for rendering
3 | * Author: Daniel Hepper
4 | *
5 | * https://github.com/dhepper/font8x8/blob/master/font8x8_basic.h
6 | *
7 | * License: Public Domain
8 | *
9 | * Based on:
10 | * // Summary: font8x8.h
11 | * // 8x8 monochrome bitmap fonts for rendering
12 | * //
13 | * // Author:
14 | * // Marcel Sondaar
15 | * // International Business Machines (public domain VGA fonts)
16 | * //
17 | * // License:
18 | * // Public Domain
19 | *
20 | * Fetched from: http://dimensionalrift.homelinux.net/combuster/mos3/?p=viewsource&file=/modules/gfx/font8_8.asm
21 | **/
22 |
23 | // Constant: font8x8_basic
24 | // Contains an 8x8 font map for unicode points U+0000 - U+007F (basic latin)
25 | const uint8_t font8x8_basic[95][8] = {
26 | /*
27 | { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0000 (nul)
28 | { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0001
29 | { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0002
30 | { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0003
31 | { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0004
32 | { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0005
33 | { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0006
34 | { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0007
35 | { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0008
36 | { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0009
37 | { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+000A
38 | { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+000B
39 | { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+000C
40 | { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+000D
41 | { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+000E
42 | { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+000F
43 | { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0010
44 | { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0011
45 | { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0012
46 | { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0013
47 | { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0014
48 | { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0015
49 | { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0016
50 | { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0017
51 | { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0018
52 | { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0019
53 | { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+001A
54 | { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+001B
55 | { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+001C
56 | { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+001D
57 | { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+001E
58 | { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+001F
59 | */
60 | { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0020 (space)
61 | { 0x18, 0x3C, 0x3C, 0x18, 0x18, 0x00, 0x18, 0x00}, // U+0021 (!)
62 | { 0x36, 0x36, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0022 (")
63 | { 0x36, 0x36, 0x7F, 0x36, 0x7F, 0x36, 0x36, 0x00}, // U+0023 (#)
64 | { 0x0C, 0x3E, 0x03, 0x1E, 0x30, 0x1F, 0x0C, 0x00}, // U+0024 ($)
65 | { 0x00, 0x63, 0x33, 0x18, 0x0C, 0x66, 0x63, 0x00}, // U+0025 (%)
66 | { 0x1C, 0x36, 0x1C, 0x6E, 0x3B, 0x33, 0x6E, 0x00}, // U+0026 (&)
67 | { 0x06, 0x06, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0027 (')
68 | { 0x18, 0x0C, 0x06, 0x06, 0x06, 0x0C, 0x18, 0x00}, // U+0028 (()
69 | { 0x06, 0x0C, 0x18, 0x18, 0x18, 0x0C, 0x06, 0x00}, // U+0029 ())
70 | { 0x00, 0x66, 0x3C, 0xFF, 0x3C, 0x66, 0x00, 0x00}, // U+002A (*)
71 | { 0x00, 0x0C, 0x0C, 0x3F, 0x0C, 0x0C, 0x00, 0x00}, // U+002B (+)
72 | { 0x00, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x0C, 0x06}, // U+002C (,)
73 | { 0x00, 0x00, 0x00, 0x3F, 0x00, 0x00, 0x00, 0x00}, // U+002D (-)
74 | { 0x00, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x0C, 0x00}, // U+002E (.)
75 | { 0x60, 0x30, 0x18, 0x0C, 0x06, 0x03, 0x01, 0x00}, // U+002F (/)
76 | { 0x3E, 0x63, 0x73, 0x7B, 0x6F, 0x67, 0x3E, 0x00}, // U+0030 (0)
77 | { 0x0C, 0x0E, 0x0C, 0x0C, 0x0C, 0x0C, 0x3F, 0x00}, // U+0031 (1)
78 | { 0x1E, 0x33, 0x30, 0x1C, 0x06, 0x33, 0x3F, 0x00}, // U+0032 (2)
79 | { 0x1E, 0x33, 0x30, 0x1C, 0x30, 0x33, 0x1E, 0x00}, // U+0033 (3)
80 | { 0x38, 0x3C, 0x36, 0x33, 0x7F, 0x30, 0x78, 0x00}, // U+0034 (4)
81 | { 0x3F, 0x03, 0x1F, 0x30, 0x30, 0x33, 0x1E, 0x00}, // U+0035 (5)
82 | { 0x1C, 0x06, 0x03, 0x1F, 0x33, 0x33, 0x1E, 0x00}, // U+0036 (6)
83 | { 0x3F, 0x33, 0x30, 0x18, 0x0C, 0x0C, 0x0C, 0x00}, // U+0037 (7)
84 | { 0x1E, 0x33, 0x33, 0x1E, 0x33, 0x33, 0x1E, 0x00}, // U+0038 (8)
85 | { 0x1E, 0x33, 0x33, 0x3E, 0x30, 0x18, 0x0E, 0x00}, // U+0039 (9)
86 | { 0x00, 0x0C, 0x0C, 0x00, 0x00, 0x0C, 0x0C, 0x00}, // U+003A (:)
87 | { 0x00, 0x0C, 0x0C, 0x00, 0x00, 0x0C, 0x0C, 0x06}, // U+003B (;)
88 | { 0x18, 0x0C, 0x06, 0x03, 0x06, 0x0C, 0x18, 0x00}, // U+003C (<)
89 | { 0x00, 0x00, 0x3F, 0x00, 0x00, 0x3F, 0x00, 0x00}, // U+003D (=)
90 | { 0x06, 0x0C, 0x18, 0x30, 0x18, 0x0C, 0x06, 0x00}, // U+003E (>)
91 | { 0x1E, 0x33, 0x30, 0x18, 0x0C, 0x00, 0x0C, 0x00}, // U+003F (?)
92 | { 0x3E, 0x63, 0x7B, 0x7B, 0x7B, 0x03, 0x1E, 0x00}, // U+0040 (@)
93 | { 0x0C, 0x1E, 0x33, 0x33, 0x3F, 0x33, 0x33, 0x00}, // U+0041 (A)
94 | { 0x3F, 0x66, 0x66, 0x3E, 0x66, 0x66, 0x3F, 0x00}, // U+0042 (B)
95 | { 0x3C, 0x66, 0x03, 0x03, 0x03, 0x66, 0x3C, 0x00}, // U+0043 (C)
96 | { 0x1F, 0x36, 0x66, 0x66, 0x66, 0x36, 0x1F, 0x00}, // U+0044 (D)
97 | { 0x7F, 0x46, 0x16, 0x1E, 0x16, 0x46, 0x7F, 0x00}, // U+0045 (E)
98 | { 0x7F, 0x46, 0x16, 0x1E, 0x16, 0x06, 0x0F, 0x00}, // U+0046 (F)
99 | { 0x3C, 0x66, 0x03, 0x03, 0x73, 0x66, 0x7C, 0x00}, // U+0047 (G)
100 | { 0x33, 0x33, 0x33, 0x3F, 0x33, 0x33, 0x33, 0x00}, // U+0048 (H)
101 | { 0x1E, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x1E, 0x00}, // U+0049 (I)
102 | { 0x78, 0x30, 0x30, 0x30, 0x33, 0x33, 0x1E, 0x00}, // U+004A (J)
103 | { 0x67, 0x66, 0x36, 0x1E, 0x36, 0x66, 0x67, 0x00}, // U+004B (K)
104 | { 0x0F, 0x06, 0x06, 0x06, 0x46, 0x66, 0x7F, 0x00}, // U+004C (L)
105 | { 0x63, 0x77, 0x7F, 0x7F, 0x6B, 0x63, 0x63, 0x00}, // U+004D (M)
106 | { 0x63, 0x67, 0x6F, 0x7B, 0x73, 0x63, 0x63, 0x00}, // U+004E (N)
107 | { 0x1C, 0x36, 0x63, 0x63, 0x63, 0x36, 0x1C, 0x00}, // U+004F (O)
108 | { 0x3F, 0x66, 0x66, 0x3E, 0x06, 0x06, 0x0F, 0x00}, // U+0050 (P)
109 | { 0x1E, 0x33, 0x33, 0x33, 0x3B, 0x1E, 0x38, 0x00}, // U+0051 (Q)
110 | { 0x3F, 0x66, 0x66, 0x3E, 0x36, 0x66, 0x67, 0x00}, // U+0052 (R)
111 | { 0x1E, 0x33, 0x07, 0x0E, 0x38, 0x33, 0x1E, 0x00}, // U+0053 (S)
112 | { 0x3F, 0x2D, 0x0C, 0x0C, 0x0C, 0x0C, 0x1E, 0x00}, // U+0054 (T)
113 | { 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x3F, 0x00}, // U+0055 (U)
114 | { 0x33, 0x33, 0x33, 0x33, 0x33, 0x1E, 0x0C, 0x00}, // U+0056 (V)
115 | { 0x63, 0x63, 0x63, 0x6B, 0x7F, 0x77, 0x63, 0x00}, // U+0057 (W)
116 | { 0x63, 0x63, 0x36, 0x1C, 0x1C, 0x36, 0x63, 0x00}, // U+0058 (X)
117 | { 0x33, 0x33, 0x33, 0x1E, 0x0C, 0x0C, 0x1E, 0x00}, // U+0059 (Y)
118 | { 0x7F, 0x63, 0x31, 0x18, 0x4C, 0x66, 0x7F, 0x00}, // U+005A (Z)
119 | { 0x1E, 0x06, 0x06, 0x06, 0x06, 0x06, 0x1E, 0x00}, // U+005B ([)
120 | { 0x03, 0x06, 0x0C, 0x18, 0x30, 0x60, 0x40, 0x00}, // U+005C (\)
121 | { 0x1E, 0x18, 0x18, 0x18, 0x18, 0x18, 0x1E, 0x00}, // U+005D (])
122 | { 0x08, 0x1C, 0x36, 0x63, 0x00, 0x00, 0x00, 0x00}, // U+005E (^)
123 | { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF}, // U+005F (_)
124 | { 0x0C, 0x0C, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0060 (`)
125 | { 0x00, 0x00, 0x1E, 0x30, 0x3E, 0x33, 0x6E, 0x00}, // U+0061 (a)
126 | { 0x07, 0x06, 0x06, 0x3E, 0x66, 0x66, 0x3B, 0x00}, // U+0062 (b)
127 | { 0x00, 0x00, 0x1E, 0x33, 0x03, 0x33, 0x1E, 0x00}, // U+0063 (c)
128 | { 0x38, 0x30, 0x30, 0x3e, 0x33, 0x33, 0x6E, 0x00}, // U+0064 (d)
129 | { 0x00, 0x00, 0x1E, 0x33, 0x3f, 0x03, 0x1E, 0x00}, // U+0065 (e)
130 | { 0x1C, 0x36, 0x06, 0x0f, 0x06, 0x06, 0x0F, 0x00}, // U+0066 (f)
131 | { 0x00, 0x00, 0x6E, 0x33, 0x33, 0x3E, 0x30, 0x1F}, // U+0067 (g)
132 | { 0x07, 0x06, 0x36, 0x6E, 0x66, 0x66, 0x67, 0x00}, // U+0068 (h)
133 | { 0x0C, 0x00, 0x0E, 0x0C, 0x0C, 0x0C, 0x1E, 0x00}, // U+0069 (i)
134 | { 0x30, 0x00, 0x30, 0x30, 0x30, 0x33, 0x33, 0x1E}, // U+006A (j)
135 | { 0x07, 0x06, 0x66, 0x36, 0x1E, 0x36, 0x67, 0x00}, // U+006B (k)
136 | { 0x0E, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x1E, 0x00}, // U+006C (l)
137 | { 0x00, 0x00, 0x33, 0x7F, 0x7F, 0x6B, 0x63, 0x00}, // U+006D (m)
138 | { 0x00, 0x00, 0x1F, 0x33, 0x33, 0x33, 0x33, 0x00}, // U+006E (n)
139 | { 0x00, 0x00, 0x1E, 0x33, 0x33, 0x33, 0x1E, 0x00}, // U+006F (o)
140 | { 0x00, 0x00, 0x3B, 0x66, 0x66, 0x3E, 0x06, 0x0F}, // U+0070 (p)
141 | { 0x00, 0x00, 0x6E, 0x33, 0x33, 0x3E, 0x30, 0x78}, // U+0071 (q)
142 | { 0x00, 0x00, 0x3B, 0x6E, 0x66, 0x06, 0x0F, 0x00}, // U+0072 (r)
143 | { 0x00, 0x00, 0x3E, 0x03, 0x1E, 0x30, 0x1F, 0x00}, // U+0073 (s)
144 | { 0x08, 0x0C, 0x3E, 0x0C, 0x0C, 0x2C, 0x18, 0x00}, // U+0074 (t)
145 | { 0x00, 0x00, 0x33, 0x33, 0x33, 0x33, 0x6E, 0x00}, // U+0075 (u)
146 | { 0x00, 0x00, 0x33, 0x33, 0x33, 0x1E, 0x0C, 0x00}, // U+0076 (v)
147 | { 0x00, 0x00, 0x63, 0x6B, 0x7F, 0x7F, 0x36, 0x00}, // U+0077 (w)
148 | { 0x00, 0x00, 0x63, 0x36, 0x1C, 0x36, 0x63, 0x00}, // U+0078 (x)
149 | { 0x00, 0x00, 0x33, 0x33, 0x33, 0x3E, 0x30, 0x1F}, // U+0079 (y)
150 | { 0x00, 0x00, 0x3F, 0x19, 0x0C, 0x26, 0x3F, 0x00}, // U+007A (z)
151 | { 0x38, 0x0C, 0x0C, 0x07, 0x0C, 0x0C, 0x38, 0x00}, // U+007B ({)
152 | { 0x18, 0x18, 0x18, 0x00, 0x18, 0x18, 0x18, 0x00}, // U+007C (|)
153 | { 0x07, 0x0C, 0x0C, 0x38, 0x0C, 0x0C, 0x07, 0x00}, // U+007D (})
154 | { 0x6E, 0x3B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}//, // U+007E (~)
155 | //{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} // U+007F
156 | };
157 |
--------------------------------------------------------------------------------
/external/utf8.h:
--------------------------------------------------------------------------------
1 | /* The latest version of this library is available on GitHub;
2 | * https://github.com/sheredom/utf8.h */
3 |
4 | /* This is free and unencumbered software released into the public domain.
5 | *
6 | * Anyone is free to copy, modify, publish, use, compile, sell, or
7 | * distribute this software, either in source code form or as a compiled
8 | * binary, for any purpose, commercial or non-commercial, and by any
9 | * means.
10 | *
11 | * In jurisdictions that recognize copyright laws, the author or authors
12 | * of this software dedicate any and all copyright interest in the
13 | * software to the public domain. We make this dedication for the benefit
14 | * of the public at large and to the detriment of our heirs and
15 | * successors. We intend this dedication to be an overt act of
16 | * relinquishment in perpetuity of all present and future rights to this
17 | * software under copyright law.
18 | *
19 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
20 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
21 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
22 | * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
23 | * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
24 | * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
25 | * OTHER DEALINGS IN THE SOFTWARE.
26 | *
27 | * For more information, please refer to */
28 |
29 | #ifndef SHEREDOM_UTF8_H_INCLUDED
30 | #define SHEREDOM_UTF8_H_INCLUDED
31 |
32 | #if defined(_MSC_VER)
33 | #pragma warning(push)
34 |
35 | /* disable warning: no function prototype given: converting '()' to '(void)' */
36 | #pragma warning(disable : 4255)
37 |
38 | /* disable warning: '__cplusplus' is not defined as a preprocessor macro,
39 | * replacing with '0' for '#if/#elif' */
40 | #pragma warning(disable : 4668)
41 |
42 | /* disable warning: bytes padding added after construct */
43 | #pragma warning(disable : 4820)
44 | #endif
45 |
46 | #if defined(__cplusplus)
47 | #if defined(_MSC_VER)
48 | #define utf8_cplusplus _MSVC_LANG
49 | #else
50 | #define utf8_cplusplus __cplusplus
51 | #endif
52 | #endif
53 |
54 | #include
55 | #include
56 |
57 | #if defined(_MSC_VER)
58 | #pragma warning(pop)
59 | #endif
60 |
61 | #if defined(_MSC_VER) && (_MSC_VER < 1920)
62 | typedef __int32 utf8_int32_t;
63 | #else
64 | #include
65 | typedef int32_t utf8_int32_t;
66 | #endif
67 |
68 | #if defined(__clang__)
69 | #pragma clang diagnostic push
70 | #pragma clang diagnostic ignored "-Wold-style-cast"
71 | #pragma clang diagnostic ignored "-Wcast-qual"
72 |
73 | #if __has_warning("-Wunsafe-buffer-usage")
74 | #pragma clang diagnostic ignored "-Wunsafe-buffer-usage"
75 | #endif
76 | #endif
77 |
78 | #ifdef utf8_cplusplus
79 | extern "C" {
80 | #endif
81 |
82 | #if defined(__TINYC__)
83 | #define UTF8_ATTRIBUTE(a) __attribute((a))
84 | #else
85 | #define UTF8_ATTRIBUTE(a) __attribute__((a))
86 | #endif
87 |
88 | #if defined(_MSC_VER)
89 | #define utf8_nonnull
90 | #define utf8_pure
91 | #define utf8_restrict __restrict
92 | #define utf8_weak __inline
93 | #elif defined(__clang__) || defined(__GNUC__)
94 | #define utf8_nonnull UTF8_ATTRIBUTE(nonnull)
95 | #define utf8_pure UTF8_ATTRIBUTE(pure)
96 | #define utf8_restrict __restrict__
97 | #define utf8_weak UTF8_ATTRIBUTE(weak)
98 | #elif defined(__TINYC__)
99 | #define utf8_nonnull UTF8_ATTRIBUTE(nonnull)
100 | #define utf8_pure UTF8_ATTRIBUTE(pure)
101 | #define utf8_restrict
102 | #define utf8_weak UTF8_ATTRIBUTE(weak)
103 | #elif defined(__IAR_SYSTEMS_ICC__)
104 | #define utf8_nonnull
105 | #define utf8_pure UTF8_ATTRIBUTE(pure)
106 | #define utf8_restrict __restrict
107 | #define utf8_weak UTF8_ATTRIBUTE(weak)
108 | #else
109 | #error Non clang, non gcc, non MSVC, non tcc, non iar compiler found!
110 | #endif
111 |
112 | #ifdef utf8_cplusplus
113 | #define utf8_null NULL
114 | #else
115 | #define utf8_null 0
116 | #endif
117 |
118 | #if defined(utf8_cplusplus) && utf8_cplusplus >= 201402L && (!defined(_MSC_VER) || (defined(_MSC_VER) && _MSC_VER >= 1910))
119 | #define utf8_constexpr14 constexpr
120 | #define utf8_constexpr14_impl constexpr
121 | #else
122 | /* constexpr and weak are incompatible. so only enable one of them */
123 | #define utf8_constexpr14 utf8_weak
124 | #define utf8_constexpr14_impl
125 | #endif
126 |
127 | #if defined(utf8_cplusplus) && utf8_cplusplus >= 202002L && defined(__cpp_char8_t)
128 | using utf8_int8_t = char8_t; /* Introduced in C++20 */
129 | #else
130 | typedef char utf8_int8_t;
131 | #endif
132 |
133 | /* Return less than 0, 0, greater than 0 if src1 < src2, src1 == src2, src1 >
134 | * src2 respectively, case insensitive. */
135 | utf8_constexpr14 utf8_nonnull utf8_pure int
136 | utf8casecmp(const utf8_int8_t *src1, const utf8_int8_t *src2);
137 |
138 | /* Append the utf8 string src onto the utf8 string dst. */
139 | utf8_nonnull utf8_weak utf8_int8_t *
140 | utf8cat(utf8_int8_t *utf8_restrict dst, const utf8_int8_t *utf8_restrict src);
141 |
142 | /* Find the first match of the utf8 codepoint chr in the utf8 string src. */
143 | utf8_constexpr14 utf8_nonnull utf8_pure utf8_int8_t *
144 | utf8chr(const utf8_int8_t *src, utf8_int32_t chr);
145 |
146 | /* Return less than 0, 0, greater than 0 if src1 < src2,
147 | * src1 == src2, src1 > src2 respectively. */
148 | utf8_constexpr14 utf8_nonnull utf8_pure int utf8cmp(const utf8_int8_t *src1,
149 | const utf8_int8_t *src2);
150 |
151 | /* Copy the utf8 string src onto the memory allocated in dst. */
152 | utf8_nonnull utf8_weak utf8_int8_t *
153 | utf8cpy(utf8_int8_t *utf8_restrict dst, const utf8_int8_t *utf8_restrict src);
154 |
155 | /* Number of utf8 codepoints in the utf8 string src that consists entirely
156 | * of utf8 codepoints not from the utf8 string reject. */
157 | utf8_constexpr14 utf8_nonnull utf8_pure size_t
158 | utf8cspn(const utf8_int8_t *src, const utf8_int8_t *reject);
159 |
160 | /* Duplicate the utf8 string src by getting its size, malloc'ing a new buffer
161 | * copying over the data, and returning that. Or 0 if malloc failed. */
162 | utf8_weak utf8_int8_t *utf8dup(const utf8_int8_t *src);
163 |
164 | /* Number of utf8 codepoints in the utf8 string str,
165 | * excluding the null terminating byte. */
166 | utf8_constexpr14 utf8_nonnull utf8_pure size_t utf8len(const utf8_int8_t *str);
167 |
168 | /* Similar to utf8len, except that only at most n bytes of src are looked. */
169 | utf8_constexpr14 utf8_nonnull utf8_pure size_t utf8nlen(const utf8_int8_t *str,
170 | size_t n);
171 |
172 | /* Return less than 0, 0, greater than 0 if src1 < src2, src1 == src2, src1 >
173 | * src2 respectively, case insensitive. Checking at most n bytes of each utf8
174 | * string. */
175 | utf8_constexpr14 utf8_nonnull utf8_pure int
176 | utf8ncasecmp(const utf8_int8_t *src1, const utf8_int8_t *src2, size_t n);
177 |
178 | /* Append the utf8 string src onto the utf8 string dst,
179 | * writing at most n+1 bytes. Can produce an invalid utf8
180 | * string if n falls partway through a utf8 codepoint. */
181 | utf8_nonnull utf8_weak utf8_int8_t *
182 | utf8ncat(utf8_int8_t *utf8_restrict dst, const utf8_int8_t *utf8_restrict src,
183 | size_t n);
184 |
185 | /* Return less than 0, 0, greater than 0 if src1 < src2,
186 | * src1 == src2, src1 > src2 respectively. Checking at most n
187 | * bytes of each utf8 string. */
188 | utf8_constexpr14 utf8_nonnull utf8_pure int
189 | utf8ncmp(const utf8_int8_t *src1, const utf8_int8_t *src2, size_t n);
190 |
191 | /* Copy the utf8 string src onto the memory allocated in dst.
192 | * Copies at most n bytes. If n falls partway through a utf8
193 | * codepoint, or if dst doesn't have enough room for a null
194 | * terminator, the final string will be cut short to preserve
195 | * utf8 validity. */
196 |
197 | utf8_nonnull utf8_weak utf8_int8_t *
198 | utf8ncpy(utf8_int8_t *utf8_restrict dst, const utf8_int8_t *utf8_restrict src,
199 | size_t n);
200 |
201 | /* Similar to utf8dup, except that at most n bytes of src are copied. If src is
202 | * longer than n, only n bytes are copied and a null byte is added.
203 | *
204 | * Returns a new string if successful, 0 otherwise */
205 | utf8_weak utf8_int8_t *utf8ndup(const utf8_int8_t *src, size_t n);
206 |
207 | /* Locates the first occurrence in the utf8 string str of any byte in the
208 | * utf8 string accept, or 0 if no match was found. */
209 | utf8_constexpr14 utf8_nonnull utf8_pure utf8_int8_t *
210 | utf8pbrk(const utf8_int8_t *str, const utf8_int8_t *accept);
211 |
212 | /* Find the last match of the utf8 codepoint chr in the utf8 string src. */
213 | utf8_constexpr14 utf8_nonnull utf8_pure utf8_int8_t *
214 | utf8rchr(const utf8_int8_t *src, int chr);
215 |
216 | /* Number of bytes in the utf8 string str,
217 | * including the null terminating byte. */
218 | utf8_constexpr14 utf8_nonnull utf8_pure size_t utf8size(const utf8_int8_t *str);
219 |
220 | /* Similar to utf8size, except that the null terminating byte is excluded. */
221 | utf8_constexpr14 utf8_nonnull utf8_pure size_t
222 | utf8size_lazy(const utf8_int8_t *str);
223 |
224 | /* Similar to utf8size, except that only at most n bytes of src are looked and
225 | * the null terminating byte is excluded. */
226 | utf8_constexpr14 utf8_nonnull utf8_pure size_t
227 | utf8nsize_lazy(const utf8_int8_t *str, size_t n);
228 |
229 | /* Number of utf8 codepoints in the utf8 string src that consists entirely
230 | * of utf8 codepoints from the utf8 string accept. */
231 | utf8_constexpr14 utf8_nonnull utf8_pure size_t
232 | utf8spn(const utf8_int8_t *src, const utf8_int8_t *accept);
233 |
234 | /* The position of the utf8 string needle in the utf8 string haystack. */
235 | utf8_constexpr14 utf8_nonnull utf8_pure utf8_int8_t *
236 | utf8str(const utf8_int8_t *haystack, const utf8_int8_t *needle);
237 |
238 | /* The position of the utf8 string needle in the utf8 string haystack, case
239 | * insensitive. */
240 | utf8_constexpr14 utf8_nonnull utf8_pure utf8_int8_t *
241 | utf8casestr(const utf8_int8_t *haystack, const utf8_int8_t *needle);
242 |
243 | /* Return 0 on success, or the position of the invalid
244 | * utf8 codepoint on failure. */
245 | utf8_constexpr14 utf8_nonnull utf8_pure utf8_int8_t *
246 | utf8valid(const utf8_int8_t *str);
247 |
248 | /* Similar to utf8valid, except that only at most n bytes of src are looked. */
249 | utf8_constexpr14 utf8_nonnull utf8_pure utf8_int8_t *
250 | utf8nvalid(const utf8_int8_t *str, size_t n);
251 |
252 | /* Given a null-terminated string, makes the string valid by replacing invalid
253 | * codepoints with a 1-byte replacement. Returns 0 on success. */
254 | utf8_nonnull utf8_weak int utf8makevalid(utf8_int8_t *str,
255 | const utf8_int32_t replacement);
256 |
257 | /* Sets out_codepoint to the current utf8 codepoint in str, and returns the
258 | * address of the next utf8 codepoint after the current one in str. */
259 | utf8_constexpr14 utf8_nonnull utf8_int8_t *
260 | utf8codepoint(const utf8_int8_t *utf8_restrict str,
261 | utf8_int32_t *utf8_restrict out_codepoint);
262 |
263 | /* Calculates the size of the next utf8 codepoint in str. */
264 | utf8_constexpr14 utf8_nonnull size_t
265 | utf8codepointcalcsize(const utf8_int8_t *str);
266 |
267 | /* Returns the size of the given codepoint in bytes. */
268 | utf8_constexpr14 size_t utf8codepointsize(utf8_int32_t chr);
269 |
270 | /* Write a codepoint to the given string, and return the address to the next
271 | * place after the written codepoint. Pass how many bytes left in the buffer to
272 | * n. If there is not enough space for the codepoint, this function returns
273 | * null. */
274 | utf8_nonnull utf8_weak utf8_int8_t *
275 | utf8catcodepoint(utf8_int8_t *str, utf8_int32_t chr, size_t n);
276 |
277 | /* Returns 1 if the given character is lowercase, or 0 if it is not. */
278 | utf8_constexpr14 int utf8islower(utf8_int32_t chr);
279 |
280 | /* Returns 1 if the given character is uppercase, or 0 if it is not. */
281 | utf8_constexpr14 int utf8isupper(utf8_int32_t chr);
282 |
283 | /* Transform the given string into all lowercase codepoints. */
284 | utf8_nonnull utf8_weak void utf8lwr(utf8_int8_t *utf8_restrict str);
285 |
286 | /* Transform the given string into all uppercase codepoints. */
287 | utf8_nonnull utf8_weak void utf8upr(utf8_int8_t *utf8_restrict str);
288 |
289 | /* Make a codepoint lower case if possible. */
290 | utf8_constexpr14 utf8_int32_t utf8lwrcodepoint(utf8_int32_t cp);
291 |
292 | /* Make a codepoint upper case if possible. */
293 | utf8_constexpr14 utf8_int32_t utf8uprcodepoint(utf8_int32_t cp);
294 |
295 | /* Sets out_codepoint to the current utf8 codepoint in str, and returns the
296 | * address of the previous utf8 codepoint before the current one in str. */
297 | utf8_constexpr14 utf8_nonnull utf8_int8_t *
298 | utf8rcodepoint(const utf8_int8_t *utf8_restrict str,
299 | utf8_int32_t *utf8_restrict out_codepoint);
300 |
301 | /* Duplicate the utf8 string src by getting its size, calling alloc_func_ptr to
302 | * copy over data to a new buffer, and returning that. Or 0 if alloc_func_ptr
303 | * returned null. */
304 | utf8_weak utf8_int8_t *utf8dup_ex(const utf8_int8_t *src,
305 | utf8_int8_t *(*alloc_func_ptr)(utf8_int8_t *,
306 | size_t),
307 | utf8_int8_t *user_data);
308 |
309 | /* Similar to utf8dup, except that at most n bytes of src are copied. If src is
310 | * longer than n, only n bytes are copied and a null byte is added.
311 | *
312 | * Returns a new string if successful, 0 otherwise. */
313 | utf8_weak utf8_int8_t *utf8ndup_ex(const utf8_int8_t *src, size_t n,
314 | utf8_int8_t *(*alloc_func_ptr)(utf8_int8_t *,
315 | size_t),
316 | utf8_int8_t *user_data);
317 |
318 | #undef utf8_weak
319 | #undef utf8_pure
320 | #undef utf8_nonnull
321 |
322 | utf8_constexpr14_impl int utf8casecmp(const utf8_int8_t *src1,
323 | const utf8_int8_t *src2) {
324 | utf8_int32_t src1_lwr_cp = 0, src2_lwr_cp = 0, src1_upr_cp = 0,
325 | src2_upr_cp = 0, src1_orig_cp = 0, src2_orig_cp = 0;
326 |
327 | for (;;) {
328 | src1 = utf8codepoint(src1, &src1_orig_cp);
329 | src2 = utf8codepoint(src2, &src2_orig_cp);
330 |
331 | /* lower the srcs if required */
332 | src1_lwr_cp = utf8lwrcodepoint(src1_orig_cp);
333 | src2_lwr_cp = utf8lwrcodepoint(src2_orig_cp);
334 |
335 | /* lower the srcs if required */
336 | src1_upr_cp = utf8uprcodepoint(src1_orig_cp);
337 | src2_upr_cp = utf8uprcodepoint(src2_orig_cp);
338 |
339 | /* check if the lowered codepoints match */
340 | if ((0 == src1_orig_cp) && (0 == src2_orig_cp)) {
341 | return 0;
342 | } else if ((src1_lwr_cp == src2_lwr_cp) || (src1_upr_cp == src2_upr_cp)) {
343 | continue;
344 | }
345 |
346 | /* if they don't match, then we return the difference between the characters
347 | */
348 | return src1_lwr_cp - src2_lwr_cp;
349 | }
350 | }
351 |
352 | utf8_int8_t *utf8cat(utf8_int8_t *utf8_restrict dst,
353 | const utf8_int8_t *utf8_restrict src) {
354 | utf8_int8_t *d = dst;
355 | /* find the null terminating byte in dst */
356 | while ('\0' != *d) {
357 | d++;
358 | }
359 |
360 | /* overwriting the null terminating byte in dst, append src byte-by-byte */
361 | while ('\0' != *src) {
362 | *d++ = *src++;
363 | }
364 |
365 | /* write out a new null terminating byte into dst */
366 | *d = '\0';
367 |
368 | return dst;
369 | }
370 |
371 | utf8_constexpr14_impl utf8_int8_t *utf8chr(const utf8_int8_t *src,
372 | utf8_int32_t chr) {
373 | utf8_int8_t c[5] = {'\0', '\0', '\0', '\0', '\0'};
374 |
375 | if (0 == chr) {
376 | /* being asked to return position of null terminating byte, so
377 | * just run s to the end, and return! */
378 | while ('\0' != *src) {
379 | src++;
380 | }
381 | return (utf8_int8_t *)src;
382 | } else if (0 == ((utf8_int32_t)0xffffff80 & chr)) {
383 | /* 1-byte/7-bit ascii
384 | * (0b0xxxxxxx) */
385 | c[0] = (utf8_int8_t)chr;
386 | } else if (0 == ((utf8_int32_t)0xfffff800 & chr)) {
387 | /* 2-byte/11-bit utf8 code point
388 | * (0b110xxxxx 0b10xxxxxx) */
389 | c[0] = (utf8_int8_t)(0xc0 | (utf8_int8_t)(chr >> 6));
390 | c[1] = (utf8_int8_t)(0x80 | (utf8_int8_t)(chr & 0x3f));
391 | } else if (0 == ((utf8_int32_t)0xffff0000 & chr)) {
392 | /* 3-byte/16-bit utf8 code point
393 | * (0b1110xxxx 0b10xxxxxx 0b10xxxxxx) */
394 | c[0] = (utf8_int8_t)(0xe0 | (utf8_int8_t)(chr >> 12));
395 | c[1] = (utf8_int8_t)(0x80 | (utf8_int8_t)((chr >> 6) & 0x3f));
396 | c[2] = (utf8_int8_t)(0x80 | (utf8_int8_t)(chr & 0x3f));
397 | } else { /* if (0 == ((int)0xffe00000 & chr)) { */
398 | /* 4-byte/21-bit utf8 code point
399 | * (0b11110xxx 0b10xxxxxx 0b10xxxxxx 0b10xxxxxx) */
400 | c[0] = (utf8_int8_t)(0xf0 | (utf8_int8_t)(chr >> 18));
401 | c[1] = (utf8_int8_t)(0x80 | (utf8_int8_t)((chr >> 12) & 0x3f));
402 | c[2] = (utf8_int8_t)(0x80 | (utf8_int8_t)((chr >> 6) & 0x3f));
403 | c[3] = (utf8_int8_t)(0x80 | (utf8_int8_t)(chr & 0x3f));
404 | }
405 |
406 | /* we've made c into a 2 utf8 codepoint string, one for the chr we are
407 | * seeking, another for the null terminating byte. Now use utf8str to
408 | * search */
409 | return utf8str(src, c);
410 | }
411 |
412 | utf8_constexpr14_impl int utf8cmp(const utf8_int8_t *src1,
413 | const utf8_int8_t *src2) {
414 | while (('\0' != *src1) || ('\0' != *src2)) {
415 | if (*src1 < *src2) {
416 | return -1;
417 | } else if (*src1 > *src2) {
418 | return 1;
419 | }
420 |
421 | src1++;
422 | src2++;
423 | }
424 |
425 | /* both utf8 strings matched */
426 | return 0;
427 | }
428 |
429 | utf8_constexpr14_impl int utf8coll(const utf8_int8_t *src1,
430 | const utf8_int8_t *src2);
431 |
432 | utf8_int8_t *utf8cpy(utf8_int8_t *utf8_restrict dst,
433 | const utf8_int8_t *utf8_restrict src) {
434 | utf8_int8_t *d = dst;
435 |
436 | /* overwriting anything previously in dst, write byte-by-byte
437 | * from src */
438 | while ('\0' != *src) {
439 | *d++ = *src++;
440 | }
441 |
442 | /* append null terminating byte */
443 | *d = '\0';
444 |
445 | return dst;
446 | }
447 |
448 | utf8_constexpr14_impl size_t utf8cspn(const utf8_int8_t *src,
449 | const utf8_int8_t *reject) {
450 | size_t chars = 0;
451 |
452 | while ('\0' != *src) {
453 | const utf8_int8_t *r = reject;
454 | size_t offset = 0;
455 |
456 | while ('\0' != *r) {
457 | /* checking that if *r is the start of a utf8 codepoint
458 | * (it is not 0b10xxxxxx) and we have successfully matched
459 | * a previous character (0 < offset) - we found a match */
460 | if ((0x80 != (0xc0 & *r)) && (0 < offset)) {
461 | return chars;
462 | } else {
463 | if (*r == src[offset]) {
464 | /* part of a utf8 codepoint matched, so move our checking
465 | * onwards to the next byte */
466 | offset++;
467 | r++;
468 | } else {
469 | /* r could be in the middle of an unmatching utf8 code point,
470 | * so we need to march it on to the next character beginning, */
471 |
472 | do {
473 | r++;
474 | } while (0x80 == (0xc0 & *r));
475 |
476 | /* reset offset too as we found a mismatch */
477 | offset = 0;
478 | }
479 | }
480 | }
481 |
482 | /* found a match at the end of *r, so didn't get a chance to test it */
483 | if (0 < offset) {
484 | return chars;
485 | }
486 |
487 | /* the current utf8 codepoint in src did not match reject, but src
488 | * could have been partway through a utf8 codepoint, so we need to
489 | * march it onto the next utf8 codepoint starting byte */
490 | do {
491 | src++;
492 | } while ((0x80 == (0xc0 & *src)));
493 | chars++;
494 | }
495 |
496 | return chars;
497 | }
498 |
499 | utf8_int8_t *utf8dup(const utf8_int8_t *src) {
500 | return utf8dup_ex(src, utf8_null, utf8_null);
501 | }
502 |
503 | utf8_int8_t *utf8dup_ex(const utf8_int8_t *src,
504 | utf8_int8_t *(*alloc_func_ptr)(utf8_int8_t *, size_t),
505 | utf8_int8_t *user_data) {
506 | utf8_int8_t *n = utf8_null;
507 |
508 | /* figure out how many bytes (including the terminator) we need to copy first
509 | */
510 | size_t bytes = utf8size(src);
511 |
512 | if (alloc_func_ptr) {
513 | n = alloc_func_ptr(user_data, bytes);
514 | } else {
515 | #if !defined(UTF8_NO_STD_MALLOC)
516 | n = (utf8_int8_t *)malloc(bytes);
517 | #else
518 | return utf8_null;
519 | #endif
520 | }
521 |
522 | if (utf8_null == n) {
523 | /* out of memory so we bail */
524 | return utf8_null;
525 | } else {
526 | bytes = 0;
527 |
528 | /* copy src byte-by-byte into our new utf8 string */
529 | while ('\0' != src[bytes]) {
530 | n[bytes] = src[bytes];
531 | bytes++;
532 | }
533 |
534 | /* append null terminating byte */
535 | n[bytes] = '\0';
536 | return n;
537 | }
538 | }
539 |
540 | utf8_constexpr14_impl utf8_int8_t *utf8fry(const utf8_int8_t *str);
541 |
542 | utf8_constexpr14_impl size_t utf8len(const utf8_int8_t *str) {
543 | return utf8nlen(str, SIZE_MAX);
544 | }
545 |
546 | utf8_constexpr14_impl size_t utf8nlen(const utf8_int8_t *str, size_t n) {
547 | const utf8_int8_t *t = str;
548 | size_t length = 0;
549 |
550 | while ((size_t)(str - t) < n && '\0' != *str) {
551 | if (0xf0 == (0xf8 & *str)) {
552 | /* 4-byte utf8 code point (began with 0b11110xxx) */
553 | str += 4;
554 | } else if (0xe0 == (0xf0 & *str)) {
555 | /* 3-byte utf8 code point (began with 0b1110xxxx) */
556 | str += 3;
557 | } else if (0xc0 == (0xe0 & *str)) {
558 | /* 2-byte utf8 code point (began with 0b110xxxxx) */
559 | str += 2;
560 | } else { /* if (0x00 == (0x80 & *s)) { */
561 | /* 1-byte ascii (began with 0b0xxxxxxx) */
562 | str += 1;
563 | }
564 |
565 | /* no matter the bytes we marched s forward by, it was
566 | * only 1 utf8 codepoint */
567 | length++;
568 | }
569 |
570 | if ((size_t)(str - t) > n) {
571 | length--;
572 | }
573 | return length;
574 | }
575 |
576 | utf8_constexpr14_impl int utf8ncasecmp(const utf8_int8_t *src1,
577 | const utf8_int8_t *src2, size_t n) {
578 | utf8_int32_t src1_lwr_cp = 0, src2_lwr_cp = 0, src1_upr_cp = 0,
579 | src2_upr_cp = 0, src1_orig_cp = 0, src2_orig_cp = 0;
580 |
581 | do {
582 | const utf8_int8_t *const s1 = src1;
583 | const utf8_int8_t *const s2 = src2;
584 |
585 | /* first check that we have enough bytes left in n to contain an entire
586 | * codepoint */
587 | if (0 == n) {
588 | return 0;
589 | }
590 |
591 | if ((1 == n) && ((0xc0 == (0xe0 & *s1)) || (0xc0 == (0xe0 & *s2)))) {
592 | const utf8_int32_t c1 = (0xe0 & *s1);
593 | const utf8_int32_t c2 = (0xe0 & *s2);
594 |
595 | if (c1 != c2) {
596 | return c1 - c2;
597 | } else {
598 | return 0;
599 | }
600 | }
601 |
602 | if ((2 >= n) && ((0xe0 == (0xf0 & *s1)) || (0xe0 == (0xf0 & *s2)))) {
603 | const utf8_int32_t c1 = (0xf0 & *s1);
604 | const utf8_int32_t c2 = (0xf0 & *s2);
605 |
606 | if (c1 != c2) {
607 | return c1 - c2;
608 | } else {
609 | return 0;
610 | }
611 | }
612 |
613 | if ((3 >= n) && ((0xf0 == (0xf8 & *s1)) || (0xf0 == (0xf8 & *s2)))) {
614 | const utf8_int32_t c1 = (0xf8 & *s1);
615 | const utf8_int32_t c2 = (0xf8 & *s2);
616 |
617 | if (c1 != c2) {
618 | return c1 - c2;
619 | } else {
620 | return 0;
621 | }
622 | }
623 |
624 | src1 = utf8codepoint(src1, &src1_orig_cp);
625 | src2 = utf8codepoint(src2, &src2_orig_cp);
626 | n -= utf8codepointsize(src1_orig_cp);
627 |
628 | src1_lwr_cp = utf8lwrcodepoint(src1_orig_cp);
629 | src2_lwr_cp = utf8lwrcodepoint(src2_orig_cp);
630 |
631 | src1_upr_cp = utf8uprcodepoint(src1_orig_cp);
632 | src2_upr_cp = utf8uprcodepoint(src2_orig_cp);
633 |
634 | /* check if the lowered codepoints match */
635 | if ((0 == src1_orig_cp) && (0 == src2_orig_cp)) {
636 | return 0;
637 | } else if ((src1_lwr_cp == src2_lwr_cp) || (src1_upr_cp == src2_upr_cp)) {
638 | continue;
639 | }
640 |
641 | /* if they don't match, then we return the difference between the characters
642 | */
643 | return src1_lwr_cp - src2_lwr_cp;
644 | } while (0 < n);
645 |
646 | /* both utf8 strings matched */
647 | return 0;
648 | }
649 |
650 | utf8_int8_t *utf8ncat(utf8_int8_t *utf8_restrict dst,
651 | const utf8_int8_t *utf8_restrict src, size_t n) {
652 | utf8_int8_t *d = dst;
653 |
654 | /* find the null terminating byte in dst */
655 | while ('\0' != *d) {
656 | d++;
657 | }
658 |
659 | /* overwriting the null terminating byte in dst, append src byte-by-byte
660 | * stopping if we run out of space */
661 | while (('\0' != *src) && (0 != n--)) {
662 | *d++ = *src++;
663 | }
664 |
665 | /* write out a new null terminating byte into dst */
666 | *d = '\0';
667 |
668 | return dst;
669 | }
670 |
671 | utf8_constexpr14_impl int utf8ncmp(const utf8_int8_t *src1,
672 | const utf8_int8_t *src2, size_t n) {
673 | while ((0 != n--) && (('\0' != *src1) || ('\0' != *src2))) {
674 | if (*src1 < *src2) {
675 | return -1;
676 | } else if (*src1 > *src2) {
677 | return 1;
678 | }
679 |
680 | src1++;
681 | src2++;
682 | }
683 |
684 | /* both utf8 strings matched */
685 | return 0;
686 | }
687 |
688 | utf8_int8_t *utf8ncpy(utf8_int8_t *utf8_restrict dst,
689 | const utf8_int8_t *utf8_restrict src, size_t n) {
690 | utf8_int8_t *d = dst;
691 | size_t index = 0, check_index = 0;
692 |
693 | if (n == 0) {
694 | return dst;
695 | }
696 |
697 | /* overwriting anything previously in dst, write byte-by-byte
698 | * from src */
699 | for (index = 0; index < n; index++) {
700 | d[index] = src[index];
701 | if ('\0' == src[index]) {
702 | break;
703 | }
704 | }
705 |
706 | for (check_index = index - 1;
707 | check_index > 0 && 0x80 == (0xc0 & d[check_index]); check_index--) {
708 | /* just moving the index */
709 | }
710 |
711 | if (check_index < index &&
712 | ((index - check_index) < utf8codepointcalcsize(&d[check_index]) ||
713 | (index - check_index) == n)) {
714 | index = check_index;
715 | }
716 |
717 | /* append null terminating byte */
718 | for (; index < n; index++) {
719 | d[index] = 0;
720 | }
721 |
722 | return dst;
723 | }
724 |
725 | utf8_int8_t *utf8ndup(const utf8_int8_t *src, size_t n) {
726 | return utf8ndup_ex(src, n, utf8_null, utf8_null);
727 | }
728 |
729 | utf8_int8_t *utf8ndup_ex(const utf8_int8_t *src, size_t n,
730 | utf8_int8_t *(*alloc_func_ptr)(utf8_int8_t *, size_t),
731 | utf8_int8_t *user_data) {
732 | utf8_int8_t *c = utf8_null;
733 | size_t bytes = 0;
734 |
735 | /* Find the end of the string or stop when n is reached */
736 | while ('\0' != src[bytes] && bytes < n) {
737 | bytes++;
738 | }
739 |
740 | /* In case bytes is actually less than n, we need to set it
741 | * to be used later in the copy byte by byte. */
742 | n = bytes;
743 |
744 | if (alloc_func_ptr) {
745 | c = alloc_func_ptr(user_data, bytes + 1);
746 | } else {
747 | #if !defined(UTF8_NO_STD_MALLOC)
748 | c = (utf8_int8_t *)malloc(bytes + 1);
749 | #else
750 | c = utf8_null;
751 | #endif
752 | }
753 |
754 | if (utf8_null == c) {
755 | /* out of memory so we bail */
756 | return utf8_null;
757 | }
758 |
759 | bytes = 0;
760 |
761 | /* copy src byte-by-byte into our new utf8 string */
762 | while ('\0' != src[bytes] && bytes < n) {
763 | c[bytes] = src[bytes];
764 | bytes++;
765 | }
766 |
767 | /* append null terminating byte */
768 | c[bytes] = '\0';
769 | return c;
770 | }
771 |
772 | utf8_constexpr14_impl utf8_int8_t *utf8rchr(const utf8_int8_t *src, int chr) {
773 |
774 | utf8_int8_t *match = utf8_null;
775 | utf8_int8_t c[5] = {'\0', '\0', '\0', '\0', '\0'};
776 |
777 | if (0 == chr) {
778 | /* being asked to return position of null terminating byte, so
779 | * just run s to the end, and return! */
780 | while ('\0' != *src) {
781 | src++;
782 | }
783 | return (utf8_int8_t *)src;
784 | } else if (0 == ((int)0xffffff80 & chr)) {
785 | /* 1-byte/7-bit ascii
786 | * (0b0xxxxxxx) */
787 | c[0] = (utf8_int8_t)chr;
788 | } else if (0 == ((int)0xfffff800 & chr)) {
789 | /* 2-byte/11-bit utf8 code point
790 | * (0b110xxxxx 0b10xxxxxx) */
791 | c[0] = (utf8_int8_t)(0xc0 | (utf8_int8_t)(chr >> 6));
792 | c[1] = (utf8_int8_t)(0x80 | (utf8_int8_t)(chr & 0x3f));
793 | } else if (0 == ((int)0xffff0000 & chr)) {
794 | /* 3-byte/16-bit utf8 code point
795 | * (0b1110xxxx 0b10xxxxxx 0b10xxxxxx) */
796 | c[0] = (utf8_int8_t)(0xe0 | (utf8_int8_t)(chr >> 12));
797 | c[1] = (utf8_int8_t)(0x80 | (utf8_int8_t)((chr >> 6) & 0x3f));
798 | c[2] = (utf8_int8_t)(0x80 | (utf8_int8_t)(chr & 0x3f));
799 | } else { /* if (0 == ((int)0xffe00000 & chr)) { */
800 | /* 4-byte/21-bit utf8 code point
801 | * (0b11110xxx 0b10xxxxxx 0b10xxxxxx 0b10xxxxxx) */
802 | c[0] = (utf8_int8_t)(0xf0 | (utf8_int8_t)(chr >> 18));
803 | c[1] = (utf8_int8_t)(0x80 | (utf8_int8_t)((chr >> 12) & 0x3f));
804 | c[2] = (utf8_int8_t)(0x80 | (utf8_int8_t)((chr >> 6) & 0x3f));
805 | c[3] = (utf8_int8_t)(0x80 | (utf8_int8_t)(chr & 0x3f));
806 | }
807 |
808 | /* we've created a 2 utf8 codepoint string in c that is
809 | * the utf8 character asked for by chr, and a null
810 | * terminating byte */
811 |
812 | while ('\0' != *src) {
813 | size_t offset = 0;
814 |
815 | while ((src[offset] == c[offset]) && ('\0' != src[offset])) {
816 | offset++;
817 | }
818 |
819 | if ('\0' == c[offset]) {
820 | /* we found a matching utf8 code point */
821 | match = (utf8_int8_t *)src;
822 | src += offset;
823 |
824 | if ('\0' == *src) {
825 | break;
826 | }
827 | } else {
828 | src += offset;
829 |
830 | /* need to march s along to next utf8 codepoint start
831 | * (the next byte that doesn't match 0b10xxxxxx) */
832 | if ('\0' != *src) {
833 | do {
834 | src++;
835 | } while (0x80 == (0xc0 & *src));
836 | }
837 | }
838 | }
839 |
840 | /* return the last match we found (or 0 if no match was found) */
841 | return match;
842 | }
843 |
844 | utf8_constexpr14_impl utf8_int8_t *utf8pbrk(const utf8_int8_t *str,
845 | const utf8_int8_t *accept) {
846 | while ('\0' != *str) {
847 | const utf8_int8_t *a = accept;
848 | size_t offset = 0;
849 |
850 | while ('\0' != *a) {
851 | /* checking that if *a is the start of a utf8 codepoint
852 | * (it is not 0b10xxxxxx) and we have successfully matched
853 | * a previous character (0 < offset) - we found a match */
854 | if ((0x80 != (0xc0 & *a)) && (0 < offset)) {
855 | return (utf8_int8_t *)str;
856 | } else {
857 | if (*a == str[offset]) {
858 | /* part of a utf8 codepoint matched, so move our checking
859 | * onwards to the next byte */
860 | offset++;
861 | a++;
862 | } else {
863 | /* r could be in the middle of an unmatching utf8 code point,
864 | * so we need to march it on to the next character beginning, */
865 |
866 | do {
867 | a++;
868 | } while (0x80 == (0xc0 & *a));
869 |
870 | /* reset offset too as we found a mismatch */
871 | offset = 0;
872 | }
873 | }
874 | }
875 |
876 | /* we found a match on the last utf8 codepoint */
877 | if (0 < offset) {
878 | return (utf8_int8_t *)str;
879 | }
880 |
881 | /* the current utf8 codepoint in src did not match accept, but src
882 | * could have been partway through a utf8 codepoint, so we need to
883 | * march it onto the next utf8 codepoint starting byte */
884 | do {
885 | str++;
886 | } while ((0x80 == (0xc0 & *str)));
887 | }
888 |
889 | return utf8_null;
890 | }
891 |
892 | utf8_constexpr14_impl size_t utf8size(const utf8_int8_t *str) {
893 | return utf8size_lazy(str) + 1;
894 | }
895 |
896 | utf8_constexpr14_impl size_t utf8size_lazy(const utf8_int8_t *str) {
897 | return utf8nsize_lazy(str, SIZE_MAX);
898 | }
899 |
900 | utf8_constexpr14_impl size_t utf8nsize_lazy(const utf8_int8_t *str, size_t n) {
901 | size_t size = 0;
902 | while (size < n && '\0' != str[size]) {
903 | size++;
904 | }
905 | return size;
906 | }
907 |
908 | utf8_constexpr14_impl size_t utf8spn(const utf8_int8_t *src,
909 | const utf8_int8_t *accept) {
910 | size_t chars = 0;
911 |
912 | while ('\0' != *src) {
913 | const utf8_int8_t *a = accept;
914 | size_t offset = 0;
915 |
916 | while ('\0' != *a) {
917 | /* checking that if *r is the start of a utf8 codepoint
918 | * (it is not 0b10xxxxxx) and we have successfully matched
919 | * a previous character (0 < offset) - we found a match */
920 | if ((0x80 != (0xc0 & *a)) && (0 < offset)) {
921 | /* found a match, so increment the number of utf8 codepoints
922 | * that have matched and stop checking whether any other utf8
923 | * codepoints in a match */
924 | chars++;
925 | src += offset;
926 | offset = 0;
927 | break;
928 | } else {
929 | if (*a == src[offset]) {
930 | offset++;
931 | a++;
932 | } else {
933 | /* a could be in the middle of an unmatching utf8 codepoint,
934 | * so we need to march it on to the next character beginning, */
935 | do {
936 | a++;
937 | } while (0x80 == (0xc0 & *a));
938 |
939 | /* reset offset too as we found a mismatch */
940 | offset = 0;
941 | }
942 | }
943 | }
944 |
945 | /* found a match at the end of *a, so didn't get a chance to test it */
946 | if (0 < offset) {
947 | chars++;
948 | src += offset;
949 | continue;
950 | }
951 |
952 | /* if a got to its terminating null byte, then we didn't find a match.
953 | * Return the current number of matched utf8 codepoints */
954 | if ('\0' == *a) {
955 | return chars;
956 | }
957 | }
958 |
959 | return chars;
960 | }
961 |
962 | utf8_constexpr14_impl utf8_int8_t *utf8str(const utf8_int8_t *haystack,
963 | const utf8_int8_t *needle) {
964 | utf8_int32_t throwaway_codepoint = 0;
965 |
966 | /* if needle has no utf8 codepoints before the null terminating
967 | * byte then return haystack */
968 | if ('\0' == *needle) {
969 | return (utf8_int8_t *)haystack;
970 | }
971 |
972 | while ('\0' != *haystack) {
973 | const utf8_int8_t *maybeMatch = haystack;
974 | const utf8_int8_t *n = needle;
975 |
976 | while (*haystack == *n && (*haystack != '\0' && *n != '\0')) {
977 | n++;
978 | haystack++;
979 | }
980 |
981 | if ('\0' == *n) {
982 | /* we found the whole utf8 string for needle in haystack at
983 | * maybeMatch, so return it */
984 | return (utf8_int8_t *)maybeMatch;
985 | } else {
986 | /* h could be in the middle of an unmatching utf8 codepoint,
987 | * so we need to march it on to the next character beginning
988 | * starting from the current character */
989 | haystack = utf8codepoint(maybeMatch, &throwaway_codepoint);
990 | }
991 | }
992 |
993 | /* no match */
994 | return utf8_null;
995 | }
996 |
997 | utf8_constexpr14_impl utf8_int8_t *utf8casestr(const utf8_int8_t *haystack,
998 | const utf8_int8_t *needle) {
999 | /* if needle has no utf8 codepoints before the null terminating
1000 | * byte then return haystack */
1001 | if ('\0' == *needle) {
1002 | return (utf8_int8_t *)haystack;
1003 | }
1004 |
1005 | for (;;) {
1006 | const utf8_int8_t *maybeMatch = haystack;
1007 | const utf8_int8_t *n = needle;
1008 | utf8_int32_t h_cp = 0, n_cp = 0;
1009 |
1010 | /* Get the next code point and track it */
1011 | const utf8_int8_t *nextH = haystack = utf8codepoint(haystack, &h_cp);
1012 | n = utf8codepoint(n, &n_cp);
1013 |
1014 | while ((0 != h_cp) && (0 != n_cp)) {
1015 | h_cp = utf8lwrcodepoint(h_cp);
1016 | n_cp = utf8lwrcodepoint(n_cp);
1017 |
1018 | /* if we find a mismatch, bail out! */
1019 | if (h_cp != n_cp) {
1020 | break;
1021 | }
1022 |
1023 | haystack = utf8codepoint(haystack, &h_cp);
1024 | n = utf8codepoint(n, &n_cp);
1025 | }
1026 |
1027 | if (0 == n_cp) {
1028 | /* we found the whole utf8 string for needle in haystack at
1029 | * maybeMatch, so return it */
1030 | return (utf8_int8_t *)maybeMatch;
1031 | }
1032 |
1033 | if (0 == h_cp) {
1034 | /* no match */
1035 | return utf8_null;
1036 | }
1037 |
1038 | /* Roll back to the next code point in the haystack to test */
1039 | haystack = nextH;
1040 | }
1041 | }
1042 |
1043 | utf8_constexpr14_impl utf8_int8_t *utf8valid(const utf8_int8_t *str) {
1044 | return utf8nvalid(str, SIZE_MAX);
1045 | }
1046 |
1047 | utf8_constexpr14_impl utf8_int8_t *utf8nvalid(const utf8_int8_t *str,
1048 | size_t n) {
1049 | const utf8_int8_t *t = str;
1050 | size_t consumed = 0;
1051 |
1052 | while ((void)(consumed = (size_t)(str - t)), consumed < n && '\0' != *str) {
1053 | const size_t remaining = n - consumed;
1054 |
1055 | if (0xf0 == (0xf8 & *str)) {
1056 | /* ensure that there's 4 bytes or more remaining */
1057 | if (remaining < 4) {
1058 | return (utf8_int8_t *)str;
1059 | }
1060 |
1061 | /* ensure each of the 3 following bytes in this 4-byte
1062 | * utf8 codepoint began with 0b10xxxxxx */
1063 | if ((0x80 != (0xc0 & str[1])) || (0x80 != (0xc0 & str[2])) ||
1064 | (0x80 != (0xc0 & str[3]))) {
1065 | return (utf8_int8_t *)str;
1066 | }
1067 |
1068 | /* ensure that our utf8 codepoint ended after 4 bytes */
1069 | if ((remaining != 4) && (0x80 == (0xc0 & str[4]))) {
1070 | return (utf8_int8_t *)str;
1071 | }
1072 |
1073 | /* ensure that the top 5 bits of this 4-byte utf8
1074 | * codepoint were not 0, as then we could have used
1075 | * one of the smaller encodings */
1076 | if ((0 == (0x07 & str[0])) && (0 == (0x30 & str[1]))) {
1077 | return (utf8_int8_t *)str;
1078 | }
1079 |
1080 | /* 4-byte utf8 code point (began with 0b11110xxx) */
1081 | str += 4;
1082 | } else if (0xe0 == (0xf0 & *str)) {
1083 | /* ensure that there's 3 bytes or more remaining */
1084 | if (remaining < 3) {
1085 | return (utf8_int8_t *)str;
1086 | }
1087 |
1088 | /* ensure each of the 2 following bytes in this 3-byte
1089 | * utf8 codepoint began with 0b10xxxxxx */
1090 | if ((0x80 != (0xc0 & str[1])) || (0x80 != (0xc0 & str[2]))) {
1091 | return (utf8_int8_t *)str;
1092 | }
1093 |
1094 | /* ensure that our utf8 codepoint ended after 3 bytes */
1095 | if ((remaining != 3) && (0x80 == (0xc0 & str[3]))) {
1096 | return (utf8_int8_t *)str;
1097 | }
1098 |
1099 | /* ensure that the top 5 bits of this 3-byte utf8
1100 | * codepoint were not 0, as then we could have used
1101 | * one of the smaller encodings */
1102 | if ((0 == (0x0f & str[0])) && (0 == (0x20 & str[1]))) {
1103 | return (utf8_int8_t *)str;
1104 | }
1105 |
1106 | /* 3-byte utf8 code point (began with 0b1110xxxx) */
1107 | str += 3;
1108 | } else if (0xc0 == (0xe0 & *str)) {
1109 | /* ensure that there's 2 bytes or more remaining */
1110 | if (remaining < 2) {
1111 | return (utf8_int8_t *)str;
1112 | }
1113 |
1114 | /* ensure the 1 following byte in this 2-byte
1115 | * utf8 codepoint began with 0b10xxxxxx */
1116 | if (0x80 != (0xc0 & str[1])) {
1117 | return (utf8_int8_t *)str;
1118 | }
1119 |
1120 | /* ensure that our utf8 codepoint ended after 2 bytes */
1121 | if ((remaining != 2) && (0x80 == (0xc0 & str[2]))) {
1122 | return (utf8_int8_t *)str;
1123 | }
1124 |
1125 | /* ensure that the top 4 bits of this 2-byte utf8
1126 | * codepoint were not 0, as then we could have used
1127 | * one of the smaller encodings */
1128 | if (0 == (0x1e & str[0])) {
1129 | return (utf8_int8_t *)str;
1130 | }
1131 |
1132 | /* 2-byte utf8 code point (began with 0b110xxxxx) */
1133 | str += 2;
1134 | } else if (0x00 == (0x80 & *str)) {
1135 | /* 1-byte ascii (began with 0b0xxxxxxx) */
1136 | str += 1;
1137 | } else {
1138 | /* we have an invalid 0b1xxxxxxx utf8 code point entry */
1139 | return (utf8_int8_t *)str;
1140 | }
1141 | }
1142 |
1143 | return utf8_null;
1144 | }
1145 |
1146 | int utf8makevalid(utf8_int8_t *str, const utf8_int32_t replacement) {
1147 | utf8_int8_t *read = str;
1148 | utf8_int8_t *write = read;
1149 | const utf8_int8_t r = (utf8_int8_t)replacement;
1150 | utf8_int32_t codepoint = 0;
1151 |
1152 | if (replacement > 0x7f) {
1153 | return -1;
1154 | }
1155 |
1156 | while ('\0' != *read) {
1157 | if (0xf0 == (0xf8 & *read)) {
1158 | /* ensure each of the 3 following bytes in this 4-byte
1159 | * utf8 codepoint began with 0b10xxxxxx */
1160 | if ((0x80 != (0xc0 & read[1])) || (0x80 != (0xc0 & read[2])) ||
1161 | (0x80 != (0xc0 & read[3]))) {
1162 | *write++ = r;
1163 | read++;
1164 | continue;
1165 | }
1166 |
1167 | /* 4-byte utf8 code point (began with 0b11110xxx) */
1168 | read = utf8codepoint(read, &codepoint);
1169 | write = utf8catcodepoint(write, codepoint, 4);
1170 | } else if (0xe0 == (0xf0 & *read)) {
1171 | /* ensure each of the 2 following bytes in this 3-byte
1172 | * utf8 codepoint began with 0b10xxxxxx */
1173 | if ((0x80 != (0xc0 & read[1])) || (0x80 != (0xc0 & read[2]))) {
1174 | *write++ = r;
1175 | read++;
1176 | continue;
1177 | }
1178 |
1179 | /* 3-byte utf8 code point (began with 0b1110xxxx) */
1180 | read = utf8codepoint(read, &codepoint);
1181 | write = utf8catcodepoint(write, codepoint, 3);
1182 | } else if (0xc0 == (0xe0 & *read)) {
1183 | /* ensure the 1 following byte in this 2-byte
1184 | * utf8 codepoint began with 0b10xxxxxx */
1185 | if (0x80 != (0xc0 & read[1])) {
1186 | *write++ = r;
1187 | read++;
1188 | continue;
1189 | }
1190 |
1191 | /* 2-byte utf8 code point (began with 0b110xxxxx) */
1192 | read = utf8codepoint(read, &codepoint);
1193 | write = utf8catcodepoint(write, codepoint, 2);
1194 | } else if (0x00 == (0x80 & *read)) {
1195 | /* 1-byte ascii (began with 0b0xxxxxxx) */
1196 | read = utf8codepoint(read, &codepoint);
1197 | write = utf8catcodepoint(write, codepoint, 1);
1198 | } else {
1199 | /* if we got here then we've got a dangling continuation (0b10xxxxxx) */
1200 | *write++ = r;
1201 | read++;
1202 | continue;
1203 | }
1204 | }
1205 |
1206 | *write = '\0';
1207 |
1208 | return 0;
1209 | }
1210 |
1211 | utf8_constexpr14_impl utf8_int8_t *
1212 | utf8codepoint(const utf8_int8_t *utf8_restrict str,
1213 | utf8_int32_t *utf8_restrict out_codepoint) {
1214 | if (0xf0 == (0xf8 & str[0])) {
1215 | /* 4 byte utf8 codepoint */
1216 | *out_codepoint = ((0x07 & str[0]) << 18) | ((0x3f & str[1]) << 12) |
1217 | ((0x3f & str[2]) << 6) | (0x3f & str[3]);
1218 | str += 4;
1219 | } else if (0xe0 == (0xf0 & str[0])) {
1220 | /* 3 byte utf8 codepoint */
1221 | *out_codepoint =
1222 | ((0x0f & str[0]) << 12) | ((0x3f & str[1]) << 6) | (0x3f & str[2]);
1223 | str += 3;
1224 | } else if (0xc0 == (0xe0 & str[0])) {
1225 | /* 2 byte utf8 codepoint */
1226 | *out_codepoint = ((0x1f & str[0]) << 6) | (0x3f & str[1]);
1227 | str += 2;
1228 | } else {
1229 | /* 1 byte utf8 codepoint otherwise */
1230 | *out_codepoint = str[0];
1231 | str += 1;
1232 | }
1233 |
1234 | return (utf8_int8_t *)str;
1235 | }
1236 |
1237 | utf8_constexpr14_impl size_t utf8codepointcalcsize(const utf8_int8_t *str) {
1238 | if (0xf0 == (0xf8 & str[0])) {
1239 | /* 4 byte utf8 codepoint */
1240 | return 4;
1241 | } else if (0xe0 == (0xf0 & str[0])) {
1242 | /* 3 byte utf8 codepoint */
1243 | return 3;
1244 | } else if (0xc0 == (0xe0 & str[0])) {
1245 | /* 2 byte utf8 codepoint */
1246 | return 2;
1247 | }
1248 |
1249 | /* 1 byte utf8 codepoint otherwise */
1250 | return 1;
1251 | }
1252 |
1253 | utf8_constexpr14_impl size_t utf8codepointsize(utf8_int32_t chr) {
1254 | if (0 == ((utf8_int32_t)0xffffff80 & chr)) {
1255 | return 1;
1256 | } else if (0 == ((utf8_int32_t)0xfffff800 & chr)) {
1257 | return 2;
1258 | } else if (0 == ((utf8_int32_t)0xffff0000 & chr)) {
1259 | return 3;
1260 | } else { /* if (0 == ((int)0xffe00000 & chr)) { */
1261 | return 4;
1262 | }
1263 | }
1264 |
1265 | utf8_int8_t *utf8catcodepoint(utf8_int8_t *str, utf8_int32_t chr, size_t n) {
1266 | if (0 == ((utf8_int32_t)0xffffff80 & chr)) {
1267 | /* 1-byte/7-bit ascii
1268 | * (0b0xxxxxxx) */
1269 | if (n < 1) {
1270 | return utf8_null;
1271 | }
1272 | str[0] = (utf8_int8_t)chr;
1273 | str += 1;
1274 | } else if (0 == ((utf8_int32_t)0xfffff800 & chr)) {
1275 | /* 2-byte/11-bit utf8 code point
1276 | * (0b110xxxxx 0b10xxxxxx) */
1277 | if (n < 2) {
1278 | return utf8_null;
1279 | }
1280 | str[0] = (utf8_int8_t)(0xc0 | (utf8_int8_t)((chr >> 6) & 0x1f));
1281 | str[1] = (utf8_int8_t)(0x80 | (utf8_int8_t)(chr & 0x3f));
1282 | str += 2;
1283 | } else if (0 == ((utf8_int32_t)0xffff0000 & chr)) {
1284 | /* 3-byte/16-bit utf8 code point
1285 | * (0b1110xxxx 0b10xxxxxx 0b10xxxxxx) */
1286 | if (n < 3) {
1287 | return utf8_null;
1288 | }
1289 | str[0] = (utf8_int8_t)(0xe0 | (utf8_int8_t)((chr >> 12) & 0x0f));
1290 | str[1] = (utf8_int8_t)(0x80 | (utf8_int8_t)((chr >> 6) & 0x3f));
1291 | str[2] = (utf8_int8_t)(0x80 | (utf8_int8_t)(chr & 0x3f));
1292 | str += 3;
1293 | } else { /* if (0 == ((int)0xffe00000 & chr)) { */
1294 | /* 4-byte/21-bit utf8 code point
1295 | * (0b11110xxx 0b10xxxxxx 0b10xxxxxx 0b10xxxxxx) */
1296 | if (n < 4) {
1297 | return utf8_null;
1298 | }
1299 | str[0] = (utf8_int8_t)(0xf0 | (utf8_int8_t)((chr >> 18) & 0x07));
1300 | str[1] = (utf8_int8_t)(0x80 | (utf8_int8_t)((chr >> 12) & 0x3f));
1301 | str[2] = (utf8_int8_t)(0x80 | (utf8_int8_t)((chr >> 6) & 0x3f));
1302 | str[3] = (utf8_int8_t)(0x80 | (utf8_int8_t)(chr & 0x3f));
1303 | str += 4;
1304 | }
1305 |
1306 | return str;
1307 | }
1308 |
1309 | utf8_constexpr14_impl int utf8islower(utf8_int32_t chr) {
1310 | return chr != utf8uprcodepoint(chr);
1311 | }
1312 |
1313 | utf8_constexpr14_impl int utf8isupper(utf8_int32_t chr) {
1314 | return chr != utf8lwrcodepoint(chr);
1315 | }
1316 |
1317 | void utf8lwr(utf8_int8_t *utf8_restrict str) {
1318 | utf8_int32_t cp = 0;
1319 | utf8_int8_t *pn = utf8codepoint(str, &cp);
1320 |
1321 | while (cp != 0) {
1322 | const utf8_int32_t lwr_cp = utf8lwrcodepoint(cp);
1323 | const size_t size = utf8codepointsize(lwr_cp);
1324 |
1325 | if (lwr_cp != cp) {
1326 | utf8catcodepoint(str, lwr_cp, size);
1327 | }
1328 |
1329 | str = pn;
1330 | pn = utf8codepoint(str, &cp);
1331 | }
1332 | }
1333 |
1334 | void utf8upr(utf8_int8_t *utf8_restrict str) {
1335 | utf8_int32_t cp = 0;
1336 | utf8_int8_t *pn = utf8codepoint(str, &cp);
1337 |
1338 | while (cp != 0) {
1339 | const utf8_int32_t lwr_cp = utf8uprcodepoint(cp);
1340 | const size_t size = utf8codepointsize(lwr_cp);
1341 |
1342 | if (lwr_cp != cp) {
1343 | utf8catcodepoint(str, lwr_cp, size);
1344 | }
1345 |
1346 | str = pn;
1347 | pn = utf8codepoint(str, &cp);
1348 | }
1349 | }
1350 |
1351 | utf8_constexpr14_impl utf8_int32_t utf8lwrcodepoint(utf8_int32_t cp) {
1352 | if (((0x0041 <= cp) && (0x005a >= cp)) ||
1353 | ((0x00c0 <= cp) && (0x00d6 >= cp)) ||
1354 | ((0x00d8 <= cp) && (0x00de >= cp)) ||
1355 | ((0x0391 <= cp) && (0x03a1 >= cp)) ||
1356 | ((0x03a3 <= cp) && (0x03ab >= cp)) ||
1357 | ((0x0410 <= cp) && (0x042f >= cp))) {
1358 | cp += 32;
1359 | } else if ((0x0400 <= cp) && (0x040f >= cp)) {
1360 | cp += 80;
1361 | } else if (((0x0100 <= cp) && (0x012f >= cp)) ||
1362 | ((0x0132 <= cp) && (0x0137 >= cp)) ||
1363 | ((0x014a <= cp) && (0x0177 >= cp)) ||
1364 | ((0x0182 <= cp) && (0x0185 >= cp)) ||
1365 | ((0x01a0 <= cp) && (0x01a5 >= cp)) ||
1366 | ((0x01de <= cp) && (0x01ef >= cp)) ||
1367 | ((0x01f8 <= cp) && (0x021f >= cp)) ||
1368 | ((0x0222 <= cp) && (0x0233 >= cp)) ||
1369 | ((0x0246 <= cp) && (0x024f >= cp)) ||
1370 | ((0x03d8 <= cp) && (0x03ef >= cp)) ||
1371 | ((0x0460 <= cp) && (0x0481 >= cp)) ||
1372 | ((0x048a <= cp) && (0x04ff >= cp))) {
1373 | cp |= 0x1;
1374 | } else if (((0x0139 <= cp) && (0x0148 >= cp)) ||
1375 | ((0x0179 <= cp) && (0x017e >= cp)) ||
1376 | ((0x01af <= cp) && (0x01b0 >= cp)) ||
1377 | ((0x01b3 <= cp) && (0x01b6 >= cp)) ||
1378 | ((0x01cd <= cp) && (0x01dc >= cp))) {
1379 | cp += 1;
1380 | cp &= ~0x1;
1381 | } else {
1382 | switch (cp) {
1383 | default:
1384 | break;
1385 | case 0x0178:
1386 | cp = 0x00ff;
1387 | break;
1388 | case 0x0243:
1389 | cp = 0x0180;
1390 | break;
1391 | case 0x018e:
1392 | cp = 0x01dd;
1393 | break;
1394 | case 0x023d:
1395 | cp = 0x019a;
1396 | break;
1397 | case 0x0220:
1398 | cp = 0x019e;
1399 | break;
1400 | case 0x01b7:
1401 | cp = 0x0292;
1402 | break;
1403 | case 0x01c4:
1404 | cp = 0x01c6;
1405 | break;
1406 | case 0x01c7:
1407 | cp = 0x01c9;
1408 | break;
1409 | case 0x01ca:
1410 | cp = 0x01cc;
1411 | break;
1412 | case 0x01f1:
1413 | cp = 0x01f3;
1414 | break;
1415 | case 0x01f7:
1416 | cp = 0x01bf;
1417 | break;
1418 | case 0x0187:
1419 | cp = 0x0188;
1420 | break;
1421 | case 0x018b:
1422 | cp = 0x018c;
1423 | break;
1424 | case 0x0191:
1425 | cp = 0x0192;
1426 | break;
1427 | case 0x0198:
1428 | cp = 0x0199;
1429 | break;
1430 | case 0x01a7:
1431 | cp = 0x01a8;
1432 | break;
1433 | case 0x01ac:
1434 | cp = 0x01ad;
1435 | break;
1436 | case 0x01b8:
1437 | cp = 0x01b9;
1438 | break;
1439 | case 0x01bc:
1440 | cp = 0x01bd;
1441 | break;
1442 | case 0x01f4:
1443 | cp = 0x01f5;
1444 | break;
1445 | case 0x023b:
1446 | cp = 0x023c;
1447 | break;
1448 | case 0x0241:
1449 | cp = 0x0242;
1450 | break;
1451 | case 0x03fd:
1452 | cp = 0x037b;
1453 | break;
1454 | case 0x03fe:
1455 | cp = 0x037c;
1456 | break;
1457 | case 0x03ff:
1458 | cp = 0x037d;
1459 | break;
1460 | case 0x037f:
1461 | cp = 0x03f3;
1462 | break;
1463 | case 0x0386:
1464 | cp = 0x03ac;
1465 | break;
1466 | case 0x0388:
1467 | cp = 0x03ad;
1468 | break;
1469 | case 0x0389:
1470 | cp = 0x03ae;
1471 | break;
1472 | case 0x038a:
1473 | cp = 0x03af;
1474 | break;
1475 | case 0x038c:
1476 | cp = 0x03cc;
1477 | break;
1478 | case 0x038e:
1479 | cp = 0x03cd;
1480 | break;
1481 | case 0x038f:
1482 | cp = 0x03ce;
1483 | break;
1484 | case 0x0370:
1485 | cp = 0x0371;
1486 | break;
1487 | case 0x0372:
1488 | cp = 0x0373;
1489 | break;
1490 | case 0x0376:
1491 | cp = 0x0377;
1492 | break;
1493 | case 0x03f4:
1494 | cp = 0x03b8;
1495 | break;
1496 | case 0x03cf:
1497 | cp = 0x03d7;
1498 | break;
1499 | case 0x03f9:
1500 | cp = 0x03f2;
1501 | break;
1502 | case 0x03f7:
1503 | cp = 0x03f8;
1504 | break;
1505 | case 0x03fa:
1506 | cp = 0x03fb;
1507 | break;
1508 | }
1509 | }
1510 |
1511 | return cp;
1512 | }
1513 |
1514 | utf8_constexpr14_impl utf8_int32_t utf8uprcodepoint(utf8_int32_t cp) {
1515 | if (((0x0061 <= cp) && (0x007a >= cp)) ||
1516 | ((0x00e0 <= cp) && (0x00f6 >= cp)) ||
1517 | ((0x00f8 <= cp) && (0x00fe >= cp)) ||
1518 | ((0x03b1 <= cp) && (0x03c1 >= cp)) ||
1519 | ((0x03c3 <= cp) && (0x03cb >= cp)) ||
1520 | ((0x0430 <= cp) && (0x044f >= cp))) {
1521 | cp -= 32;
1522 | } else if ((0x0450 <= cp) && (0x045f >= cp)) {
1523 | cp -= 80;
1524 | } else if (((0x0100 <= cp) && (0x012f >= cp)) ||
1525 | ((0x0132 <= cp) && (0x0137 >= cp)) ||
1526 | ((0x014a <= cp) && (0x0177 >= cp)) ||
1527 | ((0x0182 <= cp) && (0x0185 >= cp)) ||
1528 | ((0x01a0 <= cp) && (0x01a5 >= cp)) ||
1529 | ((0x01de <= cp) && (0x01ef >= cp)) ||
1530 | ((0x01f8 <= cp) && (0x021f >= cp)) ||
1531 | ((0x0222 <= cp) && (0x0233 >= cp)) ||
1532 | ((0x0246 <= cp) && (0x024f >= cp)) ||
1533 | ((0x03d8 <= cp) && (0x03ef >= cp)) ||
1534 | ((0x0460 <= cp) && (0x0481 >= cp)) ||
1535 | ((0x048a <= cp) && (0x04ff >= cp))) {
1536 | cp &= ~0x1;
1537 | } else if (((0x0139 <= cp) && (0x0148 >= cp)) ||
1538 | ((0x0179 <= cp) && (0x017e >= cp)) ||
1539 | ((0x01af <= cp) && (0x01b0 >= cp)) ||
1540 | ((0x01b3 <= cp) && (0x01b6 >= cp)) ||
1541 | ((0x01cd <= cp) && (0x01dc >= cp))) {
1542 | cp -= 1;
1543 | cp |= 0x1;
1544 | } else {
1545 | switch (cp) {
1546 | default:
1547 | break;
1548 | case 0x00ff:
1549 | cp = 0x0178;
1550 | break;
1551 | case 0x0180:
1552 | cp = 0x0243;
1553 | break;
1554 | case 0x01dd:
1555 | cp = 0x018e;
1556 | break;
1557 | case 0x019a:
1558 | cp = 0x023d;
1559 | break;
1560 | case 0x019e:
1561 | cp = 0x0220;
1562 | break;
1563 | case 0x0292:
1564 | cp = 0x01b7;
1565 | break;
1566 | case 0x01c6:
1567 | cp = 0x01c4;
1568 | break;
1569 | case 0x01c9:
1570 | cp = 0x01c7;
1571 | break;
1572 | case 0x01cc:
1573 | cp = 0x01ca;
1574 | break;
1575 | case 0x01f3:
1576 | cp = 0x01f1;
1577 | break;
1578 | case 0x01bf:
1579 | cp = 0x01f7;
1580 | break;
1581 | case 0x0188:
1582 | cp = 0x0187;
1583 | break;
1584 | case 0x018c:
1585 | cp = 0x018b;
1586 | break;
1587 | case 0x0192:
1588 | cp = 0x0191;
1589 | break;
1590 | case 0x0199:
1591 | cp = 0x0198;
1592 | break;
1593 | case 0x01a8:
1594 | cp = 0x01a7;
1595 | break;
1596 | case 0x01ad:
1597 | cp = 0x01ac;
1598 | break;
1599 | case 0x01b9:
1600 | cp = 0x01b8;
1601 | break;
1602 | case 0x01bd:
1603 | cp = 0x01bc;
1604 | break;
1605 | case 0x01f5:
1606 | cp = 0x01f4;
1607 | break;
1608 | case 0x023c:
1609 | cp = 0x023b;
1610 | break;
1611 | case 0x0242:
1612 | cp = 0x0241;
1613 | break;
1614 | case 0x037b:
1615 | cp = 0x03fd;
1616 | break;
1617 | case 0x037c:
1618 | cp = 0x03fe;
1619 | break;
1620 | case 0x037d:
1621 | cp = 0x03ff;
1622 | break;
1623 | case 0x03f3:
1624 | cp = 0x037f;
1625 | break;
1626 | case 0x03ac:
1627 | cp = 0x0386;
1628 | break;
1629 | case 0x03ad:
1630 | cp = 0x0388;
1631 | break;
1632 | case 0x03ae:
1633 | cp = 0x0389;
1634 | break;
1635 | case 0x03af:
1636 | cp = 0x038a;
1637 | break;
1638 | case 0x03cc:
1639 | cp = 0x038c;
1640 | break;
1641 | case 0x03cd:
1642 | cp = 0x038e;
1643 | break;
1644 | case 0x03ce:
1645 | cp = 0x038f;
1646 | break;
1647 | case 0x0371:
1648 | cp = 0x0370;
1649 | break;
1650 | case 0x0373:
1651 | cp = 0x0372;
1652 | break;
1653 | case 0x0377:
1654 | cp = 0x0376;
1655 | break;
1656 | case 0x03d1:
1657 | cp = 0x0398;
1658 | break;
1659 | case 0x03d7:
1660 | cp = 0x03cf;
1661 | break;
1662 | case 0x03f2:
1663 | cp = 0x03f9;
1664 | break;
1665 | case 0x03f8:
1666 | cp = 0x03f7;
1667 | break;
1668 | case 0x03fb:
1669 | cp = 0x03fa;
1670 | break;
1671 | }
1672 | }
1673 |
1674 | return cp;
1675 | }
1676 |
1677 | utf8_constexpr14_impl utf8_int8_t *
1678 | utf8rcodepoint(const utf8_int8_t *utf8_restrict str,
1679 | utf8_int32_t *utf8_restrict out_codepoint) {
1680 | const utf8_int8_t *s = (const utf8_int8_t *)str;
1681 |
1682 | if (0xf0 == (0xf8 & s[0])) {
1683 | /* 4 byte utf8 codepoint */
1684 | *out_codepoint = ((0x07 & s[0]) << 18) | ((0x3f & s[1]) << 12) |
1685 | ((0x3f & s[2]) << 6) | (0x3f & s[3]);
1686 | } else if (0xe0 == (0xf0 & s[0])) {
1687 | /* 3 byte utf8 codepoint */
1688 | *out_codepoint =
1689 | ((0x0f & s[0]) << 12) | ((0x3f & s[1]) << 6) | (0x3f & s[2]);
1690 | } else if (0xc0 == (0xe0 & s[0])) {
1691 | /* 2 byte utf8 codepoint */
1692 | *out_codepoint = ((0x1f & s[0]) << 6) | (0x3f & s[1]);
1693 | } else {
1694 | /* 1 byte utf8 codepoint otherwise */
1695 | *out_codepoint = s[0];
1696 | }
1697 |
1698 | do {
1699 | s--;
1700 | } while ((0 != (0x80 & s[0])) && (0x80 == (0xc0 & s[0])));
1701 |
1702 | return (utf8_int8_t *)s;
1703 | }
1704 |
1705 | #undef utf8_restrict
1706 | #undef utf8_constexpr14
1707 | #undef utf8_null
1708 |
1709 | #ifdef utf8_cplusplus
1710 | } /* extern "C" */
1711 | #endif
1712 |
1713 | #if defined(__clang__)
1714 | #pragma clang diagnostic pop
1715 | #endif
1716 |
1717 | #endif /* SHEREDOM_UTF8_H_INCLUDED */
1718 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "pntr",
3 | "version": "0.0.1",
4 | "private": true,
5 | "description": "pntr: Header-only CPU graphics library for C99 and C++, with a focus on ease-of-use.",
6 | "main": "pntr.h",
7 | "scripts": {
8 | "test": "ctest --test-dir build -V",
9 | "pretest": "npm run build",
10 | "build": "cmake --build build",
11 | "prebuild": "cmake -B build",
12 | "predocs": "npm it",
13 | "docs": "gh-pages -d docs/html"
14 | },
15 | "repository": {
16 | "type": "git",
17 | "url": "git+https://github.com/RobLoach/pntr.git"
18 | },
19 | "author": "Rob Loach (https://robloach.net)",
20 | "license": "Zlib",
21 | "bugs": {
22 | "url": "https://github.com/RobLoach/pntr/issues"
23 | },
24 | "homepage": "https://github.com/RobLoach/pntr#readme",
25 | "devDependencies": {
26 | "gh-pages": "^6.1.1"
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/pntr_assert.h:
--------------------------------------------------------------------------------
1 | /**
2 | * pntr_assert: Assertion library for pntr.
3 | *
4 | * @file pntr_assert.h
5 | *
6 | * @copyright 2023 Rob Loach (@RobLoach, https://robloach.net)
7 | * @license Zlib
8 | *
9 | * Copyright (c) 2023 Rob Loach (@RobLoach, https://robloach.net)
10 | *
11 | * This software is provided "as-is", without any express or implied warranty. In no event
12 | * will the authors be held liable for any damages arising from the use of this software.
13 | *
14 | * Permission is granted to anyone to use this software for any purpose, including commercial
15 | * applications, and to alter it and redistribute it freely, subject to the following restrictions:
16 | *
17 | * 1. The origin of this software must not be misrepresented; you must not claim that you
18 | * wrote the original software. If you use this software in a product, an acknowledgment
19 | * in the product documentation would be appreciated but is not required.
20 | *
21 | * 2. Altered source versions must be plainly marked as such, and must not be misrepresented
22 | * as being the original software.
23 | *
24 | * 3. This notice may not be removed or altered from any source distribution.
25 | */
26 | #ifndef PNTR_ASSERT_H__
27 | #define PNTR_ASSERT_H__
28 |
29 | /**
30 | * @defgroup pntr_assert pntr_assert
31 | * @{
32 | *
33 | * @brief Assertion library for pntr.
34 | */
35 |
36 | #ifndef PNTR_ASSERT
37 | #ifdef NDEBUG
38 | #if defined __cplusplus && __GNUC_PREREQ (2,95)
39 | #define PNTR_ASSERT_VOID_CAST static_cast
40 | #else
41 | #define PNTR_ASSERT_VOID_CAST (void)
42 | #endif
43 | #define PNTR_ASSERT(condition) (PNTR_ASSERT_VOID_CAST 0)
44 | #else
45 | #include
46 | /**
47 | * Assert whether the given condition is true or not.
48 | *
49 | * @details By default, will use `assert()`, but you can override this by defining it to your own assertion call.
50 | *
51 | * @param condition Expression of scalar type.
52 | */
53 | #define PNTR_ASSERT(condition) assert(condition)
54 | #endif
55 | #endif
56 |
57 | #ifndef PNTR_ASSERT_EQUALS
58 | /**
59 | * Evaluates whether or not the given parameters are equal.
60 | *
61 | * @param actual The evaluated value to check against.
62 | * @param expected The expected value.
63 | */
64 | #define PNTR_ASSERT_EQUALS(actual, expected) PNTR_ASSERT((actual) == (expected))
65 | #endif
66 |
67 | #ifndef PNTR_ASSERT_NEQUALS
68 | /**
69 | * Evaluates whether or not the given parameters are not equal.
70 | *
71 | * @param actual The evaluated value to check against.
72 | * @param expected The expected value that should not be equal.
73 | */
74 | #define PNTR_ASSERT_NEQUALS(actual, expected) PNTR_ASSERT((actual) != (expected))
75 | #endif
76 |
77 | #ifndef PNTR_ASSERT_COLOR_EQUALS
78 | /**
79 | * Check whether or not the given colors are the same.
80 | *
81 | * @param actual The actual color to check.
82 | * @param expected The color that is expected.
83 | */
84 | #define PNTR_ASSERT_COLOR_EQUALS(actual, expected) do { \
85 | pntr_color actualColor = (actual); \
86 | pntr_color expectedColor = (expected); \
87 | PNTR_ASSERT_EQUALS(actualColor.rgba.r, expectedColor.rgba.r); \
88 | PNTR_ASSERT_EQUALS(actualColor.rgba.g, expectedColor.rgba.g); \
89 | PNTR_ASSERT_EQUALS(actualColor.rgba.b, expectedColor.rgba.b); \
90 | PNTR_ASSERT_EQUALS(actualColor.rgba.a, expectedColor.rgba.a); \
91 | } while (0)
92 | #endif
93 |
94 | #ifndef PNTR_ASSERT_IMAGE_EQUALS
95 | /**
96 | * Check whether or not the given images are the same.
97 | *
98 | * @param actual The image to check.
99 | * @param expected The image that is expected.
100 | */
101 | #define PNTR_ASSERT_IMAGE_EQUALS(actual, expected) do { \
102 | PNTR_ASSERT_NEQUALS(actual, NULL); \
103 | PNTR_ASSERT_NEQUALS(expected, NULL); \
104 | PNTR_ASSERT_EQUALS(actual->width, expected->width); \
105 | PNTR_ASSERT_EQUALS(actual->height, expected->height); \
106 | for (int x = 0; x < actual->width; x++) { \
107 | for (int y = 0; y < actual->width; y++) { \
108 | PNTR_ASSERT_COLOR_EQUALS(pntr_image_get_color(actual, x, y), pntr_image_get_color(expected, x, y)); \
109 | } \
110 | } \
111 | } while (0)
112 | #endif
113 |
114 | #ifndef PNTR_ASSERT_RECT_EQUALS
115 | /**
116 | * Validate whether or not the given rectangles are equal.
117 | *
118 | * @param actual The rectangle to check.
119 | * @param expected The rectangle that is expected.
120 | */
121 | #define PNTR_ASSERT_RECT_EQUALS(actual, expected) do { \
122 | pntr_rectangle actualRect = (actual); \
123 | pntr_rectangle expectedRect = (expected); \
124 | PNTR_ASSERT_EQUALS(actualRect.x, expectedRect.x); \
125 | PNTR_ASSERT_EQUALS(actualRect.y, expectedRect.y); \
126 | PNTR_ASSERT_EQUALS(actualRect.width, expectedRect.width); \
127 | PNTR_ASSERT_EQUALS(actualRect.height, expectedRect.height); \
128 | } while(0)
129 | #endif // PNTR_ASSERT_RECT_EQUALS
130 |
131 | #ifndef PNTR_ASSERT_VECTOR_EQUALS
132 | /**
133 | * Validate whether or not the given vectors are equal.
134 | *
135 | * @param actual The vector to check.
136 | * @param expected The vector that is expected.
137 | */
138 | #define PNTR_ASSERT_VECTOR_EQUALS(actual, expected) do { \
139 | pntr_vector actualVect = (actual); \
140 | pntr_vector expectedVect = (expected); \
141 | PNTR_ASSERT_EQUALS(actualVect.x, expectedVect.x); \
142 | PNTR_ASSERT_EQUALS(actualVect.y, expectedVect.y); \
143 | } while(0)
144 | #endif // PNTR_ASSERT_VECTOR_EQUALS
145 |
146 | #ifndef pntr_assert
147 | #define pntr_assert(condition) PNTR_ASSERT(condition)
148 | #endif // pntr_assert
149 |
150 | /**
151 | * @}
152 | */
153 |
154 | #endif // PNTR_ASSERT_H__
155 |
--------------------------------------------------------------------------------
/test/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | # pntr_test
2 | add_executable(pntr_test pntr_test.c)
3 | target_compile_options(pntr_test PRIVATE -Wall -Wextra -Wconversion -Wsign-conversion)
4 | target_link_libraries(pntr_test PUBLIC
5 | pntr
6 | #m
7 | )
8 |
9 | # C99 Standard
10 | set_property(TARGET pntr_test PROPERTY C_STANDARD 99)
11 | set_property(TARGET pntr_test PROPERTY C_STANDARD_REQUIRED TRUE)
12 |
13 | # Strict Warnings and Errors
14 | if(MSVC)
15 | target_compile_options(pntr_test PRIVATE /W4 /WX)
16 | else()
17 | target_compile_options(pntr_test PRIVATE -Wall -Wextra -Wpedantic -Werror)
18 | endif()
19 |
20 | # Resources
21 | file(GLOB resources resources/*)
22 | set(examples_resources)
23 | list(APPEND examples_resources ${resources})
24 | file(COPY ${examples_resources} DESTINATION "resources/")
25 |
26 | # Set up the test
27 | list(APPEND CMAKE_CTEST_ARGUMENTS "--output-on-failure")
28 | add_test(NAME pntr_test COMMAND pntr_test)
29 |
--------------------------------------------------------------------------------
/test/pntr_test.c:
--------------------------------------------------------------------------------
1 | #define UNIT_TEST_PREFIX ""
2 | #define UNIT_STATIC
3 | #include "unit.h"
4 |
5 | #define PNTR_ENABLE_DEFAULT_FONT
6 | #define PNTR_ENABLE_TTF
7 | #define PNTR_ENABLE_UTF8
8 |
9 | #define PNTR_IMPLEMENTATION
10 | #define PNTR_ASSERT(condition) EQUALS((bool)(condition), true)
11 | #include "../pntr.h"
12 |
13 | #define COLOREQUALS PNTR_ASSERT_COLOR_EQUALS
14 | #define IMAGEEQUALS PNTR_ASSERT_IMAGE_EQUALS
15 | #define RECTEQUALS PNTR_ASSERT_RECT_EQUALS
16 | #include "../pntr_assert.h"
17 |
18 | bool pntr_utf8() {
19 | #ifdef PNTR_ENABLE_UTF8
20 | return true;
21 | #else
22 | return false;
23 | #endif
24 | }
25 |
26 | MODULE(pntr_math, {
27 | IT("PNTR_SINF", {
28 | EQUALS((int)PNTR_SINF(PNTR_PI / 2.0f), 1);
29 | });
30 |
31 | IT("PNTR_COSF", {
32 | float value = PNTR_COSF(PNTR_PI * 2.0f);
33 | if (value < 0.9f || value > 1.1f) {
34 | EQUALS(0, 1);
35 | }
36 | });
37 |
38 | IT("PNTR_CEILF", {
39 | EQUALS((int)PNTR_CEILF(2.4f), 3);
40 | EQUALS((int)PNTR_CEILF(-2.0f), -2);
41 | EQUALS((int)PNTR_CEILF(0.0f), 0);
42 | });
43 |
44 | IT("PNTR_FABS", {
45 | EQUALS((int)PNTR_FABSF(3.0f), 3);
46 | EQUALS((int)PNTR_FABSF(-3.0f), 3);
47 | EQUALS((int)PNTR_FABSF(0.0f), 0);
48 | });
49 |
50 | IT("PNTR_FLOORF", {
51 | EQUALS((int)PNTR_FLOORF(2.7f), 2);
52 | EQUALS((int)PNTR_FLOORF(-2.7f), -3);
53 | EQUALS((int)PNTR_FLOORF(0.0f), 0);
54 | });
55 |
56 | IT("PNTR_FMODF", {
57 | EQUALS((int)PNTR_FMODF(10.0f, 3.0f), 1);
58 | EQUALS((int)PNTR_FMODF(9.0f, 3.0f), 0);
59 | });
60 | })
61 |
62 | MODULE(pntr, {
63 | IT("pntr_load_memory(), pntr_unload_memory()", {
64 | void* memory = pntr_load_memory(100);
65 | NEQUALS(memory, NULL);
66 | pntr_unload_memory(memory);
67 | });
68 |
69 | IT("pntr_set_error(), pntr_get_error(), pntr_get_error_code()", {
70 | pntr_set_error(PNTR_ERROR_NONE);
71 | EQUALS(pntr_get_error(), NULL);
72 | pntr_image* image = pntr_new_image(-500, -500);
73 | EQUALS(image, NULL);
74 | NEQUALS(pntr_get_error(), NULL);
75 | STREQUALS(pntr_get_error(), "Invalid arguments");
76 | EQUALS(pntr_get_error_code(), PNTR_ERROR_INVALID_ARGS);
77 | pntr_unload_image(image);
78 | pntr_set_error(PNTR_ERROR_NONE);
79 | EQUALS(pntr_get_error_code(), PNTR_ERROR_NONE);
80 | });
81 |
82 | IT("pntr_color_rgba()", {
83 | pntr_color color = PNTR_RED;
84 | EQUALS(pntr_color_r(color), 230);
85 | EQUALS(pntr_color_g(color), 41);
86 | EQUALS(pntr_color_b(color), 55);
87 | EQUALS(pntr_color_a(color), 255);
88 | EQUALS(color.rgba.r, 230);
89 | EQUALS(color.rgba.g, 41);
90 | EQUALS(color.rgba.b, 55);
91 | EQUALS(color.rgba.a, 255);
92 | });
93 |
94 | IT("pntr_color_set_*()", {
95 | pntr_color blank = PNTR_BLANK;
96 | pntr_color_set_r(&blank, 10);
97 | pntr_color_set_g(&blank, 20);
98 | pntr_color_set_b(&blank, 30);
99 | pntr_color_set_a(&blank, 40);
100 | EQUALS(blank.rgba.r, 10);
101 | EQUALS(blank.rgba.g, 20);
102 | EQUALS(blank.rgba.b, 30);
103 | EQUALS(blank.rgba.a, 40);
104 | });
105 |
106 | IT("pntr_get_color()", {
107 | pntr_color color = pntr_get_color(0x052c46ff);
108 | EQUALS(color.rgba.r, 5);
109 | EQUALS(color.rgba.g, 44);
110 | EQUALS(color.rgba.b, 70);
111 | EQUALS(color.rgba.a, 255);
112 | });
113 |
114 | IT("pntr_new_color()", {
115 | pntr_color color = pntr_new_color(100, 120, 130, 200);
116 | EQUALS(color.rgba.r, 100);
117 | EQUALS(color.rgba.g, 120);
118 | EQUALS(color.rgba.b, 130);
119 | EQUALS(color.rgba.a, 200);
120 | });
121 |
122 | IT("pntr_gen_image_color(), pntr_image_get_color()", {
123 | pntr_image* image = pntr_gen_image_color(640, 480, PNTR_SKYBLUE);
124 | NEQUALS(image, NULL);
125 | EQUALS(image->width, 640);
126 | EQUALS(image->height, 480);
127 |
128 | COLOREQUALS(pntr_image_get_color(image, 10, 10), PNTR_SKYBLUE);
129 |
130 | pntr_draw_point(image, 10, 10, PNTR_PURPLE);
131 | COLOREQUALS(pntr_image_get_color(image, 10, 10), PNTR_PURPLE);
132 |
133 | pntr_unload_image(image);
134 | });
135 |
136 | IT("pntr_clear_background(), pntr_draw_rectangle_fill()", {
137 | pntr_image* image = pntr_new_image(100, 100);
138 | NEQUALS(image, NULL);
139 | pntr_clear_background(image, PNTR_RED);
140 |
141 | COLOREQUALS(pntr_image_get_color(image, 10, 10), PNTR_RED);
142 |
143 | pntr_clear_background(image, PNTR_BLANK);
144 | COLOREQUALS(pntr_image_get_color(image, 10, 10), PNTR_BLANK);
145 |
146 | pntr_draw_rectangle_fill(image, 9, 9, 3, 3, PNTR_BLUE);
147 | COLOREQUALS(pntr_image_get_color(image, 10, 10), PNTR_BLUE);
148 | pntr_unload_image(image);
149 | });
150 |
151 | IT("pntr_draw_point()", {
152 | pntr_image* image = pntr_gen_image_color(50, 50, PNTR_WHITE);
153 | COLOREQUALS(pntr_image_get_color(image, 10, 10), PNTR_WHITE);
154 | COLOREQUALS(pntr_image_get_color(image, 10, 9), PNTR_WHITE);
155 | pntr_draw_point(image, 10, 10, PNTR_RED);
156 | COLOREQUALS(pntr_image_get_color(image, 10, 10), PNTR_RED);
157 | COLOREQUALS(pntr_image_get_color(image, 10, 9), PNTR_WHITE);
158 | pntr_unload_image(image);
159 | });
160 |
161 | IT("pntr_draw_points()", {
162 | pntr_image* image = pntr_gen_image_color(50, 50, PNTR_WHITE);
163 | COLOREQUALS(pntr_image_get_color(image, 10, 10), PNTR_WHITE);
164 | COLOREQUALS(pntr_image_get_color(image, 15, 30), PNTR_WHITE);
165 | COLOREQUALS(pntr_image_get_color(image, 40, 40), PNTR_WHITE);
166 | COLOREQUALS(pntr_image_get_color(image, 0, 5), PNTR_WHITE);
167 | COLOREQUALS(pntr_image_get_color(image, 0, 4), PNTR_WHITE);
168 | pntr_vector points[10];
169 | points[0] = PNTR_CLITERAL(pntr_vector) {10, 10};
170 | points[1] = PNTR_CLITERAL(pntr_vector) {15, 30};
171 | points[2] = PNTR_CLITERAL(pntr_vector) {40, 40};
172 | points[3] = PNTR_CLITERAL(pntr_vector) {0, 5};
173 | pntr_draw_points(image, points, 4, PNTR_RED);
174 | COLOREQUALS(pntr_image_get_color(image, 10, 10), PNTR_RED);
175 | COLOREQUALS(pntr_image_get_color(image, 15, 30), PNTR_RED);
176 | COLOREQUALS(pntr_image_get_color(image, 40, 40), PNTR_RED);
177 | COLOREQUALS(pntr_image_get_color(image, 0, 5), PNTR_RED);
178 | COLOREQUALS(pntr_image_get_color(image, 0, 4), PNTR_WHITE);
179 | pntr_unload_image(image);
180 | });
181 |
182 | IT("pntr_get_file_image_type()", {
183 | EQUALS(pntr_get_file_image_type("myimage.png"), PNTR_IMAGE_TYPE_PNG);
184 | EQUALS(pntr_get_file_image_type("my/path/ima.ge.png"), PNTR_IMAGE_TYPE_PNG);
185 | EQUALS(pntr_get_file_image_type("myimage.jpg"), PNTR_IMAGE_TYPE_JPG);
186 | EQUALS(pntr_get_file_image_type("myimage.jpeg"), PNTR_IMAGE_TYPE_JPG);
187 | EQUALS(pntr_get_file_image_type("myimage.bmp"), PNTR_IMAGE_TYPE_BMP);
188 | EQUALS(pntr_get_file_image_type("myimage.exe"), PNTR_IMAGE_TYPE_UNKNOWN);
189 | EQUALS(pntr_get_file_image_type(NULL), PNTR_IMAGE_TYPE_UNKNOWN);
190 | EQUALS(pntr_get_file_image_type(""), PNTR_IMAGE_TYPE_UNKNOWN);
191 | });
192 |
193 | IT("pntr_load_image()", {
194 | pntr_image* image = pntr_load_image("NotFoundImage.png");
195 | EQUALS(image, NULL);
196 | pntr_set_error(PNTR_ERROR_NONE);
197 |
198 | image = pntr_load_image("resources/image.png");
199 | NEQUALS(image, NULL);
200 | EQUALS(image->width, 128);
201 | EQUALS(image->height, 128);
202 | NEQUALS(image->data, NULL);
203 | pntr_unload_image(image);
204 | });
205 |
206 | IT("pntr_load_image_from_memory()", {
207 | unsigned int bytes;
208 | unsigned char* fileData = pntr_load_file("resources/image.png", &bytes);
209 |
210 | pntr_image* image = pntr_load_image_from_memory(PNTR_IMAGE_TYPE_PNG, fileData, bytes);
211 | NEQUALS(image, NULL);
212 | EQUALS(image->width, 128);
213 | EQUALS(image->height, 128);
214 |
215 | pntr_unload_image(image);
216 | pntr_unload_file(fileData);
217 | });
218 |
219 | IT("pntr_load_font_bmf(), pntr_unload_font(), pntr_draw_text()", {
220 | pntr_font* font = pntr_load_font_bmf("resources/font.png", " abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789.,!?-+/");
221 | NEQUALS(font, NULL);
222 | GREATER(font->charactersLen, 10);
223 |
224 | pntr_image* image = pntr_gen_image_color(200, 200, PNTR_DARKBROWN);
225 | NEQUALS(image, NULL);
226 | pntr_draw_text(image, font, "Hello World!", 10, 10, PNTR_WHITE);
227 | pntr_draw_text_wrapped(image, font, "The quick brown fox jumped over the lazy dog.", 10, 10, 100, PNTR_BLUE);
228 |
229 | pntr_unload_image(image);
230 | pntr_unload_font(font);
231 | });
232 |
233 | IT("pntr_measure_text(), pntr_measure_text_ex(), pntr_gen_image_text()", {
234 | pntr_font* font = pntr_load_font_bmf("resources/font.png", " abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789.,!?-+/");
235 | GREATER(pntr_measure_text(font, "Hello World!"), 50);
236 | pntr_vector size = pntr_measure_text_ex(font, "Hello World!", 0);
237 | GREATER(size.x, 50);
238 | EQUALS(size.y, font->atlas->height);
239 |
240 | pntr_image* textImage = pntr_gen_image_text(font, "Hello World!", PNTR_WHITE, PNTR_BLANK);
241 | NEQUALS(textImage, NULL);
242 | EQUALS(textImage->width, size.x);
243 | EQUALS(textImage->height, size.y);
244 | pntr_unload_image(textImage);
245 |
246 | size = pntr_measure_text_ex(font, "On\nNew\nLines", 0);
247 | EQUALS(size.y, font->atlas->height * 3);
248 |
249 | pntr_unload_font(font);
250 | });
251 |
252 | IT("pntr_load_font_tty()", {
253 | pntr_font* font = pntr_load_font_tty("resources/font-tty-8x8.png", 8, 8, "\x7f !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~");
254 | NEQUALS(font, NULL);
255 | GREATER(font->charactersLen, 20);
256 | pntr_unload_font(font);
257 | });
258 |
259 | IT("pntr_load_font_default()", {
260 | pntr_font* font = pntr_load_font_default();
261 | NEQUALS(font, NULL);
262 | NEQUALS(font->atlas, NULL);
263 | GREATER(font->charactersLen, 10);
264 | pntr_unload_font(font);
265 | });
266 |
267 | IT("pntr_image_resize()", {
268 | pntr_image* image = pntr_new_image(300, 100);
269 | NEQUALS(image, NULL);
270 |
271 | IT("pntr_image_resize(PNTR_FILTER_NEARESTNEIGHBOR)", {
272 | pntr_image* resized = pntr_image_resize(image, 100, 100, PNTR_FILTER_NEARESTNEIGHBOR);
273 | NEQUALS(resized, NULL);
274 | EQUALS(resized->width, 100);
275 | EQUALS(resized->height, 100);
276 | pntr_unload_image(resized);
277 | });
278 |
279 | IT("pntr_image_resize(PNTR_FILTER_BILINEAR)", {
280 | pntr_image* resized = pntr_image_resize(image, 400, 300, PNTR_FILTER_BILINEAR);
281 | NEQUALS(resized, NULL);
282 | EQUALS(resized->width, 400);
283 | EQUALS(resized->height, 300);
284 | pntr_unload_image(resized);
285 | });
286 |
287 | pntr_unload_image(image);
288 | });
289 |
290 | IT("pntr_image_scale()", {
291 | pntr_image* image = pntr_new_image(100, 200);
292 | NEQUALS(image, NULL);
293 |
294 | pntr_image* scaled = pntr_image_scale(image, 1.5f, 2.5f, PNTR_FILTER_BILINEAR);
295 | NEQUALS(scaled, NULL);
296 | EQUALS(scaled->width, 150);
297 | EQUALS(scaled->height, 500);
298 | pntr_unload_image(scaled);
299 |
300 | scaled = pntr_image_scale(image, -2.0f, -3.0f, PNTR_FILTER_NEARESTNEIGHBOR);
301 | EQUALS(scaled, NULL);
302 | EQUALS(pntr_get_error_code(), PNTR_ERROR_INVALID_ARGS);
303 |
304 | pntr_unload_image(image);
305 | });
306 |
307 | IT("pntr_image_copy()", {
308 | pntr_image* image = pntr_gen_image_color(10, 10, PNTR_RED);
309 | pntr_draw_point(image, 5, 5, PNTR_BLUE);
310 | COLOREQUALS(pntr_image_get_color(image, 5, 5), PNTR_BLUE);
311 | COLOREQUALS(pntr_image_get_color(image, 2, 2), PNTR_RED);
312 |
313 | pntr_image* copy = pntr_image_copy(image);
314 | NEQUALS(image, copy);
315 | IMAGEEQUALS(image, copy);
316 | COLOREQUALS(pntr_image_get_color(copy, 5, 5), PNTR_BLUE);
317 | COLOREQUALS(pntr_image_get_color(copy, 2, 2), PNTR_RED);
318 |
319 | pntr_unload_image(copy);
320 | pntr_unload_image(image);
321 | });
322 |
323 | IT("pntr_image_color_replace()", {
324 | pntr_image* image = pntr_gen_image_color(100, 100, PNTR_BLUE);
325 | NEQUALS(image, NULL);
326 | COLOREQUALS(pntr_image_get_color(image, 10, 10), PNTR_BLUE);
327 |
328 | pntr_image_color_replace(image, PNTR_BLUE, PNTR_RED);
329 | COLOREQUALS(pntr_image_get_color(image, 10, 10), PNTR_RED);
330 |
331 | pntr_unload_image(image);
332 | });
333 |
334 | IT("pntr_color_invert()", {
335 | pntr_color color = pntr_new_color(21, 16, 171, 255);
336 | COLOREQUALS(pntr_color_invert(color), pntr_new_color(234, 239, 84, 255));
337 | color = pntr_new_color(64, 148, 81, 255);
338 | COLOREQUALS(pntr_color_invert(color), pntr_new_color(191, 107, 174, 255));
339 | });
340 |
341 | IT("pntr_image_color_invert()", {
342 | pntr_color color = pntr_new_color(21, 16, 171, 255);
343 | pntr_color invert = pntr_new_color(234, 239, 84, 255);
344 | pntr_image* image = pntr_gen_image_color(100, 100, color);
345 | COLOREQUALS(pntr_image_get_color(image, 10, 10), color);
346 | pntr_image_color_invert(image);
347 | COLOREQUALS(pntr_image_get_color(image, 10, 10), invert);
348 | pntr_unload_image(image);
349 | });
350 |
351 | IT("pntr_color_tint()", {
352 | pntr_color color = PNTR_WHITE;
353 | pntr_color tinted = pntr_color_tint(color, PNTR_RED);
354 | COLOREQUALS(tinted, PNTR_RED);
355 | });
356 |
357 | IT("pntr_image_color_tint()", {
358 | pntr_image* image = pntr_gen_image_color(100, 100, PNTR_WHITE);
359 | COLOREQUALS(pntr_image_get_color(image, 10, 10), PNTR_WHITE);
360 | pntr_image_color_tint(image, PNTR_RED);
361 | COLOREQUALS(pntr_image_get_color(image, 10, 10), PNTR_RED);
362 | pntr_unload_image(image);
363 | });
364 |
365 | IT("pntr_color_fade()", {
366 | pntr_color color = PNTR_RED;
367 | EQUALS(color.rgba.a, 255);
368 | EQUALS(color.rgba.r, 230);
369 |
370 | pntr_color faded = pntr_color_fade(color, -0.5f);
371 | EQUALS(faded.rgba.a, 127);
372 | EQUALS(faded.rgba.r, 230);
373 |
374 | faded = pntr_color_fade(faded, 0.5f);
375 | EQUALS(faded.rgba.a, 191);
376 | EQUALS(faded.rgba.r, 230);
377 | });
378 |
379 | IT("pntr_image_color_fade()", {
380 | pntr_color red = PNTR_RED;
381 | pntr_image* image = pntr_gen_image_color(50, 50, red);
382 | NEQUALS(image, NULL);
383 | COLOREQUALS(pntr_image_get_color(image, 10, 10), red);
384 | pntr_image_color_fade(image, -0.5f);
385 | red.rgba.a = 127;
386 | COLOREQUALS(pntr_image_get_color(image, 10, 10), red);
387 | pntr_unload_image(image);
388 | });
389 |
390 | IT("pntr_load_file(), pntr_unload_file()", {
391 | unsigned int bytesRead;
392 | unsigned char* fileData = pntr_load_file("resources/text.txt", &bytesRead);
393 |
394 | GREATER(bytesRead, 5);
395 | STRCEQUALS((const char*)fileData, "Hello", 5);
396 | pntr_unload_file(fileData);
397 |
398 | // Try to load a file that doesn't exist.
399 | unsigned char* fileNotFound = pntr_load_file("FileNotFound.txt", NULL);
400 | EQUALS(fileNotFound, NULL);
401 |
402 | // Expect an error to result.
403 | const char* error = pntr_get_error();
404 | NEQUALS(error, NULL);
405 | pntr_set_error(PNTR_ERROR_NONE);
406 | });
407 |
408 | IT("pntr_load_file_text()", {
409 | const char* text = pntr_load_file_text("resources/text.txt");
410 | STRCEQUALS(text, "Hello, World!", 13);
411 | pntr_unload_file_text(text);
412 | });
413 |
414 | IT("pntr_load_font_ttf()", {
415 | pntr_font* font = pntr_load_font_ttf("resources/tuffy.ttf", 20);
416 | NEQUALS(font, NULL);
417 | GREATER(font->charactersLen, 20);
418 |
419 | pntr_image* canvas = pntr_gen_image_text(font, "Hello World!", PNTR_RED, PNTR_BLANK);
420 | NEQUALS(canvas, NULL);
421 | GREATER(canvas->width, 10);
422 | GREATER(canvas->height, 10);
423 |
424 | IT("pntr_measure_text_ex()", {
425 | pntr_vector size = pntr_measure_text_ex(font, "Hello!!", 0);
426 | GREATER(size.x, 20);
427 | GREATER(size.y, 5);
428 | });
429 |
430 | pntr_unload_image(canvas);
431 | pntr_unload_font(font);
432 | });
433 |
434 | IT("pntr_save_file()", {
435 | const char* fileName = "tempFile.txt";
436 | const char* fileData = "Hello World!";
437 | unsigned int bytes = 12;
438 | bool result = pntr_save_file(fileName, (unsigned char*)fileData, bytes);
439 | EQUALS(result, true);
440 |
441 | unsigned char* fileDataResult = pntr_load_file(fileName, &bytes);
442 | GREATER(bytes, 5);
443 | STRCEQUALS((const char*)fileDataResult, "Hello", 5);
444 | pntr_unload_file(fileDataResult);
445 | });
446 |
447 | IT("pntr_save_image()", {
448 | int width = 400;
449 | int height = 300;
450 | pntr_image* saveImage = pntr_gen_image_color(width, height, PNTR_RED);
451 | NEQUALS(saveImage, NULL);
452 | pntr_draw_circle_fill(saveImage, 200, 150, 80, PNTR_BLUE);
453 | pntr_draw_rectangle_fill(saveImage, 10, 10, 20, 20, PNTR_GREEN);
454 | bool result = pntr_save_image(saveImage, "saveImage.png");
455 | EQUALS(result, true);
456 | pntr_unload_image(saveImage);
457 |
458 | pntr_image* loadedImage = pntr_load_image("saveImage.png");
459 | NEQUALS(loadedImage, NULL);
460 | EQUALS(loadedImage->width, 400);
461 | EQUALS(loadedImage->height, height);
462 | COLOREQUALS(pntr_image_get_color(loadedImage, 15, 15), PNTR_GREEN);
463 | pntr_unload_image(loadedImage);
464 | });
465 |
466 | IT("pntr_get_pixel_data_size()", {
467 | EQUALS(pntr_get_pixel_data_size(1, 1, PNTR_PIXELFORMAT_RGBA8888), 4);
468 | EQUALS(pntr_get_pixel_data_size(2, 3, PNTR_PIXELFORMAT_RGBA8888), 24);
469 | EQUALS(pntr_get_pixel_data_size(3, 2, PNTR_PIXELFORMAT_ARGB8888), 24);
470 | EQUALS(pntr_get_pixel_data_size(4, 4, PNTR_PIXELFORMAT_GRAYSCALE), 16);
471 | });
472 |
473 | IT("pntr_image_alpha_border(), pntr_image_alpha_crop()", {
474 | pntr_image* image = pntr_gen_image_color(400, 400, PNTR_BLANK);
475 | NEQUALS(image, NULL);
476 | EQUALS(image->width, 400);
477 | EQUALS(image->height, 400);
478 |
479 | pntr_draw_rectangle_fill(image, 100, 100, 200, 200, PNTR_BLUE);
480 |
481 | pntr_rectangle crop = pntr_image_alpha_border(image, 0);
482 | EQUALS(crop.x, 100);
483 | EQUALS(crop.y, 100);
484 | EQUALS(crop.width, 200);
485 | EQUALS(crop.height, 200);
486 |
487 | pntr_image_alpha_crop(image, 0);
488 | NEQUALS(image, NULL);
489 | EQUALS(image->width, 200);
490 | EQUALS(image->height, 200);
491 |
492 | COLOREQUALS(pntr_image_get_color(image, 50, 50), PNTR_BLUE);
493 |
494 | pntr_unload_image(image);
495 | });
496 |
497 | IT("pntr_image_crop()", {
498 | pntr_image* image = pntr_gen_image_color(200, 200, PNTR_RED);
499 | NEQUALS(image, NULL);
500 | pntr_image_crop(image, 10, 30, 20, 50);
501 | NEQUALS(image, NULL);
502 | EQUALS(image->width, 20);
503 | EQUALS(image->height, 50);
504 | COLOREQUALS(pntr_image_get_color(image, 10, 20), PNTR_RED);
505 | pntr_unload_image(image);
506 | });
507 |
508 | IT("pntr_image_flip()", {
509 | pntr_image* image = pntr_gen_image_color(100, 100, PNTR_RED);
510 | NEQUALS(image, NULL);
511 | pntr_draw_rectangle_fill(image, 0, 0, 20, 20, PNTR_BLUE);
512 | COLOREQUALS(pntr_image_get_color(image, 10, 10), PNTR_BLUE);
513 | COLOREQUALS(pntr_image_get_color(image, 90, 10), PNTR_RED);
514 | COLOREQUALS(pntr_image_get_color(image, 10, 90), PNTR_RED);
515 | COLOREQUALS(pntr_image_get_color(image, 90, 90), PNTR_RED);
516 | pntr_image_flip(image, true, true);
517 | COLOREQUALS(pntr_image_get_color(image, 10, 10), PNTR_RED);
518 | COLOREQUALS(pntr_image_get_color(image, 90, 10), PNTR_RED);
519 | COLOREQUALS(pntr_image_get_color(image, 10, 90), PNTR_RED);
520 | COLOREQUALS(pntr_image_get_color(image, 90, 90), PNTR_BLUE);
521 | pntr_unload_image(image);
522 | });
523 |
524 | IT("pntr_image_resize_canvas()", {
525 | pntr_image* image = pntr_gen_image_color(200, 200, PNTR_BLUE);
526 | NEQUALS(image, NULL);
527 | EQUALS(image->width, 200);
528 | EQUALS(image->height, 200);
529 | pntr_image_resize_canvas(image, 400, 400, 100, 100, PNTR_RED);
530 | NEQUALS(image, NULL);
531 | EQUALS(image->width, 400);
532 | EQUALS(image->height, 400);
533 | COLOREQUALS(pntr_image_get_color(image, 50, 50), PNTR_RED);
534 | COLOREQUALS(pntr_image_get_color(image, 150, 150), PNTR_BLUE);
535 | pntr_unload_image(image);
536 | });
537 |
538 | IT("pntr_image_rotate()", {
539 | pntr_image* image = pntr_gen_image_color(40, 30, PNTR_BLUE);
540 | NEQUALS(image, NULL);
541 | pntr_draw_rectangle_fill(image, 9, 9, 3, 3, PNTR_RED);
542 |
543 | IT("pntr_image_rotate(image, 0.0f)", {
544 | pntr_image* rotated = pntr_image_rotate(image, 0.0f, PNTR_FILTER_NEARESTNEIGHBOR);
545 | NEQUALS(rotated, NULL);
546 | EQUALS(rotated->width, 40);
547 | EQUALS(rotated->height, 30);
548 | pntr_unload_image(rotated);
549 | });
550 |
551 | IT("pntr_image_rotate(image, 90.0f)", {
552 | pntr_image* rotated = pntr_image_rotate(image, 90.0f, PNTR_FILTER_BILINEAR);
553 | NEQUALS(rotated, NULL);
554 | EQUALS(rotated->width, image->height);
555 | EQUALS(rotated->height, image->width);
556 | COLOREQUALS(pntr_image_get_color(rotated, 10, 10), PNTR_BLUE);
557 | COLOREQUALS(pntr_image_get_color(rotated, 10, 30), PNTR_RED);
558 | pntr_unload_image(rotated);
559 | });
560 |
561 | IT("pntr_image_rotate(image, 180.0f)", {
562 | pntr_image* rotated = pntr_image_rotate(image, 180.0f, PNTR_FILTER_BILINEAR);
563 | NEQUALS(rotated, NULL);
564 | EQUALS(rotated->width, image->width);
565 | EQUALS(rotated->height, image->height);
566 | COLOREQUALS(pntr_image_get_color(rotated, 10, 10), PNTR_BLUE);
567 | COLOREQUALS(pntr_image_get_color(rotated, 30, 20), PNTR_RED);
568 | pntr_unload_image(rotated);
569 | });
570 |
571 | IT("pntr_image_rotate(image, 270.0f)", {
572 | pntr_image* rotated = pntr_image_rotate(image, 270.0f, PNTR_FILTER_NEARESTNEIGHBOR);
573 | NEQUALS(rotated, NULL);
574 | EQUALS(rotated->width, image->height);
575 | EQUALS(rotated->height, image->width);
576 | COLOREQUALS(pntr_image_get_color(rotated, 10, 10), PNTR_BLUE);
577 | COLOREQUALS(pntr_image_get_color(rotated, 20, 10), PNTR_RED);
578 | pntr_unload_image(rotated);
579 | });
580 |
581 | IT("pntr_image_rotate(image, 48.0f)", {
582 | pntr_image* rotated = pntr_image_rotate(image, 48.0f, PNTR_FILTER_BILINEAR);
583 | NEQUALS(rotated, NULL);
584 | NEQUALS(rotated->width, image->height);
585 | NEQUALS(rotated->height, image->width);
586 | COLOREQUALS(pntr_image_get_color(rotated, 5, 5), PNTR_BLANK);
587 | COLOREQUALS(pntr_image_get_color(rotated, rotated->width / 2, rotated->height / 2), PNTR_BLUE);
588 | pntr_unload_image(rotated);
589 | });
590 |
591 | IT("pntr_gen_image_gradient", {
592 | pntr_image* image = pntr_gen_image_gradient(500, 500, PNTR_RED, PNTR_GREEN, PNTR_BLUE, PNTR_GOLD);
593 | NEQUALS(image, NULL);
594 |
595 | pntr_color red = pntr_image_get_color(image, 0, 0);
596 | COLOREQUALS(red, PNTR_RED);
597 | pntr_color green = pntr_image_get_color(image, image->width - 1, 0);
598 | GREATER(green.rgba.g, 220);
599 | pntr_color blue = pntr_image_get_color(image, 0, image->height - 1);
600 | GREATER(blue.rgba.b, 230);
601 | pntr_color gold = pntr_image_get_color(image, image->width - 1, image->height - 1);
602 | GREATER(gold.rgba.r, 230);
603 | GREATER(gold.rgba.g, 180);
604 | pntr_unload_image(image);
605 | });
606 |
607 | pntr_unload_image(image);
608 | });
609 |
610 | IT("pntr_font_copy()", {
611 | pntr_font* font = pntr_load_font_default();
612 | NEQUALS(font, NULL);
613 |
614 | pntr_font* copy = pntr_font_copy(font);
615 | NEQUALS(copy, NULL);
616 |
617 | EQUALS(font->charactersLen, copy->charactersLen);
618 | EQUALS(font->atlas->width, copy->atlas->width);
619 | EQUALS(font->atlas->height, copy->atlas->height);
620 | NEQUALS(font->atlas, copy->atlas);
621 |
622 | pntr_unload_font(copy);
623 | pntr_unload_font(font);
624 | });
625 |
626 | IT("pntr_font_scale()", {
627 | pntr_font* font = pntr_load_font_default();
628 | NEQUALS(font, NULL);
629 |
630 | int scaleX = 5;
631 | int scaleY = 2;
632 | pntr_font* resized = pntr_font_scale(font, (float)scaleX, (float)scaleY, PNTR_FILTER_BILINEAR);
633 | NEQUALS(resized, NULL);
634 |
635 | EQUALS(font->charactersLen, resized->charactersLen);
636 | EQUALS(font->atlas->width * scaleX, resized->atlas->width);
637 | EQUALS(font->atlas->height * scaleY, resized->atlas->height);
638 | EQUALS(resized->glyphRects[0].width, font->glyphRects[0].width * scaleX);
639 | NEQUALS(font->atlas, resized->atlas);
640 |
641 | pntr_unload_font(font);
642 | pntr_unload_font(resized);
643 | });
644 |
645 | IT("pntr_image_subimage", {
646 | pntr_image* image = pntr_gen_image_color(300, 300, PNTR_RED);
647 | pntr_draw_rectangle_fill(image, 100, 100, 100, 100, PNTR_BLUE);
648 |
649 | COLOREQUALS(pntr_image_get_color(image, 50, 50), PNTR_RED);
650 | COLOREQUALS(pntr_image_get_color(image, 150, 150), PNTR_BLUE);
651 |
652 | pntr_image* subimage = pntr_image_subimage(image, 100, 100, 100, 100);
653 | COLOREQUALS(pntr_image_get_color(subimage, 50, 50), PNTR_BLUE);
654 |
655 | pntr_unload_image(subimage);
656 | pntr_unload_image(image);
657 | });
658 |
659 | IT("pntr_image_set_clip", {
660 | pntr_image* image = pntr_gen_image_color(300, 300, PNTR_RED);
661 | COLOREQUALS(pntr_image_get_color(image, 50, 50), PNTR_RED);
662 | COLOREQUALS(pntr_image_get_color(image, 125, 125), PNTR_RED);
663 |
664 | pntr_image_set_clip(image, 100, 100, 50, 50);
665 | pntr_draw_rectangle_fill(image, 0, 0, image->width, image->height, PNTR_BLUE);
666 | COLOREQUALS(pntr_image_get_color(image, 50, 50), PNTR_RED);
667 | COLOREQUALS(pntr_image_get_color(image, 125, 125), PNTR_BLUE);
668 |
669 | pntr_unload_image(image);
670 | });
671 |
672 | IT("pntr_image_get_clip", {
673 | pntr_image* image = pntr_gen_image_color(300, 100, PNTR_RED);
674 |
675 | pntr_rectangle expected;
676 | expected.width = 300;
677 | expected.height = 100;
678 | expected.x = 0;
679 | expected.y = 0;
680 | RECTEQUALS(expected, pntr_image_get_clip(image));
681 |
682 | expected.width = 0;
683 | expected.height = 0;
684 | expected.x = 0;
685 | expected.y = 0;
686 | RECTEQUALS(expected, pntr_image_get_clip(NULL));
687 |
688 | pntr_unload_image(image);
689 | });
690 |
691 | IT("pntr_image_reset_clip", {
692 | pntr_image* image = pntr_gen_image_color(300, 300, PNTR_RED);
693 |
694 | EQUALS(image->clip.x, 0);
695 | EQUALS(image->clip.y, 0);
696 | EQUALS(image->clip.width, image->width);
697 | EQUALS(image->clip.height, image->height);
698 |
699 | pntr_image_set_clip(image, 100, 200, 50, 60);
700 |
701 | EQUALS(image->clip.x, 100);
702 | EQUALS(image->clip.y, 200);
703 | EQUALS(image->clip.width, 50);
704 | EQUALS(image->clip.height, 60);
705 |
706 | pntr_image_reset_clip(image);
707 |
708 | EQUALS(image->clip.x, 0);
709 | EQUALS(image->clip.y, 0);
710 | EQUALS(image->clip.width, image->width);
711 | EQUALS(image->clip.height, image->height);
712 |
713 | pntr_unload_image(image);
714 | });
715 |
716 | IT("_pntr_rectangle_intersect", {
717 | pntr_rectangle out;
718 | EQUALS(_pntr_rectangle_intersect(-10, -10, 5, 5, 0, 0, 100, 100, &out), false);
719 | EQUALS(_pntr_rectangle_intersect(5, 6, 10, 5, 0, 0, 100, 100, &out), true);
720 | EQUALS(out.x, 5);
721 | EQUALS(out.y, 6);
722 | EQUALS(out.width, 10);
723 | EQUALS(out.height, 5);
724 | EQUALS(_pntr_rectangle_intersect(-5, -5, 10, 10, 0, 0, 20, 20, &out), true);
725 | EQUALS(out.x, 0);
726 | EQUALS(out.y, 0);
727 | EQUALS(out.width, 5);
728 | EQUALS(out.height, 5);
729 |
730 | EQUALS(_pntr_rectangle_intersect(10, 10, 50, 50, 20, 20, 10, 10, &out), true);
731 | pntr_rectangle expected = (pntr_rectangle) {20, 20, 10, 10};
732 | RECTEQUALS(out, expected);
733 | });
734 |
735 | if (pntr_utf8()) {
736 | IT("PNTR_ENABLE_UTF8", {
737 | pntr_font* font = pntr_load_font_ttf("resources/tuffy.ttf", 38);
738 | NEQUALS(font, NULL);
739 |
740 | // Generate the image displaying UTF-8 text.
741 | const char* text = "Добрий день!";
742 | pntr_image* image = pntr_gen_image_text(font, text, PNTR_BLACK, PNTR_WHITE);
743 | NEQUALS(image, NULL);
744 | pntr_save_image(image, "pntr_test_utf8.png");
745 |
746 | EQUALS(image->width, 190);
747 | EQUALS(image->height, 37);
748 |
749 | pntr_unload_font(font);
750 | pntr_unload_image(image);
751 | });
752 | }
753 | else {
754 | IT("PNTR_ENABLE_UTF8: Not enabled", {
755 | // Nothing
756 | });
757 | }
758 |
759 | IT("No reported errors", {
760 | const char* err = "";
761 | if (pntr_get_error() != NULL) {
762 | err = pntr_get_error();
763 | }
764 |
765 | STREQUALS(err, "");
766 | });
767 | })
768 |
769 | int main() {
770 | UNIT_CREATE("pntr");
771 | UNIT_MODULE(pntr);
772 | UNIT_MODULE(pntr_math);
773 | return UNIT_RUN();
774 | }
775 |
--------------------------------------------------------------------------------
/test/resources/font-tty-8x8.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RobLoach/pntr/922aed03c4c53803466100596211da7549945da7/test/resources/font-tty-8x8.png
--------------------------------------------------------------------------------
/test/resources/font.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RobLoach/pntr/922aed03c4c53803466100596211da7549945da7/test/resources/font.png
--------------------------------------------------------------------------------
/test/resources/image.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RobLoach/pntr/922aed03c4c53803466100596211da7549945da7/test/resources/image.png
--------------------------------------------------------------------------------
/test/resources/logo-256x256.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RobLoach/pntr/922aed03c4c53803466100596211da7549945da7/test/resources/logo-256x256.png
--------------------------------------------------------------------------------
/test/resources/text.txt:
--------------------------------------------------------------------------------
1 | Hello, World!
2 |
--------------------------------------------------------------------------------
/test/resources/tuffy.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RobLoach/pntr/922aed03c4c53803466100596211da7549945da7/test/resources/tuffy.ttf
--------------------------------------------------------------------------------
/test/unit.h:
--------------------------------------------------------------------------------
1 | /**
2 | zpl - Unit testing framework
3 |
4 | Usage:
5 | #include "unit.h" in EXACTLY one source file, usually the one containing your testing app's entry point.
6 |
7 | There really is no need to include this file multiple times within a project, unless you wish to run
8 | multiple tests within a single executable or split test cases to multiple compilation units, in such case
9 | define UNIT_STATIC to ensure the library won't leak symbols outside compilation units.
10 |
11 | and cover your beautiful code already!
12 |
13 | GitHub:
14 | https://github.com/zpl-c/tester
15 |
16 | Version History:
17 | 1.1.1 - ensure memory arena only works with zpl present
18 | 1.1.0 - introduce memory arena for per-module allocations
19 | 1.0.1 - Small tweaks
20 | 1.0.0 - Where it all started... (not really)
21 |
22 | License:
23 | This Software is dual licensed under the following licenses:
24 |
25 | Unlicense
26 | This is free and unencumbered software released into the public domain.
27 |
28 | Anyone is free to copy, modify, publish, use, compile, sell, or
29 | distribute this software, either in source code form or as a compiled
30 | binary, for any purpose, commercial or non-commercial, and by any
31 | means.
32 |
33 | In jurisdictions that recognize copyright laws, the author or authors
34 | of this software dedicate any and all copyright interest in the
35 | software to the public domain. We make this dedication for the benefit
36 | of the public at large and to the detriment of our heirs and
37 | successors. We intend this dedication to be an overt act of
38 | relinquishment in perpetuity of all present and future rights to this
39 | software under copyright law.
40 |
41 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
42 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
43 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
44 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
45 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
46 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
47 | OTHER DEALINGS IN THE SOFTWARE.
48 |
49 | For more information, please refer to
50 |
51 | BSD 3-Clause
52 | Copyright (c) 2016-2021 Dominik Madarász. All rights reserved.
53 |
54 | Redistribution and use in source and binary forms, with or without
55 | modification, are permitted provided that the following conditions are met:
56 |
57 | 1. Redistributions of source code must retain the above copyright notice, this
58 | list of conditions and the following disclaimer.
59 | 2. Redistributions in binary form must reproduce the above copyright notice,
60 | this list of conditions and the following disclaimer in the documentation
61 | and/or other materials provided with the distribution.
62 | 3. Neither the name of the copyright holder nor the names of its contributors
63 | may be used to endorse or promote products derived from this software without
64 | specific prior written permission.
65 |
66 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
67 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
68 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
69 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
70 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
71 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
72 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
73 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
74 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
75 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
76 |
77 | */
78 |
79 | /* Adjust it to your needs, preferably to the number of modules you wish to test against. */
80 | #ifndef UNIT_MAX_MODULES
81 | #define UNIT_MAX_MODULES 256
82 | #endif
83 |
84 | #ifndef UNIT_SKIP_MAGIC
85 | #define UNIT_SKIP_MAGIC 0xFF
86 | #endif
87 |
88 | #ifndef UNIT_JOIN2
89 | #define UNIT_JOIN2(a,b) a##b
90 | #endif
91 |
92 | #ifndef UNIT_TEST_PREFIX
93 | #define UNIT_TEST_PREFIX "It "
94 | #endif
95 |
96 | /* Isolates test results within compilation units, this allows for running
97 | multiple test suites per single binary. */
98 | #ifdef UNIT_STATIC
99 | #define UNIT_DEF static
100 | #else
101 | #define UNIT_DEF
102 | #endif
103 |
104 | #include
105 | #include
106 | #include
107 | #include
108 |
109 | /* zpl specific */
110 |
111 | /* verify if zpl is present */
112 | #if defined(ZPL_H) && !defined(UNIT_ZPL_DISABLE_ARENA)
113 | /* specifies memory limit for per-module memory allocator */
114 | #ifndef UNIT_ARENA_MEM
115 | #define UNIT_ARENA_MEM (512*1024)
116 | #endif
117 |
118 | #define UNIT_ZPL_INITIALIZE_MEM_ARENA() \
119 | zpl_arena hunk_mem={0}; \
120 | zpl_arena_init_from_allocator(&hunk_mem, zpl_heap(), UNIT_ARENA_MEM); \
121 | zpl_allocator mem_alloc = zpl_arena_allocator(&hunk_mem); \
122 | zpl_unused(mem_alloc);
123 | #define UNIT_ZPL_DESTROY_MEM_ARENA() \
124 | zpl_arena_free(&hunk_mem);
125 | #define UNIT_ZPL_CAPTURE_MEMORY() \
126 | zpl_temp_arena_memory mem_snapshot = zpl_temp_arena_memory_begin(&hunk_mem);
127 | #define UNIT_ZPL_RESTORE_MEMORY() \
128 | zpl_temp_arena_memory_end(mem_snapshot);
129 | #else
130 | #define UNIT_ZPL_INITIALIZE_MEM_ARENA()
131 | #define UNIT_ZPL_DESTROY_MEM_ARENA()
132 | #define UNIT_ZPL_CAPTURE_MEMORY()
133 | #define UNIT_ZPL_RESTORE_MEMORY()
134 | #endif
135 |
136 | #define MODULE(name, ...) \
137 | int32_t UNIT_JOIN2(module__,name)() { \
138 | printf("--------------------------------------\n"); \
139 | printf(" module: %s\n", #name); \
140 | printf("--------------------------------------\n"); \
141 | fflush(stdout); \
142 | _g_modules++; \
143 | int32_t _total = 0; \
144 | int32_t _errors = 0; \
145 | int32_t _lasterr = 0; \
146 | char *_errstr = 0; \
147 | UNIT_ZPL_INITIALIZE_MEM_ARENA(); \
148 | {__VA_ARGS__}; \
149 | UNIT_ZPL_DESTROY_MEM_ARENA(); \
150 | fflush(stdout); \
151 | printf("\n results: %d total, %s%d failed\x1B[0m, %s%d passed\x1B[0m\n", _total, _errors>0?"\x1B[31m":"", _errors, _errors==0?"\x1B[32m":"", _total - _errors); \
152 | _g_total += _total; \
153 | _g_errors += _errors; \
154 | if (_errors) _g_modules_err++; \
155 | return (_errors); \
156 | }
157 |
158 | #define IT(desc, ...) \
159 | _lasterr = 0; \
160 | _errstr = ""; \
161 | _total += 1; \
162 | { \
163 | UNIT_ZPL_CAPTURE_MEMORY(); \
164 | do {__VA_ARGS__} while(0); \
165 | UNIT_ZPL_RESTORE_MEMORY(); \
166 | } \
167 | if (_lasterr != UNIT_SKIP_MAGIC) _errors += _lasterr; \
168 | printf(" * [%s]: " UNIT_TEST_PREFIX "%s %s\n", (_lasterr == UNIT_SKIP_MAGIC) ? "\x1B[33mSKIP\x1B[0m" : (_lasterr) ? "\x1B[31mFAIL\x1B[0m" : "\x1B[32mPASS\x1B[0m", desc, _errstr);
169 |
170 | /* TEST CHECKS */
171 | #define FAIL(a, b) { _errstr = unit__bprintf("\n\n\tassert: \x1B[31m%s:%lld %s %s:%lld\x1B[0m\n\tat %s:%d\n", #a, a, (a == b)?"==":"!=", #b, b, __FILE__, __LINE__); _lasterr = 1; break; }
172 | #define UFAIL(a, b) { _errstr = unit__bprintf("\n\n\tassert: \x1B[31m%s:%llu %s %s:%llu\x1B[0m\n\tat %s:%d\n", #a, a, (a == b)?"==":"!=", #b, b, __FILE__, __LINE__); _lasterr = 1; break; }
173 | #define FFAIL(a, b) { _errstr = unit__bprintf("\n\n\tassert: \x1B[31m%s:%g %s %s:%g\x1B[0m\n\tat %s:%d\n", #a, a, (a == b)?"==":"!=", #b, b, __FILE__, __LINE__); _lasterr = 1; break; }
174 | #define STRFAIL(a, b) { _errstr = unit__bprintf("\n\n\tassert: \x1B[31m%s:%s %s %s:%s\x1B[0m\n\tat %s:%d\n", #a, (char *)a, (!strcmp(a,b))?"==":"!=", #b, b, __FILE__, __LINE__); _lasterr = 1; break; }
175 | #define EQUALS(a, b) if (a != b) { FAIL(a, b); }
176 | #define UEQUALS(a, b) if (a != b) { UFAIL(a, b); }
177 | #define FEQUALS(a, b) if (a != b) { FFAIL(a, b); }
178 | #define STREQUALS(a, b) if (!!strcmp(a,b)) { STRFAIL(a, b); }
179 | #define STRCEQUALS(a, b, c) if (!!strncmp(a,b, c)) { STRFAIL(a, b); }
180 | #define STRCNEQUALS(a, b, c) if (!strncmp(a,b, c)) { STRFAIL(a, b); }
181 | #define STRNEQUALS(a, b) if (!strcmp(a,b)) { STRFAIL(a, b); }
182 | #define NEQUALS(a, b) if (a == b) { FAIL(a, b); }
183 | #define LESSER(a, b) if (a >= b) { FAIL(a, b); }
184 | #define GREATER(a, b) if (a <=b) { FAIL(a, b); }
185 | #define LESSEREQ(a, b) if (a < b) { FAIL(a, b); }
186 | #define GREATEREQ(a, b) if (a > b) { FAIL(a, b); }
187 | #define SKIP() { _lasterr = UNIT_SKIP_MAGIC; break; }
188 |
189 | #if defined(__GCC__) || defined(__GNUC__) || defined(__clang__)
190 | #pragma GCC diagnostic push
191 | #pragma GCC diagnostic ignored "-Wattributes"
192 | #pragma GCC diagnostic ignored "-Wunused-value"
193 | #pragma GCC diagnostic ignored "-Wignored-qualifiers"
194 | #pragma GCC diagnostic ignored "-Wunused-variable"
195 | #pragma GCC diagnostic ignored "-Wunused-function"
196 | #pragma GCC diagnostic ignored "-Wwrite-strings"
197 | #pragma GCC diagnostic ignored "-Wunused-parameter"
198 | #pragma GCC diagnostic ignored "-Wdeprecated-declarations"
199 | #pragma GCC diagnostic ignored "-Wmissing-braces"
200 | #pragma GCC diagnostic ignored "-Wmissing-field-initializers"
201 | #pragma GCC diagnostic ignored "-Wpointer-to-int-cast"
202 | #endif
203 |
204 | #if defined(_MSC_VER)
205 | #pragma warning(push)
206 | #pragma warning(disable : 4201)
207 | #pragma warning(disable : 4127) // Conditional expression is constant
208 | #endif
209 |
210 | typedef int32_t (*unit_case)();
211 |
212 | #define UNIT_CREATE(name) \
213 | const char *unit_name = name; \
214 | unit_case unit_modules[UNIT_MAX_MODULES] = {0}; \
215 | int32_t unit_count = 0;
216 |
217 | #define UNIT_MODULE(name) \
218 | unit_modules[unit_count++] = UNIT_JOIN2(module__,name)
219 |
220 | #define UNIT_RUN() \
221 | unit_main(unit_name, unit_modules, unit_count)
222 |
223 | /* INTERNALS */
224 |
225 | UNIT_DEF int32_t _g_modules = 0;
226 | UNIT_DEF int32_t _g_modules_err = 0;
227 | UNIT_DEF int32_t _g_total = 0;
228 | UNIT_DEF int32_t _g_errors = 0;
229 |
230 | UNIT_DEF int32_t unit_main(const char *name, unit_case *cases, int32_t count) {
231 | int32_t err = 0, cnt = count;
232 | printf("> testing suite:\n\n");
233 | printf(" * suite: %s\n", name);
234 | printf(" * modules: %d\n", cnt);
235 | printf("\n");
236 |
237 | for (int32_t i = 0; i < count; ++i) {
238 | err += cases[i]();
239 | }
240 |
241 | fflush(stdout);
242 | printf("--------------------------------------\n"); \
243 | printf("> total:\n\n");
244 | printf(" * modules: %d total, %s%d failed\x1B[0m, %s%d passed\x1B[0m\n", _g_modules, _g_modules_err>0?"\x1B[31m":"" ,_g_modules_err, _g_modules_err==0?"\x1B[32m":"", _g_modules - _g_modules_err);
245 | printf(" * tests: %d total, %s%d failed\x1B[0m, %s%d passed\x1B[0m\n", _g_total, _g_errors>0?"\x1B[31m":"" ,_g_errors, _g_errors==0?"\x1B[32m":"", _g_total - _g_errors);
246 | printf("\n");
247 |
248 | return -err;
249 | }
250 |
251 | // Locally persisting buffer
252 | static inline char* unit__bprintf(const char* fmt, ...)
253 | {
254 | static char buf[128];
255 | va_list args;
256 | va_start(args, fmt);
257 | vsprintf(buf, fmt, args);
258 | va_end(args);
259 | return buf;
260 | }
261 |
262 | #if defined(ZPL_COMPILER_MSVC)
263 | #pragma warning(pop)
264 | #endif
265 |
266 | #if defined(__GCC__) || defined(__GNUC__) || defined(__clang__)
267 | #pragma GCC diagnostic pop
268 | #endif
269 |
--------------------------------------------------------------------------------