Ir al contenido



Foto

Transparencias


  • Por favor identifícate para responder
11 respuestas en este tema

#1 escafandra

escafandra

    Advanced Member

  • Moderadores
  • PipPipPip
  • 3.837 mensajes
  • LocationMadrid - España

Escrito 15 mayo 2010 - 04:34

Tanto delphi como builder permiten cierto grado de transparencia en el manejo de TBitmap y TImage, pero no siempre satisface las necesidades que se nos puedan plantear.

Vamos a tratar de solucionar algunos problemas simples o no tan simples.
1.- Transparencia de un color usando sólo API, sin usar las bondades de la VCL
2.- Transparencia de un color usando AlphaBlend
3.- Semitransparencias.
4.- Semitransparencias degradadas y capas.

Vallamos pues al grano.

 

1.- Transparencia de un color usando sólo API.
En ocasiones podemos encontrarnos con que necesitamos usar API pura. Partiendo de un HDC donde pintar, un HBITMAP y un color, podemos crear la transparencia. Para ello creamos un bitmap máscara. El color de transparencia lo convertiremos al blanco y el resto al negro. El resto será usar la API BitBlt con los modificadores apropiados y como base SRCINVERT y SRCAND para eliminar el color que nos interesa:
 


delphi
  1. procedure DrawTransparent(DC: HDC; Left, Top: integer; hImage: HBITMAP; TrColor: COLORREF);
  2. var
  3.   hMask: HBITMAP ;
  4.   dcImage, dcMask: HDC;
  5.   Bitmap: TagBITMAP;
  6. begin
  7.   // Crear la máscara:
  8.   // Crer bitmap monocromo: mask
  9.   GetObject(hImage, sizeof(TagBITMAP), @Bitmap);
  10.   hMask := CreateBitmap(Bitmap.bmWidth, Bitmap.bmHeight, 1, 1, nil);
  11.   dcImage := CreateCompatibleDC(0);
  12.   dcMask := CreateCompatibleDC(0);
  13.  
  14.   SelectObject(dcImage, hImage);
  15.   SelectObject(dcMask, hMask);
  16.  
  17.   // Creamos el MaskBitmap según el TrColor
  18.   SetBkColor(dcImage, TrColor);
  19.   BitBlt(dcMask, 0, 0, Bitmap.bmWidth, Bitmap.bmHeight, dcImage, 0, 0, SRCCOPY);
  20.  
  21.   // Dibujar transparente
  22.   BitBlt(DC, Left, Top, Bitmap.bmWidth, Bitmap.bmHeight, dcImage, 0,0, SRCINVERT);
  23.   BitBlt(DC, Left, Top, Bitmap.bmWidth, Bitmap.bmHeight, dcMask, 0,0, SRCAND);
  24.   BitBlt(DC, Left, Top, Bitmap.bmWidth, Bitmap.bmHeight, dcImage, 0,0, SRCINVERT);
  25.  
  26.   // Liberar los HDC y la Mascara hMask
  27.   DeleteDC(dcImage);
  28.   DeleteDC(dcMask);
  29.   DeleteObject(hMask);
  30. end;

Un ejemplo de uso partiendo de dos TImages con un TBitmap cada una:


delphi
  1. var
  2.   TrColor: COLORREF;
  3. begin
  4.   // Para compatibilizar los Bitmap podemos pasarlo a 32 bits
  5.   Image1.Picture.Bitmap.PixelFormat := pf32bit;
  6.  
  7.   // Tomamos un color que será el transparente
  8.   TrColor:= Image2.Picture.Bitmap.Canvas.Pixels[0,0];
  9.  
  10.   // Dibujamos
  11.   DrawTransparent(Image1.Canvas.Handle, 0,0, Image2.Picture.Bitmap.Handle, TrColor);
  12.   Invalidate;
  13. end;

Saludos.


  • 0

#2 escafandra

escafandra

    Advanced Member

  • Moderadores
  • PipPipPip
  • 3.837 mensajes
  • LocationMadrid - España

Escrito 15 mayo 2010 - 05:00

2.- Transparencia de un color usando AlphaBlend.

