2016-02-13 21 views
10

W kolorowym opakowaniu golang, istnieje metoda, aby uzyskać R, G, B, A wartości z RGBA obiektu:Dlaczego metoda golang RGBA.RGBA() używa | i <<?

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 
} 

Gdybym miał realizować tę prostą funkcję, chciałbym tylko napisać tę

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 
} 

Jaki jest powód użycia r |= r << 8?

Odpowiedz

7

Z doskonałą "The Go image package" blogpost:

[...] Kanały posiada 16-bitowe skuteczny zakres 100% czerwonego jest reprezentowany przez RGBA zwrócenie R 65535, a nie 255 , więc konwersja z CMYK lub YCbCr nie jest tak stratna. Po trzecie, zwracany typ to uint32, mimo że maksymalna wartość to 65535, aby zagwarantować, że pomnożenie dwóch wartości razem nie spowoduje przepełnienia.

i

że pole R w RGBA jest 8-bitowy alfa premultiplied koloru w zakresie [0, 255]. RGBA spełnia interfejs kolorów przez pomnożenie tej wartości od 0x101 do wytworzenia 16-bitowego alfa premultiplied koloru w zakresie [0, 65535]

Tak więc, jeśli patrzymy na reprezentację bitową koloru z wartością c.R = 10101010 następnie ta operacja skutecznie kopiuje pierwszy bajt do drugiego bajtu.

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

Jest to równoważne pomnożenie współczynnika 0x101 i rozprowadza cały 256 możliwych wartości równomiernie w przedziale [0, 65535].

2

Aby dokonać konwersji z 8 do 16 bitów na składnik RGB, skopiuj bajt do wysokiego bajtu wartości 16-bitowej. np. 0x03 staje się 0x0303, 0xFE staje się 0xFEFE, tak aby 8-bitowe wartości od 0 do 255 (0xFF) tworzyły 16-bitowe wartości od 0 do 65 535 (0xFFFF) z równomiernym rozkładem wartości.

5

typu color.RGBA realizuje sposób RGBA się zaspokoić interfejs 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) 
} 

teraz typu RGBA oznacza kanałów barwnych z typem uint8, dając szereg 0xFF [0]. Po prostu konwersja tych wartości na uint32 nie rozszerzy zakresu do [0, 0xffff].

Odpowiednia konwersja byłoby coś jak:

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

, chcą jednak, aby uniknąć arytmetyki zmiennoprzecinkowej.Na szczęście 0xffff/0xff jest 0x0101, więc możemy uprościć wyrażenie (ignorując konwersje typu na razie):

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 

I to właśnie ten kod w bibliotece standardowej robi się zasadniczo.

1

Konwersja wartości z zakresu od 0 do 255 (8-bitowy komponent RGB) na wartość z zakresu od 0 do 65535 (16-bitowy komponent RGB) zostanie wykonana przez pomnożenie wartości 8-bitowej przez 65535/255; 65535/255 to dokładnie 257, czyli heksadecymalna liczba 101, więc mnożenie jednobajtowego przez 65535/255 może być wykonane przez przesunięcie tej wartości bajtu o 8 bitów i ORing z wartością początkową.

(Nic Go-specyficzny o to;. Podobne sztuczki wykonywane są w innym miejscu, w innych językach, podczas konwersji 8-bitowe składniki RGB/RGBA 16-bitowych składników RGB/RGBA)