MonoTouch: Doubling Appearance Image size when Hue adjusted on Retina display

ぐ巨炮叔叔 提交于 2019-12-09 01:50:34

问题


I am setting the NavBar's background with this code which works great in Retina and non-Retina displays. There is a @2x and normal image. So, all good:

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

Now, when I apply this ChangeHue() transformation to the image to adjust its hue, on Retina displays the image is twice the size. Non-Retina displays are fine:

    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;
}

Here is the result in Non-Retina and Retina displays after the Hue is applied:


回答1:


Ignore those HACKs, and edit this line in your ChangeHue method:

var i = UIImage.FromImage(cgimage);

to do this instead:

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);

This should return a UIImage object that is has the correct 'scale' information to be properly displayed in the UINavigationBar.




回答2:


I never tried for CoreImage but, for CoreGraphics, you need to use UIGraphics.BeginImageContextWithOptions and specify 0 for scaling (so it will be done automagically for both Retina and non-Retina displays).

So the first thing I would try is to replace your:

var context = CIContext.FromOptions(null);

with the following block:

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

UPDATE: FromContext is not available in iOS (it's OSX specific) so the above code won't work.




回答3:


Filed a bug with the MonoTouch team. Will post solution shortly.




回答4:


I've got three 'hacks' to suggest for now:

Hack #1

To force the extent to trim the image by replacing this line:

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

with this:

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);

HOWEVER you 'lose' the Retina resolution on the adjusted image (it uses the @2x image as the source, but only displays the bottom-left quadrant of it after applying the filter, thanks to the image origin starting at the bottom-left).

Hack #2

Along the same lines, you can scale the image returned from the ChangeHue method so that it doesn't expand beyond the navigation bar:

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);

UNFORTUNATELY you 'lose' the Retina resolution again, but at least the image is displayed correctly (just downsampled to 320 wide).

Hack #3

You could save the filtered image to disk and then set the UIAppearance using the image file on 'disk'. The code would look like this:

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);
}

The benefit of this final hack is that the image looks correct (ie. Retina resolution is preserved).

I'm still looking for the "perfect" solution, but at least these ideas 'fix' your problem one-way-or-another...



来源:https://stackoverflow.com/questions/11805628/monotouch-doubling-appearance-image-size-when-hue-adjusted-on-retina-display

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!