El caso anterior puede ser resuelto usando la API AlphaBlend. Esta API nos permite dar un tono de transparencia a toda la imagen o controlado por el canal alpha. Para este caso vamos a usar el canal alpha de un bitmap. Para que un bitmap tenga canal alpha es necesario que sea de 32 bits. El Byte mas alto será dicho canal.

La API AlphaBlend usa una estructura BLENDFUNCTION para configurar como la vamos a utilizar. Un valor alpha de 0 indica transparencia total y el $FF indica opacidad. Un detalle importante cuando usamos el canal alpha del Bitmap es que debemos modificar los pixels multiplicando cada componente de color por el valor alpha y dividiendo por 255. Usaré instrucciones de desplazamiento para acelerar el proceso:
 


delphi
  1. function AjustaAlphaBlend(src: DWORD): DWORD;
  2. var
  3.   alpha, R, G, B: DWORD;
  4. begin
  5.     alpha := src shr 24;
  6.     B := (src and $000000FF)*alpha shr 8;
  7.     G := (((src shr 8) and $000000FF)*alpha) and $0000FF00;
  8.     R := ((((src shr 16) and $000000FF)*alpha) shl 8) and $00FF0000;
  9.     Result:= (alpha shl 24) or R or G or B;
  10. end;

Ahora vamos a crear la transparencia:


delphi
  1. // nos creamos un tipo para facilitar el uso de punteros y arrays
  2. type ADWORD = array [0..0] of DWORD;
  3. type PADWORD = ^ADWORD;
  4.  
  5. // Este procedimiento ajusta el Bitmap para la transparencia
  6. procedure TransparentAlfa(bmp: TBitmap; TrColor: TColor);
  7. var
  8.   tc: DWORD;
  9.   pixel: PADWORD;
  10.   i: integer;
  11. begin
  12.   if bmp.Empty then exit;
  13.  
  14.   // Nos aseguramos que el bitmap va a tener canal alpha
  15.   bmp.HandleType := bmDIB;
  16.   bmp.PixelFormat := pf32bit;
  17.  
  18.   pixel:= PADWORD(bmp.ScanLine[bmp.Height-1]);
  19.   tc:= (TrColor and $FF00FF00) or (GetRValue(TrColor) shl 16) or GetBValue(TrColor);
  20.   for i:=0 to bmp.Height * bmp.Width-1 do
  21.   begin
  22.       if pixel[i] = tc then pixel[i] := 0    // Ese color será transparente
  23.       else pixel[i]:= pixel[i] or $FF000000; // Los demás serán opacos
  24.       pixel[i]:= AjustaAlphaBlend(pixel[i]); // Ajustamos todos los pixels
  25.   end;
  26. end;

Ahora vamos a dibujar con AlphaBlend:


delphi
  1. procedure DrawDegradaAlfa(Canvas: TCanvas; bmp: TBitmap);
  2. var
  3.   temp: TBitmap;
  4.   bf: BLENDFUNCTION;
  5. begin
  6.   // Usamos un bitmap temporal para no alterar el original
  7.   temp := TBitmap.Create;
  8.   temp.Assign(bmp);
  9.   DegradaAlfa(temp);
  10.   bf.BlendOp:= 0;
  11.   bf.BlendFlags:= 0;
  12.   bf.SourceConstantAlpha:= $FF;
  13.   bf.AlphaFormat:= AC_SRC_ALPHA;
  14.   Windows.AlphaBlend(Canvas.Handle, 0, 0, temp.Width, temp.Height, temp.Canvas.Handle, 0, 0, temp.Width, temp.Height, bf);
  15.   temp.Free;
  16. end;

El ejemplo de su uso partiendo de dos TImages con un TBitmap cada una:


delphi
  1. var
  2.   TrColor: TColor;
  3. begin
  4.   // Para no alterar los colores compatibilizamos los Bitmaps
  5.   Image1.Picture.Bitmap.PixelFormat := pf32bit;
  6.   TrColor:= DWORD(Image2.Picture.Bitmap.Canvas.Pixels[0,0]);
  7.   DrawTransparentAlfa(Image1.Canvas, Image2.Picture.Bitmap, TrColor);
  8.   Invalidate;
  9. end;

Saludos.


  • 0

#3 escafandra

