├── .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 | ![pntr logo](logo-128x128.png) 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 | [![Example: Image](examples/pntr_examples_image.png)](examples/pntr_examples_image.h) 25 | [![Example: Shapes](examples/pntr_examples_shapes.png)](examples/pntr_examples_shapes.h) 26 | [![Example: Fonts](examples/pntr_examples_fonts.png)](examples/pntr_examples_fonts.h) 27 | [![Example: Sprite](examples/pntr_examples_sprite.png)](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 | --------------------------------------------------------------------------------