mirror of
				https://github.com/go-gitea/gitea.git
				synced 2025-10-26 12:27:06 +00:00 
			
		
		
		
	Actually compute proper foreground color for labels (#16729)
This commit is contained in:
		| @@ -8,6 +8,7 @@ package models | |||||||
| import ( | import ( | ||||||
| 	"fmt" | 	"fmt" | ||||||
| 	"html/template" | 	"html/template" | ||||||
|  | 	"math" | ||||||
| 	"regexp" | 	"regexp" | ||||||
| 	"strconv" | 	"strconv" | ||||||
| 	"strings" | 	"strings" | ||||||
| @@ -138,19 +139,44 @@ func (label *Label) BelongsToRepo() bool { | |||||||
| 	return label.RepoID > 0 | 	return label.RepoID > 0 | ||||||
| } | } | ||||||
|  |  | ||||||
|  | // SrgbToLinear converts a component of an sRGB color to its linear intensity | ||||||
|  | // See: https://en.wikipedia.org/wiki/SRGB#The_reverse_transformation_(sRGB_to_CIE_XYZ) | ||||||
|  | func SrgbToLinear(color uint8) float64 { | ||||||
|  | 	flt := float64(color) / 255 | ||||||
|  | 	if flt <= 0.04045 { | ||||||
|  | 		return flt / 12.92 | ||||||
|  | 	} | ||||||
|  | 	return math.Pow((flt+0.055)/1.055, 2.4) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Luminance returns the luminance of an sRGB color | ||||||
|  | func Luminance(color uint32) float64 { | ||||||
|  | 	r := SrgbToLinear(uint8(0xFF & (color >> 16))) | ||||||
|  | 	g := SrgbToLinear(uint8(0xFF & (color >> 8))) | ||||||
|  | 	b := SrgbToLinear(uint8(0xFF & color)) | ||||||
|  |  | ||||||
|  | 	// luminance ratios for sRGB | ||||||
|  | 	return 0.2126*r + 0.7152*g + 0.0722*b | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // LuminanceThreshold is the luminance at which white and black appear to have the same contrast | ||||||
|  | // i.e. x such that 1.05 / (x + 0.05) = (x + 0.05) / 0.05 | ||||||
|  | // i.e. math.Sqrt(1.05*0.05) - 0.05 | ||||||
|  | const LuminanceThreshold float64 = 0.179 | ||||||
|  |  | ||||||
| // ForegroundColor calculates the text color for labels based | // ForegroundColor calculates the text color for labels based | ||||||
| // on their background color. | // on their background color. | ||||||
| func (label *Label) ForegroundColor() template.CSS { | func (label *Label) ForegroundColor() template.CSS { | ||||||
| 	if strings.HasPrefix(label.Color, "#") { | 	if strings.HasPrefix(label.Color, "#") { | ||||||
| 		if color, err := strconv.ParseUint(label.Color[1:], 16, 64); err == nil { | 		if color, err := strconv.ParseUint(label.Color[1:], 16, 64); err == nil { | ||||||
| 			r := float32(0xFF & (color >> 16)) | 			// NOTE: see web_src/js/components/ContextPopup.vue for similar implementation | ||||||
| 			g := float32(0xFF & (color >> 8)) | 			luminance := Luminance(uint32(color)) | ||||||
| 			b := float32(0xFF & color) |  | ||||||
| 			luminance := (0.2126*r + 0.7152*g + 0.0722*b) / 255 |  | ||||||
|  |  | ||||||
| 			if luminance < 0.66 { | 			// prefer white or black based upon contrast | ||||||
|  | 			if luminance < LuminanceThreshold { | ||||||
| 				return template.CSS("#fff") | 				return template.CSS("#fff") | ||||||
| 			} | 			} | ||||||
|  | 			return template.CSS("#000") | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|   | |||||||
| @@ -24,6 +24,22 @@ import {SvgIcon} from '../svg.js'; | |||||||
|  |  | ||||||
| const {AppSubUrl} = window.config; | const {AppSubUrl} = window.config; | ||||||
|  |  | ||||||
|  | // NOTE: see models/issue_label.go for similar implementation | ||||||
|  | const srgbToLinear = (color) => { | ||||||
|  |   color /= 255; | ||||||
|  |   if (color <= 0.04045) { | ||||||
|  |     return color / 12.92; | ||||||
|  |   } | ||||||
|  |   return ((color + 0.055) / 1.055) ** 2.4; | ||||||
|  | }; | ||||||
|  | const luminance = (colorString) => { | ||||||
|  |   const r = srgbToLinear(parseInt(colorString.substring(0, 2), 16)); | ||||||
|  |   const g = srgbToLinear(parseInt(colorString.substring(2, 4), 16)); | ||||||
|  |   const b = srgbToLinear(parseInt(colorString.substring(4, 6), 16)); | ||||||
|  |   return 0.2126 * r + 0.7152 * g + 0.0722 * b; | ||||||
|  | }; | ||||||
|  | const luminanceThreshold = 0.179; | ||||||
|  |  | ||||||
| export default { | export default { | ||||||
|   name: 'ContextPopup', |   name: 'ContextPopup', | ||||||
|  |  | ||||||
| @@ -74,14 +90,13 @@ export default { | |||||||
|  |  | ||||||
|     labels() { |     labels() { | ||||||
|       return this.issue.labels.map((label) => { |       return this.issue.labels.map((label) => { | ||||||
|         const red = parseInt(label.color.substring(0, 2), 16); |         let textColor; | ||||||
|         const green = parseInt(label.color.substring(2, 4), 16); |         if (luminance(label.color) < luminanceThreshold) { | ||||||
|         const blue = parseInt(label.color.substring(4, 6), 16); |           textColor = '#ffffff'; | ||||||
|         let color = '#ffffff'; |         } else { | ||||||
|         if ((red * 0.299 + green * 0.587 + blue * 0.114) > 125) { |           textColor = '#000000'; | ||||||
|           color = '#000000'; |  | ||||||
|         } |         } | ||||||
|         return {name: label.name, color: `#${label.color}`, textColor: color}; |         return {name: label.name, color: `#${label.color}`, textColor}; | ||||||
|       }); |       }); | ||||||
|     } |     } | ||||||
|   }, |   }, | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 Clar Fon
					Clar Fon