iOS7 UIImage drawAtPoint not retina - ios

i am trying to draw an image with the following code:
[img drawAtPoint:CGPointZero];
but the problem is that on an iphone with a retina display the image doesn´t get drawn in retina scale. It seems like the image gets upscaled and then drawn.
I don´t want to use drawInRect because the image is in right size and it´s way slower to use drawInRect.
Any ideas?

You probably are not setting the appropriate scale factor. When you create the bitmap context one of the arguments is the scale:
void UIGraphicsBeginImageContextWithOptions(
CGSize size,
BOOL opaque,
CGFloat scale
);
According to the official documentation scale is:
The scale factor to apply to the bitmap. If you specify a value of
0.0, the scale factor is set to the scale factor of the device’s main screen.
You're probably passing 1.0f which will result in the issue you've described. Try passing 0.0f.

Related

UIImage resize performance and quality issue

I'm working with UIImage and like everyone else have to deal with retina and non-retina display adaptability. As for as I know, retina display requires double pixels.
I'm wondering if I could simply use a large image with the same width/height ratio, just resize it smaller to adapt all device?
For example, I made a original image with size of 200*200 pixel. Now I want to use it in application as 20*20 pixel, and 80*80 pixel (two situations). Then I have to make four copies like img2020.png, img2020#2x.png, img8080.png and img8080#2x.png
So if I want to use it in three situations with difference size, I have to store 6 copies. Can I just use UIImage's resize function to do this? I've tried a bit but cannot figure out it's quality and performance.
Any ideas? Thanks a lot :)
All native API suppose you to use image.png and image#2x.png, so it may be difficult sometimes to use just one image and scale it depending on retina/non-retina. Moreover using retina graphics on non-retina devices lead to more extensive use of these devices' resource causing battery drain. And, of course, if you have many images, that will decrease performance of your application. In other words there are reasons to use double set of images and you should better use it instead of one large image being scaled.
You don't need to make 6 copies. You should use the size 200*200 pixel. And set the property contentMode of imageview to aspectFit. Or you can also use below function and change the size of images at run time.
-(UIImage *)Resize_Image:(UIImage *)image requiredHeight:(float)requiredheight andWidth:(float)requiredwidth
{
float actualHeight = image.size.height;
float actualWidth = image.size.width;
if (actualWidth*requiredheight <actualHeight*requiredwidth)
{
actualWidth=requiredheight*(actualWidth/actualHeight);
actualHeight=requiredheight;
}
else
{
actualHeight=requiredwidth*(actualWidth/actualHeight); actualWidth=requiredwidth;
}
CGRect rect = CGRectMake(0.0, 0.0, actualWidth, actualHeight);
UIGraphicsBeginImageContext(rect.size);
[image drawInRect:rect];
UIImage *img = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return img;
}
I made some comparisons before. Leaving iOS handle the resizing causes lower quality, and really unacceptable sometimes.
I feel lazy sometimes, my approach is to run it with the retina version, and if it looks bad, I will create a low-res version.
If you're writing an iPhone-only app, most of iPhones on the market has retina, so I don't think you should worry about non-retina version. Just my opinion though.

iOS image size of retina image from web service

