0

The problem:

I've to render a big image 7148x15000px (a custom map), so i've looking around for something usefull and i've found BitmapSlice, but the problem is that the very first time i run the app (on device and simulator) several slices aren't loaded correctly and i see the image with large black holes.

Code:

BitmapSliceViewController.h

#import <UIKit/UIKit.h>

@interface BitmapSliceViewController : UIViewController<UIScrollViewDelegate>
    @property (nonatomic, retain) UIImageView *_zoomView;
    @property (nonatomic, retain) IBOutlet UIScrollView *scrollView;

    - (void)saveTilesOfSize:(CGSize)size forImage:(UIImage*)image toDirectory
    (NSString*)directoryPath usingPrefix:(NSString*)prefix;
@end

BitmapSliceViewController.m

#import "BitmapSliceViewController.h"
#import "TileView.h"

@implementation BitmapSliceViewController

@synthesize scrollView;

- (void)dealloc
{
    [super dealloc];
}

- (void)viewDidUnload 
{
    [super viewDidUnload];
}

- (void)viewDidLoad
{
    [super viewDidLoad];

    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES);
    NSString *directoryPath = [paths objectAtIndex:0];

    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        UIImage *big = [UIImage imageNamed:@"map.jpg"];
        [self saveTilesOfSize:(CGSize){256, 256} forImage:big toDirectory:directoryPath usingPrefix:@"map_"];
        dispatch_async(dispatch_get_main_queue(), ^{
            [scrollView setNeedsDisplay];
        });
    });

    TileView *tv = [[TileView alloc] initWithFrame:(CGRect){{0,0}, (CGSize){7148,15000}}];
    [tv setTileTag:@"map_"];
    [tv setTileDirectory:directoryPath];
    [scrollView addSubview:tv];
    [scrollView setContentSize:(CGSize){7148,15000}];
    [scrollView setDelegate:self];
}

- (void)saveTilesOfSize:(CGSize)size 
           forImage:(UIImage*)image 
        toDirectory:(NSString*)directoryPath 
        usingPrefix:(NSString*)prefix
{
    CGFloat cols = [image size].width / size.width;
    CGFloat rows = [image size].height / size.height;

    int fullColumns = floorf(cols);
    int fullRows = floorf(rows);

    CGFloat remainderWidth = [image size].width - (fullColumns * size.width);
    CGFloat remainderHeight = [image size].height - (fullRows * size.height);

    if (cols > fullColumns) fullColumns++;

    if (rows > fullRows) fullRows++;

    CGImageRef fullImage = [image CGImage];

    for (int y = 0; y < fullRows; ++y) {
        for (int x = 0; x < fullColumns; ++x) {
            CGSize tileSize = size;
            if (x + 1 == fullColumns && remainderWidth > 0) {
                // Last column
                tileSize.width = remainderWidth;
            }
            if (y + 1 == fullRows && remainderHeight > 0) {
                // Last row
                tileSize.height = remainderHeight;
            }

            CGImageRef tileImage = CGImageCreateWithImageInRect(fullImage, 
                                    (CGRect){{x*size.width, y*size.height}, 
                                      tileSize});
            NSData *imageData = UIImageJPEGRepresentation([UIImage imageWithCGImage:tileImage], 1);

            CGImageRelease(tileImage);
            NSString *path = [NSString stringWithFormat:@"%@/%@%d_%d.png", 
                    directoryPath, prefix, x, y];
            [imageData writeToFile:path atomically:NO];
        }
    }
}

@end

TileView.h

@interface TileView : UIView 

@property (nonatomic, copy) NSString *tileTag;
@property (nonatomic, copy) NSString *tileDirectory;

- (UIImage*)tileAtCol:(int)col row:(int)row;

@end

TileView.m

#import "TileView.h"

@implementation TileView
@synthesize tileTag;
@synthesize tileDirectory;

+ layerClass
{
    return [CATiledLayer class];
}

- (id)initWithFrame:(CGRect)frame
{
    self = [super initWithFrame:frame];
    if (!self) return  nil;
    return self;
}

- (void)dealloc
{
    [super dealloc];
}

- (void)drawRect:(CGRect)rect {
    //CGContextRef context = UIGraphicsGetCurrentContext();

    CGSize tileSize = (CGSize){256, 256};

    int firstCol = floorf(CGRectGetMinX(rect) / tileSize.width);
    int lastCol = floorf((CGRectGetMaxX(rect)-1) / tileSize.width);
    int firstRow = floorf(CGRectGetMinY(rect) / tileSize.height);
    int lastRow = floorf((CGRectGetMaxY(rect)-1) / tileSize.height);

    for (int row = firstRow; row <= lastRow; row++) {
        for (int col = firstCol; col <= lastCol; col++) {
            UIImage *tile = [self tileAtCol:col row:row];
            if (tile)
            {
                CGRect tileRect = CGRectMake(tileSize.width * col, tileSize.height * row, tileSize.width, tileSize.height);

                tileRect = CGRectIntersection(self.bounds, tileRect);

                [tile drawInRect:tileRect];
                //        [[UIColor whiteColor] set];
                //        CGContextSetLineWidth(context, 6.0);
                //        CGContextStrokeRect(context, tileRect);
            }
        }
    }
}

- (UIImage*)tileAtCol:(int)col row:(int)row
{
    NSString *path = [NSString stringWithFormat:@"%@/%@%d_%d.png", tileDirectory, tileTag, col, row];
    return [UIImage imageWithContentsOfFile:path];  
}

@end

This is the main code of the app, you can download the entire example from the site linked on the top of the post.

As i said the main problem is the rendering of some slices that fail in the first run of the app, other runs seem works correctly.

modifing a bit - (UIImage*)tileAtCol:(int)col row:(int)row

- (UIImage*)tileAtCol:(int)col row:(int)row
{
    NSString *path = [NSString stringWithFormat:@"%@/%@%d_%d.png", tileDirectory, tileTag, col, row];
    UIImage *img = [UIImage imageWithContentsOfFile:path];

    if (img) {
        NSLog(@"good");
}
else {
    NSLog(@"bad");
    }
    return img;  
}

The problem seems to be here...

Any ideas to fix it?

Thanks in advance

Lucabro
  • 525
  • 3
  • 9
  • 30
  • 1
    run it through the profiler, you may find that various slices are failing to load as you are allocating too fast and hence the CGIMage fails to create/nils out. have a watch of "iOS App Performance: Memory" from WWDC 2012 for more info – Warren Burton Apr 04 '13 at 14:13
  • it seems that i cannot see documents wwdc2012 contents from apple site – Lucabro Apr 04 '13 at 14:57
  • If you have a free developer account you can login and then gain access to the videos. You don't need to pay anything to access the WWDC videos but you will be re-directed to iTunes. – Robotic Cat Apr 04 '13 at 16:06
  • yeah, I visited the wrong link :) thanks – Lucabro Apr 05 '13 at 07:32

0 Answers0