调整用作水印的位图的大小结果显示深色边框

Resizing a Bitmap used as watermark the result shows dark borders

问题:
我有一个要打印在图像上的水印。图像大小不一,因此有时水印太大,有时太小。为了解决这个问题,我计算了图像的大小并调整了水印的大小。但是,调整图像大小后,其边缘出现黑色边框。

代码

我正在 Mac 使用 .NET Core3.1 并且我正在使用两个有助于绘制图像/位图的 NuGet 包。一个是 System.Drawing.Common 另一个,因为我在 macOS 上是 runtime.osx.10.10x64.CoreCompat.System.Drawing.

我用来调整水印大小的代码创建here:

Bitmap watermarkNew = new Bitmap(watermark, new Size(image.Width / 10 * 3, image.Height / 10 * 3));

我必须使用 / 10 * 3 因为位图构造函数不接受浮点值,所以我不能乘以 * 0.3

结果:

watermark before            watermark after

要将一个图像叠加在另一个图像上,最好使用未缩放的图像,而不是事先根据所需大小生成新的位图。

▶ 这两张图片是为了混合,因此应该对其中一张图片(在本例中为水印图片)进行缩放,同时将要缩放的图片涂在另一张图片上 SourceOver操作。
这样,内部 GDI+(好吧,这里是 GDI+ 副本)函数可以正确计算混合过程。
这也可以防止副本显示使用 new Bitmap() 方法创建较小图像时生成的不完美的半透明像素(类似于暗光晕)。

▶ 此外,我们需要确保所有操作都在 32BitArgb 位图上执行。
最好创建目标图像的 32BitArgb 副本并在该副本上绘制水印。这也可以确保更好的结果。 GDI+ 函数在这种图像上效果更好。
这里,CopyToArgb32() 方法处理了这方面的问题,还将原始图像的 DPI 分辨率应用于副本。

▶ 此外,这会产生扭曲的图像(除非这是预期的结果,即):

Bitmap watermarkNew = new Bitmap(watermark, new Size(image.Width / 10 * 3, image.Height / 10 * 3));

应调整水印图像尺寸的大小,计算比例因子,该比例因子是所需的分数(百分比或固定度量)或目标图像。

例如,占用的最大尺寸等于目标位图最小尺寸的三分之一。
换句话说,如果目标位图大小为 1500x600 px,水印位图将按比例缩放以具有最大高度 200px:

float scale = (Math.Min(original.Width, original.Height) * .33f) / 
               Math.Min(watermark.Width, watermark.Height);
SizeF watermarkSize = new SizeF(watermark.Width * scale, watermark.Height * scale);

为了进一步改善混合效果,水印可以做得更少不透明(或者,更多透明,如您所愿).
这可以简单地使用 as ColorMatrix 来实现,如下所示:


所有组合在一个 class 对象中,该对象公开了一个 Watermark([Bitmap], [Bitmap], [Imageformat]) 静态方法。

在示例代码中,水印被缩放到目标图像最大尺寸的 1/3 并居中(只是一个通用的放置,因为没有指定水印的位置):

using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;

public class BitmapOperations
{
    public static Bitmap Watermark(Bitmap watermark, Bitmap original, ImageFormat format)
    {
        var units = GraphicsUnit.Pixel;
        float scale = (Math.Max(original.Width, original.Height) * .33f) / 
                       Math.Max(watermark.Width, watermark.Height);
        var watermarkSize = new SizeF(watermark.Width * scale, watermark.Height * scale);
        
        var watermarkBounds = CenterRectangleOnRectangle(
            new RectangleF(PointF.Empty, watermarkSize), original.GetBounds(ref units));
        var workImage = CopyToArgb32(original);
        // Using the SetOpacity() extension method described in the linked question
        // watermark = watermark.SetOpacity(.5f, 1.05f);

        using (var g = Graphics.FromImage(workImage)) {
            g.PixelOffsetMode = PixelOffsetMode.Half;
            g.InterpolationMode = InterpolationMode.HighQualityBicubic;
            g.DrawImage(watermark, watermarkBounds);

            return workImage;
        }
    }

    private static Bitmap CopyToArgb32(Bitmap source)
    {
        var bitmap = new Bitmap(source.Width, source.Height, PixelFormat.Format32bppArgb);
        bitmap.SetResolution(source.HorizontalResolution, source.VerticalResolution);
        using (var g = Graphics.FromImage(bitmap)) {
            g.DrawImage(source, new Rectangle(0, 0, bitmap.Width, bitmap.Height),
                                new Rectangle(0, 0, bitmap.Width, bitmap.Height), GraphicsUnit.Pixel);
            g.Flush();
        }
        return bitmap;
    }

    private static RectangleF CenterRectangleOnRectangle(RectangleF source, RectangleF destination)
    {
        source.Location = new PointF((destination.Width - source.Width) / 2,
                                     (destination.Height - source.Height) / 2);
        return source;
    }
}

结果:

应用 50% 的不透明度和小的伽玛校正: