5.7. Composição de imagens

As primitivas de desenho da página anterior pintam marcas geométricas numa imagem – uma linha, um retângulo, um fragmento de texto. Isso cobre a maior parte das anotações que um algoritmo precisa de tornar visíveis, mas não todas. Por vezes a anotação é em si mesma uma imagem: um fotograma de referência capturado para mostrar lado a lado com o atual, uma miniatura de uma captura anterior apresentada num canto da pré-visualização, ou um modelo previamente armazenado visualizado sobre um fotograma em direto para calibração. O mecanismo para desenhar uma imagem sobre outra é um único método – draw_image() – com parâmetros suficientes para gerir a posição, o escalonamento, a paleta de cores e a transparência que uma composição real necessita.

5.7.1. A chamada básica

Na sua forma mais simples, draw_image recebe outra Image e uma posição onde desenhá-la:

reference = image.Image("/sdcard/reference.bmp")
img.draw_image(reference, x=10, y=10)

O destino é img; a fonte é reference; o pixel superior esquerdo da fonte fica em (10, 10) de img, e os restantes pixels da fonte seguem para a direita e para baixo a partir daí. Os pixels do destino que a fonte cobre são substituídos pelos pixels correspondentes da fonte; os pixels fora da área da fonte ficam intocados.

Se a fonte ultrapassar o limite do destino, as partes que ficam de fora são silenciosamente cortadas – o mesmo comportamento tolerante que set_pixel apresenta para posições fora do intervalo. O código da aplicação não tem de limitar a posição às dimensões da imagem antecipadamente; pode passar a posição desejada e deixar que o método trate do corte.

5.7.2. Carregamento de ficheiro inline

draw_image aceita um caminho de ficheiro em vez do argumento Image e carrega o ficheiro antes de o compor:

img.draw_image("/sdcard/reference.bmp", x=10, y=10)

Parece uma conveniência – uma linha em vez de duas – e é-o, mas a diferença é mais do que sintaxe. Construir uma Image a partir de um ficheiro aloca um buffer para guardar os pixels descodificados, e esse buffer subsiste até que o recolhedor de lixo o liberte. Passar o caminho diretamente para draw_image permite que o módulo descodifique o ficheiro num buffer temporário, componha a partir dele e liberte o buffer quando a chamada retorna, sem que o código da aplicação tenha de manter uma referência a uma Image separada entre fotogramas.

5.7.3. Escalonamento

Quando a fonte e o destino têm tamanhos diferentes – uma captura de baixa resolução a ser composta sobre uma tela de maior resolução, ou uma miniatura que precisa de ser dimensionada para uma determinada fração do fotograma – dois parâmetros de escala tratam do redimensionamento da fonte à medida que é desenhada:

img.draw_image(reference, x=10, y=10, x_scale=2.0, y_scale=2.0)

x_scale e y_scale são floats independentes; passar ambos com o mesmo valor escala uniformemente, e passar valores diferentes estica ou encolhe a fonte ao longo de um eixo. O escalonamento ocorre no momento do desenho; a fonte reference não é modificada.

Uma máscara de bits de flags de dica decide como o escalonamento interpola entre pixels. image.BILINEAR produz resultados mais suaves ao custo de mais computação; image.BICUBIC produz resultados ainda mais suaves e custa mais; o padrão usa o vizinho mais próximo, que é o mais económico e a escolha certa quando a fonte já está na resolução de pixel do destino. As flags de gestão de aspeto – SCALE_ASPECT_KEEP, SCALE_ASPECT_EXPAND, SCALE_ASPECT_IGNORE – decidem o que fazer quando a proporção da fonte não corresponde ao retângulo em que está a ser desenhada.

5.7.4. Mistura alfa

Por defeito, draw_image substitui os pixels do destino pelos pixels da fonte. Quando o objetivo é uma sobreposição translúcida – para que o destino apareça através da fonte – o parâmetro alpha controla como os dois são misturados. alpha=0 mostra apenas o destino (sem fonte); alpha=255 é o padrão e mostra apenas a fonte (substituição total); valores intermédios misturam os dois proporcionalmente:

img.draw_image(overlay, x=0, y=0, alpha=128)

O argumento separado alpha_palette é o único mecanismo de alfa por pixel do módulo. Recebe uma imagem GRAYSCALE cujos valores são usados como alfa na posição correspondente na fonte – um mapa de calor cujo alfa varia com a sua intensidade, por exemplo. O alfa tem de ser fornecido como esse argumento em escala de cinzentos separado; uma imagem fonte que tenha o seu próprio canal alfa (um PNG com transparência, por exemplo) não o traz automaticamente.

5.7.5. ROI da fonte e paleta

Dois parâmetros adicionais completam o mecanismo de composição:

  • roi=(x, y, w, h) restringe a fonte a um sub-retângulo de si mesma, de forma a que apenas esse retângulo seja composto sobre o destino. Útil para cortar dentro da mesma chamada, sem preparar um intermediário cortado.

  • color_palette substitui o valor de cada pixel da fonte através de uma tabela de consulta antes de desenhar – o mesmo mecanismo que to_rainbow() e to_ironbow() usam, exposto aqui para que uma sobreposição possa ser paletizada no caminho para o destino sem uma passagem de conversão separada.

Ambos compõem com tudo o mais na chamada: o escalonamento, o alfa, o argumento mask do lado do destino e o parâmetro roi do lado do destino que limita a escrita a um retângulo do destino.