[PATCH] gdiplus: Avoid unnecessary pre-multiplied alpha conversions in GdipDrawImagePointsRect
Andrew Eikum
aeikum at codeweavers.com
Fri Apr 3 14:26:35 CDT 2015
We can avoid doing a pre-multiplied alpha conversion by detecting
whether we need to perform any graphics operations ahead of time.
Avoiding this conversion saves considerable time, about 3/1000 seconds
in my particular test case.
---
On Wed, Apr 01, 2015 at 10:19:59PM -0500, Vincent Povirk wrote:
> It may make sense for now to say that if do_resampling is TRUE, we
> should still do the conversion, removing the need to modify
> resample_bitmap_pixel.
>
> If we do make resample_bitmap_pixel aware of pixel format, we also
> need to modify sample_bitmap_pixel so that it converts outside_color.
>
> I'd forgotten how complicated drawing images was. It was nice while it
> lasted.
Good catch. I think it makes more sense to just not do the optimization
than turn each of these image modification functions into a fractal of
supporting specific formats. So this version will only do the
optimization if we're just passing the image straight through
unmodified.
dlls/gdiplus/gdiplus_private.h | 20 +++++++++++++
dlls/gdiplus/graphics.c | 66 ++++++++++++++++++++++++++++--------------
2 files changed, 65 insertions(+), 21 deletions(-)
diff --git a/dlls/gdiplus/gdiplus_private.h b/dlls/gdiplus/gdiplus_private.h
index 065f5ed..57b3de3 100644
--- a/dlls/gdiplus/gdiplus_private.h
+++ b/dlls/gdiplus/gdiplus_private.h
@@ -121,6 +121,26 @@ static inline ARGB color_over(ARGB bg, ARGB fg)
return (a<<24)|(r<<16)|(g<<8)|b;
}
+/* fg is premult, bg and return value are not */
+static inline ARGB color_over_fgpremult(ARGB bg, ARGB fg)
+{
+ BYTE b, g, r, a;
+ BYTE bg_alpha, fg_alpha;
+
+ fg_alpha = (fg>>24)&0xff;
+
+ if (fg_alpha == 0) return bg;
+
+ bg_alpha = (((bg>>24)&0xff) * (0xff-fg_alpha)) / 0xff;
+
+ a = bg_alpha + fg_alpha;
+ b = ((bg&0xff)*bg_alpha + (fg&0xff)*0xff)/a;
+ g = (((bg>>8)&0xff)*bg_alpha + ((fg>>8)&0xff)*0xff)/a;
+ r = (((bg>>16)&0xff)*bg_alpha + ((fg>>16)&0xff)*0xff)/a;
+
+ return (a<<24)|(r<<16)|(g<<8)|b;
+}
+
extern const char *debugstr_rectf(const RectF* rc) DECLSPEC_HIDDEN;
extern const char *debugstr_pointf(const PointF* pt) DECLSPEC_HIDDEN;
diff --git a/dlls/gdiplus/graphics.c b/dlls/gdiplus/graphics.c
index 580390f..92bc194 100644
--- a/dlls/gdiplus/graphics.c
+++ b/dlls/gdiplus/graphics.c
@@ -354,9 +354,9 @@ static GpStatus get_clip_hrgn(GpGraphics *graphics, HRGN *hrgn)
return GdipGetRegionHRgn(graphics->clip, NULL, hrgn);
}
-/* Draw non-premultiplied ARGB data to the given graphics object */
+/* Draw ARGB data to the given graphics object */
static GpStatus alpha_blend_bmp_pixels(GpGraphics *graphics, INT dst_x, INT dst_y,
- const BYTE *src, INT src_width, INT src_height, INT src_stride)
+ const BYTE *src, INT src_width, INT src_height, INT src_stride, const PixelFormat fmt)
{
GpBitmap *dst_bitmap = (GpBitmap*)graphics->image;
INT x, y;
@@ -372,7 +372,10 @@ static GpStatus alpha_blend_bmp_pixels(GpGraphics *graphics, INT dst_x, INT dst_
continue;
GdipBitmapGetPixel(dst_bitmap, x+dst_x, y+dst_y, &dst_color);
- GdipBitmapSetPixel(dst_bitmap, x+dst_x, y+dst_y, color_over(dst_color, src_color));
+ if (fmt & PixelFormatPAlpha)
+ GdipBitmapSetPixel(dst_bitmap, x+dst_x, y+dst_y, color_over_fgpremult(dst_color, src_color));
+ else
+ GdipBitmapSetPixel(dst_bitmap, x+dst_x, y+dst_y, color_over(dst_color, src_color));
}
}
@@ -380,7 +383,7 @@ static GpStatus alpha_blend_bmp_pixels(GpGraphics *graphics, INT dst_x, INT dst_
}
static GpStatus alpha_blend_hdc_pixels(GpGraphics *graphics, INT dst_x, INT dst_y,
- const BYTE *src, INT src_width, INT src_height, INT src_stride)
+ const BYTE *src, INT src_width, INT src_height, INT src_stride, PixelFormat fmt)
{
HDC hdc;
HBITMAP hbitmap;
@@ -404,7 +407,8 @@ static GpStatus alpha_blend_hdc_pixels(GpGraphics *graphics, INT dst_x, INT dst_
hbitmap = CreateDIBSection(hdc, (BITMAPINFO*)&bih, DIB_RGB_COLORS,
(void**)&temp_bits, NULL, 0);
- if (GetDeviceCaps(graphics->hdc, SHADEBLENDCAPS) == SB_NONE)
+ if (GetDeviceCaps(graphics->hdc, SHADEBLENDCAPS) == SB_NONE ||
+ fmt & PixelFormatPAlpha)
memcpy(temp_bits, src, src_width * src_height * 4);
else
convert_32bppARGB_to_32bppPARGB(src_width, src_height, temp_bits,
@@ -420,7 +424,7 @@ static GpStatus alpha_blend_hdc_pixels(GpGraphics *graphics, INT dst_x, INT dst_
}
static GpStatus alpha_blend_pixels_hrgn(GpGraphics *graphics, INT dst_x, INT dst_y,
- const BYTE *src, INT src_width, INT src_height, INT src_stride, HRGN hregion)
+ const BYTE *src, INT src_width, INT src_height, INT src_stride, HRGN hregion, PixelFormat fmt)
{
GpStatus stat=Ok;
@@ -470,7 +474,7 @@ static GpStatus alpha_blend_pixels_hrgn(GpGraphics *graphics, INT dst_x, INT dst
stat = alpha_blend_bmp_pixels(graphics, rects[i].left, rects[i].top,
&src[(rects[i].left - dst_x) * 4 + (rects[i].top - dst_y) * src_stride],
rects[i].right - rects[i].left, rects[i].bottom - rects[i].top,
- src_stride);
+ src_stride, fmt);
}
GdipFree(rgndata);
@@ -503,7 +507,7 @@ static GpStatus alpha_blend_pixels_hrgn(GpGraphics *graphics, INT dst_x, INT dst
ExtSelectClipRgn(graphics->hdc, hregion, RGN_AND);
stat = alpha_blend_hdc_pixels(graphics, dst_x, dst_y, src, src_width,
- src_height, src_stride);
+ src_height, src_stride, fmt);
RestoreDC(graphics->hdc, save);
@@ -514,9 +518,9 @@ static GpStatus alpha_blend_pixels_hrgn(GpGraphics *graphics, INT dst_x, INT dst
}
static GpStatus alpha_blend_pixels(GpGraphics *graphics, INT dst_x, INT dst_y,
- const BYTE *src, INT src_width, INT src_height, INT src_stride)
+ const BYTE *src, INT src_width, INT src_height, INT src_stride, PixelFormat fmt)
{
- return alpha_blend_pixels_hrgn(graphics, dst_x, dst_y, src, src_width, src_height, src_stride, NULL);
+ return alpha_blend_pixels_hrgn(graphics, dst_x, dst_y, src, src_width, src_height, src_stride, NULL, fmt);
}
static ARGB blend_colors(ARGB start, ARGB end, REAL position)
@@ -656,8 +660,9 @@ static BOOL color_is_gray(ARGB color)
return (r == g) && (g == b);
}
-static void apply_image_attributes(const GpImageAttributes *attributes, LPBYTE data,
- UINT width, UINT height, INT stride, ColorAdjustType type)
+/* returns preferred pixel format for the applied attributes */
+static PixelFormat apply_image_attributes(const GpImageAttributes *attributes, LPBYTE data,
+ UINT width, UINT height, INT stride, ColorAdjustType type, PixelFormat fmt)
{
UINT x, y;
INT i;
@@ -669,6 +674,9 @@ static void apply_image_attributes(const GpImageAttributes *attributes, LPBYTE d
BYTE min_blue, min_green, min_red;
BYTE max_blue, max_green, max_red;
+ if (!data || fmt != PixelFormat32bppARGB)
+ return PixelFormat32bppARGB;
+
if (attributes->colorkeys[type].enabled)
key = &attributes->colorkeys[type];
else
@@ -702,6 +710,9 @@ static void apply_image_attributes(const GpImageAttributes *attributes, LPBYTE d
{
const struct color_remap_table *table;
+ if (!data || fmt != PixelFormat32bppARGB)
+ return PixelFormat32bppARGB;
+
if (attributes->colorremaptables[type].enabled)
table = &attributes->colorremaptables[type];
else
@@ -731,6 +742,9 @@ static void apply_image_attributes(const GpImageAttributes *attributes, LPBYTE d
int gray_matrix[5][5];
BOOL identity;
+ if (!data || fmt != PixelFormat32bppARGB)
+ return PixelFormat32bppARGB;
+
if (attributes->colormatrices[type].enabled)
colormatrices = &attributes->colormatrices[type];
else
@@ -769,6 +783,9 @@ static void apply_image_attributes(const GpImageAttributes *attributes, LPBYTE d
{
REAL gamma;
+ if (!data || fmt != PixelFormat32bppARGB)
+ return PixelFormat32bppARGB;
+
if (attributes->gamma_enabled[type])
gamma = attributes->gamma[type];
else
@@ -793,6 +810,8 @@ static void apply_image_attributes(const GpImageAttributes *attributes, LPBYTE d
*src_color = (*src_color & 0xff000000) | (red << 16) | (green << 8) | blue;
}
}
+
+ return fmt;
}
/* Given a bitmap and its source rectangle, find the smallest rectangle in the
@@ -1237,7 +1256,7 @@ static GpStatus brush_fill_pixels(GpGraphics *graphics, GpBrush *brush,
if (stat == Ok)
apply_image_attributes(fill->imageattributes, fill->bitmap_bits,
bitmap->width, bitmap->height,
- src_stride, ColorAdjustTypeBitmap);
+ src_stride, ColorAdjustTypeBitmap, lockeddata.PixelFormat);
if (stat != Ok)
{
@@ -2990,15 +3009,18 @@ GpStatus WINGDIPAPI GdipDrawImagePointsRect(GpGraphics *graphics, GpImage *image
return OutOfMemory;
src_stride = sizeof(ARGB) * src_area.Width;
- /* Read the bits we need from the source bitmap into an ARGB buffer. */
+ /* Read the bits we need from the source bitmap into a compatible buffer. */
lockeddata.Width = src_area.Width;
lockeddata.Height = src_area.Height;
lockeddata.Stride = src_stride;
- lockeddata.PixelFormat = PixelFormat32bppARGB;
lockeddata.Scan0 = src_data;
+ if (!do_resampling && bitmap->format == PixelFormat32bppPARGB)
+ lockeddata.PixelFormat = apply_image_attributes(imageAttributes, NULL, 0, 0, 0, ColorAdjustTypeBitmap, bitmap->format);
+ else
+ lockeddata.PixelFormat = PixelFormat32bppARGB;
stat = GdipBitmapLockBits(bitmap, &src_area, ImageLockModeRead|ImageLockModeUserInputBuf,
- PixelFormat32bppARGB, &lockeddata);
+ lockeddata.PixelFormat, &lockeddata);
if (stat == Ok)
stat = GdipBitmapUnlockBits(bitmap, &lockeddata);
@@ -3011,7 +3033,7 @@ GpStatus WINGDIPAPI GdipDrawImagePointsRect(GpGraphics *graphics, GpImage *image
apply_image_attributes(imageAttributes, src_data,
src_area.Width, src_area.Height,
- src_stride, ColorAdjustTypeBitmap);
+ src_stride, ColorAdjustTypeBitmap, lockeddata.PixelFormat);
if (do_resampling)
{
@@ -3059,7 +3081,8 @@ GpStatus WINGDIPAPI GdipDrawImagePointsRect(GpGraphics *graphics, GpImage *image
}
stat = alpha_blend_pixels(graphics, dst_area.left, dst_area.top,
- dst_data, dst_area.right - dst_area.left, dst_area.bottom - dst_area.top, dst_stride);
+ dst_data, dst_area.right - dst_area.left, dst_area.bottom - dst_area.top, dst_stride,
+ lockeddata.PixelFormat);
GdipFree(src_data);
@@ -4009,7 +4032,8 @@ static GpStatus SOFTWARE_GdipFillRegion(GpGraphics *graphics, GpBrush *brush,
if (stat == Ok)
stat = alpha_blend_pixels_hrgn(graphics, gp_bound_rect.X,
gp_bound_rect.Y, (BYTE*)pixel_data, gp_bound_rect.Width,
- gp_bound_rect.Height, gp_bound_rect.Width * 4, hregion);
+ gp_bound_rect.Height, gp_bound_rect.Width * 4, hregion,
+ PixelFormat32bppARGB);
GdipFree(pixel_data);
}
@@ -5811,7 +5835,7 @@ GpStatus WINGDIPAPI GdipReleaseDC(GpGraphics *graphics, HDC hdc)
/* Write the changed pixels to the real target. */
alpha_blend_pixels(graphics, 0, 0, graphics->temp_bits,
graphics->temp_hbitmap_width, graphics->temp_hbitmap_height,
- graphics->temp_hbitmap_width * 4);
+ graphics->temp_hbitmap_width * 4, PixelFormat32bppARGB);
/* Clean up. */
DeleteDC(graphics->temp_hdc);
@@ -6366,7 +6390,7 @@ static GpStatus SOFTWARE_GdipDrawDriverString(GpGraphics *graphics, GDIPCONST UI
/* draw the result */
stat = alpha_blend_pixels(graphics, min_x, min_y, pixel_data, pixel_area.Width,
- pixel_area.Height, pixel_data_stride);
+ pixel_area.Height, pixel_data_stride, PixelFormat32bppARGB);
GdipFree(pixel_data);
--
2.3.5
More information about the wine-patches
mailing list