2012-04-26 18 views
10

general¿Cómo pintar en un lienzo con transparencia y opacidad?

Desde la biblioteca GR32, estoy usando TImgView32 para hacer una rejilla que va a ser mi fondo transparente así:

enter image description here

coloca dentro de la TImgView32 Tengo un TImage regular, donde se le dibujo en el lienzo, algo como esto:

enter image description here

Tarea

Lo que me gustaría lograr es la capacidad de establecer la opacidad del pincel, permitiendo más posibilidades de edición de imágenes en mi programa. En lugar de tener un solo color plano se dibuja, establecer la opacidad del pincel permite a diferentes niveles de profundidad de color, etc.

Me encontraron esta pregunta anterior mientras que la búsqueda en torno a: Draw opacity ellipse in Delphi 2010 - Andreas Rejbrand ha proporcionado algunos ejemplos en su respuesta para eso pregunta.

He visto lo que hizo Andreas, y se me ocurrió mi propio intento simplificado, pero estoy atascado con un problema. Echar un vistazo a estas dos imágenes siguientes, la primera es con el fondo transparente y el segundo con un fondo negro para mostrar el problema más clara:

enter image description here enter image description here

Como se puede ver, en el cepillo (círculo) es un cuadrado realmente molesto del que no me puedo deshacer. Todo lo que debería ser visible es el pincel. Este es mi código utilizado para producir esos resultados:

procedure DrawOpacityBrush(ACanvasBitmap: TBitmap; X, Y: Integer; 
    AColor: TColor; ASize: Integer; Opacity: Integer); 
var 
    Bmp: TBitmap; 
begin 
    Bmp := TBitmap.Create; 
    try 
    Bmp.SetSize(ASize, ASize); 
    Bmp.Transparent := False; 

    with Bmp.Canvas do 
    begin 
     Pen.Color := AColor; 
     Pen.Style := psSolid; 
     Pen.Width := ASize; 
     MoveTo(ASize div 2, ASize div 2); 
     LineTo(ASize div 2, ASize div 2); 
    end; 

    ACanvasBitmap.Canvas.Draw(X, Y, Bmp, Opacity); 
    finally 
    Bmp.Free; 
    end; 
end; 

procedure TForm1.Image1MouseDown(Sender: TObject; Button: TMouseButton; 
    Shift: TShiftState; X, Y: Integer); 
begin 
    DrawOpacityBrush(Image1.Picture.Bitmap, X, Y, clRed, 50, 85); 
end; 

que produce esto en un mapa de bits normal:

enter image description here

La idea que había (en base a Andreas manera de crear una elipse con la opacidad) consistía en hacer un pincel típico en el lienzo, asignarlo a un mapa de bits fuera de pantalla y luego volver a dibujarlo en el mapa de bits principal con la opacidad. Lo que funciona, excepto ese molesto cuadrado alrededor del borde.

¿Cómo puedo renderizar un pincel con opacidad como se ilustra en las capturas de pantalla, pero sin ese cuadrado alrededor del círculo de pincel?

Si configuro Bmp.Transparent := True, todavía hay una caja blanca, pero ya no hay opacidad. Solo un cuadrado blanco sólido y un círculo rojo lleno de sólidos.

Respuesta

13

La característica Opacidad de TCanvas.Draw() no es compatible con lo que está intentando hacer, al menos no de la manera que está tratando de usarlo. Para lograr el efecto que desea, necesita crear un TBitmap de 32 bits para tener un canal alfa por píxel, luego complete el alfa para cada píxel, establezca el alfa en 0 para los píxeles que no desee y configure el alfa a la opacidad deseada para los píxeles que desea. Luego, cuando llame al TCanvas.Draw(), configure su opacidad en 255 para indicarle que use solo las opacidades por píxel.

procedure DrawOpacityBrush(ACanvas: TCanvas; X, Y: Integer; AColor: TColor; ASize: Integer; Opacity: Byte); 
var 
    Bmp: TBitmap; 
    I, J: Integer; 
    Pixels: PRGBQuad; 
    ColorRgb: Integer; 
    ColorR, ColorG, ColorB: Byte; 
begin 
    Bmp := TBitmap.Create; 
    try 
    Bmp.PixelFormat := pf32Bit; // needed for an alpha channel 
    Bmp.SetSize(ASize, ASize); 

    with Bmp.Canvas do 
    begin 
     Brush.Color := clFuchsia; // background color to mask out 
     ColorRgb := ColorToRGB(Brush.Color); 
     FillRect(Rect(0, 0, ASize, ASize)); 
     Pen.Color := AColor; 
     Pen.Style := psSolid; 
     Pen.Width := ASize; 
     MoveTo(ASize div 2, ASize div 2); 
     LineTo(ASize div 2, ASize div 2); 
    end; 

    ColorR := GetRValue(ColorRgb); 
    ColorG := GetGValue(ColorRgb); 
    ColorB := GetBValue(ColorRgb); 

    for I := 0 to Bmp.Height-1 do 
    begin 
     Pixels := PRGBQuad(Bmp.ScanLine[I]); 
     for J := 0 to Bmp.Width-1 do 
     begin 
     with Pixels^ do 
     begin 
      if (rgbRed = ColorR) and (rgbGreen = ColorG) and (rgbBlue = ColorB) then 
      rgbReserved := 0 
      else 
      rgbReserved := Opacity; 
      // must pre-multiply the pixel with its alpha channel before drawing 
      rgbRed := (rgbRed * rgbReserved) div $FF; 
      rgbGreen := (rgbGreen * rgbReserved) div $FF; 
      rgbBlue := (rgbBlue * rgbReserved) div $FF; 
     end; 
     Inc(Pixels); 
     end; 
    end; 

    ACanvas.Draw(X, Y, Bmp, 255); 
    finally 
    Bmp.Free; 
    end; 
end; 

procedure TForm1.Image1MouseDown(Sender: TObject; Button: TMouseButton; 
    Shift: TShiftState; X, Y: Integer); 
begin 
    DrawOpacityBrush(Image1.Picture.Bitmap.Canvas, X, Y, clRed, 50, 85); 
end; 
+0

Gran respuesta gracias Remy.Estaba al tanto de que mis bitmaps habían sido pf24Bit, pero no sabía ni consideraba configurarlo para pf32bit. Probablemente también esté borrando el fondo, porque estoy configurando el color de transparencia como fijo y el valor para clBtnFace cuando borro. –

+1

También puede encontrar útil esta clase: http://code.google.com/p/transparent-canvas/. Es un contenedor alrededor del dibujo de 32 bits por píxel alfa con una interfaz muy similar a TCanvas. Crea uno, dibuja hacia él, luego dibuja sobre un lienzo, listo. Licencia MPL para que pueda usarlo en cualquier lugar. –

Cuestiones relacionadas