WinRT:我如何确保在画布上以像素完美的方式绘制图像?

5
我正在Windows Runtime环境中将Image实例添加到Canvas,但是当在缩放分辨率为140和180的显示器上时,我的图像会被放大,而在100的缩放分辨率下则看起来完美。我尝试创建了3个PNG图像,每个缩放大小一个:100、140、180,但它们仍然被放大并且变得模糊。 我创建了一个测试图像,在青色背景上有4个黑色像素,并从模拟器中截取了屏幕截图,可以看到图像已经变得模糊了,而我的原始图像只有4个完美的黑色像素:

it is blurry, trust me

我尝试更改我的图像对象的拉伸模式,但它没有任何作用。 我使用以下代码在运行时添加图像:

var bitmapImage = new BitmapImage();
StorageFile bitmapFile = await StorageFile.GetFileFromApplicationUriAsync(imageUri);
await bitmapImage.SetSourceAsync(await bitmapFile.OpenReadAsync());

Image image = new Image{ Source = bitmapImage};
image.SetValue(Canvas.LeftProperty, x);
image.SetValue(Canvas.TopProperty, y);
canvas.Children.Add(image);

如何在画布中完美绘制像素图像,不进行缩放,并在我想要的确切x/y坐标处绘制?


http://blogs.msdn.com/b/b8/archive/2012/03/21/scaling-to-different-screens.aspx - Hans Passant
5个回答

2

我认为我有一个解决方法,但需要两个步骤:

首先,我必须使用更标准的方式加载图像,而不涉及获取文件路径,如下所示。

var bitmapImage = new BitmapImage(imageUri);

不知何故,此处必须保留更多的内部信息,以表明该图像来自具有当前显示器ResolutionScale对应的文件。因此,当画布绘制它时,它根本没有被缩放。但是,这只解决了问题的一半。

下一个问题是用于指定图像绘制位置的x、y坐标被缩放,因此图像绘制在错误的位置,超出我想要的位置。我唯一能想到的方法是首先取消缩放,就像这样:

var resScale = DisplayInformation.GetForCurrentView().ResolutionScale;
Image image = new Image{ Source = bitmapImage};
image.SetValue(Canvas.LeftProperty, (x * 100.0) / (int)resScale);
image.SetValue(Canvas.TopProperty, (y * 100.0) / (int)resScale);
canvas.Children.Add(image);

整个事情看起来有点疯狂,但目前似乎还有效... 有人有更好的解决方案或者能解释为什么所有这些都是必要的吗?似乎Canvas类需要一个不会影响不同分辨率显示器上的图像或坐标的未缩放模式。

更新:这并不完美,使用double存储值会导致精度损失,有时会出现反锯齿伪像。如果您想要像素完美的图形,这是不可接受的。我仍在寻找完美的解决方案。


你只是在相对于画布改变X和Y。这如何帮助像素完美度?这就像在网格内部添加图像边距一样。你还做了什么吗?它只会解决小问题,比如在哪里设置图像。但是缩放怎么办,是的,我们可以将其减少或增加以隐藏像素损失,但真正的像素损失呢?你使用了其他方法吗?请添加它 :) - Anobik
尝试使用(new BitmapImage()).PixelHeight(new BitmapImage()).PixelWidth来决定你正在使用的图像控件或画布的高度。希望这能有所帮助 :) - Anobik

2

以下是一些可能有助于您解决问题的内容:

  1. 在您的图像上使用 UseLayoutRounding="False"
  2. 将您的 Canvas 放置在全屏 Viewbox 中,然后将 CanvasWidthHeight 设置为屏幕分辨率。在这种情况下,您会使用未缩放的 Canvas.Left/Top 值。
  3. 使用 Direct2D/Direct3D 进行渲染。

祝好运。


1
哎呀,那就是答案了。这可不是一件轻松的事情。 - Jerry Nixon
我前几天也遇到了同样的问题,最终放弃了。希望超高分辨率屏幕能够使这一切都不再相关。 - Filip Skakun
我尝试了Viewbox,但是无法使其正常工作... 我将不得不联系微软寻求帮助... - satur9nine

1
你可以将Stretch属性更改为"None",如果您的图像仍然混乱:
您应该查看它保存在哪个DPI上。WPF试图成为DPI无关,因此它尝试在每个监视器上绘制一个5"x5"的图像以相同的大小。即使分辨率更高,它仍应该是5"x5",只有高分辨率才会以更高的质量呈现(光栅化)图像。
这里有一些信息:http://www.wpflearningexperience.com/?p=41 如何将WPF大小转换为物理像素?

你的回答似乎是关于WPF的,但我对Windows Store应用程序开发感兴趣。我尝试将Stretch设置为None,但没有效果。我知道你所说的5英寸缩放问题,我不希望这种情况发生在我的应用程序中,因为我想自己处理它,以便可以完美呈现像素。 - satur9nine

0
这是一段XAML代码:
你可以通过编写代码使用比例变换(scale transform)来缩放图像,使其适应不同的大小。
<Image  Canvas.Left="150"  Height="170" Width="170" Visibility="Visible" Stretch="None">
                <Image.Source >
                    <BitmapImage UriSource="ms-appx:///Assets/rollingDieSprite.png"></BitmapImage>
                </Image.Source>

                <Image.RenderTransform>
                    <ScaleTransform ScaleX="4" ScaleY="4" x:Name="scaleTfDie"></ScaleTransform>
                </Image.RenderTransform>
            </Image>

在C#的代码后台,你可以选择以下方式:
ScaleTransform sc = new ScaleTransform();
            sc.ScaleX = 0.9;
            sc.ScaleY = 0.9;
            imgDieRolling.RenderTransform = sc;

这将控制缩放。尝试使用fill=none。如果有效,请告诉我。


这个不起作用,我不想要任何缩放。使用ScaleTransform并不能消除模糊问题。我不想要任何缩放,我只想在屏幕上绘制我的图像,而不希望它们以任何方式被缩放。 - satur9nine

0

我也觉得这个问题很棘手。我正在创建自定义位图并在画布上的不同位置绘制它们。我在XAML中找不到方法,但我在Direct2D中找到了一种方法。当您设置D2DContext时,有一个名为SetUnitMode()的函数。默认单位模式是“ DIPS”,这会导致所有绘图被缩放。通过切换到PIXELS模式,系统停止缩放绘图并进行1:1的所有操作。

m_d2dContext->SetUnitMode(D2D1_UNIT_MODE_PIXELS);

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接