summaryrefslogtreecommitdiffhomepage
path: root/src/textures.c
diff options
context:
space:
mode:
authorraysan5 <[email protected]>2018-01-07 00:51:26 +0100
committerraysan5 <[email protected]>2018-01-07 00:51:26 +0100
commite4be917d1b36a65286ca49b85328187ea8c190ad (patch)
tree54070c2148103728581d5ebebe7454ba121b23d8 /src/textures.c
parentb97134c3e157f241cef485b34b8fb0e09575e7bc (diff)
downloadraylib-e4be917d1b36a65286ca49b85328187ea8c190ad.tar.gz
raylib-e4be917d1b36a65286ca49b85328187ea8c190ad.zip
Added new image functions
- Added: ImageAlphaClear() - Added: ImageAlphaPremultiply() - Reorganized some functions
Diffstat (limited to 'src/textures.c')
-rw-r--r--src/textures.c374
1 files changed, 206 insertions, 168 deletions
diff --git a/src/textures.c b/src/textures.c
index fe75bd74..44a9fef2 100644
--- a/src/textures.c
+++ b/src/textures.c
@@ -593,6 +593,74 @@ void SaveImageAs(const char *fileName, Image image)
#endif
}
+// Copy an image to a new image
+Image ImageCopy(Image image)
+{
+ Image newImage = { 0 };
+
+ int size = GetPixelDataSize(image.width, image.height, image.format);
+
+ newImage.data = malloc(size);
+
+ if (newImage.data != NULL)
+ {
+ // NOTE: Size must be provided in bytes
+ memcpy(newImage.data, image.data, size);
+
+ newImage.width = image.width;
+ newImage.height = image.height;
+ newImage.mipmaps = image.mipmaps;
+ newImage.format = image.format;
+ }
+
+ return newImage;
+}
+
+// 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)
+{
+ Color *pixels = GetImageData(*image); // Get pixels data
+
+ // Calculate next power-of-two values
+ // NOTE: Just add the required amount of pixels at the right and bottom sides of image...
+ int potWidth = (int)powf(2, ceilf(logf((float)image->width)/logf(2)));
+ int potHeight = (int)powf(2, ceilf(logf((float)image->height)/logf(2)));
+
+ // Check if POT texture generation is required (if texture is not already POT)
+ if ((potWidth != image->width) || (potHeight != image->height))
+ {
+ Color *pixelsPOT = NULL;
+
+ // Generate POT array from NPOT data
+ pixelsPOT = (Color *)malloc(potWidth*potHeight*sizeof(Color));
+
+ for (int j = 0; j < potHeight; j++)
+ {
+ for (int i = 0; i < potWidth; i++)
+ {
+ if ((j < image->height) && (i < image->width)) pixelsPOT[j*potWidth + i] = pixels[j*image->width + i];
+ else pixelsPOT[j*potWidth + i] = fillColor;
+ }
+ }
+
+ TraceLog(LOG_WARNING, "Image converted to POT: (%ix%i) -> (%ix%i)", image->width, image->height, potWidth, potHeight);
+
+ free(pixels); // Free pixels data
+ free(image->data); // Free old image data
+
+ int format = image->format; // Store image data format to reconvert later
+
+ // TODO: Image width and height changes... do we want to store new values or keep the old ones?
+ // NOTE: Issues when using image.width and image.height for sprite animations...
+ *image = LoadImageEx(pixelsPOT, potWidth, potHeight);
+
+ free(pixelsPOT); // Free POT pixels data
+
+ ImageFormat(image, format); // Reconvert image to previous format
+ }
+}
+
// Convert image data to desired format
void ImageFormat(Image *image, int newFormat)
{
@@ -806,75 +874,45 @@ void ImageAlphaMask(Image *image, Image alphaMask)
}
}
-// 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)
+// Clear alpha channel to desired color
+// NOTE: Threshold defines the alpha limit, 0.0f to 1.0f
+void ImageAlphaClear(Image *image, Color color, float threshold)
{
- Color *pixels = GetImageData(*image); // Get pixels data
-
- // Calculate next power-of-two values
- // NOTE: Just add the required amount of pixels at the right and bottom sides of image...
- int potWidth = (int)powf(2, ceilf(logf((float)image->width)/logf(2)));
- int potHeight = (int)powf(2, ceilf(logf((float)image->height)/logf(2)));
-
- // Check if POT texture generation is required (if texture is not already POT)
- if ((potWidth != image->width) || (potHeight != image->height))
- {
- Color *pixelsPOT = NULL;
-
- // Generate POT array from NPOT data
- pixelsPOT = (Color *)malloc(potWidth*potHeight*sizeof(Color));
-
- for (int j = 0; j < potHeight; j++)
- {
- for (int i = 0; i < potWidth; i++)
- {
- if ((j < image->height) && (i < image->width)) pixelsPOT[j*potWidth + i] = pixels[j*image->width + i];
- else pixelsPOT[j*potWidth + i] = fillColor;
- }
- }
-
- TraceLog(LOG_WARNING, "Image converted to POT: (%ix%i) -> (%ix%i)", image->width, image->height, potWidth, potHeight);
-
- free(pixels); // Free pixels data
- free(image->data); // Free old image data
-
- int format = image->format; // Store image data format to reconvert later
-
- // TODO: Image width and height changes... do we want to store new values or keep the old ones?
- // NOTE: Issues when using image.width and image.height for sprite animations...
- *image = LoadImageEx(pixelsPOT, potWidth, potHeight);
-
- free(pixelsPOT); // Free POT pixels data
+ Color *pixels = GetImageData(*image);
+
+ for (int i = 0; i < image->width*image->height; i++) if (pixels[i].a <= (unsigned char)(threshold*255.0f)) pixels[i] = color;
- ImageFormat(image, format); // Reconvert image to previous format
- }
+ UnloadImage(*image);
+
+ int prevFormat = image->format;
+ *image = LoadImageEx(pixels, image->width, image->height);
+
+ ImageFormat(image, prevFormat);
}
-#if defined(SUPPORT_IMAGE_MANIPULATION)
-// Copy an image to a new image
-Image ImageCopy(Image image)
+// Premultiply alpha channel
+void ImageAlphaPremultiply(Image *image)
{
- Image newImage = { 0 };
-
- int size = GetPixelDataSize(image.width, image.height, image.format);
-
- newImage.data = malloc(size);
-
- if (newImage.data != NULL)
+ float alpha = 0.0f;
+ Color *pixels = GetImageData(*image);
+
+ for (int i = 0; i < image->width*image->height; i++)
{
- // NOTE: Size must be provided in bytes
- memcpy(newImage.data, image.data, size);
-
- newImage.width = image.width;
- newImage.height = image.height;
- newImage.mipmaps = image.mipmaps;
- newImage.format = image.format;
+ alpha = (float)pixels[i].a/255.0f;
+ pixels[i].r = (unsigned char)((float)pixels[i].r*alpha);
+ pixels[i].g = (unsigned char)((float)pixels[i].g*alpha);
+ pixels[i].b = (unsigned char)((float)pixels[i].b*alpha);
}
- return newImage;
+ UnloadImage(*image);
+
+ int prevFormat = image->format;
+ *image = LoadImageEx(pixels, image->width, image->height);
+
+ ImageFormat(image, prevFormat);
}
+#if defined(SUPPORT_IMAGE_MANIPULATION)
// Crop an image to area defined by a rectangle
// NOTE: Security checks are performed in case rectangle goes out of bounds
void ImageCrop(Image *image, Rectangle crop)
@@ -982,6 +1020,115 @@ void ImageResizeNN(Image *image,int newWidth,int newHeight)
free(pixels);
}
+// 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(LOG_WARNING, "Compressed data formats can not be dithered");
+ return;
+ }
+
+ if ((rBpp+gBpp+bBpp+aBpp) > 16)
+ {
+ TraceLog(LOG_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(LOG_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(LOG_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);
+ }
+}
+
// Draw an image (source) within an image (destination)
// TODO: Feel this function could be simplified...
void ImageDraw(Image *dst, Image src, Rectangle srcRec, Rectangle dstRec)
@@ -1205,115 +1352,6 @@ 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(LOG_WARNING, "Compressed data formats can not be dithered");
- return;
- }
-
- if ((rBpp+gBpp+bBpp+aBpp) > 16)
- {
- TraceLog(LOG_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(LOG_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(LOG_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)
{