Been struggling with this for days. I create an image algorithmically from some data. I also need to take an image and recreate the data, so I want to implement an accurate two-way operation, data -> image and image -> data, say d1 -> i1 -> d2.
Then I ran into some bugs and after a lot of struggling I realised that d1 != d2!
Most of the trouble appears to be in the lower right hand corner of the image, but, since creating the test code below, I note that the problem seems to be wider than this.
The code below illustrates this heart of this problem, which is the [img drawInRect:]
message. It seems that if you have some image and you draw it in some rect, no matter what you do with scaling and insets and blend modes and so on, it does not accurately give you back the original image.
So my question is: how can I use [img drawInRect:]
to render img
accurately.
Here is some sample code to illustrate the problem.
#import "ViewController.h"
@interface ViewController ()
@property (weak, nonatomic) IBOutlet UIImageView * topImage;
@property (weak, nonatomic) IBOutlet UIImageView * botImage;
@end
@implementation ViewController
- (IBAction)drawAction:(id)sender
{
CGFloat w = self.topImage.bounds.size.width / 20;
CGFloat h = self.topImage.bounds.size.height / 20;
// Create random image
UIGraphicsImageRendererFormat * format = UIGraphicsImageRendererFormat.defaultFormat;
format.scale = 1;
UIImage * top = [[[UIGraphicsImageRenderer alloc] initWithSize:CGSizeMake ( w, h )
format:format]
imageWithActions: ^ ( UIGraphicsImageRendererContext * ctx )
{
for ( CGFloat x = 0; x < w; x ++ )
{
for ( CGFloat y = 0; y < w; y ++ )
{
if ( arc4random_uniform ( 2 ) )
{
[ctx fillRect:CGRectMake( x, y, 1, 1 )];
}
}
}
}];
// Duplicate image
UIImage * bot = [[[UIGraphicsImageRenderer alloc] initWithSize:CGSizeMake ( w, h )
format:format]
imageWithActions: ^ ( UIGraphicsImageRendererContext * ctx )
{
[top drawInRect:CGRectMake ( 0, 0, w, h )];
}];
// Done
self.topImage.image = top;
self.botImage.image = bot;
}
@end
This is inside a standard view controller with two image views and a button, as below.
In the original problem I do not scale down by 20 as here - the 20 is just to illustrate, but it makes no difference if you work at a scale of 1.
Note the difference between the two images - note especially the bottom right hand corner! Why the difference?
FWIW after drawing the image I access the image data and build the data (e.g. d2) from there. Sometimes the image needs to be scaled and thus I want to use [img drawInRect:]
first to render the image and afterwards to work with the image data. If the image is already the correct size then I use the image data directly and that sidesteps this problem, but still, I need to use [img drawInRect:]
when the sizes differ and I would expect [img drawInRect:]
to be faithful to the original!
TIA