Parcourir la source

Added SDL_BlitSurfaceTiledWithScale()

Sam Lantinga il y a 9 mois
Parent
commit
9e55ee9391

+ 27 - 0
include/SDL3/SDL_surface.h

@@ -1097,6 +1097,33 @@ extern SDL_DECLSPEC int SDLCALL SDL_BlitSurfaceUncheckedScaled(SDL_Surface *src,
  */
 extern SDL_DECLSPEC int SDLCALL SDL_BlitSurfaceTiled(SDL_Surface *src, const SDL_Rect *srcrect, SDL_Surface *dst, const SDL_Rect *dstrect);
 
+/**
+ * Perform a scaled and tiled blit to a destination surface, which may be of a different
+ * format.
+ *
+ * The pixels in `srcrect` will be scaled and repeated as many times as needed to completely fill `dstrect`.
+ *
+ * \param src the SDL_Surface structure to be copied from.
+ * \param srcrect the SDL_Rect structure representing the rectangle to be
+ *                copied, or NULL to copy the entire surface.
+ * \param scale the scale used to transform srcrect into the destination rectangle, e.g. a 32x32 texture with a scale of 2 would fill 64x64 tiles.
+ * \param scaleMode scale algorithm to be used.
+ * \param dst the SDL_Surface structure that is the blit target.
+ * \param dstrect the SDL_Rect structure representing the target rectangle in
+ *                the destination surface, or NULL to fill the entire surface.
+ * \returns 0 on success or a negative error code on failure; call
+ *          SDL_GetError() for more information.
+ *
+ * \threadsafety The same destination surface should not be used from two
+ *               threads at once. It is safe to use the same source surface
+ *               from multiple threads.
+ *
+ * \since This function is available since SDL 3.0.0.
+ *
+ * \sa SDL_BlitSurface
+ */
+extern SDL_DECLSPEC int SDLCALL SDL_BlitSurfaceTiledWithScale(SDL_Surface *src, const SDL_Rect *srcrect, float scale, SDL_ScaleMode scaleMode, SDL_Surface *dst, const SDL_Rect *dstrect);
+
 /**
  * Map an RGB triple to an opaque pixel value for a surface.
  *

+ 1 - 0
src/dynapi/SDL_dynapi.sym

@@ -26,6 +26,7 @@ SDL3_0.0.0 {
     SDL_BlitSurface;
     SDL_BlitSurfaceScaled;
     SDL_BlitSurfaceTiled;
+    SDL_BlitSurfaceTiledWithScale;
     SDL_BlitSurfaceUnchecked;
     SDL_BlitSurfaceUncheckedScaled;
     SDL_BroadcastCondition;

+ 1 - 0
src/dynapi/SDL_dynapi_overrides.h

@@ -51,6 +51,7 @@
 #define SDL_BlitSurface SDL_BlitSurface_REAL
 #define SDL_BlitSurfaceScaled SDL_BlitSurfaceScaled_REAL
 #define SDL_BlitSurfaceTiled SDL_BlitSurfaceTiled_REAL
+#define SDL_BlitSurfaceTiledWithScale SDL_BlitSurfaceTiledWithScale_REAL
 #define SDL_BlitSurfaceUnchecked SDL_BlitSurfaceUnchecked_REAL
 #define SDL_BlitSurfaceUncheckedScaled SDL_BlitSurfaceUncheckedScaled_REAL
 #define SDL_BroadcastCondition SDL_BroadcastCondition_REAL

+ 1 - 0
src/dynapi/SDL_dynapi_procs.h

@@ -71,6 +71,7 @@ SDL_DYNAPI_PROC(int,SDL_BindAudioStreams,(SDL_AudioDeviceID a, SDL_AudioStream *
 SDL_DYNAPI_PROC(int,SDL_BlitSurface,(SDL_Surface *a, const SDL_Rect *b, SDL_Surface *c, SDL_Rect *d),(a,b,c,d),return)
 SDL_DYNAPI_PROC(int,SDL_BlitSurfaceScaled,(SDL_Surface *a, const SDL_Rect *b, SDL_Surface *c, SDL_Rect *d, SDL_ScaleMode e),(a,b,c,d,e),return)
 SDL_DYNAPI_PROC(int,SDL_BlitSurfaceTiled,(SDL_Surface *a, const SDL_Rect *b, SDL_Surface *c, const SDL_Rect *d),(a,b,c,d),return)
+SDL_DYNAPI_PROC(int,SDL_BlitSurfaceTiledWithScale,(SDL_Surface *a, const SDL_Rect *b, float c, SDL_ScaleMode d, SDL_Surface *e, const SDL_Rect *f),(a,b,c,d,e,f),return)
 SDL_DYNAPI_PROC(int,SDL_BlitSurfaceUnchecked,(SDL_Surface *a, const SDL_Rect *b, SDL_Surface *c, const SDL_Rect *d),(a,b,c,d),return)
 SDL_DYNAPI_PROC(int,SDL_BlitSurfaceUncheckedScaled,(SDL_Surface *a, const SDL_Rect *b, SDL_Surface *c, const SDL_Rect *d, SDL_ScaleMode e),(a,b,c,d,e),return)
 SDL_DYNAPI_PROC(int,SDL_BroadcastCondition,(SDL_Condition *a),(a),return)

+ 113 - 0
src/video/SDL_surface.c

@@ -1341,6 +1341,119 @@ int SDL_BlitSurfaceTiled(SDL_Surface *src, const SDL_Rect *srcrect, SDL_Surface
     return 0;
 }
 
+int SDL_BlitSurfaceTiledWithScale(SDL_Surface *src, const SDL_Rect *srcrect, float scale, SDL_ScaleMode scaleMode, SDL_Surface *dst, const SDL_Rect *dstrect)
+{
+    SDL_Rect r_src, r_dst;
+
+    /* Make sure the surfaces aren't locked */
+    if (!SDL_SurfaceValid(src)) {
+        return SDL_InvalidParamError("src");
+    } else if (!SDL_SurfaceValid(dst)) {
+        return SDL_InvalidParamError("dst");
+    } else if ((src->flags & SDL_SURFACE_LOCKED) || (dst->flags & SDL_SURFACE_LOCKED)) {
+        return SDL_SetError("Surfaces must not be locked during blit");
+    }
+
+    if (scale <= 0.0f) {
+        return SDL_InvalidParamError("scale");
+    }
+
+    /* 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;
+        r_dst.w = dstrect->w;
+        r_dst.h = dstrect->h;
+    } else {
+        r_dst.x = 0;
+        r_dst.y = 0;
+        r_dst.w = dst->w;
+        r_dst.h = dst->h;
+    }
+
+    /* clip the source rectangle to the source surface */
+    if (srcrect) {
+        if (SDL_GetRectIntersection(srcrect, &r_src, &r_src) == SDL_FALSE) {
+            return 0;
+        }
+
+        /* For tiling we don't adjust the destination rectangle */
+    }
+
+    /* clip the destination rectangle against the clip rectangle */
+    {
+        if (SDL_GetRectIntersection(&r_dst, &dst->internal->clip_rect, &r_dst) == SDL_FALSE) {
+            return 0;
+        }
+
+        /* For tiling we don't adjust the source rectangle */
+    }
+
+    /* Switch back to a fast blit if we were previously stretching */
+    if (src->internal->map.info.flags & SDL_COPY_NEAREST) {
+        src->internal->map.info.flags &= ~SDL_COPY_NEAREST;
+        SDL_InvalidateMap(&src->internal->map);
+    }
+
+    int tile_width = (int)(r_src.w * scale);
+    int tile_height = (int)(r_src.h * scale);
+    int rows = r_dst.h / tile_height;
+    int cols = r_dst.w / tile_width;
+    int remaining_dst_w = (r_dst.w - cols * tile_width);
+    int remaining_dst_h = (r_dst.h - rows * tile_height);
+    int remaining_src_w = (int)(remaining_dst_w / scale);
+    int remaining_src_h = (int)(remaining_dst_h / scale);
+    SDL_Rect curr_src, curr_dst;
+
+    SDL_copyp(&curr_src, &r_src);
+    curr_dst.y = r_dst.y;
+    curr_dst.w = tile_width;
+    curr_dst.h = tile_height;
+    for (int y = 0; y < rows; ++y) {
+        curr_dst.x = r_dst.x;
+        for (int x = 0; x < cols; ++x) {
+            if (SDL_BlitSurfaceUncheckedScaled(src, &curr_src, dst, &curr_dst, scaleMode) < 0) {
+                return -1;
+            }
+            curr_dst.x += curr_dst.w;
+        }
+        if (remaining_dst_w > 0) {
+            curr_src.w = remaining_src_w;
+            curr_dst.w = remaining_dst_w;
+            if (SDL_BlitSurfaceUncheckedScaled(src, &curr_src, dst, &curr_dst, scaleMode) < 0) {
+                return -1;
+            }
+            curr_src.w = r_src.w;
+            curr_dst.w = tile_width;
+        }
+        curr_dst.y += curr_dst.h;
+    }
+    if (remaining_dst_h > 0) {
+        curr_src.h = remaining_src_h;
+        curr_dst.h = remaining_dst_h;
+        curr_dst.x = r_dst.x;
+        for (int x = 0; x < cols; ++x) {
+            if (SDL_BlitSurfaceUncheckedScaled(src, &curr_src, dst, &curr_dst, scaleMode) < 0) {
+                return -1;
+            }
+            curr_dst.x += curr_dst.w;
+        }
+        if (remaining_dst_w > 0) {
+            curr_src.w = remaining_src_w;
+            curr_dst.w = remaining_dst_w;
+            if (SDL_BlitSurfaceUncheckedScaled(src, &curr_src, dst, &curr_dst, scaleMode) < 0) {
+                return -1;
+            }
+        }
+    }
+    return 0;
+}
+
 /*
  * Lock a surface to directly access the pixels
  */

