8000 SDL/src/video/SDL_surface.c at SDL2 · aljen/SDL · GitHub
[go: up one dir, main page]

Skip to content

Latest commit

 

History

History
1585 lines (1374 loc) · 45.8 KB

SDL_surface.c

File metadata and controls

1585 lines (1374 loc) · 45.8 KB
/*
Simple DirectMedia Layer
Copyright (C) 1997-2026 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#include "../SDL_internal.h"
#include "SDL_video.h"
#include "SDL_sysvideo.h"
#include "SDL_blit.h"
#include "SDL_RLEaccel_c.h"
#include "SDL_pixels_c.h"
#include "SDL_yuv_c.h"
#include "../render/SDL_sysrender.h"
/* Check to make sure we can safely check multiplication of surface w and pitch and it won't overflow size_t */
SDL_COMPILE_TIME_ASSERT(surface_size_assumptions,
sizeof(int) == sizeof(Sint32) && sizeof(size_t) >= sizeof(Sint32));
SDL_COMPILE_TIME_ASSERT(can_indicate_overflow, SDL_SIZE_MAX > SDL_MAX_SINT32);
/* Public routines */
/*
* Calculate the pad-aligned scanline width of a surface.
* Return SDL_SIZE_MAX on overflow.
*
* for FOURCC, use SDL_CalculateYUVSize()
*/
static size_t SDL_CalculatePitch(Uint32 format, size_t width, SDL_bool minimal)
{
size_t pitch;
if (SDL_BITSPERPIXEL(format) >= 8) {
if (SDL_size_mul_overflow(width, SDL_BYTESPERPIXEL(format), &pitch)) {
return SDL_SIZE_MAX;
}
} else {
if (SDL_size_mul_overflow(width, SDL_BITSPERPIXEL(format), &pitch)) {
return SDL_SIZE_MAX;
}
if (SDL_size_add_overflow(pitch, 7, &pitch)) {
return SDL_SIZE_MAX;
}
pitch /= 8;
}
if (!minimal) {
/* 4-byte aligning for speed */
if (SDL_size_add_overflow(pitch, 3, &pitch)) {
return SDL_SIZE_MAX;
}
pitch &= ~3;
}
return pitch;
}
/* TODO: In SDL 3, drop the unused flags and depth parameters */
/*
* Create an empty RGB surface of the appropriate depth using the given
* enum SDL_PIXELFORMAT_* format
*/
SDL_Surface *SDL_CreateRGBSurfaceWithFormat(Uint32 flags, int width, int height, int depth,
Uint32 format)
{
size_t pitch;
SDL_Surface *surface;
/* The flags are no longer used, make the compiler happy */
(void)flags;
if (width < 0) {
SDL_InvalidParamError("width");
return NULL;
}
if (height < 0) {
SDL_InvalidParamError("height");
return NULL;
}
if (SDL_ISPIXELFORMAT_FOURCC(format)) {
SDL_SetError("invalid format");
return NULL;
} else {
pitch = SDL_CalculatePitch(format, width, SDL_FALSE);
if (pitch > SDL_MAX_SINT32) {
/* Overflow... */
SDL_OutOfMemory();
return NULL;
}
}
/* Allocate the surface */
surface = (SDL_Surface *)SDL_calloc(1, sizeof(*surface));
if (!surface) {
SDL_OutOfMemory();
return NULL;
}
surface->format = SDL_AllocFormat(format);
if (!surface->format) {
SDL_FreeSurface(surface);
return NULL;
}
surface->w = width;
surface->h = height;
surface->pitch = (int)pitch;
SDL_SetClipRect(surface, NULL);
if (SDL_ISPIXELFORMAT_INDEXED(surface->format->format)) {
SDL_Palette *palette =
SDL_AllocPalette((1 << surface->format->BitsPerPixel));
if (!palette) {
SDL_FreeSurface(surface);
return NULL;
}
if (palette->ncolors == 2) {
/* Create a black and white bitmap palette */
palette->colors[0].r = 0xFF;
palette->colors[0].g = 0xFF;
palette->colors[0].b = 0xFF;
palette->colors[1].r = 0x00;
palette->colors[1].g = 0x00;
palette->colors[1].b = 0x00;
}
SDL_SetSurfacePalette(surface, palette);
SDL_FreePalette(palette);
}
/* Get the pixels */
if (surface->w && surface->h) {
/* Assumptions checked in surface_size_assumptions assert above */
size_t size;
if (SDL_size_mul_overflow(surface->h, surface->pitch, &size)) {
/* Overflow... */
SDL_FreeSurface(surface);
SDL_OutOfMemory();
return NULL;
}
surface->pixels = SDL_SIMDAlloc(size);
if (!surface->pixels) {
SDL_FreeSurface(surface);
SDL_OutOfMemory();
return NULL;
}
surface->flags |= SDL_SIMD_ALIGNED;
/* This is important for bitmaps */
SDL_memset(surface->pixels, 0, size);
}
/* Allocate an empty mapping */
surface->map = SDL_AllocBlitMap();
if (!surface->map) {
SDL_FreeSurface(surface);
return NULL;
}
/* By default surface with an alpha mask are set up for blending */
if (surface->format->Amask) {
SDL_SetSurfaceBlendMode(surface, SDL_BLENDMODE_BLEND);
}
/* The surface is ready to go */
surface->refcount = 1;
return surface;
}
/* TODO: In SDL 3, drop the unused flags parameter */
/*
* Create an empty RGB surface of the appropriate depth
*/
SDL_Surface *SDL_CreateRGBSurface(Uint32 flags,
int width, int height, int depth,
Uint32 Rmask, Uint32 Gmask, Uint32 Bmask, Uint32 Amask)
{
Uint32 format;
/* Get the pixel format */
format = SDL_MasksToPixelFormatEnum(depth, Rmask, Gmask, Bmask, Amask);
if (format == SDL_PIXELFORMAT_UNKNOWN) {
SDL_SetError("Unknown pixel format");
return NULL;
}
return SDL_CreateRGBSurfaceWithFormat(flags, width, height, depth, format);
}
/*
* Create an RGB surface from an existing memory buffer
*/
SDL_Surface *SDL_CreateRGBSurfaceFrom(void *pixels,
int width, int height, int depth, int pitch,
Uint32 Rmask, Uint32 Gmask, Uint32 Bmask,
Uint32 Amask)
{
SDL_Surface *surface;
Uint32 format;
size_t minimalPitch;
if (width < 0) {
SDL_InvalidParamError("width");
return NULL;
}
if (height < 0) {
SDL_InvalidParamError("height");
return NULL;
}
format = SDL_MasksToPixelFormatEnum(depth, Rmask, Gmask, Bmask, Amask);
if (format == SDL_PIXELFORMAT_UNKNOWN) {
SDL_SetError("Unknown pixel format");
return NULL;
}
minimalPitch = SDL_CalculatePitch(format, width, SDL_TRUE);
if (pitch < 0 || (pitch > 0 && ((size_t)pitch) < minimalPitch)) {
SDL_InvalidParamError("pitch");
return NULL;
}
surface = SDL_CreateRGBSurfaceWithFormat(0, 0, 0, depth, format);
if (surface) {
surface->flags |= SDL_PREALLOC;
surface->pixels = pixels;
surface->w = width;
surface->h = height;
surface->pitch = pitch;
SDL_SetClipRect(surface, NULL);
}
return surface;
}
/* TODO: In SDL 3, drop the unused depth parameter */
/*
* Create an RGB surface from an existing memory buffer using the given given
* enum SDL_PIXELFORMAT_* format
*/
SDL_Surface *SDL_CreateRGBSurfaceWithFormatFrom(void *pixels,
int width, int height, int depth, int pitch,
Uint32 format)
{
SDL_Surface *surface;
size_t minimalPitch;
if (width < 0) {
SDL_InvalidParamError("width");
return NULL;
}
if (height < 0) {
SDL_InvalidParamError("height");
return NULL;
}
if (SDL_ISPIXELFORMAT_FOURCC(format)) {
SDL_SetError("invalid format");
return NULL;
} else {
minimalPitch = SDL_CalculatePitch(format, width, SDL_TRUE);
}
if (pitch < 0 || (pitch > 0 && ((size_t)pitch) < minimalPitch)) {
SDL_InvalidParamError("pitch");
return NULL;
}
surface = SDL_CreateRGBSurfaceWithFormat(0, 0, 0, depth, format);
if (surface) {
surface->flags |= SDL_PREALLOC;
surface->pixels = pixels;
surface->w = width;
surface->h = height;
surface->pitch = pitch;
SDL_SetClipRect(surface, NULL);
}
return surface;
}
int SDL_SetSurfacePalette(SDL_Surface *surface, SDL_Palette *palette)
{
if (!surface) {
return SDL_InvalidParamError("SDL_SetSurfacePalette(): surface");
}
if (SDL_SetPixelFormatPalette(surface->format, palette) < 0) {
return -1;
}
SDL_InvalidateMap(surface->map);
return 0;
}
int SDL_SetSurfaceRLE(SDL_Surface *surface, int flag)
{
int flags;
if (!surface) {
return SDL_InvalidParamError("surface");
}
flags = surface->map->info.flags;
if (flag) {
surface->map->info.flags |= SDL_COPY_RLE_DESIRED;
} else {
surface->map->info.flags &= ~SDL_COPY_RLE_DESIRED;
}
if (surface->map->info.flags != flags) {
SDL_InvalidateMap(surface->map);
}
return 0;
}
SDL_bool SDL_HasSurfaceRLE(SDL_Surface *surface)
{
if (!surface) {
return SDL_FALSE;
}
if (!(surface->map->info.flags & SDL_COPY_RLE_DESIRED)) {
return SDL_FALSE;
}
return SDL_TRUE;
}
int SDL_SetColorKey(SDL_Surface *surface, int flag, Uint32 key)
{
int flags;
if (!surface) {
return SDL_InvalidParamError("surface");
}
if (surface->format->palette && key >= ((Uint32)surface->format->palette->ncolors)) {
return SDL_InvalidParamError("key");
}
if (flag & SDL_RLEACCEL) {
SDL_SetSurfaceRLE(surface, 1);
}
flags = surface->map->info.flags;
if (flag) {
surface->map->info.flags |= SDL_COPY_COLORKEY;
surface->map->info.colorkey = key;
} else {
surface->map->info.flags &= ~SDL_COPY_COLORKEY;
}
if (surface->map->info.flags != flags) {
SDL_InvalidateMap(surface->map);
}
return 0;
}
SDL_bool SDL_HasColorKey(SDL_Surface *surface)
{
if (!surface) {
return SDL_FALSE;
}
if (!(surface->map->info.flags & SDL_COPY_COLORKEY)) {
return SDL_FALSE;
}
return SDL_TRUE;
}
int SDL_GetColorKey(SDL_Surface *surface, Uint32 *key)
{
if (!surface) {
return SDL_InvalidParamError("surface");
}
if (!(surface->map->info.flags & SDL_COPY_COLORKEY)) {
return SDL_SetError("Surface doesn't have a colorkey");
}
if (key) {
*key = surface->map->info.colorkey;
}
return 0;
}
/* This is a fairly slow function to switch from colorkey to alpha
NB: it doesn't handle bpp 1 or 3, because they have no alpha channel */
static void SDL_ConvertColorkeyToAlpha(SDL_Surface *surface, SDL_bool ignore_alpha)
{
int x, y, bpp;
if (!surface) {
return;
}
if (!(surface->map->info.flags & SDL_COPY_COLORKEY) ||
!surface->format->Amask) {
return;
}
bpp = surface->format->BytesPerPixel;
SDL_LockSurface(surface);
if (bpp == 2) {
Uint16 *row, *spot;
Uint16 ckey = (Uint16)surface->map->info.colorkey;
Uint16 mask = (Uint16)(~surface->format->Amask);
/* Ignore, or not, alpha in colorkey comparison */
if (ignore_alpha) {
ckey &= mask;
row = (Uint16 *)surface->pixels;
for (y = surface->h; y--;) {
spot = row;
for (x = surface->w; x--;) {
if ((*spot & mask) == ckey) {
*spot &= mask;
}
++spot;
}
row += surface->pitch / 2;
}
} else {
row = (Uint16 *)surface->pixels;
for (y = surface->h; y--;) {
spot = row;
for (x = surface->w; x--;) {
if (*spot == ckey) {
*spot &= mask;
}
++spot;
}
row += surface->pitch / 2;
}
}
} else if (bpp == 4) {
Uint32 *row, *spot;
Uint32 ckey = surface->map->info.colorkey;
Uint32 mask = ~surface->format->Amask;
/* Ignore, or not, alpha in colorkey comparison */
if (ignore_alpha) {
ckey &= mask;
row = (Uint32 *)surface->pixels;
for (y = surface->h; y--;) {
spot = row;
for (x = surface->w; x--;) {
if ((*spot & mask) == ckey) {
*spot &= mask;
}
++spot;
}
row += surface->pitch / 4;
}
} else {
row = (Uint32 *)surface->pixels;
for (y = surface->h; y--;) {
spot = row;
for (x = surface->w; x--;) {
if (*spot == ckey) {
*spot &= mask;
}
++spot;
}
row += surface->pitch / 4;
}
}
}
SDL_UnlockSurface(surface);
SDL_SetColorKey(surface, 0, 0);
SDL_SetSurfaceBlendMode(surface, SDL_BLENDMODE_BLEND);
}
int SDL_SetSurfaceColorMod(SDL_Surface *surface, Uint8 r, Uint8 g, Uint8 b)
{
int flags;
if (!surface) {
return SDL_InvalidParamError("surface");
}
surface->map->info.r = r;
surface->map->info.g = g;
surface->map->info.b = b;
flags = surface->map->info.flags;
if (r != 0xFF || g != 0xFF || b != 0xFF) {
surface->map->info.flags |= SDL_COPY_MODULATE_COLOR;
} else {
surface->map->info.flags &= ~SDL_COPY_MODULATE_COLOR;
}
if (surface->map->info.flags != flags) {
SDL_InvalidateMap(surface->map);
}
return 0;
}
int SDL_GetSurfaceColorMod(SDL_Surface *surface, Uint8 *r, Uint8 *g, Uint8 *b)
{
if (!surface) {
return SDL_InvalidParamError("surface");
}
if (r) {
*r = surface->map->info.r;
}
if (g) {
*g = surface->map->info.g;
}
if (b) {
*b = surface->map->info.b;
}
return 0;
}
int SDL_SetSurfaceAlphaMod(SDL_Surface *surface, Uint8 alpha)
{
int flags;
if (!surface) {
return SDL_InvalidParamError("surface");
}
surface->map->info.a = alpha;
flags = surface->map->info.flags;
if (alpha != 0xFF) {
surface->map->info.flags |= SDL_COPY_MODULATE_ALPHA;
} else {
surface->map->info.flags &= ~SDL_COPY_MODULATE_ALPHA;
}
if (surface->map->info.flags != flags) {
SDL_InvalidateMap(surface->map);
}
return 0;
}
int SDL_GetSurfaceAlphaMod(SDL_Surface *surface, Uint8 *alpha)
{
if (!surface) {
return SDL_InvalidParamError("surface");
}
if (alpha) {
*alpha = surface->map->info.a;
}
return 0;
}
int SDL_SetSurfaceBlendMode(SDL_Surface *surface, SDL_BlendMode blendMode)
{
int flags, status;
if (!surface) {
return SDL_InvalidParamError("surface");
}
status = 0;
flags = surface->map->info.flags;
surface->map->info.flags &=
~(SDL_COPY_BLEND | SDL_COPY_ADD | SDL_COPY_MOD | SDL_COPY_MUL);
switch (blendMode) {
case SDL_BLENDMODE_NONE:
break;
case SDL_BLENDMODE_BLEND:
surface->map->info.flags |= SDL_COPY_BLEND;
break;
case SDL_BLENDMODE_ADD:
surface->map->info.flags |= SDL_COPY_ADD;
break;
case SDL_BLENDMODE_MOD:
surface->map->info.flags |= SDL_COPY_MOD;
break;
case SDL_BLENDMODE_MUL:
surface->map->info.flags |= SDL_COPY_MUL;
break;
default:
status = SDL_Unsupported();
break;
}
if (surface->map->info.flags != flags) {
SDL_InvalidateMap(surface->map);
}
return status;
}
int SDL_GetSurfaceBlendMode(SDL_Surface *surface, SDL_BlendMode *blendMode)
{
if (!surface) {
return SDL_InvalidParamError("surface");
}
if (!blendMode) {
return 0;
}
switch (surface->map->info.flags & (SDL_COPY_BLEND | SDL_COPY_ADD | SDL_COPY_MOD | SDL_COPY_MUL)) {
case SDL_COPY_BLEND:
*blendMode = SDL_BLENDMODE_BLEND;
break;
case SDL_COPY_ADD:
*blendMode = SDL_BLENDMODE_ADD;
break;
case SDL_COPY_MOD:
*blendMode = SDL_BLENDMODE_MOD;
break;
case SDL_COPY_MUL:
*blendMode = SDL_BLENDMODE_MUL;
break;
default:
*blendMode = SDL_BLENDMODE_NONE;
break;
}
return 0;
}
SDL_bool SDL_SetClipRect(SDL_Surface *surface, const SDL_Rect *rect)
{
SDL_Rect full_rect;
/* Don't do anything if there's no surface to act on */
if (!surface) {
return SDL_FALSE;
}
/* Set up the full surface rectangle */
full_rect.x = 0;
full_rect.y = 0;
full_rect.w = surface->w;
full_rect.h = surface->h;
/* Set the clipping rectangle */
if (!rect) {
surface->clip_rect = full_rect;
return SDL_TRUE;
}
return SDL_IntersectRect(rect, &full_rect, &surface->clip_rect);
}
void SDL_GetClipRect(SDL_Surface *surface, SDL_Rect *rect)
{
if (surface && rect) {
*rect = surface->clip_rect;
}
}
/*
* Set up a blit between two surfaces -- split into three parts:
* The upper part, SDL_UpperBlit(), performs clipping and rectangle
* verification. The lower part is a pointer to a low level
* accelerated blitting function.
*
* These parts are separated out and each used internally by this
* library in the optimimum places. They are exported so that if
* you know exactly what you are doing, you can optimize your code
* by calling the one(s) you need.
*/
int SDL_LowerBlit(SDL_Surface *src, SDL_Rect *srcrect,
SDL_Surface *dst, SDL_Rect *dstrect)
{
/* Check to make sure the blit mapping is valid */
if ((src->map->dst != dst) ||
(dst->format->palette &&
src->map->dst_palette_version != dst->format->palette->version) ||
(src->format->palette &&
src->map->src_palette_version != src->format->palette->version)) {
if (SDL_MapSurface(src, dst) < 0) {
return -1;
}
/* just here for debugging */
/* printf */
/* ("src = 0x%08X src->flags = %08X src->map->info.flags = %08x\ndst = 0x%08X dst->flags = %08X dst->map->info.flags = %08X\nsrc->map->blit = 0x%08x\n", */
/* src, dst->flags, src->map->info.flags, dst, dst->flags, */
/* dst->map->info.flags, src->map->blit); */
}
return src->map->blit(src, srcrect, dst, dstrect);
}
int SDL_UpperBlit(SDL_Surface *src, const SDL_Rect *srcrect,
SDL_Surface *dst, SDL_Rect *dstrect)
{
SDL_Rect r_src, r_dst;
/* Make sure the surfaces aren't locked */
if (!src) {
return SDL_InvalidParamError("src");
} else if (!dst) {
return SDL_InvalidParamError("dst");
} else if (src->locked || dst->locked) {
return SDL_SetError("Surfaces must not be locked during blit");
}
/* Full src surface */
r_src.x = 0;
r_src.y = 0;
r_src.w = src->w;
r_src.h = src->h;
if (dstrect) {
r_dst.x = dstrect->x;
r_dst.y = dstrect->y;
} else {
r_dst.x = 0;
r_dst.y = 0;
}
/* clip the source rectangle to the source surface */
if (srcrect) {
SDL_Rect tmp;
if (SDL_IntersectRect(srcrect, &r_src, &tmp) == SDL_FALSE) {
goto end;
}
/* Shift dstrect, if srcrect origin has changed */
r_dst.x += tmp.x - srcrect->x;
r_dst.y += tmp.y - srcrect->y;
/* Update srcrect */
r_src = tmp;
}
/* There're no dstrect.w/h parameters. It's the same as srcrect */
r_dst.w = r_src.w;
r_dst.h = r_src.h;
/* clip the destination rectangle against the clip rectangle */
{
SDL_Rect tmp;
if (SDL_IntersectRect(&r_dst, &dst->clip_rect, &tmp) == SDL_FALSE) {
goto end;
}
/* Shift srcrect, if dstrect has changed */
r_src.x += tmp.x - r_dst.x;
r_src.y += tmp.y - r_dst.y;
r_src.w = tmp.w;
r_src.h = tmp.h;
/* Update dstrect */
r_dst = tmp;
}
/* Switch back to a fast blit if we were previously stretching */
if (src->map->info.flags & SDL_COPY_NEAREST) {
src->map->info.flags &= ~SDL_COPY_NEAREST;
SDL_InvalidateMap(src->map);
}
if (r_dst.w > 0 && r_dst.h > 0) {
if (dstrect) { /* update output parameter */
*dstrect = r_dst;
}
return SDL_LowerBlit(src, &r_src, dst, &r_dst);
}
end:
if (dstrect) { /* update output parameter */
dstrect->w = dstrect->h = 0;
}
return 0;
}
int SDL_UpperBlitScaled(SDL_Surface *src, const SDL_Rect *srcrect,
SDL_Surface *dst, SDL_Rect *dstrect)
{
return SDL_PrivateUpperBlitScaled(src, srcrect, dst, dstrect, SDL_ScaleModeNearest);
}
int SDL_PrivateUpperBlitScaled(SDL_Surface *src, const SDL_Rect *srcrect,
SDL_Surface *dst, SDL_Rect *dstrect, SDL_ScaleMode scaleMode)
{
double src_x0, src_y0, src_x1, src_y1;
double dst_x0, dst_y0, dst_x1, dst_y1;
SDL_Rect final_src, final_dst;
double scaling_w, scaling_h;
int src_w, src_h;
int dst_w, dst_h;
/* Make sure the surfaces aren't locked */
if (!src || !dst) {
return SDL_InvalidParamError("SDL_UpperBlitScaled(): src/dst");
}
if (src->locked || dst->locked) {
return SDL_SetError("Surfaces must not be locked during blit");
}
if (!srcrect) {
src_w = src->w;
src_h = src->h;
} else {
src_w = srcrect->w;
src_h = srcrect->h;
}
if (!dstrect) {
dst_w = dst->w;
dst_h = dst->h;
} else {
dst_w = dstrect->w;
dst_h = dstrect->h;
}
if (dst_w == src_w && dst_h == src_h) {
/* No scaling, defer to regular blit */
return SDL_BlitSurface(src, srcrect, dst, dstrect);
}
scaling_w = (double)dst_w / src_w;
scaling_h = (double)dst_h / src_h;
if (!dstrect) {
dst_x0 = 0;
dst_y0 = 0;
dst_x1 = dst_w;
dst_y1 = dst_h;
} else {
dst_x0 = dstrect->x;
dst_y0 = dstrect->y;
dst_x1 = dst_x0 + dst_w;
dst_y1 = dst_y0 + dst_h;
}
if (!srcrect) {
src_x0 = 0;
src_y0 = 0;
src_x1 = src_w;
src_y1 = src_h;
} else {
src_x0 = srcrect->x;
src_y0 = srcrect->y;
src_x1 = src_x0 + src_w;
src_y1 = src_y0 + src_h;
/* Clip source rectangle to the source surface */
if (src_x0 < 0) {
dst_x0 -= src_x0 * scaling_w;
src_x0 = 0;
}
if (src_x1 > src->w) {
dst_x1 -= (src_x1 - src->w) * scaling_w;
src_x1 = src->w;
}
if (src_y0 < 0) {
dst_y0 -= src_y0 * scaling_h;
src_y0 = 0;
}
if (src_y1 > src->h) {
dst_y1 -= (src_y1 - src->h) * scaling_h;
src_y1 = src->h;
}
}
/* Clip destination rectangle to the clip rectangle */
/* Translate to clip space for easier calculations */
dst_x0 -= dst->clip_rect.x;
dst_x1 -= dst->clip_rect.x;
dst_y0 -= dst->clip_rect.y;
dst_y1 -= dst->clip_rect.y;
if (dst_x0 < 0) {
src_x0 -= dst_x0 / scaling_w;
dst_x0 = 0;
}
if (dst_x1 > dst->clip_rect.w) {
src_x1 -= (dst_x1 - dst->clip_rect.w) / scaling_w;
dst_x1 = dst->clip_rect.w;
}
if (dst_y0 < 0) {
src_y0 -= dst_y0 / scaling_h;
dst_y0 = 0;
}
if (dst_y1 > dst->clip_rect.h) {
src_y1 -= (dst_y1 - dst->clip_rect.h) / scaling_h;
dst_y1 = dst->clip_rect.h;
}
/* Translate back to surface coordinates */
dst_x0 += dst->clip_rect.x;
dst_x1 += dst->clip_rect.x;
dst_y0 += dst->clip_rect.y;
dst_y1 += dst->clip_rect.y;
final_src.x = (int)SDL_round(src_x0);
final_src.y = (int)SDL_round(src_y0);
final_src.w = (int)SDL_round(src_x1 - src_x0);
final_src.h = (int)SDL_round(src_y1 - src_y0);
final_dst.x = (int)SDL_round(dst_x0);
final_dst.y = (int)SDL_round(dst_y0);
final_dst.w = (int)SDL_round(dst_x1 - dst_x0);
final_dst.h = (int)SDL_round(dst_y1 - dst_y0);
/* Clip again */
{
SDL_Rect tmp;
tmp.x = 0;
tmp.y = 0;
tmp.w = src->w;
tmp.h = src->h;
SDL_IntersectRect(&tmp, &final_src, &final_src);
}
/* Clip again */
SDL_IntersectRect(&dst->clip_rect, &final_dst, &final_dst);
if (dstrect) {
*dstrect = final_dst;
}
if (final_dst.w == 0 || final_dst.h == 0 ||
final_src.w <= 0 || final_src.h <= 0) {
/* No-op. */
return 0;
}
return SDL_PrivateLowerBlitScaled(src, &final_src, dst, &final_dst, scaleMode);
}
/**
* This is a semi-private blit function and it performs low-level surface
* scaled blitting only.
*/
int SDL_LowerBlitScaled(SDL_Surface *src, SDL_Rect *srcrect,
SDL_Surface *dst, SDL_Rect *dstrect)
{
return SDL_PrivateLowerBlitScaled(src, srcrect, dst, dstrect, SDL_ScaleModeNearest);
}
int SDL_PrivateLowerBlitScaled(SDL_Surface *src, SDL_Rect *srcrect,
SDL_Surface *dst, SDL_Rect *dstrect, SDL_ScaleMode scaleMode)
{
static const Uint32 complex_copy_flags = (SDL_COPY_MODULATE_COLOR | SDL_COPY_MODULATE_ALPHA |
SDL_COPY_BLEND | SDL_COPY_ADD | SDL_COPY_MOD | SDL_COPY_MUL |
SDL_COPY_COLORKEY);
if (srcrect->w > SDL_MAX_UINT16 || srcrect->h > SDL_MAX_UINT16 ||
dstrect->w > SDL_MAX_UINT16 || dstrect->h > SDL_MAX_UINT16) {
return SDL_SetError("Size too large for scaling");
}
if (!(src->map->info.flags & SDL_COPY_NEAREST)) {
src->map->info.flags |= SDL_COPY_NEAREST;
SDL_InvalidateMap(src->map);
}
if (scaleMode == SDL_ScaleModeNearest) {
if (!(src->map->info.flags & complex_copy_flags) &&
src->format->format == dst->format->format &&
!SDL_ISPIXELFORMAT_INDEXED(src->format->format)) {
return SDL_SoftStretch(src, srcrect, dst, dstrect);
} else {
return SDL_LowerBlit(src, srcrect, dst, dstrect);
}
} else {
if (!(src->map->info.flags & complex_copy_flags) &&
src->format->format == dst->format->format &&
!SDL_ISPIXELFORMAT_INDEXED(src->format->format) &&
src->format->BytesPerPixel == 4 &&
src->format->format != SDL_PIXELFORMAT_ARGB2101010) {
/* fast path */
return SDL_SoftStretchLinear(src, srcrect, dst, dstrect);
} else {
/* Use intermediate surface(s) */
SDL_Surface *tmp1 = NULL;
int ret;
SDL_Rect srcrect2;
int is_complex_copy_flags = (src->map->info.flags & complex_copy_flags);
Uint32 flags;
Uint8 r, g, b;
0