Ver Fonte

Merge remote-tracking branch 'origin/develop' into games-linux

RDR8 há 8 anos atrás
pai
commit
72b90bff0f
5 ficheiros alterados com 228 adições e 148 exclusões
  1. 3 3
      src/core.c
  2. 1 1
      src/raylib.h
  3. 18 3
      src/rlgl.c
  4. 205 140
      src/textures.c
  5. 1 1
      src/utils.c

+ 3 - 3
src/core.c

@@ -70,13 +70,13 @@
 *
 **********************************************************************************************/
 
-// Default supported features
-//-------------------------------------
+// Default configuration flags (supported features)
+//-------------------------------------------------
 #define SUPPORT_DEFAULT_FONT
 #define SUPPORT_MOUSE_GESTURES
 #define SUPPORT_CAMERA_SYSTEM
 #define SUPPORT_GESTURES_SYSTEM
-//-------------------------------------
+//-------------------------------------------------
 
 #include "raylib.h"
 

+ 1 - 1
src/raylib.h

@@ -13,7 +13,7 @@
 *       - Multiple textures support, including compressed formats and mipmaps generation
 *       - Basic 3d support for Shapes, Models, Billboards, Heightmaps and Cubicmaps
 *       - Powerful math module for Vector, Matrix and Quaternion operations: [raymath]
-*       - Audio loading and playing with streaming support and mixing channels [audio]
+*       - Audio loading and playing with streaming support and mixing channels: [audio]
 *       - VR stereo rendering support with configurable HMD device parameters
 *       - Multiple platforms support: Windows, Linux, Mac, Android, Raspberry Pi, HTML5 and Oculus Rift CV1
 *       - Custom color palette for fancy visuals on raywhite background

+ 18 - 3
src/rlgl.c

@@ -25,7 +25,7 @@
 *   #define SUPPORT_VR_SIMULATION / SUPPORT_STEREO_RENDERING
 *       Support VR simulation functionality (stereo rendering)
 *
-*   #define SUPPORT_SHADER_DISTORTION
+*   #define SUPPORT_DISTORTION_SHADER
 *       Include stereo rendering distortion shader (shader_distortion.h)
 *
 *   DEPENDENCIES:
@@ -54,6 +54,11 @@
 *
 **********************************************************************************************/
 
+// Default configuration flags (supported features)
+//-------------------------------------------------
+#define SUPPORT_VR_SIMULATION
+//-------------------------------------------------
+
 #include "rlgl.h"
 
 #include <stdio.h>                  // Required for: fopen(), fclose(), fread()... [Used only on LoadText()]
@@ -100,7 +105,7 @@
     #include <stdarg.h>             // Required for: va_list, va_start(), vfprintf(), va_end() [Used only on TraceLog()]
 #endif
 
-#if !defined(GRAPHICS_API_OPENGL_11) && !defined(RLGL_NO_DISTORTION_SHADER)
+#if !defined(GRAPHICS_API_OPENGL_11) && defined(SUPPORT_DISTORTION_SHADER)
     #include "shader_distortion.h"  // Distortion shader to be embedded
 #endif
 
@@ -2591,10 +2596,12 @@ void InitVrSimulator(int vrDevice)
     // Initialize framebuffer and textures for stereo rendering
     // NOTE: screen size should match HMD aspect ratio
     vrConfig.stereoFbo = rlglLoadRenderTexture(screenWidth, screenHeight);
-
+    
+#if defined(SUPPORT_DISTORTION_SHADER)
     // Load distortion shader (initialized by default with Oculus Rift CV1 parameters)
     vrConfig.distortionShader.id = LoadShaderProgram(vDistortionShaderStr, fDistortionShaderStr);
     if (vrConfig.distortionShader.id != 0) LoadDefaultShaderLocations(&vrConfig.distortionShader);
+#endif
 
     SetStereoConfig(hmd);
 
@@ -2613,7 +2620,9 @@ void CloseVrSimulator(void)
     if (vrSimulatorReady)
     {
         rlDeleteRenderTextures(vrConfig.stereoFbo); // Unload stereo framebuffer and texture
+        #if defined(SUPPORT_DISTORTION_SHADER)
         UnloadShader(vrConfig.distortionShader);    // Unload distortion shader
+        #endif
     }
 #endif
 }
@@ -2700,8 +2709,12 @@ void EndVrDrawing(void)
         rlMatrixMode(RL_MODELVIEW);                             // Enable internal modelview matrix
         rlLoadIdentity();                                       // Reset internal modelview matrix
 
