pntr
Header-only CPU graphics library
Loading...
Searching...
No Matches
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
303#ifndef PNTR_CLITERAL
304 #if defined(__cplusplus)
305 #define PNTR_CLITERAL(type) type
306 #else
318 #define PNTR_CLITERAL(type) (type)
319 #endif
320#endif
321
333typedef union pntr_color {
337 uint32_t value;
338
347 struct pntr_color_rgba_t {
348 #if defined(PNTR_PIXELFORMAT_RGBA)
349 unsigned char r;
350 unsigned char g;
351 unsigned char b;
352 unsigned char a;
353 #elif defined(PNTR_PIXELFORMAT_ARGB)
354 unsigned char b;
355 unsigned char g;
356 unsigned char r;
357 unsigned char a;
358 #endif
359 } rgba;
360} pntr_color;
361
365typedef struct pntr_rectangle {
366 int x;
367 int y;
368 int width;
369 int height;
370} pntr_rectangle;
371
378typedef struct pntr_image {
379 pntr_color* data;
380 int width;
381 int height;
382 int pitch;
389 bool subimage;
390
398 pntr_rectangle clip;
399} pntr_image;
400
404typedef struct pntr_vector {
405 int x;
406 int y;
407} pntr_vector;
408
418typedef struct pntr_font {
419 pntr_image* atlas;
420 pntr_rectangle* srcRects;
421 pntr_rectangle* glyphRects;
422 char* characters;
423 int charactersLen;
424 void* user_data;
425} pntr_font;
426
430typedef enum pntr_pixelformat {
431 PNTR_PIXELFORMAT_RGBA8888 = 0,
432 PNTR_PIXELFORMAT_ARGB8888,
433 PNTR_PIXELFORMAT_GRAYSCALE
434} pntr_pixelformat;
435
439typedef enum pntr_filter {
440 PNTR_FILTER_NEARESTNEIGHBOR = 0,
441 PNTR_FILTER_BILINEAR
442} pntr_filter;
443
450typedef enum pntr_error {
451 PNTR_ERROR_NONE = 0,
452 PNTR_ERROR_INVALID_ARGS = -1,
453 PNTR_ERROR_NO_MEMORY = -2,
454 PNTR_ERROR_NOT_SUPPORTED = -3,
455 PNTR_ERROR_FAILED_TO_OPEN = -4,
456 PNTR_ERROR_FAILED_TO_WRITE = -5,
457 PNTR_ERROR_UNKNOWN = -6
458} pntr_error;
459
463typedef enum pntr_image_type {
464 PNTR_IMAGE_TYPE_UNKNOWN = 0,
465 PNTR_IMAGE_TYPE_PNG,
466 PNTR_IMAGE_TYPE_JPG,
467 PNTR_IMAGE_TYPE_BMP
468} pntr_image_type;
469
470#ifdef __cplusplus
471extern "C" {
472#endif
473
474PNTR_API pntr_image* pntr_new_image(int width, int height);
475PNTR_API pntr_image* pntr_gen_image_color(int width, int height, pntr_color color);
476PNTR_API pntr_image* pntr_image_copy(pntr_image* image);
477PNTR_API pntr_image* pntr_image_from_image(pntr_image* image, int x, int y, int width, int height);
478PNTR_API pntr_image* pntr_image_subimage(pntr_image* image, int x, int y, int width, int height);
479PNTR_API pntr_rectangle pntr_image_get_clip(pntr_image* image);
480PNTR_API void pntr_image_set_clip(pntr_image* image, int x, int y, int width, int height);
481PNTR_API void pntr_image_set_clip_rec(pntr_image* image, pntr_rectangle clip);
482PNTR_API void pntr_image_reset_clip(pntr_image* image);
483PNTR_API void pntr_unload_image(pntr_image* image);
484PNTR_API void pntr_clear_background(pntr_image* image, pntr_color color);
485PNTR_API void pntr_draw_point(pntr_image* dst, int x, int y, pntr_color color);
486PNTR_API void pntr_draw_point_vec(pntr_image* dst, pntr_vector* point, pntr_color color);
487PNTR_API void pntr_draw_points(pntr_image* dst, pntr_vector* points, int pointsCount, pntr_color color);
488PNTR_API void pntr_draw_line(pntr_image* dst, int startPosX, int startPosY, int endPosX, int endPosY, pntr_color color);
489PNTR_API void pntr_draw_line_curve(pntr_image* dst, pntr_vector point1, pntr_vector point2, pntr_vector point3, pntr_vector point4, int segments, pntr_color color);
490PNTR_API void pntr_draw_line_vec(pntr_image* dst, pntr_vector start, pntr_vector end, pntr_color color);
491PNTR_API void pntr_draw_line_vertical(pntr_image* dst, int posX, int posY, int height, pntr_color color);
492PNTR_API void pntr_draw_line_horizontal(pntr_image* dst, int posX, int posY, int width, pntr_color color);
493PNTR_API void pntr_draw_rectangle(pntr_image* dst, int posX, int posY, int width, int height, pntr_color color);
494PNTR_API void pntr_draw_rectangle_rec(pntr_image* dst, pntr_rectangle rec, pntr_color color);
495PNTR_API void pntr_draw_rectangle_fill(pntr_image* dst, int posX, int posY, int width, int height, pntr_color color);
496PNTR_API void pntr_draw_rectangle_fill_rec(pntr_image* dst, pntr_rectangle rect, pntr_color color);
497PNTR_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);
498PNTR_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);
499PNTR_API void pntr_draw_triangle(pntr_image* dst, int x1, int y1, int x2, int y2, int x3, int y3, pntr_color color);
500PNTR_API void pntr_draw_triangle_vec(pntr_image* dst, pntr_vector point1, pntr_vector point2, pntr_vector point3, pntr_color color);
501PNTR_API void pntr_draw_triangle_fill(pntr_image* dst, int x1, int y1, int x2, int y2, int x3, int y3, pntr_color color);
502PNTR_API void pntr_draw_triangle_fill_vec(pntr_image* dst, pntr_vector point1, pntr_vector point2, pntr_vector point3, pntr_color color);
503PNTR_API void pntr_draw_ellipse(pntr_image* dst, int centerX, int centerY, int radiusX, int radiusY, pntr_color color);
504PNTR_API void pntr_draw_ellipse_fill(pntr_image* dst, int centerX, int centerY, int radiusX, int radiusY, pntr_color color);
505PNTR_API void pntr_draw_circle(pntr_image* dst, int centerX, int centerY, int radius, pntr_color color);
506PNTR_API void pntr_draw_circle_fill(pntr_image* dst, int centerX, int centerY, int radius, pntr_color color);
507PNTR_API void pntr_draw_polygon(pntr_image* dst, pntr_vector* points, int numPoints, pntr_color color);
508PNTR_API void pntr_draw_polygon_fill(pntr_image* dst, pntr_vector* points, int numPoints, pntr_color color);
509PNTR_API void pntr_draw_polyline(pntr_image* dst, pntr_vector* points, int numPoints, pntr_color color);
510PNTR_API void pntr_draw_arc(pntr_image* dst, int centerX, int centerY, float radius, float startAngle, float endAngle, int segments, pntr_color color);
511PNTR_API void pntr_draw_arc_fill(pntr_image* dst, int centerX, int centerY, float radius, float startAngle, float endAngle, int segments, pntr_color color);
512PNTR_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);
513PNTR_API void pntr_draw_rectangle_rounded_fill(pntr_image* dst, int x, int y, int width, int height, int cornerRadius, pntr_color color);
514PNTR_API void pntr_draw_image(pntr_image* dst, pntr_image* src, int posX, int posY);
515PNTR_API void pntr_draw_image_rec(pntr_image* dst, pntr_image* src, pntr_rectangle srcRect, int posX, int posY);
516PNTR_API void pntr_draw_image_tint(pntr_image* dst, pntr_image* src, int posX, int posY, pntr_color tint);
517PNTR_API void pntr_draw_image_tint_rec(pntr_image* dst, pntr_image* src, pntr_rectangle srcRect, int posX, int posY, pntr_color tint);
518PNTR_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);
519PNTR_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);
520PNTR_API void pntr_draw_image_flipped(pntr_image* dst, pntr_image* src, int posX, int posY, bool flipHorizontal, bool flipVertical, bool flipDiagonal);
521PNTR_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);
522PNTR_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);
523PNTR_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);
524PNTR_API void pntr_draw_text(pntr_image* dst, pntr_font* font, const char* text, int posX, int posY, pntr_color tint);
525PNTR_API void pntr_draw_text_len(pntr_image* dst, pntr_font* font, const char* text, int textLength, int posX, int posY, pntr_color tint);
526PNTR_API void pntr_draw_text_wrapped(pntr_image* dst, pntr_font* font, const char* text, int posX, int posY, int maxWidth, pntr_color tint);
527#ifdef PNTR_ENABLE_VARGS
528PNTR_API void pntr_draw_text_ex(pntr_image* dst, pntr_font* font, int posX, int posY, pntr_color tint, int maxlen, const char* text, ...);
529#endif
530PNTR_API pntr_color pntr_new_color(unsigned char r, unsigned char g, unsigned char b, unsigned char a);
531PNTR_API pntr_color pntr_get_color(unsigned int hexValue);
532PNTR_API unsigned char pntr_color_r(pntr_color color);
533PNTR_API unsigned char pntr_color_g(pntr_color color);
534PNTR_API unsigned char pntr_color_b(pntr_color color);
535PNTR_API unsigned char pntr_color_a(pntr_color color);
536PNTR_API void pntr_color_set_r(pntr_color* color, unsigned char r);
537PNTR_API void pntr_color_set_g(pntr_color* color, unsigned char g);
538PNTR_API void pntr_color_set_b(pntr_color* color, unsigned char b);
539PNTR_API void pntr_color_set_a(pntr_color* color, unsigned char a);
540PNTR_API pntr_color pntr_image_get_color(pntr_image* image, int x, int y);
541PNTR_API bool pntr_save_file(const char *fileName, const void *data, unsigned int bytesToWrite);
542PNTR_API void* pntr_image_to_pixelformat(pntr_image* image, unsigned int* dataSize, pntr_pixelformat pixelFormat);
543PNTR_API bool pntr_save_image(pntr_image* image, const char* fileName);
544PNTR_API unsigned char* pntr_save_image_to_memory(pntr_image* image, pntr_image_type type, unsigned int* dataSize);
545PNTR_API int pntr_get_pixel_data_size(int width, int height, pntr_pixelformat pixelFormat);
546PNTR_API pntr_image* pntr_load_image(const char* fileName);
547PNTR_API pntr_image* pntr_load_image_from_memory(pntr_image_type type, const unsigned char* fileData, unsigned int dataSize);
548PNTR_API pntr_image* pntr_image_from_pixelformat(const void* data, int width, int height, pntr_pixelformat pixelFormat);
549PNTR_API void* pntr_set_error(pntr_error error);
550PNTR_API const char* pntr_get_error(void);
551PNTR_API pntr_error pntr_get_error_code(void);
552PNTR_API pntr_image* pntr_image_resize(pntr_image* image, int newWidth, int newHeight, pntr_filter filter);
553PNTR_API pntr_image* pntr_image_scale(pntr_image* image, float scaleX, float scaleY, pntr_filter filter);
554PNTR_API void pntr_image_color_replace(pntr_image* image, pntr_color color, pntr_color replace);
555PNTR_API pntr_color pntr_color_tint(pntr_color color, pntr_color tint);
556PNTR_API void pntr_image_color_tint(pntr_image* image, pntr_color color);
557PNTR_API pntr_color pntr_color_fade(pntr_color color, float alpha);
558PNTR_API void pntr_image_color_fade(pntr_image* image, float alpha);
559PNTR_API pntr_color pntr_color_brightness(pntr_color color, float factor);
560PNTR_API pntr_color pntr_get_pixel_color(void* srcPtr, pntr_pixelformat srcPixelFormat);
561PNTR_API void pntr_set_pixel_color(void* dstPtr, pntr_pixelformat dstPixelFormat, pntr_color color);
562PNTR_API pntr_font* pntr_load_font_default(void);
563PNTR_API void pntr_unload_font(pntr_font* font);
564PNTR_API pntr_font* pntr_font_copy(pntr_font* font);
565PNTR_API pntr_font* pntr_font_scale(pntr_font* font, float scaleX, float scaleY, pntr_filter filter);
566PNTR_API pntr_font* pntr_load_font_bmf(const char* fileName, const char* characters);
567PNTR_API pntr_font* pntr_load_font_bmf_from_image(pntr_image* image, const char* characters);
568PNTR_API pntr_font* pntr_load_font_bmf_from_memory(const unsigned char* fileData, unsigned int dataSize, const char* characters);
569PNTR_API int pntr_measure_text(pntr_font* font, const char* text);
570PNTR_API pntr_vector pntr_measure_text_ex(pntr_font* font, const char* text, int textLength);
571PNTR_API pntr_image* pntr_gen_image_text(pntr_font* font, const char* text, pntr_color tint, pntr_color backgroundColor);
572PNTR_API pntr_font* pntr_load_font_tty(const char* fileName, int glyphWidth, int glyphHeight, const char* characters);
573PNTR_API pntr_font* pntr_load_font_tty_from_memory(const unsigned char* fileData, unsigned int dataSize, int glyphWidth, int glyphHeight, const char* characters);
574PNTR_API pntr_font* pntr_load_font_tty_from_image(pntr_image* image, int glyphWidth, int glyphHeight, const char* characters);
575PNTR_API unsigned char* pntr_load_file(const char *fileName, unsigned int *bytesRead);
576PNTR_API void pntr_unload_file(unsigned char* fileData);
577PNTR_API const char* pntr_load_file_text(const char *fileName);
578PNTR_API void pntr_unload_file_text(const char* text);
579PNTR_API pntr_font* pntr_load_font_ttf(const char* fileName, int fontSize);
580PNTR_API pntr_font* pntr_load_font_ttf_from_memory(const unsigned char* fileData, unsigned int dataSize, int fontSize);
581PNTR_API pntr_color pntr_color_invert(pntr_color color);
582PNTR_API void pntr_image_color_invert(pntr_image* image);
583PNTR_API pntr_color pntr_color_alpha_blend(pntr_color dst, pntr_color src);
584PNTR_API pntr_rectangle pntr_image_alpha_border(pntr_image* image, float threshold);
585PNTR_API bool pntr_image_crop(pntr_image* image, int x, int y, int width, int height);
586PNTR_API void pntr_image_alpha_crop(pntr_image* image, float threshold);
587PNTR_API void pntr_image_color_brightness(pntr_image* image, float factor);
588PNTR_API void pntr_image_flip(pntr_image* image, bool horizontal, bool vertical);
589PNTR_API pntr_color pntr_color_contrast(pntr_color color, float contrast);
590PNTR_API void pntr_image_color_contrast(pntr_image* image, float contrast);
591PNTR_API void pntr_image_alpha_mask(pntr_image* image, pntr_image* alphaMask, int posX, int posY);
592PNTR_API bool pntr_image_resize_canvas(pntr_image* image, int newWidth, int newHeight, int offsetX, int offsetY, pntr_color fill);
593PNTR_API pntr_image* pntr_image_rotate(pntr_image* image, float degrees, pntr_filter filter);
594PNTR_API pntr_image* pntr_gen_image_gradient(int width, int height, pntr_color topLeft, pntr_color topRight, pntr_color bottomLeft, pntr_color bottomRight);
595PNTR_API pntr_color pntr_color_bilinear_interpolate(pntr_color color00, pntr_color color01, pntr_color color10, pntr_color color11, float coordinateX, float coordinateY);
596PNTR_API void* pntr_load_memory(size_t size);
597PNTR_API void pntr_unload_memory(void* pointer);
598PNTR_API void* pntr_memory_copy(void* destination, void* source, size_t size);
599PNTR_API pntr_image_type pntr_get_file_image_type(const char* filePath);
600
601PNTR_API void pntr_draw_line_thick(pntr_image* dst, int startPosX, int startPosY, int endPosX, int endPosY, int thickness, pntr_color color);
602PNTR_API void pntr_draw_line_thick_vec(pntr_image* dst, pntr_vector start, pntr_vector end, int thickness, pntr_color color);
603PNTR_API void pntr_draw_rectangle_thick(pntr_image* dst, int posX, int posY, int width, int height, int thickness, pntr_color color);
604PNTR_API void pntr_draw_rectangle_thick_rec(pntr_image* dst, pntr_rectangle rect, int thickness, pntr_color color);
605PNTR_API void pntr_draw_triangle_thick(pntr_image* dst, int x1, int y1, int x2, int y2, int x3, int y3, int thickness, pntr_color color);
606PNTR_API void pntr_draw_triangle_thick_vec(pntr_image* dst, pntr_vector point1, pntr_vector point2, pntr_vector point3, int thickness, pntr_color color);
607PNTR_API void pntr_draw_ellipse_thick(pntr_image* dst, int centerX, int centerY, int radiusX, int radiusY, int thickness, pntr_color color);
608PNTR_API void pntr_draw_circle_thick(pntr_image* dst, int centerX, int centerY, int radius, int thickness, pntr_color color);
609PNTR_API void pntr_draw_polygon_thick(pntr_image* dst, pntr_vector* points, int numPoints, int thickness, pntr_color color);
610PNTR_API void pntr_draw_polyline_thick(pntr_image* dst, pntr_vector* points, int numPoints, int thickness, pntr_color color);
611PNTR_API void pntr_draw_arc_thick(pntr_image* dst, int centerX, int centerY, float radius, float startAngle, float endAngle, int segments, int thickness, pntr_color color);
612PNTR_API void pntr_draw_rectangle_thick_rounded(pntr_image* dst, int x, int y, int width, int height, int topLeftRadius, int topRightRadius, int bottomLeftRadius, int bottomRightRadius, int thickness, pntr_color color);
613PNTR_API void pntr_draw_line_vertical_thick(pntr_image* dst, int posX, int posY, int height, int thickness, pntr_color color);
614PNTR_API void pntr_draw_line_horizontal_thick(pntr_image* dst, int posX, int posY, int width, int thickness, pntr_color color);
615PNTR_API void pntr_draw_line_curve_thick(pntr_image* dst, pntr_vector point1, pntr_vector point2, pntr_vector point3, pntr_vector point4, int segments, int thickness, pntr_color color);
616
617// Internal
618PNTR_API void pntr_put_horizontal_line_unsafe(pntr_image* dst, int posX, int posY, int width, pntr_color color);
619PNTR_API void pntr_draw_point_unsafe(pntr_image* dst, int x, int y, pntr_color color);
620
621#ifdef __cplusplus
622}
623#endif
624
630#ifndef PNTR_LIGHTGRAY
634#define PNTR_LIGHTGRAY pntr_new_color(200, 200, 200, 255)
635#endif
636#ifndef PNTR_GRAY
640#define PNTR_GRAY pntr_new_color(130, 130, 130, 255)
641#endif
642#ifndef PNTR_DARKGRAY
646#define PNTR_DARKGRAY pntr_new_color(80, 80, 80, 255)
647#endif
648#ifndef PNTR_YELLOW
652#define PNTR_YELLOW pntr_new_color(253, 249, 0, 255)
653#endif
654#ifndef PNTR_GOLD
658#define PNTR_GOLD pntr_new_color(255, 203, 0, 255)
659#endif
660#ifndef PNTR_ORANGE
664#define PNTR_ORANGE pntr_new_color(255, 161, 0, 255)
665#endif
666#ifndef PNTR_PINK
670#define PNTR_PINK pntr_new_color(255, 109, 194, 255)
671#endif
672#ifndef PNTR_RED
676#define PNTR_RED pntr_new_color(230, 41, 55, 255)
677#endif
678#ifndef PNTR_MAROON
682#define PNTR_MAROON pntr_new_color(190, 33, 55, 255)
683#endif
684#ifndef PNTR_GREEN
688#define PNTR_GREEN pntr_new_color(0, 228, 48, 255)
689#endif
690#ifndef PNTR_LIME
694#define PNTR_LIME pntr_new_color(0, 158, 47, 255)
695#endif
696#ifndef PNTR_DARKGREEN
700#define PNTR_DARKGREEN pntr_new_color(0, 117, 44, 255)
701#endif
702#ifndef PNTR_SKYBLUE
706#define PNTR_SKYBLUE pntr_new_color(102, 191, 255, 255)
707#endif
708#ifndef PNTR_BLUE
712#define PNTR_BLUE pntr_new_color(0, 121, 241, 255)
713#endif
714#ifndef PNTR_DARKBLUE
718#define PNTR_DARKBLUE pntr_new_color(0, 82, 172, 255)
719#endif
720#ifndef PNTR_PURPLE
724#define PNTR_PURPLE pntr_new_color(200, 122, 255, 255)
725#endif
726#ifndef PNTR_VIOLET
730#define PNTR_VIOLET pntr_new_color(135, 60, 190, 255)
731#endif
732#ifndef PNTR_DARKPURPLE
736#define PNTR_DARKPURPLE pntr_new_color(112, 31, 126, 255)
737#endif
738#ifndef PNTR_BEIGE
742#define PNTR_BEIGE pntr_new_color(211, 176, 131, 255)
743#endif
744#ifndef PNTR_BROWN
748#define PNTR_BROWN pntr_new_color(127, 106, 79, 255)
749#endif
750#ifndef PNTR_DARKBROWN
754#define PNTR_DARKBROWN pntr_new_color(76, 63, 47, 255)
755#endif
756#ifndef PNTR_WHITE
760#define PNTR_WHITE pntr_new_color(255, 255, 255, 255)
761#endif
762
763#ifndef PNTR_WHITE_VALUE
772#define PNTR_WHITE_VALUE 4294967295
773#endif // PNTR_WHITE_VALUE
774
775#ifndef PNTR_BLACK
779#define PNTR_BLACK pntr_new_color(0, 0, 0, 255)
780#endif
781#ifndef PNTR_BLANK
785#define PNTR_BLANK pntr_new_color(0, 0, 0, 0)
786#endif
787#ifndef PNTR_MAGENTA
791#define PNTR_MAGENTA pntr_new_color(255, 0, 255, 255)
792#endif
793#ifndef PNTR_RAYWHITE
797#define PNTR_RAYWHITE pntr_new_color(245, 245, 245, 255)
798#endif
799
804#endif // PNTR_H__
805
806#ifdef PNTR_IMPLEMENTATION
807#ifndef PNTR_IMPLEMENTATION_ONCE
808#define PNTR_IMPLEMENTATION_ONCE
809
810#if defined(PNTR_ENABLE_UTF8) && !defined(_DOXYGEN_)
811 #include "external/utf8.h"
812 #define PNTR_STRSTR utf8str
813 #define PNTR_STRCHR utf8chr
814 #define PNTR_STRLEN utf8len
815 #define PNTR_STRSIZE utf8size
816 #define PNTR_STRCODEPOINT utf8codepoint
817 typedef utf8_int32_t pntr_codepoint_t;
818#else
826 typedef char pntr_codepoint_t;
827#endif
828
829#ifdef __cplusplus
830extern "C" {
831#endif
832
838#ifndef PNTR_MALLOC
839 #include <stdlib.h>
849 #define PNTR_MALLOC(size) malloc(size)
850#endif // PNTR_MALLOC
851
852#ifndef PNTR_FREE
853 #include <stdlib.h>
862 #define PNTR_FREE(ptr) free(ptr)
863#endif // PNTR_FREE
864
865#ifndef PNTR_REALLOC
866 #include <stdlib.h>
877 #define PNTR_REALLOC(ptr, new_size) realloc(ptr, new_size)
878#endif // PNTR_REALLOC
879
880#ifndef PNTR_MEMCPY
881 #include <string.h>
891 #define PNTR_MEMCPY(dest, src, n) memcpy(dest, src, (n))
892#endif // PNTR_MEMCPY
893
894#ifndef PNTR_MEMSET
895 #include <string.h>
899 #define PNTR_MEMSET(str, c, n) memset((str), (c), (n))
900#endif // PNTR_MEMSET
901
911#ifndef PNTR_STRSTR
912 #include <string.h>
925 #define PNTR_STRSTR strstr
926#endif
927
928#ifndef PNTR_STRCHR
929 #include <string.h>
937 #define PNTR_STRCHR strchr
938#endif
939
940#ifndef PNTR_STRLEN
941 #include <string.h>
949 #define PNTR_STRLEN strlen
950#endif
951
952#ifndef PNTR_STRSIZE
953 #include <string.h>
961 #define PNTR_STRSIZE(text) ((PNTR_STRLEN(text) + (size_t)1))
962#endif
963
964#ifndef PNTR_STRCODEPOINT
978 char* pntr_strcodepoint(const char * str, char* out_codepoint) {
979 if (str == NULL) {
980 *out_codepoint = 0;
981 }
982
983 *out_codepoint = str[0];
984 return (char*)(str + 1);
985 }
986
995 #define PNTR_STRCODEPOINT pntr_strcodepoint
996#endif
997
1007#ifndef PNTR_PI
1013 #define PNTR_PI 3.1415926535897932f
1014#endif
1015
1016#ifndef PNTR_DEG2RAD
1024 #define PNTR_DEG2RAD 0.017453293f
1025#endif
1026
1027#if !defined(PNTR_ENABLE_MATH) || defined(_DOXYGEN_)
1028 #ifndef PNTR_SINF
1034 float _pntr_sinf(float x) {
1035 static const float a0 = +1.91059300966915117e-31f;
1036 static const float a1 = +1.00086760103908896f;
1037 static const float a2 = -1.21276126894734565e-2f;
1038 static const float a3 = -1.38078780785773762e-1f;
1039 static const float a4 = -2.67353392911981221e-2f;
1040 static const float a5 = +2.08026600266304389e-2f;
1041 static const float a6 = -3.03996055049204407e-3f;
1042 static const float a7 = +1.38235642404333740e-4f;
1043 return a0 + x*(a1 + x*(a2 + x*(a3 + x*(a4 + x*(a5 + x*(a6 + x*a7))))));
1044 }
1045
1056 #define PNTR_SINF(value) _pntr_sinf(value)
1057 #endif // PNTR_SINF
1058
1059 #ifndef PNTR_COSF
1065 float _pntr_cosf(float x) {
1066 static const float a0 = 9.9995999154986614e-1f;
1067 static const float a1 = 1.2548995793001028e-3f;
1068 static const float a2 = -5.0648546280678015e-1f;
1069 static const float a3 = 1.2942246466519995e-2f;
1070 static const float a4 = 2.8668384702547972e-2f;
1071 static const float a5 = 7.3726485210586547e-3f;
1072 static const float a6 = -3.8510875386947414e-3f;
1073 static const float a7 = 4.7196604604366623e-4f;
1074 static const float a8 = -1.8776444013090451e-5f;
1075 return a0 + x*(a1 + x*(a2 + x*(a3 + x*(a4 + x*(a5 + x*(a6 + x*(a7 + x*a8)))))));
1076 }
1077
1088 #define PNTR_COSF(value) _pntr_cosf(value)
1089 #endif // PNTR_COSF
1090
1091 #ifndef PNTR_CEILF
1097 float _pntr_ceilf(float x) {
1098 if (x >= 0.0f) {
1099 int i = (int)x;
1100 return (x > i) ? (float)i + 1.0f : (float)i;
1101 } else {
1102 int t = (int)x;
1103 float r = x - (float)t;
1104 return (r > 0.0f) ? (float)t + 1.0f: (float)t;
1105 }
1106 }
1107
1117 #define PNTR_CEILF(x) _pntr_ceilf(x)
1118 #endif // PNTR_CEILF
1119
1120 #ifndef PNTR_FABSF
1130 #define PNTR_FABSF(x) (((x) < 0) ? -(x) : (x))
1131 #endif // PNTR_FABSF
1132
1133 #ifndef PNTR_FLOORF
1143 #define PNTR_FLOORF(x) (float)(((int)(x)) - (((x) < 0.0f) ? 1 : 0))
1144 #endif // PNTR_FLOORF
1145
1146 #ifndef PNTR_FMODF
1156 #define PNTR_FMODF(dividend, divisor) ((divisor) == 0.0f ? 0.0f : (dividend) - ((int)((dividend) / (divisor))) * (divisor))
1157 #endif // PNTR_FMOD
1158#else
1159 #ifndef PNTR_SINF
1160 #include <math.h>
1161 #define PNTR_SINF sinf
1162 #endif // PNTR_SINF
1163
1164 #ifndef PNTR_COSF
1165 #include <math.h>
1166 #define PNTR_COSF cosf
1167 #endif // PNTR_COSF
1168
1169 #ifndef PNTR_CEILF
1170 #include <math.h>
1171 #define PNTR_CEILF ceilf
1172 #endif // PNTR_CEILF
1173
1174 #ifndef PNTR_FABSF
1175 #include <math.h>
1176 #define PNTR_FABSF fabsf
1177 #endif // PNTR_FABSF
1178
1179 #ifndef PNTR_FLOORF
1180 #include <math.h>
1181 #define PNTR_FLOORF floorf
1182 #endif // PNTR_FLOORF
1183
1184 #ifndef PNTR_SQRTF
1185 #include <math.h>
1186 #define PNTR_SQRTF sqrtf
1187 #endif // PNTR_SQRTF
1188
1189 #ifndef PNTR_FMODF
1190 #include <math.h>
1191 #define PNTR_FMODF fmodf
1192 #endif // PNTR_FMODF
1193#endif // PNTR_ENABLE_MATH
1194
1195#ifndef PNTR_MAX
1204 #define PNTR_MAX(a, b) ((a) > (b) ? (a) : (b))
1205#endif
1206
1207#ifndef PNTR_MIN
1216 #define PNTR_MIN(a, b) ((a) < (b) ? (a) : (b))
1217#endif
1218
1223// STB TrueType
1224#ifdef PNTR_ENABLE_TTF
1225 #ifdef PNTR_NO_STB_TRUETYPE_IMPLEMENTATION
1226 #ifdef STB_TRUETYPE_IMPLEMENTATION
1227 #undef STB_TRUETYPE_IMPLEMENTATION
1228 #endif // STB_TRUETYPE_IMPLEMENTATION
1229 #else // PNTR_NO_STB_TRUETYPE_IMPLEMENTATION
1230
1231 #ifndef STBTT_ifloor
1232 #define STBTT_ifloor(x) ((int)PNTR_FLOORF(x))
1233 #endif
1234
1235 #ifndef STBTT_iceil
1236 #define STBTT_iceil(x) ((int)PNTR_CEILF(x))
1237 #endif
1238
1239 #ifndef STBTT_fmod
1240 #define STBTT_fmod(x, y) PNTR_FMODF((x), (y))
1241 #endif
1242
1243 #ifndef STBTT_cos
1244 #define STBTT_cos(x) PNTR_COSF((float)(x))
1245 #endif
1246
1247 #ifndef STBTT_fabs
1248 #define STBTT_fabs(x) PNTR_FABSF(x)
1249 #endif
1250
1251 #ifndef PNTR_ENABLE_MATH
1252 #ifndef STBTT_sqrt
1253 float _pntr_sqrtf(float number) {
1254 float guess = number / 2.0f;
1255 float epsilon = 1e-6f;
1256 while (true) {
1257 float next_guess = 0.5f * (guess + number / guess);
1258 if (PNTR_FABSF(next_guess - guess) < epsilon) {
1259 return next_guess;
1260 }
1261 guess = next_guess;
1262 }
1263 }
1264 #define STBTT_sqrt(x) _pntr_sqrtf(x)
1265 #endif // PNTR_SQRTF
1266
1267 #ifndef STBTT_pow
1268 float _pntr_pow(float base, float exponent) {
1269 float result = 1.0f;
1270 if (exponent >= 0) {
1271 for (int i = 0; i < exponent; i++) {
1272 result *= base;
1273 }
1274 } else {
1275 for (int i = 0; i > exponent; i--) {
1276 result /= base;
1277 }
1278 }
1279 return result;
1280 }
1281 #define STBTT_pow(x, y) _pntr_pow((x), (y))
1282 #endif
1283
1284 #ifndef STBTT_acos
1285 float _pntr_acos(float x) {
1286 float negate = (float)(x < 0);
1287 x = PNTR_FABSF(x);
1288 float ret = -0.0187293f;
1289 ret = ret * x;
1290 ret = ret + 0.0742610f;
1291 ret = ret * x;
1292 ret = ret - 0.2121144f;
1293 ret = ret * x;
1294 ret = ret + 1.5707288f;
1295 ret = ret * STBTT_sqrt(1.0f - x);
1296 ret = ret - 2 * negate * ret;
1297 return negate * PNTR_PI + ret;
1298 }
1299 #define STBTT_acos(x) _pntr_acos((x))
1300 #endif
1301 #else // PNTR_ENABLE_MATH
1302 #ifndef STBTT_sqrt
1303 #define STBTT_sqrt(x) sqrt(x)
1304 #endif
1305 #ifndef STBTT_pow
1306 #define STBTT_pow(x, y) pow(x, y)
1307 #endif
1308 #ifndef STBTT_acos
1309 #define STBTT_acos(x) acos(x)
1310 #endif
1311 #endif // PNTR_ENABLE_MATH
1312
1313 #ifndef STBTT_malloc
1314 #define STBTT_malloc(x,u) ((void)(u), PNTR_MALLOC(x))
1315 #endif // STBTT_malloc
1316
1317 #ifndef STBTT_free
1318 #define STBTT_free(x,u) ((void)(u), PNTR_FREE(x))
1319 #endif // STBTT_free
1320
1321 #ifndef STBTT_assert
1322 #define STBTT_assert(x) ((void)(0))
1323 #endif // STBTT_assert
1324
1325 #ifndef STBTT_strlen
1326 #define STBTT_strlen(x) PNTR_STRLEN(x)
1327 #endif // STBTT_strlen
1328
1329 #ifndef STBTT_memcpy
1330 #define STBTT_memcpy PNTR_MEMCPY
1331 #endif // STBTT_memcpy
1332
1333 #ifndef STBTT_memset
1334 #define STBTT_memset PNTR_MEMSET
1335 #endif // STBTT_memset
1336
1337 #define STB_TRUETYPE_IMPLEMENTATION
1338 #endif // PNTR_NO_STB_TRUETYPE_IMPLEMENTATION
1339
1340 #if defined(__GNUC__) || defined(__clang__)
1341 #pragma GCC diagnostic push
1342 #pragma GCC diagnostic ignored "-Wpragmas"
1343 #pragma GCC diagnostic ignored "-Wunknown-pragmas"
1344 #pragma GCC diagnostic ignored "-Wsign-conversion"
1345 #pragma GCC diagnostic ignored "-Wconversion"
1346 #endif // defined(__GNUC__) || defined(__clang__)
1347
1348 #include "external/stb_truetype.h"
1349 #define PNTR_NO_STB_TRUETYPE_IMPLEMENTATION
1350
1351 #if defined(__GNUC__) || defined(__clang__)
1352 #pragma GCC diagnostic pop
1353 #endif // defined(__GNUC__) || defined(__clang__)
1354#endif // PNTR_ENABLE_TTF
1355
1356#ifdef PNTR_ENABLE_VARGS
1357 // For pntr_draw_text_ex()
1358 #include <stdarg.h> // va_list, va_start, va_end
1359 #include <stdio.h> // vsprintf
1360#endif
1361
1372#define PNTR_PIXEL(image, x, y) image->data[(y) * (image->pitch >> 2) + (x)]
1373
1386#ifndef PNTR_NEW_COLOR
1387 #if defined(PNTR_PIXELFORMAT_RGBA)
1388 #define PNTR_NEW_COLOR(red, green, blue, alpha) PNTR_CLITERAL(pntr_color) { \
1389 .rgba = { \
1390 .r = red, \
1391 .g = green, \
1392 .b = blue, \
1393 .a = alpha \
1394 } \
1395 }
1396 #elif defined(PNTR_PIXELFORMAT_ARGB)
1397 #define PNTR_NEW_COLOR(red, green, blue, alpha) PNTR_CLITERAL(pntr_color) { \
1398 .rgba = { \
1399 .b = blue, \
1400 .g = green, \
1401 .r = red, \
1402 .a = alpha, \
1403 } \
1404 }
1405 #endif
1406#endif
1407
1418pntr_error _pntr_error;
1419
1420PNTR_API const char* pntr_get_error(void) {
1421 switch (_pntr_error) {
1422 case PNTR_ERROR_NONE: return NULL;
1423 case PNTR_ERROR_INVALID_ARGS: return "Invalid arguments";
1424 case PNTR_ERROR_NO_MEMORY: return "No memory";
1425 case PNTR_ERROR_NOT_SUPPORTED: return "Not supported";
1426 case PNTR_ERROR_FAILED_TO_OPEN: return "Failed to open";
1427 case PNTR_ERROR_FAILED_TO_WRITE: return "Failed to write";
1428 case PNTR_ERROR_UNKNOWN: return "Unknown error";
1429 }
1430
1431 return NULL;
1432}
1433
1434PNTR_API pntr_error pntr_get_error_code(void) {
1435 return _pntr_error;
1436}
1437
1445PNTR_API void* pntr_set_error(pntr_error error) {
1446 _pntr_error = error;
1447
1448 #ifdef PNTR_SET_ERROR
1449 PNTR_SET_ERROR(error);
1450 #endif
1451
1452 return NULL;
1453}
1454
1467PNTR_API pntr_image* pntr_new_image(int width, int height) {
1468 if (width <= 0 || height <= 0) {
1469 return (pntr_image*)pntr_set_error(PNTR_ERROR_INVALID_ARGS);
1470 }
1471
1472 pntr_image* image = (pntr_image*)PNTR_MALLOC(sizeof(pntr_image));
1473 if (image == NULL) {
1474 return (pntr_image*)pntr_set_error(PNTR_ERROR_NO_MEMORY);
1475 }
1476
1477 image->pitch = width * (int)sizeof(pntr_color);
1478 image->width = width;
1479 image->height = height;
1480 pntr_image_reset_clip(image);
1481 image->subimage = false;
1482 image->data = (pntr_color*)PNTR_MALLOC((size_t)(image->pitch * height));
1483 if (image->data == NULL) {
1484 PNTR_FREE(image);
1485 return (pntr_image*)pntr_set_error(PNTR_ERROR_NO_MEMORY);
1486 }
1487
1488 return image;
1489}
1490
1500PNTR_API pntr_image* pntr_gen_image_color(int width, int height, pntr_color color) {
1501 pntr_image* image = pntr_new_image(width, height);
1502 pntr_clear_background(image, color);
1503
1504 return image;
1505}
1506
1514PNTR_API pntr_image* pntr_image_copy(pntr_image* image) {
1515 if (image == NULL) {
1516 return (pntr_image*)pntr_set_error(PNTR_ERROR_INVALID_ARGS);
1517 }
1518
1519 pntr_image* newImage = pntr_gen_image_color(image->width, image->height, PNTR_BLANK);
1520 if (newImage == NULL) {
1521 return NULL;
1522 }
1523
1524 pntr_draw_image(newImage, image, 0, 0);
1525 newImage->clip = image->clip;
1526
1527 return newImage;
1528}
1529
1540#ifdef PNTR_NO_ALPHABLEND
1541inline
1542#endif
1543void pntr_blend_color(pntr_color* dst, pntr_color src) {
1544 if (src.rgba.a == 255) {
1545 *dst = src;
1546 return;
1547 }
1548 #ifndef PNTR_NO_ALPHABLEND
1549 if (src.rgba.a == 0) {
1550 return;
1551 }
1552
1553 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
1554 unsigned int dstAlpha = (unsigned int)dst->rgba.a * (256 - alpha);
1555 dst->rgba.a = (unsigned char)((alpha * 256 + dstAlpha) >> 8);
1556
1557 if (dst->rgba.a > 0) {
1558 dst->rgba.r = (unsigned char)((((unsigned int)src.rgba.r * alpha * 256 + (unsigned int)dst->rgba.r * dstAlpha) / dst->rgba.a) >> 8);
1559 dst->rgba.g = (unsigned char)((((unsigned int)src.rgba.g * alpha * 256 + (unsigned int)dst->rgba.g * dstAlpha) / dst->rgba.a) >> 8);
1560 dst->rgba.b = (unsigned char)((((unsigned int)src.rgba.b * alpha * 256 + (unsigned int)dst->rgba.b * dstAlpha) / dst->rgba.a) >> 8);
1561 }
1562 #endif
1563}
1564
1589PNTR_API bool _pntr_rectangle_intersect(int x, int y, int width, int height, int destX, int destY, int destWidth, int destHeight, pntr_rectangle *out) {
1590 if (width <= 0 || height <= 0) {
1591 return false;
1592 }
1593
1594 out->x = PNTR_MAX(x, destX);
1595 out->width = PNTR_MIN(x + width, destX + destWidth) - out->x;
1596 if (out->width <= 0) {
1597 return false;
1598 }
1599
1600 out->y = PNTR_MAX(y, destY);
1601 out->height = PNTR_MIN(y + height, destY + destHeight) - out->y;
1602 if (out->height <= 0) {
1603 return false;
1604 }
1605
1606 return true;
1607}
1608
1625PNTR_API pntr_image* pntr_image_from_image(pntr_image* image, int x, int y, int width, int height) {
1626 if (image == NULL) {
1627 return (pntr_image*)pntr_set_error(PNTR_ERROR_INVALID_ARGS);
1628 }
1629
1630 pntr_rectangle dstRect;
1631 if (!_pntr_rectangle_intersect(x, y, width, height, 0, 0, image->width, image->height, &dstRect)) {
1632 return NULL;
1633 }
1634
1635 pntr_image* result = pntr_new_image(dstRect.width, dstRect.height);
1636 if (result == NULL) {
1637 return NULL;
1638 }
1639
1640 for (int destY = 0; destY < dstRect.height; destY++) {
1641 PNTR_MEMCPY(&PNTR_PIXEL(result, 0, destY),
1642 &PNTR_PIXEL(image, dstRect.x, dstRect.y + destY),
1643 (size_t)result->pitch);
1644 }
1645
1646 return result;
1647}
1648
1665PNTR_API pntr_image* pntr_image_subimage(pntr_image* image, int x, int y, int width, int height) {
1666 if (image == NULL) {
1667 return (pntr_image*)pntr_set_error(PNTR_ERROR_INVALID_ARGS);
1668 }
1669
1670 // Ensure we are referencing an actual portion of the image.
1671 pntr_rectangle dstRect;
1672 if (!_pntr_rectangle_intersect(x, y, width, height, 0, 0, image->width, image->height, &dstRect)) {
1673 return NULL;
1674 }
1675
1676 // Build the subimage.
1677 pntr_image* subimage = (pntr_image*)PNTR_MALLOC(sizeof(pntr_image));
1678 if (subimage == NULL) {
1679 return (pntr_image*)pntr_set_error(PNTR_ERROR_NO_MEMORY);
1680 }
1681
1682 subimage->pitch = image->pitch;
1683 subimage->width = dstRect.width;
1684 subimage->height = dstRect.height;
1685 subimage->subimage = true;
1686 pntr_image_reset_clip(subimage);
1687 subimage->data = &PNTR_PIXEL(image, dstRect.x, dstRect.y);
1688
1689 return subimage;
1690}
1691
1697PNTR_API void pntr_unload_image(pntr_image* image) {
1698 if (image == NULL) {
1699 return;
1700 }
1701
1702 // Only clear full image data.
1703 if (!image->subimage && image->data != NULL) {
1704 PNTR_FREE(image->data);
1705 }
1706
1707 PNTR_FREE(image);
1708}
1709
1713PNTR_API void pntr_put_horizontal_line_unsafe(pntr_image* dst, int posX, int posY, int width, pntr_color color) {
1714 pntr_color *row = &PNTR_PIXEL(dst, posX, posY);
1715 while (--width >= 0) {
1716 row[width] = color;
1717 }
1718}
1719
1728PNTR_API void pntr_clear_background(pntr_image* image, pntr_color color) {
1729 if (image == NULL) {
1730 return;
1731 }
1732
1733 // Blank or white can have some performance optimization.
1734 if (!image->subimage) {
1735 // White
1736 if (color.value == PNTR_WHITE_VALUE) {
1737 PNTR_MEMSET((void*)image->data, 255, (size_t)(image->height * image->pitch));
1738 return;
1739 }
1740
1741 // Blank
1742 if (color.rgba.a == 0) {
1743 PNTR_MEMSET((void*)image->data, 0, (size_t)(image->height * image->pitch));
1744 return;
1745 }
1746 }
1747
1748 // Draw the first line
1749 pntr_put_horizontal_line_unsafe(image, 0, 0, image->width, color);
1750
1751 // Copy the line for the rest of the background
1752 for (int y = 1; y < image->height; y++) {
1753 PNTR_MEMCPY(&PNTR_PIXEL(image, 0, y), image->data, (size_t)image->pitch);
1754 }
1755}
1756
1767PNTR_API pntr_color pntr_new_color(unsigned char red, unsigned char green, unsigned char blue, unsigned char alpha) {
1768 return PNTR_NEW_COLOR(red, green, blue, alpha);
1769}
1770
1778PNTR_API pntr_color pntr_get_color(unsigned int hexValue) {
1779 return PNTR_NEW_COLOR(
1780 (unsigned char)((hexValue >> 24U) & (unsigned int)0xFF),
1781 (unsigned char)((hexValue >> 16U) & (unsigned int)0xFF),
1782 (unsigned char)((hexValue >> 8U) & (unsigned int)0xFF),
1783 (unsigned char)(hexValue & (unsigned int)0xFF)
1784 );
1785}
1786
1787PNTR_API unsigned char pntr_color_r(pntr_color color) {
1788 return color.rgba.r;
1789}
1790
1791PNTR_API unsigned char pntr_color_g(pntr_color color) {
1792 return color.rgba.g;
1793}
1794
1795PNTR_API unsigned char pntr_color_b(pntr_color color) {
1796 return color.rgba.b;
1797}
1798
1799PNTR_API unsigned char pntr_color_a(pntr_color color) {
1800 return color.rgba.a;
1801}
1802
1803PNTR_API void pntr_color_set_r(pntr_color* color, unsigned char r) {
1804 color->rgba.r = r;
1805}
1806
1807PNTR_API void pntr_color_set_g(pntr_color* color, unsigned char g) {
1808 color->rgba.g = g;
1809}
1810
1811PNTR_API void pntr_color_set_b(pntr_color* color, unsigned char b) {
1812 color->rgba.b = b;
1813}
1814
1815PNTR_API void pntr_color_set_a(pntr_color* color, unsigned char a) {
1816 color->rgba.a = a;
1817}
1818
1822PNTR_API void pntr_draw_point_unsafe(pntr_image* dst, int x, int y, pntr_color color) {
1823 pntr_blend_color(&PNTR_PIXEL(dst, x, y), color);
1824}
1825
1829PNTR_API void pntr_draw_point(pntr_image* dst, int x, int y, pntr_color color) {
1830 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)) {
1831 return;
1832 }
1833
1834 pntr_draw_point_unsafe(dst, x, y, color);
1835}
1836
1837PNTR_API void pntr_draw_point_vec(pntr_image* dst, pntr_vector* point, pntr_color color) {
1838 if (point != NULL) {
1839 pntr_draw_point(dst, point->x, point->y, color);
1840 }
1841}
1842
1843PNTR_API void pntr_draw_points(pntr_image* dst, pntr_vector* points, int pointsCount, pntr_color color) {
1844 if (dst == NULL || color.rgba.a == 0 || points == NULL || pointsCount <= 0) {
1845 return;
1846 }
1847
1848 for (int i = 0; i < pointsCount; i++) {
1849 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) {
1850 pntr_draw_point_unsafe(dst, points[i].x, points[i].y, color);
1851 }
1852 }
1853}
1854
1863PNTR_API void pntr_draw_line(pntr_image *dst, int startPosX, int startPosY, int endPosX, int endPosY, pntr_color color) {
1864 if (dst == NULL || color.rgba.a == 0) {
1865 return;
1866 }
1867
1868 int changeInX = (endPosX - startPosX);
1869 int absChangeInX = (changeInX < 0) ? -changeInX : changeInX;
1870 int changeInY = (endPosY - startPosY);
1871 int absChangeInY = (changeInY < 0) ? -changeInY : changeInY;
1872
1873 // Drawing a straight line is fast.
1874 if (startPosX == endPosX) {
1875 pntr_draw_line_vertical(dst, startPosX, (startPosY > endPosY) ? endPosY : startPosY, absChangeInY, color);
1876 return;
1877 }
1878
1879 if (startPosY == endPosY) {
1880 pntr_draw_line_horizontal(dst, (startPosX > endPosX) ? endPosX : startPosX, startPosY, absChangeInX, color);
1881 return;
1882 }
1883
1884 int startU, startV, endU, stepV;
1885 int A, B, P;
1886 int reversedXY = (absChangeInY < absChangeInX);
1887
1888 if (reversedXY) {
1889 A = 2 * absChangeInY;
1890 B = A - 2 * absChangeInX;
1891 P = A - absChangeInX;
1892
1893 if (changeInX > 0) {
1894 startU = startPosX;
1895 startV = startPosY;
1896 endU = endPosX;
1897 //endV = endPosY;
1898 }
1899 else {
1900 startU = endPosX;
1901 startV = endPosY;
1902 endU = startPosX;
1903 //endV = startPosY;
1904
1905 // Since start and end are reversed
1906 changeInX = -changeInX;
1907 changeInY = -changeInY;
1908 }
1909
1910 stepV = (changeInY < 0) ? -1 : 1;
1911
1912 pntr_draw_point(dst, startU, startV, color);
1913 }
1914 else {
1915 A = 2 * absChangeInX;
1916 B = A - 2 * absChangeInY;
1917 P = A - absChangeInY;
1918
1919 if (changeInY > 0) {
1920 startU = startPosY;
1921 startV = startPosX;
1922 endU = endPosY;
1923 }
1924 else {
1925 startU = endPosY;
1926 startV = endPosX;
1927 endU = startPosY;
1928
1929 changeInX = -changeInX;
1930 changeInY = -changeInY;
1931 }
1932
1933 stepV = (changeInX < 0) ? -1 : 1;
1934
1935 pntr_draw_point(dst, startV, startU, color);
1936 }
1937
1938 for (int u = startU + 1, v = startV; u <= endU; u++) {
1939 if (P >= 0) {
1940 v += stepV;
1941 P += B;
1942 }
1943 else {
1944 P += A;
1945 }
1946
1947 if (reversedXY) {
1948 pntr_draw_point(dst, u, v, color);
1949 }
1950 else {
1951 pntr_draw_point(dst, v, u, color);
1952 }
1953 }
1954}
1955
1959PNTR_API void pntr_draw_line_thick(pntr_image *dst, int startPosX, int startPosY, int endPosX, int endPosY, int thickness, pntr_color color) {
1960 if (thickness < 1) {
1961 return;
1962 }
1963 if (thickness == 1) {
1964 pntr_draw_line(dst, startPosX, startPosY, endPosX, endPosY, color);
1965 return;
1966 }
1967
1968 if (dst == NULL || color.rgba.a == 0) {
1969 return;
1970 }
1971
1972 int changeInX = (endPosX - startPosX);
1973 int absChangeInX = (changeInX < 0) ? -changeInX : changeInX;
1974 int changeInY = (endPosY - startPosY);
1975 int absChangeInY = (changeInY < 0) ? -changeInY : changeInY;
1976
1977 // Drawing a straight line is fast.
1978 if (startPosX == endPosX) {
1979 pntr_draw_line_vertical_thick(dst, startPosX, (startPosY > endPosY) ? endPosY : startPosY, absChangeInY, thickness, color);
1980 return;
1981 }
1982
1983 if (startPosY == endPosY) {
1984 pntr_draw_line_horizontal_thick(dst, (startPosX > endPosX) ? endPosX : startPosX, startPosY, absChangeInX, thickness, color);
1985 return;
1986 }
1987
1988 int startU, startV, endU, stepV;
1989 int A, B, P;
1990 int reversedXY = (absChangeInY < absChangeInX);
1991
1992 if (reversedXY) {
1993 A = 2 * absChangeInY;
1994 B = A - 2 * absChangeInX;
1995 P = A - absChangeInX;
1996
1997 if (changeInX > 0) {
1998 startU = startPosX;
1999 startV = startPosY;
2000 endU = endPosX;
2001 //endV = endPosY;
2002 }
2003 else {
2004 startU = endPosX;
2005 startV = endPosY;
2006 endU = startPosX;
2007 //endV = startPosY;
2008
2009 // Since start and end are reversed
2010 changeInX = -changeInX;
2011 changeInY = -changeInY;
2012 }
2013
2014 stepV = (changeInY < 0) ? -1 : 1;
2015
2016 // pntr_draw_point(dst, startU, startV, color);
2017 pntr_draw_circle_fill(dst, startU, startV, thickness/2, color);
2018 }
2019 else {
2020 A = 2 * absChangeInX;
2021 B = A - 2 * absChangeInY;
2022 P = A - absChangeInY;
2023
2024 if (changeInY > 0) {
2025 startU = startPosY;
2026 startV = startPosX;
2027 endU = endPosY;
2028 }
2029 else {
2030 startU = endPosY;
2031 startV = endPosX;
2032 endU = startPosY;
2033
2034 changeInX = -changeInX;
2035 changeInY = -changeInY;
2036 }
2037
2038 stepV = (changeInX < 0) ? -1 : 1;
2039
2040 // pntr_draw_point(dst, startV, startU, color);
2041 pntr_draw_circle_fill(dst, startV, startU, thickness/2, color);
2042
2043 }
2044
2045 for (int u = startU + 1, v = startV; u <= endU; u++) {
2046 if (P >= 0) {
2047 v += stepV;
2048 P += B;
2049 }
2050 else {
2051 P += A;
2052 }
2053
2054 if (reversedXY) {
2055 // pntr_draw_point(dst, u, v, color);
2056 pntr_draw_circle_fill(dst, u, v, thickness/2, color);
2057 }
2058 else {
2059 // pntr_draw_point(dst, v, u, color);
2060 pntr_draw_circle_fill(dst, v, u, thickness/2, color);
2061 }
2062 }
2063}
2064
2065PNTR_API void pntr_draw_line_curve(pntr_image* dst, pntr_vector point1, pntr_vector point2, pntr_vector point3, pntr_vector point4, int segments, pntr_color color) {
2066 if (dst == NULL || color.rgba.a == 0 || segments <= 0) {
2067 return;
2068 }
2069
2070 float t_step = 1.0f / (float)segments;
2071 pntr_vector last = point1;
2072 for (int i_step = 1; i_step <= segments; ++i_step) {
2073 float t = t_step * (float)i_step;
2074 float u = 1.0f - t;
2075 float w1 = u * u * u;
2076 float w2 = 3 * u * u * t;
2077 float w3 = 3 * u * t * t;
2078 float w4 = t * t * t;
2079 float x = w1 * (float)point1.x + w2 * (float)point2.x + w3 * (float)point3.x + w4 * (float)point4.x;
2080 float y = w1 * (float)point1.y + w2 * (float)point2.y + w3 * (float)point3.y + w4 * (float)point4.y;
2081 pntr_draw_line(dst, last.x, last.y, (int)x, (int)y, color);
2082 last.x = (int)x;
2083 last.y = (int)y;
2084 }
2085}
2086
2087PNTR_API void pntr_draw_line_curve_thick(pntr_image* dst, pntr_vector point1, pntr_vector point2, pntr_vector point3, pntr_vector point4, int segments, int thickness, pntr_color color) {
2088 if (dst == NULL || color.rgba.a == 0 || segments <= 0) {
2089 return;
2090 }
2091
2092 float t_step = 1.0f / (float)segments;
2093 pntr_vector last = point1;
2094 for (int i_step = 1; i_step <= segments; ++i_step) {
2095 float t = t_step * (float)i_step;
2096 float u = 1.0f - t;
2097 float w1 = u * u * u;
2098 float w2 = 3 * u * u * t;
2099 float w3 = 3 * u * t * t;
2100 float w4 = t * t * t;
2101 float x = w1 * (float)point1.x + w2 * (float)point2.x + w3 * (float)point3.x + w4 * (float)point4.x;
2102 float y = w1 * (float)point1.y + w2 * (float)point2.y + w3 * (float)point3.y + w4 * (float)point4.y;
2103 pntr_draw_line_thick(dst, last.x, last.y, (int)x, (int)y, thickness, color);
2104 last.x = (int)x;
2105 last.y = (int)y;
2106 }
2107}
2108
2109PNTR_API void pntr_draw_polyline(pntr_image* dst, pntr_vector* points, int numPoints, pntr_color color) {
2110 if (color.rgba.a == 0 || dst == NULL || numPoints <= 0 || points == NULL) {
2111 return;
2112 }
2113
2114 if (numPoints == 1) {
2115 pntr_draw_point_vec(dst, points, color);
2116 return;
2117 }
2118
2119 for (int i = 0; i < numPoints - 1; i++) {
2120 pntr_draw_line_vec(dst, points[i], points[i + 1], color);
2121 }
2122}
2123
2124PNTR_API void pntr_draw_polyline_thick(pntr_image* dst, pntr_vector* points, int numPoints, int thickness, pntr_color color) {
2125 if (color.rgba.a == 0 || dst == NULL || numPoints <= 0 || points == NULL) {
2126 return;
2127 }
2128
2129 if (numPoints == 1) {
2130 pntr_draw_point_vec(dst, points, color);
2131 return;
2132 }
2133
2134 for (int i = 0; i < numPoints - 1; i++) {
2135 pntr_draw_line_thick_vec(dst, points[i], points[i + 1], thickness, color);
2136 }
2137}
2138
2149PNTR_API void pntr_draw_line_horizontal(pntr_image* dst, int posX, int posY, int width, pntr_color color) {
2150 if (width < 0) {
2151 posX += width;
2152 width = -width;
2153 }
2154 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) {
2155 return;
2156 }
2157
2158 if (posX < dst->clip.x) {
2159 width += posX - dst->clip.x;
2160 posX = dst->clip.x;
2161 }
2162 if (posX + width >= dst->clip.x + dst->clip.width) {
2163 width = dst->clip.x + dst->clip.width - posX;
2164 }
2165
2166 if (color.rgba.a == 255) {
2167 pntr_put_horizontal_line_unsafe(dst, posX, posY, width, color);
2168 }
2169 else {
2170 pntr_color *row = &PNTR_PIXEL(dst, posX, posY);
2171 while (--width >= 0) {
2172 pntr_blend_color(row + width, color);
2173 }
2174 }
2175}
2176
2177PNTR_API void pntr_draw_line_horizontal_thick(pntr_image* dst, int posX, int posY, int width, int thickness, pntr_color color) {
2178 if (thickness == 0) {
2179 return;
2180 }
2181 if (thickness == 1) {
2182 pntr_draw_line_horizontal(dst, posX, posY, width, color);
2183 return;
2184 }
2185
2186 pntr_draw_rectangle_fill(dst, posX, posY - thickness / 2, width, thickness, color);
2187 pntr_draw_circle_fill(dst, posX, posY, thickness / 2, color);
2188 pntr_draw_circle_fill(dst, posX + width, posY, thickness / 2, color);
2189}
2190
2201PNTR_API void pntr_draw_line_vertical(pntr_image* dst, int posX, int posY, int height, pntr_color color) {
2202 if (height < 0) {
2203 posY += height;
2204 height = -height;
2205 }
2206 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) {
2207 return;
2208 }
2209
2210 if (posY < dst->clip.y) {
2211 height += posY - dst->clip.y;
2212 posY = dst->clip.y;
2213 }
2214 if (posY + height >= dst->clip.y + dst->clip.height) {
2215 height = dst->clip.y + dst->clip.height - posY;
2216 }
2217
2218 if (color.rgba.a == 255) {
2219 for (int y = 0; y < height; y++) {
2220 PNTR_PIXEL(dst, posX, posY + y) = color;
2221 }
2222 }
2223 else {
2224 for (int y = 0; y < height; y++) {
2225 pntr_blend_color(&PNTR_PIXEL(dst, posX, posY + y), color);
2226 }
2227 }
2228}
2229
2230PNTR_API void pntr_draw_line_vertical_thick(pntr_image* dst, int posX, int posY, int height, int thickness, pntr_color color) {
2231 if (thickness == 0) {
2232 return;
2233 }
2234 if (thickness == 1) {
2235 pntr_draw_line_vertical(dst, posX, posY, height, color);
2236 return;
2237 }
2238 pntr_draw_rectangle_fill(dst, posX - thickness / 2, posY, thickness, height, color);
2239 pntr_draw_circle_fill(dst, posX, posY, thickness / 2, color);
2240 pntr_draw_circle_fill(dst, posX, posY + height, thickness / 2, color);
2241}
2242
2250PNTR_API void pntr_draw_rectangle_rec(pntr_image* dst, pntr_rectangle rec, pntr_color color) {
2251 pntr_draw_rectangle(dst, rec.x, rec.y, rec.width, rec.height, color);
2252}
2253
2267PNTR_API void pntr_draw_rectangle(pntr_image* dst, int posX, int posY, int width, int height, pntr_color color) {
2268 if (color.rgba.a == 0 || dst == NULL || width <= 0 || height <= 0) {
2269 return;
2270 }
2271
2272 pntr_draw_line_horizontal(dst, posX, posY, width, color);
2273 pntr_draw_line_horizontal(dst, posX, posY + height - 1, width, color);
2274 pntr_draw_line_vertical(dst, posX, posY + 1, height - 2, color);
2275 pntr_draw_line_vertical(dst, posX + width - 1, posY + 1, height - 2, color);
2276}
2277
2278PNTR_API void pntr_draw_rectangle_thick(pntr_image* dst, int posX, int posY, int width, int height, int thickness, pntr_color color) {
2279 for (int i = 0; i < thickness; i++) {
2280 pntr_draw_rectangle(dst, posX + i, posY + i, width - i * 2, height - i * 2, color);
2281 }
2282}
2283
2284PNTR_API void pntr_draw_rectangle_thick_rec(pntr_image* dst, pntr_rectangle rect, int thickness, pntr_color color) {
2285 pntr_draw_rectangle_thick(dst, rect.x, rect.y, rect.width, rect.height, thickness, color);
2286}
2287
2300PNTR_API void pntr_draw_rectangle_fill(pntr_image* dst, int posX, int posY, int width, int height, pntr_color color) {
2301 pntr_draw_rectangle_fill_rec(dst, PNTR_CLITERAL(pntr_rectangle) { posX, posY, width, height }, color);
2302}
2303
2313PNTR_API void pntr_draw_rectangle_fill_rec(pntr_image* dst, pntr_rectangle rect, pntr_color color) {
2314 if (color.rgba.a == 0 || dst == NULL) {
2315 return;
2316 }
2317
2318 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)) {
2319 return;
2320 }
2321
2322 // When the color is solid, we can do some performance improvements.
2323 if (color.rgba.a == 255) {
2324 pntr_put_horizontal_line_unsafe(dst, rect.x, rect.y, rect.width, color);
2325
2326 pntr_color* srcPixel = &PNTR_PIXEL(dst, rect.x, rect.y);
2327 for (int y = rect.y + 1; y < rect.y + rect.height; y++) {
2328 PNTR_MEMCPY(&PNTR_PIXEL(dst, rect.x, y), srcPixel, (size_t)rect.width * sizeof(pntr_color));
2329 }
2330 }
2331 else {
2332 for (int y = 0; y < rect.height; y++) {
2333 pntr_color* col = &PNTR_PIXEL(dst, rect.x, rect.y + y);
2334 for (int x = 0; x < rect.width; x++) {
2335 pntr_blend_color(col++, color);
2336 }
2337 }
2338 }
2339}
2340
2341PNTR_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) {
2342 if (dst == NULL) {
2343 return;
2344 }
2345
2346 pntr_rectangle dstRect;
2347 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)) {
2348 return;
2349 }
2350
2351 // Protect against division by zero.
2352 if (rect.width == 0 || rect.height == 0) {
2353 return;
2354 }
2355
2356 float width = (float)rect.width;
2357 float height = (float)rect.height;
2358 for (int x = dstRect.x; x < dstRect.x + dstRect.width; x++) {
2359 float factorX = (float)(x - rect.x) / width;
2360 for (int y = dstRect.y; y < dstRect.y + dstRect.height; y++) {
2362 topLeft, bottomLeft,
2363 topRight, bottomRight,
2364 factorX,
2365 (float)(y - rect.y) / height
2366 ));
2367 }
2368 }
2369}
2370
2371PNTR_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) {
2372 pntr_draw_rectangle_gradient_rec(dst, PNTR_CLITERAL(pntr_rectangle) {x, y, width, height}, topLeft, topRight, bottomLeft, bottomRight);
2373}
2374
2391PNTR_API void pntr_draw_circle(pntr_image* dst, int centerX, int centerY, int radius, pntr_color color) {
2392 if (dst == NULL || color.rgba.a == 0) {
2393 return;
2394 }
2395
2396 if (radius == 0) {
2397 return;
2398 }
2399
2400 if (radius < 0) {
2401 radius = -radius;
2402 }
2403
2404 if (radius == 1) {
2405 pntr_draw_point(dst, centerX, centerY, color);
2406 return;
2407 }
2408
2409 // Check that the circle is in the bounds.
2410 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) {
2411 return;
2412 }
2413
2414 int largestX = radius;
2415 int r2 = radius * radius;
2416 for (int y = 0; y <= radius; ++y) {
2417 int y2 = y * y;
2418 for (int x = largestX; x >= 0; --x) {
2419 if (x * x + y2 <= r2) {
2420 pntr_draw_point(dst, centerX + x, centerY + y, color);
2421 pntr_draw_point(dst, centerX - x, centerY + y, color);
2422 pntr_draw_point(dst, centerX + x, centerY - y, color);
2423 pntr_draw_point(dst, centerX - x, centerY - y, color);
2424 pntr_draw_point(dst, centerX + y, centerY + x, color);
2425 pntr_draw_point(dst, centerX - y, centerY + x, color);
2426 pntr_draw_point(dst, centerX + y, centerY - x, color);
2427 pntr_draw_point(dst, centerX - y, centerY - x, color);
2428 largestX = x;
2429 break;
2430 }
2431 }
2432 }
2433}
2434
2448PNTR_API void pntr_draw_circle_fill(pntr_image* dst, int centerX, int centerY, int radius, pntr_color color) {
2449 if (radius == 0) {
2450 return;
2451 }
2452
2453 if (radius < 0) {
2454 radius = -radius;
2455 }
2456
2457 if (radius == 1) {
2458 pntr_draw_point(dst, centerX, centerY, color);
2459 return;
2460 }
2461
2462 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) {
2463 return;
2464 }
2465
2466 int largestX = radius;
2467 int r2 = radius * radius;
2468 for (int y = 0; y <= radius; ++y) {
2469 int y2 = y * y;
2470 for (int x = largestX; x >= 0; --x) {
2471 if (x * x + y2 <= r2) {
2472 pntr_draw_line_horizontal(dst, centerX - x, centerY + y, x, color);
2473 pntr_draw_line_horizontal(dst, centerX - x, centerY - y, x, color);
2474 pntr_draw_line_horizontal(dst, centerX, centerY + y, x, color);
2475 pntr_draw_line_horizontal(dst, centerX, centerY - y, x, color);
2476 largestX = x;
2477 break;
2478 }
2479 }
2480 }
2481}
2482
2493PNTR_API void pntr_draw_circle_thick(pntr_image* dst, int centerX, int centerY, int radius, int thickness, pntr_color color) {
2494 if (thickness < 1) {
2495 return;
2496 }
2497 if (thickness == 1) {
2498 pntr_draw_circle(dst, centerX, centerY, radius, color);
2499 return;
2500 }
2501 if (dst == NULL || color.rgba.a == 0) {
2502 return;
2503 }
2504
2505 if (radius < 0) {
2506 radius = -radius;
2507 }
2508
2509 // Check that the circle is in the bounds.
2510 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) {
2511 return;
2512 }
2513
2514 int largestX = radius;
2515 int r2 = radius * radius;
2516 for (int y = 0; y <= radius; ++y) {
2517 int y2 = y * y;
2518 for (int x = largestX; x >= 0; --x) {
2519 if (x * x + y2 <= r2) {
2520 pntr_draw_circle_fill(dst, centerX + x, centerY + y, thickness/2, color);
2521 pntr_draw_circle_fill(dst, centerX - x, centerY + y, thickness/2, color);
2522 pntr_draw_circle_fill(dst, centerX + x, centerY - y, thickness/2, color);
2523 pntr_draw_circle_fill(dst, centerX - x, centerY - y, thickness/2, color);
2524 pntr_draw_circle_fill(dst, centerX + y, centerY + x, thickness/2, color);
2525 pntr_draw_circle_fill(dst, centerX - y, centerY + x, thickness/2, color);
2526 pntr_draw_circle_fill(dst, centerX + y, centerY - x, thickness/2, color);
2527 pntr_draw_circle_fill(dst, centerX - y, centerY - x, thickness/2, color);
2528 largestX = x;
2529 break;
2530 }
2531 }
2532 }
2533}
2534
2547PNTR_API void pntr_draw_ellipse(pntr_image* dst, int centerX, int centerY, int radiusX, int radiusY, pntr_color color) {
2548 if (dst == NULL || radiusX == 0 || radiusY == 0 || color.rgba.a == 0) {
2549 return;
2550 }
2551
2552 int x = 0;
2553 if (radiusX < 0) {
2554 radiusX = -radiusX;
2555 }
2556 if (radiusY < 0) {
2557 radiusY = -radiusY;
2558 }
2559
2560 int radiusXSquared = radiusX * radiusX;
2561 int radiusXSquared2 = radiusXSquared * 2;
2562 int radiusYSquared = radiusY * radiusY;
2563 int radiusYSquared2 = radiusYSquared * 2;
2564 int error = radiusYSquared - radiusXSquared * radiusY;
2565
2566 while (radiusY >= 0) {
2567 pntr_draw_point(dst, centerX + x, centerY + radiusY, color);
2568 pntr_draw_point(dst, centerX - x, centerY + radiusY, color);
2569 pntr_draw_point(dst, centerX - x, centerY - radiusY, color);
2570 pntr_draw_point(dst, centerX + x, centerY - radiusY, color);
2571
2572 if (error <= 0) {
2573 x++;
2574 error += radiusYSquared2 * x + radiusYSquared;
2575 }
2576 if (error > 0) {
2577 radiusY--;
2578 error -= radiusXSquared2 * radiusY - radiusXSquared;
2579 }
2580 }
2581}
2582
2597PNTR_API void pntr_draw_ellipse_fill(pntr_image* dst, int centerX, int centerY, int radiusX, int radiusY, pntr_color color) {
2598 if (radiusX < 0) {
2599 radiusX = -radiusX;
2600 }
2601 if (radiusY < 0) {
2602 radiusY = -radiusY;
2603 }
2604
2605 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) {
2606 return;
2607 }
2608
2609 int x = 0;
2610 int radiusXSquared = radiusX * radiusX;
2611 int radiusXSquared2 = radiusXSquared * 2;
2612 int radiusYSquared = radiusY * radiusY;
2613 int radiusYSquared2 = radiusYSquared * 2;
2614 int error = radiusYSquared - radiusXSquared * radiusY;
2615
2616 while (radiusY >= 0) {
2617 pntr_draw_line_horizontal(dst, centerX - x, centerY + radiusY, x, color);
2618 pntr_draw_line_horizontal(dst, centerX - x, centerY - radiusY, x, color);
2619 pntr_draw_line_horizontal(dst, centerX, centerY + radiusY, x, color);
2620 pntr_draw_line_horizontal(dst, centerX, centerY - radiusY, x, color);
2621
2622 if (error <= 0) {
2623 x++;
2624 error += radiusYSquared2 * x + radiusYSquared;
2625 }
2626 if (error > 0) {
2627 radiusY--;
2628 error -= radiusXSquared2 * radiusY - radiusXSquared;
2629 }
2630 }
2631}
2632
2645PNTR_API void pntr_draw_ellipse_thick(pntr_image* dst, int centerX, int centerY, int radiusX, int radiusY, int thickness, pntr_color color) {
2646 if (thickness < 1) {
2647 return;
2648 }
2649 if (thickness == 1) {
2650 pntr_draw_ellipse(dst, centerX, centerY, radiusX, radiusY, color);
2651 return;
2652 }
2653 if (dst == NULL || radiusX == 0 || radiusY == 0 || color.rgba.a == 0) {
2654 return;
2655 }
2656
2657 int x = 0;
2658 if (radiusX < 0) {
2659 radiusX = -radiusX;
2660 }
2661 if (radiusY < 0) {
2662 radiusY = -radiusY;
2663 }
2664
2665 int radiusXSquared = radiusX * radiusX;
2666 int radiusXSquared2 = radiusXSquared * 2;
2667 int radiusYSquared = radiusY * radiusY;
2668 int radiusYSquared2 = radiusYSquared * 2;
2669 int error = radiusYSquared - radiusXSquared * radiusY;
2670
2671 while (radiusY >= 0) {
2672 pntr_draw_circle_fill(dst, centerX + x, centerY + radiusY, thickness/2, color);
2673 pntr_draw_circle_fill(dst, centerX - x, centerY + radiusY, thickness/2, color);
2674 pntr_draw_circle_fill(dst, centerX - x, centerY - radiusY, thickness/2, color);
2675 pntr_draw_circle_fill(dst, centerX + x, centerY - radiusY, thickness/2, color);
2676
2677 if (error <= 0) {
2678 x++;
2679 error += radiusYSquared2 * x + radiusYSquared;
2680 }
2681 if (error > 0) {
2682 radiusY--;
2683 error -= radiusXSquared2 * radiusY - radiusXSquared;
2684 }
2685 }
2686}
2687
2697PNTR_API void pntr_draw_triangle_vec(pntr_image* dst, pntr_vector point1, pntr_vector point2, pntr_vector point3, pntr_color color) {
2698 pntr_draw_triangle(dst, point1.x, point1.y, point2.x, point2.y, point3.x, point3.y, color);
2699}
2700
2711PNTR_API void pntr_draw_triangle_thick_vec(pntr_image *dst, pntr_vector point1, pntr_vector point2, pntr_vector point3, int thickness, pntr_color color) {
2712 pntr_draw_line_thick(dst, point1.x, point1.y, point2.x, point2.y, thickness, color);
2713 pntr_draw_line_thick(dst, point2.x, point2.y, point3.x, point3.y, thickness, color);
2714 pntr_draw_line_thick(dst, point3.x, point3.y, point1.x, point1.y, thickness, color);
2715}
2716
2729PNTR_API void pntr_draw_triangle(pntr_image* dst, int x1, int y1, int x2, int y2, int x3, int y3, pntr_color color) {
2730 pntr_draw_line(dst, x1, y1, x2, y2, color);
2731 pntr_draw_line(dst, x2, y2, x3, y3, color);
2732 pntr_draw_line(dst, x3, y3, x1, y1, color);
2733}
2734
2748PNTR_API void pntr_draw_triangle_thick(pntr_image* dst, int x1, int y1, int x2, int y2, int x3, int y3, int thickness, pntr_color color) {
2749 pntr_draw_line_thick(dst, x1, y1, x2, y2, thickness, color);
2750 pntr_draw_line_thick(dst, x2, y2, x3, y3, thickness, color);
2751 pntr_draw_line_thick(dst, x3, y3, x1, y1, thickness, color);
2752}
2753
2754
2767PNTR_API void pntr_draw_triangle_fill(pntr_image* dst, int x1, int y1, int x2, int y2, int x3, int y3, pntr_color color) {
2769 PNTR_CLITERAL(pntr_vector) { .x = x1, .y = y1 },
2770 PNTR_CLITERAL(pntr_vector) { .x = x2, .y = y2 },
2771 PNTR_CLITERAL(pntr_vector) { .x = x3, .y = y3 },
2772 color
2773 );
2774}
2775
2776PNTR_API void pntr_draw_line_vec(pntr_image* dst, pntr_vector start, pntr_vector end, pntr_color color) {
2777 pntr_draw_line(dst, start.x, start.y, end.x, end.y, color);
2778}
2779
2780PNTR_API void pntr_draw_line_thick_vec(pntr_image* dst, pntr_vector start, pntr_vector end, int thickness, pntr_color color) {
2781 pntr_draw_line_thick(dst, start.x, start.y, end.x, end.y, thickness, color);
2782}
2783
2784PNTR_API void pntr_draw_polygon(pntr_image* dst, pntr_vector* points, int numPoints, pntr_color color) {
2785 if (dst == NULL || color.rgba.a == 0 || numPoints <= 0 || points == NULL) {
2786 return;
2787 }
2788
2789 int nextPointIndex;
2790 for (int i = 0; i < numPoints; i++) {
2791 if (i < numPoints - 1) {
2792 nextPointIndex = i + 1;
2793 }
2794 else {
2795 nextPointIndex = 0;
2796 }
2797
2798 pntr_draw_line(dst, points[i].x, points[i].y, points[nextPointIndex].x, points[nextPointIndex].y, color);
2799 }
2800}
2801
2802PNTR_API void pntr_draw_polygon_thick(pntr_image* dst, pntr_vector* points, int numPoints, int thickness, pntr_color color) {
2803 if (dst == NULL || color.rgba.a == 0 || numPoints <= 0 || points == NULL) {
2804 return;
2805 }
2806
2807 int nextPointIndex;
2808 for (int i = 0; i < numPoints; i++) {
2809 if (i < numPoints - 1) {
2810 nextPointIndex = i + 1;
2811 }
2812 else {
2813 nextPointIndex = 0;
2814 }
2815
2816 pntr_draw_line_thick(dst, points[i].x, points[i].y, points[nextPointIndex].x, points[nextPointIndex].y, thickness, color);
2817 }
2818}
2819
2820PNTR_API void pntr_draw_polygon_fill(pntr_image* dst, pntr_vector* points, int numPoints, pntr_color color) {
2821 if (dst == NULL || points == NULL || numPoints <= 0 || color.rgba.a == 0) {
2822 return;
2823 }
2824
2825 int i = 0;
2826 // Big numbers to find the max/min values
2827 int left = points[0].x, top = points[0].y, bottom = points[0].y, right = points[0].x;
2828 int nodes, pixelX, pixelY, j, swap;
2829 int* nodeX = (int*)PNTR_MALLOC(sizeof(int) * (size_t)numPoints);
2830 if (nodeX == NULL) {
2831 return;
2832 }
2833
2834 // Get polygon dimensions
2835 for (i = 0; i < numPoints; i++) {
2836 if (left > points[i].x)
2837 left = points[i].x;
2838 if (right < points[i].x)
2839 right = points[i].x;
2840 if (top > points[i].y)
2841 top = points[i].y;
2842 if (bottom < points[i].y)
2843 bottom = points[i].y;
2844 }
2845 bottom++;
2846 right++;
2847
2848 // Polygon scanline algorithm released under public-domain by Darel Rex Finley, 2007.
2849 // Loop through the rows of the image.
2850 for (pixelY = top; pixelY < bottom; pixelY ++) {
2851 nodes = 0; /* Build a list of nodes. */
2852 j = numPoints - 1;
2853 for (i = 0; i < numPoints; i++) {
2854 if (((points[i].y < pixelY) && (points[j].y >= pixelY)) ||
2855 ((points[j].y < pixelY) && (points[i].y >= pixelY))) {
2856 nodeX[nodes++]= (int)((float)points[i].x
2857 + ((float)pixelY - (float)points[i].y) / ((float)points[j].y - (float)points[i].y)
2858 * ((float)points[j].x - (float)points[i].x));
2859 } j = i;
2860 }
2861
2862 // Sort the nodes, via a simple “Bubble” sort.
2863 i = 0;
2864 while (i < nodes - 1) {
2865 if (nodeX[i] > nodeX[i+1]) {
2866 swap = nodeX[i];
2867 nodeX[i] = nodeX[i+1];
2868 nodeX[i+1] = swap;
2869 if (i) i--;
2870 } else i++;
2871 }
2872 // Fill the pixels between node pairs.
2873 for (i = 0; i < nodes; i += 2) {
2874 if (nodeX[i+0] >= right) break;
2875 if (nodeX[i+1] > left) {
2876 if (nodeX[i+0] < left) nodeX[i+0] = left ;
2877 if (nodeX[i+1] > right) nodeX[i+1] = right;
2878 for (pixelX = nodeX[i]; pixelX < nodeX[i + 1]; pixelX++)
2879 pntr_draw_point(dst, pixelX, pixelY, color);
2880 }
2881 }
2882 }
2883
2884 PNTR_FREE(nodeX);
2885}
2886
2896PNTR_API void pntr_draw_triangle_fill_vec(pntr_image* dst, pntr_vector point1, pntr_vector point2, pntr_vector point3, pntr_color color) {
2897 pntr_vector points[3];
2898 points[0] = point1;
2899 points[1] = point2;
2900 points[2] = point3;
2901 pntr_draw_polygon_fill(dst, points, 3, color);
2902}
2903
2904PNTR_API void pntr_draw_arc(pntr_image* dst, int centerX, int centerY, float radius, float startAngle, float endAngle, int segments, pntr_color color) {
2905 if (radius <= 0.0f) {
2906 pntr_draw_point(dst, centerX, centerY, color);
2907 return;
2908 }
2909 if (segments <= 0) {
2910 return;
2911 }
2912
2913 float startAngleRad = startAngle * PNTR_PI / 180.0f;
2914 float endAngleRad = endAngle * PNTR_PI / 180.0f;
2915
2916 // Calculate how much distance between each segment
2917 float stepAngle = (endAngleRad - startAngleRad) / (float)(segments);
2918
2919 // Draw the arc with line segments
2920 /*
2921 int x1 = centerX + (int)((float)radius * PNTR_COSF(startAngleRad));
2922 int y1 = centerY + (int)((float)radius * PNTR_SINF(startAngleRad));
2923 float angle;
2924 for (int i = 1; i < segments; i++) {
2925 angle = startAngleRad + (float)i * stepAngle;
2926 int x2 = centerX + (int)((float)radius * PNTR_COSF(angle));
2927 int y2 = centerY + (int)((float)radius * PNTR_SINF(angle));
2928 pntr_draw_line(dst, x1, y1, x2, y2, color);
2929 x1 = x2;
2930 y1 = y2;
2931 }
2932 */
2933
2934 // Draw each line segment
2935 for (int i = 0; i < segments; i++) {
2936 endAngleRad = startAngleRad + (float)i * stepAngle;
2937 pntr_draw_point(dst,
2938 centerX + (int)(radius * PNTR_COSF(endAngleRad)), // TODO: arc angle: Is the - correct here?
2939 centerY + (int)(radius * PNTR_SINF(endAngleRad)),
2940 color);
2941 }
2942}
2943
2944PNTR_API void pntr_draw_arc_thick(pntr_image* dst, int centerX, int centerY, float radius, float startAngle, float endAngle, int segments, int thickness, pntr_color color) {
2945 if (radius <= 0.0f) {
2946 pntr_draw_point(dst, centerX, centerY, color);
2947 return;
2948 }
2949 if (segments <= 0) {
2950 return;
2951 }
2952
2953 float startAngleRad = startAngle * PNTR_PI / 180.0f;
2954 float endAngleRad = endAngle * PNTR_PI / 180.0f;
2955
2956 // Calculate how much distance between each segment
2957 float stepAngle = (endAngleRad - startAngleRad) / (float)(segments);
2958
2959 // Draw the arc with line segments
2960 int x1 = centerX + (int)((float)radius * PNTR_COSF(startAngleRad));
2961 int y1 = centerY + (int)((float)radius * PNTR_SINF(startAngleRad));
2962 float angle;
2963 for (int i = 1; i < segments; i++) {
2964 angle = startAngleRad + (float)i * stepAngle;
2965 int x2 = centerX + (int)((float)radius * PNTR_COSF(angle));
2966 int y2 = centerY + (int)((float)radius * PNTR_SINF(angle));
2967 pntr_draw_line_thick(dst, x1, y1, x2, y2, thickness, color);
2968 x1 = x2;
2969 y1 = y2;
2970 }
2971}
2972
2973PNTR_API void pntr_draw_arc_fill(pntr_image* dst, int centerX, int centerY, float radius, float startAngle, float endAngle, int segments, pntr_color color) {
2974 if (radius <= 0.0f) {
2975 pntr_draw_point(dst, centerX, centerY, color);
2976 return;
2977 }
2978 if (segments <= 0) {
2979 return;
2980 }
2981 float startAngleRad = startAngle * PNTR_PI / 180.0f;
2982 float endAngleRad = endAngle * PNTR_PI / 180.0f;
2983
2984 // Calculate how much distance between each segment
2985 float stepAngle = (endAngleRad - startAngleRad) / (float)segments;
2986 pntr_vector* points = (pntr_vector*)PNTR_MALLOC(sizeof(pntr_vector) * (size_t)segments + (size_t)1);
2987
2988 // TODO: pntr_draw_arc_fill(): Is pntr_draw_polygon_fill ample here?
2989 for (int i = 0; i < segments; i++) {
2990 endAngleRad = startAngleRad + (float)i * stepAngle;
2991 points[i].x = centerX + (int)(radius * PNTR_COSF(endAngleRad));
2992 points[i].y = centerY + (int)(radius * PNTR_SINF(endAngleRad));
2993 }
2994
2995 points[segments].x = centerX;
2996 points[segments].y = centerY;
2997
2998 pntr_draw_polygon_fill(dst, points, segments + 1, color);
2999 pntr_unload_memory((void*)points);
3000}
3001
3002PNTR_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) {
3003 if (topLeftRadius == 0 && topRightRadius == 0 && bottomLeftRadius == 0 && bottomRightRadius == 0) {
3004 pntr_draw_rectangle(dst, x, y, width, height, color);
3005 return;
3006 }
3007
3008 pntr_draw_line_horizontal(dst, x + topLeftRadius, y, width - topLeftRadius - topRightRadius, color); // Top
3009 pntr_draw_line_horizontal(dst, x + bottomLeftRadius, y + height, width - bottomLeftRadius - bottomRightRadius - 1, color); // Bottom
3010 pntr_draw_line_vertical(dst, x, y + topLeftRadius, height - topLeftRadius - bottomLeftRadius, color); // Left
3011 pntr_draw_line_vertical(dst, x + width - 1, y + topRightRadius, height - topRightRadius - bottomRightRadius, color); // Right
3012
3013 // TODO: pntr_draw_rectangle_rounded(): Do the angles here make sense?
3014 pntr_draw_arc(dst, x + topLeftRadius, y + topLeftRadius, (float)topLeftRadius, 180.0f, 270.0f, topLeftRadius * 2, color); // Top Left
3015 pntr_draw_arc(dst, x + width - topRightRadius - 1, y + topRightRadius, (float)topRightRadius, 0.0f, -90.0f, topRightRadius * 2, color); // Top Right
3016 pntr_draw_arc(dst, x + bottomLeftRadius, y + height - bottomLeftRadius, (float)bottomLeftRadius, -180.0f, -270.0f, bottomLeftRadius * 2, color); // Bottom Left
3017 pntr_draw_arc(dst, x + width - bottomRightRadius - 1, y + height - bottomRightRadius, (float)bottomRightRadius, 0.0f, 90.0f, bottomRightRadius * 2, color); // Bottom Right
3018}
3019
3020PNTR_API void pntr_draw_rectangle_thick_rounded(pntr_image* dst, int x, int y, int width, int height, int topLeftRadius, int topRightRadius, int bottomLeftRadius, int bottomRightRadius, int thickness, pntr_color color) {
3021 if (thickness < 1) {
3022 return;
3023 }
3024 if (thickness == 1) {
3025 pntr_draw_rectangle_rounded(dst, x, y, width, height, topLeftRadius, topRightRadius, bottomLeftRadius, bottomRightRadius, color);
3026 return;
3027 }
3028 for (int offset = 0; offset < thickness; offset++ ) {
3029 pntr_draw_line_horizontal(dst, x + topLeftRadius - offset, y - offset, width - topLeftRadius - topRightRadius - offset, color); // Top
3030 pntr_draw_line_horizontal(dst, x + bottomLeftRadius - offset, y + height - offset, width - bottomLeftRadius - bottomRightRadius - 1 - offset, color); // Bottom
3031 pntr_draw_line_vertical(dst, x - offset, y + topLeftRadius - offset, height - topLeftRadius - bottomLeftRadius - offset, color); // Left
3032 pntr_draw_line_vertical(dst, x + width - 1 - offset, y + topRightRadius - offset, height - topRightRadius - bottomRightRadius - offset, color); // Right
3033 }
3034 pntr_draw_arc_thick(dst, x + topLeftRadius, y + topLeftRadius, (float)topLeftRadius, 180.0f, 270.0f, topLeftRadius * 2, thickness, color); // Top Left
3035 pntr_draw_arc_thick(dst, x + width - topRightRadius - 1, y + topRightRadius, (float)topRightRadius, 0.0f, -90.0f, topRightRadius * 2, thickness, color); // Top Right
3036 pntr_draw_arc_thick(dst, x + bottomLeftRadius, y + height - bottomLeftRadius, (float)bottomLeftRadius, -180.0f, -270.0f, bottomLeftRadius * 2, thickness, color); // Bottom Left
3037 pntr_draw_arc_thick(dst, x + width - bottomRightRadius - 1, y + height - bottomRightRadius, (float)bottomRightRadius, 0.0f, 90.0f, bottomRightRadius * 2, thickness, color); // Bottom Right
3038}
3039
3040PNTR_API void pntr_draw_rectangle_rounded_fill(pntr_image* dst, int x, int y, int width, int height, int cornerRadius, pntr_color color) {
3041 if (cornerRadius == 0) {
3042 pntr_draw_rectangle_fill(dst, x, y, width, height, color);
3043 return;
3044 }
3045
3046 // Corners
3047 // TODO: Replace this with pntr_draw_arc_fill()
3048 pntr_draw_circle_fill(dst, x + cornerRadius, y + cornerRadius, cornerRadius, color); // Top Left
3049 pntr_draw_circle_fill(dst, x + width - cornerRadius - 1, y + cornerRadius, cornerRadius, color); // Top Right
3050 pntr_draw_circle_fill(dst, x + cornerRadius, y + height - cornerRadius, cornerRadius, color); // Bottom Left
3051 pntr_draw_circle_fill(dst, x + width - cornerRadius - 1, y + height - cornerRadius, cornerRadius, color); // Bottom Right
3052
3053 // Edge bars
3054 pntr_draw_rectangle_fill(dst, x, y + cornerRadius, cornerRadius, height - cornerRadius * 2, color); // Left bar
3055 pntr_draw_rectangle_fill(dst, x + width - cornerRadius - 1, y + cornerRadius, cornerRadius, height - cornerRadius * 2, color); // Right bar
3056 pntr_draw_rectangle_fill(dst, x + cornerRadius, y, width - cornerRadius * 2, cornerRadius, color); // Top bar
3057 pntr_draw_rectangle_fill(dst, x + cornerRadius, y + height - cornerRadius, width - cornerRadius * 2, cornerRadius, color); // Bottom bar
3058
3059 // Center fill
3060 pntr_draw_rectangle_fill(dst, x + cornerRadius, y + cornerRadius, width - cornerRadius * 2, height - cornerRadius * 2, color);
3061}
3062
3072PNTR_API pntr_color pntr_image_get_color(pntr_image* image, int x, int y) {
3073 if (image == NULL || x < 0 || y < 0 || x >= image->width || y >= image->height) {
3074 return PNTR_BLANK;
3075 }
3076
3077 return PNTR_PIXEL(image, x, y);
3078}
3079
3092PNTR_API pntr_image_type pntr_get_file_image_type(const char* filePath) {
3093 if (filePath == NULL) {
3094 return PNTR_IMAGE_TYPE_UNKNOWN;
3095 }
3096
3097 if (PNTR_STRSTR(filePath, ".png") != NULL || PNTR_STRSTR(filePath, ".PNG") != NULL) {
3098 return PNTR_IMAGE_TYPE_PNG;
3099 }
3100
3101 if (PNTR_STRSTR(filePath, ".bmp") != NULL || PNTR_STRSTR(filePath, ".BMP") != NULL) {
3102 return PNTR_IMAGE_TYPE_BMP;
3103 }
3104
3105 if (PNTR_STRSTR(filePath, ".jpg") != NULL || PNTR_STRSTR(filePath, ".jpeg") != NULL || PNTR_STRSTR(filePath, ".JPG") != NULL || PNTR_STRSTR(filePath, ".JPEG") != NULL) {
3106 return PNTR_IMAGE_TYPE_JPG;
3107 }
3108
3109 return PNTR_IMAGE_TYPE_UNKNOWN;
3110}
3111
3112// Load stb_image or cute_png.
3113#ifndef PNTR_LOAD_IMAGE_FROM_MEMORY
3114 #ifdef PNTR_STB_IMAGE
3115 #include "extensions/pntr_stb_image.h"
3116 #elif defined(PNTR_CUTE_PNG)
3117 #include "extensions/pntr_cute_png.h"
3118 #else
3119 // Allow disabling image loading.
3120 #ifdef PNTR_NO_LOAD_IMAGE
3121 #define PNTR_LOAD_IMAGE_FROM_MEMORY(type, fileData, dataSize) NULL
3122 #else
3123 // Default to stb_image.
3124 #include "extensions/pntr_stb_image.h"
3125 #endif
3126 #endif
3127#endif
3128
3129#ifndef PNTR_SAVE_IMAGE_TO_MEMORY
3130 #ifdef PNTR_STB_IMAGE
3131 #include "extensions/pntr_stb_image_write.h"
3132 #elif defined(PNTR_CUTE_PNG)
3133 #include "extensions/pntr_cute_png.h"
3134 #else
3135 // Allow disabling image saving.
3136 #ifdef PNTR_NO_SAVE_IMAGE
3137 #define PNTR_SAVE_IMAGE_TO_MEMORY(image, type, dataSize) NULL
3138 #else
3139 // Default to stb_image_write.
3140 #include "extensions/pntr_stb_image_write.h"
3141 #endif
3142 #endif
3143#endif
3144
3159PNTR_API pntr_image* pntr_load_image_from_memory(pntr_image_type type, const unsigned char *fileData, unsigned int dataSize) {
3160 if (fileData == NULL || dataSize == 0) {
3161 return (pntr_image*)pntr_set_error(PNTR_ERROR_INVALID_ARGS);
3162 }
3163
3164 return PNTR_LOAD_IMAGE_FROM_MEMORY(type, fileData, dataSize);
3165}
3166
3176PNTR_API pntr_image* pntr_load_image(const char* fileName) {
3177 if (fileName == NULL) {
3178 return (pntr_image*)pntr_set_error(PNTR_ERROR_INVALID_ARGS);
3179 }
3180
3181 unsigned int bytesRead;
3182 const unsigned char* fileData = pntr_load_file(fileName, &bytesRead);
3183 if (fileData == NULL) {
3184 return (pntr_image*)pntr_set_error(PNTR_ERROR_FAILED_TO_OPEN);
3185 }
3186
3187 pntr_image_type type = pntr_get_file_image_type(fileName);
3188 pntr_image* output = pntr_load_image_from_memory(type, fileData, bytesRead);
3189 pntr_unload_file((unsigned char*)fileData);
3190
3191 return output;
3192}
3193
3203PNTR_API void pntr_draw_image_tint(pntr_image* dst, pntr_image* src, int posX, int posY, pntr_color tint) {
3204 if (src == NULL) {
3205 return;
3206 }
3207 pntr_draw_image_tint_rec(dst, src,
3208 PNTR_CLITERAL(pntr_rectangle) { 0, 0, src->width, src->height },
3209 posX, posY, tint
3210 );
3211}
3212
3221PNTR_API void pntr_draw_image(pntr_image* dst, pntr_image* src, int posX, int posY) {
3222 if (src == NULL) {
3223 return;
3224 }
3225 pntr_draw_image_tint_rec(dst, src,
3226 PNTR_CLITERAL(pntr_rectangle) { 0, 0, src->width, src->height },
3227 posX, posY, PNTR_WHITE);
3228}
3229
3240PNTR_API pntr_color pntr_color_alpha_blend(pntr_color dst, pntr_color src) {
3241 pntr_blend_color(&dst, src);
3242 return dst;
3243}
3244
3256PNTR_API void pntr_draw_image_rec(pntr_image* dst, pntr_image* src, pntr_rectangle srcRect, int posX, int posY) {
3257 pntr_draw_image_tint_rec(dst, src, srcRect, posX, posY, PNTR_WHITE);
3258}
3259
3272PNTR_API void pntr_draw_image_tint_rec(pntr_image* dst, pntr_image* src, pntr_rectangle srcRect, int posX, int posY, pntr_color tint) {
3273 if (dst == NULL || src == NULL || posX >= dst->clip.x + dst->clip.width || posY >= dst->clip.y + dst->clip.height) {
3274 return;
3275 }
3276
3277 // Make sure the source rectangle is within bounds.
3278 if (!_pntr_rectangle_intersect(srcRect.x, srcRect.y,
3279 srcRect.width <= 0 ? src->width : srcRect.width,
3280 srcRect.height <= 0 ? src->height : srcRect.height,
3281 0, 0,
3282 src->width, src->height, &srcRect)) {
3283 return;
3284 }
3285
3286 // Update the source coordinates based on the destination
3287 if (posX < dst->clip.x) {
3288 srcRect.x -= posX - dst->clip.x;
3289 srcRect.width += posX - dst->clip.x;
3290 posX = dst->clip.x;
3291 }
3292 if (posY < dst->clip.y) {
3293 srcRect.y -= posY - dst->clip.y;
3294 srcRect.height += posY - dst->clip.y;
3295 posY = dst->clip.y;
3296 }
3297
3298 // Confine the destination.
3299 pntr_rectangle dstRect = PNTR_CLITERAL(pntr_rectangle) { posX, posY, srcRect.width, srcRect.height };
3300 if (!_pntr_rectangle_intersect(dstRect.x, dstRect.y,
3301 srcRect.width,
3302 srcRect.height,
3303 dst->clip.x, dst->clip.y,
3304 dst->clip.width, dst->clip.height, &dstRect)) {
3305 return;
3306 }
3307
3308 // Determine how many bits to skip for each line.
3309 int dst_skip = dst->pitch >> 2;
3310 int src_skip = src->pitch >> 2;
3311
3312 // Find the first pixel to render.
3313 pntr_color *dstPixel = dst->data + dst_skip * dstRect.y + dstRect.x;
3314 pntr_color *srcPixel = src->data + src_skip * srcRect.y + srcRect.x;
3315
3316 if (tint.value == PNTR_WHITE_VALUE) {
3317 while (dstRect.height-- > 0) {
3318 for (int x = 0; x < dstRect.width; ++x) {
3319 pntr_blend_color(dstPixel + x, srcPixel[x]);
3320 }
3321
3322 dstPixel += dst_skip;
3323 srcPixel += src_skip;
3324 }
3325 }
3326 else {
3327 while (dstRect.height-- > 0) {
3328 for (int x = 0; x < dstRect.width; ++x) {
3329 pntr_blend_color(dstPixel + x, pntr_color_tint(srcPixel[x], tint));
3330 }
3331
3332 dstPixel += dst_skip;
3333 srcPixel += src_skip;
3334 }
3335 }
3336}
3337
3350PNTR_API pntr_image* pntr_image_from_pixelformat(const void* imageData, int width, int height, pntr_pixelformat pixelFormat) {
3351 if (imageData == NULL || width <= 0 || height <= 0 || pixelFormat < 0) {
3352 return (pntr_image*)pntr_set_error(PNTR_ERROR_INVALID_ARGS);
3353 }
3354
3355 // Check how we are to convert the pixel format.
3356 switch (pixelFormat) {
3357 case PNTR_PIXELFORMAT_GRAYSCALE: {
3358 pntr_image* output = pntr_new_image(width, height);
3359 if (output == NULL) {
3360 return NULL;
3361 }
3362
3363 unsigned char* source = (unsigned char*)imageData;
3364 for (int i = 0; i < width * height; i++) {
3365 output->data[i] = pntr_get_pixel_color((void*)(source + i), pixelFormat);
3366 }
3367
3368 return output;
3369 }
3370
3371 case PNTR_PIXELFORMAT_ARGB8888:
3372 case PNTR_PIXELFORMAT_RGBA8888: {
3373 pntr_image* output = pntr_new_image(width, height);
3374
3375 pntr_color* source = (pntr_color*)imageData;
3376 for (int i = 0; i < width * height; i++) {
3377 output->data[i] = pntr_get_pixel_color((void*)(source + i), pixelFormat);
3378 }
3379
3380 return output;
3381 }
3382
3383 default: {
3384 return (pntr_image*)pntr_set_error(PNTR_ERROR_INVALID_ARGS);
3385 }
3386 }
3387}
3388
3401PNTR_API pntr_image* pntr_image_scale(pntr_image* image, float scaleX, float scaleY, pntr_filter filter) {
3402 if (image == NULL || scaleX <= 0.0f || scaleY <= 0.0f) {
3403 return (pntr_image*)pntr_set_error(PNTR_ERROR_INVALID_ARGS);
3404 }
3405
3406 return pntr_image_resize(image, (int)((float)image->width * scaleX), (int)((float)image->height * scaleY), filter);
3407}
3408
3421PNTR_API pntr_image* pntr_image_resize(pntr_image* image, int newWidth, int newHeight, pntr_filter filter) {
3422 if (image == NULL || newWidth <= 0 || newHeight <= 0 || filter < 0) {
3423 return (pntr_image*)pntr_set_error(PNTR_ERROR_INVALID_ARGS);
3424 }
3425
3426 pntr_image* output = pntr_new_image(newWidth, newHeight);
3427 if (output == NULL) {
3428 return NULL;
3429 }
3430
3431 switch (filter) {
3432 case PNTR_FILTER_BILINEAR: {
3433 float xRatio = (float)image->width / (float)newWidth;
3434 float yRatio = (float)image->height / (float)newHeight;
3435
3436 for (int y = 0; y < newHeight; y++) {
3437 float srcY = (float)y * yRatio;
3438 int srcYPixel = (int)srcY;
3439 int srcYPixelPlusOne = y == newHeight - 1 ? (int)srcY : (int)srcY + 1;
3440 for (int x = 0; x < newWidth; x++) {
3441 float srcX = (float)x * xRatio;
3442 int srcXPixel = (int)srcX;
3443 int srcXPixelPlusOne = x == newWidth - 1 ? (int)srcX : (int)srcX + 1;
3445 image->data[srcYPixel * (image->pitch >> 2) + srcXPixel],
3446 image->data[srcYPixelPlusOne * (image->pitch >> 2) + srcXPixel],
3447 image->data[srcYPixel * (image->pitch >> 2) + srcXPixelPlusOne],
3448 image->data[srcYPixelPlusOne * (image->pitch >> 2) + srcXPixelPlusOne],
3449 srcX - PNTR_FLOORF(srcX),
3450 srcY - PNTR_FLOORF(srcY)
3451 );
3452 }
3453 }
3454 }
3455 break;
3456 case PNTR_FILTER_NEARESTNEIGHBOR:
3457 default: {
3458 int xRatio = (image->width << 16) / newWidth + 1;
3459 int yRatio = (image->height << 16) / newHeight + 1;
3460
3461 for (int y = 0; y < newHeight; y++) {
3462 int y2 = (y * yRatio) >> 16;
3463 for (int x = 0; x < newWidth; x++) {
3464 PNTR_PIXEL(output, x, y) = PNTR_PIXEL(image, (x * xRatio) >> 16, y2);
3465 }
3466 }
3467 }
3468 break;
3469 }
3470
3471 // TODO: Copy the clip values scaled from the original image?
3472
3473 return output;
3474}
3475
3485PNTR_API void pntr_image_flip(pntr_image* image, bool horizontal, bool vertical) {
3486 if (image == NULL) {
3487 return;
3488 }
3489
3490 pntr_color swap;
3491 if (vertical) {
3492 for (int y = 0; y < image->height / 2; y++) {
3493 for (int x = 0; x < image->width; x++) {
3494 swap = PNTR_PIXEL(image, x, y);
3495 PNTR_PIXEL(image, x, y) = PNTR_PIXEL(image, x, image->height - 1 - y);
3496 PNTR_PIXEL(image, x, image->height - 1 - y) = swap;
3497 }
3498 }
3499 }
3500
3501 if (horizontal) {
3502 for (int y = 0; y < image->height; y++) {
3503 for (int x = 0; x < image->width / 2; x++) {
3504 swap = PNTR_PIXEL(image, x, y);
3505 PNTR_PIXEL(image, x, y) = PNTR_PIXEL(image, image->width - 1 - x, y);
3506 PNTR_PIXEL(image, image->width - 1 - x, y) = swap;
3507 }
3508 }
3509 }
3510}
3511
3519PNTR_API void pntr_image_color_replace(pntr_image* image, pntr_color color, pntr_color replace) {
3520 if (image == NULL) {
3521 return;
3522 }
3523
3524 for (int y = image->clip.y; y < image->clip.y + image->clip.height; y++) {
3525 pntr_color* pixel = &PNTR_PIXEL(image, 0, y);
3526 for (int x = image->clip.x; x < image->clip.x + image->clip.width; x++) {
3527 if (pixel->value == color.value) {
3528 *pixel = replace;
3529 }
3530 pixel++;
3531 }
3532 }
3533}
3534
3545PNTR_API pntr_color pntr_color_tint(pntr_color color, pntr_color tint) {
3546 if (tint.value == PNTR_WHITE_VALUE) {
3547 return color;
3548 }
3549
3550 return PNTR_NEW_COLOR(
3551 (unsigned char)(((float)color.rgba.r / 255.0f * (float)tint.rgba.r / 255.0f) * 255.0f),
3552 (unsigned char)(((float)color.rgba.g / 255.0f * (float)tint.rgba.g / 255.0f) * 255.0f),
3553 (unsigned char)(((float)color.rgba.b / 255.0f * (float)tint.rgba.b / 255.0f) * 255.0f),
3554 (unsigned char)(((float)color.rgba.a / 255.0f * (float)tint.rgba.a / 255.0f) * 255.0f)
3555 );
3556}
3557
3566PNTR_API pntr_color pntr_color_brightness(pntr_color color, float factor) {
3567 if (factor < -1.0f) {
3568 factor = -1.0f;
3569 }
3570 else if (factor > 1.0f) {
3571 factor = 1.0f;
3572 }
3573
3574 if (factor < 0.0f) {
3575 factor = 1.0f + factor;
3576 color.rgba.r = (unsigned char)((float)color.rgba.r * factor);
3577 color.rgba.g = (unsigned char)((float)color.rgba.g * factor);
3578 color.rgba.b = (unsigned char)((float)color.rgba.b * factor);
3579 }
3580 else {
3581 color.rgba.r = (unsigned char)(((float)(255 - color.rgba.r) * factor) + color.rgba.r);
3582 color.rgba.g = (unsigned char)(((float)(255 - color.rgba.g) * factor) + color.rgba.g);
3583 color.rgba.b = (unsigned char)(((float)(255 - color.rgba.b) * factor) + color.rgba.b);
3584 }
3585
3586 return color;
3587}
3588
3599PNTR_API pntr_color pntr_color_fade(pntr_color color, float factor) {
3600 if (factor < -1.0f) {
3601 factor = -1.0f;
3602 }
3603 else if (factor > 1.0f) {
3604 factor = 1.0f;
3605 }
3606
3607 if (factor < 0.0f) {
3608 color.rgba.a = (unsigned char)((float)color.rgba.a * (1.0f + factor));
3609 }
3610 else {
3611 color.rgba.a = (unsigned char)(((float)(255 - color.rgba.a) * factor) + color.rgba.a);
3612 }
3613
3614 return color;
3615}
3616
3625PNTR_API void pntr_image_color_fade(pntr_image* image, float factor) {
3626 if (image == NULL) {
3627 return;
3628 }
3629
3630 if (factor < -1.0f) {
3631 factor = -1.0f;
3632 }
3633 else if (factor > 1.0f) {
3634 factor = 1.0f;
3635 }
3636
3637 for (int y = image->clip.y; y < image->clip.y + image->clip.height; y++) {
3638 pntr_color* pixel = &PNTR_PIXEL(image, image->clip.x, y);
3639 for (int x = 0; x < image->clip.width; x++) {
3640 if (pixel->rgba.a > 0) {
3641 *pixel = pntr_color_fade(*pixel, factor);
3642 }
3643 pixel++;
3644 }
3645 }
3646}
3647
3655PNTR_API void pntr_set_pixel_color(void* dstPtr, pntr_pixelformat dstPixelFormat, pntr_color color) {
3656 if (PNTR_PIXELFORMAT == dstPixelFormat) {
3657 *((pntr_color*)dstPtr) = color;
3658 return;
3659 }
3660
3661 switch (dstPixelFormat) {
3662 case PNTR_PIXELFORMAT_RGBA8888:
3663 *((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;
3664 break;
3665 case PNTR_PIXELFORMAT_ARGB8888:
3666 *((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;
3667 break;
3668 case PNTR_PIXELFORMAT_GRAYSCALE: {
3669 float r = (float)color.rgba.r / 255.0f;
3670 float g = (float)color.rgba.g / 255.0f;
3671 float b = (float)color.rgba.b / 255.0f;
3672 ((unsigned char *)dstPtr)[0] = (unsigned char)((r * 0.299f + g * 0.587f + b * 0.114f) * 255.0f);
3673 }
3674 break;
3675 }
3676}
3677
3686PNTR_API pntr_color pntr_get_pixel_color(void* srcPtr, pntr_pixelformat srcPixelFormat) {
3687 switch (srcPixelFormat) {
3688 case PNTR_PIXELFORMAT_RGBA8888:
3689 return PNTR_NEW_COLOR(
3690 ((unsigned char *)srcPtr)[0],
3691 ((unsigned char *)srcPtr)[1],
3692 ((unsigned char *)srcPtr)[2],
3693 ((unsigned char *)srcPtr)[3]
3694 );
3695 case PNTR_PIXELFORMAT_ARGB8888:
3696 return PNTR_NEW_COLOR(
3697 ((unsigned char *)srcPtr)[1],
3698 ((unsigned char *)srcPtr)[2],
3699 ((unsigned char *)srcPtr)[3],
3700 ((unsigned char *)srcPtr)[0]
3701 );
3702 case PNTR_PIXELFORMAT_GRAYSCALE:
3703 // White, with alpha determining grayscale value. Use tint to change color afterwards.
3704 return PNTR_NEW_COLOR(255, 255, 255, ((unsigned char*)srcPtr)[0]);
3705 }
3706
3707 return PNTR_BLANK;
3708}
3709
3718PNTR_API void pntr_image_color_tint(pntr_image* image, pntr_color tint) {
3719 if (image == NULL) {
3720 return;
3721 }
3722
3723 for (int y = image->clip.y; y < image->clip.y + image->clip.height; y++) {
3724 pntr_color* pixel = &PNTR_PIXEL(image, image->clip.x, y);
3725 for (int x = 0; x < image->clip.width; x++) {
3726 *pixel = pntr_color_tint(*pixel, tint);
3727 pixel++;
3728 }
3729 }
3730}
3731
3742PNTR_API pntr_font* pntr_load_font_bmf(const char* fileName, const char* characters) {
3743 pntr_image* image = pntr_load_image(fileName);
3744 if (image == NULL) {
3745 return NULL;
3746 }
3747
3748 return pntr_load_font_bmf_from_image(image, characters);
3749}
3750
3760PNTR_API pntr_font* pntr_load_font_bmf_from_memory(const unsigned char* fileData, unsigned int dataSize, const char* characters) {
3761 if (fileData == NULL || dataSize == 0 || characters == NULL) {
3762 return (pntr_font*)pntr_set_error(PNTR_ERROR_INVALID_ARGS);
3763 }
3764
3765 pntr_image* image = pntr_load_image_from_memory(PNTR_IMAGE_TYPE_PNG, fileData, dataSize);
3766 if (image == NULL) {
3767 return NULL;
3768 }
3769
3770 return pntr_load_font_bmf_from_image(image, characters);
3771}
3772
3784PNTR_API pntr_font* _pntr_new_font(int numCharacters, size_t characterByteSize, pntr_image* atlas) {
3785 if (numCharacters <= 0) {
3786 return (pntr_font*)pntr_set_error(PNTR_ERROR_INVALID_ARGS);
3787 }
3788
3789 // Create the new font
3790 pntr_font* font = (pntr_font*)PNTR_MALLOC(sizeof(pntr_font));
3791 if (font == NULL) {
3792 return (pntr_font*)pntr_set_error(PNTR_ERROR_NO_MEMORY);
3793 }
3794
3795 // Source Rectangles
3796 font->srcRects = (pntr_rectangle*)PNTR_MALLOC(sizeof(pntr_rectangle) * (size_t)numCharacters);
3797 if (font->srcRects == NULL) {
3798 PNTR_FREE(font);
3799 return (pntr_font*)pntr_set_error(PNTR_ERROR_NO_MEMORY);
3800 }
3801
3802 // Glyph Rectangles
3803 font->glyphRects = (pntr_rectangle*)PNTR_MALLOC(sizeof(pntr_rectangle) * (size_t)numCharacters);
3804 if (font->glyphRects == NULL) {
3805 PNTR_FREE(font->srcRects);
3806 PNTR_FREE(font);
3807 return (pntr_font*)pntr_set_error(PNTR_ERROR_NO_MEMORY);
3808 }
3809
3810 // Characters
3811 font->characters = (char*)PNTR_MALLOC(characterByteSize);
3812 if (font->characters == NULL) {
3813 PNTR_FREE(font->srcRects);
3814 PNTR_FREE(font->glyphRects);
3815 PNTR_FREE(font);
3816 return (pntr_font*)pntr_set_error(PNTR_ERROR_NO_MEMORY);
3817 }
3818
3819 font->characters[0] = '\0';
3820 font->charactersLen = numCharacters;
3821 font->atlas = atlas;
3822
3823 return font;
3824}
3825
3834PNTR_API pntr_font* pntr_load_font_bmf_from_image(pntr_image* image, const char* characters) {
3835 if (image == NULL || characters == NULL) {
3836 return (pntr_font*)pntr_set_error(PNTR_ERROR_INVALID_ARGS);
3837 }
3838
3839 // Set up the initial font data.
3840 size_t charactersSize = PNTR_STRSIZE(characters);
3841 pntr_color seperator = pntr_image_get_color(image, 0, 0);
3842 pntr_rectangle currentRectangle = PNTR_CLITERAL(pntr_rectangle) {1, 0, 0, image->height};
3843
3844 // Find out how many characters there are.
3845 int numCharacters = 0;
3846 for (int i = 0; i < image->width; i++) {
3847 if (pntr_image_get_color(image, i, 0).value == seperator.value) {
3848 numCharacters++;
3849 }
3850 }
3851
3852 // Build the font.
3853 pntr_font* font = _pntr_new_font(numCharacters, charactersSize, image);
3854 if (font == NULL) {
3855 return NULL;
3856 }
3857
3858 // Set up the data structures.
3859 // TODO: Allow loading BMFont characters vertically
3860 int currentCharacter = 0;
3861 for (int i = 1; i < image->width; i++) {
3862 if (pntr_image_get_color(image, i, 0).value == seperator.value) {
3863 font->srcRects[currentCharacter] = currentRectangle;
3864 font->glyphRects[currentCharacter] = PNTR_CLITERAL(pntr_rectangle) {
3865 .x = 0,
3866 .y = 0,
3867 .width = currentRectangle.width,
3868 .height = currentRectangle.height,
3869 };
3870 currentRectangle.width = 0;
3871 currentRectangle.x = i + 1;
3872 currentCharacter++;
3873 }
3874 else {
3875 // Increase the width of the active glyph rectangle.
3876 currentRectangle.width++;
3877 }
3878 }
3879
3880 PNTR_MEMCPY(font->characters, characters, charactersSize);
3881
3882 return font;
3883}
3884
3897PNTR_API pntr_font* pntr_load_font_tty(const char* fileName, int glyphWidth, int glyphHeight, const char* characters) {
3898 pntr_image* image = pntr_load_image(fileName);
3899 if (image == NULL) {
3900 return NULL;
3901 }
3902
3903 pntr_font* output = pntr_load_font_tty_from_image(image, glyphWidth, glyphHeight, characters);
3904 if (output == NULL) {
3905 pntr_unload_image(image);
3906 }
3907
3908 return output;
3909}
3910
3911PNTR_API pntr_font* pntr_load_font_tty_from_memory(const unsigned char* fileData, unsigned int dataSize, int glyphWidth, int glyphHeight, const char* characters) {
3912 if (fileData == NULL || dataSize == 0 || characters == NULL || glyphWidth <= 0 || glyphHeight <= 0) {
3913 return (pntr_font*)pntr_set_error(PNTR_ERROR_INVALID_ARGS);
3914 }
3915
3916 pntr_image* image = pntr_load_image_from_memory(PNTR_IMAGE_TYPE_PNG, fileData, dataSize);
3917 if (image == NULL) {
3918 return NULL;
3919 }
3920
3921 pntr_font* output = pntr_load_font_tty_from_image(image, glyphWidth, glyphHeight, characters);
3922 if (output == NULL) {
3923 pntr_unload_image(image);
3924 }
3925
3926 return output;
3927}
3928
3929PNTR_API pntr_font* pntr_load_font_tty_from_image(pntr_image* image, int glyphWidth, int glyphHeight, const char* characters) {
3930 if (image == NULL || characters == NULL || glyphWidth <= 0 || glyphHeight <= 0) {
3931 return (pntr_font*)pntr_set_error(PNTR_ERROR_INVALID_ARGS);
3932 }
3933
3934 // Find out how many characters there are.
3935 int numCharacters = (int)PNTR_STRLEN(characters);
3936 size_t charactersSize = PNTR_STRSIZE(characters);
3937
3938 // Create the font.
3939 pntr_font* font = _pntr_new_font(numCharacters, charactersSize, image);
3940 if (font == NULL) {
3941 return NULL;
3942 }
3943
3944 // Set up the font data.
3945 for (int currentCharIndex = 0; currentCharIndex < numCharacters; currentCharIndex++) {
3946 // Source rectangle.
3947 font->srcRects[currentCharIndex] = PNTR_CLITERAL(pntr_rectangle) {
3948 .x = (currentCharIndex % (image->width / glyphWidth)) * glyphWidth,
3949 .y = (currentCharIndex / (image->width / glyphWidth)) * glyphHeight,
3950 .width = glyphWidth,
3951 .height = glyphHeight
3952 };
3953
3954 // Where the glyph will be rendered.
3955 font->glyphRects[currentCharIndex] = PNTR_CLITERAL(pntr_rectangle) {
3956 .x = 0,
3957 .y = 0,
3958 .width = glyphWidth,
3959 .height = glyphHeight,
3960 };
3961 }
3962
3963 PNTR_MEMCPY(font->characters, characters, charactersSize);
3964
3965 return font;
3966}
3967
3973PNTR_API void pntr_unload_font(pntr_font* font) {
3974 if (font == NULL) {
3975 return;
3976 }
3977
3978 pntr_unload_image(font->atlas);
3979 pntr_unload_memory(font->srcRects);
3980 pntr_unload_memory(font->glyphRects);
3981 pntr_unload_memory(font->characters);
3982 PNTR_FREE(font);
3983}
3984
3992PNTR_API pntr_font* pntr_font_copy(pntr_font* font) {
3993 if (font == NULL) {
3994 return (pntr_font*)pntr_set_error(PNTR_ERROR_INVALID_ARGS);
3995 }
3996
3997 pntr_image* atlas = pntr_image_copy(font->atlas);
3998 if (atlas == NULL) {
3999 return NULL;
4000 }
4001
4002 size_t charactersSize = PNTR_STRSIZE(font->characters);
4003 pntr_font* output = _pntr_new_font(font->charactersLen, charactersSize, atlas);
4004 if (output == NULL) {
4005 pntr_unload_image(atlas);
4006 return NULL;
4007 }
4008
4009 PNTR_MEMCPY(output->srcRects, font->srcRects, sizeof(pntr_rectangle) * (size_t)output->charactersLen);
4010 PNTR_MEMCPY(output->glyphRects, font->glyphRects, sizeof(pntr_rectangle) * (size_t)output->charactersLen);
4011 PNTR_MEMCPY(output->characters, font->characters, charactersSize);
4012
4013 return output;
4014}
4015
4026PNTR_API pntr_font* pntr_font_scale(pntr_font* font, float scaleX, float scaleY, pntr_filter filter) {
4027 if (font == NULL || scaleX <= 0.0f || scaleY <= 0.0f) {
4028 return (pntr_font*)pntr_set_error(PNTR_ERROR_INVALID_ARGS);
4029 }
4030
4031 // Create the new font.
4032 pntr_font* output = pntr_font_copy(font);
4033 if (output == NULL) {
4034 return NULL;
4035 }
4036
4037 // Resize the atlas.
4038 pntr_image* resizedAtlas = pntr_image_scale(output->atlas, scaleX, scaleY, filter);
4039 pntr_unload_image(output->atlas);
4040 output->atlas = resizedAtlas;
4041
4042 // Resize the rectangles.
4043 for (int i = 0; i < font->charactersLen; i++) {
4044 output->srcRects[i].x = (int)((float)output->srcRects[i].x * scaleX);
4045 output->srcRects[i].y = (int)((float)output->srcRects[i].y * scaleY);
4046 output->srcRects[i].width = (int)((float)output->srcRects[i].width * scaleX);
4047 output->srcRects[i].height = (int)((float)output->srcRects[i].height * scaleY);
4048 output->glyphRects[i].x = (int)((float)output->glyphRects[i].x * scaleX);
4049 output->glyphRects[i].y = (int)((float)output->glyphRects[i].y * scaleY);
4050 output->glyphRects[i].width = (int)((float)output->glyphRects[i].width * scaleX);
4051 output->glyphRects[i].height = (int)((float)output->glyphRects[i].height * scaleY);
4052 }
4053
4054 return output;
4055}
4056
4070PNTR_API void pntr_draw_text_len(pntr_image* dst, pntr_font* font, const char* text, int textLength, int posX, int posY, pntr_color tint) {
4071 if (dst == NULL || font == NULL || text == NULL) {
4072 return;
4073 }
4074
4075 int x = posX;
4076 int y = posY;
4077 int tallestCharacter = 0;
4078
4079 // Iterate through each character.
4080 pntr_codepoint_t codepoint;
4081 int count = 0;
4082 for (const char* v = PNTR_STRCODEPOINT(text, &codepoint); codepoint; v = PNTR_STRCODEPOINT(v, &codepoint)) {
4083 // If there is a text length provided, only draw up to that length.
4084 if (textLength > 0) {
4085 if (++count > textLength) {
4086 break;
4087 }
4088 }
4089
4090 // If the character is a newline, move to the next line.
4091 if (codepoint == '\n') {
4092 // TODO: pntr_draw_text(): Allow for center/right alignment
4093 x = posX;
4094 y += tallestCharacter;
4095 continue;
4096 }
4097
4098 // Find the character in the font's character index.
4099 char* foundCharacter = PNTR_STRCHR(font->characters, codepoint);
4100 if (!foundCharacter) {
4101 continue;
4102 }
4103
4104 // Find the index of the character in the string.
4105 #ifdef PNTR_ENABLE_UTF8
4106 int i = (int)utf8nlen(font->characters, (size_t)(foundCharacter - font->characters));
4107 #else
4108 int i = (int)(foundCharacter - font->characters);
4109 #endif
4110
4111 // Draw the character, unless it's a space.
4112 if (codepoint != ' ') {
4113 pntr_draw_image_tint_rec(dst, font->atlas, font->srcRects[i], x + font->glyphRects[i].x, y + font->glyphRects[i].y, tint);
4114 }
4115
4116 x += font->glyphRects[i].x + font->glyphRects[i].width;
4117 if (tallestCharacter < font->glyphRects[i].y + font->glyphRects[i].height) {
4118 tallestCharacter = font->glyphRects[i].y + font->glyphRects[i].height;
4119 }
4120 }
4121}
4122
4135PNTR_API void pntr_draw_text(pntr_image* dst, pntr_font* font, const char* text, int posX, int posY, pntr_color tint) {
4136 pntr_draw_text_len(dst, font, text, 0, posX, posY, tint);
4137}
4138
4152PNTR_API void pntr_draw_text_wrapped(pntr_image* dst, pntr_font* font, const char* text, int posX, int posY, int maxWidth, pntr_color tint) {
4153 if (dst == NULL || font == NULL || text == NULL) {
4154 return;
4155 }
4156
4157 pntr_codepoint_t codepoint;
4158 char* currentChar = (char*)text;
4159 char* lineStart = currentChar;
4160 int lineLength = 1;
4161 char* lastSpace = NULL;
4162 int currentY = 0;
4163 pntr_vector textSize = {0, 0};
4164
4165 // Iterate through each character.
4166 for (char* nextChar = PNTR_STRCODEPOINT(text, &codepoint); codepoint; nextChar = PNTR_STRCODEPOINT(nextChar, &codepoint)) {
4167 if (codepoint == ' ' || codepoint == '\n') {
4168 textSize = pntr_measure_text_ex(font, lineStart, lineLength - 1);
4169 if (textSize.x > maxWidth) {
4170 if (lastSpace != NULL) {
4171 #ifdef PNTR_ENABLE_UTF8
4172 lineLength = (int)utf8nlen(lineStart, (size_t)(lastSpace - lineStart));
4173 #else
4174 lineLength = (int)(lastSpace - lineStart);
4175 #endif
4176 pntr_draw_text_len(dst, font, lineStart, lineLength, posX, posY + currentY, tint);
4177 currentY += textSize.y;
4178 lineStart = lastSpace + 1;
4179 #ifdef PNTR_ENABLE_UTF8
4180 lineLength = (int)utf8nlen(lineStart, (size_t)(currentChar - lineStart));
4181 #else
4182 lineLength = (int)(currentChar - lineStart);
4183 #endif
4184 }
4185 else {
4186 // No current space, so draw what's in and move to new line.
4187 pntr_draw_text_len(dst, font, lineStart, lineLength, posX, posY + currentY, tint);
4188 currentY += textSize.y;
4189 lineStart = nextChar;
4190 lineLength = 0;
4191 }
4192 }
4193 else if (codepoint == '\n') {
4194 #ifdef PNTR_ENABLE_UTF8
4195 lineLength = (int)utf8nlen(lineStart, (size_t)(currentChar - lineStart));
4196 #else
4197 lineLength = (int)(currentChar - lineStart);
4198 #endif
4199 pntr_draw_text_len(dst, font, lineStart, lineLength, posX, posY + currentY, tint);
4200 currentY += textSize.y;
4201 lineStart = nextChar;
4202 lineLength = 0;
4203 lastSpace = NULL;
4204 }
4205 else {
4206 lastSpace = currentChar;
4207 }
4208 }
4209
4210 currentChar = nextChar;
4211 lineLength++;
4212 }
4213
4214 // Check if the last line is too long, and split it by the last space.
4215 if (pntr_measure_text(font, lineStart) > maxWidth && lastSpace != NULL) {
4216 #ifdef PNTR_ENABLE_UTF8
4217 lineLength = (int)utf8nlen(lineStart, (size_t)(lastSpace - lineStart));
4218 #else
4219 lineLength = (int)(lastSpace - lineStart);
4220 #endif
4221 pntr_draw_text_len(dst, font, lineStart, lineLength, posX, posY + currentY, tint);
4222 currentY += textSize.y;
4223 lineStart = lastSpace + 1;
4224 }
4225
4226 pntr_draw_text(dst, font, lineStart, posX, posY + currentY, tint);
4227}
4228
4229#ifdef PNTR_ENABLE_VARGS
4247PNTR_API void pntr_draw_text_ex(pntr_image* dst, pntr_font* font, int posX, int posY, pntr_color tint, int maxlen, const char* text, ...) {
4248 #ifndef PNTR_DRAW_TEXT_EX_STRING_LENGTH
4252 #define PNTR_DRAW_TEXT_EX_STRING_LENGTH 512
4253 #endif
4254 int length = (maxlen > 0) ? maxlen : PNTR_DRAW_TEXT_EX_STRING_LENGTH;
4255 char output[length];
4256
4257 va_list arg_ptr;
4258 va_start(arg_ptr, text);
4259 vsnprintf(output, length, text, arg_ptr);
4260 va_end(arg_ptr);
4261
4262 pntr_draw_text(dst, font, output, posX, posY, tint);
4263}
4264#endif
4265
4274PNTR_API int pntr_measure_text(pntr_font* font, const char* text) {
4275 return pntr_measure_text_ex(font, text, 0).x;
4276}
4277
4287PNTR_API pntr_vector pntr_measure_text_ex(pntr_font* font, const char* text, int textLength) {
4288 if (font == NULL || text == NULL) {
4289 return PNTR_CLITERAL(pntr_vector){0, 0};
4290 }
4291
4292 pntr_vector output = PNTR_CLITERAL(pntr_vector) { .x = 0, .y = 0 };
4293 int currentX = 0;
4294 int currentY = 0;
4295 int index = 0;
4296
4297 pntr_codepoint_t codepoint;
4298 for (const char* v = PNTR_STRCODEPOINT(text, &codepoint); codepoint; v = PNTR_STRCODEPOINT(v, &codepoint)) {
4299 // Stop drawing if we're only counting a certain amount of characters.
4300 if (textLength > 0 && index++ >= textLength) {
4301 break;
4302 }
4303
4304 // Consider any newlines
4305 if (codepoint == '\n') {
4306 output.y += currentY;
4307 currentX = 0;
4308 continue;
4309 }
4310
4311 // Find the index of the character in the font atlas.
4312 char* foundCharacter = PNTR_STRCHR(font->characters, codepoint);
4313 if (foundCharacter != NULL) {
4314 // Find the index of the character in the string.
4315 #ifdef PNTR_ENABLE_UTF8
4316 int i = (int)utf8nlen(font->characters, (size_t)(foundCharacter - font->characters));
4317 #else
4318 int i = (int)(foundCharacter - font->characters);
4319 #endif
4320
4321 currentX += font->glyphRects[i].x + font->glyphRects[i].width;
4322 if (currentX > output.x) {
4323 output.x = currentX;
4324 }
4325
4326 // Find the tallest character
4327 if (currentY < font->glyphRects[i].y + font->glyphRects[i].height) {
4328 currentY = font->glyphRects[i].y + font->glyphRects[i].height;
4329 }
4330 }
4331 }
4332
4333 // Has at least one line.
4334 output.y += currentY;
4335
4336 return output;
4337}
4338
4349PNTR_API pntr_image* pntr_gen_image_text(pntr_font* font, const char* text, pntr_color tint, pntr_color backgroundColor) {
4350 pntr_vector size = pntr_measure_text_ex(font, text, 0);
4351 if (size.x <= 0 || size.y <= 0) {
4352 return NULL;
4353 }
4354
4355 pntr_image* output = pntr_gen_image_color(size.x, size.y, backgroundColor);
4356 if (output == NULL) {
4357 return NULL;
4358 }
4359
4360 pntr_draw_text(output, font, text, 0, 0, tint);
4361 return output;
4362}
4363
4383 #ifdef PNTR_DEFAULT_FONT
4384 return PNTR_DEFAULT_FONT();
4385 #elif defined(PNTR_ENABLE_DEFAULT_FONT)
4386 // Load font8x8 character atlas.
4387 // https://github.com/dhepper/font8x8
4388 #include "external/font8x8_basic.h"
4389
4390 // Default parameters for font8x8.
4391 #define PNTR_DEFAULT_FONT_NAME font8x8_basic
4392 #define PNTR_DEFAULT_FONT_GLYPH_WIDTH 8
4393 #define PNTR_DEFAULT_FONT_GLYPH_HEIGHT 8
4394 #define PNTR_DEFAULT_FONT_CHARACTERS_LEN 95
4395
4396 // Build the atlas.
4397 pntr_image* atlas = pntr_gen_image_color(
4398 PNTR_DEFAULT_FONT_GLYPH_WIDTH * PNTR_DEFAULT_FONT_CHARACTERS_LEN,
4399 PNTR_DEFAULT_FONT_GLYPH_HEIGHT,
4400 PNTR_BLANK);
4401 if (atlas == NULL) {
4402 return NULL;
4403 }
4404
4405 // Iterate through all the characters and draw them manually.
4406 for (int i = 0; i < PNTR_DEFAULT_FONT_CHARACTERS_LEN; i++) {
4407 const unsigned char* bitmap = PNTR_DEFAULT_FONT_NAME[i];
4408 for (int x = 0; x < 8; x++) {
4409 for (int y = 0; y < 8; y++) {
4410 if (bitmap[y] & 1 << x) {
4411 pntr_draw_point(atlas, PNTR_DEFAULT_FONT_GLYPH_WIDTH * i + x, y, PNTR_WHITE);
4412 }
4413 }
4414 }
4415 }
4416
4417 // Build the character set with a null character at the end.
4418 char characters[PNTR_DEFAULT_FONT_CHARACTERS_LEN + 1];
4419 for (int i = 0; i < PNTR_DEFAULT_FONT_CHARACTERS_LEN; i++) {
4420 characters[i] = (char)(i + 32); // ASCII
4421 }
4422 characters[PNTR_DEFAULT_FONT_CHARACTERS_LEN] = '\0';
4423
4424 // Use TTY to build the remaining font parameters.
4425 pntr_font* font = pntr_load_font_tty_from_image(atlas, PNTR_DEFAULT_FONT_GLYPH_WIDTH, PNTR_DEFAULT_FONT_GLYPH_HEIGHT, characters);
4426 if (font == NULL) {
4427 pntr_unload_image(atlas);
4428 return NULL;
4429 }
4430
4431 return font;
4432 #else
4433 return (pntr_font*)pntr_set_error(PNTR_ERROR_NOT_SUPPORTED);
4434 #endif
4435}
4436
4451PNTR_API pntr_font* pntr_load_font_ttf(const char* fileName, int fontSize) {
4452 if (fileName == NULL || fontSize <= 0) {
4453 return (pntr_font*)pntr_set_error(PNTR_ERROR_INVALID_ARGS);
4454 }
4455
4456 #ifndef PNTR_ENABLE_TTF
4457 return (pntr_font*)pntr_set_error(PNTR_ERROR_NOT_SUPPORTED);
4458 #else
4459 unsigned int bytesRead;
4460 unsigned char* fileData = pntr_load_file(fileName, &bytesRead);
4461 if (fileData == NULL) {
4462 return NULL;
4463 }
4464
4465 pntr_font* output = pntr_load_font_ttf_from_memory(fileData, bytesRead, fontSize);
4466 pntr_unload_file(fileData);
4467
4468 return output;
4469 #endif
4470}
4471
4487PNTR_API pntr_font* pntr_load_font_ttf_from_memory(const unsigned char* fileData, unsigned int dataSize, int fontSize) {
4488 if (fileData == NULL || dataSize == 0 || fontSize <= 0) {
4489 return (pntr_font*)pntr_set_error(PNTR_ERROR_INVALID_ARGS);
4490 }
4491
4492 #ifndef PNTR_ENABLE_TTF
4493 return (pntr_font*)pntr_set_error(PNTR_ERROR_NOT_SUPPORTED);
4494 #else
4495 // Which ASCII character to start rendering into the atlas
4496 #define PNTR_FONT_TTF_GLYPH_START 32
4497
4498 // Find out how many glyhs we should prepare
4499 #ifndef PNTR_FONT_TTF_GLYPH_NUM
4500 #ifdef PNTR_ENABLE_UTF8
4501 // Up to the Cyrillic Supplement, minus the first 32 ascii characters
4502 // https://www.w3schools.com/charsets/ref_html_utf8.asp
4503 #define PNTR_FONT_TTF_GLYPH_NUM 1295
4504 #else
4505 // ASCII characater set
4506 #define PNTR_FONT_TTF_GLYPH_NUM 95
4507 #endif
4508 #endif
4509
4510 // Create the bitmap data with ample space based on the font size
4511 int columns = 32;
4512 int rows = PNTR_FONT_TTF_GLYPH_NUM / columns;
4513 int width = fontSize * columns;
4514 int height = fontSize * rows;
4515 unsigned char* bitmap = (unsigned char*)PNTR_MALLOC((size_t)(width * height));
4516 if (bitmap == NULL) {
4517 return (pntr_font*)pntr_set_error(PNTR_ERROR_NO_MEMORY);
4518 }
4519
4520 // Bake the font into the bitmap
4521 stbtt_bakedchar characterData[PNTR_FONT_TTF_GLYPH_NUM];
4522 int result = stbtt_BakeFontBitmap(fileData, 0, (float)fontSize, bitmap, width, height, PNTR_FONT_TTF_GLYPH_START, PNTR_FONT_TTF_GLYPH_NUM, characterData);
4523
4524 // Check to make sure the font was baked correctly
4525 if (result == 0) {
4526 PNTR_FREE(bitmap);
4527 return (pntr_font*)pntr_set_error(PNTR_ERROR_UNKNOWN);
4528 }
4529
4530 // Port the bitmap to a pntr_image as the font atlas
4531 pntr_image* atlas = pntr_image_from_pixelformat((const void*)bitmap, width, height, PNTR_PIXELFORMAT_GRAYSCALE);
4532 PNTR_FREE(bitmap);
4533 if (atlas == NULL) {
4534 return NULL;
4535 }
4536
4537 // Clear up the unused atlas space from memory, from the top left
4538 pntr_rectangle crop = pntr_image_alpha_border(atlas, 0.0f);
4539 pntr_image_crop(atlas, 0, 0, crop.x + crop.width, crop.y + crop.height);
4540
4541 // Create the font data, with a null terminator at the end.
4542 size_t charactersSize = sizeof(pntr_codepoint_t) * (size_t)PNTR_FONT_TTF_GLYPH_NUM + 1;
4543 pntr_font* font = _pntr_new_font(PNTR_FONT_TTF_GLYPH_NUM, charactersSize, atlas);
4544 if (font == NULL) {
4545 pntr_unload_image(atlas);
4546 return NULL;
4547 }
4548
4549 // Capture each glyph data
4550 #ifdef PNTR_ENABLE_UTF8
4551 char* destination = font->characters; // Where to write the new character.
4552 #endif
4553
4554 // Build each character
4555 for (int i = 0; i < PNTR_FONT_TTF_GLYPH_NUM; i++) {
4556 // Calculate the source rectangles
4557 font->srcRects[i] = PNTR_CLITERAL(pntr_rectangle) {
4558 .x = characterData[i].x0,
4559 .y = characterData[i].y0,
4560 .width = characterData[i].x1 - characterData[i].x0,
4561 .height = characterData[i].y1 - characterData[i].y0
4562 };
4563
4564 // Find where the glyphs will be rendered
4565 font->glyphRects[i] = PNTR_CLITERAL(pntr_rectangle) {
4566 .x = (int)characterData[i].xoff,
4567 .y = (int)(characterData[i].yoff + ((float)fontSize / 1.5f)), // TODO: Determine correct y glyph value
4568 .width = (int)characterData[i].xadvance,
4569 .height = (int)((float)fontSize / 3.0f) // TODO: Determine the correct glyph height
4570 };
4571
4572 // Set up the active character
4573 #ifndef PNTR_ENABLE_UTF8
4574 font->characters[i] = (char)(PNTR_FONT_TTF_GLYPH_START + i);
4575 #else
4576 // Append the character to the destination, considering the remaining memory
4577 destination = utf8catcodepoint(destination, (pntr_codepoint_t)(PNTR_FONT_TTF_GLYPH_START + i), charactersSize - (size_t)(destination - font->characters));
4578 #endif
4579 }
4580
4581 // Stick a null terminator at the end of the character string.
4582 #ifdef PNTR_ENABLE_UTF8
4583 destination[0] = '\0';
4584
4585 // Resize the character string to the correct size.
4586 size_t newSize = PNTR_STRSIZE(font->characters);
4587 char* newCharacters = (char*)PNTR_MALLOC(newSize);
4588 if (newCharacters != NULL) {
4589 PNTR_MEMCPY(newCharacters, font->characters, newSize);
4590 PNTR_FREE(font->characters);
4591 font->characters = newCharacters;
4592 }
4593 #else
4594 font->characters[PNTR_FONT_TTF_GLYPH_NUM] = '\0';
4595 #endif
4596
4597 return font;
4598 #endif
4599}
4600
4610PNTR_API pntr_color pntr_color_invert(pntr_color color) {
4611 return PNTR_NEW_COLOR(
4612 (unsigned char)(255 - color.rgba.r),
4613 (unsigned char)(255 - color.rgba.g),
4614 (unsigned char)(255 - color.rgba.b),
4615 color.rgba.a
4616 );
4617}
4618
4626PNTR_API void pntr_image_color_invert(pntr_image* image) {
4627 if (image == NULL) {
4628 return;
4629 }
4630
4631 for (int y = image->clip.y; y < image->clip.y + image->clip.height; y++) {
4632 pntr_color* pixel = &PNTR_PIXEL(image, image->clip.x, y);
4633 for (int x = 0; x < image->clip.width; x++) {
4634 *pixel = pntr_color_invert(*pixel);
4635 pixel++;
4636 }
4637 }
4638}
4639
4648PNTR_API void pntr_image_color_brightness(pntr_image* image, float factor) {
4649 if (image == NULL) {
4650 return;
4651 }
4652
4653 if (factor < -1.0f) {
4654 factor = -1.0f;
4655 }
4656 else if (factor > 1.0f) {
4657 factor = 1.0f;
4658 }
4659
4660 for (int y = image->clip.y; y < image->clip.y + image->clip.height; y++) {
4661 pntr_color* pixel = &PNTR_PIXEL(image, image->clip.x, y);
4662 for (int x = 0; x < image->clip.width; x++) {
4663 *pixel = pntr_color_brightness(*pixel, factor);
4664 pixel++;
4665 }
4666 }
4667}
4668
4669#ifndef PNTR_LOAD_FILE
4670 #ifdef PNTR_NO_STDIO
4671 #define PNTR_LOAD_FILE(fileName, bytesRead) NULL
4672 #else
4673 #include <stdio.h> // FILE, fopen, fread
4674 #endif
4675#endif // PNTR_LOAD_FILE
4676
4692PNTR_API unsigned char* pntr_load_file(const char* fileName, unsigned int* bytesRead) {
4693 if (fileName == NULL) {
4694 return (unsigned char*)pntr_set_error(PNTR_ERROR_INVALID_ARGS);
4695 }
4696
4697 #ifdef PNTR_LOAD_FILE
4698 return PNTR_LOAD_FILE(fileName, bytesRead);
4699 #else
4700 FILE* file = fopen(fileName, "rb");
4701 if (file == NULL) {
4702 if (bytesRead != NULL) {
4703 *bytesRead = 0;
4704 }
4705 return (unsigned char*)pntr_set_error(PNTR_ERROR_FAILED_TO_OPEN);
4706 }
4707
4708 fseek(file, 0, SEEK_END);
4709 size_t size = (size_t)ftell(file);
4710 fseek(file, 0, SEEK_SET);
4711
4712 if (size <= 0) {
4713 fclose(file);
4714 if (bytesRead != NULL) {
4715 *bytesRead = 0;
4716 }
4717 return (unsigned char*)pntr_set_error(PNTR_ERROR_FAILED_TO_OPEN);
4718 }
4719
4720 unsigned char* data = (unsigned char*)PNTR_MALLOC(size * sizeof(unsigned char));
4721 if (data == NULL) {
4722 fclose(file);
4723 if (bytesRead != NULL) {
4724 *bytesRead = 0;
4725 }
4726 return (unsigned char*)pntr_set_error(PNTR_ERROR_NO_MEMORY);
4727 }
4728
4729 // Read the file
4730 unsigned int bytes = (unsigned int)fread(data, sizeof(unsigned char), size, file);
4731 fclose(file);
4732 if (bytesRead != NULL) {
4733 *bytesRead = bytes;
4734 }
4735
4736 return data;
4737 #endif
4738}
4739
4750PNTR_API const char* pntr_load_file_text(const char *fileName) {
4751 unsigned int bytesRead;
4752 unsigned char* data = pntr_load_file(fileName, &bytesRead);
4753
4754 if (data == NULL) {
4755 return NULL;
4756 }
4757
4758 // While we have the loaded data, we'll need to null terminate it.
4759 char* output = (char*)PNTR_MALLOC(bytesRead + 1);
4760 if (output == NULL) {
4761 PNTR_FREE(data);
4762 return NULL;
4763 }
4764
4765 PNTR_MEMCPY(output, data, bytesRead);
4766 output[bytesRead] = '\0';
4767 PNTR_FREE(data);
4768 return (const char*)output;
4769}
4770
4778PNTR_API void pntr_unload_file_text(const char* text) {
4779 pntr_unload_memory((void*)text);
4780}
4781
4782#ifndef PNTR_SAVE_FILE
4783 #ifdef PNTR_NO_STDIO
4784 #define PNTR_SAVE_FILE(fileName, data, bytesToWrite) NULL
4785 #else
4786 #include <stdio.h> // FILE, fopen, fwrite
4787 #endif
4788#endif // PNTR_SAVE_FILE
4789
4803PNTR_API bool pntr_save_file(const char *fileName, const void *data, unsigned int bytesToWrite) {
4804 if (fileName == NULL || data == NULL) {
4805 pntr_set_error(PNTR_ERROR_INVALID_ARGS);
4806 return false;
4807 }
4808
4809 #ifdef PNTR_SAVE_FILE
4810 return PNTR_SAVE_FILE(fileName, data, bytesToWrite);
4811 #else
4812 FILE *file = fopen(fileName, "wb");
4813 if (file == NULL) {
4814 pntr_set_error(PNTR_ERROR_FAILED_TO_OPEN);
4815 return false;
4816 }
4817
4818 size_t count = fwrite(data, sizeof(unsigned char), bytesToWrite, file);
4819
4820 if (count <= 0) {
4821 fclose(file);
4822 pntr_set_error(PNTR_ERROR_FAILED_TO_OPEN);
4823 return false;
4824 }
4825
4826 if (count != (size_t)bytesToWrite) {
4827 fclose(file);
4828 pntr_set_error(PNTR_ERROR_FAILED_TO_OPEN);
4829 return false;
4830 }
4831
4832 return fclose(file) == 0;
4833 #endif
4834}
4835
4845PNTR_API int pntr_get_pixel_data_size(int width, int height, pntr_pixelformat pixelFormat) {
4846 if (width <= 0 || height <= 0 || pixelFormat < 0) {
4847 return 0;
4848 }
4849
4850 int bitsPerPixel = 0;
4851 int bitsPerByte = 8;
4852 switch (pixelFormat) {
4853 case PNTR_PIXELFORMAT_RGBA8888:
4854 case PNTR_PIXELFORMAT_ARGB8888:
4855 bitsPerPixel = (int)sizeof(pntr_color) * bitsPerByte;
4856 break;
4857 case PNTR_PIXELFORMAT_GRAYSCALE:
4858 bitsPerPixel = (int)sizeof(unsigned char) * bitsPerByte;
4859 break;
4860 default:
4861 bitsPerPixel = (int)sizeof(pntr_color) * bitsPerByte;
4862 pntr_set_error(PNTR_ERROR_NOT_SUPPORTED);
4863 break;
4864 }
4865
4866 return bitsPerPixel * width * height / bitsPerByte; // Bytes
4867}
4868
4878PNTR_API void* pntr_image_to_pixelformat(pntr_image* image, unsigned int* dataSize, pntr_pixelformat pixelFormat) {
4879 if (image == NULL) {
4880 return pntr_set_error(PNTR_ERROR_INVALID_ARGS);
4881 }
4882
4883 int imageSize = pntr_get_pixel_data_size(image->width, image->height, pixelFormat);
4884 if (imageSize <= 0) {
4885 return pntr_set_error(PNTR_ERROR_UNKNOWN);
4886 }
4887
4888 void* data = PNTR_MALLOC((size_t)imageSize);
4889 if (data == NULL) {
4890 return pntr_set_error(PNTR_ERROR_NO_MEMORY);
4891 }
4892
4893 int pixelSize = pntr_get_pixel_data_size(1, 1, pixelFormat);
4894
4895 int i = 0;
4896 for (int y = 0; y < image->height; y++) {
4897 for (int x = 0; x < image->width; x++) {
4899 ((unsigned char*)data) + (i++ * pixelSize),
4900 pixelFormat,
4901 PNTR_PIXEL(image, x, y)
4902 );
4903 }
4904 }
4905
4906 // Output the data size
4907 if (dataSize != NULL) {
4908 *dataSize = (unsigned int)imageSize;
4909 }
4910
4911 return data;
4912}
4913
4930PNTR_API unsigned char* pntr_save_image_to_memory(pntr_image* image, pntr_image_type type, unsigned int* dataSize) {
4931 if (image == NULL) {
4932 return (unsigned char*)pntr_set_error(PNTR_ERROR_INVALID_ARGS);
4933 }
4934
4935 return PNTR_SAVE_IMAGE_TO_MEMORY(image, type, dataSize);
4936}
4937
4949PNTR_API bool pntr_save_image(pntr_image* image, const char* fileName) {
4950 pntr_image_type type = pntr_get_file_image_type(fileName);
4951 unsigned int dataSize;
4952 unsigned char* data = pntr_save_image_to_memory(image, type, &dataSize);
4953 if (data == NULL) {
4954 return false;
4955 }
4956
4957 bool result = pntr_save_file(fileName, data, dataSize);
4958 PNTR_FREE(data);
4959
4960 return result;
4961}
4962
4970PNTR_API void pntr_unload_file(unsigned char* fileData) {
4971 pntr_unload_memory((void*)fileData);
4972}
4973
4982PNTR_API pntr_rectangle pntr_image_alpha_border(pntr_image* image, float threshold) {
4983 if (image == NULL) {
4984 return PNTR_CLITERAL(pntr_rectangle) {0, 0, 0, 0};
4985 }
4986
4987 unsigned char alphaThreshold = (unsigned char)(threshold * 255.0f);
4988 int xMin = 9999999;
4989 int xMax = 0;
4990 int yMin = 9999999;
4991 int yMax = 0;
4992
4993 for (int y = 0; y < image->height; y++) {
4994 for (int x = 0; x < image->width; x++) {
4995 if (image->data[y * (image->pitch >> 2) + x].rgba.a > alphaThreshold) {
4996 if (x < xMin) {
4997 xMin = x;
4998 }
4999 if (x > xMax) {
5000 xMax = x;
5001 }
5002 if (y < yMin) {
5003 yMin = y;
5004 }
5005 if (y > yMax) {
5006 yMax = y;
5007 }
5008 }
5009 }
5010 }
5011
5012 // Check for empty blank image
5013 if ((xMin != 9999999) && (xMax != 9999999)) {
5014 return PNTR_CLITERAL(pntr_rectangle) {
5015 .x = xMin,
5016 .y = yMin,
5017 .width = xMax + 1 - xMin,
5018 .height = yMax + 1 - yMin
5019 };
5020 }
5021
5022 return PNTR_CLITERAL(pntr_rectangle) {0, 0, 0, 0};
5023}
5024
5036PNTR_API bool pntr_image_crop(pntr_image* image, int x, int y, int width, int height) {
5037 if (image == NULL) {
5038 return false;
5039 }
5040
5041 pntr_image* newImage = pntr_image_from_image(image, x, y, width, height);
5042 if (newImage == NULL) {
5043 return false;
5044 }
5045
5046 // Clear the data if it isn't owned by another image.
5047 if (!image->subimage) {
5048 PNTR_FREE(image->data);
5049 }
5050
5051 image->data = newImage->data;
5052 image->width = newImage->width;
5053 image->height = newImage->height;
5054 image->pitch = newImage->pitch;
5055 image->subimage = false;
5056 pntr_image_reset_clip(image);
5057
5058 PNTR_FREE(newImage);
5059
5060 return true;
5061}
5062
5072PNTR_API void pntr_image_alpha_crop(pntr_image* image, float threshold) {
5073 if (image == NULL) {
5074 return;
5075 }
5076
5077 pntr_rectangle crop = pntr_image_alpha_border(image, threshold);
5078
5079 if (crop.width > 0 && crop.height > 0) {
5080 pntr_image_crop(image, crop.x, crop.y, crop.width, crop.height);
5081 }
5082}
5083
5092PNTR_API pntr_color pntr_color_contrast(pntr_color color, float contrast) {
5093 if (contrast < -1.0f) {
5094 contrast = -1.0f;
5095 }
5096 else if (contrast > 1.0f) {
5097 contrast = 1.0f;
5098 }
5099
5100 contrast = (1.0f + contrast) * contrast;
5101
5102 float pR = (float)color.rgba.r / 255.0f - 0.5f;
5103 pR *= contrast;
5104 pR += 0.5f;
5105 pR *= 255;
5106 if (pR < 0) {
5107 pR = 0;
5108 }
5109 else if (pR > 255) {
5110 pR = 255;
5111 }
5112
5113 float pG = (float)color.rgba.g / 255.0f - 0.5f;
5114 pG *= contrast;
5115 pG += 0.5f;
5116 pG *= 255;
5117 if (pG < 0) {
5118 pG = 0;
5119 }
5120 else if (pG > 255) {
5121 pG = 255;
5122 }
5123
5124 float pB = (float)color.rgba.b / 255.0f - 0.5f;
5125 pB *= contrast;
5126 pB += 0.5f;
5127 pB *= 255;
5128 if (pB < 0) {
5129 pB = 0;
5130 }
5131 else if (pB > 255) {
5132 pB = 255;
5133 }
5134
5135 return PNTR_NEW_COLOR((unsigned char)pR, (unsigned char)pG, (unsigned char)pB, color.rgba.a);
5136}
5137
5146PNTR_API void pntr_image_color_contrast(pntr_image* image, float contrast) {
5147 if (image == NULL) {
5148 return;
5149 }
5150
5151 if (contrast < -1.0f) {
5152 contrast = -1.0f;
5153 }
5154 else if (contrast > 1.0f) {
5155 contrast = 1.0f;
5156 }
5157
5158 for (int y = image->clip.y; y < image->clip.y + image->clip.height; y++) {
5159 pntr_color* pixel = &PNTR_PIXEL(image, image->clip.x, y);
5160 for (int x = 0; x < image->clip.width; x++) {
5161 *pixel = pntr_color_contrast(*pixel, contrast);
5162 pixel++;
5163 }
5164 }
5165}
5166
5177PNTR_API void pntr_image_alpha_mask(pntr_image* image, pntr_image* alphaMask, int posX, int posY) {
5178 if (image == NULL || alphaMask == NULL) {
5179 return;
5180 }
5181
5182 pntr_rectangle srcRect = PNTR_CLITERAL(pntr_rectangle) { 0, 0, alphaMask->width, alphaMask->height };
5183 pntr_rectangle dstRect = PNTR_CLITERAL(pntr_rectangle) { posX, posY, alphaMask->width, alphaMask->height };
5184
5185 // Update the source coordinates based on the destination
5186 if (dstRect.x < 0) {
5187 srcRect.x -= dstRect.x;
5188 srcRect.width += dstRect.x;
5189 }
5190 if (dstRect.y < 0) {
5191 srcRect.y -= dstRect.y;
5192 srcRect.height += dstRect.y;
5193 }
5194
5195 if (!_pntr_rectangle_intersect(dstRect.x, dstRect.y,
5196 PNTR_MIN(dstRect.width, srcRect.width),
5197 PNTR_MIN(dstRect.height, srcRect.height),
5198 image->clip.x, image->clip.y,
5199 image->clip.width, image->clip.height, &dstRect)) {
5200 return;
5201 }
5202
5203 for (int y = 0; y < dstRect.height; y++) {
5204 pntr_color* pixel = &PNTR_PIXEL(image, dstRect.x, dstRect.y + y);
5205 for (int x = 0; x < dstRect.width; x++) {
5206 if (pixel->rgba.a > 0) {
5207 pixel->rgba.a = PNTR_PIXEL(alphaMask, x, y).rgba.a;
5208 }
5209 pixel++;
5210 }
5211 }
5212}
5213
5226PNTR_API bool pntr_image_resize_canvas(pntr_image* image, int newWidth, int newHeight, int offsetX, int offsetY, pntr_color fill) {
5227 if (image == NULL) {
5228 pntr_set_error(PNTR_ERROR_INVALID_ARGS);
5229 return false;
5230 }
5231
5232 pntr_image* newImage = pntr_gen_image_color(newWidth, newHeight, fill);
5233 if (newImage == NULL) {
5234 return false;
5235 }
5236
5237 pntr_draw_image(newImage, image, offsetX, offsetY);
5238
5239 // Clear the image if it's not a subimage
5240 if (!image->subimage) {
5241 PNTR_FREE(image->data);
5242 }
5243
5244 image->data = newImage->data;
5245 image->width = newImage->width;
5246 image->height = newImage->height;
5247 image->pitch = newImage->pitch;
5248
5249 // TODO: pntr_image_resize_canvas - Adust the new image clip with the original one.
5250 pntr_image_reset_clip(image);
5251 image->subimage = false;
5252
5253 PNTR_FREE(newImage);
5254 return true;
5255}
5256
5257PNTR_API void pntr_draw_image_flipped(pntr_image* dst, pntr_image* src, int posX, int posY, bool flipHorizontal, bool flipVertical, bool flipDiagonal) {
5258 if (dst == NULL || src == NULL) {
5259 return;
5260 }
5261
5262 pntr_draw_image_flipped_rec(dst, src,
5263 PNTR_CLITERAL(pntr_rectangle) { .x = 0, .y = 0, .width = src->width, .height = src->height },
5264 posX, posY,
5265 flipHorizontal,
5266 flipVertical,
5267 flipDiagonal
5268 );
5269}
5270
5271PNTR_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) {
5272 // If we are not flipping at all, use the simpler draw function.
5273 if (!flipHorizontal && !flipVertical && !flipDiagonal) {
5274 pntr_draw_image_tint_rec(dst, src, srcRec, posX, posY, PNTR_WHITE);
5275 return;
5276 }
5277
5278 if (dst == NULL || src == NULL) {
5279 return;
5280 }
5281
5282 if (!_pntr_rectangle_intersect(srcRec.x, srcRec.y, srcRec.width, srcRec.height, 0, 0, src->width, src->height, &srcRec)) {
5283 return;
5284 }
5285
5286 int dstX, dstY;
5287 for (int y = 0; y < srcRec.height; y++) {
5288 for (int x = 0; x < srcRec.width; x++) {
5289 // Determine the destination pixels based on flip parameters
5290 if (flipDiagonal) {
5291 dstX = flipHorizontal ? srcRec.height - y - 1 : y;
5292 dstY = flipVertical ? srcRec.width - x - 1 : x;
5293 }
5294 else {
5295 dstY = flipVertical ? srcRec.height - y - 1 : y;
5296 dstX = flipHorizontal ? srcRec.width - x - 1 : x;
5297 }
5298
5299 pntr_draw_point(dst, posX + dstX, posY + dstY,
5300 pntr_image_get_color(src, x, y)
5301 );
5302 }
5303 }
5304}
5305
5319PNTR_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) {
5320 if (dst == NULL || src == NULL) {
5321 return;
5322 }
5323
5324 pntr_draw_image_scaled_rec(dst, src,
5325 PNTR_CLITERAL(pntr_rectangle) { .x = 0, .y = 0, .width = src->width, .height = src->height },
5326 posX, posY,
5327 scaleX, scaleY,
5328 offsetX, offsetY,
5329 filter);
5330}
5331
5332PNTR_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) {
5333 if (dst == NULL || src == NULL || scaleX <= 0.0f || scaleY <= 0.0f) {
5334 return;
5335 }
5336
5337 if (!_pntr_rectangle_intersect(srcRect.x, srcRect.y, srcRect.width, srcRect.height, 0, 0, src->width, src->height, &srcRect)) {
5338 return;
5339 }
5340
5341 int newWidth = (int)((float)srcRect.width * scaleX);
5342 int newHeight = (int)((float)srcRect.height * scaleY);
5343 int offsetXRatio = (int)(offsetX / (float)srcRect.width * (float)newWidth);
5344 int offsetYRatio = (int)(offsetY / (float)srcRect.height * (float)newHeight);
5345
5346 switch (filter) {
5347 case PNTR_FILTER_BILINEAR: {
5348 float xRatio = (float)srcRect.width / (float)newWidth;
5349 float yRatio = (float)srcRect.height / (float)newHeight;
5350
5351 for (int y = 0; y < newHeight; y++) {
5352 int yPosition = posY + y - offsetYRatio;
5353 if (yPosition < dst->clip.y || yPosition >= dst->clip.y + dst->clip.height) {
5354 continue;
5355 }
5356 float srcY = (float)y * yRatio;
5357 int srcYPixel = srcRect.y + (int)srcY;
5358 int srcYPixelPlusOne = y == newHeight - 1 ? (int)srcYPixel : (int)srcYPixel + 1;
5359 for (int x = 0; x < newWidth; x++) {
5360 int xPosition = posX + x - offsetXRatio;
5361 if (xPosition < dst->clip.x || xPosition >= dst->clip.x + dst->clip.width) {
5362 continue;
5363 }
5364 float srcX = (float)x * xRatio;
5365 int srcXPixel = srcRect.y + (int)srcX;
5366 int srcXPixelPlusOne = x == newWidth - 1 ? (int)srcXPixel : (int)srcXPixel + 1;
5368 src->data[srcYPixel * (src->pitch >> 2) + srcXPixel],
5369 src->data[srcYPixelPlusOne * (src->pitch >> 2) + srcXPixel],
5370 src->data[srcYPixel * (src->pitch >> 2) + srcXPixelPlusOne],
5371 src->data[srcYPixelPlusOne * (src->pitch >> 2) + srcXPixelPlusOne],
5372 srcX - PNTR_FLOORF(srcX),
5373 srcY - PNTR_FLOORF(srcY)
5374 ));
5375 }
5376 }
5377 }
5378 break;
5379 case PNTR_FILTER_NEARESTNEIGHBOR:
5380 default: {
5381 int xRatio = (srcRect.width << 16) / newWidth + 1;
5382 int yRatio = (srcRect.height << 16) / newHeight + 1;
5383
5384 for (int y = 0; y < newHeight; y++) {
5385 int yPosition = posY + y - offsetYRatio;
5386 if (yPosition < dst->clip.y || yPosition >= dst->clip.y + dst->clip.height) {
5387 continue;
5388 }
5389 int y2 = (y * yRatio) >> 16;
5390 for (int x = 0; x < newWidth; x++) {
5391 int xPosition = posX + x - offsetXRatio;
5392 if (xPosition < dst->clip.x || xPosition >= dst->clip.x + dst->clip.width) {
5393 continue;
5394 }
5395 int x2 = (x * xRatio) >> 16;
5397 xPosition,
5398 yPosition,
5399 PNTR_PIXEL(src, srcRect.x + x2, srcRect.y + y2)
5400 );
5401 }
5402 }
5403 }
5404 break;
5405 }
5406}
5407
5417float _pntr_normalize_degrees(float degrees) {
5418 if (degrees < 0) {
5419 return 360.0f - PNTR_FMODF(-degrees, 360.0f);
5420 }
5421
5422 return PNTR_FMODF(degrees, 360.0f);
5423}
5424
5436PNTR_API pntr_image* pntr_image_rotate(pntr_image* image, float degrees, pntr_filter filter) {
5437 if (image == NULL) {
5438 return NULL;
5439 }
5440
5441 degrees = _pntr_normalize_degrees(degrees);
5442
5443 if (degrees == 0.0f) {
5444 return pntr_image_copy(image);
5445 }
5446
5447 if (degrees == 90.0f || degrees == 180.0f || degrees == 270.0f) {
5448 pntr_image* output;
5449 if (degrees == 180.0f) {
5450 output = pntr_gen_image_color(image->width, image->height, PNTR_BLANK);
5451 }
5452 else {
5453 output = pntr_gen_image_color(image->height, image->width, PNTR_BLANK);
5454 }
5455 if (output == NULL) {
5456 return NULL;
5457 }
5458
5459 pntr_draw_image_rotated(output, image, 0, 0, degrees, 0.0f, 0.0f, filter);
5460
5461 return output;
5462 }
5463
5464 float radians = degrees * PNTR_DEG2RAD;
5465 float cosTheta = PNTR_COSF(radians);
5466 float sinTheta = PNTR_SINF(radians);
5467
5468 int newWidth = (int)PNTR_CEILF(PNTR_FABSF((float)image->width * cosTheta) + PNTR_FABSF((float)image->height * sinTheta));
5469 int newHeight = (int)PNTR_CEILF(PNTR_FABSF((float)image->width * sinTheta) + PNTR_FABSF((float)image->height * cosTheta));
5470
5471 pntr_image* rotatedImage = pntr_gen_image_color(newWidth, newHeight, PNTR_BLANK);
5472 if (rotatedImage == NULL) {
5473 return NULL;
5474 }
5475
5476 pntr_draw_image_rotated(rotatedImage, image, 0, 0, degrees, 0.0f, 0.0f, filter);
5477
5478 return rotatedImage;
5479}
5480
5498PNTR_API pntr_color pntr_color_bilinear_interpolate(pntr_color color00, pntr_color color01, pntr_color color10, pntr_color color11, float coordinateX, float coordinateY) {
5499 return PNTR_NEW_COLOR(
5500 (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),
5501 (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),
5502 (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),
5503 (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)
5504 );
5505}
5506
5522PNTR_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) {
5523 if (dst == NULL || src == NULL) {
5524 return;
5525 }
5526
5528 PNTR_CLITERAL(pntr_rectangle) { .x = 0, .y = 0, .width = src->width, .height = src->height },
5529 posX, posY,
5530 degrees,
5531 offsetX, offsetY,
5532 filter);
5533}
5534
5550PNTR_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) {
5551 if (dst == NULL || src == NULL) {
5552 return;
5553 }
5554
5555 degrees = _pntr_normalize_degrees(degrees);
5556
5557 // Draw the image normally if not rotated.
5558 if (degrees == 0.0f) {
5559 pntr_draw_image_rec(dst, src, srcRect, posX - (int)offsetX, posY - (int)offsetY);
5560 return;
5561 }
5562
5563 // Clean up the source rectangle.
5564 if (srcRect.x < 0) {
5565 srcRect.x = 0;
5566 }
5567 if (srcRect.y < 0) {
5568 srcRect.y = 0;
5569 }
5570 if (srcRect.width <= 0 || srcRect.width > src->width) {
5571 srcRect.width = src->width - srcRect.x;
5572 }
5573 if (srcRect.height <= 0 || srcRect.height > src->height) {
5574 srcRect.height = src->height - srcRect.y;
5575 }
5576
5577 // Simple rotation by 90 degrees can be fast.
5578 if (degrees == 90.0f || degrees == 180.0f || degrees == 270.0f) {
5579 // Build the destination coordinates of the image.
5580 pntr_rectangle dstRect = PNTR_CLITERAL(pntr_rectangle) { .x = posX, .y = posY, .width = srcRect.width, .height = srcRect.height };
5581 if (degrees == 90.0f || degrees == 270.0f) {
5582 dstRect.width = srcRect.height;
5583 dstRect.height = srcRect.width;
5584 dstRect.x -= (int)offsetY;
5585 dstRect.y -= (int)offsetX;
5586 }
5587 else {
5588 dstRect.x -= (int)offsetX;
5589 dstRect.y -= (int)offsetY;
5590 }
5591
5592 // Exit if it's not even on the screen.
5593 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) {
5594 return;
5595 }
5596
5597 // Draw the source portion on the destination.
5598 for (int y = 0; y < srcRect.height; y++) {
5599 for (int x = 0; x < srcRect.width; x++) {
5600 if (degrees == 90.0f) {
5601 pntr_draw_point(dst,
5602 dstRect.x + y,
5603 dstRect.y + srcRect.width - x,
5604 PNTR_PIXEL(src, srcRect.x + x, srcRect.y + y)
5605 );
5606 } else if (degrees == 180.0f) {
5607 pntr_draw_point(dst,
5608 dstRect.x + srcRect.width - x,
5609 dstRect.y + srcRect.height - y,
5610 PNTR_PIXEL(src, srcRect.x + x, srcRect.y + y)
5611 );
5612 }
5613 else {
5614 pntr_draw_point(dst,
5615 dstRect.x + srcRect.height - y,
5616 dstRect.y + x,
5617 PNTR_PIXEL(src, srcRect.x + x, srcRect.y + y)
5618 );
5619 }
5620 }
5621 }
5622
5623 return;
5624 }
5625
5626 float radians = degrees * PNTR_DEG2RAD;
5627 float cosTheta = PNTR_COSF(radians);
5628 float sinTheta = PNTR_SINF(radians);
5629
5630 int newWidth = (int)PNTR_CEILF(PNTR_FABSF((float)srcRect.width * cosTheta) + PNTR_FABSF((float)srcRect.height * sinTheta));
5631 int newHeight = (int)PNTR_CEILF(PNTR_FABSF((float)srcRect.width * sinTheta) + PNTR_FABSF((float)srcRect.height * cosTheta));
5632
5633 int offsetXRatio = (int)(offsetX / (float)srcRect.width * (float)newWidth);
5634 int offsetYRatio = (int)(offsetY / (float)srcRect.height * (float)newHeight);
5635
5636 // Make sure we're actually drawing on the screen.
5637 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) {
5638 return;
5639 }
5640
5641 float centerX = (float)srcRect.width / 2.0f;
5642 float centerY = (float)srcRect.height / 2.0f;
5643 int srcXint, srcYint;
5644 float srcX, srcY;
5645
5646 for (int y = 0; y < newHeight; y++) {
5647 // Only draw onto the screen.
5648 int destY = posY + y - offsetYRatio;
5649 if (destY < dst->clip.y || destY >= dst->clip.y + dst->clip.height) {
5650 continue;
5651 }
5652
5653 for (int x = 0; x < newWidth; x++) {
5654 // Make sure we're actually drawing onto the screen.
5655 int destX = posX + x - offsetXRatio;
5656 if (destX < dst->clip.x || destX >= dst->clip.x + dst->clip.width ) {
5657 continue;
5658 }
5659
5660 srcX = (float)(x - newWidth / 2) * cosTheta - (float)(y - newHeight / 2) * sinTheta + centerX;
5661 srcY = (float)(x - newWidth / 2) * sinTheta + (float)(y - newHeight / 2) * cosTheta + centerY;
5662
5663 // Only draw from the source rectangle.
5664 if (srcX < 0 || srcX >= srcRect.width || srcY < 0 || srcY >= srcRect.height) {
5665 continue;
5666 }
5667
5668 srcXint = (int)srcX + srcRect.x;
5669 srcYint = (int)srcY + srcRect.y;
5670
5671 if (filter == PNTR_FILTER_NEARESTNEIGHBOR) {
5673 destX,
5674 destY,
5675 PNTR_PIXEL(src, srcXint, srcYint)
5676 );
5677 }
5678 else {
5679 // For bilinear, don't overscan.
5680 if (srcX >= srcRect.width - 1 || srcY >= srcRect.height - 1) {
5681 continue;
5682 }
5683
5685 destX,
5686 destY,
5688 PNTR_PIXEL(src, srcXint, srcYint),
5689 PNTR_PIXEL(src, srcXint, srcYint + 1),
5690 PNTR_PIXEL(src, srcXint + 1, srcYint),
5691 PNTR_PIXEL(src, srcXint + 1, srcYint + 1),
5692 srcX - PNTR_FLOORF(srcX),
5693 srcY - PNTR_FLOORF(srcY)
5694 )
5695 );
5696 }
5697 }
5698 }
5699}
5700
5713PNTR_API pntr_image* pntr_gen_image_gradient(int width, int height, pntr_color topLeft, pntr_color topRight, pntr_color bottomLeft, pntr_color bottomRight) {
5714 pntr_image* image = pntr_gen_image_color(width, height, PNTR_BLANK);
5715 if (image == NULL) {
5716 return NULL;
5717 }
5718
5719 pntr_draw_rectangle_gradient(image, 0, 0, width, height, topLeft, topRight, bottomLeft, bottomRight);
5720
5721 return image;
5722}
5723
5731PNTR_API pntr_rectangle pntr_image_get_clip(pntr_image* image) {
5732 if (image == NULL) {
5733 return PNTR_CLITERAL(pntr_rectangle) {
5734 .x = 0,
5735 .y = 0,
5736 .width = 0,
5737 .height = 0
5738 };
5739 }
5740
5741 return image->clip;
5742}
5743
5756PNTR_API void pntr_image_set_clip(pntr_image* image, int x, int y, int width, int height) {
5757 if (image == NULL) {
5758 return;
5759 }
5760
5761 pntr_rectangle clip;
5762 if (_pntr_rectangle_intersect(x, y, width, height, 0, 0, image->width, image->height, &clip)) {
5763 image->clip = clip;
5764 }
5765}
5766
5776PNTR_API void pntr_image_set_clip_rec(pntr_image* image, pntr_rectangle clip) {
5777 pntr_image_set_clip(image, clip.x, clip.y, clip.width, clip.height);
5778}
5779
5787PNTR_API void pntr_image_reset_clip(pntr_image* image) {
5788 if (image == NULL) {
5789 return;
5790 }
5791
5792 image->clip.x = 0;
5793 image->clip.y = 0;
5794 image->clip.width = image->width;
5795 image->clip.height = image->height;
5796}
5797
5807PNTR_API void* pntr_load_memory(size_t size) {
5808 return PNTR_MALLOC(size);
5809}
5810
5818PNTR_API void pntr_unload_memory(void* pointer) {
5819 if (pointer == NULL) {
5820 return;
5821 }
5822
5823 PNTR_FREE(pointer);
5824}
5825
5837PNTR_API void* pntr_memory_copy(void* destination, void* source, size_t size) {
5838 return PNTR_MEMCPY(destination, source, size);
5839}
5840
5845#ifdef __cplusplus
5846}
5847#endif
5848
5849#endif // PNTR_IMPLEMENTATION_ONCE
5850#endif // PNTR_IMPLEMENTATION
#define PNTR_MAX(a, b)
Definition pntr.h:1204
#define PNTR_SINF(value)
Definition pntr.h:1056
#define PNTR_FLOORF(x)
Definition pntr.h:1143
#define PNTR_FABSF(x)
Definition pntr.h:1130
#define PNTR_COSF(value)
Definition pntr.h:1088
#define PNTR_MIN(a, b)
Definition pntr.h:1216
#define PNTR_FMODF(dividend, divisor)
Definition pntr.h:1156
#define PNTR_CEILF(x)
Definition pntr.h:1117
#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
PNTR_API void pntr_draw_ellipse_thick(pntr_image *dst, int centerX, int centerY, int radiusX, int radiusY, int thickness, pntr_color color)
Definition pntr.h:2645
PNTR_API pntr_color pntr_color_invert(pntr_color color)
Definition pntr.h:4610
PNTR_API pntr_image * pntr_gen_image_color(int width, int height, pntr_color color)
Definition pntr.h:1500
PNTR_API pntr_font * pntr_load_font_ttf_from_memory(const unsigned char *fileData, unsigned int dataSize, int fontSize)
Definition pntr.h:4487
PNTR_API pntr_image * pntr_image_from_pixelformat(const void *imageData, int width, int height, pntr_pixelformat pixelFormat)
Definition pntr.h:3350
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:2697
PNTR_API void pntr_image_set_clip(pntr_image *image, int x, int y, int width, int height)
Definition pntr.h:5756
PNTR_API void pntr_image_set_clip_rec(pntr_image *image, pntr_rectangle clip)
Definition pntr.h:5776
PNTR_API pntr_color pntr_get_color(unsigned int hexValue)
Definition pntr.h:1778
PNTR_API void pntr_draw_circle(pntr_image *dst, int centerX, int centerY, int radius, pntr_color color)
Definition pntr.h:2391
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:5550
PNTR_API void pntr_image_color_invert(pntr_image *image)
Definition pntr.h:4626
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:2896
PNTR_API void pntr_unload_image(pntr_image *image)
Definition pntr.h:1697
PNTR_API void pntr_draw_line_horizontal(pntr_image *dst, int posX, int posY, int width, pntr_color color)
Definition pntr.h:2149
PNTR_API void pntr_blend_color(pntr_color *dst, pntr_color src)
Definition pntr.h:1543
PNTR_API void pntr_draw_line_thick(pntr_image *dst, int startPosX, int startPosY, int endPosX, int endPosY, int thickness, pntr_color color)
Definition pntr.h:1959
PNTR_API pntr_image * pntr_image_from_image(pntr_image *image, int x, int y, int width, int height)
Definition pntr.h:1625
PNTR_API void pntr_image_alpha_crop(pntr_image *image, float threshold)
Definition pntr.h:5072
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:5319
PNTR_API pntr_rectangle pntr_image_get_clip(pntr_image *image)
Definition pntr.h:5731
PNTR_API pntr_font * pntr_load_font_bmf_from_memory(const unsigned char *fileData, unsigned int dataSize, const char *characters)
Definition pntr.h:3760
PNTR_API void pntr_image_color_contrast(pntr_image *image, float contrast)
Definition pntr.h:5146
PNTR_API pntr_image * pntr_load_image(const char *fileName)
Definition pntr.h:3176
PNTR_API pntr_rectangle pntr_image_alpha_border(pntr_image *image, float threshold)
Definition pntr.h:4982
PNTR_API void pntr_draw_point(pntr_image *dst, int x, int y, pntr_color color)
Definition pntr.h:1829
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:2767
PNTR_API pntr_image * pntr_new_image(int width, int height)
Definition pntr.h:1467
PNTR_API pntr_color pntr_color_alpha_blend(pntr_color dst, pntr_color src)
Definition pntr.h:3240
PNTR_API unsigned char * pntr_save_image_to_memory(pntr_image *image, pntr_image_type type, unsigned int *dataSize)
Definition pntr.h:4930
PNTR_API int pntr_measure_text(pntr_font *font, const char *text)
Definition pntr.h:4274
PNTR_API void pntr_draw_image_rec(pntr_image *dst, pntr_image *src, pntr_rectangle srcRect, int posX, int posY)
Definition pntr.h:3256
PNTR_API unsigned char * pntr_load_file(const char *fileName, unsigned int *bytesRead)
Definition pntr.h:4692
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:5713
PNTR_API int pntr_get_pixel_data_size(int width, int height, pntr_pixelformat pixelFormat)
Definition pntr.h:4845
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:5498
PNTR_API bool pntr_image_crop(pntr_image *image, int x, int y, int width, int height)
Definition pntr.h:5036
PNTR_API void pntr_put_horizontal_line_unsafe(pntr_image *dst, int posX, int posY, int width, pntr_color color)
Definition pntr.h:1713
PNTR_API void pntr_image_color_fade(pntr_image *image, float factor)
Definition pntr.h:3625
PNTR_API void pntr_draw_point_unsafe(pntr_image *dst, int x, int y, pntr_color color)
Definition pntr.h:1822
PNTR_API void pntr_image_color_tint(pntr_image *image, pntr_color tint)
Definition pntr.h:3718
PNTR_API pntr_color pntr_image_get_color(pntr_image *image, int x, int y)
Definition pntr.h:3072
PNTR_API pntr_image * pntr_load_image_from_memory(pntr_image_type type, const unsigned char *fileData, unsigned int dataSize)
Definition pntr.h:3159
PNTR_API const char * pntr_load_file_text(const char *fileName)
Definition pntr.h:4750
PNTR_API void pntr_draw_image(pntr_image *dst, pntr_image *src, int posX, int posY)
Definition pntr.h:3221
PNTR_API void pntr_draw_line(pntr_image *dst, int startPosX, int startPosY, int endPosX, int endPosY, pntr_color color)
Definition pntr.h:1863
PNTR_API bool pntr_save_image(pntr_image *image, const char *fileName)
Definition pntr.h:4949
PNTR_API bool pntr_save_file(const char *fileName, const void *data, unsigned int bytesToWrite)
Definition pntr.h:4803
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:2729
PNTR_API void pntr_unload_memory(void *pointer)
Definition pntr.h:5818
PNTR_API void pntr_draw_rectangle_rec(pntr_image *dst, pntr_rectangle rec, pntr_color color)
Definition pntr.h:2250
PNTR_API void pntr_draw_ellipse_fill(pntr_image *dst, int centerX, int centerY, int radiusX, int radiusY, pntr_color color)
Definition pntr.h:2597
PNTR_API void pntr_draw_rectangle_fill(pntr_image *dst, int posX, int posY, int width, int height, pntr_color color)
Definition pntr.h:2300
PNTR_API pntr_font * pntr_load_font_ttf(const char *fileName, int fontSize)
Definition pntr.h:4451
PNTR_API void pntr_draw_rectangle_fill_rec(pntr_image *dst, pntr_rectangle rect, pntr_color color)
Definition pntr.h:2313
PNTR_API void pntr_draw_text_len(pntr_image *dst, pntr_font *font, const char *text, int textLength, int posX, int posY, pntr_color tint)
Definition pntr.h:4070
PNTR_API pntr_color pntr_color_fade(pntr_color color, float factor)
Definition pntr.h:3599
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:4152
PNTR_API void * pntr_set_error(pntr_error error)
Definition pntr.h:1445
PNTR_API void pntr_draw_circle_thick(pntr_image *dst, int centerX, int centerY, int radius, int thickness, pntr_color color)
Definition pntr.h:2493
PNTR_API pntr_color pntr_new_color(unsigned char red, unsigned char green, unsigned char blue, unsigned char alpha)
Definition pntr.h:1767
PNTR_API pntr_image * pntr_image_resize(pntr_image *image, int newWidth, int newHeight, pntr_filter filter)
Definition pntr.h:3421
PNTR_API void pntr_set_pixel_color(void *dstPtr, pntr_pixelformat dstPixelFormat, pntr_color color)
Definition pntr.h:3655
PNTR_API void pntr_unload_file(unsigned char *fileData)
Definition pntr.h:4970
PNTR_API void pntr_draw_triangle_thick(pntr_image *dst, int x1, int y1, int x2, int y2, int x3, int y3, int thickness, pntr_color color)
Definition pntr.h:2748
PNTR_API pntr_image * pntr_gen_image_text(pntr_font *font, const char *text, pntr_color tint, pntr_color backgroundColor)
Definition pntr.h:4349
#define PNTR_PIXEL(image, x, y)
Definition pntr.h:1372
PNTR_API void pntr_draw_text(pntr_image *dst, pntr_font *font, const char *text, int posX, int posY, pntr_color tint)
Definition pntr.h:4135
PNTR_API pntr_font * pntr_load_font_tty(const char *fileName, int glyphWidth, int glyphHeight, const char *characters)
Definition pntr.h:3897
PNTR_API pntr_color pntr_color_brightness(pntr_color color, float factor)
Definition pntr.h:3566
PNTR_API void * pntr_memory_copy(void *destination, void *source, size_t size)
Definition pntr.h:5837
PNTR_API void pntr_image_color_brightness(pntr_image *image, float factor)
Definition pntr.h:4648
PNTR_API pntr_font * pntr_load_font_bmf(const char *fileName, const char *characters)
Definition pntr.h:3742
PNTR_API void pntr_unload_file_text(const char *text)
Definition pntr.h:4778
PNTR_API pntr_font * pntr_font_copy(pntr_font *font)
Definition pntr.h:3992
#define PNTR_API
Definition pntr.h:275
PNTR_API pntr_image_type pntr_get_file_image_type(const char *filePath)
Definition pntr.h:3092
PNTR_API pntr_color pntr_color_tint(pntr_color color, pntr_color tint)
Definition pntr.h:3545
PNTR_API void pntr_image_alpha_mask(pntr_image *image, pntr_image *alphaMask, int posX, int posY)
Definition pntr.h:5177
PNTR_API pntr_font * pntr_load_font_bmf_from_image(pntr_image *image, const char *characters)
Definition pntr.h:3834
PNTR_API void * pntr_image_to_pixelformat(pntr_image *image, unsigned int *dataSize, pntr_pixelformat pixelFormat)
Definition pntr.h:4878
PNTR_API pntr_font * pntr_load_font_default(void)
Definition pntr.h:4382
#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:3519
PNTR_API void pntr_image_reset_clip(pntr_image *image)
Definition pntr.h:5787
PNTR_API pntr_color pntr_get_pixel_color(void *srcPtr, pntr_pixelformat srcPixelFormat)
Definition pntr.h:3686
PNTR_API pntr_image * pntr_image_scale(pntr_image *image, float scaleX, float scaleY, pntr_filter filter)
Definition pntr.h:3401
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:3272
PNTR_API void pntr_draw_circle_fill(pntr_image *dst, int centerX, int centerY, int radius, pntr_color color)
Definition pntr.h:2448
PNTR_API void pntr_draw_ellipse(pntr_image *dst, int centerX, int centerY, int radiusX, int radiusY, pntr_color color)
Definition pntr.h:2547
#define PNTR_NEW_COLOR(red, green, blue, alpha)
Definition pntr.h:1388
PNTR_API void pntr_draw_rectangle(pntr_image *dst, int posX, int posY, int width, int height, pntr_color color)
Definition pntr.h:2267
PNTR_API pntr_color pntr_color_contrast(pntr_color color, float contrast)
Definition pntr.h:5092
PNTR_API pntr_image * pntr_image_subimage(pntr_image *image, int x, int y, int width, int height)
Definition pntr.h:1665
PNTR_API pntr_font * pntr_font_scale(pntr_font *font, float scaleX, float scaleY, pntr_filter filter)
Definition pntr.h:4026
PNTR_API void pntr_clear_background(pntr_image *image, pntr_color color)
Definition pntr.h:1728
PNTR_API void * pntr_load_memory(size_t size)
Definition pntr.h:5807
PNTR_API void pntr_image_flip(pntr_image *image, bool horizontal, bool vertical)
Definition pntr.h:3485
PNTR_API void pntr_draw_triangle_thick_vec(pntr_image *dst, pntr_vector point1, pntr_vector point2, pntr_vector point3, int thickness, pntr_color color)
Definition pntr.h:2711
PNTR_API pntr_image * pntr_image_copy(pntr_image *image)
Definition pntr.h:1514
PNTR_API pntr_image * pntr_image_rotate(pntr_image *image, float degrees, pntr_filter filter)
Definition pntr.h:5436
PNTR_API void pntr_draw_image_tint(pntr_image *dst, pntr_image *src, int posX, int posY, pntr_color tint)
Definition pntr.h:3203
PNTR_API bool pntr_image_resize_canvas(pntr_image *image, int newWidth, int newHeight, int offsetX, int offsetY, pntr_color fill)
Definition pntr.h:5226
PNTR_API void pntr_draw_line_vertical(pntr_image *dst, int posX, int posY, int height, pntr_color color)
Definition pntr.h:2201
PNTR_API pntr_vector pntr_measure_text_ex(pntr_font *font, const char *text, int textLength)
Definition pntr.h:4287
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:5522
PNTR_API void pntr_unload_font(pntr_font *font)
Definition pntr.h:3973