问题描述
问题:
我有要在图像上打印的水印.图片的大小各不相同,因此有时水印太大而有时又太小.为了解决这个问题,我计算了图像的大小并调整了水印的大小.但是,调整图像大小后,其边框周围会出现黑色边框.
代码
我在使用 .NET Core3.1
的Mac上,并且正在使用两个有助于绘制图像/位图的NuGet软件包.一个是 System.Drawing.Common
,另一个是因为我在macOS上,所以是 runtime.osx.10.10x64.CoreCompat.System.Drawing
.
我用来调整水印大小的代码建立了
水印之前的水印之后
要将图像叠加在另一个图像上,最好使用未缩放的图像,而不是事先根据所需的大小生成新的位图.
►这两个图像是要混合的,因此,应在缩放图像的同时用 SourceOver
操作.
这样,内部GDI +(这里是GDI +副本)功能可以正确计算混合过程.
当使用 new Bitmap()
方法创建较小的Image时,这还可以防止副本显示不完美的半透明像素(类似于暗晕).
►另外,我们需要确保所有操作都在32BitArgb位图上执行.
最好创建目标Image的32BitArgb副本并在此副本上绘制水印.这也可以确保更好的结果.GDI +功能在此类图像上效果更好.
在这里, CopyToArgb32()
方法会处理此方面,还将原始Image的DPI分辨率应用于副本.
►此外,这会产生失真的图像(除非是预期的结果):
位图watermarkNew =新的位图(水印,新的Size(image.Width/10 * 3,image.Height/10 * 3));
应该调整水印图像尺寸的大小,以计算比例因子,该比例因子是所需的分数(百分比或固定度量)或目标图像.
例如,要占用的最大大小等于目标位图的最小尺寸的三分之一.
换句话说,如果目标位图大小为 1500x600 px
,则水印位图将按比例缩放以具有最大高度为 200px
:
浮标=(Math.Min(原始宽度,原始高度)* .33f)/Math.Min(watermark.Width,watermark.Height);SizeF watermarkSize =新的SizeF(watermark.Width *比例尺,watermark.Height *比例尺);
为进一步改善混合效果,可以使水印的不透明更少(或者,如您想看到的那样,更多的 transparent ).
可以简单地使用 ColorMatrix
来实现,如下所示:
应用不透明度为50%的灰度级并进行较小的校正:
Problem:
I have a watermark that I want to print on an Image. The Image varies in size so sometimes the watermark is too big and sometimes it's too small. In order to fix this I calculate the size of the image and resize the watermark. However, after resizing the Image, black borders appear around its margins.
Code
I am on a Mac using .NET Core3.1
and I am using two NuGet packages that helps to draw images / bitmaps. One is System.Drawing.Common
and the other one, because I am on macOS is, runtime.osx.10.10x64.CoreCompat.System.Drawing
.
The code that I use to resize the watermark founded here:
Bitmap watermarkNew = new Bitmap(watermark, new Size(image.Width / 10 * 3, image.Height / 10 * 3));
I have to use / 10 * 3
because the Bitmap constructor doesn't accept floats values, so I cannot multiply by * 0.3
.
Results:
watermark before watermark after
To superimpose an Image on another, it's preferable to use an unscaled Image than generate a new Bitmap based on the desired size beforehand.
► The two Image are meant to blend, thus the scaling of one of the Images, in this case the Watermark Image, should be performed while the Image to scale is painted over the other with a SourceOver
operation.
This way, the internal GDI+ (well, the GDI+ replica here) functions have means to calculate the blending procedure correctly.
This also prevents the copy to show imperfect semi-transparent pixels (similar to a dark halo) generated when a smaller Image is created using the new Bitmap()
method.
► Also, we need to be sure that all operations are performed on a 32BitArgb Bitmaps.
It's better to create a 32BitArgb copy of the destination Image and draw the watermark on this copy. This can also ensure a better result. GDI+ function work better on this kind of Images.
Here, the CopyToArgb32()
method takes care of this aspect, also applying the DPI resolution of the original Image to the copy.
► Furthermore, this produces a distorted Image (unless that's the expected result, that is):
Bitmap watermarkNew = new Bitmap(watermark, new Size(image.Width / 10 * 3, image.Height / 10 * 3));
The watermark Image dimensions should be resized calculating a scale factor that is a desired fraction (a percentage or a fixed measure) or the destination Image.
For example, to occupy a maximum size equals to one third of the destination Bitmap's minimum dimension.
In other words, if the destination Bitmap size is 1500x600 px
, the watermark Bitmap will be scaled proportionally to have a maximum Height of 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);
To further improve the blending, the Watermark could be made less opaque (or, more transparent, as you want to see it).
This can be simply achieved using as ColorMatrix
as shown here:
How to apply a fade transition effect to Images
All combined in a class object that exposes a Watermark(Bitmap, Bitmap, Imageformat)
static method.
In the sample code, the Watermark is scaled to 1/3 of the maximum dimension of destination image and centered (just a generic placement, since the position of the watermark is not specified):
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;
}
}
Results:
Applying an opacity level of 50% and small correction in gamma:
这篇关于调整用作水印的位图的大小,结果将显示黑色边框的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!