+#if defined(SUPPORT_DISTORTION_SHADER)
         // Draw RenderTexture (stereoFbo) using distortion shader
         currentShader = vrConfig.distortionShader;
+#else
+        currentShader = GetDefaultShader();
+#endif
 
         rlEnableTexture(vrConfig.stereoFbo.texture.id);
 
@@ -3536,6 +3549,7 @@ static void SetStereoConfig(VrDeviceInfo hmd)
     TraceLog(DEBUG, "VR: Distortion Shader: Scale = { %f, %f }", scale[0], scale[1]);
     TraceLog(DEBUG, "VR: Distortion Shader: ScaleIn = { %f, %f }", scaleIn[0], scaleIn[1]);
 
+#if defined(SUPPORT_DISTORTION_SHADER)
     // Update distortion shader with lens and distortion-scale parameters
     SetShaderValue(vrConfig.distortionShader, GetShaderLocation(vrConfig.distortionShader, "leftLensCenter"), leftLensCenter, 2);
     SetShaderValue(vrConfig.distortionShader, GetShaderLocation(vrConfig.distortionShader, "rightLensCenter"), rightLensCenter, 2);
@@ -3546,6 +3560,7 @@ static void SetStereoConfig(VrDeviceInfo hmd)
     SetShaderValue(vrConfig.distortionShader, GetShaderLocation(vrConfig.distortionShader, "scaleIn"), scaleIn, 2);
     SetShaderValue(vrConfig.distortionShader, GetShaderLocation(vrConfig.distortionShader, "hmdWarpParam"), hmd.distortionK, 4);
     SetShaderValue(vrConfig.distortionShader, GetShaderLocation(vrConfig.distortionShader, "chromaAbParam"), hmd.chromaAbCorrection, 4);
+#endif
 
     // Fovy is normally computed with: 2*atan2(hmd.vScreenSize, 2*hmd.eyeToScreenDistance)*RAD2DEG
     // ...but with lens distortion it is increased (see Oculus SDK Documentation)

+ 205 - 140
src/textures.c

@@ -4,24 +4,24 @@
 *
 *   CONFIGURATION:
 *
-*   #define SUPPORT_STB_IMAGE / INCLUDE_STB_IMAGE
-*
-*   #define SUPPORT_FILEFORMAT_BMP / SUPPORT_LOAD_BMP
-*   #define SUPPORT_FILEFORMAT_PNG / SUPPORT_LOAD_PNG
+*   #define SUPPORT_FILEFORMAT_BMP
+*   #define SUPPORT_FILEFORMAT_PNG
 *   #define SUPPORT_FILEFORMAT_TGA
-*   #define SUPPORT_FILEFORMAT_JPG / ENABLE_LOAD_JPG
+*   #define SUPPORT_FILEFORMAT_JPG
 *   #define SUPPORT_FILEFORMAT_GIF
+*   #define SUPPORT_FILEFORMAT_PSD
 *   #define SUPPORT_FILEFORMAT_HDR
-*   #define SUPPORT_FILEFORMAT_DDS / ENABLE_LOAD_DDS
+*   #define SUPPORT_FILEFORMAT_DDS
 *   #define SUPPORT_FILEFORMAT_PKM
 *   #define SUPPORT_FILEFORMAT_KTX
 *   #define SUPPORT_FILEFORMAT_PVR
 *   #define SUPPORT_FILEFORMAT_ASTC
-*       Selected desired fileformats to be supported for loading. Some of those formats are 
+*       Selecte desired fileformats to be supported for image data loading. Some of those formats are 
 *       supported by default, to remove support, just comment unrequired #define in this module
 *
-*   #define SUPPORT_IMAGE_RESIZE / INCLUDE_STB_IMAGE_RESIZE
 *   #define SUPPORT_IMAGE_MANIPULATION
+*       Support multiple image editing functions to scale, adjust colors, flip, draw on images, crop...
+*       If not defined only three image editing functions supported: ImageFormat(), ImageAlphaMask(), ImageToPOT()
 *
 *   DEPENDENCIES:
 *       stb_image        - Multiple image formats loading (JPEG, PNG, BMP, TGA, PSD, GIF, PIC)
@@ -50,6 +50,12 @@
 *
 **********************************************************************************************/
 
