1 |
|
|
2 |
|
|
3 |
|
|
4 |
|
|
5 |
|
|
6 |
|
|
7 |
|
|
8 |
|
|
9 |
|
|
10 |
|
|
11 |
|
|
12 |
|
|
13 |
|
|
14 |
|
|
15 |
|
|
16 |
|
|
17 |
|
|
18 |
|
|
19 |
|
|
20 |
|
|
21 |
|
|
22 |
|
|
23 |
|
|
24 |
|
|
25 |
|
|
26 |
|
|
27 |
|
|
28 |
|
|
29 |
|
|
30 |
|
|
31 |
|
|
32 |
|
|
33 |
|
|
34 |
|
package info.magnolia.imaging.util; |
35 |
|
|
36 |
|
import java.awt.Color; |
37 |
|
import java.lang.reflect.Field; |
38 |
|
import java.lang.reflect.Modifier; |
39 |
|
import java.util.Map; |
40 |
|
import java.util.TreeMap; |
41 |
|
import java.util.regex.Matcher; |
42 |
|
import java.util.regex.Pattern; |
43 |
|
|
44 |
|
import org.apache.commons.lang3.StringUtils; |
45 |
|
|
46 |
|
|
47 |
|
|
48 |
|
@link |
49 |
|
|
50 |
|
|
|
|
| 76.3% |
Uncovered Elements: 32 (135) |
Complexity: 38 |
Complexity Density: 0.43 |
|
51 |
|
public class ColorConverter { |
52 |
|
private static final Map<String, Color> namedColors = reflectivelyGetNamedColors(); |
53 |
|
private static final Pattern HEX = Pattern.compile("^#[0-9A-Fa-f]{3,6}$"); |
54 |
|
private static final Pattern FUNC = Pattern.compile("^(rgb|rgba|hsl|hsla)\\(" + |
55 |
|
"([0-9]{1,3})" + |
56 |
|
",([0-9]{1,3}%?)" + |
57 |
|
",([0-9]{1,3}%?)" + |
58 |
|
"(?:,([0-9.]{1,4}))?" + |
59 |
|
"\\)$"); |
60 |
|
|
|
|
| 80.8% |
Uncovered Elements: 10 (52) |
Complexity: 13 |
Complexity Density: 0.36 |
|
61 |
23 |
public static Color toColor(final String input) {... |
62 |
22 |
if (input == null) { |
63 |
0 |
throw cantParse(input, "please provide either an #ffffff or #fff hexadecimal value or a known named color."); |
64 |
|
} |
65 |
22 |
final String code = StringUtils.deleteWhitespace(input).toLowerCase(); |
66 |
22 |
if (namedColors.containsKey(code)) { |
67 |
4 |
return namedColors.get(code); |
68 |
18 |
} else if (HEX.matcher(code).matches()) { |
69 |
8 |
if (StringUtils.length(code) == 4) { |
70 |
|
|
71 |
4 |
return Color.decode("#" + code.charAt(1) + code.charAt(1) + code.charAt(2) + code.charAt(2) + code.charAt(3) + code.charAt(3)); |
72 |
4 |
} else if (StringUtils.length(code) == 7) { |
73 |
4 |
return Color.decode(code); |
74 |
|
} else { |
75 |
0 |
throw cantParse(input, "please provide a 6 or 3 digit long hexadecimal string, e.g #ffffff or #fff."); |
76 |
|
} |
77 |
|
|
78 |
|
} else { |
79 |
10 |
final Matcher matcher = FUNC.matcher(code); |
80 |
8 |
if (matcher.matches()) { |
81 |
8 |
final String function = matcher.group(1); |
82 |
|
|
83 |
|
|
84 |
8 |
final String alphaStr = matcher.group(5); |
85 |
8 |
final int alpha; |
86 |
8 |
if (StringUtils.endsWith(function, "a")) { |
87 |
3 |
alpha = (int) (rangedFloat(alphaStr, input, 0, 1) * 255); |
88 |
4 |
} else if (alphaStr != null) { |
89 |
0 |
throw cantParse(input, "the " + function + "() function does not take an <alphavalue> argument."); |
90 |
|
} else { |
91 |
4 |
alpha = 255; |
92 |
|
} |
93 |
|
|
94 |
6 |
switch (function) { |
95 |
3 |
case "rgb": |
96 |
3 |
return new Color( |
97 |
|
rangedInt(0, 255, matcher, 2, input), |
98 |
|
rangedInt(0, 255, matcher, 3, input), |
99 |
|
rangedInt(0, 255, matcher, 4, input) |
100 |
|
); |
101 |
1 |
case "rgba": |
102 |
1 |
return new Color( |
103 |
|
rangedInt(0, 255, matcher, 2, input), |
104 |
|
rangedInt(0, 255, matcher, 3, input), |
105 |
|
rangedInt(0, 255, matcher, 4, input), |
106 |
|
alpha |
107 |
|
); |
108 |
1 |
case "hsl": |
109 |
1 |
return new Color(hsbToRgb(input, matcher)); |
110 |
1 |
case "hsla": |
111 |
1 |
int rgb = hsbToRgb(input, matcher); |
112 |
1 |
int rgba = (alpha & 0xFF) << 24 | rgb; |
113 |
1 |
final Color color = new Color(rgba); |
114 |
1 |
return new Color(color.getRed(), color.getGreen(), color.getBlue(), alpha); |
115 |
0 |
default: |
116 |
0 |
throw cantParse(input, "'" + function + "()' is not a valid function. Please use rgb(), rgba(), hsl(), hsla(), a #ffffff hexadecimal value or a known named color."); |
117 |
|
} |
118 |
|
} |
119 |
|
} |
120 |
0 |
throw cantParse(input, "please provide either an #ffffff or #fff hexadecimal value or a known named color."); |
121 |
|
} |
122 |
|
|
|
|
| 100% |
Uncovered Elements: 0 (2) |
Complexity: 1 |
Complexity Density: 0.5 |
|
123 |
2 |
private static int hsbToRgb(String input, Matcher matcher) {... |
124 |
2 |
final float[] hsb = hslToHsv( |
125 |
|
normalizeAngle(Integer.parseInt(matcher.group(2))), |
126 |
|
percentage(matcher.group(3), input), |
127 |
|
percentage(matcher.group(4), input)); |
128 |
2 |
return Color.HSBtoRGB(hsb[0], hsb[1], hsb[2]); |
129 |
|
} |
130 |
|
|
131 |
|
|
132 |
|
@param |
133 |
|
@param |
134 |
|
@param |
135 |
|
@return |
136 |
|
|
|
|
| 0% |
Uncovered Elements: 6 (6) |
Complexity: 1 |
Complexity Density: 0.17 |
|
137 |
0 |
private static float[] convertHslToHsb(float hslH, float hslS, float hslL) {... |
138 |
0 |
final float h = hslH / 360; |
139 |
0 |
final float s = ((float) hslS / 100); |
140 |
0 |
final float l = ((float) hslL / 100); |
141 |
0 |
final float hsbB = |
142 |
|
( |
143 |
|
(2 * l) + (hslS * (1 - Math.abs((2 * l) - 1))) |
144 |
|
) |
145 |
|
/ 2; |
146 |
0 |
final float hsbS = (2 * (hsbB - l)) / hsbB; |
147 |
|
|
148 |
0 |
return new float[] { h, hsbS, hsbB }; |
149 |
|
} |
150 |
|
|
151 |
|
|
152 |
|
@param |
153 |
|
@param |
154 |
|
@param |
155 |
|
@return |
156 |
|
|
157 |
|
|
158 |
|
|
|
|
| 85% |
Uncovered Elements: 3 (20) |
Complexity: 4 |
Complexity Density: 0.29 |
|
159 |
2 |
static float[] hslToHsv(int hslH, int hslS, int hslL) {... |
160 |
2 |
final float h = (float) hslH / 360; |
161 |
2 |
final float s = ((float) hslS / 100); |
162 |
2 |
final float l = ((float) hslL / 100); |
163 |
|
|
164 |
2 |
float saturation = 0.0f; |
165 |
2 |
float value = 0.0f; |
166 |
2 |
if (l > 0) { |
167 |
2 |
float lumScale = 1.0f - Math.max(l - 0.5f, 0f) * 2f; |
168 |
2 |
lumScale = (lumScale == 0f) ? 0f : 1.0f / lumScale; |
169 |
2 |
float lumStart = Math.max(0f, lumScale - 0.5f); |
170 |
2 |
float lumDiv = (lumScale - lumStart); |
171 |
2 |
lumDiv = (lumStart + (s * lumDiv)); |
172 |
2 |
saturation = (lumDiv == 0) ? 0 : (s / lumDiv); |
173 |
2 |
value = l + (1.0f - l) * s; |
174 |
|
} |
175 |
2 |
return new float[] { h, saturation, value }; |
176 |
|
} |
177 |
|
|
|
|
| 100% |
Uncovered Elements: 0 (2) |
Complexity: 1 |
Complexity Density: 0.5 |
|
178 |
12 |
private static int rangedInt(int min, int max, Matcher matcher, int groupNr, String wholeInputForReporting) {... |
179 |
12 |
final String value = matcher.group(groupNr); |
180 |
12 |
return rangedInt(min, max, value, wholeInputForReporting); |
181 |
|
} |
182 |
|
|
|
|
| 60% |
Uncovered Elements: 4 (10) |
Complexity: 4 |
Complexity Density: 0.67 |
|
183 |
16 |
private static int rangedInt(int min, int max, String value, String wholeInputForReporting) {... |
184 |
16 |
if (StringUtils.endsWith(value, "%")) { |
185 |
0 |
throw cantParse(wholeInputForReporting, value + " should not be a percentage value."); |
186 |
|
} |
187 |
16 |
final int i = Integer.parseInt(value); |
188 |
15 |
if (i < min || i > max) { |
189 |
0 |
throw cantParse(wholeInputForReporting, "%s is not in the allowed %d-%d range.", value, min, max); |
190 |
|
} |
191 |
15 |
return i; |
192 |
|
} |
193 |
|
|
|
|
| 66.7% |
Uncovered Elements: 2 (6) |
Complexity: 3 |
Complexity Density: 0.75 |
|
194 |
3 |
private static float rangedFloat(String value, String wholeInputForReporting, float min, float max) {... |
195 |
3 |
final float f = Float.parseFloat(value); |
196 |
2 |
if (f < min || f > max) { |
197 |
0 |
throw cantParse(wholeInputForReporting, "%s is not in the allowed %.1f-%.1f <alphavalue> range.", value, min, max); |
198 |
|
} |
199 |
2 |
return f; |
200 |
|
} |
201 |
|
|
|
|
| 60% |
Uncovered Elements: 2 (5) |
Complexity: 2 |
Complexity Density: 0.67 |
|
202 |
5 |
static int percentage(String value, String wholeInputForReporting) {... |
203 |
4 |
if (!StringUtils.endsWith(value, "%")) { |
204 |
0 |
throw cantParse(wholeInputForReporting, value + " should be a percentage value."); |
205 |
|
} |
206 |
4 |
return rangedInt(0, 100, value.substring(0, value.length() - 1), wholeInputForReporting); |
207 |
|
} |
208 |
|
|
|
|
| 100% |
Uncovered Elements: 0 (6) |
Complexity: 2 |
Complexity Density: 0.5 |
|
209 |
16 |
static int normalizeAngle(int angle) {... |
210 |
16 |
final int modulo = angle % 360; |
211 |
16 |
if (modulo < 0) { |
212 |
5 |
return modulo + 360; |
213 |
|
} else { |
214 |
11 |
return modulo; |
215 |
|
} |
216 |
|
} |
217 |
|
|
218 |
|
|
219 |
|
|
220 |
|
|
221 |
|
|
222 |
|
|
223 |
|
|
224 |
|
|
225 |
|
|
226 |
|
|
227 |
|
|
|
|
| 85.7% |
Uncovered Elements: 2 (14) |
Complexity: 6 |
Complexity Density: 0.6 |
|
228 |
1 |
private static Map<String, Color> reflectivelyGetNamedColors() {... |
229 |
1 |
try { |
230 |
1 |
final Map<String, Color> namedColors = new TreeMap<String, Color>(String.CASE_INSENSITIVE_ORDER); |
231 |
1 |
final Field[] fields = Color.class.getFields(); |
232 |
1 |
for (final Field field : fields) { |
233 |
29 |
int mod = field.getModifiers(); |
234 |
29 |
if (Modifier.isStatic(mod) && Modifier.isPublic(mod) && Modifier.isFinal(mod)) { |
235 |
29 |
if (Color.class.equals(field.getType())) { |
236 |
26 |
namedColors.put(field.getName().toLowerCase(), (Color) field.get(null)); |
237 |
|
} |
238 |
|
} |
239 |
|
} |
240 |
1 |
return namedColors; |
241 |
|
} catch (IllegalAccessException e) { |
242 |
0 |
throw new RuntimeException("Can't access field values of java.awt.Color, is this system too secure?", e); |
243 |
|
} |
244 |
|
} |
245 |
|
|
|
|
| 0% |
Uncovered Elements: 1 (1) |
Complexity: 1 |
Complexity Density: 1 |
|
246 |
0 |
private static RuntimeException cantParse(String input, String messageFmt, Object... args) {... |
247 |
0 |
return new IllegalArgumentException("Can't decode color " + input + ": " + String.format(messageFmt, args)); |
248 |
|
} |
249 |
|
|
250 |
|
} |