escafandra

    Advanced Member

  • Moderadores
  • PipPipPip
  • 3.837 mensajes
  • LocationMadrid - España

Escrito 15 mayo 2010 - 05:09

3.- Semitransparencias.

Si buscamos un efecto general para todo el Bitmap, la cosa es mas sencilla. Basta con indicar un nivel de transparencia en la estructura BLENDFUNCTIONy no usar el canal alpha del bitmap.


delphi
  1. procedure DrawSemiTransparent(Canvas: TCanvas; bmp: TBitmap; Percent: integer);
  2. var
  3.   bf: BLENDFUNCTION;
  4. begin
  5.   bf.BlendOp:= AC_SRC_OVER;
  6.   bf.BlendFlags:= 0;
  7.   bf.SourceConstantAlpha:= MulDiv($FF, Percent, 100);
  8.   bf.AlphaFormat:= 0;
  9.   Windows.AlphaBlend(Canvas.Handle, 0, 0, bmp.Width, bmp.Height, bmp.Canvas.Handle, 0, 0, bmp.Width, bmp.Height, bf);
  10. end;


El ejemplo de su uso partiendo de dos TImages con un TBitmap cada una y un porcentaje de opacidad:


delphi
  1. begin
  2.   Image1.Picture.Bitmap.PixelFormat := pf32bit;
  3.   // Dibujaremos con un 20% de opacidad
  4.   DrawSemiTransparent(Image1.Canvas, Image2.Picture.Bitmap, 20);
  5.   Invalidate;
  6. end;


Saludos.
  • 0

#4 escafandra

escafandra

    Advanced Member

  • Moderadores
  • PipPipPip
  • 3.837 mensajes
  • LocationMadrid - España

Escrito 15 mayo 2010 - 05:23

4.- Semitransparencias degradadas y capas.

Para conseguir el control de la transparencia pixel a pixel, volveremos a usar la API AlphaBlend y el canal alpha. Este caso es parecido al que usamos para conseguir la transparencia de un color, sólo que ahora jugaremos con semitransparencias.

Para demostrar como hacerlo vamos a crear un efecto de degradado en la transparencia de un bitmap.

Primero ajustamos el canal alpha del bitmap:


delphi
  1. // nos creamos un tipo para facilitar el uso de punteros y arrays
  2. type ADWORD = array [0..0] of DWORD;
  3. type PADWORD = ^ADWORD;
  4.  
  5. procedure DegradaAlfa(bmp: TBitmap);
  6. var
  7.   pixel: PADWORD;
  8.   i, x, y: integer;
  9. begin
  10.   if (bmp.Empty) then exit;
  11.  
  12.   bmp.HandleType := bmDIB;
  13.   bmp.PixelFormat := pf32bit;
  14.   pixel := PADWORD(bmp.ScanLine[bmp.Height-1]);
  15.   for i:= 0 to bmp.Height * bmp.Width-1 do
  16.       pixel[i] := pixel[i] and $00FFFFFF;
  17.   for y:=0 to bmp.Height-1 do
  18.   begin
  19.       pixel:= PADWORD(bmp.ScanLine[y]);
  20.       for x:=0 to bmp.Width-1 do
  21.       begin
  22.         pixel[x] := pixel[x] or ((x shl 8) div bmp.Width) shl 24;
  23.         pixel[x] := AjustaAlphaBlend(pixel[x]);
  24.       end;
  25.   end;
  26. end;


Ahora vamos a dibujar:


delphi
  1. procedure DrawDegradaAlfa(Canvas: TCanvas; bmp: TBitmap);
  2. var
  3.   temp: TBitmap;
  4.   bf: BLENDFUNCTION;
  5. begin
  6.   temp := TBitmap.Create;
  7.   temp.Assign(bmp);
  8.   DegradaAlfa(temp);
  9.   bf.BlendOp:= 0;
  10.   bf.BlendFlags:= 0;
  11.   bf.SourceConstantAlpha:= $FF;
  12.   bf.AlphaFormat:= AC_SRC_ALPHA;
  13.   Windows.AlphaBlend(Canvas.Handle, 0, 0, temp.Width, temp.Height, temp.Canvas.Handle, 0, 0, temp.Width, temp.Height, bf);
  14.   temp.Free;
  15. end;


