2010-11-01 12 views
6

Ich möchte ein CATiledLayer in iPhone OS 3.1.3 verwenden, und dazu muss alle Zeichnung in -(void)drawLayer:(CALayer *)layer inContext:(CGContext)context nur mit Core Graphics getan werden.Zeichnen in CATiledLayer mit CoreGraphics CGContextDrawImage

Jetzt laufe ich in die Probleme der gewendeten Koordinatensystem auf dem iPhone, und es gibt einige Vorschläge, wie man es beheben Transformationen mit:

Mein Problem ist, dass ich es nicht zur Arbeit bringen kann. Ich habe den PhotoScroller-Beispielcode verwendet und die Zeichenmethode nur durch coregraphics-Aufrufe ersetzt. Es sieht aus wie dieses

- (void)drawLayer:(CALayer *)layer inContext:(CGContextRef)context { 
CGContextSaveGState(context); 
CGRect rect = CGContextGetClipBoundingBox(context); 
CGFloat scale = CGContextGetCTM(context).a; 
CGContextConcatCTM(context, CGAffineTransformMakeTranslation(0.f, rect.size.height)); 
CGContextConcatCTM(context, CGAffineTransformMakeScale(1.f, -1.f)); 

CATiledLayer *tiledLayer = (CATiledLayer *)layer; 
CGSize tileSize = tiledLayer.tileSize; 


tileSize.width /= scale; 
tileSize.height /= scale; 
// calculate the rows and columns of tiles that intersect the rect we have been asked to draw 
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++) { 
    // if (row == 0) continue; 
     UIImage *tile = [self tileForScale:scale row:row col:col]; 
     CGImageRef tileRef = [tile CGImage]; 
     CGRect tileRect = CGRectMake(tileSize.width * col, tileSize.height * row, 
            tileSize.width, tileSize.height); 
     // if the tile would stick outside of our bounds, we need to truncate it so as to avoid 
     // stretching out the partial tiles at the right and bottom edges 
     tileRect = CGRectIntersection(self.bounds, tileRect); 
     NSLog(@"row:%d, col:%d, x:%.0f y:%.0f, height:%.0f, width:%.0f", row, col,tileRect.origin.x, tileRect.origin.y, tileRect.size.height, tileRect.size.width); 

     //[tile drawInRect:tileRect]; 
     CGContextDrawImage(context, tileRect, tileRef); 
// just to draw the row and column index in the tile and mark the origin of the tile with a red line   
     if (self.annotates) { 
      CGContextSetStrokeColorWithColor(context, [[UIColor whiteColor]CGColor]); 
      CGContextSetLineWidth(context, 6.0/scale); 
      CGContextStrokeRect(context, tileRect); 
      CGContextSetStrokeColorWithColor(context, [[UIColor redColor]CGColor]); 
      CGContextMoveToPoint(context, tileRect.origin.x, tileRect.origin.y); 
      CGContextAddLineToPoint(context, tileRect.origin.x+100.f, tileRect.origin.y+100.f); 
      CGContextStrokePath(context); 
      CGContextSetStrokeColorWithColor(context, [[UIColor redColor]CGColor]); 
      CGContextSetFillColorWithColor(context, [[UIColor whiteColor]CGColor]); 
      CGContextSelectFont(context, "Courier", 128, kCGEncodingMacRoman); 
      CGContextSetTextDrawingMode(context, kCGTextFill); 
      CGContextSetShouldAntialias(context, true); 
      char text[30]; 
      int length = sprintf(text,"row:%d col:%d",row,col); 
      CGContextSaveGState(context); 
      CGContextShowTextAtPoint(context, tileRect.origin.x+110.f,tileRect.origin.y+100.f, text, length); 
      CGContextRestoreGState(context); 
     } 
    } 
} 

CGContextRestoreGState(context); 

}

Wie man sehen kann ich ein Scale-Transformation bin mit dem Koordinatensystem invertieren und eine Übersetzung verwandeln den Ursprung der linken unteren Ecke zu verschieben. Die Bilder werden korrekt gezeichnet, aber nur die erste Reihe von Kacheln wird gezeichnet. Ich denke, es gibt ein Problem mit der Übersetzungsoperation oder der Art, wie die Koordinaten der Kacheln berechnet werden.

Dies ist, wie es aussieht:

ich ein wenig verwirrt bin mit all diesen Transformationen.

Bonus Frage: Wie würde man die Retina-Display-Bilder in Kerngrafiken behandeln?

EDIT: Um es auf dem Retina-Display arbeitet Ich habe gerade die ursprüngliche Methode aus dem Beispielcode die Bilder zur Verfügung zu stellen:

