创建带圆角的图像缩略图

发布于 2024-10-21 16:48:58 字数 1922 浏览 1 评论 0原文

我需要创建一个带有透明圆角的缩略图。在这个要求之前,我使用了 simple:

using (var b = new Bitmap(dataSize.Width, dataSize.Height, PixelFormat.Format32bppArgb))
using (var g = Graphics.FromImage(b))
{
    g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.Default;
    g.DrawImage(original, 0, 0, b.Width, b.Height);
}

即使没有任何插值,它也产生了很好的结果(缩小到大约 50x50px)。现在,对于圆角,我使用了以下算法(有 4 个“if”,因此我可以在 4 个角中的每个角上具有可变的圆度):

using (var b = new Bitmap(dataSize.Width, dataSize.Height, PixelFormat.Format32bppArgb))
using (var g = Graphics.FromImage(b))
{
    // set interpolation
    g.InterpolationMode = InterpolationMode.HighQualityBicubic;
    g.SmoothingMode = SmoothingMode.HighQuality;

    // transformation to scale and shift the brush
    var transform = new Matrix();
    transform.Scale(ratio, ratio);
    transform.Translate(start.X / ratio, start.Y / ratio);
    var brush = new TextureBrush(original) { Transform = transform };

    // create path for stamping the iamge
    var gp = new GraphicsPath(FillMode.Winding);
    if (descriptor.CornerRadiusLeftTop > 0)
        gp.AddArc(descriptor.GetLeftTopCorner(b.Size), 180, 90);
    else
        gp.AddLine(-1, -1, -1, -1);

    if (descriptor.CornerRadiusRightTop > 0)
        gp.AddArc(descriptor.GetRightTopCorner(b.Size), 270, 90);
    else
        gp.AddLine(b.Width + 1, -1, b.Width + 1, -1);

    if (descriptor.CornerRadiusRightBottom > 0)
        gp.AddArc(descriptor.GetRightBottomCorner(b.Size), 0, 90);
    else
        gp.AddLine(b.Width + 1, b.Height + 1, b.Width + 1, b.Height + 1);

    if (descriptor.CornerRadiusLeftBottom > 0)
        gp.AddArc(descriptor.GetLeftBottomCorner(b.Size), 90, 90);
    else
        gp.AddLine(-1, b.Height + 1, -1, b.Height + 1);
    // stamp the image with original
    g.FillPath(brush, gp);
}

但这种方法产生了丑陋的未插值图像,具有真正的锯齿状渐变。是否有更好的方法来创建透明缩略图,或者是否有一些设置可以用来改进输出?

I need to create a thumbnail image with transparent rounded corners. Before this requirement I used the simple:

using (var b = new Bitmap(dataSize.Width, dataSize.Height, PixelFormat.Format32bppArgb))
using (var g = Graphics.FromImage(b))
{
    g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.Default;
    g.DrawImage(original, 0, 0, b.Width, b.Height);
}

which produced great results (for reductions to approx 50x50px) even without any interpolation. Now with the rounded corners I used the following algorithm (the 4 'if's are there so I can have variable roundness on each of the 4 corners):

using (var b = new Bitmap(dataSize.Width, dataSize.Height, PixelFormat.Format32bppArgb))
using (var g = Graphics.FromImage(b))
{
    // set interpolation
    g.InterpolationMode = InterpolationMode.HighQualityBicubic;
    g.SmoothingMode = SmoothingMode.HighQuality;

    // transformation to scale and shift the brush
    var transform = new Matrix();
    transform.Scale(ratio, ratio);
    transform.Translate(start.X / ratio, start.Y / ratio);
    var brush = new TextureBrush(original) { Transform = transform };

    // create path for stamping the iamge
    var gp = new GraphicsPath(FillMode.Winding);
    if (descriptor.CornerRadiusLeftTop > 0)
        gp.AddArc(descriptor.GetLeftTopCorner(b.Size), 180, 90);
    else
        gp.AddLine(-1, -1, -1, -1);

    if (descriptor.CornerRadiusRightTop > 0)
        gp.AddArc(descriptor.GetRightTopCorner(b.Size), 270, 90);
    else
        gp.AddLine(b.Width + 1, -1, b.Width + 1, -1);

    if (descriptor.CornerRadiusRightBottom > 0)
        gp.AddArc(descriptor.GetRightBottomCorner(b.Size), 0, 90);
    else
        gp.AddLine(b.Width + 1, b.Height + 1, b.Width + 1, b.Height + 1);

    if (descriptor.CornerRadiusLeftBottom > 0)
        gp.AddArc(descriptor.GetLeftBottomCorner(b.Size), 90, 90);
    else
        gp.AddLine(-1, b.Height + 1, -1, b.Height + 1);
    // stamp the image with original
    g.FillPath(brush, gp);
}