+// Default configuration flags (supported features)
+//-------------------------------------------------
+#define SUPPORT_FILEFORMAT_PNG
+#define SUPPORT_IMAGE_MANIPULATION
+//-------------------------------------------------
+
 #include "raylib.h"
 
 #include <stdlib.h>             // Required for: malloc(), free()
@@ -61,23 +67,46 @@
 
 #include "utils.h"              // Required for: fopen() Android mapping, TraceLog()
 
-// Support only desired texture formats, by default: JPEG, PNG, BMP, TGA
-//#define STBI_NO_JPEG          // Image format .jpg and .jpeg
-//#define STBI_NO_PNG
-//#define STBI_NO_BMP
-//#define STBI_NO_TGA
-#define STBI_NO_PSD
-#define STBI_NO_GIF
-#define STBI_NO_HDR
+// Support only desired texture formats on stb_image
+#if !defined(SUPPORT_FILEFORMAT_BMP)
+    #define STBI_NO_BMP
+#endif
+#if !defined(SUPPORT_FILEFORMAT_PNG)
+    #define STBI_NO_PNG
+#endif
+#if !defined(SUPPORT_FILEFORMAT_TGA)
+    #define STBI_NO_TGA
+#endif
+#if !defined(SUPPORT_FILEFORMAT_JPG)
+    #define STBI_NO_JPEG        // Image format .jpg and .jpeg
+#endif
+#if !defined(SUPPORT_FILEFORMAT_PSD)
+    #define STBI_NO_PSD
+#endif
+#if !defined(SUPPORT_FILEFORMAT_GIF)
+    #define STBI_NO_GIF
+#endif
+#if !defined(SUPPORT_FILEFORMAT_HDR)
+    #define STBI_NO_HDR
+#endif
+
+// Image fileformats not supported by default
 #define STBI_NO_PIC
 #define STBI_NO_PNM             // Image format .ppm and .pgm
-#define STB_IMAGE_IMPLEMENTATION
-#include "external/stb_image.h" // Required for: stbi_load()
-                                // NOTE: Used to read image data (multiple formats support)
 
-#define STB_IMAGE_RESIZE_IMPLEMENTATION
-#include "external/stb_image_resize.h"  // Required for: stbir_resize_uint8()
-                                        // NOTE: Used for image scaling on ImageResize()
+#if (defined(SUPPORT_FILEFORMAT_BMP) || defined(SUPPORT_FILEFORMAT_PNG) || defined(SUPPORT_FILEFORMAT_TGA) || \
+     defined(SUPPORT_FILEFORMAT_JPG) || defined(SUPPORT_FILEFORMAT_PSD) || defined(SUPPORT_FILEFORMAT_GIF) || \
+     defined(SUPPORT_FILEFORMAT_HDR))
+    #define STB_IMAGE_IMPLEMENTATION
+    #include "external/stb_image.h"     // Required for: stbi_load()
+                                        // NOTE: Used to read image data (multiple formats support)
+#endif
+                                
+#if defined(SUPPORT_IMAGE_MANIPULATION)
+    #define STB_IMAGE_RESIZE_IMPLEMENTATION
+    #include "external/stb_image_resize.h"  // Required for: stbir_resize_uint8()
+                                            // NOTE: Used for image scaling on ImageResize()
+#endif
 
 //----------------------------------------------------------------------------------
 // Defines and Macros
@@ -102,11 +131,21 @@
 //----------------------------------------------------------------------------------
 // Module specific Functions Declaration
 //----------------------------------------------------------------------------------
+#if defined(SUPPORT_FILEFORMAT_DDS)
 static Image LoadDDS(const char *fileName);   // Load DDS file
+#endif
+#if defined(SUPPORT_FILEFORMAT_PKM)
 static Image LoadPKM(const char *fileName);   // Load PKM file
+#endif
+#if defined(SUPPORT_FILEFORMAT_KTX)
 static Image LoadKTX(const char *fileName);   // Load KTX file
+#endif
+#if defined(SUPPORT_FILEFORMAT_PVR)
 static Image LoadPVR(const char *fileName);   // Load PVR file
+#endif
+#if defined(SUPPORT_FILEFORMAT_ASTC)
 static Image LoadASTC(const char *fileName);  // Load ASTC file
+#endif
 
 //----------------------------------------------------------------------------------
 // Module Functions Definition
@@ -124,18 +163,21 @@ Image LoadImage(const char *fileName)
     image.mipmaps = 0;
     image.format = 0;
 
-    if ((strcmp(GetExtension(fileName),"png") == 0) ||
-        (strcmp(GetExtension(fileName),"bmp") == 0) ||
-        (strcmp(GetExtension(fileName),"tga") == 0) ||
-        (strcmp(GetExtension(fileName),"jpg") == 0)
-#ifndef STBI_NO_GIF
+    if ((strcmp(GetExtension(fileName),"png") == 0)
+#if defined(SUPPORT_FILEFORMAT_BMP)
+        || (strcmp(GetExtension(fileName),"bmp") == 0)
+#endif
+#if defined(SUPPORT_FILEFORMAT_TGA)
+        || (strcmp(GetExtension(fileName),"tga") == 0)
+#endif
+#if defined(SUPPORT_FILEFORMAT_JPG)
+        || (strcmp(GetExtension(fileName),"jpg") == 0)
+#endif
+#if defined(SUPPORT_FILEFORMAT_DDS)
         || (strcmp(GetExtension(fileName),"gif") == 0)
 #endif
-#ifndef STBI_NO_PSD
+#if defined(SUPPORT_FILEFORMAT_PSD)
         || (strcmp(GetExtension(fileName),"psd") == 0)
-#endif
-#ifndef STBI_NO_PIC
-        || (strcmp(GetExtension(fileName),"pic") == 0)
 #endif
        )
     {
@@ -155,11 +197,21 @@ Image LoadImage(const char *fileName)
         else if (imgBpp == 3) image.format = UNCOMPRESSED_R8G8B8;
         else if (imgBpp == 4) image.format = UNCOMPRESSED_R8G8B8A8;
     }
+#if defined(SUPPORT_FILEFORMAT_DDS)
     else if (strcmp(GetExtension(fileName),"dds") == 0) image = LoadDDS(fileName);
+#endif
+#if defined(SUPPORT_FILEFORMAT_PKM)
     else if (strcmp(GetExtension(fileName),"pkm") == 0) image = LoadPKM(fileName);
+#endif
+#if defined(SUPPORT_FILEFORMAT_KTX)
     else if (strcmp(GetExtension(fileName),"ktx") == 0) image = LoadKTX(fileName);
+#endif
+#if defined(SUPPORT_FILEFORMAT_PVR)
     else if (strcmp(GetExtension(fileName),"pvr") == 0) image = LoadPVR(fileName);
+#endif
+#if defined(SUPPORT_FILEFORMAT_ASTC)
     else if (strcmp(GetExtension(fileName),"astc") == 0) image = LoadASTC(fileName);
+#endif
     else if (strcmp(GetExtension(fileName),"rres") == 0)
     {
         RRES rres = LoadResource(fileName, 0);
@@ -171,6 +223,7 @@ Image LoadImage(const char *fileName)
 
         UnloadResource(rres);
     }
+    else TraceLog("[%s] Image fileformat not supported", fileName);
 
     if (image.data != NULL) TraceLog(INFO, "[%s] Image loaded successfully (%ix%i)", fileName, image.width, image.height);
     else TraceLog(WARNING, "[%s] Image could not be loaded", fileName);
@@ -664,115 +717,6 @@ void ImageAlphaMask(Image *image, Image alphaMask)
     }
 }
 
-// Dither image data to 16bpp or lower (Floyd-Steinberg dithering)
-// NOTE: In case selected bpp do not represent an known 16bit format,
-// dithered data is stored in the LSB part of the unsigned short
-void ImageDither(Image *image, int rBpp, int gBpp, int bBpp, int aBpp)
-{
-    if (image->format >= COMPRESSED_DXT1_RGB)
-    {
-        TraceLog(WARNING, "Compressed data formats can not be dithered");
-        return;
-    }
-
-    if ((rBpp+gBpp+bBpp+aBpp) > 16)
-    {
-        TraceLog(WARNING, "Unsupported dithering bpps (%ibpp), only 16bpp or lower modes supported", (rBpp+gBpp+bBpp+aBpp));
-    }
-    else
-    {
-        Color *pixels = GetImageData(*image);
-
-        free(image->data);      // free old image data
-
-        if ((image->format != UNCOMPRESSED_R8G8B8) && (image->format != UNCOMPRESSED_R8G8B8A8))
-        {
-            TraceLog(WARNING, "Image format is already 16bpp or lower, dithering could have no effect");
-        }
-
-        // Define new image format, check if desired bpp match internal known format
-        if ((rBpp == 5) && (gBpp == 6) && (bBpp == 5) && (aBpp == 0)) image->format = UNCOMPRESSED_R5G6B5;
-        else if ((rBpp == 5) && (gBpp == 5) && (bBpp == 5) && (aBpp == 1)) image->format = UNCOMPRESSED_R5G5B5A1;
-        else if ((rBpp == 4) && (gBpp == 4) && (bBpp == 4) && (aBpp == 4)) image->format = UNCOMPRESSED_R4G4B4A4;
-        else
-        {
-            image->format = 0;
-            TraceLog(WARNING, "Unsupported dithered OpenGL internal format: %ibpp (R%iG%iB%iA%i)", (rBpp+gBpp+bBpp+aBpp), rBpp, gBpp, bBpp, aBpp);
-        }
-
-        // NOTE: We will store the dithered data as unsigned short (16bpp)
-        image->data = (unsigned short *)malloc(image->width*image->height*sizeof(unsigned short));
-
-        Color oldPixel = WHITE;
-        Color newPixel = WHITE;
-
-        int rError, gError, bError;
-        unsigned short rPixel, gPixel, bPixel, aPixel;   // Used for 16bit pixel composition
-
-        #define MIN(a,b) (((a)<(b))?(a):(b))
-
-        for (int y = 0; y < image->height; y++)
-        {
-            for (int x = 0; x < image->width; x++)
-            {
-                oldPixel = pixels[y*image->width + x];
-
-                // NOTE: New pixel obtained by bits truncate, it would be better to round values (check ImageFormat())
-                newPixel.r = oldPixel.r >> (8 - rBpp);     // R bits
-                newPixel.g = oldPixel.g >> (8 - gBpp);     // G bits
-                newPixel.b = oldPixel.b >> (8 - bBpp);     // B bits
-                newPixel.a = oldPixel.a >> (8 - aBpp);     // A bits (not used on dithering)
-
-                // NOTE: Error must be computed between new and old pixel but using same number of bits!
-                // We want to know how much color precision we have lost...
-                rError = (int)oldPixel.r - (int)(newPixel.r << (8 - rBpp));
-                gError = (int)oldPixel.g - (int)(newPixel.g << (8 - gBpp));
-                bError = (int)oldPixel.b - (int)(newPixel.b << (8 - bBpp));
-
-                pixels[y*image->width + x] = newPixel;
-
-                // NOTE: Some cases are out of the array and should be ignored
-                if (x < (image->width - 1))
-                {
-                    pixels[y*image->width + x+1].r = MIN((int)pixels[y*image->width + x+1].r + (int)((float)rError*7.0f/16), 0xff);
-                    pixels[y*image->width + x+1].g = MIN((int)pixels[y*image->width + x+1].g + (int)((float)gError*7.0f/16), 0xff);
-                    pixels[y*image->width + x+1].b = MIN((int)pixels[y*image->width + x+1].b + (int)((float)bError*7.0f/16), 0xff);
-                }
-
-                if ((x > 0) && (y < (image->height - 1)))
-                {
-                    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);
-                    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);
-                    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);
-                }
-
-                if (y < (image->height - 1))
-                {
-                    pixels[(y+1)*image->width + x].r = MIN((int)pixels[(y+1)*image->width + x].r + (int)((float)rError*5.0f/16), 0xff);
-                    pixels[(y+1)*image->width + x].g = MIN((int)pixels[(y+1)*image->width + x].g + (int)((float)gError*5.0f/16), 0xff);
-                    pixels[(y+1)*image->width + x].b = MIN((int)pixels[(y+1)*image->width + x].b + (int)((float)bError*5.0f/16), 0xff);
-                }
-
-                if ((x < (image->width - 1)) && (y < (image->height - 1)))
-                {
-                    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);
-                    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);
-                    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);
-                }
-
-                rPixel = (unsigned short)newPixel.r;
-                gPixel = (unsigned short)newPixel.g;
-                bPixel = (unsigned short)newPixel.b;
-                aPixel = (unsigned short)newPixel.a;
-
-                ((unsigned short *)image->data)[y*image->width + x] = (rPixel << (gBpp + bBpp + aBpp)) | (gPixel << (bBpp + aBpp)) | (bPixel << aBpp) | aPixel;
-            }
-        }
-
-        free(pixels);
-    }
-}
-
 // Convert image to POT (power-of-two)
 // NOTE: It could be useful on OpenGL ES 2.0 (RPI, HTML5)
 void ImageToPOT(Image *image, Color fillColor)