- (UIImage *)tileForScale:(CGFloat)scale row:(int)row col:(int)col 
{ 
// we use "imageWithContentsOfFile:" instead of "imageNamed:" here because we don't want UIImage to cache our tiles 
NSString *tileName = [NSString stringWithFormat:@"%@_%d_%d_%d", imageName, (int)(scale * 1000), col, row]; 
NSString *path = [[NSBundle mainBundle] pathForResource:tileName ofType:@"png"]; 
UIImage *image = [UIImage imageWithContentsOfFile:path]; 
return image; 
} 

Im Prinzip ist die Skalierung der Anzeige, da Kern ignoriert Die Grafik arbeitet in Pixel nicht Punkte also, wenn aufgefordert, mehr Pixel zu zeichnen, mehr CATiledLayers (oder Sublayer) werden verwendet, um den Bildschirm zu füllen.

Thanks a lot Thomas

Antwort

11

Thomas, begann ich, indem Sie die WWDC 2010 Scroll reden, und es gibt wenig oder gar keine Dokumentation über die Arbeitszeit innerhalb drawLayer:inContext für iOS 3.x. Ich hatte dieselben Probleme wie Sie, als ich den Democode von drawRect: auf drawLayer:inContext: verschoben habe.

Einige Untersuchung zeigte mir, dass innerhalb drawLayer:inContext: die Größe und Offset von rect von CGContextGetClipBoundingBox(context) zurückgekehrt ist genau das, was Sie in zeichnen möchten. Wo drawRect: Sie die ganze Grenzen gibt.

Wenn Sie dies wissen, können Sie die Zeilen- und Spalteniteration sowie die CGRect-Schnittmenge für die Edge-Kacheln entfernen und nach der Übersetzung des Kontexts einfach zum Rect zeichnen.

Hier ist, was ich am Ende habe sich mit:

- (void)drawLayer:(CALayer *)layer inContext:(CGContextRef)ctx { 
    CGRect rect = CGContextGetClipBoundingBox(ctx); 
    CGFloat scale = CGContextGetCTM(ctx).a; 

    CATiledLayer *tiledLayer = (CATiledLayer *)[self layer]; 
    CGSize tileSize = tiledLayer.tileSize; 
    tileSize.width /= scale; 
    tileSize.height /= scale; 

    int col = floorf((CGRectGetMaxX(rect)-1)/tileSize.width); 
    int row = floorf((CGRectGetMaxY(rect)-1)/tileSize.height); 

    CGImageRef image = [self imageForScale:scale row:row col:col]; 

    if(NULL != image) { 
    CGContextTranslateCTM(ctx, 0.0, rect.size.height); 
    CGContextScaleCTM(ctx, 1.0, -1.0); 
    rect = CGContextGetClipBoundingBox(ctx); 

    CGContextDrawImage(ctx, rect, image); 
    CGImageRelease(image); 
    } 
} 

Beachten Sie, dass rect nach dem TranslateCTM und ScaleCTM neu definiert wird.

Und als Referenz hier ist meine imageForScale:row:col Funktion:

- (CGImageRef) imageForScale:(CGFloat)scale row:(int)row col:(int)col { 
    CGImageRef image = NULL; 
    CGDataProviderRef provider = NULL; 
    NSString *filename = [NSString stringWithFormat:@"img_name_here%0.0f_%d_%d",ceilf(scale * 100),col,row]; 
    NSString *path = [[NSBundle mainBundle] pathForResource:filename ofType:@"png"]; 

    if(path != nil) { 
    NSURL *imageURL = [NSURL fileURLWithPath:path]; 

    provider = CGDataProviderCreateWithURL((CFURLRef)imageURL); 

    image = CGImageCreateWithPNGDataProvider(provider,NULL,FALSE,kCGRenderingIntentDefault); 
    CFRelease(provider); 
    } 
    return image; 
} 

Es ist immer noch ein bisschen Arbeit auf diesen beiden Funktionen, um zu tun richtig hochauflösende Grafiken zu unterstützen, aber es ist alles schön aussehen, aber ein iPhone 4.

+0

Danke für Ihre Antwort. Kann man nicht einfach die Methode [[UIImage imageNamed: @ "imageName"] CGImage] verwenden, um das richtige Bild für die Retina-Anzeige zu erhalten, da die UIImage-Methode die richtige Auflösung erhalten sollte? – GorillaPatch

+0

Es funktioniert wirklich gut! Vielen Dank! Ich benutze grundsätzlich die Methode aus dem Beispielcode, um die Bildkacheln bereitzustellen, so dass auf einem Retina-Display nur mehr Kacheln auf einer anderen Zoomskala angezeigt werden, um das Bild zu zeichnen. Ich habe die Methode zur ursprünglichen Frage hinzugefügt. – GorillaPatch