@ -2493,128 +2493,241 @@ void ImageDraw(Image *dst, Image src, Rectangle srcRec, Rectangle dstRec, Color
if ( ( dst - > data = = NULL ) | | ( dst - > width = = 0 ) | | ( dst - > height = = 0 ) | |
( src . data = = NULL ) | | ( src . width = = 0 ) | | ( src . height = = 0 ) ) return ;
/ / Security checks to avoid size and rectangle issues ( out of bounds )
/ / Check that srcRec is inside src image
if ( srcRec . x < 0 ) srcRec . x = 0 ;
if ( srcRec . y < 0 ) srcRec . y = 0 ;
if ( ( srcRec . x + srcRec . width ) > src . width )
if ( dst - > mipmaps > 1 ) TRACELOG ( LOG_WARNING , " Image drawing only applied to base mipmap level " ) ;
if ( dst - > format > = COMPRESSED_DXT1_RGB ) TRACELOG ( LOG_WARNING , " Image drawing not supported for compressed formats " ) ;
else
{
srcRec . width = src . width - srcRec . x ;
TRACELOG ( LOG_WARNING , " IMAGE: Source rectangle width out of bounds, rescaled width: %i " , srcRec . width ) ;
}
/ / Despite all my efforts for optimization , original implementation is faster . . .
/ / I left here other implementations for future reference
# define IMAGEDRAW_METHOD01
# if defined(IMAGEDRAW_METHOD01)
/ / Security checks to avoid size and rectangle issues ( out of bounds )
/ / Check that srcRec is inside src image
if ( srcRec . x < 0 ) srcRec . x = 0 ;
if ( srcRec . y < 0 ) srcRec . y = 0 ;
if ( ( srcRec . x + srcRec . width ) > src . width ) srcRec . width = src . width - srcRec . x ;
if ( ( srcRec . y + srcRec . height ) > src . height ) srcRec . height = src . height - srcRec . y ;
Image srcMod = ImageCopy ( src ) ; / / Make a copy of source image to work with it
if ( ( srcRec . y + srcRec . height ) > src . height )
{
srcRec . height = src . height - srcRec . y ;
TRACELOG ( LOG_WARNING , " IMAGE: Source rectangle height out of bounds, rescaled height: %i " , srcRec . height ) ;
}
/ / TODO : OPTIMIZATION : Avoid ImageCrop ( ) , not required , it should be enough to know the src / dst rectangles to copy data
/ / Crop source image to desired source rectangle ( if required )
if ( ( src . width ! = ( int ) srcRec . width ) & & ( src . height ! = ( int ) srcRec . height ) )
{
ImageCrop ( & srcMod , srcRec ) ;
}
Image srcCopy = ImageCopy ( src ) ; / / Make a copy of source image to work with it
/ / Scale source image in case destination rec size is different than source rec size
if ( ( ( int ) dstRec . width ! = ( int ) srcRec . width ) | | ( ( int ) dstRec . height ! = ( int ) srcRec . height ) )
{
ImageResize ( & srcMod , ( int ) dstRec . width , ( int ) dstRec . height ) ;
}
/ / Crop source image to desired source rectangle ( if required )
if ( ( src . width ! = ( int ) srcRec . width ) & & ( src . height ! = ( int ) srcRec . height ) )
{
ImageCrop ( & srcCopy , srcRec ) ;
}
/ / Check that dstRec is inside dst image
/ / Allow negative position within destination with cropping
if ( dstRec . x < 0 )
{
ImageCrop ( & srcMod , ( Rectangle ) { - dstRec . x , 0 , dstRec . width + dstRec . x , dstRec . height } ) ;
dstRec . width = dstRec . width + dstRec . x ;
dstRec . x = 0 ;
}
/ / Scale source image in case destination rec size is different than source rec size
if ( ( ( int ) dstRec . width ! = ( int ) srcRec . width ) | | ( ( int ) dstRec . height ! = ( int ) srcRec . height ) )
{
ImageResize ( & srcCopy , ( int ) dstRec . width , ( int ) dstRec . height ) ;
}
if ( ( dstRec . x + dstRec . width ) > dst - > width )
{
ImageCrop ( & srcMod , ( Rectangle ) { 0 , 0 , dst - > width - dstRec . x , dstRec . height } ) ;
dstRec . width = dst - > width - dstRec . x ;
}
/ / Check that dstRec is inside dst image
/ / Allow negative position within destination with cropping
if ( dstRec . x < 0 )
{
ImageCrop ( & srcCopy , ( Rectangle ) { - dstRec . x , 0 , dstRec . width + dstRec . x , dstRec . height } ) ;
dstRec . width = dstRec . width + dstRec . x ;
dstRec . x = 0 ;
}
if ( dstRec . y < 0 )
{
ImageCrop ( & srcMod , ( Rectangle ) { 0 , - dstRec . y , dstRec . width , dstRec . height + dstRec . y } ) ;
dstRec . height = dstRec . height + dstRec . y ;
dstRec . y = 0 ;
}
if ( ( dstRec . x + dstRec . width ) > dst - > width )
{
ImageCrop ( & srcCopy , ( Rectangle ) { 0 , 0 , dst- > width - dstRec . x , dstRec . height } ) ;
dstRec . width = dst - > width - dstRec . x ;
}
if ( ( dstRec . y + dstRec . height ) > dst - > height )
{
ImageCrop ( & srcMod , ( Rectangle ) { 0 , 0 , dstRec . width , dst - > height - dstRec . y } ) ;
dstRec . height = dst - > height - dstRec . y ;
}
if ( dstRec . y < 0 )
{
ImageCrop ( & srcCopy , ( Rectangle ) { 0 , - dstRec . y , dstRec . width , dstRec . height + dstRec . y } ) ;
dstRec . height = dstRec . height + dstRec . y ;
dstRec . y = 0 ;
}
/ / Get image data as Color pixels array to work with it
Color * dstPixels = GetImageData ( * dst ) ;
Color * srcPixels = GetImageData ( srcMod ) ;
if ( ( dstRec . y + dstRec . height ) > dst - > height )
{
ImageCrop ( & srcCopy , ( Rectangle ) { 0 , 0 , dstRec . width , dst - > height - dstRec . y } ) ;
dstRec . height = dst - > height - dstRec . y ;
}
UnloadImage ( srcMod ) ; / / Source copy not required any more
/ / Get image data as Color pixels array to work with it
Color * dstPixels = GetImageData ( * dst ) ;
Color * srcPixels = GetImageData ( srcCopy ) ;
Vector4 fsrc , fdst , fout ; / / Normalized pixel data ( ready for operation )
Vector4 ftint = ColorNormalize ( tint ) ; / / Normalized color tint
UnloadImage ( srcCopy ) ; / / Source copy not required any more
/ / Blit pixels , copy source image into destination
for ( int j = ( int ) dstRec . y ; j < ( int ) ( dstRec . y + dstRec . height ) ; j + + )
{
for ( int i = ( int ) dstRec . x ; i < ( int ) ( dstRec . x + dstRec . width ) ; i + + )
{
/ / Alpha blending ( https : / / en . wikipedia . org / wiki / Alpha_compositing )
Vector4 fsrc , fdst , fout ; / / Normalized pixel data ( ready for operation )
Vector4 ftint = ColorNormalize ( tint ) ; / / Normalized color tint
fdst = ColorNormalize ( dstPixels [ j * ( int ) dst - > width + i ] ) ;
fsrc = ColorNormalize ( srcPixels [ ( j - ( int ) dstRec . y ) * ( int ) dstRec . width + ( i - ( int ) dstRec . x ) ] ) ;
/ / Blit pixels , copy source image into destination
for ( int j = ( int ) dstRec . y ; j < ( int ) ( dstRec . y + dstRec . height ) ; j + + )
{
for ( int i = ( int ) dstRec . x ; i < ( int ) ( dstRec . x + dstRec . width ) ; i + + )
{
/ / Alpha blending ( https : / / en . wikipedia . org / wiki / Alpha_compositing )
/ / Apply color tint to source image
fsrc . x * = ftint . x ; fsrc . y * = ftint . y ; fsrc . z * = ftint . z ; fsrc . w * = ftint . w ;
fdst = ColorNormalize ( dstPixels [ j * ( int ) dst - > width + i ] ) ;
fsrc = ColorNormalize ( srcPixels [ ( j - ( int ) dstRec . y ) * ( int ) dstRec . width + ( i - ( int ) dstRec . x ) ] ) ;
fout . w = fsrc . w + fdst . w * ( 1.0f - fsrc . w ) ;
/ / Apply color tint to source image
fsrc . x * = ftint . x ; fsrc . y * = ftint . y ; fsrc . z * = ftint . z ; fsrc . w * = ftint . w ;
if ( fout . w < = 0.0f )
{
fout . x = 0.0f ;
fout . y = 0.0f ;
fout . z = 0.0f ;
}
else
{
fout . x = ( fsrc . x * fsrc . w + fdst . x * fdst . w * ( 1 - fsrc . w ) ) / fout . w ;
fout . y = ( fsrc . y * fsrc . w + fdst . y * fdst . w * ( 1 - fsrc . w ) ) / fout . w ;
fout . z = ( fsrc . z * fsrc . w + fdst . z * fdst . w * ( 1 - fsrc . w ) ) / fout . w ;
}
fout . w = fsrc . w + fdst . w * ( 1.0f - fsrc . w ) ;
dstPixels [ j * ( int ) dst - > width + i ] = ( 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 ) } ;
if ( fout . w < = 0.0f )
{
fout . x = 0.0f ;
fout . y = 0.0f ;
fout . z = 0.0f ;
}
else
{
fout . x = ( fsrc . x * fsrc . w + fdst . x * fdst . w * ( 1 - fsrc . w ) ) / fout . w ;
fout . y = ( fsrc . y * fsrc . w + fdst . y * fdst . w * ( 1 - fsrc . w ) ) / fout . w ;
fout . z = ( fsrc . z * fsrc . w + fdst . z * fdst . w * ( 1 - fsrc . w ) ) / fout . w ;
/ / TODO : Support other blending options
}
}
dstPixels [ j * ( int ) dst - > width + i ] = ( 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 ) } ;
Image final = {
. data = dstPixels ,
. width = dst - > width ,
. height = dst - > height ,
. format = UNCOMPRESSED_R8G8B8A8 ,
. mipmaps = 1
} ;
/ / NOTE : dstPixels are free ( ) inside ImageFormat ( )
ImageFormat ( & final , dst - > format ) ;
UnloadImage ( * dst ) ;
* dst = final ;
RL_FREE ( srcPixels ) ;
# elif defined(IMAGEDRAW_METHOD02)
Image srcMod = ImageCopy ( src ) ; / / Make a copy of source image to work with it
ImageFormat ( & srcMod , UNCOMPRESSED_R8G8B8A8 ) ; / / Convert to R8G8B8A8 to help on blending
/ / Source rectangle out - of - bounds security checks
if ( srcRec . x < 0 ) { srcRec . width - = srcRec . x ; srcRec . x = 0 ; }
if ( srcRec . y < 0 ) { srcRec . height - = srcRec . y ; srcRec . y = 0 ; }
if ( ( srcRec . x + srcRec . width ) > src . width ) srcRec . width = src . width - srcRec . x ;
if ( ( srcRec . y + srcRec . height ) > src . height ) srcRec . height = src . height - srcRec . y ;
/ / Check if source rectangle needs to be resized to destination rectangle
/ / In that case , we make a copy of source and we apply all required transform
if ( ( srcRec . width ! = fabs ( dstRec . width - dstRec . x ) ) | | ( srcRec . height ! = fabs ( dstRec . height - dstRec . y ) ) )
{
ImageCrop ( & srcMod , srcRec ) ; / / Crop to source rectangle
ImageResize ( & srcMod , ( int ) dstRec . width , ( int ) dstRec . height ) ; / / Resize to destination rectangle
srcRec = ( Rectangle ) { 0 , 0 , srcMod . width , srcMod . height } ;
}
/ / Check if destination format is different than source format and no source copy created yet
if ( dst - > format ! = src . format )
{
ImageCrop ( & srcMod , srcRec ) ; / / Crop to source rectangle
srcRec = ( Rectangle ) { 0 , 0 , srcMod . width , srcMod . height } ;
}
/ / TODO : Support other blending options
/ / Destination rectangle out - of - bounds security checks
if ( dstRec . x < 0 )
{
srcRec . x = - dstRec . x ;
srcRec . width + = dstRec . x ;
dstRec . x = 0 ;
}
}
else if ( ( dstRec . x + srcMod . width ) > dst - > width ) srcRec . width = dst - > width - dstRec . x ;
Image final = {
. data = dstPixels ,
. width = dst - > width ,
. height = dst - > height ,
. format = UNCOMPRESSED_R8G8B8A8 ,
. mipmaps = 1
} ;
if ( dstRec . y < 0 )
{
srcRec . y = - dstRec . y ;
srcRec . height + = dstRec . y ;
dstRec . y = 0 ;
}
else if ( ( dstRec . y + srcMod . height ) > dst - > height ) srcRec . height = dst - > height - dstRec . y ;
/ / NOTE : dstPixels are free ( ) inside ImageFormat ( )
ImageFormat ( & final , dst - > format ) ;
UnloadImage ( * dst ) ;
* dst = final ;
if ( dst - > width < srcRec . width ) srcRec . width = dst - > width ;
if ( dst - > height < srcRec . height ) srcRec . height = dst - > height ;
# if defined(IMAGEDRAW_NO_BLENDING)
/ / This method is very fast but no pixels blending is considered
int dataSize = GetPixelDataSize ( dst - > width , dst - > height , dst - > format ) ;
int bytesPerPixel = dataSize / ( dst - > width * dst - > height ) ;
/ / Image blitting src - > destination , line by line
for ( int y = 0 ; y < ( int ) srcRec . height ; y + + )
{
memcpy ( ( unsigned char * ) dst - > data + ( ( int ) dstRec . y * dst - > width + ( int ) dstRec . x + y * dst - > width ) * bytesPerPixel ,
( unsigned char * ) srcMod . data + ( ( y + ( int ) srcRec . y ) * srcMod . width + ( int ) srcRec . x ) * bytesPerPixel ,
( int ) srcRec . width * bytesPerPixel ) ;
}
# else
/ / This method is very slow considering alpha blending . . .
/ / Convert destination to R8G8B8A8 for blending calculation
int dstFormat = dst - > format ;
ImageFormat ( dst , UNCOMPRESSED_R8G8B8A8 ) ; / / Force 4 bytes per pixel with alpha for blending
Vector4 fsrc , fdst , fout ; / / Normalized pixel data ( ready for operation )
Vector4 ftint = ColorNormalize ( tint ) ; / / Normalized color tint
unsigned char srcAlpha = 0 ;
for ( int y = 0 ; y < ( int ) srcRec . height ; y + + )
{
for ( int x = 0 ; x < ( int ) srcRec . width ; x + + )
{
srcAlpha = ( ( Color * ) srcMod . data ) [ ( ( y + ( int ) srcRec . y ) * srcMod . width + ( int ) srcRec . x ) + x ] . a ;
if ( srcAlpha = = 255 )
{
( ( Color * ) dst - > data ) [ ( ( int ) dstRec . y * dst - > width + ( int ) dstRec . x ) + y * ( dst - > width ) + x ] = ( ( Color * ) srcMod . data ) [ ( ( y + ( int ) srcRec . y ) * srcMod . width + ( int ) srcRec . x ) + x ] ;
}
else if ( srcAlpha > 0 )
{
/ / Alpha blending ( https : / / en . wikipedia . org / wiki / Alpha_compositing )
RL_FREE ( srcPixels ) ;
fdst = ColorNormalize ( ( ( Color * ) dst - > data ) [ ( ( int ) dstRec . y * dst - > width + ( int ) dstRec . x ) + y * ( dst - > width ) + x ] ) ;
fsrc = ColorNormalize ( ( ( Color * ) srcMod . data ) [ ( ( y + ( int ) srcRec . y ) * srcMod . width + ( int ) srcRec . x ) + x ] ) ;
/ / Apply color tint to source image
fsrc . x * = ftint . x ; fsrc . y * = ftint . y ; fsrc . z * = ftint . z ; fsrc . w * = ftint . w ;
fout . w = fsrc . w + fdst . w * ( 1.0f - fsrc . w ) ;
if ( fout . w < = 0.0f )
{
fout . x = 0.0f ;
fout . y = 0.0f ;
fout . z = 0.0f ;
}
else
{
fout . x = ( fsrc . x * fsrc . w + fdst . x * fdst . w * ( 1 - fsrc . w ) ) / fout . w ;
fout . y = ( fsrc . y * fsrc . w + fdst . y * fdst . w * ( 1 - fsrc . w ) ) / fout . w ;
fout . z = ( fsrc . z * fsrc . w + fdst . z * fdst . w * ( 1 - fsrc . w ) ) / fout . w ;
}
( ( Color * ) dst - > data ) [ ( ( int ) dstRec . y * dst - > width + ( int ) dstRec . x ) + y * ( dst - > width ) + x ] = ( 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 ) } ;
/ / TODO : Support other blending options
}
}
}
ImageFormat ( dst , dstFormat ) ; / / Restore original image format after drawing with blending
UnloadImage ( srcMod ) ; / / Unload source modified image
# endif
# endif
}
}
/ / Draw text ( default font ) within an image ( destination )