@@ -818,6 +762,7 @@ void ImageToPOT(Image *image, Color fillColor)
     }
 }
 
+#if defined(SUPPORT_IMAGE_MANIPULATION)
 // Copy an image to a new image
 Image ImageCopy(Image image)
 {
@@ -1203,6 +1148,115 @@ void ImageFlipHorizontal(Image *image)
     image->data = processed.data;
 }
 
+// Dither image data to 16bpp or lower (Floyd-Steinberg dithering)
+// NOTE: In case selected bpp do not represent an known 16bit format,
+// dithered data is stored in the LSB part of the unsigned short
+void ImageDither(Image *image, int rBpp, int gBpp, int bBpp, int aBpp)
+{
+    if (image->format >= COMPRESSED_DXT1_RGB)
+    {
+        TraceLog(WARNING, "Compressed data formats can not be dithered");
+        return;
+    }
+
+    if ((rBpp+gBpp+bBpp+aBpp) > 16)
+    {
+        TraceLog(WARNING, "Unsupported dithering bpps (%ibpp), only 16bpp or lower modes supported", (rBpp+gBpp+bBpp+aBpp));
+    }
+    else
+    {
+        Color *pixels = GetImageData(*image);
+
+        free(image->data);      // free old image data
+
+        if ((image->format != UNCOMPRESSED_R8G8B8) && (image->format != UNCOMPRESSED_R8G8B8A8))
+        {
+            TraceLog(WARNING, "Image format is already 16bpp or lower, dithering could have no effect");
+        }
+
+        // Define new image format, check if desired bpp match internal known format
+        if ((rBpp == 5) && (gBpp == 6) && (bBpp == 5) && (aBpp == 0)) image->format = UNCOMPRESSED_R5G6B5;
+        else if ((rBpp == 5) && (gBpp == 5) && (bBpp == 5) && (aBpp == 1)) image->format = UNCOMPRESSED_R5G5B5A1;
+        else if ((rBpp == 4) && (gBpp == 4) && (bBpp == 4) && (aBpp == 4)) image->format = UNCOMPRESSED_R4G4B4A4;
+        else
+        {
+            image->format = 0;
+            TraceLog(WARNING, "Unsupported dithered OpenGL internal format: %ibpp (R%iG%iB%iA%i)", (rBpp+gBpp+bBpp+aBpp), rBpp, gBpp, bBpp, aBpp);
+        }
+
+        // NOTE: We will store the dithered data as unsigned short (16bpp)
+        image->data = (unsigned short *)malloc(image->width*image->height*sizeof(unsigned short));
+
+        Color oldPixel = WHITE;
+        Color newPixel = WHITE;
+
+        int rError, gError, bError;
+        unsigned short rPixel, gPixel, bPixel, aPixel;   // Used for 16bit pixel composition
+
+        #define MIN(a,b) (((a)<(b))?(a):(b))
+
+        for (int y = 0; y < image->height; y++)
+        {
+            for (int x = 0; x < image->width; x++)
+            {
+                oldPixel = pixels[y*image->width + x];
+
+                // NOTE: New pixel obtained by bits truncate, it would be better to round values (check ImageFormat())
+                newPixel.r = oldPixel.r >> (8 - rBpp);     // R bits
+                newPixel.g = oldPixel.g >> (8 - gBpp);     // G bits
+                newPixel.b = oldPixel.b >> (8 - bBpp);     // B bits
+                newPixel.a = oldPixel.a >> (8 - aBpp);     // A bits (not used on dithering)
+
+                // NOTE: Error must be computed between new and old pixel but using same number of bits!
+                // We want to know how much color precision we have lost...
+                rError = (int)oldPixel.r - (int)(newPixel.r << (8 - rBpp));
+                gError = (int)oldPixel.g - (int)(newPixel.g << (8 - gBpp));
+                bError = (int)oldPixel.b - (int)(newPixel.b << (8 - bBpp));
+
+                pixels[y*image->width + x] = newPixel;
+
+                // NOTE: Some cases are out of the array and should be ignored
+                if (x < (image->width - 1))
+                {
+                    pixels[y*image->width + x+1].r = MIN((int)pixels[y*image->width + x+1].r + (int)((float)rError*7.0f/16), 0xff);
+                    pixels[y*image->width + x+1].g = MIN((int)pixels[y*image->width + x+1].g + (int)((float)gError*7.0f/16), 0xff);
+                    pixels[y*image->width + x+1].b = MIN((int)pixels[y*image->width + x+1].b + (int)((float)bError*7.0f/16), 0xff);
+                }
+
+                if ((x > 0) && (y < (image->height - 1)))
+                {
+                    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);
+                    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);
+                    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);
+                }
+
+                if (y < (image->height - 1))
+                {
+                    pixels[(y+1)*image->width + x].r = MIN((int)pixels[(y+1)*image->width + x].r + (int)((float)rError*5.0f/16), 0xff);
+                    pixels[(y+1)*image->width + x].g = MIN((int)pixels[(y+1)*image->width + x].g + (int)((float)gError*5.0f/16), 0xff);
+                    pixels[(y+1)*image->width + x].b = MIN((int)pixels[(y+1)*image->width + x].b + (int)((float)bError*5.0f/16), 0xff);
+                }
+
+                if ((x < (image->width - 1)) && (y < (image->height - 1)))
+                {
+                    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);
+                    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);
+                    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);
+                }
+
+                rPixel = (unsigned short)newPixel.r;
+                gPixel = (unsigned short)newPixel.g;
+                bPixel = (unsigned short)newPixel.b;
+                aPixel = (unsigned short)newPixel.a;
+
+                ((unsigned short *)image->data)[y*image->width + x] = (rPixel << (gBpp + bBpp + aBpp)) | (gPixel << (bBpp + aBpp)) | (bPixel << aBpp) | aPixel;
+            }
+        }
+
+        free(pixels);
+    }
+}
+
 // Modify image color: tint
 void ImageColorTint(Image *image, Color color)
 {
@@ -1359,6 +1413,7 @@ void ImageColorBrightness(Image *image, int brightness)
 
     image->data = processed.data;
 }
