Konversi RGB ke RGBA di atas putih

196

Saya memiliki warna hex, misalnya #F4F8FB(atau rgb(244, 248, 251)) yang ingin saya ubah menjadi warna rgba yang setransparan mungkin (bila ditampilkan di atas putih). Masuk akal? Saya sedang mencari suatu algoritma, atau setidaknya ide dari suatu algoritma untuk bagaimana melakukannya.

Sebagai contoh:

rgb( 128, 128, 255 ) --> rgba(   0,   0, 255,  .5 )
rgb( 152, 177, 202 ) --> rgba(  50, 100, 150,  .5 ) // can be better(lower alpha)

Ide ide?


Solusi FYI berdasarkan jawaban Guffa:

function RGBtoRGBA(r, g, b){
    if((g == null) && (typeof r === 'string')){
        var hex = r.replace(/^\s*#|\s*$/g, '');
        if(hex.length === 3){
            hex = hex.replace(/(.)/g, '$1$1');
        }
        r = parseInt(hex.substr(0, 2), 16);
        g = parseInt(hex.substr(2, 2), 16);
        b = parseInt(hex.substr(4, 2), 16);
    }

    var min, a = (255 - (min = Math.min(r, g, b))) / 255;

    return {
        r    : r = 0|(r - min) / a,
        g    : g = 0|(g - min) / a,
        b    : b = 0|(b - min) / a,
        a    : a = (0|1000*a)/1000,
        rgba : 'rgba(' + r + ', ' + g + ', ' + b + ', ' + a + ')'
    };
}

RGBtoRGBA(204, 153, 102) == RGBtoRGBA('#CC9966') == RGBtoRGBA('C96') == 
    {
       r    : 170,
       g    : 85 ,
       b    : 0  ,
       a    : 0.6,
       rgba : 'rgba(170, 85, 0, 0.6)' 
    }
Mark Kahn
sumber
Menarik! Anda masih ingin itu terlihat benar, tidak sepenuhnya transparan?
Mrchief
6
Pertanyaan menarik! Ada pertanyaan sebaliknya yang tepat di stackoverflow.com/questions/2049230/convert-rgba-color-to-rgb , mungkin membantu
apscience
@Mrchief - Ya, saya ingin warnanya terlihat sama saat ditampilkan di atas putih, tetapi setransparan mungkin.
Mark Kahn
1
Pertanyaan yang bagus, saya selalu bertanya-tanya bagaimana melakukan ini, tetapi biasanya menghindarinya;) +1
Dominic K
1
@ e100 - Pertanyaan spesifik yang memicu ini adalah stackoverflow.com/questions/6658350/… . Pada dasarnya saya ingin dapat melapisi bayangan CSS di atas elemen dengan warna latar belakang tanpa menghalangi klik-tayang dengan meletakkan elemen bayangan di atas segalanya. Saya juga ingin melakukan ini untuk sementara waktu sehingga saya dapat melakukan hal-hal acak seperti: jsfiddle.net/qwySW/2 . Ditambah lagi, saya baru saja menganggapnya sebagai pertanyaan yang menarik :)
Mark Kahn

Jawaban:

185

Ambil komponen warna terendah, dan ubah itu menjadi nilai alpha. Kemudian skala komponen warna dengan mengurangi yang terendah, dan membaginya dengan nilai alpha.

Contoh:

152 converts to an alpha value of (255 - 152) / 255 ~ 0.404

152 scales using (152 - 152) / 0.404 = 0
177 scales using (177 - 152) / 0.404 ~ 62
202 scales using (202 - 152) / 0.404 ~ 123

Jadi, rgb(152, 177, 202)tampilkan sebagai rgba(0, 62, 123, .404).

Saya telah memverifikasi di Photoshop bahwa warnanya benar-benar cocok dengan sempurna.

