diff --git a/patches/gdiplus-Performance-Improvements/0001-gdiplus-Change-the-order-of-x-y-loops-in-the-scaler.patch b/patches/gdiplus-Performance-Improvements/0001-gdiplus-Change-the-order-of-x-y-loops-in-the-scaler.patch new file mode 100644 index 00000000..a2b9a139 --- /dev/null +++ b/patches/gdiplus-Performance-Improvements/0001-gdiplus-Change-the-order-of-x-y-loops-in-the-scaler.patch @@ -0,0 +1,29 @@ +From f7c317dac3581075e75168ce50c4523973422384 Mon Sep 17 00:00:00 2001 +From: Dmitry Timoshkov +Date: Sun, 5 Mar 2017 12:55:51 +0800 +Subject: gdiplus: Change the order of x/y loops in the scaler. + +This improves performance by about 5%. +--- + dlls/gdiplus/graphics.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/dlls/gdiplus/graphics.c b/dlls/gdiplus/graphics.c +index 142b68115e..02d699b00b 100644 +--- a/dlls/gdiplus/graphics.c ++++ b/dlls/gdiplus/graphics.c +@@ -3043,9 +3043,9 @@ GpStatus WINGDIPAPI GdipDrawImagePointsRect(GpGraphics *graphics, GpImage *image + 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 +Date: Sun, 5 Mar 2017 13:07:43 +0800 +Subject: gdiplus: Change multiplications by additions in the x/y scaler loops. + +This should imrove performance when floating point math will be replaced +by fixed point calculations. +--- + dlls/gdiplus/graphics.c | 18 ++++++++++++++++-- + 1 file changed, 16 insertions(+), 2 deletions(-) + +diff --git a/dlls/gdiplus/graphics.c b/dlls/gdiplus/graphics.c +index 02d699b00b..ef26887d14 100644 +--- a/dlls/gdiplus/graphics.c ++++ b/dlls/gdiplus/graphics.c +@@ -3026,6 +3026,8 @@ GpStatus WINGDIPAPI GdipDrawImagePointsRect(GpGraphics *graphics, GpImage *image + + if (do_resampling) + { ++ REAL delta_xx, delta_xy, delta_yx, delta_yy; ++ + /* Transform the bits as needed to the destination. */ + dst_data = dst_dyn_data = heap_alloc_zero(sizeof(ARGB) * (dst_area.right - dst_area.left) * (dst_area.bottom - dst_area.top)); + if (!dst_data) +@@ -3043,15 +3045,21 @@ GpStatus WINGDIPAPI GdipDrawImagePointsRect(GpGraphics *graphics, GpImage *image + 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; + ++ delta_yy = dst_area.top * y_dy; ++ delta_yx = dst_area.top * y_dx; ++ + for (y=dst_area.top; y +Date: Sun, 5 Mar 2017 13:18:21 +0800 +Subject: gdiplus: Remove ceilf/floorf calls from bilinear scaler. (v2) + +This improves performance by about 55%. +--- + dlls/gdiplus/graphics.c | 19 ++++++++++++------- + 1 file changed, 12 insertions(+), 7 deletions(-) + +diff --git a/dlls/gdiplus/graphics.c b/dlls/gdiplus/graphics.c +index ef26887d14..58cfebb923 100644 +--- a/dlls/gdiplus/graphics.c ++++ b/dlls/gdiplus/graphics.c +@@ -526,7 +526,7 @@ static ARGB blend_colors(ARGB start, ARGB end, REAL position) + INT start_a, end_a, final_a; + INT pos; + +- pos = gdip_round(position * 0xff); ++ pos = (INT)(position * 255.0f + 0.5f); + + start_a = ((start >> 24) & 0xff) * (pos ^ 0xff); + end_a = ((end >> 24) & 0xff) * pos; +@@ -928,6 +928,11 @@ static ARGB sample_bitmap_pixel(GDIPCONST GpRect *src_rect, LPBYTE bits, UINT wi + return ((DWORD*)(bits))[(x - src_rect->X) + (y - src_rect->Y) * src_rect->Width]; + } + ++static FORCEINLINE int positive_ceilf(float f) ++{ ++ return f - (int)f > 0.0f ? f + 1.0f : f; ++} ++ + static ARGB resample_bitmap_pixel(GDIPCONST GpRect *src_rect, LPBYTE bits, UINT width, + UINT height, GpPointF *point, GDIPCONST GpImageAttributes *attributes, + InterpolationMode interpolation, PixelOffsetMode offset_mode) +@@ -948,12 +953,12 @@ static ARGB resample_bitmap_pixel(GDIPCONST GpRect *src_rect, LPBYTE bits, UINT + ARGB top, bottom; + float x_offset; + +- leftxf = floorf(point->X); +- leftx = (INT)leftxf; +- rightx = (INT)ceilf(point->X); +- topyf = floorf(point->Y); +- topy = (INT)topyf; +- bottomy = (INT)ceilf(point->Y); ++ leftx = (INT)point->X; ++ leftxf = (REAL)leftx; ++ rightx = positive_ceilf(point->X); ++ topy = (INT)point->Y; ++ topyf = (REAL)topy; ++ bottomy = positive_ceilf(point->Y); + + if (leftx == rightx && topy == bottomy) + return sample_bitmap_pixel(src_rect, bits, width, height, +-- +2.11.0 + diff --git a/patches/gdiplus-Performance-Improvements/0004-gdiplus-Prefer-using-pre-multiplied-ARGB-data-in-the.patch b/patches/gdiplus-Performance-Improvements/0004-gdiplus-Prefer-using-pre-multiplied-ARGB-data-in-the.patch new file mode 100644 index 00000000..2ede68a1 --- /dev/null +++ b/patches/gdiplus-Performance-Improvements/0004-gdiplus-Prefer-using-pre-multiplied-ARGB-data-in-the.patch @@ -0,0 +1,140 @@ +From 37b9499d8da295cd8819d85e9a563629ef13f22e Mon Sep 17 00:00:00 2001 +From: Dmitry Timoshkov +Date: Sun, 5 Mar 2017 14:34:51 +0800 +Subject: gdiplus: Prefer using pre-multiplied ARGB data in the scaler. + +This further improves performance by about 20%. +--- + dlls/gdiplus/graphics.c | 94 +++++++++++++++++++++++++++++++++++++++++++++++-- + 1 file changed, 91 insertions(+), 3 deletions(-) + +diff --git a/dlls/gdiplus/graphics.c b/dlls/gdiplus/graphics.c +index 58cfebb923..bdee2e8318 100644 +--- a/dlls/gdiplus/graphics.c ++++ b/dlls/gdiplus/graphics.c +@@ -521,6 +521,17 @@ static GpStatus alpha_blend_pixels(GpGraphics *graphics, INT dst_x, INT dst_y, + return alpha_blend_pixels_hrgn(graphics, dst_x, dst_y, src, src_width, src_height, src_stride, NULL, fmt); + } + ++/* NOTE: start and end pixels must be in pre-multiplied ARGB format */ ++static FORCEINLINE ARGB blend_colors_premult(ARGB start, ARGB end, REAL position) ++{ ++ UINT pos = position * 255.0f + 0.5f; ++ return ++ (((((start >> 24) ) << 8) + (((end >> 24) ) - ((start >> 24) )) * pos) >> 8) << 24 | ++ (((((start >> 16) & 0xff) << 8) + (((end >> 16) & 0xff) - ((start >> 16) & 0xff)) * pos) >> 8) << 16 | ++ (((((start >> 8) & 0xff) << 8) + (((end >> 8) & 0xff) - ((start >> 8) & 0xff)) * pos) >> 8) << 8 | ++ (((((start ) & 0xff) << 8) + (((end ) & 0xff) - ((start ) & 0xff)) * pos) >> 8); ++} ++ + static ARGB blend_colors(ARGB start, ARGB end, REAL position) + { + INT start_a, end_a, final_a; +@@ -1002,6 +1013,75 @@ static ARGB resample_bitmap_pixel(GDIPCONST GpRect *src_rect, LPBYTE bits, UINT + } + } + ++static ARGB resample_bitmap_pixel_premult(GDIPCONST GpRect *src_rect, LPBYTE bits, UINT width, ++ UINT height, GpPointF *point, GDIPCONST GpImageAttributes *attributes, ++ InterpolationMode interpolation, PixelOffsetMode offset_mode) ++{ ++ static int fixme; ++ ++ switch (interpolation) ++ { ++ default: ++ if (!fixme++) ++ FIXME("Unimplemented interpolation %i\n", interpolation); ++ /* fall-through */ ++ case InterpolationModeBilinear: ++ { ++ REAL leftxf, topyf; ++ INT leftx, rightx, topy, bottomy; ++ ARGB topleft, topright, bottomleft, bottomright; ++ ARGB top, bottom; ++ float x_offset; ++ ++ leftx = (INT)point->X; ++ leftxf = (REAL)leftx; ++ rightx = positive_ceilf(point->X); ++ topy = (INT)point->Y; ++ topyf = (REAL)topy; ++ bottomy = positive_ceilf(point->Y); ++ ++ if (leftx == rightx && topy == bottomy) ++ return sample_bitmap_pixel(src_rect, bits, width, height, ++ leftx, topy, attributes); ++ ++ topleft = sample_bitmap_pixel(src_rect, bits, width, height, ++ leftx, topy, attributes); ++ topright = sample_bitmap_pixel(src_rect, bits, width, height, ++ rightx, topy, attributes); ++ bottomleft = sample_bitmap_pixel(src_rect, bits, width, height, ++ leftx, bottomy, attributes); ++ bottomright = sample_bitmap_pixel(src_rect, bits, width, height, ++ rightx, bottomy, attributes); ++ ++ x_offset = point->X - leftxf; ++ top = blend_colors_premult(topleft, topright, x_offset); ++ bottom = blend_colors_premult(bottomleft, bottomright, x_offset); ++ ++ return blend_colors_premult(top, bottom, point->Y - topyf); ++ } ++ case InterpolationModeNearestNeighbor: ++ { ++ FLOAT pixel_offset; ++ switch (offset_mode) ++ { ++ default: ++ case PixelOffsetModeNone: ++ case PixelOffsetModeHighSpeed: ++ pixel_offset = 0.5; ++ break; ++ ++ case PixelOffsetModeHalf: ++ case PixelOffsetModeHighQuality: ++ pixel_offset = 0.0; ++ break; ++ } ++ return sample_bitmap_pixel(src_rect, bits, width, height, ++ floorf(point->X + pixel_offset), point->Y + pixel_offset, attributes); ++ } ++ ++ } ++} ++ + static REAL intersect_line_scanline(const GpPointF *p1, const GpPointF *p2, REAL y) + { + return (p1->X - p2->X) * (p2->Y - y) / (p2->Y - p1->Y) + p2->X; +@@ -3010,8 +3090,10 @@ GpStatus WINGDIPAPI GdipDrawImagePointsRect(GpGraphics *graphics, GpImage *image + lockeddata.Scan0 = src_data; + if (!do_resampling && bitmap->format == PixelFormat32bppPARGB) + lockeddata.PixelFormat = apply_image_attributes(imageAttributes, NULL, 0, 0, 0, ColorAdjustTypeBitmap, bitmap->format); +- else ++ else if (imageAttributes != &defaultImageAttributes) + lockeddata.PixelFormat = PixelFormat32bppARGB; ++ else ++ lockeddata.PixelFormat = PixelFormat32bppPARGB; + + stat = GdipBitmapLockBits(bitmap, &src_area, ImageLockModeRead|ImageLockModeUserInputBuf, + lockeddata.PixelFormat, &lockeddata); +@@ -3069,8 +3151,14 @@ GpStatus WINGDIPAPI GdipDrawImagePointsRect(GpGraphics *graphics, GpImage *image + dst_color = (ARGB*)(dst_data + dst_stride * (y - dst_area.top) + sizeof(ARGB) * (x - dst_area.left)); + + if (src_pointf.X >= srcx && src_pointf.X < srcx + srcwidth && src_pointf.Y >= srcy && src_pointf.Y < srcy+srcheight) +- *dst_color = resample_bitmap_pixel(&src_area, src_data, bitmap->width, bitmap->height, &src_pointf, +- imageAttributes, interpolation, offset_mode); ++ { ++ if (lockeddata.PixelFormat != PixelFormat32bppPARGB) ++ *dst_color = resample_bitmap_pixel(&src_area, src_data, bitmap->width, bitmap->height, &src_pointf, ++ imageAttributes, interpolation, offset_mode); ++ else ++ *dst_color = resample_bitmap_pixel_premult(&src_area, src_data, bitmap->width, bitmap->height, &src_pointf, ++ imageAttributes, interpolation, offset_mode); ++ } + else + *dst_color = 0; + +-- +2.11.0 + diff --git a/patches/gdiplus-Performance-Improvements/definition b/patches/gdiplus-Performance-Improvements/definition new file mode 100644 index 00000000..2a6f400c --- /dev/null +++ b/patches/gdiplus-Performance-Improvements/definition @@ -0,0 +1 @@ +Fixes: Improve performance of bilinear bitmap scaling diff --git a/patches/patchinstall.sh b/patches/patchinstall.sh index 1cd016e6..6f5b6ebe 100755 --- a/patches/patchinstall.sh +++ b/patches/patchinstall.sh @@ -163,6 +163,7 @@ patch_enable_all () enable_gdi32_Symbol_Truetype_Font="$1" enable_gdiplus_DC_Handling="$1" enable_gdiplus_Grayscale_PNG="$1" + enable_gdiplus_Performance_Improvements="$1" enable_hal_KeQueryPerformanceCounter="$1" enable_hnetcfg_INetFwAuthorizedApplication="$1" enable_ieframe_IViewObject_Draw="$1" @@ -713,6 +714,9 @@ patch_enable () gdiplus-Grayscale_PNG) enable_gdiplus_Grayscale_PNG="$2" ;; + gdiplus-Performance-Improvements) + enable_gdiplus_Performance_Improvements="$2" + ;; hal-KeQueryPerformanceCounter) enable_hal_KeQueryPerformanceCounter="$2" ;; @@ -4202,6 +4206,24 @@ if test "$enable_gdiplus_Grayscale_PNG" -eq 1; then ) >> "$patchlist" fi +# Patchset gdiplus-Performance-Improvements +# | +# | Modified files: +# | * dlls/gdiplus/graphics.c +# | +if test "$enable_gdiplus_Performance_Improvements" -eq 1; then + patch_apply gdiplus-Performance-Improvements/0001-gdiplus-Change-the-order-of-x-y-loops-in-the-scaler.patch + patch_apply gdiplus-Performance-Improvements/0002-gdiplus-Change-multiplications-by-additions-in-the-x.patch + patch_apply gdiplus-Performance-Improvements/0003-gdiplus-Remove-ceilf-floorf-calls-from-bilinear-scal.patch + patch_apply gdiplus-Performance-Improvements/0004-gdiplus-Prefer-using-pre-multiplied-ARGB-data-in-the.patch + ( + printf '%s\n' '+ { "Dmitry Timoshkov", "gdiplus: Change the order of x/y loops in the scaler.", 1 },'; + printf '%s\n' '+ { "Dmitry Timoshkov", "gdiplus: Change multiplications by additions in the x/y scaler loops.", 1 },'; + printf '%s\n' '+ { "Dmitry Timoshkov", "gdiplus: Remove ceilf/floorf calls from bilinear scaler.", 2 },'; + printf '%s\n' '+ { "Dmitry Timoshkov", "gdiplus: Prefer using pre-multiplied ARGB data in the scaler.", 1 },'; + ) >> "$patchlist" +fi + # Patchset hal-KeQueryPerformanceCounter # | # | This patchset fixes the following Wine bugs: