2011-10-11 12 views
8

Una bitmap appena creata sembra avere uno sfondo (bianco) per impostazione predefinita. Almeno, una query sulla proprietà Pixels conferma. Ma perché il colore di sfondo non è utilizzato come colore trasparente quando Transparent è impostato su true?Come ottenere la trasparenza bitmap senza la necessità di dipingere prima?

Considerate questo semplice codice di prova:

procedure TForm1.Button1Click(Sender: TObject); 
var 
    Bmp: TBitmap; 
begin 
    Bmp := TBitmap.Create; 
    try 
    Bmp.Width := 100; 
    Bmp.Height := 100; 
    Bmp.Transparent := True; 
    Canvas.Draw(0, 0, Bmp);        // A white block is drawn 
    Bmp.Canvas.Brush.Color := Bmp.Canvas.Pixels[0, 99]; // = 'clWhite' 
    Bmp.Canvas.FillRect(Rect(0, 0, 100, 100)); 
    Canvas.Draw(0, 100, Bmp);       // "Nothing" is drawn 
    finally 
    Bmp.Free; 
    end; 
end; 

Per qualche ragione, l'intera superficie bitmap deve essere dipinta prima che possa apparire trasparente, che suona un po 'strano.

le seguenti variazioni sono provati per eliminare la chiamata a FillRect, tutti con lo stesso risultato (senza trasparenza):

  • impostazione Solo Brush.Color,
  • Brush.Handle := CreateSolidBrush(clWhite),
  • PixelFormat := pf32Bit,
  • IgnorePalette := True ,
  • TransparantColor := clWhite,
  • TransparantMode := tmFixed,
  • Canvas.Pixels[0, 99] := clWhite che fa solo quel pixel trasparente,
  • Modified := True.

Quindi, il desiderio è di dipingere solo una parte di una bitmap appena creata e ottenere la superficie rimanente trasparente.

Utilizzo: Delphi 7, Win 7/64.

+1