Guffa
sumber
1
Terima kasih! Saya melakukan sesuatu yang serupa (misalnya mendapatkan .404) tetapi tidak bisa mendapatkan angka untuk berhasil.
Mark Kahn
3
FYI, memposting solusi akhir berdasarkan jawaban Anda dalam pertanyaan
Mark Kahn
2
@ templatetypedef: Mengkonversi komponen terendah menjadi alpha adalah penskalaan dari rentang 0..255ke 0..1, dan pembalikan. Menggunakan 1.0 - 152 / 255juga akan berhasil. Konversi komponen warna hanyalah penskalaan dari n..255ke 0..255tempat nkomponen terendah.
Guffa
1
@Christoph: Prinsipnya akan sama, tetapi lebih rumit. Alih-alih hanya menggunakan komponen terendah untuk menghitung alpha, Anda akan menghitung alpha terendah yang mungkin untuk setiap komponen (yaitu nilai alpha apa yang digunakan untuk mendapatkan warna yang tepat saat menggunakan 0 atau 255 sebagai nilai komponen), lalu gunakan yang tertinggi dari nilai-nilai alpha. Dari itu Anda dapat menghitung nilai warna apa yang digunakan untuk setiap komponen dengan nilai alfa itu untuk mendapatkan warna yang tepat. Perhatikan bahwa beberapa kombinasi warna (misalnya putih pada latar belakang hitam) akan memberikan 1,0 sebagai nilai alfa serendah mungkin untuk digunakan.
Guffa
2
Saya menulis biola kecil yang mengimplementasikan solusi ini. Ini tautannya. jsfiddle.net/wb5fwLoc/1 . Mungkin salah satu dari Anda dapat menggunakannya. Itu hanya skrip cepat, bukan bebas bug .. itu harus cukup baik untuk dimainkan.
chsymann
23

Biarkan r, g, dan b menjadi nilai input dan r ', g', b ', dan a' menjadi nilai output, semua diskalakan (untuk saat ini, karena membuat matematika lebih cantik) antara 1 dan 0. Kemudian, dengan rumus untuk overlay warna:

r = a' * r' + 1 - a'
g = a' * g' + 1 - a'
b = a' * b' + 1 - a'

Istilah 1 - a 'mewakili kontribusi latar belakang, dan istilah lainnya mewakili latar depan. Lakukan beberapa aljabar:

