pntr
Image manipulation library
pntr.h
1 
52 #ifndef PNTR_H__
53 #define PNTR_H__
54 
55 #include <stdint.h> // uint32_t
56 #include <stdbool.h> // bool
57 #include <stddef.h> // size_t
58 
84 #ifdef _DOXYGEN_
104  #define PNTR_IMPLEMENTATION
105 
111  #define PNTR_PIXELFORMAT_RGBA
112 
118  #define PNTR_PIXELFORMAT_ARGB
119 
125  #define PNTR_ENABLE_DEFAULT_FONT
126 
133  #define PNTR_ENABLE_TTF
134 
142  #define PNTR_ENABLE_UTF8
143 
153  #define PNTR_SAVE_IMAGE_TO_MEMORY
154 
164  #define PNTR_LOAD_IMAGE_FROM_MEMORY
165 
169  #define PNTR_ENABLE_MATH
170 
177  #define PNTR_LOAD_FILE
178 
185  #define PNTR_SAVE_FILE
186 
198  #define PNTR_STB_IMAGE
199 
211  #define PNTR_CUTE_PNG
212 
218  #define PNTR_NO_ALPHABLEND
219 
226  #define PNTR_NO_STDIO
227 
233  #define PNTR_NO_LOAD_IMAGE
234 
240  #define PNTR_NO_SAVE_IMAGE
241 
245  #define PNTR_NO_CUTE_PNG_IMPLEMENTATION
246 
250  #define PNTR_NO_STB_IMAGE_WRITE_IMPLEMENTATION
251 
255  #define PNTR_NO_STB_IMAGE_IMPLEMENTATION
256 
260  #define PNTR_NO_STB_TRUETYPE_IMPLEMENTATION
261 
265 #endif
266 
267 #ifndef PNTR_API
275  #define PNTR_API
276 #endif
277 
278 // Pixel Format. Default to PNTR_PIXELFORMAT_RGBA
279 #if !defined(PNTR_PIXELFORMAT_RGBA) && !defined(PNTR_PIXELFORMAT_ARGB)
280  #define PNTR_PIXELFORMAT_RGBA
281 #endif
282 #if defined(PNTR_PIXELFORMAT_RGBA) && defined(PNTR_PIXELFORMAT_ARGB)
283  #undef PNTR_PIXELFORMAT_ARGB
284 #endif
285 
286 #ifdef PNTR_PIXELFORMAT
287  #undef PNTR_PIXELFORMAT
288 #endif
289 #ifdef PNTR_PIXELFORMAT_RGBA
298  #define PNTR_PIXELFORMAT PNTR_PIXELFORMAT_RGBA8888
299 #elif defined(PNTR_PIXELFORMAT_ARGB)
300  #define PNTR_PIXELFORMAT PNTR_PIXELFORMAT_ARGB8888
301 #endif
302 
314 typedef union pntr_color {
318  uint32_t value;
319 
329  #if defined(PNTR_PIXELFORMAT_RGBA)
333  unsigned char r;
334 
338  unsigned char g;
339 
343  unsigned char b;
344 
348  unsigned char a;
349  #elif defined(PNTR_PIXELFORMAT_ARGB)
353  unsigned char b;
354 
358  unsigned char g;
359 
363  unsigned char r;
364 
368  unsigned char a;
369  #endif
370  } rgba;
372 
376 typedef struct pntr_rectangle {
380  int x;
381 
385  int y;
386 
390  int width;
391 
395  int height;
397 
404 typedef struct pntr_image {
409 
413  int width;
414 
418  int height;
419 
423  int pitch;
424 
430  bool subimage;
431 
441 
445 typedef struct pntr_vector {
449  int x;
450 
454  int y;
456 
467 typedef struct pntr_font {
472 
477 
482 
486  char* characters;
487 
493 
497 typedef enum pntr_pixelformat {
502 
507 
513 
517 typedef enum pntr_filter {
524 
532 
539 typedef enum pntr_error {
544 
549 
554 
559 
564 
569 
573  PNTR_ERROR_UNKNOWN = -6
575 
579 typedef enum pntr_image_type {
597 
598 #ifdef __cplusplus
599 extern "C" {
600 #endif
601 
602 PNTR_API pntr_image* pntr_new_image(int width, int height);
603 PNTR_API pntr_image* pntr_gen_image_color(int width, int height, pntr_color color);
605 PNTR_API pntr_image* pntr_image_from_image(pntr_image* image, int x, int y, int width, int height);
606 PNTR_API pntr_image* pntr_image_subimage(pntr_image* image, int x, int y, int width, int height);
608 PNTR_API void pntr_image_set_clip(pntr_image* image, int x, int y, int width, int height);
613 PNTR_API void pntr_draw_point(pntr_image* dst, int x, int y, pntr_color color);
614 PNTR_API void pntr_draw_point_vec(pntr_image* dst, pntr_vector* point, pntr_color color);
615 PNTR_API void pntr_draw_points(pntr_image* dst, pntr_vector* points, int pointsCount, pntr_color color);
616 PNTR_API void pntr_draw_line(pntr_image* dst, int startPosX, int startPosY, int endPosX, int endPosY, pntr_color color);
617 PNTR_API void pntr_draw_line_vec(pntr_image* dst, pntr_vector start, pntr_vector end, pntr_color color);
618 PNTR_API void pntr_draw_line_vertical(pntr_image* dst, int posX, int posY, int height, pntr_color color);
619 PNTR_API void pntr_draw_line_horizontal(pntr_image* dst, int posX, int posY, int width, pntr_color color);
620 PNTR_API void pntr_draw_rectangle(pntr_image* dst, int posX, int posY, int width, int height, pntr_color color);
622 PNTR_API void pntr_draw_rectangle_thick(pntr_image* dst, int posX, int posY, int width, int height, int thickness, pntr_color color);
623 PNTR_API void pntr_draw_rectangle_thick_rec(pntr_image* dst, pntr_rectangle rect, int thickness, pntr_color color);
624 PNTR_API void pntr_draw_rectangle_fill(pntr_image* dst, int posX, int posY, int width, int height, pntr_color color);
626 PNTR_API 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);
627 PNTR_API void pntr_draw_rectangle_gradient_rec(pntr_image* dst, pntr_rectangle rect, pntr_color topLeft, pntr_color topRight, pntr_color bottomLeft, pntr_color bottomRight);
628 PNTR_API void pntr_draw_triangle(pntr_image* dst, int x1, int y1, int x2, int y2, int x3, int y3, pntr_color color);
630 PNTR_API void pntr_draw_triangle_fill(pntr_image* dst, int x1, int y1, int x2, int y2, int x3, int y3, pntr_color color);
632 PNTR_API void pntr_draw_ellipse(pntr_image* dst, int centerX, int centerY, int radiusX, int radiusY, pntr_color color);
633 PNTR_API void pntr_draw_ellipse_fill(pntr_image* dst, int centerX, int centerY, int radiusX, int radiusY, pntr_color color);
634 PNTR_API void pntr_draw_circle(pntr_image* dst, int centerX, int centerY, int radius, pntr_color color);
635 PNTR_API void pntr_draw_circle_fill(pntr_image* dst, int centerX, int centerY, int radius, pntr_color color);
636 PNTR_API void pntr_draw_polygon(pntr_image* dst, pntr_vector* points, int numPoints, pntr_color color);
637 PNTR_API void pntr_draw_polygon_fill(pntr_image* dst, pntr_vector* points, int numPoints, pntr_color color);
638 PNTR_API void pntr_draw_polyline(pntr_image* dst, pntr_vector* points, int numPoints, pntr_color color);
639 PNTR_API void pntr_draw_arc(pntr_image* dst, int centerX, int centerY, float radius, float startAngle, float endAngle, int segments, pntr_color color);
640 PNTR_API void pntr_draw_arc_fill(pntr_image* dst, int centerX, int centerY, float radius, float startAngle, float endAngle, int segments, pntr_color color);
641 PNTR_API 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);
642 PNTR_API void pntr_draw_rectangle_rounded_fill(pntr_image* dst, int x, int y, int width, int height, int cornerRadius, pntr_color color);
643 PNTR_API void pntr_draw_image(pntr_image* dst, pntr_image* src, int posX, int posY);
644 PNTR_API void pntr_draw_image_rec(pntr_image* dst, pntr_image* src, pntr_rectangle srcRect, int posX, int posY);
645 PNTR_API void pntr_draw_image_tint(pntr_image* dst, pntr_image* src, int posX, int posY, pntr_color tint);
646 PNTR_API void pntr_draw_image_tint_rec(pntr_image* dst, pntr_image* src, pntr_rectangle srcRect, int posX, int posY, pntr_color tint);
647 PNTR_API void pntr_draw_image_rotated(pntr_image* dst, pntr_image* src, int posX, int posY, float degrees, float offsetX, float offsetY, pntr_filter filter);
648 PNTR_API 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);
649 PNTR_API void pntr_draw_image_flipped(pntr_image* dst, pntr_image* src, int posX, int posY, bool flipHorizontal, bool flipVertical, bool flipDiagonal);
650 PNTR_API 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);
651 PNTR_API 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);
652 PNTR_API 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);
653 PNTR_API void pntr_draw_text(pntr_image* dst, pntr_font* font, const char* text, int posX, int posY, pntr_color color);
654 PNTR_API void pntr_draw_text_wrapped(pntr_image* dst, pntr_font* font, const char* text, int posX, int posY, int maxWidth, pntr_color tint);
655 #ifdef PNTR_ENABLE_VARGS
656 PNTR_API void pntr_draw_text_ex(pntr_image* dst, pntr_font* font, int posX, int posY, pntr_color tint, const char* text, ...);
657 #endif
658 PNTR_API pntr_color pntr_new_color(unsigned char r, unsigned char g, unsigned char b, unsigned char a);
659 PNTR_API pntr_color pntr_get_color(unsigned int hexValue);
660 PNTR_API unsigned char pntr_color_r(pntr_color color);
661 PNTR_API unsigned char pntr_color_g(pntr_color color);
662 PNTR_API unsigned char pntr_color_b(pntr_color color);
663 PNTR_API unsigned char pntr_color_a(pntr_color color);
664 PNTR_API void pntr_color_set_r(pntr_color* color, unsigned char r);
665 PNTR_API void pntr_color_set_g(pntr_color* color, unsigned char g);
666 PNTR_API void pntr_color_set_b(pntr_color* color, unsigned char b);
667 PNTR_API void pntr_color_set_a(pntr_color* color, unsigned char a);
668 PNTR_API pntr_color pntr_image_get_color(pntr_image* image, int x, int y);
669 PNTR_API bool pntr_save_file(const char *fileName, const void *data, unsigned int bytesToWrite);
670 PNTR_API void* pntr_image_to_pixelformat(pntr_image* image, unsigned int* dataSize, pntr_pixelformat pixelFormat);
671 PNTR_API bool pntr_save_image(pntr_image* image, const char* fileName);
672 PNTR_API unsigned char* pntr_save_image_to_memory(pntr_image* image, pntr_image_type type, unsigned int* dataSize);
673 PNTR_API int pntr_get_pixel_data_size(int width, int height, pntr_pixelformat pixelFormat);
674 PNTR_API pntr_image* pntr_load_image(const char* fileName);
675 PNTR_API pntr_image* pntr_load_image_from_memory(pntr_image_type type, const unsigned char* fileData, unsigned int dataSize);
676 PNTR_API pntr_image* pntr_image_from_pixelformat(const void* data, int width, int height, pntr_pixelformat pixelFormat);
677 PNTR_API void* pntr_set_error(pntr_error error);
678 PNTR_API const char* pntr_get_error(void);
679 PNTR_API pntr_error pntr_get_error_code(void);
680 PNTR_API pntr_image* pntr_image_resize(pntr_image* image, int newWidth, int newHeight, pntr_filter filter);
681 PNTR_API pntr_image* pntr_image_scale(pntr_image* image, float scaleX, float scaleY, pntr_filter filter);
685 PNTR_API pntr_color pntr_color_fade(pntr_color color, float alpha);
686 PNTR_API void pntr_image_color_fade(pntr_image* image, float alpha);
688 PNTR_API pntr_color pntr_get_pixel_color(void* srcPtr, pntr_pixelformat srcPixelFormat);
689 PNTR_API void pntr_set_pixel_color(void* dstPtr, pntr_pixelformat dstPixelFormat, pntr_color color);
693 PNTR_API pntr_font* pntr_font_scale(pntr_font* font, float scaleX, float scaleY, pntr_filter filter);
694 PNTR_API pntr_font* pntr_load_font_bmf(const char* fileName, const char* characters);
695 PNTR_API pntr_font* pntr_load_font_bmf_from_image(pntr_image* image, const char* characters);
696 PNTR_API pntr_font* pntr_load_font_bmf_from_memory(const unsigned char* fileData, unsigned int dataSize, const char* characters);
697 PNTR_API int pntr_measure_text(pntr_font* font, const char* text);
698 PNTR_API pntr_vector pntr_measure_text_ex(pntr_font* font, const char* text, int textLength);
699 PNTR_API pntr_image* pntr_gen_image_text(pntr_font* font, const char* text, pntr_color tint);
700 PNTR_API pntr_font* pntr_load_font_tty(const char* fileName, int glyphWidth, int glyphHeight, const char* characters);
701 PNTR_API pntr_font* pntr_load_font_tty_from_memory(const unsigned char* fileData, unsigned int dataSize, int glyphWidth, int glyphHeight, const char* characters);
702 PNTR_API pntr_font* pntr_load_font_tty_from_image(pntr_image* image, int glyphWidth, int glyphHeight, const char* characters);
703 PNTR_API unsigned char* pntr_load_file(const char *fileName, unsigned int *bytesRead);
704 PNTR_API void pntr_unload_file(unsigned char* fileData);
705 PNTR_API const char* pntr_load_file_text(const char *fileName);
706 PNTR_API void pntr_unload_file_text(const char* text);
707 PNTR_API pntr_font* pntr_load_font_ttf(const char* fileName, int fontSize);
708 PNTR_API pntr_font* pntr_load_font_ttf_from_memory(const unsigned char* fileData, unsigned int dataSize, int fontSize);
713 PNTR_API bool pntr_image_crop(pntr_image* image, int x, int y, int width, int height);
714 PNTR_API void pntr_image_alpha_crop(pntr_image* image, float threshold);
715 PNTR_API void pntr_image_color_brightness(pntr_image* image, float factor);
716 PNTR_API void pntr_image_flip(pntr_image* image, bool horizontal, bool vertical);
717 PNTR_API pntr_color pntr_color_contrast(pntr_color color, float contrast);
718 PNTR_API void pntr_image_color_contrast(pntr_image* image, float contrast);
719 PNTR_API void pntr_image_alpha_mask(pntr_image* image, pntr_image* alphaMask, int posX, int posY);
720 PNTR_API bool pntr_image_resize_canvas(pntr_image* image, int newWidth, int newHeight, int offsetX, int offsetY, pntr_color fill);
721 PNTR_API pntr_image* pntr_image_rotate(pntr_image* image, float degrees, pntr_filter filter);
722 PNTR_API pntr_image* pntr_gen_image_gradient(int width, int height, pntr_color topLeft, pntr_color topRight, pntr_color bottomLeft, pntr_color bottomRight);
723 PNTR_API pntr_color pntr_color_bilinear_interpolate(pntr_color color00, pntr_color color01, pntr_color color10, pntr_color color11, float coordinateX, float coordinateY);
724 PNTR_API void* pntr_load_memory(size_t size);
725 PNTR_API void pntr_unload_memory(void* pointer);
726 PNTR_API void* pntr_memory_copy(void* destination, void* source, size_t size);
728 
729 // Internal
730 PNTR_API void pntr_put_horizontal_line_unsafe(pntr_image* dst, int posX, int posY, int width, pntr_color color);
731 PNTR_API void pntr_draw_point_unsafe(pntr_image* dst, int x, int y, pntr_color color);
732 
733 #ifdef __cplusplus
734 }
735 #endif
736 
742 #ifndef PNTR_LIGHTGRAY
746 #define PNTR_LIGHTGRAY pntr_new_color(200, 200, 200, 255)
747 #endif
748 #ifndef PNTR_GRAY
752 #define PNTR_GRAY pntr_new_color(130, 130, 130, 255)
753 #endif
754 #ifndef PNTR_DARKGRAY
758 #define PNTR_DARKGRAY pntr_new_color(80, 80, 80, 255)
759 #endif
760 #ifndef PNTR_YELLOW
764 #define PNTR_YELLOW pntr_new_color(253, 249, 0, 255)
765 #endif
766 #ifndef PNTR_GOLD
770 #define PNTR_GOLD pntr_new_color(255, 203, 0, 255)
771 #endif
772 #ifndef PNTR_ORANGE
776 #define PNTR_ORANGE pntr_new_color(255, 161, 0, 255)
777 #endif
778 #ifndef PNTR_PINK
782 #define PNTR_PINK pntr_new_color(255, 109, 194, 255)
783 #endif
784 #ifndef PNTR_RED
788 #define PNTR_RED pntr_new_color(230, 41, 55, 255)
789 #endif
790 #ifndef PNTR_MAROON
794 #define PNTR_MAROON pntr_new_color(190, 33, 55, 255)
795 #endif
796 #ifndef PNTR_GREEN
800 #define PNTR_GREEN pntr_new_color(0, 228, 48, 255)
801 #endif
802 #ifndef PNTR_LIME
806 #define PNTR_LIME pntr_new_color(0, 158, 47, 255)
807 #endif
808 #ifndef PNTR_DARKGREEN
812 #define PNTR_DARKGREEN pntr_new_color(0, 117, 44, 255)
813 #endif
814 #ifndef PNTR_SKYBLUE
818 #define PNTR_SKYBLUE pntr_new_color(102, 191, 255, 255)
819 #endif
820 #ifndef PNTR_BLUE
824 #define PNTR_BLUE pntr_new_color(0, 121, 241, 255)
825 #endif
826 #ifndef PNTR_DARKBLUE
830 #define PNTR_DARKBLUE pntr_new_color(0, 82, 172, 255)
831 #endif
832 #ifndef PNTR_PURPLE
836 #define PNTR_PURPLE pntr_new_color(200, 122, 255, 255)
837 #endif
838 #ifndef PNTR_VIOLET
842 #define PNTR_VIOLET pntr_new_color(135, 60, 190, 255)
843 #endif
844 #ifndef PNTR_DARKPURPLE
848 #define PNTR_DARKPURPLE pntr_new_color(112, 31, 126, 255)
849 #endif
850 #ifndef PNTR_BEIGE
854 #define PNTR_BEIGE pntr_new_color(211, 176, 131, 255)
855 #endif
856 #ifndef PNTR_BROWN
860 #define PNTR_BROWN pntr_new_color(127, 106, 79, 255)
861 #endif
862 #ifndef PNTR_DARKBROWN
866 #define PNTR_DARKBROWN pntr_new_color(76, 63, 47, 255)
867 #endif
868 #ifndef PNTR_WHITE
872 #define PNTR_WHITE pntr_new_color(255, 255, 255, 255)
873 #endif
874 
875 #ifndef PNTR_WHITE_VALUE
884 #define PNTR_WHITE_VALUE 4294967295
885 #endif // PNTR_WHITE_VALUE
886 
887 #ifndef PNTR_BLACK
891 #define PNTR_BLACK pntr_new_color(0, 0, 0, 255)
892 #endif
893 #ifndef PNTR_BLANK
897 #define PNTR_BLANK pntr_new_color(0, 0, 0, 0)
898 #endif
899 #ifndef PNTR_MAGENTA
903 #define PNTR_MAGENTA pntr_new_color(255, 0, 255, 255)
904 #endif
905 #ifndef PNTR_RAYWHITE
909 #define PNTR_RAYWHITE pntr_new_color(245, 245, 245, 255)
910 #endif
911 
916 #endif // PNTR_H__
917 
918 #ifdef PNTR_IMPLEMENTATION
919 #ifndef PNTR_IMPLEMENTATION_ONCE
920 #define PNTR_IMPLEMENTATION_ONCE
921 
922 #ifdef __cplusplus
923 extern "C" {
924 #endif
925 
931 #ifndef PNTR_MALLOC
932  #include <stdlib.h>
942  #define PNTR_MALLOC(size) malloc(size)
943 #endif // PNTR_MALLOC
944 
945 #ifndef PNTR_FREE
946  #include <stdlib.h>
955  #define PNTR_FREE(ptr) free(ptr)
956 #endif // PNTR_FREE
957 
958 #ifndef PNTR_REALLOC
959  #include <stdlib.h>
970  #define PNTR_REALLOC(ptr, new_size) realloc(ptr, new_size)
971 #endif // PNTR_REALLOC
972 
973 #ifndef PNTR_MEMCPY
974  #include <string.h>
984  #define PNTR_MEMCPY(dest, src, n) memcpy(dest, src, n)
985 #endif // PNTR_MEMCPY
986 
987 #ifndef PNTR_MEMSET
988  #include <string.h>
992  #define PNTR_MEMSET(str, c, n) memset((str), (c), (n))
993 #endif // PNTR_MEMSET
994 
1004 #if defined(PNTR_ENABLE_UTF8) && !defined(_DOXYGEN_)
1005  #include "external/utf8.h"
1006  #define PNTR_STRSTR utf8str
1007  #define PNTR_STRCHR utf8chr
1008  #define PNTR_STRLEN utf8len
1009  #define PNTR_STRSIZE utf8size
1010  #define PNTR_STRCODEPOINT utf8codepoint
1011  typedef utf8_int32_t pntr_codepoint_t;
1012 #else
1020  typedef char pntr_codepoint_t;
1021 #endif
1022 
1023 #ifndef PNTR_STRSTR
1024  #include <string.h>
1037  #define PNTR_STRSTR strstr
1038 #endif
1039 
1040 #ifndef PNTR_STRCHR
1041  #include <string.h>
1049  #define PNTR_STRCHR strchr
1050 #endif
1051 
1052 #ifndef PNTR_STRLEN
1053  #include <string.h>
1061  #define PNTR_STRLEN strlen
1062 #endif
1063 
1064 #ifndef PNTR_STRSIZE
1065  #include <string.h>
1073  #define PNTR_STRSIZE(text) ((PNTR_STRLEN(text) + (size_t)1))
1074 #endif
1075 
1076 #ifndef PNTR_STRCODEPOINT
1083  const char* pntr_strcodepoint(const char * str, char* out_codepoint) {
1084  if (str == NULL) {
1085  *out_codepoint = 0;
1086  }
1087 
1088  char character = str[0];
1089  *out_codepoint = character;
1090  return str + 1;
1091  }
1092 
1101  #define PNTR_STRCODEPOINT pntr_strcodepoint
1102 #endif
1103 
1113 #ifndef PNTR_PI
1119  #define PNTR_PI 3.1415926535897932f
1120 #endif
1121 
1122 #ifndef PNTR_DEG2RAD
1130  #define PNTR_DEG2RAD 0.017453293f
1131 #endif
1132 
1133 #if !defined(PNTR_ENABLE_MATH) || defined(_DOXYGEN_)
1134  #ifndef PNTR_SINF
1135  float _pntr_sinf(float x) {
1136  static const float a0 = +1.91059300966915117e-31f;
1137  static const float a1 = +1.00086760103908896f;
1138  static const float a2 = -1.21276126894734565e-2f;
1139  static const float a3 = -1.38078780785773762e-1f;
1140  static const float a4 = -2.67353392911981221e-2f;
1141  static const float a5 = +2.08026600266304389e-2f;
1142  static const float a6 = -3.03996055049204407e-3f;
1143  static const float a7 = +1.38235642404333740e-4f;
1144  return a0 + x*(a1 + x*(a2 + x*(a3 + x*(a4 + x*(a5 + x*(a6 + x*a7))))));
1145  }
1154  #define PNTR_SINF _pntr_sinf
1155  #endif // PNTR_SINF
1156 
1157  #ifndef PNTR_COSF
1158  float _pntr_cosf(float x) {
1159  static const float a0 = 9.9995999154986614e-1f;
1160  static const float a1 = 1.2548995793001028e-3f;
1161  static const float a2 = -5.0648546280678015e-1f;
1162  static const float a3 = 1.2942246466519995e-2f;
1163  static const float a4 = 2.8668384702547972e-2f;
1164  static const float a5 = 7.3726485210586547e-3f;
1165  static const float a6 = -3.8510875386947414e-3f;
1166  static const float a7 = 4.7196604604366623e-4f;
1167  static const float a8 = -1.8776444013090451e-5f;
1168  return a0 + x*(a1 + x*(a2 + x*(a3 + x*(a4 + x*(a5 + x*(a6 + x*(a7 + x*a8)))))));
1169  }
1178  #define PNTR_COSF _pntr_cosf
1179  #endif // PNTR_COSF
1180 
1181  #ifndef PNTR_CEILF
1182  float _pntr_ceilf(float x) {
1183  if (x >= 0.0f) {
1184  int i = (int)x;
1185  return (x > i) ? (float)i + 1.0f : (float)i;
1186  } else {
1187  int t = (int)x;
1188  float r = x - (float)t;
1189  return (r > 0.0f) ? (float)t + 1.0f: (float)t;
1190  }
1191  }
1199  #define PNTR_CEILF _pntr_ceilf
1200  #endif // PNTR_CEILF
1201 
1202  #ifndef PNTR_FABSF
1210  #define PNTR_FABSF(a) (((a) < 0) ? -(a) : (a))
1211  #endif // PNTR_FABSF
1212 
1213  #ifndef PNTR_FLOORF
1221  #define PNTR_FLOORF(x) (float)((int)x - ((x < 0.0f) ? 1 : 0))
1222  #endif // PNTR_FLOORF
1223 
1224  #ifndef PNTR_FMODF
1225  float _pntr_fmodf(float dividend, float divisor) {
1226  if (divisor == 0.0f) {
1227  return 0.0f;
1228  }
1229  float quotient = dividend / divisor;
1230  return dividend - ((int)quotient) * divisor;
1231  }
1240  #define PNTR_FMODF _pntr_fmodf
1241  #endif // PNTR_FMOD
1242 #else
1243  #ifndef PNTR_SINF
1244  #include <math.h>
1245  #define PNTR_SINF sinf
1246  #endif // PNTR_SINF
1247 
1248  #ifndef PNTR_COSF
1249  #include <math.h>
1250  #define PNTR_COSF cosf
1251  #endif // PNTR_COSF
1252 
1253  #ifndef PNTR_CEILF
1254  #include <math.h>
1255  #define PNTR_CEILF ceilf
1256  #endif // PNTR_CEILF
1257 
1258  #ifndef PNTR_FABSF
1259  #include <math.h>
1260  #define PNTR_FABSF fabsf
1261  #endif // PNTR_FABSF
1262 
1263  #ifndef PNTR_FLOORF
1264  #include <math.h>
1265  #define PNTR_FLOORF floorf
1266  #endif // PNTR_FLOORF
1267 
1268  #ifndef PNTR_SQRTF
1269  #include <math.h>
1270  #define PNTR_SQRTF sqrtf
1271  #endif // PNTR_SQRTF
1272 
1273  #ifndef PNTR_FMODF
1274  #include <math.h>
1275  #define PNTR_FMODF fmodf
1276  #endif // PNTR_FMODF
1277 #endif // PNTR_ENABLE_MATH
1278 
1279 #ifndef PNTR_CLITERAL
1280 #define PNTR_CLITERAL(type) (type)
1281 #endif
1282 
1283 #ifndef PNTR_MAX
1292  #define PNTR_MAX(a, b) ((a) > (b) ? (a) : (b))
1293 #endif
1294 
1295 #ifndef PNTR_MIN
1304  #define PNTR_MIN(a, b) ((a) < (b) ? (a) : (b))
1305 #endif
1306 
1311 // STB TrueType
1312 #ifdef PNTR_ENABLE_TTF
1313  #ifdef PNTR_NO_STB_TRUETYPE_IMPLEMENTATION
1314  #ifdef STB_TRUETYPE_IMPLEMENTATION
1315  #undef STB_TRUETYPE_IMPLEMENTATION
1316  #endif // STB_TRUETYPE_IMPLEMENTATION
1317  #else // PNTR_NO_STB_TRUETYPE_IMPLEMENTATION
1318 
1319  #ifndef STBTT_ifloor
1320  #define STBTT_ifloor(x) ((int)PNTR_FLOORF(x))
1321  #endif
1322 
1323  #ifndef STBTT_iceil
1324  #define STBTT_iceil(x) ((int)PNTR_CEILF(x))
1325  #endif
1326 
1327  #ifndef STBTT_fmod
1328  #define STBTT_fmod(x, y) PNTR_FMODF((x), (y))
1329  #endif
1330 
1331  #ifndef STBTT_cos
1332  #define STBTT_cos(x) PNTR_COSF((float)(x))
1333  #endif
1334 
1335  #ifndef PNTR_ENABLE_MATH
1336  #ifndef STBTT_sqrt
1337  float _pntr_sqrtf(float number) {
1338  float guess = number / 2.0f;
1339  float epsilon = 1e-6f;
1340  while (true) {
1341  float next_guess = 0.5f * (guess + number / guess);
1342  if (PNTR_FABSF(next_guess - guess) < epsilon) {
1343  return next_guess;
1344  }
1345  guess = next_guess;
1346  }
1347  }
1348  #define STBTT_sqrt(x) _pntr_sqrtf(x)
1349  #endif // PNTR_SQRTF
1350 
1351  #ifndef STBTT_pow
1352  float _pntr_pow(float base, float exponent) {
1353  float result = 1.0f;
1354  if (exponent >= 0) {
1355  for (int i = 0; i < exponent; i++) {
1356  result *= base;
1357  }
1358  } else {
1359  for (int i = 0; i > exponent; i--) {
1360  result /= base;
1361  }
1362  }
1363  return result;
1364  }
1365  #define STBTT_pow(x, y) _pntr_pow((x), (y))
1366  #endif
1367 
1368  #ifndef STBTT_acos
1369  float _pntr_acos(float x) {
1370  float negate = (float)(x < 0);
1371  x = PNTR_FABSF(x);
1372  float ret = -0.0187293f;
1373  ret = ret * x;
1374  ret = ret + 0.0742610f;
1375  ret = ret * x;
1376  ret = ret - 0.2121144f;
1377  ret = ret * x;
1378  ret = ret + 1.5707288f;
1379  ret = ret * STBTT_sqrt(1.0f - x);
1380  ret = ret - 2 * negate * ret;
1381  return negate * PNTR_PI + ret;
1382  }
1383  #define STBTT_acos(x) _pntr_acos((x))
1384  #endif
1385  #else
1386  #ifndef STBTT_sqrt
1387  #define STBTT_sqrt(x) sqrt(x)
1388  #endif
1389  #ifndef STBTT_pow
1390  #define STBTT_pow(x, y) pow(x, y)
1391  #endif
1392  #ifndef STBTT_acos
1393  #define STBTT_acos(x) acos(x)
1394  #endif
1395  #endif
1396 
1397  #ifndef STBTT_malloc
1398  #define STBTT_malloc(x,u) ((void)(u), PNTR_MALLOC(x))
1399  #endif // STBTT_malloc
1400 
1401  #ifndef STBTT_free
1402  #define STBTT_free(x,u) ((void)(u), PNTR_FREE(x))
1403  #endif // STBTT_free
1404 
1405  #ifndef STBTT_assert
1406  #define STBTT_assert(x) ((void)(0))
1407  #endif // STBTT_assert
1408 
1409  #ifndef STBTT_strlen
1410  #define STBTT_strlen(x) PNTR_STRLEN(x)
1411  #endif // STBTT_strlen
1412 
1413  #ifndef STBTT_memcpy
1414  #define STBTT_memcpy PNTR_MEMCPY
1415  #endif // STBTT_memcpy
1416 
1417  #ifndef STBTT_memset
1418  #define STBTT_memset PNTR_MEMSET
1419  #endif // STBTT_memset
1420 
1421  #define STB_TRUETYPE_IMPLEMENTATION
1422  #endif // PNTR_NO_STB_TRUETYPE_IMPLEMENTATION
1423 
1424  #if defined(__GNUC__) || defined(__clang__)
1425  #pragma GCC diagnostic push
1426  #pragma GCC diagnostic ignored "-Wpragmas"
1427  #pragma GCC diagnostic ignored "-Wunknown-pragmas"
1428  #pragma GCC diagnostic ignored "-Wsign-conversion"
1429  #pragma GCC diagnostic ignored "-Wconversion"
1430  #endif // defined(__GNUC__) || defined(__clang__)
1431 
1432  #include "external/stb_truetype.h"
1433  #define PNTR_NO_STB_TRUETYPE_IMPLEMENTATION
1434 
1435  #if defined(__GNUC__) || defined(__clang__)
1436  #pragma GCC diagnostic pop
1437  #endif // defined(__GNUC__) || defined(__clang__)
1438 #endif // PNTR_ENABLE_TTF
1439 
1440 #ifdef PNTR_ENABLE_VARGS
1441  // For pntr_draw_text_ex()
1442  #include <stdarg.h> // va_list, va_start, va_end
1443  #include <stdio.h> // vsprintf
1444 #endif
1445 
1455 #define PNTR_PIXEL(image, x, y) image->data[(y) * (image->pitch >> 2) + (x)]
1456 
1467 pntr_error _pntr_error;
1468 
1469 PNTR_API const char* pntr_get_error(void) {
1470  switch (_pntr_error) {
1471  case PNTR_ERROR_NONE: return NULL;
1472  case PNTR_ERROR_INVALID_ARGS: return "Invalid arguments";
1473  case PNTR_ERROR_NO_MEMORY: return "No memory";
1474  case PNTR_ERROR_NOT_SUPPORTED: return "Not supported";
1475  case PNTR_ERROR_FAILED_TO_OPEN: return "Failed to open";
1476  case PNTR_ERROR_FAILED_TO_WRITE: return "Failed to write";
1477  case PNTR_ERROR_UNKNOWN: return "Unknown error";
1478  }
1479 
1480  return NULL;
1481 }
1482 
1483 PNTR_API pntr_error pntr_get_error_code(void) {
1484  return _pntr_error;
1485 }
1486 
1495  _pntr_error = error;
1496 
1497  #ifdef PNTR_SET_ERROR
1498  PNTR_SET_ERROR(error);
1499  #endif
1500 
1501  return NULL;
1502 }
1503 
1516 PNTR_API pntr_image* pntr_new_image(int width, int height) {
1517  if (width <= 0 || height <= 0) {
1519  }
1520 
1521  pntr_image* image = (pntr_image*)PNTR_MALLOC(sizeof(pntr_image));
1522  if (image == NULL) {
1524  }
1525 
1526  image->pitch = width * (int)sizeof(pntr_color);
1527  image->width = width;
1528  image->height = height;
1529  pntr_image_reset_clip(image);
1530  image->subimage = false;
1531  image->data = (pntr_color*)PNTR_MALLOC((size_t)(image->pitch * height));
1532  if (image->data == NULL) {
1533  PNTR_FREE(image);
1535  }
1536 
1537  return image;
1538 }
1539 
1549 PNTR_API pntr_image* pntr_gen_image_color(int width, int height, pntr_color color) {
1550  pntr_image* image = pntr_new_image(width, height);
1551  pntr_clear_background(image, color);
1552 
1553  return image;
1554 }
1555 
1564  if (image == NULL) {
1566  }
1567 
1568  pntr_image* newImage = pntr_gen_image_color(image->width, image->height, PNTR_BLANK);
1569  if (newImage == NULL) {
1570  return NULL;
1571  }
1572 
1573  pntr_draw_image(newImage, image, 0, 0);
1574  newImage->clip = image->clip;
1575 
1576  return newImage;
1577 }
1578 
1588 PNTR_API
1589 #ifdef PNTR_NO_ALPHABLEND
1590 inline
1591 #endif
1593  if (src.rgba.a == 255) {
1594  *dst = src;
1595  return;
1596  }
1597  #ifndef PNTR_NO_ALPHABLEND
1598  if (src.rgba.a == 0) {
1599  return;
1600  }
1601 
1602  unsigned int alpha = (unsigned int)src.rgba.a + 1; // We are shifting by 8 (dividing by 256), so we need to take that excess into account
1603  unsigned int dstAlpha = (unsigned int)dst->rgba.a * (256 - alpha);
1604  dst->rgba.a = (unsigned char)((alpha * 256 + dstAlpha) >> 8);
1605 
1606  if (dst->rgba.a > 0) {
1607  dst->rgba.r = (unsigned char)((((unsigned int)src.rgba.r * alpha * 256 + (unsigned int)dst->rgba.r * dstAlpha) / dst->rgba.a) >> 8);
1608  dst->rgba.g = (unsigned char)((((unsigned int)src.rgba.g * alpha * 256 + (unsigned int)dst->rgba.g * dstAlpha) / dst->rgba.a) >> 8);
1609  dst->rgba.b = (unsigned char)((((unsigned int)src.rgba.b * alpha * 256 + (unsigned int)dst->rgba.b * dstAlpha) / dst->rgba.a) >> 8);
1610  }
1611  #endif
1612 }
1613 
1636 PNTR_API bool _pntr_rectangle_intersect(int x, int y, int width, int height, int destX, int destY, int destWidth, int destHeight, pntr_rectangle *out) {
1637  if (width <= 0 || height <= 0) {
1638  return false;
1639  }
1640 
1641  out->x = PNTR_MAX(x, destX);
1642  out->width = PNTR_MIN(x + width, destX + destWidth) - out->x;
1643  if (out->width <= 0) {
1644  return false;
1645  }
1646 
1647  out->y = PNTR_MAX(y, destY);
1648  out->height = PNTR_MIN(y + height, destY + destHeight) - out->y;
1649  if (out->height <= 0) {
1650  return false;
1651  }
1652 
1653  return true;
1654 }
1655 
1672 PNTR_API pntr_image* pntr_image_from_image(pntr_image* image, int x, int y, int width, int height) {
1673  if (image == NULL) {
1675  }
1676 
1677  pntr_rectangle dstRect;
1678  if (!_pntr_rectangle_intersect(x, y, width, height, 0, 0, image->width, image->height, &dstRect)) {
1679  return NULL;
1680  }
1681 
1682  pntr_image* result = pntr_new_image(dstRect.width, dstRect.height);
1683  if (result == NULL) {
1684  return NULL;
1685  }
1686 
1687  for (int destY = 0; destY < dstRect.height; destY++) {
1688  PNTR_MEMCPY(&PNTR_PIXEL(result, 0, destY),
1689  &PNTR_PIXEL(image, dstRect.x, dstRect.y + destY),
1690  (size_t)result->pitch);
1691  }
1692 
1693  return result;
1694 }
1695 
1712 PNTR_API pntr_image* pntr_image_subimage(pntr_image* image, int x, int y, int width, int height) {
1713  if (image == NULL) {
1715  }
1716 
1717  // Ensure we are referencing an actual portion of the image.
1718  pntr_rectangle dstRect;
1719  if (!_pntr_rectangle_intersect(x, y, width, height, 0, 0, image->width, image->height, &dstRect)) {
1720  return NULL;
1721  }
1722 
1723  // Build the subimage.
1724  pntr_image* subimage = (pntr_image*)PNTR_MALLOC(sizeof(pntr_image));
1725  if (subimage == NULL) {
1727  }
1728 
1729  subimage->pitch = image->pitch;
1730  subimage->width = dstRect.width;
1731  subimage->height = dstRect.height;
1732  subimage->subimage = true;
1733  pntr_image_reset_clip(subimage);
1734  subimage->data = &PNTR_PIXEL(image, dstRect.x, dstRect.y);
1735 
1736  return subimage;
1737 }
1738 
1745  if (image == NULL) {
1746  return;
1747  }
1748 
1749  // Only clear full image data.
1750  if (!image->subimage && image->data != NULL) {
1751  PNTR_FREE(image->data);
1752  }
1753 
1754  PNTR_FREE(image);
1755 }
1756 
1760 PNTR_API inline void pntr_put_horizontal_line_unsafe(pntr_image* dst, int posX, int posY, int width, pntr_color color) {
1761  pntr_color *row = &PNTR_PIXEL(dst, posX, posY);
1762  while (--width >= 0) {
1763  row[width] = color;
1764  }
1765 }
1766 
1776  if (image == NULL) {
1777  return;
1778  }
1779 
1780  // Blank or white can have some performance optimization.
1781  if (!image->subimage) {
1782  // White
1783  if (color.value == PNTR_WHITE_VALUE) {
1784  PNTR_MEMSET((void*)image->data, 255, (size_t)(image->height * image->pitch));
1785  return;
1786  }
1787 
1788  // Blank
1789  if (color.rgba.a == 0) {
1790  PNTR_MEMSET((void*)image->data, 0, (size_t)(image->height * image->pitch));
1791  return;
1792  }
1793  }
1794 
1795  // Draw the first line
1796  pntr_put_horizontal_line_unsafe(image, 0, 0, image->width, color);
1797 
1798  // Copy the line for the rest of the background
1799  for (int y = 1; y < image->height; y++) {
1800  PNTR_MEMCPY(&PNTR_PIXEL(image, 0, y), image->data, (size_t)image->pitch);
1801  }
1802 }
1803 
1814 PNTR_API inline pntr_color pntr_new_color(unsigned char red, unsigned char green, unsigned char blue, unsigned char alpha) {
1815  return PNTR_CLITERAL(pntr_color){
1816  .rgba = {
1817  .r = red,
1818  .g = green,
1819  .b = blue,
1820  .a = alpha
1821  }
1822  };
1823 }
1824 
1832 PNTR_API inline pntr_color pntr_get_color(unsigned int hexValue) {
1833  return PNTR_CLITERAL(pntr_color) {
1834  .rgba = {
1835  .r = (unsigned char)(hexValue >> 24) & 0xFF,
1836  .g = (unsigned char)(hexValue >> 16) & 0xFF,
1837  .b = (unsigned char)(hexValue >> 8) & 0xFF,
1838  .a = (unsigned char)hexValue & 0xFF
1839  }
1840  };
1841 }
1842 
1843 PNTR_API inline unsigned char pntr_color_r(pntr_color color) {
1844  return color.rgba.r;
1845 }
1846 
1847 PNTR_API inline unsigned char pntr_color_g(pntr_color color) {
1848  return color.rgba.g;
1849 }
1850 
1851 PNTR_API inline unsigned char pntr_color_b(pntr_color color) {
1852  return color.rgba.b;
1853 }
1854 
1855 PNTR_API inline unsigned char pntr_color_a(pntr_color color) {
1856  return color.rgba.a;
1857 }
1858 
1859 PNTR_API inline void pntr_color_set_r(pntr_color* color, unsigned char r) {
1860  color->rgba.r = r;
1861 }
1862 
1863 PNTR_API inline void pntr_color_set_g(pntr_color* color, unsigned char g) {
1864  color->rgba.g = g;
1865 }
1866 
1867 PNTR_API inline void pntr_color_set_b(pntr_color* color, unsigned char b) {
1868  color->rgba.b = b;
1869 }
1870 
1871 PNTR_API inline void pntr_color_set_a(pntr_color* color, unsigned char a) {
1872  color->rgba.a = a;
1873 }
1874 
1878 PNTR_API inline void pntr_draw_point_unsafe(pntr_image* dst, int x, int y, pntr_color color) {
1879  pntr_blend_color(&PNTR_PIXEL(dst, x, y), color);
1880 }
1881 
1885 PNTR_API void pntr_draw_point(pntr_image* dst, int x, int y, pntr_color color) {
1886  if ((color.rgba.a == 0) || (dst == NULL) || (x < dst->clip.x) || (x >= dst->clip.x + dst->clip.width) || (y < dst->clip.y) || (y >= dst->clip.y + dst->clip.height)) {
1887  return;
1888  }
1889 
1890  pntr_draw_point_unsafe(dst, x, y, color);
1891 }
1892 
1893 PNTR_API void pntr_draw_point_vec(pntr_image* dst, pntr_vector* point, pntr_color color) {
1894  if (point != NULL) {
1895  pntr_draw_point(dst, point->x, point->y, color);
1896  }
1897 }
1898 
1899 PNTR_API void pntr_draw_points(pntr_image* dst, pntr_vector* points, int pointsCount, pntr_color color) {
1900  if (dst == NULL || color.rgba.a == 0 || points == NULL || pointsCount <= 0) {
1901  return;
1902  }
1903 
1904  for (int i = 0; i < pointsCount; i++) {
1905  if (points[i].x >= dst->clip.x && points[i].x < dst->clip.x + dst->clip.width && points[i].y >= dst->clip.y && points[i].y < dst->clip.y + dst->clip.height) {
1906  pntr_draw_point_unsafe(dst, points[i].x, points[i].y, color);
1907  }
1908  }
1909 }
1910 
1919 PNTR_API void pntr_draw_line(pntr_image *dst, int startPosX, int startPosY, int endPosX, int endPosY, pntr_color color) {
1920  if (dst == NULL || color.rgba.a == 0) {
1921  return;
1922  }
1923 
1924  int changeInX = (endPosX - startPosX);
1925  int absChangeInX = (changeInX < 0) ? -changeInX : changeInX;
1926  int changeInY = (endPosY - startPosY);
1927  int absChangeInY = (changeInY < 0) ? -changeInY : changeInY;
1928 
1929  // Drawing a straight line is fast.
1930  if (startPosX == endPosX) {
1931  pntr_draw_line_vertical(dst, startPosX, (startPosY > endPosY) ? endPosY : startPosY, absChangeInY, color);
1932  return;
1933  }
1934 
1935  if (startPosY == endPosY) {
1936  pntr_draw_line_horizontal(dst, (startPosX > endPosX) ? endPosX : startPosX, startPosY, absChangeInX, color);
1937  return;
1938  }
1939 
1940  int startU, startV, endU, stepV;
1941  int A, B, P;
1942  int reversedXY = (absChangeInY < absChangeInX);
1943 
1944  if (reversedXY) {
1945  A = 2 * absChangeInY;
1946  B = A - 2 * absChangeInX;
1947  P = A - absChangeInX;
1948 
1949  if (changeInX > 0) {
1950  startU = startPosX;
1951  startV = startPosY;
1952  endU = endPosX;
1953  //endV = endPosY;
1954  }
1955  else {
1956  startU = endPosX;
1957  startV = endPosY;
1958  endU = startPosX;
1959  //endV = startPosY;
1960 
1961  // Since start and end are reversed
1962  changeInX = -changeInX;
1963  changeInY = -changeInY;
1964  }
1965 
1966  stepV = (changeInY < 0) ? -1 : 1;
1967 
1968  pntr_draw_point(dst, startU, startV, color);
1969  }
1970  else {
1971  A = 2 * absChangeInX;
1972  B = A - 2 * absChangeInY;
1973  P = A - absChangeInY;
1974 
1975  if (changeInY > 0) {
1976  startU = startPosY;
1977  startV = startPosX;
1978  endU = endPosY;
1979  }
1980  else {
1981  startU = endPosY;
1982  startV = endPosX;
1983  endU = startPosY;
1984 
1985  changeInX = -changeInX;
1986  changeInY = -changeInY;
1987  }
1988 
1989  stepV = (changeInX < 0) ? -1 : 1;
1990 
1991  pntr_draw_point(dst, startV, startU, color);
1992  }
1993 
1994  for (int u = startU + 1, v = startV; u <= endU; u++) {
1995  if (P >= 0) {
1996  v += stepV;
1997  P += B;
1998  }
1999  else {
2000  P += A;
2001  }
2002 
2003  if (reversedXY) {
2004  pntr_draw_point(dst, u, v, color);
2005  }
2006  else {
2007  pntr_draw_point(dst, v, u, color);
2008  }
2009  }
2010 }
2011 
2012 PNTR_API void pntr_draw_polyline(pntr_image* dst, pntr_vector* points, int numPoints, pntr_color color) {
2013  if (color.rgba.a == 0 || dst == NULL || numPoints <= 0 || points == NULL) {
2014  return;
2015  }
2016 
2017  if (numPoints == 1) {
2018  pntr_draw_point_vec(dst, points, color);
2019  return;
2020  }
2021 
2022  for (int i = 0; i < numPoints - 1; i++) {
2023  pntr_draw_line_vec(dst, points[i], points[i + 1], color);
2024  }
2025 }
2026 
2038 PNTR_API void pntr_draw_line_horizontal(pntr_image* dst, int posX, int posY, int width, pntr_color color) {
2039  if (color.rgba.a == 0 || dst == NULL || posY < dst->clip.y || posY >= dst->clip.y + dst->clip.height || posX >= dst->clip.x + dst->clip.width || posX + width < dst->clip.x) {
2040  return;
2041  }
2042 
2043  if (posX < dst->clip.x) {
2044  width += posX - dst->clip.x;
2045  posX = dst->clip.x;
2046  }
2047  if (posX + width >= dst->clip.x + dst->clip.width) {
2048  width = dst->clip.x + dst->clip.width - posX;
2049  }
2050 
2051  if (color.rgba.a == 255) {
2052  pntr_put_horizontal_line_unsafe(dst, posX, posY, width, color);
2053  }
2054  else {
2055  pntr_color *row = &PNTR_PIXEL(dst, posX, posY);
2056  while (--width >= 0) {
2057  pntr_blend_color(row + width, color);
2058  }
2059  }
2060 }
2061 
2073 PNTR_API void pntr_draw_line_vertical(pntr_image* dst, int posX, int posY, int height, pntr_color color) {
2074  if (color.rgba.a == 0 || dst == NULL || posX < dst->clip.x || posX >= dst->clip.x + dst->clip.width || posY >= dst->clip.y + dst->clip.height || posY + height < dst->clip.y) {
2075  return;
2076  }
2077 
2078  if (posY < dst->clip.y) {
2079  height += posY - dst->clip.y;
2080  posY = dst->clip.y;
2081  }
2082  if (posY + height >= dst->clip.y + dst->clip.height) {
2083  height = dst->clip.y + dst->clip.height - posY;
2084  }
2085 
2086  if (color.rgba.a == 255) {
2087  for (int y = 0; y < height; y++) {
2088  PNTR_PIXEL(dst, posX, posY + y) = color;
2089  }
2090  }
2091  else {
2092  for (int y = 0; y < height; y++) {
2093  pntr_blend_color(&PNTR_PIXEL(dst, posX, posY + y), color);
2094  }
2095  }
2096 }
2097 
2106  pntr_draw_rectangle(dst, rec.x, rec.y, rec.width, rec.height, color);
2107 }
2108 
2122 PNTR_API void pntr_draw_rectangle(pntr_image* dst, int posX, int posY, int width, int height, pntr_color color) {
2123  if (color.rgba.a == 0 || dst == NULL || width <= 0 || height <= 0) {
2124  return;
2125  }
2126 
2127  pntr_draw_line_horizontal(dst, posX, posY, width, color);
2128  pntr_draw_line_horizontal(dst, posX, posY + height - 1, width, color);
2129  pntr_draw_line_vertical(dst, posX, posY + 1, height - 2, color);
2130  pntr_draw_line_vertical(dst, posX + width - 1, posY + 1, height - 2, color);
2131 }
2132 
2133 PNTR_API void pntr_draw_rectangle_thick(pntr_image* dst, int posX, int posY, int width, int height, int thickness, pntr_color color) {
2134  for (int i = 0; i < thickness; i++) {
2135  pntr_draw_rectangle(dst, posX + i, posY + i, width - i * 2, height - i * 2, color);
2136  }
2137 }
2138 
2139 PNTR_API inline void pntr_draw_rectangle_thick_rec(pntr_image* dst, pntr_rectangle rect, int thickness, pntr_color color) {
2140  pntr_draw_rectangle_thick(dst, rect.x, rect.y, rect.width, rect.height, thickness, color);
2141 }
2142 
2155 PNTR_API inline void pntr_draw_rectangle_fill(pntr_image* dst, int posX, int posY, int width, int height, pntr_color color) {
2156  pntr_draw_rectangle_fill_rec(dst, PNTR_CLITERAL(pntr_rectangle) { posX, posY, width, height }, color);
2157 }
2158 
2169  if (color.rgba.a == 0 || dst == NULL) {
2170  return;
2171  }
2172 
2173  if (!_pntr_rectangle_intersect(rect.x, rect.y, rect.width, rect.height, dst->clip.x, dst->clip.y, dst->clip.width, dst->clip.height, &rect)) {
2174  return;
2175  }
2176 
2177  // When the color is solid, we can do some performance improvements.
2178  if (color.rgba.a == 255) {
2179  pntr_put_horizontal_line_unsafe(dst, rect.x, rect.y, rect.width, color);
2180 
2181  pntr_color* srcPixel = &PNTR_PIXEL(dst, rect.x, rect.y);
2182  for (int y = rect.y + 1; y < rect.y + rect.height; y++) {
2183  PNTR_MEMCPY(&PNTR_PIXEL(dst, rect.x, y), srcPixel, (size_t)rect.width * sizeof(pntr_color));
2184  }
2185  }
2186  else {
2187  for (int y = 0; y < rect.height; y++) {
2188  pntr_color* col = &PNTR_PIXEL(dst, rect.x, rect.y + y);
2189  for (int x = 0; x < rect.width; x++) {
2190  pntr_blend_color(col++, color);
2191  }
2192  }
2193  }
2194 }
2195 
2196 PNTR_API void pntr_draw_rectangle_gradient_rec(pntr_image* dst, pntr_rectangle rect, pntr_color topLeft, pntr_color topRight, pntr_color bottomLeft, pntr_color bottomRight) {
2197  if (dst == NULL) {
2198  return;
2199  }
2200 
2201  pntr_rectangle dstRect;
2202  if (!_pntr_rectangle_intersect(rect.x, rect.y, rect.width, rect.height, dst->clip.x, dst->clip.y, dst->clip.width, dst->clip.height, &dstRect)) {
2203  return;
2204  }
2205 
2206  float width = (float)rect.width;
2207  float height = (float)rect.height;
2208  for (int x = dstRect.x; x < dstRect.x + dstRect.width; x++) {
2209  float factorX = (float)(x - rect.x) / width;
2210  for (int y = dstRect.y; y < dstRect.y + dstRect.height; y++) {
2212  topLeft, bottomLeft,
2213  topRight, bottomRight,
2214  factorX,
2215  (float)(y - rect.y) / height
2216  ));
2217  }
2218  }
2219 }
2220 
2221 PNTR_API inline 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) {
2222  pntr_draw_rectangle_gradient_rec(dst, PNTR_CLITERAL(pntr_rectangle) {x, y, width, height}, topLeft, topRight, bottomLeft, bottomRight);
2223 }
2224 
2241 PNTR_API inline void pntr_draw_circle(pntr_image* dst, int centerX, int centerY, int radius, pntr_color color) {
2242  if (dst == NULL || color.rgba.a == 0) {
2243  return;
2244  }
2245 
2246  if (radius < 0) {
2247  radius = -radius;
2248  }
2249 
2250  // Check that the circle is in the bounds.
2251  if (centerX + radius < dst->clip.x || centerY + radius < dst->clip.y || centerX - radius > dst->clip.x + dst->clip.width || centerY - radius > dst->clip.y + dst->clip.height) {
2252  return;
2253  }
2254 
2255  int largestX = radius;
2256  int r2 = radius * radius;
2257  for (int y = 0; y <= radius; ++y) {
2258  int y2 = y * y;
2259  for (int x = largestX; x >= 0; --x) {
2260  if (x * x + y2 <= r2) {
2261  pntr_draw_point(dst, centerX + x, centerY + y, color);
2262  pntr_draw_point(dst, centerX - x, centerY + y, color);
2263  pntr_draw_point(dst, centerX + x, centerY - y, color);
2264  pntr_draw_point(dst, centerX - x, centerY - y, color);
2265  pntr_draw_point(dst, centerX + y, centerY + x, color);
2266  pntr_draw_point(dst, centerX - y, centerY + x, color);
2267  pntr_draw_point(dst, centerX + y, centerY - x, color);
2268  pntr_draw_point(dst, centerX - y, centerY - x, color);
2269  largestX = x;
2270  break;
2271  }
2272  }
2273  }
2274 }
2275 
2289 PNTR_API void pntr_draw_circle_fill(pntr_image* dst, int centerX, int centerY, int radius, pntr_color color) {
2290  if (radius < 0) {
2291  radius = -radius;
2292  }
2293 
2294  if (dst == NULL || color.rgba.a == 0 || radius == 0 || centerX + radius < dst->clip.x || centerX - radius >= dst->clip.x + dst->clip.width || centerY + radius < dst->clip.y || centerY - radius >= dst->clip.y + dst->clip.height) {
2295  return;
2296  }
2297 
2298  int largestX = radius;
2299  int r2 = radius * radius;
2300  for (int y = 0; y <= radius; ++y) {
2301  int y2 = y * y;
2302  for (int x = largestX; x >= 0; --x) {
2303  if (x * x + y2 <= r2) {
2304  pntr_draw_line_horizontal(dst, centerX - x, centerY + y, x, color);
2305  pntr_draw_line_horizontal(dst, centerX - x, centerY - y, x, color);
2306  pntr_draw_line_horizontal(dst, centerX, centerY + y, x, color);
2307  pntr_draw_line_horizontal(dst, centerX, centerY - y, x, color);
2308  largestX = x;
2309  break;
2310  }
2311  }
2312  }
2313 }
2314 
2327 PNTR_API void pntr_draw_ellipse(pntr_image* dst, int centerX, int centerY, int radiusX, int radiusY, pntr_color color) {
2328  if (dst == NULL || radiusX == 0 || radiusY == 0 || color.rgba.a == 0) {
2329  return;
2330  }
2331 
2332  int x = 0;
2333  if (radiusX < 0) {
2334  radiusX = -radiusX;
2335  }
2336  if (radiusY < 0) {
2337  radiusY = -radiusY;
2338  }
2339 
2340  int radiusXSquared = radiusX * radiusX;
2341  int radiusXSquared2 = radiusXSquared * 2;
2342  int radiusYSquared = radiusY * radiusY;
2343  int radiusYSquared2 = radiusYSquared * 2;
2344  int error = radiusYSquared - radiusXSquared * radiusY;
2345 
2346  while (radiusY >= 0) {
2347  pntr_draw_point(dst, centerX + x, centerY + radiusY, color);
2348  pntr_draw_point(dst, centerX - x, centerY + radiusY, color);
2349  pntr_draw_point(dst, centerX - x, centerY - radiusY, color);
2350  pntr_draw_point(dst, centerX + x, centerY - radiusY, color);
2351 
2352  if (error <= 0) {
2353  x++;
2354  error += radiusYSquared2 * x + radiusYSquared;
2355  }
2356  if (error > 0) {
2357  radiusY--;
2358  error -= radiusXSquared2 * radiusY - radiusXSquared;
2359  }
2360  }
2361 }
2362 
2377 PNTR_API void pntr_draw_ellipse_fill(pntr_image* dst, int centerX, int centerY, int radiusX, int radiusY, pntr_color color) {
2378  if (radiusX < 0) {
2379  radiusX = -radiusX;
2380  }
2381  if (radiusY < 0) {
2382  radiusY = -radiusY;
2383  }
2384 
2385  if (dst == NULL || radiusX == 0 || radiusY == 0 || color.rgba.a == 0 || centerX + radiusX < dst->clip.x || centerX - radiusX > dst->clip.x + dst->clip.width || centerY + radiusY < dst->clip.y || centerY - radiusY > dst->clip.y + dst->clip.height) {
2386  return;
2387  }
2388 
2389  int x = 0;
2390  int radiusXSquared = radiusX * radiusX;
2391  int radiusXSquared2 = radiusXSquared * 2;
2392  int radiusYSquared = radiusY * radiusY;
2393  int radiusYSquared2 = radiusYSquared * 2;
2394  int error = radiusYSquared - radiusXSquared * radiusY;
2395 
2396  while (radiusY >= 0) {
2397  pntr_draw_line_horizontal(dst, centerX - x, centerY + radiusY, x, color);
2398  pntr_draw_line_horizontal(dst, centerX - x, centerY - radiusY, x, color);
2399  pntr_draw_line_horizontal(dst, centerX, centerY + radiusY, x, color);
2400  pntr_draw_line_horizontal(dst, centerX, centerY - radiusY, x, color);
2401 
2402  if (error <= 0) {
2403  x++;
2404  error += radiusYSquared2 * x + radiusYSquared;
2405  }
2406  if (error > 0) {
2407  radiusY--;
2408  error -= radiusXSquared2 * radiusY - radiusXSquared;
2409  }
2410  }
2411 }
2412 
2422 PNTR_API inline void pntr_draw_triangle_vec(pntr_image* dst, pntr_vector point1, pntr_vector point2, pntr_vector point3, pntr_color color) {
2423  pntr_draw_triangle(dst, point1.x, point1.y, point2.x, point2.y, point3.x, point3.y, color);
2424 }
2425 
2438 PNTR_API void pntr_draw_triangle(pntr_image* dst, int x1, int y1, int x2, int y2, int x3, int y3, pntr_color color) {
2439  pntr_draw_line(dst, x1, y1, x2, y2, color);
2440  pntr_draw_line(dst, x2, y2, x3, y3, color);
2441  pntr_draw_line(dst, x3, y3, x1, y1, color);
2442 }
2443 
2456 PNTR_API inline void pntr_draw_triangle_fill(pntr_image* dst, int x1, int y1, int x2, int y2, int x3, int y3, pntr_color color) {
2458  PNTR_CLITERAL(pntr_vector) { .x = x1, .y = y1 },
2459  PNTR_CLITERAL(pntr_vector) { .x = x2, .y = y2 },
2460  PNTR_CLITERAL(pntr_vector) { .x = x3, .y = y3 },
2461  color
2462  );
2463 }
2464 
2465 PNTR_API inline void pntr_draw_line_vec(pntr_image* dst, pntr_vector start, pntr_vector end, pntr_color color) {
2466  pntr_draw_line(dst, start.x, start.y, end.x, end.y, color);
2467 }
2468 
2469 PNTR_API void pntr_draw_polygon(pntr_image* dst, pntr_vector* points, int numPoints, pntr_color color) {
2470  if (dst == NULL || color.rgba.a == 0 || numPoints <= 0 || points == NULL) {
2471  return;
2472  }
2473 
2474  int nextPointIndex;
2475  for (int i = 0; i < numPoints; i++) {
2476  if (i < numPoints - 1) {
2477  nextPointIndex = i + 1;
2478  }
2479  else {
2480  nextPointIndex = 0;
2481  }
2482 
2483  pntr_draw_line(dst, points[i].x, points[i].y, points[nextPointIndex].x, points[nextPointIndex].y, color);
2484  }
2485 }
2486 
2487 PNTR_API void pntr_draw_polygon_fill(pntr_image* dst, pntr_vector* points, int numPoints, pntr_color color) {
2488  if (dst == NULL || points == NULL || numPoints <= 0 || color.rgba.a == 0) {
2489  return;
2490  }
2491 
2492  // Discover the top and bottom of the polygon.
2493  int ymin = dst->height + 1;
2494  int ymax = -1;
2495  for (int i = 0; i < numPoints; ++i) {
2496  ymin = PNTR_MIN(ymin, points[i].y);
2497  ymax = PNTR_MAX(ymax, points[i].y);
2498  }
2499 
2500  // The following algorithm is correct for convex polygons only.
2501  for (int yy = ymin; yy <= ymax; yy++) {
2502  int xmin = dst->width + 1;
2503  int xmax = -1;
2504  for (int i = 0; i < numPoints; ++i) {
2505  pntr_vector point1 = points[i];
2506  pntr_vector point2 = i < (numPoints - 1) ? points[i + 1] : points[0];
2507 
2508  if ((point1.y > yy) != (point2.y > yy)) {
2509  int testx = point1.x + ((point2.x - point1.x) * (yy - point1.y)) / (point2.y - point1.y);
2510  xmin = PNTR_MIN(xmin, testx);
2511  xmax = PNTR_MAX(xmax, testx);
2512  }
2513  }
2514 
2515  pntr_draw_line_horizontal(dst, xmin, yy, xmax - xmin, color);
2516  }
2517 }
2518 
2529  pntr_vector points[3];
2530  points[0] = point1;
2531  points[1] = point2;
2532  points[2] = point3;
2533  pntr_draw_polygon_fill(dst, points, 3, color);
2534 }
2535 
2536 PNTR_API void pntr_draw_arc(pntr_image* dst, int centerX, int centerY, float radius, float startAngle, float endAngle, int segments, pntr_color color) {
2537  if (radius == 0.0f) {
2538  pntr_draw_point(dst, centerX, centerY, color);
2539  return;
2540  }
2541  if (segments < 0) {
2542  return;
2543  }
2544 
2545  float startAngleRad = startAngle * PNTR_PI / 180.0f;
2546  float endAngleRad = endAngle * PNTR_PI / 180.0f;
2547 
2548  // Calculate how much distance between each segment
2549  float stepAngle = (endAngleRad - startAngleRad) / (float)(segments);
2550 
2551  // Draw the arc with line segments
2552  /*
2553  int x1 = centerX + (int)((float)radius * PNTR_COSF(startAngleRad));
2554  int y1 = centerY + (int)((float)radius * PNTR_SINF(startAngleRad));
2555  float angle;
2556  for (int i = 1; i < segments; i++) {
2557  angle = startAngleRad + (float)i * stepAngle;
2558  int x2 = centerX + (int)((float)radius * PNTR_COSF(angle));
2559  int y2 = centerY + (int)((float)radius * PNTR_SINF(angle));
2560  pntr_draw_line(dst, x1, y1, x2, y2, color);
2561  x1 = x2;
2562  y1 = y2;
2563  }
2564  */
2565 
2566  // Draw each line segment
2567  for (int i = 0; i < segments; i++) {
2568  endAngleRad = startAngleRad + (float)i * stepAngle;
2569  pntr_draw_point(dst,
2570  centerX + (int)(radius * PNTR_COSF(endAngleRad)), // TODO: arc angle: Is the - correct here?
2571  centerY + (int)(radius * PNTR_SINF(endAngleRad)),
2572  color);
2573  }
2574 }
2575 
2576 PNTR_API void pntr_draw_arc_fill(pntr_image* dst, int centerX, int centerY, float radius, float startAngle, float endAngle, int segments, pntr_color color) {
2577  if (radius <= 0.0f) {
2578  pntr_draw_point(dst, centerX, centerY, color);
2579  return;
2580  }
2581  if (segments < 0) {
2582  return;
2583  }
2584  float startAngleRad = startAngle * PNTR_PI / 180.0f;
2585  float endAngleRad = endAngle * PNTR_PI / 180.0f;
2586 
2587  // Calculate how much distance between each segment
2588  float stepAngle = (endAngleRad - startAngleRad) / (float)segments;
2589  pntr_vector* points = (pntr_vector*)PNTR_MALLOC(sizeof(pntr_vector) * (size_t)segments + (size_t)1);
2590 
2591  // TODO: pntr_draw_arc_fill(): Is pntr_draw_polygon_fill ample here?
2592  for (int i = 0; i < segments; i++) {
2593  endAngleRad = startAngleRad + (float)i * stepAngle;
2594  points[i].x = centerX + (int)(radius * PNTR_COSF(endAngleRad));
2595  points[i].y = centerY + (int)(radius * PNTR_SINF(endAngleRad));
2596  }
2597 
2598  points[segments].x = centerX;
2599  points[segments].y = centerY;
2600 
2601  pntr_draw_polygon_fill(dst, points, segments + 1, color);
2602  pntr_unload_memory((void*)points);
2603 }
2604 
2605 PNTR_API 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) {
2606  pntr_draw_line_horizontal(dst, x + topLeftRadius, y, width - topLeftRadius - topRightRadius, color); // Top
2607  pntr_draw_line_horizontal(dst, x + bottomLeftRadius, y + height, width - bottomLeftRadius - bottomRightRadius, color); // Bottom
2608  pntr_draw_line_vertical(dst, x, y + topLeftRadius, height - topLeftRadius - bottomLeftRadius, color); // Left
2609  pntr_draw_line_vertical(dst, x + width, y + topRightRadius, height - topRightRadius - bottomRightRadius, color); // Right
2610 
2611  // TODO: pntr_draw_rectangle_rounded(): Do the angles here make sense?
2612  pntr_draw_arc(dst, x + topLeftRadius, y + topLeftRadius, (float)topLeftRadius, 180.0f, 270.0f, topLeftRadius * 2, color); // Top Left
2613  pntr_draw_arc(dst, x + width - topRightRadius, y + topRightRadius, (float)topRightRadius, 0.0f, -90.0f, topRightRadius * 2, color); // Top Right
2614  pntr_draw_arc(dst, x + bottomLeftRadius, y + height - bottomLeftRadius, (float)bottomLeftRadius, -180.0f, -270.0f, bottomLeftRadius * 2, color); // Bottom Left
2615  pntr_draw_arc(dst, x + width - bottomRightRadius, y + height - bottomRightRadius, (float)bottomRightRadius, 0.0f, 90.0f, bottomRightRadius * 2, color); // Bottom Right
2616 }
2617 
2618 PNTR_API void pntr_draw_rectangle_rounded_fill(pntr_image* dst, int x, int y, int width, int height, int cornerRadius, pntr_color color) {
2619  // Corners
2620  // TODO: Replace this with pntr_draw_arc_fill()
2621  pntr_draw_circle_fill(dst, x + cornerRadius, y + cornerRadius, cornerRadius, color); // Top Left
2622  pntr_draw_circle_fill(dst, x + width - cornerRadius, y + cornerRadius, cornerRadius, color); // Top Right
2623  pntr_draw_circle_fill(dst, x + cornerRadius, y + height - cornerRadius, cornerRadius, color); // Bottom Left
2624  pntr_draw_circle_fill(dst, x + width - cornerRadius, y + height - cornerRadius, cornerRadius, color); // Bottom Right
2625 
2626  // Edge bars
2627  pntr_draw_rectangle_fill(dst, x, y + cornerRadius, cornerRadius, height - cornerRadius * 2, color); // Left bar
2628  pntr_draw_rectangle_fill(dst, x + width - cornerRadius, y + cornerRadius, cornerRadius, height - cornerRadius * 2, color); // Right bar
2629  pntr_draw_rectangle_fill(dst, x + cornerRadius, y, width - cornerRadius * 2, cornerRadius, color); // Top bar
2630  pntr_draw_rectangle_fill(dst, x + cornerRadius, y + height - cornerRadius, width - cornerRadius * 2, cornerRadius, color); // Bottom bar
2631 
2632  // Center fill
2633  pntr_draw_rectangle_fill(dst, x + cornerRadius, y + cornerRadius, width - cornerRadius * 2, height - cornerRadius * 2, color);
2634 }
2635 
2646  if (image == NULL || x < 0 || y < 0 || x >= image->width || y >= image->height) {
2647  return PNTR_BLANK;
2648  }
2649 
2650  return PNTR_PIXEL(image, x, y);
2651 }
2652 
2666  if (filePath == NULL) {
2667  return PNTR_IMAGE_TYPE_UNKNOWN;
2668  }
2669 
2670  if (PNTR_STRSTR(filePath, ".png") != NULL) {
2671  return PNTR_IMAGE_TYPE_PNG;
2672  }
2673 
2674  if (PNTR_STRSTR(filePath, ".bmp") != NULL) {
2675  return PNTR_IMAGE_TYPE_BMP;
2676  }
2677 
2678  if (PNTR_STRSTR(filePath, ".jpg") != NULL || PNTR_STRSTR(filePath, ".jpeg") != NULL) {
2679  return PNTR_IMAGE_TYPE_JPG;
2680  }
2681 
2682  return PNTR_IMAGE_TYPE_UNKNOWN;
2683 }
2684 
2685 // Load stb_image or cute_png.
2686 #ifndef PNTR_LOAD_IMAGE_FROM_MEMORY
2687  #ifdef PNTR_STB_IMAGE
2688  #include "extensions/pntr_stb_image.h"
2689  #elif defined(PNTR_CUTE_PNG)
2690  #include "extensions/pntr_cute_png.h"
2691  #else
2692  // Allow disabling image loading.
2693  #ifdef PNTR_NO_LOAD_IMAGE
2694  #define PNTR_LOAD_IMAGE_FROM_MEMORY(type, fileData, dataSize) NULL
2695  #else
2696  // Default to stb_image.
2697  #include "extensions/pntr_stb_image.h"
2698  #endif
2699  #endif
2700 #endif
2701 
2702 #ifndef PNTR_SAVE_IMAGE_TO_MEMORY
2703  #ifdef PNTR_STB_IMAGE
2704  #include "extensions/pntr_stb_image_write.h"
2705  #elif defined(PNTR_CUTE_PNG)
2706  #include "extensions/pntr_cute_png.h"
2707  #else
2708  // Allow disabling image saving.
2709  #ifdef PNTR_NO_SAVE_IMAGE
2710  #define PNTR_SAVE_IMAGE_TO_MEMORY(image, type, dataSize) NULL
2711  #else
2712  // Default to stb_image_write.
2713  #include "extensions/pntr_stb_image_write.h"
2714  #endif
2715  #endif
2716 #endif
2717 
2731 PNTR_API pntr_image* pntr_load_image_from_memory(pntr_image_type type, const unsigned char *fileData, unsigned int dataSize) {
2732  if (fileData == NULL || dataSize == 0) {
2734  }
2735 
2736  return PNTR_LOAD_IMAGE_FROM_MEMORY(type, fileData, dataSize);
2737 }
2738 
2746 PNTR_API pntr_image* pntr_load_image(const char* fileName) {
2747  if (fileName == NULL) {
2749  }
2750 
2751  unsigned int bytesRead;
2752  const unsigned char* fileData = pntr_load_file(fileName, &bytesRead);
2753  if (fileData == NULL) {
2755  }
2756 
2757  pntr_image_type type = pntr_get_file_image_type(fileName);
2758  pntr_image* output = pntr_load_image_from_memory(type, fileData, bytesRead);
2759  pntr_unload_file((unsigned char*)fileData);
2760 
2761  return output;
2762 }
2763 
2767 PNTR_API inline void pntr_draw_image_tint(pntr_image* dst, pntr_image* src, int posX, int posY, pntr_color tint) {
2768  if (src == NULL) {
2769  return;
2770  }
2771  pntr_draw_image_tint_rec(dst, src,
2772  PNTR_CLITERAL(pntr_rectangle) { 0, 0, src->width, src->height },
2773  posX, posY, tint
2774  );
2775 }
2776 
2780 PNTR_API inline void pntr_draw_image(pntr_image* dst, pntr_image* src, int posX, int posY) {
2781  if (src == NULL) {
2782  return;
2783  }
2784  pntr_draw_image_tint_rec(dst, src,
2785  PNTR_CLITERAL(pntr_rectangle) { 0, 0, src->width, src->height },
2786  posX, posY, PNTR_WHITE);
2787 }
2788 
2800  pntr_blend_color(&dst, src);
2801  return dst;
2802 }
2803 
2815 PNTR_API inline void pntr_draw_image_rec(pntr_image* dst, pntr_image* src, pntr_rectangle srcRect, int posX, int posY) {
2816  pntr_draw_image_tint_rec(dst, src, srcRect, posX, posY, PNTR_WHITE);
2817 }
2818 
2831 PNTR_API void pntr_draw_image_tint_rec(pntr_image* dst, pntr_image* src, pntr_rectangle srcRect, int posX, int posY, pntr_color tint) {
2832  if (dst == NULL || src == NULL || posX >= dst->clip.x + dst->clip.width || posY >= dst->clip.y + dst->clip.height) {
2833  return;
2834  }
2835 
2836  // Make sure the source rectangle is within bounds.
2837  if (!_pntr_rectangle_intersect(srcRect.x, srcRect.y,
2838  srcRect.width <= 0 ? src->width : srcRect.width,
2839  srcRect.height <= 0 ? src->height : srcRect.height,
2840  0, 0,
2841  src->width, src->height, &srcRect)) {
2842  return;
2843  }
2844 
2845  // Update the source coordinates based on the destination
2846  if (posX < dst->clip.x) {
2847  srcRect.x -= posX - dst->clip.x;
2848  srcRect.width += posX - dst->clip.x;
2849  posX = dst->clip.x;
2850  }
2851  if (posY < dst->clip.y) {
2852  srcRect.y -= posY - dst->clip.y;
2853  srcRect.height += posY - dst->clip.y;
2854  posY = dst->clip.y;
2855  }
2856 
2857  // Confine the destination.
2858  pntr_rectangle dstRect = PNTR_CLITERAL(pntr_rectangle) { posX, posY, srcRect.width, srcRect.height };
2859  if (!_pntr_rectangle_intersect(dstRect.x, dstRect.y,
2860  srcRect.width,
2861  srcRect.height,
2862  dst->clip.x, dst->clip.y,
2863  dst->clip.width, dst->clip.height, &dstRect)) {
2864  return;
2865  }
2866 
2867  // Determine how many bits to skip for each line.
2868  int dst_skip = dst->pitch >> 2;
2869  int src_skip = src->pitch >> 2;
2870 
2871  // Find the first pixel to render.
2872  pntr_color *dstPixel = dst->data + dst_skip * dstRect.y + dstRect.x;
2873  pntr_color *srcPixel = src->data + src_skip * srcRect.y + srcRect.x;
2874 
2875  if (tint.value == PNTR_WHITE_VALUE) {
2876  while (dstRect.height-- > 0) {
2877  for (int x = 0; x < dstRect.width; ++x) {
2878  pntr_blend_color(dstPixel + x, srcPixel[x]);
2879  }
2880 
2881  dstPixel += dst_skip;
2882  srcPixel += src_skip;
2883  }
2884  }
2885  else {
2886  while (dstRect.height-- > 0) {
2887  for (int x = 0; x < dstRect.width; ++x) {
2888  pntr_blend_color(dstPixel + x, pntr_color_tint(srcPixel[x], tint));
2889  }
2890 
2891  dstPixel += dst_skip;
2892  srcPixel += src_skip;
2893  }
2894  }
2895 }
2896 
2909 PNTR_API pntr_image* pntr_image_from_pixelformat(const void* imageData, int width, int height, pntr_pixelformat pixelFormat) {
2910  if (imageData == NULL || width <= 0 || height <= 0 || pixelFormat < 0) {
2912  }
2913 
2914  // Check how we are to convert the pixel format.
2915  switch (pixelFormat) {
2917  pntr_image* output = pntr_new_image(width, height);
2918  if (output == NULL) {
2919  return NULL;
2920  }
2921 
2922  unsigned char* source = (unsigned char*)imageData;
2923  for (int i = 0; i < width * height; i++) {
2924  output->data[i] = pntr_get_pixel_color((void*)(source + i), pixelFormat);
2925  }
2926 
2927  return output;
2928  }
2929 
2932  pntr_image* output = pntr_new_image(width, height);
2933 
2934  pntr_color* source = (pntr_color*)imageData;
2935  for (int i = 0; i < width * height; i++) {
2936  output->data[i] = pntr_get_pixel_color((void*)(source + i), pixelFormat);
2937  }
2938 
2939  return output;
2940  }
2941 
2942  default: {
2944  }
2945  }
2946 }
2947 
2960 PNTR_API pntr_image* pntr_image_scale(pntr_image* image, float scaleX, float scaleY, pntr_filter filter) {
2961  if (image == NULL || scaleX <= 0.0f || scaleY <= 0.0f) {
2963  }
2964 
2965  return pntr_image_resize(image, (int)((float)image->width * scaleX), (int)((float)image->height * scaleY), filter);
2966 }
2967 
2980 PNTR_API pntr_image* pntr_image_resize(pntr_image* image, int newWidth, int newHeight, pntr_filter filter) {
2981  if (image == NULL || newWidth <= 0 || newHeight <= 0 || filter < 0) {
2983  }
2984 
2985  pntr_image* output = pntr_new_image(newWidth, newHeight);
2986  if (output == NULL) {
2987  return NULL;
2988  }
2989 
2990  switch (filter) {
2991  case PNTR_FILTER_BILINEAR: {
2992  float xRatio = (float)image->width / (float)newWidth;
2993  float yRatio = (float)image->height / (float)newHeight;
2994 
2995  for (int y = 0; y < newHeight; y++) {
2996  float srcY = (float)y * yRatio;
2997  int srcYPixel = (int)srcY;
2998  int srcYPixelPlusOne = y == newHeight - 1 ? (int)srcY : (int)srcY + 1;
2999  for (int x = 0; x < newWidth; x++) {
3000  float srcX = (float)x * xRatio;
3001  int srcXPixel = (int)srcX;
3002  int srcXPixelPlusOne = x == newWidth - 1 ? (int)srcX : (int)srcX + 1;
3004  image->data[srcYPixel * (image->pitch >> 2) + srcXPixel],
3005  image->data[srcYPixelPlusOne * (image->pitch >> 2) + srcXPixel],
3006  image->data[srcYPixel * (image->pitch >> 2) + srcXPixelPlusOne],
3007  image->data[srcYPixelPlusOne * (image->pitch >> 2) + srcXPixelPlusOne],
3008  srcX - PNTR_FLOORF(srcX),
3009  srcY - PNTR_FLOORF(srcY)
3010  );
3011  }
3012  }
3013  }
3014  break;
3016  default: {
3017  int xRatio = (image->width << 16) / newWidth + 1;
3018  int yRatio = (image->height << 16) / newHeight + 1;
3019 
3020  for (int y = 0; y < newHeight; y++) {
3021  int y2 = (y * yRatio) >> 16;
3022  for (int x = 0; x < newWidth; x++) {
3023  PNTR_PIXEL(output, x, y) = PNTR_PIXEL(image, (x * xRatio) >> 16, y2);
3024  }
3025  }
3026  }
3027  break;
3028  }
3029 
3030  // TODO: Copy the clip values scaled from the original image?
3031 
3032  return output;
3033 }
3034 
3044 PNTR_API void pntr_image_flip(pntr_image* image, bool horizontal, bool vertical) {
3045  if (image == NULL) {
3046  return;
3047  }
3048 
3049  pntr_color swap;
3050  if (vertical) {
3051  for (int y = 0; y < image->height / 2; y++) {
3052  for (int x = 0; x < image->width; x++) {
3053  swap = PNTR_PIXEL(image, x, y);
3054  PNTR_PIXEL(image, x, y) = PNTR_PIXEL(image, x, image->height - 1 - y);
3055  PNTR_PIXEL(image, x, image->height - 1 - y) = swap;
3056  }
3057  }
3058  }
3059 
3060  if (horizontal) {
3061  for (int y = 0; y < image->height; y++) {
3062  for (int x = 0; x < image->width / 2; x++) {
3063  swap = PNTR_PIXEL(image, x, y);
3064  PNTR_PIXEL(image, x, y) = PNTR_PIXEL(image, image->width - 1 - x, y);
3065  PNTR_PIXEL(image, image->width - 1 - x, y) = swap;
3066  }
3067  }
3068  }
3069 }
3070 
3079  if (image == NULL) {
3080  return;
3081  }
3082 
3083  for (int y = image->clip.y; y < image->clip.y + image->clip.height; y++) {
3084  pntr_color* pixel = &PNTR_PIXEL(image, 0, y);
3085  for (int x = image->clip.x; x < image->clip.x + image->clip.width; x++) {
3086  if (pixel->value == color.value) {
3087  *pixel = replace;
3088  }
3089  pixel++;
3090  }
3091  }
3092 }
3093 
3105  if (tint.value == PNTR_WHITE_VALUE) {
3106  return color;
3107  }
3108 
3109  return PNTR_CLITERAL(pntr_color) {
3110  .rgba = {
3111  .r = (unsigned char)(((float)color.rgba.r / 255.0f * (float)tint.rgba.r / 255.0f) * 255.0f),
3112  .g = (unsigned char)(((float)color.rgba.g / 255.0f * (float)tint.rgba.g / 255.0f) * 255.0f),
3113  .b = (unsigned char)(((float)color.rgba.b / 255.0f * (float)tint.rgba.b / 255.0f) * 255.0f),
3114  .a = (unsigned char)(((float)color.rgba.a / 255.0f * (float)tint.rgba.a / 255.0f) * 255.0f)
3115  }
3116  };
3117 }
3118 
3128  if (factor < -1.0f) {
3129  factor = -1.0f;
3130  }
3131  else if (factor > 1.0f) {
3132  factor = 1.0f;
3133  }
3134 
3135  if (factor < 0.0f) {
3136  factor = 1.0f + factor;
3137  color.rgba.r = (unsigned char)((float)color.rgba.r * factor);
3138  color.rgba.g = (unsigned char)((float)color.rgba.g * factor);
3139  color.rgba.b = (unsigned char)((float)color.rgba.b * factor);
3140  }
3141  else {
3142  color.rgba.r = (unsigned char)(((float)(255 - color.rgba.r) * factor) + color.rgba.r);
3143  color.rgba.g = (unsigned char)(((float)(255 - color.rgba.g) * factor) + color.rgba.g);
3144  color.rgba.b = (unsigned char)(((float)(255 - color.rgba.b) * factor) + color.rgba.b);
3145  }
3146 
3147  return color;
3148 }
3149 
3161  if (factor < -1.0f) {
3162  factor = -1.0f;
3163  }
3164  else if (factor > 1.0f) {
3165  factor = 1.0f;
3166  }
3167 
3168  if (factor < 0.0f) {
3169  color.rgba.a = (unsigned char)((float)color.rgba.a * (1.0f + factor));
3170  }
3171  else {
3172  color.rgba.a = (unsigned char)(((float)(255 - color.rgba.a) * factor) + color.rgba.a);
3173  }
3174 
3175  return color;
3176 }
3177 
3186 PNTR_API void pntr_image_color_fade(pntr_image* image, float factor) {
3187  if (image == NULL) {
3188  return;
3189  }
3190 
3191  if (factor < -1.0f) {
3192  factor = -1.0f;
3193  }
3194  else if (factor > 1.0f) {
3195  factor = 1.0f;
3196  }
3197 
3198  for (int y = image->clip.y; y < image->clip.y + image->clip.height; y++) {
3199  pntr_color* pixel = &PNTR_PIXEL(image, image->clip.x, y);
3200  for (int x = 0; x < image->clip.width; x++) {
3201  if (pixel->rgba.a > 0) {
3202  *pixel = pntr_color_fade(*pixel, factor);
3203  }
3204  pixel++;
3205  }
3206  }
3207 }
3208 
3216 PNTR_API void pntr_set_pixel_color(void* dstPtr, pntr_pixelformat dstPixelFormat, pntr_color color) {
3217  if (PNTR_PIXELFORMAT == dstPixelFormat) {
3218  *((pntr_color*)dstPtr) = color;
3219  return;
3220  }
3221 
3222  switch (dstPixelFormat) {
3224  *((uint32_t*)(dstPtr)) = ((uint32_t)color.rgba.a << 24) | ((uint32_t)color.rgba.b << 16) | ((uint32_t)color.rgba.g << 8) | (uint32_t)color.rgba.r;
3225  break;
3227  *((uint32_t*)(dstPtr)) = ((uint32_t)color.rgba.b << 24) | ((uint32_t)color.rgba.g << 16) | ((uint32_t)color.rgba.r << 8) | (uint32_t)color.rgba.a;
3228  break;
3230  float r = (float)color.rgba.r / 255.0f;
3231  float g = (float)color.rgba.g / 255.0f;
3232  float b = (float)color.rgba.b / 255.0f;
3233  ((unsigned char *)dstPtr)[0] = (unsigned char)((r * 0.299f + g * 0.587f + b * 0.114f) * 255.0f);
3234  }
3235  break;
3236  }
3237 }
3238 
3248  switch (srcPixelFormat) {
3250  return PNTR_CLITERAL(pntr_color) {
3251  .rgba = {
3252  .r = ((unsigned char *)srcPtr)[0],
3253  .g = ((unsigned char *)srcPtr)[1],
3254  .b = ((unsigned char *)srcPtr)[2],
3255  .a = ((unsigned char *)srcPtr)[3]
3256  }
3257  };
3259  return PNTR_CLITERAL(pntr_color) {
3260  .rgba = {
3261  .a = ((unsigned char *)srcPtr)[0],
3262  .r = ((unsigned char *)srcPtr)[1],
3263  .g = ((unsigned char *)srcPtr)[2],
3264  .b = ((unsigned char *)srcPtr)[3]
3265  }
3266  };
3268  // White, with alpha determining grayscale value. Use tint to change color afterwards.
3269  return PNTR_CLITERAL(pntr_color) {
3270  .rgba = {
3271  .r = 255,
3272  .g = 255,
3273  .b = 255,
3274  .a = ((unsigned char*)srcPtr)[0]
3275  }
3276  };
3277  }
3278 
3279  return PNTR_BLANK;
3280 }
3281 
3291  if (image == NULL) {
3292  return;
3293  }
3294 
3295  for (int y = image->clip.y; y < image->clip.y + image->clip.height; y++) {
3296  pntr_color* pixel = &PNTR_PIXEL(image, image->clip.x, y);
3297  for (int x = 0; x < image->clip.width; x++) {
3298  *pixel = pntr_color_tint(*pixel, tint);
3299  pixel++;
3300  }
3301  }
3302 }
3303 
3314 PNTR_API pntr_font* pntr_load_font_bmf(const char* fileName, const char* characters) {
3315  pntr_image* image = pntr_load_image(fileName);
3316  if (image == NULL) {
3317  return NULL;
3318  }
3319 
3320  return pntr_load_font_bmf_from_image(image, characters);
3321 }
3322 
3323 PNTR_API pntr_font* pntr_load_font_bmf_from_memory(const unsigned char* fileData, unsigned int dataSize, const char* characters) {
3324  if (fileData == NULL || dataSize == 0 || characters == NULL) {
3326  }
3327 
3328  pntr_image* image = pntr_load_image_from_memory(PNTR_IMAGE_TYPE_PNG, fileData, dataSize);
3329  if (image == NULL) {
3330  return NULL;
3331  }
3332 
3333  return pntr_load_font_bmf_from_image(image, characters);
3334 }
3335 
3347 PNTR_API pntr_font* _pntr_new_font(int numCharacters, size_t characterByteSize, pntr_image* atlas) {
3348  if (numCharacters <= 0) {
3350  }
3351 
3352  pntr_font* font = PNTR_MALLOC(sizeof(pntr_font));
3353  if (font == NULL) {
3355  }
3356 
3357  // Source Rectangles
3358  font->srcRects = PNTR_MALLOC(sizeof(pntr_rectangle) * (size_t)numCharacters);
3359  if (font->srcRects == NULL) {
3360  PNTR_FREE(font);
3362  }
3363 
3364  // Glyph Rectangles
3365  font->glyphRects = PNTR_MALLOC(sizeof(pntr_rectangle) * (size_t)numCharacters);
3366  if (font->glyphRects == NULL) {
3367  PNTR_FREE(font->srcRects);
3368  PNTR_FREE(font);
3370  }
3371 
3372  // Characters
3373  font->characters = PNTR_MALLOC(characterByteSize);
3374  if (font->characters == NULL) {
3375  PNTR_FREE(font->srcRects);
3376  PNTR_FREE(font->glyphRects);
3377  PNTR_FREE(font);
3379  }
3380 
3381  font->characters[0] = '\0';
3382  font->charactersLen = numCharacters;
3383  font->atlas = atlas;
3384 
3385  return font;
3386 }
3387 
3388 PNTR_API pntr_font* pntr_load_font_bmf_from_image(pntr_image* image, const char* characters) {
3389  if (image == NULL || characters == NULL) {
3391  }
3392 
3393  pntr_color seperator = pntr_image_get_color(image, 0, 0);
3394  pntr_rectangle currentRectangle = PNTR_CLITERAL(pntr_rectangle){1, 0, 0, image->height};
3395 
3396  // Find out how many characters there are.
3397  int numCharacters = 0;
3398  for (int i = 0; i < image->width; i++) {
3399  if (pntr_image_get_color(image, i, 0).value == seperator.value) {
3400  numCharacters++;
3401  }
3402  }
3403 
3404  pntr_font* font = _pntr_new_font(numCharacters, PNTR_STRSIZE(characters), image);
3405  if (font == NULL) {
3406  return NULL;
3407  }
3408 
3409  // Set up the data structures.
3410  int currentCharacter = 0;
3411  for (int i = 1; i < image->width; i++) {
3412  if (pntr_image_get_color(image, i, 0).value == seperator.value) {
3413  font->characters[currentCharacter] = characters[currentCharacter];
3414  font->srcRects[currentCharacter] = currentRectangle;
3415  font->glyphRects[currentCharacter] = PNTR_CLITERAL(pntr_rectangle) {
3416  .x = 0,
3417  .y = 0,
3418  .width = currentRectangle.width,
3419  .height = currentRectangle.height,
3420  };
3421  currentRectangle.width = 0;
3422  currentRectangle.x = i + 1;
3423  currentCharacter++;
3424  }
3425  else {
3426  // Increase the width of the active glyph rectangle.
3427  currentRectangle.width++;
3428  }
3429  }
3430 
3431  #ifdef PNTR_ENABLE_UTF8
3432  utf8cpy(font->characters, characters);
3433  #endif
3434 
3435  return font;
3436 }
3437 
3450 PNTR_API pntr_font* pntr_load_font_tty(const char* fileName, int glyphWidth, int glyphHeight, const char* characters) {
3451  pntr_image* image = pntr_load_image(fileName);
3452  if (image == NULL) {
3453  return NULL;
3454  }
3455 
3456  pntr_font* output = pntr_load_font_tty_from_image(image, glyphWidth, glyphHeight, characters);
3457  if (output == NULL) {
3458  pntr_unload_image(image);
3459  }
3460 
3461  return output;
3462 }
3463 
3464 PNTR_API pntr_font* pntr_load_font_tty_from_memory(const unsigned char* fileData, unsigned int dataSize, int glyphWidth, int glyphHeight, const char* characters) {
3465  if (fileData == NULL || dataSize == 0 || characters == NULL || glyphWidth <= 0 || glyphHeight <= 0) {
3467  }
3468 
3469  pntr_image* image = pntr_load_image_from_memory(PNTR_IMAGE_TYPE_PNG, fileData, dataSize);
3470  if (image == NULL) {
3471  return NULL;
3472  }
3473 
3474  pntr_font* output = pntr_load_font_tty_from_image(image, glyphWidth, glyphHeight, characters);
3475  if (output == NULL) {
3476  pntr_unload_image(image);
3477  }
3478 
3479  return output;
3480 }
3481 
3482 PNTR_API pntr_font* pntr_load_font_tty_from_image(pntr_image* image, int glyphWidth, int glyphHeight, const char* characters) {
3483  if (image == NULL || characters == NULL || glyphWidth <= 0 || glyphHeight <= 0) {
3485  }
3486 
3487  // Find out how many characters there are.
3488  int numCharacters = (int)PNTR_STRLEN(characters);
3489 
3490  // Create the font.
3491  pntr_font* font = _pntr_new_font(numCharacters, PNTR_STRSIZE(characters), image);
3492  if (font == NULL) {
3493  return NULL;
3494  }
3495 
3496  // Set up the font data.
3497  for (int currentCharIndex = 0; currentCharIndex < font->charactersLen; currentCharIndex++) {
3498  // Source rectangle.
3499  font->srcRects[currentCharIndex] = PNTR_CLITERAL(pntr_rectangle) {
3500  .x = (currentCharIndex % (image->width / glyphWidth)) * glyphWidth,
3501  .y = (currentCharIndex / (image->width / glyphWidth)) * glyphHeight,
3502  .width = glyphWidth,
3503  .height = glyphHeight
3504  };
3505 
3506  // Where the glyph will be rendered.
3507  font->glyphRects[currentCharIndex] = PNTR_CLITERAL(pntr_rectangle) {
3508  .x = 0,
3509  .y = 0,
3510  .width = glyphWidth,
3511  .height = glyphHeight,
3512  };
3513 
3514  // Set the character.
3515  font->characters[currentCharIndex] = characters[currentCharIndex];
3516  }
3517 
3518  #ifdef PNTR_ENABLE_UTF8
3519  utf8cpy(font->characters, characters);
3520  #endif
3521 
3522  return font;
3523 }
3524 
3531  if (font == NULL) {
3532  return;
3533  }
3534 
3535  pntr_unload_image(font->atlas);
3539  PNTR_FREE(font);
3540 }
3541 
3550  if (font == NULL) {
3552  }
3553 
3554  pntr_image* atlas = pntr_image_copy(font->atlas);
3555  if (atlas == NULL) {
3556  return NULL;
3557  }
3558 
3559  size_t charactersSize = PNTR_STRSIZE(font->characters);
3560  pntr_font* output = _pntr_new_font(font->charactersLen, charactersSize, atlas);
3561  if (output == NULL) {
3562  pntr_unload_image(atlas);
3563  return NULL;
3564  }
3565 
3566  PNTR_MEMCPY(output->srcRects, font->srcRects, sizeof(pntr_rectangle) * (size_t)output->charactersLen);
3567  PNTR_MEMCPY(output->glyphRects, font->glyphRects, sizeof(pntr_rectangle) * (size_t)output->charactersLen);
3568  PNTR_MEMCPY(output->characters, font->characters, charactersSize);
3569 
3570  return output;
3571 }
3572 
3583 PNTR_API pntr_font* pntr_font_scale(pntr_font* font, float scaleX, float scaleY, pntr_filter filter) {
3584  if (font == NULL || scaleX <= 0.0f || scaleY <= 0.0f) {
3586  }
3587 
3588  // Create the new font.
3589  pntr_font* output = pntr_font_copy(font);
3590  if (output == NULL) {
3591  return NULL;
3592  }
3593 
3594  // Resize the atlas.
3595  pntr_image* resizedAtlas = pntr_image_scale(output->atlas, scaleX, scaleY, filter);
3596  pntr_unload_image(output->atlas);
3597  output->atlas = resizedAtlas;
3598 
3599  // Resize the rectangles.
3600  for (int i = 0; i < font->charactersLen; i++) {
3601  output->srcRects[i].x = (int)((float)output->srcRects[i].x * scaleX);
3602  output->srcRects[i].y = (int)((float)output->srcRects[i].y * scaleY);
3603  output->srcRects[i].width = (int)((float)output->srcRects[i].width * scaleX);
3604  output->srcRects[i].height = (int)((float)output->srcRects[i].height * scaleY);
3605  output->glyphRects[i].x = (int)((float)output->glyphRects[i].x * scaleX);
3606  output->glyphRects[i].y = (int)((float)output->glyphRects[i].y * scaleY);
3607  output->glyphRects[i].width = (int)((float)output->glyphRects[i].width * scaleX);
3608  output->glyphRects[i].height = (int)((float)output->glyphRects[i].height * scaleY);
3609  }
3610 
3611  return output;
3612 }
3613 
3626 PNTR_API void pntr_draw_text(pntr_image* dst, pntr_font* font, const char* text, int posX, int posY, pntr_color tint) {
3627  if (dst == NULL || font == NULL || text == NULL) {
3628  return;
3629  }
3630 
3631  int x = posX;
3632  int y = posY;
3633  int tallestCharacter = 0;
3634 
3635  pntr_codepoint_t codepoint;
3636 
3637  for (const char* v = PNTR_STRCODEPOINT(text, &codepoint); codepoint; v = PNTR_STRCODEPOINT(v, &codepoint)) {
3638  if (codepoint == '\n') {
3639  // TODO: pntr_draw_text(): Allow for center/right alignment
3640  x = posX;
3641  y += tallestCharacter;
3642  }
3643  else {
3644  char* foundCharacter = PNTR_STRCHR(font->characters, codepoint);
3645  if (foundCharacter != NULL) {
3646 
3647  // Find the index of the character in the string.
3648  #ifdef PNTR_ENABLE_UTF8
3649  int i = (int)utf8nlen(font->characters, (size_t)(foundCharacter - font->characters));
3650  #else
3651  int i = (int)(foundCharacter - font->characters);
3652  #endif
3653 
3654  // Draw the character, unless it's a space.
3655  if (codepoint != ' ') {
3656  pntr_draw_image_tint_rec(dst, font->atlas, font->srcRects[i], x + font->glyphRects[i].x, y + font->glyphRects[i].y, tint);
3657  }
3658 
3659  x += font->glyphRects[i].x + font->glyphRects[i].width;
3660  if (tallestCharacter < font->glyphRects[i].y + font->glyphRects[i].height) {
3661  tallestCharacter = font->glyphRects[i].y + font->glyphRects[i].height;
3662  }
3663  }
3664  }
3665  }
3666 }
3667 
3681 PNTR_API void pntr_draw_text_wrapped(pntr_image* dst, pntr_font* font, const char* text, int posX, int posY, int maxWidth, pntr_color tint) {
3682  if (dst == NULL || font == NULL || text == NULL) {
3683  return;
3684  }
3685 
3686  // Copy the string, along with its null terminator
3687  size_t length = PNTR_STRLEN(text);
3688  char* newText = pntr_load_memory(length);
3689  pntr_memory_copy((void*)newText, (void*)text, length);
3690 
3691  // Go through and figure out where new lines should be placed in the string.
3692  int currentLineLength = 0;
3693  int i = 0;
3694  int lastSpace = 0;
3695 
3696  while (text[i] != '\0') {
3697  if (text[i] == ' ' || text[i] == '\n') {
3698  // Measure the width of the line from the previous word.
3699  if (pntr_measure_text_ex(font, text + i - currentLineLength, currentLineLength).x >= maxWidth) {
3700  // Have the space before the line end become a new line.
3701  newText[lastSpace] = '\n';
3702  currentLineLength = i - lastSpace - 1; // -1 to remove the active space from the line count.
3703  }
3704  lastSpace = i;
3705  }
3706 
3707  currentLineLength++;
3708  i++;
3709  }
3710 
3711  // Perform one more check on the last line.
3712  if (pntr_measure_text_ex(font, text + i - currentLineLength, currentLineLength).x >= maxWidth) {
3713  newText[lastSpace] = '\n';
3714  }
3715 
3716  // Display the new text with the newlines, and clean up the memory usage.
3717  pntr_draw_text(dst, font, newText, posX, posY, tint);
3718  pntr_unload_memory((void*)newText);
3719 }
3720 
3721 #ifdef PNTR_ENABLE_VARGS
3738 PNTR_API void pntr_draw_text_ex(pntr_image* dst, pntr_font* font, int posX, int posY, pntr_color tint, const char* text, ...) {
3739  #ifndef PNTR_DRAW_TEXT_EX_STRING_LENGTH
3740  #define PNTR_DRAW_TEXT_EX_STRING_LENGTH 256
3741  #endif
3742  char output[PNTR_DRAW_TEXT_EX_STRING_LENGTH];
3743 
3744  va_list arg_ptr;
3745  va_start(arg_ptr, text);
3746  vsnprintf(output, PNTR_DRAW_TEXT_EX_STRING_LENGTH, text, arg_ptr);
3747  va_end(arg_ptr);
3748 
3749  pntr_draw_text(dst, font, output, posX, posY, tint);
3750 }
3751 #endif
3752 
3761 PNTR_API inline int pntr_measure_text(pntr_font* font, const char* text) {
3762  return pntr_measure_text_ex(font, text, 0).x;
3763 }
3764 
3774 PNTR_API pntr_vector pntr_measure_text_ex(pntr_font* font, const char* text, int textLength) {
3775  if (font == NULL || text == NULL) {
3776  return PNTR_CLITERAL(pntr_vector){0, 0};
3777  }
3778 
3779  pntr_vector output = PNTR_CLITERAL(pntr_vector) { .x = 0, .y = 0 };
3780  int currentX = 0;
3781  int currentY = 0;
3782  int index = 0;
3783 
3784  pntr_codepoint_t codepoint;
3785 
3786  for (const char* v = PNTR_STRCODEPOINT(text, &codepoint); codepoint; v = PNTR_STRCODEPOINT(v, &codepoint)) {
3787  // Stop drawing if we're only counting a certain amount of characters.
3788  if (textLength > 0 && index++ >= textLength) {
3789  break;
3790  }
3791 
3792  // Consider any newlines
3793  if (codepoint == '\n') {
3794  output.y += currentY;
3795  currentX = 0;
3796  continue;
3797  }
3798 
3799  // Find the index of the character in the font atlas.
3800  char* foundCharacter = PNTR_STRCHR(font->characters, codepoint);
3801  if (foundCharacter != NULL) {
3802  // Find the index of the character in the string.
3803  #ifdef PNTR_ENABLE_UTF8
3804  int i = (int)utf8nlen(font->characters, (size_t)(foundCharacter - font->characters));
3805  #else
3806  int i = (int)(foundCharacter - font->characters);
3807  #endif
3808 
3809  currentX += font->glyphRects[i].x + font->glyphRects[i].width;
3810  if (currentX > output.x) {
3811  output.x = currentX;
3812  }
3813 
3814  // Find the tallest character
3815  if (currentY < font->glyphRects[i].y + font->glyphRects[i].height) {
3816  currentY = font->glyphRects[i].y + font->glyphRects[i].height;
3817  }
3818  }
3819  }
3820 
3821  // Has at least one line.
3822  output.y += currentY;
3823 
3824  return output;
3825 }
3826 
3837  pntr_vector size = pntr_measure_text_ex(font, text, 0);
3838  if (size.x <= 0 || size.y <= 0) {
3839  return NULL;
3840  }
3841 
3842  pntr_image* output = pntr_gen_image_color(size.x, size.y, PNTR_BLANK);
3843  if (output == NULL) {
3844  return NULL;
3845  }
3846 
3847  pntr_draw_text(output, font, text, 0, 0, tint);
3848  return output;
3849 }
3850 
3870  #ifdef PNTR_DEFAULT_FONT
3871  return PNTR_DEFAULT_FONT();
3872  #elif defined(PNTR_ENABLE_DEFAULT_FONT)
3873  // Load font8x8 character atlas.
3874  // https://github.com/dhepper/font8x8
3875  #include "external/font8x8_basic.h"
3876 
3877  // Default parameters for font8x8.
3878  // TODO: pntr_load_font_default: Add UTF-8 support for the default font
3879  #define PNTR_DEFAULT_FONT_NAME font8x8_basic
3880  #define PNTR_DEFAULT_FONT_GLYPH_WIDTH 8
3881  #define PNTR_DEFAULT_FONT_GLYPH_HEIGHT 8
3882  #define PNTR_DEFAULT_FONT_CHARACTERS_LEN 97
3883 
3884  // Build the atlas.
3886  PNTR_DEFAULT_FONT_GLYPH_WIDTH * PNTR_DEFAULT_FONT_CHARACTERS_LEN,
3887  PNTR_DEFAULT_FONT_GLYPH_HEIGHT,
3888  PNTR_BLANK);
3889  if (atlas == NULL) {
3890  return NULL;
3891  }
3892 
3893  // Iterate through all the characters and draw them manually.
3894  for (int i = 0; i < PNTR_DEFAULT_FONT_CHARACTERS_LEN; i++) {
3895  const unsigned char* bitmap = PNTR_DEFAULT_FONT_NAME[i];
3896  for (int x = 0; x < 8; x++) {
3897  for (int y = 0; y < 8; y++) {
3898  if (bitmap[y] & 1 << x) {
3899  pntr_draw_point(atlas, PNTR_DEFAULT_FONT_GLYPH_WIDTH * i + x, y, PNTR_WHITE);
3900  }
3901  }
3902  }
3903  }
3904 
3905  // Build the character set.
3906  char characters[PNTR_DEFAULT_FONT_CHARACTERS_LEN];
3907  for (int i = 0; i < PNTR_DEFAULT_FONT_CHARACTERS_LEN; i++) {
3908  characters[i] = (char)(i + 32); // ASCII
3909  }
3910 
3911  // Use TTY to build the remaining font parameters.
3912  pntr_font* font = pntr_load_font_tty_from_image(atlas, PNTR_DEFAULT_FONT_GLYPH_WIDTH, PNTR_DEFAULT_FONT_GLYPH_HEIGHT, characters);
3913  if (font == NULL) {
3914  pntr_unload_image(atlas);
3915  return NULL;
3916  }
3917 
3918  return font;
3919  #else
3921  #endif
3922 }
3923 
3938 PNTR_API pntr_font* pntr_load_font_ttf(const char* fileName, int fontSize) {
3939  if (fileName == NULL || fontSize <= 0) {
3941  }
3942 
3943  #ifndef PNTR_ENABLE_TTF
3945  #else
3946  unsigned int bytesRead;
3947  unsigned char* fileData = pntr_load_file(fileName, &bytesRead);
3948  if (fileData == NULL) {
3949  return NULL;
3950  }
3951 
3952  pntr_font* output = pntr_load_font_ttf_from_memory(fileData, bytesRead, fontSize);
3953  pntr_unload_file(fileData);
3954 
3955  return output;
3956  #endif
3957 }
3958 
3974 PNTR_API pntr_font* pntr_load_font_ttf_from_memory(const unsigned char* fileData, unsigned int dataSize, int fontSize) {
3975  if (fileData == NULL || dataSize == 0 || fontSize <= 0) {
3977  }
3978 
3979  #ifndef PNTR_ENABLE_TTF
3981  #else
3982  // Which ASCII character to start rendering into the atlas
3983  #define PNTR_FONT_TTF_GLYPH_START 32
3984 
3985  // Find out how many glyhs we should prepare
3986  #ifndef PNTR_FONT_TTF_GLYPH_NUM
3987  #ifdef PNTR_ENABLE_UTF8
3988  // Up to the Cyrillic Supplement, minus the first 32 ascii characters
3989  // https://www.w3schools.com/charsets/ref_html_utf8.asp
3990  #define PNTR_FONT_TTF_GLYPH_NUM 1295
3991  #else
3992  // ASCII characater set
3993  #define PNTR_FONT_TTF_GLYPH_NUM 95
3994  #endif
3995  #endif
3996 
3997  // Create the bitmap data with ample space based on the font size
3998  int columns = 32;
3999  int rows = PNTR_FONT_TTF_GLYPH_NUM / columns;
4000  int width = fontSize * columns;
4001  int height = fontSize * rows;
4002  unsigned char* bitmap = (unsigned char*)PNTR_MALLOC((size_t)(width * height));
4003  if (bitmap == NULL) {
4005  }
4006 
4007  // Bake the font into the bitmap
4008  stbtt_bakedchar characterData[PNTR_FONT_TTF_GLYPH_NUM];
4009  int result = stbtt_BakeFontBitmap(fileData, 0, (float)fontSize, bitmap, width, height, PNTR_FONT_TTF_GLYPH_START, PNTR_FONT_TTF_GLYPH_NUM, characterData);
4010 
4011  // Check to make sure the font was baked correctly
4012  if (result == 0) {
4013  PNTR_FREE(bitmap);
4015  }
4016 
4017  // Port the bitmap to a pntr_image as the font atlas
4018  pntr_image* atlas = pntr_image_from_pixelformat((const void*)bitmap, width, height, PNTR_PIXELFORMAT_GRAYSCALE);
4019  PNTR_FREE(bitmap);
4020  if (atlas == NULL) {
4021  return NULL;
4022  }
4023 
4024  // Clear up the unused atlas space from memory, from the top left
4025  pntr_rectangle crop = pntr_image_alpha_border(atlas, 0.0f);
4026  pntr_image_crop(atlas, 0, 0, crop.x + crop.width, crop.y + crop.height);
4027 
4028  // Create the font data
4029  size_t charactersSize = sizeof(pntr_codepoint_t) * (size_t)PNTR_FONT_TTF_GLYPH_NUM;
4030  pntr_font* font = _pntr_new_font(PNTR_FONT_TTF_GLYPH_NUM, charactersSize, atlas);
4031  if (font == NULL) {
4032  pntr_unload_image(atlas);
4033  return NULL;
4034  }
4035 
4036  // Capture each glyph data
4037  #ifdef PNTR_ENABLE_UTF8
4038  char* destination = font->characters; // Where to write the new character.
4039  #endif
4040 
4041  // Build each character
4042  for (int i = 0; i < PNTR_FONT_TTF_GLYPH_NUM; i++) {
4043  // Calculate the source rectangles
4044  font->srcRects[i] = PNTR_CLITERAL(pntr_rectangle) {
4045  .x = characterData[i].x0,
4046  .y = characterData[i].y0,
4047  .width = characterData[i].x1 - characterData[i].x0,
4048  .height = characterData[i].y1 - characterData[i].y0
4049  };
4050 
4051  // Find where the glyphs will be rendered
4052  font->glyphRects[i] = PNTR_CLITERAL(pntr_rectangle) {
4053  .x = (int)characterData[i].xoff,
4054  .y = (int)(characterData[i].yoff + ((float)fontSize / 1.5f)), // TODO: Determine correct y glyph value
4055  .width = (int)characterData[i].xadvance,
4056  .height = (int)((float)fontSize / 3.0f) // TODO: Determine the correct glyph height
4057  };
4058 
4059  // Set up the active character
4060  #ifndef PNTR_ENABLE_UTF8
4061  font->characters[i] = (char)(PNTR_FONT_TTF_GLYPH_START + i);
4062  #else
4063  // Append the character to the destination, considering the remaining memory
4064  destination = utf8catcodepoint(destination, (pntr_codepoint_t)(PNTR_FONT_TTF_GLYPH_START + i), charactersSize - (size_t)(destination - font->characters));
4065  #endif
4066  }
4067 
4068  #ifdef PNTR_ENABLE_UTF8
4069  {
4070  // Clear out the unused memory in the character list by building a new UTF-8 string
4071  char* newCharacters = pntr_load_memory(PNTR_STRSIZE(font->characters));
4072  utf8cpy(newCharacters, font->characters);
4073  PNTR_FREE(font->characters);
4074  font->characters = newCharacters;
4075  }
4076  #endif
4077 
4078  return font;
4079  #endif
4080 }
4081 
4092  return PNTR_CLITERAL(pntr_color) {
4093  .rgba = {
4094  .r = 255 - color.rgba.r,
4095  .g = 255 - color.rgba.g,
4096  .b = 255 - color.rgba.b,
4097  .a = color.rgba.a
4098  }
4099  };
4100 }
4101 
4110  if (image == NULL) {
4111  return;
4112  }
4113 
4114  for (int y = image->clip.y; y < image->clip.y + image->clip.height; y++) {
4115  pntr_color* pixel = &PNTR_PIXEL(image, image->clip.x, y);
4116  for (int x = 0; x < image->clip.width; x++) {
4117  *pixel = pntr_color_invert(*pixel);
4118  pixel++;
4119  }
4120  }
4121 }
4122 
4132  if (image == NULL) {
4133  return;
4134  }
4135 
4136  if (factor < -1.0f) {
4137  factor = -1.0f;
4138  }
4139  else if (factor > 1.0f) {
4140  factor = 1.0f;
4141  }
4142 
4143  for (int y = image->clip.y; y < image->clip.y + image->clip.height; y++) {
4144  pntr_color* pixel = &PNTR_PIXEL(image, image->clip.x, y);
4145  for (int x = 0; x < image->clip.width; x++) {
4146  *pixel = pntr_color_brightness(*pixel, factor);
4147  pixel++;
4148  }
4149  }
4150 }
4151 
4152 #ifndef PNTR_LOAD_FILE
4153  #ifdef PNTR_NO_STDIO
4154  #define PNTR_LOAD_FILE(fileName, bytesRead) NULL
4155  #else
4156  #include <stdio.h> // FILE, fopen, fread
4157  #endif
4158 #endif // PNTR_LOAD_FILE
4159 
4175 PNTR_API unsigned char* pntr_load_file(const char* fileName, unsigned int* bytesRead) {
4176  if (fileName == NULL) {
4178  }
4179 
4180  #ifdef PNTR_LOAD_FILE
4181  return PNTR_LOAD_FILE(fileName, bytesRead);
4182  #else
4183  FILE* file = fopen(fileName, "rb");
4184  if (file == NULL) {
4185  if (bytesRead != NULL) {
4186  *bytesRead = 0;
4187  }
4189  }
4190 
4191  fseek(file, 0, SEEK_END);
4192  size_t size = (size_t)ftell(file);
4193  fseek(file, 0, SEEK_SET);
4194 
4195  if (size <= 0) {
4196  fclose(file);
4197  if (bytesRead != NULL) {
4198  *bytesRead = 0;
4199  }
4201  }
4202 
4203  unsigned char* data = (unsigned char*)PNTR_MALLOC(size * sizeof(unsigned char));
4204  if (data == NULL) {
4205  fclose(file);
4206  if (bytesRead != NULL) {
4207  *bytesRead = 0;
4208  }
4210  }
4211 
4212  // Read the file
4213  unsigned int bytes = (unsigned int)fread(data, sizeof(unsigned char), size, file);
4214  fclose(file);
4215  if (bytesRead != NULL) {
4216  *bytesRead = bytes;
4217  }
4218 
4219  return data;
4220  #endif
4221 }
4222 
4231 PNTR_API const char* pntr_load_file_text(const char *fileName) {
4232  unsigned int bytesRead;
4233  unsigned char* data = pntr_load_file(fileName, &bytesRead);
4234 
4235  if (data == NULL) {
4236  return NULL;
4237  }
4238 
4239  // While we have the loaded data, we'll need to null terminate it.
4240  char* output = (char*)PNTR_MALLOC(bytesRead + 1);
4241  if (output == NULL) {
4242  PNTR_FREE(data);
4243  return NULL;
4244  }
4245 
4246  PNTR_MEMCPY(output, data, bytesRead);
4247  output[bytesRead] = '\0';
4248  PNTR_FREE(data);
4249  return (const char*)output;
4250 }
4251 
4259 PNTR_API inline void pntr_unload_file_text(const char* text) {
4260  pntr_unload_memory((void*)text);
4261 }
4262 
4263 #ifndef PNTR_SAVE_FILE
4264  #ifdef PNTR_NO_STDIO
4265  #define PNTR_SAVE_FILE(fileName, data, bytesToWrite) NULL
4266  #else
4267  #include <stdio.h> // FILE, fopen, fwrite
4268  #endif
4269 #endif // PNTR_SAVE_FILE
4270 
4284 PNTR_API bool pntr_save_file(const char *fileName, const void *data, unsigned int bytesToWrite) {
4285  if (fileName == NULL || data == NULL) {
4287  }
4288 
4289  #ifdef PNTR_SAVE_FILE
4290  return PNTR_SAVE_FILE(fileName, data, bytesToWrite);
4291  #else
4292  FILE *file = fopen(fileName, "wb");
4293  if (file == NULL) {
4295  }
4296 
4297  size_t count = fwrite(data, sizeof(unsigned char), bytesToWrite, file);
4298 
4299  if (count <= 0) {
4300  fclose(file);
4302  }
4303 
4304  if (count != (size_t)bytesToWrite) {
4305  fclose(file);
4307  }
4308 
4309  return fclose(file) == 0;
4310  #endif
4311 }
4312 
4322 PNTR_API int pntr_get_pixel_data_size(int width, int height, pntr_pixelformat pixelFormat) {
4323  if (width <= 0 || height <= 0 || pixelFormat < 0) {
4324  return 0;
4325  }
4326 
4327  int bitsPerPixel = 0;
4328  int bitsPerByte = 8;
4329  switch (pixelFormat) {
4332  bitsPerPixel = (int)sizeof(pntr_color) * bitsPerByte;
4333  break;
4335  bitsPerPixel = (int)sizeof(unsigned char) * bitsPerByte;
4336  break;
4337  default:
4338  bitsPerPixel = (int)sizeof(pntr_color) * bitsPerByte;
4340  break;
4341  }
4342 
4343  return bitsPerPixel * width * height / bitsPerByte; // Bytes
4344 }
4345 
4355 PNTR_API void* pntr_image_to_pixelformat(pntr_image* image, unsigned int* dataSize, pntr_pixelformat pixelFormat) {
4356  if (image == NULL) {
4358  }
4359 
4360  int imageSize = pntr_get_pixel_data_size(image->width, image->height, pixelFormat);
4361  if (imageSize <= 0) {
4363  }
4364 
4365  void* data = PNTR_MALLOC((size_t)imageSize);
4366  if (data == NULL) {
4368  }
4369 
4370  int pixelSize = pntr_get_pixel_data_size(1, 1, pixelFormat);
4371 
4372  int i = 0;
4373  for (int y = 0; y < image->height; y++) {
4374  for (int x = 0; x < image->width; x++) {
4376  ((unsigned char*)data) + (i++ * pixelSize),
4377  pixelFormat,
4378  PNTR_PIXEL(image, x, y)
4379  );
4380  }
4381  }
4382 
4383  // Output the data size
4384  if (dataSize != NULL) {
4385  *dataSize = (unsigned int)imageSize;
4386  }
4387 
4388  return data;
4389 }
4390 
4407 PNTR_API unsigned char* pntr_save_image_to_memory(pntr_image* image, pntr_image_type type, unsigned int* dataSize) {
4408  if (image == NULL) {
4410  }
4411 
4412  return PNTR_SAVE_IMAGE_TO_MEMORY(image, type, dataSize);
4413 }
4414 
4426 PNTR_API bool pntr_save_image(pntr_image* image, const char* fileName) {
4427  unsigned int dataSize;
4428  pntr_image_type type = pntr_get_file_image_type(fileName);
4429  unsigned char* data = pntr_save_image_to_memory(image, type, &dataSize);
4430  if (data == NULL) {
4431  return false;
4432  }
4433 
4434  bool result = pntr_save_file(fileName, data, dataSize);
4435  PNTR_FREE(data);
4436 
4437  return result;
4438 }
4439 
4447 PNTR_API inline void pntr_unload_file(unsigned char* fileData) {
4448  pntr_unload_memory((void*)fileData);
4449 }
4450 
4460  if (image == NULL) {
4461  return PNTR_CLITERAL(pntr_rectangle) {0, 0, 0, 0};
4462  }
4463 
4464  unsigned char alphaThreshold = (unsigned char)(threshold * 255.0f);
4465  int xMin = 9999999;
4466  int xMax = 0;
4467  int yMin = 9999999;
4468  int yMax = 0;
4469 
4470  for (int y = 0; y < image->height; y++) {
4471  for (int x = 0; x < image->width; x++) {
4472  if (image->data[y * (image->pitch >> 2) + x].rgba.a > alphaThreshold) {
4473  if (x < xMin) {
4474  xMin = x;
4475  }
4476  if (x > xMax) {
4477  xMax = x;
4478  }
4479  if (y < yMin) {
4480  yMin = y;
4481  }
4482  if (y > yMax) {
4483  yMax = y;
4484  }
4485  }
4486  }
4487  }
4488 
4489  // Check for empty blank image
4490  if ((xMin != 9999999) && (xMax != 9999999)) {
4491  return PNTR_CLITERAL(pntr_rectangle) {
4492  .x = xMin,
4493  .y = yMin,
4494  .width = xMax + 1 - xMin,
4495  .height = yMax + 1 - yMin
4496  };
4497  }
4498 
4499  return PNTR_CLITERAL(pntr_rectangle) {0, 0, 0, 0};
4500 }
4501 
4513 PNTR_API bool pntr_image_crop(pntr_image* image, int x, int y, int width, int height) {
4514  if (image == NULL) {
4515  return false;
4516  }
4517 
4518  pntr_image* newImage = pntr_image_from_image(image, x, y, width, height);
4519  if (newImage == NULL) {
4520  return false;
4521  }
4522 
4523  // Clear the data if it isn't owned by another image.
4524  if (!image->subimage) {
4525  PNTR_FREE(image->data);
4526  }
4527 
4528  image->data = newImage->data;
4529  image->width = newImage->width;
4530  image->height = newImage->height;
4531  image->pitch = newImage->pitch;
4532  image->subimage = false;
4533  pntr_image_reset_clip(image);
4534 
4535  PNTR_FREE(newImage);
4536 
4537  return true;
4538 }
4539 
4549 PNTR_API void pntr_image_alpha_crop(pntr_image* image, float threshold) {
4550  if (image == NULL) {
4551  return;
4552  }
4553 
4554  pntr_rectangle crop = pntr_image_alpha_border(image, threshold);
4555 
4556  if (crop.width > 0 && crop.height > 0) {
4557  pntr_image_crop(image, crop.x, crop.y, crop.width, crop.height);
4558  }
4559 }
4560 
4570  if (contrast < -1.0f) {
4571  contrast = -1.0f;
4572  }
4573  else if (contrast > 1.0f) {
4574  contrast = 1.0f;
4575  }
4576 
4577  contrast = (1.0f + contrast) * contrast;
4578 
4579  float pR = (float)color.rgba.r / 255.0f - 0.5f;
4580  pR *= contrast;
4581  pR += 0.5f;
4582  pR *= 255;
4583  if (pR < 0) {
4584  pR = 0;
4585  }
4586  else if (pR > 255) {
4587  pR = 255;
4588  }
4589 
4590  float pG = (float)color.rgba.g / 255.0f - 0.5f;
4591  pG *= contrast;
4592  pG += 0.5f;
4593  pG *= 255;
4594  if (pG < 0) {
4595  pG = 0;
4596  }
4597  else if (pG > 255) {
4598  pG = 255;
4599  }
4600 
4601  float pB = (float)color.rgba.b / 255.0f - 0.5f;
4602  pB *= contrast;
4603  pB += 0.5f;
4604  pB *= 255;
4605  if (pB < 0) {
4606  pB = 0;
4607  }
4608  else if (pB > 255) {
4609  pB = 255;
4610  }
4611 
4612  return PNTR_CLITERAL(pntr_color) {
4613  .rgba = {
4614  .r = (unsigned char)pR,
4615  .g = (unsigned char)pG,
4616  .b = (unsigned char)pB,
4617  .a = color.rgba.a
4618  }
4619  };
4620 }
4621 
4630 PNTR_API void pntr_image_color_contrast(pntr_image* image, float contrast) {
4631  if (image == NULL) {
4632  return;
4633  }
4634 
4635  if (contrast < -1.0f) {
4636  contrast = -1.0f;
4637  }
4638  else if (contrast > 1.0f) {
4639  contrast = 1.0f;
4640  }
4641 
4642  for (int y = image->clip.y; y < image->clip.y + image->clip.height; y++) {
4643  pntr_color* pixel = &PNTR_PIXEL(image, image->clip.x, y);
4644  for (int x = 0; x < image->clip.width; x++) {
4645  *pixel = pntr_color_contrast(*pixel, contrast);
4646  pixel++;
4647  }
4648  }
4649 }
4650 
4661 PNTR_API void pntr_image_alpha_mask(pntr_image* image, pntr_image* alphaMask, int posX, int posY) {
4662  if (image == NULL || alphaMask == NULL) {
4663  return;
4664  }
4665 
4666  pntr_rectangle srcRect = PNTR_CLITERAL(pntr_rectangle) { 0, 0, alphaMask->width, alphaMask->height };
4667  pntr_rectangle dstRect = PNTR_CLITERAL(pntr_rectangle) { posX, posY, alphaMask->width, alphaMask->height };
4668 
4669  // Update the source coordinates based on the destination
4670  if (dstRect.x < 0) {
4671  srcRect.x -= dstRect.x;
4672  srcRect.width += dstRect.x;
4673  }
4674  if (dstRect.y < 0) {
4675  srcRect.y -= dstRect.y;
4676  srcRect.height += dstRect.y;
4677  }
4678 
4679  if (!_pntr_rectangle_intersect(dstRect.x, dstRect.y,
4680  PNTR_MIN(dstRect.width, srcRect.width),
4681  PNTR_MIN(dstRect.height, srcRect.height),
4682  image->clip.x, image->clip.y,
4683  image->clip.width, image->clip.height, &dstRect)) {
4684  return;
4685  }
4686 
4687  for (int y = 0; y < dstRect.height; y++) {
4688  pntr_color* pixel = &PNTR_PIXEL(image, dstRect.x, dstRect.y + y);
4689  for (int x = 0; x < dstRect.width; x++) {
4690  if (pixel->rgba.a > 0) {
4691  pixel->rgba.a = PNTR_PIXEL(alphaMask, x, y).rgba.a;
4692  }
4693  pixel++;
4694  }
4695  }
4696 }
4697 
4710 PNTR_API bool pntr_image_resize_canvas(pntr_image* image, int newWidth, int newHeight, int offsetX, int offsetY, pntr_color fill) {
4711  if (image == NULL) {
4713  return false;
4714  }
4715 
4716  pntr_image* newImage = pntr_gen_image_color(newWidth, newHeight, fill);
4717  if (newImage == NULL) {
4718  return false;
4719  }
4720 
4721  pntr_draw_image(newImage, image, offsetX, offsetY);
4722 
4723  // Clear the image if it's not a subimage
4724  if (!image->subimage) {
4725  PNTR_FREE(image->data);
4726  }
4727 
4728  image->data = newImage->data;
4729  image->width = newImage->width;
4730  image->height = newImage->height;
4731  image->pitch = newImage->pitch;
4732 
4733  // TODO: pntr_image_resize_canvas - Adust the new image clip with the original one.
4734  pntr_image_reset_clip(image);
4735  image->subimage = false;
4736 
4737  PNTR_FREE(newImage);
4738  return true;
4739 }
4740 
4741 PNTR_API inline void pntr_draw_image_flipped(pntr_image* dst, pntr_image* src, int posX, int posY, bool flipHorizontal, bool flipVertical, bool flipDiagonal) {
4742  if (dst == NULL || src == NULL) {
4743  return;
4744  }
4745 
4746  pntr_draw_image_flipped_rec(dst, src,
4747  PNTR_CLITERAL(pntr_rectangle) { .x = 0, .y = 0, .width = src->width, .height = src->height },
4748  posX, posY,
4749  flipHorizontal,
4750  flipVertical,
4751  flipDiagonal
4752  );
4753 }
4754 
4755 PNTR_API 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) {
4756  // If we are not flipping at all, use the simpler draw function.
4757  if (!flipHorizontal && !flipVertical && !flipDiagonal) {
4758  pntr_draw_image_tint_rec(dst, src, srcRec, posX, posY, PNTR_WHITE);
4759  return;
4760  }
4761 
4762  if (dst == NULL || src == NULL) {
4763  return;
4764  }
4765 
4766  if (!_pntr_rectangle_intersect(srcRec.x, srcRec.y, srcRec.width, srcRec.height, 0, 0, src->width, src->height, &srcRec)) {
4767  return;
4768  }
4769 
4770  int dstX, dstY;
4771  for (int y = 0; y < srcRec.height; y++) {
4772  for (int x = 0; x < srcRec.width; x++) {
4773  // Determine the destination pixels based on flip parameters
4774  if (flipDiagonal) {
4775  dstX = flipHorizontal ? srcRec.height - y - 1 : y;
4776  dstY = flipVertical ? srcRec.width - x - 1 : x;
4777  }
4778  else {
4779  dstY = flipVertical ? srcRec.height - y - 1 : y;
4780  dstX = flipHorizontal ? srcRec.width - x - 1 : x;
4781  }
4782 
4783  pntr_draw_point(dst, posX + dstX, posY + dstY,
4784  pntr_image_get_color(src, x, y)
4785  );
4786  }
4787  }
4788 }
4789 
4803 PNTR_API inline 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) {
4804  if (dst == NULL || src == NULL) {
4805  return;
4806  }
4807 
4808  pntr_draw_image_scaled_rec(dst, src,
4809  PNTR_CLITERAL(pntr_rectangle) { .x = 0, .y = 0, .width = src->width, .height = src->height },
4810  posX, posY,
4811  scaleX, scaleY,
4812  offsetX, offsetY,
4813  filter);
4814 }
4815 
4816 PNTR_API 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) {
4817  if (dst == NULL || src == NULL || scaleX <= 0.0f || scaleY <= 0.0f) {
4818  return;
4819  }
4820 
4821  if (!_pntr_rectangle_intersect(srcRect.x, srcRect.y, srcRect.width, srcRect.height, 0, 0, src->width, src->height, &srcRect)) {
4822  return;
4823  }
4824 
4825  int newWidth = (int)((float)srcRect.width * scaleX);
4826  int newHeight = (int)((float)srcRect.height * scaleY);
4827  int offsetXRatio = (int)(offsetX / (float)srcRect.width * (float)newWidth);
4828  int offsetYRatio = (int)(offsetY / (float)srcRect.height * (float)newHeight);
4829 
4830  switch (filter) {
4831  case PNTR_FILTER_BILINEAR: {
4832  float xRatio = (float)srcRect.width / (float)newWidth;
4833  float yRatio = (float)srcRect.height / (float)newHeight;
4834 
4835  for (int y = 0; y < newHeight; y++) {
4836  int yPosition = posY + y - offsetYRatio;
4837  if (yPosition < dst->clip.y || yPosition >= dst->clip.y + dst->clip.height) {
4838  continue;
4839  }
4840  float srcY = (float)y * yRatio;
4841  int srcYPixel = srcRect.y + (int)srcY;
4842  int srcYPixelPlusOne = y == newHeight - 1 ? (int)srcYPixel : (int)srcYPixel + 1;
4843  for (int x = 0; x < newWidth; x++) {
4844  int xPosition = posX + x - offsetXRatio;
4845  if (xPosition < dst->clip.x || xPosition >= dst->clip.x + dst->clip.width) {
4846  continue;
4847  }
4848  float srcX = (float)x * xRatio;
4849  int srcXPixel = srcRect.y + (int)srcX;
4850  int srcXPixelPlusOne = x == newWidth - 1 ? (int)srcXPixel : (int)srcXPixel + 1;
4851  pntr_draw_point_unsafe(dst, xPosition, yPosition, pntr_color_bilinear_interpolate(
4852  src->data[srcYPixel * (src->pitch >> 2) + srcXPixel],
4853  src->data[srcYPixelPlusOne * (src->pitch >> 2) + srcXPixel],
4854  src->data[srcYPixel * (src->pitch >> 2) + srcXPixelPlusOne],
4855  src->data[srcYPixelPlusOne * (src->pitch >> 2) + srcXPixelPlusOne],
4856  srcX - PNTR_FLOORF(srcX),
4857  srcY - PNTR_FLOORF(srcY)
4858  ));
4859  }
4860  }
4861  }
4862  break;
4864  default: {
4865  int xRatio = (srcRect.width << 16) / newWidth + 1;
4866  int yRatio = (srcRect.height << 16) / newHeight + 1;
4867 
4868  for (int y = 0; y < newHeight; y++) {
4869  int yPosition = posY + y - offsetYRatio;
4870  if (yPosition < dst->clip.y || yPosition >= dst->clip.y + dst->clip.height) {
4871  continue;
4872  }
4873  int y2 = (y * yRatio) >> 16;
4874  for (int x = 0; x < newWidth; x++) {
4875  int xPosition = posX + x - offsetXRatio;
4876  if (xPosition < dst->clip.x || xPosition >= dst->clip.x + dst->clip.width) {
4877  continue;
4878  }
4879  int x2 = (x * xRatio) >> 16;
4881  xPosition,
4882  yPosition,
4883  PNTR_PIXEL(src, srcRect.x + x2, srcRect.y + y2)
4884  );
4885  }
4886  }
4887  }
4888  break;
4889  }
4890 }
4891 
4899 float _pntr_normalize_degrees(float degrees) {
4900  if (degrees < 0) {
4901  return 360.0f - PNTR_FMODF(-degrees, 360.0f);
4902  }
4903 
4904  return PNTR_FMODF(degrees, 360.0f);
4905 }
4906 
4919  if (image == NULL) {
4920  return NULL;
4921  }
4922 
4923  degrees = _pntr_normalize_degrees(degrees);
4924 
4925  if (degrees == 0.0f) {
4926  return pntr_image_copy(image);
4927  }
4928 
4929  if (degrees == 90.0f || degrees == 180.0f || degrees == 270.0f) {
4930  pntr_image* output;
4931  if (degrees == 180.0f) {
4932  output = pntr_gen_image_color(image->width, image->height, PNTR_BLANK);
4933  }
4934  else {
4935  output = pntr_gen_image_color(image->height, image->width, PNTR_BLANK);
4936  }
4937  if (output == NULL) {
4938  return NULL;
4939  }
4940 
4941  pntr_draw_image_rotated(output, image, 0, 0, degrees, 0.0f, 0.0f, filter);
4942 
4943  return output;
4944  }
4945 
4946  float radians = degrees * PNTR_DEG2RAD;
4947  float cosTheta = PNTR_COSF(radians);
4948  float sinTheta = PNTR_SINF(radians);
4949 
4950  int newWidth = (int)PNTR_CEILF(PNTR_FABSF((float)image->width * cosTheta) + PNTR_FABSF((float)image->height * sinTheta));
4951  int newHeight = (int)PNTR_CEILF(PNTR_FABSF((float)image->width * sinTheta) + PNTR_FABSF((float)image->height * cosTheta));
4952 
4953  pntr_image* rotatedImage = pntr_gen_image_color(newWidth, newHeight, PNTR_BLANK);
4954  if (rotatedImage == NULL) {
4955  return NULL;
4956  }
4957 
4958  pntr_draw_image_rotated(rotatedImage, image, 0, 0, degrees, 0.0f, 0.0f, filter);
4959 
4960  return rotatedImage;
4961 }
4962 
4980 PNTR_API inline pntr_color pntr_color_bilinear_interpolate(pntr_color color00, pntr_color color01, pntr_color color10, pntr_color color11, float coordinateX, float coordinateY) {
4981  return PNTR_CLITERAL(pntr_color) {
4982  .rgba = {
4983  .r = (uint8_t)(color00.rgba.r * (1 - coordinateX) * (1 - coordinateY) + color01.rgba.r * (1 - coordinateX) * coordinateY + color10.rgba.r * coordinateX * (1 - coordinateY) + color11.rgba.r * coordinateX * coordinateY),
4984  .g = (uint8_t)(color00.rgba.g * (1 - coordinateX) * (1 - coordinateY) + color01.rgba.g * (1 - coordinateX) * coordinateY + color10.rgba.g * coordinateX * (1 - coordinateY) + color11.rgba.g * coordinateX * coordinateY),
4985  .b = (uint8_t)(color00.rgba.b * (1 - coordinateX) * (1 - coordinateY) + color01.rgba.b * (1 - coordinateX) * coordinateY + color10.rgba.b * coordinateX * (1 - coordinateY) + color11.rgba.b * coordinateX * coordinateY),
4986  .a = (uint8_t)(color00.rgba.a * (1 - coordinateX) * (1 - coordinateY) + color01.rgba.a * (1 - coordinateX) * coordinateY + color10.rgba.a * coordinateX * (1 - coordinateY) + color11.rgba.a * coordinateX * coordinateY)
4987  }
4988  };
4989 }
4990 
5006 PNTR_API inline void pntr_draw_image_rotated(pntr_image* dst, pntr_image* src, int posX, int posY, float degrees, float offsetX, float offsetY, pntr_filter filter) {
5007  if (dst == NULL || src == NULL) {
5008  return;
5009  }
5010 
5011  pntr_draw_image_rotated_rec(dst, src,
5012  PNTR_CLITERAL(pntr_rectangle) { .x = 0, .y = 0, .width = src->width, .height = src->height },
5013  posX, posY,
5014  degrees,
5015  offsetX, offsetY,
5016  filter);
5017 }
5018 
5034 PNTR_API 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) {
5035  if (dst == NULL || src == NULL) {
5036  return;
5037  }
5038 
5039  degrees = _pntr_normalize_degrees(degrees);
5040 
5041  // Draw the image normally if not rotated.
5042  if (degrees == 0.0f) {
5043  pntr_draw_image_rec(dst, src, srcRect, posX - (int)offsetX, posY - (int)offsetY);
5044  return;
5045  }
5046 
5047  // Clean up the source rectangle.
5048  if (srcRect.x < 0) {
5049  srcRect.x = 0;
5050  }
5051  if (srcRect.y < 0) {
5052  srcRect.y = 0;
5053  }
5054  if (srcRect.width <= 0 || srcRect.width > src->width) {
5055  srcRect.width = src->width - srcRect.x;
5056  }
5057  if (srcRect.height <= 0 || srcRect.height > src->height) {
5058  srcRect.height = src->height - srcRect.y;
5059  }
5060 
5061  // Simple rotation by 90 degrees can be fast.
5062  if (degrees == 90.0f || degrees == 180.0f || degrees == 270.0f) {
5063  // Build the destination coordinates of the image.
5064  pntr_rectangle dstRect = PNTR_CLITERAL(pntr_rectangle) { .x = posX, .y = posY, .width = srcRect.width, .height = srcRect.height };
5065  if (degrees == 90.0f || degrees == 270.0f) {
5066  dstRect.width = srcRect.height;
5067  dstRect.height = srcRect.width;
5068  dstRect.x -= (int)offsetY;
5069  dstRect.y -= (int)offsetX;
5070  }
5071  else {
5072  dstRect.x -= (int)offsetX;
5073  dstRect.y -= (int)offsetY;
5074  }
5075 
5076  // Exit if it's not even on the screen.
5077  if (dstRect.x + dstRect.width < dst->clip.x || dstRect.y + dstRect.height < dst->clip.y || dstRect.x >= dst->clip.x + dst->clip.width || dstRect.y >= dst->clip.y + dst->clip.height) {
5078  return;
5079  }
5080 
5081  // Draw the source portion on the destination.
5082  for (int y = 0; y < srcRect.height; y++) {
5083  for (int x = 0; x < srcRect.width; x++) {
5084  if (degrees == 90.0f) {
5085  pntr_draw_point(dst,
5086  dstRect.x + y,
5087  dstRect.y + srcRect.width - x,
5088  PNTR_PIXEL(src, srcRect.x + x, srcRect.y + y)
5089  );
5090  } else if (degrees == 180.0f) {
5091  pntr_draw_point(dst,
5092  dstRect.x + srcRect.width - x,
5093  dstRect.y + srcRect.height - y,
5094  PNTR_PIXEL(src, srcRect.x + x, srcRect.y + y)
5095  );
5096  }
5097  else {
5098  pntr_draw_point(dst,
5099  dstRect.x + srcRect.height - y,
5100  dstRect.y + x,
5101  PNTR_PIXEL(src, srcRect.x + x, srcRect.y + y)
5102  );
5103  }
5104  }
5105  }
5106 
5107  return;
5108  }
5109 
5110  float radians = degrees * PNTR_DEG2RAD;
5111  float cosTheta = PNTR_COSF(radians);
5112  float sinTheta = PNTR_SINF(radians);
5113 
5114  int newWidth = (int)PNTR_CEILF(PNTR_FABSF((float)srcRect.width * cosTheta) + PNTR_FABSF((float)srcRect.height * sinTheta));
5115  int newHeight = (int)PNTR_CEILF(PNTR_FABSF((float)srcRect.width * sinTheta) + PNTR_FABSF((float)srcRect.height * cosTheta));
5116 
5117  int offsetXRatio = (int)(offsetX / (float)srcRect.width * (float)newWidth);
5118  int offsetYRatio = (int)(offsetY / (float)srcRect.height * (float)newHeight);
5119 
5120  // Make sure we're actually drawing on the screen.
5121  if (posX - offsetXRatio + newWidth < dst->clip.x || posX - offsetXRatio >= dst->clip.x + dst->clip.width || posY - offsetYRatio + newHeight < dst->clip.y || posY - offsetYRatio >= dst->clip.y + dst->clip.height) {
5122  return;
5123  }
5124 
5125  float centerX = (float)srcRect.width / 2.0f;
5126  float centerY = (float)srcRect.height / 2.0f;
5127  int srcXint, srcYint;
5128  float srcX, srcY;
5129 
5130  for (int y = 0; y < newHeight; y++) {
5131  // Only draw onto the screen.
5132  int destY = posY + y - offsetYRatio;
5133  if (destY < dst->clip.y || destY >= dst->clip.y + dst->clip.height) {
5134  continue;
5135  }
5136 
5137  for (int x = 0; x < newWidth; x++) {
5138  // Make sure we're actually drawing onto the screen.
5139  int destX = posX + x - offsetXRatio;
5140  if (destX < dst->clip.x || destX >= dst->clip.x + dst->clip.width ) {
5141  continue;
5142  }
5143 
5144  srcX = (float)(x - newWidth / 2) * cosTheta - (float)(y - newHeight / 2) * sinTheta + centerX;
5145  srcY = (float)(x - newWidth / 2) * sinTheta + (float)(y - newHeight / 2) * cosTheta + centerY;
5146 
5147  // Only draw from the source rectangle.
5148  if (srcX < 0 || srcX >= srcRect.width || srcY < 0 || srcY >= srcRect.height) {
5149  continue;
5150  }
5151 
5152  srcXint = (int)srcX + srcRect.x;
5153  srcYint = (int)srcY + srcRect.y;
5154 
5155  if (filter == PNTR_FILTER_NEARESTNEIGHBOR) {
5157  destX,
5158  destY,
5159  PNTR_PIXEL(src, srcXint, srcYint)
5160  );
5161  }
5162  else {
5163  // For bilinear, don't overscan.
5164  if (srcX >= srcRect.width - 1 || srcY >= srcRect.height - 1) {
5165  continue;
5166  }
5167 
5169  destX,
5170  destY,
5172  PNTR_PIXEL(src, srcXint, srcYint),
5173  PNTR_PIXEL(src, srcXint, srcYint + 1),
5174  PNTR_PIXEL(src, srcXint + 1, srcYint),
5175  PNTR_PIXEL(src, srcXint + 1, srcYint + 1),
5176  srcX - PNTR_FLOORF(srcX),
5177  srcY - PNTR_FLOORF(srcY)
5178  )
5179  );
5180  }
5181  }
5182  }
5183 }
5184 
5197 PNTR_API pntr_image* pntr_gen_image_gradient(int width, int height, pntr_color topLeft, pntr_color topRight, pntr_color bottomLeft, pntr_color bottomRight) {
5198  pntr_image* image = pntr_gen_image_color(width, height, PNTR_BLANK);
5199  if (image == NULL) {
5200  return NULL;
5201  }
5202 
5203  pntr_draw_rectangle_gradient(image, 0, 0, width, height, topLeft, topRight, bottomLeft, bottomRight);
5204 
5205  return image;
5206 }
5207 
5216  if (image == NULL) {
5217  return PNTR_CLITERAL(pntr_rectangle) {
5218  .x = 0,
5219  .y = 0,
5220  .width = 0,
5221  .height = 0
5222  };
5223  }
5224 
5225  return image->clip;
5226 }
5227 
5240 PNTR_API void pntr_image_set_clip(pntr_image* image, int x, int y, int width, int height) {
5241  if (image == NULL) {
5242  return;
5243  }
5244 
5245  pntr_rectangle clip;
5246  if (_pntr_rectangle_intersect(x, y, width, height, 0, 0, image->width, image->height, &clip)) {
5247  image->clip = clip;
5248  }
5249 }
5250 
5261  pntr_image_set_clip(image, clip.x, clip.y, clip.width, clip.height);
5262 }
5263 
5272  if (image == NULL) {
5273  return;
5274  }
5275 
5276  image->clip.x = 0;
5277  image->clip.y = 0;
5278  image->clip.width = image->width;
5279  image->clip.height = image->height;
5280 }
5281 
5291 PNTR_API inline void* pntr_load_memory(size_t size) {
5292  return PNTR_MALLOC(size);
5293 }
5294 
5302 PNTR_API void pntr_unload_memory(void* pointer) {
5303  if (pointer != NULL) {
5304  PNTR_FREE(pointer);
5305  }
5306 }
5307 
5319 PNTR_API inline void* pntr_memory_copy(void* destination, void* source, size_t size) {
5320  return PNTR_MEMCPY(destination, source, size);
5321 }
5322 
5327 #ifdef __cplusplus
5328 }
5329 #endif
5330 
5331 #endif // PNTR_IMPLEMENTATION_ONCE
5332 #endif // PNTR_IMPLEMENTATION
#define PNTR_BLANK
Definition: pntr.h:897
#define PNTR_WHITE
Definition: pntr.h:872
#define PNTR_WHITE_VALUE
Definition: pntr.h:884
#define PNTR_SAVE_IMAGE_TO_MEMORY
Definition: pntr.h:153
#define PNTR_SAVE_FILE
Definition: pntr.h:185
#define PNTR_LOAD_FILE
Definition: pntr.h:177
#define PNTR_LOAD_IMAGE_FROM_MEMORY
Definition: pntr.h:164
#define PNTR_FABSF(a)
Definition: pntr.h:1210
#define PNTR_MAX(a, b)
Definition: pntr.h:1292
#define PNTR_CEILF
Definition: pntr.h:1199
#define PNTR_SINF
Definition: pntr.h:1154
#define PNTR_FLOORF(x)
Definition: pntr.h:1221
#define PNTR_FMODF
Definition: pntr.h:1240
#define PNTR_COSF
Definition: pntr.h:1178
#define PNTR_MIN(a, b)
Definition: pntr.h:1304
#define PNTR_PI
Definition: pntr.h:1119
#define PNTR_DEG2RAD
Definition: pntr.h:1130
#define PNTR_MALLOC(size)
Definition: pntr.h:942
#define PNTR_MEMSET(str, c, n)
Definition: pntr.h:992
#define PNTR_FREE(ptr)
Definition: pntr.h:955
#define PNTR_MEMCPY(dest, src, n)
Definition: pntr.h:984
PNTR_API pntr_color pntr_color_invert(pntr_color color)
Definition: pntr.h:4091
PNTR_API pntr_image * pntr_gen_image_color(int width, int height, pntr_color color)
Definition: pntr.h:1549
PNTR_API pntr_font * pntr_load_font_ttf_from_memory(const unsigned char *fileData, unsigned int dataSize, int fontSize)
Definition: pntr.h:3974
PNTR_API void pntr_draw_triangle_vec(pntr_image *dst, pntr_vector point1, pntr_vector point2, pntr_vector point3, pntr_color color)
Definition: pntr.h:2422
PNTR_API void pntr_image_set_clip(pntr_image *image, int x, int y, int width, int height)
Definition: pntr.h:5240
PNTR_API void pntr_image_set_clip_rec(pntr_image *image, pntr_rectangle clip)
Definition: pntr.h:5260
PNTR_API pntr_color pntr_get_color(unsigned int hexValue)
Definition: pntr.h:1832
PNTR_API void pntr_draw_circle(pntr_image *dst, int centerX, int centerY, int radius, pntr_color color)
Definition: pntr.h:2241
PNTR_API 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)
Definition: pntr.h:5034
PNTR_API void pntr_image_color_invert(pntr_image *image)
Definition: pntr.h:4109
struct pntr_image pntr_image
PNTR_API void pntr_draw_triangle_fill_vec(pntr_image *dst, pntr_vector point1, pntr_vector point2, pntr_vector point3, pntr_color color)
Definition: pntr.h:2528
PNTR_API void pntr_unload_image(pntr_image *image)
Definition: pntr.h:1744
PNTR_API void pntr_image_color_tint(pntr_image *image, pntr_color color)
Definition: pntr.h:3290
PNTR_API void pntr_draw_line_horizontal(pntr_image *dst, int posX, int posY, int width, pntr_color color)
Definition: pntr.h:2038
PNTR_API void pntr_blend_color(pntr_color *dst, pntr_color src)
Definition: pntr.h:1592
PNTR_API pntr_image * pntr_image_from_image(pntr_image *image, int x, int y, int width, int height)
Definition: pntr.h:1672
PNTR_API void pntr_image_alpha_crop(pntr_image *image, float threshold)
Definition: pntr.h:4549
PNTR_API 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)
Definition: pntr.h:4803
PNTR_API pntr_rectangle pntr_image_get_clip(pntr_image *image)
Definition: pntr.h:5215
pntr_error
Definition: pntr.h:539
PNTR_API void pntr_image_color_contrast(pntr_image *image, float contrast)
Definition: pntr.h:4630
PNTR_API void pntr_draw_text(pntr_image *dst, pntr_font *font, const char *text, int posX, int posY, pntr_color color)
Definition: pntr.h:3626
union pntr_color pntr_color
PNTR_API pntr_image * pntr_load_image(const char *fileName)
Definition: pntr.h:2746
PNTR_API pntr_rectangle pntr_image_alpha_border(pntr_image *image, float threshold)
Definition: pntr.h:4459
PNTR_API void pntr_draw_point(pntr_image *dst, int x, int y, pntr_color color)
Definition: pntr.h:1885
PNTR_API void pntr_draw_triangle_fill(pntr_image *dst, int x1, int y1, int x2, int y2, int x3, int y3, pntr_color color)
Definition: pntr.h:2456
PNTR_API pntr_image * pntr_new_image(int width, int height)
Definition: pntr.h:1516
PNTR_API pntr_color pntr_color_alpha_blend(pntr_color dst, pntr_color src)
Definition: pntr.h:2799
pntr_pixelformat
Definition: pntr.h:497
PNTR_API unsigned char * pntr_save_image_to_memory(pntr_image *image, pntr_image_type type, unsigned int *dataSize)
Definition: pntr.h:4407
PNTR_API int pntr_measure_text(pntr_font *font, const char *text)
Definition: pntr.h:3761
PNTR_API void pntr_draw_image_rec(pntr_image *dst, pntr_image *src, pntr_rectangle srcRect, int posX, int posY)
Definition: pntr.h:2815
PNTR_API unsigned char * pntr_load_file(const char *fileName, unsigned int *bytesRead)
Definition: pntr.h:4175
PNTR_API pntr_image * pntr_gen_image_gradient(int width, int height, pntr_color topLeft, pntr_color topRight, pntr_color bottomLeft, pntr_color bottomRight)
Definition: pntr.h:5197
PNTR_API int pntr_get_pixel_data_size(int width, int height, pntr_pixelformat pixelFormat)
Definition: pntr.h:4322
PNTR_API pntr_color pntr_color_bilinear_interpolate(pntr_color color00, pntr_color color01, pntr_color color10, pntr_color color11, float coordinateX, float coordinateY)
Definition: pntr.h:4980
PNTR_API bool pntr_image_crop(pntr_image *image, int x, int y, int width, int height)
Definition: pntr.h:4513
PNTR_API void pntr_put_horizontal_line_unsafe(pntr_image *dst, int posX, int posY, int width, pntr_color color)
Definition: pntr.h:1760
PNTR_API void pntr_draw_point_unsafe(pntr_image *dst, int x, int y, pntr_color color)
Definition: pntr.h:1878
PNTR_API void pntr_image_color_fade(pntr_image *image, float alpha)
Definition: pntr.h:3186
PNTR_API pntr_image * pntr_gen_image_text(pntr_font *font, const char *text, pntr_color tint)
Definition: pntr.h:3836
PNTR_API pntr_color pntr_image_get_color(pntr_image *image, int x, int y)
Definition: pntr.h:2645
PNTR_API pntr_image * pntr_load_image_from_memory(pntr_image_type type, const unsigned char *fileData, unsigned int dataSize)
Definition: pntr.h:2731
PNTR_API const char * pntr_load_file_text(const char *fileName)
Definition: pntr.h:4231
PNTR_API void pntr_draw_image(pntr_image *dst, pntr_image *src, int posX, int posY)
Definition: pntr.h:2780
PNTR_API void pntr_draw_line(pntr_image *dst, int startPosX, int startPosY, int endPosX, int endPosY, pntr_color color)
Definition: pntr.h:1919
PNTR_API bool pntr_save_image(pntr_image *image, const char *fileName)
Definition: pntr.h:4426
PNTR_API bool pntr_save_file(const char *fileName, const void *data, unsigned int bytesToWrite)
Definition: pntr.h:4284
PNTR_API void pntr_draw_triangle(pntr_image *dst, int x1, int y1, int x2, int y2, int x3, int y3, pntr_color color)
Definition: pntr.h:2438
PNTR_API void pntr_unload_memory(void *pointer)
Definition: pntr.h:5302
PNTR_API void pntr_draw_rectangle_rec(pntr_image *dst, pntr_rectangle rec, pntr_color color)
Definition: pntr.h:2105
PNTR_API void pntr_draw_ellipse_fill(pntr_image *dst, int centerX, int centerY, int radiusX, int radiusY, pntr_color color)
Definition: pntr.h:2377
PNTR_API void pntr_draw_rectangle_fill(pntr_image *dst, int posX, int posY, int width, int height, pntr_color color)
Definition: pntr.h:2155
PNTR_API pntr_font * pntr_load_font_ttf(const char *fileName, int fontSize)
Definition: pntr.h:3938
PNTR_API void pntr_draw_rectangle_fill_rec(pntr_image *dst, pntr_rectangle rect, pntr_color color)
Definition: pntr.h:2168
struct pntr_font pntr_font
PNTR_API void pntr_draw_text_wrapped(pntr_image *dst, pntr_font *font, const char *text, int posX, int posY, int maxWidth, pntr_color tint)
Definition: pntr.h:3681
PNTR_API void * pntr_set_error(pntr_error error)
Definition: pntr.h:1494
PNTR_API pntr_image * pntr_image_resize(pntr_image *image, int newWidth, int newHeight, pntr_filter filter)
Definition: pntr.h:2980
PNTR_API void pntr_set_pixel_color(void *dstPtr, pntr_pixelformat dstPixelFormat, pntr_color color)
Definition: pntr.h:3216
PNTR_API void pntr_unload_file(unsigned char *fileData)
Definition: pntr.h:4447
#define PNTR_PIXEL(image, x, y)
Definition: pntr.h:1455
struct pntr_vector pntr_vector
PNTR_API pntr_font * pntr_load_font_tty(const char *fileName, int glyphWidth, int glyphHeight, const char *characters)
Definition: pntr.h:3450
PNTR_API pntr_color pntr_color_brightness(pntr_color color, float factor)
Definition: pntr.h:3127
PNTR_API void * pntr_memory_copy(void *destination, void *source, size_t size)
Definition: pntr.h:5319
PNTR_API void pntr_image_color_brightness(pntr_image *image, float factor)
Definition: pntr.h:4131
PNTR_API pntr_font * pntr_load_font_bmf(const char *fileName, const char *characters)
Definition: pntr.h:3314
PNTR_API void pntr_unload_file_text(const char *text)
Definition: pntr.h:4259
PNTR_API pntr_font * pntr_font_copy(pntr_font *font)
Definition: pntr.h:3549
#define PNTR_API
Definition: pntr.h:275
PNTR_API pntr_image_type pntr_get_file_image_type(const char *filePath)
Definition: pntr.h:2665
PNTR_API pntr_color pntr_color_tint(pntr_color color, pntr_color tint)
Definition: pntr.h:3104
PNTR_API void pntr_image_alpha_mask(pntr_image *image, pntr_image *alphaMask, int posX, int posY)
Definition: pntr.h:4661
PNTR_API void * pntr_image_to_pixelformat(pntr_image *image, unsigned int *dataSize, pntr_pixelformat pixelFormat)
Definition: pntr.h:4355
PNTR_API pntr_font * pntr_load_font_default(void)
Definition: pntr.h:3869
#define PNTR_PIXELFORMAT
Definition: pntr.h:298
PNTR_API void pntr_image_color_replace(pntr_image *image, pntr_color color, pntr_color replace)
Definition: pntr.h:3078
struct pntr_rectangle pntr_rectangle
PNTR_API void pntr_image_reset_clip(pntr_image *image)
Definition: pntr.h:5271
PNTR_API pntr_color pntr_get_pixel_color(void *srcPtr, pntr_pixelformat srcPixelFormat)
Definition: pntr.h:3247
PNTR_API pntr_image * pntr_image_scale(pntr_image *image, float scaleX, float scaleY, pntr_filter filter)
Definition: pntr.h:2960
pntr_image_type
Definition: pntr.h:579
PNTR_API void pntr_draw_image_tint_rec(pntr_image *dst, pntr_image *src, pntr_rectangle srcRect, int posX, int posY, pntr_color tint)
Definition: pntr.h:2831
PNTR_API void pntr_draw_circle_fill(pntr_image *dst, int centerX, int centerY, int radius, pntr_color color)
Definition: pntr.h:2289
PNTR_API void pntr_draw_ellipse(pntr_image *dst, int centerX, int centerY, int radiusX, int radiusY, pntr_color color)
Definition: pntr.h:2327
PNTR_API void pntr_draw_rectangle(pntr_image *dst, int posX, int posY, int width, int height, pntr_color color)
Definition: pntr.h:2122
PNTR_API pntr_color pntr_color_contrast(pntr_color color, float contrast)
Definition: pntr.h:4569
PNTR_API pntr_image * pntr_image_from_pixelformat(const void *data, int width, int height, pntr_pixelformat pixelFormat)
Definition: pntr.h:2909
PNTR_API pntr_image * pntr_image_subimage(pntr_image *image, int x, int y, int width, int height)
Definition: pntr.h:1712
PNTR_API pntr_font * pntr_font_scale(pntr_font *font, float scaleX, float scaleY, pntr_filter filter)
Definition: pntr.h:3583
PNTR_API void pntr_clear_background(pntr_image *image, pntr_color color)
Definition: pntr.h:1775
PNTR_API void * pntr_load_memory(size_t size)
Definition: pntr.h:5291
PNTR_API void pntr_image_flip(pntr_image *image, bool horizontal, bool vertical)
Definition: pntr.h:3044
PNTR_API pntr_image * pntr_image_copy(pntr_image *image)
Definition: pntr.h:1563
PNTR_API pntr_image * pntr_image_rotate(pntr_image *image, float degrees, pntr_filter filter)
Definition: pntr.h:4918
PNTR_API void pntr_draw_image_tint(pntr_image *dst, pntr_image *src, int posX, int posY, pntr_color tint)
Definition: pntr.h:2767
pntr_filter
Definition: pntr.h:517
PNTR_API pntr_color pntr_color_fade(pntr_color color, float alpha)
Definition: pntr.h:3160
PNTR_API pntr_color pntr_new_color(unsigned char r, unsigned char g, unsigned char b, unsigned char a)
Definition: pntr.h:1814
PNTR_API bool pntr_image_resize_canvas(pntr_image *image, int newWidth, int newHeight, int offsetX, int offsetY, pntr_color fill)
Definition: pntr.h:4710
PNTR_API void pntr_draw_line_vertical(pntr_image *dst, int posX, int posY, int height, pntr_color color)
Definition: pntr.h:2073
PNTR_API pntr_vector pntr_measure_text_ex(pntr_font *font, const char *text, int textLength)
Definition: pntr.h:3774
PNTR_API void pntr_draw_image_rotated(pntr_image *dst, pntr_image *src, int posX, int posY, float degrees, float offsetX, float offsetY, pntr_filter filter)
Definition: pntr.h:5006
PNTR_API void pntr_unload_font(pntr_font *font)
Definition: pntr.h:3530
@ PNTR_ERROR_NOT_SUPPORTED
Definition: pntr.h:558
@ PNTR_ERROR_INVALID_ARGS
Definition: pntr.h:548
@ PNTR_ERROR_FAILED_TO_WRITE
Definition: pntr.h:568
@ PNTR_ERROR_UNKNOWN
Definition: pntr.h:573
@ PNTR_ERROR_NONE
Definition: pntr.h:543
@ PNTR_ERROR_NO_MEMORY
Definition: pntr.h:553
@ PNTR_ERROR_FAILED_TO_OPEN
Definition: pntr.h:563
@ PNTR_PIXELFORMAT_ARGB8888
Definition: pntr.h:506
@ PNTR_PIXELFORMAT_RGBA8888
Definition: pntr.h:501
@ PNTR_PIXELFORMAT_GRAYSCALE
Definition: pntr.h:511
@ PNTR_IMAGE_TYPE_JPG
Definition: pntr.h:591
@ PNTR_IMAGE_TYPE_BMP
Definition: pntr.h:595
@ PNTR_IMAGE_TYPE_PNG
Definition: pntr.h:587
@ PNTR_IMAGE_TYPE_UNKNOWN
Definition: pntr.h:583
@ PNTR_FILTER_NEARESTNEIGHBOR
Definition: pntr.h:523
@ PNTR_FILTER_BILINEAR
Definition: pntr.h:530
#define PNTR_STRCODEPOINT
Definition: pntr.h:1101
#define PNTR_STRLEN
Definition: pntr.h:1061
#define PNTR_STRCHR
Definition: pntr.h:1049
#define PNTR_STRSTR
Definition: pntr.h:1037
#define PNTR_STRSIZE(text)
Definition: pntr.h:1073
char pntr_codepoint_t
Definition: pntr.h:1020
Definition: pntr.h:328
unsigned char g
Definition: pntr.h:338
unsigned char b
Definition: pntr.h:343
unsigned char r
Definition: pntr.h:333
unsigned char a
Definition: pntr.h:348
Definition: pntr.h:467
pntr_rectangle * srcRects
Definition: pntr.h:476
char * characters
Definition: pntr.h:486
pntr_rectangle * glyphRects
Definition: pntr.h:481
int charactersLen
Definition: pntr.h:491
pntr_image * atlas
Definition: pntr.h:471
Definition: pntr.h:404
bool subimage
Definition: pntr.h:430
int width
Definition: pntr.h:413
int height
Definition: pntr.h:418
int pitch
Definition: pntr.h:423
pntr_rectangle clip
Definition: pntr.h:439
pntr_color * data
Definition: pntr.h:408
Definition: pntr.h:376
int x
Definition: pntr.h:380
int height
Definition: pntr.h:395
int y
Definition: pntr.h:385
int width
Definition: pntr.h:390
Definition: pntr.h:445
int x
Definition: pntr.h:449
int y
Definition: pntr.h:454
Definition: pntr.h:314
uint32_t value
Definition: pntr.h:318