diff src/render/SDL_render.c @ 5159:307ccc9c135e

Made it possible to create a texture of any format, even if not supported by the renderer. This allows me to reduce the set of formats supported by the renderers to the most optimal set, for a nice speed boost.
author Sam Lantinga <slouken@libsdl.org>
date Thu, 03 Feb 2011 00:19:40 -0800
parents fb424691cfc7
children 657543cc92f9
line wrap: on
line diff
--- a/src/render/SDL_render.c	Wed Feb 02 22:55:12 2011 -0800
+++ b/src/render/SDL_render.c	Thu Feb 03 00:19:40 2011 -0800
@@ -152,6 +152,34 @@
     return 0;
 }
 
+static SDL_bool
+IsSupportedFormat(SDL_Renderer * renderer, Uint32 format)
+{
+    Uint32 i;
+
+    for (i = 0; i < renderer->info.num_texture_formats; ++i) {
+        if (renderer->info.texture_formats[i] == format) {
+            return SDL_TRUE;
+        }
+    }
+    return SDL_FALSE;
+}
+
+static Uint32
+GetClosestSupportedFormat(SDL_Renderer * renderer, Uint32 format)
+{
+    Uint32 i;
+    SDL_bool hasAlpha = SDL_ISPIXELFORMAT_ALPHA(format);
+
+    /* We just want to match the first format that has the same channels */
+    for (i = 0; i < renderer->info.num_texture_formats; ++i) {
+        if (SDL_ISPIXELFORMAT_ALPHA(renderer->info.texture_formats[i]) == hasAlpha) {
+            return renderer->info.texture_formats[i];
+        }
+    }
+    return renderer->info.texture_formats[0];
+}
+
 SDL_Texture *
 SDL_CreateTexture(SDL_Renderer * renderer, Uint32 format, int access, int w, int h)
 {
@@ -159,14 +187,18 @@
 
     CHECK_RENDERER_MAGIC(renderer, NULL);
 
+    if (SDL_ISPIXELFORMAT_INDEXED(format)) {
+        SDL_SetError("Palettized textures are not supported");
+        return NULL;
+    }
     if (w <= 0 || h <= 0) {
         SDL_SetError("Texture dimensions can't be 0");
-        return 0;
+        return NULL;
     }
     texture = (SDL_Texture *) SDL_calloc(1, sizeof(*texture));
     if (!texture) {
         SDL_OutOfMemory();
-        return 0;
+        return NULL;
     }
     texture->magic = &texture_magic;
     texture->format = format;
@@ -184,9 +216,35 @@
     }
     renderer->textures = texture;
 
-    if (renderer->CreateTexture(renderer, texture) < 0) {
-        SDL_DestroyTexture(texture);
-        return 0;
+    if (IsSupportedFormat(renderer, format)) {
+        if (renderer->CreateTexture(renderer, texture) < 0) {
+            SDL_DestroyTexture(texture);
+            return 0;
+        }
+    } else {
+        texture->native = SDL_CreateTexture(renderer,
+                                GetClosestSupportedFormat(renderer, format),
+                                access, w, h);
+        if (!texture->native) {
+            SDL_DestroyTexture(texture);
+            return NULL;
+        }
+
+        if (SDL_ISPIXELFORMAT_FOURCC(texture->format)) {
+            texture->yuv = SDL_SW_CreateYUVTexture(format, w, h);
+            if (!texture->yuv) {
+                SDL_DestroyTexture(texture);
+                return NULL;
+            }
+        } else if (access == SDL_TEXTUREACCESS_STREAMING) {
+            /* The pitch is 4 byte aligned */
+            texture->pitch = (((w * SDL_BYTESPERPIXEL(format)) + 3) & ~3);
+            texture->pixels = SDL_malloc(texture->pitch * h);
+            if (!texture->pixels) {
+                SDL_DestroyTexture(texture);
+                return NULL;
+            }
+        }
     }
     return texture;
 }