+ 32 - 7
test/testautomation_surface.c

@@ -366,6 +366,8 @@ static int surface_testSaveLoadBitmap(void *arg)
 static int surface_testBlitTiled(void *arg)
 {
     SDL_Surface *face = NULL;
+    SDL_Surface *testSurface2x = NULL;
+    SDL_Surface *referenceSurface2x = NULL;
     int ret = 0;
 
     /* Create sample surface */
@@ -375,17 +377,40 @@ static int surface_testBlitTiled(void *arg)
         return TEST_ABORTED;
     }
 
-    ret = SDL_BlitSurfaceTiled(face, NULL, testSurface, NULL);
-    SDLTest_AssertCheck(ret == 0, "Verify result from SDL_BlitSurfaceTiled expected: 0, got: %i", ret);
+    /* Tiled blit - 1.0 scale */
+    {
+        ret = SDL_BlitSurfaceTiled(face, NULL, testSurface, NULL);
+        SDLTest_AssertCheck(ret == 0, "Verify result from SDL_BlitSurfaceTiled expected: 0, got: %i", ret);
 
-    /* See if it's the same */
-    SDL_DestroySurface(referenceSurface);
-    referenceSurface = SDLTest_ImageBlitTiled();
-    ret = SDLTest_CompareSurfaces(testSurface, referenceSurface, 0);
-    SDLTest_AssertCheck(ret == 0, "Validate result from SDLTest_CompareSurfaces, expected: 0, got: %i", ret);
+        /* See if it's the same */
+        SDL_DestroySurface(referenceSurface);
+        referenceSurface = SDLTest_ImageBlitTiled();
+        ret = SDLTest_CompareSurfaces(testSurface, referenceSurface, 0);
+        SDLTest_AssertCheck(ret == 0, "Validate result from SDLTest_CompareSurfaces, expected: 0, got: %i", ret);
+    }
+
+    /* Tiled blit - 2.0 scale */
+    {
+        testSurface2x = SDL_CreateSurface(testSurface->w * 2, testSurface->h * 2, testSurface->format);
+        SDLTest_AssertCheck(testSurface != NULL, "Check that testSurface2x is not NULL");
+        ret = SDL_FillSurfaceRect(testSurface2x, NULL, SDL_MapSurfaceRGBA(testSurface2x, 0, 0, 0, 255));
+        SDLTest_AssertCheck(ret == 0, "Validate result from SDL_FillSurfaceRect, expected: 0, got: %i", ret);
+
+        ret = SDL_BlitSurfaceTiledWithScale(face, NULL, 2.0f, SDL_SCALEMODE_NEAREST, testSurface2x, NULL);
+        SDLTest_AssertCheck(ret == 0, "Validate results from call to SDL_RenderTextureTiled, expected: 0, got: %i", ret);
+
+        /* See if it's the same */
+        referenceSurface2x = SDL_CreateSurface(referenceSurface->w * 2, referenceSurface->h * 2, referenceSurface->format);
+        SDL_BlitSurfaceScaled(referenceSurface, NULL, referenceSurface2x, NULL, SDL_SCALEMODE_NEAREST);
+        SDLTest_AssertCheck(ret == 0, "Validate results from call to SDL_BlitSurfaceScaled, expected: 0, got: %i", ret);
+        ret = SDLTest_CompareSurfaces(testSurface2x, referenceSurface2x, 0);
+        SDLTest_AssertCheck(ret == 0, "Validate result from SDLTest_CompareSurfaces, expected: 0, got: %i", ret);
+    }
 
     /* Clean up. */
     SDL_DestroySurface(face);
+    SDL_DestroySurface(testSurface2x);
+    SDL_DestroySurface(referenceSurface2x);
 
     return TEST_COMPLETED;
 }