MonoTouch:加倍外观在Retina显示屏上调整Hue时的图像尺寸

时间:2012-08-04 03:14:35

标签: c# ios mono xamarin.ios retina-display

我使用此代码设置NavBar的背景,这在Retina和非Retina显示器中非常有效。有一个@ 2x和普通图像。所以,一切都很好:

UINavigationBar.Appearance.SetBackgroundImage(
     GetImage(ImageTheme.menubar), UIBarMetrics.Default);

现在,当我将此ChangeHue()转换应用于图像以调整其色调时,在Retina上显示图像的大小是其两倍。非Retina显示器很好:

    UINavigationBar.Appearance.SetBackgroundImage(
       ChangeHue(GetImage(ImageTheme.menubar)), UIBarMetrics.Default);
    ...

    UIImage ChangeHue(UIImage originalImage){
        var hueAdjust = new CIHueAdjust() {
            Image = CIImage.FromCGImage(originalImage.CGImage),
            Angle = hue * (float)Math.PI / 180f // angles to radians
        };

        var output = hueAdjust.OutputImage;
        var context = CIContext.FromOptions(null);
        var cgimage = context.CreateCGImage(output, output.Extent);
        var i = UIImage.FromImage(cgimage);
        return i;
}

以下是应用Hue后非Retina和Retina显示的结果:

Non-Retina

Retina

4 个答案:

答案 0 :(得分:3)

忽略这些HACK,并在ChangeHue方法中修改此行:

var i = UIImage.FromImage(cgimage);

代替这样做:

float scale = 1f;
if (UIScreen.MainScreen.RespondsToSelector (new MonoTouch.ObjCRuntime.Selector ("scale"))) {
    scale = UIScreen.MainScreen.Scale; // will be 2.0 for Retina 
}
var i = new UIImage(cgimage, scale, UIImageOrientation.Up);

这应返回UIImage对象,该对象具有正确的“比例”信息,以便在UINavigationBar中正确显示。

答案 1 :(得分:2)

我从未尝试使用CoreImage,但是对于CoreGraphics,您需要使用UIGraphics.BeginImageContextWithOptions并指定0进行缩放(因此它将自动完成Retina和非Retina显示)。

所以我要尝试的第一件事是替换你的:

var context = CIContext.FromOptions(null);

使用以下块:

UIGraphics.BeginImageContextWithOptions (new SizeF (size, size), false, 0);
using (var c = UIGraphics.GetCurrentContext ()) {
   var context = CIContext.FromContext (c);
   ...
}
UIGraphics.EndImageContext ();

更新FromContext在iOS中无法使用(特定于OSX),因此上述代码无效。

答案 2 :(得分:0)

提交了MonoTouch团队的错误。将很快发布解决方案。

答案 3 :(得分:0)

我现在有三个'黑客'建议:

Hack#1

通过替换此行来强制修剪图像的范围:

var cgimage = context.CreateCGImage(output, output.Extent);

用这个:

var extent = output.Extent;
if (UIScreen.MainScreen.RespondsToSelector (new MonoTouch.ObjCRuntime.Selector("scale"))) {
    if (UIScreen.MainScreen.Scale == 2f) {
        extent = new System.Drawing.RectangleF(extent.X, extent.Y, extent.Width / 2f, extent.Height / 2f);
    }
}
var cgimage = context.CreateCGImage(output, extent);

但是你在调整后的图像上失去了'Retina分辨率(它使用@ 2x图像作为光源,但是在应用滤镜后仅显示它的左下象限,这要归功于图像原点从底部开始-left)。

Hack#2

沿着相同的行,您可以缩放从ChangeHue方法返回的图像,使其不会超出导航栏:

var hued = ChangeHue (navBarImage);
if (hued.RespondsToSelector(new MonoTouch.ObjCRuntime.Selector("scale")))
    hued = hued.Scale (new System.Drawing.SizeF(320, 47));
UINavigationBar.Appearance.SetBackgroundImage (hued, UIBarMetrics.Default);

不幸的是你再次失去了Retina分辨率,但至少图像显示正确(只是下采样到320宽)。

Hack#3

您可以将过滤后的图像保存到磁盘,然后使用“磁盘”上的图像文件设置UIAppearance。代码如下所示:

bool retina = false;
if (UIScreen.MainScreen.RespondsToSelector (new MonoTouch.ObjCRuntime.Selector ("scale"))) {
    if (UIScreen.MainScreen.Scale == 2f) {
        retina = true;
    }
}
if (retina) {
    NSError err; // unitialized
    UIImage img = ChangeHue (navBarImage);
    img.AsPNG ().Save ("tempNavBar@2x.png", true, out err);
    if (err != null && err.Code != 0) {
        // error handling
    }
    UINavigationBar.Appearance.SetBackgroundImage (UIImage.FromFile ("tempNavBar.png"), UIBarMetrics.Default);
} else {
    UINavigationBar.Appearance.SetBackgroundImage (ChangeHue (navBarImage), UIBarMetrics.Default);
}

最终黑客的好处是图像看起来正确(即视网膜分辨率得以保留)。

我仍然在寻找“完美”的解决方案,但至少这些想法会“单独解决”你的问题...