Introdução
No post anterior (Bitmaps em AdvPL – Parte 08) vimos o método FlipV(), para inverter verticalmente uma imagem. Agora, vamos ver como fazer um resample ou resize da imagem usando a classe zBitmap 😀
Método Resize()
Muitas vezes precisamos mexer nas dimensões de uma imagem, aumentando ou diminuindo proporcionalmente sua dimensão, ou alterar em proporções diferentes horizontal e verticalmente a imagem. O Windows PaintBrush — entre inúmeros outros editores de imagem — possui esse recurso, permitindo o RESIZE da imagem, mantendo ou não a proporção entre os ajustes, por percentual ou por pixels.
A primeira versão do método Resize() da classe zBitmap recebe como parâmetros por hora o percentual de aumento ou redução, com base no tamanho atual (100%) horizontal e vertical, respectivamente. 100% não mexe na imagem, < 100% redux a imagem, e maior que 100% aumenta a imagem
Por exemplo, uma imagem com resolução de 400×280 pixels (colunas x linhas), ao ser reduzida horizontal e verticalmente em 50%, terá um tamanho total de 1/4 da imagem original, e ficará com 200×140 pontos. Por outro lado, se especificarmos um percentual de, por exemplo, 120%, ela será aumentada em 20% do seu tamanho original, indo para 480×336 pixels.
Como aumentar ou reduzir
A forma mais simples é criar uma nova imagem em memória, com os novos tamanhos proporcionais aos percentuais informados, e depois fazer dois loops encadeados, para definir todos os pontos da imagem nova usando como base a imagem antiga.
A sacada da operação é definir um passo proporcional de incremento de coluna e de linha para varrer a imagem antiga, contando que a imagem nova terá sempre passo 1 (todos os seus pontos serão atribuídos).
Por exemplo, um aumento para 200% — dobrar as dimensões horizontal e vertical — de uma imagem de 320×200 (colunas x linhas) vai gerar uma imagem de 640×400. Logo, criamos dois loops encadeados para varrer todas as linhas e colinas da imagem NOVA, e usamos duas variáveis sincronizadas, iniciando da primeira coluna e na primeira linha da imagem atual, usando um passo de incremento de coluna de 0,5 (obtido dividindo o tamanho horizontal atual pelo novo tamanho — 320 / 640 = 0.5), e o mesmo passo de 0,5 para o passo vertical ( 200/400 = 0.5).
Ao iniciar o processamento dos pontos das colunas da primeira linha da imagem nova, ambas começam lendo o valor da linha 1 e coluna 1 da imagem atual, e atribuindo seu valor na linha 1 coluna 1 da imagem nova. Para ir para a próxima coluna da imagem nova, incrementamos o valor da nova coluna em uma unidade, e o valor da coluna da imagem atual em 0,5, sendo assim a coluna 1.5 da imagem atual será lida. Porém, como não existe tal coluna, o acesso ao array despreza as casas decimais, e vai acessar novamente a primeira coluna da tabela atual, e atribuir seu valor na coluna 2 da nova imagem. Na próxima iteração, a coluna 1,5 da tabela atual é incrementada em 0,5, e passa a ser 2, enquanto a coluna da nova tabela será a 3. Assim, sucessivamente, a cada par de pontos nesta linha da imagem nova será atribuída com o valor de uma coluna da imagem atual, dobrando o tamanho da linha. O mesmo acontece com as linhas, usando esse mecanismo de incremento.
No caso de uma redução, fica mais simples ainda de entender o que se passa. Ao reduzir em 50% o tamanho da imagem, a nova imagem terá um looping para varrer apenas metade dos pontos de cada linha da imagem atual. Para que os pontos sejam considerados em toda a sua extensão, o passo será 2. Isto é, para cada novo ponto a ser transferido em uma coluna da nova tabela, a anterior vai pular uma coluna 😀
Essa abordagem, tanto para aumentar como parra diminuir uma imagem, é o mais simples de ser aplicado, mas tem suas vantagens e desvantagens. Por exemplo, uma imagem com uma moldura de um ponto nas bordas, e um grid de pontos com intervalo de uma linha e uma coluna, parecendo um waffle, se for reduzida por este método em 50%, vai acabar pulando todas as linhas e colunas pares, que teriam pontos brancos, e vai pegar todas as linhas ímpares — e a imagem final reduzida fica inteirinha preta.
Usando o Paintbrush, por exemplo, o algoritmo de redução tenta minimizar estas perdas, trocando alguns pontos de cor, para tentar pelo menos manter o aspecto geral da imagem anterior. Veja por exemplo as imagens abaixo:
A primeira imagem da esquerda é um zoom em uma imagem 16×16 bits, monocromática. A segunda imagem corresponde à primeira imagem, reduzida em 50% usando o código AdvPL, e a terceira imagem é um zoom da mesma redução feita pelo PaintBrush. Ele tomou a liberdade de transformar a imagem de monocromática para Grayscale (tons de cinza), para tentar manter o aspecto da imagem original. Já o resize para aumentar a imagem comportou-se de forma igual entre o PaintBrush e a zBitmap. Os pontos da imagem foram “duplicados” horizontal e verticalmente.
Vamos ao fonte
METHOD Resize(nPctH, nPctV) CLASS ZBITMAP Local nNewWidth Local nNewHeight Local nNewX := 1 Local nNewY := 1 Local nOldX Local nOldY Local nStepNewX Local nStepNewY Local nStepOldX Local nStepOldY Local oNewBMP If nPctH = 100 .and. nPctV = 100 Return .T. Endif nNewWidth := ::nWidth * (nPctH / 100) nNewHeight := ::nHeight * (nPctV / 100) nStepOldX := ::nWidth / nNewWidth nStepNewX := 1 nStepOldY := ::nHeight / nNewHeight nStepNewY := 1 // Cria a nova imagem usando a própria classe oNewBMP := zBitmap():New( nNewWidth , nNewHeight , ::nBPP ) nNewX := 1 nNewY := 1 nOldX := 1 nOldY := 1 // Varre a imagem nova com passo 1, e a antiga // com o passo determinado pela proporção entre as imagens While int(nOldY) <= ::nHeight .and. int(nNewY) <= nNewHeight nOldX := 1 nNewX := 1 While int(nOldX) <= ::nWidth .and. int(nNewX) <= nNewWidth oNewBMP:aMatrix[nNewY][nNewX] := ::aMatrix[nOldY][nOldX] nOldX += nStepOldX nNewX += nStepNewX Enddo nOldY += nStepOldY nNewY += nStepNewY Enddo // Copia para a imagem atual todos os dados e propriedades // da nova imagem gerada ::nFileSize := oNewBMP:nFileSize ::nHeight := oNewBMP:nHeight ::nWidth := oNewBMP:nWidth ::aMatrix := aClone(oNewBMP:aMatrix) ::aClipBoard := aClone(oNewBMP:aClipBoard) ::aColors := aClone(oNewBMP:aColors) ::cFormat := oNewBMP:cFormat ::nOffSet := oNewBMP:nOffSet ::nRawData := oNewBMP:nRawData ::nRowSize := oNewBMP:nRowSize ::nHeadSize := oNewBMP:nHeadSize ::nCompress := oNewBMP:nCompress ::nColorPlanes := oNewBMP:nColorPlanes ::nHRes := oNewBMP:nHRes ::nVRes := oNewBMP:nVRes ::nColorPal := oNewBMP:nColorPal ::nImpColors := oNewBMP:nImpColors ::nFRColor := oNewBMP:nFRColor ::nBgColor := oNewBMP:nBgColor // joga a nova imagem de uso temporário fora e retorna freeobj(oNewBMP) Return .T.
Com este recurso na classe zBitmap, você pode por exemplo criar um QRCode ou um zBarCode128, originalmente com o menor tamanho possível (1 pixel por módulo), e então utilizar o método Resize() para criar uma imagem final com mais pontos, para ser utilizada para visualização em tela como para impressão.
É recomendado fortemente que, para que não hajam distorções no aumento da imagem, que as proporções sejam sempre múltiplas de 100% — 200%, dobra o tamanho, 300% triplica, e assim por diante. Caso a proporção de aumento seja um fator multiplicativo quebrado, como estamos partindo de uma imagem onde cada pixel conta, pode haver alteração da proporção entre as barras de um código, o que pode levar a um leitor simplesmente não conseguir ler corretamente a imagem impressa.
Conclusão
Para cada cenário existe uma solução adequada. Se esta solução se encaixa no seu cenário, baixe a ZLIB e seja feliz !!!
E desejo novamente a todos TERABYTES DE SUCESSO !!!
Referências