Suppose I want an image to be 320x100 points for a retina screen, I would have to make an image 640x200 pixel and named it #2x. The problem is when I download an image from a web service of size 640x200 pixel. Normally a #2x image would be translate to size 320x100 points in the phone, but the image from the web service is still 640x200 points.
Note - the web service is my own, so I can fix it if it's the web service's problem.
Sorry If might have not worded the problem well, but this is what I meant(similar):
Retina display and [UIImage initWithData]
You'll have to set the height and width of the control in points manually and it will automatically display the image with a higher DPI without unnecesarily downscaling.
An #2x image will always be twice the dimensions of a non-retina image if it wasn't loaded through imageNamed.
Create an image like so on a retina and non-retina device:
UIImage *anImage = [UIImage imageNamed: #"anImageName"];
NSLog(#"%#: scale: %f", NSStringFromCGSize(anImage.size), anImage.scale);
The CGSize object printed on the retina device will be the same size of the non-retina image, but will have the scale set to 2.0.
Creating an image using the explicit retina suffix will reveal that the image is actually twice as big - so imageNamed does its own image scaling.
For the case where you want to display this in an already created and sized image view, you still don't need to do anything - just load it straight in and the image view will adjust the image to the correct size.
If, however, you want to create a new image view, then you'll need to create a frame paying attention to the UIScreen's resolution like so (unfortunately, you can't just set the scale property as its read only):
CGRect newFrame = CGRectZero;
newFrame.size.width = (anImage.size.width / [UIScreen mainScreen].scale);
newFrame.size.height = (anImage.size.height / [UIScreen mainScreen].scale);
This assumes that your web service is aware of wether the screen of the device is retina or not; some services will pick this up automatically, some will require you to tell them this up front. YMMV.

UIImageView content mode and scale factor

I have a programmatically created UIImage image, using this kind of code:
UIGraphicsBeginImageContextWithOptions(CGSizeMake(128, 128), NO, 0.0f);
// Render in context
UIImage *resultImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
Since my context options specify scale of 0, on retina devices it will be set to 2, and I can confirm that on resulting UIImage scale property.
Now, the problem is that this image size is 128x128, scale 2. When I am putting it into UIImageView of size 64x64 and contentMode = Center, it renders my image outside imageview, presumably rendering into 128x128 box without any scaling.
My understanding of retina graphics was that if an image has scale factor 2.0, the it should be rendered at 1/2 size, thus resulting in higher DPI.
So I was expecting the image view to render 64x64 image at retina quality. Where am I wrong?
The image will be rendered at the size you give it - 128 x 128. The scale factor means that you will have better rendered curves etc, but the image will still be 128 x 128 points. As stated in the documentation, the size parameter is:
The size (measured in points) of the new bitmap context. This represents the size of the image returned by the UIGraphicsGetImageFromCurrentImageContext function.
If you want a retina-quality 64x64 image, use a 64x64 size for your context.

renderInContext: producing an image with blurry text

I am prerendering a composited image with a couple different UIImageViews and UILabels to speed up scrolling in a large tableview. Unfortunately, the main UILabel is looking a little blurry compared to other UILabels on the same view.
The black letters "PLoS ONE" are in a UILabel, and they look much blurrier than the words "Medical" or "Medicine". The logo "PLoS one" is probably similarly being blurred, but it's not as noticeable as the crisp text.
The entire magazine cover is a single UIImage assigned to a UIButton.
(source: karlbecker.com)
This is the code I'm using to draw the image. The magazineView is a rectangle that's 125 x 151 pixels.
I have tried different scaling qualities, but that has not changed anything. And it shouldn't, since the scaling shouldn't be different at all. The UIButton I'm assigning this image to is the exact same size as the magazineView.
UIGraphicsBeginImageContextWithOptions(magazineView.bounds.size, NO, 0.0);
[magazineView.layer renderInContext:UIGraphicsGetCurrentContext()];
[coverImage release];
coverImage = UIGraphicsGetImageFromCurrentImageContext();
[coverImage retain];
UIGraphicsEndImageContext();
Any ideas why it's blurry?
When I begin an image context and render into it right away, is the rendering happening on an even pixel, or do I need to manually set where that render is occurring?
Make sure that your label coordinates are integer values. If they are not whole numbers they will appear blurry.
I think you need to use CGRectIntegral for more information please see: What is the usage of CGRectIntegral? and Reference of CGRectIntegral
I came across the same problem today where my content got pixelated when I am producing an image from UILabel text.
We use UIGraphicsBeginImageContextWithOptions() to configure the drawing environment for rendering into a bitmap which accepts three parameters:
size: The size of the new bitmap context. This represents the size of the image returned by the UIGraphicsGetImageFromCurrentImageContext function.
opaque: A Boolean flag indicating whether the bitmap is opaque. If the opaque parameter is YES, the alpha channel is ignored and the bitmap is treated as fully opaque.
scale: The scale factor to apply to the bitmap. If you specify a value of 0.0, the scale factor is set to the scale factor of the device’s main screen.
So we should use a proper scale factor with respect to the device display (1x, 2x, 3x) to fix this issue.
Swift 5 version:
UIGraphicsBeginImageContextWithOptions(frame.size, true, UIScreen.main.scale)
if let currentContext = UIGraphicsGetCurrentContext() {
nameLabel.layer.render(in: currentContext)
let nameImage = UIGraphicsGetImageFromCurrentImageContext()
return nameImage
}

iOS: How do I support Retina Display with CGLayer?

I'm drawing a graph on a CALayer in its delegate method drawLayer:inContext:.
Now I want to support Retina Display, as the graph looks blurry on the latest devices.
For the parts that I draw directly on the graphics context passed by the CALayer, I could nicely draw in high resolution by setting the CALayer's contentScale property as follows.
if ([myLayer respondsToSelector:#selector(setContentsScale:)]) {
myLayer.contentsScale = [[UIScreen mainScreen] scale];
}
But for the parts that I use CGLayer are still drawn blurry.
How do I draw on a CGLayer in high resolution to support Retina Display?
I want to use CGLayer to draw the same plot shapes of the graph repeatedly, as well as to cut off the graph lines exceeding the edge of the layer.
I get CGLayer by CGLayerCreateWithContex with the graphics context passed from the CALayer, and draw on its context using CG functions such as CGContextFillPath or CGContextAddLineToPoint.
I need to support both iOS 4.x and iOS 3.1.3, both Retina and legacy display.
Thanks,
Kura
This is how to draw a CGLayer correctly for all resolutions.
When first creating the layer, you need to calculate the correct bounds by multiplying the dimensions with the scale:
int width = 25;
int height = 25;
float scale = [self contentScaleFactor];
CGRect bounds = CGRectMake(0, 0, width * scale, height * scale);
CGLayer layer = CGLayerCreateWithContext(context, bounds.size, NULL);
CGContextRef layerContext = CGLayerGetContext(layer);
You then need to set the correct scale for your layer context:
CGContextScaleCTM(layerContext, scale, scale);
If the current device has a retina display, all drawing made to the layer will now be drawn twice as large.
When you finally draw the contents of your layer, make sure you use CGContextDrawLayerInRect and supply the unscaled CGRect:
CGRect bounds = CGRectMake(0, 0, width, height);
CGContextDrawLayerInRect(context, bounds, layerContext);
That's it!
I decided not to use CGLayer and directly draw on the graphics context of the CALayer, and now it's drawn nicely in high resolution on retina display.
I found the similar question here, and found that there is no point of using CGLayer in my case.
I used CGLayer because of the Apple's sample program "Using Multiple CGLayer Objects to Draw a Flag" found in the Quartz 2D Programming guide. In this example, it creates one CGLayer for a star and uses it multiple times to draw 50 stars. I thought this was for a performance reason, but I didn't see any performance difference.
For the purpose of cutting off the graph lines exceeding the edge of the layer, I decided to use multiple CALayers.

Resources