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