+#endif      // SUPPORT_IMAGE_MANIPULATION
 
 // Generate GPU mipmaps for a texture
 void GenTextureMipmaps(Texture2D *texture)
@@ -1547,6 +1602,7 @@ void DrawTexturePro(Texture2D texture, Rectangle sourceRec, Rectangle destRec, V
 // Module specific Functions Definition
 //----------------------------------------------------------------------------------
 
+#if defined(SUPPORT_FILEFORMAT_DDS)
 // Loading DDS image data (compressed or uncompressed)
 static Image LoadDDS(const char *fileName)
 {
@@ -1744,7 +1800,9 @@ static Image LoadDDS(const char *fileName)
 
     return image;
 }
+#endif
 
+#if defined(SUPPORT_FILEFORMAT_PKM)
 // Loading PKM image data (ETC1/ETC2 compression)
 // NOTE: KTX is the standard Khronos Group compression format (ETC1/ETC2, mipmaps)
 // PKM is a much simpler file format used mainly to contain a single ETC1/ETC2 compressed image (no mipmaps)
@@ -1836,7 +1894,9 @@ static Image LoadPKM(const char *fileName)
 
     return image;
 }
+#endif
 
+#if defined(SUPPORT_FILEFORMAT_KTX)
 // Load KTX compressed image data (ETC1/ETC2 compression)
 static Image LoadKTX(const char *fileName)
 {
@@ -1929,7 +1989,9 @@ static Image LoadKTX(const char *fileName)
 
     return image;
 }
+#endif
 
+#if defined(SUPPORT_FILEFORMAT_PVR)
 // Loading PVR image data (uncompressed or PVRT compression)
 // NOTE: PVR v2 not supported, use PVR v3 instead
 static Image LoadPVR(const char *fileName)
@@ -2087,7 +2149,9 @@ static Image LoadPVR(const char *fileName)
 
     return image;
 }
+#endif
 
+#if defined(SUPPORT_FILEFORMAT_ASTC)
 // Load ASTC compressed image data (ASTC compression)
 static Image LoadASTC(const char *fileName)
 {
@@ -2170,3 +2234,4 @@ static Image LoadASTC(const char *fileName)
 
     return image;
 }
+#endif

+ 1 - 1
src/utils.c

@@ -20,7 +20,7 @@
 *       Show TraceLog() DEBUG messages
 *
 *   DEPENDENCIES:
-*       stb_image_write - PNG writting functions
+*       stb_image_write - BMP/PNG writting functions
 *
 *
 *   LICENSE: zlib/libpng