2012-04-26 12 views
10

PanoramicaCome dipingere su una tela con trasparenza e opacità?

Dalla libreria GR32, sto usando TImgView32 per rendere una griglia, che sarà il mio sfondo trasparente in questo modo:

enter image description here

Situato all'interno del TImgView32 ho un TImage regolare, dove sarò disegnando sulla tela, qualcosa di simile:

enter image description here

Task

Quello che mi piacerebbe ottenere è la possibilità di impostare l'opacità del pennello, consentendo ulteriori possibilità di modifica delle immagini nel mio programma. Piuttosto che avere un colore piatto, impostare l'opacità del pennello consente diversi livelli di profondità del colore ecc.

Ho trovato questa domanda prima mentre cercavo in giro: Draw opacity ellipse in Delphi 2010 - Andreas Rejbrand ha fornito alcuni esempi nella sua risposta per questo domanda.

Ho visto cosa ha fatto Andreas e ho trovato il mio tentativo semplificato, ma sono bloccato da un problema. Date un'occhiata a questi prossimi due immagini, la prima è con lo sfondo trasparente e la seconda con uno sfondo nero per mostrare il problema più chiara:

enter image description here enter image description here

Come si può vedere, in giro per il pennello (cerchio) è una piazza davvero fastidiosa di cui non posso sbarazzarmi. Tutto ciò che dovrebbe essere visibile è il pennello. Questo è il mio codice utilizzato per produrre tali risultati:

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; 

che produce questo su una bitmap regolare:

enter image description here

L'idea che avevo (sulla base di Andreas modo di creare un ellisse con opacità) era quello di rendere un pennello tipico sulla tela, assegnarlo a una bitmap fuori schermo, quindi ridisegnarlo sulla bitmap principale con l'opacità. Che funziona, tranne quella fastidiosa piazza attorno al bordo.

Come posso rendere un pennello con opacità come illustrato negli screenshot, ma senza quel quadrato attorno al cerchio del pennello?

Se si imposta Bmp.Transparent := True c'è ancora una casella bianca, ma non più opacità. Solo un quadrato bianco solido e un cerchio rosso pieno pieno.

risposta

13

La funzione Opacità di TCanvas.Draw() non supporta ciò che si sta tentando di fare, almeno non nel modo in cui si sta tentando di utilizzarlo. Per ottenere l'effetto desiderato, è necessario creare un valore di 322 bit TBitmap in modo da disporre di un canale alfa per pixel, quindi inserire l'alfa per ciascun pixel, impostare l'alfa su 0 per i pixel che non si desidera e impostare l'alfa all'opacità desiderata per i pixel che vuoi. Quindi, quando si chiama TCanvas.Draw(), impostarne l'opacità su 255 per indicare che utilizza solo le opacità per pixel.

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

Grande risposta, grazie Remy.Sapevo che i miei bitmap erano pf24Bit, ma non sapevo o pensavo di impostarlo su pf32bit. Probabilmente faccio il mio background cancellando male allora, perché sto impostando il colore della trasparenza su fisso e il valore su clBtnFace quando cancoro. –

+1

Potresti trovare utile questa classe: http://code.google.com/p/transparent-canvas/. È un wrapper attorno al disegno a 32 bit, per pixel-alfa con un'interfaccia molto simile a TCanvas. Creane uno, disegnalo, poi disegnalo su una tela, fatto. Autorizzato MPL in modo da poterlo utilizzare ovunque. –

Problemi correlati