r = a' * (r' - 1) + 1
r - 1 = a' * (r' - 1)
(r - 1) / (r' - 1) = a'
(r' - 1) / (r - 1) = 1 / a'
r' - 1 = (r - 1) / a'
r' = (r - 1) / a' + 1

Secara intuitif, tampaknya nilai warna minimum akan menjadi faktor pembatas dalam masalah, jadi ikat ini ke m:

m = min(r, g, b)

Tetapkan nilai output yang sesuai, m ', ke nol, karena kami ingin memaksimalkan transparansi:

0 = (m - 1) / a' + 1
-1 = (m - 1) / a'
-a' = m - 1
a' = 1 - m

Jadi, dalam javascript (menerjemahkan dari 1 hingga 255 sepanjang jalan):

function rgba(r, g, b) {
    var a = 1 - Math.min(r, Math.min(g, b)) / 255;
    return [255 + (r - 255) / a, 255 + (g - 255) / a, 255 + (b - 255) / a, a];
}

Perhatikan bahwa saya berasumsi bahwa 'opacity di sini. Sangat mudah untuk mengubahnya menjadi transparansi - cukup hapus "1 -" dari rumus untuk a '. Yang perlu diperhatikan adalah bahwa ini tampaknya tidak menghasilkan hasil yang tepat - dikatakan bahwa opacity adalah 0,498 untuk contoh yang Anda berikan di atas (128, 128, 255). Namun, ini sangat dekat.

Gereeter
sumber
Jika Anda mendistribusikan 255 yang dikalikan dalam persamaan Anda, Anda mendapatkan hasil yang tepat -[255 * (r/255 - 1) / a + 1 * 255, ...] == [(255*r/255 - 255 * 1) / a + 255, ...] == [(r - 255) / a + 255, ...]
gereeter
6

Saya akan melihat konversi RGB <-> HSL. Yaitu luminositas == jumlah putih == jumlah transparansi.

Sebagai contoh Anda rgb( 128, 128, 255 ), kami perlu menggeser nilai RGB ke 0pertama dengan jumlah maksimum, yaitu ke rgb( 0, 0, 128 )- yang akan menjadi warna kami dengan sesedikit mungkin putih. Dan setelah itu, menggunakan formula untuk luminance, kita menghitung jumlah putih yang kita perlu tambahkan ke warna gelap kita untuk mendapatkan warna asli - yang akan menjadi alpha kita:

L = (MAX(R,G,B) + MIN(R,G,B))/2
L1 = (255 + 128) / 2 = 191.5
L2 = (128 + 0) /2 = 64
A = (191,5 - 64) / 255 = 0,5;

Harapan itu masuk akal. :)

lxa
sumber
6

Bagi Anda yang menggunakan SASS / SCSS, saya telah menulis fungsi SCSS kecil sehingga Anda dapat dengan mudah menggunakan algoritma yang dijelaskan oleh @Guffa

@function transparentize-on-white($color)
{
    $red: red($color);
    $green: green($color);
    $blue: blue($color);
    $lowestColorComponent: min($red, $green, $blue);
    $alpha: (255 - $lowestColorComponent) / 255;

    @return rgba(
        ($red - $lowestColorComponent) / $alpha,
        ($green - $lowestColorComponent) / $alpha,
        ($blue - $lowestColorComponent) / $alpha,
        $alpha
    );
}
Stefan
sumber
3

Saya hanya menjelaskan ide untuk algoritma, tidak ada solusi lengkap:

Pada dasarnya, Anda memiliki tiga angka x, y, zdan Anda mencari tiga angka baru x', y', z'dan pengganda adalam rentang [0,1] sehingga:

x = a + (1 - a) x'
y = a + (1 - a) y'
z = a + (1 - a) z'

Ini ditulis dalam satuan di mana saluran juga mengambil nilai dalam kisaran [0,1]. Dalam nilai diskrit 8bit, itu akan menjadi seperti ini:

x = 255 a + (1 - a) x'
y = 255 a + (1 - a) y'
z = 255 a + (1 - a) z'

Selain itu, Anda menginginkan nilai sebesar mungkin a. Anda dapat memecahkan:

a  = (x - x')/(255 - x')          x' = (x - 255 a)/(1 - a)

Dll. Dalam nilai riil ini memiliki banyak solusi tak terbatas, cukup tancapkan bilangan real mana pun a, tetapi masalahnya adalah menemukan nomor yang kesalahan diskritasinya minimal.

Kerrek SB
sumber
0

Ini harus dilakukan:

let x = min(r,g,b)
a = 1 - x/255                    # Correction 1
r,g,b = ( (r,g,b) - x ) / a      # Correction 2
kebenaran
sumber
Saya sebenarnya hanya memperhatikan bahwa saya memiliki kesalahan dalam perhitungan alpha (diperbaiki sekarang). Jadi itu mungkin ada hubungannya dengan itu juga.
trutheality
Masih ada masalah yang diperkenalkan oleh Anda ydan (y-x). The 255 / (y-x)benar memaksa jumlah terbesar untuk 255, sehingga Anda akan selalu berakhir dengan satu 0, tunggal 255dan kemudian nomor lain: 0, 22, 255, 255, 0, 55, dll ...
Mark Kahn
Begitu, jadi itu tidak akan memungkinkan untuk warna yang lebih gelap ... Aku harus membuatnya /adan kemudian cocok dengan jawaban yang benar.
trutheality
-1

Jawaban teratas tidak berfungsi untuk saya dengan komponen warna rendah. Misalnya tidak menghitung alpha yang benar jika warnanya # 80000. Secara teknis itu harus membuatnya menjadi # ff0000 dengan alpha 0,5. Untuk mengatasi ini, Anda perlu menggunakan RGB -> HSL -> konversi RGBA. Ini adalah kode semu untuk mendapatkan nilai yang benar:

//Convert RGB to HSL
hsl = new HSL(rgb)

//Use lightness as alpha
alpha = hsl.Lightness

//For #80000 lightness is 0.5, so we have to take this into account.
//Lightness more than 0.5 converts to alpha 1 and below is a linear ratio
if (alpha > 0.5)
{
    alpha = 1;
}
else
{
    alpha = alpha / 0.5;
    //We need to drop the lightness of the color to 0.5 to get the actual color
    //that needs to display. Alpha blending will take care of that.
    hsl.Lightness = 0.5;
}

newRgb = hsl.convertToRgb()

"newRgb" akan berisi nilai warna yang disesuaikan baru dan menggunakan variabel "alpha" untuk mengontrol transparansi.

Arvydas Juskevicius
sumber
#800000dan rgba( 255, 0, 0, .5 )jauh dari warna yang sama. Yang pertama adalah merah tua, salmon-ish kedua. Kecuali menampilkan lebih dari hitam, maka saya pikir mereka akan sama.
Mark Kahn