cocoacatiledlayer

CATiledLayer artifacts at high scale


I am using CATiledLayer to draw map tiles and when zooming it, I start to see some artifacts, which I guess, are caused by precision problem.

I scale my CATiledLayer up to 2^18 (262'144x), the artifacts start to appear at about 15'000x

I thought CoreAnimation would be smart enough to handle those large scales, but apparently not.

Before I start writing my tile engine implementation from scratch to handle this, I was wondering if you had any suggestion.

This is how I setup my layer:

self.tiledLayer.tileSize = CGSizeMake(256, 256);
self.tiledLayer.levelsOfDetail = 1;
self.tiledLayer.levelsOfDetailBias = 10000;

My example, drawing code is really simple:

- (void)drawLayer:(CATiledLayer *)layer inContext:(CGContextRef)context
{
    CGRect rect = CGContextGetClipBoundingBox(context);
    CGContextSetFillColorWithColor(context, [NSColor colorWithCalibratedRed:((CGFloat)rand() / (CGFloat)RAND_MAX) green:((CGFloat)rand() / (CGFloat)RAND_MAX) blue:0 alpha:1].CGColor);
    CGContextFillRect(context, rect);
}

This is how I set the scale on the layer:

CGAffineTransform t = CGAffineTransformIdentity;

CGFloat scale = pow(2, self.zoomLevel); // self.zoomLevel ranges from 0 to 18
t = CGAffineTransformScale(t, scale, scale);

NSLog(@"%g", scale);

self.tiledLayer.affineTransform = t;

NOTE: I discovered that the artifacts appears only "far from the origin" of the layer. If I look the tiles near the origin, they are not deformed, if I look far from the origin (screenshot if from the center) it get worse.

Here is an example of the artifacts I see:

artefacts


Solution

  • Ok, I just found a very simple solution while cooking, just modulo the scale to something reasonable and loop with a hint to the drawing code. So for example when the scale hits 64, reset it to 1 and give 64 to the drawing code, so when the scale is 2, the drawing code will pick tiles for the zoom at 128.

    I will try this tomorrow and see how I can work with CATiledLayer caching mechanism to ensure a smooth experience.

    EDIT: I just took a few minutes to try this method, but I effectively got a problem with caching, as when I set the scale back to a preview value, drawInContext isn't called again. I tried calling setNeedsDisplay, but it kills the smoothness of the zoom and makes everything super slow.

    EDIT 2: After tinkering with this solution for a few hours, I was unable to get a smooth zooming and panning. I tried a simplistic approach drawing my tiles in a single NSView drawRect:, it works, but would require significant work to be smooth and fast. So I'm keeping this question open to suggestions.

    EDIT 3: I finally wrote my own implementation from scratch that doesn't have this bug.