Pergunta

Para parte da minha aplicação, preciso criar uma imagem de uma determinada visualização e de todas as suas subvisualizações.

Para fazer isso, estou criando um contexto que envolve um bitmap com o mesmo tamanho da visualização, mas não tenho certeza de como desenhar a hierarquia da visualização nele.Posso desenhar uma única visualização apenas definindo o contexto e chamando drawRect explicitamente, mas isso não lida com todas as subvisualizações.

Não consigo ver nada na interface do NSView que possa ajudar com isso, então suspeito que a solução possa estar em um nível superior.

Foi útil?

Solução

Descobri que escrever o código do desenho sozinho era a melhor maneira de:

  • lidar com possíveis problemas de transparência (algumas das outras opções adicionam um fundo branco a toda a imagem)
  • o desempenho foi muito melhor

O código abaixo não é perfeito, porque não lida com problemas de escala ao passar dos limites para os quadros, mas leva em consideração o estado isFlipped e funciona muito bem para o que eu usei.Observe que ele desenha apenas as subvisualizações (e as subsubvisualizações,...recursivamente), mas fazer com que ele também se desenhe é muito fácil, basta adicionar um [self drawRect:[self bounds]] na implementação de imageWithSubviews.

- (void)drawSubviews
{
    BOOL flipped = [self isFlipped];

    for ( NSView *subview in [self subviews] ) {

        // changes the coordinate system so that the local coordinates of the subview (bounds) become the coordinates of the superview (frame)
        // the transform assumes bounds and frame have the same size, and bounds origin is (0,0)
        // handling of 'isFlipped' also probably unreliable
        NSAffineTransform *transform = [NSAffineTransform transform];
        if ( flipped ) {
            [transform translateXBy:subview.frame.origin.x yBy:NSMaxY(subview.frame)];
            [transform scaleXBy:+1.0 yBy:-1.0];
        } else
            [transform translateXBy:subview.frame.origin.x yBy:subview.frame.origin.y];
        [transform concat];

        // recursively draw the subview and sub-subviews
        [subview drawRect:[subview bounds]];
        [subview drawSubviews];

        // reset the transform to get back a clean graphic contexts for the rest of the drawing
        [transform invert];
        [transform concat];
    }
}

- (NSImage *)imageWithSubviews
{
    NSImage *image = [[[NSImage alloc] initWithSize:[self bounds].size] autorelease];
    [image lockFocus];
    // it seems NSImage cannot use flipped coordinates the way NSView does (the method 'setFlipped:' does not seem to help)
    // Use instead an NSAffineTransform
    if ( [self isFlipped] ) {
        NSAffineTransform *transform = [NSAffineTransform transform];
        [transform translateXBy:0 yBy:NSMaxY(self.bounds)];
        [transform scaleXBy:+1.0 yBy:-1.0];
        [transform concat];
    }
    [self drawSubviews];
    [image unlockFocus];
    return image;
}

Outras dicas

Você pode usar -[NSView dataWithPDFInsideRect:] para renderizar toda a hierarquia da visualização para a qual você a envia em um PDF, retornado como um NSData objeto.Você pode então fazer o que quiser com isso, inclusive renderizá-lo em um bitmap.

Tem certeza de que deseja uma representação de bitmap?Afinal, esse PDF poderia ser (pelo menos em teoria) independente da resolução.

Você pode usar -[NSBitmapImageRep initWithFocusedViewRect:] depois de bloquear o foco em uma visualização para que a visualização seja renderizada (e suas subvisualizações) no retângulo fornecido.

O que você deseja fazer já está disponível explicitamente.Consulte a seção "API de redirecionamento de desenho NSView" nas notas de versão 10.4 do AppKit.

Faça um NSBitmapImageRep para armazenamento em cache e limpe-o:

NSGraphicsContext *bitmapGraphicsContext = [NSGraphicsContext graphicsContextWithBitmapImageRep:cacheBitmapImageRep];
[NSGraphicsContext saveGraphicsState];
[NSGraphicsContext setCurrentContext:bitmapGraphicsContext];
[[NSColor clearColor] set];
NSRectFill(NSMakeRect(0, 0, [cacheBitmapImageRep size].width, [cacheBitmapImageRep size].height));
[NSGraphicsContext restoreGraphicsState];

Cache para ele:

-[NSView cacheDisplayInRect:toBitmapImageRep:]

Se você quiser desenhar de forma mais geral em um contexto especificado, manipulando a recursão e a transparência corretamente,

-[NSView displayRectIgnoringOpacity:inContext:]

Sim, para esse uso específico, os dados de imagem não dimensionados são exatamente o que preciso.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top