2011-11-21 6 views
5

Estoy usando GR32 para dibujar múltiples imágenes PNG semitransparentes. Hasta ahora he estado utilizando el siguiente método:Delphi, GR32 + PngObject: la conversión a Bitmap32 no funciona como se esperaba

png:= TPNGObject.Create; 
    png.LoadFromFile(...); 
    PaintBox321.Buffer.Canvas.Draw(120, 20, png); 

sin embargo yo quería cambiar al método propuesto en el sitio web GR32 (http://graphics32.org/wiki/FAQ/ImageFormatRelated):

tmp:= TBitmap32.Create; 
    LoadPNGintoBitmap32(tmp, ..., foo); 
    tmp.DrawMode:= dmBlend; 
    PaintBox321.Buffer.Draw(Rect(20, 20, 20+ tmp.Width, 20+tmp.Height), 
    tmp.ClipRect, tmp); 

Mientras que el primer método funciona perfectamente bien, el segundo - que debería dar el mismo resultado - causa un problema muy extraño con el canal alfa, vea la imagen (que también muestra una comparación con la misma imagen "arreglada" en Paint.NET - tanto el fondo como el ícono se abrieron en las capas del editor)) La imagen muestra que Bitmap32 se carga o dibuja de manera apropiada. ¿Algun consejo?

Problem with TBitmap32 alpha channel

- Agregado 22 Nov

me he dado cuenta de que no se trata de dibujo, se trata de carga PNG a BMP32. Guardar de BMP32 a PNG genera la imagen PNG incorrecta, "blanqueada" (la de la izquierda).

+1

supongo que es debido a la "opacidad", también se ha configurado "tmp.DrawMode: = dmBlend;", no he utilizado GR32, pero yo supongo que la diferencia se debe a la opacidad. – ComputerSaysNo

+1

@Dorin Duminica, no lo es. El ejemplo en su sitio web muestra que el modo debe ser dmBlend si hay alguna transparencia en la imagen PNG cargada. Como sé que todas mis imágenes son transparentes, no tengo que verificarlo. – migajek

Respuesta

9

La razón parece ser que la transparencia se aplica dos veces a la imagen cuando se carga con LoadPNGintoBitmap32, dándole un aspecto más transparente y grisáceo (más sobre esto más adelante).

En primer lugar la transparencia:

Este es el código de la original LoadPNGintoBitmap32, las partes críticas están marcados con los comentarios:

PNGObject := TPngObject.Create; 
PNGObject.LoadFromStream(srcStream); 

destBitmap.Assign(PNGObject); // <--- paint to destBitmap's canvas with transparency (!) 
destBitmap.ResetAlpha;   

case PNGObject.TransparencyMode of // <--- the following code sets the transparency again for the TBitmap32 
{ ... } 

El destBitmap.Assign hace internamente la misma medida que en su enfoque anterior: que deje de la imagen PNG se pinta a sí misma en su lienzo. Esta operación respeta el canal alfa del PNG. ¡Pero esto no es necesario, ya que el canal alfa está asignado a los píxeles de TBitmap32 en un segundo paso!

Ahora cambiar el código de la siguiente manera, las partes críticas están marcados con el nuevo comentarios:

PNGObject := TPngObject.Create; 
PNGObject.LoadFromStream(srcStream); 

PNGObject.RemoveTransparency; // <--- paint PNG without any transparency... 
destBitmap.Assign(PNGObject); // <--- ...here 
destBitmap.ResetAlpha; 

srcStream.Position:=0; 
PNGObject.LoadFromStream(srcStream); // <--- read the image again to get the alpha channel back 

case PNGObject.TransparencyMode of // <--- this is ok now, the alpha channel now only exists in the TBitmap32 
{ ... } 

La solución anterior es ineficiente porque lee la imagen dos veces. Pero muestra por qué su segundo enfoque produce una imagen más transparente.

Y para la greyishness: Hay un problema más en el código original: destBitmap.Assign primero llena el fondo con clWhite32, luego pinta la imagen de forma transparente en él. Y luego viene LoadPNGintoBitmap32 y agrega otra capa de transparencia encima.

+0

gracias, esta debería ser una manera de respuesta perfecta, no solo el código corregido, sino también la explicación detallada de lo que estaba mal :) – migajek

+1

Jeje, de nada :) ¡Fue una pregunta interesante y bien presentada! –

0

El problema puede ser que el PNG se haya convertido incorrectamente a TBitmap32, perdiendo la información de transparencia en tránsito. Es un caso común con imágenes PNG con paleta. De lo contrario, no tendría que usar "Bitmap.DrawMode: = dmTransparent" y "OuterColor". Si la información transparencry de PNG se hubiera transferido correctamente a TBitmpa32, DrawMode: = dmBlend habría funcionado, sin la necesidad de establecer OuterColor.

Lo que más importa es cómo cargar un archivo PNG en el TBitmap32. El TPngImage desde Vcl.Imaging.La unidad pngimage (implementada en Delphi XE2 y posterior) puede dibujar transparentemente en mapas de bits, preservando lo que estaba en esos mapas de bits, combinando colores usando la capa alfa PNG, etc., pero no permite convertir fácilmente varios formatos de transparencia PNG (incluida la paleta) en el componente alfa de cada píxel de TBitmap32. Una vez que TPngImage ha dibujado una imagen, obtienes el RGB combinado para cada píxel, pero el componente alfa no se transfiere al mapa de bits objetivo.

Hay rutinas de ayuda disponibles que intenta cargar un archivo PNG en un TBitmap32 con transparencia, pero tienen inconvenientes:

(1) “LoadPNGintoBitmap32” de http://graphics32.org/wiki/FAQ/ImageFormatRelated - se aplica la transparencia en dos ocasiones, por lo que las imágenes con los valores alfa distintos de 0 o 255 se verán de forma diferente que en otro software (más notable en las imágenes translúcidas con efectos de cristal). Este código primero aplicará alfa a RGB y luego establecer alfa como una capa separada, por lo que cuando pintes, alfa se aplicará nuevamente. Puede encontrar más información sobre este tema aquí: Delphi, GR32 + PngObject: converting to Bitmap32 doesn't work as expected . Además de eso, no convierte correctamente la transparencia de las imágenes con paleta en la capa alfa de TBitmap32. Establecen manualmente la transparencia alfa para los píxeles de un cierto color del mapa de bits de salida (renderizado a RGB) en lugar de hacerlo a RGB, por lo que la transparencia real se pierde como en su imagen de muestra cuando todos los píxeles blancos son transparentes.

(2) “LoadBitmap32FromPNG” de la biblioteca gr32ex: https://code.google.com/archive/p/gr32ex/ - una implementación ligeramente diferente del mismo algoritmo que (1), y tiene los mismos problemas que (1).

Por lo tanto, las soluciones son:

  1. No utilice TBitmap32; use Vcl.Imaging.pngimage.TPngImage dibuje directamente en el mapa de bits del objetivo (pantalla, etc.): esta es la forma más compatible que maneja correctamente varios formatos PNG.
  2. Use un enrutador auxiliar para transferir información de transparencia de Vcl.Imaging.pngimage.TPngImage a TBitmap32.
  3. Utilice la biblioteca GR32 PNG que puede cargar de forma nativa un archivo PNG en TBitmap32 https://sourceforge.net/projects/gr32pnglibrary/ Dado que ahora tiene toda la información sobre este tema, puede obtener la solución adecuada para usted.

cómo cargar la capa alfa en una sola pasada

Heinrich Ulbricht hizo una buena sugerencia para eliminar la capa de transparencia antes de doler y después de leer la imagen de nuevo. Para evitar cargar la imagen dos veces, puede guardar la capa alfa antes de llamar a PNGObject.RemoveTransparency. Aquí está el código que aplica correctamente la capa alfa y carga la imagen solo una vez. Desafortunadamente, no funciona con imágenes con paleta. Si sabe cómo rellenar correctamente la capa alfa de TBitmap32 desde cualquier imagen con paleta, sin los efectos descritos en Transparent Png to TBitmap32, por favor avíseme.

procedure LoadPNGintoBitmap32(DstBitmap: TBitmap32; SrcStream: TStream; out AlphaChannelUsed: Boolean); 
var 
    PNGObject: TPngImage; 
    PixelPtr: PColor32; 
    AlphaPtr: PByte; 
    SaveAlpha: PByte; 
    I, AlphaSize: Integer; 
begin 
    AlphaChannelUsed := False; 
    PNGObject := TPngImage.Create; 
    try 
    PNGObject.LoadFromStream(SrcStream); 
    AlphaPtr := PByte(PNGObject.AlphaScanline[0]); 
    if Assigned(AlphaPtr) then 
    begin 
     AlphaSize := PNGObject.Width * PNGObject.Height; 
     if AlphaSize <= 0 then raise Exception.Create('PNG files with zero dimensions are not supported to be loaded to TBitmap32'); 
     GetMem(SaveAlpha, AlphaSize); 
     try 
     Move(AlphaPtr^, SaveAlpha^, AlphaSize); 
     PNGObject.RemoveTransparency; 
     DstBitmap.Assign(PNGObject); 
     DstBitmap.ResetAlpha; 
     PixelPtr := PColor32(@DstBitmap.Bits[0]); 
     AlphaPtr := SaveAlpha; 
     for I := 0 to AlphaSize-1 do 
     begin 
      PixelPtr^ := (PixelPtr^ and $00FFFFFF) or (TColor32(AlphaPtr^) shl 24); 
      Inc(PixelPtr); 
      Inc(AlphaPtr); 
     end; 
     finally 
     FreeMem(SaveAlpha, AlphaSize); 
     end; 
     AlphaChannelUsed := True; 
    end else 
    if PNGObject.TransparencyMode = ptmNone then 
    begin 
     DstBitmap.Assign(PNGObject); 
    end else 
    begin 
     raise Exception.Create('Paletted PNG images are not supported in LoadPNGintoBitmap32, transparency cannot be stored to TBitmap32'); 
    end; 
    finally 
    FreeAndNil(PNGObject); 
    end; 
end; 
Cuestiones relacionadas