2010-10-15 13 views
7

In Win32, è una tecnica comune per generare una maschera di bit in bianco e nero da una bitmap per l'utilizzo di trasparenza nel modo seguente:Come genrate una maschera di bit in bianco e nero per una bitmap a 32 bit

SetBkColor(hdcSource, clrTransparency); 
VERIFY(BitBlt(hdcMask, 0, 0, bm.bmWidth, bm.bmHeight, hdcSource, 0, 0, SRCCOPY)); 

Questo presuppone che hdcSource è una memoria DC contenente l'immagine sorgente e hdcMask è una memoria DC che contiene una bitmap monocromatica della stessa dimensione (quindi entrambi sono 32x32, ma la sorgente è a 4 bit, mentre la destinazione è monocromatica 1 bit).

Tuttavia, questo sembra non funzionare quando l'origine è a 32 bit colore + alfa. Invece di ottenere una bitmap monocromatica in hdcMask, ottengo una maschera completamente nera. Nessun bit viene impostato su bianco (1). Mentre questo funziona per la fonte di colori a 4 bit.

La mia ricerca-foo non funziona, in quanto non riesco a trovare alcun riferimento a questo particolare problema.

Ho isolato che questo è davvero il problema nel mio codice: ad esempio se utilizzo una bitmap di origine che è a 16 colori (4 bit), funziona; se uso un'immagine a 32 bit, produce la maschera completamente nera.

C'è un metodo alternativo che dovrei usare nel caso di immagini a 32 bit a colori? C'è un problema con il canale alfa che sovrascrive il normale comportamento della tecnica sopra?

Grazie per l'aiuto che potresti avere da offrire!

ADDENDUM: Non riesco ancora a trovare una tecnica che crei una bitmap monocromatica valida per la mia bitmap sorgente prodotta GDI +.

Ho in qualche modo alleviato il mio particolare problema semplicemente non generando un bitmask monocromatico, e invece sto usando TransparentBlt(), che sembra averlo corretto (ma non so cosa stiano facendo internamente è diverso che consente loro di mascherare correttamente l'immagine).

Potrebbe essere utile avere una buona, la funzione di lavoro:

HBITMAP CreateTransparencyMask(HDC hdc, HBITMAP hSource, COLORREF crTransparency); 

Dove si crea sempre una maschera di trasparenza valida, a prescindere dalla profondità di colore di hSource.

Idee?

+3

GDI è bloccato a 24bpp. TransparentBlt() è un po 'insolito, è documentato per supportare 32bpp. È ora di passare a GDI +, forse. –

risposta

3

Non è possibile farlo se è presente un canale alfa. COLORREF usa gli 8 bit più importanti per un numero di scopi, inclusa la specificazione se i 3 byte inferiori sono un indice della tabella dei colori nella tavolozza corrente o una terzina RGB. Pertanto non è possibile specificare nulla tranne 0x00 nel byte superiore di clrTransparency.

Se si dispone di una bitmap alpha, quindi su GDI che rimane "inconsapevole" del canale alfa, non esiste un modo corretto per confrontare effettivamente un BkColor a 24 bit con i pixel a 32 bit nella bitmap.

Mi aspetto che GDI tratti il ​​canale alfa in 32bpp bitmap come "Riservato" e confronti solo con successo i pixel in cui il canale riservato è zero. Ad esempio, il colore della maschera deve essere completamente trasparente per avere una possibilità di successo. (e, se hai creato una bitmap premoltiplicata legittima, ciò implica che i valori RGV sarebbero uguali a zero, piuttosto limitando la scelta dei colori della maschera: P)

+0

In questo caso particolare, il problema è che sto iniziando con una bitmap a colori a 4 bit. Quindi, per visualizzarlo in modo carino quando disabilitato, mi piacerebbe fare un rendering in scala di grigi di esso, e quindi usarlo come faccia del pulsante (usando la tecnica della trasparenza mascherata come prima). Ma il codice che sto usando per produrre la scala dei grigi è basato su GDI +, che sospetto crei un'immagine a 32 bit piuttosto che un'immagine a 24 bit. – Mordachai

+1

@Mordachai: considerato ciò che hai appena descritto, puoi creare la maschera dall'immagine originale a 4 bit e usarla con quella creata da GDI? –

+0

Mi piace l'idea per i miei scopi. Ma sarei ancora interessato a un generico "come creare una maschera bit monocromatica per * QUALSIASI * fonte HBITMAP". – Mordachai

0

Un metodo alternativo sarebbe quello di scansionare i pixel te stesso e generare e monocromatico bitmap in base al colore di origine (o alfa di origine rispetto a una soglia).

Si noti che se si utilizza GDI +, quindi, a seconda dell'operazione, i pixel potrebbero essere stati sottoposti a un antialiasing, con il risultato che nessuno di questi è una corrispondenza esatta per il colore "trasparente".

+0

Ho salvato l'immagine in scala di grigi generata da GDI + come PNG a 32 bit di profondità, quindi ho campionato i pixel in Paint. Stanno bene. Tuttavia, ho ricontrollato l'HBITMAP risultante prodotto da GDI + (tramite Bitmap :: GetHBitmap()) ed è in effetti sempre a 32 bit di profondità, nonostante l'istanza Bitmap sottostante sia PixelFormat24bppRGB). – Mordachai

3

Può fare :)
Come indicato in precedenza da "Chris Becke", GDI può confrontare solo se il canale Alfa riservato è zero.
L'HBITMAP ottenuto da BITMAP :: GetHBITMAP() restituisce un HBITMAP con canale alfa impostato su 0xFF.
Questo deve essere 0x00 per il confronto SetBkColor() per funzionare.
Quindi, il soln: passa attraverso ogni pixel e imposta l'Alpha Component su Zero.

Bitmap img(L"X.bmp"); 
HBITMAP hBM; 
img.GetHBITMAP(Color::White, &hBM); 
BITMAP bm; 
GetObject(g_hbmBall, sizeof(BITMAP), &bm); 
for(UINT i = 0, n = -1; i < bm.bmHeight; i++) 
    for(UINT j = 0; j < bm.bmWidth; j++) 
    { 
     n += 4; // Once per Pixel of 4 Bytes 
     ((LPBYTE)bm.bmBits)[n] = 0; 
    }
// Now SetBkColor and BitBlt will work as expected
+0

Grazie per l'idea. Alla fine ho deciso di non usare questa tecnica, quindi questo ramo di codifica è attualmente in disuso. Potrei ritornarci in futuro e proverò di nuovo quest'idea. ;) – Mordachai

0

Il metodo che ha funzionato per me era convertire prima la bitmap da 32 bit a 24 bit.

1. CreateCompatibleDC 
2. CreateDIBSection with 24 as the biBitCount. 
3. SelectObject 
4. BitBlt from 32bit DC to 24 bit. This removes alpha. 
5. BitBlt from 24 bit DC to the monochrome DC works as expected. 

Sulla mia macchina questo esegue più veloce del doppio ciclo dalla risposta di Ujjwal.

+0

Grazie. Buono a sapersi. – Mordachai

+0

Giusto per rendere questo metodo più chiaro, a. 3) SelectObject sta selezionando la bitmap creata con CreateDIBSection in una nuova DC creata in 1) b. Per creare la maschera, devi assicurarti di chiamare SetBkColor sul nuovo DC a 24 bit creato in 1), per caso ho chiamato SetBkColor sul controller a 32 bit, e ci è voluto un po 'per risolvere il problema. – frank