@@ -501,21 +559,6 @@
 }
 
 int
-SDL_QueryTexturePixels(SDL_Texture * texture, void **pixels, int *pitch)
-{
-    SDL_Renderer *renderer;
-
-    CHECK_TEXTURE_MAGIC(texture, -1);
-
-    renderer = texture->renderer;
-    if (!renderer->QueryTexturePixels) {
-        SDL_Unsupported();
-        return -1;
-    }
-    return renderer->QueryTexturePixels(renderer, texture, pixels, pitch);
-}
-
-int
 SDL_SetTextureColorMod(SDL_Texture * texture, Uint8 r, Uint8 g, Uint8 b)
 {
     SDL_Renderer *renderer;
@@ -531,7 +574,9 @@
     texture->r = r;
     texture->g = g;
     texture->b = b;
-    if (renderer->SetTextureColorMod) {
+    if (texture->native) {
+        return SDL_SetTextureColorMod(texture->native, r, g, b);
+    } else if (renderer->SetTextureColorMod) {
         return renderer->SetTextureColorMod(renderer, texture);
     } else {
         return 0;
@@ -573,7 +618,9 @@
         texture->modMode &= ~SDL_TEXTUREMODULATE_ALPHA;
     }
     texture->a = alpha;
-    if (renderer->SetTextureAlphaMod) {
+    if (texture->native) {
+        return SDL_SetTextureAlphaMod(texture->native, alpha);
+    } else if (renderer->SetTextureAlphaMod) {
         return renderer->SetTextureAlphaMod(renderer, texture);
     } else {
         return 0;
@@ -600,7 +647,9 @@
 
     renderer = texture->renderer;
     texture->blendMode = blendMode;
-    if (renderer->SetTextureBlendMode) {
+    if (texture->native) {
+        return SDL_SetTextureBlendMode(texture, blendMode);
+    } else if (renderer->SetTextureBlendMode) {
         return renderer->SetTextureBlendMode(renderer, texture);
     } else {
         return 0;
@@ -618,6 +667,91 @@
     return 0;
 }
 
+static int
+SDL_UpdateTextureYUV(SDL_Texture * texture, const SDL_Rect * rect,
+                     const void *pixels, int pitch)
+{
+    SDL_Texture *native = texture->native;
+    SDL_Rect full_rect;
+
+    if (SDL_SW_UpdateYUVTexture(texture->yuv, rect, pixels, pitch) < 0) {
+        return -1;
+    }
+
+    full_rect.x = 0;
+    full_rect.y = 0;
+    full_rect.w = texture->w;
+    full_rect.h = texture->h;
+    rect = &full_rect;
+
+    if (texture->access == SDL_TEXTUREACCESS_STREAMING) {
+        /* We can lock the texture and copy to it */
+        void *native_pixels;
+        int native_pitch;
+
+        if (SDL_LockTexture(native, rect, &native_pixels, &native_pitch) < 0) {
+            return -1;
+        }
+        SDL_SW_CopyYUVToRGB(texture->yuv, rect, native->format,
+                            rect->w, rect->h, native_pixels, native_pitch);
+        SDL_UnlockTexture(native);
+    } else {
+        /* Use a temporary buffer for updating */
+        void *temp_pixels;
+        int temp_pitch;
+
+        temp_pitch = (((rect->w * SDL_BYTESPERPIXEL(native->format)) + 3) & ~3);
+        temp_pixels = SDL_malloc(rect->h * temp_pitch);
+        if (!temp_pixels) {
+            SDL_OutOfMemory();
+            return -1;
+        }
+        SDL_SW_CopyYUVToRGB(texture->yuv, rect, native->format,
+                            rect->w, rect->h, temp_pixels, temp_pitch);
+        SDL_UpdateTexture(native, rect, temp_pixels, temp_pitch);
+        SDL_free(temp_pixels);
+    }
+    return 0;
+}
+
+static int
+SDL_UpdateTextureNative(SDL_Texture * texture, const SDL_Rect * rect,
+                        const void *pixels, int pitch)
+{
+    SDL_Texture *native = texture->native;
+
+    if (texture->access == SDL_TEXTUREACCESS_STREAMING) {
+        /* We can lock the texture and copy to it */
+        void *native_pixels;
+        int native_pitch;
+
+        if (SDL_LockTexture(native, rect, &native_pixels, &native_pitch) < 0) {
+            return -1;
+        }
+        SDL_ConvertPixels(rect->w, rect->h,
+                          texture->format, pixels, pitch,
+                          native->format, native_pixels, native_pitch);
+        SDL_UnlockTexture(native);
+    } else {
+        /* Use a temporary buffer for updating */
+        void *temp_pixels;
+        int temp_pitch;
+
+        temp_pitch = (((rect->w * SDL_BYTESPERPIXEL(native->format)) + 3) & ~3);
+        temp_pixels = SDL_malloc(rect->h * temp_pitch);
+        if (!temp_pixels) {
+            SDL_OutOfMemory();
+            return -1;
+        }
+        SDL_ConvertPixels(rect->w, rect->h,
+                          texture->format, pixels, pitch,
+                          native->format, temp_pixels, temp_pitch);
+        SDL_UpdateTexture(native, rect, temp_pixels, temp_pitch);
+        SDL_free(temp_pixels);
+    }
+    return 0;
+}
+
 int
 SDL_UpdateTexture(SDL_Texture * texture, const SDL_Rect * rect,
                   const void *pixels, int pitch)
@@ -627,11 +761,6 @@
 
     CHECK_TEXTURE_MAGIC(texture, -1);
 
-    renderer = texture->renderer;
-    if (!renderer->UpdateTexture) {
-        SDL_Unsupported();
-        return -1;
-    }
     if (!rect) {
         full_rect.x = 0;
         full_rect.y = 0;
@@ -639,11 +768,38 @@
         full_rect.h = texture->h;
         rect = &full_rect;
     }
-    return renderer->UpdateTexture(renderer, texture, rect, pixels, pitch);
+
+    if (texture->yuv) {
+        return SDL_UpdateTextureYUV(texture, rect, pixels, pitch);
+    } else if (texture->native) {
+        return SDL_UpdateTextureNative(texture, rect, pixels, pitch);
+    } else {
+        renderer = texture->renderer;
+        return renderer->UpdateTexture(renderer, texture, rect, pixels, pitch);
+    }
+}
+
+static int
+SDL_LockTextureYUV(SDL_Texture * texture, const SDL_Rect * rect,
+                   void **pixels, int *pitch)
+{
+    return SDL_SW_LockYUVTexture(texture->yuv, rect, pixels, pitch);
+}
+
+static int
+SDL_LockTextureNative(SDL_Texture * texture, const SDL_Rect * rect,
+                      void **pixels, int *pitch)
+{
+    texture->locked_rect = *rect;
+    *pixels = (void *) ((Uint8 *) texture->pixels +
+                        rect->y * texture->pitch +
+                        rect->x * SDL_BYTESPERPIXEL(texture->format));
+    *pitch = texture->pitch;
+    return 0;
 }
 
 int
-SDL_LockTexture(SDL_Texture * texture, const SDL_Rect * rect, int markDirty,
+SDL_LockTexture(SDL_Texture * texture, const SDL_Rect * rect,
                 void **pixels, int *pitch)
 {
     SDL_Renderer *renderer;
@@ -655,11 +811,7 @@
         SDL_SetError("SDL_LockTexture(): texture must be streaming");
         return -1;
     }
-    renderer = texture->renderer;
-    if (!renderer->LockTexture) {
-        SDL_Unsupported();
-        return -1;
-    }
+
     if (!rect) {
         full_rect.x = 0;
         full_rect.y = 0;
@@ -667,8 +819,57 @@
         full_rect.h = texture->h;
         rect = &full_rect;
     }
-    return renderer->LockTexture(renderer, texture, rect, markDirty, pixels,
-                                 pitch);
+
+    if (texture->yuv) {
+        return SDL_LockTextureYUV(texture, rect, pixels, pitch);
+    } else if (texture->native) {
+        return SDL_LockTextureNative(texture, rect, pixels, pitch);
+    } else {
+        renderer = texture->renderer;
+        return renderer->LockTexture(renderer, texture, rect, pixels, pitch);
+    }
+}
+
+static void
+SDL_UnlockTextureYUV(SDL_Texture * texture)
+{
+    SDL_Texture *native = texture->native;
+    void *native_pixels;
+    int native_pitch;
+    SDL_Rect rect;
+
+    rect.x = 0;
+    rect.y = 0;
+    rect.w = texture->w;
+    rect.h = texture->h;
+
+    if (SDL_LockTexture(native, &rect, &native_pixels, &native_pitch) < 0) {
+        return;
+    }
+    SDL_SW_CopyYUVToRGB(texture->yuv, &rect, native->format,
+                        rect.w, rect.h, native_pixels, native_pitch);
+    SDL_UnlockTexture(native);
+}
+
+void
+SDL_UnlockTextureNative(SDL_Texture * texture)
+{
+    SDL_Texture *native = texture->native;
+    void *native_pixels;
+    int native_pitch;
+    const SDL_Rect *rect = &texture->locked_rect;
+    const void* pixels = (void *) ((Uint8 *) texture->pixels +
+                        rect->y * texture->pitch +
+                        rect->x * SDL_BYTESPERPIXEL(texture->format));
+    int pitch = texture->pitch;
+
+    if (SDL_LockTexture(native, rect, &native_pixels, &native_pitch) < 0) {
+        return;
+    }
+    SDL_ConvertPixels(rect->w, rect->h,
+                      texture->format, pixels, pitch,
+                      native->format, native_pixels, native_pitch);
+    SDL_UnlockTexture(native);
 }
 
 void
@@ -681,29 +882,14 @@
     if (texture->access != SDL_TEXTUREACCESS_STREAMING) {
         return;
     }
-    renderer = texture->renderer;
-    if (!renderer->UnlockTexture) {
-        return;
+    if (texture->yuv) {
+        SDL_UnlockTextureYUV(texture);
+    } else if (texture->native) {
+        SDL_UnlockTextureNative(texture);
+    } else {
+        renderer = texture->renderer;
+        renderer->UnlockTexture(renderer, texture);
     }
-    renderer->UnlockTexture(renderer, texture);
-}
-
-void
-SDL_DirtyTexture(SDL_Texture * texture, int numrects,
-                 const SDL_Rect * rects)
-{
-    SDL_Renderer *renderer;
-
-    CHECK_TEXTURE_MAGIC(texture, );
-
-    if (texture->access != SDL_TEXTUREACCESS_STREAMING) {
-        return;
-    }
-    renderer = texture->renderer;
-    if (!renderer->DirtyTexture) {
-        return;
-    }
-    renderer->DirtyTexture(renderer, texture, numrects, rects);
 }
 
 int
@@ -979,6 +1165,10 @@
         }
     }
 
+    if (texture->native) {
+        texture = texture->native;
+    }
+
     return renderer->RenderCopy(renderer, texture, &real_srcrect,
                                 &real_dstrect);
 }
@@ -1087,6 +1277,16 @@
         renderer->textures = texture->next;
     }
 
+    if (texture->native) {
+        SDL_DestroyTexture(texture->native);
+    }
+    if (texture->yuv) {
+        SDL_SW_DestroyYUVTexture(texture->yuv);
+    }
+    if (texture->pixels) {
+        SDL_free(texture->pixels);
+    }
+
     renderer->DestroyTexture(renderer, texture);
     SDL_free(texture);
 }