but this approach produced ugly un-interpolated imaged with really jagged gradients. Is there a better approach to create transparent thumbnails or are there some settings I could use to improve the output?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

嘴硬脾气大 2024-10-28 16:48:58

我写了一篇博文,详细解释了如何做到这一点。

http://danbystrom.se/2008/08/24/ soft-edged-images-in-gdi/

如果您查看第一个示例图像,您会看到 5),我将展示如何到达 6)。祝你好运。

I've written a blog post which explains exactly how to do this.

http://danbystrom.se/2008/08/24/soft-edged-images-in-gdi/

If you look at the first sample images, you're seeing 5) and I show how to arrive at 6). Good luck.

初与友歌 2024-10-28 16:48:58

我首先将图像复制到带有圆角的第二个图像,然后使用 GetThumbnailImage 将其缩小。

I would first copy the image to second one with rounded corners, then use GetThumbnailImage to scale it down.

热鲨 2024-10-28 16:48:58

我使用了修改后的 TransferChannel 方法来添加掩码,这并不像 danbystrom 的博客文章中那样不安全。

    public static void TransferChannel(Bitmap src, Bitmap dst, ChannelARGB sourceChannel, ChannelARGB destChannel)
    {
        if (src.Size != dst.Size)
            throw new ArgumentException();

        var r = new Rectangle(Point.Empty, src.Size);
        var bdSrc = src.LockBits(r, ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);
        var bdDst = dst.LockBits(r, ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb);

        var s = bdSrc.Stride * src.Height;
        var baSrc = new byte[s];
        var baDst = new byte[s];

        Marshal.Copy(bdSrc.Scan0, baSrc, 0, s);
        Marshal.Copy(bdDst.Scan0, baDst, 0, s);

        for (var counter = 0; counter < baSrc.Length; counter += 4)
            baDst[counter + (int)destChannel] = baSrc[counter + (int)sourceChannel];

        Marshal.Copy(baDst, 0, bdDst.Scan0, s);

        src.UnlockBits(bdSrc);
        dst.UnlockBits(bdDst);
    }

我调整大小和制作圆角的方法是:

        var b = new Bitmap(dataSize.Width, dataSize.Height, PixelFormat.Format32bppArgb);

        using (var g = Graphics.FromImage(b))
        {
            g.InterpolationMode = InterpolationMode.HighQualityBicubic;
            g.DrawImage(original, start.X, start.Y, original.Width * ratio, original.Height * ratio);

            if (hasRoundedCorners)
                using (var mask = CreateMask(dataSize, radius))
                    TransferChannel(mask, b, ChannelARGB.Blue, ChannelARGB.Alpha);
        }
        return b;

I've used a modified TransferChannel method to add mask, which is not unsafe as in blog post by danbystrom.

    public static void TransferChannel(Bitmap src, Bitmap dst, ChannelARGB sourceChannel, ChannelARGB destChannel)
    {
        if (src.Size != dst.Size)
            throw new ArgumentException();

        var r = new Rectangle(Point.Empty, src.Size);
        var bdSrc = src.LockBits(r, ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);
        var bdDst = dst.LockBits(r, ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb);

        var s = bdSrc.Stride * src.Height;
        var baSrc = new byte[s];
        var baDst = new byte[s];

        Marshal.Copy(bdSrc.Scan0, baSrc, 0, s);
        Marshal.Copy(bdDst.Scan0, baDst, 0, s);

        for (var counter = 0; counter < baSrc.Length; counter += 4)
            baDst[counter + (int)destChannel] = baSrc[counter + (int)sourceChannel];

        Marshal.Copy(baDst, 0, bdDst.Scan0, s);

        src.UnlockBits(bdSrc);
        dst.UnlockBits(bdDst);
    }

And my method to resize and make rounded corners is:

        var b = new Bitmap(dataSize.Width, dataSize.Height, PixelFormat.Format32bppArgb);

        using (var g = Graphics.FromImage(b))
        {
            g.InterpolationMode = InterpolationMode.HighQualityBicubic;
            g.DrawImage(original, start.X, start.Y, original.Width * ratio, original.Height * ratio);

            if (hasRoundedCorners)
                using (var mask = CreateMask(dataSize, radius))
                    TransferChannel(mask, b, ChannelARGB.Blue, ChannelARGB.Alpha);
        }
        return b;
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文