Y nuestro ejemplo con dos TImages:

delphi
  1. begin
  2.   Image1.Picture.Bitmap.PixelFormat := pf32bit;
  3.   DrawDegradaAlfa(Image1.Canvas, Image2.Picture.Bitmap);
  4.   Invalidate;
  5. end;


Para conseguir transparencias y semitransparencias en distintas capas procederemos de la misma manera manejando el canal alpha pixel a pixel. En este caso será el diseño original del bitmap el que establecerá la transparencia y para ello podemos partir de archivos png. Las bases para hacerlo están dadas ;).

Espero haber podido aclarar algunas ideas sobre estos temas y que los ejemplos dados sean de utilidad.

Saludos.
  • 0

#5 Delphius

Delphius

    Advanced Member

  • Administrador
  • 6.248 mensajes
  • LocationArgentina

Escrito 15 mayo 2010 - 09:15

Hola,
¡Gracias escafandra!

Tengo que "jugar" un rato con el código que expones, se ve interesante. Y muy seguramente, bien moderno y cool.

Saludos,
  • 0

#6 egostar

egostar

    missing my father, I love my mother.

  • Administrador
  • 13.948 mensajes
  • LocationMéxico

Escrito 15 mayo 2010 - 09:40

Este es un tema muy bueno, yo estoy usando una ventanita de dialogo que me creé pero la transparencia solo es en el form y no en todos las capas. Ya le daré uso a esto, muchas gracias escafandra ;)

Salud OS

Archivos adjuntos


  • 0

#7 escafandra

escafandra

    Advanced Member

  • Moderadores
  • PipPipPip
  • 3.837 mensajes
  • LocationMadrid - España

Escrito 15 mayo 2010 - 11:51

Aquí os pongo unas muestras de los efectos conseguidos con el código que he escrito. Concretamente para transparencia global y degradada de un bitmap:

Imagen Enviada
Imagen Enviada


Saludos.
  • 0

#8 egostar

egostar

    missing my father, I love my mother.

  • Administrador
  • 13.948 mensajes
  • LocationMéxico

Escrito 15 mayo 2010 - 08:38

wow !!! , que bien se ve, si que esta muy interesante eset asunto (y)

Salud OS
  • 0

#9 Rolphy Reyes

Rolphy Reyes

    Advanced Member

  • Moderadores
  • PipPipPip
  • 2.091 mensajes
  • LocationRepública Dominicana

Escrito 17 mayo 2010 - 03:38

Saludos.

A mí parecer este tema aplica para ser un manual o tutorial.

¿Qué les parece?
  • 0

#10 escafandra

escafandra

    Advanced Member

  • Moderadores
  • PipPipPip
  • 3.837 mensajes
  • LocationMadrid - España

Escrito 17 mayo 2010 - 03:56

A mí parecer este tema aplica para ser un manual o tutorial.


Cuando decidí publicar el tema, pensé si ponerlo en el foro Gráficos o en Tutoriales. De hecho le di cierta forma de manual o tutorial porque el tema del canal alpha de los bitmaps y el control de la transparencia pixel a pixel está poco documentado y quería arrojar un poco de luz sobre el tema. Así que aproveche para estructurar el hilo como un manual.

Por otro lado quería darle vidilla al foro de Gráficos y al final lo publiqué en él.

Si os parece mejor, no tengo inconveniente en moverlo o que sea movido a Tutoriales

Saludos.
  • 0

#11 escafandra

escafandra

    Advanced Member

  • Moderadores
  • PipPipPip
  • 3.837 mensajes
  • LocationMadrid - España

Escrito 12 octubre 2010 - 05:45

He pensado, como en su día sugirió Rolphy Reyes mover este tema a tutoriales.

Saludos.
  • 0

#12 egostar

egostar

    missing my father, I love my mother.

  • Administrador
  • 13.948 mensajes
  • LocationMéxico

Escrito 12 octubre 2010 - 07:06

He pensado, como en su día sugirió Rolphy Reyes mover este tema a tutoriales.

Saludos.


Sin duda, un excelente tutorial (y)

Salud OS
  • 0