Vincent Povirk : gdiplus: Read drawn bitmaps in one step instead of using getpixel.

Alexandre Julliard julliard at winehq.org
Thu Mar 10 11:30:25 CST 2011


Module: wine
Branch: master
Commit: 870fdaf6cef2fa194c17b2cfb74b14cd92cd8a81
URL:    http://source.winehq.org/git/wine.git/?a=commit;h=870fdaf6cef2fa194c17b2cfb74b14cd92cd8a81

Author: Vincent Povirk <vincent at codeweavers.com>
Date:   Thu Mar  3 14:18:47 2011 -0600

gdiplus: Read drawn bitmaps in one step instead of using getpixel.

---

 dlls/gdiplus/graphics.c |  200 ++++++++++++++++++++++++++++++++++++++---------
 1 files changed, 163 insertions(+), 37 deletions(-)

diff --git a/dlls/gdiplus/graphics.c b/dlls/gdiplus/graphics.c
index 09eaa01..7b51c61 100644
--- a/dlls/gdiplus/graphics.c
+++ b/dlls/gdiplus/graphics.c
@@ -414,6 +414,95 @@ static void apply_image_attributes(const GpImageAttributes *attributes, LPBYTE d
     }
 }
 
+/* Given a bitmap and its source rectangle, find the smallest rectangle in the
+ * bitmap that contains all the pixels we may need to draw it. */
+static void get_bitmap_sample_size(InterpolationMode interpolation, WrapMode wrap,
+    GpBitmap* bitmap, REAL srcx, REAL srcy, REAL srcwidth, REAL srcheight,
+    GpRect *rect)
+{
+    INT left, top, right, bottom;
+
+    switch (interpolation)
+    {
+    case InterpolationModeHighQualityBilinear:
+    case InterpolationModeHighQualityBicubic:
+    /* FIXME: Include a greater range for the prefilter? */
+    case InterpolationModeBicubic:
+    case InterpolationModeBilinear:
+        left = (INT)(floorf(srcx));
+        top = (INT)(floorf(srcy));
+        right = (INT)(ceilf(srcx+srcwidth));
+        bottom = (INT)(ceilf(srcy+srcheight));
+        break;
+    case InterpolationModeNearestNeighbor:
+    default:
+        left = roundr(srcx);
+        top = roundr(srcy);
+        right = roundr(srcx+srcwidth);
+        bottom = roundr(srcy+srcheight);
+        break;
+    }
+
+    if (wrap == WrapModeClamp)
+    {
+        if (left < 0)
+            left = 0;
+        if (top < 0)
+            top = 0;
+        if (right >= bitmap->width)
+            right = bitmap->width-1;
+        if (bottom >= bitmap->height)
+            bottom = bitmap->height-1;
+    }
+    else
+    {
+        /* In some cases we can make the rectangle smaller here, but the logic
+         * is hard to get right, and tiling suggests we're likely to use the
+         * entire source image. */
+        if (left < 0 || right >= bitmap->width)
+        {
+            left = 0;
+            right = bitmap->width-1;
+        }
+
+        if (top < 0 || bottom >= bitmap->height)
+        {
+            top = 0;
+            bottom = bitmap->height-1;
+        }
+    }
+
+    rect->X = left;
+    rect->Y = top;
+    rect->Width = right - left + 1;
+    rect->Height = bottom - top + 1;
+}
+
+static ARGB sample_bitmap_pixel(GDIPCONST GpRect *src_rect, LPBYTE bits, UINT width,
+    UINT height, INT x, INT y, GDIPCONST GpImageAttributes *attributes)
+{
+    static int fixme[4];
+
+    switch (attributes->wrap)
+    {
+    default:
+        if (!fixme[attributes->wrap]++)
+            FIXME("not implemented for wrap mode %i\n", attributes->wrap);
+    case WrapModeClamp:
+        if (x < 0 || y < 0 || x >= width || y >= height)
+            return attributes->outside_color;
+        break;
+    }
+
+    if (x < src_rect->X || y < src_rect->Y || x >= src_rect->X + src_rect->Width || y >= src_rect->Y + src_rect->Height)
+    {
+        ERR("out of range pixel requested\n");
+        return 0xffcd0084;
+    }
+
+    return ((DWORD*)(bits))[(x - src_rect->X) + (y - src_rect->Y) * src_rect->Width];
+}
+
 static void brush_fill_path(GpGraphics *graphics, GpBrush* brush)
 {
     switch (brush->bt)
@@ -2309,16 +2398,25 @@ GpStatus WINGDIPAPI GdipDrawImagePointsRect(GpGraphics *graphics, GpImage *image
 
         if (use_software)
         {
-            RECT src_area, dst_area;
-            int i, x, y, stride;
+            RECT dst_area;
+            GpRect src_area;
+            int i, x, y, src_stride, dst_stride;
             GpMatrix *dst_to_src;
             REAL m11, m12, m21, m22, mdx, mdy;
-            LPBYTE data;
+            LPBYTE src_data, dst_data;
+            BitmapData lockeddata;
+            InterpolationMode interpolation = graphics->interpolation;
+            GpPointF dst_to_src_points[3] = {{0.0, 0.0}, {1.0, 0.0}, {0.0, 1.0}};
+            REAL x_dx, x_dy, y_dx, y_dy;
+            static const GpImageAttributes defaultImageAttributes = {WrapModeClamp, 0, FALSE};
+
+            if (!imageAttributes)
+                imageAttributes = &defaultImageAttributes;
 
-            src_area.left = srcx*dx;
-            src_area.top = srcy*dy;
-            src_area.right = (srcx+srcwidth)*dx;
-            src_area.bottom = (srcy+srcheight)*dy;
+            srcx = srcx * dx;
+            srcy = srcy * dy;
+            srcwidth = srcwidth * dx;
+            srcheight = srcheight * dy;
 
             dst_area.left = dst_area.right = pti[0].x;
             dst_area.top = dst_area.bottom = pti[0].y;
@@ -2347,65 +2445,93 @@ GpStatus WINGDIPAPI GdipDrawImagePointsRect(GpGraphics *graphics, GpImage *image
                 return stat;
             }
 
-            data = GdipAlloc(sizeof(ARGB) * (dst_area.right - dst_area.left) * (dst_area.bottom - dst_area.top));
-            if (!data)
+            dst_data = GdipAlloc(sizeof(ARGB) * (dst_area.right - dst_area.left) * (dst_area.bottom - dst_area.top));
+            if (!dst_data)
+            {
+                GdipDeleteMatrix(dst_to_src);
+                return OutOfMemory;
+            }
+
+            dst_stride = sizeof(ARGB) * (dst_area.right - dst_area.left);
+
+            get_bitmap_sample_size(interpolation, imageAttributes->wrap,
+                bitmap, srcx, srcy, srcwidth, srcheight, &src_area);
+
+            src_data = GdipAlloc(sizeof(ARGB) * src_area.Width * src_area.Height);
+            if (!src_data)
             {
+                GdipFree(dst_data);
                 GdipDeleteMatrix(dst_to_src);
                 return OutOfMemory;
             }
+            src_stride = sizeof(ARGB) * src_area.Width;
+
+            /* Read the bits we need from the source bitmap into an ARGB buffer. */
+            lockeddata.Width = src_area.Width;
+            lockeddata.Height = src_area.Height;
+            lockeddata.Stride = src_stride;
+            lockeddata.PixelFormat = PixelFormat32bppARGB;
+            lockeddata.Scan0 = src_data;
 
-            stride = sizeof(ARGB) * (dst_area.right - dst_area.left);
+            stat = GdipBitmapLockBits(bitmap, &src_area, ImageLockModeRead|ImageLockModeUserInputBuf,
+                PixelFormat32bppARGB, &lockeddata);
+
+            if (stat == Ok)
+                stat = GdipBitmapUnlockBits(bitmap, &lockeddata);
 
-            if (imageAttributes &&
-                (imageAttributes->wrap != WrapModeClamp ||
-                 imageAttributes->outside_color != 0x00000000 ||
-                 imageAttributes->clamp))
+            if (stat != Ok)
             {
-                static int fixme;
-                if (!fixme++)
-                    FIXME("Image wrap mode not implemented\n");
+                if (src_data != dst_data)
+                    GdipFree(src_data);
+                GdipFree(dst_data);
+                GdipDeleteMatrix(dst_to_src);
+                return OutOfMemory;
             }
 
+            /* Transform the bits as needed to the destination. */
+            GdipTransformMatrixPoints(dst_to_src, dst_to_src_points, 3);
+
+            x_dx = dst_to_src_points[1].X - dst_to_src_points[0].X;
+            x_dy = dst_to_src_points[1].Y - dst_to_src_points[0].Y;
+            y_dx = dst_to_src_points[2].X - dst_to_src_points[0].X;
+            y_dy = dst_to_src_points[2].Y - dst_to_src_points[0].Y;
+
             for (x=dst_area.left; x<dst_area.right; x++)
             {
                 for (y=dst_area.top; y<dst_area.bottom; y++)
                 {
                     GpPointF src_pointf;
-                    int src_x, src_y;
-                    ARGB *src_color;
-
-                    src_pointf.X = x;
-                    src_pointf.Y = y;
+                    INT src_x, src_y;
+                    ARGB *dst_color;
 
-                    GdipTransformMatrixPoints(dst_to_src, &src_pointf, 1);
+                    src_pointf.X = dst_to_src_points[0].X + x * x_dx + y * y_dx;
+                    src_pointf.Y = dst_to_src_points[0].Y + x * x_dy + y * y_dy;
 
                     src_x = roundr(src_pointf.X);
                     src_y = roundr(src_pointf.Y);
 
-                    src_color = (ARGB*)(data + stride * (y - dst_area.top) + sizeof(ARGB) * (x - dst_area.left));
+                    dst_color = (ARGB*)(dst_data + dst_stride * (y - dst_area.top) + sizeof(ARGB) * (x - dst_area.left));
 
-                    if (src_x < src_area.left || src_x >= src_area.right ||
-                        src_y < src_area.top || src_y >= src_area.bottom)
-                        *src_color = 0;
+                    if (src_pointf.X >= srcx && src_pointf.X < srcx + srcwidth && src_pointf.Y >= srcy && src_pointf.Y < srcy+srcheight)
+                        *dst_color = sample_bitmap_pixel(&src_area, src_data, bitmap->width, bitmap->height, src_x, src_y, imageAttributes);
                     else
-                        GdipBitmapGetPixel(bitmap, src_x, src_y, src_color);
+                        *dst_color = 0;
                 }
             }
 
             GdipDeleteMatrix(dst_to_src);
 
-            if (imageAttributes)
-            {
-                apply_image_attributes(imageAttributes, data,
-                    dst_area.right - dst_area.left,
-                    dst_area.bottom - dst_area.top,
-                    stride, ColorAdjustTypeBitmap);
-            }
+            GdipFree(src_data);
+
+            apply_image_attributes(imageAttributes, dst_data,
+                dst_area.right - dst_area.left,
+                dst_area.bottom - dst_area.top,
+                dst_stride, ColorAdjustTypeBitmap);
 
             stat = alpha_blend_pixels(graphics, dst_area.left, dst_area.top,
-                data, dst_area.right - dst_area.left, dst_area.bottom - dst_area.top, stride);
+                dst_data, dst_area.right - dst_area.left, dst_area.bottom - dst_area.top, dst_stride);
 
-            GdipFree(data);
+            GdipFree(dst_data);
 
             return stat;
         }




More information about the wine-cvs mailing list