Che cosa succede se si segue [questo] (http://docs.embarcadero.com/products/rad_studio/delphiAndcpp2009/HelpUpdate2/EN/html/delphivclwin32/Graphics_TBitmap_TransparentMode.html) esempio ? –

+0

Vuoi semplicemente disegnare nulla? Vuoi una Bitmap completa e trasparente da disegnare? – Shambhala

+0

@Shamballa No, voglio disegnare solo una (piccola) parte. – NGLN

risposta

8

Basta impostare TransparentColor e Canvas.Brush.Color prima di impostare le dimensioni della bitmap.

+0

Sì! E non c'è bisogno di impostare TransparentColor. Impostare Canvas.Brush.Color befóre dare le dimensioni fa il trucco. Grazie. – NGLN

+3

Qualcuno sa perché è così? Hai anche bisogno di impostare il colore del pennello? E 'sufficiente solo per forzare la maniglia del pennello ad esistere? –

+0

@David Buon punto. 'Canvas.Brush.Handle: = 0' funziona anche, abbastanza stranamente. – NGLN

1

Questo disegnerà un quadrato rosso e il resto è trasparente.

procedure TForm1.btnDrawClick(Sender: TObject); 
var 
    Bmp: TBitmap; 
begin 
    Bmp := TBitmap.Create; 
    try 
    Bmp.Width := 100; 
    Bmp.Height := 100; 
    Bmp.Transparent := TRUE; 
    Bmp.TransparentColor := clWhite; 

    Bmp.Canvas.Brush.Color := clWhite; 
    Bmp.Canvas.FillRect(Rect(0, 0, Bmp.Width, Bmp.Height)); 
    Bmp.Canvas.Brush.Color := clRed; 
    Bmp.Canvas.FillRect(Rect(42, 42, 20, 20)); 
    Canvas.Draw(12, 12, Bmp); 
    finally 
    Bmp.Free; 
    end; 
end; 
+0

Il desiderio è ottenere questo risultato senza il primo FillRect, come chiaramente indicato nella domanda. -1 – NGLN

3

Avevo davvero bisogno di essere in grado di creare una TBitmap completamente trasparente (e altrimenti vuota/vuota) di una dimensione arbitraria nel formato 32A RGBA. Molte volte. Lazarus è in grado di caricare tale bitmap in TBitmap e dopo averlo caricato, è possibile manipolarlo con scanline e ciò che non utilizza il formato RGBA. Ma non funziona quando crei TBitmap tu stesso. Il formato dei pixel sembra essere completamente ignorato. Quindi quello che ho fatto è così fuori dalla scatola, e semplice, che è quasi INCREDIBILE (!). Ma è pratico, funziona molto bene ed è completamente indipendente da LCL e da qualsiasi libreria di terze parti. Anche non dipende dall'unità grafica, perché genera l'effettivo file BMP RGBA a 32 bit (io lo genera su TMemoryStream, è possibile generare in modo diverso). Quindi, una volta ottenuto, altrove nel codice, è sufficiente caricarlo utilizzando TBitmap.LoadFrom source o TPicture.LoadFrom source.

La storia di fondo inizialmente ho voluto per generare file BMP formattato correttamente seguendo il formato come descritto qui: http://www.fileformat.info/format/bmp/egff.htm Ma c'erano poche varianti di formato BMP, e io non sono stato chiaro su quale avrei dovuto seguire . Quindi ho deciso di adottare un approccio di reverse engineering, ma la descrizione del formato mi ha aiutato in seguito. Ho usato un editor grafico (ho usato GIMP) per creare un file BMP RGBA vuoto 1x1 pixel 32 e lo ho chiamato alpha1p.bmp, conteneva solo trasparenza nient'altro. Poi ho ridimensionato la tela a 10x10 pixel e salvata come alpha10p.file bmp. Poi ho confrontato i due file: compating two bmp files in vbindiff on Ubuntu Quindi ho scoperto che le uniche differenze erano i pixel aggiunti (ognuno era di 4 byte tutti gli zeri RGBA) e pochi altri byte nell'intestazione. A causa della documentazione di formato sul link che ho condiviso, ho scoperto che erano: FileSize (in byte), BitmapWidth (in pixel), BitmapHeight (in pixel) e BitmapDataSize (in byte). L'ultimo era BitmapWidth*BitmapHeight*4, perché ogni pixel in RGBA è 4 byte. Così ora, potrei semplicemente generare l'intera sequenza di byte come si vede all'interno dei file alpha1p.bmp, meno 4 byte dalla fine (il 1 ° dello), quindi aggiungere 4 byte (tutti gli zeri) dei dati RGBA per ogni pixel del BMP Voglio generare, quindi tornare alla sequenza iniziale e aggiornare le parti variabili: Dimensione file, larghezza, altezza e dimensione dati BMP. E funziona perfettamente! Ho appena dovuto aggiungere test per BigEndian e scambiare i numeri parola e dword prima della scrittura. Questo diventerebbe un problema per le piattaforme ARM che lavorano in BigEndian.

Il codice

const 
    C_BLANK_ALPHA_BMP32_PREFIX : array[0..137]of byte 
    = ($42, $4D, $00, $00, $00, $00, $00, $00, $00, $00, $8A, $00, $00, $00, $7C, $00, 
    $00, $00, $0A, $00, $00, $00, $0A, $00, $00, $00, $01, $00, $20, $00, $03, $00, 
    $00, $00, $90, $01, $00, $00, $13, $0B, $00, $00, $13, $0B, $00, $00, $00, $00, 
    $00, $00, $00, $00, $00, $00, $00, $00, $00, $FF, $00, $00, $FF, $00, $00, $FF, 
    $00, $00, $FF, $00, $00, $00, $42, $47, $52, $73, $00, $00, $00, $00, $00, $00, 
    $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, 
    $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, 
    $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $02, $00, $00, $00, $00, $00, 
    $00, $00, $00, $00, $00, $00, $00, $00, $00, $00); 

(...) 

    Function RenderEmptyAlphaBitmap(AWidth,AHeight: integer): TMemoryStream; 
    var 
    buf : array[1..4096]of byte; 
    i,p : int64; 
    w : word; 
    dw : dword; 
    BE : Boolean; 
    begin 
    buf[low(buf)] := $00; //this is jyst to prevent compiler warning about not initializing buf variable 
    Result := TMemoryStream.Create; 
    if(AWidth <1)then AWidth := 1; 
    if(AHeight<1)then AHeight := 1; 
    //Write File Header: 
    Result.Write(C_BLANK_ALPHA_BMP32_PREFIX, SizeOf(C_BLANK_ALPHA_BMP32_PREFIX)); 
    //Now start writing the pixels: 
    FillChar(buf[Low(buf)],Length(buf),$00); 
    p := Result.Position; 
    Result.Size := Result.Size+int64(AWidth)*int64(AHeight)*4; 
    Result.Position := p; 
    i := int64(AWidth)*int64(AHeight)*4; //4 because RGBA has 4 bytes 
    while(i>0)do 
     begin 
     if(i>Length(buf)) 
      then w := Length(buf) 
      else w := i; 
     Result.Write(buf[Low(buf)], w); 
     dec(i,w); 
     end; 
    //Go back to the original header and update FileSize, Width, Height, and offset fields: 
    BE := IsBigEndian; 
    Result.Position := 2; dw := Result.Size; 
    if BE then SwapEndian(dw); Result.Write(dw, SizeOf(dw)); 
    Result.Position := 18; dw := AWidth; 
    if BE then SwapEndian(dw); Result.Write(dw, SizeOf(dw)); 
    Result.Position := 22; dw := AHeight; 
    if BE then SwapEndian(dw); Result.Write(dw, SizeOf(dw)); 
    Result.Position := 34; dw := AWidth*AHeight*4; 
    if BE then SwapEndian(dw); Result.Write(dw, SizeOf(dw)); 
    //Done: 
    Result.Position := 0; 
    end; 

Notate come C_BLANK_ALPHA_BMP32_PREFIX costante è sostanzialmente la copia della sequenza di byte dal mio file di esempio alpha1p.bmp, meno ultimi 4 byte, che erano il pixel RGBA. : D

Inoltre, sto usando IsBigEndian funzione che va in questo modo:

Function IsBigEndian: Boolean; 
    type 
    Q = record case Boolean of 
      True : (i: Integer); 
      False : (p: array[1..4] of Byte); 
     end; 
    var 
    x : ^Q; 
    begin 
    New(x); 
    x^.i := 5; 
    Result := (x^.p[4]=5); 
    Dispose(x); 
    end; 

Questo è copiato da Lazzaro Wiki: http://wiki.freepascal.org/Writing_portable_code_regarding_the_processor_architecture è possibile saltare questa parte se non trattare con le piattaforme bigEndian, oppure può usare una direttiva IFDEF di compilatori. Il fatto è che se si utilizza {$IFDEF ENDIAN_BIG}, allora è quello che è il caso del compilatore, mentre la funzione verifica effettivamente il sistema. Questo è spiegato nel wiki collegato.

utilizzo Esempio

Procedure TForm1.Button1Click(Sender: TObject); 
var 
    MS : TMemoryStream; 
begin 
    MS := RenderEmptyAlphaBitmap(Image1.Width, Image1.Height); 
    try 
    if Assigned(MS)then Image1.Picture.LoadFromStream(MS); 
    //you can also MS.SaveToFile('my_file.bmp'); if you want 
    finally 
    FreeAndNil(MS); 
    end; 
end; 
+0

su Quora ho suggerito di adottare questo metodo con SDL: https://www.quora.com/A-simple-way-to-create-a-transparent-surface-with-SDL-Pascal/answer/Krzysztof-Kamil-Jacewicz –

+0

Non penso che la domanda riguardi bitmap trasparenti a 32 bit * alfa *. in ogni caso, bella idea. ma questo non funziona come previsto ?: https://pastebin.com/EXDRu3zG – kobik

+0

@kobik: il tuo metodo non ha funzionato per me in alcune occasioni. Penso che dove non ha funzionato era su Linux usando gtk widgetset. –

Problemi correlati