1 /**********************************************************************************************
3 * raylib.textures - Basic functions to load and draw Textures (2d)
7 * #define SUPPORT_FILEFORMAT_BMP
8 * #define SUPPORT_FILEFORMAT_PNG
9 * #define SUPPORT_FILEFORMAT_TGA
10 * #define SUPPORT_FILEFORMAT_JPG
11 * #define SUPPORT_FILEFORMAT_GIF
12 * #define SUPPORT_FILEFORMAT_PSD
13 * #define SUPPORT_FILEFORMAT_PIC
14 * #define SUPPORT_FILEFORMAT_HDR
15 * #define SUPPORT_FILEFORMAT_DDS
16 * #define SUPPORT_FILEFORMAT_PKM
17 * #define SUPPORT_FILEFORMAT_KTX
18 * #define SUPPORT_FILEFORMAT_PVR
19 * #define SUPPORT_FILEFORMAT_ASTC
20 * Select desired fileformats to be supported for image data loading. Some of those formats are
21 * supported by default, to remove support, just comment unrequired #define in this module
23 * #define SUPPORT_IMAGE_EXPORT
24 * Support image export in multiple file formats
26 * #define SUPPORT_IMAGE_MANIPULATION
27 * Support multiple image editing functions to scale, adjust colors, flip, draw on images, crop...
28 * If not defined only three image editing functions supported: ImageFormat(), ImageAlphaMask(), ImageToPOT()
30 * #define SUPPORT_IMAGE_GENERATION
31 * Support procedural image generation functionality (gradient, spot, perlin-noise, cellular)
34 * stb_image - Multiple image formats loading (JPEG, PNG, BMP, TGA, PSD, GIF, PIC)
35 * NOTE: stb_image has been slightly modified to support Android platform.
36 * stb_image_resize - Multiple image resize algorythms
39 * LICENSE: zlib/libpng
41 * Copyright (c) 2013-2021 Ramon Santamaria (@raysan5)
43 * This software is provided "as-is", without any express or implied warranty. In no event
44 * will the authors be held liable for any damages arising from the use of this software.
46 * Permission is granted to anyone to use this software for any purpose, including commercial
47 * applications, and to alter it and redistribute it freely, subject to the following restrictions:
49 * 1. The origin of this software must not be misrepresented; you must not claim that you
50 * wrote the original software. If you use this software in a product, an acknowledgment
51 * in the product documentation would be appreciated but is not required.
53 * 2. Altered source versions must be plainly marked as such, and must not be misrepresented
54 * as being the original software.
56 * 3. This notice may not be removed or altered from any source distribution.
58 **********************************************************************************************/
60 #include "raylib.h" // Declares module functions
62 // Check if config flags have been externally provided on compilation line
63 #if !defined(EXTERNAL_CONFIG_FLAGS)
64 #include "config.h" // Defines module configuration flags
67 #include <stdlib.h> // Required for: malloc(), free()
68 #include <string.h> // Required for: strlen() [Used in ImageTextEx()]
69 #include <math.h> // Required for: fabsf()
71 #include "utils.h" // Required for: fopen() Android mapping
73 #include "rlgl.h" // raylib OpenGL abstraction layer to OpenGL 1.1, 3.3 or ES2
74 // Required for: rlLoadTexture() rlUnloadTexture(),
75 // rlGenerateMipmaps(), some funcs for DrawTexturePro()
77 // Support only desired texture formats on stb_image
78 #if !defined(SUPPORT_FILEFORMAT_BMP)
81 #if !defined(SUPPORT_FILEFORMAT_PNG)
84 #if !defined(SUPPORT_FILEFORMAT_TGA)
87 #if !defined(SUPPORT_FILEFORMAT_JPG)
88 #define STBI_NO_JPEG // Image format .jpg and .jpeg
90 #if !defined(SUPPORT_FILEFORMAT_PSD)
93 #if !defined(SUPPORT_FILEFORMAT_GIF)
96 #if !defined(SUPPORT_FILEFORMAT_PIC)
99 #if !defined(SUPPORT_FILEFORMAT_HDR)
103 // Image fileformats not supported by default
105 #define STBI_NO_PNM // Image format .ppm and .pgm
107 #if (defined(SUPPORT_FILEFORMAT_BMP) || \
108 defined(SUPPORT_FILEFORMAT_PNG) || \
109 defined(SUPPORT_FILEFORMAT_TGA) || \
110 defined(SUPPORT_FILEFORMAT_JPG) || \
111 defined(SUPPORT_FILEFORMAT_PSD) || \
112 defined(SUPPORT_FILEFORMAT_GIF) || \
113 defined(SUPPORT_FILEFORMAT_PIC) || \
114 defined(SUPPORT_FILEFORMAT_HDR))
116 #define STBI_MALLOC RL_MALLOC
117 #define STBI_FREE RL_FREE
118 #define STBI_REALLOC RL_REALLOC
120 #define STB_IMAGE_IMPLEMENTATION
121 #include "external/stb_image.h" // Required for: stbi_load_from_file()
122 // NOTE: Used to read image data (multiple formats support)
125 #if (defined(SUPPORT_IMAGE_EXPORT) || defined(SUPPORT_COMPRESSION_API))
126 #define STBIW_MALLOC RL_MALLOC
127 #define STBIW_FREE RL_FREE
128 #define STBIW_REALLOC RL_REALLOC
130 #define STB_IMAGE_WRITE_IMPLEMENTATION
131 #include "external/stb_image_write.h" // Required for: stbi_write_*()
134 #if defined(SUPPORT_IMAGE_MANIPULATION)
135 #define STBIR_MALLOC(size,c) ((void)(c), RL_MALLOC(size))
136 #define STBIR_FREE(ptr,c) ((void)(c), RL_FREE(ptr))
138 #define STB_IMAGE_RESIZE_IMPLEMENTATION
139 #include "external/stb_image_resize.h" // Required for: stbir_resize_uint8() [ImageResize()]
142 #if defined(SUPPORT_IMAGE_GENERATION)
143 #define STB_PERLIN_IMPLEMENTATION
144 #include "external/stb_perlin.h" // Required for: stb_perlin_fbm_noise3
147 //----------------------------------------------------------------------------------
148 // Defines and Macros
149 //----------------------------------------------------------------------------------
150 #ifndef PIXELFORMAT_UNCOMPRESSED_R5G5B5A1_ALPHA_THRESHOLD
151 #define PIXELFORMAT_UNCOMPRESSED_R5G5B5A1_ALPHA_THRESHOLD 50 // Threshold over 255 to set alpha as 0
154 //----------------------------------------------------------------------------------
155 // Types and Structures Definition
156 //----------------------------------------------------------------------------------
159 //----------------------------------------------------------------------------------
160 // Global Variables Definition
161 //----------------------------------------------------------------------------------
162 // It's lonely here...
164 //----------------------------------------------------------------------------------
165 // Other Modules Functions Declaration (required by text)
166 //----------------------------------------------------------------------------------
169 //----------------------------------------------------------------------------------
170 // Module specific Functions Declaration
171 //----------------------------------------------------------------------------------
172 #if defined(SUPPORT_FILEFORMAT_DDS)
173 static Image LoadDDS(const unsigned char *fileData, unsigned int fileSize); // Load DDS file data
175 #if defined(SUPPORT_FILEFORMAT_PKM)
176 static Image LoadPKM(const unsigned char *fileData, unsigned int fileSize); // Load PKM file data
178 #if defined(SUPPORT_FILEFORMAT_KTX)
179 static Image LoadKTX(const unsigned char *fileData, unsigned int fileSize); // Load KTX file data
180 static int SaveKTX(Image image, const char *fileName); // Save image data as KTX file
182 #if defined(SUPPORT_FILEFORMAT_PVR)
183 static Image LoadPVR(const unsigned char *fileData, unsigned int fileSize); // Load PVR file data
185 #if defined(SUPPORT_FILEFORMAT_ASTC)
186 static Image LoadASTC(const unsigned char *fileData, unsigned int fileSize); // Load ASTC file data
188 static Vector4 *LoadImageDataNormalized(Image image); // Load pixel data from image as Vector4 array (float normalized)
190 //----------------------------------------------------------------------------------
191 // Module Functions Definition
192 //----------------------------------------------------------------------------------
194 // Load image from file into CPU memory (RAM)
195 Image LoadImage(const char *fileName)
199 #if defined(SUPPORT_FILEFORMAT_PNG) || \
200 defined(SUPPORT_FILEFORMAT_BMP) || \
201 defined(SUPPORT_FILEFORMAT_TGA) || \
202 defined(SUPPORT_FILEFORMAT_JPG) || \
203 defined(SUPPORT_FILEFORMAT_GIF) || \
204 defined(SUPPORT_FILEFORMAT_PIC) || \
205 defined(SUPPORT_FILEFORMAT_HDR) || \
206 defined(SUPPORT_FILEFORMAT_PSD)
207 #define STBI_REQUIRED
210 // Loading file to memory
211 unsigned int fileSize = 0;
212 unsigned char *fileData = LoadFileData(fileName, &fileSize);
214 if (fileData != NULL)
216 // Loading image from memory data
217 image = LoadImageFromMemory(GetFileExtension(fileName), fileData, fileSize);
219 if (image.data != NULL) TRACELOG(LOG_INFO, "IMAGE: [%s] Data loaded successfully (%ix%i)", fileName, image.width, image.height);
220 else TRACELOG(LOG_WARNING, "IMAGE: [%s] Failed to load data", fileName);
228 // Load an image from RAW file data
229 Image LoadImageRaw(const char *fileName, int width, int height, int format, int headerSize)
233 unsigned int dataSize = 0;
234 unsigned char *fileData = LoadFileData(fileName, &dataSize);
236 if (fileData != NULL)
238 unsigned char *dataPtr = fileData;
239 unsigned int size = GetPixelDataSize(width, height, format);
241 if (headerSize > 0) dataPtr += headerSize;
243 image.data = RL_MALLOC(size); // Allocate required memory in bytes
244 memcpy(image.data, dataPtr, size); // Copy required data to image
246 image.height = height;
248 image.format = format;
256 // Load animated image data
257 // - Image.data buffer includes all frames: [image#0][image#1][image#2][...]
258 // - Number of frames is returned through 'frames' parameter
259 // - All frames are returned in RGBA format
260 // - Frames delay data is discarded
261 Image LoadImageAnim(const char *fileName, int *frames)
266 #if defined(SUPPORT_FILEFORMAT_GIF)
267 if (IsFileExtension(fileName, ".gif"))
269 unsigned int dataSize = 0;
270 unsigned char *fileData = LoadFileData(fileName, &dataSize);
272 if (fileData != NULL)
276 image.data = stbi_load_gif_from_memory(fileData, dataSize, delays, &image.width, &image.height, &framesCount, &comp, 4);
279 image.format = PIXELFORMAT_UNCOMPRESSED_R8G8B8A8;
282 RL_FREE(delays); // NOTE: Frames delays are discarded
288 else image = LoadImage(fileName);
290 // TODO: Support APNG animated images?
292 *frames = framesCount;
296 // Load image from memory buffer, fileType refers to extension: i.e. ".png"
297 Image LoadImageFromMemory(const char *fileType, const unsigned char *fileData, int dataSize)
301 char fileExtLower[16] = { 0 };
302 strcpy(fileExtLower, TextToLower(fileType));
304 #if defined(SUPPORT_FILEFORMAT_PNG)
305 if ((TextIsEqual(fileExtLower, ".png"))
309 #if defined(SUPPORT_FILEFORMAT_BMP)
310 || (TextIsEqual(fileExtLower, ".bmp"))
312 #if defined(SUPPORT_FILEFORMAT_TGA)
313 || (TextIsEqual(fileExtLower, ".tga"))
315 #if defined(SUPPORT_FILEFORMAT_JPG)
316 || (TextIsEqual(fileExtLower, ".jpg") ||
317 TextIsEqual(fileExtLower, ".jpeg"))
319 #if defined(SUPPORT_FILEFORMAT_GIF)
320 || (TextIsEqual(fileExtLower, ".gif"))
322 #if defined(SUPPORT_FILEFORMAT_PIC)
323 || (TextIsEqual(fileExtLower, ".pic"))
325 #if defined(SUPPORT_FILEFORMAT_PSD)
326 || (TextIsEqual(fileExtLower, ".psd"))
330 #if defined(STBI_REQUIRED)
331 // NOTE: Using stb_image to load images (Supports multiple image formats)
333 if (fileData != NULL)
336 image.data = stbi_load_from_memory(fileData, dataSize, &image.width, &image.height, &comp, 0);
340 if (comp == 1) image.format = PIXELFORMAT_UNCOMPRESSED_GRAYSCALE;
341 else if (comp == 2) image.format = PIXELFORMAT_UNCOMPRESSED_GRAY_ALPHA;
342 else if (comp == 3) image.format = PIXELFORMAT_UNCOMPRESSED_R8G8B8;
343 else if (comp == 4) image.format = PIXELFORMAT_UNCOMPRESSED_R8G8B8A8;
347 #if defined(SUPPORT_FILEFORMAT_HDR)
348 else if (TextIsEqual(fileExtLower, ".hdr"))
350 #if defined(STBI_REQUIRED)
351 if (fileData != NULL)
354 image.data = stbi_loadf_from_memory(fileData, dataSize, &image.width, &image.height, &comp, 0);
358 if (comp == 1) image.format = PIXELFORMAT_UNCOMPRESSED_R32;
359 else if (comp == 3) image.format = PIXELFORMAT_UNCOMPRESSED_R32G32B32;
360 else if (comp == 4) image.format = PIXELFORMAT_UNCOMPRESSED_R32G32B32A32;
363 TRACELOG(LOG_WARNING, "IMAGE: HDR file format not supported");
370 #if defined(SUPPORT_FILEFORMAT_DDS)
371 else if (TextIsEqual(fileExtLower, ".dds")) image = LoadDDS(fileData, dataSize);
373 #if defined(SUPPORT_FILEFORMAT_PKM)
374 else if (TextIsEqual(fileExtLower, ".pkm")) image = LoadPKM(fileData, dataSize);
376 #if defined(SUPPORT_FILEFORMAT_KTX)
377 else if (TextIsEqual(fileExtLower, ".ktx")) image = LoadKTX(fileData, dataSize);
379 #if defined(SUPPORT_FILEFORMAT_PVR)
380 else if (TextIsEqual(fileExtLower, ".pvr")) image = LoadPVR(fileData, dataSize);
382 #if defined(SUPPORT_FILEFORMAT_ASTC)
383 else if (TextIsEqual(fileExtLower, ".astc")) image = LoadASTC(fileData, dataSize);
385 else TRACELOG(LOG_WARNING, "IMAGE: File format not supported");
390 // Unload image from CPU memory (RAM)
391 void UnloadImage(Image image)
396 // Export image data to file
397 // NOTE: File format depends on fileName extension
398 bool ExportImage(Image image, const char *fileName)
402 #if defined(SUPPORT_IMAGE_EXPORT)
404 bool allocatedData = false;
405 unsigned char *imgData = (unsigned char *)image.data;
407 if (image.format == PIXELFORMAT_UNCOMPRESSED_GRAYSCALE) channels = 1;
408 else if (image.format == PIXELFORMAT_UNCOMPRESSED_GRAY_ALPHA) channels = 2;
409 else if (image.format == PIXELFORMAT_UNCOMPRESSED_R8G8B8) channels = 3;
410 else if (image.format == PIXELFORMAT_UNCOMPRESSED_R8G8B8A8) channels = 4;
413 // NOTE: Getting Color array as RGBA unsigned char values
414 imgData = (unsigned char *)LoadImageColors(image);
415 allocatedData = true;
418 #if defined(SUPPORT_FILEFORMAT_PNG)
419 if (IsFileExtension(fileName, ".png")) success = stbi_write_png(fileName, image.width, image.height, channels, imgData, image.width*channels);
423 #if defined(SUPPORT_FILEFORMAT_BMP)
424 else if (IsFileExtension(fileName, ".bmp")) success = stbi_write_bmp(fileName, image.width, image.height, channels, imgData);
426 #if defined(SUPPORT_FILEFORMAT_TGA)
427 else if (IsFileExtension(fileName, ".tga")) success = stbi_write_tga(fileName, image.width, image.height, channels, imgData);
429 #if defined(SUPPORT_FILEFORMAT_JPG)
430 else if (IsFileExtension(fileName, ".jpg")) success = stbi_write_jpg(fileName, image.width, image.height, channels, imgData, 90); // JPG quality: between 1 and 100
432 #if defined(SUPPORT_FILEFORMAT_KTX)
433 else if (IsFileExtension(fileName, ".ktx")) success = SaveKTX(image, fileName);
435 else if (IsFileExtension(fileName, ".raw"))
437 // Export raw pixel data (without header)
438 // NOTE: It's up to the user to track image parameters
439 success = SaveFileData(fileName, image.data, GetPixelDataSize(image.width, image.height, image.format));
442 if (allocatedData) RL_FREE(imgData);
443 #endif // SUPPORT_IMAGE_EXPORT
445 if (success != 0) TRACELOG(LOG_INFO, "FILEIO: [%s] Image exported successfully", fileName);
446 else TRACELOG(LOG_WARNING, "FILEIO: [%s] Failed to export image", fileName);
451 // Export image as code file (.h) defining an array of bytes
452 bool ExportImageAsCode(Image image, const char *fileName)
454 bool success = false;
456 #ifndef TEXT_BYTES_PER_LINE
457 #define TEXT_BYTES_PER_LINE 20
460 int dataSize = GetPixelDataSize(image.width, image.height, image.format);
462 // NOTE: Text data buffer size is estimated considering image data size in bytes
463 // and requiring 6 char bytes for every byte: "0x00, "
464 char *txtData = (char *)RL_CALLOC(6*dataSize + 2000, sizeof(char));
467 bytesCount += sprintf(txtData + bytesCount, "////////////////////////////////////////////////////////////////////////////////////////\n");
468 bytesCount += sprintf(txtData + bytesCount, "// //\n");
469 bytesCount += sprintf(txtData + bytesCount, "// ImageAsCode exporter v1.0 - Image pixel data exported as an array of bytes //\n");
470 bytesCount += sprintf(txtData + bytesCount, "// //\n");
471 bytesCount += sprintf(txtData + bytesCount, "// more info and bugs-report: github.com/raysan5/raylib //\n");
472 bytesCount += sprintf(txtData + bytesCount, "// feedback and support: ray[at]raylib.com //\n");
473 bytesCount += sprintf(txtData + bytesCount, "// //\n");
474 bytesCount += sprintf(txtData + bytesCount, "// Copyright (c) 2020 Ramon Santamaria (@raysan5) //\n");
475 bytesCount += sprintf(txtData + bytesCount, "// //\n");
476 bytesCount += sprintf(txtData + bytesCount, "////////////////////////////////////////////////////////////////////////////////////////\n\n");
478 // Get file name from path and convert variable name to uppercase
479 char varFileName[256] = { 0 };
480 strcpy(varFileName, GetFileNameWithoutExt(fileName));
481 for (int i = 0; varFileName[i] != '\0'; i++) if ((varFileName[i] >= 'a') && (varFileName[i] <= 'z')) { varFileName[i] = varFileName[i] - 32; }
483 // Add image information
484 bytesCount += sprintf(txtData + bytesCount, "// Image data information\n");
485 bytesCount += sprintf(txtData + bytesCount, "#define %s_WIDTH %i\n", varFileName, image.width);
486 bytesCount += sprintf(txtData + bytesCount, "#define %s_HEIGHT %i\n", varFileName, image.height);
487 bytesCount += sprintf(txtData + bytesCount, "#define %s_FORMAT %i // raylib internal pixel format\n\n", varFileName, image.format);
489 bytesCount += sprintf(txtData + bytesCount, "static unsigned char %s_DATA[%i] = { ", varFileName, dataSize);
490 for (int i = 0; i < dataSize - 1; i++) bytesCount += sprintf(txtData + bytesCount, ((i%TEXT_BYTES_PER_LINE == 0)? "0x%x,\n" : "0x%x, "), ((unsigned char *)image.data)[i]);
491 bytesCount += sprintf(txtData + bytesCount, "0x%x };\n", ((unsigned char *)image.data)[dataSize - 1]);
493 // NOTE: Text data length exported is determined by '\0' (NULL) character
494 success = SaveFileText(fileName, txtData);
501 //------------------------------------------------------------------------------------
502 // Image generation functions
503 //------------------------------------------------------------------------------------
504 // Generate image: plain color
505 Image GenImageColor(int width, int height, Color color)
507 Color *pixels = (Color *)RL_CALLOC(width*height, sizeof(Color));
509 for (int i = 0; i < width*height; i++) pixels[i] = color;
515 .format = PIXELFORMAT_UNCOMPRESSED_R8G8B8A8,
522 #if defined(SUPPORT_IMAGE_GENERATION)
523 // Generate image: vertical gradient
524 Image GenImageGradientV(int width, int height, Color top, Color bottom)
526 Color *pixels = (Color *)RL_MALLOC(width*height*sizeof(Color));
528 for (int j = 0; j < height; j++)
530 float factor = (float)j/(float)height;
531 for (int i = 0; i < width; i++)
533 pixels[j*width + i].r = (int)((float)bottom.r*factor + (float)top.r*(1.f - factor));
534 pixels[j*width + i].g = (int)((float)bottom.g*factor + (float)top.g*(1.f - factor));
535 pixels[j*width + i].b = (int)((float)bottom.b*factor + (float)top.b*(1.f - factor));
536 pixels[j*width + i].a = (int)((float)bottom.a*factor + (float)top.a*(1.f - factor));
544 .format = PIXELFORMAT_UNCOMPRESSED_R8G8B8A8,
551 // Generate image: horizontal gradient
552 Image GenImageGradientH(int width, int height, Color left, Color right)
554 Color *pixels = (Color *)RL_MALLOC(width*height*sizeof(Color));
556 for (int i = 0; i < width; i++)
558 float factor = (float)i/(float)width;
559 for (int j = 0; j < height; j++)
561 pixels[j*width + i].r = (int)((float)right.r*factor + (float)left.r*(1.f - factor));
562 pixels[j*width + i].g = (int)((float)right.g*factor + (float)left.g*(1.f - factor));
563 pixels[j*width + i].b = (int)((float)right.b*factor + (float)left.b*(1.f - factor));
564 pixels[j*width + i].a = (int)((float)right.a*factor + (float)left.a*(1.f - factor));
572 .format = PIXELFORMAT_UNCOMPRESSED_R8G8B8A8,
579 // Generate image: radial gradient
580 Image GenImageGradientRadial(int width, int height, float density, Color inner, Color outer)
582 Color *pixels = (Color *)RL_MALLOC(width*height*sizeof(Color));
583 float radius = (width < height)? (float)width/2.0f : (float)height/2.0f;
585 float centerX = (float)width/2.0f;
586 float centerY = (float)height/2.0f;
588 for (int y = 0; y < height; y++)
590 for (int x = 0; x < width; x++)
592 float dist = hypotf((float)x - centerX, (float)y - centerY);
593 float factor = (dist - radius*density)/(radius*(1.0f - density));
595 factor = (float)fmax(factor, 0.0f);
596 factor = (float)fmin(factor, 1.f); // dist can be bigger than radius so we have to check
598 pixels[y*width + x].r = (int)((float)outer.r*factor + (float)inner.r*(1.0f - factor));
599 pixels[y*width + x].g = (int)((float)outer.g*factor + (float)inner.g*(1.0f - factor));
600 pixels[y*width + x].b = (int)((float)outer.b*factor + (float)inner.b*(1.0f - factor));
601 pixels[y*width + x].a = (int)((float)outer.a*factor + (float)inner.a*(1.0f - factor));
609 .format = PIXELFORMAT_UNCOMPRESSED_R8G8B8A8,
616 // Generate image: checked
617 Image GenImageChecked(int width, int height, int checksX, int checksY, Color col1, Color col2)
619 Color *pixels = (Color *)RL_MALLOC(width*height*sizeof(Color));
621 for (int y = 0; y < height; y++)
623 for (int x = 0; x < width; x++)
625 if ((x/checksX + y/checksY)%2 == 0) pixels[y*width + x] = col1;
626 else pixels[y*width + x] = col2;
634 .format = PIXELFORMAT_UNCOMPRESSED_R8G8B8A8,
641 // Generate image: white noise
642 Image GenImageWhiteNoise(int width, int height, float factor)
644 Color *pixels = (Color *)RL_MALLOC(width*height*sizeof(Color));
646 for (int i = 0; i < width*height; i++)
648 if (GetRandomValue(0, 99) < (int)(factor*100.0f)) pixels[i] = WHITE;
649 else pixels[i] = BLACK;
656 .format = PIXELFORMAT_UNCOMPRESSED_R8G8B8A8,
663 // Generate image: perlin noise
664 Image GenImagePerlinNoise(int width, int height, int offsetX, int offsetY, float scale)
666 Color *pixels = (Color *)RL_MALLOC(width*height*sizeof(Color));
668 for (int y = 0; y < height; y++)
670 for (int x = 0; x < width; x++)
672 float nx = (float)(x + offsetX)*scale/(float)width;
673 float ny = (float)(y + offsetY)*scale/(float)height;
675 // Typical values to start playing with:
676 // lacunarity = ~2.0 -- spacing between successive octaves (use exactly 2.0 for wrapping output)
677 // gain = 0.5 -- relative weighting applied to each successive octave
678 // octaves = 6 -- number of "octaves" of noise3() to sum
680 // NOTE: We need to translate the data from [-1..1] to [0..1]
681 float p = (stb_perlin_fbm_noise3(nx, ny, 1.0f, 2.0f, 0.5f, 6) + 1.0f)/2.0f;
683 int intensity = (int)(p*255.0f);
684 pixels[y*width + x] = (Color){intensity, intensity, intensity, 255};
692 .format = PIXELFORMAT_UNCOMPRESSED_R8G8B8A8,
699 // Generate image: cellular algorithm. Bigger tileSize means bigger cells
700 Image GenImageCellular(int width, int height, int tileSize)
702 Color *pixels = (Color *)RL_MALLOC(width*height*sizeof(Color));
704 int seedsPerRow = width/tileSize;
705 int seedsPerCol = height/tileSize;
706 int seedsCount = seedsPerRow*seedsPerCol;
708 Vector2 *seeds = (Vector2 *)RL_MALLOC(seedsCount*sizeof(Vector2));
710 for (int i = 0; i < seedsCount; i++)
712 int y = (i/seedsPerRow)*tileSize + GetRandomValue(0, tileSize - 1);
713 int x = (i%seedsPerRow)*tileSize + GetRandomValue(0, tileSize - 1);
714 seeds[i] = (Vector2){ (float)x, (float)y};
717 for (int y = 0; y < height; y++)
719 int tileY = y/tileSize;
721 for (int x = 0; x < width; x++)
723 int tileX = x/tileSize;
725 float minDistance = (float)strtod("Inf", NULL);
727 // Check all adjacent tiles
728 for (int i = -1; i < 2; i++)
730 if ((tileX + i < 0) || (tileX + i >= seedsPerRow)) continue;
732 for (int j = -1; j < 2; j++)
734 if ((tileY + j < 0) || (tileY + j >= seedsPerCol)) continue;
736 Vector2 neighborSeed = seeds[(tileY + j)*seedsPerRow + tileX + i];
738 float dist = (float)hypot(x - (int)neighborSeed.x, y - (int)neighborSeed.y);
739 minDistance = (float)fmin(minDistance, dist);
743 // I made this up but it seems to give good results at all tile sizes
744 int intensity = (int)(minDistance*256.0f/tileSize);
745 if (intensity > 255) intensity = 255;
747 pixels[y*width + x] = (Color){ intensity, intensity, intensity, 255 };
757 .format = PIXELFORMAT_UNCOMPRESSED_R8G8B8A8,
763 #endif // SUPPORT_IMAGE_GENERATION
765 //------------------------------------------------------------------------------------
766 // Image manipulation functions
767 //------------------------------------------------------------------------------------
768 // Copy an image to a new image
769 Image ImageCopy(Image image)
771 Image newImage = { 0 };
773 int width = image.width;
774 int height = image.height;
777 for (int i = 0; i < image.mipmaps; i++)
779 size += GetPixelDataSize(width, height, image.format);
784 // Security check for NPOT textures
785 if (width < 1) width = 1;
786 if (height < 1) height = 1;
789 newImage.data = RL_MALLOC(size);
791 if (newImage.data != NULL)
793 // NOTE: Size must be provided in bytes
794 memcpy(newImage.data, image.data, size);
796 newImage.width = image.width;
797 newImage.height = image.height;
798 newImage.mipmaps = image.mipmaps;
799 newImage.format = image.format;
805 // Create an image from another image piece
806 Image ImageFromImage(Image image, Rectangle rec)
808 Image result = { 0 };
810 int bytesPerPixel = GetPixelDataSize(1, 1, image.format);
812 // TODO: Check rec is valid?
814 result.width = (int)rec.width;
815 result.height = (int)rec.height;
816 result.data = RL_CALLOC((int)(rec.width*rec.height)*bytesPerPixel, 1);
817 result.format = image.format;
820 for (int y = 0; y < rec.height; y++)
822 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);
828 // Crop an image to area defined by a rectangle
829 // NOTE: Security checks are performed in case rectangle goes out of bounds
830 void ImageCrop(Image *image, Rectangle crop)
832 // Security check to avoid program crash
833 if ((image->data == NULL) || (image->width == 0) || (image->height == 0)) return;
835 // Security checks to validate crop rectangle
836 if (crop.x < 0) { crop.width += crop.x; crop.x = 0; }
837 if (crop.y < 0) { crop.height += crop.y; crop.y = 0; }
838 if ((crop.x + crop.width) > image->width) crop.width = image->width - crop.x;
839 if ((crop.y + crop.height) > image->height) crop.height = image->height - crop.y;
840 if ((crop.x > image->width) || (crop.y > image->height))
842 TRACELOG(LOG_WARNING, "IMAGE: Failed to crop, rectangle out of bounds");
846 if (image->mipmaps > 1) TRACELOG(LOG_WARNING, "Image manipulation only applied to base mipmap level");
847 if (image->format >= PIXELFORMAT_COMPRESSED_DXT1_RGB) TRACELOG(LOG_WARNING, "Image manipulation not supported for compressed formats");
850 int bytesPerPixel = GetPixelDataSize(1, 1, image->format);
852 unsigned char *croppedData = (unsigned char *)RL_MALLOC((int)(crop.width*crop.height)*bytesPerPixel);
854 // OPTION 1: Move cropped data line-by-line
855 for (int y = (int)crop.y, offsetSize = 0; y < (int)(crop.y + crop.height); y++)
857 memcpy(croppedData + offsetSize, ((unsigned char *)image->data) + (y*image->width + (int)crop.x)*bytesPerPixel, (int)crop.width*bytesPerPixel);
858 offsetSize += ((int)crop.width*bytesPerPixel);
862 // OPTION 2: Move cropped data pixel-by-pixel or byte-by-byte
863 for (int y = (int)crop.y; y < (int)(crop.y + crop.height); y++)
865 for (int x = (int)crop.x; x < (int)(crop.x + crop.width); x++)
867 //memcpy(croppedData + ((y - (int)crop.y)*(int)crop.width + (x - (int)crop.x))*bytesPerPixel, ((unsigned char *)image->data) + (y*image->width + x)*bytesPerPixel, bytesPerPixel);
868 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];
873 RL_FREE(image->data);
874 image->data = croppedData;
875 image->width = (int)crop.width;
876 image->height = (int)crop.height;
880 // Convert image data to desired format
881 void ImageFormat(Image *image, int newFormat)
883 // Security check to avoid program crash
884 if ((image->data == NULL) || (image->width == 0) || (image->height == 0)) return;
886 if ((newFormat != 0) && (image->format != newFormat))
888 if ((image->format < PIXELFORMAT_COMPRESSED_DXT1_RGB) && (newFormat < PIXELFORMAT_COMPRESSED_DXT1_RGB))
890 Vector4 *pixels = LoadImageDataNormalized(*image); // Supports 8 to 32 bit per channel
892 RL_FREE(image->data); // WARNING! We loose mipmaps data --> Regenerated at the end...
894 image->format = newFormat;
898 switch (image->format)
900 case PIXELFORMAT_UNCOMPRESSED_GRAYSCALE:
902 image->data = (unsigned char *)RL_MALLOC(image->width*image->height*sizeof(unsigned char));
904 for (int i = 0; i < image->width*image->height; i++)
906 ((unsigned char *)image->data)[i] = (unsigned char)((pixels[i].x*0.299f + pixels[i].y*0.587f + pixels[i].z*0.114f)*255.0f);
910 case PIXELFORMAT_UNCOMPRESSED_GRAY_ALPHA:
912 image->data = (unsigned char *)RL_MALLOC(image->width*image->height*2*sizeof(unsigned char));
914 for (int i = 0; i < image->width*image->height*2; i += 2, k++)
916 ((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);
917 ((unsigned char *)image->data)[i + 1] = (unsigned char)(pixels[k].w*255.0f);
921 case PIXELFORMAT_UNCOMPRESSED_R5G6B5:
923 image->data = (unsigned short *)RL_MALLOC(image->width*image->height*sizeof(unsigned short));
929 for (int i = 0; i < image->width*image->height; i++)
931 r = (unsigned char)(round(pixels[i].x*31.0f));
932 g = (unsigned char)(round(pixels[i].y*63.0f));
933 b = (unsigned char)(round(pixels[i].z*31.0f));
935 ((unsigned short *)image->data)[i] = (unsigned short)r << 11 | (unsigned short)g << 5 | (unsigned short)b;
939 case PIXELFORMAT_UNCOMPRESSED_R8G8B8:
941 image->data = (unsigned char *)RL_MALLOC(image->width*image->height*3*sizeof(unsigned char));
943 for (int i = 0, k = 0; i < image->width*image->height*3; i += 3, k++)
945 ((unsigned char *)image->data)[i] = (unsigned char)(pixels[k].x*255.0f);
946 ((unsigned char *)image->data)[i + 1] = (unsigned char)(pixels[k].y*255.0f);
947 ((unsigned char *)image->data)[i + 2] = (unsigned char)(pixels[k].z*255.0f);
950 case PIXELFORMAT_UNCOMPRESSED_R5G5B5A1:
952 image->data = (unsigned short *)RL_MALLOC(image->width*image->height*sizeof(unsigned short));
959 for (int i = 0; i < image->width*image->height; i++)
961 r = (unsigned char)(round(pixels[i].x*31.0f));
962 g = (unsigned char)(round(pixels[i].y*31.0f));
963 b = (unsigned char)(round(pixels[i].z*31.0f));
964 a = (pixels[i].w > ((float)PIXELFORMAT_UNCOMPRESSED_R5G5B5A1_ALPHA_THRESHOLD/255.0f))? 1 : 0;
966 ((unsigned short *)image->data)[i] = (unsigned short)r << 11 | (unsigned short)g << 6 | (unsigned short)b << 1 | (unsigned short)a;
970 case PIXELFORMAT_UNCOMPRESSED_R4G4B4A4:
972 image->data = (unsigned short *)RL_MALLOC(image->width*image->height*sizeof(unsigned short));
979 for (int i = 0; i < image->width*image->height; i++)
981 r = (unsigned char)(round(pixels[i].x*15.0f));
982 g = (unsigned char)(round(pixels[i].y*15.0f));
983 b = (unsigned char)(round(pixels[i].z*15.0f));
984 a = (unsigned char)(round(pixels[i].w*15.0f));
986 ((unsigned short *)image->data)[i] = (unsigned short)r << 12 | (unsigned short)g << 8 | (unsigned short)b << 4 | (unsigned short)a;
990 case PIXELFORMAT_UNCOMPRESSED_R8G8B8A8:
992 image->data = (unsigned char *)RL_MALLOC(image->width*image->height*4*sizeof(unsigned char));
994 for (int i = 0, k = 0; i < image->width*image->height*4; i += 4, k++)
996 ((unsigned char *)image->data)[i] = (unsigned char)(pixels[k].x*255.0f);
997 ((unsigned char *)image->data)[i + 1] = (unsigned char)(pixels[k].y*255.0f);
998 ((unsigned char *)image->data)[i + 2] = (unsigned char)(pixels[k].z*255.0f);
999 ((unsigned char *)image->data)[i + 3] = (unsigned char)(pixels[k].w*255.0f);
1002 case PIXELFORMAT_UNCOMPRESSED_R32:
1004 // WARNING: Image is converted to GRAYSCALE eqeuivalent 32bit
1006 image->data = (float *)RL_MALLOC(image->width*image->height*sizeof(float));
1008 for (int i = 0; i < image->width*image->height; i++)
1010 ((float *)image->data)[i] = (float)(pixels[i].x*0.299f + pixels[i].y*0.587f + pixels[i].z*0.114f);
1013 case PIXELFORMAT_UNCOMPRESSED_R32G32B32:
1015 image->data = (float *)RL_MALLOC(image->width*image->height*3*sizeof(float));
1017 for (int i = 0, k = 0; i < image->width*image->height*3; i += 3, k++)
1019 ((float *)image->data)[i] = pixels[k].x;
1020 ((float *)image->data)[i + 1] = pixels[k].y;
1021 ((float *)image->data)[i + 2] = pixels[k].z;
1024 case PIXELFORMAT_UNCOMPRESSED_R32G32B32A32:
1026 image->data = (float *)RL_MALLOC(image->width*image->height*4*sizeof(float));
1028 for (int i = 0, k = 0; i < image->width*image->height*4; i += 4, k++)
1030 ((float *)image->data)[i] = pixels[k].x;
1031 ((float *)image->data)[i + 1] = pixels[k].y;
1032 ((float *)image->data)[i + 2] = pixels[k].z;
1033 ((float *)image->data)[i + 3] = pixels[k].w;
1042 // In case original image had mipmaps, generate mipmaps for formated image
1043 // NOTE: Original mipmaps are replaced by new ones, if custom mipmaps were used, they are lost
1044 if (image->mipmaps > 1)
1047 #if defined(SUPPORT_IMAGE_MANIPULATION)
1048 if (image->data != NULL) ImageMipmaps(image);
1052 else TRACELOG(LOG_WARNING, "IMAGE: Data format is compressed, can not be converted");
1056 // Convert image to POT (power-of-two)
1057 // NOTE: It could be useful on OpenGL ES 2.0 (RPI, HTML5)
1058 void ImageToPOT(Image *image, Color fill)
1060 // Security check to avoid program crash
1061 if ((image->data == NULL) || (image->width == 0) || (image->height == 0)) return;
1063 // Calculate next power-of-two values
1064 // NOTE: Just add the required amount of pixels at the right and bottom sides of image...
1065 int potWidth = (int)powf(2, ceilf(logf((float)image->width)/logf(2)));
1066 int potHeight = (int)powf(2, ceilf(logf((float)image->height)/logf(2)));
1068 // Check if POT texture generation is required (if texture is not already POT)
1069 if ((potWidth != image->width) || (potHeight != image->height)) ImageResizeCanvas(image, potWidth, potHeight, 0, 0, fill);
1072 #if defined(SUPPORT_IMAGE_MANIPULATION)
1073 // Create an image from text (default font)
1074 Image ImageText(const char *text, int fontSize, Color color)
1076 int defaultFontSize = 10; // Default Font chars height in pixel
1077 if (fontSize < defaultFontSize) fontSize = defaultFontSize;
1078 int spacing = fontSize/defaultFontSize;
1080 Image imText = ImageTextEx(GetFontDefault(), text, (float)fontSize, (float)spacing, color);
1085 // Create an image from text (custom sprite font)
1086 Image ImageTextEx(Font font, const char *text, float fontSize, float spacing, Color tint)
1088 int length = (int)strlen(text);
1090 int textOffsetX = 0; // Image drawing position X
1091 int textOffsetY = 0; // Offset between lines (on line break '\n')
1093 // NOTE: Text image is generated at font base size, later scaled to desired font size
1094 Vector2 imSize = MeasureTextEx(font, text, (float)font.baseSize, spacing);
1096 // Create image to store text
1097 Image imText = GenImageColor((int)imSize.x, (int)imSize.y, BLANK);
1099 for (int i = 0; i < length; i++)
1101 // Get next codepoint from byte string and glyph index in font
1102 int codepointByteCount = 0;
1103 int codepoint = GetNextCodepoint(&text[i], &codepointByteCount);
1104 int index = GetGlyphIndex(font, codepoint);
1106 // NOTE: Normally we exit the decoding sequence as soon as a bad byte is found (and return 0x3f)
1107 // but we need to draw all of the bad bytes using the '?' symbol moving one byte
1108 if (codepoint == 0x3f) codepointByteCount = 1;
1110 if (codepoint == '\n')
1112 // NOTE: Fixed line spacing of 1.5 line-height
1113 // TODO: Support custom line spacing defined by user
1114 textOffsetY += (font.baseSize + font.baseSize/2);
1119 if ((codepoint != ' ') && (codepoint != '\t'))
1121 Rectangle rec = { (float)(textOffsetX + font.chars[index].offsetX), (float)(textOffsetY + font.chars[index].offsetY), (float)font.recs[index].width, (float)font.recs[index].height };
1122 ImageDraw(&imText, font.chars[index].image, (Rectangle){ 0, 0, (float)font.chars[index].image.width, (float)font.chars[index].image.height }, rec, tint);
1125 if (font.chars[index].advanceX == 0) textOffsetX += (int)(font.recs[index].width + spacing);
1126 else textOffsetX += font.chars[index].advanceX + (int)spacing;
1129 i += (codepointByteCount - 1); // Move text bytes counter to next codepoint
1132 // Scale image depending on text size
1133 if (fontSize > imSize.y)
1135 float scaleFactor = fontSize/imSize.y;
1136 TRACELOG(LOG_INFO, "IMAGE: Text scaled by factor: %f", scaleFactor);
1138 // Using nearest-neighbor scaling algorithm for default font
1139 if (font.texture.id == GetFontDefault().texture.id) ImageResizeNN(&imText, (int)(imSize.x*scaleFactor), (int)(imSize.y*scaleFactor));
1140 else ImageResize(&imText, (int)(imSize.x*scaleFactor), (int)(imSize.y*scaleFactor));
1146 // Crop image depending on alpha value
1147 // NOTE: Threshold is defined as a percentatge: 0.0f -> 1.0f
1148 void ImageAlphaCrop(Image *image, float threshold)
1150 // Security check to avoid program crash
1151 if ((image->data == NULL) || (image->width == 0) || (image->height == 0)) return;
1153 Rectangle crop = GetImageAlphaBorder(*image, threshold);
1155 // Crop if rectangle is valid
1156 if (((int)crop.width != 0) && ((int)crop.height != 0)) ImageCrop(image, crop);
1159 // Clear alpha channel to desired color
1160 // NOTE: Threshold defines the alpha limit, 0.0f to 1.0f
1161 void ImageAlphaClear(Image *image, Color color, float threshold)
1163 // Security check to avoid program crash
1164 if ((image->data == NULL) || (image->width == 0) || (image->height == 0)) return;
1166 if (image->mipmaps > 1) TRACELOG(LOG_WARNING, "Image manipulation only applied to base mipmap level");
1167 if (image->format >= PIXELFORMAT_COMPRESSED_DXT1_RGB) TRACELOG(LOG_WARNING, "Image manipulation not supported for compressed formats");
1170 switch (image->format)
1172 case PIXELFORMAT_UNCOMPRESSED_GRAY_ALPHA:
1174 unsigned char thresholdValue = (unsigned char)(threshold*255.0f);
1175 for (int i = 1; i < image->width*image->height*2; i += 2)
1177 if (((unsigned char *)image->data)[i] <= thresholdValue)
1179 ((unsigned char *)image->data)[i - 1] = color.r;
1180 ((unsigned char *)image->data)[i] = color.a;
1184 case PIXELFORMAT_UNCOMPRESSED_R5G5B5A1:
1186 unsigned char thresholdValue = ((threshold < 0.5f)? 0 : 1);
1188 unsigned char r = (unsigned char)(round((float)color.r*31.0f));
1189 unsigned char g = (unsigned char)(round((float)color.g*31.0f));
1190 unsigned char b = (unsigned char)(round((float)color.b*31.0f));
1191 unsigned char a = (color.a < 128)? 0 : 1;
1193 for (int i = 0; i < image->width*image->height; i++)
1195 if ((((unsigned short *)image->data)[i] & 0b0000000000000001) <= thresholdValue)
1197 ((unsigned short *)image->data)[i] = (unsigned short)r << 11 | (unsigned short)g << 6 | (unsigned short)b << 1 | (unsigned short)a;
1201 case PIXELFORMAT_UNCOMPRESSED_R4G4B4A4:
1203 unsigned char thresholdValue = (unsigned char)(threshold*15.0f);
1205 unsigned char r = (unsigned char)(round((float)color.r*15.0f));
1206 unsigned char g = (unsigned char)(round((float)color.g*15.0f));
1207 unsigned char b = (unsigned char)(round((float)color.b*15.0f));
1208 unsigned char a = (unsigned char)(round((float)color.a*15.0f));
1210 for (int i = 0; i < image->width*image->height; i++)
1212 if ((((unsigned short *)image->data)[i] & 0x000f) <= thresholdValue)
1214 ((unsigned short *)image->data)[i] = (unsigned short)r << 12 | (unsigned short)g << 8 | (unsigned short)b << 4 | (unsigned short)a;
1218 case PIXELFORMAT_UNCOMPRESSED_R8G8B8A8:
1220 unsigned char thresholdValue = (unsigned char)(threshold*255.0f);
1221 for (int i = 3; i < image->width*image->height*4; i += 4)
1223 if (((unsigned char *)image->data)[i] <= thresholdValue)
1225 ((unsigned char *)image->data)[i - 3] = color.r;
1226 ((unsigned char *)image->data)[i - 2] = color.g;
1227 ((unsigned char *)image->data)[i - 1] = color.b;
1228 ((unsigned char *)image->data)[i] = color.a;
1232 case PIXELFORMAT_UNCOMPRESSED_R32G32B32A32:
1234 for (int i = 3; i < image->width*image->height*4; i += 4)
1236 if (((float *)image->data)[i] <= threshold)
1238 ((float *)image->data)[i - 3] = (float)color.r/255.0f;
1239 ((float *)image->data)[i - 2] = (float)color.g/255.0f;
1240 ((float *)image->data)[i - 1] = (float)color.b/255.0f;
1241 ((float *)image->data)[i] = (float)color.a/255.0f;
1250 // Apply alpha mask to image
1251 // NOTE 1: Returned image is GRAY_ALPHA (16bit) or RGBA (32bit)
1252 // NOTE 2: alphaMask should be same size as image
1253 void ImageAlphaMask(Image *image, Image alphaMask)
1255 if ((image->width != alphaMask.width) || (image->height != alphaMask.height))
1257 TRACELOG(LOG_WARNING, "IMAGE: Alpha mask must be same size as image");
1259 else if (image->format >= PIXELFORMAT_COMPRESSED_DXT1_RGB)
1261 TRACELOG(LOG_WARNING, "IMAGE: Alpha mask can not be applied to compressed data formats");
1265 // Force mask to be Grayscale
1266 Image mask = ImageCopy(alphaMask);
1267 if (mask.format != PIXELFORMAT_UNCOMPRESSED_GRAYSCALE) ImageFormat(&mask, PIXELFORMAT_UNCOMPRESSED_GRAYSCALE);
1269 // In case image is only grayscale, we just add alpha channel
1270 if (image->format == PIXELFORMAT_UNCOMPRESSED_GRAYSCALE)
1272 unsigned char *data = (unsigned char *)RL_MALLOC(image->width*image->height*2);
1274 // Apply alpha mask to alpha channel
1275 for (int i = 0, k = 0; (i < mask.width*mask.height) || (i < image->width*image->height); i++, k += 2)
1277 data[k] = ((unsigned char *)image->data)[i];
1278 data[k + 1] = ((unsigned char *)mask.data)[i];
1281 RL_FREE(image->data);
1283 image->format = PIXELFORMAT_UNCOMPRESSED_GRAY_ALPHA;
1287 // Convert image to RGBA
1288 if (image->format != PIXELFORMAT_UNCOMPRESSED_R8G8B8A8) ImageFormat(image, PIXELFORMAT_UNCOMPRESSED_R8G8B8A8);
1290 // Apply alpha mask to alpha channel
1291 for (int i = 0, k = 3; (i < mask.width*mask.height) || (i < image->width*image->height); i++, k += 4)
1293 ((unsigned char *)image->data)[k] = ((unsigned char *)mask.data)[i];
1301 // Premultiply alpha channel
1302 void ImageAlphaPremultiply(Image *image)
1304 // Security check to avoid program crash
1305 if ((image->data == NULL) || (image->width == 0) || (image->height == 0)) return;
1308 Color *pixels = LoadImageColors(*image);
1310 for (int i = 0; i < image->width*image->height; i++)
1312 if (pixels[i].a == 0)
1318 else if (pixels[i].a < 255)
1320 alpha = (float)pixels[i].a/255.0f;
1321 pixels[i].r = (unsigned char)((float)pixels[i].r*alpha);
1322 pixels[i].g = (unsigned char)((float)pixels[i].g*alpha);
1323 pixels[i].b = (unsigned char)((float)pixels[i].b*alpha);
1327 RL_FREE(image->data);
1329 int format = image->format;
1330 image->data = pixels;
1331 image->format = PIXELFORMAT_UNCOMPRESSED_R8G8B8A8;
1333 ImageFormat(image, format);
1336 // Resize and image to new size
1337 // NOTE: Uses stb default scaling filters (both bicubic):
1338 // STBIR_DEFAULT_FILTER_UPSAMPLE STBIR_FILTER_CATMULLROM
1339 // STBIR_DEFAULT_FILTER_DOWNSAMPLE STBIR_FILTER_MITCHELL (high-quality Catmull-Rom)
1340 void ImageResize(Image *image, int newWidth, int newHeight)
1342 // Security check to avoid program crash
1343 if ((image->data == NULL) || (image->width == 0) || (image->height == 0)) return;
1345 bool fastPath = true;
1346 if ((image->format != PIXELFORMAT_UNCOMPRESSED_GRAYSCALE) && (image->format != PIXELFORMAT_UNCOMPRESSED_GRAY_ALPHA) && (image->format != PIXELFORMAT_UNCOMPRESSED_R8G8B8) && (image->format != PIXELFORMAT_UNCOMPRESSED_R8G8B8A8)) fastPath = true;
1350 int bytesPerPixel = GetPixelDataSize(1, 1, image->format);
1351 unsigned char *output = RL_MALLOC(newWidth*newHeight*bytesPerPixel);
1353 switch (image->format)
1355 case PIXELFORMAT_UNCOMPRESSED_GRAYSCALE: stbir_resize_uint8((unsigned char *)image->data, image->width, image->height, 0, output, newWidth, newHeight, 0, 1); break;
1356 case PIXELFORMAT_UNCOMPRESSED_GRAY_ALPHA: stbir_resize_uint8((unsigned char *)image->data, image->width, image->height, 0, output, newWidth, newHeight, 0, 2); break;
1357 case PIXELFORMAT_UNCOMPRESSED_R8G8B8: stbir_resize_uint8((unsigned char *)image->data, image->width, image->height, 0, output, newWidth, newHeight, 0, 3); break;
1358 case PIXELFORMAT_UNCOMPRESSED_R8G8B8A8: stbir_resize_uint8((unsigned char *)image->data, image->width, image->height, 0, output, newWidth, newHeight, 0, 4); break;
1362 RL_FREE(image->data);
1363 image->data = output;
1364 image->width = newWidth;
1365 image->height = newHeight;
1369 // Get data as Color pixels array to work with it
1370 Color *pixels = LoadImageColors(*image);
1371 Color *output = (Color *)RL_MALLOC(newWidth*newHeight*sizeof(Color));
1373 // NOTE: Color data is casted to (unsigned char *), there shouldn't been any problem...
1374 stbir_resize_uint8((unsigned char *)pixels, image->width, image->height, 0, (unsigned char *)output, newWidth, newHeight, 0, 4);
1376 int format = image->format;
1378 UnloadImageColors(pixels);
1379 RL_FREE(image->data);
1381 image->data = output;
1382 image->width = newWidth;
1383 image->height = newHeight;
1384 image->format = PIXELFORMAT_UNCOMPRESSED_R8G8B8A8;
1386 ImageFormat(image, format); // Reformat 32bit RGBA image to original format
1390 // Resize and image to new size using Nearest-Neighbor scaling algorithm
1391 void ImageResizeNN(Image *image,int newWidth,int newHeight)
1393 // Security check to avoid program crash
1394 if ((image->data == NULL) || (image->width == 0) || (image->height == 0)) return;
1396 Color *pixels = LoadImageColors(*image);
1397 Color *output = (Color *)RL_MALLOC(newWidth*newHeight*sizeof(Color));
1399 // EDIT: added +1 to account for an early rounding problem
1400 int xRatio = (int)((image->width << 16)/newWidth) + 1;
1401 int yRatio = (int)((image->height << 16)/newHeight) + 1;
1404 for (int y = 0; y < newHeight; y++)
1406 for (int x = 0; x < newWidth; x++)
1408 x2 = ((x*xRatio) >> 16);
1409 y2 = ((y*yRatio) >> 16);
1411 output[(y*newWidth) + x] = pixels[(y2*image->width) + x2] ;
1415 int format = image->format;
1417 RL_FREE(image->data);
1419 image->data = output;
1420 image->width = newWidth;
1421 image->height = newHeight;
1422 image->format = PIXELFORMAT_UNCOMPRESSED_R8G8B8A8;
1424 ImageFormat(image, format); // Reformat 32bit RGBA image to original format
1426 UnloadImageColors(pixels);
1429 // Resize canvas and fill with color
1430 // NOTE: Resize offset is relative to the top-left corner of the original image
1431 void ImageResizeCanvas(Image *image, int newWidth, int newHeight, int offsetX, int offsetY, Color fill)
1433 // Security check to avoid program crash
1434 if ((image->data == NULL) || (image->width == 0) || (image->height == 0)) return;
1436 if (image->mipmaps > 1) TRACELOG(LOG_WARNING, "Image manipulation only applied to base mipmap level");
1437 if (image->format >= PIXELFORMAT_COMPRESSED_DXT1_RGB) TRACELOG(LOG_WARNING, "Image manipulation not supported for compressed formats");
1438 else if ((newWidth != image->width) || (newHeight != image->height))
1440 Rectangle srcRec = { 0, 0, (float)image->width, (float)image->height };
1441 Vector2 dstPos = { (float)offsetX, (float)offsetY };
1445 srcRec.x = (float)-offsetX;
1446 srcRec.width += (float)offsetX;
1449 else if ((offsetX + image->width) > newWidth) srcRec.width = (float)(newWidth - offsetX);
1453 srcRec.y = (float)-offsetY;
1454 srcRec.height += (float)offsetY;
1457 else if ((offsetY + image->height) > newHeight) srcRec.height = (float)(newHeight - offsetY);
1459 if (newWidth < srcRec.width) srcRec.width = (float)newWidth;
1460 if (newHeight < srcRec.height) srcRec.height = (float)newHeight;
1462 int bytesPerPixel = GetPixelDataSize(1, 1, image->format);
1463 unsigned char *resizedData = (unsigned char *)RL_CALLOC(newWidth*newHeight*bytesPerPixel, 1);
1465 // TODO: Fill resizedData with fill color (must be formatted to image->format)
1467 int dstOffsetSize = ((int)dstPos.y*newWidth + (int)dstPos.x)*bytesPerPixel;
1469 for (int y = 0; y < (int)srcRec.height; y++)
1471 memcpy(resizedData + dstOffsetSize, ((unsigned char *)image->data) + ((y + (int)srcRec.y)*image->width + (int)srcRec.x)*bytesPerPixel, (int)srcRec.width*bytesPerPixel);
1472 dstOffsetSize += (newWidth*bytesPerPixel);
1475 RL_FREE(image->data);
1476 image->data = resizedData;
1477 image->width = newWidth;
1478 image->height = newHeight;
1482 // Generate all mipmap levels for a provided image
1483 // NOTE 1: Supports POT and NPOT images
1484 // NOTE 2: image.data is scaled to include mipmap levels
1485 // NOTE 3: Mipmaps format is the same as base image
1486 void ImageMipmaps(Image *image)
1488 // Security check to avoid program crash
1489 if ((image->data == NULL) || (image->width == 0) || (image->height == 0)) return;
1491 int mipCount = 1; // Required mipmap levels count (including base level)
1492 int mipWidth = image->width; // Base image width
1493 int mipHeight = image->height; // Base image height
1494 int mipSize = GetPixelDataSize(mipWidth, mipHeight, image->format); // Image data size (in bytes)
1496 // Count mipmap levels required
1497 while ((mipWidth != 1) || (mipHeight != 1))
1499 if (mipWidth != 1) mipWidth /= 2;
1500 if (mipHeight != 1) mipHeight /= 2;
1502 // Security check for NPOT textures
1503 if (mipWidth < 1) mipWidth = 1;
1504 if (mipHeight < 1) mipHeight = 1;
1506 TRACELOGD("IMAGE: Next mipmap level: %i x %i - current size %i", mipWidth, mipHeight, mipSize);
1509 mipSize += GetPixelDataSize(mipWidth, mipHeight, image->format); // Add mipmap size (in bytes)
1512 if (image->mipmaps < mipCount)
1514 void *temp = RL_REALLOC(image->data, mipSize);
1516 if (temp != NULL) image->data = temp; // Assign new pointer (new size) to store mipmaps data
1517 else TRACELOG(LOG_WARNING, "IMAGE: Mipmaps required memory could not be allocated");
1519 // Pointer to allocated memory point where store next mipmap level data
1520 unsigned char *nextmip = (unsigned char *)image->data + GetPixelDataSize(image->width, image->height, image->format);
1522 mipWidth = image->width/2;
1523 mipHeight = image->height/2;
1524 mipSize = GetPixelDataSize(mipWidth, mipHeight, image->format);
1525 Image imCopy = ImageCopy(*image);
1527 for (int i = 1; i < mipCount; i++)
1529 TRACELOGD("IMAGE: Generating mipmap level: %i (%i x %i) - size: %i - offset: 0x%x", i, mipWidth, mipHeight, mipSize, nextmip);
1531 ImageResize(&imCopy, mipWidth, mipHeight); // Uses internally Mitchell cubic downscale filter
1533 memcpy(nextmip, imCopy.data, mipSize);
1540 // Security check for NPOT textures
1541 if (mipWidth < 1) mipWidth = 1;
1542 if (mipHeight < 1) mipHeight = 1;
1544 mipSize = GetPixelDataSize(mipWidth, mipHeight, image->format);
1547 UnloadImage(imCopy);
1549 else TRACELOG(LOG_WARNING, "IMAGE: Mipmaps already available");
1552 // Dither image data to 16bpp or lower (Floyd-Steinberg dithering)
1553 // NOTE: In case selected bpp do not represent an known 16bit format,
1554 // dithered data is stored in the LSB part of the unsigned short
1555 void ImageDither(Image *image, int rBpp, int gBpp, int bBpp, int aBpp)
1557 // Security check to avoid program crash
1558 if ((image->data == NULL) || (image->width == 0) || (image->height == 0)) return;
1560 if (image->format >= PIXELFORMAT_COMPRESSED_DXT1_RGB)
1562 TRACELOG(LOG_WARNING, "IMAGE: Compressed data formats can not be dithered");
1566 if ((rBpp + gBpp + bBpp + aBpp) > 16)
1568 TRACELOG(LOG_WARNING, "IMAGE: Unsupported dithering bpps (%ibpp), only 16bpp or lower modes supported", (rBpp+gBpp+bBpp+aBpp));
1572 Color *pixels = LoadImageColors(*image);
1574 RL_FREE(image->data); // free old image data
1576 if ((image->format != PIXELFORMAT_UNCOMPRESSED_R8G8B8) && (image->format != PIXELFORMAT_UNCOMPRESSED_R8G8B8A8))
1578 TRACELOG(LOG_WARNING, "IMAGE: Format is already 16bpp or lower, dithering could have no effect");
1581 // Define new image format, check if desired bpp match internal known format
1582 if ((rBpp == 5) && (gBpp == 6) && (bBpp == 5) && (aBpp == 0)) image->format = PIXELFORMAT_UNCOMPRESSED_R5G6B5;
1583 else if ((rBpp == 5) && (gBpp == 5) && (bBpp == 5) && (aBpp == 1)) image->format = PIXELFORMAT_UNCOMPRESSED_R5G5B5A1;
1584 else if ((rBpp == 4) && (gBpp == 4) && (bBpp == 4) && (aBpp == 4)) image->format = PIXELFORMAT_UNCOMPRESSED_R4G4B4A4;
1588 TRACELOG(LOG_WARNING, "IMAGE: Unsupported dithered OpenGL internal format: %ibpp (R%iG%iB%iA%i)", (rBpp+gBpp+bBpp+aBpp), rBpp, gBpp, bBpp, aBpp);
1591 // NOTE: We will store the dithered data as unsigned short (16bpp)
1592 image->data = (unsigned short *)RL_MALLOC(image->width*image->height*sizeof(unsigned short));
1594 Color oldPixel = WHITE;
1595 Color newPixel = WHITE;
1597 int rError, gError, bError;
1598 unsigned short rPixel, gPixel, bPixel, aPixel; // Used for 16bit pixel composition
1600 #define MIN(a,b) (((a)<(b))?(a):(b))
1602 for (int y = 0; y < image->height; y++)
1604 for (int x = 0; x < image->width; x++)
1606 oldPixel = pixels[y*image->width + x];
1608 // NOTE: New pixel obtained by bits truncate, it would be better to round values (check ImageFormat())
1609 newPixel.r = oldPixel.r >> (8 - rBpp); // R bits
1610 newPixel.g = oldPixel.g >> (8 - gBpp); // G bits
1611 newPixel.b = oldPixel.b >> (8 - bBpp); // B bits
1612 newPixel.a = oldPixel.a >> (8 - aBpp); // A bits (not used on dithering)
1614 // NOTE: Error must be computed between new and old pixel but using same number of bits!
1615 // We want to know how much color precision we have lost...
1616 rError = (int)oldPixel.r - (int)(newPixel.r << (8 - rBpp));
1617 gError = (int)oldPixel.g - (int)(newPixel.g << (8 - gBpp));
1618 bError = (int)oldPixel.b - (int)(newPixel.b << (8 - bBpp));
1620 pixels[y*image->width + x] = newPixel;
1622 // NOTE: Some cases are out of the array and should be ignored
1623 if (x < (image->width - 1))
1625 pixels[y*image->width + x+1].r = MIN((int)pixels[y*image->width + x+1].r + (int)((float)rError*7.0f/16), 0xff);
1626 pixels[y*image->width + x+1].g = MIN((int)pixels[y*image->width + x+1].g + (int)((float)gError*7.0f/16), 0xff);
1627 pixels[y*image->width + x+1].b = MIN((int)pixels[y*image->width + x+1].b + (int)((float)bError*7.0f/16), 0xff);
1630 if ((x > 0) && (y < (image->height - 1)))
1632 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);
1633 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);
1634 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);
1637 if (y < (image->height - 1))
1639 pixels[(y+1)*image->width + x].r = MIN((int)pixels[(y+1)*image->width + x].r + (int)((float)rError*5.0f/16), 0xff);
1640 pixels[(y+1)*image->width + x].g = MIN((int)pixels[(y+1)*image->width + x].g + (int)((float)gError*5.0f/16), 0xff);
1641 pixels[(y+1)*image->width + x].b = MIN((int)pixels[(y+1)*image->width + x].b + (int)((float)bError*5.0f/16), 0xff);
1644 if ((x < (image->width - 1)) && (y < (image->height - 1)))
1646 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);
1647 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);
1648 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);
1651 rPixel = (unsigned short)newPixel.r;
1652 gPixel = (unsigned short)newPixel.g;
1653 bPixel = (unsigned short)newPixel.b;
1654 aPixel = (unsigned short)newPixel.a;
1656 ((unsigned short *)image->data)[y*image->width + x] = (rPixel << (gBpp + bBpp + aBpp)) | (gPixel << (bBpp + aBpp)) | (bPixel << aBpp) | aPixel;
1660 UnloadImageColors(pixels);
1664 // Flip image vertically
1665 void ImageFlipVertical(Image *image)
1667 // Security check to avoid program crash
1668 if ((image->data == NULL) || (image->width == 0) || (image->height == 0)) return;
1670 if (image->mipmaps > 1) TRACELOG(LOG_WARNING, "Image manipulation only applied to base mipmap level");
1671 if (image->format >= PIXELFORMAT_COMPRESSED_DXT1_RGB) TRACELOG(LOG_WARNING, "Image manipulation not supported for compressed formats");
1674 int bytesPerPixel = GetPixelDataSize(1, 1, image->format);
1675 unsigned char *flippedData = (unsigned char *)RL_MALLOC(image->width*image->height*bytesPerPixel);
1677 for (int i = (image->height - 1), offsetSize = 0; i >= 0; i--)
1679 memcpy(flippedData + offsetSize, ((unsigned char *)image->data) + i*image->width*bytesPerPixel, image->width*bytesPerPixel);
1680 offsetSize += image->width*bytesPerPixel;
1683 RL_FREE(image->data);
1684 image->data = flippedData;
1688 // Flip image horizontally
1689 void ImageFlipHorizontal(Image *image)
1691 // Security check to avoid program crash
1692 if ((image->data == NULL) || (image->width == 0) || (image->height == 0)) return;
1694 if (image->mipmaps > 1) TRACELOG(LOG_WARNING, "Image manipulation only applied to base mipmap level");
1695 if (image->format >= PIXELFORMAT_COMPRESSED_DXT1_RGB) TRACELOG(LOG_WARNING, "Image manipulation not supported for compressed formats");
1698 int bytesPerPixel = GetPixelDataSize(1, 1, image->format);
1699 unsigned char *flippedData = (unsigned char *)RL_MALLOC(image->width*image->height*bytesPerPixel);
1701 for (int y = 0; y < image->height; y++)
1703 for (int x = 0; x < image->width; x++)
1705 // OPTION 1: Move pixels with memcopy()
1706 //memcpy(flippedData + (y*image->width + x)*bytesPerPixel, ((unsigned char *)image->data) + (y*image->width + (image->width - 1 - x))*bytesPerPixel, bytesPerPixel);
1708 // OPTION 2: Just copy data pixel by pixel
1709 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];
1713 RL_FREE(image->data);
1714 image->data = flippedData;
1717 // OPTION 3: Faster implementation (specific for 32bit pixels)
1718 // NOTE: It does not require additional allocations
1719 uint32_t *ptr = (uint32_t *)image->data;
1720 for (int y = 0; y < image->height; y++)
1722 for (int x = 0; x < image->width/2; x++)
1724 uint32_t backup = ptr[y*image->width + x];
1725 ptr[y*image->width + x] = ptr[y*image->width + (image->width - 1 - x)];
1726 ptr[y*image->width + (image->width - 1 - x)] = backup;
1733 // Rotate image clockwise 90deg
1734 void ImageRotateCW(Image *image)
1736 // Security check to avoid program crash
1737 if ((image->data == NULL) || (image->width == 0) || (image->height == 0)) return;
1739 if (image->mipmaps > 1) TRACELOG(LOG_WARNING, "Image manipulation only applied to base mipmap level");
1740 if (image->format >= PIXELFORMAT_COMPRESSED_DXT1_RGB) TRACELOG(LOG_WARNING, "Image manipulation not supported for compressed formats");
1743 int bytesPerPixel = GetPixelDataSize(1, 1, image->format);
1744 unsigned char *rotatedData = (unsigned char *)RL_MALLOC(image->width*image->height*bytesPerPixel);
1746 for (int y = 0; y < image->height; y++)
1748 for (int x = 0; x < image->width; x++)
1750 //memcpy(rotatedData + (x*image->height + (image->height - y - 1))*bytesPerPixel, ((unsigned char *)image->data) + (y*image->width + x)*bytesPerPixel, bytesPerPixel);
1751 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];
1755 RL_FREE(image->data);
1756 image->data = rotatedData;
1757 int width = image->width;
1758 int height = image-> height;
1760 image->width = height;
1761 image->height = width;
1765 // Rotate image counter-clockwise 90deg
1766 void ImageRotateCCW(Image *image)
1768 // Security check to avoid program crash
1769 if ((image->data == NULL) || (image->width == 0) || (image->height == 0)) return;
1771 if (image->mipmaps > 1) TRACELOG(LOG_WARNING, "Image manipulation only applied to base mipmap level");
1772 if (image->format >= PIXELFORMAT_COMPRESSED_DXT1_RGB) TRACELOG(LOG_WARNING, "Image manipulation not supported for compressed formats");
1775 int bytesPerPixel = GetPixelDataSize(1, 1, image->format);
1776 unsigned char *rotatedData = (unsigned char *)RL_MALLOC(image->width*image->height*bytesPerPixel);
1778 for (int y = 0; y < image->height; y++)
1780 for (int x = 0; x < image->width; x++)
1782 //memcpy(rotatedData + (x*image->height + y))*bytesPerPixel, ((unsigned char *)image->data) + (y*image->width + (image->width - x - 1))*bytesPerPixel, bytesPerPixel);
1783 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];
1787 RL_FREE(image->data);
1788 image->data = rotatedData;
1789 int width = image->width;
1790 int height = image-> height;
1792 image->width = height;
1793 image->height = width;
1797 // Modify image color: tint
1798 void ImageColorTint(Image *image, Color color)
1800 // Security check to avoid program crash
1801 if ((image->data == NULL) || (image->width == 0) || (image->height == 0)) return;
1803 Color *pixels = LoadImageColors(*image);
1805 float cR = (float)color.r/255;
1806 float cG = (float)color.g/255;
1807 float cB = (float)color.b/255;
1808 float cA = (float)color.a/255;
1810 for (int y = 0; y < image->height; y++)
1812 for (int x = 0; x < image->width; x++)
1814 int index = y*image->width + x;
1815 unsigned char r = (unsigned char)(((float)pixels[index].r/255*cR)*255.0f);
1816 unsigned char g = (unsigned char)(((float)pixels[index].g/255*cG)*255.0f);
1817 unsigned char b = (unsigned char)(((float)pixels[index].b/255*cB)*255.0f);
1818 unsigned char a = (unsigned char)(((float)pixels[index].a/255*cA)*255.0f);
1820 pixels[y*image->width + x].r = r;
1821 pixels[y*image->width + x].g = g;
1822 pixels[y*image->width + x].b = b;
1823 pixels[y*image->width + x].a = a;
1827 int format = image->format;
1828 RL_FREE(image->data);
1830 image->data = pixels;
1831 image->format = PIXELFORMAT_UNCOMPRESSED_R8G8B8A8;
1833 ImageFormat(image, format);
1836 // Modify image color: invert
1837 void ImageColorInvert(Image *image)
1839 // Security check to avoid program crash
1840 if ((image->data == NULL) || (image->width == 0) || (image->height == 0)) return;
1842 Color *pixels = LoadImageColors(*image);
1844 for (int y = 0; y < image->height; y++)
1846 for (int x = 0; x < image->width; x++)
1848 pixels[y*image->width + x].r = 255 - pixels[y*image->width + x].r;
1849 pixels[y*image->width + x].g = 255 - pixels[y*image->width + x].g;
1850 pixels[y*image->width + x].b = 255 - pixels[y*image->width + x].b;
1854 int format = image->format;
1855 RL_FREE(image->data);
1857 image->data = pixels;
1858 image->format = PIXELFORMAT_UNCOMPRESSED_R8G8B8A8;
1860 ImageFormat(image, format);
1863 // Modify image color: grayscale
1864 void ImageColorGrayscale(Image *image)
1866 ImageFormat(image, PIXELFORMAT_UNCOMPRESSED_GRAYSCALE);
1869 // Modify image color: contrast
1870 // NOTE: Contrast values between -100 and 100
1871 void ImageColorContrast(Image *image, float contrast)
1873 // Security check to avoid program crash
1874 if ((image->data == NULL) || (image->width == 0) || (image->height == 0)) return;
1876 if (contrast < -100) contrast = -100;
1877 if (contrast > 100) contrast = 100;
1879 contrast = (100.0f + contrast)/100.0f;
1880 contrast *= contrast;
1882 Color *pixels = LoadImageColors(*image);
1884 for (int y = 0; y < image->height; y++)
1886 for (int x = 0; x < image->width; x++)
1888 float pR = (float)pixels[y*image->width + x].r/255.0f;
1894 if (pR > 255) pR = 255;
1896 float pG = (float)pixels[y*image->width + x].g/255.0f;
1902 if (pG > 255) pG = 255;
1904 float pB = (float)pixels[y*image->width + x].b/255.0f;
1910 if (pB > 255) pB = 255;
1912 pixels[y*image->width + x].r = (unsigned char)pR;
1913 pixels[y*image->width + x].g = (unsigned char)pG;
1914 pixels[y*image->width + x].b = (unsigned char)pB;
1918 int format = image->format;
1919 RL_FREE(image->data);
1921 image->data = pixels;
1922 image->format = PIXELFORMAT_UNCOMPRESSED_R8G8B8A8;
1924 ImageFormat(image, format);
1927 // Modify image color: brightness
1928 // NOTE: Brightness values between -255 and 255
1929 void ImageColorBrightness(Image *image, int brightness)
1931 // Security check to avoid program crash
1932 if ((image->data == NULL) || (image->width == 0) || (image->height == 0)) return;
1934 if (brightness < -255) brightness = -255;
1935 if (brightness > 255) brightness = 255;
1937 Color *pixels = LoadImageColors(*image);
1939 for (int y = 0; y < image->height; y++)
1941 for (int x = 0; x < image->width; x++)
1943 int cR = pixels[y*image->width + x].r + brightness;
1944 int cG = pixels[y*image->width + x].g + brightness;
1945 int cB = pixels[y*image->width + x].b + brightness;
1948 if (cR > 255) cR = 255;
1951 if (cG > 255) cG = 255;
1954 if (cB > 255) cB = 255;
1956 pixels[y*image->width + x].r = (unsigned char)cR;
1957 pixels[y*image->width + x].g = (unsigned char)cG;
1958 pixels[y*image->width + x].b = (unsigned char)cB;
1962 int format = image->format;
1963 RL_FREE(image->data);
1965 image->data = pixels;
1966 image->format = PIXELFORMAT_UNCOMPRESSED_R8G8B8A8;
1968 ImageFormat(image, format);
1971 // Modify image color: replace color
1972 void ImageColorReplace(Image *image, Color color, Color replace)
1974 // Security check to avoid program crash
1975 if ((image->data == NULL) || (image->width == 0) || (image->height == 0)) return;
1977 Color *pixels = LoadImageColors(*image);
1979 for (int y = 0; y < image->height; y++)
1981 for (int x = 0; x < image->width; x++)
1983 if ((pixels[y*image->width + x].r == color.r) &&
1984 (pixels[y*image->width + x].g == color.g) &&
1985 (pixels[y*image->width + x].b == color.b) &&
1986 (pixels[y*image->width + x].a == color.a))
1988 pixels[y*image->width + x].r = replace.r;
1989 pixels[y*image->width + x].g = replace.g;
1990 pixels[y*image->width + x].b = replace.b;
1991 pixels[y*image->width + x].a = replace.a;
1996 int format = image->format;
1997 RL_FREE(image->data);
1999 image->data = pixels;
2000 image->format = PIXELFORMAT_UNCOMPRESSED_R8G8B8A8;
2002 ImageFormat(image, format);
2004 #endif // SUPPORT_IMAGE_MANIPULATION
2006 // Load color data from image as a Color array (RGBA - 32bit)
2007 // NOTE: Memory allocated should be freed using UnloadImageColors();
2008 Color *LoadImageColors(Image image)
2010 if ((image.width == 0) || (image.height == 0)) return NULL;
2012 Color *pixels = (Color *)RL_MALLOC(image.width*image.height*sizeof(Color));
2014 if (image.format >= PIXELFORMAT_COMPRESSED_DXT1_RGB) TRACELOG(LOG_WARNING, "IMAGE: Pixel data retrieval not supported for compressed image formats");
2017 if ((image.format == PIXELFORMAT_UNCOMPRESSED_R32) ||
2018 (image.format == PIXELFORMAT_UNCOMPRESSED_R32G32B32) ||
2019 (image.format == PIXELFORMAT_UNCOMPRESSED_R32G32B32A32)) TRACELOG(LOG_WARNING, "IMAGE: Pixel format converted from 32bit to 8bit per channel");
2021 for (int i = 0, k = 0; i < image.width*image.height; i++)
2023 switch (image.format)
2025 case PIXELFORMAT_UNCOMPRESSED_GRAYSCALE:
2027 pixels[i].r = ((unsigned char *)image.data)[i];
2028 pixels[i].g = ((unsigned char *)image.data)[i];
2029 pixels[i].b = ((unsigned char *)image.data)[i];
2033 case PIXELFORMAT_UNCOMPRESSED_GRAY_ALPHA:
2035 pixels[i].r = ((unsigned char *)image.data)[k];
2036 pixels[i].g = ((unsigned char *)image.data)[k];
2037 pixels[i].b = ((unsigned char *)image.data)[k];
2038 pixels[i].a = ((unsigned char *)image.data)[k + 1];
2042 case PIXELFORMAT_UNCOMPRESSED_R5G5B5A1:
2044 unsigned short pixel = ((unsigned short *)image.data)[i];
2046 pixels[i].r = (unsigned char)((float)((pixel & 0b1111100000000000) >> 11)*(255/31));
2047 pixels[i].g = (unsigned char)((float)((pixel & 0b0000011111000000) >> 6)*(255/31));
2048 pixels[i].b = (unsigned char)((float)((pixel & 0b0000000000111110) >> 1)*(255/31));
2049 pixels[i].a = (unsigned char)((pixel & 0b0000000000000001)*255);
2052 case PIXELFORMAT_UNCOMPRESSED_R5G6B5:
2054 unsigned short pixel = ((unsigned short *)image.data)[i];
2056 pixels[i].r = (unsigned char)((float)((pixel & 0b1111100000000000) >> 11)*(255/31));
2057 pixels[i].g = (unsigned char)((float)((pixel & 0b0000011111100000) >> 5)*(255/63));
2058 pixels[i].b = (unsigned char)((float)(pixel & 0b0000000000011111)*(255/31));
2062 case PIXELFORMAT_UNCOMPRESSED_R4G4B4A4:
2064 unsigned short pixel = ((unsigned short *)image.data)[i];
2066 pixels[i].r = (unsigned char)((float)((pixel & 0b1111000000000000) >> 12)*(255/15));
2067 pixels[i].g = (unsigned char)((float)((pixel & 0b0000111100000000) >> 8)*(255/15));
2068 pixels[i].b = (unsigned char)((float)((pixel & 0b0000000011110000) >> 4)*(255/15));
2069 pixels[i].a = (unsigned char)((float)(pixel & 0b0000000000001111)*(255/15));
2072 case PIXELFORMAT_UNCOMPRESSED_R8G8B8A8:
2074 pixels[i].r = ((unsigned char *)image.data)[k];
2075 pixels[i].g = ((unsigned char *)image.data)[k + 1];
2076 pixels[i].b = ((unsigned char *)image.data)[k + 2];
2077 pixels[i].a = ((unsigned char *)image.data)[k + 3];
2081 case PIXELFORMAT_UNCOMPRESSED_R8G8B8:
2083 pixels[i].r = (unsigned char)((unsigned char *)image.data)[k];
2084 pixels[i].g = (unsigned char)((unsigned char *)image.data)[k + 1];
2085 pixels[i].b = (unsigned char)((unsigned char *)image.data)[k + 2];
2090 case PIXELFORMAT_UNCOMPRESSED_R32:
2092 pixels[i].r = (unsigned char)(((float *)image.data)[k]*255.0f);
2098 case PIXELFORMAT_UNCOMPRESSED_R32G32B32:
2100 pixels[i].r = (unsigned char)(((float *)image.data)[k]*255.0f);
2101 pixels[i].g = (unsigned char)(((float *)image.data)[k + 1]*255.0f);
2102 pixels[i].b = (unsigned char)(((float *)image.data)[k + 2]*255.0f);
2107 case PIXELFORMAT_UNCOMPRESSED_R32G32B32A32:
2109 pixels[i].r = (unsigned char)(((float *)image.data)[k]*255.0f);
2110 pixels[i].g = (unsigned char)(((float *)image.data)[k]*255.0f);
2111 pixels[i].b = (unsigned char)(((float *)image.data)[k]*255.0f);
2112 pixels[i].a = (unsigned char)(((float *)image.data)[k]*255.0f);
2124 // Load colors palette from image as a Color array (RGBA - 32bit)
2125 // NOTE: Memory allocated should be freed using UnloadImagePalette()
2126 Color *LoadImagePalette(Image image, int maxPaletteSize, int *colorsCount)
2128 #define COLOR_EQUAL(col1, col2) ((col1.r == col2.r)&&(col1.g == col2.g)&&(col1.b == col2.b)&&(col1.a == col2.a))
2131 Color *palette = NULL;
2132 Color *pixels = LoadImageColors(image);
2136 palette = (Color *)RL_MALLOC(maxPaletteSize*sizeof(Color));
2138 for (int i = 0; i < maxPaletteSize; i++) palette[i] = BLANK; // Set all colors to BLANK
2140 for (int i = 0; i < image.width*image.height; i++)
2142 if (pixels[i].a > 0)
2144 bool colorInPalette = false;
2146 // Check if the color is already on palette
2147 for (int j = 0; j < maxPaletteSize; j++)
2149 if (COLOR_EQUAL(pixels[i], palette[j]))
2151 colorInPalette = true;
2156 // Store color if not on the palette
2157 if (!colorInPalette)
2159 palette[palCount] = pixels[i]; // Add pixels[i] to palette
2162 // We reached the limit of colors supported by palette
2163 if (palCount >= maxPaletteSize)
2165 i = image.width*image.height; // Finish palette get
2166 TRACELOG(LOG_WARNING, "IMAGE: Palette is greater than %i colors", maxPaletteSize);
2172 UnloadImageColors(pixels);
2175 *colorsCount = palCount;
2180 // Unload color data loaded with LoadImageColors()
2181 void UnloadImageColors(Color *colors)
2186 // Unload colors palette loaded with LoadImagePalette()
2187 void UnloadImagePalette(Color *colors)
2192 // Get pixel data from image as Vector4 array (float normalized)
2193 static Vector4 *LoadImageDataNormalized(Image image)
2195 Vector4 *pixels = (Vector4 *)RL_MALLOC(image.width*image.height*sizeof(Vector4));
2197 if (image.format >= PIXELFORMAT_COMPRESSED_DXT1_RGB) TRACELOG(LOG_WARNING, "IMAGE: Pixel data retrieval not supported for compressed image formats");
2200 for (int i = 0, k = 0; i < image.width*image.height; i++)
2202 switch (image.format)
2204 case PIXELFORMAT_UNCOMPRESSED_GRAYSCALE:
2206 pixels[i].x = (float)((unsigned char *)image.data)[i]/255.0f;
2207 pixels[i].y = (float)((unsigned char *)image.data)[i]/255.0f;
2208 pixels[i].z = (float)((unsigned char *)image.data)[i]/255.0f;
2212 case PIXELFORMAT_UNCOMPRESSED_GRAY_ALPHA:
2214 pixels[i].x = (float)((unsigned char *)image.data)[k]/255.0f;
2215 pixels[i].y = (float)((unsigned char *)image.data)[k]/255.0f;
2216 pixels[i].z = (float)((unsigned char *)image.data)[k]/255.0f;
2217 pixels[i].w = (float)((unsigned char *)image.data)[k + 1]/255.0f;
2221 case PIXELFORMAT_UNCOMPRESSED_R5G5B5A1:
2223 unsigned short pixel = ((unsigned short *)image.data)[i];
2225 pixels[i].x = (float)((pixel & 0b1111100000000000) >> 11)*(1.0f/31);
2226 pixels[i].y = (float)((pixel & 0b0000011111000000) >> 6)*(1.0f/31);
2227 pixels[i].z = (float)((pixel & 0b0000000000111110) >> 1)*(1.0f/31);
2228 pixels[i].w = ((pixel & 0b0000000000000001) == 0)? 0.0f : 1.0f;
2231 case PIXELFORMAT_UNCOMPRESSED_R5G6B5:
2233 unsigned short pixel = ((unsigned short *)image.data)[i];
2235 pixels[i].x = (float)((pixel & 0b1111100000000000) >> 11)*(1.0f/31);
2236 pixels[i].y = (float)((pixel & 0b0000011111100000) >> 5)*(1.0f/63);
2237 pixels[i].z = (float)(pixel & 0b0000000000011111)*(1.0f/31);
2241 case PIXELFORMAT_UNCOMPRESSED_R4G4B4A4:
2243 unsigned short pixel = ((unsigned short *)image.data)[i];
2245 pixels[i].x = (float)((pixel & 0b1111000000000000) >> 12)*(1.0f/15);
2246 pixels[i].y = (float)((pixel & 0b0000111100000000) >> 8)*(1.0f/15);
2247 pixels[i].z = (float)((pixel & 0b0000000011110000) >> 4)*(1.0f/15);
2248 pixels[i].w = (float)(pixel & 0b0000000000001111)*(1.0f/15);
2251 case PIXELFORMAT_UNCOMPRESSED_R8G8B8A8:
2253 pixels[i].x = (float)((unsigned char *)image.data)[k]/255.0f;
2254 pixels[i].y = (float)((unsigned char *)image.data)[k + 1]/255.0f;
2255 pixels[i].z = (float)((unsigned char *)image.data)[k + 2]/255.0f;
2256 pixels[i].w = (float)((unsigned char *)image.data)[k + 3]/255.0f;
2260 case PIXELFORMAT_UNCOMPRESSED_R8G8B8:
2262 pixels[i].x = (float)((unsigned char *)image.data)[k]/255.0f;
2263 pixels[i].y = (float)((unsigned char *)image.data)[k + 1]/255.0f;
2264 pixels[i].z = (float)((unsigned char *)image.data)[k + 2]/255.0f;
2269 case PIXELFORMAT_UNCOMPRESSED_R32:
2271 pixels[i].x = ((float *)image.data)[k];
2277 case PIXELFORMAT_UNCOMPRESSED_R32G32B32:
2279 pixels[i].x = ((float *)image.data)[k];
2280 pixels[i].y = ((float *)image.data)[k + 1];
2281 pixels[i].z = ((float *)image.data)[k + 2];
2286 case PIXELFORMAT_UNCOMPRESSED_R32G32B32A32:
2288 pixels[i].x = ((float *)image.data)[k];
2289 pixels[i].y = ((float *)image.data)[k + 1];
2290 pixels[i].z = ((float *)image.data)[k + 2];
2291 pixels[i].w = ((float *)image.data)[k + 3];
2303 // Get image alpha border rectangle
2304 // NOTE: Threshold is defined as a percentatge: 0.0f -> 1.0f
2305 Rectangle GetImageAlphaBorder(Image image, float threshold)
2307 Rectangle crop = { 0 };
2309 Color *pixels = LoadImageColors(image);
2313 int xMin = 65536; // Define a big enough number
2318 for (int y = 0; y < image.height; y++)
2320 for (int x = 0; x < image.width; x++)
2322 if (pixels[y*image.width + x].a > (unsigned char)(threshold*255.0f))
2324 if (x < xMin) xMin = x;
2325 if (x > xMax) xMax = x;
2326 if (y < yMin) yMin = y;
2327 if (y > yMax) yMax = y;
2332 // Check for empty blank image
2333 if ((xMin != 65536) && (xMax != 65536))
2335 crop = (Rectangle){ (float)xMin, (float)yMin, (float)((xMax + 1) - xMin), (float)((yMax + 1) - yMin) };
2338 UnloadImageColors(pixels);
2344 //------------------------------------------------------------------------------------
2345 // Image drawing functions
2346 //------------------------------------------------------------------------------------
2347 // Clear image background with given color
2348 void ImageClearBackground(Image *dst, Color color)
2350 for (int i = 0; i < dst->width*dst->height; ++i) ImageDrawPixel(dst, i%dst->width, i/dst->width, color);
2353 // Draw pixel within an image
2354 // NOTE: Compressed image formats not supported
2355 void ImageDrawPixel(Image *dst, int x, int y, Color color)
2357 // Security check to avoid program crash
2358 if ((dst->data == NULL) || (x < 0) || (x >= dst->width) || (y < 0) || (y >= dst->height)) return;
2360 switch (dst->format)
2362 case PIXELFORMAT_UNCOMPRESSED_GRAYSCALE:
2364 // NOTE: Calculate grayscale equivalent color
2365 Vector3 coln = { (float)color.r/255.0f, (float)color.g/255.0f, (float)color.b/255.0f };
2366 unsigned char gray = (unsigned char)((coln.x*0.299f + coln.y*0.587f + coln.z*0.114f)*255.0f);
2368 ((unsigned char *)dst->data)[y*dst->width + x] = gray;
2371 case PIXELFORMAT_UNCOMPRESSED_GRAY_ALPHA:
2373 // NOTE: Calculate grayscale equivalent color
2374 Vector3 coln = { (float)color.r/255.0f, (float)color.g/255.0f, (float)color.b/255.0f };
2375 unsigned char gray = (unsigned char)((coln.x*0.299f + coln.y*0.587f + coln.z*0.114f)*255.0f);
2377 ((unsigned char *)dst->data)[(y*dst->width + x)*2] = gray;
2378 ((unsigned char *)dst->data)[(y*dst->width + x)*2 + 1] = color.a;
2381 case PIXELFORMAT_UNCOMPRESSED_R5G6B5:
2383 // NOTE: Calculate R5G6B5 equivalent color
2384 Vector3 coln = { (float)color.r/255.0f, (float)color.g/255.0f, (float)color.b/255.0f };
2386 unsigned char r = (unsigned char)(round(coln.x*31.0f));
2387 unsigned char g = (unsigned char)(round(coln.y*63.0f));
2388 unsigned char b = (unsigned char)(round(coln.z*31.0f));
2390 ((unsigned short *)dst->data)[y*dst->width + x] = (unsigned short)r << 11 | (unsigned short)g << 5 | (unsigned short)b;
2393 case PIXELFORMAT_UNCOMPRESSED_R5G5B5A1:
2395 // NOTE: Calculate R5G5B5A1 equivalent color
2396 Vector4 coln = { (float)color.r/255.0f, (float)color.g/255.0f, (float)color.b/255.0f, (float)color.a/255.0f };
2398 unsigned char r = (unsigned char)(round(coln.x*31.0f));
2399 unsigned char g = (unsigned char)(round(coln.y*31.0f));
2400 unsigned char b = (unsigned char)(round(coln.z*31.0f));
2401 unsigned char a = (coln.w > ((float)PIXELFORMAT_UNCOMPRESSED_R5G5B5A1_ALPHA_THRESHOLD/255.0f))? 1 : 0;;
2403 ((unsigned short *)dst->data)[y*dst->width + x] = (unsigned short)r << 11 | (unsigned short)g << 6 | (unsigned short)b << 1 | (unsigned short)a;
2406 case PIXELFORMAT_UNCOMPRESSED_R4G4B4A4:
2408 // NOTE: Calculate R5G5B5A1 equivalent color
2409 Vector4 coln = { (float)color.r/255.0f, (float)color.g/255.0f, (float)color.b/255.0f, (float)color.a/255.0f };
2411 unsigned char r = (unsigned char)(round(coln.x*15.0f));
2412 unsigned char g = (unsigned char)(round(coln.y*15.0f));
2413 unsigned char b = (unsigned char)(round(coln.z*15.0f));
2414 unsigned char a = (unsigned char)(round(coln.w*15.0f));
2416 ((unsigned short *)dst->data)[y*dst->width + x] = (unsigned short)r << 12 | (unsigned short)g << 8 | (unsigned short)b << 4 | (unsigned short)a;
2419 case PIXELFORMAT_UNCOMPRESSED_R8G8B8:
2421 ((unsigned char *)dst->data)[(y*dst->width + x)*3] = color.r;
2422 ((unsigned char *)dst->data)[(y*dst->width + x)*3 + 1] = color.g;
2423 ((unsigned char *)dst->data)[(y*dst->width + x)*3 + 2] = color.b;
2426 case PIXELFORMAT_UNCOMPRESSED_R8G8B8A8:
2428 ((unsigned char *)dst->data)[(y*dst->width + x)*4] = color.r;
2429 ((unsigned char *)dst->data)[(y*dst->width + x)*4 + 1] = color.g;
2430 ((unsigned char *)dst->data)[(y*dst->width + x)*4 + 2] = color.b;
2431 ((unsigned char *)dst->data)[(y*dst->width + x)*4 + 3] = color.a;
2434 case PIXELFORMAT_UNCOMPRESSED_R32:
2436 // NOTE: Calculate grayscale equivalent color (normalized to 32bit)
2437 Vector3 coln = { (float)color.r/255.0f, (float)color.g/255.0f, (float)color.b/255.0f };
2439 ((float *)dst->data)[y*dst->width + x] = coln.x*0.299f + coln.y*0.587f + coln.z*0.114f;
2442 case PIXELFORMAT_UNCOMPRESSED_R32G32B32:
2444 // NOTE: Calculate R32G32B32 equivalent color (normalized to 32bit)
2445 Vector3 coln = { (float)color.r/255.0f, (float)color.g/255.0f, (float)color.b/255.0f };
2447 ((float *)dst->data)[(y*dst->width + x)*3] = coln.x;
2448 ((float *)dst->data)[(y*dst->width + x)*3 + 1] = coln.y;
2449 ((float *)dst->data)[(y*dst->width + x)*3 + 2] = coln.z;
2451 case PIXELFORMAT_UNCOMPRESSED_R32G32B32A32:
2453 // NOTE: Calculate R32G32B32A32 equivalent color (normalized to 32bit)
2454 Vector4 coln = { (float)color.r/255.0f, (float)color.g/255.0f, (float)color.b/255.0f, (float)color.a/255.0f };
2456 ((float *)dst->data)[(y*dst->width + x)*4] = coln.x;
2457 ((float *)dst->data)[(y*dst->width + x)*4 + 1] = coln.y;
2458 ((float *)dst->data)[(y*dst->width + x)*4 + 2] = coln.z;
2459 ((float *)dst->data)[(y*dst->width + x)*4 + 3] = coln.w;
2466 // Draw pixel within an image (Vector version)
2467 void ImageDrawPixelV(Image *dst, Vector2 position, Color color)
2469 ImageDrawPixel(dst, (int)position.x, (int)position.y, color);
2472 // Draw line within an image
2473 void ImageDrawLine(Image *dst, int startPosX, int startPosY, int endPosX, int endPosY, Color color)
2475 int m = 2*(endPosY - startPosY);
2476 int slopeError = m - (endPosX - startPosX);
2478 for (int x = startPosX, y = startPosY; x <= endPosX; x++)
2480 ImageDrawPixel(dst, x, y, color);
2483 if (slopeError >= 0)
2486 slopeError -= 2*(endPosX - startPosX);
2491 // Draw line within an image (Vector version)
2492 void ImageDrawLineV(Image *dst, Vector2 start, Vector2 end, Color color)
2494 ImageDrawLine(dst, (int)start.x, (int)start.y, (int)end.x, (int)end.y, color);
2497 // Draw circle within an image
2498 void ImageDrawCircle(Image *dst, int centerX, int centerY, int radius, Color color)
2500 int x = 0, y = radius;
2501 int decesionParameter = 3 - 2*radius;
2505 ImageDrawPixel(dst, centerX + x, centerY + y, color);
2506 ImageDrawPixel(dst, centerX - x, centerY + y, color);
2507 ImageDrawPixel(dst, centerX + x, centerY - y, color);
2508 ImageDrawPixel(dst, centerX - x, centerY - y, color);
2509 ImageDrawPixel(dst, centerX + y, centerY + x, color);
2510 ImageDrawPixel(dst, centerX - y, centerY + x, color);
2511 ImageDrawPixel(dst, centerX + y, centerY - x, color);
2512 ImageDrawPixel(dst, centerX - y, centerY - x, color);
2515 if (decesionParameter > 0)
2518 decesionParameter = decesionParameter + 4*(x - y) + 10;
2520 else decesionParameter = decesionParameter + 4*x + 6;
2524 // Draw circle within an image (Vector version)
2525 void ImageDrawCircleV(Image *dst, Vector2 center, int radius, Color color)
2527 ImageDrawCircle(dst, (int)center.x, (int)center.y, radius, color);
2530 // Draw rectangle within an image
2531 void ImageDrawRectangle(Image *dst, int posX, int posY, int width, int height, Color color)
2533 ImageDrawRectangleRec(dst, (Rectangle){ (float)posX, (float)posY, (float)width, (float)height }, color);
2536 // Draw rectangle within an image (Vector version)
2537 void ImageDrawRectangleV(Image *dst, Vector2 position, Vector2 size, Color color)
2539 ImageDrawRectangle(dst, (int)position.x, (int)position.y, (int)size.x, (int)size.y, color);
2542 // Draw rectangle within an image
2543 void ImageDrawRectangleRec(Image *dst, Rectangle rec, Color color)
2545 // Security check to avoid program crash
2546 if ((dst->data == NULL) || (dst->width == 0) || (dst->height == 0)) return;
2548 int sy = (int)rec.y;
2549 int ey = sy + (int)rec.height;
2551 int sx = (int)rec.x;
2552 int ex = sx + (int)rec.width;
2554 for (int y = sy; y < ey; y++)
2556 for (int x = sx; x < ex; x++)
2558 ImageDrawPixel(dst, x, y, color);
2563 // Draw rectangle lines within an image
2564 void ImageDrawRectangleLines(Image *dst, Rectangle rec, int thick, Color color)
2566 ImageDrawRectangle(dst, (int)rec.x, (int)rec.y, (int)rec.width, thick, color);
2567 ImageDrawRectangle(dst, (int)rec.x, (int)(rec.y + thick), thick, (int)(rec.height - thick*2), color);
2568 ImageDrawRectangle(dst, (int)(rec.x + rec.width - thick), (int)(rec.y + thick), thick, (int)(rec.height - thick*2), color);
2569 ImageDrawRectangle(dst, (int)rec.x, (int)(rec.y + rec.height - thick), (int)rec.width, thick, color);
2572 // Draw an image (source) within an image (destination)
2573 // NOTE: Color tint is applied to source image
2574 void ImageDraw(Image *dst, Image src, Rectangle srcRec, Rectangle dstRec, Color tint)
2576 // Security check to avoid program crash
2577 if ((dst->data == NULL) || (dst->width == 0) || (dst->height == 0) ||
2578 (src.data == NULL) || (src.width == 0) || (src.height == 0)) return;
2580 if (dst->mipmaps > 1) TRACELOG(LOG_WARNING, "Image drawing only applied to base mipmap level");
2581 if (dst->format >= PIXELFORMAT_COMPRESSED_DXT1_RGB) TRACELOG(LOG_WARNING, "Image drawing not supported for compressed formats");
2584 Image srcMod = { 0 }; // Source copy (in case it was required)
2585 Image *srcPtr = &src; // Pointer to source image
2586 bool useSrcMod = false; // Track source copy required
2588 // Source rectangle out-of-bounds security checks
2589 if (srcRec.x < 0) { srcRec.width += srcRec.x; srcRec.x = 0; }
2590 if (srcRec.y < 0) { srcRec.height += srcRec.y; srcRec.y = 0; }
2591 if ((srcRec.x + srcRec.width) > src.width) srcRec.width = src.width - srcRec.x;
2592 if ((srcRec.y + srcRec.height) > src.height) srcRec.height = src.height - srcRec.y;
2594 // Check if source rectangle needs to be resized to destination rectangle
2595 // In that case, we make a copy of source and we apply all required transform
2596 if (((int)srcRec.width != (int)dstRec.width) || ((int)srcRec.height != (int)dstRec.height))
2598 srcMod = ImageFromImage(src, srcRec); // Create image from another image
2599 ImageResize(&srcMod, (int)dstRec.width, (int)dstRec.height); // Resize to destination rectangle
2600 srcRec = (Rectangle){ 0, 0, (float)srcMod.width, (float)srcMod.height };
2606 // Destination rectangle out-of-bounds security checks
2609 srcRec.x = -dstRec.x;
2610 srcRec.width += dstRec.x;
2613 else if ((dstRec.x + srcRec.width) > dst->width) srcRec.width = dst->width - dstRec.x;
2617 srcRec.y = -dstRec.y;
2618 srcRec.height += dstRec.y;
2621 else if ((dstRec.y + srcRec.height) > dst->height) srcRec.height = dst->height - dstRec.y;
2623 if (dst->width < srcRec.width) srcRec.width = (float)dst->width;
2624 if (dst->height < srcRec.height) srcRec.height = (float)dst->height;
2626 // This blitting method is quite fast! The process followed is:
2627 // for every pixel -> [get_src_format/get_dst_format -> blend -> format_to_dst]
2628 // Some optimization ideas:
2629 // [x] Avoid creating source copy if not required (no resize required)
2630 // [x] Optimize ImageResize() for pixel format (alternative: ImageResizeNN())
2631 // [x] Optimize ColorAlphaBlend() to avoid processing (alpha = 0) and (alpha = 1)
2632 // [x] Optimize ColorAlphaBlend() for faster operations (maybe avoiding divs?)
2633 // [x] Consider fast path: no alpha blending required cases (src has no alpha)
2634 // [x] Consider fast path: same src/dst format with no alpha -> direct line copy
2635 // [-] GetPixelColor(): Return Vector4 instead of Color, easier for ColorAlphaBlend()
2637 Color colSrc, colDst, blend;
2638 bool blendRequired = true;
2640 // Fast path: Avoid blend if source has no alpha to blend
2641 if ((tint.a == 255) && ((srcPtr->format == PIXELFORMAT_UNCOMPRESSED_GRAYSCALE) || (srcPtr->format == PIXELFORMAT_UNCOMPRESSED_R8G8B8) || (srcPtr->format == PIXELFORMAT_UNCOMPRESSED_R5G6B5))) blendRequired = false;
2643 int strideDst = GetPixelDataSize(dst->width, 1, dst->format);
2644 int bytesPerPixelDst = strideDst/(dst->width);
2646 int strideSrc = GetPixelDataSize(srcPtr->width, 1, srcPtr->format);
2647 int bytesPerPixelSrc = strideSrc/(srcPtr->width);
2649 unsigned char *pSrcBase = (unsigned char *)srcPtr->data + ((int)srcRec.y*srcPtr->width + (int)srcRec.x)*bytesPerPixelSrc;
2650 unsigned char *pDstBase = (unsigned char *)dst->data + ((int)dstRec.y*dst->width + (int)dstRec.x)*bytesPerPixelDst;
2652 for (int y = 0; y < (int)srcRec.height; y++)
2654 unsigned char *pSrc = pSrcBase;
2655 unsigned char *pDst = pDstBase;
2657 // Fast path: Avoid moving pixel by pixel if no blend required and same format
2658 if (!blendRequired && (srcPtr->format == dst->format)) memcpy(pDst, pSrc, (int)(srcRec.width)*bytesPerPixelSrc);
2661 for (int x = 0; x < (int)srcRec.width; x++)
2663 colSrc = GetPixelColor(pSrc, srcPtr->format);
2664 colDst = GetPixelColor(pDst, dst->format);
2666 // Fast path: Avoid blend if source has no alpha to blend
2667 if (blendRequired) blend = ColorAlphaBlend(colDst, colSrc, tint);
2668 else blend = colSrc;
2670 SetPixelColor(pDst, blend, dst->format);
2672 pDst += bytesPerPixelDst;
2673 pSrc += bytesPerPixelSrc;
2677 pSrcBase += strideSrc;
2678 pDstBase += strideDst;
2681 if (useSrcMod) UnloadImage(srcMod); // Unload source modified image
2685 // Draw text (default font) within an image (destination)
2686 void ImageDrawText(Image *dst, const char *text, int posX, int posY, int fontSize, Color color)
2688 Vector2 position = { (float)posX, (float)posY };
2690 // NOTE: For default font, sapcing is set to desired font size / default font size (10)
2691 ImageDrawTextEx(dst, GetFontDefault(), text, position, (float)fontSize, (float)fontSize/10, color);
2694 // Draw text (custom sprite font) within an image (destination)
2695 void ImageDrawTextEx(Image *dst, Font font, const char *text, Vector2 position, float fontSize, float spacing, Color tint)
2697 Image imText = ImageTextEx(font, text, fontSize, spacing, tint);
2699 Rectangle srcRec = { 0.0f, 0.0f, (float)imText.width, (float)imText.height };
2700 Rectangle dstRec = { position.x, position.y, (float)imText.width, (float)imText.height };
2702 ImageDraw(dst, imText, srcRec, dstRec, WHITE);
2704 UnloadImage(imText);
2707 //------------------------------------------------------------------------------------
2708 // Texture loading functions
2709 //------------------------------------------------------------------------------------
2710 // Load texture from file into GPU memory (VRAM)
2711 Texture2D LoadTexture(const char *fileName)
2713 Texture2D texture = { 0 };
2715 Image image = LoadImage(fileName);
2717 if (image.data != NULL)
2719 texture = LoadTextureFromImage(image);
2726 // Load a texture from image data
2727 // NOTE: image is not unloaded, it must be done manually
2728 Texture2D LoadTextureFromImage(Image image)
2730 Texture2D texture = { 0 };
2732 if ((image.data != NULL) && (image.width != 0) && (image.height != 0))
2734 texture.id = rlLoadTexture(image.data, image.width, image.height, image.format, image.mipmaps);
2736 else TRACELOG(LOG_WARNING, "IMAGE: Data is not valid to load texture");
2738 texture.width = image.width;
2739 texture.height = image.height;
2740 texture.mipmaps = image.mipmaps;
2741 texture.format = image.format;
2746 // Load cubemap from image, multiple image cubemap layouts supported
2747 TextureCubemap LoadTextureCubemap(Image image, int layout)
2749 TextureCubemap cubemap = { 0 };
2751 if (layout == CUBEMAP_LAYOUT_AUTO_DETECT) // Try to automatically guess layout type
2753 // Check image width/height to determine the type of cubemap provided
2754 if (image.width > image.height)
2756 if ((image.width/6) == image.height) { layout = CUBEMAP_LAYOUT_LINE_HORIZONTAL; cubemap.width = image.width/6; }
2757 else if ((image.width/4) == (image.height/3)) { layout = CUBEMAP_LAYOUT_CROSS_FOUR_BY_THREE; cubemap.width = image.width/4; }
2758 else if (image.width >= (int)((float)image.height*1.85f)) { layout = CUBEMAP_LAYOUT_PANORAMA; cubemap.width = image.width/4; }
2760 else if (image.height > image.width)
2762 if ((image.height/6) == image.width) { layout = CUBEMAP_LAYOUT_LINE_VERTICAL; cubemap.width = image.height/6; }
2763 else if ((image.width/3) == (image.height/4)) { layout = CUBEMAP_LAYOUT_CROSS_THREE_BY_FOUR; cubemap.width = image.width/3; }
2766 cubemap.height = cubemap.width;
2769 if (layout != CUBEMAP_LAYOUT_AUTO_DETECT)
2771 int size = cubemap.width;
2773 Image faces = { 0 }; // Vertical column image
2774 Rectangle faceRecs[6] = { 0 }; // Face source rectangles
2775 for (int i = 0; i < 6; i++) faceRecs[i] = (Rectangle){ 0, 0, (float)size, (float)size };
2777 if (layout == CUBEMAP_LAYOUT_LINE_VERTICAL)
2780 for (int i = 0; i < 6; i++) faceRecs[i].y = (float)size*i;
2782 else if (layout == CUBEMAP_LAYOUT_PANORAMA)
2784 // TODO: Convert panorama image to square faces...
2785 // Ref: https://github.com/denivip/panorama/blob/master/panorama.cpp
2789 if (layout == CUBEMAP_LAYOUT_LINE_HORIZONTAL) for (int i = 0; i < 6; i++) faceRecs[i].x = (float)size*i;
2790 else if (layout == CUBEMAP_LAYOUT_CROSS_THREE_BY_FOUR)
2792 faceRecs[0].x = (float)size; faceRecs[0].y = (float)size;
2793 faceRecs[1].x = (float)size; faceRecs[1].y = (float)size*3;
2794 faceRecs[2].x = (float)size; faceRecs[2].y = 0;
2795 faceRecs[3].x = (float)size; faceRecs[3].y = (float)size*2;
2796 faceRecs[4].x = 0; faceRecs[4].y = (float)size;
2797 faceRecs[5].x = (float)size*2; faceRecs[5].y = (float)size;
2799 else if (layout == CUBEMAP_LAYOUT_CROSS_FOUR_BY_THREE)
2801 faceRecs[0].x = (float)size*2; faceRecs[0].y = (float)size;
2802 faceRecs[1].x = 0; faceRecs[1].y = (float)size;
2803 faceRecs[2].x = (float)size; faceRecs[2].y = 0;
2804 faceRecs[3].x = (float)size; faceRecs[3].y = (float)size*2;
2805 faceRecs[4].x = (float)size; faceRecs[4].y = (float)size;
2806 faceRecs[5].x = (float)size*3; faceRecs[5].y = (float)size;
2809 // Convert image data to 6 faces in a vertical column, that's the optimum layout for loading
2810 faces = GenImageColor(size, size*6, MAGENTA);
2811 ImageFormat(&faces, image.format);
2813 // TODO: Image formating does not work with compressed textures!
2816 for (int i = 0; i < 6; i++) ImageDraw(&faces, image, faceRecs[i], (Rectangle){ 0, (float)size*i, (float)size, (float)size }, WHITE);
2818 cubemap.id = rlLoadTextureCubemap(faces.data, size, faces.format);
2819 if (cubemap.id == 0) TRACELOG(LOG_WARNING, "IMAGE: Failed to load cubemap image");
2823 else TRACELOG(LOG_WARNING, "IMAGE: Failed to detect cubemap image layout");
2828 // Load texture for rendering (framebuffer)
2829 // NOTE: Render texture is loaded by default with RGBA color attachment and depth RenderBuffer
2830 RenderTexture2D LoadRenderTexture(int width, int height)
2832 RenderTexture2D target = { 0 };
2834 target.id = rlLoadFramebuffer(width, height); // Load an empty framebuffer
2838 rlEnableFramebuffer(target.id);
2840 // Create color texture (default to RGBA)
2841 target.texture.id = rlLoadTexture(NULL, width, height, PIXELFORMAT_UNCOMPRESSED_R8G8B8A8, 1);
2842 target.texture.width = width;
2843 target.texture.height = height;
2844 target.texture.format = PIXELFORMAT_UNCOMPRESSED_R8G8B8A8;
2845 target.texture.mipmaps = 1;
2847 // Create depth renderbuffer/texture
2848 target.depth.id = rlLoadTextureDepth(width, height, true);
2849 target.depth.width = width;
2850 target.depth.height = height;
2851 target.depth.format = 19; //DEPTH_COMPONENT_24BIT?
2852 target.depth.mipmaps = 1;
2854 // Attach color texture and depth renderbuffer/texture to FBO
2855 rlFramebufferAttach(target.id, target.texture.id, RL_ATTACHMENT_COLOR_CHANNEL0, RL_ATTACHMENT_TEXTURE2D, 0);
2856 rlFramebufferAttach(target.id, target.depth.id, RL_ATTACHMENT_DEPTH, RL_ATTACHMENT_RENDERBUFFER, 0);
2858 // Check if fbo is complete with attachments (valid)
2859 if (rlFramebufferComplete(target.id)) TRACELOG(LOG_INFO, "FBO: [ID %i] Framebuffer object created successfully", target.id);
2861 rlDisableFramebuffer();
2863 else TRACELOG(LOG_WARNING, "FBO: Framebuffer object can not be created");
2868 // Unload texture from GPU memory (VRAM)
2869 void UnloadTexture(Texture2D texture)
2873 rlUnloadTexture(texture.id);
2875 TRACELOG(LOG_INFO, "TEXTURE: [ID %i] Unloaded texture data from VRAM (GPU)", texture.id);
2879 // Unload render texture from GPU memory (VRAM)
2880 void UnloadRenderTexture(RenderTexture2D target)
2884 // Color texture attached to FBO is deleted
2885 rlUnloadTexture(target.texture.id);
2887 // NOTE: Depth texture/renderbuffer is automatically
2888 // queried and deleted before deleting framebuffer
2889 rlUnloadFramebuffer(target.id);
2893 // Update GPU texture with new data
2894 // NOTE: pixels data must match texture.format
2895 void UpdateTexture(Texture2D texture, const void *pixels)
2897 rlUpdateTexture(texture.id, 0, 0, texture.width, texture.height, texture.format, pixels);
2900 // Update GPU texture rectangle with new data
2901 // NOTE: pixels data must match texture.format
2902 void UpdateTextureRec(Texture2D texture, Rectangle rec, const void *pixels)
2904 rlUpdateTexture(texture.id, (int)rec.x, (int)rec.y, (int)rec.width, (int)rec.height, texture.format, pixels);
2907 // Get pixel data from GPU texture and return an Image
2908 // NOTE: Compressed texture formats not supported
2909 Image GetTextureData(Texture2D texture)
2911 Image image = { 0 };
2913 if (texture.format < PIXELFORMAT_COMPRESSED_DXT1_RGB)
2915 image.data = rlReadTexturePixels(texture);
2917 if (image.data != NULL)
2919 image.width = texture.width;
2920 image.height = texture.height;
2921 image.format = texture.format;
2924 #if defined(GRAPHICS_API_OPENGL_ES2)
2925 // NOTE: Data retrieved on OpenGL ES 2.0 should be RGBA,
2926 // coming from FBO color buffer attachment, but it seems
2927 // original texture format is retrieved on RPI...
2928 image.format = PIXELFORMAT_UNCOMPRESSED_R8G8B8A8;
2930 TRACELOG(LOG_INFO, "TEXTURE: [ID %i] Pixel data retrieved successfully", texture.id);
2932 else TRACELOG(LOG_WARNING, "TEXTURE: [ID %i] Failed to retrieve pixel data", texture.id);
2934 else TRACELOG(LOG_WARNING, "TEXTURE: [ID %i] Failed to retrieve compressed pixel data", texture.id);
2939 // Get pixel data from GPU frontbuffer and return an Image (screenshot)
2940 Image GetScreenData(void)
2942 Image image = { 0 };
2944 image.width = GetScreenWidth();
2945 image.height = GetScreenHeight();
2947 image.format = PIXELFORMAT_UNCOMPRESSED_R8G8B8A8;
2948 image.data = rlReadScreenPixels(image.width, image.height);
2953 //------------------------------------------------------------------------------------
2954 // Texture configuration functions
2955 //------------------------------------------------------------------------------------
2956 // Generate GPU mipmaps for a texture
2957 void GenTextureMipmaps(Texture2D *texture)
2959 // NOTE: NPOT textures support check inside function
2960 // On WebGL (OpenGL ES 2.0) NPOT textures support is limited
2961 rlGenerateMipmaps(texture);
2964 // Set texture scaling filter mode
2965 void SetTextureFilter(Texture2D texture, int filter)
2969 case TEXTURE_FILTER_POINT:
2971 if (texture.mipmaps > 1)
2973 // RL_TEXTURE_FILTER_MIP_NEAREST - tex filter: POINT, mipmaps filter: POINT (sharp switching between mipmaps)
2974 rlTextureParameters(texture.id, RL_TEXTURE_MIN_FILTER, RL_TEXTURE_FILTER_MIP_NEAREST);
2976 // RL_TEXTURE_FILTER_NEAREST - tex filter: POINT (no filter), no mipmaps
2977 rlTextureParameters(texture.id, RL_TEXTURE_MAG_FILTER, RL_TEXTURE_FILTER_NEAREST);
2981 // RL_TEXTURE_FILTER_NEAREST - tex filter: POINT (no filter), no mipmaps
2982 rlTextureParameters(texture.id, RL_TEXTURE_MIN_FILTER, RL_TEXTURE_FILTER_NEAREST);
2983 rlTextureParameters(texture.id, RL_TEXTURE_MAG_FILTER, RL_TEXTURE_FILTER_NEAREST);
2986 case TEXTURE_FILTER_BILINEAR:
2988 if (texture.mipmaps > 1)
2990 // RL_TEXTURE_FILTER_LINEAR_MIP_NEAREST - tex filter: BILINEAR, mipmaps filter: POINT (sharp switching between mipmaps)
2991 // Alternative: RL_TEXTURE_FILTER_NEAREST_MIP_LINEAR - tex filter: POINT, mipmaps filter: BILINEAR (smooth transition between mipmaps)
2992 rlTextureParameters(texture.id, RL_TEXTURE_MIN_FILTER, RL_TEXTURE_FILTER_LINEAR_MIP_NEAREST);
2994 // RL_TEXTURE_FILTER_LINEAR - tex filter: BILINEAR, no mipmaps
2995 rlTextureParameters(texture.id, RL_TEXTURE_MAG_FILTER, RL_TEXTURE_FILTER_LINEAR);
2999 // RL_TEXTURE_FILTER_LINEAR - tex filter: BILINEAR, no mipmaps
3000 rlTextureParameters(texture.id, RL_TEXTURE_MIN_FILTER, RL_TEXTURE_FILTER_LINEAR);
3001 rlTextureParameters(texture.id, RL_TEXTURE_MAG_FILTER, RL_TEXTURE_FILTER_LINEAR);
3004 case TEXTURE_FILTER_TRILINEAR:
3006 if (texture.mipmaps > 1)
3008 // RL_TEXTURE_FILTER_MIP_LINEAR - tex filter: BILINEAR, mipmaps filter: BILINEAR (smooth transition between mipmaps)
3009 rlTextureParameters(texture.id, RL_TEXTURE_MIN_FILTER, RL_TEXTURE_FILTER_MIP_LINEAR);
3011 // RL_TEXTURE_FILTER_LINEAR - tex filter: BILINEAR, no mipmaps
3012 rlTextureParameters(texture.id, RL_TEXTURE_MAG_FILTER, RL_TEXTURE_FILTER_LINEAR);
3016 TRACELOG(LOG_WARNING, "TEXTURE: [ID %i] No mipmaps available for TRILINEAR texture filtering", texture.id);
3018 // RL_TEXTURE_FILTER_LINEAR - tex filter: BILINEAR, no mipmaps
3019 rlTextureParameters(texture.id, RL_TEXTURE_MIN_FILTER, RL_TEXTURE_FILTER_LINEAR);
3020 rlTextureParameters(texture.id, RL_TEXTURE_MAG_FILTER, RL_TEXTURE_FILTER_LINEAR);
3023 case TEXTURE_FILTER_ANISOTROPIC_4X: rlTextureParameters(texture.id, RL_TEXTURE_FILTER_ANISOTROPIC, 4); break;
3024 case TEXTURE_FILTER_ANISOTROPIC_8X: rlTextureParameters(texture.id, RL_TEXTURE_FILTER_ANISOTROPIC, 8); break;
3025 case TEXTURE_FILTER_ANISOTROPIC_16X: rlTextureParameters(texture.id, RL_TEXTURE_FILTER_ANISOTROPIC, 16); break;
3030 // Set texture wrapping mode
3031 void SetTextureWrap(Texture2D texture, int wrap)
3035 case TEXTURE_WRAP_REPEAT:
3037 rlTextureParameters(texture.id, RL_TEXTURE_WRAP_S, RL_TEXTURE_WRAP_REPEAT);
3038 rlTextureParameters(texture.id, RL_TEXTURE_WRAP_T, RL_TEXTURE_WRAP_REPEAT);
3040 case TEXTURE_WRAP_CLAMP:
3042 rlTextureParameters(texture.id, RL_TEXTURE_WRAP_S, RL_TEXTURE_WRAP_CLAMP);
3043 rlTextureParameters(texture.id, RL_TEXTURE_WRAP_T, RL_TEXTURE_WRAP_CLAMP);
3045 case TEXTURE_WRAP_MIRROR_REPEAT:
3047 rlTextureParameters(texture.id, RL_TEXTURE_WRAP_S, RL_TEXTURE_WRAP_MIRROR_REPEAT);
3048 rlTextureParameters(texture.id, RL_TEXTURE_WRAP_T, RL_TEXTURE_WRAP_MIRROR_REPEAT);
3050 case TEXTURE_WRAP_MIRROR_CLAMP:
3052 rlTextureParameters(texture.id, RL_TEXTURE_WRAP_S, RL_TEXTURE_WRAP_MIRROR_CLAMP);
3053 rlTextureParameters(texture.id, RL_TEXTURE_WRAP_T, RL_TEXTURE_WRAP_MIRROR_CLAMP);
3059 //------------------------------------------------------------------------------------
3060 // Texture drawing functions
3061 //------------------------------------------------------------------------------------
3063 void DrawTexture(Texture2D texture, int posX, int posY, Color tint)
3065 DrawTextureEx(texture, (Vector2){ (float)posX, (float)posY }, 0.0f, 1.0f, tint);
3068 // Draw a Texture2D with position defined as Vector2
3069 void DrawTextureV(Texture2D texture, Vector2 position, Color tint)
3071 DrawTextureEx(texture, position, 0, 1.0f, tint);
3074 // Draw a Texture2D with extended parameters
3075 void DrawTextureEx(Texture2D texture, Vector2 position, float rotation, float scale, Color tint)
3077 Rectangle source = { 0.0f, 0.0f, (float)texture.width, (float)texture.height };
3078 Rectangle dest = { position.x, position.y, (float)texture.width*scale, (float)texture.height*scale };
3079 Vector2 origin = { 0.0f, 0.0f };
3081 DrawTexturePro(texture, source, dest, origin, rotation, tint);
3084 // Draw a part of a texture (defined by a rectangle)
3085 void DrawTextureRec(Texture2D texture, Rectangle source, Vector2 position, Color tint)
3087 Rectangle dest = { position.x, position.y, fabsf(source.width), fabsf(source.height) };
3088 Vector2 origin = { 0.0f, 0.0f };
3090 DrawTexturePro(texture, source, dest, origin, 0.0f, tint);
3093 // Draw texture quad with tiling and offset parameters
3094 // NOTE: Tiling and offset should be provided considering normalized texture values [0..1]
3095 // i.e tiling = { 1.0f, 1.0f } refers to all texture, offset = { 0.5f, 0.5f } moves texture origin to center
3096 void DrawTextureQuad(Texture2D texture, Vector2 tiling, Vector2 offset, Rectangle quad, Color tint)
3098 Rectangle source = { offset.x*texture.width, offset.y*texture.height, tiling.x*texture.width, tiling.y*texture.height };
3099 Vector2 origin = { 0.0f, 0.0f };
3101 DrawTexturePro(texture, source, quad, origin, 0.0f, tint);
3104 // Draw part of a texture (defined by a rectangle) with rotation and scale tiled into dest.
3105 // NOTE: For tilling a whole texture DrawTextureQuad() is better
3106 void DrawTextureTiled(Texture2D texture, Rectangle source, Rectangle dest, Vector2 origin, float rotation, float scale, Color tint)
3108 if ((texture.id <= 0) || (scale <= 0.0f)) return; // Wanna see a infinite loop?!...just delete this line!
3110 int tileWidth = (int)(source.width*scale), tileHeight = (int)(source.height*scale);
3111 if ((dest.width < tileWidth) && (dest.height < tileHeight))
3113 // Can fit only one tile
3114 DrawTexturePro(texture, (Rectangle){source.x, source.y, ((float)dest.width/tileWidth)*source.width, ((float)dest.height/tileHeight)*source.height},
3115 (Rectangle){dest.x, dest.y, dest.width, dest.height}, origin, rotation, tint);
3117 else if (dest.width <= tileWidth)
3119 // Tiled vertically (one column)
3121 for (;dy+tileHeight < dest.height; dy += tileHeight)
3123 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);
3127 if (dy < dest.height)
3129 DrawTexturePro(texture, (Rectangle){source.x, source.y, ((float)dest.width/tileWidth)*source.width, ((float)(dest.height - dy)/tileHeight)*source.height},
3130 (Rectangle){dest.x, dest.y + dy, dest.width, dest.height - dy}, origin, rotation, tint);
3133 else if (dest.height <= tileHeight)
3135 // Tiled horizontally (one row)
3137 for (;dx+tileWidth < dest.width; dx += tileWidth)
3139 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);
3143 if (dx < dest.width)
3145 DrawTexturePro(texture, (Rectangle){source.x, source.y, ((float)(dest.width - dx)/tileWidth)*source.width, ((float)dest.height/tileHeight)*source.height},
3146 (Rectangle){dest.x + dx, dest.y, dest.width - dx, dest.height}, origin, rotation, tint);
3151 // Tiled both horizontally and vertically (rows and columns)
3153 for (;dx+tileWidth < dest.width; dx += tileWidth)
3156 for (;dy+tileHeight < dest.height; dy += tileHeight)
3158 DrawTexturePro(texture, source, (Rectangle){dest.x + dx, dest.y + dy, (float)tileWidth, (float)tileHeight}, origin, rotation, tint);
3161 if (dy < dest.height)
3163 DrawTexturePro(texture, (Rectangle){source.x, source.y, source.width, ((float)(dest.height - dy)/tileHeight)*source.height},
3164 (Rectangle){dest.x + dx, dest.y + dy, (float)tileWidth, dest.height - dy}, origin, rotation, tint);
3168 // Fit last column of tiles
3169 if (dx < dest.width)
3172 for (;dy+tileHeight < dest.height; dy += tileHeight)
3174 DrawTexturePro(texture, (Rectangle){source.x, source.y, ((float)(dest.width - dx)/tileWidth)*source.width, source.height},
3175 (Rectangle){dest.x + dx, dest.y + dy, dest.width - dx, (float)tileHeight}, origin, rotation, tint);
3178 // Draw final tile in the bottom right corner
3179 if (dy < dest.height)
3181 DrawTexturePro(texture, (Rectangle){source.x, source.y, ((float)(dest.width - dx)/tileWidth)*source.width, ((float)(dest.height - dy)/tileHeight)*source.height},
3182 (Rectangle){dest.x + dx, dest.y + dy, dest.width - dx, dest.height - dy}, origin, rotation, tint);
3188 // Draw a part of a texture (defined by a rectangle) with 'pro' parameters
3189 // NOTE: origin is relative to destination rectangle size
3190 void DrawTexturePro(Texture2D texture, Rectangle source, Rectangle dest, Vector2 origin, float rotation, Color tint)
3192 // Check if texture is valid
3195 float width = (float)texture.width;
3196 float height = (float)texture.height;
3200 if (source.width < 0) { flipX = true; source.width *= -1; }
3201 if (source.height < 0) source.y -= source.height;
3203 Vector2 topLeft = { 0 };
3204 Vector2 topRight = { 0 };
3205 Vector2 bottomLeft = { 0 };
3206 Vector2 bottomRight = { 0 };
3208 // Only calculate rotation if needed
3209 if (rotation == 0.0f)
3211 float x = dest.x - origin.x;
3212 float y = dest.y - origin.y;
3213 topLeft = (Vector2){ x, y };
3214 topRight = (Vector2){ x + dest.width, y };
3215 bottomLeft = (Vector2){ x, y + dest.height };
3216 bottomRight = (Vector2){ x + dest.width, y + dest.height };
3220 float sinRotation = sinf(rotation*DEG2RAD);
3221 float cosRotation = cosf(rotation*DEG2RAD);
3224 float dx = -origin.x;
3225 float dy = -origin.y;
3227 topLeft.x = x + dx*cosRotation - dy*sinRotation;
3228 topLeft.y = y + dx*sinRotation + dy*cosRotation;
3230 topRight.x = x + (dx + dest.width)*cosRotation - dy*sinRotation;
3231 topRight.y = y + (dx + dest.width)*sinRotation + dy*cosRotation;
3233 bottomLeft.x = x + dx*cosRotation - (dy + dest.height)*sinRotation;
3234 bottomLeft.y = y + dx*sinRotation + (dy + dest.height)*cosRotation;
3236 bottomRight.x = x + (dx + dest.width)*cosRotation - (dy + dest.height)*sinRotation;
3237 bottomRight.y = y + (dx + dest.width)*sinRotation + (dy + dest.height)*cosRotation;
3240 rlCheckRenderBatchLimit(4); // Make sure there is enough free space on the batch buffer
3242 rlSetTexture(texture.id);
3245 rlColor4ub(tint.r, tint.g, tint.b, tint.a);
3246 rlNormal3f(0.0f, 0.0f, 1.0f); // Normal vector pointing towards viewer
3248 // Top-left corner for texture and quad
3249 if (flipX) rlTexCoord2f((source.x + source.width)/width, source.y/height);
3250 else rlTexCoord2f(source.x/width, source.y/height);
3251 rlVertex2f(topLeft.x, topLeft.y);
3253 // Bottom-left corner for texture and quad
3254 if (flipX) rlTexCoord2f((source.x + source.width)/width, (source.y + source.height)/height);
3255 else rlTexCoord2f(source.x/width, (source.y + source.height)/height);
3256 rlVertex2f(bottomLeft.x, bottomLeft.y);
3258 // Bottom-right corner for texture and quad
3259 if (flipX) rlTexCoord2f(source.x/width, (source.y + source.height)/height);
3260 else rlTexCoord2f((source.x + source.width)/width, (source.y + source.height)/height);
3261 rlVertex2f(bottomRight.x, bottomRight.y);
3263 // Top-right corner for texture and quad
3264 if (flipX) rlTexCoord2f(source.x/width, source.y/height);
3265 else rlTexCoord2f((source.x + source.width)/width, source.y/height);
3266 rlVertex2f(topRight.x, topRight.y);
3271 // NOTE: Vertex position can be transformed using matrices
3272 // but the process is way more costly than just calculating
3273 // the vertex positions manually, like done above.
3274 // I leave here the old implementation for educational pourposes,
3275 // just in case someone wants to do some performance test
3277 rlSetTexture(texture.id);
3279 rlTranslatef(dest.x, dest.y, 0.0f);
3280 if (rotation != 0.0f) rlRotatef(rotation, 0.0f, 0.0f, 1.0f);
3281 rlTranslatef(-origin.x, -origin.y, 0.0f);
3284 rlColor4ub(tint.r, tint.g, tint.b, tint.a);
3285 rlNormal3f(0.0f, 0.0f, 1.0f); // Normal vector pointing towards viewer
3287 // Bottom-left corner for texture and quad
3288 if (flipX) rlTexCoord2f((source.x + source.width)/width, source.y/height);
3289 else rlTexCoord2f(source.x/width, source.y/height);
3290 rlVertex2f(0.0f, 0.0f);
3292 // Bottom-right corner for texture and quad
3293 if (flipX) rlTexCoord2f((source.x + source.width)/width, (source.y + source.height)/height);
3294 else rlTexCoord2f(source.x/width, (source.y + source.height)/height);
3295 rlVertex2f(0.0f, dest.height);
3297 // Top-right corner for texture and quad
3298 if (flipX) rlTexCoord2f(source.x/width, (source.y + source.height)/height);
3299 else rlTexCoord2f((source.x + source.width)/width, (source.y + source.height)/height);
3300 rlVertex2f(dest.width, dest.height);
3302 // Top-left corner for texture and quad
3303 if (flipX) rlTexCoord2f(source.x/width, source.y/height);
3304 else rlTexCoord2f((source.x + source.width)/width, source.y/height);
3305 rlVertex2f(dest.width, 0.0f);
3313 // Draws a texture (or part of it) that stretches or shrinks nicely using n-patch info
3314 void DrawTextureNPatch(Texture2D texture, NPatchInfo nPatchInfo, Rectangle dest, Vector2 origin, float rotation, Color tint)
3318 float width = (float)texture.width;
3319 float height = (float)texture.height;
3321 float patchWidth = (dest.width <= 0.0f)? 0.0f : dest.width;
3322 float patchHeight = (dest.height <= 0.0f)? 0.0f : dest.height;
3324 if (nPatchInfo.source.width < 0) nPatchInfo.source.x -= nPatchInfo.source.width;
3325 if (nPatchInfo.source.height < 0) nPatchInfo.source.y -= nPatchInfo.source.height;
3326 if (nPatchInfo.layout == NPATCH_THREE_PATCH_HORIZONTAL) patchHeight = nPatchInfo.source.height;
3327 if (nPatchInfo.layout == NPATCH_THREE_PATCH_VERTICAL) patchWidth = nPatchInfo.source.width;
3329 bool drawCenter = true;
3330 bool drawMiddle = true;
3331 float leftBorder = (float)nPatchInfo.left;
3332 float topBorder = (float)nPatchInfo.top;
3333 float rightBorder = (float)nPatchInfo.right;
3334 float bottomBorder = (float)nPatchInfo.bottom;
3336 // adjust the lateral (left and right) border widths in case patchWidth < texture.width
3337 if (patchWidth <= (leftBorder + rightBorder) && nPatchInfo.layout != NPATCH_THREE_PATCH_VERTICAL)
3340 leftBorder = (leftBorder/(leftBorder + rightBorder))*patchWidth;
3341 rightBorder = patchWidth - leftBorder;
3343 // adjust the lateral (top and bottom) border heights in case patchHeight < texture.height
3344 if (patchHeight <= (topBorder + bottomBorder) && nPatchInfo.layout != NPATCH_THREE_PATCH_HORIZONTAL)
3347 topBorder = (topBorder/(topBorder + bottomBorder))*patchHeight;
3348 bottomBorder = patchHeight - topBorder;
3351 Vector2 vertA, vertB, vertC, vertD;
3352 vertA.x = 0.0f; // outer left
3353 vertA.y = 0.0f; // outer top
3354 vertB.x = leftBorder; // inner left
3355 vertB.y = topBorder; // inner top
3356 vertC.x = patchWidth - rightBorder; // inner right
3357 vertC.y = patchHeight - bottomBorder; // inner bottom
3358 vertD.x = patchWidth; // outer right
3359 vertD.y = patchHeight; // outer bottom
3361 Vector2 coordA, coordB, coordC, coordD;
3362 coordA.x = nPatchInfo.source.x/width;
3363 coordA.y = nPatchInfo.source.y/height;
3364 coordB.x = (nPatchInfo.source.x + leftBorder)/width;
3365 coordB.y = (nPatchInfo.source.y + topBorder)/height;
3366 coordC.x = (nPatchInfo.source.x + nPatchInfo.source.width - rightBorder)/width;
3367 coordC.y = (nPatchInfo.source.y + nPatchInfo.source.height - bottomBorder)/height;
3368 coordD.x = (nPatchInfo.source.x + nPatchInfo.source.width)/width;
3369 coordD.y = (nPatchInfo.source.y + nPatchInfo.source.height)/height;
3371 rlSetTexture(texture.id);
3374 rlTranslatef(dest.x, dest.y, 0.0f);
3375 rlRotatef(rotation, 0.0f, 0.0f, 1.0f);
3376 rlTranslatef(-origin.x, -origin.y, 0.0f);
3379 rlColor4ub(tint.r, tint.g, tint.b, tint.a);
3380 rlNormal3f(0.0f, 0.0f, 1.0f); // Normal vector pointing towards viewer
3382 if (nPatchInfo.layout == NPATCH_NINE_PATCH)
3384 // ------------------------------------------------------------
3386 rlTexCoord2f(coordA.x, coordB.y); rlVertex2f(vertA.x, vertB.y); // Bottom-left corner for texture and quad
3387 rlTexCoord2f(coordB.x, coordB.y); rlVertex2f(vertB.x, vertB.y); // Bottom-right corner for texture and quad
3388 rlTexCoord2f(coordB.x, coordA.y); rlVertex2f(vertB.x, vertA.y); // Top-right corner for texture and quad
3389 rlTexCoord2f(coordA.x, coordA.y); rlVertex2f(vertA.x, vertA.y); // Top-left corner for texture and quad
3393 rlTexCoord2f(coordB.x, coordB.y); rlVertex2f(vertB.x, vertB.y); // Bottom-left corner for texture and quad
3394 rlTexCoord2f(coordC.x, coordB.y); rlVertex2f(vertC.x, vertB.y); // Bottom-right corner for texture and quad
3395 rlTexCoord2f(coordC.x, coordA.y); rlVertex2f(vertC.x, vertA.y); // Top-right corner for texture and quad
3396 rlTexCoord2f(coordB.x, coordA.y); rlVertex2f(vertB.x, vertA.y); // Top-left corner for texture and quad
3399 rlTexCoord2f(coordC.x, coordB.y); rlVertex2f(vertC.x, vertB.y); // Bottom-left corner for texture and quad
3400 rlTexCoord2f(coordD.x, coordB.y); rlVertex2f(vertD.x, vertB.y); // Bottom-right corner for texture and quad
3401 rlTexCoord2f(coordD.x, coordA.y); rlVertex2f(vertD.x, vertA.y); // Top-right corner for texture and quad
3402 rlTexCoord2f(coordC.x, coordA.y); rlVertex2f(vertC.x, vertA.y); // Top-left corner for texture and quad
3405 // ------------------------------------------------------------
3407 rlTexCoord2f(coordA.x, coordC.y); rlVertex2f(vertA.x, vertC.y); // Bottom-left corner for texture and quad
3408 rlTexCoord2f(coordB.x, coordC.y); rlVertex2f(vertB.x, vertC.y); // Bottom-right corner for texture and quad
3409 rlTexCoord2f(coordB.x, coordB.y); rlVertex2f(vertB.x, vertB.y); // Top-right corner for texture and quad
3410 rlTexCoord2f(coordA.x, coordB.y); rlVertex2f(vertA.x, vertB.y); // Top-left corner for texture and quad
3413 // MIDDLE-CENTER QUAD
3414 rlTexCoord2f(coordB.x, coordC.y); rlVertex2f(vertB.x, vertC.y); // Bottom-left corner for texture and quad
3415 rlTexCoord2f(coordC.x, coordC.y); rlVertex2f(vertC.x, vertC.y); // Bottom-right corner for texture and quad
3416 rlTexCoord2f(coordC.x, coordB.y); rlVertex2f(vertC.x, vertB.y); // Top-right corner for texture and quad
3417 rlTexCoord2f(coordB.x, coordB.y); rlVertex2f(vertB.x, vertB.y); // Top-left corner for texture and quad
3420 // MIDDLE-RIGHT QUAD
3421 rlTexCoord2f(coordC.x, coordC.y); rlVertex2f(vertC.x, vertC.y); // Bottom-left corner for texture and quad
3422 rlTexCoord2f(coordD.x, coordC.y); rlVertex2f(vertD.x, vertC.y); // Bottom-right corner for texture and quad
3423 rlTexCoord2f(coordD.x, coordB.y); rlVertex2f(vertD.x, vertB.y); // Top-right corner for texture and quad
3424 rlTexCoord2f(coordC.x, coordB.y); rlVertex2f(vertC.x, vertB.y); // Top-left corner for texture and quad
3427 // ------------------------------------------------------------
3429 rlTexCoord2f(coordA.x, coordD.y); rlVertex2f(vertA.x, vertD.y); // Bottom-left corner for texture and quad
3430 rlTexCoord2f(coordB.x, coordD.y); rlVertex2f(vertB.x, vertD.y); // Bottom-right corner for texture and quad
3431 rlTexCoord2f(coordB.x, coordC.y); rlVertex2f(vertB.x, vertC.y); // Top-right corner for texture and quad
3432 rlTexCoord2f(coordA.x, coordC.y); rlVertex2f(vertA.x, vertC.y); // Top-left corner for texture and quad
3435 // BOTTOM-CENTER QUAD
3436 rlTexCoord2f(coordB.x, coordD.y); rlVertex2f(vertB.x, vertD.y); // Bottom-left corner for texture and quad
3437 rlTexCoord2f(coordC.x, coordD.y); rlVertex2f(vertC.x, vertD.y); // Bottom-right corner for texture and quad
3438 rlTexCoord2f(coordC.x, coordC.y); rlVertex2f(vertC.x, vertC.y); // Top-right corner for texture and quad
3439 rlTexCoord2f(coordB.x, coordC.y); rlVertex2f(vertB.x, vertC.y); // Top-left corner for texture and quad
3442 // BOTTOM-RIGHT QUAD
3443 rlTexCoord2f(coordC.x, coordD.y); rlVertex2f(vertC.x, vertD.y); // Bottom-left corner for texture and quad
3444 rlTexCoord2f(coordD.x, coordD.y); rlVertex2f(vertD.x, vertD.y); // Bottom-right corner for texture and quad
3445 rlTexCoord2f(coordD.x, coordC.y); rlVertex2f(vertD.x, vertC.y); // Top-right corner for texture and quad
3446 rlTexCoord2f(coordC.x, coordC.y); rlVertex2f(vertC.x, vertC.y); // Top-left corner for texture and quad
3448 else if (nPatchInfo.layout == NPATCH_THREE_PATCH_VERTICAL)
3451 // -----------------------------------------------------------
3452 // Texture coords Vertices
3453 rlTexCoord2f(coordA.x, coordB.y); rlVertex2f(vertA.x, vertB.y); // Bottom-left corner for texture and quad
3454 rlTexCoord2f(coordD.x, coordB.y); rlVertex2f(vertD.x, vertB.y); // Bottom-right corner for texture and quad
3455 rlTexCoord2f(coordD.x, coordA.y); rlVertex2f(vertD.x, vertA.y); // Top-right corner for texture and quad
3456 rlTexCoord2f(coordA.x, coordA.y); rlVertex2f(vertA.x, vertA.y); // Top-left corner for texture and quad
3460 // -----------------------------------------------------------
3461 // Texture coords Vertices
3462 rlTexCoord2f(coordA.x, coordC.y); rlVertex2f(vertA.x, vertC.y); // Bottom-left corner for texture and quad
3463 rlTexCoord2f(coordD.x, coordC.y); rlVertex2f(vertD.x, vertC.y); // Bottom-right corner for texture and quad
3464 rlTexCoord2f(coordD.x, coordB.y); rlVertex2f(vertD.x, vertB.y); // Top-right corner for texture and quad
3465 rlTexCoord2f(coordA.x, coordB.y); rlVertex2f(vertA.x, vertB.y); // Top-left corner for texture and quad
3468 // -----------------------------------------------------------
3469 // Texture coords Vertices
3470 rlTexCoord2f(coordA.x, coordD.y); rlVertex2f(vertA.x, vertD.y); // Bottom-left corner for texture and quad
3471 rlTexCoord2f(coordD.x, coordD.y); rlVertex2f(vertD.x, vertD.y); // Bottom-right corner for texture and quad
3472 rlTexCoord2f(coordD.x, coordC.y); rlVertex2f(vertD.x, vertC.y); // Top-right corner for texture and quad
3473 rlTexCoord2f(coordA.x, coordC.y); rlVertex2f(vertA.x, vertC.y); // Top-left corner for texture and quad
3475 else if (nPatchInfo.layout == NPATCH_THREE_PATCH_HORIZONTAL)
3478 // -----------------------------------------------------------
3479 // Texture coords Vertices
3480 rlTexCoord2f(coordA.x, coordD.y); rlVertex2f(vertA.x, vertD.y); // Bottom-left corner for texture and quad
3481 rlTexCoord2f(coordB.x, coordD.y); rlVertex2f(vertB.x, vertD.y); // Bottom-right corner for texture and quad
3482 rlTexCoord2f(coordB.x, coordA.y); rlVertex2f(vertB.x, vertA.y); // Top-right corner for texture and quad
3483 rlTexCoord2f(coordA.x, coordA.y); rlVertex2f(vertA.x, vertA.y); // Top-left corner for texture and quad
3487 // -----------------------------------------------------------
3488 // Texture coords Vertices
3489 rlTexCoord2f(coordB.x, coordD.y); rlVertex2f(vertB.x, vertD.y); // Bottom-left corner for texture and quad
3490 rlTexCoord2f(coordC.x, coordD.y); rlVertex2f(vertC.x, vertD.y); // Bottom-right corner for texture and quad
3491 rlTexCoord2f(coordC.x, coordA.y); rlVertex2f(vertC.x, vertA.y); // Top-right corner for texture and quad
3492 rlTexCoord2f(coordB.x, coordA.y); rlVertex2f(vertB.x, vertA.y); // Top-left corner for texture and quad
3495 // -----------------------------------------------------------
3496 // Texture coords Vertices
3497 rlTexCoord2f(coordC.x, coordD.y); rlVertex2f(vertC.x, vertD.y); // Bottom-left corner for texture and quad
3498 rlTexCoord2f(coordD.x, coordD.y); rlVertex2f(vertD.x, vertD.y); // Bottom-right corner for texture and quad
3499 rlTexCoord2f(coordD.x, coordA.y); rlVertex2f(vertD.x, vertA.y); // Top-right corner for texture and quad
3500 rlTexCoord2f(coordC.x, coordA.y); rlVertex2f(vertC.x, vertA.y); // Top-left corner for texture and quad
3509 // Draw textured polygon, defined by vertex and texturecoordinates
3510 // NOTE: Polygon center must have straight line path to all points
3511 // without crossing perimeter, points must be in anticlockwise order
3512 void DrawTexturePoly(Texture2D texture, Vector2 center, Vector2 *points, Vector2 *texcoords, int pointsCount, Color tint)
3514 rlCheckRenderBatchLimit((pointsCount - 1)*4);
3516 rlSetTexture(texture.id);
3518 // Texturing is only supported on QUADs
3521 rlColor4ub(tint.r, tint.g, tint.b, tint.a);
3523 for (int i = 0; i < pointsCount - 1; i++)
3525 rlTexCoord2f(0.5f, 0.5f);
3526 rlVertex2f(center.x, center.y);
3528 rlTexCoord2f(texcoords[i].x, texcoords[i].y);
3529 rlVertex2f(points[i].x + center.x, points[i].y + center.y);
3531 rlTexCoord2f(texcoords[i + 1].x, texcoords[i + 1].y);
3532 rlVertex2f(points[i + 1].x + center.x, points[i + 1].y + center.y);
3534 rlTexCoord2f(texcoords[i + 1].x, texcoords[i + 1].y);
3535 rlVertex2f(points[i + 1].x + center.x, points[i + 1].y + center.y);
3542 // Returns color with alpha applied, alpha goes from 0.0f to 1.0f
3543 Color Fade(Color color, float alpha)
3545 if (alpha < 0.0f) alpha = 0.0f;
3546 else if (alpha > 1.0f) alpha = 1.0f;
3548 return (Color){color.r, color.g, color.b, (unsigned char)(255.0f*alpha)};
3551 // Returns hexadecimal value for a Color
3552 int ColorToInt(Color color)
3554 return (((int)color.r << 24) | ((int)color.g << 16) | ((int)color.b << 8) | (int)color.a);
3557 // Returns color normalized as float [0..1]
3558 Vector4 ColorNormalize(Color color)
3562 result.x = (float)color.r/255.0f;
3563 result.y = (float)color.g/255.0f;
3564 result.z = (float)color.b/255.0f;
3565 result.w = (float)color.a/255.0f;
3570 // Returns color from normalized values [0..1]
3571 Color ColorFromNormalized(Vector4 normalized)
3575 result.r = (unsigned char)(normalized.x*255.0f);
3576 result.g = (unsigned char)(normalized.y*255.0f);
3577 result.b = (unsigned char)(normalized.z*255.0f);
3578 result.a = (unsigned char)(normalized.w*255.0f);
3583 // Returns HSV values for a Color
3584 // NOTE: Hue is returned as degrees [0..360]
3585 Vector3 ColorToHSV(Color color)
3587 Vector3 hsv = { 0 };
3588 Vector3 rgb = { (float)color.r/255.0f, (float)color.g/255.0f, (float)color.b/255.0f };
3589 float min, max, delta;
3591 min = rgb.x < rgb.y? rgb.x : rgb.y;
3592 min = min < rgb.z? min : rgb.z;
3594 max = rgb.x > rgb.y? rgb.x : rgb.y;
3595 max = max > rgb.z? max : rgb.z;
3597 hsv.z = max; // Value
3600 if (delta < 0.00001f)
3603 hsv.x = 0.0f; // Undefined, maybe NAN?
3609 // NOTE: If max is 0, this divide would cause a crash
3610 hsv.y = (delta/max); // Saturation
3614 // NOTE: If max is 0, then r = g = b = 0, s = 0, h is undefined
3616 hsv.x = NAN; // Undefined
3620 // NOTE: Comparing float values could not work properly
3621 if (rgb.x >= max) hsv.x = (rgb.y - rgb.z)/delta; // Between yellow & magenta
3624 if (rgb.y >= max) hsv.x = 2.0f + (rgb.z - rgb.x)/delta; // Between cyan & yellow
3625 else hsv.x = 4.0f + (rgb.x - rgb.y)/delta; // Between magenta & cyan
3628 hsv.x *= 60.0f; // Convert to degrees
3630 if (hsv.x < 0.0f) hsv.x += 360.0f;
3635 // Returns a Color from HSV values
3636 // Implementation reference: https://en.wikipedia.org/wiki/HSL_and_HSV#Alternative_HSV_conversion
3637 // NOTE: Color->HSV->Color conversion will not yield exactly the same color due to rounding errors
3638 // Hue is provided in degrees: [0..360]
3639 // Saturation/Value are provided normalized: [0.0f..1.0f]
3640 Color ColorFromHSV(float hue, float saturation, float value)
3642 Color color = { 0, 0, 0, 255 };
3645 float k = fmodf((5.0f + hue/60.0f), 6);
3650 color.r = (unsigned char)((value - value*saturation*k)*255.0f);
3653 k = fmodf((3.0f + hue/60.0f), 6);
3658 color.g = (unsigned char)((value - value*saturation*k)*255.0f);
3661 k = fmodf((1.0f + hue/60.0f), 6);
3666 color.b = (unsigned char)((value - value*saturation*k)*255.0f);
3671 // Returns color with alpha applied, alpha goes from 0.0f to 1.0f
3672 Color ColorAlpha(Color color, float alpha)
3674 if (alpha < 0.0f) alpha = 0.0f;
3675 else if (alpha > 1.0f) alpha = 1.0f;
3677 return (Color){color.r, color.g, color.b, (unsigned char)(255.0f*alpha)};
3680 // Returns src alpha-blended into dst color with tint
3681 Color ColorAlphaBlend(Color dst, Color src, Color tint)
3685 // Apply color tint to source color
3686 src.r = (unsigned char)(((unsigned int)src.r*(unsigned int)tint.r) >> 8);
3687 src.g = (unsigned char)(((unsigned int)src.g*(unsigned int)tint.g) >> 8);
3688 src.b = (unsigned char)(((unsigned int)src.b*(unsigned int)tint.b) >> 8);
3689 src.a = (unsigned char)(((unsigned int)src.a*(unsigned int)tint.a) >> 8);
3691 //#define COLORALPHABLEND_FLOAT
3692 #define COLORALPHABLEND_INTEGERS
3693 #if defined(COLORALPHABLEND_INTEGERS)
3694 if (src.a == 0) out = dst;
3695 else if (src.a == 255) out = src;
3698 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
3699 out.a = (unsigned char)(((unsigned int)alpha*256 + (unsigned int)dst.a*(256 - alpha)) >> 8);
3703 out.r = (unsigned char)((((unsigned int)src.r*alpha*256 + (unsigned int)dst.r*(unsigned int)dst.a*(256 - alpha))/out.a) >> 8);
3704 out.g = (unsigned char)((((unsigned int)src.g*alpha*256 + (unsigned int)dst.g*(unsigned int)dst.a*(256 - alpha))/out.a) >> 8);
3705 out.b = (unsigned char)((((unsigned int)src.b*alpha*256 + (unsigned int)dst.b*(unsigned int)dst.a*(256 - alpha))/out.a) >> 8);
3709 #if defined(COLORALPHABLEND_FLOAT)
3710 if (src.a == 0) out = dst;
3711 else if (src.a == 255) out = src;
3714 Vector4 fdst = ColorNormalize(dst);
3715 Vector4 fsrc = ColorNormalize(src);
3716 Vector4 ftint = ColorNormalize(tint);
3717 Vector4 fout = { 0 };
3719 fout.w = fsrc.w + fdst.w*(1.0f - fsrc.w);
3723 fout.x = (fsrc.x*fsrc.w + fdst.x*fdst.w*(1 - fsrc.w))/fout.w;
3724 fout.y = (fsrc.y*fsrc.w + fdst.y*fdst.w*(1 - fsrc.w))/fout.w;
3725 fout.z = (fsrc.z*fsrc.w + fdst.z*fdst.w*(1 - fsrc.w))/fout.w;
3728 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) };
3735 // Returns a Color struct from hexadecimal value
3736 Color GetColor(int hexValue)
3740 color.r = (unsigned char)(hexValue >> 24) & 0xFF;
3741 color.g = (unsigned char)(hexValue >> 16) & 0xFF;
3742 color.b = (unsigned char)(hexValue >> 8) & 0xFF;
3743 color.a = (unsigned char)hexValue & 0xFF;
3748 // Get color from a pixel from certain format
3749 Color GetPixelColor(void *srcPtr, int format)
3755 case PIXELFORMAT_UNCOMPRESSED_GRAYSCALE: col = (Color){ ((unsigned char *)srcPtr)[0], ((unsigned char *)srcPtr)[0], ((unsigned char *)srcPtr)[0], 255 }; break;
3756 case PIXELFORMAT_UNCOMPRESSED_GRAY_ALPHA: col = (Color){ ((unsigned char *)srcPtr)[0], ((unsigned char *)srcPtr)[0], ((unsigned char *)srcPtr)[0], ((unsigned char *)srcPtr)[1] }; break;
3757 case PIXELFORMAT_UNCOMPRESSED_R5G6B5:
3759 col.r = (unsigned char)((((unsigned short *)srcPtr)[0] >> 11)*255/31);
3760 col.g = (unsigned char)(((((unsigned short *)srcPtr)[0] >> 5) & 0b0000000000111111)*255/63);
3761 col.b = (unsigned char)((((unsigned short *)srcPtr)[0] & 0b0000000000011111)*255/31);
3765 case PIXELFORMAT_UNCOMPRESSED_R5G5B5A1:
3767 col.r = (unsigned char)((((unsigned short *)srcPtr)[0] >> 11)*255/31);
3768 col.g = (unsigned char)(((((unsigned short *)srcPtr)[0] >> 6) & 0b0000000000011111)*255/31);
3769 col.b = (unsigned char)((((unsigned short *)srcPtr)[0] & 0b0000000000011111)*255/31);
3770 col.a = (((unsigned short *)srcPtr)[0] & 0b0000000000000001)? 255 : 0;
3773 case PIXELFORMAT_UNCOMPRESSED_R4G4B4A4:
3775 col.r = (unsigned char)((((unsigned short *)srcPtr)[0] >> 12)*255/15);
3776 col.g = (unsigned char)(((((unsigned short *)srcPtr)[0] >> 8) & 0b0000000000001111)*255/15);
3777 col.b = (unsigned char)(((((unsigned short *)srcPtr)[0] >> 4) & 0b0000000000001111)*255/15);
3778 col.a = (unsigned char)((((unsigned short *)srcPtr)[0] & 0b0000000000001111)*255/15);
3781 case PIXELFORMAT_UNCOMPRESSED_R8G8B8A8: col = (Color){ ((unsigned char *)srcPtr)[0], ((unsigned char *)srcPtr)[1], ((unsigned char *)srcPtr)[2], ((unsigned char *)srcPtr)[3] }; break;
3782 case PIXELFORMAT_UNCOMPRESSED_R8G8B8: col = (Color){ ((unsigned char *)srcPtr)[0], ((unsigned char *)srcPtr)[1], ((unsigned char *)srcPtr)[2], 255 }; break;
3783 // TODO: case PIXELFORMAT_UNCOMPRESSED_R32: break;
3784 // TODO: case PIXELFORMAT_UNCOMPRESSED_R32G32B32: break;
3785 // TODO: case PIXELFORMAT_UNCOMPRESSED_R32G32B32A32: break;
3792 // Set pixel color formatted into destination pointer
3793 void SetPixelColor(void *dstPtr, Color color, int format)
3797 case PIXELFORMAT_UNCOMPRESSED_GRAYSCALE:
3799 // NOTE: Calculate grayscale equivalent color
3800 Vector3 coln = { (float)color.r/255.0f, (float)color.g/255.0f, (float)color.b/255.0f };
3801 unsigned char gray = (unsigned char)((coln.x*0.299f + coln.y*0.587f + coln.z*0.114f)*255.0f);
3803 ((unsigned char *)dstPtr)[0] = gray;
3806 case PIXELFORMAT_UNCOMPRESSED_GRAY_ALPHA:
3808 // NOTE: Calculate grayscale equivalent color
3809 Vector3 coln = { (float)color.r/255.0f, (float)color.g/255.0f, (float)color.b/255.0f };
3810 unsigned char gray = (unsigned char)((coln.x*0.299f + coln.y*0.587f + coln.z*0.114f)*255.0f);
3812 ((unsigned char *)dstPtr)[0] = gray;
3813 ((unsigned char *)dstPtr)[1] = color.a;
3816 case PIXELFORMAT_UNCOMPRESSED_R5G6B5:
3818 // NOTE: Calculate R5G6B5 equivalent color
3819 Vector3 coln = { (float)color.r/255.0f, (float)color.g/255.0f, (float)color.b/255.0f };
3821 unsigned char r = (unsigned char)(round(coln.x*31.0f));
3822 unsigned char g = (unsigned char)(round(coln.y*63.0f));
3823 unsigned char b = (unsigned char)(round(coln.z*31.0f));
3825 ((unsigned short *)dstPtr)[0] = (unsigned short)r << 11 | (unsigned short)g << 5 | (unsigned short)b;
3828 case PIXELFORMAT_UNCOMPRESSED_R5G5B5A1:
3830 // NOTE: Calculate R5G5B5A1 equivalent color
3831 Vector4 coln = { (float)color.r/255.0f, (float)color.g/255.0f, (float)color.b/255.0f, (float)color.a/255.0f };
3833 unsigned char r = (unsigned char)(round(coln.x*31.0f));
3834 unsigned char g = (unsigned char)(round(coln.y*31.0f));
3835 unsigned char b = (unsigned char)(round(coln.z*31.0f));
3836 unsigned char a = (coln.w > ((float)PIXELFORMAT_UNCOMPRESSED_R5G5B5A1_ALPHA_THRESHOLD/255.0f))? 1 : 0;;
3838 ((unsigned short *)dstPtr)[0] = (unsigned short)r << 11 | (unsigned short)g << 6 | (unsigned short)b << 1 | (unsigned short)a;
3841 case PIXELFORMAT_UNCOMPRESSED_R4G4B4A4:
3843 // NOTE: Calculate R5G5B5A1 equivalent color
3844 Vector4 coln = { (float)color.r/255.0f, (float)color.g/255.0f, (float)color.b/255.0f, (float)color.a/255.0f };
3846 unsigned char r = (unsigned char)(round(coln.x*15.0f));
3847 unsigned char g = (unsigned char)(round(coln.y*15.0f));
3848 unsigned char b = (unsigned char)(round(coln.z*15.0f));
3849 unsigned char a = (unsigned char)(round(coln.w*15.0f));
3851 ((unsigned short *)dstPtr)[0] = (unsigned short)r << 12 | (unsigned short)g << 8 | (unsigned short)b << 4 | (unsigned short)a;
3854 case PIXELFORMAT_UNCOMPRESSED_R8G8B8:
3856 ((unsigned char *)dstPtr)[0] = color.r;
3857 ((unsigned char *)dstPtr)[1] = color.g;
3858 ((unsigned char *)dstPtr)[2] = color.b;
3861 case PIXELFORMAT_UNCOMPRESSED_R8G8B8A8:
3863 ((unsigned char *)dstPtr)[0] = color.r;
3864 ((unsigned char *)dstPtr)[1] = color.g;
3865 ((unsigned char *)dstPtr)[2] = color.b;
3866 ((unsigned char *)dstPtr)[3] = color.a;
3873 // Get pixel data size in bytes for certain format
3874 // NOTE: Size can be requested for Image or Texture data
3875 int GetPixelDataSize(int width, int height, int format)
3877 int dataSize = 0; // Size in bytes
3878 int bpp = 0; // Bits per pixel
3882 case PIXELFORMAT_UNCOMPRESSED_GRAYSCALE: bpp = 8; break;
3883 case PIXELFORMAT_UNCOMPRESSED_GRAY_ALPHA:
3884 case PIXELFORMAT_UNCOMPRESSED_R5G6B5:
3885 case PIXELFORMAT_UNCOMPRESSED_R5G5B5A1:
3886 case PIXELFORMAT_UNCOMPRESSED_R4G4B4A4: bpp = 16; break;
3887 case PIXELFORMAT_UNCOMPRESSED_R8G8B8A8: bpp = 32; break;
3888 case PIXELFORMAT_UNCOMPRESSED_R8G8B8: bpp = 24; break;
3889 case PIXELFORMAT_UNCOMPRESSED_R32: bpp = 32; break;
3890 case PIXELFORMAT_UNCOMPRESSED_R32G32B32: bpp = 32*3; break;
3891 case PIXELFORMAT_UNCOMPRESSED_R32G32B32A32: bpp = 32*4; break;
3892 case PIXELFORMAT_COMPRESSED_DXT1_RGB:
3893 case PIXELFORMAT_COMPRESSED_DXT1_RGBA:
3894 case PIXELFORMAT_COMPRESSED_ETC1_RGB:
3895 case PIXELFORMAT_COMPRESSED_ETC2_RGB:
3896 case PIXELFORMAT_COMPRESSED_PVRT_RGB:
3897 case PIXELFORMAT_COMPRESSED_PVRT_RGBA: bpp = 4; break;
3898 case PIXELFORMAT_COMPRESSED_DXT3_RGBA:
3899 case PIXELFORMAT_COMPRESSED_DXT5_RGBA:
3900 case PIXELFORMAT_COMPRESSED_ETC2_EAC_RGBA:
3901 case PIXELFORMAT_COMPRESSED_ASTC_4x4_RGBA: bpp = 8; break;
3902 case PIXELFORMAT_COMPRESSED_ASTC_8x8_RGBA: bpp = 2; break;
3906 dataSize = width*height*bpp/8; // Total data size in bytes
3908 // Most compressed formats works on 4x4 blocks,
3909 // if texture is smaller, minimum dataSize is 8 or 16
3910 if ((width < 4) && (height < 4))
3912 if ((format >= PIXELFORMAT_COMPRESSED_DXT1_RGB) && (format < PIXELFORMAT_COMPRESSED_DXT3_RGBA)) dataSize = 8;
3913 else if ((format >= PIXELFORMAT_COMPRESSED_DXT3_RGBA) && (format < PIXELFORMAT_COMPRESSED_ASTC_8x8_RGBA)) dataSize = 16;
3919 //----------------------------------------------------------------------------------
3920 // Module specific Functions Definition
3921 //----------------------------------------------------------------------------------
3922 #if defined(SUPPORT_FILEFORMAT_DDS)
3923 // Loading DDS image data (compressed or uncompressed)
3924 static Image LoadDDS(const unsigned char *fileData, unsigned int fileSize)
3926 unsigned char *fileDataPtr = (unsigned char *)fileData;
3928 // Required extension:
3929 // GL_EXT_texture_compression_s3tc
3931 // Supported tokens (defined by extensions)
3932 // GL_COMPRESSED_RGB_S3TC_DXT1_EXT 0x83F0
3933 // GL_COMPRESSED_RGBA_S3TC_DXT1_EXT 0x83F1
3934 // GL_COMPRESSED_RGBA_S3TC_DXT3_EXT 0x83F2
3935 // GL_COMPRESSED_RGBA_S3TC_DXT5_EXT 0x83F3
3937 #define FOURCC_DXT1 0x31545844 // Equivalent to "DXT1" in ASCII
3938 #define FOURCC_DXT3 0x33545844 // Equivalent to "DXT3" in ASCII
3939 #define FOURCC_DXT5 0x35545844 // Equivalent to "DXT5" in ASCII
3945 unsigned int fourCC;
3946 unsigned int rgbBitCount;
3947 unsigned int rBitMask;
3948 unsigned int gBitMask;
3949 unsigned int bBitMask;
3950 unsigned int aBitMask;
3953 // DDS Header (124 bytes)
3957 unsigned int height;
3959 unsigned int pitchOrLinearSize;
3961 unsigned int mipmapCount;
3962 unsigned int reserved1[11];
3963 DDSPixelFormat ddspf;
3968 unsigned int reserved2;
3971 Image image = { 0 };
3973 if (fileDataPtr != NULL)
3975 // Verify the type of file
3976 unsigned char *ddsHeaderId = fileDataPtr;
3979 if ((ddsHeaderId[0] != 'D') || (ddsHeaderId[1] != 'D') || (ddsHeaderId[2] != 'S') || (ddsHeaderId[3] != ' '))
3981 TRACELOG(LOG_WARNING, "IMAGE: DDS file data not valid");
3985 DDSHeader *ddsHeader = (DDSHeader *)fileDataPtr;
3987 TRACELOGD("IMAGE: DDS file data info:");
3988 TRACELOGD(" > Header size: %i", sizeof(DDSHeader));
3989 TRACELOGD(" > Pixel format size: %i", ddsHeader->ddspf.size);
3990 TRACELOGD(" > Pixel format flags: 0x%x", ddsHeader->ddspf.flags);
3991 TRACELOGD(" > File format: 0x%x", ddsHeader->ddspf.fourCC);
3992 TRACELOGD(" > File bit count: 0x%x", ddsHeader->ddspf.rgbBitCount);
3994 fileDataPtr += sizeof(DDSHeader); // Skip header
3996 image.width = ddsHeader->width;
3997 image.height = ddsHeader->height;
3999 if (ddsHeader->mipmapCount == 0) image.mipmaps = 1; // Parameter not used
4000 else image.mipmaps = ddsHeader->mipmapCount;
4002 if (ddsHeader->ddspf.rgbBitCount == 16) // 16bit mode, no compressed
4004 if (ddsHeader->ddspf.flags == 0x40) // no alpha channel
4006 int dataSize = image.width*image.height*sizeof(unsigned short);
4007 image.data = (unsigned short *)RL_MALLOC(dataSize);
4009 memcpy(image.data, fileDataPtr, dataSize);
4011 image.format = PIXELFORMAT_UNCOMPRESSED_R5G6B5;
4013 else if (ddsHeader->ddspf.flags == 0x41) // with alpha channel
4015 if (ddsHeader->ddspf.aBitMask == 0x8000) // 1bit alpha
4017 int dataSize = image.width*image.height*sizeof(unsigned short);
4018 image.data = (unsigned short *)RL_MALLOC(dataSize);
4020 memcpy(image.data, fileDataPtr, dataSize);
4022 unsigned char alpha = 0;
4024 // NOTE: Data comes as A1R5G5B5, it must be reordered to R5G5B5A1
4025 for (int i = 0; i < image.width*image.height; i++)
4027 alpha = ((unsigned short *)image.data)[i] >> 15;
4028 ((unsigned short *)image.data)[i] = ((unsigned short *)image.data)[i] << 1;
4029 ((unsigned short *)image.data)[i] += alpha;
4032 image.format = PIXELFORMAT_UNCOMPRESSED_R5G5B5A1;
4034 else if (ddsHeader->ddspf.aBitMask == 0xf000) // 4bit alpha
4036 int dataSize = image.width*image.height*sizeof(unsigned short);
4037 image.data = (unsigned short *)RL_MALLOC(dataSize);
4039 memcpy(image.data, fileDataPtr, dataSize);
4041 unsigned char alpha = 0;
4043 // NOTE: Data comes as A4R4G4B4, it must be reordered R4G4B4A4
4044 for (int i = 0; i < image.width*image.height; i++)
4046 alpha = ((unsigned short *)image.data)[i] >> 12;
4047 ((unsigned short *)image.data)[i] = ((unsigned short *)image.data)[i] << 4;
4048 ((unsigned short *)image.data)[i] += alpha;
4051 image.format = PIXELFORMAT_UNCOMPRESSED_R4G4B4A4;
4055 else if (ddsHeader->ddspf.flags == 0x40 && ddsHeader->ddspf.rgbBitCount == 24) // DDS_RGB, no compressed
4057 int dataSize = image.width*image.height*3*sizeof(unsigned char);
4058 image.data = (unsigned short *)RL_MALLOC(dataSize);
4060 memcpy(image.data, fileDataPtr, dataSize);
4062 image.format = PIXELFORMAT_UNCOMPRESSED_R8G8B8;
4064 else if (ddsHeader->ddspf.flags == 0x41 && ddsHeader->ddspf.rgbBitCount == 32) // DDS_RGBA, no compressed
4066 int dataSize = image.width*image.height*4*sizeof(unsigned char);
4067 image.data = (unsigned short *)RL_MALLOC(dataSize);
4069 memcpy(image.data, fileDataPtr, dataSize);
4071 unsigned char blue = 0;
4073 // NOTE: Data comes as A8R8G8B8, it must be reordered R8G8B8A8 (view next comment)
4074 // DirecX understand ARGB as a 32bit DWORD but the actual memory byte alignment is BGRA
4075 // So, we must realign B8G8R8A8 to R8G8B8A8
4076 for (int i = 0; i < image.width*image.height*4; i += 4)
4078 blue = ((unsigned char *)image.data)[i];
4079 ((unsigned char *)image.data)[i] = ((unsigned char *)image.data)[i + 2];
4080 ((unsigned char *)image.data)[i + 2] = blue;
4083 image.format = PIXELFORMAT_UNCOMPRESSED_R8G8B8A8;
4085 else if (((ddsHeader->ddspf.flags == 0x04) || (ddsHeader->ddspf.flags == 0x05)) && (ddsHeader->ddspf.fourCC > 0)) // Compressed
4089 // Calculate data size, including all mipmaps
4090 if (ddsHeader->mipmapCount > 1) dataSize = ddsHeader->pitchOrLinearSize*2;
4091 else dataSize = ddsHeader->pitchOrLinearSize;
4093 image.data = (unsigned char *)RL_MALLOC(dataSize*sizeof(unsigned char));
4095 memcpy(image.data, fileDataPtr, dataSize);
4097 switch (ddsHeader->ddspf.fourCC)
4101 if (ddsHeader->ddspf.flags == 0x04) image.format = PIXELFORMAT_COMPRESSED_DXT1_RGB;
4102 else image.format = PIXELFORMAT_COMPRESSED_DXT1_RGBA;
4104 case FOURCC_DXT3: image.format = PIXELFORMAT_COMPRESSED_DXT3_RGBA; break;
4105 case FOURCC_DXT5: image.format = PIXELFORMAT_COMPRESSED_DXT5_RGBA; break;
4116 #if defined(SUPPORT_FILEFORMAT_PKM)
4117 // Loading PKM image data (ETC1/ETC2 compression)
4118 // NOTE: KTX is the standard Khronos Group compression format (ETC1/ETC2, mipmaps)
4119 // PKM is a much simpler file format used mainly to contain a single ETC1/ETC2 compressed image (no mipmaps)
4120 static Image LoadPKM(const unsigned char *fileData, unsigned int fileSize)
4122 unsigned char *fileDataPtr = (unsigned char *)fileData;
4124 // Required extensions:
4125 // GL_OES_compressed_ETC1_RGB8_texture (ETC1) (OpenGL ES 2.0)
4126 // GL_ARB_ES3_compatibility (ETC2/EAC) (OpenGL ES 3.0)
4128 // Supported tokens (defined by extensions)
4129 // GL_ETC1_RGB8_OES 0x8D64
4130 // GL_COMPRESSED_RGB8_ETC2 0x9274
4131 // GL_COMPRESSED_RGBA8_ETC2_EAC 0x9278
4133 // PKM file (ETC1) Header (16 bytes)
4135 char id[4]; // "PKM "
4136 char version[2]; // "10" or "20"
4137 unsigned short format; // Data format (big-endian) (Check list below)
4138 unsigned short width; // Texture width (big-endian) (origWidth rounded to multiple of 4)
4139 unsigned short height; // Texture height (big-endian) (origHeight rounded to multiple of 4)
4140 unsigned short origWidth; // Original width (big-endian)
4141 unsigned short origHeight; // Original height (big-endian)
4145 // version 10: format: 0=ETC1_RGB, [1=ETC1_RGBA, 2=ETC1_RGB_MIP, 3=ETC1_RGBA_MIP] (not used)
4146 // 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
4148 // NOTE: The extended width and height are the widths rounded up to a multiple of 4.
4149 // NOTE: ETC is always 4bit per pixel (64 bit for each 4x4 block of pixels)
4151 Image image = { 0 };
4153 if (fileDataPtr != NULL)
4155 PKMHeader *pkmHeader = (PKMHeader *)fileDataPtr;
4157 if ((pkmHeader->id[0] != 'P') || (pkmHeader->id[1] != 'K') || (pkmHeader->id[2] != 'M') || (pkmHeader->id[3] != ' '))
4159 TRACELOG(LOG_WARNING, "IMAGE: PKM file data not valid");
4163 fileDataPtr += sizeof(PKMHeader); // Skip header
4165 // NOTE: format, width and height come as big-endian, data must be swapped to little-endian
4166 pkmHeader->format = ((pkmHeader->format & 0x00FF) << 8) | ((pkmHeader->format & 0xFF00) >> 8);
4167 pkmHeader->width = ((pkmHeader->width & 0x00FF) << 8) | ((pkmHeader->width & 0xFF00) >> 8);
4168 pkmHeader->height = ((pkmHeader->height & 0x00FF) << 8) | ((pkmHeader->height & 0xFF00) >> 8);
4170 TRACELOGD("IMAGE: PKM file data info:");
4171 TRACELOGD(" > Image width: %i", pkmHeader->width);
4172 TRACELOGD(" > Image height: %i", pkmHeader->height);
4173 TRACELOGD(" > Image format: %i", pkmHeader->format);
4175 image.width = pkmHeader->width;
4176 image.height = pkmHeader->height;
4180 if (pkmHeader->format == 3) bpp = 8;
4182 int dataSize = image.width*image.height*bpp/8; // Total data size in bytes
4184 image.data = (unsigned char *)RL_MALLOC(dataSize*sizeof(unsigned char));
4186 memcpy(image.data, fileDataPtr, dataSize);
4188 if (pkmHeader->format == 0) image.format = PIXELFORMAT_COMPRESSED_ETC1_RGB;
4189 else if (pkmHeader->format == 1) image.format = PIXELFORMAT_COMPRESSED_ETC2_RGB;
4190 else if (pkmHeader->format == 3) image.format = PIXELFORMAT_COMPRESSED_ETC2_EAC_RGBA;
4198 #if defined(SUPPORT_FILEFORMAT_KTX)
4199 // Load KTX compressed image data (ETC1/ETC2 compression)
4200 static Image LoadKTX(const unsigned char *fileData, unsigned int fileSize)
4202 unsigned char *fileDataPtr = (unsigned char *)fileData;
4204 // Required extensions:
4205 // GL_OES_compressed_ETC1_RGB8_texture (ETC1)
4206 // GL_ARB_ES3_compatibility (ETC2/EAC)
4208 // Supported tokens (defined by extensions)
4209 // GL_ETC1_RGB8_OES 0x8D64
4210 // GL_COMPRESSED_RGB8_ETC2 0x9274
4211 // GL_COMPRESSED_RGBA8_ETC2_EAC 0x9278
4213 // KTX file Header (64 bytes)
4214 // v1.1 - https://www.khronos.org/opengles/sdk/tools/KTX/file_format_spec/
4215 // v2.0 - http://github.khronos.org/KTX-Specification/
4217 // TODO: Support KTX 2.2 specs!
4220 char id[12]; // Identifier: "«KTX 11»\r\n\x1A\n"
4221 unsigned int endianness; // Little endian: 0x01 0x02 0x03 0x04
4222 unsigned int glType; // For compressed textures, glType must equal 0
4223 unsigned int glTypeSize; // For compressed texture data, usually 1
4224 unsigned int glFormat; // For compressed textures is 0
4225 unsigned int glInternalFormat; // Compressed internal format
4226 unsigned int glBaseInternalFormat; // Same as glFormat (RGB, RGBA, ALPHA...)
4227 unsigned int width; // Texture image width in pixels
4228 unsigned int height; // Texture image height in pixels
4229 unsigned int depth; // For 2D textures is 0
4230 unsigned int elements; // Number of array elements, usually 0
4231 unsigned int faces; // Cubemap faces, for no-cubemap = 1
4232 unsigned int mipmapLevels; // Non-mipmapped textures = 1
4233 unsigned int keyValueDataSize; // Used to encode any arbitrary data...
4236 // NOTE: Before start of every mipmap data block, we have: unsigned int dataSize
4238 Image image = { 0 };
4240 if (fileDataPtr != NULL)
4242 KTXHeader *ktxHeader = (KTXHeader *)fileDataPtr;
4244 if ((ktxHeader->id[1] != 'K') || (ktxHeader->id[2] != 'T') || (ktxHeader->id[3] != 'X') ||
4245 (ktxHeader->id[4] != ' ') || (ktxHeader->id[5] != '1') || (ktxHeader->id[6] != '1'))
4247 TRACELOG(LOG_WARNING, "IMAGE: KTX file data not valid");
4251 fileDataPtr += sizeof(KTXHeader); // Move file data pointer
4253 image.width = ktxHeader->width;
4254 image.height = ktxHeader->height;
4255 image.mipmaps = ktxHeader->mipmapLevels;
4257 TRACELOGD("IMAGE: KTX file data info:");
4258 TRACELOGD(" > Image width: %i", ktxHeader->width);
4259 TRACELOGD(" > Image height: %i", ktxHeader->height);
4260 TRACELOGD(" > Image format: 0x%x", ktxHeader->glInternalFormat);
4262 fileDataPtr += ktxHeader->keyValueDataSize; // Skip value data size
4264 int dataSize = ((int *)fileDataPtr)[0];
4265 fileDataPtr += sizeof(int);
4267 image.data = (unsigned char *)RL_MALLOC(dataSize*sizeof(unsigned char));
4269 memcpy(image.data, fileDataPtr, dataSize);
4271 if (ktxHeader->glInternalFormat == 0x8D64) image.format = PIXELFORMAT_COMPRESSED_ETC1_RGB;
4272 else if (ktxHeader->glInternalFormat == 0x9274) image.format = PIXELFORMAT_COMPRESSED_ETC2_RGB;
4273 else if (ktxHeader->glInternalFormat == 0x9278) image.format = PIXELFORMAT_COMPRESSED_ETC2_EAC_RGBA;
4280 // Save image data as KTX file
4281 // NOTE: By default KTX 1.1 spec is used, 2.0 is still on draft (01Oct2018)
4282 static int SaveKTX(Image image, const char *fileName)
4284 // KTX file Header (64 bytes)
4285 // v1.1 - https://www.khronos.org/opengles/sdk/tools/KTX/file_format_spec/
4286 // v2.0 - http://github.khronos.org/KTX-Specification/ - still on draft, not ready for implementation
4288 char id[12]; // Identifier: "«KTX 11»\r\n\x1A\n" // KTX 2.0: "«KTX 22»\r\n\x1A\n"
4289 unsigned int endianness; // Little endian: 0x01 0x02 0x03 0x04
4290 unsigned int glType; // For compressed textures, glType must equal 0
4291 unsigned int glTypeSize; // For compressed texture data, usually 1
4292 unsigned int glFormat; // For compressed textures is 0
4293 unsigned int glInternalFormat; // Compressed internal format
4294 unsigned int glBaseInternalFormat; // Same as glFormat (RGB, RGBA, ALPHA...) // KTX 2.0: UInt32 vkFormat
4295 unsigned int width; // Texture image width in pixels
4296 unsigned int height; // Texture image height in pixels
4297 unsigned int depth; // For 2D textures is 0
4298 unsigned int elements; // Number of array elements, usually 0
4299 unsigned int faces; // Cubemap faces, for no-cubemap = 1
4300 unsigned int mipmapLevels; // Non-mipmapped textures = 1
4301 unsigned int keyValueDataSize; // Used to encode any arbitrary data... // KTX 2.0: UInt32 levelOrder - ordering of the mipmap levels, usually 0
4302 // KTX 2.0: UInt32 supercompressionScheme - 0 (None), 1 (Crunch CRN), 2 (Zlib DEFLATE)...
4303 // KTX 2.0 defines additional header elements...
4306 // Calculate file dataSize required
4307 int dataSize = sizeof(KTXHeader);
4309 for (int i = 0, width = image.width, height = image.height; i < image.mipmaps; i++)
4311 dataSize += GetPixelDataSize(width, height, image.format);
4312 width /= 2; height /= 2;
4315 unsigned char *fileData = RL_CALLOC(dataSize, 1);
4316 unsigned char *fileDataPtr = fileData;
4318 KTXHeader ktxHeader = { 0 };
4320 // KTX identifier (v1.1)
4321 //unsigned char id[12] = { '«', 'K', 'T', 'X', ' ', '1', '1', '»', '\r', '\n', '\x1A', '\n' };
4322 //unsigned char id[12] = { 0xAB, 0x4B, 0x54, 0x58, 0x20, 0x31, 0x31, 0xBB, 0x0D, 0x0A, 0x1A, 0x0A };
4324 const char ktxIdentifier[12] = { 0xAB, 'K', 'T', 'X', ' ', '1', '1', 0xBB, '\r', '\n', 0x1A, '\n' };
4326 // Get the image header
4327 memcpy(ktxHeader.id, ktxIdentifier, 12); // KTX 1.1 signature
4328 ktxHeader.endianness = 0;
4329 ktxHeader.glType = 0; // Obtained from image.format
4330 ktxHeader.glTypeSize = 1;
4331 ktxHeader.glFormat = 0; // Obtained from image.format
4332 ktxHeader.glInternalFormat = 0; // Obtained from image.format
4333 ktxHeader.glBaseInternalFormat = 0;
4334 ktxHeader.width = image.width;
4335 ktxHeader.height = image.height;
4336 ktxHeader.depth = 0;
4337 ktxHeader.elements = 0;
4338 ktxHeader.faces = 1;
4339 ktxHeader.mipmapLevels = image.mipmaps; // If it was 0, it means mipmaps should be generated on loading (not for compressed formats)
4340 ktxHeader.keyValueDataSize = 0; // No extra data after the header
4342 rlGetGlTextureFormats(image.format, &ktxHeader.glInternalFormat, &ktxHeader.glFormat, &ktxHeader.glType); // rlgl module function
4343 ktxHeader.glBaseInternalFormat = ktxHeader.glFormat; // KTX 1.1 only
4345 // NOTE: We can save into a .ktx all PixelFormats supported by raylib, including compressed formats like DXT, ETC or ASTC
4347 if (ktxHeader.glFormat == -1) TRACELOG(LOG_WARNING, "IMAGE: GL format not supported for KTX export (%i)", ktxHeader.glFormat);
4350 memcpy(fileDataPtr, &ktxHeader, sizeof(KTXHeader));
4351 fileDataPtr += sizeof(KTXHeader);
4353 int width = image.width;
4354 int height = image.height;
4357 // Save all mipmaps data
4358 for (int i = 0; i < image.mipmaps; i++)
4360 unsigned int dataSize = GetPixelDataSize(width, height, image.format);
4362 memcpy(fileDataPtr, &dataSize, sizeof(unsigned int));
4363 memcpy(fileDataPtr + 4, (unsigned char *)image.data + dataOffset, dataSize);
4367 dataOffset += dataSize;
4368 fileDataPtr += (4 + dataSize);
4372 int success = SaveFileData(fileName, fileData, dataSize);
4374 RL_FREE(fileData); // Free file data buffer
4376 // If all data has been written correctly to file, success = 1
4381 #if defined(SUPPORT_FILEFORMAT_PVR)
4382 // Loading PVR image data (uncompressed or PVRT compression)
4383 // NOTE: PVR v2 not supported, use PVR v3 instead
4384 static Image LoadPVR(const unsigned char *fileData, unsigned int fileSize)
4386 unsigned char *fileDataPtr = (unsigned char *)fileData;
4388 // Required extension:
4389 // GL_IMG_texture_compression_pvrtc
4391 // Supported tokens (defined by extensions)
4392 // GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG 0x8C00
4393 // GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG 0x8C02
4395 #if 0 // Not used...
4396 // PVR file v2 Header (52 bytes)
4398 unsigned int headerLength;
4399 unsigned int height;
4401 unsigned int numMipmaps;
4403 unsigned int dataLength;
4405 unsigned int bitmaskRed;
4406 unsigned int bitmaskGreen;
4407 unsigned int bitmaskBlue;
4408 unsigned int bitmaskAlpha;
4409 unsigned int pvrTag;
4410 unsigned int numSurfs;
4414 // PVR file v3 Header (52 bytes)
4415 // NOTE: After it could be metadata (15 bytes?)
4419 unsigned char channels[4]; // pixelFormat high part
4420 unsigned char channelDepth[4]; // pixelFormat low part
4421 unsigned int colourSpace;
4422 unsigned int channelType;
4423 unsigned int height;
4426 unsigned int numSurfaces;
4427 unsigned int numFaces;
4428 unsigned int numMipmaps;
4429 unsigned int metaDataSize;
4432 #if 0 // Not used...
4433 // Metadata (usually 15 bytes)
4435 unsigned int devFOURCC;
4437 unsigned int dataSize; // Not used?
4438 unsigned char *data; // Not used?
4442 Image image = { 0 };
4444 if (fileDataPtr != NULL)
4446 // Check PVR image version
4447 unsigned char pvrVersion = fileDataPtr[0];
4449 // Load different PVR data formats
4450 if (pvrVersion == 0x50)
4452 PVRHeaderV3 *pvrHeader = (PVRHeaderV3 *)fileDataPtr;
4454 if ((pvrHeader->id[0] != 'P') || (pvrHeader->id[1] != 'V') || (pvrHeader->id[2] != 'R') || (pvrHeader->id[3] != 3))
4456 TRACELOG(LOG_WARNING, "IMAGE: PVR file data not valid");
4460 fileDataPtr += sizeof(PVRHeaderV3); // Skip header
4462 image.width = pvrHeader->width;
4463 image.height = pvrHeader->height;
4464 image.mipmaps = pvrHeader->numMipmaps;
4466 // Check data format
4467 if (((pvrHeader->channels[0] == 'l') && (pvrHeader->channels[1] == 0)) && (pvrHeader->channelDepth[0] == 8)) image.format = PIXELFORMAT_UNCOMPRESSED_GRAYSCALE;
4468 else if (((pvrHeader->channels[0] == 'l') && (pvrHeader->channels[1] == 'a')) && ((pvrHeader->channelDepth[0] == 8) && (pvrHeader->channelDepth[1] == 8))) image.format = PIXELFORMAT_UNCOMPRESSED_GRAY_ALPHA;
4469 else if ((pvrHeader->channels[0] == 'r') && (pvrHeader->channels[1] == 'g') && (pvrHeader->channels[2] == 'b'))
4471 if (pvrHeader->channels[3] == 'a')
4473 if ((pvrHeader->channelDepth[0] == 5) && (pvrHeader->channelDepth[1] == 5) && (pvrHeader->channelDepth[2] == 5) && (pvrHeader->channelDepth[3] == 1)) image.format = PIXELFORMAT_UNCOMPRESSED_R5G5B5A1;
4474 else if ((pvrHeader->channelDepth[0] == 4) && (pvrHeader->channelDepth[1] == 4) && (pvrHeader->channelDepth[2] == 4) && (pvrHeader->channelDepth[3] == 4)) image.format = PIXELFORMAT_UNCOMPRESSED_R4G4B4A4;
4475 else if ((pvrHeader->channelDepth[0] == 8) && (pvrHeader->channelDepth[1] == 8) && (pvrHeader->channelDepth[2] == 8) && (pvrHeader->channelDepth[3] == 8)) image.format = PIXELFORMAT_UNCOMPRESSED_R8G8B8A8;
4477 else if (pvrHeader->channels[3] == 0)
4479 if ((pvrHeader->channelDepth[0] == 5) && (pvrHeader->channelDepth[1] == 6) && (pvrHeader->channelDepth[2] == 5)) image.format = PIXELFORMAT_UNCOMPRESSED_R5G6B5;
4480 else if ((pvrHeader->channelDepth[0] == 8) && (pvrHeader->channelDepth[1] == 8) && (pvrHeader->channelDepth[2] == 8)) image.format = PIXELFORMAT_UNCOMPRESSED_R8G8B8;
4483 else if (pvrHeader->channels[0] == 2) image.format = PIXELFORMAT_COMPRESSED_PVRT_RGB;
4484 else if (pvrHeader->channels[0] == 3) image.format = PIXELFORMAT_COMPRESSED_PVRT_RGBA;
4486 fileDataPtr += pvrHeader->metaDataSize; // Skip meta data header
4488 // Calculate data size (depends on format)
4490 switch (image.format)
4492 case PIXELFORMAT_UNCOMPRESSED_GRAYSCALE: bpp = 8; break;
4493 case PIXELFORMAT_UNCOMPRESSED_GRAY_ALPHA:
4494 case PIXELFORMAT_UNCOMPRESSED_R5G5B5A1:
4495 case PIXELFORMAT_UNCOMPRESSED_R5G6B5:
4496 case PIXELFORMAT_UNCOMPRESSED_R4G4B4A4: bpp = 16; break;
4497 case PIXELFORMAT_UNCOMPRESSED_R8G8B8A8: bpp = 32; break;
4498 case PIXELFORMAT_UNCOMPRESSED_R8G8B8: bpp = 24; break;
4499 case PIXELFORMAT_COMPRESSED_PVRT_RGB:
4500 case PIXELFORMAT_COMPRESSED_PVRT_RGBA: bpp = 4; break;
4504 int dataSize = image.width*image.height*bpp/8; // Total data size in bytes
4505 image.data = (unsigned char *)RL_MALLOC(dataSize*sizeof(unsigned char));
4507 memcpy(image.data, fileDataPtr, dataSize);
4510 else if (pvrVersion == 52) TRACELOG(LOG_INFO, "IMAGE: PVRv2 format not supported, update your files to PVRv3");
4517 #if defined(SUPPORT_FILEFORMAT_ASTC)
4518 // Load ASTC compressed image data (ASTC compression)
4519 static Image LoadASTC(const unsigned char *fileData, unsigned int fileSize)
4521 unsigned char *fileDataPtr = (unsigned char *)fileData;
4523 // Required extensions:
4524 // GL_KHR_texture_compression_astc_hdr
4525 // GL_KHR_texture_compression_astc_ldr
4527 // Supported tokens (defined by extensions)
4528 // GL_COMPRESSED_RGBA_ASTC_4x4_KHR 0x93b0
4529 // GL_COMPRESSED_RGBA_ASTC_8x8_KHR 0x93b7
4531 // ASTC file Header (16 bytes)
4533 unsigned char id[4]; // Signature: 0x13 0xAB 0xA1 0x5C
4534 unsigned char blockX; // Block X dimensions
4535 unsigned char blockY; // Block Y dimensions
4536 unsigned char blockZ; // Block Z dimensions (1 for 2D images)
4537 unsigned char width[3]; // Image width in pixels (24bit value)
4538 unsigned char height[3]; // Image height in pixels (24bit value)
4539 unsigned char length[3]; // Image Z-size (1 for 2D images)
4542 Image image = { 0 };
4544 if (fileDataPtr != NULL)
4546 ASTCHeader *astcHeader = (ASTCHeader *)fileDataPtr;
4548 if ((astcHeader->id[3] != 0x5c) || (astcHeader->id[2] != 0xa1) || (astcHeader->id[1] != 0xab) || (astcHeader->id[0] != 0x13))
4550 TRACELOG(LOG_WARNING, "IMAGE: ASTC file data not valid");
4554 fileDataPtr += sizeof(ASTCHeader); // Skip header
4556 // NOTE: Assuming Little Endian (could it be wrong?)
4557 image.width = 0x00000000 | ((int)astcHeader->width[2] << 16) | ((int)astcHeader->width[1] << 8) | ((int)astcHeader->width[0]);
4558 image.height = 0x00000000 | ((int)astcHeader->height[2] << 16) | ((int)astcHeader->height[1] << 8) | ((int)astcHeader->height[0]);
4560 TRACELOGD("IMAGE: ASTC file data info:");
4561 TRACELOGD(" > Image width: %i", image.width);
4562 TRACELOGD(" > Image height: %i", image.height);
4563 TRACELOGD(" > Image blocks: %ix%i", astcHeader->blockX, astcHeader->blockY);
4565 image.mipmaps = 1; // NOTE: ASTC format only contains one mipmap level
4567 // NOTE: Each block is always stored in 128bit so we can calculate the bpp
4568 int bpp = 128/(astcHeader->blockX*astcHeader->blockY);
4570 // NOTE: Currently we only support 2 blocks configurations: 4x4 and 8x8
4571 if ((bpp == 8) || (bpp == 2))
4573 int dataSize = image.width*image.height*bpp/8; // Data size in bytes
4575 image.data = (unsigned char *)RL_MALLOC(dataSize*sizeof(unsigned char));
4577 memcpy(image.data, fileDataPtr, dataSize);
4579 if (bpp == 8) image.format = PIXELFORMAT_COMPRESSED_ASTC_4x4_RGBA;
4580 else if (bpp == 2) image.format = PIXELFORMAT_COMPRESSED_ASTC_8x8_RGBA;
4582 else TRACELOG(LOG_WARNING, "IMAGE: ASTC block size configuration not supported");