-
-
Notifications
You must be signed in to change notification settings - Fork 5.9k
Modify luminance calculation and extract related functions into single files #24586
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 39 commits
17ecae7
8ab875e
ad2b5e7
22c507f
d2ffc63
40ef2e2
29c5a8d
37933b4
4d3ed67
75f1f66
5f1aa4d
1efcd07
c1fd625
b6aab0c
d437286
dc60ea7
550dde9
21fa83b
61e7d42
51f5419
690d6d4
9433b8d
cfb0f00
9bc8c9f
ed4c615
1182b08
8394fa9
8dce9d4
6f109da
1b9395a
1c337dd
d427c9d
745eb19
cb6790e
9dd72c4
520931a
efa389b
87581ce
524bec8
1c27944
bebecd7
9757085
32e381e
c13f8c7
02d7036
5ae6973
ac49366
0a26397
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
// Copyright 2023 The Gitea Authors. All rights reserved. | ||
// SPDX-License-Identifier: MIT | ||
package util | ||
|
||
import ( | ||
"fmt" | ||
"math" | ||
"strconv" | ||
"strings" | ||
) | ||
|
||
// Check similar implementation in web_src/js/utils/color.js and keep synchronization | ||
|
||
// Return R, G, B values defined in reletive luminance | ||
func getLuminanceRGB(channel float64) float64 { | ||
sRGB := channel / 255 | ||
if sRGB <= 0.03928 { | ||
return sRGB / 12.92 | ||
} | ||
return math.Pow((sRGB+0.055)/1.055, 2.4) | ||
} | ||
|
||
// Get color as RGB values in 0..255 range from the hex color string (with or without #) | ||
// TODO: support return of rgba | ||
func HexToRBGColor(colorString string) (float64, float64, float64) { | ||
hexString := colorString | ||
if strings.HasPrefix(colorString, "#") { | ||
hexString = hexString[1:] | ||
} | ||
// only support transfer of rgb and rrggbb | ||
// if not in this format, use default values 0, 0, 0 | ||
if len(hexString) != 3 && len(hexString) != 6 { | ||
return 0, 0, 0 | ||
} | ||
if len(hexString) == 3 { | ||
hexString = fmt.Sprintf("%c%c%c%c%c%c", hexString[0], hexString[0], hexString[1], hexString[1], hexString[2], hexString[2]) | ||
} | ||
color, err := strconv.ParseUint(hexString, 16, 64) | ||
if err != nil { | ||
return 0, 0, 0 | ||
} | ||
r := float64(uint8(0xFF & (uint32(color) >> 16))) | ||
g := float64(uint8(0xFF & (uint32(color) >> 8))) | ||
b := float64(uint8(0xFF & uint32(color))) | ||
return r, g, b | ||
} | ||
|
||
// return luminance given RGB channels | ||
// Reference from: https://www.w3.org/WAI/GL/wiki/Relative_luminance | ||
func GetLuminance(r, g, b float64) float64 { | ||
R := getLuminanceRGB(r) | ||
G := getLuminanceRGB(g) | ||
B := getLuminanceRGB(b) | ||
luminance := 0.2126*R + 0.7152*G + 0.0722*B | ||
return luminance | ||
} | ||
|
||
// Reference from: https://firsching.ch/github_labels.html | ||
// In the future WCAG 3 APCA may be a better solution. | ||
// Check if text should use light color based on RGB of background | ||
func UseLightTextOnBackground(r, g, b float64) bool { | ||
return GetLuminance(r, g, b) < 0.453 | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
// Copyright 2023 The Gitea Authors. All rights reserved. | ||
// SPDX-License-Identifier: MIT | ||
package util | ||
|
||
import ( | ||
"testing" | ||
|
||
"github.com/stretchr/testify/assert" | ||
) | ||
|
||
func Test_HexToRBGColor(t *testing.T) { | ||
cases := []struct { | ||
colorString string | ||
expectedR float64 | ||
expectedG float64 | ||
expectedB float64 | ||
}{ | ||
{"2b8685", 43, 134, 133}, | ||
{"1e1", 17, 238, 17}, | ||
{"#1e1", 17, 238, 17}, | ||
{"3bb6", 0, 0, 0}, | ||
{"3bb64", 0, 0, 0}, | ||
{"#7e716c", 126, 113, 108}, | ||
{"#1e11", 0, 0, 0}, | ||
{"#3bb64", 0, 0, 0}, | ||
{"#2e2", 34, 238, 34}, | ||
{"2e2", 34, 238, 34}, | ||
} | ||
for n, c := range cases { | ||
r, g, b := HexToRBGColor(c.colorString) | ||
assert.Equal(t, c.expectedR, r, "case %d: error R should match: expected %f, but get %f", n, c.expectedR, r) | ||
assert.Equal(t, c.expectedG, g, "case %d: error G should match: expected %f, but get %f", n, c.expectedG, g) | ||
assert.Equal(t, c.expectedB, b, "case %d: error B should match: expected %f, but get %f", n, c.expectedB, b) | ||
} | ||
} | ||
|
||
func Test_UseLightTextOnBackground(t *testing.T) { | ||
cases := []struct { | ||
r float64 | ||
g float64 | ||
b float64 | ||
expected bool | ||
}{ | ||
{215, 58, 74, true}, | ||
{0, 117, 202, true}, | ||
{207, 211, 215, false}, | ||
{162, 238, 239, false}, | ||
{112, 87, 255, true}, | ||
{0, 134, 114, true}, | ||
{228, 230, 105, false}, | ||
{216, 118, 227, true}, | ||
{255, 255, 255, false}, | ||
{43, 134, 133, true}, | ||
{43, 135, 134, true}, | ||
{44, 135, 134, true}, | ||
{59, 182, 179, true}, | ||
{124, 114, 104, true}, | ||
{126, 113, 108, true}, | ||
{129, 112, 109, true}, | ||
{128, 112, 112, true}, | ||
} | ||
for n, c := range cases { | ||
result := UseLightTextOnBackground(c.r, c.g, c.b) | ||
assert.Equal(t, c.expected, result, "case %d: error should match", n) | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
// Check similar implementation in modules/util/color.go and keep synchronization | ||
// Return R, G, B values defined in reletive luminance | ||
function getLuminanceRGB(channel) { | ||
const sRGB = channel / 255; | ||
const res = (sRGB <= 0.03928) ? sRGB / 12.92 : ((sRGB + 0.055) / 1.055) ** 2.4; | ||
return res; | ||
} | ||
|
||
// Reference from: https://www.w3.org/WAI/GL/wiki/Relative_luminance | ||
function getLuminance(r, g, b) { | ||
const R = getLuminanceRGB(r); | ||
const G = getLuminanceRGB(g); | ||
const B = getLuminanceRGB(b); | ||
const luminance = 0.2126 * R + 0.7152 * G + 0.0722 * B; | ||
return luminance; | ||
HesterG marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} | ||
|
||
const convertHexUnitTo256 = (hexStr) => parseInt(hexStr.repeat(2 / hexStr.length), 16); | ||
|
||
const getAlphafloat = (a) => { | ||
if (a !== undefined) { | ||
return a / 255; | ||
} | ||
return 1; | ||
}; | ||
HesterG marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
const re = /.{1,2}/g; | ||
HesterG marked this conversation as resolved.
Show resolved
Hide resolved
|
||
// Get color as RGB values in 0..255 range from the hex color string (with or without #) | ||
export function hexToRGBColor(backgroundColorStr, ignoreAlpha = true) { | ||
HesterG marked this conversation as resolved.
Show resolved
Hide resolved
|
||
let backgroundColor = backgroundColorStr; | ||
if (backgroundColorStr[0] === '#') { | ||
backgroundColor = backgroundColorStr.substring(1); | ||
} | ||
// only support transfer of rgb, rgba, rrggbb, and rrggbbaa | ||
// if not in this format, use default values 0, 0, 0 or 0, 0, 0, 1 | ||
if (![3, 4, 6, 8].includes(backgroundColor.length)) { | ||
return ignoreAlpha ? [0, 0, 0] : [0, 0, 0, 1]; | ||
} | ||
// chunkSize is number of digits that should be grouped together to form a RGBA channel | ||
const chunkSize = Math.floor(backgroundColor.length / 3); | ||
// hexArr is array of [r, g, b] or [r, g, b, a], a could be undefined | ||
// and will be processed in getAlphafloat if ignoreAlpha is false | ||
const hexArr = chunkSize === 1 ? backgroundColor.split('') : backgroundColor.match(re); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Does the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is to get rgb/rgba from rrggbb/rrggbbaa, and I am not sure how to get them as a group of two using string methods without using match. Update: |
||
const [r, g, b, a] = hexArr.map(convertHexUnitTo256); | ||
return ignoreAlpha ? [r, g, b] : [r, g, b, getAlphafloat(a)]; | ||
} | ||
|
||
// Reference from: https://firsching.ch/github_labels.html | ||
// In the future WCAG 3 APCA may be a better solution. | ||
// Check if text should use light color based on RGB of background | ||
export function useLightTextOnBackground(r, g, b) { | ||
return getLuminance(r, g, b) < 0.453; | ||
} |
Uh oh!
There was an error while loading. Please reload this page.