[PATCH (try2)] gdiplus: Avoid unnecessary pre-multiplied alpha conversions in GdipDrawImagePointsRect

Vincent Povirk madewokherd at gmail.com
Wed Apr 1 22:19:59 CDT 2015


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.

On Wed, Apr 1, 2015 at 12:53 PM, Vincent Povirk <madewokherd at gmail.com> wrote:
> I think we also need resample_bitmap_pixel to be set up to handle
> PARGB. Nearest neighbor interpolation should work OK as is, but
> bilinear interpolation uses blend_colors which only works correctly
> for ARGB. (The implementation of blend_colors that we had before
> 9c579023f0750539bdf7815f8545c4620540387a should actually be correct
> for PARGB.)
>
> On Wed, Apr 1, 2015 at 11:38 AM, Andrew Eikum <aeikum at codeweavers.com> wrote:
>> Not all of the operations we perform in the DrawImage family need to
>> be post-multiplied. We can avoid doing a pre-to-post-multiplied alpha
>> conversion by detecting whether we need post-multiplied ahead of time.
>> Avoiding this conversion saves considerable time, about 3/1000s in my
>> particular test case.
>>
>> ---
>>
>> try2: Fix return value from color_over_fgpremult
>>
>>  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..89c108e 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 (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-devel mailing list