Wise&mystical  1.0
Project about Europe
Loading...
Searching...
No Matches
rtextures.c
Go to the documentation of this file.
1
64#include "raylib.h" // Declares module functions
65
66// Check if config flags have been externally provided on compilation line
67#if !defined(EXTERNAL_CONFIG_FLAGS)
68 #include "config.h" // Defines module configuration flags
69#endif
70
71#if defined(SUPPORT_MODULE_RTEXTURES)
72
73#include "utils.h" // Required for: TRACELOG() and fopen() Android mapping
74#include "rlgl.h" // OpenGL abstraction layer to OpenGL 1.1, 3.3 or ES2
75
76#include <stdlib.h> // Required for: malloc(), free()
77#include <string.h> // Required for: strlen() [Used in ImageTextEx()], strcmp() [Used in LoadImageFromMemory()]
78#include <math.h> // Required for: fabsf()
79#include <stdio.h> // Required for: sprintf() [Used in ExportImageAsCode()]
80
81// Support only desired texture formats on stb_image
82#if !defined(SUPPORT_FILEFORMAT_BMP)
83 #define STBI_NO_BMP
84#endif
85#if !defined(SUPPORT_FILEFORMAT_PNG)
86 #define STBI_NO_PNG
87#endif
88#if !defined(SUPPORT_FILEFORMAT_TGA)
89 #define STBI_NO_TGA
90#endif
91#if !defined(SUPPORT_FILEFORMAT_JPG)
92 #define STBI_NO_JPEG // Image format .jpg and .jpeg
93#endif
94#if !defined(SUPPORT_FILEFORMAT_PSD)
95 #define STBI_NO_PSD
96#endif
97#if !defined(SUPPORT_FILEFORMAT_GIF)
98 #define STBI_NO_GIF
99#endif
100#if !defined(SUPPORT_FILEFORMAT_PIC)
101 #define STBI_NO_PIC
102#endif
103#if !defined(SUPPORT_FILEFORMAT_HDR)
104 #define STBI_NO_HDR
105#endif
106
107// Image fileformats not supported by default
108#define STBI_NO_PIC
109#define STBI_NO_PNM // Image format .ppm and .pgm
110
111#if defined(__TINYC__)
112 #define STBI_NO_SIMD
113#endif
114
115#if (defined(SUPPORT_FILEFORMAT_BMP) || \
116 defined(SUPPORT_FILEFORMAT_PNG) || \
117 defined(SUPPORT_FILEFORMAT_TGA) || \
118 defined(SUPPORT_FILEFORMAT_JPG) || \
119 defined(SUPPORT_FILEFORMAT_PSD) || \
120 defined(SUPPORT_FILEFORMAT_GIF) || \
121 defined(SUPPORT_FILEFORMAT_PIC) || \
122 defined(SUPPORT_FILEFORMAT_HDR))
123
124 #define STBI_MALLOC RL_MALLOC
125 #define STBI_FREE RL_FREE
126 #define STBI_REALLOC RL_REALLOC
127
128 #define STB_IMAGE_IMPLEMENTATION
129 #include "external/stb_image.h" // Required for: stbi_load_from_file()
130 // NOTE: Used to read image data (multiple formats support)
131#endif
132
133#if defined(SUPPORT_FILEFORMAT_QOI)
134 #define QOI_MALLOC RL_MALLOC
135 #define QOI_FREE RL_FREE
136
137 #define QOI_IMPLEMENTATION
138 #include "external/qoi.h"
139#endif
140
141#if defined(SUPPORT_IMAGE_EXPORT)
142 #define STBIW_MALLOC RL_MALLOC
143 #define STBIW_FREE RL_FREE
144 #define STBIW_REALLOC RL_REALLOC
145
146 #define STB_IMAGE_WRITE_IMPLEMENTATION
147 #include "external/stb_image_write.h" // Required for: stbi_write_*()
148#endif
149
150#if defined(SUPPORT_IMAGE_MANIPULATION)
151 #define STBIR_MALLOC(size,c) ((void)(c), RL_MALLOC(size))
152 #define STBIR_FREE(ptr,c) ((void)(c), RL_FREE(ptr))
153
154 #define STB_IMAGE_RESIZE_IMPLEMENTATION
155 #include "external/stb_image_resize.h" // Required for: stbir_resize_uint8() [ImageResize()]
156#endif
157
158//----------------------------------------------------------------------------------
159// Defines and Macros
160//----------------------------------------------------------------------------------
161#ifndef PIXELFORMAT_UNCOMPRESSED_R5G5B5A1_ALPHA_THRESHOLD
162 #define PIXELFORMAT_UNCOMPRESSED_R5G5B5A1_ALPHA_THRESHOLD 50 // Threshold over 255 to set alpha as 0
163#endif
164
165//----------------------------------------------------------------------------------
166// Types and Structures Definition
167//----------------------------------------------------------------------------------
168// ...
169
170//----------------------------------------------------------------------------------
171// Global Variables Definition
172//----------------------------------------------------------------------------------
173// It's lonely here...
174
175//----------------------------------------------------------------------------------
176// Other Modules Functions Declaration (required by text)
177//----------------------------------------------------------------------------------
178// ...
179
180//----------------------------------------------------------------------------------
181// Module specific Functions Declaration
182//----------------------------------------------------------------------------------
183#if defined(SUPPORT_FILEFORMAT_DDS)
184static Image LoadDDS(const unsigned char *fileData, unsigned int fileSize); // Load DDS file data
185#endif
186#if defined(SUPPORT_FILEFORMAT_PKM)
187static Image LoadPKM(const unsigned char *fileData, unsigned int fileSize); // Load PKM file data
188#endif
189#if defined(SUPPORT_FILEFORMAT_KTX)
190static Image LoadKTX(const unsigned char *fileData, unsigned int fileSize); // Load KTX file data
191static int SaveKTX(Image image, const char *fileName); // Save image data as KTX file
192#endif
193#if defined(SUPPORT_FILEFORMAT_PVR)
194static Image LoadPVR(const unsigned char *fileData, unsigned int fileSize); // Load PVR file data
195#endif
196#if defined(SUPPORT_FILEFORMAT_ASTC)
197static Image LoadASTC(const unsigned char *fileData, unsigned int fileSize); // Load ASTC file data
198#endif
199
200static Vector4 *LoadImageDataNormalized(Image image); // Load pixel data from image as Vector4 array (float normalized)
201
202//----------------------------------------------------------------------------------
203// Module Functions Definition
204//----------------------------------------------------------------------------------
205
206// Load image from file into CPU memory (RAM)
207Image LoadImage(const char *fileName)
208{
209 Image image = { 0 };
210
211#if defined(SUPPORT_FILEFORMAT_PNG) || \
212 defined(SUPPORT_FILEFORMAT_BMP) || \
213 defined(SUPPORT_FILEFORMAT_TGA) || \
214 defined(SUPPORT_FILEFORMAT_JPG) || \
215 defined(SUPPORT_FILEFORMAT_GIF) || \
216 defined(SUPPORT_FILEFORMAT_PIC) || \
217 defined(SUPPORT_FILEFORMAT_HDR) || \
218 defined(SUPPORT_FILEFORMAT_PSD)
219
220 #define STBI_REQUIRED
221#endif
222
223 // Loading file to memory
224 unsigned int fileSize = 0;
225 unsigned char *fileData = LoadFileData(fileName, &fileSize);
226
227 // Loading image from memory data
228 if (fileData != NULL) image = LoadImageFromMemory(GetFileExtension(fileName), fileData, fileSize);
229
230 RL_FREE(fileData);
231
232 return image;
233}
234
235// Load an image from RAW file data
236Image LoadImageRaw(const char *fileName, int width, int height, int format, int headerSize)
237{
238 Image image = { 0 };
239
240 unsigned int dataSize = 0;
241 unsigned char *fileData = LoadFileData(fileName, &dataSize);
242
243 if (fileData != NULL)
244 {
245 unsigned char *dataPtr = fileData;
246 unsigned int size = GetPixelDataSize(width, height, format);
247
248 if (headerSize > 0) dataPtr += headerSize;
249
250 image.data = RL_MALLOC(size); // Allocate required memory in bytes
251 memcpy(image.data, dataPtr, size); // Copy required data to image
252 image.width = width;
253 image.height = height;
254 image.mipmaps = 1;
255 image.format = format;
256
257 RL_FREE(fileData);
258 }
259
260 return image;
261}
262
263// Load animated image data
264// - Image.data buffer includes all frames: [image#0][image#1][image#2][...]
265// - Number of frames is returned through 'frames' parameter
266// - All frames are returned in RGBA format
267// - Frames delay data is discarded
268Image LoadImageAnim(const char *fileName, int *frames)
269{
270 Image image = { 0 };
271 int frameCount = 1;
272
273#if defined(SUPPORT_FILEFORMAT_GIF)
274 if (IsFileExtension(fileName, ".gif"))
275 {
276 unsigned int dataSize = 0;
277 unsigned char *fileData = LoadFileData(fileName, &dataSize);
278
279 if (fileData != NULL)
280 {
281 int comp = 0;
282 int *delays = NULL;
283 image.data = stbi_load_gif_from_memory(fileData, dataSize, &delays, &image.width, &image.height, &frameCount, &comp, 4);
284
285 image.mipmaps = 1;
287
288 RL_FREE(fileData);
289 RL_FREE(delays); // NOTE: Frames delays are discarded
290 }
291 }
292#else
293 if (false) { }
294#endif
295 else image = LoadImage(fileName);
296
297 // TODO: Support APNG animated images
298
299 *frames = frameCount;
300 return image;
301}
302
303// Load image from memory buffer, fileType refers to extension: i.e. ".png"
304// WARNING: File extension must be provided in lower-case
305Image LoadImageFromMemory(const char *fileType, const unsigned char *fileData, int dataSize)
306{
307 Image image = { 0 };
308
309 if ((false)
310#if defined(SUPPORT_FILEFORMAT_PNG)
311 || (strcmp(fileType, ".png") == 0)
312#endif
313#if defined(SUPPORT_FILEFORMAT_BMP)
314 || (strcmp(fileType, ".bmp") == 0)
315#endif
316#if defined(SUPPORT_FILEFORMAT_TGA)
317 || (strcmp(fileType, ".tga") == 0)
318#endif
319#if defined(SUPPORT_FILEFORMAT_JPG)
320 || ((strcmp(fileType, ".jpg") == 0) || (strcmp(fileType, ".jpeg") == 0))
321#endif
322#if defined(SUPPORT_FILEFORMAT_GIF)
323 || (strcmp(fileType, ".gif") == 0)
324#endif
325#if defined(SUPPORT_FILEFORMAT_PIC)
326 || (strcmp(fileType, ".pic") == 0)
327#endif
328#if defined(SUPPORT_FILEFORMAT_PSD)
329 || (strcmp(fileType, ".psd") == 0)
330#endif
331 )
332 {
333#if defined(STBI_REQUIRED)
334 // NOTE: Using stb_image to load images (Supports multiple image formats)
335
336 if (fileData != NULL)
337 {
338 int comp = 0;
339 image.data = stbi_load_from_memory(fileData, dataSize, &image.width, &image.height, &comp, 0);
340
341 if (image.data != NULL)
342 {
343 image.mipmaps = 1;
344
345 if (comp == 1) image.format = PIXELFORMAT_UNCOMPRESSED_GRAYSCALE;
346 else if (comp == 2) image.format = PIXELFORMAT_UNCOMPRESSED_GRAY_ALPHA;
347 else if (comp == 3) image.format = PIXELFORMAT_UNCOMPRESSED_R8G8B8;
348 else if (comp == 4) image.format = PIXELFORMAT_UNCOMPRESSED_R8G8B8A8;
349 }
350 }
351#endif
352 }
353#if defined(SUPPORT_FILEFORMAT_HDR)
354 else if (strcmp(fileType, ".hdr") == 0)
355 {
356#if defined(STBI_REQUIRED)
357 if (fileData != NULL)
358 {
359 int comp = 0;
360 image.data = stbi_loadf_from_memory(fileData, dataSize, &image.width, &image.height, &comp, 0);
361
362 image.mipmaps = 1;
363
364 if (comp == 1) image.format = PIXELFORMAT_UNCOMPRESSED_R32;
365 else if (comp == 3) image.format = PIXELFORMAT_UNCOMPRESSED_R32G32B32;
366 else if (comp == 4) image.format = PIXELFORMAT_UNCOMPRESSED_R32G32B32A32;
367 else
368 {
369 TRACELOG(LOG_WARNING, "IMAGE: HDR file format not supported");
370 UnloadImage(image);
371 }
372 }
373#endif
374 }
375#endif
376#if defined(SUPPORT_FILEFORMAT_QOI)
377 else if (strcmp(fileType, ".qoi") == 0)
378 {
379 qoi_desc desc = { 0 };
380 image.data = qoi_decode(fileData, dataSize, &desc, 4);
381 image.width = desc.width;
382 image.height = desc.height;
384 image.mipmaps = 1;
385 }
386#endif
387#if defined(SUPPORT_FILEFORMAT_DDS)
388 else if (strcmp(fileType, ".dds") == 0) image = LoadDDS(fileData, dataSize);
389#endif
390#if defined(SUPPORT_FILEFORMAT_PKM)
391 else if (strcmp(fileType, ".pkm") == 0) image = LoadPKM(fileData, dataSize);
392#endif
393#if defined(SUPPORT_FILEFORMAT_KTX)
394 else if (strcmp(fileType, ".ktx") == 0) image = LoadKTX(fileData, dataSize);
395#endif
396#if defined(SUPPORT_FILEFORMAT_PVR)
397 else if (strcmp(fileType, ".pvr") == 0) image = LoadPVR(fileData, dataSize);
398#endif
399#if defined(SUPPORT_FILEFORMAT_ASTC)
400 else if (strcmp(fileType, ".astc") == 0) image = LoadASTC(fileData, dataSize);
401#endif
402 else TRACELOG(LOG_WARNING, "IMAGE: Data format not supported");
403
404 if (image.data != NULL) TRACELOG(LOG_INFO, "IMAGE: Data loaded successfully (%ix%i | %s | %i mipmaps)", image.width, image.height, rlGetPixelFormatName(image.format), image.mipmaps);
405 else TRACELOG(LOG_WARNING, "IMAGE: Failed to load image data");
406
407 return image;
408}
409
410// Load image from GPU texture data
411// NOTE: Compressed texture formats not supported
413{
414 Image image = { 0 };
415
417 {
418 image.data = rlReadTexturePixels(texture.id, texture.width, texture.height, texture.format);
419
420 if (image.data != NULL)
421 {
422 image.width = texture.width;
423 image.height = texture.height;
424 image.format = texture.format;
425 image.mipmaps = 1;
426
427#if defined(GRAPHICS_API_OPENGL_ES2)
428 // NOTE: Data retrieved on OpenGL ES 2.0 should be RGBA,
429 // coming from FBO color buffer attachment, but it seems
430 // original texture format is retrieved on RPI...
432#endif
433 TRACELOG(LOG_INFO, "TEXTURE: [ID %i] Pixel data retrieved successfully", texture.id);
434 }
435 else TRACELOG(LOG_WARNING, "TEXTURE: [ID %i] Failed to retrieve pixel data", texture.id);
436 }
437 else TRACELOG(LOG_WARNING, "TEXTURE: [ID %i] Failed to retrieve compressed pixel data", texture.id);
438
439 return image;
440}
441
442// Load image from screen buffer and (screenshot)
444{
445 Image image = { 0 };
446
447 image.width = GetScreenWidth();
448 image.height = GetScreenHeight();
449 image.mipmaps = 1;
451 image.data = rlReadScreenPixels(image.width, image.height);
452
453 return image;
454}
455
456// Unload image from CPU memory (RAM)
458{
459 RL_FREE(image.data);
460}
461
462// Export image data to file
463// NOTE: File format depends on fileName extension
464bool ExportImage(Image image, const char *fileName)
465{
466 int success = 0;
467
468#if defined(SUPPORT_IMAGE_EXPORT)
469 int channels = 4;
470 bool allocatedData = false;
471 unsigned char *imgData = (unsigned char *)image.data;
472
473 if (image.format == PIXELFORMAT_UNCOMPRESSED_GRAYSCALE) channels = 1;
474 else if (image.format == PIXELFORMAT_UNCOMPRESSED_GRAY_ALPHA) channels = 2;
475 else if (image.format == PIXELFORMAT_UNCOMPRESSED_R8G8B8) channels = 3;
476 else if (image.format == PIXELFORMAT_UNCOMPRESSED_R8G8B8A8) channels = 4;
477 else
478 {
479 // NOTE: Getting Color array as RGBA unsigned char values
480 imgData = (unsigned char *)LoadImageColors(image);
481 allocatedData = true;
482 }
483
484#if defined(SUPPORT_FILEFORMAT_PNG)
485 if (IsFileExtension(fileName, ".png"))
486 {
487 int dataSize = 0;
488 unsigned char *fileData = stbi_write_png_to_mem((const unsigned char *)imgData, image.width*channels, image.width, image.height, channels, &dataSize);
489 success = SaveFileData(fileName, fileData, dataSize);
490 RL_FREE(fileData);
491 }
492#else
493 if (false) { }
494#endif
495#if defined(SUPPORT_FILEFORMAT_BMP)
496 else if (IsFileExtension(fileName, ".bmp")) success = stbi_write_bmp(fileName, image.width, image.height, channels, imgData);
497#endif
498#if defined(SUPPORT_FILEFORMAT_TGA)
499 else if (IsFileExtension(fileName, ".tga")) success = stbi_write_tga(fileName, image.width, image.height, channels, imgData);
500#endif
501#if defined(SUPPORT_FILEFORMAT_JPG)
502 else if (IsFileExtension(fileName, ".jpg") ||
503 IsFileExtension(fileName, ".jpeg")) success = stbi_write_jpg(fileName, image.width, image.height, channels, imgData, 90); // JPG quality: between 1 and 100
504#endif
505#if defined(SUPPORT_FILEFORMAT_QOI)
506 else if (IsFileExtension(fileName, ".qoi"))
507 {
508 channels = 0;
509 if (image.format == PIXELFORMAT_UNCOMPRESSED_R8G8B8) channels = 3;
510 else if (image.format == PIXELFORMAT_UNCOMPRESSED_R8G8B8A8) channels = 4;
511 else TRACELOG(LOG_WARNING, "IMAGE: Image pixel format must be R8G8B8 or R8G8B8A8");
512
513 if ((channels == 3) || (channels == 4))
514 {
515 qoi_desc desc = { 0 };
516 desc.width = image.width;
517 desc.height = image.height;
518 desc.channels = channels;
519 desc.colorspace = QOI_SRGB;
520
521 success = qoi_write(fileName, imgData, &desc);
522 }
523 }
524#endif
525#if defined(SUPPORT_FILEFORMAT_KTX)
526 else if (IsFileExtension(fileName, ".ktx")) success = SaveKTX(image, fileName);
527#endif
528 else if (IsFileExtension(fileName, ".raw"))
529 {
530 // Export raw pixel data (without header)
531 // NOTE: It's up to the user to track image parameters
532 success = SaveFileData(fileName, image.data, GetPixelDataSize(image.width, image.height, image.format));
533 }
534
535 if (allocatedData) RL_FREE(imgData);
536#endif // SUPPORT_IMAGE_EXPORT
537
538 if (success != 0) TRACELOG(LOG_INFO, "FILEIO: [%s] Image exported successfully", fileName);
539 else TRACELOG(LOG_WARNING, "FILEIO: [%s] Failed to export image", fileName);
540
541 return success;
542}
543
544// Export image as code file (.h) defining an array of bytes
545bool ExportImageAsCode(Image image, const char *fileName)
546{
547 bool success = false;
548
549#if defined(SUPPORT_IMAGE_EXPORT)
550
551#ifndef TEXT_BYTES_PER_LINE
552 #define TEXT_BYTES_PER_LINE 20
553#endif
554
555 int dataSize = GetPixelDataSize(image.width, image.height, image.format);
556
557 // NOTE: Text data buffer size is estimated considering image data size in bytes
558 // and requiring 6 char bytes for every byte: "0x00, "
559 char *txtData = (char *)RL_CALLOC(dataSize*6 + 2000, sizeof(char));
560
561 int byteCount = 0;
562 byteCount += sprintf(txtData + byteCount, "////////////////////////////////////////////////////////////////////////////////////////\n");
563 byteCount += sprintf(txtData + byteCount, "// //\n");
564 byteCount += sprintf(txtData + byteCount, "// ImageAsCode exporter v1.0 - Image pixel data exported as an array of bytes //\n");
565 byteCount += sprintf(txtData + byteCount, "// //\n");
566 byteCount += sprintf(txtData + byteCount, "// more info and bugs-report: github.com/raysan5/raylib //\n");
567 byteCount += sprintf(txtData + byteCount, "// feedback and support: ray[at]raylib.com //\n");
568 byteCount += sprintf(txtData + byteCount, "// //\n");
569 byteCount += sprintf(txtData + byteCount, "// Copyright (c) 2018-2022 Ramon Santamaria (@raysan5) //\n");
570 byteCount += sprintf(txtData + byteCount, "// //\n");
571 byteCount += sprintf(txtData + byteCount, "////////////////////////////////////////////////////////////////////////////////////////\n\n");
572
573 // Get file name from path and convert variable name to uppercase
574 char varFileName[256] = { 0 };
575 strcpy(varFileName, GetFileNameWithoutExt(fileName));
576 for (int i = 0; varFileName[i] != '\0'; i++) if ((varFileName[i] >= 'a') && (varFileName[i] <= 'z')) { varFileName[i] = varFileName[i] - 32; }
577
578 // Add image information
579 byteCount += sprintf(txtData + byteCount, "// Image data information\n");
580 byteCount += sprintf(txtData + byteCount, "#define %s_WIDTH %i\n", varFileName, image.width);
581 byteCount += sprintf(txtData + byteCount, "#define %s_HEIGHT %i\n", varFileName, image.height);
582 byteCount += sprintf(txtData + byteCount, "#define %s_FORMAT %i // raylib internal pixel format\n\n", varFileName, image.format);
583
584 byteCount += sprintf(txtData + byteCount, "static unsigned char %s_DATA[%i] = { ", varFileName, dataSize);
585 for (int i = 0; i < dataSize - 1; i++) byteCount += sprintf(txtData + byteCount, ((i%TEXT_BYTES_PER_LINE == 0)? "0x%x,\n" : "0x%x, "), ((unsigned char *)image.data)[i]);
586 byteCount += sprintf(txtData + byteCount, "0x%x };\n", ((unsigned char *)image.data)[dataSize - 1]);
587
588 // NOTE: Text data size exported is determined by '\0' (NULL) character
589 success = SaveFileText(fileName, txtData);
590
591 RL_FREE(txtData);
592
593#endif // SUPPORT_IMAGE_EXPORT
594
595 if (success != 0) TRACELOG(LOG_INFO, "FILEIO: [%s] Image as code exported successfully", fileName);
596 else TRACELOG(LOG_WARNING, "FILEIO: [%s] Failed to export image as code", fileName);
597
598 return success;
599}
600
601//------------------------------------------------------------------------------------
602// Image generation functions
603//------------------------------------------------------------------------------------
604// Generate image: plain color
605Image GenImageColor(int width, int height, Color color)
606{
607 Color *pixels = (Color *)RL_CALLOC(width*height, sizeof(Color));
608
609 for (int i = 0; i < width*height; i++) pixels[i] = color;
610
611 Image image = {
612 .data = pixels,
613 .width = width,
614 .height = height,
616 .mipmaps = 1
617 };
618
619 return image;
620}
621
622#if defined(SUPPORT_IMAGE_GENERATION)
623// Generate image: vertical gradient
624Image GenImageGradientV(int width, int height, Color top, Color bottom)
625{
626 Color *pixels = (Color *)RL_MALLOC(width*height*sizeof(Color));
627
628 for (int j = 0; j < height; j++)
629 {
630 float factor = (float)j/(float)height;
631 for (int i = 0; i < width; i++)
632 {
633 pixels[j*width + i].r = (int)((float)bottom.r*factor + (float)top.r*(1.f - factor));
634 pixels[j*width + i].g = (int)((float)bottom.g*factor + (float)top.g*(1.f - factor));
635 pixels[j*width + i].b = (int)((float)bottom.b*factor + (float)top.b*(1.f - factor));
636 pixels[j*width + i].a = (int)((float)bottom.a*factor + (float)top.a*(1.f - factor));
637 }
638 }
639
640 Image image = {
641 .data = pixels,
642 .width = width,
643 .height = height,
645 .mipmaps = 1
646 };
647
648 return image;
649}
650
651// Generate image: horizontal gradient
652Image GenImageGradientH(int width, int height, Color left, Color right)
653{
654 Color *pixels = (Color *)RL_MALLOC(width*height*sizeof(Color));
655
656 for (int i = 0; i < width; i++)
657 {
658 float factor = (float)i/(float)width;
659 for (int j = 0; j < height; j++)
660 {
661 pixels[j*width + i].r = (int)((float)right.r*factor + (float)left.r*(1.f - factor));
662 pixels[j*width + i].g = (int)((float)right.g*factor + (float)left.g*(1.f - factor));
663 pixels[j*width + i].b = (int)((float)right.b*factor + (float)left.b*(1.f - factor));
664 pixels[j*width + i].a = (int)((float)right.a*factor + (float)left.a*(1.f - factor));
665 }
666 }
667
668 Image image = {
669 .data = pixels,
670 .width = width,
671 .height = height,
673 .mipmaps = 1
674 };
675
676 return image;
677}
678
679// Generate image: radial gradient
680Image GenImageGradientRadial(int width, int height, float density, Color inner, Color outer)
681{
682 Color *pixels = (Color *)RL_MALLOC(width*height*sizeof(Color));
683 float radius = (width < height)? (float)width/2.0f : (float)height/2.0f;
684
685 float centerX = (float)width/2.0f;
686 float centerY = (float)height/2.0f;
687
688 for (int y = 0; y < height; y++)
689 {
690 for (int x = 0; x < width; x++)
691 {
692 float dist = hypotf((float)x - centerX, (float)y - centerY);
693 float factor = (dist - radius*density)/(radius*(1.0f - density));
694
695 factor = (float)fmax(factor, 0.0f);
696 factor = (float)fmin(factor, 1.f); // dist can be bigger than radius so we have to check
697
698 pixels[y*width + x].r = (int)((float)outer.r*factor + (float)inner.r*(1.0f - factor));
699 pixels[y*width + x].g = (int)((float)outer.g*factor + (float)inner.g*(1.0f - factor));
700 pixels[y*width + x].b = (int)((float)outer.b*factor + (float)inner.b*(1.0f - factor));
701 pixels[y*width + x].a = (int)((float)outer.a*factor + (float)inner.a*(1.0f - factor));
702 }
703 }
704
705 Image image = {
706 .data = pixels,
707 .width = width,
708 .height = height,
710 .mipmaps = 1
711 };
712
713 return image;
714}
715
716// Generate image: checked
717Image GenImageChecked(int width, int height, int checksX, int checksY, Color col1, Color col2)
718{
719 Color *pixels = (Color *)RL_MALLOC(width*height*sizeof(Color));
720
721 for (int y = 0; y < height; y++)
722 {
723 for (int x = 0; x < width; x++)
724 {
725 if ((x/checksX + y/checksY)%2 == 0) pixels[y*width + x] = col1;
726 else pixels[y*width + x] = col2;
727 }
728 }
729
730 Image image = {
731 .data = pixels,
732 .width = width,
733 .height = height,
735 .mipmaps = 1
736 };
737
738 return image;
739}
740
741// Generate image: white noise
742Image GenImageWhiteNoise(int width, int height, float factor)
743{
744 Color *pixels = (Color *)RL_MALLOC(width*height*sizeof(Color));
745
746 for (int i = 0; i < width*height; i++)
747 {
748 if (GetRandomValue(0, 99) < (int)(factor*100.0f)) pixels[i] = WHITE;
749 else pixels[i] = BLACK;
750 }
751
752 Image image = {
753 .data = pixels,
754 .width = width,
755 .height = height,
757 .mipmaps = 1
758 };
759
760 return image;
761}
762
763// Generate image: cellular algorithm. Bigger tileSize means bigger cells
764Image GenImageCellular(int width, int height, int tileSize)
765{
766 Color *pixels = (Color *)RL_MALLOC(width*height*sizeof(Color));
767
768 int seedsPerRow = width/tileSize;
769 int seedsPerCol = height/tileSize;
770 int seedCount = seedsPerRow*seedsPerCol;
771
772 Vector2 *seeds = (Vector2 *)RL_MALLOC(seedCount*sizeof(Vector2));
773
774 for (int i = 0; i < seedCount; i++)
775 {
776 int y = (i/seedsPerRow)*tileSize + GetRandomValue(0, tileSize - 1);
777 int x = (i%seedsPerRow)*tileSize + GetRandomValue(0, tileSize - 1);
778 seeds[i] = (Vector2){ (float)x, (float)y };
779 }
780
781 for (int y = 0; y < height; y++)
782 {
783 int tileY = y/tileSize;
784
785 for (int x = 0; x < width; x++)
786 {
787 int tileX = x/tileSize;
788
789 float minDistance = 65536.0f; //(float)strtod("Inf", NULL);
790
791 // Check all adjacent tiles
792 for (int i = -1; i < 2; i++)
793 {
794 if ((tileX + i < 0) || (tileX + i >= seedsPerRow)) continue;
795
796 for (int j = -1; j < 2; j++)
797 {
798 if ((tileY + j < 0) || (tileY + j >= seedsPerCol)) continue;
799
800 Vector2 neighborSeed = seeds[(tileY + j)*seedsPerRow + tileX + i];
801
802 float dist = (float)hypot(x - (int)neighborSeed.x, y - (int)neighborSeed.y);
803 minDistance = (float)fmin(minDistance, dist);
804 }
805 }
806
807 // I made this up but it seems to give good results at all tile sizes
808 int intensity = (int)(minDistance*256.0f/tileSize);
809 if (intensity > 255) intensity = 255;
810
811 pixels[y*width + x] = (Color){ intensity, intensity, intensity, 255 };
812 }
813 }
814
815 RL_FREE(seeds);
816
817 Image image = {
818 .data = pixels,
819 .width = width,
820 .height = height,
822 .mipmaps = 1
823 };
824
825 return image;
826}
827#endif // SUPPORT_IMAGE_GENERATION
828
829//------------------------------------------------------------------------------------
830// Image manipulation functions
831//------------------------------------------------------------------------------------
832// Copy an image to a new image
834{
835 Image newImage = { 0 };
836
837 int width = image.width;
838 int height = image.height;
839 int size = 0;
840
841 for (int i = 0; i < image.mipmaps; i++)
842 {
843 size += GetPixelDataSize(width, height, image.format);
844
845 width /= 2;
846 height /= 2;
847
848 // Security check for NPOT textures
849 if (width < 1) width = 1;
850 if (height < 1) height = 1;
851 }
852
853 newImage.data = RL_MALLOC(size);
854
855 if (newImage.data != NULL)
856 {
857 // NOTE: Size must be provided in bytes
858 memcpy(newImage.data, image.data, size);
859
860 newImage.width = image.width;
861 newImage.height = image.height;
862 newImage.mipmaps = image.mipmaps;
863 newImage.format = image.format;
864 }
865
866 return newImage;
867}
868
869// Create an image from another image piece
871{
872 Image result = { 0 };
873
874 int bytesPerPixel = GetPixelDataSize(1, 1, image.format);
875
876 result.width = (int)rec.width;
877 result.height = (int)rec.height;
878 result.data = RL_CALLOC((int)(rec.width*rec.height)*bytesPerPixel, 1);
879 result.format = image.format;
880 result.mipmaps = 1;
881
882 for (int y = 0; y < rec.height; y++)
883 {
884 memcpy(((unsigned char *)result.data) + y*(int)rec.width*bytesPerPixel, ((unsigned char *)image.data) + ((y + (int)rec.y)*image.width + (int)rec.x)*bytesPerPixel, (int)rec.width*bytesPerPixel);
885 }
886
887 return result;
888}
889
890// Crop an image to area defined by a rectangle
891// NOTE: Security checks are performed in case rectangle goes out of bounds
892void ImageCrop(Image *image, Rectangle crop)
893{
894 // Security check to avoid program crash
895 if ((image->data == NULL) || (image->width == 0) || (image->height == 0)) return;
896
897 // Security checks to validate crop rectangle
898 if (crop.x < 0) { crop.width += crop.x; crop.x = 0; }
899 if (crop.y < 0) { crop.height += crop.y; crop.y = 0; }
900 if ((crop.x + crop.width) > image->width) crop.width = image->width - crop.x;
901 if ((crop.y + crop.height) > image->height) crop.height = image->height - crop.y;
902 if ((crop.x > image->width) || (crop.y > image->height))
903 {
904 TRACELOG(LOG_WARNING, "IMAGE: Failed to crop, rectangle out of bounds");
905 return;
906 }
907
908 if (image->mipmaps > 1) TRACELOG(LOG_WARNING, "Image manipulation only applied to base mipmap level");
909 if (image->format >= PIXELFORMAT_COMPRESSED_DXT1_RGB) TRACELOG(LOG_WARNING, "Image manipulation not supported for compressed formats");
910 else
911 {
912 int bytesPerPixel = GetPixelDataSize(1, 1, image->format);
913
914 unsigned char *croppedData = (unsigned char *)RL_MALLOC((int)(crop.width*crop.height)*bytesPerPixel);
915
916 // OPTION 1: Move cropped data line-by-line
917 for (int y = (int)crop.y, offsetSize = 0; y < (int)(crop.y + crop.height); y++)
918 {
919 memcpy(croppedData + offsetSize, ((unsigned char *)image->data) + (y*image->width + (int)crop.x)*bytesPerPixel, (int)crop.width*bytesPerPixel);
920 offsetSize += ((int)crop.width*bytesPerPixel);
921 }
922
923 /*
924 // OPTION 2: Move cropped data pixel-by-pixel or byte-by-byte
925 for (int y = (int)crop.y; y < (int)(crop.y + crop.height); y++)
926 {
927 for (int x = (int)crop.x; x < (int)(crop.x + crop.width); x++)
928 {
929 //memcpy(croppedData + ((y - (int)crop.y)*(int)crop.width + (x - (int)crop.x))*bytesPerPixel, ((unsigned char *)image->data) + (y*image->width + x)*bytesPerPixel, bytesPerPixel);
930 for (int i = 0; i < bytesPerPixel; i++) croppedData[((y - (int)crop.y)*(int)crop.width + (x - (int)crop.x))*bytesPerPixel + i] = ((unsigned char *)image->data)[(y*image->width + x)*bytesPerPixel + i];
931 }
932 }
933 */
934
935 RL_FREE(image->data);
936 image->data = croppedData;
937 image->width = (int)crop.width;
938 image->height = (int)crop.height;
939 }
940}
941
942// Convert image data to desired format
943void ImageFormat(Image *image, int newFormat)
944{
945 // Security check to avoid program crash
946 if ((image->data == NULL) || (image->width == 0) || (image->height == 0)) return;
947
948 if ((newFormat != 0) && (image->format != newFormat))
949 {
951 {
952 Vector4 *pixels = LoadImageDataNormalized(*image); // Supports 8 to 32 bit per channel
953
954 RL_FREE(image->data); // WARNING! We loose mipmaps data --> Regenerated at the end...
955 image->data = NULL;
956 image->format = newFormat;
957
958 int k = 0;
959
960 switch (image->format)
961 {
963 {
964 image->data = (unsigned char *)RL_MALLOC(image->width*image->height*sizeof(unsigned char));
965
966 for (int i = 0; i < image->width*image->height; i++)
967 {
968 ((unsigned char *)image->data)[i] = (unsigned char)((pixels[i].x*0.299f + pixels[i].y*0.587f + pixels[i].z*0.114f)*255.0f);
969 }
970
971 } break;
973 {
974 image->data = (unsigned char *)RL_MALLOC(image->width*image->height*2*sizeof(unsigned char));
975
976 for (int i = 0; i < image->width*image->height*2; i += 2, k++)
977 {
978 ((unsigned char *)image->data)[i] = (unsigned char)((pixels[k].x*0.299f + (float)pixels[k].y*0.587f + (float)pixels[k].z*0.114f)*255.0f);
979 ((unsigned char *)image->data)[i + 1] = (unsigned char)(pixels[k].w*255.0f);
980 }
981
982 } break;
984 {
985 image->data = (unsigned short *)RL_MALLOC(image->width*image->height*sizeof(unsigned short));
986
987 unsigned char r = 0;
988 unsigned char g = 0;
989 unsigned char b = 0;
990
991 for (int i = 0; i < image->width*image->height; i++)
992 {
993 r = (unsigned char)(round(pixels[i].x*31.0f));
994 g = (unsigned char)(round(pixels[i].y*63.0f));
995 b = (unsigned char)(round(pixels[i].z*31.0f));
996
997 ((unsigned short *)image->data)[i] = (unsigned short)r << 11 | (unsigned short)g << 5 | (unsigned short)b;
998 }
999
1000 } break;
1002 {
1003 image->data = (unsigned char *)RL_MALLOC(image->width*image->height*3*sizeof(unsigned char));
1004
1005 for (int i = 0, k = 0; i < image->width*image->height*3; i += 3, k++)
1006 {
1007 ((unsigned char *)image->data)[i] = (unsigned char)(pixels[k].x*255.0f);
1008 ((unsigned char *)image->data)[i + 1] = (unsigned char)(pixels[k].y*255.0f);
1009 ((unsigned char *)image->data)[i + 2] = (unsigned char)(pixels[k].z*255.0f);
1010 }
1011 } break;
1013 {
1014 image->data = (unsigned short *)RL_MALLOC(image->width*image->height*sizeof(unsigned short));
1015
1016 unsigned char r = 0;
1017 unsigned char g = 0;
1018 unsigned char b = 0;
1019 unsigned char a = 0;
1020
1021 for (int i = 0; i < image->width*image->height; i++)
1022 {
1023 r = (unsigned char)(round(pixels[i].x*31.0f));
1024 g = (unsigned char)(round(pixels[i].y*31.0f));
1025 b = (unsigned char)(round(pixels[i].z*31.0f));
1026 a = (pixels[i].w > ((float)PIXELFORMAT_UNCOMPRESSED_R5G5B5A1_ALPHA_THRESHOLD/255.0f))? 1 : 0;
1027
1028 ((unsigned short *)image->data)[i] = (unsigned short)r << 11 | (unsigned short)g << 6 | (unsigned short)b << 1 | (unsigned short)a;
1029 }
1030
1031 } break;
1033 {
1034 image->data = (unsigned short *)RL_MALLOC(image->width*image->height*sizeof(unsigned short));
1035
1036 unsigned char r = 0;
1037 unsigned char g = 0;
1038 unsigned char b = 0;
1039 unsigned char a = 0;
1040
1041 for (int i = 0; i < image->width*image->height; i++)
1042 {
1043 r = (unsigned char)(round(pixels[i].x*15.0f));
1044 g = (unsigned char)(round(pixels[i].y*15.0f));
1045 b = (unsigned char)(round(pixels[i].z*15.0f));
1046 a = (unsigned char)(round(pixels[i].w*15.0f));
1047
1048 ((unsigned short *)image->data)[i] = (unsigned short)r << 12 | (unsigned short)g << 8 | (unsigned short)b << 4 | (unsigned short)a;
1049 }
1050
1051 } break;
1053 {
1054 image->data = (unsigned char *)RL_MALLOC(image->width*image->height*4*sizeof(unsigned char));
1055
1056 for (int i = 0, k = 0; i < image->width*image->height*4; i += 4, k++)
1057 {
1058 ((unsigned char *)image->data)[i] = (unsigned char)(pixels[k].x*255.0f);
1059 ((unsigned char *)image->data)[i + 1] = (unsigned char)(pixels[k].y*255.0f);
1060 ((unsigned char *)image->data)[i + 2] = (unsigned char)(pixels[k].z*255.0f);
1061 ((unsigned char *)image->data)[i + 3] = (unsigned char)(pixels[k].w*255.0f);
1062 }
1063 } break;
1065 {
1066 // WARNING: Image is converted to GRAYSCALE eqeuivalent 32bit
1067
1068 image->data = (float *)RL_MALLOC(image->width*image->height*sizeof(float));
1069
1070 for (int i = 0; i < image->width*image->height; i++)
1071 {
1072 ((float *)image->data)[i] = (float)(pixels[i].x*0.299f + pixels[i].y*0.587f + pixels[i].z*0.114f);
1073 }
1074 } break;
1076 {
1077 image->data = (float *)RL_MALLOC(image->width*image->height*3*sizeof(float));
1078
1079 for (int i = 0, k = 0; i < image->width*image->height*3; i += 3, k++)
1080 {
1081 ((float *)image->data)[i] = pixels[k].x;
1082 ((float *)image->data)[i + 1] = pixels[k].y;
1083 ((float *)image->data)[i + 2] = pixels[k].z;
1084 }
1085 } break;
1087 {
1088 image->data = (float *)RL_MALLOC(image->width*image->height*4*sizeof(float));
1089
1090 for (int i = 0, k = 0; i < image->width*image->height*4; i += 4, k++)
1091 {
1092 ((float *)image->data)[i] = pixels[k].x;
1093 ((float *)image->data)[i + 1] = pixels[k].y;
1094 ((float *)image->data)[i + 2] = pixels[k].z;
1095 ((float *)image->data)[i + 3] = pixels[k].w;
1096 }
1097 } break;
1098 default: break;
1099 }
1100
1101 RL_FREE(pixels);
1102 pixels = NULL;
1103
1104 // In case original image had mipmaps, generate mipmaps for formated image
1105 // NOTE: Original mipmaps are replaced by new ones, if custom mipmaps were used, they are lost
1106 if (image->mipmaps > 1)
1107 {
1108 image->mipmaps = 1;
1109 #if defined(SUPPORT_IMAGE_MANIPULATION)
1110 if (image->data != NULL) ImageMipmaps(image);
1111 #endif
1112 }
1113 }
1114 else TRACELOG(LOG_WARNING, "IMAGE: Data format is compressed, can not be converted");
1115 }
1116}
1117
1118// Convert image to POT (power-of-two)
1119// NOTE: It could be useful on OpenGL ES 2.0 (RPI, HTML5)
1120void ImageToPOT(Image *image, Color fill)
1121{
1122 // Security check to avoid program crash
1123 if ((image->data == NULL) || (image->width == 0) || (image->height == 0)) return;
1124
1125 // Calculate next power-of-two values
1126 // NOTE: Just add the required amount of pixels at the right and bottom sides of image...
1127 int potWidth = (int)powf(2, ceilf(logf((float)image->width)/logf(2)));
1128 int potHeight = (int)powf(2, ceilf(logf((float)image->height)/logf(2)));
1129
1130 // Check if POT texture generation is required (if texture is not already POT)
1131 if ((potWidth != image->width) || (potHeight != image->height)) ImageResizeCanvas(image, potWidth, potHeight, 0, 0, fill);
1132}
1133
1134#if defined(SUPPORT_IMAGE_MANIPULATION)
1135// Create an image from text (default font)
1136Image ImageText(const char *text, int fontSize, Color color)
1137{
1138 Image imText = { 0 };
1139#if defined(SUPPORT_MODULE_RTEXT)
1140 int defaultFontSize = 10; // Default Font chars height in pixel
1141 if (fontSize < defaultFontSize) fontSize = defaultFontSize;
1142 int spacing = fontSize/defaultFontSize;
1143 imText = ImageTextEx(GetFontDefault(), text, (float)fontSize, (float)spacing, color); // WARNING: Module required: rtext
1144#else
1145 imText = GenImageColor(200, 60, BLACK); // Generating placeholder black image rectangle
1146 TRACELOG(LOG_WARNING, "IMAGE: ImageTextEx() requires module: rtext");
1147#endif
1148 return imText;
1149}
1150
1151// Create an image from text (custom sprite font)
1152Image ImageTextEx(Font font, const char *text, float fontSize, float spacing, Color tint)
1153{
1154 Image imText = { 0 };
1155#if defined(SUPPORT_MODULE_RTEXT)
1156 int size = (int)strlen(text); // Get size in bytes of text
1157
1158 int textOffsetX = 0; // Image drawing position X
1159 int textOffsetY = 0; // Offset between lines (on line break '\n')
1160
1161 // NOTE: Text image is generated at font base size, later scaled to desired font size
1162 Vector2 imSize = MeasureTextEx(font, text, (float)font.baseSize, spacing); // WARNING: Module required: rtext
1163
1164 // Create image to store text
1165 imText = GenImageColor((int)imSize.x, (int)imSize.y, BLANK);
1166
1167 for (int i = 0; i < size; i++)
1168 {
1169 // Get next codepoint from byte string and glyph index in font
1170 int codepointByteCount = 0;
1171 int codepoint = GetCodepoint(&text[i], &codepointByteCount); // WARNING: Module required: rtext
1172 int index = GetGlyphIndex(font, codepoint); // WARNING: Module required: rtext
1173
1174 // NOTE: Normally we exit the decoding sequence as soon as a bad byte is found (and return 0x3f)
1175 // but we need to draw all of the bad bytes using the '?' symbol moving one byte
1176 if (codepoint == 0x3f) codepointByteCount = 1;
1177
1178 if (codepoint == '\n')
1179 {
1180 // NOTE: Fixed line spacing of 1.5 line-height
1181 // TODO: Support custom line spacing defined by user
1182 textOffsetY += (font.baseSize + font.baseSize/2);
1183 textOffsetX = 0;
1184 }
1185 else
1186 {
1187 if ((codepoint != ' ') && (codepoint != '\t'))
1188 {
1189 Rectangle rec = { (float)(textOffsetX + font.glyphs[index].offsetX), (float)(textOffsetY + font.glyphs[index].offsetY), (float)font.recs[index].width, (float)font.recs[index].height };
1190 ImageDraw(&imText, font.glyphs[index].image, (Rectangle){ 0, 0, (float)font.glyphs[index].image.width, (float)font.glyphs[index].image.height }, rec, tint);
1191 }
1192
1193 if (font.glyphs[index].advanceX == 0) textOffsetX += (int)(font.recs[index].width + spacing);
1194 else textOffsetX += font.glyphs[index].advanceX + (int)spacing;
1195 }
1196
1197 i += (codepointByteCount - 1); // Move text bytes counter to next codepoint
1198 }
1199
1200 // Scale image depending on text size
1201 if (fontSize > imSize.y)
1202 {
1203 float scaleFactor = fontSize/imSize.y;
1204 TRACELOG(LOG_INFO, "IMAGE: Text scaled by factor: %f", scaleFactor);
1205
1206 // Using nearest-neighbor scaling algorithm for default font
1207 // WARNING: Module required: rtext
1208 if (font.texture.id == GetFontDefault().texture.id) ImageResizeNN(&imText, (int)(imSize.x*scaleFactor), (int)(imSize.y*scaleFactor));
1209 else ImageResize(&imText, (int)(imSize.x*scaleFactor), (int)(imSize.y*scaleFactor));
1210 }
1211#else
1212 imText = GenImageColor(200, 60, BLACK); // Generating placeholder black image rectangle
1213 TRACELOG(LOG_WARNING, "IMAGE: ImageTextEx() requires module: rtext");
1214#endif
1215 return imText;
1216}
1217
1218// Crop image depending on alpha value
1219// NOTE: Threshold is defined as a percentatge: 0.0f -> 1.0f
1220void ImageAlphaCrop(Image *image, float threshold)
1221{
1222 // Security check to avoid program crash
1223 if ((image->data == NULL) || (image->width == 0) || (image->height == 0)) return;
1224
1225 Rectangle crop = GetImageAlphaBorder(*image, threshold);
1226
1227 // Crop if rectangle is valid
1228 if (((int)crop.width != 0) && ((int)crop.height != 0)) ImageCrop(image, crop);
1229}
1230
1231// Clear alpha channel to desired color
1232// NOTE: Threshold defines the alpha limit, 0.0f to 1.0f
1233void ImageAlphaClear(Image *image, Color color, float threshold)
1234{
1235 // Security check to avoid program crash
1236 if ((image->data == NULL) || (image->width == 0) || (image->height == 0)) return;
1237
1238 if (image->mipmaps > 1) TRACELOG(LOG_WARNING, "Image manipulation only applied to base mipmap level");
1239 if (image->format >= PIXELFORMAT_COMPRESSED_DXT1_RGB) TRACELOG(LOG_WARNING, "Image manipulation not supported for compressed formats");
1240 else
1241 {
1242 switch (image->format)
1243 {
1245 {
1246 unsigned char thresholdValue = (unsigned char)(threshold*255.0f);
1247 for (int i = 1; i < image->width*image->height*2; i += 2)
1248 {
1249 if (((unsigned char *)image->data)[i] <= thresholdValue)
1250 {
1251 ((unsigned char *)image->data)[i - 1] = color.r;
1252 ((unsigned char *)image->data)[i] = color.a;
1253 }
1254 }
1255 } break;
1257 {
1258 unsigned char thresholdValue = ((threshold < 0.5f)? 0 : 1);
1259
1260 unsigned char r = (unsigned char)(round((float)color.r*31.0f));
1261 unsigned char g = (unsigned char)(round((float)color.g*31.0f));
1262 unsigned char b = (unsigned char)(round((float)color.b*31.0f));
1263 unsigned char a = (color.a < 128)? 0 : 1;
1264
1265 for (int i = 0; i < image->width*image->height; i++)
1266 {
1267 if ((((unsigned short *)image->data)[i] & 0b0000000000000001) <= thresholdValue)
1268 {
1269 ((unsigned short *)image->data)[i] = (unsigned short)r << 11 | (unsigned short)g << 6 | (unsigned short)b << 1 | (unsigned short)a;
1270 }
1271 }
1272 } break;
1274 {
1275 unsigned char thresholdValue = (unsigned char)(threshold*15.0f);
1276
1277 unsigned char r = (unsigned char)(round((float)color.r*15.0f));
1278 unsigned char g = (unsigned char)(round((float)color.g*15.0f));
1279 unsigned char b = (unsigned char)(round((float)color.b*15.0f));
1280 unsigned char a = (unsigned char)(round((float)color.a*15.0f));
1281
1282 for (int i = 0; i < image->width*image->height; i++)
1283 {
1284 if ((((unsigned short *)image->data)[i] & 0x000f) <= thresholdValue)
1285 {
1286 ((unsigned short *)image->data)[i] = (unsigned short)r << 12 | (unsigned short)g << 8 | (unsigned short)b << 4 | (unsigned short)a;
1287 }
1288 }
1289 } break;
1291 {
1292 unsigned char thresholdValue = (unsigned char)(threshold*255.0f);
1293 for (int i = 3; i < image->width*image->height*4; i += 4)
1294 {
1295 if (((unsigned char *)image->data)[i] <= thresholdValue)
1296 {
1297 ((unsigned char *)image->data)[i - 3] = color.r;
1298 ((unsigned char *)image->data)[i - 2] = color.g;
1299 ((unsigned char *)image->data)[i - 1] = color.b;
1300 ((unsigned char *)image->data)[i] = color.a;
1301 }
1302 }
1303 } break;
1305 {
1306 for (int i = 3; i < image->width*image->height*4; i += 4)
1307 {
1308 if (((float *)image->data)[i] <= threshold)
1309 {
1310 ((float *)image->data)[i - 3] = (float)color.r/255.0f;
1311 ((float *)image->data)[i - 2] = (float)color.g/255.0f;
1312 ((float *)image->data)[i - 1] = (float)color.b/255.0f;
1313 ((float *)image->data)[i] = (float)color.a/255.0f;
1314 }
1315 }
1316 } break;
1317 default: break;
1318 }
1319 }
1320}
1321
1322// Apply alpha mask to image
1323// NOTE 1: Returned image is GRAY_ALPHA (16bit) or RGBA (32bit)
1324// NOTE 2: alphaMask should be same size as image
1325void ImageAlphaMask(Image *image, Image alphaMask)
1326{
1327 if ((image->width != alphaMask.width) || (image->height != alphaMask.height))
1328 {
1329 TRACELOG(LOG_WARNING, "IMAGE: Alpha mask must be same size as image");
1330 }
1331 else if (image->format >= PIXELFORMAT_COMPRESSED_DXT1_RGB)
1332 {
1333 TRACELOG(LOG_WARNING, "IMAGE: Alpha mask can not be applied to compressed data formats");
1334 }
1335 else
1336 {
1337 // Force mask to be Grayscale
1338 Image mask = ImageCopy(alphaMask);
1340
1341 // In case image is only grayscale, we just add alpha channel
1343 {
1344 unsigned char *data = (unsigned char *)RL_MALLOC(image->width*image->height*2);
1345
1346 // Apply alpha mask to alpha channel
1347 for (int i = 0, k = 0; (i < mask.width*mask.height) || (i < image->width*image->height); i++, k += 2)
1348 {
1349 data[k] = ((unsigned char *)image->data)[i];
1350 data[k + 1] = ((unsigned char *)mask.data)[i];
1351 }
1352
1353 RL_FREE(image->data);
1354 image->data = data;
1356 }
1357 else
1358 {
1359 // Convert image to RGBA
1361
1362 // Apply alpha mask to alpha channel
1363 for (int i = 0, k = 3; (i < mask.width*mask.height) || (i < image->width*image->height); i++, k += 4)
1364 {
1365 ((unsigned char *)image->data)[k] = ((unsigned char *)mask.data)[i];
1366 }
1367 }
1368
1369 UnloadImage(mask);
1370 }
1371}
1372
1373// Premultiply alpha channel
1375{
1376 // Security check to avoid program crash
1377 if ((image->data == NULL) || (image->width == 0) || (image->height == 0)) return;
1378
1379 float alpha = 0.0f;
1380 Color *pixels = LoadImageColors(*image);
1381
1382 for (int i = 0; i < image->width*image->height; i++)
1383 {
1384 if (pixels[i].a == 0)
1385 {
1386 pixels[i].r = 0;
1387 pixels[i].g = 0;
1388 pixels[i].b = 0;
1389 }
1390 else if (pixels[i].a < 255)
1391 {
1392 alpha = (float)pixels[i].a/255.0f;
1393 pixels[i].r = (unsigned char)((float)pixels[i].r*alpha);
1394 pixels[i].g = (unsigned char)((float)pixels[i].g*alpha);
1395 pixels[i].b = (unsigned char)((float)pixels[i].b*alpha);
1396 }
1397 }
1398
1399 RL_FREE(image->data);
1400
1401 int format = image->format;
1402 image->data = pixels;
1404
1405 ImageFormat(image, format);
1406}
1407
1408// Resize and image to new size
1409// NOTE: Uses stb default scaling filters (both bicubic):
1410// STBIR_DEFAULT_FILTER_UPSAMPLE STBIR_FILTER_CATMULLROM
1411// STBIR_DEFAULT_FILTER_DOWNSAMPLE STBIR_FILTER_MITCHELL (high-quality Catmull-Rom)
1412void ImageResize(Image *image, int newWidth, int newHeight)
1413{
1414 // Security check to avoid program crash
1415 if ((image->data == NULL) || (image->width == 0) || (image->height == 0)) return;
1416
1417 bool fastPath = true;
1419
1420 if (fastPath)
1421 {
1422 int bytesPerPixel = GetPixelDataSize(1, 1, image->format);
1423 unsigned char *output = (unsigned char *)RL_MALLOC(newWidth*newHeight*bytesPerPixel);
1424
1425 switch (image->format)
1426 {
1427 case PIXELFORMAT_UNCOMPRESSED_GRAYSCALE: stbir_resize_uint8((unsigned char *)image->data, image->width, image->height, 0, output, newWidth, newHeight, 0, 1); break;
1428 case PIXELFORMAT_UNCOMPRESSED_GRAY_ALPHA: stbir_resize_uint8((unsigned char *)image->data, image->width, image->height, 0, output, newWidth, newHeight, 0, 2); break;
1429 case PIXELFORMAT_UNCOMPRESSED_R8G8B8: stbir_resize_uint8((unsigned char *)image->data, image->width, image->height, 0, output, newWidth, newHeight, 0, 3); break;
1430 case PIXELFORMAT_UNCOMPRESSED_R8G8B8A8: stbir_resize_uint8((unsigned char *)image->data, image->width, image->height, 0, output, newWidth, newHeight, 0, 4); break;
1431 default: break;
1432 }
1433
1434 RL_FREE(image->data);
1435 image->data = output;
1436 image->width = newWidth;
1437 image->height = newHeight;
1438 }
1439 else
1440 {
1441 // Get data as Color pixels array to work with it
1442 Color *pixels = LoadImageColors(*image);
1443 Color *output = (Color *)RL_MALLOC(newWidth*newHeight*sizeof(Color));
1444
1445 // NOTE: Color data is casted to (unsigned char *), there shouldn't been any problem...
1446 stbir_resize_uint8((unsigned char *)pixels, image->width, image->height, 0, (unsigned char *)output, newWidth, newHeight, 0, 4);
1447
1448 int format = image->format;
1449
1450 UnloadImageColors(pixels);
1451 RL_FREE(image->data);
1452
1453 image->data = output;
1454 image->width = newWidth;
1455 image->height = newHeight;
1457
1458 ImageFormat(image, format); // Reformat 32bit RGBA image to original format
1459 }
1460}
1461
1462// Resize and image to new size using Nearest-Neighbor scaling algorithm
1463void ImageResizeNN(Image *image,int newWidth,int newHeight)
1464{
1465 // Security check to avoid program crash
1466 if ((image->data == NULL) || (image->width == 0) || (image->height == 0)) return;
1467
1468 Color *pixels = LoadImageColors(*image);
1469 Color *output = (Color *)RL_MALLOC(newWidth*newHeight*sizeof(Color));
1470
1471 // EDIT: added +1 to account for an early rounding problem
1472 int xRatio = (int)((image->width << 16)/newWidth) + 1;
1473 int yRatio = (int)((image->height << 16)/newHeight) + 1;
1474
1475 int x2, y2;
1476 for (int y = 0; y < newHeight; y++)
1477 {
1478 for (int x = 0; x < newWidth; x++)
1479 {
1480 x2 = ((x*xRatio) >> 16);
1481 y2 = ((y*yRatio) >> 16);
1482
1483 output[(y*newWidth) + x] = pixels[(y2*image->width) + x2] ;
1484 }
1485 }
1486
1487 int format = image->format;
1488
1489 RL_FREE(image->data);
1490
1491 image->data = output;
1492 image->width = newWidth;
1493 image->height = newHeight;
1495
1496 ImageFormat(image, format); // Reformat 32bit RGBA image to original format
1497
1498 UnloadImageColors(pixels);
1499}
1500
1501// Resize canvas and fill with color
1502// NOTE: Resize offset is relative to the top-left corner of the original image
1503void ImageResizeCanvas(Image *image, int newWidth, int newHeight, int offsetX, int offsetY, Color fill)
1504{
1505 // Security check to avoid program crash
1506 if ((image->data == NULL) || (image->width == 0) || (image->height == 0)) return;
1507
1508 if (image->mipmaps > 1) TRACELOG(LOG_WARNING, "Image manipulation only applied to base mipmap level");
1509 if (image->format >= PIXELFORMAT_COMPRESSED_DXT1_RGB) TRACELOG(LOG_WARNING, "Image manipulation not supported for compressed formats");
1510 else if ((newWidth != image->width) || (newHeight != image->height))
1511 {
1512 Rectangle srcRec = { 0, 0, (float)image->width, (float)image->height };
1513 Vector2 dstPos = { (float)offsetX, (float)offsetY };
1514
1515 if (offsetX < 0)
1516 {
1517 srcRec.x = (float)-offsetX;
1518 srcRec.width += (float)offsetX;
1519 dstPos.x = 0;
1520 }
1521 else if ((offsetX + image->width) > newWidth) srcRec.width = (float)(newWidth - offsetX);
1522
1523 if (offsetY < 0)
1524 {
1525 srcRec.y = (float)-offsetY;
1526 srcRec.height += (float)offsetY;
1527 dstPos.y = 0;
1528 }
1529 else if ((offsetY + image->height) > newHeight) srcRec.height = (float)(newHeight - offsetY);
1530
1531 if (newWidth < srcRec.width) srcRec.width = (float)newWidth;
1532 if (newHeight < srcRec.height) srcRec.height = (float)newHeight;
1533
1534 int bytesPerPixel = GetPixelDataSize(1, 1, image->format);
1535 unsigned char *resizedData = (unsigned char *)RL_CALLOC(newWidth*newHeight*bytesPerPixel, 1);
1536
1537 // TODO: Fill resized canvas with fill color (must be formatted to image->format)
1538
1539 int dstOffsetSize = ((int)dstPos.y*newWidth + (int)dstPos.x)*bytesPerPixel;
1540
1541 for (int y = 0; y < (int)srcRec.height; y++)
1542 {
1543 memcpy(resizedData + dstOffsetSize, ((unsigned char *)image->data) + ((y + (int)srcRec.y)*image->width + (int)srcRec.x)*bytesPerPixel, (int)srcRec.width*bytesPerPixel);
1544 dstOffsetSize += (newWidth*bytesPerPixel);
1545 }
1546
1547 RL_FREE(image->data);
1548 image->data = resizedData;
1549 image->width = newWidth;
1550 image->height = newHeight;
1551 }
1552}
1553
1554// Generate all mipmap levels for a provided image
1555// NOTE 1: Supports POT and NPOT images
1556// NOTE 2: image.data is scaled to include mipmap levels
1557// NOTE 3: Mipmaps format is the same as base image
1559{
1560 // Security check to avoid program crash
1561 if ((image->data == NULL) || (image->width == 0) || (image->height == 0)) return;
1562
1563 int mipCount = 1; // Required mipmap levels count (including base level)
1564 int mipWidth = image->width; // Base image width
1565 int mipHeight = image->height; // Base image height
1566 int mipSize = GetPixelDataSize(mipWidth, mipHeight, image->format); // Image data size (in bytes)
1567
1568 // Count mipmap levels required
1569 while ((mipWidth != 1) || (mipHeight != 1))
1570 {
1571 if (mipWidth != 1) mipWidth /= 2;
1572 if (mipHeight != 1) mipHeight /= 2;
1573
1574 // Security check for NPOT textures
1575 if (mipWidth < 1) mipWidth = 1;
1576 if (mipHeight < 1) mipHeight = 1;
1577
1578 TRACELOGD("IMAGE: Next mipmap level: %i x %i - current size %i", mipWidth, mipHeight, mipSize);
1579
1580 mipCount++;
1581 mipSize += GetPixelDataSize(mipWidth, mipHeight, image->format); // Add mipmap size (in bytes)
1582 }
1583
1584 if (image->mipmaps < mipCount)
1585 {
1586 void *temp = RL_REALLOC(image->data, mipSize);
1587
1588 if (temp != NULL) image->data = temp; // Assign new pointer (new size) to store mipmaps data
1589 else TRACELOG(LOG_WARNING, "IMAGE: Mipmaps required memory could not be allocated");
1590
1591 // Pointer to allocated memory point where store next mipmap level data
1592 unsigned char *nextmip = (unsigned char *)image->data + GetPixelDataSize(image->width, image->height, image->format);
1593
1594 mipWidth = image->width/2;
1595 mipHeight = image->height/2;
1596 mipSize = GetPixelDataSize(mipWidth, mipHeight, image->format);
1597 Image imCopy = ImageCopy(*image);
1598
1599 for (int i = 1; i < mipCount; i++)
1600 {
1601 TRACELOGD("IMAGE: Generating mipmap level: %i (%i x %i) - size: %i - offset: 0x%x", i, mipWidth, mipHeight, mipSize, nextmip);
1602
1603 ImageResize(&imCopy, mipWidth, mipHeight); // Uses internally Mitchell cubic downscale filter
1604
1605 memcpy(nextmip, imCopy.data, mipSize);
1606 nextmip += mipSize;
1607 image->mipmaps++;
1608
1609 mipWidth /= 2;
1610 mipHeight /= 2;
1611
1612 // Security check for NPOT textures
1613 if (mipWidth < 1) mipWidth = 1;
1614 if (mipHeight < 1) mipHeight = 1;
1615
1616 mipSize = GetPixelDataSize(mipWidth, mipHeight, image->format);
1617 }
1618
1619 UnloadImage(imCopy);
1620 }
1621 else TRACELOG(LOG_WARNING, "IMAGE: Mipmaps already available");
1622}
1623
1624// Dither image data to 16bpp or lower (Floyd-Steinberg dithering)
1625// NOTE: In case selected bpp do not represent an known 16bit format,
1626// dithered data is stored in the LSB part of the unsigned short
1627void ImageDither(Image *image, int rBpp, int gBpp, int bBpp, int aBpp)
1628{
1629 // Security check to avoid program crash
1630 if ((image->data == NULL) || (image->width == 0) || (image->height == 0)) return;
1631
1633 {
1634 TRACELOG(LOG_WARNING, "IMAGE: Compressed data formats can not be dithered");
1635 return;
1636 }
1637
1638 if ((rBpp + gBpp + bBpp + aBpp) > 16)
1639 {
1640 TRACELOG(LOG_WARNING, "IMAGE: Unsupported dithering bpps (%ibpp), only 16bpp or lower modes supported", (rBpp+gBpp+bBpp+aBpp));
1641 }
1642 else
1643 {
1644 Color *pixels = LoadImageColors(*image);
1645
1646 RL_FREE(image->data); // free old image data
1647
1649 {
1650 TRACELOG(LOG_WARNING, "IMAGE: Format is already 16bpp or lower, dithering could have no effect");
1651 }
1652
1653 // Define new image format, check if desired bpp match internal known format
1654 if ((rBpp == 5) && (gBpp == 6) && (bBpp == 5) && (aBpp == 0)) image->format = PIXELFORMAT_UNCOMPRESSED_R5G6B5;
1655 else if ((rBpp == 5) && (gBpp == 5) && (bBpp == 5) && (aBpp == 1)) image->format = PIXELFORMAT_UNCOMPRESSED_R5G5B5A1;
1656 else if ((rBpp == 4) && (gBpp == 4) && (bBpp == 4) && (aBpp == 4)) image->format = PIXELFORMAT_UNCOMPRESSED_R4G4B4A4;
1657 else
1658 {
1659 image->format = 0;
1660 TRACELOG(LOG_WARNING, "IMAGE: Unsupported dithered OpenGL internal format: %ibpp (R%iG%iB%iA%i)", (rBpp+gBpp+bBpp+aBpp), rBpp, gBpp, bBpp, aBpp);
1661 }
1662
1663 // NOTE: We will store the dithered data as unsigned short (16bpp)
1664 image->data = (unsigned short *)RL_MALLOC(image->width*image->height*sizeof(unsigned short));
1665
1666 Color oldPixel = WHITE;
1667 Color newPixel = WHITE;
1668
1669 int rError, gError, bError;
1670 unsigned short rPixel, gPixel, bPixel, aPixel; // Used for 16bit pixel composition
1671
1672 #define MIN(a,b) (((a)<(b))?(a):(b))
1673
1674 for (int y = 0; y < image->height; y++)
1675 {
1676 for (int x = 0; x < image->width; x++)
1677 {
1678 oldPixel = pixels[y*image->width + x];
1679
1680 // NOTE: New pixel obtained by bits truncate, it would be better to round values (check ImageFormat())
1681 newPixel.r = oldPixel.r >> (8 - rBpp); // R bits
1682 newPixel.g = oldPixel.g >> (8 - gBpp); // G bits
1683 newPixel.b = oldPixel.b >> (8 - bBpp); // B bits
1684 newPixel.a = oldPixel.a >> (8 - aBpp); // A bits (not used on dithering)
1685
1686 // NOTE: Error must be computed between new and old pixel but using same number of bits!
1687 // We want to know how much color precision we have lost...
1688 rError = (int)oldPixel.r - (int)(newPixel.r << (8 - rBpp));
1689 gError = (int)oldPixel.g - (int)(newPixel.g << (8 - gBpp));
1690 bError = (int)oldPixel.b - (int)(newPixel.b << (8 - bBpp));
1691
1692 pixels[y*image->width + x] = newPixel;
1693
1694 // NOTE: Some cases are out of the array and should be ignored
1695 if (x < (image->width - 1))
1696 {
1697 pixels[y*image->width + x+1].r = MIN((int)pixels[y*image->width + x+1].r + (int)((float)rError*7.0f/16), 0xff);
1698 pixels[y*image->width + x+1].g = MIN((int)pixels[y*image->width + x+1].g + (int)((float)gError*7.0f/16), 0xff);
1699 pixels[y*image->width + x+1].b = MIN((int)pixels[y*image->width + x+1].b + (int)((float)bError*7.0f/16), 0xff);
1700 }
1701
1702 if ((x > 0) && (y < (image->height - 1)))
1703 {
1704 pixels[(y+1)*image->width + x-1].r = MIN((int)pixels[(y+1)*image->width + x-1].r + (int)((float)rError*3.0f/16), 0xff);
1705 pixels[(y+1)*image->width + x-1].g = MIN((int)pixels[(y+1)*image->width + x-1].g + (int)((float)gError*3.0f/16), 0xff);
1706 pixels[(y+1)*image->width + x-1].b = MIN((int)pixels[(y+1)*image->width + x-1].b + (int)((float)bError*3.0f/16), 0xff);
1707 }
1708
1709 if (y < (image->height - 1))
1710 {
1711 pixels[(y+1)*image->width + x].r = MIN((int)pixels[(y+1)*image->width + x].r + (int)((float)rError*5.0f/16), 0xff);
1712 pixels[(y+1)*image->width + x].g = MIN((int)pixels[(y+1)*image->width + x].g + (int)((float)gError*5.0f/16), 0xff);
1713 pixels[(y+1)*image->width + x].b = MIN((int)pixels[(y+1)*image->width + x].b + (int)((float)bError*5.0f/16), 0xff);
1714 }
1715
1716 if ((x < (image->width - 1)) && (y < (image->height - 1)))
1717 {
1718 pixels[(y+1)*image->width + x+1].r = MIN((int)pixels[(y+1)*image->width + x+1].r + (int)((float)rError*1.0f/16), 0xff);
1719 pixels[(y+1)*image->width + x+1].g = MIN((int)pixels[(y+1)*image->width + x+1].g + (int)((float)gError*1.0f/16), 0xff);
1720 pixels[(y+1)*image->width + x+1].b = MIN((int)pixels[(y+1)*image->width + x+1].b + (int)((float)bError*1.0f/16), 0xff);
1721 }
1722
1723 rPixel = (unsigned short)newPixel.r;
1724 gPixel = (unsigned short)newPixel.g;
1725 bPixel = (unsigned short)newPixel.b;
1726 aPixel = (unsigned short)newPixel.a;
1727
1728 ((unsigned short *)image->data)[y*image->width + x] = (rPixel << (gBpp + bBpp + aBpp)) | (gPixel << (bBpp + aBpp)) | (bPixel << aBpp) | aPixel;
1729 }
1730 }
1731
1732 UnloadImageColors(pixels);
1733 }
1734}
1735
1736// Flip image vertically
1738{
1739 // Security check to avoid program crash
1740 if ((image->data == NULL) || (image->width == 0) || (image->height == 0)) return;
1741
1742 if (image->mipmaps > 1) TRACELOG(LOG_WARNING, "Image manipulation only applied to base mipmap level");
1743 if (image->format >= PIXELFORMAT_COMPRESSED_DXT1_RGB) TRACELOG(LOG_WARNING, "Image manipulation not supported for compressed formats");
1744 else
1745 {
1746 int bytesPerPixel = GetPixelDataSize(1, 1, image->format);
1747 unsigned char *flippedData = (unsigned char *)RL_MALLOC(image->width*image->height*bytesPerPixel);
1748
1749 for (int i = (image->height - 1), offsetSize = 0; i >= 0; i--)
1750 {
1751 memcpy(flippedData + offsetSize, ((unsigned char *)image->data) + i*image->width*bytesPerPixel, image->width*bytesPerPixel);
1752 offsetSize += image->width*bytesPerPixel;
1753 }
1754
1755 RL_FREE(image->data);
1756 image->data = flippedData;
1757 }
1758}
1759
1760// Flip image horizontally
1762{
1763 // Security check to avoid program crash
1764 if ((image->data == NULL) || (image->width == 0) || (image->height == 0)) return;
1765
1766 if (image->mipmaps > 1) TRACELOG(LOG_WARNING, "Image manipulation only applied to base mipmap level");
1767 if (image->format >= PIXELFORMAT_COMPRESSED_DXT1_RGB) TRACELOG(LOG_WARNING, "Image manipulation not supported for compressed formats");
1768 else
1769 {
1770 int bytesPerPixel = GetPixelDataSize(1, 1, image->format);
1771 unsigned char *flippedData = (unsigned char *)RL_MALLOC(image->width*image->height*bytesPerPixel);
1772
1773 for (int y = 0; y < image->height; y++)
1774 {
1775 for (int x = 0; x < image->width; x++)
1776 {
1777 // OPTION 1: Move pixels with memcopy()
1778 //memcpy(flippedData + (y*image->width + x)*bytesPerPixel, ((unsigned char *)image->data) + (y*image->width + (image->width - 1 - x))*bytesPerPixel, bytesPerPixel);
1779
1780 // OPTION 2: Just copy data pixel by pixel
1781 for (int i = 0; i < bytesPerPixel; i++) flippedData[(y*image->width + x)*bytesPerPixel + i] = ((unsigned char *)image->data)[(y*image->width + (image->width - 1 - x))*bytesPerPixel + i];
1782 }
1783 }
1784
1785 RL_FREE(image->data);
1786 image->data = flippedData;
1787
1788 /*
1789 // OPTION 3: Faster implementation (specific for 32bit pixels)
1790 // NOTE: It does not require additional allocations
1791 uint32_t *ptr = (uint32_t *)image->data;
1792 for (int y = 0; y < image->height; y++)
1793 {
1794 for (int x = 0; x < image->width/2; x++)
1795 {
1796 uint32_t backup = ptr[y*image->width + x];
1797 ptr[y*image->width + x] = ptr[y*image->width + (image->width - 1 - x)];
1798 ptr[y*image->width + (image->width - 1 - x)] = backup;
1799 }
1800 }
1801 */
1802 }
1803}
1804
1805// Rotate image clockwise 90deg
1807{
1808 // Security check to avoid program crash
1809 if ((image->data == NULL) || (image->width == 0) || (image->height == 0)) return;
1810
1811 if (image->mipmaps > 1) TRACELOG(LOG_WARNING, "Image manipulation only applied to base mipmap level");
1812 if (image->format >= PIXELFORMAT_COMPRESSED_DXT1_RGB) TRACELOG(LOG_WARNING, "Image manipulation not supported for compressed formats");
1813 else
1814 {
1815 int bytesPerPixel = GetPixelDataSize(1, 1, image->format);
1816 unsigned char *rotatedData = (unsigned char *)RL_MALLOC(image->width*image->height*bytesPerPixel);
1817
1818 for (int y = 0; y < image->height; y++)
1819 {
1820 for (int x = 0; x < image->width; x++)
1821 {
1822 //memcpy(rotatedData + (x*image->height + (image->height - y - 1))*bytesPerPixel, ((unsigned char *)image->data) + (y*image->width + x)*bytesPerPixel, bytesPerPixel);
1823 for (int i = 0; i < bytesPerPixel; i++) rotatedData[(x*image->height + (image->height - y - 1))*bytesPerPixel + i] = ((unsigned char *)image->data)[(y*image->width + x)*bytesPerPixel + i];
1824 }
1825 }
1826
1827 RL_FREE(image->data);
1828 image->data = rotatedData;
1829 int width = image->width;
1830 int height = image-> height;
1831
1832 image->width = height;
1833 image->height = width;
1834 }
1835}
1836
1837// Rotate image counter-clockwise 90deg
1839{
1840 // Security check to avoid program crash
1841 if ((image->data == NULL) || (image->width == 0) || (image->height == 0)) return;
1842
1843 if (image->mipmaps > 1) TRACELOG(LOG_WARNING, "Image manipulation only applied to base mipmap level");
1844 if (image->format >= PIXELFORMAT_COMPRESSED_DXT1_RGB) TRACELOG(LOG_WARNING, "Image manipulation not supported for compressed formats");
1845 else
1846 {
1847 int bytesPerPixel = GetPixelDataSize(1, 1, image->format);
1848 unsigned char *rotatedData = (unsigned char *)RL_MALLOC(image->width*image->height*bytesPerPixel);
1849
1850 for (int y = 0; y < image->height; y++)
1851 {
1852 for (int x = 0; x < image->width; x++)
1853 {
1854 //memcpy(rotatedData + (x*image->height + y))*bytesPerPixel, ((unsigned char *)image->data) + (y*image->width + (image->width - x - 1))*bytesPerPixel, bytesPerPixel);
1855 for (int i = 0; i < bytesPerPixel; i++) rotatedData[(x*image->height + y)*bytesPerPixel + i] = ((unsigned char *)image->data)[(y*image->width + (image->width - x - 1))*bytesPerPixel + i];
1856 }
1857 }
1858
1859 RL_FREE(image->data);
1860 image->data = rotatedData;
1861 int width = image->width;
1862 int height = image-> height;
1863
1864 image->width = height;
1865 image->height = width;
1866 }
1867}
1868
1869// Modify image color: tint
1870void ImageColorTint(Image *image, Color color)
1871{
1872 // Security check to avoid program crash
1873 if ((image->data == NULL) || (image->width == 0) || (image->height == 0)) return;
1874
1875 Color *pixels = LoadImageColors(*image);
1876
1877 float cR = (float)color.r/255;
1878 float cG = (float)color.g/255;
1879 float cB = (float)color.b/255;
1880 float cA = (float)color.a/255;
1881
1882 for (int y = 0; y < image->height; y++)
1883 {
1884 for (int x = 0; x < image->width; x++)
1885 {
1886 int index = y*image->width + x;
1887 unsigned char r = (unsigned char)(((float)pixels[index].r/255*cR)*255.0f);
1888 unsigned char g = (unsigned char)(((float)pixels[index].g/255*cG)*255.0f);
1889 unsigned char b = (unsigned char)(((float)pixels[index].b/255*cB)*255.0f);
1890 unsigned char a = (unsigned char)(((float)pixels[index].a/255*cA)*255.0f);
1891
1892 pixels[index].r = r;
1893 pixels[index].g = g;
1894 pixels[index].b = b;
1895 pixels[index].a = a;
1896 }
1897 }
1898
1899 int format = image->format;
1900 RL_FREE(image->data);
1901
1902 image->data = pixels;
1904
1905 ImageFormat(image, format);
1906}
1907
1908// Modify image color: invert
1910{
1911 // Security check to avoid program crash
1912 if ((image->data == NULL) || (image->width == 0) || (image->height == 0)) return;
1913
1914 Color *pixels = LoadImageColors(*image);
1915
1916 for (int y = 0; y < image->height; y++)
1917 {
1918 for (int x = 0; x < image->width; x++)
1919 {
1920 pixels[y*image->width + x].r = 255 - pixels[y*image->width + x].r;
1921 pixels[y*image->width + x].g = 255 - pixels[y*image->width + x].g;
1922 pixels[y*image->width + x].b = 255 - pixels[y*image->width + x].b;
1923 }
1924 }
1925
1926 int format = image->format;
1927 RL_FREE(image->data);
1928
1929 image->data = pixels;
1931
1932 ImageFormat(image, format);
1933}
1934
1935// Modify image color: grayscale
1937{
1939}
1940
1941// Modify image color: contrast
1942// NOTE: Contrast values between -100 and 100
1943void ImageColorContrast(Image *image, float contrast)
1944{
1945 // Security check to avoid program crash
1946 if ((image->data == NULL) || (image->width == 0) || (image->height == 0)) return;
1947
1948 if (contrast < -100) contrast = -100;
1949 if (contrast > 100) contrast = 100;
1950
1951 contrast = (100.0f + contrast)/100.0f;
1952 contrast *= contrast;
1953
1954 Color *pixels = LoadImageColors(*image);
1955
1956 for (int y = 0; y < image->height; y++)
1957 {
1958 for (int x = 0; x < image->width; x++)
1959 {
1960 float pR = (float)pixels[y*image->width + x].r/255.0f;
1961 pR -= 0.5f;
1962 pR *= contrast;
1963 pR += 0.5f;
1964 pR *= 255;
1965 if (pR < 0) pR = 0;
1966 if (pR > 255) pR = 255;
1967
1968 float pG = (float)pixels[y*image->width + x].g/255.0f;
1969 pG -= 0.5f;
1970 pG *= contrast;
1971 pG += 0.5f;
1972 pG *= 255;
1973 if (pG < 0) pG = 0;
1974 if (pG > 255) pG = 255;
1975
1976 float pB = (float)pixels[y*image->width + x].b/255.0f;
1977 pB -= 0.5f;
1978 pB *= contrast;
1979 pB += 0.5f;
1980 pB *= 255;
1981 if (pB < 0) pB = 0;
1982 if (pB > 255) pB = 255;
1983
1984 pixels[y*image->width + x].r = (unsigned char)pR;
1985 pixels[y*image->width + x].g = (unsigned char)pG;
1986 pixels[y*image->width + x].b = (unsigned char)pB;
1987 }
1988 }
1989
1990 int format = image->format;
1991 RL_FREE(image->data);
1992
1993 image->data = pixels;
1995
1996 ImageFormat(image, format);
1997}
1998
1999// Modify image color: brightness
2000// NOTE: Brightness values between -255 and 255
2001void ImageColorBrightness(Image *image, int brightness)
2002{
2003 // Security check to avoid program crash
2004 if ((image->data == NULL) || (image->width == 0) || (image->height == 0)) return;
2005
2006 if (brightness < -255) brightness = -255;
2007 if (brightness > 255) brightness = 255;
2008
2009 Color *pixels = LoadImageColors(*image);
2010
2011 for (int y = 0; y < image->height; y++)
2012 {
2013 for (int x = 0; x < image->width; x++)
2014 {
2015 int cR = pixels[y*image->width + x].r + brightness;
2016 int cG = pixels[y*image->width + x].g + brightness;
2017 int cB = pixels[y*image->width + x].b + brightness;
2018
2019 if (cR < 0) cR = 1;
2020 if (cR > 255) cR = 255;
2021
2022 if (cG < 0) cG = 1;
2023 if (cG > 255) cG = 255;
2024
2025 if (cB < 0) cB = 1;
2026 if (cB > 255) cB = 255;
2027
2028 pixels[y*image->width + x].r = (unsigned char)cR;
2029 pixels[y*image->width + x].g = (unsigned char)cG;
2030 pixels[y*image->width + x].b = (unsigned char)cB;
2031 }
2032 }
2033
2034 int format = image->format;
2035 RL_FREE(image->data);
2036
2037 image->data = pixels;
2039
2040 ImageFormat(image, format);
2041}
2042
2043// Modify image color: replace color
2044void ImageColorReplace(Image *image, Color color, Color replace)
2045{
2046 // Security check to avoid program crash
2047 if ((image->data == NULL) || (image->width == 0) || (image->height == 0)) return;
2048
2049 Color *pixels = LoadImageColors(*image);
2050
2051 for (int y = 0; y < image->height; y++)
2052 {
2053 for (int x = 0; x < image->width; x++)
2054 {
2055 if ((pixels[y*image->width + x].r == color.r) &&
2056 (pixels[y*image->width + x].g == color.g) &&
2057 (pixels[y*image->width + x].b == color.b) &&
2058 (pixels[y*image->width + x].a == color.a))
2059 {
2060 pixels[y*image->width + x].r = replace.r;
2061 pixels[y*image->width + x].g = replace.g;
2062 pixels[y*image->width + x].b = replace.b;
2063 pixels[y*image->width + x].a = replace.a;
2064 }
2065 }
2066 }
2067
2068 int format = image->format;
2069 RL_FREE(image->data);
2070
2071 image->data = pixels;
2073
2074 ImageFormat(image, format);
2075}
2076#endif // SUPPORT_IMAGE_MANIPULATION
2077
2078// Load color data from image as a Color array (RGBA - 32bit)
2079// NOTE: Memory allocated should be freed using UnloadImageColors();
2081{
2082 if ((image.width == 0) || (image.height == 0)) return NULL;
2083
2084 Color *pixels = (Color *)RL_MALLOC(image.width*image.height*sizeof(Color));
2085
2086 if (image.format >= PIXELFORMAT_COMPRESSED_DXT1_RGB) TRACELOG(LOG_WARNING, "IMAGE: Pixel data retrieval not supported for compressed image formats");
2087 else
2088 {
2089 if ((image.format == PIXELFORMAT_UNCOMPRESSED_R32) ||
2091 (image.format == PIXELFORMAT_UNCOMPRESSED_R32G32B32A32)) TRACELOG(LOG_WARNING, "IMAGE: Pixel format converted from 32bit to 8bit per channel");
2092
2093 for (int i = 0, k = 0; i < image.width*image.height; i++)
2094 {
2095 switch (image.format)
2096 {
2098 {
2099 pixels[i].r = ((unsigned char *)image.data)[i];
2100 pixels[i].g = ((unsigned char *)image.data)[i];
2101 pixels[i].b = ((unsigned char *)image.data)[i];
2102 pixels[i].a = 255;
2103
2104 } break;
2106 {
2107 pixels[i].r = ((unsigned char *)image.data)[k];
2108 pixels[i].g = ((unsigned char *)image.data)[k];
2109 pixels[i].b = ((unsigned char *)image.data)[k];
2110 pixels[i].a = ((unsigned char *)image.data)[k + 1];
2111
2112 k += 2;
2113 } break;
2115 {
2116 unsigned short pixel = ((unsigned short *)image.data)[i];
2117
2118 pixels[i].r = (unsigned char)((float)((pixel & 0b1111100000000000) >> 11)*(255/31));
2119 pixels[i].g = (unsigned char)((float)((pixel & 0b0000011111000000) >> 6)*(255/31));
2120 pixels[i].b = (unsigned char)((float)((pixel & 0b0000000000111110) >> 1)*(255/31));
2121 pixels[i].a = (unsigned char)((pixel & 0b0000000000000001)*255);
2122
2123 } break;
2125 {
2126 unsigned short pixel = ((unsigned short *)image.data)[i];
2127
2128 pixels[i].r = (unsigned char)((float)((pixel & 0b1111100000000000) >> 11)*(255/31));
2129 pixels[i].g = (unsigned char)((float)((pixel & 0b0000011111100000) >> 5)*(255/63));
2130 pixels[i].b = (unsigned char)((float)(pixel & 0b0000000000011111)*(255/31));
2131 pixels[i].a = 255;
2132
2133 } break;
2135 {
2136 unsigned short pixel = ((unsigned short *)image.data)[i];
2137
2138 pixels[i].r = (unsigned char)((float)((pixel & 0b1111000000000000) >> 12)*(255/15));
2139 pixels[i].g = (unsigned char)((float)((pixel & 0b0000111100000000) >> 8)*(255/15));
2140 pixels[i].b = (unsigned char)((float)((pixel & 0b0000000011110000) >> 4)*(255/15));
2141 pixels[i].a = (unsigned char)((float)(pixel & 0b0000000000001111)*(255/15));
2142
2143 } break;
2145 {
2146 pixels[i].r = ((unsigned char *)image.data)[k];
2147 pixels[i].g = ((unsigned char *)image.data)[k + 1];
2148 pixels[i].b = ((unsigned char *)image.data)[k + 2];
2149 pixels[i].a = ((unsigned char *)image.data)[k + 3];
2150
2151 k += 4;
2152 } break;
2154 {
2155 pixels[i].r = (unsigned char)((unsigned char *)image.data)[k];
2156 pixels[i].g = (unsigned char)((unsigned char *)image.data)[k + 1];
2157 pixels[i].b = (unsigned char)((unsigned char *)image.data)[k + 2];
2158 pixels[i].a = 255;
2159
2160 k += 3;
2161 } break;
2163 {
2164 pixels[i].r = (unsigned char)(((float *)image.data)[k]*255.0f);
2165 pixels[i].g = 0;
2166 pixels[i].b = 0;
2167 pixels[i].a = 255;
2168
2169 } break;
2171 {
2172 pixels[i].r = (unsigned char)(((float *)image.data)[k]*255.0f);
2173 pixels[i].g = (unsigned char)(((float *)image.data)[k + 1]*255.0f);
2174 pixels[i].b = (unsigned char)(((float *)image.data)[k + 2]*255.0f);
2175 pixels[i].a = 255;
2176
2177 k += 3;
2178 } break;
2180 {
2181 pixels[i].r = (unsigned char)(((float *)image.data)[k]*255.0f);
2182 pixels[i].g = (unsigned char)(((float *)image.data)[k]*255.0f);
2183 pixels[i].b = (unsigned char)(((float *)image.data)[k]*255.0f);
2184 pixels[i].a = (unsigned char)(((float *)image.data)[k]*255.0f);
2185
2186 k += 4;
2187 } break;
2188 default: break;
2189 }
2190 }
2191 }
2192
2193 return pixels;
2194}
2195
2196// Load colors palette from image as a Color array (RGBA - 32bit)
2197// NOTE: Memory allocated should be freed using UnloadImagePalette()
2198Color *LoadImagePalette(Image image, int maxPaletteSize, int *colorCount)
2199{
2200 #define COLOR_EQUAL(col1, col2) ((col1.r == col2.r)&&(col1.g == col2.g)&&(col1.b == col2.b)&&(col1.a == col2.a))
2201
2202 int palCount = 0;
2203 Color *palette = NULL;
2204 Color *pixels = LoadImageColors(image);
2205
2206 if (pixels != NULL)
2207 {
2208 palette = (Color *)RL_MALLOC(maxPaletteSize*sizeof(Color));
2209
2210 for (int i = 0; i < maxPaletteSize; i++) palette[i] = BLANK; // Set all colors to BLANK
2211
2212 for (int i = 0; i < image.width*image.height; i++)
2213 {
2214 if (pixels[i].a > 0)
2215 {
2216 bool colorInPalette = false;
2217
2218 // Check if the color is already on palette
2219 for (int j = 0; j < maxPaletteSize; j++)
2220 {
2221 if (COLOR_EQUAL(pixels[i], palette[j]))
2222 {
2223 colorInPalette = true;
2224 break;
2225 }
2226 }
2227
2228 // Store color if not on the palette
2229 if (!colorInPalette)
2230 {
2231 palette[palCount] = pixels[i]; // Add pixels[i] to palette
2232 palCount++;
2233
2234 // We reached the limit of colors supported by palette
2235 if (palCount >= maxPaletteSize)
2236 {
2237 i = image.width*image.height; // Finish palette get
2238 TRACELOG(LOG_WARNING, "IMAGE: Palette is greater than %i colors", maxPaletteSize);
2239 }
2240 }
2241 }
2242 }
2243
2244 UnloadImageColors(pixels);
2245 }
2246
2247 *colorCount = palCount;
2248
2249 return palette;
2250}
2251
2252// Unload color data loaded with LoadImageColors()
2254{
2255 RL_FREE(colors);
2256}
2257
2258// Unload colors palette loaded with LoadImagePalette()
2260{
2261 RL_FREE(colors);
2262}
2263
2264// Get image alpha border rectangle
2265// NOTE: Threshold is defined as a percentatge: 0.0f -> 1.0f
2266Rectangle GetImageAlphaBorder(Image image, float threshold)
2267{
2268 Rectangle crop = { 0 };
2269
2270 Color *pixels = LoadImageColors(image);
2271
2272 if (pixels != NULL)
2273 {
2274 int xMin = 65536; // Define a big enough number
2275 int xMax = 0;
2276 int yMin = 65536;
2277 int yMax = 0;
2278
2279 for (int y = 0; y < image.height; y++)
2280 {
2281 for (int x = 0; x < image.width; x++)
2282 {
2283 if (pixels[y*image.width + x].a > (unsigned char)(threshold*255.0f))
2284 {
2285 if (x < xMin) xMin = x;
2286 if (x > xMax) xMax = x;
2287 if (y < yMin) yMin = y;
2288 if (y > yMax) yMax = y;
2289 }
2290 }
2291 }
2292
2293 // Check for empty blank image
2294 if ((xMin != 65536) && (xMax != 65536))
2295 {
2296 crop = (Rectangle){ (float)xMin, (float)yMin, (float)((xMax + 1) - xMin), (float)((yMax + 1) - yMin) };
2297 }
2298
2299 UnloadImageColors(pixels);
2300 }
2301
2302 return crop;
2303}
2304
2305// Get image pixel color at (x, y) position
2306Color GetImageColor(Image image, int x, int y)
2307{
2308 Color color = { 0 };
2309
2310 if ((x >=0) && (x < image.width) && (y >= 0) && (y < image.height))
2311 {
2312 switch (image.format)
2313 {
2315 {
2316 color.r = ((unsigned char *)image.data)[y*image.width + x];
2317 color.g = ((unsigned char *)image.data)[y*image.width + x];
2318 color.b = ((unsigned char *)image.data)[y*image.width + x];
2319 color.a = 255;
2320
2321 } break;
2323 {
2324 color.r = ((unsigned char *)image.data)[(y*image.width + x)*2];
2325 color.g = ((unsigned char *)image.data)[(y*image.width + x)*2];
2326 color.b = ((unsigned char *)image.data)[(y*image.width + x)*2];
2327 color.a = ((unsigned char *)image.data)[(y*image.width + x)*2 + 1];
2328
2329 } break;
2331 {
2332 unsigned short pixel = ((unsigned short *)image.data)[y*image.width + x];
2333
2334 color.r = (unsigned char)((float)((pixel & 0b1111100000000000) >> 11)*(255/31));
2335 color.g = (unsigned char)((float)((pixel & 0b0000011111000000) >> 6)*(255/31));
2336 color.b = (unsigned char)((float)((pixel & 0b0000000000111110) >> 1)*(255/31));
2337 color.a = (unsigned char)((pixel & 0b0000000000000001)*255);
2338
2339 } break;
2341 {
2342 unsigned short pixel = ((unsigned short *)image.data)[y*image.width + x];
2343
2344 color.r = (unsigned char)((float)((pixel & 0b1111100000000000) >> 11)*(255/31));
2345 color.g = (unsigned char)((float)((pixel & 0b0000011111100000) >> 5)*(255/63));
2346 color.b = (unsigned char)((float)(pixel & 0b0000000000011111)*(255/31));
2347 color.a = 255;
2348
2349 } break;
2351 {
2352 unsigned short pixel = ((unsigned short *)image.data)[y*image.width + x];
2353
2354 color.r = (unsigned char)((float)((pixel & 0b1111000000000000) >> 12)*(255/15));
2355 color.g = (unsigned char)((float)((pixel & 0b0000111100000000) >> 8)*(255/15));
2356 color.b = (unsigned char)((float)((pixel & 0b0000000011110000) >> 4)*(255/15));
2357 color.a = (unsigned char)((float)(pixel & 0b0000000000001111)*(255/15));
2358
2359 } break;
2361 {
2362 color.r = ((unsigned char *)image.data)[(y*image.width + x)*4];
2363 color.g = ((unsigned char *)image.data)[(y*image.width + x)*4 + 1];
2364 color.b = ((unsigned char *)image.data)[(y*image.width + x)*4 + 2];
2365 color.a = ((unsigned char *)image.data)[(y*image.width + x)*4 + 3];
2366
2367 } break;
2369 {
2370 color.r = (unsigned char)((unsigned char *)image.data)[(y*image.width + x)*3];
2371 color.g = (unsigned char)((unsigned char *)image.data)[(y*image.width + x)*3 + 1];
2372 color.b = (unsigned char)((unsigned char *)image.data)[(y*image.width + x)*3 + 2];
2373 color.a = 255;
2374
2375 } break;
2377 {
2378 color.r = (unsigned char)(((float *)image.data)[y*image.width + x]*255.0f);
2379 color.g = 0;
2380 color.b = 0;
2381 color.a = 255;
2382
2383 } break;
2385 {
2386 color.r = (unsigned char)(((float *)image.data)[(y*image.width + x)*3]*255.0f);
2387 color.g = (unsigned char)(((float *)image.data)[(y*image.width + x)*3 + 1]*255.0f);
2388 color.b = (unsigned char)(((float *)image.data)[(y*image.width + x)*3 + 2]*255.0f);
2389 color.a = 255;
2390
2391 } break;
2393 {
2394 color.r = (unsigned char)(((float *)image.data)[(y*image.width + x)*4]*255.0f);
2395 color.g = (unsigned char)(((float *)image.data)[(y*image.width + x)*4]*255.0f);
2396 color.b = (unsigned char)(((float *)image.data)[(y*image.width + x)*4]*255.0f);
2397 color.a = (unsigned char)(((float *)image.data)[(y*image.width + x)*4]*255.0f);
2398
2399 } break;
2400 default: TRACELOG(LOG_WARNING, "Compressed image format does not support color reading"); break;
2401 }
2402 }
2403 else TRACELOG(LOG_WARNING, "Requested image pixel (%i, %i) out of bounds", x, y);
2404
2405 return color;
2406}
2407
2408//------------------------------------------------------------------------------------
2409// Image drawing functions
2410//------------------------------------------------------------------------------------
2411// Clear image background with given color
2413{
2414 // Security check to avoid program crash
2415 if ((dst->data == NULL) || (dst->width == 0) || (dst->height == 0)) return;
2416
2417 // Fill in first pixel based on image format
2418 ImageDrawPixel(dst, 0, 0, color);
2419
2420 unsigned char *pSrcPixel = (unsigned char *)dst->data;
2421 int bytesPerPixel = GetPixelDataSize(1, 1, dst->format);
2422
2423 // Repeat the first pixel data throughout the image
2424 for (int i = 1; i < dst->width * dst->height; i++)
2425 {
2426 memcpy(pSrcPixel + i * bytesPerPixel, pSrcPixel, bytesPerPixel);
2427 }
2428}
2429
2430// Draw pixel within an image
2431// NOTE: Compressed image formats not supported
2432void ImageDrawPixel(Image *dst, int x, int y, Color color)
2433{
2434 // Security check to avoid program crash
2435 if ((dst->data == NULL) || (x < 0) || (x >= dst->width) || (y < 0) || (y >= dst->height)) return;
2436
2437 switch (dst->format)
2438 {
2440 {
2441 // NOTE: Calculate grayscale equivalent color
2442 Vector3 coln = { (float)color.r/255.0f, (float)color.g/255.0f, (float)color.b/255.0f };
2443 unsigned char gray = (unsigned char)((coln.x*0.299f + coln.y*0.587f + coln.z*0.114f)*255.0f);
2444
2445 ((unsigned char *)dst->data)[y*dst->width + x] = gray;
2446
2447 } break;
2449 {
2450 // NOTE: Calculate grayscale equivalent color
2451 Vector3 coln = { (float)color.r/255.0f, (float)color.g/255.0f, (float)color.b/255.0f };
2452 unsigned char gray = (unsigned char)((coln.x*0.299f + coln.y*0.587f + coln.z*0.114f)*255.0f);
2453
2454 ((unsigned char *)dst->data)[(y*dst->width + x)*2] = gray;
2455 ((unsigned char *)dst->data)[(y*dst->width + x)*2 + 1] = color.a;
2456
2457 } break;
2459 {
2460 // NOTE: Calculate R5G6B5 equivalent color
2461 Vector3 coln = { (float)color.r/255.0f, (float)color.g/255.0f, (float)color.b/255.0f };
2462
2463 unsigned char r = (unsigned char)(round(coln.x*31.0f));
2464 unsigned char g = (unsigned char)(round(coln.y*63.0f));
2465 unsigned char b = (unsigned char)(round(coln.z*31.0f));
2466
2467 ((unsigned short *)dst->data)[y*dst->width + x] = (unsigned short)r << 11 | (unsigned short)g << 5 | (unsigned short)b;
2468
2469 } break;
2471 {
2472 // NOTE: Calculate R5G5B5A1 equivalent color
2473 Vector4 coln = { (float)color.r/255.0f, (float)color.g/255.0f, (float)color.b/255.0f, (float)color.a/255.0f };
2474
2475 unsigned char r = (unsigned char)(round(coln.x*31.0f));
2476 unsigned char g = (unsigned char)(round(coln.y*31.0f));
2477 unsigned char b = (unsigned char)(round(coln.z*31.0f));
2478 unsigned char a = (coln.w > ((float)PIXELFORMAT_UNCOMPRESSED_R5G5B5A1_ALPHA_THRESHOLD/255.0f))? 1 : 0;
2479
2480 ((unsigned short *)dst->data)[y*dst->width + x] = (unsigned short)r << 11 | (unsigned short)g << 6 | (unsigned short)b << 1 | (unsigned short)a;
2481
2482 } break;
2484 {
2485 // NOTE: Calculate R5G5B5A1 equivalent color
2486 Vector4 coln = { (float)color.r/255.0f, (float)color.g/255.0f, (float)color.b/255.0f, (float)color.a/255.0f };
2487
2488 unsigned char r = (unsigned char)(round(coln.x*15.0f));
2489 unsigned char g = (unsigned char)(round(coln.y*15.0f));
2490 unsigned char b = (unsigned char)(round(coln.z*15.0f));
2491 unsigned char a = (unsigned char)(round(coln.w*15.0f));
2492
2493 ((unsigned short *)dst->data)[y*dst->width + x] = (unsigned short)r << 12 | (unsigned short)g << 8 | (unsigned short)b << 4 | (unsigned short)a;
2494
2495 } break;
2497 {
2498 ((unsigned char *)dst->data)[(y*dst->width + x)*3] = color.r;
2499 ((unsigned char *)dst->data)[(y*dst->width + x)*3 + 1] = color.g;
2500 ((unsigned char *)dst->data)[(y*dst->width + x)*3 + 2] = color.b;
2501
2502 } break;
2504 {
2505 ((unsigned char *)dst->data)[(y*dst->width + x)*4] = color.r;
2506 ((unsigned char *)dst->data)[(y*dst->width + x)*4 + 1] = color.g;
2507 ((unsigned char *)dst->data)[(y*dst->width + x)*4 + 2] = color.b;
2508 ((unsigned char *)dst->data)[(y*dst->width + x)*4 + 3] = color.a;
2509
2510 } break;
2512 {
2513 // NOTE: Calculate grayscale equivalent color (normalized to 32bit)
2514 Vector3 coln = { (float)color.r/255.0f, (float)color.g/255.0f, (float)color.b/255.0f };
2515
2516 ((float *)dst->data)[y*dst->width + x] = coln.x*0.299f + coln.y*0.587f + coln.z*0.114f;
2517
2518 } break;
2520 {
2521 // NOTE: Calculate R32G32B32 equivalent color (normalized to 32bit)
2522 Vector3 coln = { (float)color.r/255.0f, (float)color.g/255.0f, (float)color.b/255.0f };
2523
2524 ((float *)dst->data)[(y*dst->width + x)*3] = coln.x;
2525 ((float *)dst->data)[(y*dst->width + x)*3 + 1] = coln.y;
2526 ((float *)dst->data)[(y*dst->width + x)*3 + 2] = coln.z;
2527 } break;
2529 {
2530 // NOTE: Calculate R32G32B32A32 equivalent color (normalized to 32bit)
2531 Vector4 coln = { (float)color.r/255.0f, (float)color.g/255.0f, (float)color.b/255.0f, (float)color.a/255.0f };
2532
2533 ((float *)dst->data)[(y*dst->width + x)*4] = coln.x;
2534 ((float *)dst->data)[(y*dst->width + x)*4 + 1] = coln.y;
2535 ((float *)dst->data)[(y*dst->width + x)*4 + 2] = coln.z;
2536 ((float *)dst->data)[(y*dst->width + x)*4 + 3] = coln.w;
2537
2538 } break;
2539 default: break;
2540 }
2541}
2542
2543// Draw pixel within an image (Vector version)
2544void ImageDrawPixelV(Image *dst, Vector2 position, Color color)
2545{
2546 ImageDrawPixel(dst, (int)position.x, (int)position.y, color);
2547}
2548
2549// Draw line within an image
2550void ImageDrawLine(Image *dst, int startPosX, int startPosY, int endPosX, int endPosY, Color color)
2551{
2552 // Using Bresenham's algorithm as described in
2553 // Drawing Lines with Pixels - Joshua Scott - March 2012
2554 // https://classic.csunplugged.org/wp-content/uploads/2014/12/Lines.pdf
2555
2556 int changeInX = (endPosX - startPosX);
2557 int absChangeInX = (changeInX < 0)? -changeInX : changeInX;
2558 int changeInY = (endPosY - startPosY);
2559 int absChangeInY = (changeInY < 0)? -changeInY : changeInY;
2560
2561 int startU, startV, endU, stepV; // Substitutions, either U = X, V = Y or vice versa. See loop at end of function
2562 //int endV; // Not needed but left for better understanding, check code below
2563 int A, B, P; // See linked paper above, explained down in the main loop
2564 int reversedXY = (absChangeInY < absChangeInX);
2565
2566 if (reversedXY)
2567 {
2568 A = 2*absChangeInY;
2569 B = A - 2*absChangeInX;
2570 P = A - absChangeInX;
2571
2572 if (changeInX > 0)
2573 {
2574 startU = startPosX;
2575 startV = startPosY;
2576 endU = endPosX;
2577 //endV = endPosY;
2578 }
2579 else
2580 {
2581 startU = endPosX;
2582 startV = endPosY;
2583 endU = startPosX;
2584 //endV = startPosY;
2585
2586 // Since start and end are reversed
2587 changeInX = -changeInX;
2588 changeInY = -changeInY;
2589 }
2590
2591 stepV = (changeInY < 0)? -1 : 1;
2592
2593 ImageDrawPixel(dst, startU, startV, color); // At this point they are correctly ordered...
2594 }
2595 else
2596 {
2597 A = 2*absChangeInX;
2598 B = A - 2*absChangeInY;
2599 P = A - absChangeInY;
2600
2601 if (changeInY > 0)
2602 {
2603 startU = startPosY;
2604 startV = startPosX;
2605 endU = endPosY;
2606 //endV = endPosX;
2607 }
2608 else
2609 {
2610 startU = endPosY;
2611 startV = endPosX;
2612 endU = startPosY;
2613 //endV = startPosX;
2614
2615 // Since start and end are reversed
2616 changeInX = -changeInX;
2617 changeInY = -changeInY;
2618 }
2619
2620 stepV = (changeInX < 0)? -1 : 1;
2621
2622 ImageDrawPixel(dst, startV, startU, color); // ... but need to be reversed here. Repeated in the main loop below
2623 }
2624
2625 // We already drew the start point. If we started at startU + 0, the line would be crooked and too short
2626 for (int u = startU + 1, v = startV; u <= endU; u++)
2627 {
2628 if (P >= 0)
2629 {
2630 v += stepV; // Adjusts whenever we stray too far from the direct line. Details in the linked paper above
2631 P += B; // Remembers that we corrected our path
2632 }
2633 else P += A; // Remembers how far we are from the direct line
2634
2635 if (reversedXY) ImageDrawPixel(dst, u, v, color);
2636 else ImageDrawPixel(dst, v, u, color);
2637 }
2638}
2639
2640// Draw line within an image (Vector version)
2641void ImageDrawLineV(Image *dst, Vector2 start, Vector2 end, Color color)
2642{
2643 ImageDrawLine(dst, (int)start.x, (int)start.y, (int)end.x, (int)end.y, color);
2644}
2645
2646// Draw circle within an image
2647void ImageDrawCircle(Image *dst, int centerX, int centerY, int radius, Color color)
2648{
2649 int x = 0, y = radius;
2650 int decesionParameter = 3 - 2*radius;
2651
2652 while (y >= x)
2653 {
2654 ImageDrawPixel(dst, centerX + x, centerY + y, color);
2655 ImageDrawPixel(dst, centerX - x, centerY + y, color);
2656 ImageDrawPixel(dst, centerX + x, centerY - y, color);
2657 ImageDrawPixel(dst, centerX - x, centerY - y, color);
2658 ImageDrawPixel(dst, centerX + y, centerY + x, color);
2659 ImageDrawPixel(dst, centerX - y, centerY + x, color);
2660 ImageDrawPixel(dst, centerX + y, centerY - x, color);
2661 ImageDrawPixel(dst, centerX - y, centerY - x, color);
2662 x++;
2663
2664 if (decesionParameter > 0)
2665 {
2666 y--;
2667 decesionParameter = decesionParameter + 4*(x - y) + 10;
2668 }
2669 else decesionParameter = decesionParameter + 4*x + 6;
2670 }
2671}
2672
2673// Draw circle within an image (Vector version)
2674void ImageDrawCircleV(Image *dst, Vector2 center, int radius, Color color)
2675{
2676 ImageDrawCircle(dst, (int)center.x, (int)center.y, radius, color);
2677}
2678
2679// Draw rectangle within an image
2680void ImageDrawRectangle(Image *dst, int posX, int posY, int width, int height, Color color)
2681{
2682 ImageDrawRectangleRec(dst, (Rectangle){ (float)posX, (float)posY, (float)width, (float)height }, color);
2683}
2684
2685// Draw rectangle within an image (Vector version)
2686void ImageDrawRectangleV(Image *dst, Vector2 position, Vector2 size, Color color)
2687{
2688 ImageDrawRectangle(dst, (int)position.x, (int)position.y, (int)size.x, (int)size.y, color);
2689}
2690
2691// Draw rectangle within an image
2693{
2694 // Security check to avoid program crash
2695 if ((dst->data == NULL) || (dst->width == 0) || (dst->height == 0)) return;
2696
2697 int sy = (int)rec.y;
2698 int ey = sy + (int)rec.height;
2699
2700 int sx = (int)rec.x;
2701
2702 int bytesPerPixel = GetPixelDataSize(1, 1, dst->format);
2703
2704 for (int y = sy; y < ey; y++)
2705 {
2706 // Fill in the first pixel of the row based on image format
2707 ImageDrawPixel(dst, sx, y, color);
2708
2709 int bytesOffset = ((y * dst->width) + sx) * bytesPerPixel;
2710 unsigned char *pSrcPixel = (unsigned char *)dst->data + bytesOffset;
2711
2712 // Repeat the first pixel data throughout the row
2713 for (int x = 1; x < (int)rec.width; x++)
2714 {
2715 memcpy(pSrcPixel + x * bytesPerPixel, pSrcPixel, bytesPerPixel);
2716 }
2717 }
2718}
2719
2720// Draw rectangle lines within an image
2721void ImageDrawRectangleLines(Image *dst, Rectangle rec, int thick, Color color)
2722{
2723 ImageDrawRectangle(dst, (int)rec.x, (int)rec.y, (int)rec.width, thick, color);
2724 ImageDrawRectangle(dst, (int)rec.x, (int)(rec.y + thick), thick, (int)(rec.height - thick*2), color);
2725 ImageDrawRectangle(dst, (int)(rec.x + rec.width - thick), (int)(rec.y + thick), thick, (int)(rec.height - thick*2), color);
2726 ImageDrawRectangle(dst, (int)rec.x, (int)(rec.y + rec.height - thick), (int)rec.width, thick, color);
2727}
2728
2729// Draw an image (source) within an image (destination)
2730// NOTE: Color tint is applied to source image
2731void ImageDraw(Image *dst, Image src, Rectangle srcRec, Rectangle dstRec, Color tint)
2732{
2733 // Security check to avoid program crash
2734 if ((dst->data == NULL) || (dst->width == 0) || (dst->height == 0) ||
2735 (src.data == NULL) || (src.width == 0) || (src.height == 0)) return;
2736
2737 if (dst->mipmaps > 1) TRACELOG(LOG_WARNING, "Image drawing only applied to base mipmap level");
2738 if (dst->format >= PIXELFORMAT_COMPRESSED_DXT1_RGB) TRACELOG(LOG_WARNING, "Image drawing not supported for compressed formats");
2739 else
2740 {
2741 Image srcMod = { 0 }; // Source copy (in case it was required)
2742 Image *srcPtr = &src; // Pointer to source image
2743 bool useSrcMod = false; // Track source copy required
2744
2745 // Source rectangle out-of-bounds security checks
2746 if (srcRec.x < 0) { srcRec.width += srcRec.x; srcRec.x = 0; }
2747 if (srcRec.y < 0) { srcRec.height += srcRec.y; srcRec.y = 0; }
2748 if ((srcRec.x + srcRec.width) > src.width) srcRec.width = src.width - srcRec.x;
2749 if ((srcRec.y + srcRec.height) > src.height) srcRec.height = src.height - srcRec.y;
2750
2751 // Check if source rectangle needs to be resized to destination rectangle
2752 // In that case, we make a copy of source and we apply all required transform
2753 if (((int)srcRec.width != (int)dstRec.width) || ((int)srcRec.height != (int)dstRec.height))
2754 {
2755 srcMod = ImageFromImage(src, srcRec); // Create image from another image
2756 ImageResize(&srcMod, (int)dstRec.width, (int)dstRec.height); // Resize to destination rectangle
2757 srcRec = (Rectangle){ 0, 0, (float)srcMod.width, (float)srcMod.height };
2758
2759 srcPtr = &srcMod;
2760 useSrcMod = true;
2761 }
2762
2763 // Destination rectangle out-of-bounds security checks
2764 if (dstRec.x < 0)
2765 {
2766 srcRec.x = -dstRec.x;
2767 srcRec.width += dstRec.x;
2768 dstRec.x = 0;
2769 }
2770 else if ((dstRec.x + srcRec.width) > dst->width) srcRec.width = dst->width - dstRec.x;
2771
2772 if (dstRec.y < 0)
2773 {
2774 srcRec.y = -dstRec.y;
2775 srcRec.height += dstRec.y;
2776 dstRec.y = 0;
2777 }
2778 else if ((dstRec.y + srcRec.height) > dst->height) srcRec.height = dst->height - dstRec.y;
2779
2780 if (dst->width < srcRec.width) srcRec.width = (float)dst->width;
2781 if (dst->height < srcRec.height) srcRec.height = (float)dst->height;
2782
2783 // This blitting method is quite fast! The process followed is:
2784 // for every pixel -> [get_src_format/get_dst_format -> blend -> format_to_dst]
2785 // Some optimization ideas:
2786 // [x] Avoid creating source copy if not required (no resize required)
2787 // [x] Optimize ImageResize() for pixel format (alternative: ImageResizeNN())
2788 // [x] Optimize ColorAlphaBlend() to avoid processing (alpha = 0) and (alpha = 1)
2789 // [x] Optimize ColorAlphaBlend() for faster operations (maybe avoiding divs?)
2790 // [x] Consider fast path: no alpha blending required cases (src has no alpha)
2791 // [x] Consider fast path: same src/dst format with no alpha -> direct line copy
2792 // [-] GetPixelColor(): Get Vector4 instead of Color, easier for ColorAlphaBlend()
2793 // [ ] Support f32bit channels drawing
2794
2795 // TODO: Support PIXELFORMAT_UNCOMPRESSED_R32, PIXELFORMAT_UNCOMPRESSED_R32G32B32, PIXELFORMAT_UNCOMPRESSED_R32G32B32A32
2796
2797 Color colSrc, colDst, blend;
2798 bool blendRequired = true;
2799
2800 // Fast path: Avoid blend if source has no alpha to blend
2801 if ((tint.a == 255) && ((srcPtr->format == PIXELFORMAT_UNCOMPRESSED_GRAYSCALE) || (srcPtr->format == PIXELFORMAT_UNCOMPRESSED_R8G8B8) || (srcPtr->format == PIXELFORMAT_UNCOMPRESSED_R5G6B5))) blendRequired = false;
2802
2803 int strideDst = GetPixelDataSize(dst->width, 1, dst->format);
2804 int bytesPerPixelDst = strideDst/(dst->width);
2805
2806 int strideSrc = GetPixelDataSize(srcPtr->width, 1, srcPtr->format);
2807 int bytesPerPixelSrc = strideSrc/(srcPtr->width);
2808
2809 unsigned char *pSrcBase = (unsigned char *)srcPtr->data + ((int)srcRec.y*srcPtr->width + (int)srcRec.x)*bytesPerPixelSrc;
2810 unsigned char *pDstBase = (unsigned char *)dst->data + ((int)dstRec.y*dst->width + (int)dstRec.x)*bytesPerPixelDst;
2811
2812 for (int y = 0; y < (int)srcRec.height; y++)
2813 {
2814 unsigned char *pSrc = pSrcBase;
2815 unsigned char *pDst = pDstBase;
2816
2817 // Fast path: Avoid moving pixel by pixel if no blend required and same format
2818 if (!blendRequired && (srcPtr->format == dst->format)) memcpy(pDst, pSrc, (int)(srcRec.width)*bytesPerPixelSrc);
2819 else
2820 {
2821 for (int x = 0; x < (int)srcRec.width; x++)
2822 {
2823 colSrc = GetPixelColor(pSrc, srcPtr->format);
2824 colDst = GetPixelColor(pDst, dst->format);
2825
2826 // Fast path: Avoid blend if source has no alpha to blend
2827 if (blendRequired) blend = ColorAlphaBlend(colDst, colSrc, tint);
2828 else blend = colSrc;
2829
2830 SetPixelColor(pDst, blend, dst->format);
2831
2832 pDst += bytesPerPixelDst;
2833 pSrc += bytesPerPixelSrc;
2834 }
2835 }
2836
2837 pSrcBase += strideSrc;
2838 pDstBase += strideDst;
2839 }
2840
2841 if (useSrcMod) UnloadImage(srcMod); // Unload source modified image
2842 }
2843}
2844
2845// Draw text (default font) within an image (destination)
2846void ImageDrawText(Image *dst, const char *text, int posX, int posY, int fontSize, Color color)
2847{
2848#if defined(SUPPORT_MODULE_RTEXT)
2849 Vector2 position = { (float)posX, (float)posY };
2850 // NOTE: For default font, spacing is set to desired font size / default font size (10)
2851 ImageDrawTextEx(dst, GetFontDefault(), text, position, (float)fontSize, (float)fontSize/10, color); // WARNING: Module required: rtext
2852#else
2853 TRACELOG(LOG_WARNING, "IMAGE: ImageDrawText() requires module: rtext");
2854#endif
2855}
2856
2857// Draw text (custom sprite font) within an image (destination)
2858void ImageDrawTextEx(Image *dst, Font font, const char *text, Vector2 position, float fontSize, float spacing, Color tint)
2859{
2860 Image imText = ImageTextEx(font, text, fontSize, spacing, tint);
2861
2862 Rectangle srcRec = { 0.0f, 0.0f, (float)imText.width, (float)imText.height };
2863 Rectangle dstRec = { position.x, position.y, (float)imText.width, (float)imText.height };
2864
2865 ImageDraw(dst, imText, srcRec, dstRec, WHITE);
2866
2867 UnloadImage(imText);
2868}
2869
2870//------------------------------------------------------------------------------------
2871// Texture loading functions
2872//------------------------------------------------------------------------------------
2873// Load texture from file into GPU memory (VRAM)
2874Texture2D LoadTexture(const char *fileName)
2875{
2876 Texture2D texture = { 0 };
2877
2878 Image image = LoadImage(fileName);
2879
2880 if (image.data != NULL)
2881 {
2882 texture = LoadTextureFromImage(image);
2883 UnloadImage(image);
2884 }
2885
2886 return texture;
2887}
2888
2889// Load a texture from image data
2890// NOTE: image is not unloaded, it must be done manually
2892{
2893 Texture2D texture = { 0 };
2894
2895 if ((image.width != 0) && (image.height != 0))
2896 {
2897 texture.id = rlLoadTexture(image.data, image.width, image.height, image.format, image.mipmaps);
2898 }
2899 else TRACELOG(LOG_WARNING, "IMAGE: Data is not valid to load texture");
2900
2901 texture.width = image.width;
2902 texture.height = image.height;
2903 texture.mipmaps = image.mipmaps;
2904 texture.format = image.format;
2905
2906 return texture;
2907}
2908
2909// Load cubemap from image, multiple image cubemap layouts supported
2911{
2912 TextureCubemap cubemap = { 0 };
2913
2914 if (layout == CUBEMAP_LAYOUT_AUTO_DETECT) // Try to automatically guess layout type
2915 {
2916 // Check image width/height to determine the type of cubemap provided
2917 if (image.width > image.height)
2918 {
2919 if ((image.width/6) == image.height) { layout = CUBEMAP_LAYOUT_LINE_HORIZONTAL; cubemap.width = image.width/6; }
2920 else if ((image.width/4) == (image.height/3)) { layout = CUBEMAP_LAYOUT_CROSS_FOUR_BY_THREE; cubemap.width = image.width/4; }
2921 else if (image.width >= (int)((float)image.height*1.85f)) { layout = CUBEMAP_LAYOUT_PANORAMA; cubemap.width = image.width/4; }
2922 }
2923 else if (image.height > image.width)
2924 {
2925 if ((image.height/6) == image.width) { layout = CUBEMAP_LAYOUT_LINE_VERTICAL; cubemap.width = image.height/6; }
2926 else if ((image.width/3) == (image.height/4)) { layout = CUBEMAP_LAYOUT_CROSS_THREE_BY_FOUR; cubemap.width = image.width/3; }
2927 }
2928
2929 cubemap.height = cubemap.width;
2930 }
2931
2932 // Layout provided or already auto-detected
2933 if (layout != CUBEMAP_LAYOUT_AUTO_DETECT)
2934 {
2935 int size = cubemap.width;
2936
2937 Image faces = { 0 }; // Vertical column image
2938 Rectangle faceRecs[6] = { 0 }; // Face source rectangles
2939 for (int i = 0; i < 6; i++) faceRecs[i] = (Rectangle){ 0, 0, (float)size, (float)size };
2940
2941 if (layout == CUBEMAP_LAYOUT_LINE_VERTICAL)
2942 {
2943 faces = ImageCopy(image); // Image data already follows expected convention
2944 }
2945 else if (layout == CUBEMAP_LAYOUT_PANORAMA)
2946 {
2947 // TODO: Convert panorama image to square faces...
2948 // Ref: https://github.com/denivip/panorama/blob/master/panorama.cpp
2949 }
2950 else
2951 {
2952 if (layout == CUBEMAP_LAYOUT_LINE_HORIZONTAL) for (int i = 0; i < 6; i++) faceRecs[i].x = (float)size*i;
2953 else if (layout == CUBEMAP_LAYOUT_CROSS_THREE_BY_FOUR)
2954 {
2955 faceRecs[0].x = (float)size; faceRecs[0].y = (float)size;
2956 faceRecs[1].x = (float)size; faceRecs[1].y = (float)size*3;
2957 faceRecs[2].x = (float)size; faceRecs[2].y = 0;
2958 faceRecs[3].x = (float)size; faceRecs[3].y = (float)size*2;
2959 faceRecs[4].x = 0; faceRecs[4].y = (float)size;
2960 faceRecs[5].x = (float)size*2; faceRecs[5].y = (float)size;
2961 }
2962 else if (layout == CUBEMAP_LAYOUT_CROSS_FOUR_BY_THREE)
2963 {
2964 faceRecs[0].x = (float)size*2; faceRecs[0].y = (float)size;
2965 faceRecs[1].x = 0; faceRecs[1].y = (float)size;
2966 faceRecs[2].x = (float)size; faceRecs[2].y = 0;
2967 faceRecs[3].x = (float)size; faceRecs[3].y = (float)size*2;
2968 faceRecs[4].x = (float)size; faceRecs[4].y = (float)size;
2969 faceRecs[5].x = (float)size*3; faceRecs[5].y = (float)size;
2970 }
2971
2972 // Convert image data to 6 faces in a vertical column, that's the optimum layout for loading
2973 faces = GenImageColor(size, size*6, MAGENTA);
2974 ImageFormat(&faces, image.format);
2975
2976 // NOTE: Image formating does not work with compressed textures
2977
2978 for (int i = 0; i < 6; i++) ImageDraw(&faces, image, faceRecs[i], (Rectangle){ 0, (float)size*i, (float)size, (float)size }, WHITE);
2979 }
2980
2981 // NOTE: Cubemap data is expected to be provided as 6 images in a single data array,
2982 // one after the other (that's a vertical image), following convention: +X, -X, +Y, -Y, +Z, -Z
2983 cubemap.id = rlLoadTextureCubemap(faces.data, size, faces.format);
2984 if (cubemap.id == 0) TRACELOG(LOG_WARNING, "IMAGE: Failed to load cubemap image");
2985
2986 UnloadImage(faces);
2987 }
2988 else TRACELOG(LOG_WARNING, "IMAGE: Failed to detect cubemap image layout");
2989
2990 return cubemap;
2991}
2992
2993// Load texture for rendering (framebuffer)
2994// NOTE: Render texture is loaded by default with RGBA color attachment and depth RenderBuffer
2996{
2997 RenderTexture2D target = { 0 };
2998
2999 target.id = rlLoadFramebuffer(width, height); // Load an empty framebuffer
3000
3001 if (target.id > 0)
3002 {
3003 rlEnableFramebuffer(target.id);
3004
3005 // Create color texture (default to RGBA)
3006 target.texture.id = rlLoadTexture(NULL, width, height, PIXELFORMAT_UNCOMPRESSED_R8G8B8A8, 1);
3007 target.texture.width = width;
3008 target.texture.height = height;
3010 target.texture.mipmaps = 1;
3011
3012 // Create depth renderbuffer/texture
3013 target.depth.id = rlLoadTextureDepth(width, height, true);
3014 target.depth.width = width;
3015 target.depth.height = height;
3016 target.depth.format = 19; //DEPTH_COMPONENT_24BIT?
3017 target.depth.mipmaps = 1;
3018
3019 // Attach color texture and depth renderbuffer/texture to FBO
3022
3023 // Check if fbo is complete with attachments (valid)
3024 if (rlFramebufferComplete(target.id)) TRACELOG(LOG_INFO, "FBO: [ID %i] Framebuffer object created successfully", target.id);
3025
3027 }
3028 else TRACELOG(LOG_WARNING, "FBO: Framebuffer object can not be created");
3029
3030 return target;
3031}
3032
3033// Unload texture from GPU memory (VRAM)
3035{
3036 if (texture.id > 0)
3037 {
3038 rlUnloadTexture(texture.id);
3039
3040 TRACELOG(LOG_INFO, "TEXTURE: [ID %i] Unloaded texture data from VRAM (GPU)", texture.id);
3041 }
3042}
3043
3044// Unload render texture from GPU memory (VRAM)
3046{
3047 if (target.id > 0)
3048 {
3049 // Color texture attached to FBO is deleted
3050 rlUnloadTexture(target.texture.id);
3051
3052 // NOTE: Depth texture/renderbuffer is automatically
3053 // queried and deleted before deleting framebuffer
3054 rlUnloadFramebuffer(target.id);
3055 }
3056}
3057
3058// Update GPU texture with new data
3059// NOTE: pixels data must match texture.format
3060void UpdateTexture(Texture2D texture, const void *pixels)
3061{
3062 rlUpdateTexture(texture.id, 0, 0, texture.width, texture.height, texture.format, pixels);
3063}
3064
3065// Update GPU texture rectangle with new data
3066// NOTE: pixels data must match texture.format
3067void UpdateTextureRec(Texture2D texture, Rectangle rec, const void *pixels)
3068{
3069 rlUpdateTexture(texture.id, (int)rec.x, (int)rec.y, (int)rec.width, (int)rec.height, texture.format, pixels);
3070}
3071
3072//------------------------------------------------------------------------------------
3073// Texture configuration functions
3074//------------------------------------------------------------------------------------
3075// Generate GPU mipmaps for a texture
3077{
3078 // NOTE: NPOT textures support check inside function
3079 // On WebGL (OpenGL ES 2.0) NPOT textures support is limited
3080 rlGenTextureMipmaps(texture->id, texture->width, texture->height, texture->format, &texture->mipmaps);
3081}
3082
3083// Set texture scaling filter mode
3084void SetTextureFilter(Texture2D texture, int filter)
3085{
3086 switch (filter)
3087 {
3089 {
3090 if (texture.mipmaps > 1)
3091 {
3092 // RL_TEXTURE_FILTER_MIP_NEAREST - tex filter: POINT, mipmaps filter: POINT (sharp switching between mipmaps)
3094
3095 // RL_TEXTURE_FILTER_NEAREST - tex filter: POINT (no filter), no mipmaps
3097 }
3098 else
3099 {
3100 // RL_TEXTURE_FILTER_NEAREST - tex filter: POINT (no filter), no mipmaps
3103 }
3104 } break;
3106 {
3107 if (texture.mipmaps > 1)
3108 {
3109 // RL_TEXTURE_FILTER_LINEAR_MIP_NEAREST - tex filter: BILINEAR, mipmaps filter: POINT (sharp switching between mipmaps)
3110 // Alternative: RL_TEXTURE_FILTER_NEAREST_MIP_LINEAR - tex filter: POINT, mipmaps filter: BILINEAR (smooth transition between mipmaps)
3112
3113 // RL_TEXTURE_FILTER_LINEAR - tex filter: BILINEAR, no mipmaps
3115 }
3116 else
3117 {
3118 // RL_TEXTURE_FILTER_LINEAR - tex filter: BILINEAR, no mipmaps
3121 }
3122 } break;
3124 {
3125 if (texture.mipmaps > 1)
3126 {
3127 // RL_TEXTURE_FILTER_MIP_LINEAR - tex filter: BILINEAR, mipmaps filter: BILINEAR (smooth transition between mipmaps)
3129
3130 // RL_TEXTURE_FILTER_LINEAR - tex filter: BILINEAR, no mipmaps
3132 }
3133 else
3134 {
3135 TRACELOG(LOG_WARNING, "TEXTURE: [ID %i] No mipmaps available for TRILINEAR texture filtering", texture.id);
3136
3137 // RL_TEXTURE_FILTER_LINEAR - tex filter: BILINEAR, no mipmaps
3140 }
3141 } break;
3145 default: break;
3146 }
3147}
3148
3149// Set texture wrapping mode
3150void SetTextureWrap(Texture2D texture, int wrap)
3151{
3152 switch (wrap)
3153 {
3155 {
3156 // NOTE: It only works if NPOT textures are supported, i.e. OpenGL ES 2.0 could not support it
3159 } break;
3160 case TEXTURE_WRAP_CLAMP:
3161 {
3164 } break;
3166 {
3169 } break;
3171 {
3174 } break;
3175 default: break;
3176 }
3177}
3178
3179//------------------------------------------------------------------------------------
3180// Texture drawing functions
3181//------------------------------------------------------------------------------------
3182// Draw a Texture2D
3183void DrawTexture(Texture2D texture, int posX, int posY, Color tint)
3184{
3185 DrawTextureEx(texture, (Vector2){ (float)posX, (float)posY }, 0.0f, 1.0f, tint);
3186}
3187
3188// Draw a Texture2D with position defined as Vector2
3189void DrawTextureV(Texture2D texture, Vector2 position, Color tint)
3190{
3191 DrawTextureEx(texture, position, 0, 1.0f, tint);
3192}
3193
3194// Draw a Texture2D with extended parameters
3195void DrawTextureEx(Texture2D texture, Vector2 position, float rotation, float scale, Color tint)
3196{
3197 Rectangle source = { 0.0f, 0.0f, (float)texture.width, (float)texture.height };
3198 Rectangle dest = { position.x, position.y, (float)texture.width*scale, (float)texture.height*scale };
3199 Vector2 origin = { 0.0f, 0.0f };
3200
3201 DrawTexturePro(texture, source, dest, origin, rotation, tint);
3202}
3203
3204// Draw a part of a texture (defined by a rectangle)
3205void DrawTextureRec(Texture2D texture, Rectangle source, Vector2 position, Color tint)
3206{
3207 Rectangle dest = { position.x, position.y, fabsf(source.width), fabsf(source.height) };
3208 Vector2 origin = { 0.0f, 0.0f };
3209
3210 DrawTexturePro(texture, source, dest, origin, 0.0f, tint);
3211}
3212
3213// Draw texture quad with tiling and offset parameters
3214// NOTE: Tiling and offset should be provided considering normalized texture values [0..1]
3215// i.e tiling = { 1.0f, 1.0f } refers to all texture, offset = { 0.5f, 0.5f } moves texture origin to center
3216void DrawTextureQuad(Texture2D texture, Vector2 tiling, Vector2 offset, Rectangle quad, Color tint)
3217{
3218 // WARNING: This solution only works if TEXTURE_WRAP_REPEAT is supported,
3219 // NPOT textures supported is required and OpenGL ES 2.0 could not support it
3220 Rectangle source = { offset.x*texture.width, offset.y*texture.height, tiling.x*texture.width, tiling.y*texture.height };
3221 Vector2 origin = { 0.0f, 0.0f };
3222
3223 DrawTexturePro(texture, source, quad, origin, 0.0f, tint);
3224}
3225
3226// Draw part of a texture (defined by a rectangle) with rotation and scale tiled into dest.
3227// NOTE: For tilling a whole texture DrawTextureQuad() is better
3228void DrawTextureTiled(Texture2D texture, Rectangle source, Rectangle dest, Vector2 origin, float rotation, float scale, Color tint)
3229{
3230 if ((texture.id <= 0) || (scale <= 0.0f)) return; // Wanna see a infinite loop?!...just delete this line!
3231 if ((source.width == 0) || (source.height == 0)) return;
3232
3233 int tileWidth = (int)(source.width*scale), tileHeight = (int)(source.height*scale);
3234 if ((dest.width < tileWidth) && (dest.height < tileHeight))
3235 {
3236 // Can fit only one tile
3237 DrawTexturePro(texture, (Rectangle){source.x, source.y, ((float)dest.width/tileWidth)*source.width, ((float)dest.height/tileHeight)*source.height},
3238 (Rectangle){dest.x, dest.y, dest.width, dest.height}, origin, rotation, tint);
3239 }
3240 else if (dest.width <= tileWidth)
3241 {
3242 // Tiled vertically (one column)
3243 int dy = 0;
3244 for (;dy+tileHeight < dest.height; dy += tileHeight)
3245 {
3246 DrawTexturePro(texture, (Rectangle){source.x, source.y, ((float)dest.width/tileWidth)*source.width, source.height}, (Rectangle){dest.x, dest.y + dy, dest.width, (float)tileHeight}, origin, rotation, tint);
3247 }
3248
3249 // Fit last tile
3250 if (dy < dest.height)
3251 {
3252 DrawTexturePro(texture, (Rectangle){source.x, source.y, ((float)dest.width/tileWidth)*source.width, ((float)(dest.height - dy)/tileHeight)*source.height},
3253 (Rectangle){dest.x, dest.y + dy, dest.width, dest.height - dy}, origin, rotation, tint);
3254 }
3255 }
3256 else if (dest.height <= tileHeight)
3257 {
3258 // Tiled horizontally (one row)
3259 int dx = 0;
3260 for (;dx+tileWidth < dest.width; dx += tileWidth)
3261 {
3262 DrawTexturePro(texture, (Rectangle){source.x, source.y, source.width, ((float)dest.height/tileHeight)*source.height}, (Rectangle){dest.x + dx, dest.y, (float)tileWidth, dest.height}, origin, rotation, tint);
3263 }
3264
3265 // Fit last tile
3266 if (dx < dest.width)
3267 {
3268 DrawTexturePro(texture, (Rectangle){source.x, source.y, ((float)(dest.width - dx)/tileWidth)*source.width, ((float)dest.height/tileHeight)*source.height},
3269 (Rectangle){dest.x + dx, dest.y, dest.width - dx, dest.height}, origin, rotation, tint);
3270 }
3271 }
3272 else
3273 {
3274 // Tiled both horizontally and vertically (rows and columns)
3275 int dx = 0;
3276 for (;dx+tileWidth < dest.width; dx += tileWidth)
3277 {
3278 int dy = 0;
3279 for (;dy+tileHeight < dest.height; dy += tileHeight)
3280 {
3281 DrawTexturePro(texture, source, (Rectangle){dest.x + dx, dest.y + dy, (float)tileWidth, (float)tileHeight}, origin, rotation, tint);
3282 }
3283
3284 if (dy < dest.height)
3285 {
3286 DrawTexturePro(texture, (Rectangle){source.x, source.y, source.width, ((float)(dest.height - dy)/tileHeight)*source.height},
3287 (Rectangle){dest.x + dx, dest.y + dy, (float)tileWidth, dest.height - dy}, origin, rotation, tint);
3288 }
3289 }
3290
3291 // Fit last column of tiles
3292 if (dx < dest.width)
3293 {
3294 int dy = 0;
3295 for (;dy+tileHeight < dest.height; dy += tileHeight)
3296 {
3297 DrawTexturePro(texture, (Rectangle){source.x, source.y, ((float)(dest.width - dx)/tileWidth)*source.width, source.height},
3298 (Rectangle){dest.x + dx, dest.y + dy, dest.width - dx, (float)tileHeight}, origin, rotation, tint);
3299 }
3300
3301 // Draw final tile in the bottom right corner
3302 if (dy < dest.height)
3303 {
3304 DrawTexturePro(texture, (Rectangle){source.x, source.y, ((float)(dest.width - dx)/tileWidth)*source.width, ((float)(dest.height - dy)/tileHeight)*source.height},
3305 (Rectangle){dest.x + dx, dest.y + dy, dest.width - dx, dest.height - dy}, origin, rotation, tint);
3306 }
3307 }
3308 }
3309}
3310
3311// Draw a part of a texture (defined by a rectangle) with 'pro' parameters
3312// NOTE: origin is relative to destination rectangle size
3313void DrawTexturePro(Texture2D texture, Rectangle source, Rectangle dest, Vector2 origin, float rotation, Color tint)
3314{
3315 // Check if texture is valid
3316 if (texture.id > 0)
3317 {
3318 float width = (float)texture.width;
3319 float height = (float)texture.height;
3320
3321 bool flipX = false;
3322
3323 if (source.width < 0) { flipX = true; source.width *= -1; }
3324 if (source.height < 0) source.y -= source.height;
3325
3326 Vector2 topLeft = { 0 };
3327 Vector2 topRight = { 0 };
3328 Vector2 bottomLeft = { 0 };
3329 Vector2 bottomRight = { 0 };
3330
3331 // Only calculate rotation if needed
3332 if (rotation == 0.0f)
3333 {
3334 float x = dest.x - origin.x;
3335 float y = dest.y - origin.y;
3336 topLeft = (Vector2){ x, y };
3337 topRight = (Vector2){ x + dest.width, y };
3338 bottomLeft = (Vector2){ x, y + dest.height };
3339 bottomRight = (Vector2){ x + dest.width, y + dest.height };
3340 }
3341 else
3342 {
3343 float sinRotation = sinf(rotation*DEG2RAD);
3344 float cosRotation = cosf(rotation*DEG2RAD);
3345 float x = dest.x;
3346 float y = dest.y;
3347 float dx = -origin.x;
3348 float dy = -origin.y;
3349
3350 topLeft.x = x + dx*cosRotation - dy*sinRotation;
3351 topLeft.y = y + dx*sinRotation + dy*cosRotation;
3352
3353 topRight.x = x + (dx + dest.width)*cosRotation - dy*sinRotation;
3354 topRight.y = y + (dx + dest.width)*sinRotation + dy*cosRotation;
3355
3356 bottomLeft.x = x + dx*cosRotation - (dy + dest.height)*sinRotation;
3357 bottomLeft.y = y + dx*sinRotation + (dy + dest.height)*cosRotation;
3358
3359 bottomRight.x = x + (dx + dest.width)*cosRotation - (dy + dest.height)*sinRotation;
3360 bottomRight.y = y + (dx + dest.width)*sinRotation + (dy + dest.height)*cosRotation;
3361 }
3362
3363 rlCheckRenderBatchLimit(4); // Make sure there is enough free space on the batch buffer
3364
3365 rlSetTexture(texture.id);
3367
3368 rlColor4ub(tint.r, tint.g, tint.b, tint.a);
3369 rlNormal3f(0.0f, 0.0f, 1.0f); // Normal vector pointing towards viewer
3370
3371 // Top-left corner for texture and quad
3372 if (flipX) rlTexCoord2f((source.x + source.width)/width, source.y/height);
3373 else rlTexCoord2f(source.x/width, source.y/height);
3374 rlVertex2f(topLeft.x, topLeft.y);
3375
3376 // Bottom-left corner for texture and quad
3377 if (flipX) rlTexCoord2f((source.x + source.width)/width, (source.y + source.height)/height);
3378 else rlTexCoord2f(source.x/width, (source.y + source.height)/height);
3379 rlVertex2f(bottomLeft.x, bottomLeft.y);
3380
3381 // Bottom-right corner for texture and quad
3382 if (flipX) rlTexCoord2f(source.x/width, (source.y + source.height)/height);
3383 else rlTexCoord2f((source.x + source.width)/width, (source.y + source.height)/height);
3384 rlVertex2f(bottomRight.x, bottomRight.y);
3385
3386 // Top-right corner for texture and quad
3387 if (flipX) rlTexCoord2f(source.x/width, source.y/height);
3388 else rlTexCoord2f((source.x + source.width)/width, source.y/height);
3389 rlVertex2f(topRight.x, topRight.y);
3390
3391 rlEnd();
3392 rlSetTexture(0);
3393
3394 // NOTE: Vertex position can be transformed using matrices
3395 // but the process is way more costly than just calculating
3396 // the vertex positions manually, like done above.
3397 // I leave here the old implementation for educational pourposes,
3398 // just in case someone wants to do some performance test
3399 /*
3400 rlSetTexture(texture.id);
3401 rlPushMatrix();
3402 rlTranslatef(dest.x, dest.y, 0.0f);
3403 if (rotation != 0.0f) rlRotatef(rotation, 0.0f, 0.0f, 1.0f);
3404 rlTranslatef(-origin.x, -origin.y, 0.0f);
3405
3406 rlBegin(RL_QUADS);
3407 rlColor4ub(tint.r, tint.g, tint.b, tint.a);
3408 rlNormal3f(0.0f, 0.0f, 1.0f); // Normal vector pointing towards viewer
3409
3410 // Bottom-left corner for texture and quad
3411 if (flipX) rlTexCoord2f((source.x + source.width)/width, source.y/height);
3412 else rlTexCoord2f(source.x/width, source.y/height);
3413 rlVertex2f(0.0f, 0.0f);
3414
3415 // Bottom-right corner for texture and quad
3416 if (flipX) rlTexCoord2f((source.x + source.width)/width, (source.y + source.height)/height);
3417 else rlTexCoord2f(source.x/width, (source.y + source.height)/height);
3418 rlVertex2f(0.0f, dest.height);
3419
3420 // Top-right corner for texture and quad
3421 if (flipX) rlTexCoord2f(source.x/width, (source.y + source.height)/height);
3422 else rlTexCoord2f((source.x + source.width)/width, (source.y + source.height)/height);
3423 rlVertex2f(dest.width, dest.height);
3424
3425 // Top-left corner for texture and quad
3426 if (flipX) rlTexCoord2f(source.x/width, source.y/height);
3427 else rlTexCoord2f((source.x + source.width)/width, source.y/height);
3428 rlVertex2f(dest.width, 0.0f);
3429 rlEnd();
3430 rlPopMatrix();
3431 rlSetTexture(0);
3432 */
3433 }
3434}
3435
3436// Draws a texture (or part of it) that stretches or shrinks nicely using n-patch info
3437void DrawTextureNPatch(Texture2D texture, NPatchInfo nPatchInfo, Rectangle dest, Vector2 origin, float rotation, Color tint)
3438{
3439 if (texture.id > 0)
3440 {
3441 float width = (float)texture.width;
3442 float height = (float)texture.height;
3443
3444 float patchWidth = ((int)dest.width <= 0)? 0.0f : dest.width;
3445 float patchHeight = ((int)dest.height <= 0)? 0.0f : dest.height;
3446
3447 if (nPatchInfo.source.width < 0) nPatchInfo.source.x -= nPatchInfo.source.width;
3448 if (nPatchInfo.source.height < 0) nPatchInfo.source.y -= nPatchInfo.source.height;
3449 if (nPatchInfo.layout == NPATCH_THREE_PATCH_HORIZONTAL) patchHeight = nPatchInfo.source.height;
3450 if (nPatchInfo.layout == NPATCH_THREE_PATCH_VERTICAL) patchWidth = nPatchInfo.source.width;
3451
3452 bool drawCenter = true;
3453 bool drawMiddle = true;
3454 float leftBorder = (float)nPatchInfo.left;
3455 float topBorder = (float)nPatchInfo.top;
3456 float rightBorder = (float)nPatchInfo.right;
3457 float bottomBorder = (float)nPatchInfo.bottom;
3458
3459 // Adjust the lateral (left and right) border widths in case patchWidth < texture.width
3460 if (patchWidth <= (leftBorder + rightBorder) && nPatchInfo.layout != NPATCH_THREE_PATCH_VERTICAL)
3461 {
3462 drawCenter = false;
3463 leftBorder = (leftBorder/(leftBorder + rightBorder))*patchWidth;
3464 rightBorder = patchWidth - leftBorder;
3465 }
3466
3467 // Adjust the lateral (top and bottom) border heights in case patchHeight < texture.height
3468 if (patchHeight <= (topBorder + bottomBorder) && nPatchInfo.layout != NPATCH_THREE_PATCH_HORIZONTAL)
3469 {
3470 drawMiddle = false;
3471 topBorder = (topBorder/(topBorder + bottomBorder))*patchHeight;
3472 bottomBorder = patchHeight - topBorder;
3473 }
3474
3475 Vector2 vertA, vertB, vertC, vertD;
3476 vertA.x = 0.0f; // outer left
3477 vertA.y = 0.0f; // outer top
3478 vertB.x = leftBorder; // inner left
3479 vertB.y = topBorder; // inner top
3480 vertC.x = patchWidth - rightBorder; // inner right
3481 vertC.y = patchHeight - bottomBorder; // inner bottom
3482 vertD.x = patchWidth; // outer right
3483 vertD.y = patchHeight; // outer bottom
3484
3485 Vector2 coordA, coordB, coordC, coordD;
3486 coordA.x = nPatchInfo.source.x/width;
3487 coordA.y = nPatchInfo.source.y/height;
3488 coordB.x = (nPatchInfo.source.x + leftBorder)/width;
3489 coordB.y = (nPatchInfo.source.y + topBorder)/height;
3490 coordC.x = (nPatchInfo.source.x + nPatchInfo.source.width - rightBorder)/width;
3491 coordC.y = (nPatchInfo.source.y + nPatchInfo.source.height - bottomBorder)/height;
3492 coordD.x = (nPatchInfo.source.x + nPatchInfo.source.width)/width;
3493 coordD.y = (nPatchInfo.source.y + nPatchInfo.source.height)/height;
3494
3495 rlCheckRenderBatchLimit(9 * 3 * 2); // Maxium number of verts that could happen
3496
3497 rlSetTexture(texture.id);
3498
3499 rlPushMatrix();
3500 rlTranslatef(dest.x, dest.y, 0.0f);
3501 rlRotatef(rotation, 0.0f, 0.0f, 1.0f);
3502 rlTranslatef(-origin.x, -origin.y, 0.0f);
3503
3505 rlColor4ub(tint.r, tint.g, tint.b, tint.a);
3506 rlNormal3f(0.0f, 0.0f, 1.0f); // Normal vector pointing towards viewer
3507
3508 if (nPatchInfo.layout == NPATCH_NINE_PATCH)
3509 {
3510 // ------------------------------------------------------------
3511 // TOP-LEFT QUAD
3512 rlTexCoord2f(coordA.x, coordB.y); rlVertex2f(vertA.x, vertB.y); // Bottom-left corner for texture and quad
3513 rlTexCoord2f(coordB.x, coordB.y); rlVertex2f(vertB.x, vertB.y); // Bottom-right corner for texture and quad
3514 rlTexCoord2f(coordB.x, coordA.y); rlVertex2f(vertB.x, vertA.y); // Top-right corner for texture and quad
3515 rlTexCoord2f(coordA.x, coordA.y); rlVertex2f(vertA.x, vertA.y); // Top-left corner for texture and quad
3516 if (drawCenter)
3517 {
3518 // TOP-CENTER QUAD
3519 rlTexCoord2f(coordB.x, coordB.y); rlVertex2f(vertB.x, vertB.y); // Bottom-left corner for texture and quad
3520 rlTexCoord2f(coordC.x, coordB.y); rlVertex2f(vertC.x, vertB.y); // Bottom-right corner for texture and quad
3521 rlTexCoord2f(coordC.x, coordA.y); rlVertex2f(vertC.x, vertA.y); // Top-right corner for texture and quad
3522 rlTexCoord2f(coordB.x, coordA.y); rlVertex2f(vertB.x, vertA.y); // Top-left corner for texture and quad
3523 }
3524 // TOP-RIGHT QUAD
3525 rlTexCoord2f(coordC.x, coordB.y); rlVertex2f(vertC.x, vertB.y); // Bottom-left corner for texture and quad
3526 rlTexCoord2f(coordD.x, coordB.y); rlVertex2f(vertD.x, vertB.y); // Bottom-right corner for texture and quad
3527 rlTexCoord2f(coordD.x, coordA.y); rlVertex2f(vertD.x, vertA.y); // Top-right corner for texture and quad
3528 rlTexCoord2f(coordC.x, coordA.y); rlVertex2f(vertC.x, vertA.y); // Top-left corner for texture and quad
3529 if (drawMiddle)
3530 {
3531 // ------------------------------------------------------------
3532 // MIDDLE-LEFT QUAD
3533 rlTexCoord2f(coordA.x, coordC.y); rlVertex2f(vertA.x, vertC.y); // Bottom-left corner for texture and quad
3534 rlTexCoord2f(coordB.x, coordC.y); rlVertex2f(vertB.x, vertC.y); // Bottom-right corner for texture and quad
3535 rlTexCoord2f(coordB.x, coordB.y); rlVertex2f(vertB.x, vertB.y); // Top-right corner for texture and quad
3536 rlTexCoord2f(coordA.x, coordB.y); rlVertex2f(vertA.x, vertB.y); // Top-left corner for texture and quad
3537 if (drawCenter)
3538 {
3539 // MIDDLE-CENTER QUAD
3540 rlTexCoord2f(coordB.x, coordC.y); rlVertex2f(vertB.x, vertC.y); // Bottom-left corner for texture and quad
3541 rlTexCoord2f(coordC.x, coordC.y); rlVertex2f(vertC.x, vertC.y); // Bottom-right corner for texture and quad
3542 rlTexCoord2f(coordC.x, coordB.y); rlVertex2f(vertC.x, vertB.y); // Top-right corner for texture and quad
3543 rlTexCoord2f(coordB.x, coordB.y); rlVertex2f(vertB.x, vertB.y); // Top-left corner for texture and quad
3544 }
3545
3546 // MIDDLE-RIGHT QUAD
3547 rlTexCoord2f(coordC.x, coordC.y); rlVertex2f(vertC.x, vertC.y); // Bottom-left corner for texture and quad
3548 rlTexCoord2f(coordD.x, coordC.y); rlVertex2f(vertD.x, vertC.y); // Bottom-right corner for texture and quad
3549 rlTexCoord2f(coordD.x, coordB.y); rlVertex2f(vertD.x, vertB.y); // Top-right corner for texture and quad
3550 rlTexCoord2f(coordC.x, coordB.y); rlVertex2f(vertC.x, vertB.y); // Top-left corner for texture and quad
3551 }
3552
3553 // ------------------------------------------------------------
3554 // BOTTOM-LEFT QUAD
3555 rlTexCoord2f(coordA.x, coordD.y); rlVertex2f(vertA.x, vertD.y); // Bottom-left corner for texture and quad
3556 rlTexCoord2f(coordB.x, coordD.y); rlVertex2f(vertB.x, vertD.y); // Bottom-right corner for texture and quad
3557 rlTexCoord2f(coordB.x, coordC.y); rlVertex2f(vertB.x, vertC.y); // Top-right corner for texture and quad
3558 rlTexCoord2f(coordA.x, coordC.y); rlVertex2f(vertA.x, vertC.y); // Top-left corner for texture and quad
3559 if (drawCenter)
3560 {
3561 // BOTTOM-CENTER QUAD
3562 rlTexCoord2f(coordB.x, coordD.y); rlVertex2f(vertB.x, vertD.y); // Bottom-left corner for texture and quad
3563 rlTexCoord2f(coordC.x, coordD.y); rlVertex2f(vertC.x, vertD.y); // Bottom-right corner for texture and quad
3564 rlTexCoord2f(coordC.x, coordC.y); rlVertex2f(vertC.x, vertC.y); // Top-right corner for texture and quad
3565 rlTexCoord2f(coordB.x, coordC.y); rlVertex2f(vertB.x, vertC.y); // Top-left corner for texture and quad
3566 }
3567
3568 // BOTTOM-RIGHT QUAD
3569 rlTexCoord2f(coordC.x, coordD.y); rlVertex2f(vertC.x, vertD.y); // Bottom-left corner for texture and quad
3570 rlTexCoord2f(coordD.x, coordD.y); rlVertex2f(vertD.x, vertD.y); // Bottom-right corner for texture and quad
3571 rlTexCoord2f(coordD.x, coordC.y); rlVertex2f(vertD.x, vertC.y); // Top-right corner for texture and quad
3572 rlTexCoord2f(coordC.x, coordC.y); rlVertex2f(vertC.x, vertC.y); // Top-left corner for texture and quad
3573 }
3574 else if (nPatchInfo.layout == NPATCH_THREE_PATCH_VERTICAL)
3575 {
3576 // TOP QUAD
3577 // -----------------------------------------------------------
3578 // Texture coords Vertices
3579 rlTexCoord2f(coordA.x, coordB.y); rlVertex2f(vertA.x, vertB.y); // Bottom-left corner for texture and quad
3580 rlTexCoord2f(coordD.x, coordB.y); rlVertex2f(vertD.x, vertB.y); // Bottom-right corner for texture and quad
3581 rlTexCoord2f(coordD.x, coordA.y); rlVertex2f(vertD.x, vertA.y); // Top-right corner for texture and quad
3582 rlTexCoord2f(coordA.x, coordA.y); rlVertex2f(vertA.x, vertA.y); // Top-left corner for texture and quad
3583 if (drawCenter)
3584 {
3585 // MIDDLE QUAD
3586 // -----------------------------------------------------------
3587 // Texture coords Vertices
3588 rlTexCoord2f(coordA.x, coordC.y); rlVertex2f(vertA.x, vertC.y); // Bottom-left corner for texture and quad
3589 rlTexCoord2f(coordD.x, coordC.y); rlVertex2f(vertD.x, vertC.y); // Bottom-right corner for texture and quad
3590 rlTexCoord2f(coordD.x, coordB.y); rlVertex2f(vertD.x, vertB.y); // Top-right corner for texture and quad
3591 rlTexCoord2f(coordA.x, coordB.y); rlVertex2f(vertA.x, vertB.y); // Top-left corner for texture and quad
3592 }
3593 // BOTTOM QUAD
3594 // -----------------------------------------------------------
3595 // Texture coords Vertices
3596 rlTexCoord2f(coordA.x, coordD.y); rlVertex2f(vertA.x, vertD.y); // Bottom-left corner for texture and quad
3597 rlTexCoord2f(coordD.x, coordD.y); rlVertex2f(vertD.x, vertD.y); // Bottom-right corner for texture and quad
3598 rlTexCoord2f(coordD.x, coordC.y); rlVertex2f(vertD.x, vertC.y); // Top-right corner for texture and quad
3599 rlTexCoord2f(coordA.x, coordC.y); rlVertex2f(vertA.x, vertC.y); // Top-left corner for texture and quad
3600 }
3601 else if (nPatchInfo.layout == NPATCH_THREE_PATCH_HORIZONTAL)
3602 {
3603 // LEFT QUAD
3604 // -----------------------------------------------------------
3605 // Texture coords Vertices
3606 rlTexCoord2f(coordA.x, coordD.y); rlVertex2f(vertA.x, vertD.y); // Bottom-left corner for texture and quad
3607 rlTexCoord2f(coordB.x, coordD.y); rlVertex2f(vertB.x, vertD.y); // Bottom-right corner for texture and quad
3608 rlTexCoord2f(coordB.x, coordA.y); rlVertex2f(vertB.x, vertA.y); // Top-right corner for texture and quad
3609 rlTexCoord2f(coordA.x, coordA.y); rlVertex2f(vertA.x, vertA.y); // Top-left corner for texture and quad
3610 if (drawCenter)
3611 {
3612 // CENTER QUAD
3613 // -----------------------------------------------------------
3614 // Texture coords Vertices
3615 rlTexCoord2f(coordB.x, coordD.y); rlVertex2f(vertB.x, vertD.y); // Bottom-left corner for texture and quad
3616 rlTexCoord2f(coordC.x, coordD.y); rlVertex2f(vertC.x, vertD.y); // Bottom-right corner for texture and quad
3617 rlTexCoord2f(coordC.x, coordA.y); rlVertex2f(vertC.x, vertA.y); // Top-right corner for texture and quad
3618 rlTexCoord2f(coordB.x, coordA.y); rlVertex2f(vertB.x, vertA.y); // Top-left corner for texture and quad
3619 }
3620 // RIGHT QUAD
3621 // -----------------------------------------------------------
3622 // Texture coords Vertices
3623 rlTexCoord2f(coordC.x, coordD.y); rlVertex2f(vertC.x, vertD.y); // Bottom-left corner for texture and quad
3624 rlTexCoord2f(coordD.x, coordD.y); rlVertex2f(vertD.x, vertD.y); // Bottom-right corner for texture and quad
3625 rlTexCoord2f(coordD.x, coordA.y); rlVertex2f(vertD.x, vertA.y); // Top-right corner for texture and quad
3626 rlTexCoord2f(coordC.x, coordA.y); rlVertex2f(vertC.x, vertA.y); // Top-left corner for texture and quad
3627 }
3628 rlEnd();
3629 rlPopMatrix();
3630
3631 rlSetTexture(0);
3632 }
3633}
3634
3635// Draw textured polygon, defined by vertex and texturecoordinates
3636// NOTE: Polygon center must have straight line path to all points
3637// without crossing perimeter, points must be in anticlockwise order
3638void DrawTexturePoly(Texture2D texture, Vector2 center, Vector2 *points, Vector2 *texcoords, int pointCount, Color tint)
3639{
3640 rlCheckRenderBatchLimit((pointCount - 1)*4);
3641
3642 rlSetTexture(texture.id);
3643
3644 // Texturing is only supported on QUADs
3646
3647 rlColor4ub(tint.r, tint.g, tint.b, tint.a);
3648
3649 for (int i = 0; i < pointCount - 1; i++)
3650 {
3651 rlTexCoord2f(0.5f, 0.5f);
3652 rlVertex2f(center.x, center.y);
3653
3654 rlTexCoord2f(texcoords[i].x, texcoords[i].y);
3655 rlVertex2f(points[i].x + center.x, points[i].y + center.y);
3656
3657 rlTexCoord2f(texcoords[i + 1].x, texcoords[i + 1].y);
3658 rlVertex2f(points[i + 1].x + center.x, points[i + 1].y + center.y);
3659
3660 rlTexCoord2f(texcoords[i + 1].x, texcoords[i + 1].y);
3661 rlVertex2f(points[i + 1].x + center.x, points[i + 1].y + center.y);
3662 }
3663 rlEnd();
3664
3665 rlSetTexture(0);
3666}
3667
3668// Get color with alpha applied, alpha goes from 0.0f to 1.0f
3669Color Fade(Color color, float alpha)
3670{
3671 if (alpha < 0.0f) alpha = 0.0f;
3672 else if (alpha > 1.0f) alpha = 1.0f;
3673
3674 return (Color){color.r, color.g, color.b, (unsigned char)(255.0f*alpha)};
3675}
3676
3677// Get hexadecimal value for a Color
3679{
3680 return (((int)color.r << 24) | ((int)color.g << 16) | ((int)color.b << 8) | (int)color.a);
3681}
3682
3683// Get color normalized as float [0..1]
3685{
3686 Vector4 result;
3687
3688 result.x = (float)color.r/255.0f;
3689 result.y = (float)color.g/255.0f;
3690 result.z = (float)color.b/255.0f;
3691 result.w = (float)color.a/255.0f;
3692
3693 return result;
3694}
3695
3696// Get color from normalized values [0..1]
3698{
3699 Color result;
3700
3701 result.r = (unsigned char)(normalized.x*255.0f);
3702 result.g = (unsigned char)(normalized.y*255.0f);
3703 result.b = (unsigned char)(normalized.z*255.0f);
3704 result.a = (unsigned char)(normalized.w*255.0f);
3705
3706 return result;
3707}
3708
3709// Get HSV values for a Color
3710// NOTE: Hue is returned as degrees [0..360]
3712{
3713 Vector3 hsv = { 0 };
3714 Vector3 rgb = { (float)color.r/255.0f, (float)color.g/255.0f, (float)color.b/255.0f };
3715 float min, max, delta;
3716
3717 min = rgb.x < rgb.y? rgb.x : rgb.y;
3718 min = min < rgb.z? min : rgb.z;
3719
3720 max = rgb.x > rgb.y? rgb.x : rgb.y;
3721 max = max > rgb.z? max : rgb.z;
3722
3723 hsv.z = max; // Value
3724 delta = max - min;
3725
3726 if (delta < 0.00001f)
3727 {
3728 hsv.y = 0.0f;
3729 hsv.x = 0.0f; // Undefined, maybe NAN?
3730 return hsv;
3731 }
3732
3733 if (max > 0.0f)
3734 {
3735 // NOTE: If max is 0, this divide would cause a crash
3736 hsv.y = (delta/max); // Saturation
3737 }
3738 else
3739 {
3740 // NOTE: If max is 0, then r = g = b = 0, s = 0, h is undefined
3741 hsv.y = 0.0f;
3742 hsv.x = NAN; // Undefined
3743 return hsv;
3744 }
3745
3746 // NOTE: Comparing float values could not work properly
3747 if (rgb.x >= max) hsv.x = (rgb.y - rgb.z)/delta; // Between yellow & magenta
3748 else
3749 {
3750 if (rgb.y >= max) hsv.x = 2.0f + (rgb.z - rgb.x)/delta; // Between cyan & yellow
3751 else hsv.x = 4.0f + (rgb.x - rgb.y)/delta; // Between magenta & cyan
3752 }
3753
3754 hsv.x *= 60.0f; // Convert to degrees
3755
3756 if (hsv.x < 0.0f) hsv.x += 360.0f;
3757
3758 return hsv;
3759}
3760
3761// Get a Color from HSV values
3762// Implementation reference: https://en.wikipedia.org/wiki/HSL_and_HSV#Alternative_HSV_conversion
3763// NOTE: Color->HSV->Color conversion will not yield exactly the same color due to rounding errors
3764// Hue is provided in degrees: [0..360]
3765// Saturation/Value are provided normalized: [0.0f..1.0f]
3766Color ColorFromHSV(float hue, float saturation, float value)
3767{
3768 Color color = { 0, 0, 0, 255 };
3769
3770 // Red channel
3771 float k = fmodf((5.0f + hue/60.0f), 6);
3772 float t = 4.0f - k;
3773 k = (t < k)? t : k;
3774 k = (k < 1)? k : 1;
3775 k = (k > 0)? k : 0;
3776 color.r = (unsigned char)((value - value*saturation*k)*255.0f);
3777
3778 // Green channel
3779 k = fmodf((3.0f + hue/60.0f), 6);
3780 t = 4.0f - k;
3781 k = (t < k)? t : k;
3782 k = (k < 1)? k : 1;
3783 k = (k > 0)? k : 0;
3784 color.g = (unsigned char)((value - value*saturation*k)*255.0f);
3785
3786 // Blue channel
3787 k = fmodf((1.0f + hue/60.0f), 6);
3788 t = 4.0f - k;
3789 k = (t < k)? t : k;
3790 k = (k < 1)? k : 1;
3791 k = (k > 0)? k : 0;
3792 color.b = (unsigned char)((value - value*saturation*k)*255.0f);
3793
3794 return color;
3795}
3796
3797// Get color with alpha applied, alpha goes from 0.0f to 1.0f
3798Color ColorAlpha(Color color, float alpha)
3799{
3800 if (alpha < 0.0f) alpha = 0.0f;
3801 else if (alpha > 1.0f) alpha = 1.0f;
3802
3803 return (Color){color.r, color.g, color.b, (unsigned char)(255.0f*alpha)};
3804}
3805
3806// Get src alpha-blended into dst color with tint
3808{
3809 Color out = WHITE;
3810
3811 // Apply color tint to source color
3812 src.r = (unsigned char)(((unsigned int)src.r*(unsigned int)tint.r) >> 8);
3813 src.g = (unsigned char)(((unsigned int)src.g*(unsigned int)tint.g) >> 8);
3814 src.b = (unsigned char)(((unsigned int)src.b*(unsigned int)tint.b) >> 8);
3815 src.a = (unsigned char)(((unsigned int)src.a*(unsigned int)tint.a) >> 8);
3816
3817//#define COLORALPHABLEND_FLOAT
3818#define COLORALPHABLEND_INTEGERS
3819#if defined(COLORALPHABLEND_INTEGERS)
3820 if (src.a == 0) out = dst;
3821 else if (src.a == 255) out = src;
3822 else
3823 {
3824 unsigned int alpha = (unsigned int)src.a + 1; // We are shifting by 8 (dividing by 256), so we need to take that excess into account
3825 out.a = (unsigned char)(((unsigned int)alpha*256 + (unsigned int)dst.a*(256 - alpha)) >> 8);
3826
3827 if (out.a > 0)
3828 {
3829 out.r = (unsigned char)((((unsigned int)src.r*alpha*256 + (unsigned int)dst.r*(unsigned int)dst.a*(256 - alpha))/out.a) >> 8);
3830 out.g = (unsigned char)((((unsigned int)src.g*alpha*256 + (unsigned int)dst.g*(unsigned int)dst.a*(256 - alpha))/out.a) >> 8);
3831 out.b = (unsigned char)((((unsigned int)src.b*alpha*256 + (unsigned int)dst.b*(unsigned int)dst.a*(256 - alpha))/out.a) >> 8);
3832 }
3833 }
3834#endif
3835#if defined(COLORALPHABLEND_FLOAT)
3836 if (src.a == 0) out = dst;
3837 else if (src.a == 255) out = src;
3838 else
3839 {
3840 Vector4 fdst = ColorNormalize(dst);
3841 Vector4 fsrc = ColorNormalize(src);
3842 Vector4 ftint = ColorNormalize(tint);
3843 Vector4 fout = { 0 };
3844
3845 fout.w = fsrc.w + fdst.w*(1.0f - fsrc.w);
3846
3847 if (fout.w > 0.0f)
3848 {
3849 fout.x = (fsrc.x*fsrc.w + fdst.x*fdst.w*(1 - fsrc.w))/fout.w;
3850 fout.y = (fsrc.y*fsrc.w + fdst.y*fdst.w*(1 - fsrc.w))/fout.w;
3851 fout.z = (fsrc.z*fsrc.w + fdst.z*fdst.w*(1 - fsrc.w))/fout.w;
3852 }
3853
3854 out = (Color){ (unsigned char)(fout.x*255.0f), (unsigned char)(fout.y*255.0f), (unsigned char)(fout.z*255.0f), (unsigned char)(fout.w*255.0f) };
3855 }
3856#endif
3857
3858 return out;
3859}
3860
3861// Get a Color struct from hexadecimal value
3862Color GetColor(unsigned int hexValue)
3863{
3864 Color color;
3865
3866 color.r = (unsigned char)(hexValue >> 24) & 0xFF;
3867 color.g = (unsigned char)(hexValue >> 16) & 0xFF;
3868 color.b = (unsigned char)(hexValue >> 8) & 0xFF;
3869 color.a = (unsigned char)hexValue & 0xFF;
3870
3871 return color;
3872}
3873
3874// Get color from a pixel from certain format
3875Color GetPixelColor(void *srcPtr, int format)
3876{
3877 Color color = { 0 };
3878
3879 switch (format)
3880 {
3881 case PIXELFORMAT_UNCOMPRESSED_GRAYSCALE: color = (Color){ ((unsigned char *)srcPtr)[0], ((unsigned char *)srcPtr)[0], ((unsigned char *)srcPtr)[0], 255 }; break;
3882 case PIXELFORMAT_UNCOMPRESSED_GRAY_ALPHA: color = (Color){ ((unsigned char *)srcPtr)[0], ((unsigned char *)srcPtr)[0], ((unsigned char *)srcPtr)[0], ((unsigned char *)srcPtr)[1] }; break;
3884 {
3885 color.r = (unsigned char)((((unsigned short *)srcPtr)[0] >> 11)*255/31);
3886 color.g = (unsigned char)(((((unsigned short *)srcPtr)[0] >> 5) & 0b0000000000111111)*255/63);
3887 color.b = (unsigned char)((((unsigned short *)srcPtr)[0] & 0b0000000000011111)*255/31);
3888 color.a = 255;
3889
3890 } break;
3892 {
3893 color.r = (unsigned char)((((unsigned short *)srcPtr)[0] >> 11)*255/31);
3894 color.g = (unsigned char)(((((unsigned short *)srcPtr)[0] >> 6) & 0b0000000000011111)*255/31);
3895 color.b = (unsigned char)((((unsigned short *)srcPtr)[0] & 0b0000000000011111)*255/31);
3896 color.a = (((unsigned short *)srcPtr)[0] & 0b0000000000000001)? 255 : 0;
3897
3898 } break;
3900 {
3901 color.r = (unsigned char)((((unsigned short *)srcPtr)[0] >> 12)*255/15);
3902 color.g = (unsigned char)(((((unsigned short *)srcPtr)[0] >> 8) & 0b0000000000001111)*255/15);
3903 color.b = (unsigned char)(((((unsigned short *)srcPtr)[0] >> 4) & 0b0000000000001111)*255/15);
3904 color.a = (unsigned char)((((unsigned short *)srcPtr)[0] & 0b0000000000001111)*255/15);
3905
3906 } break;
3907 case PIXELFORMAT_UNCOMPRESSED_R8G8B8A8: color = (Color){ ((unsigned char *)srcPtr)[0], ((unsigned char *)srcPtr)[1], ((unsigned char *)srcPtr)[2], ((unsigned char *)srcPtr)[3] }; break;
3908 case PIXELFORMAT_UNCOMPRESSED_R8G8B8: color = (Color){ ((unsigned char *)srcPtr)[0], ((unsigned char *)srcPtr)[1], ((unsigned char *)srcPtr)[2], 255 }; break;
3910 {
3911 // NOTE: Pixel normalized float value is converted to [0..255]
3912 color.r = (unsigned char)(((float *)srcPtr)[0]*255.0f);
3913 color.g = (unsigned char)(((float *)srcPtr)[0]*255.0f);
3914 color.b = (unsigned char)(((float *)srcPtr)[0]*255.0f);
3915 color.a = 255;
3916
3917 } break;
3919 {
3920 // NOTE: Pixel normalized float value is converted to [0..255]
3921 color.r = (unsigned char)(((float *)srcPtr)[0]*255.0f);
3922 color.g = (unsigned char)(((float *)srcPtr)[1]*255.0f);
3923 color.b = (unsigned char)(((float *)srcPtr)[2]*255.0f);
3924 color.a = 255;
3925
3926 } break;
3928 {
3929 // NOTE: Pixel normalized float value is converted to [0..255]
3930 color.r = (unsigned char)(((float *)srcPtr)[0]*255.0f);
3931 color.g = (unsigned char)(((float *)srcPtr)[1]*255.0f);
3932 color.b = (unsigned char)(((float *)srcPtr)[2]*255.0f);
3933 color.a = (unsigned char)(((float *)srcPtr)[3]*255.0f);
3934
3935 } break;
3936 default: break;
3937 }
3938
3939 return color;
3940}
3941
3942// Set pixel color formatted into destination pointer
3943void SetPixelColor(void *dstPtr, Color color, int format)
3944{
3945 switch (format)
3946 {
3948 {
3949 // NOTE: Calculate grayscale equivalent color
3950 Vector3 coln = { (float)color.r/255.0f, (float)color.g/255.0f, (float)color.b/255.0f };
3951 unsigned char gray = (unsigned char)((coln.x*0.299f + coln.y*0.587f + coln.z*0.114f)*255.0f);
3952
3953 ((unsigned char *)dstPtr)[0] = gray;
3954
3955 } break;
3957 {
3958 // NOTE: Calculate grayscale equivalent color
3959 Vector3 coln = { (float)color.r/255.0f, (float)color.g/255.0f, (float)color.b/255.0f };
3960 unsigned char gray = (unsigned char)((coln.x*0.299f + coln.y*0.587f + coln.z*0.114f)*255.0f);
3961
3962 ((unsigned char *)dstPtr)[0] = gray;
3963 ((unsigned char *)dstPtr)[1] = color.a;
3964
3965 } break;
3967 {
3968 // NOTE: Calculate R5G6B5 equivalent color
3969 Vector3 coln = { (float)color.r/255.0f, (float)color.g/255.0f, (float)color.b/255.0f };
3970
3971 unsigned char r = (unsigned char)(round(coln.x*31.0f));
3972 unsigned char g = (unsigned char)(round(coln.y*63.0f));
3973 unsigned char b = (unsigned char)(round(coln.z*31.0f));
3974
3975 ((unsigned short *)dstPtr)[0] = (unsigned short)r << 11 | (unsigned short)g << 5 | (unsigned short)b;
3976
3977 } break;
3979 {
3980 // NOTE: Calculate R5G5B5A1 equivalent color
3981 Vector4 coln = { (float)color.r/255.0f, (float)color.g/255.0f, (float)color.b/255.0f, (float)color.a/255.0f };
3982
3983 unsigned char r = (unsigned char)(round(coln.x*31.0f));
3984 unsigned char g = (unsigned char)(round(coln.y*31.0f));
3985 unsigned char b = (unsigned char)(round(coln.z*31.0f));
3986 unsigned char a = (coln.w > ((float)PIXELFORMAT_UNCOMPRESSED_R5G5B5A1_ALPHA_THRESHOLD/255.0f))? 1 : 0;
3987
3988 ((unsigned short *)dstPtr)[0] = (unsigned short)r << 11 | (unsigned short)g << 6 | (unsigned short)b << 1 | (unsigned short)a;
3989
3990 } break;
3992 {
3993 // NOTE: Calculate R5G5B5A1 equivalent color
3994 Vector4 coln = { (float)color.r/255.0f, (float)color.g/255.0f, (float)color.b/255.0f, (float)color.a/255.0f };
3995
3996 unsigned char r = (unsigned char)(round(coln.x*15.0f));
3997 unsigned char g = (unsigned char)(round(coln.y*15.0f));
3998 unsigned char b = (unsigned char)(round(coln.z*15.0f));
3999 unsigned char a = (unsigned char)(round(coln.w*15.0f));
4000
4001 ((unsigned short *)dstPtr)[0] = (unsigned short)r << 12 | (unsigned short)g << 8 | (unsigned short)b << 4 | (unsigned short)a;
4002
4003 } break;
4005 {
4006 ((unsigned char *)dstPtr)[0] = color.r;
4007 ((unsigned char *)dstPtr)[1] = color.g;
4008 ((unsigned char *)dstPtr)[2] = color.b;
4009
4010 } break;
4012 {
4013 ((unsigned char *)dstPtr)[0] = color.r;
4014 ((unsigned char *)dstPtr)[1] = color.g;
4015 ((unsigned char *)dstPtr)[2] = color.b;
4016 ((unsigned char *)dstPtr)[3] = color.a;
4017
4018 } break;
4019 default: break;
4020 }
4021}
4022
4023// Get pixel data size in bytes for certain format
4024// NOTE: Size can be requested for Image or Texture data
4025int GetPixelDataSize(int width, int height, int format)
4026{
4027 int dataSize = 0; // Size in bytes
4028 int bpp = 0; // Bits per pixel
4029
4030 switch (format)
4031 {
4032 case PIXELFORMAT_UNCOMPRESSED_GRAYSCALE: bpp = 8; break;
4036 case PIXELFORMAT_UNCOMPRESSED_R4G4B4A4: bpp = 16; break;
4037 case PIXELFORMAT_UNCOMPRESSED_R8G8B8A8: bpp = 32; break;
4038 case PIXELFORMAT_UNCOMPRESSED_R8G8B8: bpp = 24; break;
4039 case PIXELFORMAT_UNCOMPRESSED_R32: bpp = 32; break;
4040 case PIXELFORMAT_UNCOMPRESSED_R32G32B32: bpp = 32*3; break;
4041 case PIXELFORMAT_UNCOMPRESSED_R32G32B32A32: bpp = 32*4; break;
4047 case PIXELFORMAT_COMPRESSED_PVRT_RGBA: bpp = 4; break;
4051 case PIXELFORMAT_COMPRESSED_ASTC_4x4_RGBA: bpp = 8; break;
4052 case PIXELFORMAT_COMPRESSED_ASTC_8x8_RGBA: bpp = 2; break;
4053 default: break;
4054 }
4055
4056 dataSize = width*height*bpp/8; // Total data size in bytes
4057
4058 // Most compressed formats works on 4x4 blocks,
4059 // if texture is smaller, minimum dataSize is 8 or 16
4060 if ((width < 4) && (height < 4))
4061 {
4062 if ((format >= PIXELFORMAT_COMPRESSED_DXT1_RGB) && (format < PIXELFORMAT_COMPRESSED_DXT3_RGBA)) dataSize = 8;
4063 else if ((format >= PIXELFORMAT_COMPRESSED_DXT3_RGBA) && (format < PIXELFORMAT_COMPRESSED_ASTC_8x8_RGBA)) dataSize = 16;
4064 }
4065
4066 return dataSize;
4067}
4068
4069//----------------------------------------------------------------------------------
4070// Module specific Functions Definition
4071//----------------------------------------------------------------------------------
4072#if defined(SUPPORT_FILEFORMAT_DDS)
4073// Loading DDS image data (compressed or uncompressed)
4074static Image LoadDDS(const unsigned char *fileData, unsigned int fileSize)
4075{
4076 unsigned char *fileDataPtr = (unsigned char *)fileData;
4077
4078 // Required extension:
4079 // GL_EXT_texture_compression_s3tc
4080
4081 // Supported tokens (defined by extensions)
4082 // GL_COMPRESSED_RGB_S3TC_DXT1_EXT 0x83F0
4083 // GL_COMPRESSED_RGBA_S3TC_DXT1_EXT 0x83F1
4084 // GL_COMPRESSED_RGBA_S3TC_DXT3_EXT 0x83F2
4085 // GL_COMPRESSED_RGBA_S3TC_DXT5_EXT 0x83F3
4086
4087 #define FOURCC_DXT1 0x31545844 // Equivalent to "DXT1" in ASCII
4088 #define FOURCC_DXT3 0x33545844 // Equivalent to "DXT3" in ASCII
4089 #define FOURCC_DXT5 0x35545844 // Equivalent to "DXT5" in ASCII
4090
4091 // DDS Pixel Format
4092 typedef struct {
4093 unsigned int size;
4094 unsigned int flags;
4095 unsigned int fourCC;
4096 unsigned int rgbBitCount;
4097 unsigned int rBitMask;
4098 unsigned int gBitMask;
4099 unsigned int bBitMask;
4100 unsigned int aBitMask;
4101 } DDSPixelFormat;
4102
4103 // DDS Header (124 bytes)
4104 typedef struct {
4105 unsigned int size;
4106 unsigned int flags;
4107 unsigned int height;
4108 unsigned int width;
4109 unsigned int pitchOrLinearSize;
4110 unsigned int depth;
4111 unsigned int mipmapCount;
4112 unsigned int reserved1[11];
4113 DDSPixelFormat ddspf;
4114 unsigned int caps;
4115 unsigned int caps2;
4116 unsigned int caps3;
4117 unsigned int caps4;
4118 unsigned int reserved2;
4119 } DDSHeader;
4120
4121 Image image = { 0 };
4122
4123 if (fileDataPtr != NULL)
4124 {
4125 // Verify the type of file
4126 unsigned char *ddsHeaderId = fileDataPtr;
4127 fileDataPtr += 4;
4128
4129 if ((ddsHeaderId[0] != 'D') || (ddsHeaderId[1] != 'D') || (ddsHeaderId[2] != 'S') || (ddsHeaderId[3] != ' '))
4130 {
4131 TRACELOG(LOG_WARNING, "IMAGE: DDS file data not valid");
4132 }
4133 else
4134 {
4135 DDSHeader *ddsHeader = (DDSHeader *)fileDataPtr;
4136
4137 TRACELOGD("IMAGE: DDS file data info:");
4138 TRACELOGD(" > Header size: %i", sizeof(DDSHeader));
4139 TRACELOGD(" > Pixel format size: %i", ddsHeader->ddspf.size);
4140 TRACELOGD(" > Pixel format flags: 0x%x", ddsHeader->ddspf.flags);
4141 TRACELOGD(" > File format: 0x%x", ddsHeader->ddspf.fourCC);
4142 TRACELOGD(" > File bit count: 0x%x", ddsHeader->ddspf.rgbBitCount);
4143
4144 fileDataPtr += sizeof(DDSHeader); // Skip header
4145
4146 image.width = ddsHeader->width;
4147 image.height = ddsHeader->height;
4148
4149 if (ddsHeader->mipmapCount == 0) image.mipmaps = 1; // Parameter not used
4150 else image.mipmaps = ddsHeader->mipmapCount;
4151
4152 if (ddsHeader->ddspf.rgbBitCount == 16) // 16bit mode, no compressed
4153 {
4154 if (ddsHeader->ddspf.flags == 0x40) // no alpha channel
4155 {
4156 int dataSize = image.width*image.height*sizeof(unsigned short);
4157 image.data = (unsigned short *)RL_MALLOC(dataSize);
4158
4159 memcpy(image.data, fileDataPtr, dataSize);
4160
4162 }
4163 else if (ddsHeader->ddspf.flags == 0x41) // with alpha channel
4164 {
4165 if (ddsHeader->ddspf.aBitMask == 0x8000) // 1bit alpha
4166 {
4167 int dataSize = image.width*image.height*sizeof(unsigned short);
4168 image.data = (unsigned short *)RL_MALLOC(dataSize);
4169
4170 memcpy(image.data, fileDataPtr, dataSize);
4171
4172 unsigned char alpha = 0;
4173
4174 // NOTE: Data comes as A1R5G5B5, it must be reordered to R5G5B5A1
4175 for (int i = 0; i < image.width*image.height; i++)
4176 {
4177 alpha = ((unsigned short *)image.data)[i] >> 15;
4178 ((unsigned short *)image.data)[i] = ((unsigned short *)image.data)[i] << 1;
4179 ((unsigned short *)image.data)[i] += alpha;
4180 }
4181
4183 }
4184 else if (ddsHeader->ddspf.aBitMask == 0xf000) // 4bit alpha
4185 {
4186 int dataSize = image.width*image.height*sizeof(unsigned short);
4187 image.data = (unsigned short *)RL_MALLOC(dataSize);
4188
4189 memcpy(image.data, fileDataPtr, dataSize);
4190
4191 unsigned char alpha = 0;
4192
4193 // NOTE: Data comes as A4R4G4B4, it must be reordered R4G4B4A4
4194 for (int i = 0; i < image.width*image.height; i++)
4195 {
4196 alpha = ((unsigned short *)image.data)[i] >> 12;
4197 ((unsigned short *)image.data)[i] = ((unsigned short *)image.data)[i] << 4;
4198 ((unsigned short *)image.data)[i] += alpha;
4199 }
4200
4202 }
4203 }
4204 }
4205 else if (ddsHeader->ddspf.flags == 0x40 && ddsHeader->ddspf.rgbBitCount == 24) // DDS_RGB, no compressed
4206 {
4207 int dataSize = image.width*image.height*3*sizeof(unsigned char);
4208 image.data = (unsigned short *)RL_MALLOC(dataSize);
4209
4210 memcpy(image.data, fileDataPtr, dataSize);
4211
4213 }
4214 else if (ddsHeader->ddspf.flags == 0x41 && ddsHeader->ddspf.rgbBitCount == 32) // DDS_RGBA, no compressed
4215 {
4216 int dataSize = image.width*image.height*4*sizeof(unsigned char);
4217 image.data = (unsigned short *)RL_MALLOC(dataSize);
4218
4219 memcpy(image.data, fileDataPtr, dataSize);
4220
4221 unsigned char blue = 0;
4222
4223 // NOTE: Data comes as A8R8G8B8, it must be reordered R8G8B8A8 (view next comment)
4224 // DirecX understand ARGB as a 32bit DWORD but the actual memory byte alignment is BGRA
4225 // So, we must realign B8G8R8A8 to R8G8B8A8
4226 for (int i = 0; i < image.width*image.height*4; i += 4)
4227 {
4228 blue = ((unsigned char *)image.data)[i];
4229 ((unsigned char *)image.data)[i] = ((unsigned char *)image.data)[i + 2];
4230 ((unsigned char *)image.data)[i + 2] = blue;
4231 }
4232
4234 }
4235 else if (((ddsHeader->ddspf.flags == 0x04) || (ddsHeader->ddspf.flags == 0x05)) && (ddsHeader->ddspf.fourCC > 0)) // Compressed
4236 {
4237 int dataSize = 0;
4238
4239 // Calculate data size, including all mipmaps
4240 if (ddsHeader->mipmapCount > 1) dataSize = ddsHeader->pitchOrLinearSize*2;
4241 else dataSize = ddsHeader->pitchOrLinearSize;
4242
4243 image.data = (unsigned char *)RL_MALLOC(dataSize*sizeof(unsigned char));
4244
4245 memcpy(image.data, fileDataPtr, dataSize);
4246
4247 switch (ddsHeader->ddspf.fourCC)
4248 {
4249 case FOURCC_DXT1:
4250 {
4251 if (ddsHeader->ddspf.flags == 0x04) image.format = PIXELFORMAT_COMPRESSED_DXT1_RGB;
4253 } break;
4256 default: break;
4257 }
4258 }
4259 }
4260 }
4261
4262 return image;
4263}
4264#endif
4265
4266#if defined(SUPPORT_FILEFORMAT_PKM)
4267// Loading PKM image data (ETC1/ETC2 compression)
4268// NOTE: KTX is the standard Khronos Group compression format (ETC1/ETC2, mipmaps)
4269// PKM is a much simpler file format used mainly to contain a single ETC1/ETC2 compressed image (no mipmaps)
4270static Image LoadPKM(const unsigned char *fileData, unsigned int fileSize)
4271{
4272 unsigned char *fileDataPtr = (unsigned char *)fileData;
4273
4274 // Required extensions:
4275 // GL_OES_compressed_ETC1_RGB8_texture (ETC1) (OpenGL ES 2.0)
4276 // GL_ARB_ES3_compatibility (ETC2/EAC) (OpenGL ES 3.0)
4277
4278 // Supported tokens (defined by extensions)
4279 // GL_ETC1_RGB8_OES 0x8D64
4280 // GL_COMPRESSED_RGB8_ETC2 0x9274
4281 // GL_COMPRESSED_RGBA8_ETC2_EAC 0x9278
4282
4283 // PKM file (ETC1) Header (16 bytes)
4284 typedef struct {
4285 char id[4]; // "PKM "
4286 char version[2]; // "10" or "20"
4287 unsigned short format; // Data format (big-endian) (Check list below)
4288 unsigned short width; // Texture width (big-endian) (origWidth rounded to multiple of 4)
4289 unsigned short height; // Texture height (big-endian) (origHeight rounded to multiple of 4)
4290 unsigned short origWidth; // Original width (big-endian)
4291 unsigned short origHeight; // Original height (big-endian)
4292 } PKMHeader;
4293
4294 // Formats list
4295 // version 10: format: 0=ETC1_RGB, [1=ETC1_RGBA, 2=ETC1_RGB_MIP, 3=ETC1_RGBA_MIP] (not used)
4296 // version 20: format: 0=ETC1_RGB, 1=ETC2_RGB, 2=ETC2_RGBA_OLD, 3=ETC2_RGBA, 4=ETC2_RGBA1, 5=ETC2_R, 6=ETC2_RG, 7=ETC2_SIGNED_R, 8=ETC2_SIGNED_R
4297
4298 // NOTE: The extended width and height are the widths rounded up to a multiple of 4.
4299 // NOTE: ETC is always 4bit per pixel (64 bit for each 4x4 block of pixels)
4300
4301 Image image = { 0 };
4302
4303 if (fileDataPtr != NULL)
4304 {
4305 PKMHeader *pkmHeader = (PKMHeader *)fileDataPtr;
4306
4307 if ((pkmHeader->id[0] != 'P') || (pkmHeader->id[1] != 'K') || (pkmHeader->id[2] != 'M') || (pkmHeader->id[3] != ' '))
4308 {
4309 TRACELOG(LOG_WARNING, "IMAGE: PKM file data not valid");
4310 }
4311 else
4312 {
4313 fileDataPtr += sizeof(PKMHeader); // Skip header
4314
4315 // NOTE: format, width and height come as big-endian, data must be swapped to little-endian
4316 pkmHeader->format = ((pkmHeader->format & 0x00FF) << 8) | ((pkmHeader->format & 0xFF00) >> 8);
4317 pkmHeader->width = ((pkmHeader->width & 0x00FF) << 8) | ((pkmHeader->width & 0xFF00) >> 8);
4318 pkmHeader->height = ((pkmHeader->height & 0x00FF) << 8) | ((pkmHeader->height & 0xFF00) >> 8);
4319
4320 TRACELOGD("IMAGE: PKM file data info:");
4321 TRACELOGD(" > Image width: %i", pkmHeader->width);
4322 TRACELOGD(" > Image height: %i", pkmHeader->height);
4323 TRACELOGD(" > Image format: %i", pkmHeader->format);
4324
4325 image.width = pkmHeader->width;
4326 image.height = pkmHeader->height;
4327 image.mipmaps = 1;
4328
4329 int bpp = 4;
4330 if (pkmHeader->format == 3) bpp = 8;
4331
4332 int dataSize = image.width*image.height*bpp/8; // Total data size in bytes
4333
4334 image.data = (unsigned char *)RL_MALLOC(dataSize*sizeof(unsigned char));
4335
4336 memcpy(image.data, fileDataPtr, dataSize);
4337
4338 if (pkmHeader->format == 0) image.format = PIXELFORMAT_COMPRESSED_ETC1_RGB;
4339 else if (pkmHeader->format == 1) image.format = PIXELFORMAT_COMPRESSED_ETC2_RGB;
4340 else if (pkmHeader->format == 3) image.format = PIXELFORMAT_COMPRESSED_ETC2_EAC_RGBA;
4341 }
4342 }
4343
4344 return image;
4345}
4346#endif
4347
4348#if defined(SUPPORT_FILEFORMAT_KTX)
4349// Load KTX compressed image data (ETC1/ETC2 compression)
4350// TODO: Review KTX loading, many things changed!
4351static Image LoadKTX(const unsigned char *fileData, unsigned int fileSize)
4352{
4353 unsigned char *fileDataPtr = (unsigned char *)fileData;
4354
4355 // Required extensions:
4356 // GL_OES_compressed_ETC1_RGB8_texture (ETC1)
4357 // GL_ARB_ES3_compatibility (ETC2/EAC)
4358
4359 // Supported tokens (defined by extensions)
4360 // GL_ETC1_RGB8_OES 0x8D64
4361 // GL_COMPRESSED_RGB8_ETC2 0x9274
4362 // GL_COMPRESSED_RGBA8_ETC2_EAC 0x9278
4363
4364 // KTX file Header (64 bytes)
4365 // v1.1 - https://www.khronos.org/opengles/sdk/tools/KTX/file_format_spec/
4366 // v2.0 - http://github.khronos.org/KTX-Specification/
4367
4368 // TODO: Support KTX 2.2 specs!
4369
4370 typedef struct {
4371 char id[12]; // Identifier: "«KTX 11»\r\n\x1A\n"
4372 unsigned int endianness; // Little endian: 0x01 0x02 0x03 0x04
4373 unsigned int glType; // For compressed textures, glType must equal 0
4374 unsigned int glTypeSize; // For compressed texture data, usually 1
4375 unsigned int glFormat; // For compressed textures is 0
4376 unsigned int glInternalFormat; // Compressed internal format
4377 unsigned int glBaseInternalFormat; // Same as glFormat (RGB, RGBA, ALPHA...)
4378 unsigned int width; // Texture image width in pixels
4379 unsigned int height; // Texture image height in pixels
4380 unsigned int depth; // For 2D textures is 0
4381 unsigned int elements; // Number of array elements, usually 0
4382 unsigned int faces; // Cubemap faces, for no-cubemap = 1
4383 unsigned int mipmapLevels; // Non-mipmapped textures = 1
4384 unsigned int keyValueDataSize; // Used to encode any arbitrary data...
4385 } KTXHeader;
4386
4387 // NOTE: Before start of every mipmap data block, we have: unsigned int dataSize
4388
4389 Image image = { 0 };
4390
4391 if (fileDataPtr != NULL)
4392 {
4393 KTXHeader *ktxHeader = (KTXHeader *)fileDataPtr;
4394
4395 if ((ktxHeader->id[1] != 'K') || (ktxHeader->id[2] != 'T') || (ktxHeader->id[3] != 'X') ||
4396 (ktxHeader->id[4] != ' ') || (ktxHeader->id[5] != '1') || (ktxHeader->id[6] != '1'))
4397 {
4398 TRACELOG(LOG_WARNING, "IMAGE: KTX file data not valid");
4399 }
4400 else
4401 {
4402 fileDataPtr += sizeof(KTXHeader); // Move file data pointer
4403
4404 image.width = ktxHeader->width;
4405 image.height = ktxHeader->height;
4406 image.mipmaps = ktxHeader->mipmapLevels;
4407
4408 TRACELOGD("IMAGE: KTX file data info:");
4409 TRACELOGD(" > Image width: %i", ktxHeader->width);
4410 TRACELOGD(" > Image height: %i", ktxHeader->height);
4411 TRACELOGD(" > Image format: 0x%x", ktxHeader->glInternalFormat);
4412
4413 fileDataPtr += ktxHeader->keyValueDataSize; // Skip value data size
4414
4415 int dataSize = ((int *)fileDataPtr)[0];
4416 fileDataPtr += sizeof(int);
4417
4418 image.data = (unsigned char *)RL_MALLOC(dataSize*sizeof(unsigned char));
4419
4420 memcpy(image.data, fileDataPtr, dataSize);
4421
4422 if (ktxHeader->glInternalFormat == 0x8D64) image.format = PIXELFORMAT_COMPRESSED_ETC1_RGB;
4423 else if (ktxHeader->glInternalFormat == 0x9274) image.format = PIXELFORMAT_COMPRESSED_ETC2_RGB;
4424 else if (ktxHeader->glInternalFormat == 0x9278) image.format = PIXELFORMAT_COMPRESSED_ETC2_EAC_RGBA;
4425
4426 // TODO: Support uncompressed data formats? Right now it returns format = 0!
4427 }
4428 }
4429
4430 return image;
4431}
4432
4433// Save image data as KTX file
4434// NOTE: By default KTX 1.1 spec is used, 2.0 is still on draft (01Oct2018)
4435// TODO: Review KTX saving, many things changed!
4436static int SaveKTX(Image image, const char *fileName)
4437{
4438 // KTX file Header (64 bytes)
4439 // v1.1 - https://www.khronos.org/opengles/sdk/tools/KTX/file_format_spec/
4440 // v2.0 - http://github.khronos.org/KTX-Specification/ - Final specs by 2021-04-18
4441 typedef struct {
4442 char id[12]; // Identifier: "«KTX 11»\r\n\x1A\n" // KTX 2.0: "«KTX 22»\r\n\x1A\n"
4443 unsigned int endianness; // Little endian: 0x01 0x02 0x03 0x04
4444 unsigned int glType; // For compressed textures, glType must equal 0
4445 unsigned int glTypeSize; // For compressed texture data, usually 1
4446 unsigned int glFormat; // For compressed textures is 0
4447 unsigned int glInternalFormat; // Compressed internal format
4448 unsigned int glBaseInternalFormat; // Same as glFormat (RGB, RGBA, ALPHA...) // KTX 2.0: UInt32 vkFormat
4449 unsigned int width; // Texture image width in pixels
4450 unsigned int height; // Texture image height in pixels
4451 unsigned int depth; // For 2D textures is 0
4452 unsigned int elements; // Number of array elements, usually 0
4453 unsigned int faces; // Cubemap faces, for no-cubemap = 1
4454 unsigned int mipmapLevels; // Non-mipmapped textures = 1
4455 unsigned int keyValueDataSize; // Used to encode any arbitrary data... // KTX 2.0: UInt32 levelOrder - ordering of the mipmap levels, usually 0
4456 // KTX 2.0: UInt32 supercompressionScheme - 0 (None), 1 (Crunch CRN), 2 (Zlib DEFLATE)...
4457 // KTX 2.0 defines additional header elements...
4458 } KTXHeader;
4459
4460 // Calculate file dataSize required
4461 int dataSize = sizeof(KTXHeader);
4462
4463 for (int i = 0, width = image.width, height = image.height; i < image.mipmaps; i++)
4464 {
4465 dataSize += GetPixelDataSize(width, height, image.format);
4466 width /= 2; height /= 2;
4467 }
4468
4469 unsigned char *fileData = RL_CALLOC(dataSize, 1);
4470 unsigned char *fileDataPtr = fileData;
4471
4472 KTXHeader ktxHeader = { 0 };
4473
4474 // KTX identifier (v1.1)
4475 //unsigned char id[12] = { '«', 'K', 'T', 'X', ' ', '1', '1', '»', '\r', '\n', '\x1A', '\n' };
4476 //unsigned char id[12] = { 0xAB, 0x4B, 0x54, 0x58, 0x20, 0x31, 0x31, 0xBB, 0x0D, 0x0A, 0x1A, 0x0A };
4477
4478 const char ktxIdentifier[12] = { 0xAB, 'K', 'T', 'X', ' ', '1', '1', 0xBB, '\r', '\n', 0x1A, '\n' };
4479
4480 // Get the image header
4481 memcpy(ktxHeader.id, ktxIdentifier, 12); // KTX 1.1 signature
4482 ktxHeader.endianness = 0;
4483 ktxHeader.glType = 0; // Obtained from image.format
4484 ktxHeader.glTypeSize = 1;
4485 ktxHeader.glFormat = 0; // Obtained from image.format
4486 ktxHeader.glInternalFormat = 0; // Obtained from image.format
4487 ktxHeader.glBaseInternalFormat = 0;
4488 ktxHeader.width = image.width;
4489 ktxHeader.height = image.height;
4490 ktxHeader.depth = 0;
4491 ktxHeader.elements = 0;
4492 ktxHeader.faces = 1;
4493 ktxHeader.mipmapLevels = image.mipmaps; // If it was 0, it means mipmaps should be generated on loading (not for compressed formats)
4494 ktxHeader.keyValueDataSize = 0; // No extra data after the header
4495
4496 rlGetGlTextureFormats(image.format, &ktxHeader.glInternalFormat, &ktxHeader.glFormat, &ktxHeader.glType); // rlgl module function
4497 ktxHeader.glBaseInternalFormat = ktxHeader.glFormat; // KTX 1.1 only
4498
4499 // NOTE: We can save into a .ktx all PixelFormats supported by raylib, including compressed formats like DXT, ETC or ASTC
4500
4501 if (ktxHeader.glFormat == -1) TRACELOG(LOG_WARNING, "IMAGE: GL format not supported for KTX export (%i)", ktxHeader.glFormat);
4502 else
4503 {
4504 memcpy(fileDataPtr, &ktxHeader, sizeof(KTXHeader));
4505 fileDataPtr += sizeof(KTXHeader);
4506
4507 int width = image.width;
4508 int height = image.height;
4509 int dataOffset = 0;
4510
4511 // Save all mipmaps data
4512 for (int i = 0; i < image.mipmaps; i++)
4513 {
4514 unsigned int dataSize = GetPixelDataSize(width, height, image.format);
4515
4516 memcpy(fileDataPtr, &dataSize, sizeof(unsigned int));
4517 memcpy(fileDataPtr + 4, (unsigned char *)image.data + dataOffset, dataSize);
4518
4519 width /= 2;
4520 height /= 2;
4521 dataOffset += dataSize;
4522 fileDataPtr += (4 + dataSize);
4523 }
4524 }
4525
4526 int success = SaveFileData(fileName, fileData, dataSize);
4527
4528 RL_FREE(fileData); // Free file data buffer
4529
4530 // If all data has been written correctly to file, success = 1
4531 return success;
4532}
4533#endif
4534
4535#if defined(SUPPORT_FILEFORMAT_PVR)
4536// Loading PVR image data (uncompressed or PVRT compression)
4537// NOTE: PVR v2 not supported, use PVR v3 instead
4538static Image LoadPVR(const unsigned char *fileData, unsigned int fileSize)
4539{
4540 unsigned char *fileDataPtr = (unsigned char *)fileData;
4541
4542 // Required extension:
4543 // GL_IMG_texture_compression_pvrtc
4544
4545 // Supported tokens (defined by extensions)
4546 // GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG 0x8C00
4547 // GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG 0x8C02
4548
4549#if 0 // Not used...
4550 // PVR file v2 Header (52 bytes)
4551 typedef struct {
4552 unsigned int headerLength;
4553 unsigned int height;
4554 unsigned int width;
4555 unsigned int numMipmaps;
4556 unsigned int flags;
4557 unsigned int dataLength;
4558 unsigned int bpp;
4559 unsigned int bitmaskRed;
4560 unsigned int bitmaskGreen;
4561 unsigned int bitmaskBlue;
4562 unsigned int bitmaskAlpha;
4563 unsigned int pvrTag;
4564 unsigned int numSurfs;
4565 } PVRHeaderV2;
4566#endif
4567
4568 // PVR file v3 Header (52 bytes)
4569 // NOTE: After it could be metadata (15 bytes?)
4570 typedef struct {
4571 char id[4];
4572 unsigned int flags;
4573 unsigned char channels[4]; // pixelFormat high part
4574 unsigned char channelDepth[4]; // pixelFormat low part
4575 unsigned int colourSpace;
4576 unsigned int channelType;
4577 unsigned int height;
4578 unsigned int width;
4579 unsigned int depth;
4580 unsigned int numSurfaces;
4581 unsigned int numFaces;
4582 unsigned int numMipmaps;
4583 unsigned int metaDataSize;
4584 } PVRHeaderV3;
4585
4586#if 0 // Not used...
4587 // Metadata (usually 15 bytes)
4588 typedef struct {
4589 unsigned int devFOURCC;
4590 unsigned int key;
4591 unsigned int dataSize; // Not used?
4592 unsigned char *data; // Not used?
4593 } PVRMetadata;
4594#endif
4595
4596 Image image = { 0 };
4597
4598 if (fileDataPtr != NULL)
4599 {
4600 // Check PVR image version
4601 unsigned char pvrVersion = fileDataPtr[0];
4602
4603 // Load different PVR data formats
4604 if (pvrVersion == 0x50)
4605 {
4606 PVRHeaderV3 *pvrHeader = (PVRHeaderV3 *)fileDataPtr;
4607
4608 if ((pvrHeader->id[0] != 'P') || (pvrHeader->id[1] != 'V') || (pvrHeader->id[2] != 'R') || (pvrHeader->id[3] != 3))
4609 {
4610 TRACELOG(LOG_WARNING, "IMAGE: PVR file data not valid");
4611 }
4612 else
4613 {
4614 fileDataPtr += sizeof(PVRHeaderV3); // Skip header
4615
4616 image.width = pvrHeader->width;
4617 image.height = pvrHeader->height;
4618 image.mipmaps = pvrHeader->numMipmaps;
4619
4620 // Check data format
4621 if (((pvrHeader->channels[0] == 'l') && (pvrHeader->channels[1] == 0)) && (pvrHeader->channelDepth[0] == 8)) image.format = PIXELFORMAT_UNCOMPRESSED_GRAYSCALE;
4622 else if (((pvrHeader->channels[0] == 'l') && (pvrHeader->channels[1] == 'a')) && ((pvrHeader->channelDepth[0] == 8) && (pvrHeader->channelDepth[1] == 8))) image.format = PIXELFORMAT_UNCOMPRESSED_GRAY_ALPHA;
4623 else if ((pvrHeader->channels[0] == 'r') && (pvrHeader->channels[1] == 'g') && (pvrHeader->channels[2] == 'b'))
4624 {
4625 if (pvrHeader->channels[3] == 'a')
4626 {
4627 if ((pvrHeader->channelDepth[0] == 5) && (pvrHeader->channelDepth[1] == 5) && (pvrHeader->channelDepth[2] == 5) && (pvrHeader->channelDepth[3] == 1)) image.format = PIXELFORMAT_UNCOMPRESSED_R5G5B5A1;
4628 else if ((pvrHeader->channelDepth[0] == 4) && (pvrHeader->channelDepth[1] == 4) && (pvrHeader->channelDepth[2] == 4) && (pvrHeader->channelDepth[3] == 4)) image.format = PIXELFORMAT_UNCOMPRESSED_R4G4B4A4;
4629 else if ((pvrHeader->channelDepth[0] == 8) && (pvrHeader->channelDepth[1] == 8) && (pvrHeader->channelDepth[2] == 8) && (pvrHeader->channelDepth[3] == 8)) image.format = PIXELFORMAT_UNCOMPRESSED_R8G8B8A8;
4630 }
4631 else if (pvrHeader->channels[3] == 0)
4632 {
4633 if ((pvrHeader->channelDepth[0] == 5) && (pvrHeader->channelDepth[1] == 6) && (pvrHeader->channelDepth[2] == 5)) image.format = PIXELFORMAT_UNCOMPRESSED_R5G6B5;
4634 else if ((pvrHeader->channelDepth[0] == 8) && (pvrHeader->channelDepth[1] == 8) && (pvrHeader->channelDepth[2] == 8)) image.format = PIXELFORMAT_UNCOMPRESSED_R8G8B8;
4635 }
4636 }
4637 else if (pvrHeader->channels[0] == 2) image.format = PIXELFORMAT_COMPRESSED_PVRT_RGB;
4638 else if (pvrHeader->channels[0] == 3) image.format = PIXELFORMAT_COMPRESSED_PVRT_RGBA;
4639
4640 fileDataPtr += pvrHeader->metaDataSize; // Skip meta data header
4641
4642 // Calculate data size (depends on format)
4643 int bpp = 0;
4644 switch (image.format)
4645 {
4646 case PIXELFORMAT_UNCOMPRESSED_GRAYSCALE: bpp = 8; break;
4650 case PIXELFORMAT_UNCOMPRESSED_R4G4B4A4: bpp = 16; break;
4651 case PIXELFORMAT_UNCOMPRESSED_R8G8B8A8: bpp = 32; break;
4652 case PIXELFORMAT_UNCOMPRESSED_R8G8B8: bpp = 24; break;
4654 case PIXELFORMAT_COMPRESSED_PVRT_RGBA: bpp = 4; break;
4655 default: break;
4656 }
4657
4658 int dataSize = image.width*image.height*bpp/8; // Total data size in bytes
4659 image.data = (unsigned char *)RL_MALLOC(dataSize*sizeof(unsigned char));
4660
4661 memcpy(image.data, fileDataPtr, dataSize);
4662 }
4663 }
4664 else if (pvrVersion == 52) TRACELOG(LOG_INFO, "IMAGE: PVRv2 format not supported, update your files to PVRv3");
4665 }
4666
4667 return image;
4668}
4669#endif
4670
4671#if defined(SUPPORT_FILEFORMAT_ASTC)
4672// Load ASTC compressed image data (ASTC compression)
4673static Image LoadASTC(const unsigned char *fileData, unsigned int fileSize)
4674{
4675 unsigned char *fileDataPtr = (unsigned char *)fileData;
4676
4677 // Required extensions:
4678 // GL_KHR_texture_compression_astc_hdr
4679 // GL_KHR_texture_compression_astc_ldr
4680
4681 // Supported tokens (defined by extensions)
4682 // GL_COMPRESSED_RGBA_ASTC_4x4_KHR 0x93b0
4683 // GL_COMPRESSED_RGBA_ASTC_8x8_KHR 0x93b7
4684
4685 // ASTC file Header (16 bytes)
4686 typedef struct {
4687 unsigned char id[4]; // Signature: 0x13 0xAB 0xA1 0x5C
4688 unsigned char blockX; // Block X dimensions
4689 unsigned char blockY; // Block Y dimensions
4690 unsigned char blockZ; // Block Z dimensions (1 for 2D images)
4691 unsigned char width[3]; // Image width in pixels (24bit value)
4692 unsigned char height[3]; // Image height in pixels (24bit value)
4693 unsigned char length[3]; // Image Z-size (1 for 2D images)
4694 } ASTCHeader;
4695
4696 Image image = { 0 };
4697
4698 if (fileDataPtr != NULL)
4699 {
4700 ASTCHeader *astcHeader = (ASTCHeader *)fileDataPtr;
4701
4702 if ((astcHeader->id[3] != 0x5c) || (astcHeader->id[2] != 0xa1) || (astcHeader->id[1] != 0xab) || (astcHeader->id[0] != 0x13))
4703 {
4704 TRACELOG(LOG_WARNING, "IMAGE: ASTC file data not valid");
4705 }
4706 else
4707 {
4708 fileDataPtr += sizeof(ASTCHeader); // Skip header
4709
4710 // NOTE: Assuming Little Endian (could it be wrong?)
4711 image.width = 0x00000000 | ((int)astcHeader->width[2] << 16) | ((int)astcHeader->width[1] << 8) | ((int)astcHeader->width[0]);
4712 image.height = 0x00000000 | ((int)astcHeader->height[2] << 16) | ((int)astcHeader->height[1] << 8) | ((int)astcHeader->height[0]);
4713
4714 TRACELOGD("IMAGE: ASTC file data info:");
4715 TRACELOGD(" > Image width: %i", image.width);
4716 TRACELOGD(" > Image height: %i", image.height);
4717 TRACELOGD(" > Image blocks: %ix%i", astcHeader->blockX, astcHeader->blockY);
4718
4719 image.mipmaps = 1; // NOTE: ASTC format only contains one mipmap level
4720
4721 // NOTE: Each block is always stored in 128bit so we can calculate the bpp
4722 int bpp = 128/(astcHeader->blockX*astcHeader->blockY);
4723
4724 // NOTE: Currently we only support 2 blocks configurations: 4x4 and 8x8
4725 if ((bpp == 8) || (bpp == 2))
4726 {
4727 int dataSize = image.width*image.height*bpp/8; // Data size in bytes
4728
4729 image.data = (unsigned char *)RL_MALLOC(dataSize*sizeof(unsigned char));
4730
4731 memcpy(image.data, fileDataPtr, dataSize);
4732
4733 if (bpp == 8) image.format = PIXELFORMAT_COMPRESSED_ASTC_4x4_RGBA;
4734 else if (bpp == 2) image.format = PIXELFORMAT_COMPRESSED_ASTC_8x8_RGBA;
4735 }
4736 else TRACELOG(LOG_WARNING, "IMAGE: ASTC block size configuration not supported");
4737 }
4738 }
4739
4740 return image;
4741}
4742#endif
4743
4744// Get pixel data from image as Vector4 array (float normalized)
4745static Vector4 *LoadImageDataNormalized(Image image)
4746{
4747 Vector4 *pixels = (Vector4 *)RL_MALLOC(image.width*image.height*sizeof(Vector4));
4748
4749 if (image.format >= PIXELFORMAT_COMPRESSED_DXT1_RGB) TRACELOG(LOG_WARNING, "IMAGE: Pixel data retrieval not supported for compressed image formats");
4750 else
4751 {
4752 for (int i = 0, k = 0; i < image.width*image.height; i++)
4753 {
4754 switch (image.format)
4755 {
4757 {
4758 pixels[i].x = (float)((unsigned char *)image.data)[i]/255.0f;
4759 pixels[i].y = (float)((unsigned char *)image.data)[i]/255.0f;
4760 pixels[i].z = (float)((unsigned char *)image.data)[i]/255.0f;
4761 pixels[i].w = 1.0f;
4762
4763 } break;
4765 {
4766 pixels[i].x = (float)((unsigned char *)image.data)[k]/255.0f;
4767 pixels[i].y = (float)((unsigned char *)image.data)[k]/255.0f;
4768 pixels[i].z = (float)((unsigned char *)image.data)[k]/255.0f;
4769 pixels[i].w = (float)((unsigned char *)image.data)[k + 1]/255.0f;
4770
4771 k += 2;
4772 } break;
4774 {
4775 unsigned short pixel = ((unsigned short *)image.data)[i];
4776
4777 pixels[i].x = (float)((pixel & 0b1111100000000000) >> 11)*(1.0f/31);
4778 pixels[i].y = (float)((pixel & 0b0000011111000000) >> 6)*(1.0f/31);
4779 pixels[i].z = (float)((pixel & 0b0000000000111110) >> 1)*(1.0f/31);
4780 pixels[i].w = ((pixel & 0b0000000000000001) == 0)? 0.0f : 1.0f;
4781
4782 } break;
4784 {
4785 unsigned short pixel = ((unsigned short *)image.data)[i];
4786
4787 pixels[i].x = (float)((pixel & 0b1111100000000000) >> 11)*(1.0f/31);
4788 pixels[i].y = (float)((pixel & 0b0000011111100000) >> 5)*(1.0f/63);
4789 pixels[i].z = (float)(pixel & 0b0000000000011111)*(1.0f/31);
4790 pixels[i].w = 1.0f;
4791
4792 } break;
4794 {
4795 unsigned short pixel = ((unsigned short *)image.data)[i];
4796
4797 pixels[i].x = (float)((pixel & 0b1111000000000000) >> 12)*(1.0f/15);
4798 pixels[i].y = (float)((pixel & 0b0000111100000000) >> 8)*(1.0f/15);
4799 pixels[i].z = (float)((pixel & 0b0000000011110000) >> 4)*(1.0f/15);
4800 pixels[i].w = (float)(pixel & 0b0000000000001111)*(1.0f/15);
4801
4802 } break;
4804 {
4805 pixels[i].x = (float)((unsigned char *)image.data)[k]/255.0f;
4806 pixels[i].y = (float)((unsigned char *)image.data)[k + 1]/255.0f;
4807 pixels[i].z = (float)((unsigned char *)image.data)[k + 2]/255.0f;
4808 pixels[i].w = (float)((unsigned char *)image.data)[k + 3]/255.0f;
4809
4810 k += 4;
4811 } break;
4813 {
4814 pixels[i].x = (float)((unsigned char *)image.data)[k]/255.0f;
4815 pixels[i].y = (float)((unsigned char *)image.data)[k + 1]/255.0f;
4816 pixels[i].z = (float)((unsigned char *)image.data)[k + 2]/255.0f;
4817 pixels[i].w = 1.0f;
4818
4819 k += 3;
4820 } break;
4822 {
4823 pixels[i].x = ((float *)image.data)[k];
4824 pixels[i].y = 0.0f;
4825 pixels[i].z = 0.0f;
4826 pixels[i].w = 1.0f;
4827
4828 } break;
4830 {
4831 pixels[i].x = ((float *)image.data)[k];
4832 pixels[i].y = ((float *)image.data)[k + 1];
4833 pixels[i].z = ((float *)image.data)[k + 2];
4834 pixels[i].w = 1.0f;
4835
4836 k += 3;
4837 } break;
4839 {
4840 pixels[i].x = ((float *)image.data)[k];
4841 pixels[i].y = ((float *)image.data)[k + 1];
4842 pixels[i].z = ((float *)image.data)[k + 2];
4843 pixels[i].w = ((float *)image.data)[k + 3];
4844
4845 k += 4;
4846 }
4847 default: break;
4848 }
4849 }
4850 }
4851
4852 return pixels;
4853}
4854
4855#endif // SUPPORT_MODULE_RTEXTURES
#define SUPPORT_FILEFORMAT_PNG
Definition: config.h:151
#define SUPPORT_FILEFORMAT_GIF
Definition: config.h:155
#define NULL
Definition: miniaudio.h:3718
int qoi_write(const char *filename, const void *data, const qoi_desc *desc)
void * qoi_decode(const void *data, int size, qoi_desc *desc, int channels)
#define QOI_SRGB
Definition: qoi.h:253
#define RL_FREE(p)
Definition: raudio.h:67
#define RL_MALLOC(sz)
raudio v1.0 - A simple and easy-to-use audio library based on miniaudio
Definition: raudio.h:61
#define RL_CALLOC(n, sz)
Definition: raudio.h:64
#define TRACELOG(level,...)
Definition: raygui.h:222
RLAPI Vector2 MeasureTextEx(Font font, const char *text, float fontSize, float spacing)
Definition: rtext.c:1157
RLAPI bool SaveFileData(const char *fileName, void *data, unsigned int bytesToWrite)
Definition: utils.c:237
@ CUBEMAP_LAYOUT_LINE_VERTICAL
Definition: raylib.h:825
@ CUBEMAP_LAYOUT_CROSS_THREE_BY_FOUR
Definition: raylib.h:827
@ CUBEMAP_LAYOUT_AUTO_DETECT
Definition: raylib.h:824
@ CUBEMAP_LAYOUT_CROSS_FOUR_BY_THREE
Definition: raylib.h:828
@ CUBEMAP_LAYOUT_PANORAMA
Definition: raylib.h:829
@ CUBEMAP_LAYOUT_LINE_HORIZONTAL
Definition: raylib.h:826
@ LOG_INFO
Definition: raylib.h:514
@ LOG_WARNING
Definition: raylib.h:515
#define BLANK
Definition: raylib.h:172
@ PIXELFORMAT_COMPRESSED_ETC1_RGB
Definition: raylib.h:793
@ PIXELFORMAT_COMPRESSED_ASTC_4x4_RGBA
Definition: raylib.h:798
@ PIXELFORMAT_UNCOMPRESSED_R32
Definition: raylib.h:786
@ PIXELFORMAT_COMPRESSED_DXT1_RGB
Definition: raylib.h:789
@ PIXELFORMAT_UNCOMPRESSED_R8G8B8
Definition: raylib.h:782
@ PIXELFORMAT_UNCOMPRESSED_R32G32B32A32
Definition: raylib.h:788
@ PIXELFORMAT_UNCOMPRESSED_R32G32B32
Definition: raylib.h:787
@ PIXELFORMAT_COMPRESSED_DXT3_RGBA
Definition: raylib.h:791
@ PIXELFORMAT_UNCOMPRESSED_GRAYSCALE
Definition: raylib.h:779
@ PIXELFORMAT_COMPRESSED_DXT5_RGBA
Definition: raylib.h:792
@ PIXELFORMAT_COMPRESSED_ETC2_EAC_RGBA
Definition: raylib.h:795
@ PIXELFORMAT_COMPRESSED_ASTC_8x8_RGBA
Definition: raylib.h:799
@ PIXELFORMAT_UNCOMPRESSED_R5G6B5
Definition: raylib.h:781
@ PIXELFORMAT_COMPRESSED_DXT1_RGBA
Definition: raylib.h:790
@ PIXELFORMAT_COMPRESSED_PVRT_RGB
Definition: raylib.h:796
@ PIXELFORMAT_COMPRESSED_ETC2_RGB
Definition: raylib.h:794
@ PIXELFORMAT_UNCOMPRESSED_R8G8B8A8
Definition: raylib.h:785
@ PIXELFORMAT_UNCOMPRESSED_R5G5B5A1
Definition: raylib.h:783
@ PIXELFORMAT_UNCOMPRESSED_R4G4B4A4
Definition: raylib.h:784
@ PIXELFORMAT_COMPRESSED_PVRT_RGBA
Definition: raylib.h:797
@ PIXELFORMAT_UNCOMPRESSED_GRAY_ALPHA
Definition: raylib.h:780
RLAPI unsigned char * LoadFileData(const char *fileName, unsigned int *bytesRead)
Definition: utils.c:182
#define RL_REALLOC(ptr, sz)
Definition: raylib.h:120
#define MAGENTA
Definition: raylib.h:173
#define BLACK
Definition: raylib.h:171
RLAPI Font GetFontDefault(void)
Definition: rtext.c:289
#define WHITE
Definition: raylib.h:170
RLAPI int GetScreenWidth(void)
Definition: rcore.c:1617
RLAPI bool SaveFileText(const char *fileName, char *text)
Definition: utils.c:376
RLAPI const char * GetFileNameWithoutExt(const char *filePath)
Definition: rcore.c:2911
@ NPATCH_NINE_PATCH
Definition: raylib.h:883
@ NPATCH_THREE_PATCH_HORIZONTAL
Definition: raylib.h:885
@ NPATCH_THREE_PATCH_VERTICAL
Definition: raylib.h:884
@ TEXTURE_FILTER_ANISOTROPIC_8X
Definition: raylib.h:810
@ TEXTURE_FILTER_ANISOTROPIC_4X
Definition: raylib.h:809
@ TEXTURE_FILTER_POINT
Definition: raylib.h:806
@ TEXTURE_FILTER_BILINEAR
Definition: raylib.h:807
@ TEXTURE_FILTER_ANISOTROPIC_16X
Definition: raylib.h:811
@ TEXTURE_FILTER_TRILINEAR
Definition: raylib.h:808
RLAPI int GetScreenHeight(void)
Definition: rcore.c:1623
RLAPI int GetCodepoint(const char *text, int *bytesProcessed)
Definition: rtext.c:1741
RLAPI int GetRandomValue(int min, int max)
Definition: rcore.c:2779
RLAPI int GetGlyphIndex(Font font, int codepoint)
Definition: rtext.c:1212
RLAPI const char * GetFileExtension(const char *fileName)
Definition: rcore.c:2882
@ TEXTURE_WRAP_CLAMP
Definition: raylib.h:817
@ TEXTURE_WRAP_MIRROR_REPEAT
Definition: raylib.h:818
@ TEXTURE_WRAP_REPEAT
Definition: raylib.h:816
@ TEXTURE_WRAP_MIRROR_CLAMP
Definition: raylib.h:819
RLAPI bool IsFileExtension(const char *fileName, const char *ext)
Definition: rcore.c:2818
#define DEG2RAD
Definition: raylib.h:106
#define RL_TEXTURE_FILTER_MIP_NEAREST
Definition: rlgl.h:241
RLAPI void rlNormal3f(float x, float y, float z)
@ RL_ATTACHMENT_COLOR_CHANNEL0
Definition: rlgl.h:294
@ RL_ATTACHMENT_DEPTH
Definition: rlgl.h:302
RLAPI unsigned int rlLoadTexture(const void *data, int width, int height, int format, int mipmapCount)
RLAPI void rlUpdateTexture(unsigned int id, int offsetX, int offsetY, int width, int height, int format, const void *data)
#define RL_QUADS
Definition: rlgl.h:260
RLAPI void rlEnd(void)
#define RL_TEXTURE_WRAP_T
Definition: rlgl.h:235
RLAPI void rlUnloadFramebuffer(unsigned int id)
RLAPI void * rlReadTexturePixels(unsigned int id, int width, int height, int format)
RLAPI void rlColor4ub(unsigned char r, unsigned char g, unsigned char b, unsigned char a)
#define RL_TEXTURE_FILTER_MIP_LINEAR
Definition: rlgl.h:244
#define RL_TEXTURE_FILTER_LINEAR_MIP_NEAREST
Definition: rlgl.h:243
RLAPI void rlGenTextureMipmaps(unsigned int id, int width, int height, int format, int *mipmaps)
RLAPI unsigned char * rlReadScreenPixels(int width, int height)
RLAPI void rlPushMatrix(void)
RLAPI const char * rlGetPixelFormatName(unsigned int format)
RLAPI unsigned int rlLoadTextureCubemap(const void *data, int size, int format)
RLAPI void rlVertex2f(float x, float y)
#define RL_TEXTURE_FILTER_LINEAR
Definition: rlgl.h:240
#define RL_TEXTURE_MIN_FILTER
Definition: rlgl.h:237
RLAPI unsigned int rlLoadTextureDepth(int width, int height, bool useRenderBuffer)
RLAPI void rlEnableFramebuffer(unsigned int id)
RLAPI void rlBegin(int mode)
RLAPI void rlDisableFramebuffer(void)
RLAPI void rlFramebufferAttach(unsigned int fboId, unsigned int texId, int attachType, int texType, int mipLevel)
RLAPI void rlTexCoord2f(float x, float y)
#define RL_TEXTURE_FILTER_NEAREST
Definition: rlgl.h:239
RLAPI void rlRotatef(float angle, float x, float y, float z)
RLAPI unsigned int rlLoadFramebuffer(int width, int height)
RLAPI void rlUnloadTexture(unsigned int id)
#define RL_TEXTURE_WRAP_MIRROR_CLAMP
Definition: rlgl.h:250
#define RL_TEXTURE_MAG_FILTER
Definition: rlgl.h:236
RLAPI void rlTextureParameters(unsigned int id, int param, int value)
#define RL_TEXTURE_WRAP_S
Definition: rlgl.h:234
#define RL_TEXTURE_FILTER_ANISOTROPIC
Definition: rlgl.h:245
RLAPI void rlPopMatrix(void)
RLAPI void rlSetTexture(unsigned int id)
RLAPI void rlGetGlTextureFormats(int format, int *glInternalFormat, int *glFormat, int *glType)
RLAPI bool rlCheckRenderBatchLimit(int vCount)
RLAPI bool rlFramebufferComplete(unsigned int id)
#define RL_TEXTURE_WRAP_REPEAT
Definition: rlgl.h:247
@ RL_ATTACHMENT_TEXTURE2D
Definition: rlgl.h:313
@ RL_ATTACHMENT_RENDERBUFFER
Definition: rlgl.h:314
RLAPI void rlTranslatef(float x, float y, float z)
#define TRACELOGD(...)
Definition: rlgl.h:130
#define RL_TEXTURE_WRAP_CLAMP
Definition: rlgl.h:248
#define RL_TEXTURE_WRAP_MIRROR_REPEAT
Definition: rlgl.h:249
Color ColorAlpha(Color color, float alpha)
Definition: rtextures.c:3798
void DrawTextureQuad(Texture2D texture, Vector2 tiling, Vector2 offset, Rectangle quad, Color tint)
Definition: rtextures.c:3216
Texture2D LoadTexture(const char *fileName)
Definition: rtextures.c:2874
Image ImageText(const char *text, int fontSize, Color color)
Definition: rtextures.c:1136
int ColorToInt(Color color)
Definition: rtextures.c:3678
Image GenImageGradientH(int width, int height, Color left, Color right)
Definition: rtextures.c:652
int GetPixelDataSize(int width, int height, int format)
Definition: rtextures.c:4025
void ImageClearBackground(Image *dst, Color color)
Definition: rtextures.c:2412
Image GenImageChecked(int width, int height, int checksX, int checksY, Color col1, Color col2)
Definition: rtextures.c:717
void DrawTextureRec(Texture2D texture, Rectangle source, Vector2 position, Color tint)
Definition: rtextures.c:3205
Image GenImageGradientRadial(int width, int height, float density, Color inner, Color outer)
Definition: rtextures.c:680
Vector4 ColorNormalize(Color color)
Definition: rtextures.c:3684
void DrawTextureNPatch(Texture2D texture, NPatchInfo nPatchInfo, Rectangle dest, Vector2 origin, float rotation, Color tint)
Definition: rtextures.c:3437
void UnloadRenderTexture(RenderTexture2D target)
Definition: rtextures.c:3045
void ImageDrawRectangleLines(Image *dst, Rectangle rec, int thick, Color color)
Definition: rtextures.c:2721
Image GenImageGradientV(int width, int height, Color top, Color bottom)
Definition: rtextures.c:624
#define FOURCC_DXT5
#define PIXELFORMAT_UNCOMPRESSED_R5G5B5A1_ALPHA_THRESHOLD
Definition: rtextures.c:162
Color ColorFromHSV(float hue, float saturation, float value)
Definition: rtextures.c:3766
Color GetPixelColor(void *srcPtr, int format)
Definition: rtextures.c:3875
void ImageColorGrayscale(Image *image)
Definition: rtextures.c:1936
void ImageResizeCanvas(Image *image, int newWidth, int newHeight, int offsetX, int offsetY, Color fill)
Definition: rtextures.c:1503
#define MIN(a, b)
void UnloadTexture(Texture2D texture)
Definition: rtextures.c:3034
Image GenImageWhiteNoise(int width, int height, float factor)
Definition: rtextures.c:742
Color ColorAlphaBlend(Color dst, Color src, Color tint)
Definition: rtextures.c:3807
void ImageDrawPixel(Image *dst, int x, int y, Color color)
Definition: rtextures.c:2432
void ImageDither(Image *image, int rBpp, int gBpp, int bBpp, int aBpp)
Definition: rtextures.c:1627
#define FOURCC_DXT1
void ImageColorTint(Image *image, Color color)
Definition: rtextures.c:1870
void ImageDrawRectangleV(Image *dst, Vector2 position, Vector2 size, Color color)
Definition: rtextures.c:2686
RenderTexture2D LoadRenderTexture(int width, int height)
Definition: rtextures.c:2995
void ImageDrawCircle(Image *dst, int centerX, int centerY, int radius, Color color)
Definition: rtextures.c:2647
Image LoadImageFromMemory(const char *fileType, const unsigned char *fileData, int dataSize)
Definition: rtextures.c:305
#define COLOR_EQUAL(col1, col2)
Image ImageCopy(Image image)
Definition: rtextures.c:833
void UnloadImage(Image image)
Definition: rtextures.c:457
Color GetColor(unsigned int hexValue)
Definition: rtextures.c:3862
Texture2D LoadTextureFromImage(Image image)
Definition: rtextures.c:2891
void UpdateTexture(Texture2D texture, const void *pixels)
Definition: rtextures.c:3060
void ImageDrawCircleV(Image *dst, Vector2 center, int radius, Color color)
Definition: rtextures.c:2674
Vector3 ColorToHSV(Color color)
Definition: rtextures.c:3711
void ImageDrawLine(Image *dst, int startPosX, int startPosY, int endPosX, int endPosY, Color color)
Definition: rtextures.c:2550
void UpdateTextureRec(Texture2D texture, Rectangle rec, const void *pixels)
Definition: rtextures.c:3067
void DrawTexture(Texture2D texture, int posX, int posY, Color tint)
Definition: rtextures.c:3183
Image LoadImageAnim(const char *fileName, int *frames)
Definition: rtextures.c:268
void ImageMipmaps(Image *image)
Definition: rtextures.c:1558
void DrawTexturePoly(Texture2D texture, Vector2 center, Vector2 *points, Vector2 *texcoords, int pointCount, Color tint)
Definition: rtextures.c:3638
void ImageAlphaMask(Image *image, Image alphaMask)
Definition: rtextures.c:1325
void ImageResize(Image *image, int newWidth, int newHeight)
Definition: rtextures.c:1412
bool ExportImageAsCode(Image image, const char *fileName)
Definition: rtextures.c:545
Color ColorFromNormalized(Vector4 normalized)
Definition: rtextures.c:3697
Image GenImageCellular(int width, int height, int tileSize)
Definition: rtextures.c:764
void DrawTextureV(Texture2D texture, Vector2 position, Color tint)
Definition: rtextures.c:3189
Rectangle GetImageAlphaBorder(Image image, float threshold)
Definition: rtextures.c:2266
void DrawTexturePro(Texture2D texture, Rectangle source, Rectangle dest, Vector2 origin, float rotation, Color tint)
Definition: rtextures.c:3313
void ImageDrawText(Image *dst, const char *text, int posX, int posY, int fontSize, Color color)
Definition: rtextures.c:2846
Image GenImageColor(int width, int height, Color color)
Definition: rtextures.c:605
Image LoadImageRaw(const char *fileName, int width, int height, int format, int headerSize)
Definition: rtextures.c:236
void SetTextureFilter(Texture2D texture, int filter)
Definition: rtextures.c:3084
void ImageColorReplace(Image *image, Color color, Color replace)
Definition: rtextures.c:2044
#define TEXT_BYTES_PER_LINE
Image ImageTextEx(Font font, const char *text, float fontSize, float spacing, Color tint)
Definition: rtextures.c:1152
bool ExportImage(Image image, const char *fileName)
Definition: rtextures.c:464
void UnloadImagePalette(Color *colors)
Definition: rtextures.c:2259
Image LoadImage(const char *fileName)
Definition: rtextures.c:207
void ImageColorInvert(Image *image)
Definition: rtextures.c:1909
void UnloadImageColors(Color *colors)
Definition: rtextures.c:2253
void ImageCrop(Image *image, Rectangle crop)
Definition: rtextures.c:892
void ImageDrawRectangle(Image *dst, int posX, int posY, int width, int height, Color color)
Definition: rtextures.c:2680
void ImageResizeNN(Image *image, int newWidth, int newHeight)
Definition: rtextures.c:1463
void ImageColorBrightness(Image *image, int brightness)
Definition: rtextures.c:2001
void SetTextureWrap(Texture2D texture, int wrap)
Definition: rtextures.c:3150
void ImageDraw(Image *dst, Image src, Rectangle srcRec, Rectangle dstRec, Color tint)
Definition: rtextures.c:2731
void ImageAlphaClear(Image *image, Color color, float threshold)
Definition: rtextures.c:1233
Image LoadImageFromScreen(void)
Definition: rtextures.c:443
Color GetImageColor(Image image, int x, int y)
Definition: rtextures.c:2306
void ImageDrawLineV(Image *dst, Vector2 start, Vector2 end, Color color)
Definition: rtextures.c:2641
Color * LoadImageColors(Image image)
Definition: rtextures.c:2080
void GenTextureMipmaps(Texture2D *texture)
Definition: rtextures.c:3076
Image LoadImageFromTexture(Texture2D texture)
Definition: rtextures.c:412
void ImageFlipHorizontal(Image *image)
Definition: rtextures.c:1761
void ImageRotateCW(Image *image)
Definition: rtextures.c:1806
void ImageDrawTextEx(Image *dst, Font font, const char *text, Vector2 position, float fontSize, float spacing, Color tint)
Definition: rtextures.c:2858
void ImageFlipVertical(Image *image)
Definition: rtextures.c:1737
TextureCubemap LoadTextureCubemap(Image image, int layout)
Definition: rtextures.c:2910
Color * LoadImagePalette(Image image, int maxPaletteSize, int *colorCount)
Definition: rtextures.c:2198
void ImageColorContrast(Image *image, float contrast)
Definition: rtextures.c:1943
Image ImageFromImage(Image image, Rectangle rec)
Definition: rtextures.c:870
void ImageDrawRectangleRec(Image *dst, Rectangle rec, Color color)
Definition: rtextures.c:2692
void ImageAlphaCrop(Image *image, float threshold)
Definition: rtextures.c:1220
void ImageRotateCCW(Image *image)
Definition: rtextures.c:1838
void DrawTextureEx(Texture2D texture, Vector2 position, float rotation, float scale, Color tint)
Definition: rtextures.c:3195
void ImageAlphaPremultiply(Image *image)
Definition: rtextures.c:1374
Color Fade(Color color, float alpha)
Definition: rtextures.c:3669
void SetPixelColor(void *dstPtr, Color color, int format)
Definition: rtextures.c:3943
void ImageFormat(Image *image, int newFormat)
Definition: rtextures.c:943
void DrawTextureTiled(Texture2D texture, Rectangle source, Rectangle dest, Vector2 origin, float rotation, float scale, Color tint)
Definition: rtextures.c:3228
void ImageDrawPixelV(Image *dst, Vector2 position, Color color)
Definition: rtextures.c:2544
void ImageToPOT(Image *image, Color fill)
Definition: rtextures.c:1120
#define FOURCC_DXT3
STBIDEF float * stbi_loadf_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *channels_in_file, int desired_channels)
STBIDEF stbi_uc * stbi_load_gif_from_memory(stbi_uc const *buffer, int len, int **delays, int *x, int *y, int *z, int *comp, int req_comp)
STBIDEF stbi_uc * stbi_load_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *channels_in_file, int desired_channels)
STBIRDEF int stbir_resize_uint8(const unsigned char *input_pixels, int input_w, int input_h, int input_stride_in_bytes, unsigned char *output_pixels, int output_w, int output_h, int output_stride_in_bytes, int num_channels)
STBIWDEF int stbi_write_jpg(char const *filename, int x, int y, int comp, const void *data, int quality)
STBIWDEF int stbi_write_bmp(char const *filename, int w, int h, int comp, const void *data)
STBIWDEF int stbi_write_tga(char const *filename, int w, int h, int comp, const void *data)
Definition: raylib.h:220
unsigned char a
Definition: raylib.h:224
unsigned char b
Definition: raylib.h:223
unsigned char r
Definition: raylib.h:221
unsigned char g
Definition: raylib.h:222
Definition: raylib.h:289
Rectangle * recs
Definition: raylib.h:294
Texture2D texture
Definition: raylib.h:293
int baseSize
Definition: raylib.h:290
GlyphInfo * glyphs
Definition: raylib.h:295
Image image
Definition: raylib.h:285
int offsetY
Definition: raylib.h:283
int offsetX
Definition: raylib.h:282
int advanceX
Definition: raylib.h:284
Definition: raylib.h:236
void * data
Definition: raylib.h:237
int format
Definition: raylib.h:241
int height
Definition: raylib.h:239
int width
Definition: raylib.h:238
int mipmaps
Definition: raylib.h:240
int right
Definition: raylib.h:274
int top
Definition: raylib.h:273
int layout
Definition: raylib.h:276
Rectangle source
Definition: raylib.h:271
int left
Definition: raylib.h:272
int bottom
Definition: raylib.h:275
float height
Definition: raylib.h:232
float x
Definition: raylib.h:229
float y
Definition: raylib.h:230
float width
Definition: raylib.h:231
Texture depth
Definition: raylib.h:263
Texture texture
Definition: raylib.h:262
unsigned int id
Definition: raylib.h:261
int width
Definition: raylib.h:247
int mipmaps
Definition: raylib.h:249
int height
Definition: raylib.h:248
int format
Definition: raylib.h:250
unsigned int id
Definition: raylib.h:246
float x
Definition: physac.h:130
float y
Definition: physac.h:131
float x
Definition: raylib.h:195
float y
Definition: raylib.h:196
float z
Definition: raylib.h:197
float x
Definition: raylib.h:202
float y
Definition: raylib.h:203
float w
Definition: raylib.h:205
float z
Definition: raylib.h:204
Definition: qoi.h:256
unsigned int width
Definition: qoi.h:257
unsigned int height
Definition: qoi.h:258
unsigned char colorspace
Definition: qoi.h:260
unsigned char channels
Definition: qoi.h:259