2016-02-13 63 views
10

Nel pacchetto colore golang, v'è un metodo per ottenere r, g, b, a valori da un RGBA oggetto:Perché il metodo golang RGBA.RGBA() usa | e <<?

func (c RGBA) RGBA() (r, g, b, a uint32) { 
    r = uint32(c.R) 
    r |= r << 8 
    g = uint32(c.G) 
    g |= g << 8 
    b = uint32(c.B) 
    b |= b << 8 
    a = uint32(c.A) 
    a |= a << 8 
    return 
} 

Se dovessi implementare questa semplice funzione, che sarebbe solo scrivendo questo

func (c RGBA) RGBA() (r, g, b, a uint32) { 
    r = uint32(c.R) 
    g = uint32(c.G) 
    b = uint32(c.B) 
    a = uint32(c.A) 
    return 
} 

Qual è il motivo per cui viene utilizzato r |= r << 8?

risposta

7

Dalla eccellente "The Go image package" blogpost:

[...] i canali hanno una portata effettiva di 16 bit: 100% rosso è rappresentato da RGBA restituendo un r 65535, non 255 , in modo che la conversione da CMYK o YCbCr non sia così dannosa. In terzo luogo, il tipo restituito è uint32, anche se il valore massimo è 65535, per garantire che la moltiplicazione di due valori insieme non venga superata.

e

nota che il campo R di una RGBA è un colore alfa-premoltiplicato 8 bit nell'intervallo [0, 255]. RGBA soddisfa l'interfaccia a colori moltiplicando tale valore per 0x101 per generare un colore alfa-premoltiplicato 16 bit nell'intervallo [0, 65535]

Quindi, se guardiamo la rappresentazione po 'di colore con il valore c.R = 10101010 quindi questa operazione

r = uint32(c.R) 
r |= r << 8 

copia efficacemente il primo byte nel secondo byte.

00000000000000000000000010101010 (r) 
| 00000000000000001010101000000000 (r << 8) 
-------------------------------------- 
    00000000000000001010101010101010 (r |= r << 8) 

Ciò equivale ad una moltiplicazione con il fattore 0x101 e distribuisce tutti 256 valori possibili in modo uniforme nell'intervallo [0, 65535].

2

Per convertire da 8 a 16 bit per componente RGB, copiare il byte nel byte alto del valore a 16 bit. ad es. 0x03 diventa 0x0303, 0xFE diventa 0xFEFE, quindi i valori da 8 a 255 (0xFF) producono valori a 16 bit da 0 a 65.535 (0xFFFF) con una distribuzione uniforme di valori.

5

Il tipo color.RGBA implementa il metodo RGBA di soddisfare l'interfaccia color.Color:

type Color interface { 
    // RGBA returns the alpha-premultiplied red, green, blue and alpha values 
    // for the color. Each value ranges within [0, 0xffff], but is represented 
    // by a uint32 so that multiplying by a blend factor up to 0xffff will not 
    // overflow. 
    // 
    // An alpha-premultiplied color component c has been scaled by alpha (a), 
    // so has valid values 0 <= c <= a. 
    RGBA() (r, g, b, a uint32) 
} 

Ora il tipo RGBA rappresenta i canali di colore con il tipo uint8, dando una gamma di [0, 0xFF]. La semplice conversione di questi valori in uint32 non estenderebbe l'intervallo fino a [0, 0xffff].

Una conversione appropriato sarebbe qualcosa di simile:

r = uint32((float64(c.R)/0xff) * 0xffff) 

Tuttavia, essi vogliono evitare l'aritmetica in virgola mobile.Per fortuna 0xffff/0xff è 0x0101, quindi possiamo semplificare l'espressione (ignorando le conversioni di tipo, per ora):

r = c.R * 0x0101 
    = c.R * 0x0100 + c.R 
    = (c.R << 8) + c.R # multiply by power of 2 is equivalent to shift 
    = (c.R << 8) | c.R # equivalent, since bottom 8 bits of first operand are 0 

E questo è essenzialmente ciò che il codice nella libreria standard sta facendo.

1

La conversione di un valore compreso tra 0 e 255 (un componente RGB a 8 bit) in un valore compreso tra 0 e 65535 (un componente RGB a 16 bit) viene eseguita moltiplicando il valore a 8 bit per 65535/255; 65535/255 è esattamente 257, che è l'esadecimale 101, quindi la moltiplicazione di un byte per 65535/255 può essere effettuata spostando quel valore di byte lasciato 8 bit e ORandolo con il valore originale.

(Non c'è niente-Go specifiche su questo;. Trucchi simili sono fatte altrove, in altre lingue, per la conversione dei componenti RGB/RGBA 8-bit ai componenti RGB/RGBA a 16 bit)

Problemi correlati